am 5b078b5b: Fix #1705293. When we about to hijack the url, we check the intent first. If it is not resolved to any activity, we will try to download it from the Market.
Merge commit '5b078b5b8f166a14ab88137e478f868959c5befa'
* commit '5b078b5b8f166a14ab88137e478f868959c5befa':
Fix #1705293. When we about to hijack the url, we check the intent first. If it is not resolved to any activity, we will try to download it from the Market.
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 6362cfa..2a58b6a 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1,20 +1,19 @@
<!--
-/* //device/apps/Browser/AndroidManifest.xml
-**
-** Copyright 2006, 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.
-*/
+/*
+ * Copyright 2006, 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.
+ */
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.browser">
@@ -121,6 +120,8 @@
<activity android:name="BrowserBookmarksPage" android:label="@string/bookmarks"
android:launchMode="singleTop" android:configChanges="orientation|keyboardHidden">
</activity>
+ <activity android:name="BookmarkGridPage" android:label="@string/bookmarks"
+ android:launchMode="singleTop" android:configChanges="orientation|keyboardHidden"/>
<activity android:name="MostVisitedActivity" android:label=""
android:launchMode="singleTop" android:configChanges="orientation|keyboardHidden"/>
@@ -153,11 +154,20 @@
android:configChanges="orientation|keyboardHidden">
</activity>
+ <activity android:name="WebsiteSettingsActivity" android:label=""
+ android:configChanges="orientation|keyboardHidden">
+ </activity>
+
<activity android:name="GearsDialog" android:process=":dialog"
android:configChanges="orientation|keyboardHidden"
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/drawable/background_titlebar.png b/res/drawable/background_titlebar.png
new file mode 100644
index 0000000..1fdb078
--- /dev/null
+++ b/res/drawable/background_titlebar.png
Binary files differ
diff --git a/res/drawable/button_line.xml b/res/drawable/button_line.xml
new file mode 100644
index 0000000..67869d0
--- /dev/null
+++ b/res/drawable/button_line.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item>
+ <shape android:width="2dip">
+ <solid android:color="#ff000000"/>
+ </shape>
+ </item>
+ <item android:left="1dip" android:width="1dip">
+ <shape>
+ <solid android:color="#ffffffff"/>
+ </shape>
+ </item>
+</layer-list>
diff --git a/res/drawable/ic_titlebar_zoom.png b/res/drawable/ic_titlebar_zoom.png
new file mode 100644
index 0000000..b497dab
--- /dev/null
+++ b/res/drawable/ic_titlebar_zoom.png
Binary files differ
diff --git a/res/drawable/ic_titlebar_zoom_out.png b/res/drawable/ic_titlebar_zoom_out.png
new file mode 100644
index 0000000..4b5a3dd
--- /dev/null
+++ b/res/drawable/ic_titlebar_zoom_out.png
Binary files differ
diff --git a/res/layout/application.xml b/res/layout/application.xml
new file mode 100644
index 0000000..d8de265
--- /dev/null
+++ b/res/layout/application.xml
@@ -0,0 +1,53 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawingCacheQuality="auto"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:padding="0dip">
+
+ <ImageView android:id="@+id/icon"
+ android:layout_width="32px"
+ android:layout_height="32px"
+ android:padding="2dip" />
+
+ <TwoLineListItem
+ android:paddingTop="2dip"
+ android:paddingBottom="2dip"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:mode="twoLine">
+
+ <TextView android:id="@+id/title"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="6dip"
+ android:layout_marginTop="6dip"
+ android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+ <TextView android:id="@+id/subtitle"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/title"
+ android:layout_alignLeft="@+id/title"
+ android:textAppearance="?android:attr/textAppearanceSmall"/>
+
+ </TwoLineListItem>
+
+</LinearLayout>
diff --git a/res/layout/bookmark_thumbnail.xml b/res/layout/bookmark_thumbnail.xml
new file mode 100644
index 0000000..57f59eb
--- /dev/null
+++ b/res/layout/bookmark_thumbnail.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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:layout_width="fill_parent"
+ android:orientation="vertical"
+ android:padding="4dip"
+ android:background="@color/white">
+
+ <ImageView android:id="@+id/thumb"
+ android:src="@drawable/app_web_browser_sm"
+ android:scaleType="center"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" />
+
+ <TextView android:id="@+id/label"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/white"
+ android:maxLines="1"
+ android:paddingTop="3dip"
+ android:paddingBottom="3dip"
+ android:paddingLeft="2dip"
+ android:paddingRight="2dip"
+ android:scrollHorizontally="true"
+ android:background="#CC000000"/>
+</LinearLayout>
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/custom_screen.xml b/res/layout/custom_screen.xml
new file mode 100644
index 0000000..95c8434
--- /dev/null
+++ b/res/layout/custom_screen.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ >
+ <com.android.browser.TitleBar android:id="@+id/title_bar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ />
+ <FrameLayout android:id="@+id/main_content"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:foreground="?android:attr/windowContentOverlay"
+ />
+</LinearLayout>
\ No newline at end of file
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/layout/title_bar.xml b/res/layout/title_bar.xml
new file mode 100644
index 0000000..0f70519
--- /dev/null
+++ b/res/layout/title_bar.xml
@@ -0,0 +1,105 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fitsSystemWindows="true"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:background="@drawable/background_titlebar"
+ android:layout_height="45dip">
+ <ImageView android:id="@+id/favicon"
+ android:layout_height="20dip"
+ android:layout_width="20dip"
+ android:layout_marginLeft="6dip"
+ android:layout_marginRight="6dip"
+ android:layout_gravity="center_vertical"
+ />
+ <!-- layout which contains the title, progress bar, and url -->
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content">
+ <!-- This part contains the favicon and the progress bar -->
+ <RelativeLayout
+ android:layout_marginTop="3dip"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ >
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_marginTop="3dip"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+ <ProgressBar android:id="@+id/progress_horizontal"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:max="100" />
+ <ProgressBar android:id="@+id/progress_circular"
+ style="?android:attr/progressBarStyleSmallTitle"
+ android:layout_marginLeft="3dip"
+ android:layout_gravity="center_vertical"
+ android:max="100"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+ <!-- need to make this no wider than the horizontal progress bar -->
+ <TextView android:id="@+id/title"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_marginLeft="6dip"
+ android:layout_marginTop="4dip"
+ android:textSize="14dip"
+ android:textColor="@color/white"
+ android:textStyle="bold"
+ android:singleLine="true"
+ />
+ <TextView android:id="@+id/url"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="6dip"
+ android:layout_below="@id/title"
+ android:textSize="12dip"
+ android:textColor="@color/white"
+ android:singleLine="true"
+ />
+ <ImageView android:id="@+id/lock_icon"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_alignBottom="@id/title"
+ android:layout_alignParentRight="true"
+ android:visibility="gone"/>
+ </RelativeLayout>
+ </LinearLayout>
+ <!-- These buttons will change look/functionality -->
+ <ImageView android:id="@+id/lft_button"
+ android:layout_width="52dip"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:src="@android:drawable/btn_star"/>
+ <!-- divider -->
+ <View android:id="@+id/divider"
+ android:layout_height="fill_parent"
+ android:layout_width="2dip"
+ android:background="@drawable/button_line"/>
+ <ImageView android:id="@+id/rt_button"
+ android:layout_width="52dip"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:src="@*android:drawable/btn_browser_zoom_page_overview"/>
+</LinearLayout>
diff --git a/res/menu/bookmarkscontext.xml b/res/menu/bookmarkscontext.xml
index b43e242..ba5d1dc 100644
--- a/res/menu/bookmarkscontext.xml
+++ b/res/menu/bookmarkscontext.xml
@@ -34,5 +34,7 @@
android:title="@string/contextmenu_copylink"/>
<item android:id="@+id/delete_context_menu_id"
android:title="@string/remove_bookmark"/>
+ <item android:id="@+id/homepage_context_menu_id"
+ android:title="@string/set_as_homepage"/>
</group>
</menu>
diff --git a/res/menu/historycontext.xml b/res/menu/historycontext.xml
index dfda010..5306396 100644
--- a/res/menu/historycontext.xml
+++ b/res/menu/historycontext.xml
@@ -26,5 +26,7 @@
<item android:id="@+id/copy_context_menu_id"
android:title="@string/contextmenu_copylink"/>
<item android:id="@+id/delete_context_menu_id"
- android:title="@string/remove_history_item"/>
+ android:title="@string/remove_history_item"/>
+ <item android:id="@+id/homepage_context_menu_id"
+ android:title="@string/set_as_homepage"/>
</menu>
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 1e5429f..ea714a4 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -58,7 +58,10 @@
<!-- Case of several matches -->
<item quantity="other"><xliff:g id="number" example="137">%d</xliff:g> matches</item>
</plurals>
-
+
+ <!-- Displayed on the title bar while the page is loading -->
+ <string name="title_bar_loading">Loading\u2026</string>
+
<!-- Menu item -->
<string name="page_info">Page info</string>
<!-- Label for a button on an SSL error dialog that allows the user to see
@@ -143,8 +146,12 @@
<string name="remove_bookmark">Delete bookmark</string>
<!-- Menu item to remove the currently highlighted history entry from the list of previously visited sites -->
<string name="remove_history_item">Remove from history</string>
+ <!-- Context menu item for setting the bookmark/history item as the homepage -->
+ <string name="set_as_homepage">Set as homepage</string>
<!-- Toast informing the user that their action to save a bookmark has succeeded -->
<string name="bookmark_saved">Saved to bookmarks.</string>
+ <!-- Toast confirming that the homepage has been set -->
+ <string name="homepage_set">Homepage set</string>
<!-- Error that appears in the title of Bookmark dialog when user selects OK with empty Name field -->
<string name="bookmark_needs_title">"Bookmark must have a name."</string>
<!-- Error that appears in the title of Bookmark dialog when user selects OK with empty Location field -->
@@ -284,14 +291,18 @@
<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 -->
<string name="pref_privacy_clear_cache">Clear cache</string>
<!-- Settings summary -->
- <string name="pref_privacy_clear_cache_summary">Delete all cached page content</string>
+ <string name="pref_privacy_clear_cache_summary">Clear locally cached content and databases</string>
<!-- Confirmation dialog message -->
- <string name="pref_privacy_clear_cache_dlg">The cache will be cleared.</string>
+ <string name="pref_privacy_clear_cache_dlg">Locally cached content and databases will be cleared.</string>
<!-- Settings label -->
<string name="pref_privacy_clear_cookies">Clear all cookie data</string>
<!-- Settings summary -->
@@ -316,6 +327,16 @@
<string name="pref_privacy_clear_passwords_summary">Clear all the saved passwords</string>
<!-- Confirmation dialog message -->
<string name="pref_privacy_clear_passwords_dlg">All saved passwords will be cleared.</string>
+ <!-- Settings label -->
+ <string name="pref_privacy_request_location">Enable location</string>
+ <!-- Settings summary -->
+ <string name="pref_privacy_request_location_summary">Allow sites to request access to your location</string>
+ <!-- Settings label -->
+ <string name="pref_privacy_clear_location_requests">Clear location access</string>
+ <!-- Settings summary -->
+ <string name="pref_privacy_clear_location_requests_summary">Clear location access for all websites</string>
+ <!-- Confirmation dialog message -->
+ <string name="pref_privacy_clear_location_requests_dlg">Clear all previous location requests</string>
<!-- Settings screen, section title -->
<string name="pref_security_title">Security settings</string>
<!-- Settings label -->
@@ -361,6 +382,10 @@
<!-- Settings summary -->
<string name="pref_extras_gears_enable_summary">Applications that extend browser functionality</string>
<!-- Settings label -->
+ <string name="pref_extras_website_settings">Website Settings</string>
+ <!-- Settings summary -->
+ <string name="pref_extras_website_settings_summary">View advanced settings for individual websites</string>
+ <!-- Settings label -->
<string name="pref_extras_gears_settings">Gears settings</string>
<!-- Settings summary -->
<string name="pref_plugin_installed">Plugins list</string>
@@ -655,6 +680,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>
@@ -711,6 +739,20 @@
<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_clear_data_title">Clear Stored 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>
+ <string name="webstorage_clear_data_dialog_ok_button">Clear All</string>
+ <string name="webstorage_clear_data_dialog_cancel_button">Cancel</string>
+ <!-- Strings used in the summary of origins -->
+ <string name="webstorage_origin_summary_mb_stored">MB stored on your phone</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..f553e2b 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" />
@@ -120,6 +126,24 @@
android:dialogTitle="@string/clear"
android:dialogIcon="@android:drawable/ic_dialog_alert"/>
+ <!-- below preferences will be shown when html5 location is implemented -->
+ <!--
+
+ <CheckBoxPreference
+ android:key="request_location"
+ android:defaultValue="true"
+ android:title="@string/pref_privacy_request_location"
+ android:summary="@string/pref_privacy_request_location_summary" />
+
+ <com.android.browser.BrowserYesNoPreference
+ android:key="privacy_clear_location_requests"
+ android:title="@string/pref_privacy_clear_location_requests"
+ android:summary="@string/pref_privacy_clear_location_requests_summary"
+ android:dialogMessage="@string/pref_privacy_clear_location_requests_dlg"
+ android:dialogTitle="@string/clear"
+ android:dialogIcon="@android:drawable/ic_dialog_alert"/>
+ -->
+
</PreferenceCategory>
<PreferenceCategory
@@ -165,6 +189,11 @@
android:title="@string/pref_extras_gears_settings"
android:summary="@string/pref_extras_gears_settings_summary" />
+ <PreferenceScreen
+ android:key="website_settings"
+ android:title="@string/pref_extras_website_settings"
+ android:summary="@string/pref_extras_website_settings_summary" />
+
<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..e827a7e 100644
--- a/src/com/android/browser/AddBookmarkPage.java
+++ b/src/com/android/browser/AddBookmarkPage.java
@@ -18,18 +18,14 @@
import android.app.Activity;
import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
import android.content.Intent;
import android.content.res.Resources;
-import android.database.Cursor;
import android.net.ParseException;
import android.net.WebAddress;
import android.os.Bundle;
import android.provider.Browser;
import android.view.View;
import android.view.Window;
-import android.webkit.WebIconDatabase;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
@@ -46,11 +42,6 @@
private View mCancelButton;
private boolean mEditingExisting;
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];
private View.OnClickListener mSaveBookmark = new View.OnClickListener() {
public void onClick(View v) {
@@ -151,70 +142,7 @@
setResult(RESULT_OK, (new Intent()).setAction(
getIntent().toString()).putExtras(mMap));
} else {
- // Want to append to the beginning of the list
- long creationTime = new Date().getTime();
- SELECTION_ARGS[0] = url;
- ContentResolver cr = getContentResolver();
- Cursor c = cr.query(Browser.BOOKMARKS_URI,
- mProjection,
- WHERE_CLAUSE,
- SELECTION_ARGS,
- null);
- ContentValues map = new ContentValues();
- if (c.moveToFirst() && c.getInt(c.getColumnIndexOrThrow(
- Browser.BookmarkColumns.BOOKMARK)) == 0) {
- // This means we have been to this site but not bookmarked
- // it, so convert the history item to a bookmark
- map.put(Browser.BookmarkColumns.CREATED, creationTime);
- map.put(Browser.BookmarkColumns.TITLE, title);
- map.put(Browser.BookmarkColumns.BOOKMARK, 1);
- cr.update(Browser.BOOKMARKS_URI, map,
- "_id = " + c.getInt(0), null);
- } else {
- int count = c.getCount();
- boolean matchedTitle = false;
- for (int i = 0; i < count; i++) {
- // One or more bookmarks already exist for this site.
- // Check the names of each
- c.moveToPosition(i);
- if (c.getString(c.getColumnIndexOrThrow(
- Browser.BookmarkColumns.TITLE)).equals(title)) {
- // The old bookmark has the same name.
- // Update its creation time.
- map.put(Browser.BookmarkColumns.CREATED,
- creationTime);
- cr.update(Browser.BOOKMARKS_URI, map,
- "_id = " + c.getInt(0), null);
- matchedTitle = true;
- }
- }
- if (!matchedTitle) {
- // Adding a bookmark for a site the user has visited,
- // or a new bookmark (with a different name) for a site
- // the user has visited
- map.put(Browser.BookmarkColumns.TITLE, title);
- map.put(Browser.BookmarkColumns.URL, url);
- map.put(Browser.BookmarkColumns.CREATED, creationTime);
- map.put(Browser.BookmarkColumns.BOOKMARK, 1);
- map.put(Browser.BookmarkColumns.DATE, 0);
- int visits = 0;
- if (count > 0) {
- // The user has already bookmarked, and possibly
- // visited this site. However, they are creating
- // a new bookmark with the same url but a different
- // name. The new bookmark should have the same
- // number of visits as the already created bookmark.
- visits = c.getInt(c.getColumnIndexOrThrow(
- Browser.BookmarkColumns.VISITS));
- }
- // Bookmark starts with 3 extra visits so that it will
- // bubble up in the most visited and goto search box
- map.put(Browser.BookmarkColumns.VISITS, visits + 3);
- cr.insert(Browser.BOOKMARKS_URI, map);
- }
- }
- WebIconDatabase.getInstance().retainIconForPageUrl(url);
- c.deactivate();
+ Bookmarks.addBookmark(null, getContentResolver(), url, title);
setResult(RESULT_OK);
}
} catch (IllegalStateException e) {
diff --git a/src/com/android/browser/BookmarkGridPage.java b/src/com/android/browser/BookmarkGridPage.java
new file mode 100644
index 0000000..a9db7ac
--- /dev/null
+++ b/src/com/android/browser/BookmarkGridPage.java
@@ -0,0 +1,243 @@
+/*
+ * 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.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.ContentObserver;
+import android.database.DataSetObserver;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.Browser;
+import android.provider.Browser.BookmarkColumns;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.GridView;
+import android.widget.ImageView;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+public class BookmarkGridPage extends Activity {
+ private final static int SPACING = 10;
+ private BookmarkGrid mGridView;
+ private BookmarkGridAdapter mAdapter;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mGridView = new BookmarkGrid(this);
+ mGridView.setNumColumns(3);
+ mAdapter = new BookmarkGridAdapter(this);
+ mGridView.setAdapter(mAdapter);
+ mGridView.setFocusable(true);
+ mGridView.setFocusableInTouchMode(true);
+ mGridView.setSelector(android.R.drawable.gallery_thumb);
+ mGridView.setVerticalSpacing(SPACING);
+ mGridView.setHorizontalSpacing(SPACING);
+ setContentView(mGridView);
+ mGridView.requestFocus();
+ }
+
+ private class BookmarkGrid extends GridView {
+ public BookmarkGrid(Context context) {
+ super(context);
+ }
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ int thumbHeight = (h - 2 * (SPACING + getListPaddingTop()
+ + getListPaddingBottom())) / 3;
+ mAdapter.heightChanged(thumbHeight);
+ super.onSizeChanged(w, h, oldw, oldh);
+ }
+ }
+
+ private class BookmarkGridAdapter implements ListAdapter {
+ private ArrayList<DataSetObserver> mDataObservers;
+ private Context mContext; // Context to use to inflate views
+ private Cursor mCursor;
+ private int mThumbHeight;
+
+ public BookmarkGridAdapter(Context context) {
+ mContext = context;
+ mDataObservers = new ArrayList<DataSetObserver>();
+ String whereClause = Browser.BookmarkColumns.BOOKMARK + " != 0";
+ String orderBy = Browser.BookmarkColumns.VISITS + " DESC";
+ mCursor = managedQuery(Browser.BOOKMARKS_URI,
+ Browser.HISTORY_PROJECTION, whereClause, null, orderBy);
+ mCursor.registerContentObserver(new ChangeObserver());
+ mGridView.setOnItemClickListener(
+ new AdapterView.OnItemClickListener() {
+ public void onItemClick(AdapterView parent, View v,
+ int position, long id) {
+ mCursor.moveToPosition(position);
+ String url = mCursor.getString(
+ Browser.HISTORY_PROJECTION_URL_INDEX);
+ Intent intent = (new Intent()).setAction(url);
+ getParent().setResult(RESULT_OK, intent);
+ finish();
+ }});
+ }
+
+ void heightChanged(int newHeight) {
+ mThumbHeight = newHeight;
+ }
+
+ private class ChangeObserver extends ContentObserver {
+ public ChangeObserver() {
+ super(new Handler());
+ }
+
+ @Override
+ public boolean deliverSelfNotifications() {
+ return true;
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ BookmarkGridAdapter.this.refreshData();
+ }
+ }
+
+ void refreshData() {
+ mCursor.requery();
+ for (DataSetObserver o : mDataObservers) {
+ o.onChanged();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see android.widget.ListAdapter#areAllItemsSelectable()
+ */
+ public boolean areAllItemsEnabled() {
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see android.widget.ListAdapter#isSelectable(int)
+ */
+ public boolean isEnabled(int position) {
+ if (position >= 0 && position < mCursor.getCount()) {
+ return true;
+ }
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see android.widget.Adapter#getCount()
+ */
+ public int getCount() {
+ return mCursor.getCount();
+ }
+
+ /* (non-Javadoc)
+ * @see android.widget.Adapter#getItem(int)
+ */
+ public Object getItem(int position) {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see android.widget.Adapter#getItemId(int)
+ */
+ public long getItemId(int position) {
+ return position;
+ }
+
+ /* (non-Javadoc)
+ * @see android.widget.Adapter#getView(int, android.view.View, android.view.ViewGroup)
+ */
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View v = null;
+ if (convertView != null) {
+ v = convertView;
+ } else {
+ LayoutInflater factory = LayoutInflater.from(mContext);
+ v = factory.inflate(R.layout.bookmark_thumbnail, null);
+ }
+ ImageView thumb = (ImageView) v.findViewById(R.id.thumb);
+ TextView tv = (TextView) v.findViewById(R.id.label);
+
+ mCursor.moveToPosition(position);
+ tv.setText(mCursor.getString(
+ Browser.HISTORY_PROJECTION_TITLE_INDEX));
+ byte[] data = mCursor.getBlob(
+ Browser.HISTORY_PROJECTION_THUMBNAIL_INDEX);
+ if (data == null) {
+ // Backup is to show the favicon
+ data = mCursor.getBlob(
+ Browser.HISTORY_PROJECTION_FAVICON_INDEX);
+ thumb.setScaleType(ImageView.ScaleType.CENTER);
+ } else {
+ thumb.setScaleType(ImageView.ScaleType.FIT_XY);
+ }
+ if (data != null) {
+ thumb.setImageBitmap(
+ BitmapFactory.decodeByteArray(data, 0, data.length));
+ } else {
+ thumb.setImageResource(R.drawable.app_web_browser_sm);
+ thumb.setScaleType(ImageView.ScaleType.CENTER);
+ }
+ ViewGroup.LayoutParams lp = thumb.getLayoutParams();
+ if (lp.height != mThumbHeight) {
+ lp.height = mThumbHeight;
+ thumb.requestLayout();
+ }
+ return v;
+ }
+
+ /* (non-Javadoc)
+ * @see android.widget.Adapter#registerDataSetObserver(android.database.DataSetObserver)
+ */
+ public void registerDataSetObserver(DataSetObserver observer) {
+ mDataObservers.add(observer);
+ }
+
+ /* (non-Javadoc)
+ * @see android.widget.Adapter#hasStableIds()
+ */
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see android.widget.Adapter#unregisterDataSetObserver(android.database.DataSetObserver)
+ */
+ public void unregisterDataSetObserver(DataSetObserver observer) {
+ mDataObservers.remove(observer);
+ }
+
+ public int getItemViewType(int position) {
+ return 0;
+ }
+
+ public int getViewTypeCount() {
+ return 1;
+ }
+
+ public boolean isEmpty() {
+ return getCount() == 0;
+ }
+ }
+}
diff --git a/src/com/android/browser/Bookmarks.java b/src/com/android/browser/Bookmarks.java
new file mode 100644
index 0000000..97e6b20
--- /dev/null
+++ b/src/com/android/browser/Bookmarks.java
@@ -0,0 +1,196 @@
+/*
+ * 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.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.Browser;
+import android.util.Log;
+import android.webkit.WebIconDatabase;
+import android.widget.Toast;
+
+import java.util.Date;
+
+/**
+ * This class is purely to have a common place for adding/deleting bookmarks.
+ */
+/* package */ class Bookmarks {
+ private static final String WHERE_CLAUSE
+ = "url = ? OR url = ? OR url = ? OR url = ?";
+ private static final String WHERE_CLAUSE_SECURE = "url = ? OR url = ?";
+
+ private static String[] SELECTION_ARGS;
+
+ /**
+ * Add a bookmark to the database.
+ * @param context Context of the calling Activity. This is used to make
+ * Toast confirming that the bookmark has been added. If the
+ * caller provides null, the Toast will not be shown.
+ * @param cr The ContentResolver being used to add the bookmark to the db.
+ * @param url URL of the website to be bookmarked.
+ * @param name Provided name for the bookmark.
+ */
+ /* package */ static void addBookmark(Context context,
+ ContentResolver cr, String url, String name) {
+ // Want to append to the beginning of the list
+ long creationTime = new Date().getTime();
+ // 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;
+ String compareString = url;
+ if (compareString.startsWith("http://")) {
+ compareString = compareString.substring(7);
+ } else if (compareString.startsWith("https://")) {
+ compareString = compareString.substring(8);
+ secure = true;
+ }
+ if (compareString.startsWith("www.")) {
+ compareString = compareString.substring(4);
+ }
+ if (secure) {
+ SELECTION_ARGS = new String[2];
+ SELECTION_ARGS[0] = "https://" + compareString;
+ SELECTION_ARGS[1] = "https://www." + compareString;
+ } else {
+ SELECTION_ARGS = new String[4];
+ SELECTION_ARGS[0] = compareString;
+ SELECTION_ARGS[1] = "www." + compareString;
+ SELECTION_ARGS[2] = "http://" + compareString;
+ SELECTION_ARGS[3] = "http://" + SELECTION_ARGS[1];
+ }
+ Cursor cursor = cr.query(Browser.BOOKMARKS_URI,
+ Browser.HISTORY_PROJECTION,
+ secure ? WHERE_CLAUSE_SECURE : WHERE_CLAUSE,
+ SELECTION_ARGS,
+ null);
+ ContentValues map = new ContentValues();
+ if (cursor.moveToFirst() && cursor.getInt(
+ Browser.HISTORY_PROJECTION_BOOKMARK_INDEX) == 0) {
+ // This means we have been to this site but not bookmarked
+ // it, so convert the history item to a bookmark
+ map.put(Browser.BookmarkColumns.CREATED, creationTime);
+ map.put(Browser.BookmarkColumns.TITLE, name);
+ map.put(Browser.BookmarkColumns.BOOKMARK, 1);
+ cr.update(Browser.BOOKMARKS_URI, map,
+ "_id = " + cursor.getInt(0), null);
+ } else {
+ int count = cursor.getCount();
+ boolean matchedTitle = false;
+ for (int i = 0; i < count; i++) {
+ // One or more bookmarks already exist for this site.
+ // Check the names of each
+ cursor.moveToPosition(i);
+ if (cursor.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX)
+ .equals(name)) {
+ // The old bookmark has the same name.
+ // Update its creation time.
+ map.put(Browser.BookmarkColumns.CREATED,
+ creationTime);
+ cr.update(Browser.BOOKMARKS_URI, map,
+ "_id = " + cursor.getInt(0), null);
+ matchedTitle = true;
+ break;
+ }
+ }
+ if (!matchedTitle) {
+ // Adding a bookmark for a site the user has visited,
+ // or a new bookmark (with a different name) for a site
+ // the user has visited
+ map.put(Browser.BookmarkColumns.TITLE, name);
+ map.put(Browser.BookmarkColumns.URL, url);
+ map.put(Browser.BookmarkColumns.CREATED, creationTime);
+ map.put(Browser.BookmarkColumns.BOOKMARK, 1);
+ map.put(Browser.BookmarkColumns.DATE, 0);
+ int visits = 0;
+ if (count > 0) {
+ // The user has already bookmarked, and possibly
+ // visited this site. However, they are creating
+ // a new bookmark with the same url but a different
+ // name. The new bookmark should have the same
+ // number of visits as the already created bookmark.
+ visits = cursor.getInt(
+ Browser.HISTORY_PROJECTION_VISITS_INDEX);
+ }
+ // Bookmark starts with 3 extra visits so that it will
+ // bubble up in the most visited and goto search box
+ map.put(Browser.BookmarkColumns.VISITS, visits + 3);
+ cr.insert(Browser.BOOKMARKS_URI, map);
+ }
+ }
+ WebIconDatabase.getInstance().retainIconForPageUrl(url);
+ cursor.deactivate();
+ if (context != null) {
+ Toast.makeText(context, R.string.added_to_bookmarks,
+ Toast.LENGTH_LONG).show();
+ }
+ }
+
+ /**
+ * Remove a bookmark from the database. If the url is a visited site, it
+ * will remain in the database, but only as a history item, and not as a
+ * bookmarked site.
+ * @param context Context of the calling Activity. This is used to make
+ * Toast confirming that the bookmark has been removed. If the
+ * caller provides null, the Toast will not be shown.
+ * @param cr The ContentResolver being used to remove the bookmark.
+ * @param url URL of the website to be removed.
+ */
+ /* package */ static void removeFromBookmarks(Context context,
+ ContentResolver cr, String url) {
+ Cursor cursor = cr.query(
+ Browser.BOOKMARKS_URI,
+ Browser.HISTORY_PROJECTION,
+ "url = ?",
+ new String[] { url },
+ null);
+ boolean first = cursor.moveToFirst();
+ // Should be in the database no matter what
+ if (!first) {
+ throw new AssertionError("URL is not in the database!");
+ }
+ // Remove from bookmarks
+ WebIconDatabase.getInstance().releaseIconForPageUrl(url);
+ Uri uri = ContentUris.withAppendedId(Browser.BOOKMARKS_URI,
+ cursor.getInt(Browser.HISTORY_PROJECTION_ID_INDEX));
+ int numVisits = cursor.getInt(
+ Browser.HISTORY_PROJECTION_VISITS_INDEX);
+ if (0 == numVisits) {
+ cr.delete(uri, null, null);
+ } else {
+ // It is no longer a bookmark, but it is still a visited
+ // site.
+ ContentValues values = new ContentValues();
+ values.put(Browser.BookmarkColumns.BOOKMARK, 0);
+ try {
+ cr.update(uri, values, null, null);
+ } catch (IllegalStateException e) {
+ Log.e("removeFromBookmarks", "no database!");
+ }
+ }
+ if (context != null) {
+ Toast.makeText(context, R.string.removed_from_bookmarks,
+ Toast.LENGTH_LONG).show();
+ }
+ cursor.deactivate();
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java
index eddcb28..b676437 100644
--- a/src/com/android/browser/BrowserActivity.java
+++ b/src/com/android/browser/BrowserActivity.java
@@ -29,6 +29,7 @@
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
+import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
@@ -36,6 +37,7 @@
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.DialogInterface.OnCancelListener;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.AssetManager;
@@ -113,11 +115,13 @@
import android.webkit.CookieSyncManager;
import android.webkit.DownloadListener;
import android.webkit.HttpAuthHandler;
+import android.webkit.PluginManager;
import android.webkit.SslErrorHandler;
import android.webkit.URLUtil;
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;
@@ -127,6 +131,7 @@
import android.widget.Toast;
import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -166,6 +171,8 @@
private SensorManager mSensorManager = null;
+ private WebStorage.QuotaUpdater mWebStorageQuotaUpdater = null;
+
// These are single-character shortcuts for searching popular sources.
private static final int SHORTCUT_INVALID = 0;
private static final int SHORTCUT_GOOGLE_SEARCH = 1;
@@ -590,11 +597,6 @@
}
copyBuildInfos();
-
- // Refresh the plugin list.
- if (mTabControl.getCurrentWebView() != null) {
- mTabControl.getCurrentWebView().refreshPlugins(false);
- }
} catch (IOException e) {
Log.e(TAG, "IO Exception: " + e);
}
@@ -641,16 +643,22 @@
}
}
+ // Flag to enable the touchable browser bar with buttons
+ private final boolean CUSTOM_BROWSER_BAR = true;
+
@Override public void onCreate(Bundle icicle) {
if (LOGV_ENABLED) {
Log.v(LOGTAG, this + " onStart");
}
super.onCreate(icicle);
- this.requestWindowFeature(Window.FEATURE_LEFT_ICON);
- this.requestWindowFeature(Window.FEATURE_RIGHT_ICON);
- this.requestWindowFeature(Window.FEATURE_PROGRESS);
- this.requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
-
+ if (CUSTOM_BROWSER_BAR) {
+ this.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ } else {
+ this.requestWindowFeature(Window.FEATURE_LEFT_ICON);
+ this.requestWindowFeature(Window.FEATURE_RIGHT_ICON);
+ this.requestWindowFeature(Window.FEATURE_PROGRESS);
+ this.requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+ }
// test the browser in OpenGL
// requestWindowFeature(Window.FEATURE_OPENGL);
@@ -678,8 +686,21 @@
mGenericFavicon = getResources().getDrawable(
R.drawable.app_web_browser_sm);
- mContentView = (FrameLayout) getWindow().getDecorView().findViewById(
- com.android.internal.R.id.content);
+ FrameLayout frameLayout = (FrameLayout) getWindow().getDecorView()
+ .findViewById(com.android.internal.R.id.content);
+ if (CUSTOM_BROWSER_BAR) {
+ // This LinearLayout will hold the title bar and a FrameLayout, which
+ // holds everything else.
+ LinearLayout linearLayout = (LinearLayout) LayoutInflater.from(this)
+ .inflate(R.layout.custom_screen, null);
+ mTitleBar = (TitleBar) linearLayout.findViewById(R.id.title_bar);
+ mTitleBar.setBrowserActivity(this);
+ mContentView = (FrameLayout) linearLayout.findViewById(
+ R.id.main_content);
+ frameLayout.addView(linearLayout, COVER_SCREEN_PARAMS);
+ } else {
+ mContentView = frameLayout;
+ }
// Create the tab control and our initial tab
mTabControl = new TabControl(this);
@@ -772,6 +793,52 @@
}
}
};
+
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ mPackageInstallationReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ final String packageName = intent.getData()
+ .getSchemeSpecificPart();
+ final boolean replacing = intent.getBooleanExtra(
+ Intent.EXTRA_REPLACING, false);
+ if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && replacing) {
+ // if it is replacing, refreshPlugins() when adding
+ return;
+ }
+ PackageManager pm = BrowserActivity.this.getPackageManager();
+ PackageInfo pkgInfo = null;
+ try {
+ pkgInfo = pm.getPackageInfo(packageName,
+ PackageManager.GET_PERMISSIONS);
+ } catch (PackageManager.NameNotFoundException e) {
+ return;
+ }
+ if (pkgInfo != null) {
+ String permissions[] = pkgInfo.requestedPermissions;
+ if (permissions == null) {
+ return;
+ }
+ boolean permissionOk = false;
+ for (String permit : permissions) {
+ if (PluginManager.PLUGIN_PERMISSION.equals(permit)) {
+ permissionOk = true;
+ break;
+ }
+ }
+ if (permissionOk) {
+ PluginManager.getInstance(BrowserActivity.this)
+ .refreshPlugins(
+ Intent.ACTION_PACKAGE_ADDED
+ .equals(action));
+ }
+ }
+ }
+ };
+ registerReceiver(mPackageInstallationReceiver, filter);
}
@Override
@@ -836,8 +903,8 @@
} else {
if (mTabOverview != null && mAnimationCount == 0) {
sendAnimateFromOverview(appTab, false,
- needsLoad ? urlData : EMPTY_URL_DATA, TAB_OVERVIEW_DELAY,
- null);
+ needsLoad ? urlData : EMPTY_URL_DATA,
+ TAB_OVERVIEW_DELAY, null);
} else {
// If the tab was the current tab, we have to attach
// it to the view system again.
@@ -1147,8 +1214,9 @@
return;
}
+ mTabControl.resumeCurrentTab();
mActivityInPause = false;
- resumeWebView();
+ resumeWebViewTimers();
if (mWakeLock.isHeld()) {
mHandler.removeMessages(RELEASE_WAKELOCK);
@@ -1206,8 +1274,9 @@
return;
}
+ mTabControl.pauseCurrentTab();
mActivityInPause = true;
- if (mTabControl.getCurrentIndex() >= 0 && !pauseWebView()) {
+ if (mTabControl.getCurrentIndex() >= 0 && !pauseWebViewTimers()) {
mWakeLock.acquire();
mHandler.sendMessageDelayed(mHandler
.obtainMessage(RELEASE_WAKELOCK), WAKELOCK_TIMEOUT);
@@ -1237,8 +1306,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();
@@ -1256,6 +1327,8 @@
// "com.android.masfproxyservice",
// "com.android.masfproxyservice.MasfProxyService"));
//stopService(proxyServiceIntent);
+
+ unregisterReceiver(mPackageInstallationReceiver);
}
@Override
@@ -1304,7 +1377,7 @@
mTabControl.freeMemory();
}
- private boolean resumeWebView() {
+ private boolean resumeWebViewTimers() {
if ((!mActivityInPause && !mPageStarted) ||
(mActivityInPause && mPageStarted)) {
CookieSyncManager.getInstance().startSync();
@@ -1318,7 +1391,7 @@
}
}
- private boolean pauseWebView() {
+ private boolean pauseWebViewTimers() {
if (mActivityInPause && !mPageStarted) {
CookieSyncManager.getInstance().stopSync();
WebView w = mTabControl.getCurrentWebView();
@@ -2017,9 +2090,9 @@
// A wrapper function of {@link #openTabAndShow(UrlData, Message, boolean, String)}
// that accepts url as string.
- private void openTabAndShow(String url, final Message msg,
+ private TabControl.Tab openTabAndShow(String url, final Message msg,
boolean closeOnExit, String appId) {
- openTabAndShow(new UrlData(url), msg, closeOnExit, appId);
+ return openTabAndShow(new UrlData(url), msg, closeOnExit, appId);
}
// This method does a ton of stuff. It will attempt to create a new tab
@@ -2030,7 +2103,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(UrlData urlData, final Message msg,
+ private TabControl.Tab openTabAndShow(UrlData urlData, final Message msg,
boolean closeOnExit, String appId) {
final boolean newTab = mTabControl.getTabCount() != TabControl.MAX_TABS;
final TabControl.Tab currentTab = mTabControl.getCurrentTab();
@@ -2059,9 +2132,10 @@
}
// Animate from the Tab overview after any animations have
// finished.
- sendAnimateFromOverview(
- mTabControl.createNewTab(closeOnExit, appId, urlData.mUrl), true,
- urlData, delay, msg);
+ final TabControl.Tab tab = mTabControl.createNewTab(
+ closeOnExit, appId, urlData.mUrl);
+ sendAnimateFromOverview(tab, true, urlData, delay, msg);
+ return tab;
}
} else if (!urlData.isEmpty()) {
// We should not have a msg here.
@@ -2076,6 +2150,7 @@
urlData.loadIn(currentTab.getWebView());
}
}
+ return currentTab;
}
private Animation createTabAnimation(final AnimatingView view,
@@ -2297,14 +2372,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);
}
}
@@ -2404,7 +2480,11 @@
// While the tab overview is animating or being shown, block changes
// to the title.
if (mAnimationCount == 0 && mTabOverview == null) {
- setTitle(buildUrlTitle(url, title));
+ if (CUSTOM_BROWSER_BAR) {
+ mTitleBar.setTitleAndUrl(title, url);
+ } else {
+ setTitle(buildUrlTitle(url, title));
+ }
}
}
@@ -2445,7 +2525,7 @@
* or an empty string if, for example, the URL in question is a
* file:// URL with no hostname.
*/
- private static String buildTitleUrl(String url) {
+ /* package */ static String buildTitleUrl(String url) {
String titleUrl = null;
if (url != null) {
@@ -2481,18 +2561,34 @@
if (mAnimationCount > 0 || mTabOverview != null) {
return;
}
- Drawable[] array = new Drawable[2];
- PaintDrawable p = new PaintDrawable(Color.WHITE);
- p.setCornerRadius(3f);
- array[0] = p;
- if (icon == null) {
- array[1] = mGenericFavicon;
+ if (CUSTOM_BROWSER_BAR) {
+ Drawable[] array = new Drawable[3];
+ array[0] = new PaintDrawable(Color.BLACK);
+ PaintDrawable p = new PaintDrawable(Color.WHITE);
+ array[1] = p;
+ 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);
+ mTitleBar.setFavicon(d);
} else {
- array[1] = new BitmapDrawable(icon);
+ Drawable[] array = new Drawable[2];
+ PaintDrawable p = new PaintDrawable(Color.WHITE);
+ p.setCornerRadius(3f);
+ array[0] = p;
+ if (icon == null) {
+ array[1] = mGenericFavicon;
+ } else {
+ array[1] = new BitmapDrawable(icon);
+ }
+ LayerDrawable d = new LayerDrawable(array);
+ d.setLayerInset(1, 2, 2, 2, 2);
+ getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, d);
}
- LayerDrawable d = new LayerDrawable(array);
- d.setLayerInset(1, 2, 2, 2, 2);
- getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, d);
}
/**
@@ -2570,9 +2666,9 @@
finish();
return;
}
- // call pauseWebView() now, we won't be able to call it in
- // onPause() as the WebView won't be valid.
- pauseWebView();
+ // call pauseWebViewTimers() now, we won't be able to call
+ // it in onPause() as the WebView won't be valid.
+ pauseWebViewTimers();
removeTabFromContentView(current);
mTabControl.removeTab(current);
}
@@ -2738,7 +2834,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,
@@ -2863,8 +2964,9 @@
if (!mPageStarted) {
mPageStarted = true;
- // if onResume() has been called, resumeWebView() does nothing.
- resumeWebView();
+ // if onResume() has been called, resumeWebViewTimers() does
+ // nothing.
+ resumeWebViewTimers();
}
// reset sync timer to avoid sync starts during loading a page
@@ -2899,6 +3001,47 @@
// Update the lock icon image only once we are done loading
updateLockIconImage(mLockIconType);
+ // If this is a bookmarked site, add a screenshot to the database.
+ // FIXME: When should we update? Every time?
+ if (url != null) {
+ // copied from BrowserBookmarksAdapter
+ int query = url.indexOf('?');
+ String noQuery = url;
+ if (query != -1) {
+ noQuery = url.substring(0, query);
+ }
+ String URL = noQuery + '?';
+ String[] selArgs = new String[] { noQuery, URL };
+ final String where = "(url == ? OR url GLOB ? || '*') AND bookmark == 1";
+ final String[] projection = new String[] { Browser.BookmarkColumns._ID };
+ ContentResolver cr = getContentResolver();
+ final Cursor c = cr.query(Browser.BOOKMARKS_URI, projection, where, selArgs, null);
+ boolean succeed = c.moveToFirst();
+ ContentValues values = null;
+ while (succeed) {
+ if (values == null) {
+ final ByteArrayOutputStream os = new ByteArrayOutputStream();
+ Picture thumbnail = view.capturePicture();
+ // Height was arbitrarily chosen
+ Bitmap bm = Bitmap.createBitmap(100, 100,
+ Bitmap.Config.ARGB_4444);
+ Canvas canvas = new Canvas(bm);
+ // Scale chosen to be about one third, since we want
+ // roughly three rows/columns for bookmark page
+ canvas.scale(.3f, .3f);
+ thumbnail.draw(canvas);
+ bm.compress(Bitmap.CompressFormat.PNG, 100, os);
+ values = new ContentValues();
+ values.put(Browser.BookmarkColumns.THUMBNAIL,
+ os.toByteArray());
+ }
+ cr.update(ContentUris.withAppendedId(Browser.BOOKMARKS_URI,
+ c.getInt(0)), values, null, null);
+ succeed = c.moveToNext();
+ }
+ c.close();
+ }
+
// Performance probe
if (false) {
long[] sysCpu = new long[7];
@@ -2985,9 +3128,9 @@
if (mPageStarted) {
mPageStarted = false;
- // pauseWebView() will do nothing and return false if onPause()
- // is not called yet.
- if (pauseWebView()) {
+ // pauseWebViewTimers() will do nothing and return false if
+ // onPause() is not called yet.
+ if (pauseWebViewTimers()) {
if (mWakeLock.isHeld()) {
mHandler.removeMessages(RELEASE_WAKELOCK);
mWakeLock.release();
@@ -3423,8 +3566,11 @@
// openTabAndShow will dispatch the message after creating the
// new WebView. This will prevent another request from coming
// in during the animation.
- openTabAndShow(EMPTY_URL_DATA, msg, false, null);
- parent.addChildTab(mTabControl.getCurrentTab());
+ final TabControl.Tab newTab =
+ openTabAndShow(EMPTY_URL_DATA, msg, false, null);
+ if (newTab != parent) {
+ parent.addChildTab(newTab);
+ }
WebView.WebViewTransport transport =
(WebView.WebViewTransport) msg.obj;
transport.setWebView(mTabControl.getCurrentWebView());
@@ -3530,8 +3676,13 @@
// Block progress updates to the title bar while the tab overview
// is animating or being displayed.
if (mAnimationCount == 0 && mTabOverview == null) {
- getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
- newProgress * 100);
+ if (CUSTOM_BROWSER_BAR) {
+ mTitleBar.setProgress(newProgress);
+ } else {
+ getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
+ newProgress * 100);
+
+ }
}
if (newProgress == 100) {
@@ -3565,6 +3716,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://")) {
@@ -3579,15 +3732,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) {
@@ -3601,6 +3751,50 @@
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);
+ }
+
+ /* Adds a JavaScript error message to the system log.
+ * @param message The error message to report.
+ * @param lineNumber The line number of the error.
+ * @param sourceID The name of the source file that caused the error.
+ */
+ @Override
+ public void addMessageToConsole(String message, int lineNumber, String sourceID) {
+ Log.w(LOGTAG, "Console: " + message + " (" + sourceID + ":" + lineNumber + ")");
+ }
+
};
/**
@@ -3716,19 +3910,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
@@ -3794,7 +3988,11 @@
// If the tab overview is animating or being shown, do not update the
// lock icon.
if (mAnimationCount == 0 && mTabOverview == null) {
- getWindow().setFeatureDrawable(Window.FEATURE_RIGHT_ICON, d);
+ if (CUSTOM_BROWSER_BAR) {
+ mTitleBar.setLock(d);
+ } else {
+ getWindow().setFeatureDrawable(Window.FEATURE_RIGHT_ICON, d);
+ }
}
}
@@ -4295,8 +4493,8 @@
// middle of an animation, animate away from it to the
// current tab.
if (mTabOverview != null && mAnimationCount == 0) {
- sendAnimateFromOverview(currentTab, false, new UrlData(data),
- TAB_OVERVIEW_DELAY, null);
+ sendAnimateFromOverview(currentTab, false,
+ new UrlData(data), TAB_OVERVIEW_DELAY, null);
} else {
dismissSubWindow(currentTab);
if (data != null && data.length() != 0) {
@@ -4306,6 +4504,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;
}
@@ -4346,8 +4552,8 @@
// was clicked on.
if (mTabControl.getTabCount() == 0) {
current = mTabControl.createNewTab();
- sendAnimateFromOverview(current, true,
- new UrlData(mSettings.getHomePage()), TAB_OVERVIEW_DELAY, null);
+ sendAnimateFromOverview(current, true, new UrlData(
+ mSettings.getHomePage()), TAB_OVERVIEW_DELAY, null);
} else {
final int index = position > 0 ? (position - 1) : 0;
current = mTabControl.getTab(index);
@@ -4383,8 +4589,8 @@
if (index == ImageGrid.NEW_TAB) {
openTabAndShow(mSettings.getHomePage(), null, false, null);
} else {
- sendAnimateFromOverview(mTabControl.getTab(index),
- false, EMPTY_URL_DATA, 0, null);
+ sendAnimateFromOverview(mTabControl.getTab(index), false,
+ EMPTY_URL_DATA, 0, null);
}
}
}
@@ -4406,13 +4612,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
@@ -4486,16 +4698,20 @@
mAnimationCount++;
// Always change the title bar to the window overview title while
// animating.
- getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, null);
- getWindow().setFeatureDrawable(Window.FEATURE_RIGHT_ICON, null);
- getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
- Window.PROGRESS_VISIBILITY_OFF);
- setTitle(R.string.tab_picker_title);
+ if (CUSTOM_BROWSER_BAR) {
+ mTitleBar.setToTabPicker();
+ } else {
+ getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, null);
+ getWindow().setFeatureDrawable(Window.FEATURE_RIGHT_ICON, null);
+ getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
+ Window.PROGRESS_VISIBILITY_OFF);
+ setTitle(R.string.tab_picker_title);
+ }
// Make the menu empty until the animation completes.
mMenuState = EMPTY_MENU;
}
- private void bookmarksOrHistoryPicker(boolean startWithHistory) {
+ /* package */ void bookmarksOrHistoryPicker(boolean startWithHistory) {
WebView current = mTabControl.getCurrentWebView();
if (current == null) {
return;
@@ -4571,7 +4787,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):\\/\\/" +
@@ -4826,6 +5042,8 @@
private Toast mStopToast;
+ private TitleBar mTitleBar;
+
// Used during animations to prevent other animations from being triggered.
// A count is used since the animation to and from the Window overview can
// overlap. A count of 0 means no animation where a count of > 0 means
@@ -4843,10 +5061,13 @@
private IntentFilter mNetworkStateChangedFilter;
private BroadcastReceiver mNetworkStateIntentReceiver;
+ private BroadcastReceiver mPackageInstallationReceiver;
+
// 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/BrowserBookmarksAdapter.java b/src/com/android/browser/BrowserBookmarksAdapter.java
index 27782e0..c403b44 100644
--- a/src/com/android/browser/BrowserBookmarksAdapter.java
+++ b/src/com/android/browser/BrowserBookmarksAdapter.java
@@ -40,8 +40,6 @@
class BrowserBookmarksAdapter extends BaseAdapter {
- private final String LOGTAG = "Bookmarks";
-
private String mCurrentPage;
private Cursor mCursor;
private int mCount;
@@ -181,18 +179,7 @@
}
mCursor.moveToPosition(position- mExtraOffset);
String url = mCursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX);
- WebIconDatabase.getInstance().releaseIconForPageUrl(url);
- Uri uri = ContentUris.withAppendedId(Browser.BOOKMARKS_URI, mCursor
- .getInt(Browser.HISTORY_PROJECTION_ID_INDEX));
- int numVisits = mCursor.getInt(Browser.HISTORY_PROJECTION_VISITS_INDEX);
- if (0 == numVisits) {
- mContentResolver.delete(uri, null, null);
- } else {
- // It is no longer a bookmark, but it is still a visited site.
- ContentValues values = new ContentValues();
- values.put(Browser.BookmarkColumns.BOOKMARK, 0);
- mContentResolver.update(uri, values, null, null);
- }
+ Bookmarks.removeFromBookmarks(null, mContentResolver, url);
refreshList();
}
diff --git a/src/com/android/browser/BrowserBookmarksPage.java b/src/com/android/browser/BrowserBookmarksPage.java
index dd34c14..45fca87 100644
--- a/src/com/android/browser/BrowserBookmarksPage.java
+++ b/src/com/android/browser/BrowserBookmarksPage.java
@@ -44,6 +44,7 @@
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.AdapterView;
import android.widget.ListView;
+import android.widget.Toast;
/**
* View showing the user's bookmarks in the browser.
@@ -107,7 +108,13 @@
break;
case R.id.copy_url_context_menu_id:
copy(getUrl(i.position));
-
+ break;
+ case R.id.homepage_context_menu_id:
+ BrowserSettings.getInstance().setHomePage(this,
+ getUrl(i.position));
+ Toast.makeText(this, R.string.homepage_set,
+ Toast.LENGTH_LONG).show();
+ break;
default:
return super.onContextItemSelected(item);
}
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..e333416 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;
@@ -48,6 +49,7 @@
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
import android.widget.TextView;
+import android.widget.Toast;
import java.util.List;
import java.util.Vector;
@@ -110,8 +112,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);
@@ -222,6 +223,11 @@
Browser.deleteFromHistory(getContentResolver(), url);
mAdapter.refreshData();
return true;
+ case R.id.homepage_context_menu_id:
+ BrowserSettings.getInstance().setHomePage(this, url);
+ Toast.makeText(this, R.string.homepage_set,
+ Toast.LENGTH_LONG).show();
+ return true;
default:
break;
}
@@ -267,18 +273,25 @@
// Array for each of our bins. Each entry represents how many items are
// in that bin.
- int mItemMap[];
+ private int mItemMap[];
// This is our GroupCount. We will have at most DateSorter.DAY_COUNT
// bins, less if the user has no items in one or more bins.
- int mNumberOfBins;
- Vector<DataSetObserver> mObservers;
- Cursor mCursor;
+ private int mNumberOfBins;
+ private Vector<DataSetObserver> mObservers;
+ private Cursor mCursor;
HistoryAdapter() {
mObservers = new Vector<DataSetObserver>();
- String whereClause = Browser.BookmarkColumns.VISITS + " > 0 ";
- String orderBy = Browser.BookmarkColumns.DATE + " DESC";
+ final String whereClause = Browser.BookmarkColumns.VISITS + " > 0"
+ // In AddBookmarkPage, where we save new bookmarks, we add
+ // three visits to newly created bookmarks, so that
+ // bookmarks that have not been visited will show up in the
+ // most visited, and higher in the goto search box.
+ // However, this puts the site in the history, unless we
+ // ignore sites with a DATE of 0, which the next line does.
+ + " AND " + Browser.BookmarkColumns.DATE + " > 0";
+ final String orderBy = Browser.BookmarkColumns.DATE + " DESC";
mCursor = managedQuery(
Browser.BOOKMARKS_URI,
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..3a9505f 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,28 @@
e = findPreference(BrowserSettings.PREF_GEARS_SETTINGS);
e.setOnPreferenceClickListener(this);
+
+ PreferenceScreen manageDatabases = (PreferenceScreen)
+ findPreference(BrowserSettings.PREF_WEBSITE_SETTINGS);
+ Intent intent = new Intent(this, WebsiteSettingsActivity.class);
+ manageDatabases.setIntent(intent);
+ }
+
+ /*
+ * We need to set the manageDatabases PreferenceScreen state
+ * in the onResume(), as the number of origins with databases
+ * could have changed after calling the WebsiteSettingsActivity.
+ */
+ @Override
+ protected void onResume() {
+ super.onResume();
+ PreferenceScreen manageDatabases = (PreferenceScreen)
+ findPreference(BrowserSettings.PREF_WEBSITE_SETTINGS);
+ manageDatabases.setEnabled(false);
+ Vector origins = WebStorage.getInstance().getOrigins();
+ if ((origins != null) && (origins.size() > 0)) {
+ manageDatabases.setEnabled(true);
+ }
}
@Override
diff --git a/src/com/android/browser/BrowserProvider.java b/src/com/android/browser/BrowserProvider.java
index 4747190..7a1e5cb 100644
--- a/src/com/android/browser/BrowserProvider.java
+++ b/src/com/android/browser/BrowserProvider.java
@@ -96,6 +96,8 @@
private static final int MAX_SUGGESTION_SHORT_ENTRIES = 3;
private static final int MAX_SUGGESTION_LONG_ENTRIES = 6;
+ private static final String MAX_SUGGESTION_LONG_ENTRIES_STRING =
+ Integer.valueOf(MAX_SUGGESTION_LONG_ENTRIES).toString();
// make sure that these match the index of TABLE_NAMES
private static final int URI_MATCH_BOOKMARKS = 0;
@@ -143,7 +145,8 @@
// 15 -> 17 Set it up for the SearchManager
// 17 -> 18 Added favicon in bookmarks table for Home shortcuts
// 18 -> 19 Remove labels table
- private static final int DATABASE_VERSION = 19;
+ // 19 -> 20 Added thumbnail
+ private static final int DATABASE_VERSION = 20;
// Regular expression which matches http://, followed by some stuff, followed by
// optionally a trailing slash, all matched as separate groups.
@@ -214,7 +217,8 @@
"created LONG," +
"description TEXT," +
"bookmark INTEGER," +
- "favicon BLOB DEFAULT NULL" +
+ "favicon BLOB DEFAULT NULL," +
+ "thumbnail BLOB DEFAULT NULL" +
");");
final CharSequence[] bookmarks = mContext.getResources()
@@ -241,9 +245,12 @@
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
- + newVersion + ", which will destroy all old data");
+ + newVersion);
if (oldVersion == 18) {
db.execSQL("DROP TABLE IF EXISTS labels");
+ }
+ if (oldVersion <= 19) {
+ db.execSQL("ALTER TABLE bookmarks ADD COLUMN thumbnail BLOB DEFAULT NULL;");
} else {
db.execSQL("DROP TABLE IF EXISTS bookmarks");
db.execSQL("DROP TABLE IF EXISTS searches");
@@ -470,22 +477,22 @@
case SUGGEST_COLUMN_ICON_1_ID:
if (mHistoryCount > mPos) {
if (mHistoryCursor.getInt(3) == 1) {
- return new Integer(
+ return Integer.valueOf(
R.drawable.ic_search_category_bookmark)
.toString();
} else {
- return new Integer(
+ return Integer.valueOf(
R.drawable.ic_search_category_history)
.toString();
}
} else {
- return new Integer(
+ return Integer.valueOf(
R.drawable.ic_search_category_suggest)
.toString();
}
case SUGGEST_COLUMN_ICON_2_ID:
- return new String("0");
+ return "0";
case SUGGEST_COLUMN_QUERY_ID:
if (mHistoryCount > mPos) {
@@ -640,7 +647,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;
@@ -658,8 +666,7 @@
Cursor c = db.query(TABLE_NAMES[URI_MATCH_BOOKMARKS],
SUGGEST_PROJECTION, suggestSelection, myArgs, null, null,
- ORDER_BY,
- (new Integer(MAX_SUGGESTION_LONG_ENTRIES)).toString());
+ ORDER_BY, MAX_SUGGESTION_LONG_ENTRIES_STRING);
if (match == URI_MATCH_BOOKMARKS_SUGGEST
|| Regex.WEB_URL_PATTERN.matcher(selectionArgs[0]).matches()) {
diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java
index 68438b3..2d2d39b 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,7 @@
*/
class BrowserSettings extends Observable {
- // Public variables for settings
+ // Private variables for settings
// NOTE: these defaults need to be kept in sync with the XML
// until the performance of PreferenceManager.setDefaultValues()
// is improved.
@@ -70,7 +74,17 @@
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 boolean domStorageEnabled = true;
+
+ private final static String TAG = "BrowserSettings";
// Development settings
public WebSettings.LayoutAlgorithm layoutAlgorithm =
@@ -100,10 +114,13 @@
"privacy_clear_form_data";
public final static String PREF_CLEAR_PASSWORDS =
"privacy_clear_passwords";
+ 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_WEBSITE_SETTINGS = "website_settings";
public final static String PREF_TEXT_SIZE = "text_size";
public final static String PREF_DEFAULT_TEXT_ENCODING =
"default_text_encoding";
@@ -159,7 +176,6 @@
s.setLoadsImagesAutomatically(b.loadsImagesAutomatically);
s.setJavaScriptEnabled(b.javaScriptEnabled);
s.setPluginsEnabled(b.pluginsEnabled);
- s.setPluginsPath(b.pluginsPath);
s.setJavaScriptCanOpenWindowsAutomatically(
b.javaScriptCanOpenWindowsAutomatically);
s.setDefaultTextEncodingName(b.defaultTextEncodingName);
@@ -179,6 +195,15 @@
s.setSupportMultipleWindows(true);
// Turn off file access
s.setAllowFileAccess(false);
+
+ s.setDatabasePath(b.databasePath);
+ s.setDatabaseEnabled(b.databaseEnabled);
+ s.setDomStorageEnabled(b.domStorageEnabled);
+ s.setWebStorageDefaultQuota(b.webStorageDefaultQuota);
+
+ // Turn on Application Caches.
+ s.setAppCachePath(b.appCachePath);
+ s.setAppCacheEnabled(b.appCacheEnabled);
}
}
@@ -198,6 +223,10 @@
// 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 = getFactoryResetHomeUrl(ctx);
@@ -220,6 +249,15 @@
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);
+ domStorageEnabled = p.getBoolean("enable_domstorage",
+ domStorageEnabled);
+ appCachePath = p.getString("appcache_path", appCachePath);
javaScriptCanOpenWindowsAutomatically = !p.getBoolean(
"block_popup_windows",
!javaScriptCanOpenWindowsAutomatically);
@@ -237,6 +275,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,14 +479,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_WEBSITE_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);
// reset homeUrl
- setHomePage(context, getFactoryResetHomeUrl(context));
+ setHomePage(ctx, getFactoryResetHomeUrl(ctx));
}
private String getFactoryResetHomeUrl(Context context) {
diff --git a/src/com/android/browser/BrowserYesNoPreference.java b/src/com/android/browser/BrowserYesNoPreference.java
index 65cde71..ae93882 100644
--- a/src/com/android/browser/BrowserYesNoPreference.java
+++ b/src/com/android/browser/BrowserYesNoPreference.java
@@ -38,6 +38,7 @@
Context context = getContext();
if (BrowserSettings.PREF_CLEAR_CACHE.equals(getKey())) {
BrowserSettings.getInstance().clearCache(context);
+ BrowserSettings.getInstance().clearDatabases(context);
} else if (BrowserSettings.PREF_CLEAR_COOKIES.equals(getKey())) {
BrowserSettings.getInstance().clearCookies(context);
} else if (BrowserSettings.PREF_CLEAR_HISTORY.equals(getKey())) {
diff --git a/src/com/android/browser/CombinedBookmarkHistoryActivity.java b/src/com/android/browser/CombinedBookmarkHistoryActivity.java
index 963f179..6926c8f 100644
--- a/src/com/android/browser/CombinedBookmarkHistoryActivity.java
+++ b/src/com/android/browser/CombinedBookmarkHistoryActivity.java
@@ -84,7 +84,7 @@
Resources resources = getResources();
getIconListenerSet(getContentResolver());
- Intent bookmarksIntent = new Intent(this, BrowserBookmarksPage.class);
+ Intent bookmarksIntent = new Intent(this, BookmarkGridPage.class);
bookmarksIntent.putExtras(extras);
tabHost.addTab(tabHost.newTabSpec(BOOKMARKS_TAB)
.setIndicator(resources.getString(R.string.tab_bookmarks),
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..2049bd0 100644
--- a/src/com/android/browser/FindDialog.java
+++ b/src/com/android/browser/FindDialog.java
@@ -42,7 +42,6 @@
private BrowserActivity mBrowserActivity;
// Views with which the user can interact.
- private View mOk;
private EditText mEditText;
private View mNextButton;
private View mPrevButton;
@@ -129,7 +128,6 @@
button = findViewById(R.id.done);
button.setOnClickListener(mFindCancelListener);
- mOk = button;
mMatches = (TextView) findViewById(R.id.matches);
mMatchesView = findViewById(R.id.matches_view);
@@ -143,23 +141,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/HistoryItem.java b/src/com/android/browser/HistoryItem.java
index 55e43f0..b37a3bd 100644
--- a/src/com/android/browser/HistoryItem.java
+++ b/src/com/android/browser/HistoryItem.java
@@ -17,23 +17,13 @@
package com.android.browser;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
import android.content.Context;
-import android.database.Cursor;
import android.graphics.Bitmap;
-import android.net.Uri;
import android.provider.Browser;
-import android.util.Log;
import android.view.View;
-import android.webkit.WebIconDatabase;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.TextView;
-import android.widget.Toast;
-
-import java.util.Date;
/**
* Layout representing a history item in the classic history viewer.
@@ -54,56 +44,13 @@
mListener = new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
- ContentResolver cr = mContext.getContentResolver();
- Cursor cursor = cr.query(
- Browser.BOOKMARKS_URI,
- Browser.HISTORY_PROJECTION,
- "url = ?",
- new String[] { mUrl },
- null);
- boolean first = cursor.moveToFirst();
- // Should be in the database no matter what
- if (!first) {
- throw new AssertionError("URL is not in the database!");
- }
if (isChecked) {
- // Add to bookmarks
- // FIXME: Share code with AddBookmarkPage.java
- ContentValues map = new ContentValues();
- map.put(Browser.BookmarkColumns.CREATED,
- new Date().getTime());
- map.put(Browser.BookmarkColumns.TITLE, getName());
- map.put(Browser.BookmarkColumns.BOOKMARK, 1);
- try {
- cr.update(Browser.BOOKMARKS_URI, map,
- "_id = " + cursor.getInt(0), null);
- } catch (IllegalStateException e) {
- Log.e("HistoryItem", "no database!");
- }
- WebIconDatabase.getInstance().retainIconForPageUrl(mUrl);
- // catch IllegalStateException?
- Toast.makeText(mContext, R.string.added_to_bookmarks,
- Toast.LENGTH_LONG).show();
+ Bookmarks.addBookmark(mContext,
+ mContext.getContentResolver(), mUrl, getName());
} else {
- // Remove from bookmarks
- // FIXME: This code should be shared with
- // BrowserBookmarksAdapter.java
- WebIconDatabase.getInstance().releaseIconForPageUrl(mUrl);
- Uri uri = ContentUris.withAppendedId(Browser.BOOKMARKS_URI,
- cursor.getInt(Browser.HISTORY_PROJECTION_ID_INDEX));
- // It is no longer a bookmark, but it is still a visited
- // site.
- ContentValues values = new ContentValues();
- values.put(Browser.BookmarkColumns.BOOKMARK, 0);
- try {
- cr.update(uri, values, null, null);
- } catch (IllegalStateException e) {
- Log.e("HistoryItem", "no database!");
- }
- Toast.makeText(mContext, R.string.removed_from_bookmarks,
- Toast.LENGTH_LONG).show();
+ Bookmarks.removeFromBookmarks(mContext,
+ mContext.getContentResolver(), mUrl);
}
- cursor.deactivate();
}
};
}
diff --git a/src/com/android/browser/MostVisitedActivity.java b/src/com/android/browser/MostVisitedActivity.java
index 704ee27..90052d3 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);
@@ -84,9 +84,9 @@
private Vector<DataSetObserver> mObservers;
private Cursor mCursor;
// These correspond with projection below.
- private final int mUrlIndex = 0;
- private final int mTitleIndex = 1;
- private final int mBookmarkIndex = 2;
+ private static final int mUrlIndex = 0;
+ private static final int mTitleIndex = 1;
+ private static final int mBookmarkIndex = 2;
MyAdapter() {
mObservers = new Vector<DataSetObserver>();
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 575be8d..bdb57fa 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);
}
@@ -681,11 +684,11 @@
return;
}
- // free the WebView cache
- Log.w(LOGTAG, "Free WebView cache");
+ // free the WebView's unused memory (this includes the cache)
+ Log.w(LOGTAG, "Free WebView's unused memory and cache");
WebView view = getCurrentWebView();
if (view != null) {
- view.clearCache(false);
+ view.freeMemory();
}
// force a gc
System.gc();
@@ -865,6 +868,48 @@
return setCurrentTab(newTab, false);
}
+ /*package*/ void pauseCurrentTab() {
+ Tab t = getCurrentTab();
+ if (t != null) {
+ t.mMainView.onPause();
+ if (t.mSubView != null) {
+ t.mSubView.onPause();
+ }
+ }
+ }
+
+ /*package*/ void resumeCurrentTab() {
+ Tab t = getCurrentTab();
+ if (t != null) {
+ t.mMainView.onResume();
+ if (t.mSubView != null) {
+ t.mSubView.onResume();
+ }
+ }
+ }
+
+ private void putViewInForeground(WebView v, WebViewClient vc,
+ WebChromeClient cc) {
+ v.setWebViewClient(vc);
+ v.setWebChromeClient(cc);
+ v.setOnCreateContextMenuListener(mActivity);
+ v.setDownloadListener(mActivity);
+ v.onResume();
+ }
+
+ private void putViewInBackground(WebView v) {
+ // Set an empty callback so that default actions are not triggered.
+ v.setWebViewClient(mEmptyClient);
+ v.setWebChromeClient(mBackgroundChromeClient);
+ v.setOnCreateContextMenuListener(null);
+ // Leave the DownloadManager attached so that downloads can start in
+ // a non-active window. This can happen when going to a site that does
+ // a redirect after a period of time. The user could have switched to
+ // another tab while waiting for the download to start.
+ v.setDownloadListener(mActivity);
+ v.onPause();
+ }
+
/**
* If force is true, this method skips the check for newTab == current.
*/
@@ -890,7 +935,6 @@
mTabQueue.add(newTab);
WebView mainView;
- WebView subView;
// Display the new current tab
mCurrentTab = mTabs.indexOf(newTab);
@@ -900,17 +944,12 @@
// Same work as in createNewTab() except don't do new Tab()
newTab.mMainView = mainView = createNewWebView();
}
- mainView.setWebViewClient(mActivity.getWebViewClient());
- mainView.setWebChromeClient(mActivity.getWebChromeClient());
- mainView.setOnCreateContextMenuListener(mActivity);
- mainView.setDownloadListener(mActivity);
+ putViewInForeground(mainView, mActivity.getWebViewClient(),
+ mActivity.getWebChromeClient());
// Add the subwindow if it exists
if (newTab.mSubViewContainer != null) {
- subView = newTab.mSubView;
- subView.setWebViewClient(newTab.mSubViewClient);
- subView.setWebChromeClient(newTab.mSubViewChromeClient);
- subView.setOnCreateContextMenuListener(mActivity);
- subView.setDownloadListener(mActivity);
+ putViewInForeground(newTab.mSubView, newTab.mSubViewClient,
+ newTab.mSubViewChromeClient);
}
if (needRestore) {
// Have to finish setCurrentTab work before calling restoreState
@@ -925,23 +964,9 @@
* Put the tab in the background using all the empty/background clients.
*/
private void putTabInBackground(Tab t) {
- WebView mainView = t.mMainView;
- // Set an empty callback so that default actions are not triggered.
- mainView.setWebViewClient(mEmptyClient);
- mainView.setWebChromeClient(mBackgroundChromeClient);
- mainView.setOnCreateContextMenuListener(null);
- // Leave the DownloadManager attached so that downloads can start in
- // a non-active window. This can happen when going to a site that does
- // a redirect after a period of time. The user could have switched to
- // another tab while waiting for the download to start.
- mainView.setDownloadListener(mActivity);
- WebView subView = t.mSubView;
- if (subView != null) {
- // Set an empty callback so that default actions are not triggered.
- subView.setWebViewClient(mEmptyClient);
- subView.setWebChromeClient(mBackgroundChromeClient);
- subView.setOnCreateContextMenuListener(null);
- subView.setDownloadListener(mActivity);
+ putViewInBackground(t.mMainView);
+ if (t.mSubView != null) {
+ putViewInBackground(t.mSubView);
}
}
diff --git a/src/com/android/browser/TitleBar.java b/src/com/android/browser/TitleBar.java
new file mode 100644
index 0000000..b818dd2
--- /dev/null
+++ b/src/com/android/browser/TitleBar.java
@@ -0,0 +1,153 @@
+/*
+ * 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.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.webkit.WebView;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+public class TitleBar extends LinearLayout {
+ private TextView mTitle;
+ private TextView mUrl;
+ private ImageView mLftButton;
+ private Drawable mBookmarkDrawable;
+ private View mRtButton;
+ private View mDivider;
+ private ProgressBar mCircularProgress;
+ private ProgressBar mHorizontalProgress;
+ private ImageView mFavicon;
+ private ImageView mLockIcon;
+ private boolean mInLoad;
+
+ public TitleBar(Context context) {
+ this(context, null);
+ }
+
+ public TitleBar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ LayoutInflater factory = LayoutInflater.from(context);
+ factory.inflate(R.layout.title_bar, this);
+
+ mTitle = (TextView) findViewById(R.id.title);
+ mUrl = (TextView) findViewById(R.id.url);
+
+ mLftButton = (ImageView) findViewById(R.id.lft_button);
+ mRtButton = findViewById(R.id.rt_button);
+
+ mCircularProgress = (ProgressBar) findViewById(R.id.progress_circular);
+ mHorizontalProgress = (ProgressBar) findViewById(
+ R.id.progress_horizontal);
+ mFavicon = (ImageView) findViewById(R.id.favicon);
+ mLockIcon = (ImageView) findViewById(R.id.lock_icon);
+ mDivider = findViewById(R.id.divider);
+ }
+
+ /* package */ void setBrowserActivity(final BrowserActivity activity) {
+ mLftButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ if (mInLoad) {
+ WebView webView = activity.getTopWindow();
+ if (webView != null) {
+ webView.stopLoading();
+ }
+ } else {
+ activity.bookmarksOrHistoryPicker(false);
+ }
+ }
+ });
+ mRtButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ WebView webView = activity.getTopWindow();
+ if (webView != null) {
+ webView.zoomScrollOut();
+ }
+ }
+ });
+ setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ activity.onSearchRequested();
+ }
+ });
+ }
+
+ /* package */ void setFavicon(Drawable d) {
+ mFavicon.setImageDrawable(d);
+ }
+
+ /* package */ void setLock(Drawable d) {
+ if (d == null) {
+ mLockIcon.setVisibility(View.GONE);
+ } else {
+ mLockIcon.setImageDrawable(d);
+ mLockIcon.setVisibility(View.VISIBLE);
+ }
+ }
+
+ /* package */ void setProgress(int newProgress) {
+ if (newProgress == mCircularProgress.getMax()) {
+ mCircularProgress.setVisibility(View.GONE);
+ mHorizontalProgress.setVisibility(View.GONE);
+ mDivider.setVisibility(View.VISIBLE);
+ mRtButton.setVisibility(View.VISIBLE);
+ mLftButton.setImageDrawable(mBookmarkDrawable);
+ mInLoad = false;
+ } else {
+ mCircularProgress.setProgress(newProgress);
+ mHorizontalProgress.setProgress(newProgress);
+ mCircularProgress.setVisibility(View.VISIBLE);
+ mHorizontalProgress.setVisibility(View.VISIBLE);
+ mDivider.setVisibility(View.GONE);
+ mRtButton.setVisibility(View.GONE);
+ if (mBookmarkDrawable == null) {
+ // The drawable was assigned in the xml file, so it already
+ // exists. Keep a pointer to it when we switch to the resource
+ // so we can easily switch back.
+ mBookmarkDrawable = mLftButton.getDrawable();
+ }
+ mLftButton.setImageResource(
+ com.android.internal.R.drawable.ic_menu_stop);
+ mInLoad = true;
+ }
+ }
+
+ /* package */ void setTitleAndUrl(CharSequence title, CharSequence url) {
+ if (null == title) {
+ mTitle.setText(R.string.title_bar_loading);
+ } else {
+ mTitle.setText(title);
+ }
+ if (url != null) {
+ url = BrowserActivity.buildTitleUrl(url.toString());
+ }
+ mUrl.setText(url);
+ }
+
+ /* package */ void setToTabPicker() {
+ mTitle.setText(R.string.tab_picker_title);
+ setFavicon(null);
+ setLock(null);
+ mCircularProgress.setVisibility(View.GONE);
+ mHorizontalProgress.setVisibility(View.GONE);
+ }
+}
diff --git a/src/com/android/browser/WebsiteSettingsActivity.java b/src/com/android/browser/WebsiteSettingsActivity.java
new file mode 100644
index 0000000..7fea766
--- /dev/null
+++ b/src/com/android/browser/WebsiteSettingsActivity.java
@@ -0,0 +1,291 @@
+/*
+ * 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.AlertDialog;
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Browser;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.WebIconDatabase;
+import android.webkit.WebStorage;
+import android.widget.ArrayAdapter;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Vector;
+
+/**
+ * Manage the settings for an origin.
+ * We use it to keep track of the HTML5 settings, i.e. database (webstorage).
+ */
+public class WebsiteSettingsActivity extends ListActivity {
+
+ private String LOGTAG = "WebsiteSettingsActivity";
+ private static String sMBStored = null;
+ private SiteAdapter mAdapter = null;
+
+ class Site {
+ private String mOrigin;
+ private String mTitle;
+ private Bitmap mIcon;
+
+ public Site(String origin, String title, Bitmap icon) {
+ mOrigin = origin;
+ mTitle = title;
+ mIcon = icon;
+ }
+
+ public String getOrigin() {
+ return mOrigin;
+ }
+
+ public void setTitle(String title) {
+ mTitle = title;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public void setIcon(Bitmap icon) {
+ mIcon = icon;
+ }
+
+ public Bitmap getIcon() {
+ return mIcon;
+ }
+ }
+
+ class SiteAdapter extends ArrayAdapter<Site>
+ implements AdapterView.OnItemClickListener {
+ private int mResource;
+ private LayoutInflater mInflater;
+ private Bitmap mDefaultIcon;
+ private Site mCurrentSite;
+ private final static int STORED_DATA = 0;
+
+ public SiteAdapter(Context context, int rsc) {
+ super(context, rsc);
+ mResource = rsc;
+ mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mDefaultIcon = BitmapFactory.decodeResource(getResources(),
+ R.drawable.ic_launcher_shortcut_browser_bookmark);
+ populateOrigins();
+ }
+
+ public void populateOrigins() {
+ clear();
+
+ // Get the list of origins we want to display
+ HashMap<String, Site> uris = new HashMap<String, Site>();
+ Vector origins = WebStorage.getInstance().getOrigins();
+ if (origins != null) {
+ for (int i = 0; i < origins.size(); i++) {
+ String origin = (String) origins.get(i);
+ Site site = new Site(origin, origin, null);
+ uris.put(Uri.parse(origin).getHost(), site);
+ }
+ }
+
+ // Check the bookmark db -- if one of our origin matches,
+ // we set its title and favicon
+ Cursor c = getContext().getContentResolver().query(Browser.BOOKMARKS_URI,
+ new String[] { Browser.BookmarkColumns.URL, Browser.BookmarkColumns.TITLE,
+ Browser.BookmarkColumns.FAVICON }, "bookmark = 1", null, null);
+
+ if ((c != null) && c.moveToFirst()) {
+ int urlIndex = c.getColumnIndex(Browser.BookmarkColumns.URL);
+ int titleIndex = c.getColumnIndex(Browser.BookmarkColumns.TITLE);
+ int faviconIndex = c.getColumnIndex(Browser.BookmarkColumns.FAVICON);
+ do {
+ String url = c.getString(urlIndex);
+ String host = Uri.parse(url).getHost();
+ if (uris.containsKey(host)) {
+ String title = c.getString(titleIndex);
+ Site site = uris.get(host);
+ site.setTitle(title);
+ byte[] data = c.getBlob(faviconIndex);
+ if (data != null) {
+ Bitmap bmp = BitmapFactory.decodeByteArray(data, 0, data.length);
+ if (bmp != null) {
+ site.setIcon(bmp);
+ }
+ }
+ }
+ } while (c.moveToNext());
+ }
+
+ // We can now simply populate our array with Site instances
+ Set keys = uris.keySet();
+ Iterator iter = keys.iterator();
+ while (iter.hasNext()) {
+ String origin = (String) iter.next();
+ Site site = uris.get(origin);
+ add(site);
+ }
+
+ if (getCount() == 0) {
+ finish(); // we close the screen
+ }
+ }
+
+ public int getCount() {
+ if (mCurrentSite == null) {
+ return super.getCount();
+ }
+ return 1; // db view
+ }
+
+ public 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);
+ }
+
+ /*
+ * If we receive the back event and are displaying
+ * site's settings, we want to go back to the main
+ * list view. If not, we just do nothing (see
+ * dispatchKeyEvent() below).
+ */
+ public boolean backKeyPressed() {
+ if (mCurrentSite != null) {
+ mCurrentSite = null;
+ populateOrigins();
+ notifyDataSetChanged();
+ return true;
+ }
+ return false;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View view;
+ TextView title;
+ TextView subtitle;
+ ImageView icon;
+
+ if (convertView == null) {
+ view = mInflater.inflate(mResource, parent, false);
+ } else {
+ view = convertView;
+ }
+
+ title = (TextView) view.findViewById(R.id.title);
+ subtitle = (TextView) view.findViewById(R.id.subtitle);
+ icon = (ImageView) view.findViewById(R.id.icon);
+
+ if (mCurrentSite == null) {
+ Site site = getItem(position);
+ title.setText(site.getTitle());
+ subtitle.setText(site.getOrigin());
+ icon.setVisibility(View.VISIBLE);
+ Bitmap bmp = site.getIcon();
+ if (bmp == null) {
+ bmp = mDefaultIcon;
+ }
+ icon.setImageBitmap(bmp);
+ // We set the site as the view's tag,
+ // so that we can get it in onItemClick()
+ view.setTag(site);
+ } else {
+ icon.setVisibility(View.GONE);
+ if (position == STORED_DATA) {
+ String origin = mCurrentSite.getOrigin();
+ long usageValue = WebStorage.getInstance().getUsageForOrigin(origin);
+ String usage = sizeValueToString(usageValue) + " " + sMBStored;
+
+ title.setText(R.string.webstorage_clear_data_title);
+ subtitle.setText(usage);
+ }
+ }
+
+ return view;
+ }
+
+ public void onItemClick(AdapterView<?> parent,
+ View view,
+ int position,
+ long id) {
+ if (mCurrentSite != null) {
+ if (position == STORED_DATA) {
+ new AlertDialog.Builder(getContext())
+ .setTitle(R.string.webstorage_clear_data_dialog_title)
+ .setMessage(R.string.webstorage_clear_data_dialog_message)
+ .setPositiveButton(R.string.webstorage_clear_data_dialog_ok_button,
+ new AlertDialog.OnClickListener() {
+ public void onClick(DialogInterface dlg, int which) {
+ WebStorage.getInstance().deleteOrigin(mCurrentSite.getOrigin());
+ mCurrentSite = null;
+ populateOrigins();
+ notifyDataSetChanged();
+ }})
+ .setNegativeButton(R.string.webstorage_clear_data_dialog_cancel_button, null)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .show();
+ }
+ } else {
+ mCurrentSite = (Site) view.getTag();
+ notifyDataSetChanged();
+ }
+ }
+ }
+
+ /**
+ * Intercepts the back key to immediately notify
+ * NativeDialog that we are done.
+ */
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if ((event.getKeyCode() == KeyEvent.KEYCODE_BACK)
+ && (event.getAction() == KeyEvent.ACTION_DOWN)) {
+ if ((mAdapter != null) && (mAdapter.backKeyPressed())){
+ return true; // event consumed
+ }
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ if (sMBStored == null) {
+ sMBStored = getString(R.string.webstorage_origin_summary_mb_stored);
+ }
+ mAdapter = new SiteAdapter(this, R.layout.application);
+ setListAdapter(mAdapter);
+ getListView().setOnItemClickListener(mAdapter);
+ }
+}