am 1ef26a30: Only swap title and subtitle for website suggestions; query suggestions should still stay the same (i.e., query in title, number of results in subtitle).
Merge commit '1ef26a30d0372d9fe55e8cb877b1b94bc93c2829'
* commit '1ef26a30d0372d9fe55e8cb877b1b94bc93c2829':
Only swap title and subtitle for website suggestions; query suggestions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index ace5750..df01226 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -171,6 +171,11 @@
android:theme="@android:style/Theme.Dialog">
</activity>
+ <activity android:name="PermissionDialog"
+ android:configChanges="orientation|keyboardHidden"
+ android:theme="@android:style/Theme.Dialog">
+ </activity>
+
<activity android:name="GearsNativeDialog"
android:configChanges="orientation|keyboardHidden"
android:theme="@android:style/Theme.Dialog">
diff --git a/res/layout/browser_downloads_page.xml b/res/layout/browser_downloads_page.xml
index c83a727..1d4d4e6 100644
--- a/res/layout/browser_downloads_page.xml
+++ b/res/layout/browser_downloads_page.xml
@@ -18,8 +18,14 @@
** limitations under the License.
*/
-->
-
-<ListView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/list"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"/>
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <ListView
+ android:id="@+id/list"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"/>
+ <ViewStub
+ android:id="@+id/empty"
+ android:layout="@layout/no_downloads"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"/>
+</merge>
diff --git a/res/layout/permission_dialog.xml b/res/layout/permission_dialog.xml
new file mode 100644
index 0000000..ff24eaf
--- /dev/null
+++ b/res/layout/permission_dialog.xml
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2009, 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.
+ */
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawingCacheQuality="auto"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:padding="0dip">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:padding="10dip">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:paddingRight="10dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"/>
+
+ <TextView
+ android:id="@+id/dialog_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:gravity="center_vertical"
+ android:visibility="gone"
+ android:textSize="16dip"
+ android:textStyle="bold"
+ android:textColor="@color/white"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <ImageView android:id="@+id/titleDivider"
+ android:layout_width="fill_parent"
+ android:layout_height="1dip"
+ android:scaleType="fitXY"
+ android:gravity="fill_horizontal"
+ android:src="@drawable/dialog_divider_horizontal_light"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="10dip"/>
+
+ </LinearLayout>
+
+ <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="10dip"
+ android:layout_weight="1">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:paddingTop="10dip"
+ android:paddingLeft="10dip"
+ android:paddingRight="10dip"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/origin"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:textStyle="bold"
+ android:gravity="left"
+ android:textSize="16dip"
+ android:textColor="@color/white"/>
+
+ <TextView
+ android:id="@+id/dialog_message"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="4dip"
+ android:paddingTop="10dip"
+ android:gravity="left"
+ android:textSize="16dip"
+ android:textColor="@color/white"/>
+
+ </LinearLayout>
+
+ </ScrollView>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:background="@color/gray"
+ android:layout_height="wrap_content"
+ android:paddingTop="4dip"
+ android:paddingLeft="0dip"
+ android:paddingRight="0dip">
+
+ <LinearLayout android:id="@+id/leftSpacer"
+ android:layout_weight="0.25"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:visibility="gone"/>
+
+ <Button
+ android:id="@+id/button_allow"
+ android:layout_width="96dip"
+ android:layout_height="48dip"
+ android:layout_gravity="left"
+ android:layout_weight="1"
+ android:maxLines="2"
+ android:textSize="13dip"/>
+
+ <Button
+ android:id="@+id/button_alwaysdeny"
+ android:layout_width="96dip"
+ android:layout_height="48dip"
+ android:layout_gravity="left"
+ android:layout_weight="1"
+ android:maxLines="2"
+ android:textSize="13dip"/>
+
+ <Button
+ android:id="@+id/button_deny"
+ android:layout_width="96dip"
+ android:layout_height="48dip"
+ android:layout_gravity="right"
+ android:layout_weight="1"
+ android:maxLines="2"
+ android:textSize="13dip"/>
+
+ <LinearLayout android:id="@+id/rightSpacer"
+ android:layout_width="0dip"
+ android:layout_weight="0.25"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:visibility="gone" />
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
new file mode 100644
index 0000000..219cc6b
--- /dev/null
+++ b/res/values/arrays.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2009 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.
+ -->
+<resources>
+ <string-array name="webstorage_quota_entries">
+ <item>No quota allowed</item>
+ <item>5 MB</item>
+ <item>10 MB</item>
+ <item>30 MB</item>
+ <item>100 MB</item>
+ </string-array>
+ <string-array name="webstorage_quota_entries_values">
+ <item>0</item>
+ <item>5</item>
+ <item>10</item>
+ <item>30</item>
+ <item>100</item>
+ </string-array>
+</resources>
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e91a6b6..21ef535 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -284,6 +284,10 @@
<string name="pref_content_autofit">Auto-fit pages</string>
<!-- Settings summary -->
<string name="pref_content_autofit_summary">Format Web pages to fit the screen</string>
+ <!-- Settings label for enabling a mode where the browser is always set to landscape mode -->
+ <string name="pref_content_landscape_only">Landscape only display</string>
+ <!-- Settings summary -->
+ <string name="pref_content_landscape_only_summary">Always read pages in the wider, landscape screen orientation</string>
<!-- Settings screen, section title -->
<string name="pref_privacy_title">Privacy settings</string>
<!-- Settings label -->
@@ -361,6 +365,31 @@
<!-- Settings summary -->
<string name="pref_extras_gears_enable_summary">Applications that extend browser functionality</string>
<!-- Settings label -->
+ <string name="pref_extras_webstorage_settings">Local Storage</string>
+ <!-- Settings summary -->
+ <string name="pref_extras_webstorage_settings_summary">Allow websites to store information on your phone</string>
+ <!-- Settings label -->
+ <string name="pref_extras_webstorage_enable">Enable Database Storage</string>
+ <!-- Settings summary -->
+ <string name="pref_extras_webstorage_enable_summary">Allow websites to store informatin in a local database</string>
+ <!-- Settings label -->
+ <string name="pref_extras_webstorage_set_location">Set Databases Location</string>
+ <!-- Settings summary -->
+ <string name="pref_extras_webstorage_set_location_summary">Define where the databases are stored</string>
+ <!-- Settings label -->
+ <string name="pref_extras_webstorage_set_quota">Set Default Quota</string>
+ <!-- Settings summary -->
+ <string name="pref_extras_webstorage_set_quota_summary">The amount of storage new websites can use without your permission</string>
+ <!-- Settings label -->
+ <string name="pref_extras_webstorage_manage_databases">Manage Databases</string>
+ <!-- Settings summary -->
+ <string name="pref_extras_webstorage_manage_databases_summary">Configure existing databases</string>
+ <!-- Settings label -->
+ <string name="pref_extras_webstorage_clear_databases">Clear All Existing Databases</string>
+ <!-- Settings summary -->
+ <string name="pref_extras_webstorage_clear_databases_summary">Clear all data stored in local databases</string>
+ <string name="pref_extras_webstorage_clear_databases_dlg">All existing databases will be cleared</string>
+ <!-- Settings label -->
<string name="pref_extras_gears_settings">Gears settings</string>
<!-- Settings summary -->
<string name="pref_plugin_installed">Plugins list</string>
@@ -652,6 +681,9 @@
<!-- Gears Dialogs -->
<string name="query_data_prompt">Allow storage</string>
<string name="query_data_message">This web site would like to store information on your phone.</string>
+ <string name="query_storage_quota_prompt">Increase storage quota</string>
+ <string name="query_storage_quota_message">This web site is over its current
+ storage limit. Would you like to increase its quota limit ?</string>
<string name="location_prompt">Access your location</string>
<string name="location_message">This web site would like to have access to your location.</string>
<string name="shortcut_prompt">Create a shortcut</string>
@@ -708,6 +740,22 @@
<string name="unrecognized_dialog_message">Unrecognized dialog type</string>
<string name="default_button">OK</string>
+ <!-- HTML5 dialogs -->
+ <!-- Used as a toast notification after the user close the html5 webstorage permission dialog -->
+ <string name="webstorage_notification">The quota for this site can be changed in the Local Storage section of the Browser settings</string>
+ <!-- Used in the Browser Settings -->
+ <string name="webstorage_manage_quota_title">Manage Quota</string>
+ <string name="webstorage_manage_quota_summary">Set a new size quota</string>
+ <string name="webstorage_clear_data_title">Clear All Data</string>
+ <string name="webstorage_clear_data_summary">Remove all databases associated with this website</string>
+ <!-- Confirmation dialog when the user ask to clear all data for an origin -->
+ <string name="webstorage_clear_data_dialog_title">Clear All Data</string>
+ <string name="webstorage_clear_data_dialog_message">All stored data by this origin will be deleted</string>
+ <!-- Strings used in the summary of origins -->
+ <string name="webstorage_origin_summary_mb_used">MB used</string>
+ <string name="webstorage_origin_summary_no_quota_left">no allowed quota left</string>
+ <string name="webstorage_origin_summary_mb_left">MB left in the quota</string>
+
<!-- Zoom-related strings --><skip />
<!-- Caption for a button that is shown when the zoom widget is showing. The button's action will switch to the zoom overview mode. -->
<string name="zoom_overview_button_text">Overview</string>
diff --git a/res/xml/browser_preferences.xml b/res/xml/browser_preferences.xml
index fdfa839..c5b6b24 100644
--- a/res/xml/browser_preferences.xml
+++ b/res/xml/browser_preferences.xml
@@ -54,6 +54,12 @@
android:summary="@string/pref_content_autofit_summary" />
<CheckBoxPreference
+ android:key="landscape_only"
+ android:defaultValue="false"
+ android:title="@string/pref_content_landscape_only"
+ android:summary="@string/pref_content_landscape_only_summary" />
+
+ <CheckBoxPreference
android:key="enable_javascript"
android:defaultValue="true"
android:title="@string/pref_content_javascript" />
@@ -165,6 +171,42 @@
android:title="@string/pref_extras_gears_settings"
android:summary="@string/pref_extras_gears_settings_summary" />
+ <PreferenceScreen
+ android:key="webstorage_settings"
+ android:title="@string/pref_extras_webstorage_settings"
+ android:summary="@string/pref_extras_webstorage_settings_summary">
+
+ <CheckBoxPreference
+ android:key="enable_database"
+ android:defaultValue="true"
+ android:title="@string/pref_extras_webstorage_enable"
+ android:summary="@string/pref_extras_webstorage_enable_summary" />
+
+ <ListPreference
+ android:key="webstorage_default_quota"
+ android:dependency="enable_database"
+ android:title="@string/pref_extras_webstorage_set_quota"
+ android:summary="@string/pref_extras_webstorage_set_quota_summary"
+ android:entries="@array/webstorage_quota_entries"
+ android:entryValues="@array/webstorage_quota_entries_values" />
+
+ <PreferenceScreen
+ android:key="webstorage_manage_databases"
+ android:dependency="enable_database"
+ android:title="@string/pref_extras_webstorage_manage_databases"
+ android:summary="@string/pref_extras_webstorage_manage_databases_summary" />
+
+ <com.android.browser.BrowserYesNoPreference
+ android:key="webstorage_clear_databases"
+ android:dependency="enable_database"
+ android:title="@string/pref_extras_webstorage_clear_databases"
+ android:summary="@string/pref_extras_webstorage_clear_databases_summary"
+ android:dialogMessage="@string/pref_extras_webstorage_clear_databases_dlg"
+ android:dialogTitle="@string/clear"
+ android:dialogIcon="@android:drawable/ic_dialog_alert" />
+
+ </PreferenceScreen>
+
<com.android.browser.BrowserYesNoPreference
android:key="reset_default_preferences"
android:title="@string/pref_extras_reset_default"
diff --git a/src/com/android/browser/AddBookmarkPage.java b/src/com/android/browser/AddBookmarkPage.java
index cf3fe70..f773d06 100644
--- a/src/com/android/browser/AddBookmarkPage.java
+++ b/src/com/android/browser/AddBookmarkPage.java
@@ -48,9 +48,11 @@
private Bundle mMap;
private static final String[] mProjection =
- { "_id", "url", "bookmark", "created", "title", "visits" };
- private static final String WHERE_CLAUSE = "url = ?";
- private final String[] SELECTION_ARGS = new String[1];
+ { "_id", "url", "bookmark", "created", "title", "visits" };
+ private static final String WHERE_CLAUSE
+ = "url = ? OR url = ? OR url = ? OR url = ?";
+ private static final String WHERE_CLAUSE_SECURE = "url = ? OR url = ?";
+ private String[] SELECTION_ARGS;
private View.OnClickListener mSaveBookmark = new View.OnClickListener() {
public void onClick(View v) {
@@ -153,11 +155,35 @@
} else {
// Want to append to the beginning of the list
long creationTime = new Date().getTime();
- SELECTION_ARGS[0] = url;
+ // First we check to see if the user has already visited this
+ // site. They may have bookmarked it in a different way from
+ // how it's stored in the database, so allow different combos
+ // to map to the same url.
+ boolean secure = false;
+ if (url.startsWith("http://")) {
+ url = url.substring(7);
+ } else if (url.startsWith("https://")) {
+ url = url.substring(8);
+ secure = true;
+ }
+ if (url.startsWith("www.")) {
+ url = url.substring(4);
+ }
+ if (secure) {
+ SELECTION_ARGS = new String[2];
+ SELECTION_ARGS[0] = "https://" + url;
+ SELECTION_ARGS[1] = "https://www." + url;
+ } else {
+ SELECTION_ARGS = new String[4];
+ SELECTION_ARGS[0] = url;
+ SELECTION_ARGS[1] = "www." + url;
+ SELECTION_ARGS[2] = "http://" + url;
+ SELECTION_ARGS[3] = "http://" + SELECTION_ARGS[1];
+ }
ContentResolver cr = getContentResolver();
Cursor c = cr.query(Browser.BOOKMARKS_URI,
mProjection,
- WHERE_CLAUSE,
+ secure ? WHERE_CLAUSE_SECURE : WHERE_CLAUSE,
SELECTION_ARGS,
null);
ContentValues map = new ContentValues();
@@ -186,6 +212,7 @@
cr.update(Browser.BOOKMARKS_URI, map,
"_id = " + c.getInt(0), null);
matchedTitle = true;
+ break;
}
}
if (!matchedTitle) {
diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java
index a3a84fb..c24577c 100644
--- a/src/com/android/browser/BrowserActivity.java
+++ b/src/com/android/browser/BrowserActivity.java
@@ -115,6 +115,7 @@
import android.webkit.WebChromeClient;
import android.webkit.WebHistoryItem;
import android.webkit.WebIconDatabase;
+import android.webkit.WebStorage;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.EditText;
@@ -162,6 +163,8 @@
private SensorManager mSensorManager = null;
+ private WebStorage.QuotaUpdater mWebStorageQuotaUpdater = null;
+
/* Whitelisted webpages
private static HashSet<String> sWhiteList;
@@ -1095,8 +1098,10 @@
super.onDestroy();
// Remove the current tab and sub window
TabControl.Tab t = mTabControl.getCurrentTab();
- dismissSubWindow(t);
- removeTabFromContentView(t);
+ if (t != null) {
+ dismissSubWindow(t);
+ removeTabFromContentView(t);
+ }
// Destroy all the tabs
mTabControl.destroy();
WebIconDatabase.getInstance().close();
@@ -1875,7 +1880,7 @@
// the given Message. If the tab overview is already showing (i.e. this
// method is called from TabListener.onClick(), the method will animate
// away from the tab overview.
- private void openTabAndShow(String url, final Message msg,
+ private TabControl.Tab openTabAndShow(String url, final Message msg,
boolean closeOnExit, String appId) {
final boolean newTab = mTabControl.getTabCount() != TabControl.MAX_TABS;
final TabControl.Tab currentTab = mTabControl.getCurrentTab();
@@ -1904,9 +1909,10 @@
}
// Animate from the Tab overview after any animations have
// finished.
- sendAnimateFromOverview(
- mTabControl.createNewTab(closeOnExit, appId, url), true,
- url, delay, msg);
+ final TabControl.Tab tab = mTabControl.createNewTab(
+ closeOnExit, appId, url);
+ sendAnimateFromOverview(tab, true, url, delay, msg);
+ return tab;
}
} else if (url != null) {
// We should not have a msg here.
@@ -1921,6 +1927,7 @@
currentTab.getWebView().loadUrl(url);
}
}
+ return currentTab;
}
private Animation createTabAnimation(final AnimatingView view,
@@ -2140,14 +2147,15 @@
mTabListener = null;
}
- private void openTab(String url) {
+ private TabControl.Tab openTab(String url) {
if (mSettings.openInBackground()) {
TabControl.Tab t = mTabControl.createNewTab();
if (t != null) {
t.getWebView().loadUrl(url);
}
+ return t;
} else {
- openTabAndShow(url, null, false, null);
+ return openTabAndShow(url, null, false, null);
}
}
@@ -2581,7 +2589,12 @@
loadURL(getTopWindow(), url);
break;
case R.id.open_newtab_context_menu_id:
- openTab(url);
+ final TabControl.Tab parent = mTabControl
+ .getCurrentTab();
+ final TabControl.Tab newTab = openTab(url);
+ if (newTab != parent) {
+ parent.addChildTab(newTab);
+ }
break;
case R.id.bookmark_context_menu_id:
Intent intent = new Intent(BrowserActivity.this,
@@ -3241,8 +3254,11 @@
// openTabAndShow will dispatch the message after creating the
// new WebView. This will prevent another request from coming
// in during the animation.
- openTabAndShow(null, msg, false, null);
- parent.addChildTab(mTabControl.getCurrentTab());
+ final TabControl.Tab newTab = openTabAndShow(null, msg, false,
+ null);
+ if (newTab != parent) {
+ parent.addChildTab(newTab);
+ }
WebView.WebViewTransport transport =
(WebView.WebViewTransport) msg.obj;
transport.setWebView(mTabControl.getCurrentWebView());
@@ -3383,6 +3399,8 @@
url.length() >= SQLiteDatabase.SQLITE_MAX_LIKE_PATTERN_LENGTH) {
return;
}
+ // See if we can find the current url in our history database and
+ // add the new title to it.
if (url.startsWith("http://www.")) {
url = url.substring(11);
} else if (url.startsWith("http://")) {
@@ -3397,15 +3415,12 @@
Cursor c = mResolver.query(Browser.BOOKMARKS_URI,
Browser.HISTORY_PROJECTION, where, selArgs, null);
if (c.moveToFirst()) {
- if (LOGV_ENABLED) {
- Log.v(LOGTAG, "updating cursor");
- }
// Current implementation of database only has one entry per
// url.
- int titleIndex =
- c.getColumnIndex(Browser.BookmarkColumns.TITLE);
- c.updateString(titleIndex, title);
- c.commitUpdates();
+ ContentValues map = new ContentValues();
+ map.put(Browser.BookmarkColumns.TITLE, title);
+ mResolver.update(Browser.BOOKMARKS_URI, map,
+ "_id = " + c.getInt(0), null);
}
c.close();
} catch (IllegalStateException e) {
@@ -3419,6 +3434,39 @@
public void onReceivedIcon(WebView view, Bitmap icon) {
updateIcon(view.getUrl(), icon);
}
+
+ /**
+ * The origin has exceeded it's database quota.
+ * @param url the URL that exceeded the quota
+ * @param databaseIdentifier the identifier of the database on
+ * which the transaction that caused the quota overflow was run
+ * @param currentQuota the current quota for the origin.
+ * @param quotaUpdater The callback to run when a decision to allow or
+ * deny quota has been made. Don't forget to call this!
+ */
+ @Override
+ public void onExceededDatabaseQuota(String url,
+ String databaseIdentifier, long currentQuota,
+ WebStorage.QuotaUpdater quotaUpdater) {
+ if(LOGV_ENABLED) {
+ Log.v(LOGTAG,
+ "BrowserActivity received onExceededDatabaseQuota for "
+ + url +
+ ":"
+ + databaseIdentifier +
+ "(current quota: "
+ + currentQuota +
+ ")");
+ }
+ mWebStorageQuotaUpdater = quotaUpdater;
+ String DIALOG_PACKAGE = "com.android.browser";
+ String DIALOG_CLASS = DIALOG_PACKAGE + ".PermissionDialog";
+ Intent intent = new Intent();
+ intent.setClassName(DIALOG_PACKAGE, DIALOG_CLASS);
+ intent.putExtra(PermissionDialog.PARAM_ORIGIN, url);
+ intent.putExtra(PermissionDialog.PARAM_QUOTA, currentQuota);
+ startActivityForResult(intent, WEBSTORAGE_QUOTA_DIALOG);
+ }
};
/**
@@ -3534,19 +3582,19 @@
String cookies = CookieManager.getInstance().getCookie(url);
ContentValues values = new ContentValues();
- values.put(Downloads.URI, uri.toString());
- values.put(Downloads.COOKIE_DATA, cookies);
- values.put(Downloads.USER_AGENT, userAgent);
- values.put(Downloads.NOTIFICATION_PACKAGE,
+ values.put(Downloads.COLUMN_URI, uri.toString());
+ values.put(Downloads.COLUMN_COOKIE_DATA, cookies);
+ values.put(Downloads.COLUMN_USER_AGENT, userAgent);
+ values.put(Downloads.COLUMN_NOTIFICATION_PACKAGE,
getPackageName());
- values.put(Downloads.NOTIFICATION_CLASS,
+ values.put(Downloads.COLUMN_NOTIFICATION_CLASS,
BrowserDownloadPage.class.getCanonicalName());
- values.put(Downloads.VISIBILITY, Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
- values.put(Downloads.MIMETYPE, mimetype);
- values.put(Downloads.FILENAME_HINT, filename);
- values.put(Downloads.DESCRIPTION, uri.getHost());
+ values.put(Downloads.COLUMN_VISIBILITY, Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
+ values.put(Downloads.COLUMN_MIME_TYPE, mimetype);
+ values.put(Downloads.COLUMN_FILE_NAME_HINT, filename);
+ values.put(Downloads.COLUMN_DESCRIPTION, uri.getHost());
if (contentLength > 0) {
- values.put(Downloads.TOTAL_BYTES, contentLength);
+ values.put(Downloads.COLUMN_TOTAL_BYTES, contentLength);
}
if (mimetype == null) {
// We must have long pressed on a link or image to download it. We
@@ -4124,6 +4172,14 @@
}
}
break;
+ case WEBSTORAGE_QUOTA_DIALOG:
+ long currentQuota = 0;
+ if (resultCode == RESULT_OK && intent != null) {
+ currentQuota = intent.getLongExtra(
+ PermissionDialog.PARAM_QUOTA, currentQuota);
+ }
+ mWebStorageQuotaUpdater.updateQuota(currentQuota);
+ break;
default:
break;
}
@@ -4224,13 +4280,19 @@
AnimatingView(Context ctxt, TabControl.Tab t) {
super(ctxt);
mTab = t;
- // Use the top window in the animation since the tab overview will
- // display the top window in each cell.
- final WebView w = t.getTopWindow();
- mPicture = w.capturePicture();
- mScale = w.getScale() / w.getWidth();
- mScrollX = w.getScrollX();
- mScrollY = w.getScrollY();
+ if (t != null && t.getTopWindow() != null) {
+ // Use the top window in the animation since the tab overview
+ // will display the top window in each cell.
+ final WebView w = t.getTopWindow();
+ mPicture = w.capturePicture();
+ mScale = w.getScale() / w.getWidth();
+ mScrollX = w.getScrollX();
+ mScrollY = w.getScrollY();
+ } else {
+ mPicture = null;
+ mScale = 1.0f;
+ mScrollX = mScrollY = 0;
+ }
}
@Override
@@ -4389,7 +4451,7 @@
return 0;
}
- static final Pattern ACCEPTED_URI_SCHEMA = Pattern.compile(
+ protected static final Pattern ACCEPTED_URI_SCHEMA = Pattern.compile(
"(?i)" + // switch on case insensitive matching
"(" + // begin group for schema
"(?:http|https|file):\\/\\/" +
@@ -4674,9 +4736,10 @@
private BroadcastReceiver mNetworkStateIntentReceiver;
// activity requestCode
- final static int COMBO_PAGE = 1;
- final static int DOWNLOAD_PAGE = 2;
- final static int PREFERENCES_PAGE = 3;
+ final static int COMBO_PAGE = 1;
+ final static int DOWNLOAD_PAGE = 2;
+ final static int PREFERENCES_PAGE = 3;
+ final static int WEBSTORAGE_QUOTA_DIALOG = 4;
// the frenquency of checking whether system memory is low
final static int CHECK_MEMORY_INTERVAL = 30000; // 30 seconds
diff --git a/src/com/android/browser/BrowserDownloadAdapter.java b/src/com/android/browser/BrowserDownloadAdapter.java
index 38b83fe..16cb982 100644
--- a/src/com/android/browser/BrowserDownloadAdapter.java
+++ b/src/com/android/browser/BrowserDownloadAdapter.java
@@ -60,14 +60,14 @@
public BrowserDownloadAdapter(Context context, int layout, Cursor c) {
super(context, layout, c);
mFilenameColumnId = c.getColumnIndexOrThrow(Downloads._DATA);
- mTitleColumnId = c.getColumnIndexOrThrow(Downloads.TITLE);
- mDescColumnId = c.getColumnIndexOrThrow(Downloads.DESCRIPTION);
- mStatusColumnId = c.getColumnIndexOrThrow(Downloads.STATUS);
- mTotalBytesColumnId = c.getColumnIndexOrThrow(Downloads.TOTAL_BYTES);
+ mTitleColumnId = c.getColumnIndexOrThrow(Downloads.COLUMN_TITLE);
+ mDescColumnId = c.getColumnIndexOrThrow(Downloads.COLUMN_DESCRIPTION);
+ mStatusColumnId = c.getColumnIndexOrThrow(Downloads.COLUMN_STATUS);
+ mTotalBytesColumnId = c.getColumnIndexOrThrow(Downloads.COLUMN_TOTAL_BYTES);
mCurrentBytesColumnId =
- c.getColumnIndexOrThrow(Downloads.CURRENT_BYTES);
- mMimetypeColumnId = c.getColumnIndexOrThrow(Downloads.MIMETYPE);
- mDateColumnId = c.getColumnIndexOrThrow(Downloads.LAST_MODIFICATION);
+ c.getColumnIndexOrThrow(Downloads.COLUMN_CURRENT_BYTES);
+ mMimetypeColumnId = c.getColumnIndexOrThrow(Downloads.COLUMN_MIME_TYPE);
+ mDateColumnId = c.getColumnIndexOrThrow(Downloads.COLUMN_LAST_MODIFICATION);
}
@Override
@@ -106,7 +106,7 @@
// We have a filename, so we can build a title from that
title = new File(fullFilename).getName();
ContentValues values = new ContentValues();
- values.put(Downloads.TITLE, title);
+ values.put(Downloads.COLUMN_TITLE, title);
// assume "_id" is the first column for the cursor
context.getContentResolver().update(
ContentUris.withAppendedId(Downloads.CONTENT_URI,
diff --git a/src/com/android/browser/BrowserDownloadPage.java b/src/com/android/browser/BrowserDownloadPage.java
index 4397337..22e0e65 100644
--- a/src/com/android/browser/BrowserDownloadPage.java
+++ b/src/com/android/browser/BrowserDownloadPage.java
@@ -66,34 +66,30 @@
setTitle(getText(R.string.download_title));
mListView = (ListView) findViewById(R.id.list);
- LayoutInflater factory = LayoutInflater.from(this);
- View v = factory.inflate(R.layout.no_downloads, null);
- addContentView(v, new LayoutParams(LayoutParams.FILL_PARENT,
- LayoutParams.FILL_PARENT));
- mListView.setEmptyView(v);
+ mListView.setEmptyView(findViewById(R.id.empty));
mDownloadCursor = managedQuery(Downloads.CONTENT_URI,
- new String [] {"_id", Downloads.TITLE, Downloads.STATUS,
- Downloads.TOTAL_BYTES, Downloads.CURRENT_BYTES,
- Downloads._DATA, Downloads.DESCRIPTION,
- Downloads.MIMETYPE, Downloads.LAST_MODIFICATION,
- Downloads.VISIBILITY},
+ new String [] {"_id", Downloads.COLUMN_TITLE, Downloads.COLUMN_STATUS,
+ Downloads.COLUMN_TOTAL_BYTES, Downloads.COLUMN_CURRENT_BYTES,
+ Downloads._DATA, Downloads.COLUMN_DESCRIPTION,
+ Downloads.COLUMN_MIME_TYPE, Downloads.COLUMN_LAST_MODIFICATION,
+ Downloads.COLUMN_VISIBILITY},
null, null);
// only attach everything to the listbox if we can access
// the download database. Otherwise, just show it empty
if (mDownloadCursor != null) {
mStatusColumnId =
- mDownloadCursor.getColumnIndexOrThrow(Downloads.STATUS);
+ mDownloadCursor.getColumnIndexOrThrow(Downloads.COLUMN_STATUS);
mIdColumnId =
mDownloadCursor.getColumnIndexOrThrow(Downloads._ID);
mTitleColumnId =
- mDownloadCursor.getColumnIndexOrThrow(Downloads.TITLE);
+ mDownloadCursor.getColumnIndexOrThrow(Downloads.COLUMN_TITLE);
// Create a list "controller" for the data
mDownloadAdapter = new BrowserDownloadAdapter(this,
R.layout.browser_download_item, mDownloadCursor);
-
+
mListView.setAdapter(mDownloadAdapter);
mListView.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
mListView.setOnCreateContextMenuListener(this);
@@ -403,7 +399,7 @@
mDownloadCursor.getColumnIndexOrThrow(Downloads._DATA);
String filename = mDownloadCursor.getString(filenameColumnId);
int mimetypeColumnId =
- mDownloadCursor.getColumnIndexOrThrow(Downloads.MIMETYPE);
+ mDownloadCursor.getColumnIndexOrThrow(Downloads.COLUMN_MIME_TYPE);
String mimetype = mDownloadCursor.getString(mimetypeColumnId);
Uri path = Uri.parse(filename);
// If there is no scheme, then it must be a file
@@ -453,13 +449,13 @@
private void hideCompletedDownload() {
int status = mDownloadCursor.getInt(mStatusColumnId);
- int visibilityColumn = mDownloadCursor.getColumnIndexOrThrow(Downloads.VISIBILITY);
+ int visibilityColumn = mDownloadCursor.getColumnIndexOrThrow(Downloads.COLUMN_VISIBILITY);
int visibility = mDownloadCursor.getInt(visibilityColumn);
if (Downloads.isStatusCompleted(status) &&
visibility == Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) {
ContentValues values = new ContentValues();
- values.put(Downloads.VISIBILITY, Downloads.VISIBILITY_VISIBLE);
+ values.put(Downloads.COLUMN_VISIBILITY, Downloads.VISIBILITY_VISIBLE);
getContentResolver().update(
ContentUris.withAppendedId(Downloads.CONTENT_URI,
mDownloadCursor.getLong(mIdColumnId)), values, null, null);
diff --git a/src/com/android/browser/BrowserHistoryPage.java b/src/com/android/browser/BrowserHistoryPage.java
index 42ca848..368decf 100644
--- a/src/com/android/browser/BrowserHistoryPage.java
+++ b/src/com/android/browser/BrowserHistoryPage.java
@@ -41,6 +41,7 @@
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ContextMenu.ContextMenuInfo;
+import android.view.ViewStub;
import android.webkit.DateSorter;
import android.webkit.WebIconDatabase.IconListener;
import android.widget.AdapterView;
@@ -110,8 +111,7 @@
setListAdapter(mAdapter);
final ExpandableListView list = getExpandableListView();
list.setOnCreateContextMenuListener(this);
- LayoutInflater factory = LayoutInflater.from(this);
- View v = factory.inflate(R.layout.empty_history, null);
+ View v = new ViewStub(this, R.layout.empty_history);
addContentView(v, new LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT));
list.setEmptyView(v);
diff --git a/src/com/android/browser/BrowserHomepagePreference.java b/src/com/android/browser/BrowserHomepagePreference.java
index d4708c3..7324f24 100644
--- a/src/com/android/browser/BrowserHomepagePreference.java
+++ b/src/com/android/browser/BrowserHomepagePreference.java
@@ -50,8 +50,8 @@
if (dialog != null) {
String url = s.toString();
dialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(
- url.length() == 0 || url.equals("about:blank") ||
- Regex.WEB_URL_PATTERN.matcher(url).matches());
+ url.length() == 0 ||
+ BrowserActivity.ACCEPTED_URI_SCHEMA.matcher(url).matches());
}
}
diff --git a/src/com/android/browser/BrowserPreferencesPage.java b/src/com/android/browser/BrowserPreferencesPage.java
index 5d6795b..c6ce8a6 100644
--- a/src/com/android/browser/BrowserPreferencesPage.java
+++ b/src/com/android/browser/BrowserPreferencesPage.java
@@ -17,12 +17,19 @@
package com.android.browser;
import java.util.List;
+import java.util.Vector;
+import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.preference.EditTextPreference;
+import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
+import android.preference.PreferenceScreen;
+import android.util.Log;
+import android.webkit.Plugin;
+import android.webkit.WebStorage;
import android.webkit.WebView;
import android.webkit.Plugin;
@@ -30,6 +37,8 @@
implements Preference.OnPreferenceChangeListener,
Preference.OnPreferenceClickListener {
+ String TAG = "BrowserPreferencesPage";
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -60,6 +69,32 @@
e = findPreference(BrowserSettings.PREF_GEARS_SETTINGS);
e.setOnPreferenceClickListener(this);
+
+ PreferenceScreen manageDatabases = (PreferenceScreen)
+ findPreference(BrowserSettings.PREF_WEBSTORAGE_SETTINGS);
+
+ Preference clearDatabases =
+ findPreference(BrowserSettings.PREF_WEBSTORAGE_CLEAR_ALL);
+
+ Vector origins = WebStorage.getInstance().getOrigins();
+ manageDatabases.setEnabled(false);
+ clearDatabases.setEnabled(false);
+ if (origins != null) {
+ if (origins.size() > 0) {
+ manageDatabases.setEnabled(true);
+ clearDatabases.setEnabled(true);
+ }
+ for (int i = 0; i < origins.size(); i++) {
+ OriginSettings origin =
+ new OriginSettings(this, (String) origins.get(i));
+ PreferenceScreen screen =
+ getPreferenceManager().createPreferenceScreen(this);
+ origin.setScreen(screen);
+ origin.setRootScreen(manageDatabases);
+ origin.setup();
+ manageDatabases.addPreference(screen);
+ }
+ }
}
@Override
diff --git a/src/com/android/browser/BrowserProvider.java b/src/com/android/browser/BrowserProvider.java
index ad8c44d..11d6443 100644
--- a/src/com/android/browser/BrowserProvider.java
+++ b/src/com/android/browser/BrowserProvider.java
@@ -527,7 +527,8 @@
myArgs = null;
} else {
String like = selectionArgs[0] + "%";
- if (selectionArgs[0].startsWith("http")) {
+ if (selectionArgs[0].startsWith("http")
+ || selectionArgs[0].startsWith("file")) {
myArgs = new String[1];
myArgs[0] = like;
suggestSelection = selection;
diff --git a/src/com/android/browser/BrowserQuotaPreference.java b/src/com/android/browser/BrowserQuotaPreference.java
new file mode 100644
index 0000000..e635bb2
--- /dev/null
+++ b/src/com/android/browser/BrowserQuotaPreference.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2009 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.preference.ListPreference;
+import android.content.Context;
+import android.preference.PreferenceScreen;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.webkit.WebStorage;
+
+import java.util.Vector;
+
+/**
+ * Utility class to display and manage the choosen quota
+ * for an origin (HTML5 WebStorage feature)
+ */
+class BrowserQuotaPreference extends ListPreference {
+
+ private String TAG = "BrowserQuotaPreference";
+ private OriginSettings mOrigin = null;
+ private static long sOneMB = 1024 * 1024;
+
+ // This is the constructor called by the inflater
+ public BrowserQuotaPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public BrowserQuotaPreference(Context context, OriginSettings origin) {
+ super(context);
+ mOrigin = origin;
+ }
+
+ /**
+ * Find the minimum quota fitting the current usage
+ * and only show larger quotas in the list
+ */
+ public void setQuotaList () {
+ CharSequence[] entries = getEntries();
+ CharSequence[] values = getEntryValues();
+ Vector<CharSequence> listEntries = new Vector<CharSequence>();
+ Vector<CharSequence> listValues = new Vector<CharSequence>();
+ long usage = 0;
+ if (mOrigin != null) {
+ usage = mOrigin.getUsage();
+ }
+ for (int i = 0; i < values.length; i++) {
+ long value = Long.parseLong(values[i].toString());
+ value *= sOneMB; // the string array is expressed in MB
+ if (value >= usage) {
+ listEntries.add(entries[i]);
+ listValues.add(values[i]);
+ }
+ }
+ CharSequence[] newEntries = new CharSequence[listEntries.size()];
+ CharSequence[] newValues = new CharSequence[listValues.size()];
+ for (int i = 0; i < listEntries.size(); i++) {
+ newEntries[i] = listEntries.get(i);
+ newValues[i] = listValues.get(i);
+ }
+ setEntries(newEntries);
+ setEntryValues(newValues);
+ setValueIndex(0);
+ }
+
+ @Override
+ protected View onCreateDialogView() {
+ setQuotaList();
+ return super.onCreateDialogView();
+ }
+
+ @Override
+ protected void onDialogClosed(boolean positiveResult) {
+ super.onDialogClosed(positiveResult);
+ if (mOrigin == null) {
+ return;
+ }
+ if (positiveResult) {
+ long quota = Long.parseLong(getValue());
+ quota *= sOneMB; // getValue() is in MB
+ mOrigin.setQuota(quota);
+ }
+ }
+}
diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java
index 0e2c5af..36a9d3f 100644
--- a/src/com/android/browser/BrowserSettings.java
+++ b/src/com/android/browser/BrowserSettings.java
@@ -25,6 +25,9 @@
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.SystemProperties;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceScreen;
+import android.util.Log;
import android.view.WindowManager;
import android.webkit.CacheManager;
import android.webkit.CookieManager;
@@ -32,6 +35,7 @@
import android.webkit.WebViewDatabase;
import android.webkit.WebIconDatabase;
import android.webkit.WebSettings;
+import android.webkit.WebStorage;
import android.preference.PreferenceManager;
import android.provider.Browser;
@@ -53,7 +57,9 @@
*/
class BrowserSettings extends Observable {
- // Public variables for settings
+ private static final String DEFAULT_HOME_URL =
+ "http://www.google.com/m?client=ms-";
+ // Private variables for settings
// NOTE: these defaults need to be kept in sync with the XML
// until the performance of PreferenceManager.setDefaultValues()
// is improved.
@@ -67,10 +73,19 @@
private boolean saveFormData = true;
private boolean openInBackground = false;
private String defaultTextEncodingName;
- private String homeUrl = "http://www.google.com/m?client=ms-";
+ private String homeUrl;
private boolean loginInitialized = false;
private boolean autoFitPage = true;
+ private boolean landscapeOnly = false;
private boolean showDebugSettings = false;
+ private String databasePath; // default value set in loadFromDb()
+ private boolean databaseEnabled = true;
+ private long webStorageDefaultQuota = 5 * 1024 * 1024;
+ // The Browser always enables Application Caches.
+ private boolean appCacheEnabled = true;
+ private String appCachePath; // default value set in loadFromDb().
+
+ private final static String TAG = "BrowserSettings";
// Development settings
public WebSettings.LayoutAlgorithm layoutAlgorithm =
@@ -100,10 +115,20 @@
"privacy_clear_form_data";
public final static String PREF_CLEAR_PASSWORDS =
"privacy_clear_passwords";
+ public final static String PREF_CLEAR_DATABASES =
+ "webstorage_clear_databases";
+ public final static String PREF_CLEAR_ALL_DATA =
+ "webstorage_clear_all_data";
+ public final static String PREF_MANAGE_QUOTA =
+ "webstorage_manage_quota";
+ public final static String PREF_DEFAULT_QUOTA =
+ "webstorage_default_quota";
public final static String PREF_EXTRAS_RESET_DEFAULTS =
"reset_default_preferences";
public final static String PREF_DEBUG_SETTINGS = "debug_menu";
public final static String PREF_GEARS_SETTINGS = "gears_settings";
+ public final static String PREF_WEBSTORAGE_SETTINGS = "webstorage_manage_databases";
+ public final static String PREF_WEBSTORAGE_CLEAR_ALL = "webstorage_clear_databases";
public final static String PREF_TEXT_SIZE = "text_size";
public final static String PREF_DEFAULT_TEXT_ENCODING =
"default_text_encoding";
@@ -179,6 +204,14 @@
s.setSupportMultipleWindows(true);
// Turn off file access
s.setAllowFileAccess(false);
+
+ s.setDatabasePath(b.databasePath);
+ s.setDatabaseEnabled(b.databaseEnabled);
+ s.setWebStorageDefaultQuota(b.webStorageDefaultQuota);
+
+ // Turn on Application Caches.
+ s.setAppCachePath(b.appCachePath);
+ s.setAppCacheEnabled(b.appCacheEnabled);
}
}
@@ -198,8 +231,13 @@
// Set the default value for the plugins path to the application's
// local directory.
pluginsPath = ctx.getDir("plugins", 0).getPath();
+ // Set the default value for the Application Caches path.
+ appCachePath = ctx.getDir("appcache", 0).getPath();
+ // Set the default value for the Database path.
+ databasePath = ctx.getDir("databases", 0).getPath();
- homeUrl += Partner.getString(ctx.getContentResolver(), Partner.CLIENT_ID);
+ homeUrl = DEFAULT_HOME_URL +
+ Partner.getString(ctx.getContentResolver(), Partner.CLIENT_ID);
// Load the defaults from the xml
// This call is TOO SLOW, need to manually keep the defaults
@@ -220,6 +258,13 @@
pluginsEnabled = p.getBoolean("enable_plugins",
pluginsEnabled);
pluginsPath = p.getString("plugins_path", pluginsPath);
+ databasePath = p.getString("database_path", databasePath);
+ databaseEnabled = p.getBoolean("enable_database", databaseEnabled);
+ webStorageDefaultQuota = Long.parseLong(p.getString(PREF_DEFAULT_QUOTA,
+ String.valueOf(webStorageDefaultQuota)));
+ appCacheEnabled = p.getBoolean("enable_appcache",
+ appCacheEnabled);
+ appCachePath = p.getString("appcache_path", appCachePath);
javaScriptCanOpenWindowsAutomatically = !p.getBoolean(
"block_popup_windows",
!javaScriptCanOpenWindowsAutomatically);
@@ -237,6 +282,14 @@
textSize = WebSettings.TextSize.valueOf(
p.getString(PREF_TEXT_SIZE, textSize.name()));
autoFitPage = p.getBoolean("autofit_pages", autoFitPage);
+ boolean landscapeOnlyTemp =
+ p.getBoolean("landscape_only", landscapeOnly);
+ if (landscapeOnlyTemp != landscapeOnly) {
+ landscapeOnly = landscapeOnlyTemp;
+ mTabControl.getBrowserActivity().setRequestedOrientation(
+ landscapeOnly ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+ : ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
+ }
useWideViewPort = true; // use wide view port for either setting
if (autoFitPage) {
layoutAlgorithm = WebSettings.LayoutAlgorithm.NARROW_COLUMNS;
@@ -433,12 +486,24 @@
db.clearHttpAuthUsernamePassword();
}
- /*package*/ void resetDefaultPreferences(Context context) {
+ /*package*/ void clearDatabases(Context context) {
+ WebStorage.getInstance().deleteAllDatabases();
+ // Remove all listed databases from the preferences
+ PreferenceActivity activity = (PreferenceActivity) context;
+ PreferenceScreen screen = (PreferenceScreen)
+ activity.findPreference(BrowserSettings.PREF_WEBSTORAGE_SETTINGS);
+ screen.removeAll();
+ screen.setEnabled(false);
+ }
+
+ /*package*/ void resetDefaultPreferences(Context ctx) {
SharedPreferences p =
- PreferenceManager.getDefaultSharedPreferences(context);
+ PreferenceManager.getDefaultSharedPreferences(ctx);
p.edit().clear().commit();
- PreferenceManager.setDefaultValues(context, R.xml.browser_preferences,
+ PreferenceManager.setDefaultValues(ctx, R.xml.browser_preferences,
true);
+ setHomePage(ctx, DEFAULT_HOME_URL +
+ Partner.getString(ctx.getContentResolver(), Partner.CLIENT_ID));
}
// Private constructor that does nothing.
diff --git a/src/com/android/browser/BrowserYesNoPreference.java b/src/com/android/browser/BrowserYesNoPreference.java
index 65cde71..11a577b 100644
--- a/src/com/android/browser/BrowserYesNoPreference.java
+++ b/src/com/android/browser/BrowserYesNoPreference.java
@@ -23,11 +23,21 @@
class BrowserYesNoPreference extends YesNoPreference {
+ // This is used for the HTML5 pref UI, where we construct
+ // BrowserYesNoPreference objects on the fly and where we need
+ // to save the corresponding origin.
+ OriginSettings mOrigin = null;
+
// This is the constructor called by the inflater
public BrowserYesNoPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
+ public BrowserYesNoPreference(Context context, OriginSettings origin) {
+ super(context);
+ mOrigin = origin;
+ }
+
@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
@@ -46,6 +56,12 @@
BrowserSettings.getInstance().clearFormData(context);
} else if (BrowserSettings.PREF_CLEAR_PASSWORDS.equals(getKey())) {
BrowserSettings.getInstance().clearPasswords(context);
+ } else if (BrowserSettings.PREF_CLEAR_DATABASES.equals(getKey())) {
+ BrowserSettings.getInstance().clearDatabases(context);
+ } else if (BrowserSettings.PREF_CLEAR_ALL_DATA.equals(getKey())) {
+ if (mOrigin != null) {
+ mOrigin.delete();
+ }
} else if (BrowserSettings.PREF_EXTRAS_RESET_DEFAULTS.equals(
getKey())) {
BrowserSettings.getInstance().resetDefaultPreferences(context);
diff --git a/src/com/android/browser/FetchUrlMimeType.java b/src/com/android/browser/FetchUrlMimeType.java
index 8578643..c585dbb 100644
--- a/src/com/android/browser/FetchUrlMimeType.java
+++ b/src/com/android/browser/FetchUrlMimeType.java
@@ -58,7 +58,7 @@
mValues = values[0];
// Check to make sure we have a URI to download
- String uri = mValues.getAsString(Downloads.URI);
+ String uri = mValues.getAsString(Downloads.COLUMN_URI);
if (uri == null || uri.length() == 0) {
return null;
}
@@ -66,15 +66,15 @@
// User agent is likely to be null, though the AndroidHttpClient
// seems ok with that.
AndroidHttpClient client = AndroidHttpClient.newInstance(
- mValues.getAsString(Downloads.USER_AGENT));
+ mValues.getAsString(Downloads.COLUMN_USER_AGENT));
HttpHead request = new HttpHead(uri);
- String cookie = mValues.getAsString(Downloads.COOKIE_DATA);
+ String cookie = mValues.getAsString(Downloads.COLUMN_COOKIE_DATA);
if (cookie != null && cookie.length() > 0) {
request.addHeader("Cookie", cookie);
}
- String referer = mValues.getAsString(Downloads.REFERER);
+ String referer = mValues.getAsString(Downloads.COLUMN_REFERER);
if (referer != null && referer.length() > 0) {
request.addHeader("Referer", referer);
}
@@ -111,19 +111,19 @@
@Override
public void onPostExecute(String mimeType) {
if (mimeType != null) {
- String url = mValues.getAsString(Downloads.URI);
+ String url = mValues.getAsString(Downloads.COLUMN_URI);
if (mimeType.equalsIgnoreCase("text/plain") ||
mimeType.equalsIgnoreCase("application/octet-stream")) {
String newMimeType =
MimeTypeMap.getSingleton().getMimeTypeFromExtension(
MimeTypeMap.getFileExtensionFromUrl(url));
if (newMimeType != null) {
- mValues.put(Downloads.MIMETYPE, newMimeType);
+ mValues.put(Downloads.COLUMN_MIME_TYPE, newMimeType);
}
}
String filename = URLUtil.guessFileName(url,
null, mimeType);
- mValues.put(Downloads.FILENAME_HINT, filename);
+ mValues.put(Downloads.COLUMN_FILE_NAME_HINT, filename);
}
// Start the download
diff --git a/src/com/android/browser/FindDialog.java b/src/com/android/browser/FindDialog.java
index 6e9574c..3482b85 100644
--- a/src/com/android/browser/FindDialog.java
+++ b/src/com/android/browser/FindDialog.java
@@ -143,23 +143,14 @@
mBrowserActivity.closeFind();
mWebView.clearMatches();
}
-
+
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
- int code = event.getKeyCode();
- boolean up = event.getAction() == KeyEvent.ACTION_UP;
- switch (code) {
- case KeyEvent.KEYCODE_DPAD_CENTER:
- case KeyEvent.KEYCODE_ENTER:
- if (!mEditText.hasFocus()) {
- break;
- }
- if (up) {
- findNext();
- }
- return true;
- default:
- break;
+ if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER
+ && event.getAction() == KeyEvent.ACTION_UP
+ && mEditText.hasFocus()) {
+ findNext();
+ return true;
}
return super.dispatchKeyEvent(event);
}
diff --git a/src/com/android/browser/MostVisitedActivity.java b/src/com/android/browser/MostVisitedActivity.java
index 704ee27..f0d1c7f 100644
--- a/src/com/android/browser/MostVisitedActivity.java
+++ b/src/com/android/browser/MostVisitedActivity.java
@@ -35,6 +35,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
+import android.view.ViewStub;
import java.util.Vector;
@@ -50,8 +51,7 @@
.addListener(new IconReceiver());
setListAdapter(mAdapter);
ListView list = getListView();
- LayoutInflater factory = LayoutInflater.from(this);
- View v = factory.inflate(R.layout.empty_history, null);
+ View v = new ViewStub(this, R.layout.empty_history);
addContentView(v, new LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT));
list.setEmptyView(v);
diff --git a/src/com/android/browser/OriginSettings.java b/src/com/android/browser/OriginSettings.java
new file mode 100644
index 0000000..e80888a
--- /dev/null
+++ b/src/com/android/browser/OriginSettings.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2009 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.Context;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceScreen;
+import android.util.Log;
+import android.webkit.WebStorage;
+
+/**
+ * Manage the settings for an origin.
+ * We use it to keep track of the HTML5 settings, i.e. database (webstorage).
+ */
+class OriginSettings {
+
+ private String TAG = "OriginSettings";
+ private String mOrigin = null;
+ private long mQuota = 0;
+ private long mUsage = 0;
+ private PreferenceScreen mInfoScreen;
+ private PreferenceScreen mRootScreen;
+ private PreferenceActivity mActivity;
+
+ private static String sMBUsed = null;
+ private static String sNoQuotaLeft = null;
+ private static String sMBLeft = null;
+
+ public OriginSettings(PreferenceActivity activity, String origin) {
+ mOrigin = origin;
+ mUsage = WebStorage.getInstance().getUsageForOrigin(mOrigin);
+ mQuota = WebStorage.getInstance().getQuotaForOrigin(mOrigin);
+ mActivity = activity;
+ if (sMBUsed == null) {
+ sMBUsed = mActivity.getString(
+ R.string.webstorage_origin_summary_mb_used);
+ sNoQuotaLeft = mActivity.getString(
+ R.string.webstorage_origin_summary_no_quota_left);
+ sMBLeft = mActivity.getString(
+ R.string.webstorage_origin_summary_mb_left);
+ }
+ }
+
+ public String getOrigin() {
+ return mOrigin;
+ }
+
+ public long getQuota() {
+ return mQuota;
+ }
+
+ public long getUsage() {
+ return mUsage;
+ }
+
+ public void setScreen(PreferenceScreen screen) {
+ mInfoScreen = screen;
+ }
+
+ public void setRootScreen(PreferenceScreen screen) {
+ mRootScreen = screen;
+ }
+
+ private String sizeValueToString(long value) {
+ float mb = (float) value / (1024.0F * 1024.0F);
+ int val = (int) (mb * 10);
+ float ret = (float) (val / 10.0F);
+ if (ret <= 0) {
+ return "0";
+ }
+ return String.valueOf(ret);
+ }
+
+ public void updateSummary() {
+ String summary = sizeValueToString(mUsage) + " " + sMBUsed;
+ if ((mQuota <= 0) || ((mQuota - mUsage) <= 0)) {
+ summary += ", " + sNoQuotaLeft;
+ } else {
+ summary += " (" + sizeValueToString(mQuota - mUsage);
+ summary += " " + sMBLeft + ")";
+ }
+ mInfoScreen.setSummary(summary);
+ mActivity.onContentChanged();
+ }
+
+ public void setup() {
+ mInfoScreen.setTitle(mOrigin);
+ mInfoScreen.setKey(mOrigin);
+ updateSummary();
+
+ BrowserQuotaPreference manageSite = new BrowserQuotaPreference(mActivity, this);
+ BrowserYesNoPreference clearAllData = new BrowserYesNoPreference(mActivity, this);
+
+ manageSite.setTitle(R.string.webstorage_manage_quota_title);
+ manageSite.setSummary(R.string.webstorage_manage_quota_summary);
+ manageSite.setKey(BrowserSettings.PREF_MANAGE_QUOTA);
+ manageSite.setEntries(R.array.webstorage_quota_entries);
+ manageSite.setEntryValues(R.array.webstorage_quota_entries_values);
+
+ clearAllData.setTitle(R.string.webstorage_clear_data_title);
+ clearAllData.setSummary(R.string.webstorage_clear_data_summary);
+ clearAllData.setKey(BrowserSettings.PREF_CLEAR_ALL_DATA);
+ clearAllData.setDialogTitle(R.string.webstorage_clear_data_dialog_title);
+ clearAllData.setDialogMessage(R.string.webstorage_clear_data_dialog_message);
+ clearAllData.setDialogIcon(android.R.drawable.ic_dialog_alert);
+
+ mInfoScreen.addPreference(manageSite);
+ mInfoScreen.addPreference(clearAllData);
+ }
+
+ public void setQuota(long quota) {
+ mQuota = quota;
+ WebStorage.getInstance().setQuotaForOrigin(mOrigin, mQuota);
+ mInfoScreen.getDialog().dismiss();
+ updateSummary();
+ }
+
+ public void delete() {
+ WebStorage.getInstance().deleteOrigin(mOrigin);
+ mInfoScreen.removeAll();
+ mRootScreen.removePreference(mInfoScreen);
+ mInfoScreen.getDialog().dismiss();
+ if (mRootScreen.getPreferenceCount() == 0) {
+ mRootScreen.getDialog().dismiss();
+ mRootScreen.setEnabled(false);
+ Preference clearDatabases = mActivity.findPreference(
+ BrowserSettings.PREF_WEBSTORAGE_CLEAR_ALL);
+ clearDatabases.setEnabled(false);
+ }
+ }
+}
diff --git a/src/com/android/browser/PermissionDialog.java b/src/com/android/browser/PermissionDialog.java
new file mode 100644
index 0000000..b71261a
--- /dev/null
+++ b/src/com/android/browser/PermissionDialog.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2009 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.app.Activity;
+import android.app.Dialog;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.Window;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Permission dialog for HTML5
+ * @hide
+ */
+public class PermissionDialog extends Activity {
+
+ private static final String TAG = "PermissionDialog";
+ public static final String PARAM_ORIGIN = "origin";
+ public static final String PARAM_QUOTA = "quota";
+
+ private String mWebStorageOrigin;
+ private long mWebStorageQuota = 0;
+ private int mNotification = 0;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ getParameters();
+ setupDialog();
+ }
+
+ private void getParameters() {
+ Intent intent = getIntent();
+ mWebStorageOrigin = intent.getStringExtra(PARAM_ORIGIN);
+ mWebStorageQuota = intent.getLongExtra(PARAM_QUOTA, 0);
+ }
+
+ private void setupDialog() {
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ setContentView(R.layout.permission_dialog);
+
+ setIcon(R.id.icon, android.R.drawable.ic_popup_disk_full);
+ setText(R.id.dialog_title, R.string.query_storage_quota_prompt);
+ setText(R.id.dialog_message, R.string.query_storage_quota_message);
+ setCharSequence(R.id.origin, mWebStorageOrigin);
+
+ setupButton(R.id.button_allow, R.string.permission_button_allow,
+ new View.OnClickListener() {
+ public void onClick(View v) { allow(); }
+ });
+ setupButton(R.id.button_alwaysdeny, R.string.permission_button_alwaysdeny,
+ new View.OnClickListener() {
+ public void onClick(View v) { alwaysdeny(); }
+ });
+ setupButton(R.id.button_deny, R.string.permission_button_deny,
+ new View.OnClickListener() {
+ public void onClick(View v) { deny(); }
+ });
+ }
+
+ private void setText(int viewID, int stringID) {
+ setCharSequence(viewID, getString(stringID));
+ }
+
+ private void setCharSequence(int viewID, CharSequence string) {
+ View view = findViewById(viewID);
+ if (view == null) {
+ return;
+ }
+ view.setVisibility(View.VISIBLE);
+ TextView textView = (TextView) view;
+ textView.setText(string);
+ }
+
+ private void setIcon(int viewID, int imageID) {
+ View view = findViewById(viewID);
+ if (view == null) {
+ return;
+ }
+ view.setVisibility(View.VISIBLE);
+ ImageView icon = (ImageView) view;
+ icon.setImageResource(imageID);
+ }
+
+ private void setupButton(int viewID, int stringID,
+ View.OnClickListener listener) {
+ View view = findViewById(viewID);
+ if (view == null) {
+ return;
+ }
+ setText(viewID, stringID);
+ view.setOnClickListener(listener);
+ }
+
+ private void useNextQuota() {
+ CharSequence[] values = getResources().getTextArray(
+ R.array.webstorage_quota_entries_values);
+ for (int i=0; i<values.length; i++) {
+ long value = Long.parseLong(values[i].toString());
+ value *= (1024 * 1024); // the string array is expressed in MB
+ if (value > mWebStorageQuota) {
+ mWebStorageQuota = value;
+ break;
+ }
+ }
+ }
+
+ private void allow() {
+ // If somehow there is no "next quota" in the ladder,
+ // we'll add 1MB anyway.
+ mWebStorageQuota += 1024*1024;
+ useNextQuota();
+ mNotification = R.string.webstorage_notification;
+ closeDialog();
+ }
+
+ private void alwaysdeny() {
+ // Setting the quota to 0 will prevent any new data to be
+ // added, but the existing data will not be deleted.
+ mWebStorageQuota = 0;
+ mNotification = R.string.webstorage_notification;
+ closeDialog();
+ }
+
+ private void deny() {
+ closeDialog();
+ }
+
+ private void closeDialog() {
+ Intent intent = new Intent();
+ intent.putExtra(PARAM_QUOTA, mWebStorageQuota);
+ setResult(RESULT_OK, intent);
+ showToast();
+ finish();
+ }
+
+ private void showToast() {
+ if (mNotification != 0) {
+ Toast toast = Toast.makeText(this, mNotification, Toast.LENGTH_LONG);
+ toast.setGravity(Gravity.BOTTOM, 0, 0);
+ toast.show();
+ }
+ }
+
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if ((event.getKeyCode() == KeyEvent.KEYCODE_BACK)
+ && (event.getAction() == KeyEvent.ACTION_DOWN)) {
+ closeDialog();
+ return true; // event consumed
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+}
diff --git a/src/com/android/browser/TabControl.java b/src/com/android/browser/TabControl.java
index 581d144..9de0198 100644
--- a/src/com/android/browser/TabControl.java
+++ b/src/com/android/browser/TabControl.java
@@ -446,6 +446,9 @@
* @return index of Tab or -1 if not found
*/
int getTabIndex(Tab tab) {
+ if (tab == null) {
+ return -1;
+ }
return mTabs.indexOf(tab);
}