From ec62bdf20cbfa709c9dea9101fe668fec315c103 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 28 Aug 2013 17:54:21 -0700 Subject: Follow stronger DocumentsProvider contract. Provides same functionality, but follows updated DocumentsProvider contract in framework. Bug: 10497206 Change-Id: Ie1f6180047ff7bad289679a14f3368238d47b1d6 --- src/com/android/providers/downloads/Constants.java | 2 +- .../downloads/DownloadStorageProvider.java | 288 +++++++-------------- .../providers/downloads/TrampolineActivity.java | 10 +- 3 files changed, 99 insertions(+), 201 deletions(-) (limited to 'src') diff --git a/src/com/android/providers/downloads/Constants.java b/src/com/android/providers/downloads/Constants.java index 3fbd400f..e33a6362 100644 --- a/src/com/android/providers/downloads/Constants.java +++ b/src/com/android/providers/downloads/Constants.java @@ -176,5 +176,5 @@ public class Constants { public static final boolean LOGVV = LOCAL_LOGVV && LOGV; public static final String STORAGE_AUTHORITY = "com.android.providers.downloads.documents"; - public static final String STORAGE_ROOT = "downloads"; + public static final String STORAGE_DOC_ID_ROOT = "downloads"; } diff --git a/src/com/android/providers/downloads/DownloadStorageProvider.java b/src/com/android/providers/downloads/DownloadStorageProvider.java index b606e652..58df58a7 100644 --- a/src/com/android/providers/downloads/DownloadStorageProvider.java +++ b/src/com/android/providers/downloads/DownloadStorageProvider.java @@ -18,63 +18,55 @@ package com.android.providers.downloads; import android.app.DownloadManager; import android.app.DownloadManager.Query; -import android.content.ContentProvider; -import android.content.ContentValues; import android.content.Context; -import android.content.UriMatcher; +import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.database.MatrixCursor; import android.database.MatrixCursor.RowBuilder; -import android.net.Uri; +import android.graphics.Point; import android.os.Binder; +import android.os.CancellationSignal; import android.os.ParcelFileDescriptor; import android.provider.DocumentsContract; import android.provider.DocumentsContract.DocumentColumns; +import android.provider.DocumentsContract.DocumentRoot; import android.provider.DocumentsContract.Documents; -import android.provider.DocumentsContract.RootColumns; -import android.provider.DocumentsContract.Roots; +import android.provider.DocumentsProvider; + +import com.google.common.collect.Lists; import libcore.io.IoUtils; import java.io.FileNotFoundException; +import java.util.List; /** * Presents a {@link DocumentsContract} view of {@link DownloadManager} * contents. */ -public class DownloadStorageProvider extends ContentProvider { - private static final String AUTHORITY = Constants.STORAGE_AUTHORITY; - private static final String ROOT = Constants.STORAGE_ROOT; - - private static final UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH); - - private static final int URI_ROOTS = 1; - private static final int URI_ROOTS_ID = 2; - private static final int URI_DOCS_ID = 3; - private static final int URI_DOCS_ID_CONTENTS = 4; - - static { - sMatcher.addURI(AUTHORITY, "roots", URI_ROOTS); - sMatcher.addURI(AUTHORITY, "roots/*", URI_ROOTS_ID); - sMatcher.addURI(AUTHORITY, "roots/*/docs/*", URI_DOCS_ID); - sMatcher.addURI(AUTHORITY, "roots/*/docs/*/contents", URI_DOCS_ID_CONTENTS); - } +public class DownloadStorageProvider extends DocumentsProvider { + private static final String DOC_ID_ROOT = Constants.STORAGE_DOC_ID_ROOT; - private static final String[] ALL_ROOTS_COLUMNS = new String[] { - RootColumns.ROOT_ID, RootColumns.ROOT_TYPE, RootColumns.ICON, RootColumns.TITLE, - RootColumns.SUMMARY, RootColumns.AVAILABLE_BYTES - }; - - private static final String[] ALL_DOCUMENTS_COLUMNS = new String[] { + private static final String[] SUPPORTED_COLUMNS = new String[] { DocumentColumns.DOC_ID, DocumentColumns.DISPLAY_NAME, DocumentColumns.SIZE, DocumentColumns.MIME_TYPE, DocumentColumns.LAST_MODIFIED, DocumentColumns.FLAGS }; + private DocumentRoot mRoot; + private DownloadManager mDm; private DownloadManager.Query mBaseQuery; @Override public boolean onCreate() { + + mRoot = new DocumentRoot(); + mRoot.docId = DOC_ID_ROOT; + mRoot.rootType = DocumentRoot.ROOT_TYPE_SHORTCUT; + mRoot.title = getContext().getString(R.string.root_downloads); + mRoot.icon = R.mipmap.ic_launcher_download; + mRoot.flags = DocumentRoot.FLAG_LOCAL_ONLY; + mDm = (DownloadManager) getContext().getSystemService(Context.DOWNLOAD_SERVICE); mDm.setAccessAllDownloads(true); mBaseQuery = new DownloadManager.Query().setOnlyIncludeVisibleInDownloadsUi(true); @@ -83,91 +75,98 @@ public class DownloadStorageProvider extends ContentProvider { } @Override - public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, - String sortOrder) { - switch (sMatcher.match(uri)) { - case URI_ROOTS: { - final MatrixCursor result = new MatrixCursor( - projection != null ? projection : ALL_ROOTS_COLUMNS); - includeDefaultRoot(result); - return result; - } - case URI_ROOTS_ID: { - final MatrixCursor result = new MatrixCursor( - projection != null ? projection : ALL_ROOTS_COLUMNS); - includeDefaultRoot(result); - return result; - } - case URI_DOCS_ID: { - final String docId = DocumentsContract.getDocId(uri); - final MatrixCursor result = new MatrixCursor( - projection != null ? projection : ALL_DOCUMENTS_COLUMNS); + public List getDocumentRoots() { + return Lists.newArrayList(mRoot); + } - if (Documents.DOC_ID_ROOT.equals(docId)) { - includeDefaultDocument(result); - } else { - // Delegate to real provider - final long token = Binder.clearCallingIdentity(); - Cursor cursor = null; - try { - cursor = mDm.query( - new Query().setFilterById(getDownloadFromDocument(docId))); - if (cursor.moveToFirst()) { - includeDownloadFromCursor(result, cursor); - } - } finally { - IoUtils.closeQuietly(cursor); - Binder.restoreCallingIdentity(token); - } - } - return result; + @Override + public void deleteDocument(String docId) throws FileNotFoundException { + // Delegate to real provider + final long token = Binder.clearCallingIdentity(); + try { + if (mDm.remove(Long.parseLong(docId)) != 1) { + throw new IllegalStateException("Failed to delete " + docId); } - case URI_DOCS_ID_CONTENTS: { - final String docId = DocumentsContract.getDocId(uri); - final MatrixCursor result = new MatrixCursor( - projection != null ? projection : ALL_DOCUMENTS_COLUMNS); - - if (!Documents.DOC_ID_ROOT.equals(docId)) { - throw new UnsupportedOperationException("Unsupported Uri " + uri); - } + } finally { + Binder.restoreCallingIdentity(token); + } + } - // Delegate to real provider - final long token = Binder.clearCallingIdentity(); - Cursor cursor = null; - try { - cursor = mDm.query(mBaseQuery); - while (cursor.moveToNext()) { - includeDownloadFromCursor(result, cursor); - } - } finally { - IoUtils.closeQuietly(cursor); - Binder.restoreCallingIdentity(token); + @Override + public Cursor queryDocument(String docId) throws FileNotFoundException { + final MatrixCursor result = new MatrixCursor(SUPPORTED_COLUMNS); + + if (DOC_ID_ROOT.equals(docId)) { + includeDefaultDocument(result); + } else { + // Delegate to real provider + final long token = Binder.clearCallingIdentity(); + Cursor cursor = null; + try { + cursor = mDm.query(new Query().setFilterById(Long.parseLong(docId))); + if (cursor.moveToFirst()) { + includeDownloadFromCursor(result, cursor); } - return result; + } finally { + IoUtils.closeQuietly(cursor); + Binder.restoreCallingIdentity(token); } - default: { - throw new UnsupportedOperationException("Unsupported Uri " + uri); + } + return result; + } + + @Override + public Cursor queryDocumentChildren(String docId) throws FileNotFoundException { + final MatrixCursor result = new MatrixCursor(SUPPORTED_COLUMNS); + + // Delegate to real provider + final long token = Binder.clearCallingIdentity(); + Cursor cursor = null; + try { + cursor = mDm.query(mBaseQuery); + while (cursor.moveToNext()) { + includeDownloadFromCursor(result, cursor); } + } finally { + IoUtils.closeQuietly(cursor); + Binder.restoreCallingIdentity(token); } + return result; } - private void includeDefaultRoot(MatrixCursor result) { - final RowBuilder row = result.newRow(); - row.offer(RootColumns.ROOT_ID, ROOT); - row.offer(RootColumns.ROOT_TYPE, Roots.ROOT_TYPE_SHORTCUT); - row.offer(RootColumns.TITLE, getContext().getString(R.string.root_downloads)); + @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)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public AssetFileDescriptor openDocumentThumbnail( + String docId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException { + // TODO: extend ExifInterface to support fds + final ParcelFileDescriptor pfd = openDocument(docId, "r", signal); + return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH); } private void includeDefaultDocument(MatrixCursor result) { final RowBuilder row = result.newRow(); - row.offer(DocumentColumns.DOC_ID, Documents.DOC_ID_ROOT); - row.offer(DocumentColumns.DISPLAY_NAME, getContext().getString(R.string.root_downloads)); + row.offer(DocumentColumns.DOC_ID, DOC_ID_ROOT); row.offer(DocumentColumns.MIME_TYPE, Documents.MIME_TYPE_DIR); } private void includeDownloadFromCursor(MatrixCursor result, Cursor cursor) { final long id = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_ID)); - final String docId = getDocumentFromDownload(id); + final String docId = String.valueOf(id); final String displayName = cursor.getString( cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TITLE)); @@ -225,101 +224,4 @@ public class DownloadStorageProvider extends ContentProvider { row.offer(DocumentColumns.LAST_MODIFIED, lastModified); row.offer(DocumentColumns.FLAGS, flags); } - - private interface TypeQuery { - final String[] PROJECTION = { - DocumentColumns.MIME_TYPE }; - - final int MIME_TYPE = 0; - } - - @Override - public String getType(Uri uri) { - switch (sMatcher.match(uri)) { - case URI_ROOTS: { - return Roots.MIME_TYPE_DIR; - } - case URI_ROOTS_ID: { - return Roots.MIME_TYPE_ITEM; - } - case URI_DOCS_ID: { - final Cursor cursor = query(uri, TypeQuery.PROJECTION, null, null, null); - try { - if (cursor.moveToFirst()) { - return cursor.getString(TypeQuery.MIME_TYPE); - } else { - return null; - } - } finally { - IoUtils.closeQuietly(cursor); - } - } - default: { - throw new UnsupportedOperationException("Unsupported Uri " + uri); - } - } - } - - public static long getDownloadFromDocument(String docId) { - return Long.parseLong(docId.substring(docId.indexOf(':') + 1)); - } - - private String getDocumentFromDownload(long id) { - return "id:" + id; - } - - @Override - public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { - switch (sMatcher.match(uri)) { - case URI_DOCS_ID: { - final String docId = DocumentsContract.getDocId(uri); - - if (!"r".equals(mode)) { - throw new IllegalArgumentException("Downloads are read-only"); - } - - // Delegate to real provider - final long token = Binder.clearCallingIdentity(); - try { - return mDm.openDownloadedFile(getDownloadFromDocument(docId)); - } finally { - Binder.restoreCallingIdentity(token); - } - } - default: { - throw new UnsupportedOperationException("Unsupported Uri " + uri); - } - } - } - - @Override - public Uri insert(Uri uri, ContentValues values) { - throw new UnsupportedOperationException("Unsupported Uri " + uri); - } - - @Override - public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { - throw new UnsupportedOperationException("Unsupported Uri " + uri); - } - - @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { - switch (sMatcher.match(uri)) { - case URI_DOCS_ID: { - final String docId = DocumentsContract.getDocId(uri); - - // Delegate to real provider - // TODO: only storage UI should be allowed to delete? - final long token = Binder.clearCallingIdentity(); - try { - return mDm.remove(getDownloadFromDocument(docId)); - } finally { - Binder.restoreCallingIdentity(token); - } - } - default: { - throw new UnsupportedOperationException("Unsupported Uri " + uri); - } - } - } } diff --git a/src/com/android/providers/downloads/TrampolineActivity.java b/src/com/android/providers/downloads/TrampolineActivity.java index a1e99161..0f494cff 100644 --- a/src/com/android/providers/downloads/TrampolineActivity.java +++ b/src/com/android/providers/downloads/TrampolineActivity.java @@ -17,10 +17,9 @@ package com.android.providers.downloads; import android.app.Activity; +import android.content.ContentUris; import android.content.Intent; -import android.net.Uri; import android.os.Bundle; -import android.provider.DocumentsContract; /** * PackageInstaller really wants raw files. @@ -30,11 +29,8 @@ public class TrampolineActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - final Uri documentUri = getIntent().getData(); - final String docId = DocumentsContract.getDocId(documentUri); - final long downloadId = DownloadStorageProvider.getDownloadFromDocument(docId); - - final Intent intent = OpenHelper.buildViewIntent(this, downloadId); + final long id = ContentUris.parseId(getIntent().getData()); + final Intent intent = OpenHelper.buildViewIntent(this, id); startActivity(intent); finish(); } -- cgit v1.2.3