Merge "Launch intents to open/delete downloads, and handle them in the browser."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 6ef43fa..22c721d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -33,6 +33,7 @@
<uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS"/>
<uses-permission android:name="com.android.browser.permission.WRITE_HISTORY_BOOKMARKS"/>
<uses-permission android:name="android.permission.BACKUP_DATA" />
+ <uses-permission android:name="android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS" />
<application android:name="Browser"
android:label="@string/application_name"
@@ -194,6 +195,13 @@
<!-- Makes .BrowserActivity the search target for any activity in Browser -->
<meta-data android:name="android.app.default_searchable" android:value=".BrowserActivity" />
+ <receiver android:name=".OpenDownloadReceiver">
+ <intent-filter>
+ <action android:name="android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED"/>
+ <action android:name="android.intent.action.DELETE"/>
+ <data android:scheme="content" android:mimeType="vnd.android.cursor.item/download"/>
+ </intent-filter>
+ </receiver>
</application>
</manifest>
diff --git a/src/com/android/browser/BrowserDownloadAdapter.java b/src/com/android/browser/BrowserDownloadAdapter.java
index 2a3b69c..0f8f721 100644
--- a/src/com/android/browser/BrowserDownloadAdapter.java
+++ b/src/com/android/browser/BrowserDownloadAdapter.java
@@ -47,7 +47,6 @@
*/
public class BrowserDownloadAdapter extends DateSortedExpandableListAdapter {
- private int mFilenameColumnId;
private int mTitleColumnId;
private int mDescColumnId;
private int mStatusColumnId;
@@ -58,7 +57,6 @@
public BrowserDownloadAdapter(Context context, Cursor c, int index) {
super(context, c, index);
- mFilenameColumnId = c.getColumnIndexOrThrow(Downloads.Impl._DATA);
mTitleColumnId = c.getColumnIndexOrThrow(Downloads.Impl.COLUMN_TITLE);
mDescColumnId = c.getColumnIndexOrThrow(Downloads.Impl.COLUMN_DESCRIPTION);
mStatusColumnId = c.getColumnIndexOrThrow(Downloads.Impl.COLUMN_STATUS);
@@ -112,14 +110,7 @@
TextView tv = (TextView) convertView.findViewById(R.id.download_title);
String title = getString(mTitleColumnId);
if (title == null) {
- String fullFilename = getString(mFilenameColumnId);
- if (fullFilename == null) {
- title = r.getString(R.string.download_unknown_filename);
- } else {
- // We have a filename, so we can build a title from that
- title = Downloads.Impl.createTitleFromFilename(context, fullFilename,
- getLong(0));
- }
+ title = r.getString(R.string.download_unknown_filename);
}
tv.setText(title);
diff --git a/src/com/android/browser/BrowserDownloadPage.java b/src/com/android/browser/BrowserDownloadPage.java
index bbc804d..5cace19 100644
--- a/src/com/android/browser/BrowserDownloadPage.java
+++ b/src/com/android/browser/BrowserDownloadPage.java
@@ -26,11 +26,9 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
-import android.database.DatabaseUtils;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Downloads;
-import android.provider.MediaStore;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
@@ -68,11 +66,14 @@
mListView = (ExpandableListView) findViewById(android.R.id.list);
mListView.setEmptyView(findViewById(R.id.empty));
mDownloadCursor = managedQuery(Downloads.Impl.CONTENT_URI,
- new String [] {"_id", Downloads.Impl.COLUMN_TITLE, Downloads.Impl.COLUMN_STATUS,
- Downloads.Impl.COLUMN_TOTAL_BYTES, Downloads.Impl.COLUMN_CURRENT_BYTES,
- Downloads.Impl._DATA, Downloads.Impl.COLUMN_DESCRIPTION,
- Downloads.Impl.COLUMN_MIME_TYPE, Downloads.Impl.COLUMN_LAST_MODIFICATION,
- Downloads.Impl.COLUMN_VISIBILITY},
+ new String [] {Downloads.Impl._ID, Downloads.Impl.COLUMN_TITLE,
+ Downloads.Impl.COLUMN_STATUS, Downloads.Impl.COLUMN_TOTAL_BYTES,
+ Downloads.Impl.COLUMN_CURRENT_BYTES,
+ Downloads.Impl.COLUMN_DESCRIPTION,
+ Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE,
+ Downloads.Impl.COLUMN_LAST_MODIFICATION,
+ Downloads.Impl.COLUMN_VISIBILITY,
+ Downloads.Impl.COLUMN_MIME_TYPE},
null, Downloads.Impl.COLUMN_LAST_MODIFICATION + " DESC");
// only attach everything to the listbox if we can access
@@ -108,7 +109,7 @@
}
}
}
-
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
if (mDownloadCursor != null) {
@@ -144,28 +145,6 @@
Downloads.Impl.CONTENT_URI, id), null, null);
}
- /**
- * Remove the file from the SD card
- * @param filename Name of the file to delete.
- * @param mimetype Mimetype of the file to delete.
- * @return boolean True on success, false on failure.
- */
- private boolean deleteFile(String filename, String mimetype) {
- Uri uri;
- if (mimetype.startsWith("image")) {
- uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
- } else if (mimetype.startsWith("audio")) {
- uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
- } else if (mimetype.startsWith("video")) {
- uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
- } else {
- File file = new File(filename);
- return file.delete();
- }
- return getContentResolver().delete(uri, MediaStore.MediaColumns.DATA
- + " = " + DatabaseUtils.sqlEscapeString(filename), null) > 0;
- }
-
@Override
public boolean onContextItemSelected(MenuItem item) {
if (!mDownloadAdapter.moveCursorToPackedChildPosition(
@@ -175,31 +154,20 @@
switch (item.getItemId()) {
case R.id.download_menu_open:
hideCompletedDownload();
- openCurrentDownload();
+ openOrDeleteCurrentDownload(false);
return true;
case R.id.download_menu_delete:
- int filenameColumnId =
- mDownloadCursor.getColumnIndexOrThrow(Downloads.Impl._DATA);
- final String filename = mDownloadCursor.getString(
- filenameColumnId);
- int mimetypeColumnId = mDownloadCursor.getColumnIndexOrThrow(
- Downloads.Impl.COLUMN_MIME_TYPE);
- final String mimetype = mDownloadCursor.getString(
- mimetypeColumnId);
- final long id = mDownloadCursor.getLong(mIdColumnId);
new AlertDialog.Builder(this)
.setTitle(R.string.download_delete_file)
.setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(filename)
+ .setMessage(mDownloadCursor.getString(mTitleColumnId))
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int whichButton) {
- if (deleteFile(filename, mimetype)) {
- clearFromDownloads(id);
- }
+ openOrDeleteCurrentDownload(true);
}
})
.show();
@@ -392,33 +360,22 @@
}
/**
- * Open the content where the download db cursor currently is
+ * Open or delete content where the download db cursor currently is. Sends
+ * an Intent to perform the action.
+ * @param delete If true, delete the content. Otherwise open it.
*/
- private void openCurrentDownload() {
- int filenameColumnId =
- mDownloadCursor.getColumnIndexOrThrow(Downloads.Impl._DATA);
- String filename = mDownloadCursor.getString(filenameColumnId);
- int mimetypeColumnId =
- mDownloadCursor.getColumnIndexOrThrow(Downloads.Impl.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_no_application_title)
- .setIcon(R.drawable.ssl_icon)
- .setMessage(R.string.download_no_application)
- .setPositiveButton(R.string.ok, null)
- .show();
- }
+ private void openOrDeleteCurrentDownload(boolean delete) {
+ int packageColumnId = mDownloadCursor.getColumnIndexOrThrow(
+ Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE);
+ String packageName = mDownloadCursor.getString(packageColumnId);
+ Intent intent = new Intent(delete ? Intent.ACTION_DELETE
+ : Downloads.Impl.ACTION_NOTIFICATION_CLICKED);
+ Uri contentUri = ContentUris.withAppendedId(
+ Downloads.Impl.CONTENT_URI,
+ mDownloadCursor.getLong(mIdColumnId));
+ intent.setData(contentUri);
+ intent.setPackage(packageName);
+ sendBroadcast(intent);
}
@Override
@@ -433,7 +390,7 @@
int status = mDownloadCursor.getInt(mStatusColumnId);
if (Downloads.Impl.isStatusSuccess(status)) {
// Open it if it downloaded successfully
- openCurrentDownload();
+ openOrDeleteCurrentDownload(false);
} else {
// Check to see if there is an error.
checkStatus(id);
diff --git a/src/com/android/browser/OpenDownloadReceiver.java b/src/com/android/browser/OpenDownloadReceiver.java
new file mode 100644
index 0000000..498afc0
--- /dev/null
+++ b/src/com/android/browser/OpenDownloadReceiver.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2010 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.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.net.Uri;
+import android.provider.Downloads;
+import android.provider.MediaStore;
+import android.widget.Toast;
+
+import java.io.File;
+
+/**
+ * This {@link BroadcastReceiver} handles {@link Intent}s to open and delete
+ * files downloaded by the Browser.
+ */
+public class OpenDownloadReceiver extends BroadcastReceiver {
+ public void onReceive(Context context, Intent intent) {
+ ContentResolver cr = context.getContentResolver();
+ Uri data = intent.getData();
+ Cursor cursor = cr.query(data,
+ new String[] { Downloads.Impl._ID, Downloads.Impl._DATA,
+ Downloads.Impl.COLUMN_MIME_TYPE }, null, null, null);
+ if (cursor.moveToFirst()) {
+ String filename = cursor.getString(1);
+ String mimetype = cursor.getString(2);
+ String action = intent.getAction();
+ if (Downloads.ACTION_NOTIFICATION_CLICKED.equals(action)) {
+ Intent launchIntent = new Intent(Intent.ACTION_VIEW);
+ 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));
+ }
+ launchIntent.setDataAndType(path, mimetype);
+ launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ context.startActivity(launchIntent);
+ } catch (ActivityNotFoundException ex) {
+ Toast.makeText(context,
+ R.string.download_no_application_title,
+ Toast.LENGTH_LONG).show();
+ }
+ } else if (Intent.ACTION_DELETE.equals(action)) {
+ if (deleteFile(cr, filename, mimetype)) {
+ cr.delete(data, null, null);
+ }
+ }
+ }
+ cursor.close();
+ }
+
+ /**
+ * Remove the file from the SD card
+ * @param cr ContentResolver used to delete the file.
+ * @param filename Name of the file to delete.
+ * @param mimetype Mimetype of the file to delete.
+ * @return boolean True on success, false on failure.
+ */
+ // FIXME: Once there are receivers in other packages to delete downloaded
+ // files, this should be moved to a common place so mutiple packages can
+ // share the code.
+ private boolean deleteFile(ContentResolver cr, String filename,
+ String mimetype) {
+ Uri uri;
+ if (mimetype.startsWith("image")) {
+ uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+ } else if (mimetype.startsWith("audio")) {
+ uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+ } else if (mimetype.startsWith("video")) {
+ uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
+ } else {
+ File file = new File(filename);
+ return file.delete();
+ }
+ return cr.delete(uri, MediaStore.MediaColumns.DATA + " = "
+ + DatabaseUtils.sqlEscapeString(filename), null) > 0;
+ }
+}