Merge "Update mdpi resources with 2.0 style icons"
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/res/drawable-hdpi/arcs.png b/res/drawable-hdpi/arcs.png
new file mode 100644
index 0000000..9555a9f
--- /dev/null
+++ b/res/drawable-hdpi/arcs.png
Binary files differ
diff --git a/res/drawable-hdpi/textfield_voice_search.9.png b/res/drawable-hdpi/textfield_voice_search.9.png
index a1d59c6..d988493 100644
--- a/res/drawable-hdpi/textfield_voice_search.9.png
+++ b/res/drawable-hdpi/textfield_voice_search.9.png
Binary files differ
diff --git a/res/drawable-mdpi/arcs.png b/res/drawable-mdpi/arcs.png
new file mode 100644
index 0000000..5e0d949
--- /dev/null
+++ b/res/drawable-mdpi/arcs.png
Binary files differ
diff --git a/res/drawable-mdpi/textfield_voice_search.9.png b/res/drawable-mdpi/textfield_voice_search.9.png
index b56eaa2..5cf6592 100644
--- a/res/drawable-mdpi/textfield_voice_search.9.png
+++ b/res/drawable-mdpi/textfield_voice_search.9.png
Binary files differ
diff --git a/res/drawable/title_voice.xml b/res/drawable/title_voice.xml
new file mode 100644
index 0000000..0bb1ad3
--- /dev/null
+++ b/res/drawable/title_voice.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true"
+ android:drawable="@*android:drawable/textfield_pressed" />
+ <item android:state_pressed="false"
+ android:drawable="@drawable/textfield_voice_search" />
+</selector>
diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java
index 8d471a2..ebb5246 100644
--- a/src/com/android/browser/BrowserActivity.java
+++ b/src/com/android/browser/BrowserActivity.java
@@ -479,6 +479,15 @@
|| MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
|| Intent.ACTION_WEB_SEARCH.equals(action)
|| activateVoiceSearch) {
+ if (current.isInVoiceSearchMode()) {
+ String title = current.getVoiceDisplayTitle();
+ if (title != null && title.equals(intent.getStringExtra(
+ SearchManager.QUERY))) {
+ // The user submitted the same search as the last voice
+ // search, so do nothing.
+ return;
+ }
+ }
// If this was a search request (e.g. search query directly typed into the address bar),
// pass it on to the default web search provider.
if (handleWebSearchIntent(intent)) {
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;
+ }
+}
diff --git a/src/com/android/browser/TitleBar.java b/src/com/android/browser/TitleBar.java
index 743af9b..14ac2ae 100644
--- a/src/com/android/browser/TitleBar.java
+++ b/src/com/android/browser/TitleBar.java
@@ -32,6 +32,10 @@
import android.os.Handler;
import android.os.Message;
import android.speech.RecognizerIntent;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.style.ImageSpan;
import android.util.TypedValue;
import android.view.ContextMenu;
import android.view.LayoutInflater;
@@ -69,6 +73,7 @@
private boolean mInVoiceMode;
private Drawable mVoiceModeBackground;
private Drawable mNormalBackground;
+ private ImageSpan mArcsSpan;
private static int LONG_PRESS = 1;
@@ -113,8 +118,10 @@
mStopDrawable = resources.getDrawable(R.drawable.ic_btn_stop_v2);
mBookmarkDrawable = mRtButton.getDrawable();
mVoiceModeBackground = resources.getDrawable(
- R.drawable.textfield_voice_search);
+ R.drawable.title_voice);
mNormalBackground = mTitleBg.getBackground();
+ mArcsSpan = new ImageSpan(context, R.drawable.arcs,
+ ImageSpan.ALIGN_BASELINE);
}
private class MyHandler extends Handler {
@@ -193,7 +200,7 @@
mHandler.removeMessages(LONG_PRESS);
if (mInVoiceMode) {
mBrowserActivity.showVoiceSearchResults(
- mTitle.getText().toString());
+ mTitle.getText().toString().trim());
} else {
mBrowserActivity.onSearchRequested();
}
@@ -236,6 +243,7 @@
if (mInVoiceMode) {
rightButtonDrawable = mVoiceDrawable;
titleDrawable = mVoiceModeBackground;
+ mTitle.setEllipsize(null);
} else {
titleDrawable = mNormalBackground;
if (mInLoad) {
@@ -243,7 +251,9 @@
} else {
rightButtonDrawable = mBookmarkDrawable;
}
+ mTitle.setEllipsize(TextUtils.TruncateAt.END);
}
+ mTitle.setSingleLine(!mInVoiceMode);
mTitleBg.setBackgroundDrawable(titleDrawable);
mRtButton.setImageDrawable(rightButtonDrawable);
}
@@ -300,7 +310,18 @@
if (title == null) {
mTitle.setText(R.string.title_bar_loading);
} else {
- mTitle.setText(title);
+ if (mInVoiceMode) {
+ // Add two spaces. The second one will be replaced with an
+ // image, and the first one will put space between it and the
+ // text
+ SpannableString spannable = new SpannableString(title + " ");
+ int end = spannable.length();
+ spannable.setSpan(mArcsSpan, end - 1, end,
+ Spanned.SPAN_MARK_POINT);
+ mTitle.setText(spannable);
+ } else {
+ mTitle.setText(title);
+ }
}
}