diff options
author | Steve Howard <showard@google.com> | 2010-09-08 17:15:27 -0700 |
---|---|---|
committer | Steve Howard <showard@google.com> | 2010-09-10 16:27:51 -0700 |
commit | 71e7fda9135a0915af1fd419d07ebf85ad09beb4 (patch) | |
tree | 965315a68abef66c21b154856923e35b9eab4b74 /ui/src/com/android/providers/downloads/ui | |
parent | f8ccad3e970434111b3920dc639e05ca48ca66c2 (diff) | |
download | android_packages_providers_DownloadProvider-71e7fda9135a0915af1fd419d07ebf85ad09beb4.tar.gz android_packages_providers_DownloadProvider-71e7fda9135a0915af1fd419d07ebf85ad09beb4.tar.bz2 android_packages_providers_DownloadProvider-71e7fda9135a0915af1fd419d07ebf85ad09beb4.zip |
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 "<Unknown>" 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
Diffstat (limited to 'ui/src/com/android/providers/downloads/ui')
3 files changed, 150 insertions, 19 deletions
diff --git a/ui/src/com/android/providers/downloads/ui/DateSortedExpandableListAdapter.java b/ui/src/com/android/providers/downloads/ui/DateSortedExpandableListAdapter.java index 88ffdee3..58dd4bb3 100644 --- a/ui/src/com/android/providers/downloads/ui/DateSortedExpandableListAdapter.java +++ b/ui/src/com/android/providers/downloads/ui/DateSortedExpandableListAdapter.java @@ -66,6 +66,16 @@ public class DateSortedExpandableListAdapter implements ExpandableListAdapter { } } + private class MyDataSetObserver extends DataSetObserver { + @Override + public void onChanged() { + buildMap(); + for (DataSetObserver o : mObservers) { + o.onChanged(); + } + } + } + public DateSortedExpandableListAdapter(Context context, Cursor cursor, int dateIndex) { mContext = context; @@ -74,6 +84,7 @@ public class DateSortedExpandableListAdapter implements ExpandableListAdapter { mCursor = cursor; mIdIndex = cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_ID); cursor.registerContentObserver(new ChangeObserver()); + cursor.registerDataSetObserver(new MyDataSetObserver()); mDateIndex = dateIndex; buildMap(); } @@ -255,10 +266,6 @@ public class DateSortedExpandableListAdapter implements ExpandableListAdapter { return; } mCursor.requery(); - buildMap(); - for (DataSetObserver o : mObservers) { - o.onChanged(); - } } public View getGroupView(int groupPosition, boolean isExpanded, diff --git a/ui/src/com/android/providers/downloads/ui/DownloadAdapter.java b/ui/src/com/android/providers/downloads/ui/DownloadAdapter.java index a79122a4..b868ffc4 100644 --- a/ui/src/com/android/providers/downloads/ui/DownloadAdapter.java +++ b/ui/src/com/android/providers/downloads/ui/DownloadAdapter.java @@ -96,8 +96,11 @@ public class DownloadAdapter extends CursorAdapter { // Retrieve the icon for this download retrieveAndSetIcon(convertView); - // TODO: default text for null title? - setTextForView(convertView, R.id.download_title, mCursor.getString(mTitleColumnId)); + String title = mCursor.getString(mTitleColumnId); + if (title.isEmpty()) { + title = mResources.getString(R.string.missing_title); + } + setTextForView(convertView, R.id.download_title, title); setTextForView(convertView, R.id.domain, mCursor.getString(mDescriptionColumnId)); setTextForView(convertView, R.id.size_text, getSizeText()); setTextForView(convertView, R.id.status_text, mResources.getString(getStatusStringId())); diff --git a/ui/src/com/android/providers/downloads/ui/DownloadList.java b/ui/src/com/android/providers/downloads/ui/DownloadList.java index 1b7c727e..dd9a6083 100644 --- a/ui/src/com/android/providers/downloads/ui/DownloadList.java +++ b/ui/src/com/android/providers/downloads/ui/DownloadList.java @@ -21,11 +21,14 @@ import android.app.AlertDialog; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.DialogInterface; +import android.content.DialogInterface.OnCancelListener; import android.content.Intent; +import android.database.ContentObserver; import android.database.Cursor; import android.net.DownloadManager; import android.net.Uri; import android.os.Bundle; +import android.os.Handler; import android.provider.Downloads; import android.view.Menu; import android.view.MenuInflater; @@ -44,6 +47,7 @@ import android.widget.Toast; import com.android.providers.downloads.ui.DownloadItem.DownloadSelectListener; +import java.io.File; import java.util.HashSet; import java.util.Iterator; import java.util.Set; @@ -53,7 +57,7 @@ import java.util.Set; */ public class DownloadList extends Activity implements OnChildClickListener, OnItemClickListener, DownloadSelectListener, - OnClickListener { + OnClickListener, OnCancelListener { private ExpandableListView mDateOrderedListView; private ListView mSizeOrderedListView; private View mEmptyView; @@ -65,6 +69,7 @@ public class DownloadList extends Activity private DateSortedDownloadAdapter mDateSortedAdapter; private Cursor mSizeSortedCursor; private DownloadAdapter mSizeSortedAdapter; + private MyContentObserver mContentObserver = new MyContentObserver(); private int mStatusColumnId; private int mIdColumnId; @@ -74,14 +79,34 @@ public class DownloadList extends Activity private boolean mIsSortedBySize = false; private Set<Long> mSelectedIds = new HashSet<Long>(); + /** + * We keep track of when a dialog is being displayed for a pending download, because if that + * download starts running, we want to immediately hide the dialog. + */ + private Long mPendingDownloadId = null; + private AlertDialog mPendingDialog; + + private class MyContentObserver extends ContentObserver { + public MyContentObserver() { + super(new Handler()); + } + + @Override + public void onChange(boolean selfChange) { + handleDownloadsChanged(); + } + } + @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); setupViews(); mDownloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE); - mDateSortedCursor = mDownloadManager.query(new DownloadManager.Query()); - mSizeSortedCursor = mDownloadManager.query(new DownloadManager.Query() + DownloadManager.Query baseQuery = new DownloadManager.Query() + .setOnlyIncludeVisibleInDownloadsUi(true); + mDateSortedCursor = mDownloadManager.query(baseQuery); + mSizeSortedCursor = mDownloadManager.query(baseQuery .orderBy(DownloadManager.COLUMN_TOTAL_SIZE_BYTES, DownloadManager.Query.ORDER_DESCENDING)); @@ -131,15 +156,28 @@ public class DownloadList extends Activity mSelectionMenuView = (ViewGroup) findViewById(R.id.selection_menu); mSelectionDeleteButton = (Button) findViewById(R.id.selection_delete); mSelectionDeleteButton.setOnClickListener(this); + + ((Button) findViewById(R.id.deselect_all)).setOnClickListener(this); } @Override protected void onResume() { super.onResume(); + if (mDateSortedCursor != null) { + mDateSortedCursor.registerContentObserver(mContentObserver); + } refresh(); } @Override + protected void onPause() { + super.onPause(); + if (mDateSortedCursor != null) { + mDateSortedCursor.unregisterContentObserver(mContentObserver); + } + } + + @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean("isSortedBySize", mIsSortedBySize); @@ -237,11 +275,28 @@ public class DownloadList extends Activity } /** + * @return an OnClickListener to restart the given downloadId in the Download Manager + */ + private DialogInterface.OnClickListener getRestartClickHandler(final long downloadId) { + return new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + mDownloadManager.restartDownload(downloadId); + } + }; + } + + /** * Send an Intent to open the download currently pointed to by the given cursor. */ private void openCurrentDownload(Cursor cursor) { - Intent intent = new Intent(Intent.ACTION_VIEW); Uri fileUri = Uri.parse(cursor.getString(mLocalUriColumnId)); + if (!new File(fileUri.getPath()).exists()) { + showFailedDialog(cursor.getLong(mIdColumnId), R.string.dialog_file_missing_body); + return; + } + + Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(fileUri, cursor.getString(mMediaTypeColumnId)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); try { @@ -255,11 +310,13 @@ public class DownloadList extends Activity long id = cursor.getInt(mIdColumnId); switch (cursor.getInt(mStatusColumnId)) { case DownloadManager.STATUS_PENDING: - new AlertDialog.Builder(this) + mPendingDownloadId = id; + mPendingDialog = new AlertDialog.Builder(this) .setTitle(R.string.dialog_title_not_available) - .setMessage("This file is queued for future download.") + .setMessage(R.string.dialog_queued_body) .setPositiveButton(R.string.keep_queued_download, null) .setNegativeButton(R.string.remove_download, getDeleteClickHandler(id)) + .setOnCancelListener(this) .show(); break; @@ -273,16 +330,20 @@ public class DownloadList extends Activity break; case DownloadManager.STATUS_FAILED: - new AlertDialog.Builder(this) - .setTitle(R.string.dialog_title_not_available) - .setMessage(getResources().getString(R.string.dialog_failed_body)) - .setPositiveButton(R.string.remove_download, getDeleteClickHandler(id)) - // TODO button to retry download - .show(); + showFailedDialog(id, R.string.dialog_failed_body); break; } } + private void showFailedDialog(long downloadId, int dialogBodyResource) { + new AlertDialog.Builder(this) + .setTitle(R.string.dialog_title_not_available) + .setMessage(getResources().getString(dialogBodyResource)) + .setPositiveButton(R.string.remove_download, getDeleteClickHandler(downloadId)) + .setNegativeButton(R.string.retry_download, getRestartClickHandler(downloadId)) + .show(); + } + /** * TODO use constants/shared code? */ @@ -377,7 +438,11 @@ public class DownloadList extends Activity deleteDownload(downloadId); } clearSelection(); - return; + break; + + case R.id.deselect_all: + clearSelection(); + break; } } @@ -406,4 +471,60 @@ public class DownloadList extends Activity public boolean isDownloadSelected(long id) { return mSelectedIds.contains(id); } + + /** + * Called when there's a change to the downloads database. + */ + void handleDownloadsChanged() { + checkSelectionForDeletedEntries(); + + if (mPendingDownloadId != null && moveToDownload(mPendingDownloadId)) { + if (mDateSortedCursor.getInt(mStatusColumnId) != DownloadManager.STATUS_PENDING) { + mPendingDialog.cancel(); + } + } + } + + /** + * Check if any of the selected downloads have been deleted from the downloads database, and + * remove such downloads from the selection. + */ + private void checkSelectionForDeletedEntries() { + // gather all existing IDs... + Set<Long> allIds = new HashSet<Long>(); + for (mDateSortedCursor.moveToFirst(); !mDateSortedCursor.isAfterLast(); + mDateSortedCursor.moveToNext()) { + allIds.add(mDateSortedCursor.getLong(mIdColumnId)); + } + + // ...and check if any selected IDs are now missing + for (Iterator<Long> iterator = mSelectedIds.iterator(); iterator.hasNext(); ) { + if (!allIds.contains(iterator.next())) { + iterator.remove(); + } + } + } + + /** + * Move {@link #mDateSortedCursor} to the download with the given ID. + * @return true if the specified download ID was found; false otherwise + */ + private boolean moveToDownload(long downloadId) { + for (mDateSortedCursor.moveToFirst(); !mDateSortedCursor.isAfterLast(); + mDateSortedCursor.moveToNext()) { + if (mDateSortedCursor.getLong(mIdColumnId) == downloadId) { + return true; + } + } + return false; + } + + /** + * Called when a dialog for a pending download is canceled. + */ + @Override + public void onCancel(DialogInterface dialog) { + mPendingDownloadId = null; + mPendingDialog = null; + } } |