OSDN Git Service

am bcd20b3: Import clean-up in BrowserProvider.
[android-x86/packages-apps-Browser.git] / src / com / android / browser / BrowserDownloadPage.java
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.android.browser;
18
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;
43
44 import java.io.File;
45 import java.util.List;
46
47 /**
48  *  View showing the user's current browser downloads
49  */
50 public class BrowserDownloadPage extends Activity 
51         implements View.OnCreateContextMenuListener, OnItemClickListener {
52     
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;
60     
61     @Override 
62     public void onCreate(Bundle icicle) {
63         super.onCreate(icicle);
64         setContentView(R.layout.browser_downloads_page);
65         
66         setTitle(getText(R.string.download_title));
67
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);
74         
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}, 
81                 null, null);
82         
83         // only attach everything to the listbox if we can access
84         // the download database. Otherwise, just show it empty
85         if (mDownloadCursor != null) {
86             mStatusColumnId = 
87                     mDownloadCursor.getColumnIndexOrThrow(Downloads.COLUMN_STATUS);
88             mIdColumnId =
89                     mDownloadCursor.getColumnIndexOrThrow(Downloads._ID);
90             mTitleColumnId = 
91                     mDownloadCursor.getColumnIndexOrThrow(Downloads.COLUMN_TITLE);
92             
93             // Create a list "controller" for the data
94             mDownloadAdapter = new BrowserDownloadAdapter(this, 
95                     R.layout.browser_download_item, mDownloadCursor);
96             
97             mListView.setAdapter(mDownloadAdapter);
98             mListView.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
99             mListView.setOnCreateContextMenuListener(this);
100             mListView.setOnItemClickListener(this);
101             
102             Intent intent = getIntent();
103             if (intent != null && intent.getData() != null) {
104                 int position = checkStatus(
105                         ContentUris.parseId(intent.getData()));
106                 if (position >= 0) {
107                     mListView.setSelection(position);
108                 }
109             }
110         }
111     }
112         
113     @Override
114     public boolean onCreateOptionsMenu(Menu menu) {
115         if (mDownloadCursor != null) {
116             MenuInflater inflater = getMenuInflater();
117             inflater.inflate(R.menu.downloadhistory, menu);
118         }
119         return true;
120     }
121     
122     @Override
123     public boolean onPrepareOptionsMenu(Menu menu) {
124         boolean showCancel = getCancelableCount() > 0;
125         menu.findItem(R.id.download_menu_cancel_all).setEnabled(showCancel);
126         
127         boolean showClear = getClearableCount() > 0;
128         menu.findItem(R.id.download_menu_clear_all).setEnabled(showClear);
129         return super.onPrepareOptionsMenu(menu);
130     }
131     
132     @Override
133     public boolean onOptionsItemSelected(MenuItem item) {
134         switch (item.getItemId()) {
135             case R.id.download_menu_cancel_all:
136                 promptCancelAll();
137                 return true;
138                 
139             case R.id.download_menu_clear_all:
140                 promptClearList();
141                 return true;
142         }
143         return false;
144     }
145
146     @Override
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();
153                 return true;
154                 
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);
160                 return true;
161         }
162         return false;
163     }
164
165     @Override
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));
174             
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);
181             } else {
182                 inflater.inflate(R.menu.downloadhistorycontextrunning, menu);
183             }
184         }
185     }
186
187     /**
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
192      */
193     int checkStatus(final long id) {
194         int position = -1;
195         for (mDownloadCursor.moveToFirst(); !mDownloadCursor.isAfterLast(); 
196                 mDownloadCursor.moveToNext()) {
197             if (id == mDownloadCursor.getLong(mIdColumnId)) {
198                 position = mDownloadCursor.getPosition();
199                 break;
200             }
201             
202         }
203         if (!mDownloadCursor.isAfterLast()) {
204             int status = mDownloadCursor.getInt(mStatusColumnId);
205             if (!Downloads.isStatusError(status)) {
206                 return position;
207             }
208             
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);
213                 }
214                 String msg = getString(R.string.download_file_error_dlg_msg, 
215                         title);
216                 new AlertDialog.Builder(this)
217                         .setTitle(R.string.download_file_error_dlg_title)
218                         .setIcon(android.R.drawable.ic_popup_disk_full)
219                         .setMessage(msg)
220                         .setPositiveButton(R.string.ok, null)
221                         .setNegativeButton(R.string.retry, 
222                                 new DialogInterface.OnClickListener() {
223                                     public void onClick(DialogInterface dialog, 
224                                             int whichButton) {
225                                         resumeDownload(id);
226                                     }
227                                 })
228                         .show();
229             } else {
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)
235                         .show();
236             }
237         }
238         return position;
239     }
240     
241     /**
242      * Resume a given download
243      * @param id Row id of the download to resume
244      */
245     private void resumeDownload(final long id) {
246         // the relevant functionality doesn't exist in the download manager
247     }
248     
249     /**
250      * Prompt the user if they would like to clear the download history
251      */
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, 
260                                    int whichButton) {
261                                clearAllDownloads();
262                            }
263                        })
264                 .setNegativeButton(R.string.cancel, null)
265                 .show();
266     }
267     
268     /**
269      * Return the number of items in the list that can be canceled.
270      * @return count
271      */
272     private int getCancelableCount() {
273         // Count the number of items that will be canceled.
274         int count = 0;
275         if (mDownloadCursor != null) {
276             for (mDownloadCursor.moveToFirst(); !mDownloadCursor.isAfterLast(); 
277                     mDownloadCursor.moveToNext()) {
278                 int status = mDownloadCursor.getInt(mStatusColumnId);
279                 if (!Downloads.isStatusCompleted(status)) {
280                     count++;
281                 }
282             }
283         }
284         
285         return count;
286     }
287     
288     /**
289      * Prompt the user if they would like to clear the download history
290      */
291     private void promptCancelAll() {
292         int count = getCancelableCount();
293         
294         // If there is nothing to do, just return
295         if (count == 0) {
296             return;
297         }
298         
299         // Don't show the dialog if there is only one download
300         if (count == 1) {
301             cancelAllDownloads();
302             return;
303         }
304         String msg = 
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)
309                 .setMessage(msg)
310                 .setPositiveButton(R.string.ok, 
311                         new DialogInterface.OnClickListener() {
312                             public void onClick(DialogInterface dialog, 
313                                     int whichButton) {
314                                 cancelAllDownloads();
315                             }
316                         })
317                  .setNegativeButton(R.string.cancel, null)
318                  .show();
319     }
320     
321     /**
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.
325      */
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)) {
333                     if (firstTime) {
334                         firstTime = false;
335                     } else {
336                         where.append(" OR ");
337                     }
338                     where.append("( ");
339                     where.append(Downloads._ID);
340                     where.append(" = '");
341                     where.append(mDownloadCursor.getLong(mIdColumnId));
342                     where.append("' )");
343                 }
344                 mDownloadCursor.moveToNext();
345             }
346             if (!firstTime) {
347                 getContentResolver().delete(Downloads.CONTENT_URI,
348                         where.toString(), null);
349             }
350         }
351     }
352     
353     private int getClearableCount() {
354         int count = 0;
355         if (mDownloadCursor.moveToFirst()) {
356             while (!mDownloadCursor.isAfterLast()) {
357                 int status = mDownloadCursor.getInt(mStatusColumnId);
358                 if (Downloads.isStatusCompleted(status)) {
359                     count++;
360                 }
361                 mDownloadCursor.moveToNext();
362             }
363         }
364         return count;
365     }
366     
367     /**
368      * Clear all stopped downloads, ie canceled (though should not be
369      * there), error and success download items.
370      */
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)) {
378                     if (firstTime) {
379                         firstTime = false;
380                     } else {
381                         where.append(" OR ");
382                     }
383                     where.append("( ");
384                     where.append(Downloads._ID);
385                     where.append(" = '");
386                     where.append(mDownloadCursor.getLong(mIdColumnId));
387                     where.append("' )");
388                 }
389                 mDownloadCursor.moveToNext();
390             }
391             if (!firstTime) {
392                 getContentResolver().delete(Downloads.CONTENT_URI,
393                         where.toString(), null);
394             }
395         }
396     }
397     
398     /**
399      * Open the content where the download db cursor currently is
400      */
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));
412         }
413         Intent intent = new Intent(Intent.ACTION_VIEW);
414         intent.setDataAndType(path, mimetype);
415         intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
416         try {
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)
424                     .show();
425         }
426     }
427
428     /*
429      * (non-Javadoc)
430      * @see android.widget.AdapterView.OnItemClickListener#onItemClick(android.widget.AdapterView, android.view.View, int, long)
431      */
432     public void onItemClick(AdapterView parent, View view, int position, 
433             long id) {
434         // Open the selected item
435         mDownloadCursor.moveToPosition(position);
436         
437         hideCompletedDownload();
438
439         int status = mDownloadCursor.getInt(mStatusColumnId);
440         if (Downloads.isStatusSuccess(status)) {
441             // Open it if it downloaded successfully
442             openCurrentDownload();
443         } else {
444             // Check to see if there is an error.
445             checkStatus(id);
446         }
447     }
448     
449     /**
450      * hides the notification for the download pointed by mDownloadCursor
451      * if the download has completed.
452      */
453     private void hideCompletedDownload() {
454         int status = mDownloadCursor.getInt(mStatusColumnId);
455
456         int visibilityColumn = mDownloadCursor.getColumnIndexOrThrow(Downloads.COLUMN_VISIBILITY);
457         int visibility = mDownloadCursor.getInt(visibilityColumn);
458
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);
466         }
467     }
468 }