diff options
5 files changed, 93 insertions, 2 deletions
diff --git a/src/com/android/providers/downloads/Constants.java b/src/com/android/providers/downloads/Constants.java index 7b8fcd24..6cea8086 100644 --- a/src/com/android/providers/downloads/Constants.java +++ b/src/com/android/providers/downloads/Constants.java @@ -54,6 +54,9 @@ public class Constants { /** the intent that gets sent when clicking an incomplete/failed download */ public static final String ACTION_LIST = "android.intent.action.DOWNLOAD_LIST"; + /** the intent that gets sent when canceling a download */ + public static final String ACTION_CANCEL = "android.intent.action.DOWNLOAD_CANCEL"; + /** the intent that gets sent when deleting the notification of a completed download */ public static final String ACTION_HIDE = "android.intent.action.DOWNLOAD_HIDE"; diff --git a/src/com/android/providers/downloads/DownloadNotifier.java b/src/com/android/providers/downloads/DownloadNotifier.java index 60c249f9..2ff8b634 100644 --- a/src/com/android/providers/downloads/DownloadNotifier.java +++ b/src/com/android/providers/downloads/DownloadNotifier.java @@ -122,6 +122,15 @@ public class DownloadNotifier { } } + private static boolean isClusterDeleted(Collection<DownloadInfo> cluster) { + boolean wasDeleted = true; + for (DownloadInfo info : cluster) { + wasDeleted = wasDeleted && info.mDeleted; + } + + return wasDeleted; + } + private void updateWithLocked(Collection<DownloadInfo> downloads) { final Resources res = mContext.getResources(); @@ -139,6 +148,11 @@ public class DownloadNotifier { final int type = getNotificationTagType(tag); final Collection<DownloadInfo> cluster = clustered.get(tag); + // If each of the downloads was canceled, don't show notification for the cluster + if (isClusterDeleted(cluster)) { + continue; + } + final Notification.Builder builder = new Notification.Builder(mContext); builder.setColor(res.getColor( com.android.internal.R.color.system_notification_accent_color)); @@ -164,16 +178,31 @@ public class DownloadNotifier { // Build action intents if (type == TYPE_ACTIVE || type == TYPE_WAITING) { + long[] downloadIds = getDownloadIds(cluster); + // build a synthetic uri for intent identification purposes final Uri uri = new Uri.Builder().scheme("active-dl").appendPath(tag).build(); final Intent intent = new Intent(Constants.ACTION_LIST, uri, mContext, DownloadReceiver.class); intent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS, - getDownloadIds(cluster)); + downloadIds); builder.setContentIntent(PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)); builder.setOngoing(true); + // Add a Cancel action + final Uri cancelUri = new Uri.Builder().scheme("cancel-dl").appendPath(tag).build(); + final Intent cancelIntent = new Intent(Constants.ACTION_CANCEL, + cancelUri, mContext, DownloadReceiver.class); + cancelIntent.putExtra(DownloadReceiver.EXTRA_CANCELED_DOWNLOAD_IDS, downloadIds); + cancelIntent.putExtra(DownloadReceiver.EXTRA_CANCELED_DOWNLOAD_NOTIFICATION_TAG, tag); + + builder.addAction( + android.R.drawable.ic_menu_close_clear_cancel, + res.getString(R.string.button_cancel_download), + PendingIntent.getBroadcast(mContext, + 0, cancelIntent, PendingIntent.FLAG_UPDATE_CURRENT)); + } else if (type == TYPE_COMPLETE) { final DownloadInfo info = cluster.iterator().next(); final Uri uri = ContentUris.withAppendedId( diff --git a/src/com/android/providers/downloads/DownloadReceiver.java b/src/com/android/providers/downloads/DownloadReceiver.java index 28e2a673..2f50dcf6 100644 --- a/src/com/android/providers/downloads/DownloadReceiver.java +++ b/src/com/android/providers/downloads/DownloadReceiver.java @@ -21,6 +21,7 @@ import static android.app.DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_ONLY import static com.android.providers.downloads.Constants.TAG; import android.app.DownloadManager; +import android.app.NotificationManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.ContentUris; @@ -45,6 +46,21 @@ import com.google.common.annotations.VisibleForTesting; * Receives system broadcasts (boot, network connectivity) */ public class DownloadReceiver extends BroadcastReceiver { + /** + * Intent extra included with {@link #ACTION_CANCEL} intents, indicating the IDs (as array of + * long) of the downloads that were canceled. + */ + public static final String EXTRA_CANCELED_DOWNLOAD_IDS = + "com.android.providers.downloads.extra.CANCELED_DOWNLOAD_IDS"; + + /** + * Intent extra included with {@link #ACTION_CANCEL} intents, indicating the tag of the + * notification corresponding to the download(s) that were canceled; this notification must be + * canceled. + */ + public static final String EXTRA_CANCELED_DOWNLOAD_NOTIFICATION_TAG = + "com.android.providers.downloads.extra.CANCELED_DOWNLOAD_NOTIFICATION_TAG"; + private static Handler sAsyncHandler; static { @@ -107,6 +123,18 @@ public class DownloadReceiver extends BroadcastReceiver { } }); } + } else if (Constants.ACTION_CANCEL.equals(action)) { + long[] downloadIds = intent.getLongArrayExtra( + DownloadReceiver.EXTRA_CANCELED_DOWNLOAD_IDS); + DownloadManager manager = (DownloadManager) context.getSystemService( + Context.DOWNLOAD_SERVICE); + manager.remove(downloadIds); + + String notifTag = intent.getStringExtra( + DownloadReceiver.EXTRA_CANCELED_DOWNLOAD_NOTIFICATION_TAG); + NotificationManager notifManager = (NotificationManager) context.getSystemService( + Context.NOTIFICATION_SERVICE); + notifManager.cancel(notifTag, 0); } } diff --git a/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java b/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java index 28c5dc7d..6934b86d 100644 --- a/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java +++ b/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java @@ -18,11 +18,13 @@ package com.android.providers.downloads; import static org.mockito.Mockito.mock; +import android.app.DownloadManager; import android.app.NotificationManager; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.pm.ProviderInfo; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; @@ -99,6 +101,7 @@ public abstract class AbstractDownloadProviderFunctionalTest extends private final ContentResolver mResolver; private final NotificationManager mNotifManager; + private final DownloadManager mDownloadManager; boolean mHasServiceBeenStarted = false; @@ -106,6 +109,7 @@ public abstract class AbstractDownloadProviderFunctionalTest extends super(realContext, FILENAME_PREFIX); mResolver = new MockContentResolverWithNotify(this); mNotifManager = mock(NotificationManager.class); + mDownloadManager = mock(DownloadManager.class); } /** @@ -123,6 +127,8 @@ public abstract class AbstractDownloadProviderFunctionalTest extends public Object getSystemService(String name) { if (Context.NOTIFICATION_SERVICE.equals(name)) { return mNotifManager; + } else if (Context.DOWNLOAD_SERVICE.equals(name)) { + return mDownloadManager; } return super.getSystemService(name); @@ -162,7 +168,10 @@ public abstract class AbstractDownloadProviderFunctionalTest extends final DownloadProvider provider = new DownloadProvider(); provider.mSystemFacade = mSystemFacade; - provider.attachInfo(mTestContext, null); + + ProviderInfo info = new ProviderInfo(); + info.authority = "downloads"; + provider.attachInfo(mTestContext, info); mResolver.addProvider(PROVIDER_AUTHORITY, provider); diff --git a/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java b/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java index d1048b02..17fed6d0 100644 --- a/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java +++ b/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java @@ -49,6 +49,8 @@ import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.Suppress; import android.text.format.DateUtils; +import com.android.providers.downloads.Constants; +import com.android.providers.downloads.DownloadReceiver; import com.google.mockwebserver.MockResponse; import com.google.mockwebserver.RecordedRequest; import com.google.mockwebserver.SocketPolicy; @@ -71,6 +73,7 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { protected File mTestDirectory; private NotificationManager mNotifManager; + private DownloadManager mDownloadManager; public PublicApiFunctionalTest() { super(new FakeSystemFacade()); @@ -82,6 +85,8 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { mNotifManager = (NotificationManager) getContext() .getSystemService(Context.NOTIFICATION_SERVICE); + mDownloadManager = (DownloadManager) getContext() + .getSystemService(Context.DOWNLOAD_SERVICE); mTestDirectory = new File(Environment.getExternalStorageDirectory() + File.separator + "download_manager_functional_test"); @@ -552,6 +557,23 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { assertEquals(PACKAGE_NAME, broadcast.getPackage()); } + public void testNotificationCancelDownloadClicked() throws Exception { + Download download = enqueueRequest(getRequest()); + + DownloadReceiver receiver = new DownloadReceiver(); + receiver.mSystemFacade = mSystemFacade; + Intent intent = new Intent(Constants.ACTION_CANCEL); + intent.setData(Uri.parse(Downloads.Impl.CONTENT_URI + "/" + download.mId)); + + long[] downloadIds = {download.mId}; + intent.putExtra(DownloadReceiver.EXTRA_CANCELED_DOWNLOAD_IDS, downloadIds); + intent.putExtra(DownloadReceiver.EXTRA_CANCELED_DOWNLOAD_NOTIFICATION_TAG, "tag"); + receiver.onReceive(mContext, intent); + + verify(mNotifManager, times(1)).cancel("tag", 0); + verify(mDownloadManager, times(1)).remove(downloadIds); + } + public void testBasicConnectivityChanges() throws Exception { enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT)); |