| /* |
| * Copyright (C) 2007 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.browser; |
| |
| import android.app.Activity; |
| import android.app.AlertDialog; |
| import android.content.ActivityNotFoundException; |
| import android.content.ContentValues; |
| import android.content.DialogInterface; |
| import android.content.Intent; |
| import android.content.ContentUris; |
| import android.content.pm.PackageManager; |
| import android.content.pm.ResolveInfo; |
| import android.database.Cursor; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.provider.Downloads; |
| import android.view.ContextMenu; |
| import android.view.ContextMenu.ContextMenuInfo; |
| import android.view.LayoutInflater; |
| import android.view.Menu; |
| import android.view.MenuItem; |
| import android.view.MenuInflater; |
| import android.view.View; |
| import android.view.ViewGroup.LayoutParams; |
| import android.widget.AdapterView; |
| import android.widget.ListView; |
| import android.widget.AdapterView.OnItemClickListener; |
| |
| import java.io.File; |
| import java.util.List; |
| |
| /** |
| * View showing the user's current browser downloads |
| */ |
| public class BrowserDownloadPage extends Activity |
| implements View.OnCreateContextMenuListener, OnItemClickListener { |
| |
| private ListView mListView; |
| private Cursor mDownloadCursor; |
| private BrowserDownloadAdapter mDownloadAdapter; |
| private int mStatusColumnId; |
| private int mIdColumnId; |
| private int mTitleColumnId; |
| private int mContextMenuPosition; |
| |
| @Override |
| public void onCreate(Bundle icicle) { |
| super.onCreate(icicle); |
| setContentView(R.layout.browser_downloads_page); |
| |
| setTitle(getText(R.string.download_title)); |
| |
| mListView = (ListView) findViewById(R.id.list); |
| mListView.setEmptyView(findViewById(R.id.empty)); |
| |
| mDownloadCursor = managedQuery(Downloads.CONTENT_URI, |
| new String [] {"_id", Downloads.COLUMN_TITLE, Downloads.COLUMN_STATUS, |
| Downloads.COLUMN_TOTAL_BYTES, Downloads.COLUMN_CURRENT_BYTES, |
| Downloads._DATA, Downloads.COLUMN_DESCRIPTION, |
| Downloads.COLUMN_MIME_TYPE, Downloads.COLUMN_LAST_MODIFICATION, |
| Downloads.COLUMN_VISIBILITY}, |
| null, null); |
| |
| // only attach everything to the listbox if we can access |
| // the download database. Otherwise, just show it empty |
| if (mDownloadCursor != null) { |
| mStatusColumnId = |
| mDownloadCursor.getColumnIndexOrThrow(Downloads.COLUMN_STATUS); |
| mIdColumnId = |
| mDownloadCursor.getColumnIndexOrThrow(Downloads._ID); |
| mTitleColumnId = |
| mDownloadCursor.getColumnIndexOrThrow(Downloads.COLUMN_TITLE); |
| |
| // Create a list "controller" for the data |
| mDownloadAdapter = new BrowserDownloadAdapter(this, |
| R.layout.browser_download_item, mDownloadCursor); |
| |
| mListView.setAdapter(mDownloadAdapter); |
| mListView.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET); |
| mListView.setOnCreateContextMenuListener(this); |
| mListView.setOnItemClickListener(this); |
| |
| Intent intent = getIntent(); |
| if (intent != null && intent.getData() != null) { |
| int position = checkStatus( |
| ContentUris.parseId(intent.getData())); |
| if (position >= 0) { |
| mListView.setSelection(position); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public boolean onCreateOptionsMenu(Menu menu) { |
| if (mDownloadCursor != null) { |
| MenuInflater inflater = getMenuInflater(); |
| inflater.inflate(R.menu.downloadhistory, menu); |
| } |
| return true; |
| } |
| |
| @Override |
| public boolean onPrepareOptionsMenu(Menu menu) { |
| boolean showCancel = getCancelableCount() > 0; |
| menu.findItem(R.id.download_menu_cancel_all).setEnabled(showCancel); |
| |
| boolean showClear = getClearableCount() > 0; |
| menu.findItem(R.id.download_menu_clear_all).setEnabled(showClear); |
| return super.onPrepareOptionsMenu(menu); |
| } |
| |
| @Override |
| public boolean onOptionsItemSelected(MenuItem item) { |
| switch (item.getItemId()) { |
| case R.id.download_menu_cancel_all: |
| promptCancelAll(); |
| return true; |
| |
| case R.id.download_menu_clear_all: |
| promptClearList(); |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean onContextItemSelected(MenuItem item) { |
| mDownloadCursor.moveToPosition(mContextMenuPosition); |
| switch (item.getItemId()) { |
| case R.id.download_menu_open: |
| hideCompletedDownload(); |
| openCurrentDownload(); |
| return true; |
| |
| case R.id.download_menu_clear: |
| case R.id.download_menu_cancel: |
| getContentResolver().delete( |
| ContentUris.withAppendedId(Downloads.CONTENT_URI, |
| mDownloadCursor.getLong(mIdColumnId)), null, null); |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public void onCreateContextMenu(ContextMenu menu, View v, |
| ContextMenuInfo menuInfo) { |
| if (mDownloadCursor != null) { |
| AdapterView.AdapterContextMenuInfo info = |
| (AdapterView.AdapterContextMenuInfo) menuInfo; |
| mDownloadCursor.moveToPosition(info.position); |
| mContextMenuPosition = info.position; |
| menu.setHeaderTitle(mDownloadCursor.getString(mTitleColumnId)); |
| |
| MenuInflater inflater = getMenuInflater(); |
| int status = mDownloadCursor.getInt(mStatusColumnId); |
| if (Downloads.isStatusSuccess(status)) { |
| inflater.inflate(R.menu.downloadhistorycontextfinished, menu); |
| } else if (Downloads.isStatusError(status)) { |
| inflater.inflate(R.menu.downloadhistorycontextfailed, menu); |
| } else { |
| inflater.inflate(R.menu.downloadhistorycontextrunning, menu); |
| } |
| } |
| } |
| |
| /** |
| * This function is called to check the status of the download and if it |
| * has an error show an error dialog. |
| * @param id Row id of the download to check |
| * @return position of item |
| */ |
| int checkStatus(final long id) { |
| int position = -1; |
| for (mDownloadCursor.moveToFirst(); !mDownloadCursor.isAfterLast(); |
| mDownloadCursor.moveToNext()) { |
| if (id == mDownloadCursor.getLong(mIdColumnId)) { |
| position = mDownloadCursor.getPosition(); |
| break; |
| } |
| |
| } |
| if (!mDownloadCursor.isAfterLast()) { |
| int status = mDownloadCursor.getInt(mStatusColumnId); |
| if (!Downloads.isStatusError(status)) { |
| return position; |
| } |
| |
| if (status == Downloads.STATUS_FILE_ERROR) { |
| String title = mDownloadCursor.getString(mTitleColumnId); |
| if (title == null || title.length() == 0) { |
| title = getString(R.string.download_unknown_filename); |
| } |
| String msg = getString(R.string.download_file_error_dlg_msg, |
| title); |
| new AlertDialog.Builder(this) |
| .setTitle(R.string.download_file_error_dlg_title) |
| .setIcon(android.R.drawable.ic_popup_disk_full) |
| .setMessage(msg) |
| .setPositiveButton(R.string.ok, null) |
| .setNegativeButton(R.string.retry, |
| new DialogInterface.OnClickListener() { |
| public void onClick(DialogInterface dialog, |
| int whichButton) { |
| resumeDownload(id); |
| } |
| }) |
| .show(); |
| } else { |
| new AlertDialog.Builder(this) |
| .setTitle(R.string.download_failed_generic_dlg_title) |
| .setIcon(R.drawable.ssl_icon) |
| .setMessage(BrowserDownloadAdapter.getErrorText(status)) |
| .setPositiveButton(R.string.ok, null) |
| .show(); |
| } |
| } |
| return position; |
| } |
| |
| /** |
| * Resume a given download |
| * @param id Row id of the download to resume |
| */ |
| private void resumeDownload(final long id) { |
| // the relevant functionality doesn't exist in the download manager |
| } |
| |
| /** |
| * Prompt the user if they would like to clear the download history |
| */ |
| private void promptClearList() { |
| new AlertDialog.Builder(this) |
| .setTitle(R.string.download_clear_dlg_title) |
| .setIcon(R.drawable.ssl_icon) |
| .setMessage(R.string.download_clear_dlg_msg) |
| .setPositiveButton(R.string.ok, |
| new DialogInterface.OnClickListener() { |
| public void onClick(DialogInterface dialog, |
| int whichButton) { |
| clearAllDownloads(); |
| } |
| }) |
| .setNegativeButton(R.string.cancel, null) |
| .show(); |
| } |
| |
| /** |
| * Return the number of items in the list that can be canceled. |
| * @return count |
| */ |
| private int getCancelableCount() { |
| // Count the number of items that will be canceled. |
| int count = 0; |
| if (mDownloadCursor != null) { |
| for (mDownloadCursor.moveToFirst(); !mDownloadCursor.isAfterLast(); |
| mDownloadCursor.moveToNext()) { |
| int status = mDownloadCursor.getInt(mStatusColumnId); |
| if (!Downloads.isStatusCompleted(status)) { |
| count++; |
| } |
| } |
| } |
| |
| return count; |
| } |
| |
| /** |
| * Prompt the user if they would like to clear the download history |
| */ |
| private void promptCancelAll() { |
| int count = getCancelableCount(); |
| |
| // If there is nothing to do, just return |
| if (count == 0) { |
| return; |
| } |
| |
| // Don't show the dialog if there is only one download |
| if (count == 1) { |
| cancelAllDownloads(); |
| return; |
| } |
| String msg = |
| getString(R.string.download_cancel_dlg_msg, count); |
| new AlertDialog.Builder(this) |
| .setTitle(R.string.download_cancel_dlg_title) |
| .setIcon(R.drawable.ssl_icon) |
| .setMessage(msg) |
| .setPositiveButton(R.string.ok, |
| new DialogInterface.OnClickListener() { |
| public void onClick(DialogInterface dialog, |
| int whichButton) { |
| cancelAllDownloads(); |
| } |
| }) |
| .setNegativeButton(R.string.cancel, null) |
| .show(); |
| } |
| |
| /** |
| * Cancel all downloads. As canceled downloads are not |
| * listed, we removed them from the db. Removing a download |
| * record, cancels the download. |
| */ |
| private void cancelAllDownloads() { |
| if (mDownloadCursor.moveToFirst()) { |
| StringBuilder where = new StringBuilder(); |
| boolean firstTime = true; |
| while (!mDownloadCursor.isAfterLast()) { |
| int status = mDownloadCursor.getInt(mStatusColumnId); |
| if (!Downloads.isStatusCompleted(status)) { |
| if (firstTime) { |
| firstTime = false; |
| } else { |
| where.append(" OR "); |
| } |
| where.append("( "); |
| where.append(Downloads._ID); |
| where.append(" = '"); |
| where.append(mDownloadCursor.getLong(mIdColumnId)); |
| where.append("' )"); |
| } |
| mDownloadCursor.moveToNext(); |
| } |
| if (!firstTime) { |
| getContentResolver().delete(Downloads.CONTENT_URI, |
| where.toString(), null); |
| } |
| } |
| } |
| |
| private int getClearableCount() { |
| int count = 0; |
| if (mDownloadCursor.moveToFirst()) { |
| while (!mDownloadCursor.isAfterLast()) { |
| int status = mDownloadCursor.getInt(mStatusColumnId); |
| if (Downloads.isStatusCompleted(status)) { |
| count++; |
| } |
| mDownloadCursor.moveToNext(); |
| } |
| } |
| return count; |
| } |
| |
| /** |
| * Clear all stopped downloads, ie canceled (though should not be |
| * there), error and success download items. |
| */ |
| private void clearAllDownloads() { |
| if (mDownloadCursor.moveToFirst()) { |
| StringBuilder where = new StringBuilder(); |
| boolean firstTime = true; |
| while (!mDownloadCursor.isAfterLast()) { |
| int status = mDownloadCursor.getInt(mStatusColumnId); |
| if (Downloads.isStatusCompleted(status)) { |
| if (firstTime) { |
| firstTime = false; |
| } else { |
| where.append(" OR "); |
| } |
| where.append("( "); |
| where.append(Downloads._ID); |
| where.append(" = '"); |
| where.append(mDownloadCursor.getLong(mIdColumnId)); |
| where.append("' )"); |
| } |
| mDownloadCursor.moveToNext(); |
| } |
| if (!firstTime) { |
| getContentResolver().delete(Downloads.CONTENT_URI, |
| where.toString(), null); |
| } |
| } |
| } |
| |
| /** |
| * Open the content where the download db cursor currently is |
| */ |
| private void openCurrentDownload() { |
| int filenameColumnId = |
| mDownloadCursor.getColumnIndexOrThrow(Downloads._DATA); |
| String filename = mDownloadCursor.getString(filenameColumnId); |
| int mimetypeColumnId = |
| mDownloadCursor.getColumnIndexOrThrow(Downloads.COLUMN_MIME_TYPE); |
| String mimetype = mDownloadCursor.getString(mimetypeColumnId); |
| Uri path = Uri.parse(filename); |
| // If there is no scheme, then it must be a file |
| if (path.getScheme() == null) { |
| path = Uri.fromFile(new File(filename)); |
| } |
| Intent intent = new Intent(Intent.ACTION_VIEW); |
| intent.setDataAndType(path, mimetype); |
| intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); |
| try { |
| startActivity(intent); |
| } catch (ActivityNotFoundException ex) { |
| new AlertDialog.Builder(this) |
| .setTitle(R.string.download_failed_generic_dlg_title) |
| .setIcon(R.drawable.ssl_icon) |
| .setMessage(R.string.download_no_application) |
| .setPositiveButton(R.string.ok, null) |
| .show(); |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * @see android.widget.AdapterView.OnItemClickListener#onItemClick(android.widget.AdapterView, android.view.View, int, long) |
| */ |
| public void onItemClick(AdapterView parent, View view, int position, |
| long id) { |
| // Open the selected item |
| mDownloadCursor.moveToPosition(position); |
| |
| hideCompletedDownload(); |
| |
| int status = mDownloadCursor.getInt(mStatusColumnId); |
| if (Downloads.isStatusSuccess(status)) { |
| // Open it if it downloaded successfully |
| openCurrentDownload(); |
| } else { |
| // Check to see if there is an error. |
| checkStatus(id); |
| } |
| } |
| |
| /** |
| * hides the notification for the download pointed by mDownloadCursor |
| * if the download has completed. |
| */ |
| private void hideCompletedDownload() { |
| int status = mDownloadCursor.getInt(mStatusColumnId); |
| |
| int visibilityColumn = mDownloadCursor.getColumnIndexOrThrow(Downloads.COLUMN_VISIBILITY); |
| int visibility = mDownloadCursor.getInt(visibilityColumn); |
| |
| if (Downloads.isStatusCompleted(status) && |
| visibility == Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) { |
| ContentValues values = new ContentValues(); |
| values.put(Downloads.COLUMN_VISIBILITY, Downloads.VISIBILITY_VISIBLE); |
| getContentResolver().update( |
| ContentUris.withAppendedId(Downloads.CONTENT_URI, |
| mDownloadCursor.getLong(mIdColumnId)), values, null, null); |
| } |
| } |
| } |