2 * Copyright (C) 2007 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.browser;
19 import android.app.Activity;
20 import android.app.AlertDialog;
21 import android.content.ActivityNotFoundException;
22 import android.content.ContentValues;
23 import android.content.DialogInterface;
24 import android.content.Intent;
25 import android.content.ContentUris;
26 import android.content.pm.PackageManager;
27 import android.content.pm.ResolveInfo;
28 import android.database.Cursor;
29 import android.net.Uri;
30 import android.os.Bundle;
31 import android.provider.Downloads;
32 import android.view.ContextMenu;
33 import android.view.ContextMenu.ContextMenuInfo;
34 import android.view.LayoutInflater;
35 import android.view.Menu;
36 import android.view.MenuItem;
37 import android.view.MenuInflater;
38 import android.view.View;
39 import android.view.ViewGroup.LayoutParams;
40 import android.widget.AdapterView;
41 import android.widget.ListView;
42 import android.widget.AdapterView.OnItemClickListener;
45 import java.util.List;
48 * View showing the user's current browser downloads
50 public class BrowserDownloadPage extends Activity
51 implements View.OnCreateContextMenuListener, OnItemClickListener {
53 private ListView mListView;
54 private Cursor mDownloadCursor;
55 private BrowserDownloadAdapter mDownloadAdapter;
56 private int mStatusColumnId;
57 private int mIdColumnId;
58 private int mTitleColumnId;
59 private int mContextMenuPosition;
62 public void onCreate(Bundle icicle) {
63 super.onCreate(icicle);
64 setContentView(R.layout.browser_downloads_page);
66 setTitle(getText(R.string.download_title));
68 mListView = (ListView) findViewById(R.id.list);
69 LayoutInflater factory = LayoutInflater.from(this);
70 View v = factory.inflate(R.layout.no_downloads, null);
71 addContentView(v, new LayoutParams(LayoutParams.FILL_PARENT,
72 LayoutParams.FILL_PARENT));
73 mListView.setEmptyView(v);
75 mDownloadCursor = managedQuery(Downloads.CONTENT_URI,
76 new String [] {"_id", Downloads.COLUMN_TITLE, Downloads.COLUMN_STATUS,
77 Downloads.COLUMN_TOTAL_BYTES, Downloads.COLUMN_CURRENT_BYTES,
78 Downloads._DATA, Downloads.COLUMN_DESCRIPTION,
79 Downloads.COLUMN_MIME_TYPE, Downloads.COLUMN_LAST_MODIFICATION,
80 Downloads.COLUMN_VISIBILITY},
83 // only attach everything to the listbox if we can access
84 // the download database. Otherwise, just show it empty
85 if (mDownloadCursor != null) {
87 mDownloadCursor.getColumnIndexOrThrow(Downloads.COLUMN_STATUS);
89 mDownloadCursor.getColumnIndexOrThrow(Downloads._ID);
91 mDownloadCursor.getColumnIndexOrThrow(Downloads.COLUMN_TITLE);
93 // Create a list "controller" for the data
94 mDownloadAdapter = new BrowserDownloadAdapter(this,
95 R.layout.browser_download_item, mDownloadCursor);
97 mListView.setAdapter(mDownloadAdapter);
98 mListView.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
99 mListView.setOnCreateContextMenuListener(this);
100 mListView.setOnItemClickListener(this);
102 Intent intent = getIntent();
103 if (intent != null && intent.getData() != null) {
104 int position = checkStatus(
105 ContentUris.parseId(intent.getData()));
107 mListView.setSelection(position);
114 public boolean onCreateOptionsMenu(Menu menu) {
115 if (mDownloadCursor != null) {
116 MenuInflater inflater = getMenuInflater();
117 inflater.inflate(R.menu.downloadhistory, menu);
123 public boolean onPrepareOptionsMenu(Menu menu) {
124 boolean showCancel = getCancelableCount() > 0;
125 menu.findItem(R.id.download_menu_cancel_all).setEnabled(showCancel);
127 boolean showClear = getClearableCount() > 0;
128 menu.findItem(R.id.download_menu_clear_all).setEnabled(showClear);
129 return super.onPrepareOptionsMenu(menu);
133 public boolean onOptionsItemSelected(MenuItem item) {
134 switch (item.getItemId()) {
135 case R.id.download_menu_cancel_all:
139 case R.id.download_menu_clear_all:
147 public boolean onContextItemSelected(MenuItem item) {
148 mDownloadCursor.moveToPosition(mContextMenuPosition);
149 switch (item.getItemId()) {
150 case R.id.download_menu_open:
151 hideCompletedDownload();
152 openCurrentDownload();
155 case R.id.download_menu_clear:
156 case R.id.download_menu_cancel:
157 getContentResolver().delete(
158 ContentUris.withAppendedId(Downloads.CONTENT_URI,
159 mDownloadCursor.getLong(mIdColumnId)), null, null);
166 public void onCreateContextMenu(ContextMenu menu, View v,
167 ContextMenuInfo menuInfo) {
168 if (mDownloadCursor != null) {
169 AdapterView.AdapterContextMenuInfo info =
170 (AdapterView.AdapterContextMenuInfo) menuInfo;
171 mDownloadCursor.moveToPosition(info.position);
172 mContextMenuPosition = info.position;
173 menu.setHeaderTitle(mDownloadCursor.getString(mTitleColumnId));
175 MenuInflater inflater = getMenuInflater();
176 int status = mDownloadCursor.getInt(mStatusColumnId);
177 if (Downloads.isStatusSuccess(status)) {
178 inflater.inflate(R.menu.downloadhistorycontextfinished, menu);
179 } else if (Downloads.isStatusError(status)) {
180 inflater.inflate(R.menu.downloadhistorycontextfailed, menu);
182 inflater.inflate(R.menu.downloadhistorycontextrunning, menu);
188 * This function is called to check the status of the download and if it
189 * has an error show an error dialog.
190 * @param id Row id of the download to check
191 * @return position of item
193 int checkStatus(final long id) {
195 for (mDownloadCursor.moveToFirst(); !mDownloadCursor.isAfterLast();
196 mDownloadCursor.moveToNext()) {
197 if (id == mDownloadCursor.getLong(mIdColumnId)) {
198 position = mDownloadCursor.getPosition();
203 if (!mDownloadCursor.isAfterLast()) {
204 int status = mDownloadCursor.getInt(mStatusColumnId);
205 if (!Downloads.isStatusError(status)) {
209 if (status == Downloads.STATUS_FILE_ERROR) {
210 String title = mDownloadCursor.getString(mTitleColumnId);
211 if (title == null || title.length() == 0) {
212 title = getString(R.string.download_unknown_filename);
214 String msg = getString(R.string.download_file_error_dlg_msg,
216 new AlertDialog.Builder(this)
217 .setTitle(R.string.download_file_error_dlg_title)
218 .setIcon(android.R.drawable.ic_popup_disk_full)
220 .setPositiveButton(R.string.ok, null)
221 .setNegativeButton(R.string.retry,
222 new DialogInterface.OnClickListener() {
223 public void onClick(DialogInterface dialog,
230 new AlertDialog.Builder(this)
231 .setTitle(R.string.download_failed_generic_dlg_title)
232 .setIcon(R.drawable.ssl_icon)
233 .setMessage(BrowserDownloadAdapter.getErrorText(status))
234 .setPositiveButton(R.string.ok, null)
242 * Resume a given download
243 * @param id Row id of the download to resume
245 private void resumeDownload(final long id) {
246 // the relevant functionality doesn't exist in the download manager
250 * Prompt the user if they would like to clear the download history
252 private void promptClearList() {
253 new AlertDialog.Builder(this)
254 .setTitle(R.string.download_clear_dlg_title)
255 .setIcon(R.drawable.ssl_icon)
256 .setMessage(R.string.download_clear_dlg_msg)
257 .setPositiveButton(R.string.ok,
258 new DialogInterface.OnClickListener() {
259 public void onClick(DialogInterface dialog,
264 .setNegativeButton(R.string.cancel, null)
269 * Return the number of items in the list that can be canceled.
272 private int getCancelableCount() {
273 // Count the number of items that will be canceled.
275 if (mDownloadCursor != null) {
276 for (mDownloadCursor.moveToFirst(); !mDownloadCursor.isAfterLast();
277 mDownloadCursor.moveToNext()) {
278 int status = mDownloadCursor.getInt(mStatusColumnId);
279 if (!Downloads.isStatusCompleted(status)) {
289 * Prompt the user if they would like to clear the download history
291 private void promptCancelAll() {
292 int count = getCancelableCount();
294 // If there is nothing to do, just return
299 // Don't show the dialog if there is only one download
301 cancelAllDownloads();
305 getString(R.string.download_cancel_dlg_msg, count);
306 new AlertDialog.Builder(this)
307 .setTitle(R.string.download_cancel_dlg_title)
308 .setIcon(R.drawable.ssl_icon)
310 .setPositiveButton(R.string.ok,
311 new DialogInterface.OnClickListener() {
312 public void onClick(DialogInterface dialog,
314 cancelAllDownloads();
317 .setNegativeButton(R.string.cancel, null)
322 * Cancel all downloads. As canceled downloads are not
323 * listed, we removed them from the db. Removing a download
324 * record, cancels the download.
326 private void cancelAllDownloads() {
327 if (mDownloadCursor.moveToFirst()) {
328 StringBuilder where = new StringBuilder();
329 boolean firstTime = true;
330 while (!mDownloadCursor.isAfterLast()) {
331 int status = mDownloadCursor.getInt(mStatusColumnId);
332 if (!Downloads.isStatusCompleted(status)) {
336 where.append(" OR ");
339 where.append(Downloads._ID);
340 where.append(" = '");
341 where.append(mDownloadCursor.getLong(mIdColumnId));
344 mDownloadCursor.moveToNext();
347 getContentResolver().delete(Downloads.CONTENT_URI,
348 where.toString(), null);
353 private int getClearableCount() {
355 if (mDownloadCursor.moveToFirst()) {
356 while (!mDownloadCursor.isAfterLast()) {
357 int status = mDownloadCursor.getInt(mStatusColumnId);
358 if (Downloads.isStatusCompleted(status)) {
361 mDownloadCursor.moveToNext();
368 * Clear all stopped downloads, ie canceled (though should not be
369 * there), error and success download items.
371 private void clearAllDownloads() {
372 if (mDownloadCursor.moveToFirst()) {
373 StringBuilder where = new StringBuilder();
374 boolean firstTime = true;
375 while (!mDownloadCursor.isAfterLast()) {
376 int status = mDownloadCursor.getInt(mStatusColumnId);
377 if (Downloads.isStatusCompleted(status)) {
381 where.append(" OR ");
384 where.append(Downloads._ID);
385 where.append(" = '");
386 where.append(mDownloadCursor.getLong(mIdColumnId));
389 mDownloadCursor.moveToNext();
392 getContentResolver().delete(Downloads.CONTENT_URI,
393 where.toString(), null);
399 * Open the content where the download db cursor currently is
401 private void openCurrentDownload() {
402 int filenameColumnId =
403 mDownloadCursor.getColumnIndexOrThrow(Downloads._DATA);
404 String filename = mDownloadCursor.getString(filenameColumnId);
405 int mimetypeColumnId =
406 mDownloadCursor.getColumnIndexOrThrow(Downloads.COLUMN_MIME_TYPE);
407 String mimetype = mDownloadCursor.getString(mimetypeColumnId);
408 Uri path = Uri.parse(filename);
409 // If there is no scheme, then it must be a file
410 if (path.getScheme() == null) {
411 path = Uri.fromFile(new File(filename));
413 Intent intent = new Intent(Intent.ACTION_VIEW);
414 intent.setDataAndType(path, mimetype);
415 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
417 startActivity(intent);
418 } catch (ActivityNotFoundException ex) {
419 new AlertDialog.Builder(this)
420 .setTitle(R.string.download_failed_generic_dlg_title)
421 .setIcon(R.drawable.ssl_icon)
422 .setMessage(R.string.download_no_application)
423 .setPositiveButton(R.string.ok, null)
430 * @see android.widget.AdapterView.OnItemClickListener#onItemClick(android.widget.AdapterView, android.view.View, int, long)
432 public void onItemClick(AdapterView parent, View view, int position,
434 // Open the selected item
435 mDownloadCursor.moveToPosition(position);
437 hideCompletedDownload();
439 int status = mDownloadCursor.getInt(mStatusColumnId);
440 if (Downloads.isStatusSuccess(status)) {
441 // Open it if it downloaded successfully
442 openCurrentDownload();
444 // Check to see if there is an error.
450 * hides the notification for the download pointed by mDownloadCursor
451 * if the download has completed.
453 private void hideCompletedDownload() {
454 int status = mDownloadCursor.getInt(mStatusColumnId);
456 int visibilityColumn = mDownloadCursor.getColumnIndexOrThrow(Downloads.COLUMN_VISIBILITY);
457 int visibility = mDownloadCursor.getInt(visibilityColumn);
459 if (Downloads.isStatusCompleted(status) &&
460 visibility == Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) {
461 ContentValues values = new ContentValues();
462 values.put(Downloads.COLUMN_VISIBILITY, Downloads.VISIBILITY_VISIBLE);
463 getContentResolver().update(
464 ContentUris.withAppendedId(Downloads.CONTENT_URI,
465 mDownloadCursor.getLong(mIdColumnId)), values, null, null);