diff options
Diffstat (limited to 'ui/src/com/android/providers/downloads/ui/DownloadList.java')
-rw-r--r-- | ui/src/com/android/providers/downloads/ui/DownloadList.java | 409 |
1 files changed, 409 insertions, 0 deletions
diff --git a/ui/src/com/android/providers/downloads/ui/DownloadList.java b/ui/src/com/android/providers/downloads/ui/DownloadList.java new file mode 100644 index 00000000..1b7c727e --- /dev/null +++ b/ui/src/com/android/providers/downloads/ui/DownloadList.java @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.providers.downloads.ui; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.database.Cursor; +import android.net.DownloadManager; +import android.net.Uri; +import android.os.Bundle; +import android.provider.Downloads; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.view.animation.AnimationUtils; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.Button; +import android.widget.ExpandableListView; +import android.widget.ExpandableListView.OnChildClickListener; +import android.widget.ListView; +import android.widget.Toast; + +import com.android.providers.downloads.ui.DownloadItem.DownloadSelectListener; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +/** + * View showing a list of all downloads the Download Manager knows about. + */ +public class DownloadList extends Activity + implements OnChildClickListener, OnItemClickListener, DownloadSelectListener, + OnClickListener { + private ExpandableListView mDateOrderedListView; + private ListView mSizeOrderedListView; + private View mEmptyView; + private ViewGroup mSelectionMenuView; + private Button mSelectionDeleteButton; + + private DownloadManager mDownloadManager; + private Cursor mDateSortedCursor; + private DateSortedDownloadAdapter mDateSortedAdapter; + private Cursor mSizeSortedCursor; + private DownloadAdapter mSizeSortedAdapter; + + private int mStatusColumnId; + private int mIdColumnId; + private int mLocalUriColumnId; + private int mMediaTypeColumnId; + + private boolean mIsSortedBySize = false; + private Set<Long> mSelectedIds = new HashSet<Long>(); + + @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() + .orderBy(DownloadManager.COLUMN_TOTAL_SIZE_BYTES, + DownloadManager.Query.ORDER_DESCENDING)); + + // only attach everything to the listbox if we can access the download database. Otherwise, + // just show it empty + if (mDateSortedCursor != null && mSizeSortedCursor != null) { + startManagingCursor(mDateSortedCursor); + startManagingCursor(mSizeSortedCursor); + + mStatusColumnId = + mDateSortedCursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS); + mIdColumnId = + mDateSortedCursor.getColumnIndexOrThrow(DownloadManager.COLUMN_ID); + mLocalUriColumnId = + mDateSortedCursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI); + mMediaTypeColumnId = + mDateSortedCursor.getColumnIndexOrThrow(DownloadManager.COLUMN_MEDIA_TYPE); + + mDateSortedAdapter = new DateSortedDownloadAdapter(this, mDateSortedCursor, this); + mDateOrderedListView.setAdapter(mDateSortedAdapter); + mSizeSortedAdapter = new DownloadAdapter(this, mSizeSortedCursor, this); + mSizeOrderedListView.setAdapter(mSizeSortedAdapter); + + // have the first group be open by default + mDateOrderedListView.post(new Runnable() { + public void run() { + if (mDateSortedAdapter.getGroupCount() > 0) { + mDateOrderedListView.expandGroup(0); + } + } + }); + } + + chooseListToShow(); + } + + private void setupViews() { + setContentView(R.layout.download_list); + setTitle(getText(R.string.download_title)); + + mDateOrderedListView = (ExpandableListView) findViewById(R.id.date_ordered_list); + mDateOrderedListView.setOnChildClickListener(this); + mSizeOrderedListView = (ListView) findViewById(R.id.size_ordered_list); + mSizeOrderedListView.setOnItemClickListener(this); + mEmptyView = findViewById(R.id.empty); + + mSelectionMenuView = (ViewGroup) findViewById(R.id.selection_menu); + mSelectionDeleteButton = (Button) findViewById(R.id.selection_delete); + mSelectionDeleteButton.setOnClickListener(this); + } + + @Override + protected void onResume() { + super.onResume(); + refresh(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putBoolean("isSortedBySize", mIsSortedBySize); + outState.putLongArray("selection", getSelectionAsArray()); + } + + private long[] getSelectionAsArray() { + long[] selectedIds = new long[mSelectedIds.size()]; + Iterator<Long> iterator = mSelectedIds.iterator(); + for (int i = 0; i < selectedIds.length; i++) { + selectedIds[i] = iterator.next(); + } + return selectedIds; + } + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + mIsSortedBySize = savedInstanceState.getBoolean("isSortedBySize"); + mSelectedIds.clear(); + for (long selectedId : savedInstanceState.getLongArray("selection")) { + mSelectedIds.add(selectedId); + } + chooseListToShow(); + showOrHideSelectionMenu(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + if (mDateSortedCursor != null) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.download_menu, menu); + } + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + menu.findItem(R.id.download_menu_sort_by_size).setVisible(!mIsSortedBySize); + menu.findItem(R.id.download_menu_sort_by_date).setVisible(mIsSortedBySize); + return super.onPrepareOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.download_menu_sort_by_size: + mIsSortedBySize = true; + chooseListToShow(); + return true; + case R.id.download_menu_sort_by_date: + mIsSortedBySize = false; + chooseListToShow(); + return true; + } + return false; + } + + /** + * Show the correct ListView and hide the other, or hide both and show the empty view. + */ + private void chooseListToShow() { + mDateOrderedListView.setVisibility(View.GONE); + mSizeOrderedListView.setVisibility(View.GONE); + + if (mDateSortedCursor.getCount() == 0) { + mEmptyView.setVisibility(View.VISIBLE); + } else { + mEmptyView.setVisibility(View.GONE); + activeListView().setVisibility(View.VISIBLE); + activeListView().invalidateViews(); // ensure checkboxes get updated + } + } + + /** + * @return the ListView that should currently be visible. + */ + private ListView activeListView() { + if (mIsSortedBySize) { + return mSizeOrderedListView; + } + return mDateOrderedListView; + } + + /** + * @return an OnClickListener to delete the given downloadId from the Download Manager + */ + private DialogInterface.OnClickListener getDeleteClickHandler(final long downloadId) { + return new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + deleteDownload(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)); + intent.setDataAndType(fileUri, cursor.getString(mMediaTypeColumnId)); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + try { + startActivity(intent); + } catch (ActivityNotFoundException ex) { + Toast.makeText(this, R.string.download_no_application_title, Toast.LENGTH_LONG).show(); + } + } + + private void handleItemClick(Cursor cursor) { + long id = cursor.getInt(mIdColumnId); + switch (cursor.getInt(mStatusColumnId)) { + case DownloadManager.STATUS_PENDING: + new AlertDialog.Builder(this) + .setTitle(R.string.dialog_title_not_available) + .setMessage("This file is queued for future download.") + .setPositiveButton(R.string.keep_queued_download, null) + .setNegativeButton(R.string.remove_download, getDeleteClickHandler(id)) + .show(); + break; + + case DownloadManager.STATUS_RUNNING: + case DownloadManager.STATUS_PAUSED: + sendRunningDownloadClickedBroadcast(id); + break; + + case DownloadManager.STATUS_SUCCESSFUL: + openCurrentDownload(cursor); + 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(); + break; + } + } + + /** + * TODO use constants/shared code? + */ + private void sendRunningDownloadClickedBroadcast(long id) { + Intent intent = new Intent("android.intent.action.DOWNLOAD_LIST"); + intent.setClassName("com.android.providers.downloads", + "com.android.providers.downloads.DownloadReceiver"); + intent.setData(Uri.parse(Downloads.Impl.CONTENT_URI + "/" + id)); + intent.putExtra("multiple", false); + sendBroadcast(intent); + } + + // handle a click from the date-sorted list + @Override + public boolean onChildClick(ExpandableListView parent, View v, + int groupPosition, int childPosition, long id) { + mDateSortedAdapter.moveCursorToChildPosition(groupPosition, childPosition); + handleItemClick(mDateSortedCursor); + return true; + } + + // handle a click from the size-sorted list + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + mSizeSortedCursor.moveToPosition(position); + handleItemClick(mSizeSortedCursor); + } + + // handle a click on one of the download item checkboxes + @Override + public void onDownloadSelectionChanged(long downloadId, boolean isSelected) { + if (isSelected) { + mSelectedIds.add(downloadId); + } else { + mSelectedIds.remove(downloadId); + } + showOrHideSelectionMenu(); + } + + private void showOrHideSelectionMenu() { + boolean shouldBeVisible = !mSelectedIds.isEmpty(); + boolean isVisible = mSelectionMenuView.getVisibility() == View.VISIBLE; + if (shouldBeVisible) { + updateSelectionMenu(); + if (!isVisible) { + // show menu + mSelectionMenuView.setVisibility(View.VISIBLE); + mSelectionMenuView.startAnimation( + AnimationUtils.loadAnimation(this, R.anim.footer_appear)); + } + } else if (!shouldBeVisible && isVisible) { + // hide menu + mSelectionMenuView.setVisibility(View.GONE); + mSelectionMenuView.startAnimation( + AnimationUtils.loadAnimation(this, R.anim.footer_disappear)); + } + } + + /** + * Set up the contents of the selection menu based on the current selection. + */ + private void updateSelectionMenu() { + int deleteButtonStringId = R.string.delete_download; + if (mSelectedIds.size() == 1) { + Cursor cursor = mDownloadManager.query(new DownloadManager.Query() + .setFilterById(mSelectedIds.iterator().next())); + try { + cursor.moveToFirst(); + switch (cursor.getInt(mStatusColumnId)) { + case DownloadManager.STATUS_FAILED: + case DownloadManager.STATUS_PENDING: + deleteButtonStringId = R.string.remove_download; + break; + + case DownloadManager.STATUS_PAUSED: + case DownloadManager.STATUS_RUNNING: + deleteButtonStringId = R.string.cancel_running_download; + break; + } + } finally { + cursor.close(); + } + } + mSelectionDeleteButton.setText(deleteButtonStringId); + } + + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.selection_delete: + for (Long downloadId : mSelectedIds) { + deleteDownload(downloadId); + } + clearSelection(); + return; + } + } + + /** + * Requery the database and update the UI. + */ + private void refresh() { + mDateSortedCursor.requery(); + mSizeSortedCursor.requery(); + // Adapters get notification of changes and update automatically + } + + private void clearSelection() { + mSelectedIds.clear(); + showOrHideSelectionMenu(); + } + + /** + * Delete a download from the Download Manager. + */ + private void deleteDownload(Long downloadId) { + mDownloadManager.remove(downloadId); + } + + @Override + public boolean isDownloadSelected(long id) { + return mSelectedIds.contains(id); + } +} |