diff options
Diffstat (limited to 'src/com/android/providers/downloads/DownloadStorageProvider.java')
-rw-r--r-- | src/com/android/providers/downloads/DownloadStorageProvider.java | 100 |
1 files changed, 87 insertions, 13 deletions
diff --git a/src/com/android/providers/downloads/DownloadStorageProvider.java b/src/com/android/providers/downloads/DownloadStorageProvider.java index 7268c5c6..d9a0aa63 100644 --- a/src/com/android/providers/downloads/DownloadStorageProvider.java +++ b/src/com/android/providers/downloads/DownloadStorageProvider.java @@ -18,36 +18,42 @@ package com.android.providers.downloads; import android.app.DownloadManager; import android.app.DownloadManager.Query; +import android.content.ContentResolver; import android.content.Context; import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.database.MatrixCursor; import android.database.MatrixCursor.RowBuilder; import android.graphics.Point; +import android.net.Uri; import android.os.Binder; import android.os.CancellationSignal; +import android.os.Environment; import android.os.ParcelFileDescriptor; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Root; import android.provider.DocumentsProvider; import android.text.TextUtils; +import android.webkit.MimeTypeMap; import libcore.io.IoUtils; +import java.io.File; import java.io.FileNotFoundException; +import java.io.IOException; /** * Presents a {@link DocumentsContract} view of {@link DownloadManager} * contents. */ public class DownloadStorageProvider extends DocumentsProvider { + private static final String AUTHORITY = Constants.STORAGE_AUTHORITY; private static final String DOC_ID_ROOT = Constants.STORAGE_ROOT_ID; private static final String[] DEFAULT_ROOT_PROJECTION = new String[] { - Root.COLUMN_ROOT_ID, Root.COLUMN_ROOT_TYPE, Root.COLUMN_FLAGS, Root.COLUMN_ICON, - Root.COLUMN_TITLE, Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID, - Root.COLUMN_AVAILABLE_BYTES, + Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, + Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID, }; private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] { @@ -77,13 +83,18 @@ public class DownloadStorageProvider extends DocumentsProvider { result.setNotificationUri(getContext().getContentResolver(), cursor.getNotificationUri()); } + static void onDownloadProviderDelete(Context context, long id) { + final Uri uri = DocumentsContract.buildDocumentUri(AUTHORITY, Long.toString(id)); + context.revokeUriPermission(uri, ~0); + } + @Override public Cursor queryRoots(String[] projection) throws FileNotFoundException { final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection)); final RowBuilder row = result.newRow(); row.add(Root.COLUMN_ROOT_ID, DOC_ID_ROOT); - row.add(Root.COLUMN_ROOT_TYPE, Root.ROOT_TYPE_SHORTCUT); - row.add(Root.COLUMN_FLAGS, Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_RECENTS); + row.add(Root.COLUMN_FLAGS, + Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_RECENTS | Root.FLAG_SUPPORTS_CREATE); row.add(Root.COLUMN_ICON, R.mipmap.ic_launcher_download); row.add(Root.COLUMN_TITLE, getContext().getString(R.string.root_downloads)); row.add(Root.COLUMN_DOCUMENT_ID, DOC_ID_ROOT); @@ -91,6 +102,41 @@ public class DownloadStorageProvider extends DocumentsProvider { } @Override + public String createDocument(String docId, String mimeType, String displayName) + throws FileNotFoundException { + final File parent = Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_DOWNLOADS); + parent.mkdirs(); + + // Delegate to real provider + final long token = Binder.clearCallingIdentity(); + try { + displayName = removeExtension(mimeType, displayName); + File file = new File(parent, addExtension(mimeType, displayName)); + + // If conflicting file, try adding counter suffix + int n = 0; + while (file.exists() && n++ < 32) { + file = new File(parent, addExtension(mimeType, displayName + " (" + n + ")")); + } + + try { + if (!file.createNewFile()) { + throw new IllegalStateException("Failed to touch " + file); + } + } catch (IOException e) { + throw new IllegalStateException("Failed to touch " + file + ": " + e); + } + + return Long.toString(mDm.addCompletedDownload( + file.getName(), file.getName(), false, mimeType, file.getAbsolutePath(), 0L, + false, true)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override public void deleteDocument(String docId) throws FileNotFoundException { // Delegate to real provider final long token = Binder.clearCallingIdentity(); @@ -209,14 +255,12 @@ public class DownloadStorageProvider extends DocumentsProvider { @Override public ParcelFileDescriptor openDocument(String docId, String mode, CancellationSignal signal) throws FileNotFoundException { - if (!"r".equals(mode)) { - throw new IllegalArgumentException("Downloads are read-only"); - } - // Delegate to real provider final long token = Binder.clearCallingIdentity(); try { - return mDm.openDownloadedFile(Long.parseLong(docId)); + final long id = Long.parseLong(docId); + final ContentResolver resolver = getContext().getContentResolver(); + return resolver.openFileDescriptor(mDm.getDownloadUri(id), mode, signal); } finally { Binder.restoreCallingIdentity(token); } @@ -234,7 +278,8 @@ public class DownloadStorageProvider extends DocumentsProvider { final RowBuilder row = result.newRow(); row.add(Document.COLUMN_DOCUMENT_ID, DOC_ID_ROOT); row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR); - row.add(Document.COLUMN_FLAGS, Document.FLAG_DIR_PREFERS_LAST_MODIFIED); + row.add(Document.COLUMN_FLAGS, + Document.FLAG_DIR_PREFERS_LAST_MODIFIED | Document.FLAG_DIR_SUPPORTS_CREATE); } private void includeDownloadFromCursor(MatrixCursor result, Cursor cursor) { @@ -248,7 +293,8 @@ public class DownloadStorageProvider extends DocumentsProvider { String mimeType = cursor.getString( cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_MEDIA_TYPE)); if (mimeType == null) { - mimeType = "application/octet-stream"; + // Provide fake MIME type so it's openable + mimeType = "vnd.android.document/file"; } Long size = cursor.getLong( cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); @@ -283,7 +329,7 @@ public class DownloadStorageProvider extends DocumentsProvider { break; } - int flags = Document.FLAG_SUPPORTS_DELETE; + int flags = Document.FLAG_SUPPORTS_DELETE | Document.FLAG_SUPPORTS_WRITE; if (mimeType != null && mimeType.startsWith("image/")) { flags |= Document.FLAG_SUPPORTS_THUMBNAIL; } @@ -300,4 +346,32 @@ public class DownloadStorageProvider extends DocumentsProvider { row.add(Document.COLUMN_LAST_MODIFIED, lastModified); row.add(Document.COLUMN_FLAGS, flags); } + + /** + * Remove file extension from name, but only if exact MIME type mapping + * exists. This means we can reapply the extension later. + */ + private static String removeExtension(String mimeType, String name) { + final int lastDot = name.lastIndexOf('.'); + if (lastDot >= 0) { + final String extension = name.substring(lastDot + 1); + final String nameMime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); + if (mimeType.equals(nameMime)) { + return name.substring(0, lastDot); + } + } + return name; + } + + /** + * Add file extension to name, but only if exact MIME type mapping exists. + */ + private static String addExtension(String mimeType, String name) { + final String extension = MimeTypeMap.getSingleton() + .getExtensionFromMimeType(mimeType); + if (extension != null) { + return name + "." + extension; + } + return name; + } } |