Merge "Swapping in updated Browser icons, in place" into honeycomb
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 5b844e0..9ceaf82 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -223,6 +223,13 @@
<action android:name="android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED"/>
</intent-filter>
</receiver>
+
+ <!-- For custom home pages (like most visited) -->
+ <provider
+ android:name=".homepages.HomeProvider"
+ android:authorities="com.android.browser.home"
+ android:readPermission="com.android.browser.permission.READ_HISTORY_BOOKMARKS"
+ android:exported="false" />
</application>
</manifest>
diff --git a/res/drawable-mdpi/ic_pie_web.png b/res/drawable-mdpi/ic_pie_web.png
new file mode 100644
index 0000000..86e41ff
--- /dev/null
+++ b/res/drawable-mdpi/ic_pie_web.png
Binary files differ
diff --git a/res/layout/new_folder_layout.xml b/res/layout/new_folder_layout.xml
index 4ce0ade..ecc730f 100644
--- a/res/layout/new_folder_layout.xml
+++ b/res/layout/new_folder_layout.xml
@@ -27,12 +27,29 @@
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeight"
android:src="@drawable/ic_folder_bookmark_widget_holo_dark" />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@*android:drawable/edit_text_holo_dark"
+ android:gravity="center_vertical"
+ android:paddingBottom="5dip"
+ android:orientation="horizontal">
<EditText
android:id="@+id/folder_namer"
- android:layout_width="match_parent"
+ android:layout_width="0dip"
android:layout_height="wrap_content"
+ android:layout_weight="1"
android:textAppearance="?android:attr/textAppearanceMedium"
+ android:background="@null"
android:gravity="center_vertical"
android:paddingLeft="6dip"
- android:minHeight="?android:attr/listPreferredItemHeight" />
+ />
+ <ImageView
+ android:id="@+id/close"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:src="@drawable/ic_tab_close"
+ />
+ </LinearLayout>
</LinearLayout>
diff --git a/res/menu/bookmarkscontext.xml b/res/menu/bookmarkscontext.xml
index 4d7ec7a..3a13b9a 100644
--- a/res/menu/bookmarkscontext.xml
+++ b/res/menu/bookmarkscontext.xml
@@ -36,6 +36,8 @@
</group>
<group android:id="@+id/FOLDER_CONTEXT_MENU"
android:visible="false">
+ <item android:id="@+id/new_window_context_menu_id"
+ android:title="@string/open_all_in_new_window"/>
<item android:id="@+id/edit_context_menu_id"
android:title="@string/edit_folder"/>
<item android:id="@+id/delete_context_menu_id"
diff --git a/res/raw/most_visited.ktpl b/res/raw/most_visited.ktpl
new file mode 100644
index 0000000..04b9eee
--- /dev/null
+++ b/res/raw/most_visited.ktpl
@@ -0,0 +1,85 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html>
+<head>
+<title><%@ string/new_tab %></title>
+<meta name="viewport" content="width=device-width; initial-scale=1.0;" />
+
+<style type="text/css">
+
+* {
+ padding: 0;
+ margin: 0;
+}
+
+body {
+ text-align: center;
+ margin: 16px auto;
+ padding: 0 8px 0 8px;
+ max-width: <%@ dimen/mv_max_width %>px;
+}
+
+#most_visited h3 {
+ text-align: center;
+ padding: 0;
+ margin: 5px 0 5px 0px;
+}
+
+.thumbwrap li {
+ display: inline-block;
+ margin: 0 7px 15px 7px;
+ padding: 0;
+}
+
+@media all and (orientation:portrait) {
+.thumbwrap li {
+ width: <%@ dimen/mv_item_width_portrait %>px;
+}
+}
+
+@media all and (orientation:landscape) {
+.thumbwrap li {
+ width: <%@ dimen/mv_item_width %>px;
+}
+}
+
+.thumbwrap a {
+ display: block;
+ text-decoration: none;
+ color: #000;
+}
+
+.thumbwrap img {
+ border: <%@ dimen/mv_border_width %>px solid #e0e0e0;
+ border-radius: 5px;
+ width: 100%;
+}
+
+.thumbwrap .caption {
+ margin-top: 2px;
+ margin-left: 4px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: block;
+ font-size: .8em;
+ text-align: left;
+}
+
+</style>
+
+</head>
+<body>
+ <h3><%@ string/tab_most_visited %></h3>
+ <ul class="thumbwrap">
+ <%{ most_visited %>
+ <li>
+ <a href="<%= url %>">
+ <img class="wrimg" src="<%= thumbnail %>" />
+ <span class="caption"><%= title %></span>
+ </a>
+ </li>
+ <%} most_visited %>
+ </ul>
+</body>
+</html>
diff --git a/res/values-es-rUS-xlarge/strings.xml b/res/values-es-rUS-xlarge/strings.xml
index 508f3fe..ed820a1 100644
--- a/res/values-es-rUS-xlarge/strings.xml
+++ b/res/values-es-rUS-xlarge/strings.xml
@@ -50,7 +50,7 @@
<!-- XL -->
<string name="pref_content_autofit" msgid="152292940099720429">"Ajuste automático de páginas"</string>
<!-- XL -->
- <string name="pref_autofill_profile_editor_summary" msgid="6606678927956330022">"Configurar & administrar datos para los formularios de autollenado"</string>
+ <string name="pref_autofill_profile_editor_summary" msgid="6606678927956330022">"Configurar & administrar datos para los formularios de autocompletado"</string>
<!-- XL -->
<string name="autofill_profile_editor_heading" msgid="376355900106687672">"La función Autocompletar utilizará tu perfil para ayudarte a completar los formularios web con solo un clic."</string>
<!-- XL -->
@@ -82,7 +82,7 @@
<!-- XL -->
<string name="pref_content_load_page" msgid="7460666469137362825">"Abrir las páginas en visión general"</string>
<!-- XL -->
- <!-- outdated translation 3239395481036882698 --> <string name="pref_extras_title" msgid="7223601187104530963">"Configuración avanzada"</string>
+ <string name="pref_extras_title" msgid="7223601187104530963">"Avanzado"</string>
<!-- XL -->
<string name="pref_extras_reset_default" msgid="113675796607112935">"Restablecer a la forma predeterminada"</string>
<!-- XL -->
@@ -96,7 +96,23 @@
<!-- XL -->
<string name="max_tabs_warning" msgid="1283136201153746764">"No hay más etiquetas disponibles"</string>
<!-- XL -->
- <string name="pref_lab_title" msgid="209824994106188072">"Lab"</string>
+ <string name="pref_general_title" msgid="1229916674669616147">"General"</string>
+ <!-- XL -->
+ <string name="pref_general_sync_title" msgid="1799321350681060075">"Sincronización"</string>
+ <!-- XL -->
+ <string name="pref_general_autofill_title" msgid="3214478293686374847">"Autocompletar"</string>
+ <!-- XL -->
+ <string name="pref_privacy_security_title" msgid="1547485785692120937">"Privacidad & Seguridad"</string>
+ <!-- XL -->
+ <string name="pref_privacy_cookies_title" msgid="2009543275661191347">"Cookies"</string>
+ <!-- XL -->
+ <string name="pref_privacy_formdata_title" msgid="808174611258405509">"Datos del formulario"</string>
+ <!-- XL -->
+ <string name="pref_privacy_location_title" msgid="4128439505504300548">"Ubicación"</string>
+ <!-- XL -->
+ <string name="pref_security_passwords_title" msgid="6502262261189522404">"Contraseñas"</string>
+ <!-- XL -->
+ <string name="pref_extras_reset_default_title" msgid="8689952816215143856">"Restablecer configuraciones predeterminadas"</string>
<!-- XL -->
<string name="pref_lab_quick_controls" msgid="1617774508416113544">"Controles rápidos"</string>
<!-- XL -->
diff --git a/res/values-xlarge/dimensions.xml b/res/values-xlarge/dimensions.xml
index 5b86c86..9f5a602 100644
--- a/res/values-xlarge/dimensions.xml
+++ b/res/values-xlarge/dimensions.xml
@@ -14,4 +14,9 @@
<dimen name="bookmarkThumbnailWidth">180dip</dimen>
<dimen name="bookmarkThumbnailHeight">120dip</dimen>
<dimen name="favicon_padded_size">24dip</dimen>
+ <!-- For the most visited page -->
+ <dimen name="mv_max_width">1010dp</dimen>
+ <dimen name="mv_item_width">231dp</dimen>
+ <dimen name="mv_item_width_portrait">213dp</dimen>
+ <dimen name="mv_border_width">3dp</dimen>
</resources>
diff --git a/res/values-xlarge/strings.xml b/res/values-xlarge/strings.xml
index 856b2b9..8dcabe2 100644
--- a/res/values-xlarge/strings.xml
+++ b/res/values-xlarge/strings.xml
@@ -25,4 +25,6 @@
<!-- Context Menu item to open the currently selected link in a new
window. [CHAR LIMIT=30] -->
<string name="contextmenu_openlink_newwindow">Open in new tab</string>
+ <!-- Context menu item to open every bookmark in a folder in new tabs [CHAR LIMIT=50] -->
+ <string name="open_all_in_new_window">Open all in new tabs</string>
</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index cb1b754..834bead 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -29,7 +29,7 @@
<color name="bookmarkWidgetDivider">#383847</color>
<color name="bookmarkWidgetItemBackground">#2b2b3c</color>
<color name="bookmarkWidgetFolderBackground">#A0383847</color>
- <color name="qc_slice_normal">#C0A0A0A0</color>
- <color name="qc_slice_active">#C02090FF</color>
+ <color name="qc_slice_normal">#E0A0A0A0</color>
+ <color name="qc_slice_active">#E02090FF</color>
<color name="bookmarkWidgetFaviconBackground">#23ffffff</color>
</resources>
diff --git a/res/values/dimensions.xml b/res/values/dimensions.xml
index 03127dd..d50ce13 100644
--- a/res/values/dimensions.xml
+++ b/res/values/dimensions.xml
@@ -33,4 +33,9 @@
<dimen name="qc_slop">15dip</dimen>
<dimen name="bookmark_widget_thumb_size">32dip</dimen>
<dimen name="bookmark_widget_favicon_size">26dip</dimen>
+ <!-- For the most visited page -->
+ <dimen name="mv_max_width">830dp</dimen>
+ <dimen name="mv_item_width">96dp</dimen>
+ <dimen name="mv_item_width_portrait">96dp</dimen>
+ <dimen name="mv_border_width">3dp</dimen>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b8a45bf..4464370 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -204,6 +204,8 @@
<string name="delete_bookmark_warning">Bookmark \"<xliff:g id="bookmark">%s</xliff:g>\" will be deleted.</string>
<!-- Context Menu item to open the selected link in a new window -->
<string name="open_in_new_window">Open in new window</string>
+ <!-- Context menu item to open every bookmark in a folder in new windows [CHAR LIMIT=50] -->
+ <string name="open_all_in_new_window">Open all in new windows</string>
<!-- Menu item to open a dialog which allows the user to enter a url or do search-->
<string name="goto_dot">Go</string>
<!-- Menu item that opens up a new incognito tab. [CHAR LIMIT=30] -->
@@ -603,12 +605,17 @@
</string-array>
<string name="pref_default_text_encoding_default" translatable="false">Latin-1</string>
<!-- Title for lab settings [CHAR LIMIT=25] -->
- <string name="pref_lab_title">Lab</string>
+ <string name="pref_lab_title">Labs</string>
<!-- Title for lab quick controls feature [CHAR LIMIT=40] -->
<string name="pref_lab_quick_controls">Quick Controls</string>
<!-- Summary for lab quick controls feature [CHAR LIMIT=80] -->
<string name="pref_lab_quick_controls_summary">
Swipe thumb from left or right edge to access quick controls</string>
+ <!-- Title for lab "Most Visited" homepage feature [CHAR LIMIT=40] -->
+ <string name="pref_lab_most_visited_homepage">Most Visited Homepage</string>
+ <!-- Summary for lab "Most Visited" homepage feature [CHAR LIMIT=80] -->
+ <string name="pref_lab_most_visited_homepage_summary">
+ Sets your homepage to show the most visited pages.</string>
<!-- Title for a dialog displayed when the browser has a data connectivity
problem -->
<string name="browserFrameNetworkErrorLabel">Data connectivity problem</string>
diff --git a/res/xml/lab_preferences.xml b/res/xml/lab_preferences.xml
index 2168471..16a5169 100644
--- a/res/xml/lab_preferences.xml
+++ b/res/xml/lab_preferences.xml
@@ -23,4 +23,10 @@
android:title="@string/pref_lab_quick_controls"
android:summary="@string/pref_lab_quick_controls_summary" />
+ <CheckBoxPreference
+ android:key="use_most_visited_homepage"
+ android:defaultValue="false"
+ android:title="@string/pref_lab_most_visited_homepage"
+ android:summary="@string/pref_lab_most_visited_homepage_summary" />
+
</PreferenceScreen>
diff --git a/src/com/android/browser/AddBookmarkPage.java b/src/com/android/browser/AddBookmarkPage.java
index 9d8c73c..5e389ea 100644
--- a/src/com/android/browser/AddBookmarkPage.java
+++ b/src/com/android/browser/AddBookmarkPage.java
@@ -98,6 +98,7 @@
private View mDefaultView;
private View mFolderSelector;
private EditText mFolderNamer;
+ private View mFolderCancel;
private boolean mIsFolderNamerShowing;
private View mFolderNamerHolder;
private View mAddNewFolder;
@@ -243,6 +244,8 @@
} else {
finish();
}
+ } else if (v == mFolderCancel) {
+ completeOrCancelFolderNaming(true);
} else if (v == mAddNewFolder) {
setShowFolderNamer(true);
mFolderNamer.setText(R.string.new_folder);
@@ -286,6 +289,11 @@
public void onNothingSelected(AdapterView<?> parent) {
}
+ /**
+ * Finish naming a folder, and close the IME
+ * @param cancel If true, the new folder is not created. If false, the new
+ * folder is created and the user is taken inside it.
+ */
private void completeOrCancelFolderNaming(boolean cancel) {
if (!cancel && !TextUtils.isEmpty(mFolderNamer.getText())) {
String name = mFolderNamer.getText().toString();
@@ -588,6 +596,8 @@
mFolderNamerHolder = getLayoutInflater().inflate(R.layout.new_folder_layout, null);
mFolderNamer = (EditText) mFolderNamerHolder.findViewById(R.id.folder_namer);
mFolderNamer.setOnEditorActionListener(this);
+ mFolderCancel = mFolderNamerHolder.findViewById(R.id.close);
+ mFolderCancel.setOnClickListener(this);
mAddNewFolder = findViewById(R.id.add_new_folder);
mAddNewFolder.setOnClickListener(this);
diff --git a/src/com/android/browser/BrowserBookmarksPage.java b/src/com/android/browser/BrowserBookmarksPage.java
index 825da33..ea97c29 100644
--- a/src/com/android/browser/BrowserBookmarksPage.java
+++ b/src/com/android/browser/BrowserBookmarksPage.java
@@ -566,10 +566,43 @@
private void openInNewWindow(int position) {
if (mCallbacks != null) {
- mCallbacks.onOpenInNewWindow(mAdapter.getItem(position));
+ Cursor c = mAdapter.getItem(position);
+ boolean isFolder = c.getInt(BookmarksLoader.COLUMN_INDEX_IS_FOLDER) == 1;
+ if (isFolder) {
+ long id = c.getLong(BookmarksLoader.COLUMN_INDEX_ID);
+ new OpenAllInTabsTask(id).execute();
+ } else {
+ mCallbacks.onOpenInNewWindow(c);
+ }
}
}
+ class OpenAllInTabsTask extends AsyncTask<Void, Void, Cursor> {
+ long mFolderId;
+ public OpenAllInTabsTask(long id) {
+ mFolderId = id;
+ }
+
+ @Override
+ protected Cursor doInBackground(Void... params) {
+ Context c = getActivity();
+ if (c == null) return null;
+ return c.getContentResolver().query(BookmarkUtils.getBookmarksUri(c),
+ BookmarksLoader.PROJECTION, BrowserContract.Bookmarks.PARENT + "=?",
+ new String[] { Long.toString(mFolderId) }, null);
+ }
+
+ @Override
+ protected void onPostExecute(Cursor result) {
+ if (mCallbacks != null) {
+ while (result.moveToNext()) {
+ mCallbacks.onOpenInNewWindow(result);
+ }
+ }
+ }
+
+ }
+
private void editBookmark(int position) {
Intent intent = new Intent(getActivity(), AddBookmarkPage.class);
Cursor cursor = mAdapter.getItem(position);
diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java
index 267056e..88bd78a 100644
--- a/src/com/android/browser/BrowserSettings.java
+++ b/src/com/android/browser/BrowserSettings.java
@@ -17,6 +17,7 @@
package com.android.browser;
+import com.android.browser.homepages.HomeProvider;
import com.android.browser.search.SearchEngine;
import com.android.browser.search.SearchEngines;
@@ -119,6 +120,7 @@
// Lab settings
private boolean quickControls = false;
+ private boolean useMostVisitedHomepage = false;
// By default the error console is shown once the user navigates to about:debug.
// The setting can be then toggled from the settings menu.
@@ -171,6 +173,7 @@
public final static String PREF_USER_AGENT = "user_agent";
public final static String PREF_QUICK_CONTROLS = "enable_quick_controls";
+ public final static String PREF_MOST_VISITED_HOMEPAGE = "use_most_visited_homepage";
private static final String DESKTOP_USERAGENT = "Mozilla/5.0 (Macintosh; " +
"U; Intel Mac OS X 10_6_3; en-us) AppleWebKit/533.16 (KHTML, " +
@@ -197,7 +200,7 @@
public static final Uri RLZ_PROVIDER_URI = Uri.parse("content://" + RLZ_PROVIDER + "/");
// Set to true to enable some of the about:debug options
- public static final boolean DEV_BUILD = true;
+ public static final boolean DEV_BUILD = false;
private Controller mController;
@@ -278,6 +281,8 @@
// enable smooth transition for better performance during panning or
// zooming
s.setEnableSmoothTransition(true);
+ // disable content url access
+ s.setAllowContentAccess(false);
// HTML5 API flags
s.setAppCacheEnabled(b.appCacheEnabled);
@@ -496,6 +501,7 @@
}
quickControls = p.getBoolean(PREF_QUICK_CONTROLS, quickControls);
+ useMostVisitedHomepage = p.getBoolean(PREF_MOST_VISITED_HOMEPAGE, useMostVisitedHomepage);
// Only set these on startup if it is a dev build
if (DEV_BUILD) {
@@ -525,6 +531,9 @@
}
public String getHomePage() {
+ if (useMostVisitedHomepage) {
+ return HomeProvider.MOST_VISITED;
+ }
return homeUrl;
}
@@ -584,6 +593,10 @@
return quickControls;
}
+ public boolean useMostVisitedHomepage() {
+ return useMostVisitedHomepage;
+ }
+
public boolean showDebugSettings() {
return showDebugSettings;
}
@@ -847,6 +860,8 @@
update();
} else if (PREF_QUICK_CONTROLS.equals(key)) {
quickControls = p.getBoolean(PREF_QUICK_CONTROLS, quickControls);
+ } else if (PREF_MOST_VISITED_HOMEPAGE.equals(key)) {
+ useMostVisitedHomepage = p.getBoolean(PREF_MOST_VISITED_HOMEPAGE, useMostVisitedHomepage);
}
}
}
diff --git a/src/com/android/browser/Controller.java b/src/com/android/browser/Controller.java
index a5a6090..23594f2 100644
--- a/src/com/android/browser/Controller.java
+++ b/src/com/android/browser/Controller.java
@@ -733,7 +733,7 @@
// WebViewController
@Override
- public void onPageStarted(Tab tab, WebView view, String url, Bitmap favicon) {
+ public void onPageStarted(Tab tab, WebView view, Bitmap favicon) {
// We've started to load a new page. If there was a pending message
// to save a screenshot then we will now take the new page and save
@@ -764,6 +764,7 @@
mUi.onTabDataChanged(tab);
+ String url = tab.getUrl();
// update the bookmark database for favicon
maybeUpdateFavicon(tab, null, url, favicon);
@@ -777,9 +778,10 @@
}
@Override
- public void onPageFinished(Tab tab, String url) {
+ public void onPageFinished(Tab tab) {
mUi.onTabDataChanged(tab);
- if (!tab.isPrivateBrowsingEnabled()) {
+ if (!tab.isPrivateBrowsingEnabled()
+ && !TextUtils.isEmpty(tab.getUrl())) {
if (tab.inForeground() && !didUserStopLoading()
|| !tab.inForeground()) {
// Only update the bookmark screenshot if the user did not
@@ -799,7 +801,7 @@
}
// Performance probe
if (false) {
- Performance.onPageFinished(url);
+ Performance.onPageFinished(tab.getUrl());
}
Performance.tracePageFinished();
@@ -843,7 +845,7 @@
public void onReceivedTitle(Tab tab, final String title) {
mUi.onTabDataChanged(tab);
final String pageUrl = tab.getUrl();
- if (pageUrl == null || pageUrl.length()
+ if (TextUtils.isEmpty(pageUrl) || pageUrl.length()
>= SQLiteDatabase.SQLITE_MAX_LIKE_PATTERN_LENGTH) {
return;
}
@@ -887,12 +889,13 @@
}
@Override
- public void doUpdateVisitedHistory(Tab tab, String url,
- boolean isReload) {
+ public void doUpdateVisitedHistory(Tab tab, boolean isReload) {
// Don't save anything in private browsing mode
if (tab.isPrivateBrowsingEnabled()) return;
+ String url = tab.getUrl();
- if (url.regionMatches(true, 0, "about:", 0, 6)) {
+ if (TextUtils.isEmpty(url)
+ || url.regionMatches(true, 0, "about:", 0, 6)) {
return;
}
mDataController.updateVisitedHistory(url);
@@ -1026,6 +1029,7 @@
intent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE,
mActivity.getComponentName().flattenToString());
intent.putExtra(SEND_APP_ID_EXTRA, false);
+ intent.putExtra(RecognizerIntent.EXTRA_WEB_SEARCH_ONLY, true);
mActivity.startActivity(intent);
}
@@ -2352,9 +2356,11 @@
* @return true if handled, false to pass to super
*/
boolean onKeyDown(int keyCode, KeyEvent event) {
+ boolean noModifiers = event.hasNoModifiers();
+
// Even if MENU is already held down, we need to call to super to open
// the IME on long press.
- if (KeyEvent.KEYCODE_MENU == keyCode) {
+ if (!noModifiers && KeyEvent.KEYCODE_MENU == keyCode) {
mMenuIsDown = true;
return false;
}
@@ -2366,23 +2372,26 @@
WebView webView = getCurrentTopWebView();
if (webView == null) return false;
- boolean ctrl = event.isCtrlPressed();
+ boolean ctrl = event.hasModifiers(KeyEvent.META_CTRL_ON);
+ boolean shift = event.hasModifiers(KeyEvent.META_SHIFT_ON);
switch(keyCode) {
case KeyEvent.KEYCODE_ESCAPE:
+ if (!noModifiers) break;
stopLoading();
return true;
case KeyEvent.KEYCODE_SPACE:
// WebView/WebTextView handle the keys in the KeyDown. As
// the Activity's shortcut keys are only handled when WebView
// doesn't, have to do it in onKeyDown instead of onKeyUp.
- if (event.isShiftPressed()) {
+ if (shift) {
pageUp();
- } else {
+ } else if (noModifiers) {
pageDown();
}
return true;
case KeyEvent.KEYCODE_BACK:
+ if (!noModifiers) break;
if (event.getRepeatCount() == 0) {
event.startTracking();
return true;
@@ -2498,6 +2507,7 @@
}
boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (!event.hasNoModifiers()) return false;
switch(keyCode) {
case KeyEvent.KEYCODE_MENU:
mMenuIsDown = false;
diff --git a/src/com/android/browser/IntentHandler.java b/src/com/android/browser/IntentHandler.java
index 06a9f6f..e4b3201 100644
--- a/src/com/android/browser/IntentHandler.java
+++ b/src/com/android/browser/IntentHandler.java
@@ -202,13 +202,6 @@
final String action = intent.getAction();
if (Intent.ACTION_VIEW.equals(action)) {
url = UrlUtils.smartUrlFilter(intent.getData());
- if (url != null && url.startsWith("content:")) {
- /* Append mimetype so webview knows how to display */
- String mimeType = intent.resolveType(mActivity.getContentResolver());
- if (mimeType != null) {
- url += "?" + mimeType;
- }
- }
if (url != null && url.startsWith("http")) {
final Bundle pairs = intent
.getBundleExtra(Browser.EXTRA_HEADERS);
diff --git a/src/com/android/browser/PieControl.java b/src/com/android/browser/PieControl.java
index 210e9ea..6326f2e 100644
--- a/src/com/android/browser/PieControl.java
+++ b/src/com/android/browser/PieControl.java
@@ -70,7 +70,7 @@
mPie.addItem(mRefresh);
mBack = makeMenuView(R.drawable.ic_pie_back);
mPie.addItem(mBack);
- mUrl = makeMenuView(R.drawable.ic_pie_search);
+ mUrl = makeMenuView(R.drawable.ic_pie_web);
mPie.addItem(mUrl);
mBookmarks = makeMenuView(R.drawable.ic_pie_bookmarks);
mPie.addItem(mBookmarks);
@@ -133,6 +133,7 @@
mUiController.bookmarksOrHistoryPicker(false);
} else if (mNewTab == v) {
mUiController.openTabToHomePage();
+ mUi.showFakeTitleBarAndEdit();
} else if (mClose == v) {
mUiController.closeCurrentTab();
} else {
diff --git a/src/com/android/browser/ScrollWebView.java b/src/com/android/browser/ScrollWebView.java
index 51df958..e2ef902 100644
--- a/src/com/android/browser/ScrollWebView.java
+++ b/src/com/android/browser/ScrollWebView.java
@@ -30,6 +30,7 @@
private ScrollListener mScrollListener;
private boolean mIsCancelled;
+ private boolean mBackgroundRemoved = false;
/**
* @param context
@@ -113,4 +114,16 @@
public void onScroll(int visibleTitleHeight);
}
+ @Override
+ protected void onDraw(android.graphics.Canvas c) {
+ super.onDraw(c);
+ if (!mBackgroundRemoved && getRootView().getBackground() != null) {
+ mBackgroundRemoved = true;
+ post(new Runnable() {
+ public void run() {
+ getRootView().setBackgroundDrawable(null);
+ }
+ });
+ }
+ }
}
diff --git a/src/com/android/browser/SuggestionsAdapter.java b/src/com/android/browser/SuggestionsAdapter.java
index 7ee5c2a..e2d9386 100644
--- a/src/com/android/browser/SuggestionsAdapter.java
+++ b/src/com/android/browser/SuggestionsAdapter.java
@@ -67,6 +67,7 @@
int mLinesLandscape;
Object mResultsLock = new Object();
List<String> mVoiceResults;
+ boolean mReverseResults;
interface CompletionListener {
@@ -133,6 +134,9 @@
@Override
public SuggestItem getItem(int position) {
+ if (mReverseResults) {
+ position = (getCount() - 1) - position;
+ }
if (mVoiceResults != null) {
return new SuggestItem(mVoiceResults.get(position), null,
TYPE_SEARCH);
@@ -143,9 +147,13 @@
return mMixedResults.items.get(position);
}
+ public void setReverseResults(boolean reverse) {
+ mReverseResults = reverse;
+ }
+
@Override
public long getItemId(int position) {
- return 0;
+ return position;
}
@Override
diff --git a/src/com/android/browser/Tab.java b/src/com/android/browser/Tab.java
index 320d3b3..d70b0ef 100644
--- a/src/com/android/browser/Tab.java
+++ b/src/com/android/browser/Tab.java
@@ -532,7 +532,7 @@
}
// finally update the UI in the activity if it is in the foreground
- mWebViewController.onPageStarted(Tab.this, view, url, favicon);
+ mWebViewController.onPageStarted(Tab.this, view, favicon);
updateBookmarkedStatus();
}
@@ -556,7 +556,7 @@
// but before a provisional load occurred
mCurrentState.mLockIcon = LockIcon.LOCK_ICON_UNSECURE;
}
- mWebViewController.onPageFinished(Tab.this, url);
+ mWebViewController.onPageFinished(Tab.this);
}
// return true if want to hijack the url to let another app to handle it
@@ -687,7 +687,7 @@
@Override
public void doUpdateVisitedHistory(WebView view, String url,
boolean isReload) {
- mWebViewController.doUpdateVisitedHistory(Tab.this, url, isReload);
+ mWebViewController.doUpdateVisitedHistory(Tab.this, isReload);
}
/**
@@ -1568,7 +1568,7 @@
}
String getUrl() {
- return mCurrentState.mUrl;
+ return UrlUtils.filteredUrl(mCurrentState.mUrl);
}
/**
diff --git a/src/com/android/browser/TabBar.java b/src/com/android/browser/TabBar.java
index e36c204..ba123d0 100644
--- a/src/com/android/browser/TabBar.java
+++ b/src/com/android/browser/TabBar.java
@@ -16,7 +16,6 @@
package com.android.browser;
-import android.graphics.Matrix;
import com.android.browser.ScrollWebView.ScrollListener;
import android.app.Activity;
@@ -26,6 +25,7 @@
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Shader;
@@ -33,6 +33,7 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.PaintDrawable;
+import android.util.Log;
import android.view.ContextMenu;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -70,9 +71,8 @@
private ImageButton mNewTab;
private int mButtonWidth;
- private Map<Tab, TabViewData> mTabMap;
+ private Map<Tab, TabView> mTabMap;
- private boolean mUserRequestedUrlbar;
private int mVisibleTitleHeight;
private Drawable mGenericFavicon;
@@ -108,7 +108,8 @@
mActiveDrawable = res.getDrawable(R.drawable.bg_urlbar);
mInactiveDrawable = res.getDrawable(R.drawable.browsertab_inactive);
- mTabMap = new HashMap<Tab, TabViewData>();
+ mTabMap = new HashMap<Tab, TabView>();
+ Resources resources = activity.getResources();
LayoutInflater factory = LayoutInflater.from(activity);
factory.inflate(R.layout.tab_bar, this);
setPadding(12, 12, 0, 0);
@@ -123,7 +124,6 @@
updateTabs(mUiController.getTabs());
- mUserRequestedUrlbar = false;
mVisibleTitleHeight = 1;
mButtonWidth = -1;
// tab dimensions
@@ -141,6 +141,8 @@
void setUseQuickControls(boolean useQuickControls) {
mUseQuickControls = useQuickControls;
+ mNewTab.setVisibility(mUseQuickControls ? View.GONE
+ : View.VISIBLE);
}
int getTabCount() {
@@ -151,8 +153,7 @@
mTabs.clearTabs();
mTabMap.clear();
for (Tab tab : tabs) {
- TabViewData data = buildTab(tab);
- TabView tv = buildView(data);
+ TabView tv = buildTabView(tab);
}
mTabs.setSelectedTab(mTabControl.getCurrentIndex());
}
@@ -162,7 +163,9 @@
super.onMeasure(hspec, vspec);
int w = getMeasuredWidth();
// adjust for new tab overlap
- w -= mTabOverlap;
+ if (!mUseQuickControls) {
+ w -= mTabOverlap;
+ }
setMeasuredDimension(w, getMeasuredHeight());
}
@@ -190,8 +193,14 @@
if (mNewTab == view) {
mUiController.openTabToHomePage();
} else if (mTabs.getSelectedTab() == view) {
- if (mUseQuickControls) return;
- if (mUi.isFakeTitleBarShowing() && !isLoading()) {
+ if (mUseQuickControls) {
+ if (mUi.isFakeTitleBarShowing() && !isLoading()) {
+ mUi.hideFakeTitleBar();
+ } else {
+ mUi.stopWebViewScrolling();
+ mUi.showFakeTitleBarAndEdit();
+ }
+ } else if (mUi.isFakeTitleBarShowing() && !isLoading()) {
mUi.hideFakeTitleBar();
} else {
showUrlBar();
@@ -208,15 +217,14 @@
private void showUrlBar() {
mUi.stopWebViewScrolling();
mUi.showFakeTitleBar();
- mUserRequestedUrlbar = true;
}
void showTitleBarIndicator(boolean show) {
Tab tab = mTabControl.getCurrentTab();
if (tab != null) {
- TabViewData tvd = mTabMap.get(tab);
- if (tvd != null) {
- tvd.mTabView.showIndicator(show);
+ TabView tv = mTabMap.get(tab);
+ if (tv != null) {
+ tv.showIndicator(show);
}
}
}
@@ -231,7 +239,6 @@
showTitleBarIndicator(mVisibleTitleHeight == 0);
Tab tab = mTabControl.getCurrentTab();
tab.getWebView().requestFocus();
- mUserRequestedUrlbar = false;
}
// webview scroll listener
@@ -259,18 +266,12 @@
mActivity.onCreateContextMenu(menu, this, null);
}
- private TabViewData buildTab(Tab tab) {
- TabViewData data = new TabViewData(tab);
- mTabMap.put(tab, data);
- return data;
- }
-
- private TabView buildView(final TabViewData data) {
- TabView tv = new TabView(mActivity, data);
- tv.setTag(data);
- tv.setOnClickListener(this);
- mTabs.addTab(tv);
- return tv;
+ private TabView buildTabView(Tab tab) {
+ TabView tabview = new TabView(mActivity, tab);
+ mTabMap.put(tab, tabview);
+ tabview.setOnClickListener(this);
+ mTabs.addTab(tabview);
+ return tabview;
}
@Override
@@ -292,7 +293,7 @@
*/
class TabView extends LinearLayout implements OnClickListener {
- TabViewData mTabData;
+ Tab mTab;
View mTabContent;
TextView mTitle;
View mIndicator;
@@ -308,12 +309,12 @@
/**
* @param context
*/
- public TabView(Context context, TabViewData tab) {
+ public TabView(Context context, Tab tab) {
super(context);
setWillNotDraw(false);
mPath = new Path();
mWindowPos = new int[2];
- mTabData = tab;
+ mTab = tab;
setGravity(Gravity.CENTER_VERTICAL);
setOrientation(LinearLayout.HORIZONTAL);
setPadding(mTabPadding, 0, 0, 0);
@@ -329,7 +330,7 @@
mSelected = false;
mInLoad = false;
// update the status
- updateFromData();
+ updateFromTab();
}
void showIndicator(boolean show) {
@@ -347,21 +348,19 @@
}
}
- private void updateFromData() {
- mTabData.mTabView = this;
- Tab tab = mTabData.mTab;
- String displayTitle = tab.getTitle();
+ private void updateFromTab() {
+ String displayTitle = mTab.getTitle();
if (displayTitle == null) {
- displayTitle = tab.getUrl();
+ displayTitle = mTab.getUrl();
}
setDisplayTitle(displayTitle);
- setProgress(mTabData.mProgress);
- if (mTabData.mIcon != null) {
- setFavicon(mTabData.mIcon);
+ setProgress(mTab.getLoadProgress());
+ if (mTab.getFavicon() != null) {
+ setFavicon(renderFavicon(mTab.getFavicon()));
}
- if (mTabData.mTab != null) {
+ if (mTab != null) {
mIncognito.setVisibility(
- mTabData.mTab.isPrivateBrowsingEnabled() ?
+ mTab.isPrivateBrowsingEnabled() ?
View.VISIBLE : View.GONE);
}
}
@@ -409,10 +408,10 @@
}
private void closeTab() {
- if (mTabData.mTab == mTabControl.getCurrentTab()) {
+ if (mTab == mTabControl.getCurrentTab()) {
mUiController.closeCurrentTab();
} else {
- mUiController.closeTab(mTabData.mTab);
+ mUiController.closeTab(mTab);
}
}
@@ -472,65 +471,28 @@
}
- /**
- * Store tab state within the title bar
- */
- class TabViewData {
-
- Tab mTab;
- TabView mTabView;
- int mProgress;
- Drawable mIcon;
-
- TabViewData(Tab tab) {
- mTab = tab;
- setUrlAndTitle(mTab.getUrl(), mTab.getTitle());
+ private Drawable renderFavicon(Bitmap icon) {
+ Drawable[] array = new Drawable[3];
+ array[0] = new PaintDrawable(Color.BLACK);
+ array[1] = new PaintDrawable(Color.WHITE);
+ if (icon == null) {
+ array[2] = mGenericFavicon;
+ } else {
+ array[2] = new BitmapDrawable(icon);
}
-
- void setUrlAndTitle(String url, String title) {
- if (mTabView != null) {
- if (title != null) {
- mTabView.setDisplayTitle(title);
- } else if (url != null) {
- mTabView.setDisplayTitle(UrlUtils.stripUrl(url));
- }
- }
- }
-
- void setProgress(int newProgress) {
- mProgress = newProgress;
- if (mTabView != null) {
- mTabView.setProgress(mProgress);
- }
- }
-
- void setFavicon(Bitmap icon) {
- Drawable[] array = new Drawable[3];
- array[0] = new PaintDrawable(Color.BLACK);
- array[1] = new PaintDrawable(Color.WHITE);
- if (icon == null) {
- array[2] = mGenericFavicon;
- } else {
- array[2] = new BitmapDrawable(icon);
- }
- LayerDrawable d = new LayerDrawable(array);
- d.setLayerInset(1, 1, 1, 1, 1);
- d.setLayerInset(2, 2, 2, 2, 2);
- mIcon = d;
- if (mTabView != null) {
- mTabView.setFavicon(mIcon);
- }
- }
-
+ LayerDrawable d = new LayerDrawable(array);
+ d.setLayerInset(1, 1, 1, 1, 1);
+ d.setLayerInset(2, 2, 2, 2, 2);
+ return d;
}
// TabChangeListener implementation
public void onSetActiveTab(Tab tab) {
mTabs.setSelectedTab(mTabControl.getTabIndex(tab));
- TabViewData tvd = mTabMap.get(tab);
- if (tvd != null) {
- tvd.setProgress(tvd.mProgress);
+ TabView tv = mTabMap.get(tab);
+ if (tv != null) {
+ tv.setProgress(tv.mTab.getLoadProgress());
// update the scroll state
WebView webview = tab.getWebView();
if (webview != null) {
@@ -542,46 +504,46 @@
}
public void onFavicon(Tab tab, Bitmap favicon) {
- TabViewData tvd = mTabMap.get(tab);
- if (tvd != null) {
- tvd.setFavicon(favicon);
+ TabView tv = mTabMap.get(tab);
+ if (tv != null) {
+ tv.setFavicon(renderFavicon(favicon));
}
}
public void onNewTab(Tab tab) {
- TabViewData tvd = buildTab(tab);
- buildView(tvd);
+ TabView tv = buildTabView(tab);
}
public void onProgress(Tab tab, int progress) {
- TabViewData tvd = mTabMap.get(tab);
- if (tvd != null) {
- tvd.setProgress(progress);
+ TabView tv = mTabMap.get(tab);
+ if (tv != null) {
+ tv.setProgress(progress);
}
}
public void onRemoveTab(Tab tab) {
- TabViewData tvd = mTabMap.get(tab);
- if (tvd != null) {
- TabView tv = tvd.mTabView;
- if (tv != null) {
- mTabs.removeTab(tv);
- }
+ TabView tv = mTabMap.get(tab);
+ if (tv != null) {
+ mTabs.removeTab(tv);
}
mTabMap.remove(tab);
}
public void onUrlAndTitle(Tab tab, String url, String title) {
- TabViewData tvd = mTabMap.get(tab);
- if (tvd != null) {
- tvd.setUrlAndTitle(url, title);
+ TabView tv = mTabMap.get(tab);
+ if (tv != null) {
+ if (title != null) {
+ tv.setDisplayTitle(title);
+ } else if (url != null) {
+ tv.setDisplayTitle(UrlUtils.stripUrl(url));
+ }
}
}
private boolean isLoading() {
- TabViewData tvd = mTabMap.get(mTabControl.getCurrentTab());
- if ((tvd != null) && (tvd.mTabView != null)) {
- return tvd.mTabView.mInLoad;
+ TabView tv = mTabMap.get(mTabControl.getCurrentTab());
+ if (tv != null) {
+ return tv.mInLoad;
} else {
return false;
}
diff --git a/src/com/android/browser/TitleBarXLarge.java b/src/com/android/browser/TitleBarXLarge.java
index b479139..8be4df5 100644
--- a/src/com/android/browser/TitleBarXLarge.java
+++ b/src/com/android/browser/TitleBarXLarge.java
@@ -17,6 +17,7 @@
package com.android.browser;
import com.android.browser.UrlInputView.UrlInputListener;
+import com.android.browser.search.SearchEngine;
import android.app.Activity;
import android.app.SearchManager;
@@ -87,11 +88,11 @@
R.drawable.textfield_active_holo_dark);
mUnfocusDrawable = resources.getDrawable(
R.drawable.textfield_default_holo_dark);
- rebuildLayout(activity, true);
+ initLayout(activity);
mInVoiceMode = false;
}
- private void rebuildLayout(Context context, boolean rebuildData) {
+ private void initLayout(Context context) {
LayoutInflater factory = LayoutInflater.from(context);
factory.inflate(R.layout.url_bar, this);
@@ -143,6 +144,7 @@
void setUseQuickControls(boolean useQuickControls) {
mUseQuickControls = useQuickControls;
+ mUrlInput.setReverseResults(mUseQuickControls);
if (mUseQuickControls) {
mBackButton.setVisibility(View.GONE);
mForwardButton.setVisibility(View.GONE);
@@ -372,7 +374,11 @@
}
private void setSearchMode(boolean voiceSearchEnabled) {
- mVoiceSearch.setVisibility(voiceSearchEnabled ? View.VISIBLE :
+ SearchEngine searchEngine = BrowserSettings.getInstance()
+ .getSearchEngine();
+ boolean showvoicebutton = voiceSearchEnabled &&
+ (searchEngine != null && searchEngine.supportsVoiceSearch());
+ mVoiceSearch.setVisibility(showvoicebutton ? View.VISIBLE :
View.GONE);
mGoButton.setVisibility(voiceSearchEnabled ? View.GONE :
View.VISIBLE);
diff --git a/src/com/android/browser/UrlInputView.java b/src/com/android/browser/UrlInputView.java
index 8aeef31..428a0f2 100644
--- a/src/com/android/browser/UrlInputView.java
+++ b/src/com/android/browser/UrlInputView.java
@@ -29,7 +29,6 @@
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
-import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.AutoCompleteTextView;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
@@ -218,4 +217,8 @@
item.extra);
}
+ public void setReverseResults(boolean reverse) {
+ mAdapter.setReverseResults(reverse);
+ }
+
}
diff --git a/src/com/android/browser/UrlUtils.java b/src/com/android/browser/UrlUtils.java
index 2df0a61..0a7e90c 100644
--- a/src/com/android/browser/UrlUtils.java
+++ b/src/com/android/browser/UrlUtils.java
@@ -16,6 +16,8 @@
package com.android.browser;
+import com.android.browser.homepages.HomeProvider;
+
import android.net.Uri;
import android.util.Patterns;
import android.webkit.URLUtil;
@@ -32,7 +34,7 @@
"(?i)" + // switch on case insensitive matching
"(" + // begin group for schema
"(?:http|https|file):\\/\\/" +
- "|(?:inline|data|about|content|javascript):" +
+ "|(?:inline|data|about|javascript):" +
")" +
"(.*)" );
@@ -145,4 +147,15 @@
return inUrl;
}
+ // Returns the filtered URL. Cannot return null, but can return an empty string
+ /* package */ static String filteredUrl(String inUrl) {
+ if (inUrl == null) {
+ return "";
+ }
+ if (inUrl.startsWith(HomeProvider.MOST_VISITED)) {
+ return "";
+ }
+ return inUrl;
+ }
+
}
diff --git a/src/com/android/browser/WebViewController.java b/src/com/android/browser/WebViewController.java
index a187af0..813b63b 100644
--- a/src/com/android/browser/WebViewController.java
+++ b/src/com/android/browser/WebViewController.java
@@ -46,9 +46,9 @@
void createSubWindow(Tab tab);
- void onPageStarted(Tab tab, WebView view, String url, Bitmap favicon);
+ void onPageStarted(Tab tab, WebView view, Bitmap favicon);
- void onPageFinished(Tab tab, String url);
+ void onPageFinished(Tab tab);
void onProgressChanged(Tab tab);
@@ -62,7 +62,7 @@
void onUnhandledKeyEvent(KeyEvent event);
- void doUpdateVisitedHistory(Tab tab, String url, boolean isReload);
+ void doUpdateVisitedHistory(Tab tab, boolean isReload);
void getVisitedHistory(final ValueCallback<String[]> callback);
diff --git a/src/com/android/browser/XLargeUi.java b/src/com/android/browser/XLargeUi.java
index f45b0a1..1e607a7 100644
--- a/src/com/android/browser/XLargeUi.java
+++ b/src/com/android/browser/XLargeUi.java
@@ -242,6 +242,9 @@
}
void editUrl(boolean clearInput) {
+ if (mUiController.isInCustomActionMode()) {
+ mUiController.endActionMode();
+ }
showFakeTitleBar();
mFakeTitleBar.onEditUrl(clearInput);
}
@@ -273,6 +276,7 @@
@Override
protected void hideFakeTitleBar() {
if (isFakeTitleBarShowing()) {
+ mFakeTitleBar.setUrlMode(false);
mContentView.removeView(mFakeTitleBar);
mTabBar.onHideTitleBar();
}
diff --git a/src/com/android/browser/homepages/HomeProvider.java b/src/com/android/browser/homepages/HomeProvider.java
new file mode 100644
index 0000000..5c368eb
--- /dev/null
+++ b/src/com/android/browser/homepages/HomeProvider.java
@@ -0,0 +1,81 @@
+
+/*
+ * Copyright (C) 2011 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.homepages;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.IOException;
+
+public class HomeProvider extends ContentProvider {
+
+ private static final String TAG = "HomeProvider";
+ public static final String AUTHORITY = "com.android.browser.home";
+ public static final String MOST_VISITED = "content://" + AUTHORITY + "/";
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return null;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public boolean onCreate() {
+ return false;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+ return null;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection,
+ String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode) {
+ try {
+ ParcelFileDescriptor[] pipes = ParcelFileDescriptor.createPipe();
+ final ParcelFileDescriptor write = pipes[1];
+ AssetFileDescriptor afd = new AssetFileDescriptor(write, 0, -1);
+ new RequestHandler(getContext(), uri, afd.createOutputStream()).start();
+ return pipes[0];
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to handle request: " + uri, e);
+ return null;
+ }
+ }
+
+}
diff --git a/src/com/android/browser/homepages/RequestHandler.java b/src/com/android/browser/homepages/RequestHandler.java
new file mode 100644
index 0000000..a53fb52
--- /dev/null
+++ b/src/com/android/browser/homepages/RequestHandler.java
@@ -0,0 +1,145 @@
+
+/*
+ * Copyright (C) 2011 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.homepages;
+
+import com.android.browser.R;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import android.content.Context;
+import android.content.UriMatcher;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.Browser;
+import android.util.Base64;
+import android.util.Log;
+
+public class RequestHandler extends Thread {
+
+ private static final String TAG = "RequestHandler";
+ private static final int INDEX = 1;
+ private static final int RESOURCE = 2;
+ private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+
+ Uri mUri;
+ Context mContext;
+ OutputStream mOutput;
+
+ static {
+ sUriMatcher.addURI(HomeProvider.AUTHORITY, "/", INDEX);
+ sUriMatcher.addURI(HomeProvider.AUTHORITY, "res/*/*", RESOURCE);
+ }
+
+ public RequestHandler(Context context, Uri uri, OutputStream out) {
+ mUri = uri;
+ mContext = context;
+ mOutput = out;
+ }
+
+ @Override
+ public void run() {
+ super.run();
+ try {
+ doHandleRequest();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to handle request: " + mUri, e);
+ } finally {
+ cleanup();
+ }
+ }
+
+ void doHandleRequest() throws IOException {
+ int match = sUriMatcher.match(mUri);
+ switch (match) {
+ case INDEX:
+ writeTemplatedIndex();
+ break;
+ case RESOURCE:
+ writeResource(getUriResourcePath());
+ break;
+ }
+ }
+
+ void writeTemplatedIndex() throws IOException {
+ Template t = Template.getCachedTemplate(mContext, R.raw.most_visited);
+ Cursor cursor = mContext.getContentResolver().query(Browser.BOOKMARKS_URI,
+ new String[] { "DISTINCT url", "title", "thumbnail" },
+ "(visits > 0 OR bookmark = 1) AND url NOT LIKE 'content:%' AND thumbnail IS NOT NULL", null, "visits DESC LIMIT 12");
+
+ t.assignLoop("most_visited", new Template.CursorListEntityWrapper(cursor) {
+ @Override
+ public void writeValue(OutputStream stream, String key) throws IOException {
+ Cursor cursor = getCursor();
+ if (key.equals("url")) {
+ stream.write(cursor.getString(0).getBytes());
+ } else if (key.equals("title")) {
+ stream.write(cursor.getString(1).getBytes());
+ } else if (key.equals("thumbnail")) {
+ stream.write("data:image/png;base64,".getBytes());
+ byte[] thumb = cursor.getBlob(2);
+ stream.write(Base64.encode(thumb, Base64.DEFAULT));
+ }
+ }
+ });
+ t.write(mOutput);
+ }
+
+ String getUriResourcePath() {
+ final Pattern pattern = Pattern.compile("/?res/([\\w/]+)");
+ Matcher m = pattern.matcher(mUri.getPath());
+ if (m.matches()) {
+ return m.group(1);
+ } else {
+ return mUri.getPath();
+ }
+ }
+
+ void writeResource(String fileName) throws IOException {
+ Resources res = mContext.getResources();
+ int id = res.getIdentifier(fileName, null, mContext.getPackageName());
+ if (id != 0) {
+ InputStream in = res.openRawResource(id);
+ byte[] buf = new byte[4096];
+ int read;
+ while ((read = in.read(buf)) > 0) {
+ mOutput.write(buf, 0, read);
+ }
+ }
+ }
+
+ void writeString(String str) throws IOException {
+ mOutput.write(str.getBytes());
+ }
+
+ void writeString(String str, int offset, int count) throws IOException {
+ mOutput.write(str.getBytes(), offset, count);
+ }
+
+ void cleanup() {
+ try {
+ mOutput.close();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to close pipe!", e);
+ }
+ }
+
+}
diff --git a/src/com/android/browser/homepages/Template.java b/src/com/android/browser/homepages/Template.java
new file mode 100644
index 0000000..c1a6b0e
--- /dev/null
+++ b/src/com/android/browser/homepages/Template.java
@@ -0,0 +1,279 @@
+
+/*
+ * Copyright (C) 2011 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.homepages;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.util.TypedValue;
+
+public class Template {
+
+ private static HashMap<Integer, Template> sCachedTemplates = new HashMap<Integer, Template>();
+
+ public static Template getCachedTemplate(Context context, int id) {
+ synchronized (sCachedTemplates) {
+ Template template = sCachedTemplates.get(id);
+ if (template == null) {
+ template = new Template(context, id);
+ sCachedTemplates.put(id, template);
+ }
+ // Return a copy so that we don't share data
+ return template.copy();
+ }
+ }
+
+ interface Entity {
+ void write(OutputStream stream, EntityData params) throws IOException;
+ }
+
+ interface EntityData {
+ void writeValue(OutputStream stream, String key) throws IOException;
+ ListEntityIterator getListIterator(String key);
+ }
+
+ interface ListEntityIterator extends EntityData {
+ void reset();
+ boolean moveToNext();
+ }
+
+ static class StringEntity implements Entity {
+
+ byte[] mValue;
+
+ public StringEntity(String value) {
+ mValue = value.getBytes();
+ }
+
+ @Override
+ public void write(OutputStream stream, EntityData params) throws IOException {
+ stream.write(mValue);
+ }
+
+ }
+
+ static class SimpleEntity implements Entity {
+
+ String mKey;
+
+ public SimpleEntity(String key) {
+ mKey = key;
+ }
+
+ @Override
+ public void write(OutputStream stream, EntityData params) throws IOException {
+ params.writeValue(stream, mKey);
+ }
+
+ }
+
+ static class ListEntity implements Entity {
+
+ String mKey;
+ Template mSubTemplate;
+
+ public ListEntity(Context context, String key, String subTemplate) {
+ mKey = key;
+ mSubTemplate = new Template(context, subTemplate);
+ }
+
+ @Override
+ public void write(OutputStream stream, EntityData params) throws IOException {
+ ListEntityIterator iter = params.getListIterator(mKey);
+ iter.reset();
+ while (iter.moveToNext()) {
+ mSubTemplate.write(stream, iter);
+ }
+ }
+
+ }
+
+ public abstract static class CursorListEntityWrapper implements ListEntityIterator {
+
+ private Cursor mCursor;
+
+ public CursorListEntityWrapper(Cursor cursor) {
+ mCursor = cursor;
+ }
+
+ @Override
+ public boolean moveToNext() {
+ return mCursor.moveToNext();
+ }
+
+ @Override
+ public void reset() {
+ mCursor.moveToPosition(-1);
+ }
+
+ @Override
+ public ListEntityIterator getListIterator(String key) {
+ return null;
+ }
+
+ public Cursor getCursor() {
+ return mCursor;
+ }
+
+ }
+
+ static class HashMapEntityData implements EntityData {
+
+ HashMap<String, Object> mData;
+
+ public HashMapEntityData(HashMap<String, Object> map) {
+ mData = map;
+ }
+
+ @Override
+ public ListEntityIterator getListIterator(String key) {
+ return (ListEntityIterator) mData.get(key);
+ }
+
+ @Override
+ public void writeValue(OutputStream stream, String key) throws IOException {
+ stream.write((byte[]) mData.get(key));
+ }
+
+ }
+
+ private List<Entity> mTemplate;
+ private HashMap<String, Object> mData = new HashMap<String, Object>();
+ private Template(Context context, int tid) {
+ this(context, readRaw(context, tid));
+ }
+
+ private Template(Context context, String template) {
+ mTemplate = new ArrayList<Entity>();
+ template = replaceConsts(context, template);
+ parseTemplate(context, template);
+ }
+
+ private Template(Template copy) {
+ mTemplate = copy.mTemplate;
+ }
+
+ Template copy() {
+ return new Template(this);
+ }
+
+ void parseTemplate(Context context, String template) {
+ final Pattern pattern = Pattern.compile("<%([=\\{])\\s*(\\w+)\\s*%>");
+ Matcher m = pattern.matcher(template);
+ int start = 0;
+ while (m.find()) {
+ String static_part = template.substring(start, m.start());
+ if (static_part.length() > 0) {
+ mTemplate.add(new StringEntity(static_part));
+ }
+ String type = m.group(1);
+ String name = m.group(2);
+ if (type.equals("=")) {
+ mTemplate.add(new SimpleEntity(name));
+ } else if (type.equals("{")) {
+ Pattern p = Pattern.compile("<%\\}\\s*" + Pattern.quote(name) + "\\s*%>");
+ Matcher end_m = p.matcher(template);
+ if (end_m.find(m.end())) {
+ start = m.end();
+ m.region(end_m.end(), template.length());
+ String subTemplate = template.substring(start, end_m.start());
+ mTemplate.add(new ListEntity(context, name, subTemplate));
+ start = end_m.end();
+ continue;
+ }
+ }
+ start = m.end();
+ }
+ String static_part = template.substring(start, template.length());
+ if (static_part.length() > 0) {
+ mTemplate.add(new StringEntity(static_part));
+ }
+ }
+
+ public void assign(String name, String value) {
+ mData.put(name, value.getBytes());
+ }
+
+ public void assignLoop(String name, ListEntityIterator iter) {
+ mData.put(name, iter);
+ }
+
+ public void write(OutputStream stream) throws IOException {
+ write(stream, new HashMapEntityData(mData));
+ }
+
+ public void write(OutputStream stream, EntityData data) throws IOException {
+ for (Entity ent : mTemplate) {
+ ent.write(stream, data);
+ }
+ }
+
+ private static String replaceConsts(Context context, String template) {
+ final Pattern pattern = Pattern.compile("<%@\\s*(\\w+/\\w+)\\s*%>");
+ final Resources res = context.getResources();
+ final String packageName = context.getPackageName();
+ Matcher m = pattern.matcher(template);
+ StringBuffer sb = new StringBuffer();
+ while (m.find()) {
+ String name = m.group(1);
+ if (name.startsWith("drawable/")) {
+ m.appendReplacement(sb, "res/" + name);
+ } else {
+ int id = res.getIdentifier(name, null, packageName);
+ if (id != 0) {
+ TypedValue value = new TypedValue();
+ res.getValue(id, value, true);
+ String replacement;
+ if (value.type == TypedValue.TYPE_DIMENSION) {
+ float dimen = res.getDimension(id);
+ int dimeni = (int) dimen;
+ if (dimeni == dimen)
+ replacement = Integer.toString(dimeni);
+ else
+ replacement = Float.toString(dimen);
+ } else {
+ replacement = value.coerceToString().toString();
+ }
+ m.appendReplacement(sb, replacement);
+ }
+ }
+ }
+ m.appendTail(sb);
+ return sb.toString();
+ }
+
+ private static String readRaw(Context context, int id) {
+ InputStream ins = context.getResources().openRawResource(id);
+ try {
+ byte[] buf = new byte[ins.available()];
+ ins.read(buf);
+ return new String(buf, "utf-8");
+ } catch (IOException ex) {
+ return "<html><body>Error</body></html>";
+ }
+ }
+
+}
diff --git a/src/com/android/browser/provider/BrowserProvider2.java b/src/com/android/browser/provider/BrowserProvider2.java
index 919a135..ff00c56 100644
--- a/src/com/android/browser/provider/BrowserProvider2.java
+++ b/src/com/android/browser/provider/BrowserProvider2.java
@@ -402,6 +402,7 @@
@Override
public void onOpen(SQLiteDatabase db) {
+ db.enableWriteAheadLogging();
mSyncHelper.onDatabaseOpened(db);
}
@@ -478,6 +479,9 @@
}
private byte[] readRaw(Resources res, int id) throws IOException {
+ if (id == 0) {
+ return null;
+ }
InputStream is = res.openRawResource(id);
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();