From 71e7fda9135a0915af1fd419d07ebf85ad09beb4 Mon Sep 17 00:00:00 2001 From: Steve Howard Date: Wed, 8 Sep 2010 17:15:27 -0700 Subject: Further work on the new downloads UI. * add support for downloads not visible in the UI * add support for restarting failed downloads and downloads for which the file is missing * add "clear selection" button to selection menu * fix DateSortedExpandableListAdapter to ensure the view refreshes properly anytime the underlying data changes * make DownloadList handle when a selected download gets deleted by another app * make DownloadList close a dialog for a pending download when the download starts * show a dialog when the user tries to open a download but the file is missing * display "" when no title is provided for a download * add a test case for DownloadManager.orderBy() (should've gone in the previous commit) Change-Id: Ibf3062e8228e7f2c1270be24d8d5527dfb064658 --- .../providers/downloads/DownloadProvider.java | 36 ++++- src/com/android/providers/downloads/Helpers.java | 2 +- .../downloads/PublicApiFunctionalTest.java | 54 ++++++++ ui/res/layout/download_list.xml | 9 +- ui/res/values/strings.xml | 13 ++ .../ui/DateSortedExpandableListAdapter.java | 15 ++- .../providers/downloads/ui/DownloadAdapter.java | 7 +- .../providers/downloads/ui/DownloadList.java | 147 +++++++++++++++++++-- 8 files changed, 258 insertions(+), 25 deletions(-) diff --git a/src/com/android/providers/downloads/DownloadProvider.java b/src/com/android/providers/downloads/DownloadProvider.java index f6b091b1..d957989a 100644 --- a/src/com/android/providers/downloads/DownloadProvider.java +++ b/src/com/android/providers/downloads/DownloadProvider.java @@ -58,7 +58,7 @@ public final class DownloadProvider extends ContentProvider { /** Database filename */ private static final String DB_NAME = "downloads.db"; /** Current database version */ - private static final int DB_VERSION = 102; + private static final int DB_VERSION = 103; /** Name of table in the database */ private static final String DB_TABLE = "downloads"; @@ -99,6 +99,7 @@ public final class DownloadProvider extends ContentProvider { Downloads.Impl.COLUMN_TITLE, Downloads.Impl.COLUMN_DESCRIPTION, Downloads.Impl.COLUMN_URI, + Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, }; private static HashSet sAppReadableColumnsSet; @@ -194,11 +195,28 @@ public final class DownloadProvider extends ContentProvider { "INTEGER NOT NULL DEFAULT 0"); break; + case 103: + addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, + "INTEGER NOT NULL DEFAULT 1"); + makeCacheDownloadsInvisible(db); + break; + default: throw new IllegalStateException("Don't know how to upgrade to " + version); } } + /** + * Set all existing downloads to the cache partition to be invisible in the downloads UI. + */ + private void makeCacheDownloadsInvisible(SQLiteDatabase db) { + ContentValues values = new ContentValues(); + values.put(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, false); + String cacheSelection = Downloads.Impl.COLUMN_DESTINATION + + " != " + Downloads.Impl.DESTINATION_EXTERNAL; + db.update(DB_TABLE, values, cacheSelection, null); + } + /** * Add a column to a table using ALTER TABLE. * @param dbTable name of the table @@ -419,6 +437,14 @@ public final class DownloadProvider extends ContentProvider { copyStringWithDefault(Downloads.Impl.COLUMN_DESCRIPTION, values, filteredValues, ""); filteredValues.put(Downloads.Impl.COLUMN_TOTAL_BYTES, -1); + if (values.containsKey(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI)) { + copyBoolean(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, values, filteredValues); + } else { + // by default, make external downloads visible in the UI + boolean isExternal = (dest == null || dest == Downloads.Impl.DESTINATION_EXTERNAL); + filteredValues.put(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, isExternal); + } + if (isPublicApi) { copyInteger(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES, values, filteredValues); copyBoolean(Downloads.Impl.COLUMN_ALLOW_ROAMING, values, filteredValues); @@ -519,6 +545,7 @@ public final class DownloadProvider extends ContentProvider { values.remove(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE); // checked later in insert() values.remove(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES); values.remove(Downloads.Impl.COLUMN_ALLOW_ROAMING); + values.remove(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI); Iterator> iterator = values.valueSet().iterator(); while (iterator.hasNext()) { String key = iterator.next().getKey(); @@ -770,7 +797,6 @@ public final class DownloadProvider extends ContentProvider { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count; - long rowId = 0; boolean startService = false; ContentValues filteredValues; @@ -798,6 +824,12 @@ public final class DownloadProvider extends ContentProvider { } c.close(); } + + Integer status = values.getAsInteger(Downloads.Impl.COLUMN_STATUS); + boolean isRestart = status != null && status == Downloads.Impl.STATUS_PENDING; + if (isRestart) { + startService = true; + } } int match = sURIMatcher.match(uri); switch (match) { diff --git a/src/com/android/providers/downloads/Helpers.java b/src/com/android/providers/downloads/Helpers.java index 5d546ff6..42a49f11 100644 --- a/src/com/android/providers/downloads/Helpers.java +++ b/src/com/android/providers/downloads/Helpers.java @@ -529,7 +529,7 @@ public class Helpers { */ public static void validateSelection(String selection, Set allowedColumns) { try { - if (selection == null) { + if (selection == null || selection.isEmpty()) { return; } Lexer lexer = new Lexer(selection, allowedColumns); diff --git a/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java b/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java index cf2b990c..e48ce22e 100644 --- a/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java +++ b/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java @@ -205,6 +205,48 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { cursor = mManager.query(new DownloadManager.Query() .setFilterByStatus(DownloadManager.STATUS_RUNNING)); checkAndCloseCursor(cursor); + + mSystemFacade.incrementTimeMillis(1); + Download invisibleDownload = enqueueRequest(getRequest().setVisibleInDownloadsUi(false)); + cursor = mManager.query(new DownloadManager.Query()); + checkAndCloseCursor(cursor, invisibleDownload, download3, download2, download1); + cursor = mManager.query(new DownloadManager.Query().setOnlyIncludeVisibleInDownloadsUi(true)); + checkAndCloseCursor(cursor, download3, download2, download1); + } + + public void testOrdering() throws Exception { + enqueueResponse(HTTP_OK, "small contents"); + Download download1 = enqueueRequest(getRequest()); + download1.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); + + mSystemFacade.incrementTimeMillis(1); + enqueueResponse(HTTP_OK, "large contents large contents"); + Download download2 = enqueueRequest(getRequest()); + download2.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); + + mSystemFacade.incrementTimeMillis(1); + enqueueEmptyResponse(HTTP_NOT_FOUND); + Download download3 = enqueueRequest(getRequest()); + download3.runUntilStatus(DownloadManager.STATUS_FAILED); + + // default ordering -- by timestamp descending + Cursor cursor = mManager.query(new DownloadManager.Query()); + checkAndCloseCursor(cursor, download3, download2, download1); + + cursor = mManager.query(new DownloadManager.Query() + .orderBy(DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP, + DownloadManager.Query.ORDER_ASCENDING)); + checkAndCloseCursor(cursor, download1, download2, download3); + + cursor = mManager.query(new DownloadManager.Query() + .orderBy(DownloadManager.COLUMN_TOTAL_SIZE_BYTES, + DownloadManager.Query.ORDER_DESCENDING)); + checkAndCloseCursor(cursor, download2, download1, download3); + + cursor = mManager.query(new DownloadManager.Query() + .orderBy(DownloadManager.COLUMN_TOTAL_SIZE_BYTES, + DownloadManager.Query.ORDER_ASCENDING)); + checkAndCloseCursor(cursor, download3, download1, download2); } private void checkAndCloseCursor(Cursor cursor, Download... downloads) { @@ -494,6 +536,18 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest { download.getLongField(DownloadManager.COLUMN_ERROR_CODE); } + public void testRestart() throws Exception { + enqueueEmptyResponse(HTTP_NOT_FOUND); + Download download = enqueueRequest(getRequest()); + download.runUntilStatus(DownloadManager.STATUS_FAILED); + + enqueueEmptyResponse(HTTP_OK); + mManager.restartDownload(download.mId); + assertEquals(DownloadManager.STATUS_PENDING, + download.getLongField(DownloadManager.COLUMN_STATUS)); + download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL); + } + private void checkCompleteDownload(Download download) throws Exception { assertEquals(FILE_CONTENT.length(), download.getLongField(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); diff --git a/ui/res/layout/download_list.xml b/ui/res/layout/download_list.xml index 241bb3d3..696030ff 100644 --- a/ui/res/layout/download_list.xml +++ b/ui/res/layout/download_list.xml @@ -54,8 +54,11 @@