/* * 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); } } }