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 mListView.setEmptyView(findViewById(R.id.empty));
71 mDownloadCursor = managedQuery(Downloads.CONTENT_URI,
72 new String [] {"_id", Downloads.COLUMN_TITLE, Downloads.COLUMN_STATUS,
73 Downloads.COLUMN_TOTAL_BYTES, Downloads.COLUMN_CURRENT_BYTES,
74 Downloads._DATA, Downloads.COLUMN_DESCRIPTION,
75 Downloads.COLUMN_MIME_TYPE, Downloads.COLUMN_LAST_MODIFICATION,
76 Downloads.COLUMN_VISIBILITY},
79 // only attach everything to the listbox if we can access
80 // the download database. Otherwise, just show it empty
81 if (mDownloadCursor != null) {
83 mDownloadCursor.getColumnIndexOrThrow(Downloads.COLUMN_STATUS);
85 mDownloadCursor.getColumnIndexOrThrow(Downloads._ID);
87 mDownloadCursor.getColumnIndexOrThrow(Downloads.COLUMN_TITLE);
89 // Create a list "controller" for the data
90 mDownloadAdapter = new BrowserDownloadAdapter(this,
91 R.layout.browser_download_item, mDownloadCursor);
93 mListView.setAdapter(mDownloadAdapter);
94 mListView.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
95 mListView.setOnCreateContextMenuListener(this);
96 mListView.setOnItemClickListener(this);
98 Intent intent = getIntent();
99 if (intent != null && intent.getData() != null) {
100 int position = checkStatus(
101 ContentUris.parseId(intent.getData()));
103 mListView.setSelection(position);
110 public boolean onCreateOptionsMenu(Menu menu) {
111 if (mDownloadCursor != null) {
112 MenuInflater inflater = getMenuInflater();
113 inflater.inflate(R.menu.downloadhistory, menu);
119 public boolean onPrepareOptionsMenu(Menu menu) {
120 boolean showCancel = getCancelableCount() > 0;
121 menu.findItem(R.id.download_menu_cancel_all).setEnabled(showCancel);
123 boolean showClear = getClearableCount() > 0;
124 menu.findItem(R.id.download_menu_clear_all).setEnabled(showClear);
125 return super.onPrepareOptionsMenu(menu);
129 public boolean onOptionsItemSelected(MenuItem item) {
130 switch (item.getItemId()) {
131 case R.id.download_menu_cancel_all:
135 case R.id.download_menu_clear_all:
143 public boolean onContextItemSelected(MenuItem item) {
144 mDownloadCursor.moveToPosition(mContextMenuPosition);
145 switch (item.getItemId()) {
146 case R.id.download_menu_open:
147 hideCompletedDownload();
148 openCurrentDownload();
151 case R.id.download_menu_clear:
152 case R.id.download_menu_cancel:
153 getContentResolver().delete(
154 ContentUris.withAppendedId(Downloads.CONTENT_URI,
155 mDownloadCursor.getLong(mIdColumnId)), null, null);
162 public void onCreateContextMenu(ContextMenu menu, View v,
163 ContextMenuInfo menuInfo) {
164 if (mDownloadCursor != null) {
165 AdapterView.AdapterContextMenuInfo info =
166 (AdapterView.AdapterContextMenuInfo) menuInfo;
167 mDownloadCursor.moveToPosition(info.position);
168 mContextMenuPosition = info.position;
169 menu.setHeaderTitle(mDownloadCursor.getString(mTitleColumnId));
171 MenuInflater inflater = getMenuInflater();
172 int status = mDownloadCursor.getInt(mStatusColumnId);
173 if (Downloads.isStatusSuccess(status)) {
174 inflater.inflate(R.menu.downloadhistorycontextfinished, menu);
175 } else if (Downloads.isStatusError(status)) {
176 inflater.inflate(R.menu.downloadhistorycontextfailed, menu);
178 inflater.inflate(R.menu.downloadhistorycontextrunning, menu);
184 * This function is called to check the status of the download and if it
185 * has an error show an error dialog.
186 * @param id Row id of the download to check
187 * @return position of item
189 int checkStatus(final long id) {
191 for (mDownloadCursor.moveToFirst(); !mDownloadCursor.isAfterLast();
192 mDownloadCursor.moveToNext()) {
193 if (id == mDownloadCursor.getLong(mIdColumnId)) {
194 position = mDownloadCursor.getPosition();
199 if (!mDownloadCursor.isAfterLast()) {
200 int status = mDownloadCursor.getInt(mStatusColumnId);
201 if (!Downloads.isStatusError(status)) {
205 if (status == Downloads.STATUS_FILE_ERROR) {
206 String title = mDownloadCursor.getString(mTitleColumnId);
207 if (title == null || title.length() == 0) {
208 title = getString(R.string.download_unknown_filename);
210 String msg = getString(R.string.download_file_error_dlg_msg,
212 new AlertDialog.Builder(this)
213 .setTitle(R.string.download_file_error_dlg_title)
214 .setIcon(android.R.drawable.ic_popup_disk_full)
216 .setPositiveButton(R.string.ok, null)
217 .setNegativeButton(R.string.retry,
218 new DialogInterface.OnClickListener() {
219 public void onClick(DialogInterface dialog,
226 new AlertDialog.Builder(this)
227 .setTitle(R.string.download_failed_generic_dlg_title)
228 .setIcon(R.drawable.ssl_icon)
229 .setMessage(BrowserDownloadAdapter.getErrorText(status))
230 .setPositiveButton(R.string.ok, null)
238 * Resume a given download
239 * @param id Row id of the download to resume
241 private void resumeDownload(final long id) {
242 // the relevant functionality doesn't exist in the download manager
246 * Prompt the user if they would like to clear the download history
248 private void promptClearList() {
249 new AlertDialog.Builder(this)
250 .setTitle(R.string.download_clear_dlg_title)
251 .setIcon(R.drawable.ssl_icon)
252 .setMessage(R.string.download_clear_dlg_msg)
253 .setPositiveButton(R.string.ok,
254 new DialogInterface.OnClickListener() {
255 public void onClick(DialogInterface dialog,
260 .setNegativeButton(R.string.cancel, null)
265 * Return the number of items in the list that can be canceled.
268 private int getCancelableCount() {
269 // Count the number of items that will be canceled.
271 if (mDownloadCursor != null) {
272 for (mDownloadCursor.moveToFirst(); !mDownloadCursor.isAfterLast();
273 mDownloadCursor.moveToNext()) {
274 int status = mDownloadCursor.getInt(mStatusColumnId);
275 if (!Downloads.isStatusCompleted(status)) {
285 * Prompt the user if they would like to clear the download history
287 private void promptCancelAll() {
288 int count = getCancelableCount();
290 // If there is nothing to do, just return
295 // Don't show the dialog if there is only one download
297 cancelAllDownloads();
301 getString(R.string.download_cancel_dlg_msg, count);
302 new AlertDialog.Builder(this)
303 .setTitle(R.string.download_cancel_dlg_title)
304 .setIcon(R.drawable.ssl_icon)
306 .setPositiveButton(R.string.ok,
307 new DialogInterface.OnClickListener() {
308 public void onClick(DialogInterface dialog,
310 cancelAllDownloads();
313 .setNegativeButton(R.string.cancel, null)
318 * Cancel all downloads. As canceled downloads are not
319 * listed, we removed them from the db. Removing a download
320 * record, cancels the download.
322 private void cancelAllDownloads() {
323 if (mDownloadCursor.moveToFirst()) {
324 StringBuilder where = new StringBuilder();
325 boolean firstTime = true;
326 while (!mDownloadCursor.isAfterLast()) {
327 int status = mDownloadCursor.getInt(mStatusColumnId);
328 if (!Downloads.isStatusCompleted(status)) {
332 where.append(" OR ");
335 where.append(Downloads._ID);
336 where.append(" = '");
337 where.append(mDownloadCursor.getLong(mIdColumnId));
340 mDownloadCursor.moveToNext();
343 getContentResolver().delete(Downloads.CONTENT_URI,
344 where.toString(), null);
349 private int getClearableCount() {
351 if (mDownloadCursor.moveToFirst()) {
352 while (!mDownloadCursor.isAfterLast()) {
353 int status = mDownloadCursor.getInt(mStatusColumnId);
354 if (Downloads.isStatusCompleted(status)) {
357 mDownloadCursor.moveToNext();
364 * Clear all stopped downloads, ie canceled (though should not be
365 * there), error and success download items.
367 private void clearAllDownloads() {
368 if (mDownloadCursor.moveToFirst()) {
369 StringBuilder where = new StringBuilder();
370 boolean firstTime = true;
371 while (!mDownloadCursor.isAfterLast()) {
372 int status = mDownloadCursor.getInt(mStatusColumnId);
373 if (Downloads.isStatusCompleted(status)) {
377 where.append(" OR ");
380 where.append(Downloads._ID);
381 where.append(" = '");
382 where.append(mDownloadCursor.getLong(mIdColumnId));
385 mDownloadCursor.moveToNext();
388 getContentResolver().delete(Downloads.CONTENT_URI,
389 where.toString(), null);
395 * Open the content where the download db cursor currently is
397 private void openCurrentDownload() {
398 int filenameColumnId =
399 mDownloadCursor.getColumnIndexOrThrow(Downloads._DATA);
400 String filename = mDownloadCursor.getString(filenameColumnId);
401 int mimetypeColumnId =
402 mDownloadCursor.getColumnIndexOrThrow(Downloads.COLUMN_MIME_TYPE);
403 String mimetype = mDownloadCursor.getString(mimetypeColumnId);
404 Uri path = Uri.parse(filename);
405 // If there is no scheme, then it must be a file
406 if (path.getScheme() == null) {
407 path = Uri.fromFile(new File(filename));
409 Intent intent = new Intent(Intent.ACTION_VIEW);
410 intent.setDataAndType(path, mimetype);
411 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
413 startActivity(intent);
414 } catch (ActivityNotFoundException ex) {
415 new AlertDialog.Builder(this)
416 .setTitle(R.string.download_failed_generic_dlg_title)
417 .setIcon(R.drawable.ssl_icon)
418 .setMessage(R.string.download_no_application)
419 .setPositiveButton(R.string.ok, null)
426 * @see android.widget.AdapterView.OnItemClickListener#onItemClick(android.widget.AdapterView, android.view.View, int, long)
428 public void onItemClick(AdapterView parent, View view, int position,
430 // Open the selected item
431 mDownloadCursor.moveToPosition(position);
433 hideCompletedDownload();
435 int status = mDownloadCursor.getInt(mStatusColumnId);
436 if (Downloads.isStatusSuccess(status)) {
437 // Open it if it downloaded successfully
438 openCurrentDownload();
440 // Check to see if there is an error.
446 * hides the notification for the download pointed by mDownloadCursor
447 * if the download has completed.
449 private void hideCompletedDownload() {
450 int status = mDownloadCursor.getInt(mStatusColumnId);
452 int visibilityColumn = mDownloadCursor.getColumnIndexOrThrow(Downloads.COLUMN_VISIBILITY);
453 int visibility = mDownloadCursor.getInt(visibilityColumn);
455 if (Downloads.isStatusCompleted(status) &&
456 visibility == Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) {
457 ContentValues values = new ContentValues();
458 values.put(Downloads.COLUMN_VISIBILITY, Downloads.VISIBILITY_VISIBLE);
459 getContentResolver().update(
460 ContentUris.withAppendedId(Downloads.CONTENT_URI,
461 mDownloadCursor.getLong(mIdColumnId)), values, null, null);