diff options
Diffstat (limited to 'src/com/android/providers/downloads')
6 files changed, 122 insertions, 8 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/src/com/android/providers/downloads/DownloadService.java b/src/com/android/providers/downloads/DownloadService.java index b0b73297..7845ce0e 100644 --- a/src/com/android/providers/downloads/DownloadService.java +++ b/src/com/android/providers/downloads/DownloadService.java @@ -33,11 +33,15 @@ import android.content.res.Resources; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; +import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; +import android.os.IDeviceIdleController; import android.os.Message; import android.os.Process; +import android.os.RemoteException; +import android.os.ServiceManager; import android.provider.Downloads; import android.text.TextUtils; import android.util.Log; @@ -84,6 +88,7 @@ public class DownloadService extends Service { SystemFacade mSystemFacade; private AlarmManager mAlarmManager; + private IDeviceIdleController mDeviceIdleController; /** Observer to get notified when the content observer's data changes */ private DownloadManagerContentObserver mObserver; @@ -192,6 +197,12 @@ public class DownloadService extends Service { } mAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); + mDeviceIdleController = IDeviceIdleController.Stub.asInterface( + ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER)); + try { + mDeviceIdleController.downloadServiceActive(new Binder()); + } catch (RemoteException e) { + } mUpdateThread = new HandlerThread(TAG + "-UpdateThread"); mUpdateThread.start(); @@ -330,6 +341,10 @@ public class DownloadService extends Service { if (DEBUG_LIFECYCLE) Log.v(TAG, "Nothing left; stopped"); getContentResolver().unregisterContentObserver(mObserver); mScanner.shutdown(); + try { + mDeviceIdleController.downloadServiceInactive(); + } catch (RemoteException e) { + } mUpdateThread.quit(); } } diff --git a/src/com/android/providers/downloads/DownloadStorageProvider.java b/src/com/android/providers/downloads/DownloadStorageProvider.java index 1b5dc844..77b8fe49 100644 --- a/src/com/android/providers/downloads/DownloadStorageProvider.java +++ b/src/com/android/providers/downloads/DownloadStorageProvider.java @@ -35,6 +35,7 @@ import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Root; import android.provider.DocumentsProvider; +import android.support.provider.DocumentArchiveHelper; import android.text.TextUtils; import android.webkit.MimeTypeMap; @@ -65,11 +66,14 @@ public class DownloadStorageProvider extends DocumentsProvider { }; private DownloadManager mDm; + private DocumentArchiveHelper mArchiveHelper; @Override public boolean onCreate() { mDm = (DownloadManager) getContext().getSystemService(Context.DOWNLOAD_SERVICE); mDm.setAccessAllDownloads(true); + mDm.setAccessFilename(true); + mArchiveHelper = new DocumentArchiveHelper(this, ':'); return true; } @@ -152,6 +156,10 @@ public class DownloadStorageProvider extends DocumentsProvider { @Override public Cursor queryDocument(String docId, String[] projection) throws FileNotFoundException { + if (mArchiveHelper.isArchivedDocument(docId)) { + return mArchiveHelper.queryDocument(docId, projection); + } + final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection)); if (DOC_ID_ROOT.equals(docId)) { @@ -164,7 +172,9 @@ public class DownloadStorageProvider extends DocumentsProvider { cursor = mDm.query(new Query().setFilterById(Long.parseLong(docId))); copyNotificationUri(result, cursor); if (cursor.moveToFirst()) { - includeDownloadFromCursor(result, cursor); + // We don't know if this queryDocument() call is from Downloads (manage) + // or Files. Safely assume it's Files. + includeDownloadFromCursor(result, cursor, false /* forManage */); } } finally { IoUtils.closeQuietly(cursor); @@ -177,6 +187,11 @@ public class DownloadStorageProvider extends DocumentsProvider { @Override public Cursor queryChildDocuments(String docId, String[] projection, String sortOrder) throws FileNotFoundException { + if (mArchiveHelper.isArchivedDocument(docId) || + mArchiveHelper.isSupportedArchiveType(getDocumentType(docId))) { + return mArchiveHelper.queryChildDocuments(docId, projection, sortOrder); + } + final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection)); // Delegate to real provider @@ -187,7 +202,7 @@ public class DownloadStorageProvider extends DocumentsProvider { .setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL)); copyNotificationUri(result, cursor); while (cursor.moveToNext()) { - includeDownloadFromCursor(result, cursor); + includeDownloadFromCursor(result, cursor, false /* forManage */); } } finally { IoUtils.closeQuietly(cursor); @@ -200,6 +215,10 @@ public class DownloadStorageProvider extends DocumentsProvider { public Cursor queryChildDocumentsForManage( String parentDocumentId, String[] projection, String sortOrder) throws FileNotFoundException { + if (mArchiveHelper.isArchivedDocument(parentDocumentId)) { + return mArchiveHelper.queryDocument(parentDocumentId, projection); + } + final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection)); // Delegate to real provider @@ -210,7 +229,7 @@ public class DownloadStorageProvider extends DocumentsProvider { new DownloadManager.Query().setOnlyIncludeVisibleInDownloadsUi(true)); copyNotificationUri(result, cursor); while (cursor.moveToNext()) { - includeDownloadFromCursor(result, cursor); + includeDownloadFromCursor(result, cursor, true /* forManage */); } } finally { IoUtils.closeQuietly(cursor); @@ -244,7 +263,7 @@ public class DownloadStorageProvider extends DocumentsProvider { continue; } - includeDownloadFromCursor(result, cursor); + includeDownloadFromCursor(result, cursor, false /* forManage */); } } finally { IoUtils.closeQuietly(cursor); @@ -256,6 +275,10 @@ public class DownloadStorageProvider extends DocumentsProvider { @Override public ParcelFileDescriptor openDocument(String docId, String mode, CancellationSignal signal) throws FileNotFoundException { + if (mArchiveHelper.isArchivedDocument(docId)) { + return mArchiveHelper.openDocument(docId, mode, signal); + } + // Delegate to real provider final long token = Binder.clearCallingIdentity(); try { @@ -283,7 +306,7 @@ public class DownloadStorageProvider extends DocumentsProvider { Document.FLAG_DIR_PREFERS_LAST_MODIFIED | Document.FLAG_DIR_SUPPORTS_CREATE); } - private void includeDownloadFromCursor(MatrixCursor result, Cursor cursor) { + private void includeDownloadFromCursor(MatrixCursor result, Cursor cursor, boolean forManage) { final long id = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_ID)); final String docId = String.valueOf(id); @@ -332,10 +355,15 @@ public class DownloadStorageProvider extends DocumentsProvider { } int flags = Document.FLAG_SUPPORTS_DELETE | Document.FLAG_SUPPORTS_WRITE; - if (mimeType != null && mimeType.startsWith("image/")) { + if (mimeType.startsWith("image/")) { flags |= Document.FLAG_SUPPORTS_THUMBNAIL; } + // TODO: Remove forManage and move the logic to DocumentsUI. b/26321218. + if (!forManage && mArchiveHelper.isSupportedArchiveType(mimeType)) { + flags |= Document.FLAG_ARCHIVE; + } + final long lastModified = cursor.getLong( cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP)); @@ -345,8 +373,18 @@ public class DownloadStorageProvider extends DocumentsProvider { row.add(Document.COLUMN_SUMMARY, summary); row.add(Document.COLUMN_SIZE, size); row.add(Document.COLUMN_MIME_TYPE, mimeType); - row.add(Document.COLUMN_LAST_MODIFIED, lastModified); row.add(Document.COLUMN_FLAGS, flags); + // Incomplete downloads get a null timestamp. This prevents thrashy UI when a bunch of + // active downloads get sorted by mod time. + if (status != DownloadManager.STATUS_RUNNING) { + row.add(Document.COLUMN_LAST_MODIFIED, lastModified); + } + + final String localFilePath = cursor.getString( + cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_FILENAME)); + if (localFilePath != null) { + row.add(DocumentArchiveHelper.COLUMN_LOCAL_FILE_PATH, localFilePath); + } } /** diff --git a/src/com/android/providers/downloads/OpenHelper.java b/src/com/android/providers/downloads/OpenHelper.java index 4eb319c4..4b051be5 100644 --- a/src/com/android/providers/downloads/OpenHelper.java +++ b/src/com/android/providers/downloads/OpenHelper.java @@ -65,6 +65,7 @@ public class OpenHelper { final DownloadManager downManager = (DownloadManager) context.getSystemService( Context.DOWNLOAD_SERVICE); downManager.setAccessAllDownloads(true); + downManager.setAccessFilename(true); final Cursor cursor = downManager.query(new DownloadManager.Query().setFilterById(id)); try { |