From b7d97dbaacfef3600db0c34a84570625979c4aa1 Mon Sep 17 00:00:00 2001 From: Zhao Wei Liew Date: Sat, 1 Oct 2016 19:36:07 +0800 Subject: DownloadProvider: Add support for manual pause/resume Implement the following features: - Manually pause/resume individual downloads in DownloadManager - Manually pause/resume all downloads in notification Based on commit ecd609e7017b8a69688bbae25c17d878ea305f19. Change-Id: I433cdee2de8b3add0248bbb0a9d02f8da4e5bb38 --- src/com/android/providers/downloads/Constants.java | 9 +++ .../providers/downloads/DownloadNotifier.java | 72 ++++++++++++++++++++-- .../providers/downloads/DownloadReceiver.java | 20 ++++++ .../downloads/DownloadStorageProvider.java | 17 ++++- .../providers/downloads/DownloadThread.java | 7 ++- 5 files changed, 116 insertions(+), 9 deletions(-) (limited to 'src/com/android/providers/downloads') 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 b325117d..dce3c949 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; @@ -47,6 +48,7 @@ import android.util.LongSparseLongArray; import com.android.internal.util.ArrayUtils; +import java.lang.StringBuilder; import java.text.NumberFormat; import javax.annotation.concurrent.GuardedBy; @@ -193,9 +195,30 @@ public class DownloadNotifier { } builder.setWhen(firstShown); + /** + * 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) { - builder.setSmallIcon(android.R.drawable.stat_sys_download); + 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) { @@ -231,6 +254,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); @@ -329,7 +378,9 @@ public class DownloadNotifier { builder.setContentTitle(getDownloadTitle(res, cursor)); if (type == TYPE_ACTIVE) { - if (speedAsSizeText != null) { + 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 { @@ -367,8 +418,19 @@ 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); if (speedAsSizeText != null) { @@ -471,7 +533,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 a0dc6947..b61ae5ec 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 4ec8e2d1..acf7c536 100644 --- a/src/com/android/providers/downloads/DownloadStorageProvider.java +++ b/src/com/android/providers/downloads/DownloadStorageProvider.java @@ -376,6 +376,10 @@ public class DownloadStorageProvider extends DocumentsProvider { 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 @@ -387,14 +391,21 @@ public class DownloadStorageProvider extends DocumentsProvider { 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 c6b4c71a..fac4e670 100644 --- a/src/com/android/providers/downloads/DownloadThread.java +++ b/src/com/android/providers/downloads/DownloadThread.java @@ -26,6 +26,7 @@ import static android.provider.Downloads.Impl.STATUS_CANNOT_RESUME; 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_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; @@ -154,9 +155,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; @@ -206,6 +209,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!"); } -- cgit v1.2.3