diff options
Diffstat (limited to 'src/com/android/providers')
5 files changed, 159 insertions, 13 deletions
diff --git a/src/com/android/providers/downloads/Constants.java b/src/com/android/providers/downloads/Constants.java index 79daeaed..6633057e 100644 --- a/src/com/android/providers/downloads/Constants.java +++ b/src/com/android/providers/downloads/Constants.java @@ -57,6 +57,15 @@ public class Constants { /** the intent that gets sent when deleting the notification of a completed download */ public static final String ACTION_HIDE = "android.intent.action.DOWNLOAD_HIDE"; + /** the intent that gets sent when manually resuming a manually paused download queue */ + public static final String ACTION_RESUME_QUEUE = "android.intent.action.DOWNLOAD_RESUME_QUEUE"; + + /** the intent that gets sent when manually resuming a manually paused download */ + public static final String ACTION_RESUME = "android.intent.action.DOWNLOAD_RESUME"; + + /** the intent that gets sent when manually pausing a running download */ + public static final String ACTION_PAUSE = "android.intent.action.DOWNLOAD_PAUSE"; + /** The default base name for downloaded files if we can't get one at the HTTP level */ public static final String DEFAULT_DL_FILENAME = "downloadfile"; diff --git a/src/com/android/providers/downloads/DownloadNotifier.java b/src/com/android/providers/downloads/DownloadNotifier.java index d38aa755..db24db66 100644 --- a/src/com/android/providers/downloads/DownloadNotifier.java +++ b/src/com/android/providers/downloads/DownloadNotifier.java @@ -19,6 +19,7 @@ package com.android.providers.downloads; import static android.app.DownloadManager.Request.VISIBILITY_VISIBLE; import static android.app.DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED; import static android.app.DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION; +import static android.provider.Downloads.Impl.STATUS_PAUSED_MANUAL; import static android.provider.Downloads.Impl.STATUS_QUEUED_FOR_WIFI; import static android.provider.Downloads.Impl.STATUS_RUNNING; @@ -40,6 +41,7 @@ import android.provider.Downloads; import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.text.format.DateUtils; +import android.text.format.Formatter; import android.util.ArrayMap; import android.util.IntArray; import android.util.Log; @@ -47,6 +49,7 @@ import android.util.LongSparseLongArray; import com.android.internal.util.ArrayUtils; +import java.lang.StringBuilder; import java.text.NumberFormat; import javax.annotation.concurrent.GuardedBy; @@ -221,6 +224,36 @@ public class DownloadNotifier { builder.setWhen(firstShown); builder.setOnlyAlertOnce(true); + /** + * If *all* current downloads in the cluster are manually paused, + * display the appropriate notification icon and content. + * + * Also keep track of the number of paused downloads so as to + * display the current number of downloading files correctly. + */ + int numPaused = 0; + for (int j = 0; j < cluster.size(); j++) { + cursor.moveToPosition(cluster.get(j)); + int status = cursor.getInt(UpdateQuery.STATUS); + if (status == Downloads.Impl.STATUS_PAUSED_MANUAL) { + numPaused++; + } + } + boolean isClusterPaused = numPaused == cluster.size(); + + // Show relevant icon + if (type == TYPE_ACTIVE) { + if (isClusterPaused) { + builder.setSmallIcon(R.drawable.download_pause); + } else { + builder.setSmallIcon(android.R.drawable.stat_sys_download); + } + } else if (type == TYPE_WAITING) { + builder.setSmallIcon(android.R.drawable.stat_sys_warning); + } else if (type == TYPE_COMPLETE) { + builder.setSmallIcon(android.R.drawable.stat_sys_download_done); + } + // Build action intents if (type == TYPE_ACTIVE || type == TYPE_WAITING) { final long[] downloadIds = getDownloadIds(cursor, cluster); @@ -252,6 +285,32 @@ public class DownloadNotifier { PendingIntent.getBroadcast(mContext, 0, cancelIntent, PendingIntent.FLAG_UPDATE_CURRENT)); + if (isClusterPaused) { + // Add a Resume action + final Intent resumeIntent = new Intent(Constants.ACTION_RESUME_QUEUE, + uri, mContext, DownloadReceiver.class); + resumeIntent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS, + downloadIds); + + builder.addAction( + com.android.internal.R.drawable.ic_media_play, + res.getString(R.string.button_resume_download), + PendingIntent.getBroadcast(mContext, + 0, resumeIntent, PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + // Add a Pause action + final Intent pauseIntent = new Intent(Constants.ACTION_PAUSE, + uri, mContext, DownloadReceiver.class); + pauseIntent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS, + downloadIds); + + builder.addAction( + com.android.internal.R.drawable.ic_media_pause, + res.getString(R.string.button_pause_download), + PendingIntent.getBroadcast(mContext, + 0, pauseIntent, PendingIntent.FLAG_UPDATE_CURRENT)); + } + } else if (type == TYPE_COMPLETE) { cursor.moveToPosition(cluster.get(0)); final long id = cursor.getLong(UpdateQuery._ID); @@ -285,6 +344,7 @@ public class DownloadNotifier { // Calculate and show progress String remainingText = null; String percentText = null; + String speedAsSizeText = null; if (type == TYPE_ACTIVE) { long current = 0; long total = 0; @@ -311,8 +371,26 @@ public class DownloadNotifier { if (speed > 0) { final long remainingMillis = ((total - current) * 1000) / speed; + final int duration, durationResId; + + // This duplicates DateUtils.formatDuration(), + // but uses our abbreviated plurals. + if (remainingMillis >= DateUtils.HOUR_IN_MILLIS) { + duration = (int) ((remainingMillis + 1800000) + / DateUtils.HOUR_IN_MILLIS); + durationResId = R.plurals.duration_hours; + } else if (remainingMillis >= DateUtils.MINUTE_IN_MILLIS) { + duration = (int) ((remainingMillis + 30000) + / DateUtils.MINUTE_IN_MILLIS); + durationResId = R.plurals.duration_minutes; + } else { + duration = (int) ((remainingMillis + 500) + / DateUtils.SECOND_IN_MILLIS); + durationResId = R.plurals.duration_seconds; + } remainingText = res.getString(R.string.download_remaining, - DateUtils.formatDuration(remainingMillis)); + res.getQuantityString(durationResId, duration, duration)); + speedAsSizeText = Formatter.formatFileSize(mContext, speed); } final int percent = (int) ((current * 100) / total); @@ -329,11 +407,18 @@ public class DownloadNotifier { builder.setContentTitle(getDownloadTitle(res, cursor)); if (type == TYPE_ACTIVE) { - final String description = cursor.getString(UpdateQuery.DESCRIPTION); - if (!TextUtils.isEmpty(description)) { - builder.setContentText(description); + if (isClusterPaused) { + builder.setContentText(res.getText(R.string.download_paused)); + } else if (speedAsSizeText != null) { + builder.setContentText(res.getString(R.string.text_download_speed, + remainingText, speedAsSizeText)); } else { - builder.setContentText(remainingText); + final String description = cursor.getString(UpdateQuery.DESCRIPTION); + if (!TextUtils.isEmpty(description)) { + builder.setContentText(description); + } else { + builder.setContentText(remainingText); + } } builder.setContentInfo(percentText); @@ -362,11 +447,27 @@ public class DownloadNotifier { } if (type == TYPE_ACTIVE) { - builder.setContentTitle(res.getQuantityString( - R.plurals.notif_summary_active, cluster.size(), cluster.size())); + if (isClusterPaused) { + builder.setContentTitle(res.getString(R.string.download_paused)); + } else if (numPaused > 0) { + StringBuilder sb = new StringBuilder(res.getQuantityString( + R.plurals.notif_summary_active, + cluster.size() - numPaused, cluster.size())); + sb.append(", "); + sb.append(res.getString(R.string.notif_summary_paused, numPaused)); + builder.setContentTitle(sb.toString()); + } else { + builder.setContentTitle(res.getQuantityString( + R.plurals.notif_summary_active, cluster.size(), cluster.size())); + } builder.setContentText(remainingText); builder.setContentInfo(percentText); - inboxStyle.setSummaryText(remainingText); + if (speedAsSizeText != null) { + inboxStyle.setSummaryText(res.getString(R.string.text_download_speed, + remainingText, speedAsSizeText)); + } else { + inboxStyle.setSummaryText(remainingText); + } } else if (type == TYPE_WAITING) { builder.setContentTitle(res.getQuantityString( @@ -461,7 +562,7 @@ public class DownloadNotifier { } private static boolean isActiveAndVisible(int status, int visibility) { - return status == STATUS_RUNNING && + return (status == STATUS_RUNNING || status == STATUS_PAUSED_MANUAL) && (visibility == VISIBILITY_VISIBLE || visibility == VISIBILITY_VISIBLE_NOTIFY_COMPLETED); } diff --git a/src/com/android/providers/downloads/DownloadReceiver.java b/src/com/android/providers/downloads/DownloadReceiver.java index 92d0bad4..96b22621 100644 --- a/src/com/android/providers/downloads/DownloadReceiver.java +++ b/src/com/android/providers/downloads/DownloadReceiver.java @@ -114,6 +114,26 @@ public class DownloadReceiver extends BroadcastReceiver { NotificationManager notifManager = (NotificationManager) context.getSystemService( Context.NOTIFICATION_SERVICE); notifManager.cancel(notifTag, 0); + } else if (Constants.ACTION_PAUSE.equals(action)) { + long[] downloadIds = intent.getLongArrayExtra( + DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS); + DownloadManager manager = (DownloadManager) context.getSystemService( + Context.DOWNLOAD_SERVICE); + for (long id : downloadIds) { + manager.pauseDownload(id); + } + } else if (Constants.ACTION_RESUME_QUEUE.equals(action)) { + long[] downloadIds = intent.getLongArrayExtra( + DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS); + DownloadManager manager = (DownloadManager) context.getSystemService( + Context.DOWNLOAD_SERVICE); + for (long id : downloadIds) { + manager.resumeDownload(id); + Helpers.scheduleJob(context, DownloadInfo.queryDownloadInfo(context, id)); + } + } else if (Constants.ACTION_RESUME.equals(action)) { + long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1); + Helpers.scheduleJob(context, DownloadInfo.queryDownloadInfo(context, id)); } } diff --git a/src/com/android/providers/downloads/DownloadStorageProvider.java b/src/com/android/providers/downloads/DownloadStorageProvider.java index afcba961..b8f47a85 100644 --- a/src/com/android/providers/downloads/DownloadStorageProvider.java +++ b/src/com/android/providers/downloads/DownloadStorageProvider.java @@ -481,6 +481,10 @@ public class DownloadStorageProvider extends FileSystemProvider { int extraFlags = Document.FLAG_PARTIAL; final int status = cursor.getInt( cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS)); + final long progress = cursor.getLong(cursor.getColumnIndexOrThrow( + DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); + final int reason = cursor.getInt( + cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_REASON)); switch (status) { case DownloadManager.STATUS_SUCCESSFUL: // Verify that the document still exists in external storage. This is necessary @@ -492,14 +496,21 @@ public class DownloadStorageProvider extends FileSystemProvider { extraFlags = Document.FLAG_SUPPORTS_RENAME; // only successful is non-partial break; case DownloadManager.STATUS_PAUSED: - summary = getContext().getString(R.string.download_queued); + if (reason == DownloadManager.PAUSED_MANUAL) { + if (size != null) { + long percent = progress * 100 / size; + summary = getContext().getString(R.string.download_paused_percent, percent); + } else { + summary = getContext().getString(R.string.download_paused); + } + } else { + summary = getContext().getString(R.string.download_queued); + } break; case DownloadManager.STATUS_PENDING: summary = getContext().getString(R.string.download_queued); break; case DownloadManager.STATUS_RUNNING: - final long progress = cursor.getLong(cursor.getColumnIndexOrThrow( - DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); if (size != null) { String percent = NumberFormat.getPercentInstance().format((double) progress / size); diff --git a/src/com/android/providers/downloads/DownloadThread.java b/src/com/android/providers/downloads/DownloadThread.java index 54cc1a5d..5dd69860 100644 --- a/src/com/android/providers/downloads/DownloadThread.java +++ b/src/com/android/providers/downloads/DownloadThread.java @@ -29,6 +29,7 @@ import static android.provider.Downloads.Impl.STATUS_FILE_ERROR; import static android.provider.Downloads.Impl.STATUS_HTTP_DATA_ERROR; import static android.provider.Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR; import static android.provider.Downloads.Impl.STATUS_PAUSED_BY_APP; +import static android.provider.Downloads.Impl.STATUS_PAUSED_MANUAL; import static android.provider.Downloads.Impl.STATUS_QUEUED_FOR_WIFI; import static android.provider.Downloads.Impl.STATUS_RUNNING; import static android.provider.Downloads.Impl.STATUS_SUCCESS; @@ -159,9 +160,11 @@ public class DownloadThread extends Thread { private static final String NOT_DELETED = COLUMN_DELETED + " == '0'"; private static final String NOT_PAUSED = "(" + COLUMN_CONTROL + " IS NULL OR " + COLUMN_CONTROL + " != '" + CONTROL_PAUSED + "')"; + private static final String NOT_PAUSED_MANUAL = COLUMN_STATUS + " != '" + + STATUS_PAUSED_MANUAL + "'"; private static final String SELECTION_VALID = NOT_CANCELED + " AND " + NOT_DELETED + " AND " - + NOT_PAUSED; + + NOT_PAUSED + " AND " + NOT_PAUSED_MANUAL; public DownloadInfoDelta(DownloadInfo info) { mUri = info.mUri; @@ -211,6 +214,8 @@ public class DownloadThread extends Thread { buildContentValues(), SELECTION_VALID, null) == 0) { if (mInfo.queryDownloadControl() == CONTROL_PAUSED) { throw new StopRequestException(STATUS_PAUSED_BY_APP, "Download paused!"); + } else if (mInfo.queryDownloadStatus() == STATUS_PAUSED_MANUAL) { + throw new StopRequestException(STATUS_PAUSED_MANUAL, "Download paused manually!"); } else { throw new StopRequestException(STATUS_CANCELED, "Download deleted or missing!"); } |