summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:32:16 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:32:16 -0800
commit0c90888c75eed12f6e2e14a9044faf50bd4af8ed (patch)
tree266c059a147232a297b2d956e04397c0f6683bc4
parent8611831e36b71c844a14788998728f3cd625b833 (diff)
downloadandroid_packages_apps_Gello-0c90888c75eed12f6e2e14a9044faf50bd4af8ed.tar.gz
android_packages_apps_Gello-0c90888c75eed12f6e2e14a9044faf50bd4af8ed.tar.bz2
android_packages_apps_Gello-0c90888c75eed12f6e2e14a9044faf50bd4af8ed.zip
auto import from //depot/cupcake/@135843
-rw-r--r--Android.mk16
-rw-r--r--AndroidManifest.xml223
-rw-r--r--MODULE_LICENSE_APACHE20
-rw-r--r--NOTICE190
-rw-r--r--assets/html/flashtest.html18
-rw-r--r--assets/plugins/gears-0.5.12.0/dummy1
-rw-r--r--assets/plugins/gears.sobin0 -> 1196880 bytes
-rw-r--r--res/anim/find_dialog_enter.xml25
-rw-r--r--res/anim/find_dialog_exit.xml26
-rw-r--r--res/drawable/app_web_browser_sm.pngbin0 -> 616 bytes
-rw-r--r--res/drawable/browser_bookmark_tab.xml20
-rw-r--r--res/drawable/browser_history_tab.xml20
-rw-r--r--res/drawable/browser_visited_tab.xml20
-rwxr-xr-xres/drawable/dialog_divider_horizontal_light.9.pngbin0 -> 2921 bytes
-rwxr-xr-xres/drawable/fav_icn_background.pngbin0 -> 186 bytes
-rw-r--r--res/drawable/gears.pngbin0 -> 3802 bytes
-rwxr-xr-xres/drawable/gears_button.xml29
-rw-r--r--res/drawable/gears_button_default.9.pngbin0 -> 1399 bytes
-rw-r--r--res/drawable/gears_button_pressed.9.pngbin0 -> 1543 bytes
-rw-r--r--res/drawable/gears_button_selected.9.pngbin0 -> 1085 bytes
-rw-r--r--res/drawable/gears_icon_32x32.pngbin0 -> 2421 bytes
-rw-r--r--res/drawable/gears_icon_48x48.pngbin0 -> 4735 bytes
-rw-r--r--res/drawable/gears_local_data.pngbin0 -> 1297 bytes
-rw-r--r--res/drawable/gears_location_data.pngbin0 -> 1640 bytes
-rw-r--r--res/drawable/ic_btn_close_panel.pngbin0 -> 814 bytes
-rw-r--r--res/drawable/ic_btn_find_next.pngbin0 -> 592 bytes
-rw-r--r--res/drawable/ic_btn_find_prev.pngbin0 -> 615 bytes
-rw-r--r--res/drawable/ic_dialog_bookmark.pngbin0 -> 3805 bytes
-rwxr-xr-xres/drawable/ic_dialog_browser_certificate_partially_secure.pngbin0 -> 4255 bytes
-rwxr-xr-xres/drawable/ic_dialog_browser_certificate_secure.pngbin0 -> 4291 bytes
-rwxr-xr-xres/drawable/ic_dialog_browser_security_bad.pngbin0 -> 3556 bytes
-rwxr-xr-xres/drawable/ic_dialog_browser_security_good.pngbin0 -> 3415 bytes
-rwxr-xr-xres/drawable/ic_dialog_menu_generic.pngbin0 -> 1187 bytes
-rwxr-xr-xres/drawable/ic_launcher_browser.pngbin0 -> 3426 bytes
-rw-r--r--res/drawable/ic_launcher_drm_file.pngbin0 -> 2738 bytes
-rw-r--r--res/drawable/ic_menu_bookmark.pngbin0 -> 1080 bytes
-rw-r--r--res/drawable/ic_menu_windows.pngbin0 -> 1244 bytes
-rw-r--r--res/drawable/ic_new_window.pngbin0 -> 3010 bytes
-rwxr-xr-xres/drawable/ic_search_category_bookmark.pngbin0 -> 806 bytes
-rw-r--r--res/drawable/ic_search_category_browser.pngbin0 -> 2395 bytes
-rwxr-xr-xres/drawable/ic_search_category_history.pngbin0 -> 900 bytes
-rwxr-xr-xres/drawable/ic_search_category_suggest.pngbin0 -> 819 bytes
-rw-r--r--res/drawable/ic_tab_browser_bookmark_selected.pngbin0 -> 1483 bytes
-rw-r--r--res/drawable/ic_tab_browser_bookmark_unselected.pngbin0 -> 1385 bytes
-rw-r--r--res/drawable/ic_tab_browser_history_selected.pngbin0 -> 2149 bytes
-rw-r--r--res/drawable/ic_tab_browser_history_unselected.pngbin0 -> 2172 bytes
-rw-r--r--res/drawable/ic_tab_browser_visited_selected.pngbin0 -> 1574 bytes
-rw-r--r--res/drawable/ic_tab_browser_visited_unselected.pngbin0 -> 1459 bytes
-rw-r--r--res/drawable/page_indicator.pngbin0 -> 1224 bytes
-rw-r--r--res/drawable/page_indicator_unselected2.pngbin0 -> 188 bytes
-rw-r--r--res/drawable/ssl_icon.pngbin0 -> 3216 bytes
-rw-r--r--res/layout-land/http_authentication.xml73
-rw-r--r--res/layout-land/page_info.xml73
-rw-r--r--res/layout-land/ssl_certificate.xml266
-rw-r--r--res/layout/add_new_bookmark.xml59
-rw-r--r--res/layout/browser_add_bookmark.xml97
-rw-r--r--res/layout/browser_bookmarks_page.xml27
-rw-r--r--res/layout/browser_download_item.xml89
-rw-r--r--res/layout/browser_downloads_page.xml25
-rw-r--r--res/layout/browser_find.xml86
-rw-r--r--res/layout/browser_subwindow.xml50
-rw-r--r--res/layout/empty_history.xml25
-rw-r--r--res/layout/gears_dialog.xml175
-rw-r--r--res/layout/gears_dialog_permission.xml111
-rw-r--r--res/layout/gears_dialog_settings.xml58
-rw-r--r--res/layout/gears_dialog_settings_row.xml127
-rw-r--r--res/layout/gears_settings.xml48
-rw-r--r--res/layout/gears_settings_row.xml99
-rw-r--r--res/layout/history_header.xml24
-rw-r--r--res/layout/history_item.xml65
-rw-r--r--res/layout/http_authentication.xml69
-rw-r--r--res/layout/no_downloads.xml25
-rw-r--r--res/layout/page_info.xml74
-rw-r--r--res/layout/ssl_certificate.xml280
-rw-r--r--res/layout/ssl_success.xml44
-rw-r--r--res/layout/ssl_warning.xml41
-rw-r--r--res/layout/ssl_warnings.xml51
-rw-r--r--res/layout/tabitem.xml53
-rw-r--r--res/layout/tabs.xml41
-rw-r--r--res/menu/bookmarks.xml21
-rw-r--r--res/menu/bookmarkscontext.xml38
-rw-r--r--res/menu/browser.xml113
-rw-r--r--res/menu/browsercontext.xml59
-rw-r--r--res/menu/downloadhistory.xml26
-rw-r--r--res/menu/downloadhistorycontextfailed.xml22
-rw-r--r--res/menu/downloadhistorycontextfinished.xml24
-rw-r--r--res/menu/downloadhistorycontextrunning.xml22
-rw-r--r--res/menu/history.xml21
-rw-r--r--res/menu/historycontext.xml30
-rw-r--r--res/menu/tabscontext.xml26
-rw-r--r--res/values-cs/strings.xml334
-rw-r--r--res/values-de/strings.xml334
-rw-r--r--res/values-es/strings.xml334
-rw-r--r--res/values-fr/strings.xml334
-rw-r--r--res/values-it/strings.xml334
-rw-r--r--res/values-ja/strings.xml334
-rw-r--r--res/values-ko/strings.xml334
-rw-r--r--res/values-nb/strings.xml326
-rw-r--r--res/values-nl/strings.xml334
-rw-r--r--res/values-pl/strings.xml334
-rw-r--r--res/values-ru/strings.xml334
-rw-r--r--res/values-zh-rCN/strings.xml334
-rw-r--r--res/values-zh-rTW/strings.xml334
-rw-r--r--res/values/colors.xml42
-rw-r--r--res/values/strings.xml707
-rw-r--r--res/values/styles.xml49
-rw-r--r--res/values/themes.xml28
-rw-r--r--res/xml/browser_preferences.xml174
-rw-r--r--res/xml/debug_preferences.xml67
-rw-r--r--res/xml/searchable.xml33
-rw-r--r--src/com/android/browser/AddBookmarkPage.java226
-rw-r--r--src/com/android/browser/AddNewBookmark.java68
-rw-r--r--src/com/android/browser/BookmarkItem.java113
-rw-r--r--src/com/android/browser/Browser.java59
-rw-r--r--src/com/android/browser/BrowserActivity.java4567
-rw-r--r--src/com/android/browser/BrowserBookmarksAdapter.java535
-rw-r--r--src/com/android/browser/BrowserBookmarksPage.java385
-rw-r--r--src/com/android/browser/BrowserDownloadAdapter.java222
-rw-r--r--src/com/android/browser/BrowserDownloadPage.java468
-rw-r--r--src/com/android/browser/BrowserHistoryPage.java468
-rw-r--r--src/com/android/browser/BrowserHomepagePreference.java64
-rw-r--r--src/com/android/browser/BrowserPluginList.java66
-rw-r--r--src/com/android/browser/BrowserPreferencesPage.java149
-rw-r--r--src/com/android/browser/BrowserProvider.java654
-rw-r--r--src/com/android/browser/BrowserSearchpagePreference.java68
-rw-r--r--src/com/android/browser/BrowserSettings.java442
-rw-r--r--src/com/android/browser/BrowserYesNoPreference.java56
-rw-r--r--src/com/android/browser/CombinedBookmarkHistoryActivity.java124
-rw-r--r--src/com/android/browser/Dots.java83
-rw-r--r--src/com/android/browser/FakeWebView.java111
-rw-r--r--src/com/android/browser/FetchUrlMimeType.java135
-rw-r--r--src/com/android/browser/FindDialog.java226
-rw-r--r--src/com/android/browser/GearsBaseDialog.java474
-rw-r--r--src/com/android/browser/GearsNativeDialog.java280
-rw-r--r--src/com/android/browser/GearsPermissions.java196
-rw-r--r--src/com/android/browser/GearsPermissionsDialog.java133
-rw-r--r--src/com/android/browser/GearsSettingsDialog.java460
-rw-r--r--src/com/android/browser/HistoryItem.java127
-rw-r--r--src/com/android/browser/ImageAdapter.java291
-rw-r--r--src/com/android/browser/ImageGrid.java233
-rw-r--r--src/com/android/browser/KeyTracker.java107
-rw-r--r--src/com/android/browser/MostVisitedActivity.java199
-rw-r--r--src/com/android/browser/TabControl.java937
143 files changed, 21405 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 00000000..9de5b560
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := user development
+
+# TODO: Remove dependency of application on the test runner (android.test.runner)
+# library.
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := googlelogin-client
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := Browser
+
+include $(BUILD_PACKAGE)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100644
index 00000000..110bda43
--- /dev/null
+++ b/AndroidManifest.xml
@@ -0,0 +1,223 @@
+<!--
+/* //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.
+*/
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.browser">
+
+ <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER"/>
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+ <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.WAKE_LOCK"/>
+ <!-- Needed by the ZoomRingController to set the bit saying we've already shown the
+ tutorial toast. -->
+ <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
+ <uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS"/>
+ <uses-permission android:name="com.android.browser.permission.WRITE_HISTORY_BOOKMARKS"/>
+
+ <!-- Allows an application to read the user Browser's history and bookmarks. -->
+ <permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS"
+ android:permissionGroup="android.permission-group.PERSONAL_INFO"
+ android:label="@string/permlab_readHistoryBookmarks"
+ android:description="@string/permdesc_readHistoryBookmarks"
+ android:protectionLevel="dangerous" />
+
+ <!-- Allows an application to write (but not read) the user Browser's history and bookmarks. -->
+ <permission android:name="com.android.browser.permission.WRITE_HISTORY_BOOKMARKS"
+ android:permissionGroup="android.permission-group.PERSONAL_INFO"
+ android:label="@string/permlab_writeHistoryBookmarks"
+ android:description="@string/permdesc_writeHistoryBookmarks"
+ android:protectionLevel="dangerous" />
+
+ <application android:name="Browser"
+ android:label="@string/application_name"
+ android:icon="@drawable/ic_launcher_browser"
+ android:taskAffinity="android.task.browser" >
+ <!-- TODO: Remove dependency of application on the test runner
+ (android.test) library. -->
+ <uses-library android:name="android.test.runner" />
+
+ <provider android:name="BrowserProvider"
+ android:authorities="browser"
+ android:multiprocess="true"
+ android:readPermission="com.android.browser.permission.READ_HISTORY_BOOKMARKS"
+ android:writePermission="com.android.browser.permission.WRITE_HISTORY_BOOKMARKS"/>
+ <activity android:name="BrowserActivity"
+ android:label="@string/application_name"
+ android:launchMode="singleTask"
+ android:alwaysRetainTaskState="true"
+ android:configChanges="orientation|keyboardHidden"
+ android:theme="@style/BrowserTheme" >
+ <!-- For these schemes were not particular MIME type has been
+ supplied, we are a good candidate. -->
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ <data android:scheme="about" />
+ </intent-filter>
+ <!-- For these schemes where any of these particular MIME types
+ have been supplied, we are a good candidate. -->
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ <data android:mimeType="text/html"/>
+ <data android:mimeType="text/plain"/>
+ <data android:mimeType="application/xhtml+xml"/>
+ <data android:mimeType="application/vnd.wap.xhtml+xml"/>
+ </intent-filter>
+ <!-- We are also the main entry point of the browser. -->
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ </intent-filter>
+ <!-- The maps app is a much better experience, so it's not
+ worth having this at all... especially for a demo!
+ <intent-filter android:label="Map In Browser">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="vnd.android.cursor.item/postal-address" />
+ </intent-filter>
+ -->
+ <intent-filter>
+ <action android:name="android.intent.action.WEB_SEARCH" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <data android:scheme="" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.WEB_SEARCH" />
+ <action android:name="android.intent.action.MEDIA_SEARCH" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.SEARCH" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <meta-data android:name="android.app.searchable"
+ android:resource="@xml/searchable" />
+ <intent-filter>
+ <action android:name="android.net.http.NETWORK_STATE" />
+ <action android:name="android.intent.action.PROXY_CHANGE" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="CombinedBookmarkHistoryActivity" android:label=""
+ android:launchMode="singleTop" android:configChanges="orientation|keyboardHidden">
+ </activity>
+
+ <activity android:name="BrowserBookmarksPage" android:label="@string/bookmarks"
+ android:launchMode="singleTop" android:configChanges="orientation|keyboardHidden">
+ </activity>
+
+ <activity android:name="MostVisitedActivity" android:label=""
+ android:launchMode="singleTop" android:configChanges="orientation|keyboardHidden"/>
+
+ <activity-alias android:name="ShortcutBookmarksPage"
+ android:targetActivity="BrowserBookmarksPage"
+ android:label="@string/shortcut_bookmark">
+
+ <intent-filter>
+ <action android:name="android.intent.action.CREATE_SHORTCUT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+
+ </activity-alias>
+
+ <activity android:name="BrowserDownloadPage" android:label=""
+ android:configChanges="orientation|keyboardHidden">
+ </activity>
+
+ <activity android:name="BrowserPreferencesPage" android:label="@string/menu_preferences"
+ android:configChanges="orientation|keyboardHidden">
+ </activity>
+
+ <activity android:name="BrowserHistoryPage" android:label=""
+ android:configChanges="orientation|keyboardHidden">
+ </activity>
+
+ <activity android:name="BrowserPluginList" 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="GearsNativeDialog"
+ android:configChanges="orientation|keyboardHidden"
+ android:theme="@android:style/Theme.Dialog">
+ </activity>
+
+ <service android:name="GearsDialogService"
+ android:process=":dialog"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="com.android.browser.IGearsDialogService" />
+ </intent-filter>
+ </service>
+
+ <activity android:name="AddBookmarkPage" android:label="Save bookmark" android:theme="@android:style/Theme.Dialog"
+ android:configChanges="orientation|keyboardHidden" android:windowSoftInputMode="stateHidden">
+ <intent-filter>
+ <action android:name="android.intent.action.INSERT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="vnd.android.cursor.dir/bookmark"/>
+ </intent-filter>
+ </activity>
+
+ <!-- Makes .BrowserActivity the search target for any activity in Browser -->
+ <meta-data android:name="android.app.default_searchable" android:value=".BrowserActivity" />
+
+
+ </application>
+
+ <!-- Browser tests. Invoke with: -->
+ <!-- adb shell am instrument -w com.android.browser/.BrowserTestRunner -->
+ <instrumentation android:name="BrowserTestRunner"
+ android:targetPackage="com.android.browser"
+ android:label="@string/activity_instrumentation_test_runner"
+ />
+ <!-- Browser tests. Invoke with: -->
+ <!-- adb shell am instrument -w com.android.browser/.BrowserFunctionalTestRunner -->
+ <instrumentation android:name="BrowserFunctionalTestRunner"
+ android:targetPackage="com.android.browser"
+ android:label="@string/activity_instrumentation_functional_test_runner"
+ />
+
+ <instrumentation android:name="BrowserLaunchPerformance"
+ android:targetPackage="com.android.browser"
+ android:label="Browser Launch Performance"
+ />
+
+</manifest>
+
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 00000000..c5b1efa7
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/assets/html/flashtest.html b/assets/html/flashtest.html
new file mode 100644
index 00000000..8e1cf48f
--- /dev/null
+++ b/assets/html/flashtest.html
@@ -0,0 +1,18 @@
+<html>
+<head>
+<title>Flash test</title>
+</head>
+<body>
+<h1>Flash test</h1>
+This is text above the plugin
+<a href="http://www.google.com">Google</a>
+<br>
+lhs
+<object type="application/x-shockwave-flash" height=120 width=240>
+ <param name="FlashFileName" value="/data/data/com.android.browser/app_plugins/Ball.swf" />
+</object>
+<br>
+
+This is some the text and more text and more text and more text and more text and more text and more text and more text and more text and more text and more text
+</body>
+</html>
diff --git a/assets/plugins/gears-0.5.12.0/dummy b/assets/plugins/gears-0.5.12.0/dummy
new file mode 100644
index 00000000..50f9ab94
--- /dev/null
+++ b/assets/plugins/gears-0.5.12.0/dummy
@@ -0,0 +1 @@
+This file is used to ensure this subdirectory is created.
diff --git a/assets/plugins/gears.so b/assets/plugins/gears.so
new file mode 100644
index 00000000..d575b147
--- /dev/null
+++ b/assets/plugins/gears.so
Binary files differ
diff --git a/res/anim/find_dialog_enter.xml b/res/anim/find_dialog_enter.xml
new file mode 100644
index 00000000..6e19c216
--- /dev/null
+++ b/res/anim/find_dialog_enter.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/options_panel_enter.xml
+**
+** Copyright 2007, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/decelerate_interpolator">
+ <translate android:fromYDelta="25%" android:toYDelta="0" android:duration="75"/>
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="75" />
+</set>
diff --git a/res/anim/find_dialog_exit.xml b/res/anim/find_dialog_exit.xml
new file mode 100644
index 00000000..5775bab8
--- /dev/null
+++ b/res/anim/find_dialog_exit.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/options_panel_exit.xml
+**
+** Copyright 2007, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/accelerate_interpolator">
+ <translate android:fromYDelta="0" android:toYDelta="50%" android:duration="50"/>
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="50" />
+</set>
+
diff --git a/res/drawable/app_web_browser_sm.png b/res/drawable/app_web_browser_sm.png
new file mode 100644
index 00000000..dbb460ad
--- /dev/null
+++ b/res/drawable/app_web_browser_sm.png
Binary files differ
diff --git a/res/drawable/browser_bookmark_tab.xml b/res/drawable/browser_bookmark_tab.xml
new file mode 100644
index 00000000..76684cb4
--- /dev/null
+++ b/res/drawable/browser_bookmark_tab.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_selected="true" android:drawable="@drawable/ic_tab_browser_bookmark_selected"/>
+ <item android:state_selected="false" android:drawable="@drawable/ic_tab_browser_bookmark_unselected"/>
+</selector>
+
diff --git a/res/drawable/browser_history_tab.xml b/res/drawable/browser_history_tab.xml
new file mode 100644
index 00000000..b5329eac
--- /dev/null
+++ b/res/drawable/browser_history_tab.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_selected="true" android:drawable="@drawable/ic_tab_browser_history_selected"/>
+ <item android:state_selected="false" android:drawable="@drawable/ic_tab_browser_history_unselected"/>
+</selector>
+
diff --git a/res/drawable/browser_visited_tab.xml b/res/drawable/browser_visited_tab.xml
new file mode 100644
index 00000000..d1b4404a
--- /dev/null
+++ b/res/drawable/browser_visited_tab.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_selected="true" android:drawable="@drawable/ic_tab_browser_visited_selected"/>
+ <item android:state_selected="false" android:drawable="@drawable/ic_tab_browser_visited_unselected"/>
+</selector>
+
diff --git a/res/drawable/dialog_divider_horizontal_light.9.png b/res/drawable/dialog_divider_horizontal_light.9.png
new file mode 100755
index 00000000..b69619b4
--- /dev/null
+++ b/res/drawable/dialog_divider_horizontal_light.9.png
Binary files differ
diff --git a/res/drawable/fav_icn_background.png b/res/drawable/fav_icn_background.png
new file mode 100755
index 00000000..9275ff33
--- /dev/null
+++ b/res/drawable/fav_icn_background.png
Binary files differ
diff --git a/res/drawable/gears.png b/res/drawable/gears.png
new file mode 100644
index 00000000..bd0fb08d
--- /dev/null
+++ b/res/drawable/gears.png
Binary files differ
diff --git a/res/drawable/gears_button.xml b/res/drawable/gears_button.xml
new file mode 100755
index 00000000..ec8cebb2
--- /dev/null
+++ b/res/drawable/gears_button.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 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.
+*/
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true" android:state_pressed="false"
+ android:state_enabled="true"
+ android:drawable="@drawable/gears_button_selected" />
+ <item android:state_enabled="true" android:state_pressed="true"
+ android:drawable="@drawable/gears_button_pressed" />
+ <item android:state_enabled="true" android:state_focused="false"
+ android:state_pressed="false"
+ android:drawable="@drawable/gears_button_default" />
+ <item android:drawable="@drawable/gears_button_default" />
+</selector>
diff --git a/res/drawable/gears_button_default.9.png b/res/drawable/gears_button_default.9.png
new file mode 100644
index 00000000..3b1d065f
--- /dev/null
+++ b/res/drawable/gears_button_default.9.png
Binary files differ
diff --git a/res/drawable/gears_button_pressed.9.png b/res/drawable/gears_button_pressed.9.png
new file mode 100644
index 00000000..7b567940
--- /dev/null
+++ b/res/drawable/gears_button_pressed.9.png
Binary files differ
diff --git a/res/drawable/gears_button_selected.9.png b/res/drawable/gears_button_selected.9.png
new file mode 100644
index 00000000..e1d5608b
--- /dev/null
+++ b/res/drawable/gears_button_selected.9.png
Binary files differ
diff --git a/res/drawable/gears_icon_32x32.png b/res/drawable/gears_icon_32x32.png
new file mode 100644
index 00000000..f703f7fd
--- /dev/null
+++ b/res/drawable/gears_icon_32x32.png
Binary files differ
diff --git a/res/drawable/gears_icon_48x48.png b/res/drawable/gears_icon_48x48.png
new file mode 100644
index 00000000..da5493be
--- /dev/null
+++ b/res/drawable/gears_icon_48x48.png
Binary files differ
diff --git a/res/drawable/gears_local_data.png b/res/drawable/gears_local_data.png
new file mode 100644
index 00000000..4c434566
--- /dev/null
+++ b/res/drawable/gears_local_data.png
Binary files differ
diff --git a/res/drawable/gears_location_data.png b/res/drawable/gears_location_data.png
new file mode 100644
index 00000000..f69ad758
--- /dev/null
+++ b/res/drawable/gears_location_data.png
Binary files differ
diff --git a/res/drawable/ic_btn_close_panel.png b/res/drawable/ic_btn_close_panel.png
new file mode 100644
index 00000000..b6e1ce14
--- /dev/null
+++ b/res/drawable/ic_btn_close_panel.png
Binary files differ
diff --git a/res/drawable/ic_btn_find_next.png b/res/drawable/ic_btn_find_next.png
new file mode 100644
index 00000000..abdc2477
--- /dev/null
+++ b/res/drawable/ic_btn_find_next.png
Binary files differ
diff --git a/res/drawable/ic_btn_find_prev.png b/res/drawable/ic_btn_find_prev.png
new file mode 100644
index 00000000..4e3da1db
--- /dev/null
+++ b/res/drawable/ic_btn_find_prev.png
Binary files differ
diff --git a/res/drawable/ic_dialog_bookmark.png b/res/drawable/ic_dialog_bookmark.png
new file mode 100644
index 00000000..6a83a16b
--- /dev/null
+++ b/res/drawable/ic_dialog_bookmark.png
Binary files differ
diff --git a/res/drawable/ic_dialog_browser_certificate_partially_secure.png b/res/drawable/ic_dialog_browser_certificate_partially_secure.png
new file mode 100755
index 00000000..9edff390
--- /dev/null
+++ b/res/drawable/ic_dialog_browser_certificate_partially_secure.png
Binary files differ
diff --git a/res/drawable/ic_dialog_browser_certificate_secure.png b/res/drawable/ic_dialog_browser_certificate_secure.png
new file mode 100755
index 00000000..53e23c61
--- /dev/null
+++ b/res/drawable/ic_dialog_browser_certificate_secure.png
Binary files differ
diff --git a/res/drawable/ic_dialog_browser_security_bad.png b/res/drawable/ic_dialog_browser_security_bad.png
new file mode 100755
index 00000000..a3f660c2
--- /dev/null
+++ b/res/drawable/ic_dialog_browser_security_bad.png
Binary files differ
diff --git a/res/drawable/ic_dialog_browser_security_good.png b/res/drawable/ic_dialog_browser_security_good.png
new file mode 100755
index 00000000..b0d63999
--- /dev/null
+++ b/res/drawable/ic_dialog_browser_security_good.png
Binary files differ
diff --git a/res/drawable/ic_dialog_menu_generic.png b/res/drawable/ic_dialog_menu_generic.png
new file mode 100755
index 00000000..de07bda3
--- /dev/null
+++ b/res/drawable/ic_dialog_menu_generic.png
Binary files differ
diff --git a/res/drawable/ic_launcher_browser.png b/res/drawable/ic_launcher_browser.png
new file mode 100755
index 00000000..f58b84a0
--- /dev/null
+++ b/res/drawable/ic_launcher_browser.png
Binary files differ
diff --git a/res/drawable/ic_launcher_drm_file.png b/res/drawable/ic_launcher_drm_file.png
new file mode 100644
index 00000000..57378b23
--- /dev/null
+++ b/res/drawable/ic_launcher_drm_file.png
Binary files differ
diff --git a/res/drawable/ic_menu_bookmark.png b/res/drawable/ic_menu_bookmark.png
new file mode 100644
index 00000000..40c69509
--- /dev/null
+++ b/res/drawable/ic_menu_bookmark.png
Binary files differ
diff --git a/res/drawable/ic_menu_windows.png b/res/drawable/ic_menu_windows.png
new file mode 100644
index 00000000..cdc4d839
--- /dev/null
+++ b/res/drawable/ic_menu_windows.png
Binary files differ
diff --git a/res/drawable/ic_new_window.png b/res/drawable/ic_new_window.png
new file mode 100644
index 00000000..3f8442df
--- /dev/null
+++ b/res/drawable/ic_new_window.png
Binary files differ
diff --git a/res/drawable/ic_search_category_bookmark.png b/res/drawable/ic_search_category_bookmark.png
new file mode 100755
index 00000000..08b5e742
--- /dev/null
+++ b/res/drawable/ic_search_category_bookmark.png
Binary files differ
diff --git a/res/drawable/ic_search_category_browser.png b/res/drawable/ic_search_category_browser.png
new file mode 100644
index 00000000..da8d4e3e
--- /dev/null
+++ b/res/drawable/ic_search_category_browser.png
Binary files differ
diff --git a/res/drawable/ic_search_category_history.png b/res/drawable/ic_search_category_history.png
new file mode 100755
index 00000000..894c254c
--- /dev/null
+++ b/res/drawable/ic_search_category_history.png
Binary files differ
diff --git a/res/drawable/ic_search_category_suggest.png b/res/drawable/ic_search_category_suggest.png
new file mode 100755
index 00000000..ada07e62
--- /dev/null
+++ b/res/drawable/ic_search_category_suggest.png
Binary files differ
diff --git a/res/drawable/ic_tab_browser_bookmark_selected.png b/res/drawable/ic_tab_browser_bookmark_selected.png
new file mode 100644
index 00000000..184885e2
--- /dev/null
+++ b/res/drawable/ic_tab_browser_bookmark_selected.png
Binary files differ
diff --git a/res/drawable/ic_tab_browser_bookmark_unselected.png b/res/drawable/ic_tab_browser_bookmark_unselected.png
new file mode 100644
index 00000000..25ddf188
--- /dev/null
+++ b/res/drawable/ic_tab_browser_bookmark_unselected.png
Binary files differ
diff --git a/res/drawable/ic_tab_browser_history_selected.png b/res/drawable/ic_tab_browser_history_selected.png
new file mode 100644
index 00000000..b39c21a9
--- /dev/null
+++ b/res/drawable/ic_tab_browser_history_selected.png
Binary files differ
diff --git a/res/drawable/ic_tab_browser_history_unselected.png b/res/drawable/ic_tab_browser_history_unselected.png
new file mode 100644
index 00000000..c0847d0d
--- /dev/null
+++ b/res/drawable/ic_tab_browser_history_unselected.png
Binary files differ
diff --git a/res/drawable/ic_tab_browser_visited_selected.png b/res/drawable/ic_tab_browser_visited_selected.png
new file mode 100644
index 00000000..f2cc55c8
--- /dev/null
+++ b/res/drawable/ic_tab_browser_visited_selected.png
Binary files differ
diff --git a/res/drawable/ic_tab_browser_visited_unselected.png b/res/drawable/ic_tab_browser_visited_unselected.png
new file mode 100644
index 00000000..b808fbd2
--- /dev/null
+++ b/res/drawable/ic_tab_browser_visited_unselected.png
Binary files differ
diff --git a/res/drawable/page_indicator.png b/res/drawable/page_indicator.png
new file mode 100644
index 00000000..73c030a7
--- /dev/null
+++ b/res/drawable/page_indicator.png
Binary files differ
diff --git a/res/drawable/page_indicator_unselected2.png b/res/drawable/page_indicator_unselected2.png
new file mode 100644
index 00000000..70818eeb
--- /dev/null
+++ b/res/drawable/page_indicator_unselected2.png
Binary files differ
diff --git a/res/drawable/ssl_icon.png b/res/drawable/ssl_icon.png
new file mode 100644
index 00000000..032e6835
--- /dev/null
+++ b/res/drawable/ssl_icon.png
Binary files differ
diff --git a/res/layout-land/http_authentication.xml b/res/layout-land/http_authentication.xml
new file mode 100644
index 00000000..b2f012d7
--- /dev/null
+++ b/res/layout-land/http_authentication.xml
@@ -0,0 +1,73 @@
+<?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:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <TableLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="12dip"
+ android:gravity="center_horizontal" >
+
+ <TableRow>
+ <TextView android:id="@+id/username_view"
+ android:textSize="18sp"
+ android:textColor="@color/username_text"
+ android:text="@string/username"
+ android:gravity="right"
+ android:layout_marginLeft="20dip" />
+
+ <EditText android:id="@+id/username_edit"
+ android:textSize="18sp"
+ android:textColor="@color/username_edit"
+ android:scrollHorizontally="true"
+ android:autoText="false"
+ android:capitalize="none"
+ android:gravity="fill_horizontal"
+ android:layout_weight="1"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="12dip" />
+ </TableRow>
+
+ <TableRow>
+ <TextView android:id="@+id/password_view"
+ android:textSize="18sp"
+ android:textColor="@color/password_text"
+ android:text="@string/password"
+ android:gravity="right"
+ android:layout_marginLeft="20dip" />
+
+ <EditText android:id="@+id/password_edit"
+ android:textSize="18sp"
+ android:textColor="@color/password_edit"
+ android:scrollHorizontally="true"
+ android:autoText="false"
+ android:capitalize="none"
+ android:gravity="fill_horizontal"
+ android:layout_weight="1"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="12dip"
+ android:password="true" />
+ </TableRow>
+ </TableLayout>
+
+</LinearLayout>
diff --git a/res/layout-land/page_info.xml b/res/layout-land/page_info.xml
new file mode 100644
index 00000000..63d18cac
--- /dev/null
+++ b/res/layout-land/page_info.xml
@@ -0,0 +1,73 @@
+<?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.
+-->
+
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <!-- Title: -->
+ <TextView
+ android:id="@+id/title"
+ android:textStyle="bold"
+ android:textSize="14sp"
+ android:textColor="@color/white"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginTop="12dip"
+ android:layout_marginBottom="12dip" />
+
+ <!-- Address: -->
+ <TableLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:shrinkColumns="1"
+ android:orientation="vertical">
+
+ <TableRow>
+ <TextView
+ android:id="@+id/address_header"
+ android:text="@string/page_info_address"
+ android:textSize="14sp"
+ android:textColor="@color/white"
+ android:gravity="left"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip" />
+
+ <TextView
+ android:id="@+id/address"
+ android:textSize="14sp"
+ android:textColor="@color/white"
+ android:gravity="left"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="12dip" />
+ </TableRow>
+
+ </TableLayout>
+
+ </LinearLayout>
+
+</ScrollView>
+
diff --git a/res/layout-land/ssl_certificate.xml b/res/layout-land/ssl_certificate.xml
new file mode 100644
index 00000000..d6036c1b
--- /dev/null
+++ b/res/layout-land/ssl_certificate.xml
@@ -0,0 +1,266 @@
+<?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.
+-->
+
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <!-- Placeholder for the success message or one or more warnings -->
+ <LinearLayout
+ android:id="@+id/placeholder"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:layout_marginTop="12dip"
+ android:orientation="vertical" />
+
+ <!-- Dialog-title line separator -->
+ <ImageView
+ android:id="@+id/title_separator"
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"
+ android:layout_weight="1"
+ android:gravity="fill_horizontal"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="12dip" />
+
+ <TableLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:shrinkColumns="1"
+ android:orientation="vertical">
+
+ <!-- Issued to: -->
+ <TextView
+ android:id="@+id/issued_to_header"
+ android:text="@string/issued_to"
+ android:textStyle="bold"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_label"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
+
+ <!-- Common name: -->
+ <TableRow>
+ <TextView
+ android:id="@+id/to_common_header"
+ android:text="@string/common_name"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_label"
+ android:gravity="left"
+ android:layout_marginLeft="20dip" />
+
+ <TextView
+ android:id="@+id/to_common"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_value"
+ android:gravity="left"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
+ </TableRow>
+
+ <!-- Organization: -->
+ <TableRow>
+ <TextView
+ android:id="@+id/to_org_header"
+ android:text="@string/org_name"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_label"
+ android:gravity="left"
+ android:layout_marginLeft="20dip" />
+
+ <TextView
+ android:id="@+id/to_org"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_value"
+ android:gravity="left"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
+ </TableRow>
+
+ <!-- Organizational unit: -->
+ <TableRow>
+ <TextView
+ android:id="@+id/to_org_unit_header"
+ android:text="@string/org_unit"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_label"
+ android:gravity="left"
+ android:layout_marginLeft="20dip" />
+
+ <TextView
+ android:id="@+id/to_org_unit"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_value"
+ android:gravity="left"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="12dip" />
+ </TableRow>
+
+ <!-- Issued by: -->
+ <TextView
+ android:id="@+id/issued_to_header"
+ android:text="@string/issued_by"
+ android:textStyle="bold"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_label"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
+
+ <!-- Common name: -->
+ <TableRow>
+ <TextView
+ android:id="@+id/by_common_header"
+ android:text="@string/common_name"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_label"
+ android:gravity="left"
+ android:layout_marginLeft="20dip" />
+
+ <TextView
+ android:id="@+id/by_common"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_value"
+ android:gravity="left"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
+ </TableRow>
+
+ <!-- Organization: -->
+ <TableRow>
+ <TextView
+ android:id="@+id/by_org_header"
+ android:text="@string/org_name"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_label"
+ android:gravity="left"
+ android:layout_marginLeft="20dip" />
+
+ <TextView
+ android:id="@+id/by_org"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_value"
+ android:gravity="left"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
+ </TableRow>
+
+ <!-- Organizational unit: -->
+ <TableRow>
+ <TextView
+ android:id="@+id/by_org_unit_header"
+ android:text="@string/org_unit"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_label"
+ android:gravity="left"
+ android:layout_marginLeft="20dip" />
+
+ <TextView
+ android:id="@+id/by_org_unit"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_value"
+ android:gravity="left"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="12dip" />
+ </TableRow>
+
+ <!-- Validity Dates: -->
+ <TextView
+ android:id="@+id/validity_header"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/validity_period"
+ android:textStyle="bold"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_label"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
+
+ <!-- Issued On: -->
+ <TableRow>
+ <TextView
+ android:id="@+id/issued_on_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/issued_on"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_label"
+ android:gravity="left"
+ android:layout_marginLeft="20dip" />
+
+ <TextView
+ android:id="@+id/issued_on"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_value"
+ android:gravity="left"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
+
+ </TableRow>
+
+ <!-- Expires On: -->
+ <TableRow>
+ <TextView
+ android:id="@+id/expires_on_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/expires_on"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_label"
+ android:gravity="left"
+ android:layout_marginLeft="20dip" />
+
+ <TextView
+ android:id="@+id/expires_on"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_value"
+ android:gravity="left"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="12dip" />
+ </TableRow>
+
+ </TableLayout>
+
+ </LinearLayout>
+
+</ScrollView>
diff --git a/res/layout/add_new_bookmark.xml b/res/layout/add_new_bookmark.xml
new file mode 100644
index 00000000..bb56b933
--- /dev/null
+++ b/res/layout/add_new_bookmark.xml
@@ -0,0 +1,59 @@
+<?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:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:orientation="horizontal"
+ android:paddingRight="6dip"
+ android:paddingLeft="6dip"
+ android:paddingTop="5dip"
+ android:paddingBottom="5dip"
+ android:gravity="center_vertical"
+ >
+ <ImageView android:id="@+id/favicon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="5dip"
+ android:layout_marginRight="11dip"
+ android:focusable="false"
+ android:src="@*android:drawable/sym_action_add"
+ android:scaleType="fitCenter"
+ />
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ >
+ <TextView android:id="@+id/title"
+ android:text="@string/add_new_bookmark"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textStyle="bold"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="2dip"
+ />
+ <TextView android:id="@+id/url"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:maxLines="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ />
+ </LinearLayout>
+</LinearLayout>
diff --git a/res/layout/browser_add_bookmark.xml b/res/layout/browser_add_bookmark.xml
new file mode 100644
index 00000000..ca84f30b
--- /dev/null
+++ b/res/layout/browser_add_bookmark.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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:layout_height="wrap_content"
+ android:orientation="vertical"
+ >
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingTop="5dip"
+ android:paddingBottom="13dip"
+ android:paddingLeft="20dip"
+ android:paddingRight="20dip" >
+
+ <TextView
+ android:id="@+id/titleText"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/name"
+ android:gravity="left"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <EditText
+ android:id="@+id/title"
+ android:layout_height="wrap_content"
+ android:layout_width="250dip"
+ android:gravity="fill_horizontal"
+ android:inputType="textCapSentences"
+ android:selectAllOnFocus="true"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+
+
+ <TextView
+ android:id="@+id/addressText"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/location"
+ android:gravity="left"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <EditText
+ android:id="@+id/address"
+ android:layout_height="wrap_content"
+ android:layout_width="250dip"
+ android:hint="@string/http"
+ android:gravity="fill_horizontal"
+ android:inputType="textUri"
+ android:selectAllOnFocus="true"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:background="#c6c3c6"
+ android:minHeight="54dip"
+ android:orientation="horizontal"
+ android:paddingTop="4dip"
+ android:paddingLeft="2dip"
+ android:paddingRight="2dip" >
+ <Button android:id="@+id/OK"
+ android:text="@string/save"
+ android:layout_width="0dip"
+ android:layout_gravity="left"
+ android:layout_weight="1"
+ android:maxLines="2"
+ android:layout_height="wrap_content" />
+ <Button android:id="@+id/cancel"
+ android:text="@string/do_not_save"
+ android:layout_width="0dip"
+ android:layout_gravity="right"
+ android:layout_weight="1"
+ android:maxLines="2"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+
+</LinearLayout>
+
diff --git a/res/layout/browser_bookmarks_page.xml b/res/layout/browser_bookmarks_page.xml
new file mode 100644
index 00000000..3fb03083
--- /dev/null
+++ b/res/layout/browser_bookmarks_page.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+ <ListView android:id="@+id/list"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ />
+</LinearLayout>
diff --git a/res/layout/browser_download_item.xml b/res/layout/browser_download_item.xml
new file mode 100644
index 00000000..c26aab9d
--- /dev/null
+++ b/res/layout/browser_download_item.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** assets/res/any/layout/browser_bookmark_item.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.
+*/
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <ImageView android:id="@+id/download_icon"
+ android:layout_width="@android:dimen/app_icon_size"
+ android:layout_height="@android:dimen/app_icon_size"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:scaleType="fitCenter"
+ />
+
+ <TextView android:id="@+id/download_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="1"
+ android:layout_alignParentTop="true"
+ android:layout_toRightOf="@id/download_icon"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ />
+ <TextView android:id="@+id/domain"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/download_icon"
+ android:layout_below="@id/download_title"
+ android:maxLines="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ />
+
+ <TextView android:id="@+id/complete_date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_below="@id/domain"
+ android:layout_alignParentRight="true"
+ android:visibility="gone"
+ />
+
+ <TextView android:id="@+id/complete_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/domain"
+ android:layout_toRightOf="@id/download_icon"
+ android:layout_toLeftOf="@id/complete_date"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:visibility="gone"
+ />
+
+ <ProgressBar android:id="@+id/download_progress"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/download_icon"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentRight="true"
+ android:max="100"
+ />
+ <TextView android:id="@+id/progress_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_centerHorizontal="true"
+ android:layout_below="@id/download_progress"
+ />
+</RelativeLayout>
+
diff --git a/res/layout/browser_downloads_page.xml b/res/layout/browser_downloads_page.xml
new file mode 100644
index 00000000..c83a727a
--- /dev/null
+++ b/res/layout/browser_downloads_page.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** assets/res/any/layout/browser_bookmarks_page.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.
+*/
+-->
+
+<ListView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/list"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"/>
diff --git a/res/layout/browser_find.xml b/res/layout/browser_find.xml
new file mode 100644
index 00000000..000799f1
--- /dev/null
+++ b/res/layout/browser_find.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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:id="@+id/findControls"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="5dip"
+ android:paddingLeft="4dip"
+ android:paddingRight="4dip"
+ android:paddingBottom="1dip"
+ android:background="@android:drawable/bottom_bar">
+ <ImageButton
+ android:src="@drawable/ic_btn_find_prev"
+ android:id="@+id/previous"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
+ <ImageButton
+ android:src="@drawable/ic_btn_find_next"
+ android:id="@+id/next"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:layout_marginRight="6dip"
+ >
+ <EditText android:id="@+id/edit"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scrollHorizontally="true"
+ android:inputType="text"
+ android:hint="@string/find_dot"
+ />
+ <LinearLayout android:id="@+id/matches_view"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:orientation="horizontal"
+ android:layout_gravity="right"
+ android:visibility="invisible"
+ >
+ <TextView android:id="@+id/matches"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:text="@string/zero"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondaryInverse"
+ />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="3dip"
+ android:layout_gravity="right"
+ android:text="@string/matches_found"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondaryInverse"
+ />
+ </LinearLayout>
+ </LinearLayout>
+ <ImageButton android:id="@+id/done"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_btn_close_panel"
+ />
+</LinearLayout>
+
diff --git a/res/layout/browser_subwindow.xml b/res/layout/browser_subwindow.xml
new file mode 100644
index 00000000..5b00bf73
--- /dev/null
+++ b/res/layout/browser_subwindow.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/subwindow_container"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" >
+ <FrameLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:padding="10dip" >
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+ android:background="@android:drawable/dialog_frame" >
+ <WebView android:id="@+id/webview"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1" />
+ </LinearLayout>
+ </FrameLayout>
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="right"
+ android:gravity="right" >
+ <ImageButton android:id="@+id/subwindow_close"
+ android:focusable="true"
+ android:padding="0dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@android:drawable/btn_default"
+ android:src="@android:drawable/ic_menu_close_clear_cancel" />
+ </LinearLayout>
+</RelativeLayout>
diff --git a/res/layout/empty_history.xml b/res/layout/empty_history.xml
new file mode 100644
index 00000000..cbaffd0d
--- /dev/null
+++ b/res/layout/empty_history.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:text="@string/empty_history"
+ android:background="@color/black"
+ android:textColor="@color/white"
+ android:gravity="center"
+ android:textStyle="bold"
+ />
diff --git a/res/layout/gears_dialog.xml b/res/layout/gears_dialog.xml
new file mode 100644
index 00000000..57d4f0bd
--- /dev/null
+++ b/res/layout/gears_dialog.xml
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 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: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="vertical"
+ android:padding="10dip">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <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"/>
+
+ <TextView
+ android:id="@+id/dialog_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:gravity="center_vertical"
+ android:textSize="20dip"
+ android:textColor="@color/white"/>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ </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>
+
+ <LinearLayout
+ android:id="@+id/panel_content"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:padding="0dip"
+ android:layout_weight="1" />
+
+ <RelativeLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="10dip"
+ android:paddingRight="10dip"
+ android:paddingTop="0dip">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/selection"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textSize="13dip"
+ android:gravity="right"
+ android:visibility="gone"
+ android:textColor="@color/white"/>
+
+ </LinearLayout>
+
+ </RelativeLayout>
+
+ <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="96px"
+ android:layout_height="48px"
+ android:layout_gravity="left"
+ android:layout_weight="1"
+ android:maxLines="2"
+ android:textSize="13dip"/>
+
+ <Button
+ android:id="@+id/button_alwaysdeny"
+ android:layout_width="96px"
+ android:layout_height="48px"
+ android:layout_gravity="left"
+ android:layout_weight="1"
+ android:maxLines="2"
+ android:textSize="13dip"/>
+
+ <Button
+ android:id="@+id/button_deny"
+ android:layout_width="96px"
+ android:layout_height="48px"
+ 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/gears_dialog_permission.xml b/res/layout/gears_dialog_permission.xml
new file mode 100644
index 00000000..01914e20
--- /dev/null
+++ b/res/layout/gears_dialog_permission.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 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.
+ */
+-->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="10dip">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:paddingTop="10dip"
+ android:paddingLeft="10dip"
+ android:paddingRight="10dip"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/origin_icon"
+ android:padding="10dip"
+ android:layout_centerHorizontal="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:adjustViewBounds="true"
+ android:src="@drawable/gears"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:padding="4dip"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/origin_title"
+ 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/origin_subtitle"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:gravity="left"
+ android:textSize="13dip"
+ android:textColor="@color/white"/>
+
+ <TextView
+ android:id="@+id/origin_message"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:paddingTop="10dip"
+ android:gravity="left"
+ android:textSize="13dip"
+ android:textColor="@color/white"/>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/privacy_policy_label"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="10dip"
+ android:gravity="center_horizontal"
+ android:textSize="13dip"
+ android:textStyle="italic"
+ android:textColor="@color/white"
+ android:visibility="gone"
+ android:text="@string/privacy_policy" />
+
+ <TextView
+ android:id="@+id/permission_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>
diff --git a/res/layout/gears_dialog_settings.xml b/res/layout/gears_dialog_settings.xml
new file mode 100644
index 00000000..a9026db8
--- /dev/null
+++ b/res/layout/gears_dialog_settings.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 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:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@color/gray"
+ android:padding="1px">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@color/white">
+
+ <ListView android:id="@+id/sites_list"
+ android:padding="4dip"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ style="@android:style/Widget.ListView.White" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/gears_version"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="10dip"
+ android:gravity="center_horizontal"
+ android:textSize="13dip"
+ android:textStyle="italic"
+ android:textColor="@color/black" />
+
+</LinearLayout>
diff --git a/res/layout/gears_dialog_settings_row.xml b/res/layout/gears_dialog_settings_row.xml
new file mode 100644
index 00000000..11d757ac
--- /dev/null
+++ b/res/layout/gears_dialog_settings_row.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 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:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textSize="13dip"
+ android:background="@color/white"
+ android:textColor="@color/black">
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:padding="4dip">
+
+ <TextView
+ android:id="@+id/origin_name"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textColor="@color/black"
+ android:textStyle="bold"/>
+
+ <Button
+ android:id="@+id/origin_remove"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/gears_button"
+ android:text="@string/remove"/>
+
+ </LinearLayout>
+
+ <TableLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:stretchColumns="0"
+ android:shrinkColumns="0">
+
+ <TableRow
+ android:id="@+id/local_storage_choice"
+ android:visibility="gone">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:textColor="@color/black"
+ android:gravity="center_vertical"
+ android:text="@string/local_storage"/>
+
+ <RadioGroup
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent">
+ <RadioButton
+ android:id="@+id/local_storage_allowed"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/black"
+ android:textSize="13dip"
+ android:text="@string/allowed"/>
+
+ <RadioButton
+ android:id="@+id/local_storage_denied"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/black"
+ android:textSize="13dip"
+ android:text="@string/denied"/>
+ </RadioGroup>
+
+ </TableRow>
+
+ <TableRow
+ android:id="@+id/location_data_choice"
+ android:visibility="gone">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:textColor="@color/black"
+ android:layout_gravity="center_vertical"
+ android:text="@string/location"/>
+
+ <RadioGroup
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent">
+ <RadioButton
+ android:id="@+id/location_data_allowed"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/black"
+ android:textSize="13dip"
+ android:text="@string/allowed"/>
+
+ <RadioButton
+ android:id="@+id/location_data_denied"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/black"
+ android:textSize="13dip"
+ android:text="@string/denied"/>
+ </RadioGroup>
+
+ </TableRow>
+
+ </TableLayout>
+
+</LinearLayout>
diff --git a/res/layout/gears_settings.xml b/res/layout/gears_settings.xml
new file mode 100644
index 00000000..e3a4cbee
--- /dev/null
+++ b/res/layout/gears_settings.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 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:drawingCacheQuality="auto"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:padding="0dip">
+
+ <ListView android:id="@+id/sites_list"
+ android:padding="4dip"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ <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>
+
+</LinearLayout>
diff --git a/res/layout/gears_settings_row.xml b/res/layout/gears_settings_row.xml
new file mode 100644
index 00000000..6a1e8513
--- /dev/null
+++ b/res/layout/gears_settings_row.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 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.
+ */
+-->
+
+<com.android.internal.view.menu.ListMenuItemView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight">
+
+ <RelativeLayout
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginLeft="6dip"
+ android:layout_marginRight="6dip"
+ android:duplicateParentState="true">
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:singleLine="true"
+ android:duplicateParentState="true"
+ android:ellipsize="start"
+ android:fadingEdge="none"/>
+
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/title"
+ android:layout_alignParentLeft="true"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:singleLine="false"
+ android:duplicateParentState="true"/>
+
+ <TextView
+ android:id="@+id/info"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/subtitle"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:singleLine="true"
+ android:layout_centerHorizontal="true"
+ android:duplicateParentState="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"/>
+
+ </RelativeLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:paddingLeft="10dip"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:src="@drawable/ic_dialog_menu_generic" />
+
+ <CheckBox
+ android:id="@+id/checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:drawablePadding="10dip"
+ android:focusable="false"
+ android:clickable="false"
+ android:duplicateParentState="true"
+ android:visibility="gone"
+ android:textSize="16dip"/>
+
+ </LinearLayout>
+
+</com.android.internal.view.menu.ListMenuItemView>
diff --git a/res/layout/history_header.xml b/res/layout/history_header.xml
new file mode 100644
index 00000000..a390c37b
--- /dev/null
+++ b/res/layout/history_header.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:paddingLeft="35dip"
+ android:gravity="center_vertical"
+ /> \ No newline at end of file
diff --git a/res/layout/history_item.xml b/res/layout/history_item.xml
new file mode 100644
index 00000000..50903a48
--- /dev/null
+++ b/res/layout/history_item.xml
@@ -0,0 +1,65 @@
+<?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:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:orientation="horizontal"
+ android:paddingRight="6dip"
+ android:paddingLeft="6dip"
+ android:paddingTop="5dip"
+ android:paddingBottom="5dip"
+ android:gravity="center_vertical"
+ >
+ <ImageView android:id="@+id/favicon"
+ android:layout_width="20dip"
+ android:layout_height="20dip"
+ android:layout_marginLeft="5dip"
+ android:layout_marginRight="11dip"
+ android:padding="2dip"
+ android:background="@drawable/fav_icn_background"
+ android:src="@drawable/app_web_browser_sm"
+ />
+ <LinearLayout android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ >
+ <TextView android:id="@+id/title"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:maxLines="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ />
+ <TextView android:id="@+id/url"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:maxLines="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ />
+ </LinearLayout>
+ <CheckBox android:id="@+id/star"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:focusable="false"
+ android:button="@android:drawable/btn_star"
+ />
+</LinearLayout>
diff --git a/res/layout/http_authentication.xml b/res/layout/http_authentication.xml
new file mode 100644
index 00000000..01cd3af9
--- /dev/null
+++ b/res/layout/http_authentication.xml
@@ -0,0 +1,69 @@
+<?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.
+-->
+
+<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/username_view"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="18sp"
+ android:textColor="@color/username_text"
+ android:text="@string/username"
+ android:gravity="left"
+ android:layout_marginTop="12dip"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip" />
+
+ <EditText
+ android:id="@+id/username_edit"
+ android:textSize="18sp"
+ android:textColor="@color/username_edit"
+ android:scrollHorizontally="true"
+ android:inputType="text"
+ android:gravity="fill_horizontal"
+ android:layout_weight="1"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="12dip" />
+
+ <TextView
+ android:id="@+id/password_view"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="18sp"
+ android:textColor="@color/password_text"
+ android:text="@string/password"
+ android:gravity="left"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip" />
+
+ <EditText
+ android:id="@+id/password_edit"
+ android:textSize="18sp"
+ android:textColor="@color/password_edit"
+ android:scrollHorizontally="true"
+ android:inputType="textPassword"
+ android:gravity="fill_horizontal"
+ android:layout_weight="1"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="12dip" />
+</TableLayout>
diff --git a/res/layout/no_downloads.xml b/res/layout/no_downloads.xml
new file mode 100644
index 00000000..1c1e1cd9
--- /dev/null
+++ b/res/layout/no_downloads.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:text="@string/no_downloads"
+ android:background="@color/black"
+ android:textColor="@color/white"
+ android:gravity="center"
+ android:textStyle="bold"
+ />
diff --git a/res/layout/page_info.xml b/res/layout/page_info.xml
new file mode 100644
index 00000000..ee450826
--- /dev/null
+++ b/res/layout/page_info.xml
@@ -0,0 +1,74 @@
+<?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.
+-->
+
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <!-- Title: -->
+ <TextView
+ android:id="@+id/title"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textStyle="bold"
+ android:textSize="14sp"
+ android:textColor="@color/white"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginTop="12dip"
+ android:layout_marginBottom="12dip" />
+
+ <!-- Address: -->
+ <TableLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/address_header"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/page_info_address"
+ android:textSize="14sp"
+ android:textColor="@color/white"
+ android:gravity="left"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="10dip" />
+
+ <TextView
+ android:id="@+id/address"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="14sp"
+ android:textColor="@color/white"
+ android:gravity="left"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="12dip" />
+
+ </TableLayout>
+
+ </LinearLayout>
+
+</ScrollView>
diff --git a/res/layout/ssl_certificate.xml b/res/layout/ssl_certificate.xml
new file mode 100644
index 00000000..9f9bbf17
--- /dev/null
+++ b/res/layout/ssl_certificate.xml
@@ -0,0 +1,280 @@
+<?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.
+-->
+
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <!-- Placeholder for the success message or one or more warnings -->
+ <LinearLayout
+ android:id="@+id/placeholder"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:layout_marginTop="12dip"
+ android:orientation="vertical" />
+
+ <!-- Dialog-title line separator -->
+ <ImageView
+ android:id="@+id/title_separator"
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"
+ android:layout_weight="1"
+ android:gravity="fill_horizontal"
+ android:layout_marginRight="20dip"
+ android:layout_marginLeft="20dip"
+ android:layout_marginBottom="12dip" />
+
+ <TableLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <!-- Issued to: -->
+ <TextView
+ android:id="@+id/issued_to_header"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/issued_to"
+ android:textStyle="bold"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_label"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="10dip" />
+
+ <!-- Common name: -->
+ <TextView
+ android:id="@+id/to_common_header"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/common_name"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_label"
+ android:gravity="left"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip" />
+
+ <TextView
+ android:id="@+id/to_common"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_value"
+ android:gravity="left"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="10dip" />
+
+ <!-- Organization: -->
+ <TextView
+ android:id="@+id/to_org_header"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/org_name"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_label"
+ android:gravity="left"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip" />
+
+ <TextView
+ android:id="@+id/to_org"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_value"
+ android:gravity="left"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="10dip" />
+
+ <!-- Organizational unit: -->
+ <TextView
+ android:id="@+id/to_org_unit_header"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/org_unit"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_label"
+ android:gravity="left"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip" />
+
+ <TextView
+ android:id="@+id/to_org_unit"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_value"
+ android:gravity="left"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="12dip" />
+
+ <!-- Issued by: -->
+ <TextView
+ android:id="@+id/issued_to_header"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/issued_by"
+ android:textStyle="bold"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_label"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="10dip" />
+
+ <!-- Common name: -->
+ <TextView
+ android:id="@+id/by_common_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/common_name"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_label"
+ android:gravity="left"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip" />
+
+ <TextView
+ android:id="@+id/by_common"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_value"
+ android:gravity="left"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="10dip" />
+
+ <!-- Organization: -->
+ <TextView
+ android:id="@+id/by_org_header"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/org_name"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_label"
+ android:gravity="left"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip" />
+
+ <TextView
+ android:id="@+id/by_org"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_value"
+ android:gravity="left"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="10dip" />
+
+ <!-- Organizational unit: -->
+ <TextView
+ android:id="@+id/by_org_unit_header"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/org_unit"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_label"
+ android:gravity="left"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip" />
+
+ <TextView
+ android:id="@+id/by_org_unit"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_value"
+ android:gravity="left"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="12dip" />
+
+ <!-- Validity Dates: -->
+ <TextView
+ android:id="@+id/validity_header"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/validity_period"
+ android:textStyle="bold"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_label"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="10dip" />
+
+ <!-- Issued On: -->
+ <TextView
+ android:id="@+id/issued_on_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/issued_on"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_label"
+ android:gravity="left"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip" />
+
+ <TextView
+ android:id="@+id/issued_on"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_value"
+ android:gravity="left"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="10dip" />
+
+ <!-- Expires On: -->
+ <TextView
+ android:id="@+id/expires_on_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/expires_on"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_label"
+ android:gravity="left"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip" />
+
+ <TextView
+ android:id="@+id/expires_on"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="14sp"
+ android:textColor="@color/ssl_text_value"
+ android:gravity="left"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="12dip" />
+
+ </TableLayout>
+
+ </LinearLayout>
+
+</ScrollView>
diff --git a/res/layout/ssl_success.xml b/res/layout/ssl_success.xml
new file mode 100644
index 00000000..dad654a3
--- /dev/null
+++ b/res/layout/ssl_success.xml
@@ -0,0 +1,44 @@
+<?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.
+-->
+
+<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <TableRow>
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:scaleType="center"
+ android:src="@drawable/ic_dialog_browser_security_good"
+ android:layout_marginLeft="20dip"
+ android:layout_marginBottom="12dip" />
+
+ <TextView
+ android:id="@+id/success"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:gravity="left"
+ android:layout_weight="1"
+ android:textSize="18sp"
+ android:textColor="@color/ssl_text_label"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="12dip" />
+ </TableRow>
+
+</TableLayout>
diff --git a/res/layout/ssl_warning.xml b/res/layout/ssl_warning.xml
new file mode 100644
index 00000000..8bc406a1
--- /dev/null
+++ b/res/layout/ssl_warning.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:shrinkColumns="1"
+ android:orientation="horizontal" >
+
+ <TableRow>
+ <ImageView
+ android:scaleType="center"
+ android:src="@drawable/ic_dialog_browser_security_bad"
+ android:layout_marginLeft="20dip"
+ android:layout_marginBottom="12dip" />
+
+ <TextView
+ android:id="@+id/warning"
+ android:gravity="left"
+ android:layout_weight="1"
+ android:textSize="18sp"
+ android:textColor="@color/ssl_text_label"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="12dip" />
+ </TableRow>
+
+</TableLayout>
diff --git a/res/layout/ssl_warnings.xml b/res/layout/ssl_warnings.xml
new file mode 100644
index 00000000..5a02d97a
--- /dev/null
+++ b/res/layout/ssl_warnings.xml
@@ -0,0 +1,51 @@
+<?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.
+-->
+
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <!-- Warnings header -->
+ <TextView
+ android:id="@+id/warnings_header"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:textSize="18sp"
+ android:textColor="@color/ssl_text_label"
+ android:gravity="left"
+ android:text="@string/ssl_warnings_header"
+ android:layout_marginRight="20dip"
+ android:layout_marginLeft="20dip"
+ android:layout_marginTop="12dip" />
+
+ <!-- Placeholder for one or more warnings -->
+ <LinearLayout
+ android:id="@+id/placeholder"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:layout_marginTop="12dip"
+ android:orientation="vertical" />
+
+ </LinearLayout>
+
+</ScrollView>
diff --git a/res/layout/tabitem.xml b/res/layout/tabitem.xml
new file mode 100644
index 00000000..9c829721
--- /dev/null
+++ b/res/layout/tabitem.xml
@@ -0,0 +1,53 @@
+<?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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:orientation="vertical"
+ android:padding="4dip"
+ android:background="@color/white">
+
+ <com.android.browser.FakeWebView android:id="@+id/icon"
+ android:background="@color/black"
+ 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"/>
+
+ <ImageView android:id="@+id/close"
+ android:focusable="false"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|right"
+ android:paddingTop="14dip"
+ android:paddingBottom="12dip"
+ android:paddingLeft="12dip"
+ android:paddingRight="12dip"
+ android:src="@android:drawable/btn_dialog" />
+
+</FrameLayout>
diff --git a/res/layout/tabs.xml b/res/layout/tabs.xml
new file mode 100644
index 00000000..7cf5b5d6
--- /dev/null
+++ b/res/layout/tabs.xml
@@ -0,0 +1,41 @@
+<?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.
+-->
+
+<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/tabhost"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <TabWidget android:id="@android:id/tabs"
+ android:layout_width="fill_parent"
+ android:layout_height="68dip"
+ android:paddingLeft="1dip"
+ android:paddingRight="1dip"
+ android:paddingTop="4dip"
+ />
+
+ <FrameLayout android:id="@android:id/tabcontent"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ />
+ </LinearLayout>
+</TabHost> \ No newline at end of file
diff --git a/res/menu/bookmarks.xml b/res/menu/bookmarks.xml
new file mode 100644
index 00000000..50dcfa98
--- /dev/null
+++ b/res/menu/bookmarks.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/new_context_menu_id"
+ android:icon="@android:drawable/ic_menu_add"
+ android:title="@string/bookmark_page" />
+</menu>
diff --git a/res/menu/bookmarkscontext.xml b/res/menu/bookmarkscontext.xml
new file mode 100644
index 00000000..b43e242d
--- /dev/null
+++ b/res/menu/bookmarkscontext.xml
@@ -0,0 +1,38 @@
+<?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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <group android:id="@+id/ADD_MENU">
+ <item android:id="@+id/new_context_menu_id"
+ android:title="@string/save_to_bookmarks"/>
+ </group>
+ <group android:id="@+id/CONTEXT_MENU">
+ <item android:id="@+id/open_context_menu_id"
+ android:title="@string/open_bookmark"/>
+ <item android:id="@+id/new_window_context_menu_id"
+ android:title="@string/open_in_new_window"/>
+ <item android:id="@+id/edit_context_menu_id"
+ android:title="@string/edit_bookmark"/>
+ <item android:id="@+id/shortcut_context_menu_id"
+ android:title="@string/create_shortcut_bookmark"/>
+ <item android:id="@+id/send_context_menu_id"
+ android:title="@string/tab_picker_send_url"/>
+ <item android:id="@+id/copy_url_context_menu_id"
+ android:title="@string/contextmenu_copylink"/>
+ <item android:id="@+id/delete_context_menu_id"
+ android:title="@string/remove_bookmark"/>
+ </group>
+</menu>
diff --git a/res/menu/browser.xml b/res/menu/browser.xml
new file mode 100644
index 00000000..088fb6e9
--- /dev/null
+++ b/res/menu/browser.xml
@@ -0,0 +1,113 @@
+<?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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <group android:id="@+id/MAIN_MENU">
+ <item android:id="@+id/goto_menu_id"
+ android:title="@string/goto_dot"
+ android:alphabeticShortcut="l"
+ android:icon="@android:drawable/ic_menu_search"/>
+ <item android:id="@+id/bookmarks_menu_id"
+ android:title="@string/bookmarks"
+ android:alphabeticShortcut="b"
+ android:icon="@drawable/ic_menu_bookmark" />
+ <item android:id="@+id/windows_menu_id"
+ android:title="@string/view_tabs"
+ android:alphabeticShortcut="t"
+ android:titleCondensed="@string/view_tabs_condensed"
+ android:icon="@drawable/ic_menu_windows" />
+ <item android:id="@+id/stop_reload_menu_id"
+ android:alphabeticShortcut="r" />
+ <item android:id="@+id/forward_menu_id"
+ android:title="@string/forward"
+ android:alphabeticShortcut="k"
+ android:icon="@*android:drawable/ic_menu_forward" />
+ <item android:id="@+id/find_menu_id"
+ android:title="@string/find_dot"
+ android:alphabeticShortcut="f" />
+ <item android:id="@+id/select_text_id"
+ android:title="@string/select_dot" />
+ <item android:id="@+id/page_info_menu_id"
+ android:title="@string/page_info"
+ android:alphabeticShortcut="g" />
+ <item android:id="@+id/share_page_menu_id"
+ android:title="@string/share_page"/>
+ <item android:id="@+id/view_downloads_menu_id"
+ android:title="@string/menu_view_download"
+ android:alphabeticShortcut="d" />
+ <item android:id="@+id/preferences_menu_id"
+ android:title="@string/menu_preferences"
+ android:alphabeticShortcut="p" />
+ <!-- followings are debug only -->
+ <item android:id="@+id/dump_nav_menu_id"
+ android:title="@string/dump_nav"
+ android:visible="false" />
+ </group>
+ <group android:id="@+id/MAIN_SHORTCUT_MENU" android:visible="false">
+ <item android:id="@+id/back_menu_id"
+ android:alphabeticShortcut="j" />
+ <item android:id="@+id/close_menu_id"
+ android:alphabeticShortcut="w" />
+ <item android:id="@+id/homepage_menu_id"
+ android:alphabeticShortcut="&#32;" />
+ <item android:id="@+id/classic_history_menu_id"
+ android:alphabeticShortcut="h" />
+ <item android:id="@+id/zoom_in_menu_id"
+ android:alphabeticShortcut="i" />
+ <item android:id="@+id/zoom_out_menu_id"
+ android:alphabeticShortcut="o" />
+ <item android:id="@+id/window_one_menu_id"
+ android:alphabeticShortcut="1" />
+ <item android:id="@+id/window_two_menu_id"
+ android:alphabeticShortcut="2" />
+ <item android:id="@+id/window_three_menu_id"
+ android:alphabeticShortcut="3" />
+ <item android:id="@+id/window_four_menu_id"
+ android:alphabeticShortcut="4" />
+ <item android:id="@+id/window_five_menu_id"
+ android:alphabeticShortcut="5" />
+ <item android:id="@+id/window_six_menu_id"
+ android:alphabeticShortcut="6" />
+ <item android:id="@+id/window_seven_menu_id"
+ android:alphabeticShortcut="7" />
+ <item android:id="@+id/window_eight_menu_id"
+ android:alphabeticShortcut="8" />
+ </group>
+ <group android:id="@+id/TAB_MENU">
+ <item android:id="@+id/new_tab_menu_id"
+ android:title="@string/tab_picker_new_tab"
+ android:alphabeticShortcut="t"
+ android:icon="@android:drawable/ic_menu_add" />
+ <item android:id="@+id/bookmarks_tab_menu_id"
+ android:title="@string/bookmarks"
+ android:alphabeticShortcut="b"
+ android:icon="@drawable/ic_menu_bookmark" />
+ <item android:id="@+id/history_tab_menu_id"
+ android:title="@string/history"
+ android:alphabeticShortcut="h"
+ android:icon="@android:drawable/ic_menu_recent_history" />
+ </group>
+ <!-- these items are toggled in and out of @+id/stop_reload_menu_id -->
+ <item android:id="@+id/stop_menu_id"
+ android:title="@string/stop"
+ android:icon="@*android:drawable/ic_menu_stop"
+ android:visible="false" />
+ <item android:id="@+id/reload_menu_id"
+ android:title="@string/reload"
+ android:icon="@*android:drawable/ic_menu_refresh"
+ android:visible="false" />
+</menu>
+
diff --git a/res/menu/browsercontext.xml b/res/menu/browsercontext.xml
new file mode 100644
index 00000000..8b2678ef
--- /dev/null
+++ b/res/menu/browsercontext.xml
@@ -0,0 +1,59 @@
+<?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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <group android:id="@+id/PHONE_MENU">
+ <item android:id="@+id/dial_context_menu_id"
+ android:title="@string/contextmenu_dial_dot"/>
+ <item android:id="@+id/add_contact_context_menu_id"
+ android:title="@string/contextmenu_add_contact"/>
+ <item android:id="@+id/copy_phone_context_menu_id"
+ android:title="@string/contextmenu_copy"/>
+ </group>
+ <group android:id="@+id/EMAIL_MENU">
+ <item android:id="@+id/email_context_menu_id"
+ android:title="@string/contextmenu_send_mail"/>
+ <item android:id="@+id/copy_mail_context_menu_id"
+ android:title="@string/contextmenu_copy"/>
+ </group>
+ <group android:id="@+id/GEO_MENU">
+ <item android:id="@+id/map_context_menu_id"
+ android:title="@string/contextmenu_map"/>
+ <item android:id="@+id/copy_geo_context_menu_id"
+ android:title="@string/contextmenu_copy"/>
+ </group>
+ <group android:id="@+id/ANCHOR_MENU">
+ <item android:id="@+id/open_context_menu_id"
+ android:title="@string/contextmenu_openlink"/>
+ <item android:id="@+id/open_newtab_context_menu_id"
+ android:title="@string/contextmenu_openlink_newwindow"/>
+ <item android:id="@+id/bookmark_context_menu_id"
+ android:title="@string/contextmenu_bookmark_thislink"/>
+ <item android:id="@+id/save_link_context_menu_id"
+ android:title="@string/contextmenu_savelink"/>
+ <item android:id="@+id/share_link_context_menu_id"
+ android:title="@string/contextmenu_sharelink"/>
+ <item android:id="@+id/copy_link_context_menu_id"
+ android:title="@string/contextmenu_copylink"/>
+ </group>
+ <group android:id="@+id/IMAGE_MENU">
+ <item android:id="@+id/download_context_menu_id"
+ android:title="@string/contextmenu_download_image"/>
+ <item android:id="@+id/view_image_context_menu_id"
+ android:title="@string/contextmenu_view_image"/>
+ </group>
+</menu>
+
diff --git a/res/menu/downloadhistory.xml b/res/menu/downloadhistory.xml
new file mode 100644
index 00000000..ee3b7519
--- /dev/null
+++ b/res/menu/downloadhistory.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:id="@+id/download_menu_clear_all"
+ android:title="@string/download_menu_clear_all"
+ android:icon="@*android:drawable/ic_menu_clear_playlist" />
+ <item android:id="@+id/download_menu_cancel_all"
+ android:title="@string/download_menu_cancel_all"
+ android:icon="@android:drawable/ic_menu_close_clear_cancel" />
+
+</menu>
diff --git a/res/menu/downloadhistorycontextfailed.xml b/res/menu/downloadhistorycontextfailed.xml
new file mode 100644
index 00000000..f51b29a9
--- /dev/null
+++ b/res/menu/downloadhistorycontextfailed.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:id="@+id/download_menu_clear"
+ android:title="@string/download_menu_clear" />
+
+</menu>
diff --git a/res/menu/downloadhistorycontextfinished.xml b/res/menu/downloadhistorycontextfinished.xml
new file mode 100644
index 00000000..2714b5b3
--- /dev/null
+++ b/res/menu/downloadhistorycontextfinished.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:id="@+id/download_menu_open"
+ android:title="@string/download_menu_open" />
+ <item android:id="@+id/download_menu_clear"
+ android:title="@string/download_menu_clear" />
+
+</menu>
diff --git a/res/menu/downloadhistorycontextrunning.xml b/res/menu/downloadhistorycontextrunning.xml
new file mode 100644
index 00000000..552d0fa2
--- /dev/null
+++ b/res/menu/downloadhistorycontextrunning.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:id="@+id/download_menu_cancel"
+ android:title="@string/download_menu_cancel" />
+
+</menu>
diff --git a/res/menu/history.xml b/res/menu/history.xml
new file mode 100644
index 00000000..3bb30a08
--- /dev/null
+++ b/res/menu/history.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/clear_history_menu_id"
+ android:title="@string/clear_history"
+ android:icon="@android:drawable/ic_menu_close_clear_cancel" />
+</menu>
diff --git a/res/menu/historycontext.xml b/res/menu/historycontext.xml
new file mode 100644
index 00000000..dfda010e
--- /dev/null
+++ b/res/menu/historycontext.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/open_context_menu_id"
+ android:title="@string/contextmenu_openlink"/>
+ <item android:id="@+id/new_window_context_menu_id"
+ android:title="@string/contextmenu_openlink_newwindow"/>
+ <item android:id="@+id/save_to_bookmarks_menu_id"
+ android:title="@string/save_to_bookmarks"/>
+ <item android:id="@+id/share_link_context_menu_id"
+ android:title="@string/contextmenu_sharelink"/>
+ <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"/>
+</menu>
diff --git a/res/menu/tabscontext.xml b/res/menu/tabscontext.xml
new file mode 100644
index 00000000..e89720e9
--- /dev/null
+++ b/res/menu/tabscontext.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/view_tab_menu_id"
+ android:title="@string/tab_picker_view_tab"/>
+ <item android:id="@+id/remove_tab_menu_id"
+ android:title="@string/tab_picker_remove_tab"/>
+ <item android:id="@+id/bookmark_tab_menu_id"
+ android:title="@string/tab_picker_bookmark"/>
+ <item android:id="@+id/properties_tab_menu_id"
+ android:title="@string/page_info"/>
+</menu>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
new file mode 100644
index 00000000..bc1597a8
--- /dev/null
+++ b/res/values-cs/strings.xml
@@ -0,0 +1,334 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name">"Prohlížeč"</string>
+ <string name="tab_bookmarks">"Záložky"</string>
+ <string name="tab_most_visited">"Nejčastěji navštěvované"</string>
+ <string name="tab_history">"Historie"</string>
+ <string name="added_to_bookmarks">"Přidáno k záložkám"</string>
+ <string name="removed_from_bookmarks">"Odstraněno ze záložek"</string>
+ <string name="sign_in_to">"Přihlásit se: <xliff:g id="HOSTNAME">%s1</xliff:g> <xliff:g id="REALM">%s2</xliff:g>"</string>
+ <string name="username">"Název"</string>
+ <string name="password">"Heslo"</string>
+ <string name="action">"Přihlásit se"</string>
+ <string name="cancel">"Zrušit"</string>
+ <string name="ok">"OK"</string>
+ <string name="matches_found">" shod"</string>
+ <string name="zero">"Ne"</string>
+ <string name="page_info">"Informace o stránce"</string>
+ <string name="page_info_view">"Zobrazit informace o stránce"</string>
+ <string name="page_info_address">"Adresa:"</string>
+ <string name="ssl_warnings_header">"Vyskytly se problémy s certifikátem zabezpečení tohoto webu."</string>
+ <string name="ssl_continue">"Pokračovat"</string>
+ <string name="security_warning">"Upozornění zabezpečení"</string>
+ <string name="view_certificate">"Zobrazit certifikát"</string>
+ <string name="ssl_untrusted">"Tento certifikát nepochází od důvěryhodné autority."</string>
+ <string name="ssl_mismatch">"Název webu se neshoduje s názvem uvedeným v certifikátu."</string>
+ <string name="ssl_expired">"Platnost certifikátu vypršela."</string>
+ <string name="ssl_not_yet_valid">"Tento certifikát zatím není platný."</string>
+ <string name="ssl_certificate">"Certifikát zabezpečení"</string>
+ <string name="ssl_certificate_is_valid">"Tento certifikát je platný."</string>
+ <string name="issued_to">"Vydáno komu:"</string>
+ <string name="common_name">"Běžný název:"</string>
+ <string name="org_name">"Organizace:"</string>
+ <string name="org_unit">"Organizační jednotka:"</string>
+ <string name="issued_by">"Vydal:"</string>
+ <string name="validity_period">"Platnost:"</string>
+ <string name="issued_on">"Datum vydání:"</string>
+ <string name="expires_on">"Platnost vyprší:"</string>
+ <string name="retrieving_creds_dlg_msg">"Načítání podrobností pro přihlášení..."</string>
+ <string name="close">"Zavřít"</string>
+ <string name="close_window">"Toto okno bude zavřeno."</string>
+ <string name="stopping">"Zastavuji..."</string>
+ <string name="stop">"Zastavit"</string>
+ <string name="reload">"Obnovit"</string>
+ <string name="forward">"Předat dál"</string>
+ <string name="save">"OK"</string>
+ <string name="do_not_save">"Zrušit"</string>
+ <string name="location">"Lokalita"</string>
+ <string name="name">"Název"</string>
+ <string name="http">"http://"</string>
+ <string name="save_to_bookmarks">"Přidat do záložek"</string>
+ <string name="edit_bookmark">"Upravit záložku"</string>
+ <string name="create_shortcut_bookmark">"Přidat odkaz na plochu"</string>
+ <string name="open_bookmark">"Otevřít"</string>
+ <string name="remove_bookmark">"Smazat záložku"</string>
+ <string name="remove_history_item">"Odstranit z historie"</string>
+ <string name="bookmark_saved">"Uloženo do záložek."</string>
+ <string name="bookmark_needs_title">"Záložka musí mít název."</string>
+ <string name="bookmark_needs_url">"Záložka musí mít umístění."</string>
+ <string name="empty_bookmark">"Nelze vytvořit prázdnou záložku."</string>
+ <string name="bookmark_url_not_valid">"Adresa URL není platná."</string>
+ <string name="delete_bookmark">"Smazat"</string>
+ <string name="bookmark_page">"Přidat poslední zobrazenou stránku do záložek"</string>
+ <string name="current_page">"od "</string>
+ <string name="delete_bookmark_warning">"Záložka <xliff:g id="BOOKMARK">%s</xliff:g> bude smazána."</string>
+ <string name="open_in_new_window">"Otevřít v novém okně"</string>
+ <string name="new_window">"Nové okno"</string>
+ <string name="goto_dot">"Přejít"</string>
+ <string name="find_dot">"Vyhledat na stránce"</string>
+ <string name="select_dot">"Označit text"</string>
+ <string name="view_tabs">"Přehled okna"</string>
+ <string name="view_tabs_condensed">"Windows"</string>
+ <string name="tab_picker_title">"Aktuální okna"</string>
+ <string name="tab_picker_view_tab">"Zobrazit"</string>
+ <string name="tab_picker_new_tab">"Nové okno"</string>
+ <string name="tab_picker_remove_tab">"Zavřít"</string>
+ <string name="tab_picker_bookmark">"Přidat do záložek"</string>
+ <string name="tab_picker_send_url">"Sdílet odkaz"</string>
+ <string name="bookmarks">"Záložky"</string>
+ <string name="shortcut_bookmark">"Přidat do záložek"</string>
+ <string name="history">"Historie"</string>
+ <string name="menu_view_download">"Stahování"</string>
+ <string name="share_page">"Sdílet stránku"</string>
+ <string name="contextmenu_openlink">"Otevřít"</string>
+ <string name="contextmenu_openlink_newwindow">"Otevřít v novém okně"</string>
+ <string name="contextmenu_bookmark_thislink">"Přidat odkaz do záložek"</string>
+ <string name="contextmenu_savelink">"Uložit odkaz"</string>
+ <string name="contextmenu_sharelink">"Sdílet odkaz"</string>
+ <string name="contextmenu_copy">"Kopírovat"</string>
+ <string name="contextmenu_copylink">"Kopírovat adresu URL odkazu"</string>
+ <string name="contextmenu_download_image">"Uložit obrázek"</string>
+ <string name="contextmenu_view_image">"Zobrazit obrázek"</string>
+ <string name="contextmenu_dial_dot">"Volat..."</string>
+ <string name="contextmenu_add_contact">"Přidat kontakt"</string>
+ <string name="contextmenu_send_mail">"Odeslat e-mail"</string>
+ <string name="contextmenu_map">"Mapa"</string>
+ <string name="clear">"Vymazat"</string>
+ <string name="replace">"Nahradit"</string>
+ <string name="browser_bookmarks_page_bookmarks_text">"Záložky"</string>
+ <string name="menu_preferences">"Nastavení"</string>
+ <string name="pref_content_title">"Nastavení obsahu stránky"</string>
+ <string name="pref_content_load_images">"Načíst obrázky"</string>
+ <string name="pref_content_load_images_summary">"Zobrazovat obrázky na webových stránkách"</string>
+ <string name="pref_content_block_popups">"Blokovat vyskakovací okna"</string>
+ <string name="pref_content_javascript">"Povolit JavaScript"</string>
+ <string name="pref_content_open_in_background">"Otevřít na pozadí"</string>
+ <string name="pref_content_open_in_background_summary">"Nová okna budou otevřena za aktuálním"</string>
+ <string name="pref_content_homepage">"Nastavit domovskou stránku"</string>
+ <string name="pref_content_autofit">"Automaticky přizpůsobit stránky"</string>
+ <string name="pref_content_autofit_summary">"Přizpůsobit velikost webových stránek obrazovce"</string>
+ <string name="pref_privacy_title">"Nastavení ochrany osobních údajů"</string>
+ <string name="pref_privacy_clear_cache">"Vymazat mezipaměť"</string>
+ <string name="pref_privacy_clear_cache_summary">"Smazat veškerý obsah stránek v mezipaměti"</string>
+ <string name="pref_privacy_clear_cache_dlg">"Mezipaměť bude vymazána."</string>
+ <string name="pref_privacy_clear_cookies">"Vymazat všechny soubory cookie"</string>
+ <string name="pref_privacy_clear_cookies_summary">"Vymazat všechny soubory cookie prohlížeče"</string>
+ <string name="pref_privacy_clear_cookies_dlg">"Všechny soubory cookie budou smazány."</string>
+ <string name="pref_privacy_clear_history">"Vymazat historii"</string>
+ <string name="pref_privacy_clear_history_summary">"Vymazat historii stránek prohlížeče"</string>
+ <string name="pref_privacy_clear_history_dlg">"Historie stránek prohlížeče bude vymazána."</string>
+ <string name="pref_privacy_clear_form_data">"Vymazat data formulářů"</string>
+ <string name="pref_privacy_clear_form_data_summary">"Vymazat všechna uložená data formulářů"</string>
+ <string name="pref_privacy_clear_form_data_dlg">"Všechna uložená data formulářů budou vymazána."</string>
+ <string name="pref_privacy_clear_passwords">"Vymazat hesla"</string>
+ <string name="pref_privacy_clear_passwords_summary">"Vymazat všechna uložená hesla"</string>
+ <string name="pref_privacy_clear_passwords_dlg">"Všechna uložená hesla budou vymazána."</string>
+ <string name="pref_security_title">"Nastavení zabezpečení"</string>
+ <string name="pref_security_remember_passwords">"Zapamatovat hesla"</string>
+ <string name="pref_security_remember_passwords_summary">"Ukládat uživatelská jména a hesla na webových stránkách"</string>
+ <string name="pref_security_save_form_data">"Zapamatovat data formuláře"</string>
+ <string name="pref_security_save_form_data_summary">"Zapamatovat si data, která zadám do formulářů, pro další použití"</string>
+ <string name="pref_security_show_security_warning">"Zobrazit upozornění zabezpečení"</string>
+ <string name="pref_security_show_security_warning_summary">"Zobrazit upozornění, pokud dojde k problému se zabezpečením webu"</string>
+ <string name="pref_security_accept_cookies">"Přijímat soubory cookie"</string>
+ <string name="pref_security_accept_cookies_summary">"Povolit webům ukládat a číst soubory cookie"</string>
+ <string name="pref_text_size">"Nastavení velikosti textu"</string>
+ <string-array name="pref_text_size_choices">
+ <item>"Malá"</item>
+ <item>"Malé"</item>
+ <item>"Běžné"</item>
+ <item>"Velké"</item>
+ <item>"Velké"</item>
+ </string-array>
+ <string name="pref_text_size_dialogtitle">"Velikost textu"</string>
+ <string name="pref_extras_title">"Upřesnit nastavení"</string>
+ <string name="pref_extras_gears_enable">"Povolit službu Gears"</string>
+ <string name="pref_extras_gears_enable_summary">"Aplikace, které rozšiřují funkce prohlížeče"</string>
+ <string name="pref_extras_gears_settings">"Nastavení služby Gears"</string>
+ <string name="pref_plugin_installed">"Seznam pluginů"</string>
+ <string name="pref_plugin_installed_empty_list">"Nejsou nainstalované žádné pluginy."</string>
+ <string name="pref_extras_gears_settings_summary">"Aplikace, které rozšiřují funkce prohlížeče"</string>
+ <string name="pref_extras_reset_default">"Obnovit výchozí"</string>
+ <string name="pref_extras_reset_default_summary">"Vymazat všechna data prohlížeče a obnovit všechna nastavení na výchozí hodnoty"</string>
+ <string name="pref_extras_reset_default_dlg">"Všechna data prohlížeče budou vymazána a bude použito výchozí nastavení."</string>
+ <string name="pref_extras_reset_default_dlg_title">"Obnovit výchozí"</string>
+ <string name="pref_development_title">"Ladit"</string>
+ <string name="pref_development_viewport">"Use wide viewport"</string>
+ <string name="pref_development_single_column_rendering">"Single column rendering"</string>
+ <string name="pref_development_normal_rendering">"Běžné vykreslování"</string>
+ <string name="pref_development_trace">"Enable tracing"</string>
+ <string name="pref_development_nav_dump">"Enable nav cache dump"</string>
+ <string name="pref_development_search_url">"Set search URL"</string>
+ <string name="pref_development_uastring">"UAString"</string>
+ <string-array name="pref_development_ua_choices">
+ <item>"Android"</item>
+ <item>"Desktop"</item>
+ <item>"iPhone"</item>
+ </string-array>
+ <string-array name="pref_development_ua_values">
+ <item>"0"</item>
+ <item>"1"</item>
+ <item>"2"</item>
+ </string-array>
+ <string name="pref_default_text_encoding">"Nastavení kódování textu"</string>
+ <string-array name="pref_default_text_encoding_choices">
+ <item>"Latin-1 (ISO-8859-1)"</item>
+ <item>"Unicode (UTF-8)"</item>
+ <item>"Japonština (ISO-2022-JP)"</item>
+ <item>"Japonština (SHIFT_JIS)"</item>
+ <item>"Japonština (EUC-JP)"</item>
+ </string-array>
+ <string name="pref_default_text_encoding_dialogtitle">"Kódování textu"</string>
+ <string name="pref_default_text_encoding_default">"Latin-1"</string>
+ <string name="browserFrameRedirect">"Přesměrování"</string>
+ <string name="browserFrame307Post">"Probíhá přesměrování webové stránky. Chcete data zadaná do formuláře znovu odeslat do nového umístění?"</string>
+ <string name="browserFrameNetworkErrorLabel">"Problémy s datovým připojením"</string>
+ <string name="browserFrameFileErrorLabel">"Problém se souborem"</string>
+ <string name="browserFrameFormResubmitLabel">"Potvrdit"</string>
+ <string name="browserFrameFormResubmitMessage">"Stránka, kterou se pokoušíte zobrazit, obsahuje data, která již byla odeslána (POSTDATA). Pokud data odešlete znovu, jakákoli akce vykonaná pomocí formuláře na stránce bude provedena znovu (např. hledání či nákup online)."</string>
+ <string name="loadSuspendedTitle">"Žádné připojení k síti"</string>
+ <string name="loadSuspended">"Po obnovení připojení bude pokračovat načítání stránky."</string>
+ <string name="clear_history">"Vymazat historii"</string>
+ <string name="browser_history">"Nedávno navštívené stránky"</string>
+ <string name="empty_history">"Historie prohlížeče je prázdná."</string>
+ <string name="add_new_bookmark">"Přidat záložku..."</string>
+ <string name="no_database">"Databáze neexistuje."</string>
+ <string name="search_hint">"Zadejte webovou adresu"</string>
+ <string name="search_button_text">"Přejít"</string>
+ <string name="attention">"Upozornění"</string>
+ <string name="popup_window_attempt">"Tato stránka se pokouší otevřít vyskakovací okno."</string>
+ <string name="allow">"Povolit"</string>
+ <string name="block">"Blokovat"</string>
+ <string name="too_many_windows_dialog_title">"Bylo dosaženo maximálního počtu oken"</string>
+ <string name="too_many_windows_dialog_message">"Nové okno nelze otevřít, protože je již otevřen maximální počet oken."</string>
+ <string name="too_many_subwindows_dialog_title">"Vyskakovací okno je již otevřené"</string>
+ <string name="too_many_subwindows_dialog_message">"Nelze otevřít nové vyskakovací okno, protože může být otevřené pouze jedno."</string>
+ <string name="download_title">"Historie stahování"</string>
+ <string name="download_unknown_filename">"&lt;Neznámé&gt;"</string>
+ <string name="download_menu_open">"Otevřít"</string>
+ <string name="download_menu_clear">"Vymazat ze seznamu"</string>
+ <string name="download_menu_cancel">"Zrušit stahování"</string>
+ <string name="download_menu_cancel_all">"Zrušit stahování všech souborů"</string>
+ <string name="download_menu_clear_all">"Vymazat seznam"</string>
+ <string name="download_clear_dlg_title">"Vymazat"</string>
+ <string name="download_clear_dlg_msg">"Všechny položky v seznamu budou vymazány a odstraněny z mezipaměti prohlížeče."</string>
+ <string name="download_cancel_dlg_title">"Zrušit stahování"</string>
+ <string name="download_cancel_dlg_msg">"Stahování všech souborů (<xliff:g id="DOWNLOAD_COUNT">%d</xliff:g>) bude zrušeno a soubory budou vymazány z historie stahování."</string>
+ <string name="download_file_error_dlg_title">"Nedostatek místa"</string>
+ <string name="download_file_error_dlg_msg">"Soubor <xliff:g id="FILENAME">%s</xliff:g> nelze stáhnout."\n"Uvolněte paměť telefonu a akci opakujte."</string>
+ <string name="download_failed_generic_dlg_title">"Stahování bylo neúspěšné"</string>
+ <string name="download_no_sdcard_dlg_title">"Žádná karta SD není dostupná."</string>
+ <string name="download_no_sdcard_dlg_msg">"Na kartu SD je třeba stáhnout soubor <xliff:g id="FILENAME">%s</xliff:g>."</string>
+ <string name="download_sdcard_busy_dlg_title">"Karta SD není dostupná"</string>
+ <!-- no translation found for download_sdcard_busy_dlg_msg (3473883538192835204) -->
+ <skip />
+ <string name="download_no_application">"Nebyla nalezena žádná aplikace, ve které lze tento soubor otevřít."</string>
+ <string name="retry">"Zkusit znovu"</string>
+ <string name="no_downloads">"Historie stahování je prázdná."</string>
+ <string name="download_error">"Stahování bylo neúspěšné."</string>
+ <string name="download_success">"<xliff:g id="FILE">%s</xliff:g> Stahování dokončeno."</string>
+ <string name="download_running">"Stahování..."</string>
+ <string name="download_pending">"Zahájení stahování..."</string>
+ <string name="download_pending_network">"Čekání na datové připojení..."</string>
+ <string name="download_running_paused">"Čekání na datové připojení..."</string>
+ <string name="download_canceled">"Stahování bylo zrušeno."</string>
+ <string name="download_not_acceptable">"Nelze stáhnout. Tento obsah není telefonem podporován."</string>
+ <string name="download_file_error">"Stahování nelze dokončit. Není dostatek místa."</string>
+ <string name="download_length_required">"Nelze stáhnout. Velikost položky nelze určit."</string>
+ <string name="download_precondition_failed">"Stahování bylo přerušeno a nelze v něm pokračovat."</string>
+ <string name="activity_instrumentation_test_runner">"Testovací verze prohlížeče"</string>
+ <string name="search_google">"Prohledat Google"</string>
+ <string name="permlab_readHistoryBookmarks">"čtení historie a záložek prohlížeče"</string>
+ <string name="permdesc_readHistoryBookmarks">"Umožňuje aplikaci číst všechny navštívené adresy URL a záložky prohlížeče."</string>
+ <string name="permlab_writeHistoryBookmarks">"zápis historie a záložek prohlížeče"</string>
+ <string name="permdesc_writeHistoryBookmarks">"Umožní aplikaci změnit historii či záložky prohlížeče uložené v telefonu. Škodlivé aplikace mohou pomocí tohoto nastavení vymazat či pozměnit data prohlížeče."</string>
+ <!-- no translation found for query_data_prompt (5474381240981604223) -->
+ <skip />
+ <!-- no translation found for query_data_message (8789381063185445197) -->
+ <skip />
+ <!-- no translation found for location_prompt (226262202057302423) -->
+ <skip />
+ <!-- no translation found for location_message (1729456751935683242) -->
+ <skip />
+ <!-- no translation found for shortcut_prompt (437193299088893596) -->
+ <skip />
+ <!-- no translation found for shortcut_message (4793042709293755892) -->
+ <skip />
+ <string name="settings_message">"Níže uvedená tabulka zobrazuje oprávnění, která jste udělili jednotlivým webům, které se pokusily použít službu Gears."</string>
+ <!-- no translation found for filepicker_message (4929726371602896039) -->
+ <skip />
+ <!-- no translation found for image_message (5450245866521896891) -->
+ <skip />
+ <string name="settings_title">"Nastavení služby Gears"</string>
+ <string name="privacy_policy">"Chcete-li zjistit, jak bude vaše poloha využita, přečtěte si zásady ochrany osobních údajů těchto stránek."</string>
+ <!-- no translation found for settings_storage_title (7089119630457156408) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_on (2733295483569432111) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_off (1338039396087898813) -->
+ <skip />
+ <!-- no translation found for settings_location_title (4953062923509886651) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_on (1496219621849158879) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_off (5216466051810596189) -->
+ <skip />
+ <!-- no translation found for settings_remove_site (1822247070226589958) -->
+ <skip />
+ <!-- no translation found for settings_empty (2008952224378583146) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove_title (5360857296753219635) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove (585560170733446898) -->
+ <skip />
+ <!-- no translation found for storage_notification (6534213782873968255) -->
+ <skip />
+ <!-- no translation found for storage_notification_alwaysdeny (5473208033665783513) -->
+ <skip />
+ <!-- no translation found for location_notification (3495804750470905804) -->
+ <skip />
+ <!-- no translation found for location_notification_alwaysdeny (6184925953783312629) -->
+ <skip />
+ <!-- no translation found for shortcut_notification (1659700220868444568) -->
+ <skip />
+ <!-- no translation found for permission_button_alwaysdeny (3837055994905214848) -->
+ <skip />
+ <!-- no translation found for permission_button_allow (7301874925704148242) -->
+ <skip />
+ <!-- no translation found for permission_button_deny (104712269725153018) -->
+ <skip />
+ <!-- no translation found for shortcut_button_alwaysdeny (4172277731325126605) -->
+ <skip />
+ <string name="shortcut_button_allow">"OK"</string>
+ <string name="shortcut_button_deny">"Zrušit"</string>
+ <string name="settings_button_allow">"Použít"</string>
+ <string name="settings_button_deny">"Zrušit"</string>
+ <string name="filepicker_button_allow">"OK"</string>
+ <string name="filepicker_button_deny">"Zrušit"</string>
+ <string name="filepicker_path">"cesta:"</string>
+ <string name="filepicker_no_files_selected">"Nebyly vybrány žádné soubory"</string>
+ <string name="filepicker_one_file_selected">"Byl vybrán jeden soubor"</string>
+ <string name="filepicker_some_files_selected">"souborů bylo vybráno"</string>
+ <string name="remove">"Odstranit"</string>
+ <string name="local_storage">"Místní úložiště"</string>
+ <string name="allowed">"Povoleno"</string>
+ <string name="denied">"Odepřeno"</string>
+ <string name="unrecognized_dialog_message">"Nerozpoznaný typ dialogu"</string>
+ <string name="default_button">"OK"</string>
+ <string name="zoom_overview_button_text">"Přehled"</string>
+</resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
new file mode 100644
index 00000000..8e1ee229
--- /dev/null
+++ b/res/values-de/strings.xml
@@ -0,0 +1,334 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name">"Browser"</string>
+ <string name="tab_bookmarks">"Lesezeichen"</string>
+ <string name="tab_most_visited">"Meistbesucht"</string>
+ <string name="tab_history">"Verlauf"</string>
+ <string name="added_to_bookmarks">"Zu Lesezeichen hinzugefügt"</string>
+ <string name="removed_from_bookmarks">"Aus Lesezeichen entfernt"</string>
+ <string name="sign_in_to">"Bei <xliff:g id="HOSTNAME">%s1</xliff:g> \"<xliff:g id="REALM">%s2</xliff:g>\" anmelden"</string>
+ <string name="username">"Name"</string>
+ <string name="password">"Passwort"</string>
+ <string name="action">"Anmelden"</string>
+ <string name="cancel">"Abbrechen"</string>
+ <string name="ok">"OK"</string>
+ <string name="matches_found">" Treffer"</string>
+ <string name="zero">"Nein"</string>
+ <string name="page_info">"Seiten-Info"</string>
+ <string name="page_info_view">"Seiten-Info anzeigen"</string>
+ <string name="page_info_address">"Adresse:"</string>
+ <string name="ssl_warnings_header">"Es ist ein Problem mit dem Sicherheitszertifikat für diese Website aufgetreten."</string>
+ <string name="ssl_continue">"Fortfahren"</string>
+ <string name="security_warning">"Sicherheitswarnung"</string>
+ <string name="view_certificate">"Zertifikat anzeigen"</string>
+ <string name="ssl_untrusted">"Dieses Zertifikat wurde nicht von einer vertrauenswürdigen Stelle ausgegeben."</string>
+ <string name="ssl_mismatch">"Der Name der Website stimmt nicht mit dem Namen auf dem Zertifikat überein."</string>
+ <string name="ssl_expired">"Dieses Zertifikat ist abgelaufen."</string>
+ <string name="ssl_not_yet_valid">"Dieses Zertifikat ist noch nicht gültig."</string>
+ <string name="ssl_certificate">"Sicherheitszertifikat"</string>
+ <string name="ssl_certificate_is_valid">"Dies ist ein gültiges Zertifikat."</string>
+ <string name="issued_to">"Ausgegeben an:"</string>
+ <string name="common_name">"Allgemeiner Name."</string>
+ <string name="org_name">"Organisation:"</string>
+ <string name="org_unit">"Organisationseinheit:"</string>
+ <string name="issued_by">"Ausgegeben von:"</string>
+ <string name="validity_period">"Gültigkeit:"</string>
+ <string name="issued_on">"Ausgegeben am:"</string>
+ <string name="expires_on">"Läuft ab am:"</string>
+ <string name="retrieving_creds_dlg_msg">"Anmeldedetails werden abgerufen…"</string>
+ <string name="close">"Schließen"</string>
+ <string name="close_window">"Dieses Fenster wird geschlossen."</string>
+ <string name="stopping">"Wird angehalten..."</string>
+ <string name="stop">"Anhalten"</string>
+ <string name="reload">"Aktualisieren"</string>
+ <string name="forward">"Weiter"</string>
+ <string name="save">"OK"</string>
+ <string name="do_not_save">"Abbrechen"</string>
+ <string name="location">"Ort"</string>
+ <string name="name">"Name"</string>
+ <string name="http">"http://"</string>
+ <string name="save_to_bookmarks">"Lesezeichen"</string>
+ <string name="edit_bookmark">"Lesezeichen bearbeiten"</string>
+ <string name="create_shortcut_bookmark">"Shortcut zur Startseite hinzufügen"</string>
+ <string name="open_bookmark">"Öffnen"</string>
+ <string name="remove_bookmark">"Lesezeichen löschen"</string>
+ <string name="remove_history_item">"Aus Verlauf entfernen"</string>
+ <string name="bookmark_saved">"Gespeichert in Lesezeichen."</string>
+ <string name="bookmark_needs_title">"Das Lesezeichen muss über einen Namen verfügen."</string>
+ <string name="bookmark_needs_url">"Das Lesezeichen muss über einen Speicherort verfügen."</string>
+ <string name="empty_bookmark">"Sie können kein leeres Lesezeichen erstellen."</string>
+ <string name="bookmark_url_not_valid">"URL ist nicht gültig."</string>
+ <string name="delete_bookmark">"Löschen"</string>
+ <string name="bookmark_page">"Lesezeichen für zuletzt besuchte Seite"</string>
+ <string name="current_page">"von "</string>
+ <string name="delete_bookmark_warning">"Lesezeichen \"<xliff:g id="BOOKMARK">%s</xliff:g>\" wird gelöscht."</string>
+ <string name="open_in_new_window">"In neuem Fenster öffnen"</string>
+ <string name="new_window">"Neues Fenster"</string>
+ <string name="goto_dot">"Los"</string>
+ <string name="find_dot">"Auf Seite suchen"</string>
+ <string name="select_dot">"Text auswählen"</string>
+ <string name="view_tabs">"Fensterübersicht"</string>
+ <string name="view_tabs_condensed">"Windows"</string>
+ <string name="tab_picker_title">"Aktuelle Fenster"</string>
+ <string name="tab_picker_view_tab">"Anzeigen"</string>
+ <string name="tab_picker_new_tab">"Neues Fenster"</string>
+ <string name="tab_picker_remove_tab">"Schließen"</string>
+ <string name="tab_picker_bookmark">"Lesezeichen"</string>
+ <string name="tab_picker_send_url">"Link weiterleiten"</string>
+ <string name="bookmarks">"Lesezeichen"</string>
+ <string name="shortcut_bookmark">"Lesezeichen"</string>
+ <string name="history">"Verlauf"</string>
+ <string name="menu_view_download">"Downloads"</string>
+ <string name="share_page">"Seitenlink weiterleiten"</string>
+ <string name="contextmenu_openlink">"Öffnen"</string>
+ <string name="contextmenu_openlink_newwindow">"In neuem Fenster öffnen"</string>
+ <string name="contextmenu_bookmark_thislink">"Lesezeichen-Link"</string>
+ <string name="contextmenu_savelink">"Link speichern"</string>
+ <string name="contextmenu_sharelink">"Link weiterleiten"</string>
+ <string name="contextmenu_copy">"Kopieren"</string>
+ <string name="contextmenu_copylink">"Link-URL kopieren"</string>
+ <string name="contextmenu_download_image">"Bild speichern"</string>
+ <string name="contextmenu_view_image">"Bild anzeigen"</string>
+ <string name="contextmenu_dial_dot">"Wählen..."</string>
+ <string name="contextmenu_add_contact">"Kontakt hinzufügen"</string>
+ <string name="contextmenu_send_mail">"E-Mail senden"</string>
+ <string name="contextmenu_map">"Karte"</string>
+ <string name="clear">"Löschen"</string>
+ <string name="replace">"Ersetzen"</string>
+ <string name="browser_bookmarks_page_bookmarks_text">"Lesezeichen"</string>
+ <string name="menu_preferences">"Einstellungen"</string>
+ <string name="pref_content_title">"Einstellungen für Seiteninhalt"</string>
+ <string name="pref_content_load_images">"Bilder laden"</string>
+ <string name="pref_content_load_images_summary">"Bilder auf Webseiten anzeigen"</string>
+ <string name="pref_content_block_popups">"Popup-Fenster blockieren"</string>
+ <string name="pref_content_javascript">"JavaScript aktivieren"</string>
+ <string name="pref_content_open_in_background">"Im Hintergrund öffnen"</string>
+ <string name="pref_content_open_in_background_summary">"Neue Fenster hinter dem aktuellen Fenster öffnen"</string>
+ <string name="pref_content_homepage">"Startseite festlegen"</string>
+ <string name="pref_content_autofit">"Seiten autom. anpassen"</string>
+ <string name="pref_content_autofit_summary">"Webseiten an den Bildschirm anpassen"</string>
+ <string name="pref_privacy_title">"Datenschutzeinstellungen"</string>
+ <string name="pref_privacy_clear_cache">"Cache löschen"</string>
+ <string name="pref_privacy_clear_cache_summary">"Alle Seiteninhalte im Cache löschen"</string>
+ <string name="pref_privacy_clear_cache_dlg">"Der Cache wird gelöscht."</string>
+ <string name="pref_privacy_clear_cookies">"Alle Cookiedaten löschen"</string>
+ <string name="pref_privacy_clear_cookies_summary">"Alle Browser-Cookies löschen"</string>
+ <string name="pref_privacy_clear_cookies_dlg">"Alle Cookies werden gelöscht."</string>
+ <string name="pref_privacy_clear_history">"Verlauf löschen"</string>
+ <string name="pref_privacy_clear_history_summary">"Navigationsverlauf des Browsers löschen"</string>
+ <string name="pref_privacy_clear_history_dlg">"Der Navigationsverlauf des Browsers wird gelöscht."</string>
+ <string name="pref_privacy_clear_form_data">"Formulardaten löschen"</string>
+ <string name="pref_privacy_clear_form_data_summary">"Alle gespeicherten Formulardaten löschen"</string>
+ <string name="pref_privacy_clear_form_data_dlg">"Alle gespeicherten Formulardaten werden gelöscht."</string>
+ <string name="pref_privacy_clear_passwords">"Passwörter löschen"</string>
+ <string name="pref_privacy_clear_passwords_summary">"Alle gespeicherten Passwörter löschen"</string>
+ <string name="pref_privacy_clear_passwords_dlg">"Alle gespeicherten Passwörter werden gelöscht."</string>
+ <string name="pref_security_title">"Sicherheitseinstellungen"</string>
+ <string name="pref_security_remember_passwords">"Passwörter merken"</string>
+ <string name="pref_security_remember_passwords_summary">"Nutzernamen und Passwörter für Websites speichern"</string>
+ <string name="pref_security_save_form_data">"Formulardaten merken"</string>
+ <string name="pref_security_save_form_data_summary">"Daten in Formularen zur späteren Verwendung merken"</string>
+ <string name="pref_security_show_security_warning">"Sicherheitswarnungen"</string>
+ <string name="pref_security_show_security_warning_summary">"Warnung anzeigen, wenn ein Problem mit der Sicherheit der Website besteht"</string>
+ <string name="pref_security_accept_cookies">"Cookies akzeptieren"</string>
+ <string name="pref_security_accept_cookies_summary">"Speichern und Lesen von \"Cookie\"-Daten zulassen"</string>
+ <string name="pref_text_size">"Textgröße einstellen"</string>
+ <string-array name="pref_text_size_choices">
+ <item>"Sehr klein"</item>
+ <item>"Klein"</item>
+ <item>"Normal"</item>
+ <item>"Groß"</item>
+ <item>"Sehr groß"</item>
+ </string-array>
+ <string name="pref_text_size_dialogtitle">"Textgröße"</string>
+ <string name="pref_extras_title">"Erweiterte Einstellungen"</string>
+ <string name="pref_extras_gears_enable">"Gears aktivieren"</string>
+ <string name="pref_extras_gears_enable_summary">"Anwendungen, die die Browserfunktionen erweitern"</string>
+ <string name="pref_extras_gears_settings">"Gears-Einstellungen"</string>
+ <string name="pref_plugin_installed">"Liste der Plug-Ins"</string>
+ <string name="pref_plugin_installed_empty_list">"Keine Plug-Ins installiert."</string>
+ <string name="pref_extras_gears_settings_summary">"Vom Browser nicht unterstützte Anwendungen"</string>
+ <string name="pref_extras_reset_default">"Auf Standard zurücksetzen"</string>
+ <string name="pref_extras_reset_default_summary">"Alle Browserdaten löschen und alle Einstellungen auf Standardeinstellung zurücksetzen"</string>
+ <string name="pref_extras_reset_default_dlg">"Alle Browserdaten werden gelöscht. Alle Einstellungen werden auf die Standardeinstellung zurückgesetzt."</string>
+ <string name="pref_extras_reset_default_dlg_title">"Auf Standardeinstellung zurücksetzen"</string>
+ <string name="pref_development_title">"Debuggen"</string>
+ <string name="pref_development_viewport">"Use wide viewport"</string>
+ <string name="pref_development_single_column_rendering">"Single column rendering"</string>
+ <string name="pref_development_normal_rendering">"Normal rendering"</string>
+ <string name="pref_development_trace">"Enable tracing"</string>
+ <string name="pref_development_nav_dump">"Enable nav cache dump"</string>
+ <string name="pref_development_search_url">"Such-URL einstellen"</string>
+ <string name="pref_development_uastring">"UAString"</string>
+ <string-array name="pref_development_ua_choices">
+ <item>"Android"</item>
+ <item>"Google Desktop"</item>
+ <item>"iPhone"</item>
+ </string-array>
+ <string-array name="pref_development_ua_values">
+ <item>"0"</item>
+ <item>"1"</item>
+ <item>"2"</item>
+ </string-array>
+ <string name="pref_default_text_encoding">"Textcodierung einstellen"</string>
+ <string-array name="pref_default_text_encoding_choices">
+ <item>"Latin-1 (ISO-8859-1)"</item>
+ <item>"Unicode (UTF-8)"</item>
+ <item>"Japanisch (ISO-2022-JP)"</item>
+ <item>"Japanisch (SHIFT_JIS)"</item>
+ <item>"Japanisch (EUC-JP)"</item>
+ </string-array>
+ <string name="pref_default_text_encoding_dialogtitle">"Textcodierung"</string>
+ <string name="pref_default_text_encoding_default">"Latin-1"</string>
+ <string name="browserFrameRedirect">"Redirect"</string>
+ <string name="browserFrame307Post">"Diese Webseite wird umgeleitet. Sollen Ihre eingegebenen Formulardaten an den neuen Ort gesendet werden?"</string>
+ <string name="browserFrameNetworkErrorLabel">"Datenverbindungsproblem"</string>
+ <string name="browserFrameFileErrorLabel">"Problem mit Datei"</string>
+ <string name="browserFrameFormResubmitLabel">"Bestätigen"</string>
+ <string name="browserFrameFormResubmitMessage">"Die Seite, die Sie anzuzeigen versuchen, enthält Daten, die bereits gesendet wurde (\"POST-DATEN\"). Wenn Sie die Daten erneut senden, wird jede Aktion, die das Formular auf der Seite ausgeführt hat, wiederholt (wie beispielsweise eine Suche oder ein Online-Kauf)."</string>
+ <string name="loadSuspendedTitle">"Keine Netzwerkverbindung"</string>
+ <string name="loadSuspended">"Die Seite wird weiter geladen, nachdem die Verbindung wiederhergestellt wurde."</string>
+ <string name="clear_history">"Verlauf löschen"</string>
+ <string name="browser_history">"Zuletzt besuchte Seiten"</string>
+ <string name="empty_history">"Browserverlauf ist leer."</string>
+ <string name="add_new_bookmark">"Lesezeichen hinzufügen..."</string>
+ <string name="no_database">"Keine Datenbank!"</string>
+ <string name="search_hint">"Webadresse eingeben"</string>
+ <string name="search_button_text">"Los"</string>
+ <string name="attention">"Achtung"</string>
+ <string name="popup_window_attempt">"Diese Website versucht, ein Pop-up-Fenster zu öffnen."</string>
+ <string name="allow">"Zulassen"</string>
+ <string name="block">"Blockieren"</string>
+ <string name="too_many_windows_dialog_title">"Fensterlimit erreicht"</string>
+ <string name="too_many_windows_dialog_message">"Es konnte kein neues Fenster geöffnet werden, da Sie bereits die maximale Anzahl an Fenstern geöffnet haben."</string>
+ <string name="too_many_subwindows_dialog_title">"Pop-up ist bereits geöffnet"</string>
+ <string name="too_many_subwindows_dialog_message">"Es konnte kein neues Pop-up-Fenster geöffnet werden, da nur jeweils ein Fenster geöffnet sein kann."</string>
+ <string name="download_title">"Downloadverlauf"</string>
+ <string name="download_unknown_filename">"&lt;Unbekannt&gt;"</string>
+ <string name="download_menu_open">"Öffnen"</string>
+ <string name="download_menu_clear">"Aus Liste löschen"</string>
+ <string name="download_menu_cancel">"Download abbrechen"</string>
+ <string name="download_menu_cancel_all">"Alle Downloads abbrechen"</string>
+ <string name="download_menu_clear_all">"Liste löschen"</string>
+ <string name="download_clear_dlg_title">"Löschen"</string>
+ <string name="download_clear_dlg_msg">"Alle Elemente werden aus der Liste gelöscht und aus dem Browser-Cache entfernt."</string>
+ <string name="download_cancel_dlg_title">"Downloads abbrechen"</string>
+ <string name="download_cancel_dlg_msg">"Alle <xliff:g id="DOWNLOAD_COUNT">%d</xliff:g> Downloads werden abgebrochen und aus dem Downloadverlauf gelöscht."</string>
+ <string name="download_file_error_dlg_title">"Kein freier Speicher vorhanden"</string>
+ <string name="download_file_error_dlg_msg">"<xliff:g id="FILENAME">%s</xliff:g> konnte nicht heruntergeladen werden."\n"Bereinigen Sie den Speicher Ihres Telefons und versuchen Sie es erneut."</string>
+ <string name="download_failed_generic_dlg_title">"Download war nicht erfolgreich"</string>
+ <string name="download_no_sdcard_dlg_title">"Keine SD-Karte"</string>
+ <string name="download_no_sdcard_dlg_msg">"Zum Download von <xliff:g id="FILENAME">%s</xliff:g> ist eine SD-Karte erforderlich."</string>
+ <string name="download_sdcard_busy_dlg_title">"SD-Karte nicht verfügbar"</string>
+ <!-- no translation found for download_sdcard_busy_dlg_msg (3473883538192835204) -->
+ <skip />
+ <string name="download_no_application">"Es kann keine Anwendung gefunden werden, um diese Datei zu öffnen."</string>
+ <string name="retry">"Wiederholen"</string>
+ <string name="no_downloads">"Downloadverlauf ist leer."</string>
+ <string name="download_error">"Download war nicht erfolgreich."</string>
+ <string name="download_success">"Download von <xliff:g id="FILE">%s</xliff:g> abgeschlossen."</string>
+ <string name="download_running">"Downloadvorgang läuft..."</string>
+ <string name="download_pending">"Download wird gestartet…"</string>
+ <string name="download_pending_network">"Warten auf Datenverbindung..."</string>
+ <string name="download_running_paused">"Warten auf Datenverbindung..."</string>
+ <string name="download_canceled">"Download abgebrochen."</string>
+ <string name="download_not_acceptable">"Download kann nicht durchgeführt werden, da der Inhalt nicht auf dem Telefon unterstützt wird."</string>
+ <string name="download_file_error">"Download kann nicht beendet werden, da nicht genügend Speicherplatz vorhanden ist."</string>
+ <string name="download_length_required">"Download kann nicht gestartet werden, da die Größe des Elements nicht bestimmt werden kann."</string>
+ <string name="download_precondition_failed">"Download wurde unterbrochen und kann nicht fortgesetzt werden."</string>
+ <string name="activity_instrumentation_test_runner">"Browser Test Runner"</string>
+ <string name="search_google">"In Google suchen"</string>
+ <string name="permlab_readHistoryBookmarks">"Browserverlauf und Lesezeichen lesen"</string>
+ <string name="permdesc_readHistoryBookmarks">"Ermöglicht der Anwendung, alle URLs, die mit dem Browser besucht wurden, sowie alle Lesezeichen des Browsers zu lesen."</string>
+ <string name="permlab_writeHistoryBookmarks">"Browserverlauf und Lesezeichen schreiben"</string>
+ <string name="permdesc_writeHistoryBookmarks">"Ermöglicht einer Anwendung, den auf Ihrem Telefon gespeicherten Browserverlauf und die Lesezeichen zu ändern. Schädliche Anwendungen können so Ihre Browserdaten löschen oder ändern."</string>
+ <!-- no translation found for query_data_prompt (5474381240981604223) -->
+ <skip />
+ <!-- no translation found for query_data_message (8789381063185445197) -->
+ <skip />
+ <!-- no translation found for location_prompt (226262202057302423) -->
+ <skip />
+ <!-- no translation found for location_message (1729456751935683242) -->
+ <skip />
+ <!-- no translation found for shortcut_prompt (437193299088893596) -->
+ <skip />
+ <!-- no translation found for shortcut_message (4793042709293755892) -->
+ <skip />
+ <string name="settings_message">"Die folgende Tabelle zeigt die Berechtigungen, die Sie jeder Website erteilt haben, die versucht hat, Gears zu verwenden."</string>
+ <!-- no translation found for filepicker_message (4929726371602896039) -->
+ <skip />
+ <!-- no translation found for image_message (5450245866521896891) -->
+ <skip />
+ <string name="settings_title">"Gears-Einstellungen"</string>
+ <string name="privacy_policy">"Lesen Sie die Datenschutzbestimmungen dieser Website, um herauszufinden, wie Ihr Ort verwendet wird."</string>
+ <!-- no translation found for settings_storage_title (7089119630457156408) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_on (2733295483569432111) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_off (1338039396087898813) -->
+ <skip />
+ <!-- no translation found for settings_location_title (4953062923509886651) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_on (1496219621849158879) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_off (5216466051810596189) -->
+ <skip />
+ <!-- no translation found for settings_remove_site (1822247070226589958) -->
+ <skip />
+ <!-- no translation found for settings_empty (2008952224378583146) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove_title (5360857296753219635) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove (585560170733446898) -->
+ <skip />
+ <!-- no translation found for storage_notification (6534213782873968255) -->
+ <skip />
+ <!-- no translation found for storage_notification_alwaysdeny (5473208033665783513) -->
+ <skip />
+ <!-- no translation found for location_notification (3495804750470905804) -->
+ <skip />
+ <!-- no translation found for location_notification_alwaysdeny (6184925953783312629) -->
+ <skip />
+ <!-- no translation found for shortcut_notification (1659700220868444568) -->
+ <skip />
+ <!-- no translation found for permission_button_alwaysdeny (3837055994905214848) -->
+ <skip />
+ <!-- no translation found for permission_button_allow (7301874925704148242) -->
+ <skip />
+ <!-- no translation found for permission_button_deny (104712269725153018) -->
+ <skip />
+ <!-- no translation found for shortcut_button_alwaysdeny (4172277731325126605) -->
+ <skip />
+ <string name="shortcut_button_allow">"OK"</string>
+ <string name="shortcut_button_deny">"Abbrechen"</string>
+ <string name="settings_button_allow">"Anwenden"</string>
+ <string name="settings_button_deny">"Abbrechen"</string>
+ <string name="filepicker_button_allow">"OK"</string>
+ <string name="filepicker_button_deny">"Abbrechen"</string>
+ <string name="filepicker_path">"Pfad:"</string>
+ <string name="filepicker_no_files_selected">"Keine Dateien ausgewählt"</string>
+ <string name="filepicker_one_file_selected">"Eine Datei ausgewählt"</string>
+ <string name="filepicker_some_files_selected">"Dateien ausgewählt"</string>
+ <string name="remove">"Entfernen"</string>
+ <string name="local_storage">"Lokaler Speicher"</string>
+ <string name="allowed">"Zugelassen"</string>
+ <string name="denied">"Abgelehnt"</string>
+ <string name="unrecognized_dialog_message">"Unbekannter Dialogtyp"</string>
+ <string name="default_button">"OK"</string>
+ <string name="zoom_overview_button_text">"Übersicht"</string>
+</resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
new file mode 100644
index 00000000..fb1b083c
--- /dev/null
+++ b/res/values-es/strings.xml
@@ -0,0 +1,334 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name">"Navegador"</string>
+ <string name="tab_bookmarks">"Marcadores"</string>
+ <string name="tab_most_visited">"Más visitados"</string>
+ <string name="tab_history">"Historial"</string>
+ <string name="added_to_bookmarks">"Añadido a marcadores"</string>
+ <string name="removed_from_bookmarks">"Eliminado de marcadores"</string>
+ <string name="sign_in_to">"Acceder a <xliff:g id="HOSTNAME">%s1</xliff:g> \"<xliff:g id="REALM">%s2</xliff:g>\""</string>
+ <string name="username">"Nombre"</string>
+ <string name="password">"Contraseña"</string>
+ <string name="action">"Acceder"</string>
+ <string name="cancel">"Cancelar"</string>
+ <string name="ok">"Aceptar"</string>
+ <string name="matches_found">" coincidencias"</string>
+ <string name="zero">"No"</string>
+ <string name="page_info">"Información de página"</string>
+ <string name="page_info_view">"Ver información de página"</string>
+ <string name="page_info_address">"Dirección:"</string>
+ <string name="ssl_warnings_header">"Hay algunos problemas con el certificado de seguridad de este sitio."</string>
+ <string name="ssl_continue">"Continuar"</string>
+ <string name="security_warning">"Advertencia de seguridad"</string>
+ <string name="view_certificate">"Ver certificado"</string>
+ <string name="ssl_untrusted">"Este certificado no procede de una entidad de certificación de confianza."</string>
+ <string name="ssl_mismatch">"El nombre del sitio no coincide con el del certificado."</string>
+ <string name="ssl_expired">"Este certificado ha caducado."</string>
+ <string name="ssl_not_yet_valid">"Este certificado aún no es válido."</string>
+ <string name="ssl_certificate">"Certificado de seguridad"</string>
+ <string name="ssl_certificate_is_valid">"Este certificado es válido."</string>
+ <string name="issued_to">"Emitido para:"</string>
+ <string name="common_name">"Nombre común:"</string>
+ <string name="org_name">"Organización:"</string>
+ <string name="org_unit">"Departamento:"</string>
+ <string name="issued_by">"Emitido por:"</string>
+ <string name="validity_period">"Validez:"</string>
+ <string name="issued_on">"Fecha de emisión:"</string>
+ <string name="expires_on">"Fecha de caducidad:"</string>
+ <string name="retrieving_creds_dlg_msg">"Recuperando detalles de acceso..."</string>
+ <string name="close">"Cerrar"</string>
+ <string name="close_window">"Se cerrará esta ventana."</string>
+ <string name="stopping">"Deteniendo..."</string>
+ <string name="stop">"Detener"</string>
+ <string name="reload">"Actualizar"</string>
+ <string name="forward">"Siguiente"</string>
+ <string name="save">"Aceptar"</string>
+ <string name="do_not_save">"Cancelar"</string>
+ <string name="location">"Ubicación"</string>
+ <string name="name">"Nombre"</string>
+ <string name="http">"http://"</string>
+ <string name="save_to_bookmarks">"Marcar"</string>
+ <string name="edit_bookmark">"Editar marcador"</string>
+ <string name="create_shortcut_bookmark">"Añadir acceso directo a página principal"</string>
+ <string name="open_bookmark">"Abrir"</string>
+ <string name="remove_bookmark">"Eliminar marcador"</string>
+ <string name="remove_history_item">"Elliminar del historial"</string>
+ <string name="bookmark_saved">"Guardar en marcadores"</string>
+ <string name="bookmark_needs_title">"El marcador debe tener un nombre."</string>
+ <string name="bookmark_needs_url">"El marcador debe tener una ubicación."</string>
+ <string name="empty_bookmark">"No se puede crear un marcador vacío."</string>
+ <string name="bookmark_url_not_valid">"La URL no es válida."</string>
+ <string name="delete_bookmark">"Suprimir"</string>
+ <string name="bookmark_page">"Marcar como última página vista"</string>
+ <string name="current_page">"de "</string>
+ <string name="delete_bookmark_warning">"Se eliminará el marcador \"<xliff:g id="BOOKMARK">%s</xliff:g>\"."</string>
+ <string name="open_in_new_window">"Abrir en ventana nueva"</string>
+ <string name="new_window">"Nueva ventana"</string>
+ <string name="goto_dot">"Ir"</string>
+ <string name="find_dot">"Buscar en la página"</string>
+ <string name="select_dot">"Seleccionar texto"</string>
+ <string name="view_tabs">"Visión general de ventanas"</string>
+ <string name="view_tabs_condensed">"Ventanas"</string>
+ <string name="tab_picker_title">"Ventanas actuales"</string>
+ <string name="tab_picker_view_tab">"Ver"</string>
+ <string name="tab_picker_new_tab">"Nueva ventana"</string>
+ <string name="tab_picker_remove_tab">"Cerrar"</string>
+ <string name="tab_picker_bookmark">"Marcar"</string>
+ <string name="tab_picker_send_url">"Compartir enlace"</string>
+ <string name="bookmarks">"Marcadores"</string>
+ <string name="shortcut_bookmark">"Marcar"</string>
+ <string name="history">"Historial"</string>
+ <string name="menu_view_download">"Descargas"</string>
+ <string name="share_page">"Compartir página"</string>
+ <string name="contextmenu_openlink">"Abrir"</string>
+ <string name="contextmenu_openlink_newwindow">"Abrir en ventana nueva"</string>
+ <string name="contextmenu_bookmark_thislink">"Marcar enlace"</string>
+ <string name="contextmenu_savelink">"Guardar enlace"</string>
+ <string name="contextmenu_sharelink">"Compartir enlace"</string>
+ <string name="contextmenu_copy">"Copiar"</string>
+ <string name="contextmenu_copylink">"Copiar URL de enlace"</string>
+ <string name="contextmenu_download_image">"Guardar imagen"</string>
+ <string name="contextmenu_view_image">"Ver imagen"</string>
+ <string name="contextmenu_dial_dot">"Llamar…"</string>
+ <string name="contextmenu_add_contact">"Añadir contacto"</string>
+ <string name="contextmenu_send_mail">"Enviar mensaje de correo electrónico"</string>
+ <string name="contextmenu_map">"Mapa"</string>
+ <string name="clear">"Borrar"</string>
+ <string name="replace">"Sustituir"</string>
+ <string name="browser_bookmarks_page_bookmarks_text">"Marcadores"</string>
+ <string name="menu_preferences">"Ajustes"</string>
+ <string name="pref_content_title">"Configuración de contenido de la página"</string>
+ <string name="pref_content_load_images">"Cargar imágenes"</string>
+ <string name="pref_content_load_images_summary">"Mostrar imágenes en páginas web"</string>
+ <string name="pref_content_block_popups">"Bloquear ventanas emergentes"</string>
+ <string name="pref_content_javascript">"Habilitar JavaScript"</string>
+ <string name="pref_content_open_in_background">"Abrir en segundo plano"</string>
+ <string name="pref_content_open_in_background_summary">"Nuevas ventanas abiertas detrás de la actual"</string>
+ <string name="pref_content_homepage">"Establecer página principal"</string>
+ <string name="pref_content_autofit">"Ajustar páginas automáticamente"</string>
+ <string name="pref_content_autofit_summary">"Configurar las páginas web para ajustarlas a la pantalla"</string>
+ <string name="pref_privacy_title">"Configuración de privacidad"</string>
+ <string name="pref_privacy_clear_cache">"Borrar caché"</string>
+ <string name="pref_privacy_clear_cache_summary">"Eliminar todo el contenido de la página almacenado en caché"</string>
+ <string name="pref_privacy_clear_cache_dlg">"Se borrará la caché."</string>
+ <string name="pref_privacy_clear_cookies">"Borrar los datos de cookies"</string>
+ <string name="pref_privacy_clear_cookies_summary">"Borrar todas las cookies del navegador"</string>
+ <string name="pref_privacy_clear_cookies_dlg">"Se borrarán todas las cookies."</string>
+ <string name="pref_privacy_clear_history">"Borrar historial"</string>
+ <string name="pref_privacy_clear_history_summary">"Borrar el historial de exploración del navegador"</string>
+ <string name="pref_privacy_clear_history_dlg">"Se borrará el historial de exploración del navegador."</string>
+ <string name="pref_privacy_clear_form_data">"Borrar datos de formulario"</string>
+ <string name="pref_privacy_clear_form_data_summary">"Borrar todos los datos guardados del formulario"</string>
+ <string name="pref_privacy_clear_form_data_dlg">"Se borrarán todos los datos guardados del formulario."</string>
+ <string name="pref_privacy_clear_passwords">"Borrar contraseñas"</string>
+ <string name="pref_privacy_clear_passwords_summary">"Borrar todas las contraseñas guardadas"</string>
+ <string name="pref_privacy_clear_passwords_dlg">"Se borrarán todas las contraseñas guardadas."</string>
+ <string name="pref_security_title">"Configuración de seguridad"</string>
+ <string name="pref_security_remember_passwords">"Recordar contraseñas"</string>
+ <string name="pref_security_remember_passwords_summary">"Guardar nombres de usuario y contraseñas de sitios web"</string>
+ <string name="pref_security_save_form_data">"Recordar datos de formulario"</string>
+ <string name="pref_security_save_form_data_summary">"Recordar datos introducidos en formularios"</string>
+ <string name="pref_security_show_security_warning">"Mostrar advertencias de seguridad"</string>
+ <string name="pref_security_show_security_warning_summary">"Mostrar advertencia si hay algún problema con la seguridad del sitio"</string>
+ <string name="pref_security_accept_cookies">"Aceptar cookies"</string>
+ <string name="pref_security_accept_cookies_summary">"Permitir que los sitios guarden y lean datos de cookies"</string>
+ <string name="pref_text_size">"Establecer tamaño de texto"</string>
+ <string-array name="pref_text_size_choices">
+ <item>"Muy pequeño"</item>
+ <item>"Pequeño"</item>
+ <item>"Normal"</item>
+ <item>"Grande"</item>
+ <item>"Enorme"</item>
+ </string-array>
+ <string name="pref_text_size_dialogtitle">"Tamaño de texto"</string>
+ <string name="pref_extras_title">"Configuración avanzada"</string>
+ <string name="pref_extras_gears_enable">"Habilitar Gears"</string>
+ <string name="pref_extras_gears_enable_summary">"Aplicaciones que amplían las funciones del navegador"</string>
+ <string name="pref_extras_gears_settings">"Configuración de Gears"</string>
+ <string name="pref_plugin_installed">"Lista de complementos"</string>
+ <string name="pref_plugin_installed_empty_list">"Ningún complemento instalado"</string>
+ <string name="pref_extras_gears_settings_summary">"Aplicaciones que amplían las funciones del navegador"</string>
+ <string name="pref_extras_reset_default">"Restablecer valores predeterminados"</string>
+ <string name="pref_extras_reset_default_summary">"Borrar todos los datos del navegador y restablecer la configuración predeterminada"</string>
+ <string name="pref_extras_reset_default_dlg">"Se borrarán todos los datos del navegador y se restablecerá la configuración predeterminada."</string>
+ <string name="pref_extras_reset_default_dlg_title">"Restablecer valores predeterminados"</string>
+ <string name="pref_development_title">"Depurar"</string>
+ <string name="pref_development_viewport">"Use wide viewport"</string>
+ <string name="pref_development_single_column_rendering">"Representación en una columna"</string>
+ <string name="pref_development_normal_rendering">"Normal rendering"</string>
+ <string name="pref_development_trace">"Enable tracing"</string>
+ <string name="pref_development_nav_dump">"Enable nav cache dump"</string>
+ <string name="pref_development_search_url">"Establecer URL de búsqueda"</string>
+ <string name="pref_development_uastring">"UAString"</string>
+ <string-array name="pref_development_ua_choices">
+ <item>"Android"</item>
+ <item>"Desktop"</item>
+ <item>"iPhone"</item>
+ </string-array>
+ <string-array name="pref_development_ua_values">
+ <item>"0"</item>
+ <item>"1"</item>
+ <item>"2"</item>
+ </string-array>
+ <string name="pref_default_text_encoding">"Establecer codificación de texto"</string>
+ <string-array name="pref_default_text_encoding_choices">
+ <item>"Latín-1 (ISO-8859-1)"</item>
+ <item>"Unicode (UTF-8)"</item>
+ <item>"Japonés (ISO-2022-JP)"</item>
+ <item>"Japonés (SHIFT_JIS)"</item>
+ <item>"Japonés (EUC-JP)"</item>
+ </string-array>
+ <string name="pref_default_text_encoding_dialogtitle">"Codificación de texto"</string>
+ <string name="pref_default_text_encoding_default">"Latín-1"</string>
+ <string name="browserFrameRedirect">"Redireccionamiento"</string>
+ <string name="browserFrame307Post">"Esta página web se ha redirigido a otra ubicación. ¿Quieres enviar los datos del formulario cumplimentado a la nueva ubicación?"</string>
+ <string name="browserFrameNetworkErrorLabel">"Problema de conectividad de datos"</string>
+ <string name="browserFrameFileErrorLabel">"Problema con archivo"</string>
+ <string name="browserFrameFormResubmitLabel">"OK"</string>
+ <string name="browserFrameFormResubmitMessage">"La página que intentas ver contiene datos que ya se han enviado (\"POSTDATA\"). Si reenvías los datos, se repetirá cualquier acción realizada por el formulario de la página (como las búsquedas o las compras online)."</string>
+ <string name="loadSuspendedTitle">"Sin conexión de red"</string>
+ <string name="loadSuspended">"La página seguirá cargándose una vez que se restablezca la conexión."</string>
+ <string name="clear_history">"Borrar historial"</string>
+ <string name="browser_history">"Páginas visitadas recientemente"</string>
+ <string name="empty_history">"El historial del navegador está vacío."</string>
+ <string name="add_new_bookmark">"Añadir marcador…"</string>
+ <string name="no_database">"No hay ninguna base de datos."</string>
+ <string name="search_hint">"Introduce la dirección web."</string>
+ <string name="search_button_text">"Ir"</string>
+ <string name="attention">"Atención"</string>
+ <string name="popup_window_attempt">"Este sitio está intentando mostrar una ventana emergente."</string>
+ <string name="allow">"Permitir"</string>
+ <string name="block">"Bloquear"</string>
+ <string name="too_many_windows_dialog_title">"Límite de ventanas alcanzado"</string>
+ <string name="too_many_windows_dialog_message">"No se ha podido abrir una ventana nueva porque ya se ha abierto el número máximo de ventanas admitido."</string>
+ <string name="too_many_subwindows_dialog_title">"Ventana emergente ya abierta"</string>
+ <string name="too_many_subwindows_dialog_message">"No se ha podido abrir una nueva ventana emergente, porque no se puede abrir más de una ventana emergente a la vez."</string>
+ <string name="download_title">"Historial de descargas"</string>
+ <string name="download_unknown_filename">"&lt;Desconocido&gt;"</string>
+ <string name="download_menu_open">"Abrir"</string>
+ <string name="download_menu_clear">"Borrar de la lista"</string>
+ <string name="download_menu_cancel">"Cancelar descarga"</string>
+ <string name="download_menu_cancel_all">"Cancelar todas las descargas"</string>
+ <string name="download_menu_clear_all">"Borrar lista"</string>
+ <string name="download_clear_dlg_title">"Borrar"</string>
+ <string name="download_clear_dlg_msg">"Todos los elementos se borrarán de la lista y se eliminarán de la caché del navegador."</string>
+ <string name="download_cancel_dlg_title">"Cancelar descargas"</string>
+ <string name="download_cancel_dlg_msg">"Las <xliff:g id="DOWNLOAD_COUNT">%d</xliff:g> descargas se cancelarán y se borrarán del historial de descargas."</string>
+ <string name="download_file_error_dlg_title">"Sin espacio"</string>
+ <string name="download_file_error_dlg_msg">"No se ha podido descargar el archivo <xliff:g id="FILENAME">%s</xliff:g>."\n"Libera espacio en el teléfono e inténtalo de nuevo."</string>
+ <string name="download_failed_generic_dlg_title">"Descarga incorrecta"</string>
+ <string name="download_no_sdcard_dlg_title">"Falta la tarjeta SD"</string>
+ <string name="download_no_sdcard_dlg_msg">"Para descargar <xliff:g id="FILENAME">%s</xliff:g> se necesita una tarjeta SD."</string>
+ <string name="download_sdcard_busy_dlg_title">"Tarjeta SD no disponible"</string>
+ <!-- no translation found for download_sdcard_busy_dlg_msg (3473883538192835204) -->
+ <skip />
+ <string name="download_no_application">"No se ha encontrado ninguna aplicación que permita abrir este archivo."</string>
+ <string name="retry">"Reintentar"</string>
+ <string name="no_downloads">"El historial de descargas está vacío."</string>
+ <string name="download_error">"Descarga incorrecta"</string>
+ <string name="download_success">"Se ha completado la descarga del archivo <xliff:g id="FILE">%s</xliff:g>."</string>
+ <string name="download_running">"Descargando..."</string>
+ <string name="download_pending">"Iniciando descarga…"</string>
+ <string name="download_pending_network">"Esperando conexión de datos…"</string>
+ <string name="download_running_paused">"Esperando conexión de datos…"</string>
+ <string name="download_canceled">"Descarga cancelada"</string>
+ <string name="download_not_acceptable">"No se puede realizar la descarga, porque el teléfono no admite el contenido que se quiere descargar."</string>
+ <string name="download_file_error">"No se puede completar la descarga porque no hay suficiente espacio."</string>
+ <string name="download_length_required">"No se puede realizar la descarga, porque no es posible determinar el tamaño del elemento."</string>
+ <string name="download_precondition_failed">"La descarga se ha interrumpido y no se puede reanudar."</string>
+ <string name="activity_instrumentation_test_runner">"Realizador de pruebas del navegador"</string>
+ <string name="search_google">"Búsqueda de Google"</string>
+ <string name="permlab_readHistoryBookmarks">"leer información de marcadores y del historial del navegador"</string>
+ <string name="permdesc_readHistoryBookmarks">"Permite que la aplicación lea todas las URL que ha visitado el navegador y todos sus marcadores."</string>
+ <string name="permlab_writeHistoryBookmarks">"escribir en marcadores y en el historial del navegador"</string>
+ <string name="permdesc_writeHistoryBookmarks">"Permite que una aplicación modifique la información de los marcadores o del historial del navegador almacenada en el teléfono. Las aplicaciones malintencionadas pueden utilizar este permiso para borrar o modificar los datos del navegador."</string>
+ <!-- no translation found for query_data_prompt (5474381240981604223) -->
+ <skip />
+ <!-- no translation found for query_data_message (8789381063185445197) -->
+ <skip />
+ <!-- no translation found for location_prompt (226262202057302423) -->
+ <skip />
+ <!-- no translation found for location_message (1729456751935683242) -->
+ <skip />
+ <!-- no translation found for shortcut_prompt (437193299088893596) -->
+ <skip />
+ <!-- no translation found for shortcut_message (4793042709293755892) -->
+ <skip />
+ <string name="settings_message">"En la tabla que aparece a continuación se muestran los permisos que has concedido a los distintos sitios web que han intentado utilizar Gears."</string>
+ <!-- no translation found for filepicker_message (4929726371602896039) -->
+ <skip />
+ <!-- no translation found for image_message (5450245866521896891) -->
+ <skip />
+ <string name="settings_title">"Configuración de Gears"</string>
+ <string name="privacy_policy">"Lee la política de privacidad del sitio para saber cómo se utilizará tu ubicación."</string>
+ <!-- no translation found for settings_storage_title (7089119630457156408) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_on (2733295483569432111) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_off (1338039396087898813) -->
+ <skip />
+ <!-- no translation found for settings_location_title (4953062923509886651) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_on (1496219621849158879) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_off (5216466051810596189) -->
+ <skip />
+ <!-- no translation found for settings_remove_site (1822247070226589958) -->
+ <skip />
+ <!-- no translation found for settings_empty (2008952224378583146) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove_title (5360857296753219635) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove (585560170733446898) -->
+ <skip />
+ <!-- no translation found for storage_notification (6534213782873968255) -->
+ <skip />
+ <!-- no translation found for storage_notification_alwaysdeny (5473208033665783513) -->
+ <skip />
+ <!-- no translation found for location_notification (3495804750470905804) -->
+ <skip />
+ <!-- no translation found for location_notification_alwaysdeny (6184925953783312629) -->
+ <skip />
+ <!-- no translation found for shortcut_notification (1659700220868444568) -->
+ <skip />
+ <!-- no translation found for permission_button_alwaysdeny (3837055994905214848) -->
+ <skip />
+ <!-- no translation found for permission_button_allow (7301874925704148242) -->
+ <skip />
+ <!-- no translation found for permission_button_deny (104712269725153018) -->
+ <skip />
+ <!-- no translation found for shortcut_button_alwaysdeny (4172277731325126605) -->
+ <skip />
+ <string name="shortcut_button_allow">"Aceptar"</string>
+ <string name="shortcut_button_deny">"Cancelar"</string>
+ <string name="settings_button_allow">"Aplicar"</string>
+ <string name="settings_button_deny">"Cancelar"</string>
+ <string name="filepicker_button_allow">"Aceptar"</string>
+ <string name="filepicker_button_deny">"Cancelar"</string>
+ <string name="filepicker_path">"ruta:"</string>
+ <string name="filepicker_no_files_selected">"Ningún archivo seleccionado"</string>
+ <string name="filepicker_one_file_selected">"Un archivo seleccionado"</string>
+ <string name="filepicker_some_files_selected">"archivos seleccionados"</string>
+ <string name="remove">"Eliminar"</string>
+ <string name="local_storage">"Almacenamiento local"</string>
+ <string name="allowed">"Permitido"</string>
+ <string name="denied">"Denegado"</string>
+ <string name="unrecognized_dialog_message">"Tipo de cuadro de diálogo no reconocido"</string>
+ <string name="default_button">"Aceptar"</string>
+ <string name="zoom_overview_button_text">"Información general"</string>
+</resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
new file mode 100644
index 00000000..5a2ec4d8
--- /dev/null
+++ b/res/values-fr/strings.xml
@@ -0,0 +1,334 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name">"Navigateur"</string>
+ <string name="tab_bookmarks">"Favoris"</string>
+ <string name="tab_most_visited">"Les + visités"</string>
+ <string name="tab_history">"Historique"</string>
+ <string name="added_to_bookmarks">"Ajouté aux favoris"</string>
+ <string name="removed_from_bookmarks">"Supprimé des favoris"</string>
+ <string name="sign_in_to">"Connectez-vous à <xliff:g id="HOSTNAME">%s1</xliff:g> \"<xliff:g id="REALM">%s2</xliff:g>\""</string>
+ <string name="username">"Nom"</string>
+ <string name="password">"Mot de passe"</string>
+ <string name="action">"Se connecter"</string>
+ <string name="cancel">"Annuler"</string>
+ <string name="ok">"OK"</string>
+ <string name="matches_found">" résultat(s) correspondant(s )"</string>
+ <string name="zero">"Non"</string>
+ <string name="page_info">"Infos sur la page"</string>
+ <string name="page_info_view">"Afficher les infos sur la page"</string>
+ <string name="page_info_address">"Adresse :"</string>
+ <string name="ssl_warnings_header">"Le certificat de sécurité pour ce site présente des problèmes."</string>
+ <string name="ssl_continue">"Continuer"</string>
+ <string name="security_warning">"Avertissement de sécurité"</string>
+ <string name="view_certificate">"Afficher le certificat"</string>
+ <string name="ssl_untrusted">"Ce certificat provient d\'une autorité non approuvée."</string>
+ <string name="ssl_mismatch">"Le nom du site ne correspond pas au nom indiqué dans le certificat."</string>
+ <string name="ssl_expired">"Le certificat a expiré."</string>
+ <string name="ssl_not_yet_valid">"Ce certificat n\'est pas encore valide."</string>
+ <string name="ssl_certificate">"Certificat de sécurité"</string>
+ <string name="ssl_certificate_is_valid">"Ce certificat est valide."</string>
+ <string name="issued_to">"Émis à :"</string>
+ <string name="common_name">"Nom commun :"</string>
+ <string name="org_name">"Organisation :"</string>
+ <string name="org_unit">"Unité d\'organisation :"</string>
+ <string name="issued_by">"Émis par :"</string>
+ <string name="validity_period">"Validité :"</string>
+ <string name="issued_on">"Émis le :"</string>
+ <string name="expires_on">"Expire le :"</string>
+ <string name="retrieving_creds_dlg_msg">"Récupération des données de connexion..."</string>
+ <string name="close">"Fermer"</string>
+ <string name="close_window">"La fenêtre sera fermée."</string>
+ <string name="stopping">"Arrêt..."</string>
+ <string name="stop">"Interrompre"</string>
+ <string name="reload">"Actualiser"</string>
+ <string name="forward">"Suivant"</string>
+ <string name="save">"OK"</string>
+ <string name="do_not_save">"Annuler"</string>
+ <string name="location">"Emplacement"</string>
+ <string name="name">"Nom"</string>
+ <string name="http">"http://"</string>
+ <string name="save_to_bookmarks">"Favori"</string>
+ <string name="edit_bookmark">"Modifier le favori"</string>
+ <string name="create_shortcut_bookmark">"Raccourci sur la page d\'accueil"</string>
+ <string name="open_bookmark">"Ouverture"</string>
+ <string name="remove_bookmark">"Supprimer le favori"</string>
+ <string name="remove_history_item">"Supprimer de l\'historique"</string>
+ <string name="bookmark_saved">"Enregistré dans les favoris"</string>
+ <string name="bookmark_needs_title">"Vous devez attribuer un nom à votre favori."</string>
+ <string name="bookmark_needs_url">"Le favori doit disposer d\'un emplacement."</string>
+ <string name="empty_bookmark">"Impossible de créer un favori vide."</string>
+ <string name="bookmark_url_not_valid">"L\'URL est incorrecte."</string>
+ <string name="delete_bookmark">"Supprimer"</string>
+ <string name="bookmark_page">"Ajouter la dernière page consultée"</string>
+ <string name="current_page">"de "</string>
+ <string name="delete_bookmark_warning">"Le favori \"<xliff:g id="BOOKMARK">%s</xliff:g>\" sera supprimé."</string>
+ <string name="open_in_new_window">"Nouvelle fenêtre"</string>
+ <string name="new_window">"Nouvelle fenêtre"</string>
+ <string name="goto_dot">"Recherche"</string>
+ <string name="find_dot">"Rechercher sur la page"</string>
+ <string name="select_dot">"Sélectionner le texte"</string>
+ <string name="view_tabs">"Vue d\'ensemble des fenêtres"</string>
+ <string name="view_tabs_condensed">"Fenêtres"</string>
+ <string name="tab_picker_title">"Fenêtres actuelles"</string>
+ <string name="tab_picker_view_tab">"Afficher"</string>
+ <string name="tab_picker_new_tab">"Nouv. fenêtre"</string>
+ <string name="tab_picker_remove_tab">"Fermer"</string>
+ <string name="tab_picker_bookmark">"Favori"</string>
+ <string name="tab_picker_send_url">"Partager le lien"</string>
+ <string name="bookmarks">"Favoris"</string>
+ <string name="shortcut_bookmark">"Favori"</string>
+ <string name="history">"Historique"</string>
+ <string name="menu_view_download">"Téléchargements"</string>
+ <string name="share_page">"Partager la page"</string>
+ <string name="contextmenu_openlink">"Ouvrir"</string>
+ <string name="contextmenu_openlink_newwindow">"Ouvrir une nouvelle fenêtre"</string>
+ <string name="contextmenu_bookmark_thislink">"Lien du favori"</string>
+ <string name="contextmenu_savelink">"Enregistrer le lien"</string>
+ <string name="contextmenu_sharelink">"Partager le lien"</string>
+ <string name="contextmenu_copy">"Copier"</string>
+ <string name="contextmenu_copylink">"Copier l\'URL du lien"</string>
+ <string name="contextmenu_download_image">"Enregistrer l\'image"</string>
+ <string name="contextmenu_view_image">"Afficher l\'image"</string>
+ <string name="contextmenu_dial_dot">"Composer..."</string>
+ <string name="contextmenu_add_contact">"Ajouter un contact"</string>
+ <string name="contextmenu_send_mail">"Envoyer un e-mail"</string>
+ <string name="contextmenu_map">"Plan"</string>
+ <string name="clear">"Effacer"</string>
+ <string name="replace">"Remplacer"</string>
+ <string name="browser_bookmarks_page_bookmarks_text">"Favoris"</string>
+ <string name="menu_preferences">"Paramètres"</string>
+ <string name="pref_content_title">"Paramètres du contenu de la page"</string>
+ <string name="pref_content_load_images">"Charger les images"</string>
+ <string name="pref_content_load_images_summary">"Afficher les images des pages Web"</string>
+ <string name="pref_content_block_popups">"Bloquer les pop-up"</string>
+ <string name="pref_content_javascript">"Activer JavaScript"</string>
+ <string name="pref_content_open_in_background">"Ouvrir en arrière-plan"</string>
+ <string name="pref_content_open_in_background_summary">"Les nouvelles fenêtres s\'ouvrent derrière la fenêtre actuelle"</string>
+ <string name="pref_content_homepage">"Configurer la page d\'accueil"</string>
+ <string name="pref_content_autofit">"Redimensionner les pages"</string>
+ <string name="pref_content_autofit_summary">"Configurer les pages Web pour qu\'elles s\'ajustent à l\'écran"</string>
+ <string name="pref_privacy_title">"Paramètres de confidentialité"</string>
+ <string name="pref_privacy_clear_cache">"Effacer le cache"</string>
+ <string name="pref_privacy_clear_cache_summary">"Supprimer tout le contenu de la page en cache"</string>
+ <string name="pref_privacy_clear_cache_dlg">"Le cache sera effacé."</string>
+ <string name="pref_privacy_clear_cookies">"Effacer tous les cookies"</string>
+ <string name="pref_privacy_clear_cookies_summary">"Effacer tous les cookies du navigateur"</string>
+ <string name="pref_privacy_clear_cookies_dlg">"Tous les cookies seront effacés."</string>
+ <string name="pref_privacy_clear_history">"Effacer l\'historique"</string>
+ <string name="pref_privacy_clear_history_summary">"Effacer l\'historique du navigateur"</string>
+ <string name="pref_privacy_clear_history_dlg">"L\'historique du navigateur sera effacé."</string>
+ <string name="pref_privacy_clear_form_data">"Effacer données formulaire"</string>
+ <string name="pref_privacy_clear_form_data_summary">"Effacer toutes les données de formulaire enregistrées"</string>
+ <string name="pref_privacy_clear_form_data_dlg">"Toutes les données de formulaire enregistrées seront effacées."</string>
+ <string name="pref_privacy_clear_passwords">"Effacer les mots de passe"</string>
+ <string name="pref_privacy_clear_passwords_summary">"Effacer tous les mots de passe enregistrés"</string>
+ <string name="pref_privacy_clear_passwords_dlg">"Tous les mots de passe enregistrés seront effacés."</string>
+ <string name="pref_security_title">"Paramètres de sécurité"</string>
+ <string name="pref_security_remember_passwords">"Mémoriser les mots de passe"</string>
+ <string name="pref_security_remember_passwords_summary">"Enregistrer les noms d\'utilisateur et les mots de passe pour les sites Web"</string>
+ <string name="pref_security_save_form_data">"Mémoriser données formulaire"</string>
+ <string name="pref_security_save_form_data_summary">"Mémoriser les données saisies dans les formulaires pour les utiliser ultérieurement"</string>
+ <string name="pref_security_show_security_warning">"Avertissements de sécurité"</string>
+ <string name="pref_security_show_security_warning_summary">"Afficher un avertissement en cas de problème de sécurité d\'un site"</string>
+ <string name="pref_security_accept_cookies">"Accepter les cookies"</string>
+ <string name="pref_security_accept_cookies_summary">"Autoriser les sites à enregistrer et lire les données \"cookie\""</string>
+ <string name="pref_text_size">"Définir la taille du texte"</string>
+ <string-array name="pref_text_size_choices">
+ <item>"Très petite"</item>
+ <item>"Petite"</item>
+ <item>"Normale"</item>
+ <item>"Grande"</item>
+ <item>"Très grande"</item>
+ </string-array>
+ <string name="pref_text_size_dialogtitle">"Taille de la police"</string>
+ <string name="pref_extras_title">"Paramètres avancés"</string>
+ <string name="pref_extras_gears_enable">"Activer Gears"</string>
+ <string name="pref_extras_gears_enable_summary">"Applications étendant les fonctionnalités du navigateur"</string>
+ <string name="pref_extras_gears_settings">"Paramètres Gears"</string>
+ <string name="pref_plugin_installed">"Liste des plug-ins"</string>
+ <string name="pref_plugin_installed_empty_list">"Aucun plug-in installé."</string>
+ <string name="pref_extras_gears_settings_summary">"Applications étendant les fonctionnalités du navigateur"</string>
+ <string name="pref_extras_reset_default">"Rétablir valeurs par défaut"</string>
+ <string name="pref_extras_reset_default_summary">"Effacer toutes les données du navigateur et rétablir les paramètres par défaut"</string>
+ <string name="pref_extras_reset_default_dlg">"Toutes les données du navigateur seront effacées et les paramètres par défaut rétablis."</string>
+ <string name="pref_extras_reset_default_dlg_title">"Rétablir les valeurs par défaut"</string>
+ <string name="pref_development_title">"Débogage"</string>
+ <string name="pref_development_viewport">"Use wide viewport"</string>
+ <string name="pref_development_single_column_rendering">"Single column rendering"</string>
+ <string name="pref_development_normal_rendering">"Normal rendering"</string>
+ <string name="pref_development_trace">"Activer le suivi"</string>
+ <string name="pref_development_nav_dump">"Enable nav cache dump"</string>
+ <string name="pref_development_search_url">"Set search URL"</string>
+ <string name="pref_development_uastring">"UAString"</string>
+ <string-array name="pref_development_ua_choices">
+ <item>"Android"</item>
+ <item>"Desktop"</item>
+ <item>"iPhone"</item>
+ </string-array>
+ <string-array name="pref_development_ua_values">
+ <item>"0"</item>
+ <item>"1"</item>
+ <item>"2"</item>
+ </string-array>
+ <string name="pref_default_text_encoding">"Définir le codage du texte"</string>
+ <string-array name="pref_default_text_encoding_choices">
+ <item>"Latin-1 (ISO-8859-1)"</item>
+ <item>"Unicode (UTF-8)"</item>
+ <item>"Japonais (ISO-2022-JP)"</item>
+ <item>"Japonais (SHIFT_JIS)"</item>
+ <item>"Japonais (EUC-JP)"</item>
+ </string-array>
+ <string name="pref_default_text_encoding_dialogtitle">"Codage du texte"</string>
+ <string name="pref_default_text_encoding_default">"Latin-1"</string>
+ <string name="browserFrameRedirect">"Rediriger"</string>
+ <string name="browserFrame307Post">"Cette page Web est en cours de redirection. Voulez-vous renvoyer les données de formulaire saisies vers le nouvel emplacement ?"</string>
+ <string name="browserFrameNetworkErrorLabel">"Problème de connectivité des données"</string>
+ <string name="browserFrameFileErrorLabel">"Problème de fichier"</string>
+ <string name="browserFrameFormResubmitLabel">"Confirmer"</string>
+ <string name="browserFrameFormResubmitMessage">"La page que vous tentez d\'afficher contient des données qui ont déjà été envoyées (\"POSTDATA\"). Si vous renvoyez les données, toute action effectuée par le formulaire sur la page exécutée (notamment les lancements de recherche ou les achats en ligne) sera répétée."</string>
+ <string name="loadSuspendedTitle">"Aucune connexion réseau"</string>
+ <string name="loadSuspended">"Une fois la connexion rétablie, le chargement de la page reprendra."</string>
+ <string name="clear_history">"Effacer l\'historique"</string>
+ <string name="browser_history">"Dernières pages consultées"</string>
+ <string name="empty_history">"L\'historique du navigateur est vide."</string>
+ <string name="add_new_bookmark">"Ajouter aux favoris..."</string>
+ <string name="no_database">"Aucune base de données !"</string>
+ <string name="search_hint">"Saisir l\'adresse Web"</string>
+ <string name="search_button_text">"Aller"</string>
+ <string name="attention">"Attention"</string>
+ <string name="popup_window_attempt">"Le site tente d\'ouvrir une fenêtre contextuelle."</string>
+ <string name="allow">"Autoriser"</string>
+ <string name="block">"Bloquer"</string>
+ <string name="too_many_windows_dialog_title">"Limite de fenêtres atteinte"</string>
+ <string name="too_many_windows_dialog_message">"Impossible d\'ouvrir une nouvelle fenêtre, car vous avez déjà ouvert le nombre maximum de fenêtres."</string>
+ <string name="too_many_subwindows_dialog_title">"Fenêtre contextuelle déjà ouverte"</string>
+ <string name="too_many_subwindows_dialog_message">"Impossible d\'ouvrir une nouvelle fenêtre. Une seule fenêtre peut être ouverte à la fois."</string>
+ <string name="download_title">"Historique de téléchargement"</string>
+ <string name="download_unknown_filename">"&lt;Inconnu&gt;"</string>
+ <string name="download_menu_open">"Ouvrir"</string>
+ <string name="download_menu_clear">"Effacer de la liste"</string>
+ <string name="download_menu_cancel">"Annuler le téléchargement"</string>
+ <string name="download_menu_cancel_all">"Annuler tous les téléchargements"</string>
+ <string name="download_menu_clear_all">"Effacer la liste"</string>
+ <string name="download_clear_dlg_title">"Effacer"</string>
+ <string name="download_clear_dlg_msg">"Tous les éléments seront effacés de la liste et supprimés du cache du navigateur."</string>
+ <string name="download_cancel_dlg_title">"Annuler les téléchargements"</string>
+ <string name="download_cancel_dlg_msg">"Les <xliff:g id="DOWNLOAD_COUNT">%d</xliff:g> téléchargements seront annulés et effacés de l\'historique des téléchargements."</string>
+ <string name="download_file_error_dlg_title">"Espace insuffisant"</string>
+ <string name="download_file_error_dlg_msg">"Impossible de télécharger <xliff:g id="FILENAME">%s</xliff:g>."\n"Libérez de l\'espace sur votre téléphone, puis réessayez."</string>
+ <string name="download_failed_generic_dlg_title">"Échec de téléchargement"</string>
+ <string name="download_no_sdcard_dlg_title">"Aucune carte SD"</string>
+ <string name="download_no_sdcard_dlg_msg">"Le téléchargement de <xliff:g id="FILENAME">%s</xliff:g> requiert une carte SD."</string>
+ <string name="download_sdcard_busy_dlg_title">"Carte SD non disponible"</string>
+ <!-- no translation found for download_sdcard_busy_dlg_msg (3473883538192835204) -->
+ <skip />
+ <string name="download_no_application">"Impossible de trouver une application permettant d\'ouvrir ce fichier."</string>
+ <string name="retry">"Réessayer"</string>
+ <string name="no_downloads">"L\'historique de téléchargement est vide."</string>
+ <string name="download_error">"Échec du téléchargement"</string>
+ <string name="download_success">"Téléchargement de <xliff:g id="FILE">%s</xliff:g> terminé."</string>
+ <string name="download_running">"Téléchargement..."</string>
+ <string name="download_pending">"Début du téléchargement..."</string>
+ <string name="download_pending_network">"En attente d\'une connexion de données..."</string>
+ <string name="download_running_paused">"En attente d\'une connexion de données..."</string>
+ <string name="download_canceled">"Téléchargement annulé"</string>
+ <string name="download_not_acceptable">"Impossible d\'effectuer le téléchargement. Le contenu n\'est pas pris en charge sur ce téléphone."</string>
+ <string name="download_file_error">"Téléchargement interrompu. Espace insuffisant."</string>
+ <string name="download_length_required">"Téléchargement impossible. La taille de l\'élément n\'a pas pu être déterminée."</string>
+ <string name="download_precondition_failed">"Téléchargement interrompu. Impossible de reprendre le téléchargement."</string>
+ <string name="activity_instrumentation_test_runner">"Testeur de navigateur"</string>
+ <string name="search_google">"Recherche Google"</string>
+ <string name="permlab_readHistoryBookmarks">"Lecture de l\'historique du navigateur et des favoris"</string>
+ <string name="permdesc_readHistoryBookmarks">"Permet à l\'application de lire toutes les URL visitées par le navigateur et tous les favoris."</string>
+ <string name="permlab_writeHistoryBookmarks">"Enregistrement de l\'historique du navigateur et des favoris"</string>
+ <string name="permdesc_writeHistoryBookmarks">"Permet à une application de modifier l\'historique du navigateur ou les favoris enregistrés sur votre téléphone. Des applications malveillantes peuvent utiliser cette fonction pour effacer ou modifier les données de votre navigateur."</string>
+ <!-- no translation found for query_data_prompt (5474381240981604223) -->
+ <skip />
+ <!-- no translation found for query_data_message (8789381063185445197) -->
+ <skip />
+ <!-- no translation found for location_prompt (226262202057302423) -->
+ <skip />
+ <!-- no translation found for location_message (1729456751935683242) -->
+ <skip />
+ <!-- no translation found for shortcut_prompt (437193299088893596) -->
+ <skip />
+ <!-- no translation found for shortcut_message (4793042709293755892) -->
+ <skip />
+ <string name="settings_message">"Le tableau ci-dessous indique les fonctions que vous avez autorisées pour chaque site ayant tenté d\'utiliser Google Gears."</string>
+ <!-- no translation found for filepicker_message (4929726371602896039) -->
+ <skip />
+ <!-- no translation found for image_message (5450245866521896891) -->
+ <skip />
+ <string name="settings_title">"Paramètres Google Gears"</string>
+ <string name="privacy_policy">"Lisez les règles de confidentialité du site pour voir comment votre emplacement sera utilisé."</string>
+ <!-- no translation found for settings_storage_title (7089119630457156408) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_on (2733295483569432111) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_off (1338039396087898813) -->
+ <skip />
+ <!-- no translation found for settings_location_title (4953062923509886651) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_on (1496219621849158879) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_off (5216466051810596189) -->
+ <skip />
+ <!-- no translation found for settings_remove_site (1822247070226589958) -->
+ <skip />
+ <!-- no translation found for settings_empty (2008952224378583146) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove_title (5360857296753219635) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove (585560170733446898) -->
+ <skip />
+ <!-- no translation found for storage_notification (6534213782873968255) -->
+ <skip />
+ <!-- no translation found for storage_notification_alwaysdeny (5473208033665783513) -->
+ <skip />
+ <!-- no translation found for location_notification (3495804750470905804) -->
+ <skip />
+ <!-- no translation found for location_notification_alwaysdeny (6184925953783312629) -->
+ <skip />
+ <!-- no translation found for shortcut_notification (1659700220868444568) -->
+ <skip />
+ <!-- no translation found for permission_button_alwaysdeny (3837055994905214848) -->
+ <skip />
+ <!-- no translation found for permission_button_allow (7301874925704148242) -->
+ <skip />
+ <!-- no translation found for permission_button_deny (104712269725153018) -->
+ <skip />
+ <!-- no translation found for shortcut_button_alwaysdeny (4172277731325126605) -->
+ <skip />
+ <string name="shortcut_button_allow">"OK"</string>
+ <string name="shortcut_button_deny">"Annuler"</string>
+ <string name="settings_button_allow">"Appliquer"</string>
+ <string name="settings_button_deny">"Annuler"</string>
+ <string name="filepicker_button_allow">"OK"</string>
+ <string name="filepicker_button_deny">"Annuler"</string>
+ <string name="filepicker_path">"chemin :"</string>
+ <string name="filepicker_no_files_selected">"Aucun fichier sélectionné"</string>
+ <string name="filepicker_one_file_selected">"Un fichier sélectionné"</string>
+ <string name="filepicker_some_files_selected">"fichiers sélectionnés"</string>
+ <string name="remove">"Supprimer"</string>
+ <string name="local_storage">"Stockage local"</string>
+ <string name="allowed">"Autorisé"</string>
+ <string name="denied">"Refusé"</string>
+ <string name="unrecognized_dialog_message">"Type de boîte de dialogue inconnu"</string>
+ <string name="default_button">"OK"</string>
+ <string name="zoom_overview_button_text">"Présentation"</string>
+</resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
new file mode 100644
index 00000000..593c4f4b
--- /dev/null
+++ b/res/values-it/strings.xml
@@ -0,0 +1,334 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name">"Browser"</string>
+ <string name="tab_bookmarks">"Segnalibri"</string>
+ <string name="tab_most_visited">"I più visitati"</string>
+ <string name="tab_history">"Cronologia"</string>
+ <string name="added_to_bookmarks">"Aggiunto a segnalibri"</string>
+ <string name="removed_from_bookmarks">"Rimosso da segnalibri"</string>
+ <string name="sign_in_to">"Accedi a <xliff:g id="HOSTNAME">%s1</xliff:g> \"<xliff:g id="REALM">%s2</xliff:g>\""</string>
+ <string name="username">"Nome"</string>
+ <string name="password">"Password"</string>
+ <string name="action">"Accedi"</string>
+ <string name="cancel">"Annulla"</string>
+ <string name="ok">"OK"</string>
+ <string name="matches_found">" corrispondenze"</string>
+ <string name="zero">"No"</string>
+ <string name="page_info">"Info pagina"</string>
+ <string name="page_info_view">"Visualizza info pagina"</string>
+ <string name="page_info_address">"Indirizzo:"</string>
+ <string name="ssl_warnings_header">"Problemi con il certificato di protezione del sito."</string>
+ <string name="ssl_continue">"Continua"</string>
+ <string name="security_warning">"Avviso di protezione"</string>
+ <string name="view_certificate">"Visualizza certificato"</string>
+ <string name="ssl_untrusted">"Questo certificato non proviene da un\'autorità attendibile."</string>
+ <string name="ssl_mismatch">"Il nome del sito non corrisponde al nome nel certificato."</string>
+ <string name="ssl_expired">"Il certificato è scaduto."</string>
+ <string name="ssl_not_yet_valid">"Questo certificato non è ancora valido."</string>
+ <string name="ssl_certificate">"Certificato di protezione"</string>
+ <string name="ssl_certificate_is_valid">"Questo certificato è valido."</string>
+ <string name="issued_to">"Rilasciato a:"</string>
+ <string name="common_name">"Nome comune:"</string>
+ <string name="org_name">"Organizzazione:"</string>
+ <string name="org_unit">"Unità organizzativa:"</string>
+ <string name="issued_by">"Rilasciato da:"</string>
+ <string name="validity_period">"Validità:"</string>
+ <string name="issued_on">"Rilasciato il:"</string>
+ <string name="expires_on">"Scade il:"</string>
+ <string name="retrieving_creds_dlg_msg">"Recupero dettagli di accesso..."</string>
+ <string name="close">"Chiudi"</string>
+ <string name="close_window">"Questa finestra verrà chiusa."</string>
+ <string name="stopping">"Interruzione..."</string>
+ <string name="stop">"Interrompi"</string>
+ <string name="reload">"Aggiorna"</string>
+ <string name="forward">"Avanti"</string>
+ <string name="save">"OK"</string>
+ <string name="do_not_save">"Annulla"</string>
+ <string name="location">"URL"</string>
+ <string name="name">"Nome"</string>
+ <string name="http">"http://"</string>
+ <string name="save_to_bookmarks">"Segnalibro"</string>
+ <string name="edit_bookmark">"Modifica segnalibro"</string>
+ <string name="create_shortcut_bookmark">"Aggiungi scorciatoia Home"</string>
+ <string name="open_bookmark">"Apri"</string>
+ <string name="remove_bookmark">"Elimina segnalibro"</string>
+ <string name="remove_history_item">"Rimuovi da cronologia"</string>
+ <string name="bookmark_saved">"Salvato nei segnalibri."</string>
+ <string name="bookmark_needs_title">"Inserisci un nome per il segnalibro."</string>
+ <string name="bookmark_needs_url">"Inserisci un URL per il segnalibro."</string>
+ <string name="empty_bookmark">"Impossibile creare un segnalibro vuoto."</string>
+ <string name="bookmark_url_not_valid">"URL non valido."</string>
+ <string name="delete_bookmark">"Elimina"</string>
+ <string name="bookmark_page">"Aggiungi ultima pagina visualizzata in segnalibri"</string>
+ <string name="current_page">"da "</string>
+ <string name="delete_bookmark_warning">"Il segnalibro \"<xliff:g id="BOOKMARK">%s</xliff:g>\" verrà eliminato."</string>
+ <string name="open_in_new_window">"Apri in nuova finestra"</string>
+ <string name="new_window">"Nuova finestra"</string>
+ <string name="goto_dot">"Vai"</string>
+ <string name="find_dot">"Trova nella pagina"</string>
+ <string name="select_dot">"Seleziona testo"</string>
+ <string name="view_tabs">"Anteprima finestre"</string>
+ <string name="view_tabs_condensed">"Finestre"</string>
+ <string name="tab_picker_title">"Finestre correnti"</string>
+ <string name="tab_picker_view_tab">"Visualizza"</string>
+ <string name="tab_picker_new_tab">"Nuova finestra"</string>
+ <string name="tab_picker_remove_tab">"Chiudi"</string>
+ <string name="tab_picker_bookmark">"Segnalibro"</string>
+ <string name="tab_picker_send_url">"Condividi link"</string>
+ <string name="bookmarks">"Segnalibri"</string>
+ <string name="shortcut_bookmark">"Segnalibro"</string>
+ <string name="history">"Cronologia"</string>
+ <string name="menu_view_download">"Download"</string>
+ <string name="share_page">"Condividi pagina"</string>
+ <string name="contextmenu_openlink">"Apri"</string>
+ <string name="contextmenu_openlink_newwindow">"Apri in nuova finestra"</string>
+ <string name="contextmenu_bookmark_thislink">"Aggiungi link in segnalibri"</string>
+ <string name="contextmenu_savelink">"Salva link"</string>
+ <string name="contextmenu_sharelink">"Condividi link"</string>
+ <string name="contextmenu_copy">"Copia"</string>
+ <string name="contextmenu_copylink">"Copia URL link"</string>
+ <string name="contextmenu_download_image">"Salva immagine"</string>
+ <string name="contextmenu_view_image">"Visualizza immagine"</string>
+ <string name="contextmenu_dial_dot">"Componi..."</string>
+ <string name="contextmenu_add_contact">"Aggiungi contatto"</string>
+ <string name="contextmenu_send_mail">"Invia email"</string>
+ <string name="contextmenu_map">"Mappa"</string>
+ <string name="clear">"Cancella"</string>
+ <string name="replace">"Sostituisci"</string>
+ <string name="browser_bookmarks_page_bookmarks_text">"Segnalibri"</string>
+ <string name="menu_preferences">"Impostazioni"</string>
+ <string name="pref_content_title">"Impostazioni contenuti pagina"</string>
+ <string name="pref_content_load_images">"Carica immagini"</string>
+ <string name="pref_content_load_images_summary">"Visualizza immagini in pagine web"</string>
+ <string name="pref_content_block_popups">"Blocca finestre pop-up"</string>
+ <string name="pref_content_javascript">"Attiva JavaScript"</string>
+ <string name="pref_content_open_in_background">"Apri in secondo piano"</string>
+ <string name="pref_content_open_in_background_summary">"Le nuove finestre vengono aperte dietro la finestra corrente"</string>
+ <string name="pref_content_homepage">"Imposta home page"</string>
+ <string name="pref_content_autofit">"Adatta autom. pagine"</string>
+ <string name="pref_content_autofit_summary">"Adatta le pagine web allo schermo"</string>
+ <string name="pref_privacy_title">"Impostazioni privacy"</string>
+ <string name="pref_privacy_clear_cache">"Cancella cache"</string>
+ <string name="pref_privacy_clear_cache_summary">"Elimina tutti i contenuti delle pagine nella cache"</string>
+ <string name="pref_privacy_clear_cache_dlg">"La cache verrà cancellata."</string>
+ <string name="pref_privacy_clear_cookies">"Cancella tutti i dati dei cookie"</string>
+ <string name="pref_privacy_clear_cookies_summary">"Cancella tutti i cookie del browser"</string>
+ <string name="pref_privacy_clear_cookies_dlg">"Verranno cancellati tutti i cookie."</string>
+ <string name="pref_privacy_clear_history">"Cancella cronologia"</string>
+ <string name="pref_privacy_clear_history_summary">"Cancella la cronologia di esplorazione del browser"</string>
+ <string name="pref_privacy_clear_history_dlg">"La cronologia di esplorazione del browser verrà cancellata."</string>
+ <string name="pref_privacy_clear_form_data">"Cancella dati moduli"</string>
+ <string name="pref_privacy_clear_form_data_summary">"Cancella tutti i dati dei moduli salvati"</string>
+ <string name="pref_privacy_clear_form_data_dlg">"Tutti i dati dei moduli salvati saranno cancellati."</string>
+ <string name="pref_privacy_clear_passwords">"Cancella password"</string>
+ <string name="pref_privacy_clear_passwords_summary">"Cancella tutte le password salvate"</string>
+ <string name="pref_privacy_clear_passwords_dlg">"Tutte le password salvate saranno cancellate."</string>
+ <string name="pref_security_title">"Impostazioni di protezione"</string>
+ <string name="pref_security_remember_passwords">"Memorizza le password"</string>
+ <string name="pref_security_remember_passwords_summary">"Salva nomi utente e password per i siti web"</string>
+ <string name="pref_security_save_form_data">"Memorizza dati moduli"</string>
+ <string name="pref_security_save_form_data_summary">"Memorizza i dati che digito nei moduli per usi futuri"</string>
+ <string name="pref_security_show_security_warning">"Mostra avvisi di protezione"</string>
+ <string name="pref_security_show_security_warning_summary">"Mostra un avviso se esiste un problema di sicurezza con un sito"</string>
+ <string name="pref_security_accept_cookies">"Accetta cookie"</string>
+ <string name="pref_security_accept_cookies_summary">"Consenti ai siti di salvare e leggere i dati \"cookie\""</string>
+ <string name="pref_text_size">"Imposta dim. testo"</string>
+ <string-array name="pref_text_size_choices">
+ <item>"Molto piccolo"</item>
+ <item>"Piccolo"</item>
+ <item>"Normale"</item>
+ <item>"Grande"</item>
+ <item>"Molto grande"</item>
+ </string-array>
+ <string name="pref_text_size_dialogtitle">"Dimensioni testo"</string>
+ <string name="pref_extras_title">"Impostazioni avanzate"</string>
+ <string name="pref_extras_gears_enable">"Attiva Gears"</string>
+ <string name="pref_extras_gears_enable_summary">"Applicazioni che estendono le funzionalità del browser"</string>
+ <string name="pref_extras_gears_settings">"Impostazioni Gears"</string>
+ <string name="pref_plugin_installed">"Elenco di plug-in"</string>
+ <string name="pref_plugin_installed_empty_list">"Nessun plug-in installato."</string>
+ <string name="pref_extras_gears_settings_summary">"Applicazioni che estendono le funzionalità del browser"</string>
+ <string name="pref_extras_reset_default">"Ripristina valori predefiniti"</string>
+ <string name="pref_extras_reset_default_summary">"Cancella tutti i dati del browser e ripristina tutte le impostazioni predefinite"</string>
+ <string name="pref_extras_reset_default_dlg">"Verranno cancellati tutti i dati del browser e ripristinate le impostazioni predefinite."</string>
+ <string name="pref_extras_reset_default_dlg_title">"Ripristina valori predefiniti"</string>
+ <string name="pref_development_title">"Debug"</string>
+ <string name="pref_development_viewport">"Use wide viewport"</string>
+ <string name="pref_development_single_column_rendering">"Single column rendering"</string>
+ <string name="pref_development_normal_rendering">"Normal rendering"</string>
+ <string name="pref_development_trace">"Enable tracing"</string>
+ <string name="pref_development_nav_dump">"Enable nav cache dump"</string>
+ <string name="pref_development_search_url">"Set search URL"</string>
+ <string name="pref_development_uastring">"UAString"</string>
+ <string-array name="pref_development_ua_choices">
+ <item>"Android"</item>
+ <item>"Desktop"</item>
+ <item>"iPhone"</item>
+ </string-array>
+ <string-array name="pref_development_ua_values">
+ <item>"0"</item>
+ <item>"1"</item>
+ <item>"2"</item>
+ </string-array>
+ <string name="pref_default_text_encoding">"Imposta codifica testo"</string>
+ <string-array name="pref_default_text_encoding_choices">
+ <item>"Latin-1 (ISO-8859-1)"</item>
+ <item>"Unicode (UTF-8)"</item>
+ <item>"Giapponese (ISO-2022-JP)"</item>
+ <item>"Giapponese (SHIFT_JIS)"</item>
+ <item>"Giapponese (EUC-JP)"</item>
+ </string-array>
+ <string name="pref_default_text_encoding_dialogtitle">"Codifica testo"</string>
+ <string name="pref_default_text_encoding_default">"Latin-1"</string>
+ <string name="browserFrameRedirect">"Reindirizza"</string>
+ <string name="browserFrame307Post">"La pagina web è in fase di reindizzamento. Inviare di nuovo i dati del modulo alla nuova posizione?"</string>
+ <string name="browserFrameNetworkErrorLabel">"Problema di connettività dati"</string>
+ <string name="browserFrameFileErrorLabel">"Problemi con il file"</string>
+ <string name="browserFrameFormResubmitLabel">"Conferma"</string>
+ <string name="browserFrameFormResubmitMessage">"La pagina che stai tentando di visualizzare contiene dati già inviati (\"POSTDATA\"). Se invii di nuovo i dati, ogni azione effettuata dal modulo nella pagina (come una ricerca o un acquisto online) verrà ripetuta."</string>
+ <string name="loadSuspendedTitle">"Nessuna connessione di rete"</string>
+ <string name="loadSuspended">"Il caricamento della pagina continuerà una volta ripristinata la connessione."</string>
+ <string name="clear_history">"Cancella cronologia"</string>
+ <string name="browser_history">"Pagine visitate di recente"</string>
+ <string name="empty_history">"La cronologia del browser è vuota."</string>
+ <string name="add_new_bookmark">"Aggiungi segnalibro…"</string>
+ <string name="no_database">"Nessun database"</string>
+ <string name="search_hint">"Digita l\'indirizzo web"</string>
+ <string name="search_button_text">"Vai"</string>
+ <string name="attention">"Attenzione"</string>
+ <string name="popup_window_attempt">"Il sito sta tentando di aprire una finestra pop-up."</string>
+ <string name="allow">"Consenti"</string>
+ <string name="block">"Blocca"</string>
+ <string name="too_many_windows_dialog_title">"Numero limite di finestre raggiunto"</string>
+ <string name="too_many_windows_dialog_message">"Impossibile aprire una nuova finestra. Numero massimo di finestre aperte già raggiunto."</string>
+ <string name="too_many_subwindows_dialog_title">"Finestra pop-up già aperta"</string>
+ <string name="too_many_subwindows_dialog_message">"Impossibile aprire una nuova finestra pop-up. È possibile aprirne soltanto una alla volta."</string>
+ <string name="download_title">"Cronologia download"</string>
+ <string name="download_unknown_filename">"&lt;Sconosciuto&gt;"</string>
+ <string name="download_menu_open">"Apri"</string>
+ <string name="download_menu_clear">"Cancella da elenco"</string>
+ <string name="download_menu_cancel">"Cancella download"</string>
+ <string name="download_menu_cancel_all">"Annulla tutti i download"</string>
+ <string name="download_menu_clear_all">"Cancella elenco"</string>
+ <string name="download_clear_dlg_title">"Cancella"</string>
+ <string name="download_clear_dlg_msg">"Tutti gli elementi verranno cancellati dall\'elenco e rimossi dalla cache del browser."</string>
+ <string name="download_cancel_dlg_title">"Annulla download"</string>
+ <string name="download_cancel_dlg_msg">"Tutti i <xliff:g id="DOWNLOAD_COUNT">%d</xliff:g> download verranno cancellati dalla cronologia download."</string>
+ <string name="download_file_error_dlg_title">"Spazio esaurito"</string>
+ <string name="download_file_error_dlg_msg">"Impossibile scaricare <xliff:g id="FILENAME">%s</xliff:g>."\n"Liberare spazio sul telefono e riprovare."</string>
+ <string name="download_failed_generic_dlg_title">"Download non riuscito"</string>
+ <string name="download_no_sdcard_dlg_title">"Nessuna scheda SD"</string>
+ <string name="download_no_sdcard_dlg_msg">"Per scaricare <xliff:g id="FILENAME">%s</xliff:g> occorre una scheda SD."</string>
+ <string name="download_sdcard_busy_dlg_title">"Scheda SD non disponibile"</string>
+ <!-- no translation found for download_sdcard_busy_dlg_msg (3473883538192835204) -->
+ <skip />
+ <string name="download_no_application">"Impossibile trovare un\'applicazione in cui aprire questo file."</string>
+ <string name="retry">"Riprova"</string>
+ <string name="no_downloads">"La cronologia download è vuota."</string>
+ <string name="download_error">"Download non riuscito."</string>
+ <string name="download_success">"Download di <xliff:g id="FILE">%s</xliff:g> completo."</string>
+ <string name="download_running">"Download..."</string>
+ <string name="download_pending">"Avvio download..."</string>
+ <string name="download_pending_network">"In attesa di connessione dati..."</string>
+ <string name="download_running_paused">"In attesa di connessione dati..."</string>
+ <string name="download_canceled">"Download annullato."</string>
+ <string name="download_not_acceptable">"Impossibile effettuare il download. I contenuti non sono supportati dal telefono."</string>
+ <string name="download_file_error">"Impossibile terminare il download. Spazio insufficiente."</string>
+ <string name="download_length_required">"Impossibile effettuare il download. Impossibile determinare le dimensioni dell\'elemento."</string>
+ <string name="download_precondition_failed">"Download interrotto. Impossibile ripristinarlo."</string>
+ <string name="activity_instrumentation_test_runner">"Browser Test Runner"</string>
+ <string name="search_google">"Cerca su Google"</string>
+ <string name="permlab_readHistoryBookmarks">"lettura cronologia e segnalibri del browser"</string>
+ <string name="permdesc_readHistoryBookmarks">"Consente all\'applicazione di leggere tutti gli URL visitati e tutti i segnalibri del browser."</string>
+ <string name="permlab_writeHistoryBookmarks">"creazione cronologia e segnalibri del browser"</string>
+ <string name="permdesc_writeHistoryBookmarks">"Consente a un\'applicazione di modificare la cronologia o i segnalibri del browser memorizzati sul telefono. Le applicazioni dannose possono sfruttare questa possibilità per cancellare o modificare i dati del browser."</string>
+ <!-- no translation found for query_data_prompt (5474381240981604223) -->
+ <skip />
+ <!-- no translation found for query_data_message (8789381063185445197) -->
+ <skip />
+ <!-- no translation found for location_prompt (226262202057302423) -->
+ <skip />
+ <!-- no translation found for location_message (1729456751935683242) -->
+ <skip />
+ <!-- no translation found for shortcut_prompt (437193299088893596) -->
+ <skip />
+ <!-- no translation found for shortcut_message (4793042709293755892) -->
+ <skip />
+ <string name="settings_message">"Nella tabella che segue vengono riportate le autorizzazioni concesse a ogni sito che ha provato a usare Gears."</string>
+ <!-- no translation found for filepicker_message (4929726371602896039) -->
+ <skip />
+ <!-- no translation found for image_message (5450245866521896891) -->
+ <skip />
+ <string name="settings_title">"Impostazioni Gears"</string>
+ <string name="privacy_policy">"Per conoscere la modalità di utilizzo della tua posizione, leggi le norme sulla privacy del sito."</string>
+ <!-- no translation found for settings_storage_title (7089119630457156408) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_on (2733295483569432111) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_off (1338039396087898813) -->
+ <skip />
+ <!-- no translation found for settings_location_title (4953062923509886651) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_on (1496219621849158879) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_off (5216466051810596189) -->
+ <skip />
+ <!-- no translation found for settings_remove_site (1822247070226589958) -->
+ <skip />
+ <!-- no translation found for settings_empty (2008952224378583146) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove_title (5360857296753219635) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove (585560170733446898) -->
+ <skip />
+ <!-- no translation found for storage_notification (6534213782873968255) -->
+ <skip />
+ <!-- no translation found for storage_notification_alwaysdeny (5473208033665783513) -->
+ <skip />
+ <!-- no translation found for location_notification (3495804750470905804) -->
+ <skip />
+ <!-- no translation found for location_notification_alwaysdeny (6184925953783312629) -->
+ <skip />
+ <!-- no translation found for shortcut_notification (1659700220868444568) -->
+ <skip />
+ <!-- no translation found for permission_button_alwaysdeny (3837055994905214848) -->
+ <skip />
+ <!-- no translation found for permission_button_allow (7301874925704148242) -->
+ <skip />
+ <!-- no translation found for permission_button_deny (104712269725153018) -->
+ <skip />
+ <!-- no translation found for shortcut_button_alwaysdeny (4172277731325126605) -->
+ <skip />
+ <string name="shortcut_button_allow">"OK"</string>
+ <string name="shortcut_button_deny">"Annulla"</string>
+ <string name="settings_button_allow">"Applica"</string>
+ <string name="settings_button_deny">"Annulla"</string>
+ <string name="filepicker_button_allow">"OK"</string>
+ <string name="filepicker_button_deny">"Annulla"</string>
+ <string name="filepicker_path">"percorso:"</string>
+ <string name="filepicker_no_files_selected">"Nessun file selezionato"</string>
+ <string name="filepicker_one_file_selected">"Un file selezionato"</string>
+ <string name="filepicker_some_files_selected">"file selezionati"</string>
+ <string name="remove">"Rimuovi"</string>
+ <string name="local_storage">"Archiviazione locale"</string>
+ <string name="allowed">"Consentito"</string>
+ <string name="denied">"Negato"</string>
+ <string name="unrecognized_dialog_message">"Tipo di finestra non riconosciuto"</string>
+ <string name="default_button">"OK"</string>
+ <string name="zoom_overview_button_text">"Panoramica"</string>
+</resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
new file mode 100644
index 00000000..b83cfce9
--- /dev/null
+++ b/res/values-ja/strings.xml
@@ -0,0 +1,334 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name">"ブラウザ"</string>
+ <string name="tab_bookmarks">"ブックマーク"</string>
+ <string name="tab_most_visited">"よくアクセスするサイト"</string>
+ <string name="tab_history">"履歴"</string>
+ <string name="added_to_bookmarks">"ブックマークに追加しました"</string>
+ <string name="removed_from_bookmarks">"ブックマークから削除しました"</string>
+ <string name="sign_in_to">"<xliff:g id="HOSTNAME">%s1</xliff:g>に「<xliff:g id="REALM">%s2</xliff:g>」でログイン"</string>
+ <string name="username">"名前"</string>
+ <string name="password">"パスワード"</string>
+ <string name="action">"ログイン"</string>
+ <string name="cancel">"キャンセル"</string>
+ <string name="ok">"OK"</string>
+ <string name="matches_found">" 件一致"</string>
+ <string name="zero">"いいえ"</string>
+ <string name="page_info">"ページ情報"</string>
+ <string name="page_info_view">"ページ情報を表示"</string>
+ <string name="page_info_address">"アドレス:"</string>
+ <string name="ssl_warnings_header">"このサイトのセキュリティ証明書には問題があります。"</string>
+ <string name="ssl_continue">"続行"</string>
+ <string name="security_warning">"セキュリティ警告"</string>
+ <string name="view_certificate">"証明書を表示"</string>
+ <string name="ssl_untrusted">"この証明書は信頼できる認証機関のものではありません。"</string>
+ <string name="ssl_mismatch">"サイト名と証明書上の名前が一致しません。"</string>
+ <string name="ssl_expired">"この証明書は有効期限切れです。"</string>
+ <string name="ssl_not_yet_valid">"この証明書はまだ有効ではありません。"</string>
+ <string name="ssl_certificate">"セキュリティ証明書"</string>
+ <string name="ssl_certificate_is_valid">"この証明書は有効です。"</string>
+ <string name="issued_to">"発行先:"</string>
+ <string name="common_name">"共通名:"</string>
+ <string name="org_name">"組織:"</string>
+ <string name="org_unit">"組織単位:"</string>
+ <string name="issued_by">"発行者:"</string>
+ <string name="validity_period">"有効期間:"</string>
+ <string name="issued_on">"発行:"</string>
+ <string name="expires_on">"有効期限:"</string>
+ <string name="retrieving_creds_dlg_msg">"ログイン詳細を取得中..."</string>
+ <string name="close">"閉じる"</string>
+ <string name="close_window">"このウィンドウを閉じます。"</string>
+ <string name="stopping">"停止中..."</string>
+ <string name="stop">"停止"</string>
+ <string name="reload">"再読み込み"</string>
+ <string name="forward">"進む"</string>
+ <string name="save">"OK"</string>
+ <string name="do_not_save">"キャンセル"</string>
+ <string name="location">"場所"</string>
+ <string name="name">"名前"</string>
+ <string name="http">"http://"</string>
+ <string name="save_to_bookmarks">"ブックマーク"</string>
+ <string name="edit_bookmark">"ブックマークを編集"</string>
+ <string name="create_shortcut_bookmark">"ショートカットをホームに追加"</string>
+ <string name="open_bookmark">"開く"</string>
+ <string name="remove_bookmark">"ブックマークを削除"</string>
+ <string name="remove_history_item">"履歴から消去"</string>
+ <string name="bookmark_saved">"ブックマークを保存しました。"</string>
+ <string name="bookmark_needs_title">"名前を指定してください。"</string>
+ <string name="bookmark_needs_url">"場所を指定してください。"</string>
+ <string name="empty_bookmark">"空のブックマークは作成できません。"</string>
+ <string name="bookmark_url_not_valid">"無効なURLです。"</string>
+ <string name="delete_bookmark">"削除"</string>
+ <string name="bookmark_page">"最後に表示したページをブックマークする"</string>
+ <string name="current_page">"ブックマークするページ "</string>
+ <string name="delete_bookmark_warning">"ブックマーク「<xliff:g id="BOOKMARK">%s</xliff:g>」を削除します。"</string>
+ <string name="open_in_new_window">"新しいウィンドウで開く"</string>
+ <string name="new_window">"新規ウィンドウ"</string>
+ <string name="goto_dot">"移動"</string>
+ <string name="find_dot">"このページの検索"</string>
+ <string name="select_dot">"テキストを選択"</string>
+ <string name="view_tabs">"ウィンドウリスト"</string>
+ <string name="view_tabs_condensed">"ウィンドウ"</string>
+ <string name="tab_picker_title">"現在のウィンドウ"</string>
+ <string name="tab_picker_view_tab">"表示"</string>
+ <string name="tab_picker_new_tab">"新規ウィンドウ"</string>
+ <string name="tab_picker_remove_tab">"閉じる"</string>
+ <string name="tab_picker_bookmark">"ブックマーク"</string>
+ <string name="tab_picker_send_url">"リンクを共有"</string>
+ <string name="bookmarks">"ブックマーク"</string>
+ <string name="shortcut_bookmark">"ブックマーク"</string>
+ <string name="history">"履歴"</string>
+ <string name="menu_view_download">"ダウンロード"</string>
+ <string name="share_page">"ページを共有"</string>
+ <string name="contextmenu_openlink">"開く"</string>
+ <string name="contextmenu_openlink_newwindow">"新しいウィンドウで開く"</string>
+ <string name="contextmenu_bookmark_thislink">"リンクをブックマーク"</string>
+ <string name="contextmenu_savelink">"リンクを保存"</string>
+ <string name="contextmenu_sharelink">"リンクを共有"</string>
+ <string name="contextmenu_copy">"コピー"</string>
+ <string name="contextmenu_copylink">"リンクのURLをコピー"</string>
+ <string name="contextmenu_download_image">"画像を保存"</string>
+ <string name="contextmenu_view_image">"画像を表示"</string>
+ <string name="contextmenu_dial_dot">"発信..."</string>
+ <string name="contextmenu_add_contact">"連絡先を追加"</string>
+ <string name="contextmenu_send_mail">"メールを送信"</string>
+ <string name="contextmenu_map">"地図"</string>
+ <string name="clear">"消去"</string>
+ <string name="replace">"入れ替え"</string>
+ <string name="browser_bookmarks_page_bookmarks_text">"ブックマーク"</string>
+ <string name="menu_preferences">"設定"</string>
+ <string name="pref_content_title">"ページコンテンツ設定"</string>
+ <string name="pref_content_load_images">"画像のロード"</string>
+ <string name="pref_content_load_images_summary">"ウェブページに画像を表示"</string>
+ <string name="pref_content_block_popups">"ポップアップウィンドウをブロック"</string>
+ <string name="pref_content_javascript">"JavaScript有効"</string>
+ <string name="pref_content_open_in_background">"バックグラウンドで開く"</string>
+ <string name="pref_content_open_in_background_summary">"新しいウィンドウを現在のウィンドウの後ろに開きます"</string>
+ <string name="pref_content_homepage">"ホームページ設定"</string>
+ <string name="pref_content_autofit">"ページの自動調整"</string>
+ <string name="pref_content_autofit_summary">"ウェブページの大きさを画面に合わせて表示します"</string>
+ <string name="pref_privacy_title">"プライバシー設定"</string>
+ <string name="pref_privacy_clear_cache">"キャッシュを消去"</string>
+ <string name="pref_privacy_clear_cache_summary">"キャッシュされたページコンテンツをすべて削除します"</string>
+ <string name="pref_privacy_clear_cache_dlg">"キャッシュを消去します。"</string>
+ <string name="pref_privacy_clear_cookies">"Cookieデータを全消去"</string>
+ <string name="pref_privacy_clear_cookies_summary">"ブラウザのCookieをすべて消去します"</string>
+ <string name="pref_privacy_clear_cookies_dlg">"Cookieをすべて消去します。"</string>
+ <string name="pref_privacy_clear_history">"履歴消去"</string>
+ <string name="pref_privacy_clear_history_summary">"ブラウザの閲覧履歴を消去します"</string>
+ <string name="pref_privacy_clear_history_dlg">"ブラウザの閲覧履歴を消去します。"</string>
+ <string name="pref_privacy_clear_form_data">"フォームデータをクリア"</string>
+ <string name="pref_privacy_clear_form_data_summary">"保存されているフォームデータをすべて消去します"</string>
+ <string name="pref_privacy_clear_form_data_dlg">"保存されているフォームデータをすべて消去します。"</string>
+ <string name="pref_privacy_clear_passwords">"パスワードを消去"</string>
+ <string name="pref_privacy_clear_passwords_summary">"保存されているパスワードをすべて消去します"</string>
+ <string name="pref_privacy_clear_passwords_dlg">"保存されているパスワードをすべて消去します。"</string>
+ <string name="pref_security_title">"セキュリティ設定"</string>
+ <string name="pref_security_remember_passwords">"パスワードを保存"</string>
+ <string name="pref_security_remember_passwords_summary">"ウェブサイトのユーザー名とパスワードを保存します"</string>
+ <string name="pref_security_save_form_data">"フォームデータを保存"</string>
+ <string name="pref_security_save_form_data_summary">"フォームに入力したデータを保存して後で再利用します"</string>
+ <string name="pref_security_show_security_warning">"セキュリティ警告を表示"</string>
+ <string name="pref_security_show_security_warning_summary">"サイトの安全性に問題がある場合に警告を表示"</string>
+ <string name="pref_security_accept_cookies">"Cookieの受け入れ"</string>
+ <string name="pref_security_accept_cookies_summary">"サイトによるCookieデータの保存と読み取りを許可します"</string>
+ <string name="pref_text_size">"テキストサイズを設定"</string>
+ <string-array name="pref_text_size_choices">
+ <item>"最小"</item>
+ <item>"小"</item>
+ <item>"中"</item>
+ <item>"大"</item>
+ <item>"最大"</item>
+ </string-array>
+ <string name="pref_text_size_dialogtitle">"文字サイズ"</string>
+ <string name="pref_extras_title">"詳細な設定"</string>
+ <string name="pref_extras_gears_enable">"Gears有効"</string>
+ <string name="pref_extras_gears_enable_summary">"ブラウザの機能を拡張するアプリケーションです"</string>
+ <string name="pref_extras_gears_settings">"Gears設定"</string>
+ <string name="pref_plugin_installed">"プラグインリスト"</string>
+ <string name="pref_plugin_installed_empty_list">"プラグインはインストールされていません。"</string>
+ <string name="pref_extras_gears_settings_summary">"ブラウザの機能を拡張するアプリケーション"</string>
+ <string name="pref_extras_reset_default">"初期設定にリセット"</string>
+ <string name="pref_extras_reset_default_summary">"ブラウザデータを消去して初期設定に戻します"</string>
+ <string name="pref_extras_reset_default_dlg">"ブラウザデータをすべて消去して初期設定に戻します。"</string>
+ <string name="pref_extras_reset_default_dlg_title">"初期設定にリセット"</string>
+ <string name="pref_development_title">"デバッグ"</string>
+ <string name="pref_development_viewport">"Use wide viewport"</string>
+ <string name="pref_development_single_column_rendering">"Single column rendering"</string>
+ <string name="pref_development_normal_rendering">"Normal rendering"</string>
+ <string name="pref_development_trace">"Enable tracing"</string>
+ <string name="pref_development_nav_dump">"Enable nav cache dump"</string>
+ <string name="pref_development_search_url">"Set search URL"</string>
+ <string name="pref_development_uastring">"UAString"</string>
+ <string-array name="pref_development_ua_choices">
+ <item>"Android"</item>
+ <item>"Desktop"</item>
+ <item>"iPhone"</item>
+ </string-array>
+ <string-array name="pref_development_ua_values">
+ <item>"0"</item>
+ <item>"1"</item>
+ <item>"2"</item>
+ </string-array>
+ <string name="pref_default_text_encoding">"テキストエンコードを設定"</string>
+ <string-array name="pref_default_text_encoding_choices">
+ <item>"Latin-1 (ISO-8859-1)"</item>
+ <item>"Unicode (UTF-8)"</item>
+ <item>"日本語 (ISO-2022-JP)"</item>
+ <item>"日本語 (Shift_JIS)"</item>
+ <item>"日本語 (EUC-JP)"</item>
+ </string-array>
+ <string name="pref_default_text_encoding_dialogtitle">"テキストエンコード"</string>
+ <string name="pref_default_text_encoding_default">"Latin-1"</string>
+ <string name="browserFrameRedirect">"リダイレクト"</string>
+ <string name="browserFrame307Post">"このウェブページはリダイレクトされます。入力したフォームデータをリダイレクト先に送信しますか?"</string>
+ <string name="browserFrameNetworkErrorLabel">"データアクセスエラー"</string>
+ <string name="browserFrameFileErrorLabel">"ファイルに問題があります"</string>
+ <string name="browserFrameFormResubmitLabel">"確認"</string>
+ <string name="browserFrameFormResubmitMessage">"表示しようとしているページには、送信済みのデータ (「POSTDATA」) があります。データを再送すると、以前このページのフォームで実行した操作 (検索やオンライン購入など) が繰り返されます。"</string>
+ <string name="loadSuspendedTitle">"ネットワークに接続していません"</string>
+ <string name="loadSuspended">"再接続時にロードを続行します。"</string>
+ <string name="clear_history">"履歴消去"</string>
+ <string name="browser_history">"最近閲覧したページ"</string>
+ <string name="empty_history">"ブラウザ履歴はありません。"</string>
+ <string name="add_new_bookmark">"ブックマークを追加..."</string>
+ <string name="no_database">"データベースがありません。"</string>
+ <string name="search_hint">"ウェブアドレスを入力してください"</string>
+ <string name="search_button_text">"移動"</string>
+ <string name="attention">"注意"</string>
+ <string name="popup_window_attempt">"このサイトはポップアップウィンドウを開こうとしています。"</string>
+ <string name="allow">"許可"</string>
+ <string name="block">"ブロック"</string>
+ <string name="too_many_windows_dialog_title">"ウィンドウの数が最大です"</string>
+ <string name="too_many_windows_dialog_message">"既にウィンドウの数が最大なので、新しいウィンドウを開けません。"</string>
+ <string name="too_many_subwindows_dialog_title">"ポップアップが既に開いています"</string>
+ <string name="too_many_subwindows_dialog_message">"一度に開けるポップアップウィンドウは1つだけなので、新しいポップアップウィンドウを開けませんでした。"</string>
+ <string name="download_title">"ダウンロード履歴"</string>
+ <string name="download_unknown_filename">"&lt;不明&gt;"</string>
+ <string name="download_menu_open">"開く"</string>
+ <string name="download_menu_clear">"リストから消去"</string>
+ <string name="download_menu_cancel">"ダウンロードをキャンセル"</string>
+ <string name="download_menu_cancel_all">"全ダウンロードをキャンセル"</string>
+ <string name="download_menu_clear_all">"リストを消去"</string>
+ <string name="download_clear_dlg_title">"消去"</string>
+ <string name="download_clear_dlg_msg">"リストの項目をすべて消去し、ブラウザのキャッシュから削除します。"</string>
+ <string name="download_cancel_dlg_title">"ダウンロードをキャンセル"</string>
+ <string name="download_cancel_dlg_msg">"全<xliff:g id="DOWNLOAD_COUNT">%d</xliff:g>件のダウンロードをキャンセルし、ダウンロード履歴から消去します。"</string>
+ <string name="download_file_error_dlg_title">"容量不足です"</string>
+ <string name="download_file_error_dlg_msg">"<xliff:g id="FILENAME">%s</xliff:g>をダウンロードできませんでした。"\n"端末の空き領域を増やしてからやり直してください。"</string>
+ <string name="download_failed_generic_dlg_title">"ダウンロードに失敗しました"</string>
+ <string name="download_no_sdcard_dlg_title">"SDカードがありません"</string>
+ <string name="download_no_sdcard_dlg_msg">"<xliff:g id="FILENAME">%s</xliff:g>をダウンロードするにはSDカードが必要です。"</string>
+ <string name="download_sdcard_busy_dlg_title">"SDカードは利用できません"</string>
+ <!-- no translation found for download_sdcard_busy_dlg_msg (3473883538192835204) -->
+ <skip />
+ <string name="download_no_application">"このファイルを開くアプリケーションがありません。"</string>
+ <string name="retry">"やり直す"</string>
+ <string name="no_downloads">"ダウンロード履歴はありません。"</string>
+ <string name="download_error">"ダウンロードできませんでした。"</string>
+ <string name="download_success">"<xliff:g id="FILE">%s</xliff:g>のダウンロードが終了しました。"</string>
+ <string name="download_running">"ダウンロード中..."</string>
+ <string name="download_pending">"ダウンロードを開始します..."</string>
+ <string name="download_pending_network">"ネットワークに接続しています..."</string>
+ <string name="download_running_paused">"ネットワークに接続しています..."</string>
+ <string name="download_canceled">"ダウンロードをキャンセルしました。"</string>
+ <string name="download_not_acceptable">"ダウンロードできません。この携帯電話ではサポートされていないコンテンツです。"</string>
+ <string name="download_file_error">"空き容量が不足しているため、ダウンロードを完了できません。"</string>
+ <string name="download_length_required">"項目のサイズを特定できないため、ダウンロードできません。"</string>
+ <string name="download_precondition_failed">"ダウンロードが中断されました。再開できません。"</string>
+ <string name="activity_instrumentation_test_runner">"Browser Test Runner"</string>
+ <string name="search_google">"Google検索"</string>
+ <string name="permlab_readHistoryBookmarks">"ブラウザの履歴とブックマークの読み取り"</string>
+ <string name="permdesc_readHistoryBookmarks">"ブラウザでアクセスしたすべてのURLとブックマークの読み取りをアプリケーションに許可します。"</string>
+ <string name="permlab_writeHistoryBookmarks">"ブラウザの履歴とブックマークの書き込み"</string>
+ <string name="permdesc_writeHistoryBookmarks">"携帯電話に保存されたブラウザの履歴やブックマークの変更をアプリケーションに許可します。これにより悪意のあるアプリケーションが、ブラウザのデータを消去または変更する恐れがあります。"</string>
+ <!-- no translation found for query_data_prompt (5474381240981604223) -->
+ <skip />
+ <!-- no translation found for query_data_message (8789381063185445197) -->
+ <skip />
+ <!-- no translation found for location_prompt (226262202057302423) -->
+ <skip />
+ <!-- no translation found for location_message (1729456751935683242) -->
+ <skip />
+ <!-- no translation found for shortcut_prompt (437193299088893596) -->
+ <skip />
+ <!-- no translation found for shortcut_message (4793042709293755892) -->
+ <skip />
+ <string name="settings_message">"Gearsを使用しようとしたサイトに許可した権限は、以下の表のとおりです。"</string>
+ <!-- no translation found for filepicker_message (4929726371602896039) -->
+ <skip />
+ <!-- no translation found for image_message (5450245866521896891) -->
+ <skip />
+ <string name="settings_title">"Gears設定"</string>
+ <string name="privacy_policy">"サイトのプライバシーポリシーを読んで、現在地の情報がどのように使用されるのか確認してください。"</string>
+ <!-- no translation found for settings_storage_title (7089119630457156408) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_on (2733295483569432111) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_off (1338039396087898813) -->
+ <skip />
+ <!-- no translation found for settings_location_title (4953062923509886651) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_on (1496219621849158879) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_off (5216466051810596189) -->
+ <skip />
+ <!-- no translation found for settings_remove_site (1822247070226589958) -->
+ <skip />
+ <!-- no translation found for settings_empty (2008952224378583146) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove_title (5360857296753219635) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove (585560170733446898) -->
+ <skip />
+ <!-- no translation found for storage_notification (6534213782873968255) -->
+ <skip />
+ <!-- no translation found for storage_notification_alwaysdeny (5473208033665783513) -->
+ <skip />
+ <!-- no translation found for location_notification (3495804750470905804) -->
+ <skip />
+ <!-- no translation found for location_notification_alwaysdeny (6184925953783312629) -->
+ <skip />
+ <!-- no translation found for shortcut_notification (1659700220868444568) -->
+ <skip />
+ <!-- no translation found for permission_button_alwaysdeny (3837055994905214848) -->
+ <skip />
+ <!-- no translation found for permission_button_allow (7301874925704148242) -->
+ <skip />
+ <!-- no translation found for permission_button_deny (104712269725153018) -->
+ <skip />
+ <!-- no translation found for shortcut_button_alwaysdeny (4172277731325126605) -->
+ <skip />
+ <string name="shortcut_button_allow">"OK"</string>
+ <string name="shortcut_button_deny">"キャンセル"</string>
+ <string name="settings_button_allow">"適用"</string>
+ <string name="settings_button_deny">"キャンセル"</string>
+ <string name="filepicker_button_allow">"OK"</string>
+ <string name="filepicker_button_deny">"キャンセル"</string>
+ <string name="filepicker_path">"パス:"</string>
+ <string name="filepicker_no_files_selected">"ファイルが選択されていません"</string>
+ <string name="filepicker_one_file_selected">"1ファイルが選択されています"</string>
+ <string name="filepicker_some_files_selected">"ファイルが選択されています"</string>
+ <string name="remove">"削除"</string>
+ <string name="local_storage">"ローカル保存容量"</string>
+ <string name="allowed">"許可"</string>
+ <string name="denied">"拒否されました"</string>
+ <string name="unrecognized_dialog_message">"不明な種類のダイアログです"</string>
+ <string name="default_button">"OK"</string>
+ <string name="zoom_overview_button_text">"全体表示"</string>
+</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
new file mode 100644
index 00000000..90ba10a4
--- /dev/null
+++ b/res/values-ko/strings.xml
@@ -0,0 +1,334 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name">"브라우저"</string>
+ <string name="tab_bookmarks">"북마크"</string>
+ <string name="tab_most_visited">"최다 방문 웹사이트"</string>
+ <string name="tab_history">"기록"</string>
+ <string name="added_to_bookmarks">"북마크에 추가되었습니다."</string>
+ <string name="removed_from_bookmarks">"북마크에서 삭제"</string>
+ <string name="sign_in_to">"<xliff:g id="HOSTNAME">%s1</xliff:g> \'<xliff:g id="REALM">%s2</xliff:g>\'에 로그인"</string>
+ <string name="username">"이름"</string>
+ <string name="password">"비밀번호"</string>
+ <string name="action">"로그인"</string>
+ <string name="cancel">"취소"</string>
+ <string name="ok">"확인"</string>
+ <string name="matches_found">" 개 일치"</string>
+ <string name="zero">"없음"</string>
+ <string name="page_info">"페이지 정보"</string>
+ <string name="page_info_view">"페이지 정보 보기"</string>
+ <string name="page_info_address">"주소:"</string>
+ <string name="ssl_warnings_header">"사이트의 보안 인증서에 문제가 있습니다."</string>
+ <string name="ssl_continue">"계속"</string>
+ <string name="security_warning">"보안 경고"</string>
+ <string name="view_certificate">"인증서 보기"</string>
+ <string name="ssl_untrusted">"신뢰된 인증기관에서 발급된 인증서가 아닙니다."</string>
+ <string name="ssl_mismatch">"사이트의 이름이 인증서에 있는 이름과 일치하지 않습니다."</string>
+ <string name="ssl_expired">"만료된 인증서입니다."</string>
+ <string name="ssl_not_yet_valid">"아직 유효하지 않은 인증서입니다."</string>
+ <string name="ssl_certificate">"보안 인증서"</string>
+ <string name="ssl_certificate_is_valid">"유효한 인증서입니다."</string>
+ <string name="issued_to">"발급 대상:"</string>
+ <string name="common_name">"일반 이름:"</string>
+ <string name="org_name">"조직:"</string>
+ <string name="org_unit">"조직 구성 단위:"</string>
+ <string name="issued_by">"발급자:"</string>
+ <string name="validity_period">"유효성:"</string>
+ <string name="issued_on">"발급일:"</string>
+ <string name="expires_on">"만료일:"</string>
+ <string name="retrieving_creds_dlg_msg">"로그인 세부정보를 가져오는 중..."</string>
+ <string name="close">"닫기"</string>
+ <string name="close_window">"창이 닫힙니다."</string>
+ <string name="stopping">"중지하는 중..."</string>
+ <string name="stop">"중지"</string>
+ <string name="reload">"새로고침"</string>
+ <string name="forward">"앞으로"</string>
+ <string name="save">"확인"</string>
+ <string name="do_not_save">"취소"</string>
+ <string name="location">"위치"</string>
+ <string name="name">"이름"</string>
+ <string name="http">"http://"</string>
+ <string name="save_to_bookmarks">"북마크"</string>
+ <string name="edit_bookmark">"북마크 수정"</string>
+ <string name="create_shortcut_bookmark">"홈에 바로가기 추가"</string>
+ <string name="open_bookmark">"열기"</string>
+ <string name="remove_bookmark">"북마크 삭제"</string>
+ <string name="remove_history_item">"기록에서 제거"</string>
+ <string name="bookmark_saved">"북마크에 저장되었습니다."</string>
+ <string name="bookmark_needs_title">"북마크에는 이름이 있어야 합니다."</string>
+ <string name="bookmark_needs_url">"북마크에는 위치가 있어야 합니다."</string>
+ <string name="empty_bookmark">"빈 북마크는 만들 수 없습니다."</string>
+ <string name="bookmark_url_not_valid">"URL이 올바르지 않습니다."</string>
+ <string name="delete_bookmark">"삭제"</string>
+ <string name="bookmark_page">"마지막으로 본 페이지를 북마크 설정"</string>
+ <string name="current_page">"북마크할 페이지 "</string>
+ <string name="delete_bookmark_warning">"\'<xliff:g id="BOOKMARK">%s</xliff:g>\' 북마크가 삭제됩니다."</string>
+ <string name="open_in_new_window">"새 창에서 열기"</string>
+ <string name="new_window">"새 창"</string>
+ <string name="goto_dot">"실행"</string>
+ <string name="find_dot">"검색 페이지"</string>
+ <string name="select_dot">"텍스트 선택"</string>
+ <string name="view_tabs">"창 세부정보"</string>
+ <string name="view_tabs_condensed">"Windows"</string>
+ <string name="tab_picker_title">"현재 창"</string>
+ <string name="tab_picker_view_tab">"보기"</string>
+ <string name="tab_picker_new_tab">"새 창"</string>
+ <string name="tab_picker_remove_tab">"닫기"</string>
+ <string name="tab_picker_bookmark">"북마크"</string>
+ <string name="tab_picker_send_url">"링크 공유"</string>
+ <string name="bookmarks">"북마크"</string>
+ <string name="shortcut_bookmark">"북마크"</string>
+ <string name="history">"기록"</string>
+ <string name="menu_view_download">"다운로드"</string>
+ <string name="share_page">"페이지 공유"</string>
+ <string name="contextmenu_openlink">"열기"</string>
+ <string name="contextmenu_openlink_newwindow">"새 창에서 열기"</string>
+ <string name="contextmenu_bookmark_thislink">"북마크 링크"</string>
+ <string name="contextmenu_savelink">"링크 저장"</string>
+ <string name="contextmenu_sharelink">"링크 공유"</string>
+ <string name="contextmenu_copy">"복사"</string>
+ <string name="contextmenu_copylink">"링크 URL 복사"</string>
+ <string name="contextmenu_download_image">"이미지 저장"</string>
+ <string name="contextmenu_view_image">"이미지 보기"</string>
+ <string name="contextmenu_dial_dot">"전화 걸기..."</string>
+ <string name="contextmenu_add_contact">"연락처 추가"</string>
+ <string name="contextmenu_send_mail">"이메일 보내기"</string>
+ <string name="contextmenu_map">"지도"</string>
+ <string name="clear">"지우기"</string>
+ <string name="replace">"기존 URL"</string>
+ <string name="browser_bookmarks_page_bookmarks_text">"북마크"</string>
+ <string name="menu_preferences">"설정"</string>
+ <string name="pref_content_title">"페이지 내용 설정"</string>
+ <string name="pref_content_load_images">"이미지 로드"</string>
+ <string name="pref_content_load_images_summary">"웹페이지에 이미지 표시"</string>
+ <string name="pref_content_block_popups">"팝업 창 차단"</string>
+ <string name="pref_content_javascript">"자바스크립트 활성화"</string>
+ <string name="pref_content_open_in_background">"배경에서 열기"</string>
+ <string name="pref_content_open_in_background_summary">"새 창이 현재 창 뒤쪽에 열립니다."</string>
+ <string name="pref_content_homepage">"홈페이지 설정"</string>
+ <string name="pref_content_autofit">"페이지 자동 맞춤"</string>
+ <string name="pref_content_autofit_summary">"화면에 맞게 웹페이지의 형식 조정"</string>
+ <string name="pref_privacy_title">"개인정보보호 설정"</string>
+ <string name="pref_privacy_clear_cache">"캐시 지우기"</string>
+ <string name="pref_privacy_clear_cache_summary">"캐시된 모든 페이지 콘텐츠 삭제"</string>
+ <string name="pref_privacy_clear_cache_dlg">"캐시가 지워집니다."</string>
+ <string name="pref_privacy_clear_cookies">"모든 쿠키 데이터 지우기"</string>
+ <string name="pref_privacy_clear_cookies_summary">"모든 브라우저 쿠키 지우기"</string>
+ <string name="pref_privacy_clear_cookies_dlg">"모든 쿠키가 지워집니다."</string>
+ <string name="pref_privacy_clear_history">"기록 지우기"</string>
+ <string name="pref_privacy_clear_history_summary">"브라우저 탐색 기록 지우기"</string>
+ <string name="pref_privacy_clear_history_dlg">"브라우저 탐색 기록이 지워집니다."</string>
+ <string name="pref_privacy_clear_form_data">"양식 데이터 지우기"</string>
+ <string name="pref_privacy_clear_form_data_summary">"저장된 양식 데이터 모두 지우기"</string>
+ <string name="pref_privacy_clear_form_data_dlg">"저장된 양식 데이터가 모두 지워집니다."</string>
+ <string name="pref_privacy_clear_passwords">"비밀번호 지우기"</string>
+ <string name="pref_privacy_clear_passwords_summary">"저장된 비밀번호 모두 지우기"</string>
+ <string name="pref_privacy_clear_passwords_dlg">"저장된 비밀번호가 모두 지워집니다."</string>
+ <string name="pref_security_title">"보안 설정"</string>
+ <string name="pref_security_remember_passwords">"비밀번호 기억"</string>
+ <string name="pref_security_remember_passwords_summary">"웹사이트의 사용자 이름과 비밀번호 저장"</string>
+ <string name="pref_security_save_form_data">"양식 데이터 기억"</string>
+ <string name="pref_security_save_form_data_summary">"나중에 사용할 수 있도록 양식에 입력한 데이터 기억"</string>
+ <string name="pref_security_show_security_warning">"보안 경고 표시"</string>
+ <string name="pref_security_show_security_warning_summary">"사이트의 보안에 문제가 있는 경우 경고 표시"</string>
+ <string name="pref_security_accept_cookies">"쿠키 허용"</string>
+ <string name="pref_security_accept_cookies_summary">"사이트에서 \'쿠키\' 데이터를 저장하고 읽을 수 있도록 허용"</string>
+ <string name="pref_text_size">"텍스트 크기 설정"</string>
+ <string-array name="pref_text_size_choices">
+ <item>"매우 작게"</item>
+ <item>"작게"</item>
+ <item>"보통"</item>
+ <item>"크게"</item>
+ <item>"매우 크게"</item>
+ </string-array>
+ <string name="pref_text_size_dialogtitle">"텍스트 크기"</string>
+ <string name="pref_extras_title">"고급 설정"</string>
+ <string name="pref_extras_gears_enable">"Gears 활성화"</string>
+ <string name="pref_extras_gears_enable_summary">"브라우저 기능을 확장하는 응용프로그램"</string>
+ <string name="pref_extras_gears_settings">"Gears 설정"</string>
+ <string name="pref_plugin_installed">"플러그인 목록"</string>
+ <string name="pref_plugin_installed_empty_list">"설치된 플러그인이 없습니다."</string>
+ <string name="pref_extras_gears_settings_summary">"브라우저 기능을 확장하는 응용프로그램"</string>
+ <string name="pref_extras_reset_default">"기본값으로 재설정"</string>
+ <string name="pref_extras_reset_default_summary">"모든 브라우저 데이터를 지우고 모든 설정을 기본값으로 재설정"</string>
+ <string name="pref_extras_reset_default_dlg">"모든 브라우저 데이터가 지워지고 설정이 기본값으로 돌아갑니다."</string>
+ <string name="pref_extras_reset_default_dlg_title">"기본값으로 재설정"</string>
+ <string name="pref_development_title">"디버그"</string>
+ <string name="pref_development_viewport">"Use wide viewport"</string>
+ <string name="pref_development_single_column_rendering">"Single column rendering"</string>
+ <string name="pref_development_normal_rendering">"Normal rendering"</string>
+ <string name="pref_development_trace">"Enable tracing"</string>
+ <string name="pref_development_nav_dump">"Enable nav cache dump"</string>
+ <string name="pref_development_search_url">"검색 URL 설정"</string>
+ <string name="pref_development_uastring">"UAString"</string>
+ <string-array name="pref_development_ua_choices">
+ <item>"Android"</item>
+ <item>"Desktop"</item>
+ <item>"iPhone"</item>
+ </string-array>
+ <string-array name="pref_development_ua_values">
+ <item>"0"</item>
+ <item>"1"</item>
+ <item>"2"</item>
+ </string-array>
+ <string name="pref_default_text_encoding">"텍스트 인코딩 설정"</string>
+ <string-array name="pref_default_text_encoding_choices">
+ <item>"라틴어-1(ISO-8859-1)"</item>
+ <item>"유니코드(UTF-8)"</item>
+ <item>"일본어(ISO-2022-JP)"</item>
+ <item>"일본어(SHIFT_JIS)"</item>
+ <item>"일본어(EUC-JP)"</item>
+ </string-array>
+ <string name="pref_default_text_encoding_dialogtitle">"텍스트 인코딩"</string>
+ <string name="pref_default_text_encoding_default">"라틴어-1"</string>
+ <string name="browserFrameRedirect">"리디렉션"</string>
+ <string name="browserFrame307Post">"웹페이지가 리디렉션됩니다. 입력한 양식 데이터를 새 위치로 다시 보내시겠습니까?"</string>
+ <string name="browserFrameNetworkErrorLabel">"데이터 연결 문제"</string>
+ <string name="browserFrameFileErrorLabel">"파일 문제 발생"</string>
+ <string name="browserFrameFormResubmitLabel">"확인"</string>
+ <string name="browserFrameFormResubmitMessage">"보려는 페이지에 이미 제출된 데이터(\'POSTDATA\')가 포함되어 있습니다. 데이터를 다시 보내면 페이지의 양식에서 수행한 작업(예: 검색 또는 온라인 구매)이 반복됩니다."</string>
+ <string name="loadSuspendedTitle">"네트워크 연결 없음"</string>
+ <string name="loadSuspended">"연결이 복원된 후에 페이지 로드가 계속됩니다."</string>
+ <string name="clear_history">"기록 지우기"</string>
+ <string name="browser_history">"최근에 방문한 페이지"</string>
+ <string name="empty_history">"브라우저 기록이 비어 있습니다."</string>
+ <string name="add_new_bookmark">"북마크에 추가..."</string>
+ <string name="no_database">"데이터베이스 없음"</string>
+ <string name="search_hint">"웹 주소 입력"</string>
+ <string name="search_button_text">"이동"</string>
+ <string name="attention">"주의"</string>
+ <string name="popup_window_attempt">"사이트에서 팝업 창을 열려고 합니다."</string>
+ <string name="allow">"허용"</string>
+ <string name="block">"차단"</string>
+ <string name="too_many_windows_dialog_title">"창 개수 제한에 도달했습니다."</string>
+ <string name="too_many_windows_dialog_message">"이미 최대 개수의 창을 열었기 때문에 새 창을 열 수 없습니다."</string>
+ <string name="too_many_subwindows_dialog_title">"팝업이 이미 열려 있습니다."</string>
+ <string name="too_many_subwindows_dialog_message">"팝업 창은 하나만 열 수 있기 때문에 새 팝업 창을 열 수 없습니다."</string>
+ <string name="download_title">"다운로드 기록"</string>
+ <string name="download_unknown_filename">"&lt;알 수 없음&gt;"</string>
+ <string name="download_menu_open">"열기"</string>
+ <string name="download_menu_clear">"목록에서 지우기"</string>
+ <string name="download_menu_cancel">"다운로드 취소"</string>
+ <string name="download_menu_cancel_all">"모든 다운로드 취소"</string>
+ <string name="download_menu_clear_all">"목록 지우기"</string>
+ <string name="download_clear_dlg_title">"지우기"</string>
+ <string name="download_clear_dlg_msg">"목록에서 모든 항목이 지워지고 브라우저 캐시에서 제거됩니다."</string>
+ <string name="download_cancel_dlg_title">"다운로드 취소"</string>
+ <string name="download_cancel_dlg_msg">"<xliff:g id="DOWNLOAD_COUNT">%d</xliff:g>개의 다운로드가 모두 취소되고 다운로드 기록에서 지워집니다."</string>
+ <string name="download_file_error_dlg_title">"공간 부족"</string>
+ <string name="download_file_error_dlg_msg">"<xliff:g id="FILENAME">%s</xliff:g>을(를) 다운로드하지 못했습니다."\n"전화기에서 여유 공간을 늘린 후에 다시 시도하세요."</string>
+ <string name="download_failed_generic_dlg_title">"다운로드 실패"</string>
+ <string name="download_no_sdcard_dlg_title">"SD 카드 없음"</string>
+ <string name="download_no_sdcard_dlg_msg">"<xliff:g id="FILENAME">%s</xliff:g>을(를) 다운로드하려면 SD 카드가 필요합니다."</string>
+ <string name="download_sdcard_busy_dlg_title">"SD 카드를 사용할 수 없음"</string>
+ <!-- no translation found for download_sdcard_busy_dlg_msg (3473883538192835204) -->
+ <skip />
+ <string name="download_no_application">"파일을 열 수 있는 응용프로그램이 없습니다."</string>
+ <string name="retry">"다시 시도"</string>
+ <string name="no_downloads">"다운로드 기록이 비어 있습니다."</string>
+ <string name="download_error">"다운로드 실패"</string>
+ <string name="download_success">"<xliff:g id="FILE">%s</xliff:g> 다운로드 완료"</string>
+ <string name="download_running">"다운로드 중..."</string>
+ <string name="download_pending">"다운로드 시작 중..."</string>
+ <string name="download_pending_network">"데이터 연결을 기다리는 중..."</string>
+ <string name="download_running_paused">"데이터 연결을 기다리는 중..."</string>
+ <string name="download_canceled">"다운로드가 취소되었습니다."</string>
+ <string name="download_not_acceptable">"다운로드할 수 없습니다. 전화기에서 지원하지 않는 콘텐츠입니다."</string>
+ <string name="download_file_error">"공간이 부족하여 다운로드를 완료할 수 없습니다."</string>
+ <string name="download_length_required">"항목의 크기를 확인할 수 없으므로 다운로드할 수 없습니다."</string>
+ <string name="download_precondition_failed">"다운로드 중단됨. 다시 시작할 수 없습니다."</string>
+ <string name="activity_instrumentation_test_runner">"브라우저 테스트 실행기"</string>
+ <string name="search_google">"Google 검색"</string>
+ <string name="permlab_readHistoryBookmarks">"브라우저의 기록 및 북마크 읽기"</string>
+ <string name="permdesc_readHistoryBookmarks">"응용프로그램이 브라우저에서 방문한 모든 URL과 브라우저의 북마크를 모두 읽을 수 있습니다."</string>
+ <string name="permlab_writeHistoryBookmarks">"브라우저의 기록 및 북마크 쓰기"</string>
+ <string name="permdesc_writeHistoryBookmarks">"응용프로그램이 전화기에 저장된 브라우저의 기록 또는 북마크를 수정할 수 있습니다. 악성 응용프로그램은 이 기능을 이용하여 브라우저의 데이터를 지우거나 수정할 수 있습니다."</string>
+ <!-- no translation found for query_data_prompt (5474381240981604223) -->
+ <skip />
+ <!-- no translation found for query_data_message (8789381063185445197) -->
+ <skip />
+ <!-- no translation found for location_prompt (226262202057302423) -->
+ <skip />
+ <!-- no translation found for location_message (1729456751935683242) -->
+ <skip />
+ <!-- no translation found for shortcut_prompt (437193299088893596) -->
+ <skip />
+ <!-- no translation found for shortcut_message (4793042709293755892) -->
+ <skip />
+ <string name="settings_message">"아래 표는 Gears를 사용하려고 시도한 각 사이트에 부여한 권한을 보여줍니다."</string>
+ <!-- no translation found for filepicker_message (4929726371602896039) -->
+ <skip />
+ <!-- no translation found for image_message (5450245866521896891) -->
+ <skip />
+ <string name="settings_title">"Gears 설정"</string>
+ <string name="privacy_policy">"위치가 어떻게 사용되는지 보려면 사이트의 개인정보 보호정책을 읽어보세요."</string>
+ <!-- no translation found for settings_storage_title (7089119630457156408) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_on (2733295483569432111) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_off (1338039396087898813) -->
+ <skip />
+ <!-- no translation found for settings_location_title (4953062923509886651) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_on (1496219621849158879) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_off (5216466051810596189) -->
+ <skip />
+ <!-- no translation found for settings_remove_site (1822247070226589958) -->
+ <skip />
+ <!-- no translation found for settings_empty (2008952224378583146) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove_title (5360857296753219635) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove (585560170733446898) -->
+ <skip />
+ <!-- no translation found for storage_notification (6534213782873968255) -->
+ <skip />
+ <!-- no translation found for storage_notification_alwaysdeny (5473208033665783513) -->
+ <skip />
+ <!-- no translation found for location_notification (3495804750470905804) -->
+ <skip />
+ <!-- no translation found for location_notification_alwaysdeny (6184925953783312629) -->
+ <skip />
+ <!-- no translation found for shortcut_notification (1659700220868444568) -->
+ <skip />
+ <!-- no translation found for permission_button_alwaysdeny (3837055994905214848) -->
+ <skip />
+ <!-- no translation found for permission_button_allow (7301874925704148242) -->
+ <skip />
+ <!-- no translation found for permission_button_deny (104712269725153018) -->
+ <skip />
+ <!-- no translation found for shortcut_button_alwaysdeny (4172277731325126605) -->
+ <skip />
+ <string name="shortcut_button_allow">"확인"</string>
+ <string name="shortcut_button_deny">"취소"</string>
+ <string name="settings_button_allow">"적용"</string>
+ <string name="settings_button_deny">"취소"</string>
+ <string name="filepicker_button_allow">"확인"</string>
+ <string name="filepicker_button_deny">"취소"</string>
+ <string name="filepicker_path">"경로:"</string>
+ <string name="filepicker_no_files_selected">"선택한 파일이 없습니다."</string>
+ <string name="filepicker_one_file_selected">"1개 파일 선택됨"</string>
+ <string name="filepicker_some_files_selected">"개 파일 선택됨"</string>
+ <string name="remove">"제거"</string>
+ <string name="local_storage">"로컬 저장공간"</string>
+ <string name="allowed">"허용됨"</string>
+ <string name="denied">"거부됨"</string>
+ <string name="unrecognized_dialog_message">"인식할 수 없는 대화상자 유형"</string>
+ <string name="default_button">"확인"</string>
+ <string name="zoom_overview_button_text">"전체보기"</string>
+</resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
new file mode 100644
index 00000000..d356f7d8
--- /dev/null
+++ b/res/values-nb/strings.xml
@@ -0,0 +1,326 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name">"Nettleser"</string>
+ <string name="tab_bookmarks">"Bokmerker"</string>
+ <string name="tab_most_visited">"Mest besøkt"</string>
+ <string name="tab_history">"Historie"</string>
+ <string name="added_to_bookmarks">"Lagt til bokmerker"</string>
+ <string name="removed_from_bookmarks">"Fjernet fra bokmerker"</string>
+ <string name="sign_in_to">"Logg inn på <xliff:g id="HOSTNAME">%s1</xliff:g> \"<xliff:g id="REALM">%s2</xliff:g>\""</string>
+ <string name="username">"Brukernavn"</string>
+ <string name="password">"Passord"</string>
+ <string name="action">"Logg inn"</string>
+ <string name="cancel">"Avbryt"</string>
+ <string name="ok">"OK"</string>
+ <string name="matches_found">" treff"</string>
+ <string name="zero">"Ingen"</string>
+ <string name="page_info">"Sideinformasjon"</string>
+ <string name="page_info_view">"Vis sideinformasjon"</string>
+ <string name="page_info_address">"Adresse:"</string>
+ <string name="ssl_warnings_header">"Det er problemer med sertifikatet for denne siden."</string>
+ <string name="ssl_continue">"Fortsett"</string>
+ <string name="security_warning">"Sikkerhetsadvarsel"</string>
+ <string name="view_certificate">"Se på sertifikat"</string>
+ <string name="ssl_untrusted">"Dette sertifikatet er ikke fra en autoritet du stoler på."</string>
+ <string name="ssl_mismatch">"Navnet på siden stemmer ikke med navnet på sertifikatet."</string>
+ <string name="ssl_expired">"Sertifikatet har gått ut."</string>
+ <string name="ssl_not_yet_valid">"Sertifikatet er ikke gyldig ennå."</string>
+ <string name="ssl_certificate">"Sikkerhetssertifikat"</string>
+ <string name="ssl_certificate_is_valid">"Sertifikatet er gyldig."</string>
+ <string name="issued_to">"Utstedt til:"</string>
+ <string name="common_name">"Navn:"</string>
+ <string name="org_name">"Organisasjon:"</string>
+ <string name="org_unit">"Organisasjonsenhet:"</string>
+ <string name="issued_by">"Utstedt av:"</string>
+ <string name="validity_period">"Gyldighet:"</string>
+ <string name="issued_on">"Utstedt:"</string>
+ <string name="expires_on">"Går ut:"</string>
+ <string name="retrieving_creds_dlg_msg">"Henter innloggingsinformasjon…"</string>
+ <string name="close">"Lukk"</string>
+ <string name="close_window">"Dette vinduet vil bli lukket."</string>
+ <string name="stopping">"Avbryter…"</string>
+ <string name="stop">"Avbryt"</string>
+ <string name="reload">"Last på nytt"</string>
+ <string name="forward">"Forover"</string>
+ <string name="save">"OK"</string>
+ <string name="do_not_save">"Avbryt"</string>
+ <string name="location">"Adresse"</string>
+ <string name="name">"Navn"</string>
+ <string name="http">"http://"</string>
+ <string name="save_to_bookmarks">"Bokmerke"</string>
+ <string name="edit_bookmark">"Rediger bokmerke"</string>
+ <string name="create_shortcut_bookmark">"Lag snarvei på skrivebordet"</string>
+ <string name="open_bookmark">"Åpne"</string>
+ <string name="remove_bookmark">"Slett bokmerke"</string>
+ <string name="remove_history_item">"Fjern fra loggen"</string>
+ <string name="bookmark_saved">"Bokmerke lagret."</string>
+ <string name="bookmark_needs_title">"Bokmerket må ha et navn."</string>
+ <string name="bookmark_needs_url">"Bokmerket må ha en adresse."</string>
+ <string name="empty_bookmark">"Kan ikke lage tomt bokmerke."</string>
+ <string name="bookmark_url_not_valid">"Adressen er ikke gyldig."</string>
+ <string name="delete_bookmark">"Slett"</string>
+ <string name="bookmark_page">"Legg til bokmerke for sist sette side"</string>
+ <string name="current_page">"Gjeldende side: "</string>
+ <string name="delete_bookmark_warning">"Bokmerket \"<xliff:g id="BOOKMARK">%s</xliff:g>\" vil bli slettet."</string>
+ <string name="open_in_new_window">"Åpne i nytt vindu"</string>
+ <string name="new_window">"Nytt vindu"</string>
+ <string name="goto_dot">"Gå til URL"</string>
+ <string name="find_dot">"Finn på siden"</string>
+ <string name="select_dot">"Velg tekst"</string>
+ <string name="view_tabs">"Vindusoversikt"</string>
+ <string name="view_tabs_condensed">"Vinduer"</string>
+ <string name="tab_picker_title">"Gjeldende vinduer"</string>
+ <string name="tab_picker_view_tab">"Vis"</string>
+ <string name="tab_picker_new_tab">"Nytt vindu"</string>
+ <string name="tab_picker_remove_tab">"Lukk"</string>
+ <string name="tab_picker_bookmark">"Legg til bokmerke"</string>
+ <string name="tab_picker_send_url">"Del lenke"</string>
+ <string name="bookmarks">"Bokmerker"</string>
+ <string name="shortcut_bookmark">"Bokmerker"</string>
+ <string name="history">"Logg"</string>
+ <string name="menu_view_download">"Nedlastinger"</string>
+ <string name="share_page">"Del side"</string>
+ <string name="contextmenu_openlink">"Åpne"</string>
+ <string name="contextmenu_openlink_newwindow">"Åpne i nytt vindu"</string>
+ <string name="contextmenu_bookmark_thislink">"Legg til lenke i bokmerker"</string>
+ <string name="contextmenu_savelink">"Lagre lenke"</string>
+ <string name="contextmenu_sharelink">"Del lenke"</string>
+ <string name="contextmenu_copy">"Kopier"</string>
+ <string name="contextmenu_copylink">"Kopier lenke-URL"</string>
+ <string name="contextmenu_download_image">"Lagre bilde"</string>
+ <string name="contextmenu_view_image">"Vis bilde"</string>
+ <string name="contextmenu_dial_dot">"Ring…"</string>
+ <string name="contextmenu_add_contact">"Legg til kontakt"</string>
+ <string name="contextmenu_send_mail">"Send e-post"</string>
+ <string name="contextmenu_map">"Kart"</string>
+ <string name="clear">"Nullstill"</string>
+ <string name="replace">"Erstatt"</string>
+ <string name="browser_bookmarks_page_bookmarks_text">"Bokmerker"</string>
+ <string name="menu_preferences">"Innstillinger"</string>
+ <string name="pref_content_title">"Sideinnhold"</string>
+ <string name="pref_content_load_images">"Last bilder"</string>
+ <string name="pref_content_load_images_summary">"Vis bilder på nettsider"</string>
+ <string name="pref_content_block_popups">"Blokker sprettoppvinduer"</string>
+ <string name="pref_content_javascript">"Slå på JavaScript"</string>
+ <string name="pref_content_open_in_background">"Åpne i bakgrunnen"</string>
+ <string name="pref_content_open_in_background_summary">"La vinduer åpnes bak dette"</string>
+ <string name="pref_content_homepage">"Sett hjemmeside"</string>
+ <string name="pref_content_autofit">"Tilpass sider"</string>
+ <string name="pref_content_autofit_summary">"Tilpass nettsider så de passer på skjermen"</string>
+ <string name="pref_privacy_title">"Personvernsinnstillinger"</string>
+ <string name="pref_privacy_clear_cache">"Nullstill hurtiglager"</string>
+ <string name="pref_privacy_clear_cache_summary">"Slett alt hurtiglagret nettinnhold"</string>
+ <string name="pref_privacy_clear_cache_dlg">"Hurtiglageret vil nullstilles."</string>
+ <string name="pref_privacy_clear_cookies">"Fjern alle informasjonskapsler"</string>
+ <string name="pref_privacy_clear_cookies_summary">"Fjern alle informasjonskapsler lagret i nettleseren"</string>
+ <string name="pref_privacy_clear_cookies_dlg">"Alle informasjonskapsler vil bli slettet."</string>
+ <string name="pref_privacy_clear_history">"Slett loggen"</string>
+ <string name="pref_privacy_clear_history_summary">"Slett navigeringsloggen"</string>
+ <string name="pref_privacy_clear_history_dlg">"Nettleserens navigeringslogg vil bli slettet."</string>
+ <string name="pref_privacy_clear_form_data">"Slett skjemadata"</string>
+ <string name="pref_privacy_clear_form_data_summary">"Slett alt lagret skjemadata"</string>
+ <string name="pref_privacy_clear_form_data_dlg">"Alt lagret skjemadata vil bli slettet."</string>
+ <string name="pref_privacy_clear_passwords">"Slett passord"</string>
+ <string name="pref_privacy_clear_passwords_summary">"Slett alle lagrede passord"</string>
+ <string name="pref_privacy_clear_passwords_dlg">"Alle lagrede passord vil bli slettet."</string>
+ <string name="pref_security_title">"Sikkerhetsinnstillinger"</string>
+ <string name="pref_security_remember_passwords">"Husk passord"</string>
+ <string name="pref_security_remember_passwords_summary">"Lagre brukernavn og passord for nettsider"</string>
+ <string name="pref_security_save_form_data">"Husk skjemadata"</string>
+ <string name="pref_security_save_form_data_summary">"Husk data skrevet inn i skjemaer til senere"</string>
+ <string name="pref_security_show_security_warning">"Vis sikkerhetsadvarsler"</string>
+ <string name="pref_security_show_security_warning_summary">"Vis advarsel hvis det er et problem med sikkerheten på en side"</string>
+ <string name="pref_security_accept_cookies">"Godta informasjonskapsler"</string>
+ <string name="pref_security_accept_cookies_summary">"Tillat nettsteder å lagre og lese informasjonskapsler"</string>
+ <string name="pref_text_size">"Sett tekststørrelse"</string>
+ <string-array name="pref_text_size_choices">
+ <item>"Bitteliten"</item>
+ <item>"Liten"</item>
+ <item>"Vanlig"</item>
+ <item>"Stor"</item>
+ <item>"Kjempestor"</item>
+ </string-array>
+ <string name="pref_text_size_dialogtitle">"Tekststørrelse"</string>
+ <string name="pref_extras_title">"Avanserte innstillinger"</string>
+ <string name="pref_extras_gears_enable">"Aktiver Gears"</string>
+ <string name="pref_extras_gears_enable_summary">"Applikasjoner som utvider nettleserfunksjonaliteten"</string>
+ <string name="pref_extras_gears_settings">"Gears-innstillinger"</string>
+ <string name="pref_plugin_installed">"Liste over utvidelser"</string>
+ <string name="pref_plugin_installed_empty_list">"Ingen installerte utvidelser."</string>
+ <string name="pref_extras_gears_settings_summary">"Applikasjoner som utvider nettleserfunksjonaliteten"</string>
+ <string name="pref_extras_reset_default">"Nullstill"</string>
+ <string name="pref_extras_reset_default_summary">"Fjern alle data fra nettleseren og nullstill alle innstillinger"</string>
+ <string name="pref_extras_reset_default_dlg">"Alle data vil bli fjernet fra nettleseren, og alle innstillinger vil bli nullstilt."</string>
+ <string name="pref_extras_reset_default_dlg_title">"Nullstill"</string>
+ <string name="pref_development_title">"Avlusing"</string>
+ <string name="pref_development_viewport">"Use wide viewport"</string>
+ <string name="pref_development_single_column_rendering">"Single column rendering"</string>
+ <string name="pref_development_normal_rendering">"Normal rendering"</string>
+ <string name="pref_development_trace">"Enable tracing"</string>
+ <string name="pref_development_nav_dump">"Enable nav cache dump"</string>
+ <string name="pref_development_search_url">"Set search URL"</string>
+ <string name="pref_development_uastring">"UAString"</string>
+ <string-array name="pref_development_ua_choices">
+ <item>"Android"</item>
+ <item>"Desktop"</item>
+ <item>"iPhone"</item>
+ </string-array>
+ <string-array name="pref_development_ua_values">
+ <item>"0"</item>
+ <item>"1"</item>
+ <item>"2"</item>
+ </string-array>
+ <string name="pref_default_text_encoding">"Velg tekstkoding"</string>
+ <string-array name="pref_default_text_encoding_choices">
+ <item>"Latin-1 (ISO-8859-1)"</item>
+ <item>"Unicode (UTF-8)"</item>
+ <item>"Japansk (ISO-2022-JP)"</item>
+ <item>"Japansk (SHIFT_JIS)"</item>
+ <item>"Japansk (EUC-JP)"</item>
+ </string-array>
+ <string name="pref_default_text_encoding_dialogtitle">"Tekstkoding"</string>
+ <string name="pref_default_text_encoding_default">"Latin-1"</string>
+ <string name="browserFrameRedirect">"Videresending"</string>
+ <string name="browserFrame307Post">"Denne nettsiden blir videresendt. Send skjemadataene videre til den nye siden?"</string>
+ <string name="browserFrameNetworkErrorLabel">"Tilkoblingsproblem"</string>
+ <string name="browserFrameFileErrorLabel">"Problem med fil"</string>
+ <string name="browserFrameFormResubmitLabel">"Bekreft"</string>
+ <string name="browserFrameFormResubmitMessage">"Siden du prøver å se, inneholder data som allerede er blitt sendt inn (\"POSTDATA\"). Hvis du sender dataene på nytt, kan det skjemaet på siden gjorde bli gjort på nytt."</string>
+ <string name="loadSuspendedTitle">"Ingen nettverkstilkobling"</string>
+ <string name="loadSuspended">"Siden vil fortsette å laste etter at tilkoblingen er blitt gjenopprettet."</string>
+ <string name="clear_history">"Slett logg"</string>
+ <string name="browser_history">"Nylig besøkte sider"</string>
+ <string name="empty_history">"Nettleserens logg er tom."</string>
+ <string name="add_new_bookmark">"Nytt bokmerke"</string>
+ <string name="no_database">"Ingen database!"</string>
+ <string name="search_hint">"Skriv inn nettadresse"</string>
+ <string name="search_button_text">"Gå"</string>
+ <string name="attention">"Obs"</string>
+ <string name="popup_window_attempt">"Denne siden prøver å åpne et sprettoppvindu."</string>
+ <string name="allow">"Tillat"</string>
+ <string name="block">"Blokker"</string>
+ <string name="too_many_windows_dialog_title">"Vindusgrense nådd"</string>
+ <string name="too_many_windows_dialog_message">"Kunne ikke åpne et nytt vindu fordi du allerede har maksialt antall åpnet."</string>
+ <string name="too_many_subwindows_dialog_title">"Sprettoppvindu allerede åpnet"</string>
+ <string name="too_many_subwindows_dialog_message">"Kunne ikke åpne et nytt sprettoppvindu, fordi det bare kan være ett åpent av gangen."</string>
+ <string name="download_title">"Nedlastingslogg"</string>
+ <string name="download_unknown_filename">"&lt;Ukjent&gt;"</string>
+ <string name="download_menu_open">"Åpne"</string>
+ <string name="download_menu_clear">"Fjern fra listen"</string>
+ <string name="download_menu_cancel">"Avbryt nedlasting"</string>
+ <string name="download_menu_cancel_all">"Avbryt alle nedlastinger"</string>
+ <string name="download_menu_clear_all">"Tøm listen"</string>
+ <string name="download_clear_dlg_title">"Tøm"</string>
+ <string name="download_clear_dlg_msg">"Alle nedlastinger vil bli fjernet fra listen samt fjernet fra nettleserens hurtiglager."</string>
+ <string name="download_cancel_dlg_title">"Avbryt nedlastinger"</string>
+ <string name="download_cancel_dlg_msg">"Alle <xliff:g id="DOWNLOAD_COUNT">%d</xliff:g> nedlastinger vil bli avbrutt og fjernet fra nedlastingsloggen."</string>
+ <string name="download_file_error_dlg_title">"Tom for plass"</string>
+ <string name="download_file_error_dlg_msg">"Kunne ikke laste ned <xliff:g id="FILENAME">%s</xliff:g>."\n"Frigjør plass på telefonen og prøv igjen."</string>
+ <string name="download_failed_generic_dlg_title">"Feil ved nedlasting"</string>
+ <string name="download_no_sdcard_dlg_title">"Mangler minnekort"</string>
+ <string name="download_no_sdcard_dlg_msg">"Du trenger et minnekort for å laste ned <xliff:g id="FILENAME">%s</xliff:g>."</string>
+ <string name="download_sdcard_busy_dlg_title">"Minnekort utilgjengelig"</string>
+ <!-- no translation found for download_sdcard_busy_dlg_msg (3473883538192835204) -->
+ <skip />
+ <string name="download_no_application">"Fant ingen applikasjon som kunne åpne denne filen."</string>
+ <string name="retry">"Prøv igjen"</string>
+ <string name="no_downloads">"Nedlastingsloggen er tom."</string>
+ <string name="download_error">"Feil ved nedlasting."</string>
+ <string name="download_success">"<xliff:g id="FILE">%s</xliff:g> Nedlasting ferdig."</string>
+ <string name="download_running">"Laster ned…"</string>
+ <string name="download_pending">"Starter nedlasting…"</string>
+ <string name="download_pending_network">"Venter på datatilkobling…"</string>
+ <string name="download_running_paused">"Venter på datatilkobling…"</string>
+ <string name="download_canceled">"Nedlastingen ble avbrutt."</string>
+ <string name="download_not_acceptable">"Kan ikke laste ned. Innholdet er ikke støttet av telefonen."</string>
+ <string name="download_file_error">"Kan ikke fullføre nedlastingen. Det er ikke nok plass."</string>
+ <string name="download_length_required">"Kan ikke laste ned. Klarte ikke bestemme størrelsen på filen."</string>
+ <string name="download_precondition_failed">"Nedlastingen ble avbrutt. Den kan ikke fortsettes."</string>
+ <string name="activity_instrumentation_test_runner">"Browser Test Runner"</string>
+ <string name="search_google">"Søk på Google"</string>
+ <string name="permlab_readHistoryBookmarks">"read Browser\'s history and bookmarks"</string>
+ <string name="permdesc_readHistoryBookmarks">"Allows the application to read all the URLs that the Browser has visited, and all of the Browser\'s bookmarks."</string>
+ <string name="permlab_writeHistoryBookmarks">"write Browser\'s history and bookmarks"</string>
+ <string name="permdesc_writeHistoryBookmarks">"Allows an application to modify the Browser\'s history or bookmarks stored on your phone. Malicious applications can use this to erase or modify your Browser\'s data."</string>
+ <!-- no translation found for query_data_prompt (5474381240981604223) -->
+ <skip />
+ <string name="query_data_message">"Nettsiden under ønsker å lagre informasjon på datamaskinen ved hjelp av Gears."</string>
+ <!-- no translation found for location_prompt (226262202057302423) -->
+ <skip />
+ <string name="location_message">"Nettsiden under ønsker å finne ut hvor du er ved hjelp av Gears."</string>
+ <!-- no translation found for shortcut_prompt (437193299088893596) -->
+ <skip />
+ <string name="shortcut_message">"Denne nettsiden ønsker å lage en snarvei på datamaskinen. Ønsker du å tillate dette?"</string>
+ <string name="settings_message">"Tabellen under viser rettighetent du har gitt til hver side som har prøvd å bruke Gears."</string>
+ <string name="filepicker_message">"Gears filplukker"</string>
+ <!-- no translation found for image_message (5450245866521896891) -->
+ <skip />
+ <string name="settings_title">"Gears-innstillinger"</string>
+ <string name="privacy_policy">"Les sidens personvernspolicy for å se hvordan informasjonen vil bli brukt."</string>
+ <!-- no translation found for settings_storage_title (7089119630457156408) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_on (2733295483569432111) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_off (1338039396087898813) -->
+ <skip />
+ <!-- no translation found for settings_location_title (4953062923509886651) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_on (1496219621849158879) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_off (5216466051810596189) -->
+ <skip />
+ <!-- no translation found for settings_remove_site (1822247070226589958) -->
+ <skip />
+ <!-- no translation found for settings_empty (2008952224378583146) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove_title (5360857296753219635) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove (585560170733446898) -->
+ <skip />
+ <!-- no translation found for storage_notification (6534213782873968255) -->
+ <skip />
+ <!-- no translation found for storage_notification_alwaysdeny (5473208033665783513) -->
+ <skip />
+ <!-- no translation found for location_notification (3495804750470905804) -->
+ <skip />
+ <!-- no translation found for location_notification_alwaysdeny (6184925953783312629) -->
+ <skip />
+ <!-- no translation found for shortcut_notification (1659700220868444568) -->
+ <skip />
+ <string name="permission_button_alwaysdeny">"Aldri tillat for denne siden"</string>
+ <string name="permission_button_allow">"Tillat"</string>
+ <string name="permission_button_deny">"Nekt"</string>
+ <string name="shortcut_button_alwaysdeny">"Aldri tillat denne snarveien"</string>
+ <string name="shortcut_button_allow">"OK"</string>
+ <string name="shortcut_button_deny">"Avbryt"</string>
+ <string name="settings_button_allow">"Bruk"</string>
+ <string name="settings_button_deny">"Avbryt"</string>
+ <string name="filepicker_button_allow">"OK"</string>
+ <string name="filepicker_button_deny">"Avbryt"</string>
+ <string name="filepicker_path">"sti:"</string>
+ <string name="filepicker_no_files_selected">"Ingen fil valgt"</string>
+ <string name="filepicker_one_file_selected">"En fil valgt"</string>
+ <string name="filepicker_some_files_selected">"filer valgt"</string>
+ <string name="remove">"Fjern"</string>
+ <string name="local_storage">"Lokal lagring"</string>
+ <string name="allowed">"Tillatt"</string>
+ <string name="denied">"Nektet"</string>
+ <string name="unrecognized_dialog_message">"Ukjent meldingstype"</string>
+ <string name="default_button">"OK"</string>
+ <string name="zoom_overview_button_text">"Oversikt"</string>
+</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
new file mode 100644
index 00000000..7f45a44f
--- /dev/null
+++ b/res/values-nl/strings.xml
@@ -0,0 +1,334 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name">"Browser"</string>
+ <string name="tab_bookmarks">"Bladwijzers"</string>
+ <string name="tab_most_visited">"Meest bezocht"</string>
+ <string name="tab_history">"Geschiedenis"</string>
+ <string name="added_to_bookmarks">"Toegevoegd aan bladwijzers"</string>
+ <string name="removed_from_bookmarks">"Verwijderd uit bladwijzers"</string>
+ <string name="sign_in_to">"Aanmelden bij <xliff:g id="HOSTNAME">%s1</xliff:g> \'<xliff:g id="REALM">%s2</xliff:g>\'"</string>
+ <string name="username">"Naam"</string>
+ <string name="password">"Wachtwoord"</string>
+ <string name="action">"Aanmelden"</string>
+ <string name="cancel">"Annuleren"</string>
+ <string name="ok">"OK"</string>
+ <string name="matches_found">" overeenkomsten"</string>
+ <string name="zero">"Nee"</string>
+ <string name="page_info">"Pagina-informatie"</string>
+ <string name="page_info_view">"Pagina-informatie weergeven"</string>
+ <string name="page_info_address">"Adres:"</string>
+ <string name="ssl_warnings_header">"Er zijn problemen met het beveiligingscertificaat van deze site."</string>
+ <string name="ssl_continue">"Doorgaan"</string>
+ <string name="security_warning">"Beveiligingswaarschuwing"</string>
+ <string name="view_certificate">"Certificaat weergeven"</string>
+ <string name="ssl_untrusted">"Dit is geen certificaat van een vertrouwde autoriteit."</string>
+ <string name="ssl_mismatch">"De naam van deze site komt niet overeen met de naam op het certificaat."</string>
+ <string name="ssl_expired">"Dit certificaat is verlopen."</string>
+ <string name="ssl_not_yet_valid">"Dit certificaat is nog niet geldig."</string>
+ <string name="ssl_certificate">"Beveiligingscertificaat"</string>
+ <string name="ssl_certificate_is_valid">"Dit certificaat is geldig."</string>
+ <string name="issued_to">"Uitgegeven voor:"</string>
+ <string name="common_name">"Algemene naam:"</string>
+ <string name="org_name">"Organisatie:"</string>
+ <string name="org_unit">"Organisatie-eenheid:"</string>
+ <string name="issued_by">"Uitgegeven door:"</string>
+ <string name="validity_period">"Geldigheid:"</string>
+ <string name="issued_on">"Uitgegeven op:"</string>
+ <string name="expires_on">"Verloopt op:"</string>
+ <string name="retrieving_creds_dlg_msg">"Aanmeldingsgegevens ophalen..."</string>
+ <string name="close">"Sluiten"</string>
+ <string name="close_window">"Dit venster wordt gesloten."</string>
+ <string name="stopping">"Stoppen..."</string>
+ <string name="stop">"Stoppen"</string>
+ <string name="reload">"Vernieuwen"</string>
+ <string name="forward">"Volgende"</string>
+ <string name="save">"OK"</string>
+ <string name="do_not_save">"Annuleren"</string>
+ <string name="location">"Locatie"</string>
+ <string name="name">"Naam"</string>
+ <string name="http">"http://"</string>
+ <string name="save_to_bookmarks">"Bladwijzer"</string>
+ <string name="edit_bookmark">"Bladwijzer bewerken"</string>
+ <string name="create_shortcut_bookmark">"Snelkoppeling naar startpagina toevoegen"</string>
+ <string name="open_bookmark">"Openen"</string>
+ <string name="remove_bookmark">"Bladwijzer verwijderen"</string>
+ <string name="remove_history_item">"Uit geschiedenis verwijderen"</string>
+ <string name="bookmark_saved">"Opgeslagen in bladwijzers."</string>
+ <string name="bookmark_needs_title">"Bladwijzer moet een naam hebben."</string>
+ <string name="bookmark_needs_url">"Bladwijzers moeten een locatie hebben."</string>
+ <string name="empty_bookmark">"Kan geen lege bladwijzer maken."</string>
+ <string name="bookmark_url_not_valid">"URL is ongeldig."</string>
+ <string name="delete_bookmark">"Verwijderen"</string>
+ <string name="bookmark_page">"Bladwijzer maken voor de laatst weergegeven pagina"</string>
+ <string name="current_page">"van "</string>
+ <string name="delete_bookmark_warning">"Bladwijzer \'<xliff:g id="BOOKMARK">%s</xliff:g>\' wordt verwijderd."</string>
+ <string name="open_in_new_window">"Openen in een nieuw venster"</string>
+ <string name="new_window">"Nieuw venster"</string>
+ <string name="goto_dot">"Ga"</string>
+ <string name="find_dot">"Zoeken op pagina"</string>
+ <string name="select_dot">"Tekst selecteren"</string>
+ <string name="view_tabs">"Vensteroverzicht"</string>
+ <string name="view_tabs_condensed">"Windows"</string>
+ <string name="tab_picker_title">"Huidige vensters"</string>
+ <string name="tab_picker_view_tab">"Weergeven"</string>
+ <string name="tab_picker_new_tab">"Nieuw venster"</string>
+ <string name="tab_picker_remove_tab">"Sluiten"</string>
+ <string name="tab_picker_bookmark">"Bladwijzer"</string>
+ <string name="tab_picker_send_url">"Link delen"</string>
+ <string name="bookmarks">"Bladwijzers"</string>
+ <string name="shortcut_bookmark">"Bladwijzer"</string>
+ <string name="history">"Geschiedenis"</string>
+ <string name="menu_view_download">"Downloads"</string>
+ <string name="share_page">"Pagina delen"</string>
+ <string name="contextmenu_openlink">"Openen"</string>
+ <string name="contextmenu_openlink_newwindow">"Openen in een nieuw venster"</string>
+ <string name="contextmenu_bookmark_thislink">"Bladwijzer maken van link"</string>
+ <string name="contextmenu_savelink">"Link opslaan"</string>
+ <string name="contextmenu_sharelink">"Link delen"</string>
+ <string name="contextmenu_copy">"Kopiëren"</string>
+ <string name="contextmenu_copylink">"URL van koppeling kopiëren"</string>
+ <string name="contextmenu_download_image">"Afbeelding opslaan"</string>
+ <string name="contextmenu_view_image">"Afbeelding weergeven"</string>
+ <string name="contextmenu_dial_dot">"Bellen..."</string>
+ <string name="contextmenu_add_contact">"Contactpersoon toevoegen"</string>
+ <string name="contextmenu_send_mail">"E-mail verzenden"</string>
+ <string name="contextmenu_map">"Kaart"</string>
+ <string name="clear">"Wissen"</string>
+ <string name="replace">"Vervangen"</string>
+ <string name="browser_bookmarks_page_bookmarks_text">"Bladwijzers"</string>
+ <string name="menu_preferences">"Instellingen"</string>
+ <string name="pref_content_title">"Instellingen voor pagina-inhoud"</string>
+ <string name="pref_content_load_images">"Afbeeldingen laden"</string>
+ <string name="pref_content_load_images_summary">"Afbeeldingen op webpagina\'s weergeven"</string>
+ <string name="pref_content_block_popups">"Pop-upvensters blokkeren"</string>
+ <string name="pref_content_javascript">"JavaScript inschakelen"</string>
+ <string name="pref_content_open_in_background">"Openen op de achtergrond"</string>
+ <string name="pref_content_open_in_background_summary">"Nieuwe vensters worden achter het huidige venster weergegeven."</string>
+ <string name="pref_content_homepage">"Startpagina instellen"</string>
+ <string name="pref_content_autofit">"Pagina\'s automatisch laten passen"</string>
+ <string name="pref_content_autofit_summary">"Webpagina\'s zo indelen dat deze op het scherm passen"</string>
+ <string name="pref_privacy_title">"Privacyinstellingen"</string>
+ <string name="pref_privacy_clear_cache">"Cache wissen"</string>
+ <string name="pref_privacy_clear_cache_summary">"Alle pagina-inhoud in cache verwijderen"</string>
+ <string name="pref_privacy_clear_cache_dlg">"De cache wordt gewist."</string>
+ <string name="pref_privacy_clear_cookies">"Alle cookiegegevens wissen"</string>
+ <string name="pref_privacy_clear_cookies_summary">"Alle browsercookies wissen"</string>
+ <string name="pref_privacy_clear_cookies_dlg">"Alle cookies worden gewist."</string>
+ <string name="pref_privacy_clear_history">"Geschiedenis wissen"</string>
+ <string name="pref_privacy_clear_history_summary">"Navigatiegeschiedenis van de browser wissen"</string>
+ <string name="pref_privacy_clear_history_dlg">"De navigatiegeschiedenis van de browser wordt gewist."</string>
+ <string name="pref_privacy_clear_form_data">"Formuliergegevens wissen"</string>
+ <string name="pref_privacy_clear_form_data_summary">"Alle opgeslagen formuliergegevens wissen"</string>
+ <string name="pref_privacy_clear_form_data_dlg">"Alle opgeslagen formuliergegevens worden gewist."</string>
+ <string name="pref_privacy_clear_passwords">"Wachtwoorden wissen"</string>
+ <string name="pref_privacy_clear_passwords_summary">"Alle opgeslagen wachtwoorden wissen"</string>
+ <string name="pref_privacy_clear_passwords_dlg">"Alle opgeslagen wachtwoorden worden gewist."</string>
+ <string name="pref_security_title">"Beveiligingsinstellingen"</string>
+ <string name="pref_security_remember_passwords">"Wachtwoorden onthouden"</string>
+ <string name="pref_security_remember_passwords_summary">"Gebruikersnamen en wachtwoorden voor websites opslaan"</string>
+ <string name="pref_security_save_form_data">"Gegevens op formulier onthouden"</string>
+ <string name="pref_security_save_form_data_summary">"Gegevens die ik typ in formulieren, onthouden voor later gebruik."</string>
+ <string name="pref_security_show_security_warning">"Beveiligingswaarschuwingen weergeven"</string>
+ <string name="pref_security_show_security_warning_summary">"Waarschuwen als er een beveiligingsprobleem is met een site"</string>
+ <string name="pref_security_accept_cookies">"Cookies accepteren"</string>
+ <string name="pref_security_accept_cookies_summary">"Sites toestaan cookiegegevens te lezen en schrijven"</string>
+ <string name="pref_text_size">"Tekengrootte instellen"</string>
+ <string-array name="pref_text_size_choices">
+ <item>"Minuscuul"</item>
+ <item>"Klein"</item>
+ <item>"Normaal"</item>
+ <item>"Groot"</item>
+ <item>"Enorm"</item>
+ </string-array>
+ <string name="pref_text_size_dialogtitle">"Tekengrootte"</string>
+ <string name="pref_extras_title">"Geavanceerde instellingen"</string>
+ <string name="pref_extras_gears_enable">"Gears inschakelen"</string>
+ <string name="pref_extras_gears_enable_summary">"Toepassingen die de functionaliteit van de browser vergroten"</string>
+ <string name="pref_extras_gears_settings">"Instellingen van Gears"</string>
+ <string name="pref_plugin_installed">"Lijst met invoegtoepassingen"</string>
+ <string name="pref_plugin_installed_empty_list">"Geen invoegtoepassingen geïnstalleerd."</string>
+ <string name="pref_extras_gears_settings_summary">"Toepassingen die de functionaliteit van de browser vergroten"</string>
+ <string name="pref_extras_reset_default">"Standaardinstellingen herstellen"</string>
+ <string name="pref_extras_reset_default_summary">"Alle browsergegevens wissen en alle standaardinstellingen herstellen"</string>
+ <string name="pref_extras_reset_default_dlg">"Alle browsergegevens worden gewist en de standaardinstellingen worden hersteld."</string>
+ <string name="pref_extras_reset_default_dlg_title">"Standaardinstellingen herstellen"</string>
+ <string name="pref_development_title">"Foutopsporing"</string>
+ <string name="pref_development_viewport">"Use wide viewport"</string>
+ <string name="pref_development_single_column_rendering">"Single column rendering"</string>
+ <string name="pref_development_normal_rendering">"Normaal gerenderd"</string>
+ <string name="pref_development_trace">"Traceren inschakelen"</string>
+ <string name="pref_development_nav_dump">"Enable nav cache dump"</string>
+ <string name="pref_development_search_url">"Set search URL"</string>
+ <string name="pref_development_uastring">"UAString"</string>
+ <string-array name="pref_development_ua_choices">
+ <item>"Android"</item>
+ <item>"Desktop"</item>
+ <item>"iPhone"</item>
+ </string-array>
+ <string-array name="pref_development_ua_values">
+ <item>"0"</item>
+ <item>"1"</item>
+ <item>"2"</item>
+ </string-array>
+ <string name="pref_default_text_encoding">"Tekstcodering instellen"</string>
+ <string-array name="pref_default_text_encoding_choices">
+ <item>"Latijn-1 (ISO-8859-1)"</item>
+ <item>"Unicode (UTF-8)"</item>
+ <item>"Japans (ISO-2022-JP)"</item>
+ <item>"Japans (SHIFT_JIS)"</item>
+ <item>"Japans (EUC-JP)"</item>
+ </string-array>
+ <string name="pref_default_text_encoding_dialogtitle">"Tekstcodering"</string>
+ <string name="pref_default_text_encoding_default">"Latijn-1"</string>
+ <string name="browserFrameRedirect">"Omleiden"</string>
+ <string name="browserFrame307Post">"Deze webpagina wordt omgeleid. Wilt u uw ingevulde formuliergegevens doorsturen naar de nieuwe locatie?"</string>
+ <string name="browserFrameNetworkErrorLabel">"Probleem met gegevensverbinding"</string>
+ <string name="browserFrameFileErrorLabel">"Probleem met bestand"</string>
+ <string name="browserFrameFormResubmitLabel">"Bevestigen"</string>
+ <string name="browserFrameFormResubmitMessage">"De pagina die u probeert te openen, bevat gegevens die al verzonden zijn (\'POSTDATA\'). Als u de gegevens opnieuw verzendt, wordt elke actie herhaald die het formulier op de pagina heeft uitgevoerd (zoals een zoekopdracht of online aanschaf)."</string>
+ <string name="loadSuspendedTitle">"Geen netwerkverbinding"</string>
+ <string name="loadSuspended">"De pagina wordt verder geladen zodra de verbinding is hersteld."</string>
+ <string name="clear_history">"Geschiedenis wissen"</string>
+ <string name="browser_history">"Onlangs bezochte pagina\'s"</string>
+ <string name="empty_history">"Browsergeschiedenis is leeg."</string>
+ <string name="add_new_bookmark">"Bladwijzer toevoegen…"</string>
+ <string name="no_database">"Geen database."</string>
+ <string name="search_hint">"Typ het webadres"</string>
+ <string name="search_button_text">"Ga"</string>
+ <string name="attention">"Opgelet"</string>
+ <string name="popup_window_attempt">"Deze site probeert een pop-upvenster te openen."</string>
+ <string name="allow">"Toestaan"</string>
+ <string name="block">"Blokkeren"</string>
+ <string name="too_many_windows_dialog_title">"Limiet van venster is bereikt"</string>
+ <string name="too_many_windows_dialog_message">"Kan geen nieuw venster openen omdat u al het maximum aantal vensters heeft geopend."</string>
+ <string name="too_many_subwindows_dialog_title">"Pop-up is al geopend."</string>
+ <string name="too_many_subwindows_dialog_message">"Kan geen nieuw pop-upvenster openen omdat er maar één venster tegelijk geopend kan zijn."</string>
+ <string name="download_title">"Downloadgeschiedenis"</string>
+ <string name="download_unknown_filename">"&lt;Onbekend&gt;"</string>
+ <string name="download_menu_open">"Openen"</string>
+ <string name="download_menu_clear">"Wissen uit lijst"</string>
+ <string name="download_menu_cancel">"Download annuleren"</string>
+ <string name="download_menu_cancel_all">"Alle downloads annuleren"</string>
+ <string name="download_menu_clear_all">"Lijst wissen"</string>
+ <string name="download_clear_dlg_title">"Wissen"</string>
+ <string name="download_clear_dlg_msg">"Alle items worden uit de lijst en uit de browsercache verwijderd."</string>
+ <string name="download_cancel_dlg_title">"Downloads annuleren"</string>
+ <string name="download_cancel_dlg_msg">"Alle <xliff:g id="DOWNLOAD_COUNT">%d</xliff:g> downloads worden geannuleerd en gewist uit de downloadgeschiedenis."</string>
+ <string name="download_file_error_dlg_title">"Geen ruimte meer"</string>
+ <string name="download_file_error_dlg_msg">"<xliff:g id="FILENAME">%s</xliff:g> kan niet worden gedownload."\n"Maak ruimte vrij op uw telefoon en probeer het opnieuw."</string>
+ <string name="download_failed_generic_dlg_title">"Downloaden is mislukt."</string>
+ <string name="download_no_sdcard_dlg_title">"Geen SD-kaart"</string>
+ <string name="download_no_sdcard_dlg_msg">"Een SD-kaart is vereist om <xliff:g id="FILENAME">%s</xliff:g> te kunnen downloaden."</string>
+ <string name="download_sdcard_busy_dlg_title">"SD-kaart niet beschikbaar"</string>
+ <!-- no translation found for download_sdcard_busy_dlg_msg (3473883538192835204) -->
+ <skip />
+ <string name="download_no_application">"Er is geen toepassing gevonden waarmee dit bestand kan worden geopend."</string>
+ <string name="retry">"Opnieuw proberen"</string>
+ <string name="no_downloads">"Downloadgeschiedenis is leeg."</string>
+ <string name="download_error">"Downloaden is mislukt."</string>
+ <string name="download_success">"Downloaden van <xliff:g id="FILE">%s</xliff:g> is voltooid."</string>
+ <string name="download_running">"Downloaden..."</string>
+ <string name="download_pending">"Downloaden starten..."</string>
+ <string name="download_pending_network">"Wachten op gegevensverbinding..."</string>
+ <string name="download_running_paused">"Wachten op gegevensverbinding..."</string>
+ <string name="download_canceled">"Download is geannuleerd."</string>
+ <string name="download_not_acceptable">"Kan niet downloaden. De inhoud wordt niet ondersteund door de telefoon."</string>
+ <string name="download_file_error">"Kan download niet voltooien. Er is niet genoeg ruimte."</string>
+ <string name="download_length_required">"Kan niet downloaden. De grootte van het item kan niet worden vastgesteld."</string>
+ <string name="download_precondition_failed">"Download is onderbroken. Deze kan niet worden voortgezet."</string>
+ <string name="activity_instrumentation_test_runner">"Browser Test Runner"</string>
+ <string name="search_google">"Zoeken met Google"</string>
+ <string name="permlab_readHistoryBookmarks">"browsergeschiedenis en bladwijzers lezen"</string>
+ <string name="permdesc_readHistoryBookmarks">"Hiermee kan een toepassing de URL\'s lezen die u via de browser heeft bezocht, evenals alle bladwijzers van de browser."</string>
+ <string name="permlab_writeHistoryBookmarks">"browsergeschiedenis en bladwijzers schrijven"</string>
+ <string name="permdesc_writeHistoryBookmarks">"Hiermee kan een toepassing de op uw telefoon opgeslagen browsergeschiedenis of bladwijzers wijzigen. Schadelijke toepassingen kunnen hiermee uw browsergegevens verwijderen of wijzigen."</string>
+ <!-- no translation found for query_data_prompt (5474381240981604223) -->
+ <skip />
+ <!-- no translation found for query_data_message (8789381063185445197) -->
+ <skip />
+ <!-- no translation found for location_prompt (226262202057302423) -->
+ <skip />
+ <!-- no translation found for location_message (1729456751935683242) -->
+ <skip />
+ <!-- no translation found for shortcut_prompt (437193299088893596) -->
+ <skip />
+ <!-- no translation found for shortcut_message (4793042709293755892) -->
+ <skip />
+ <string name="settings_message">"De onderstaande tabel geeft de toestemming weer die u heeft verleend aan elke site die Gears heeft geprobeerd te gebruiken."</string>
+ <!-- no translation found for filepicker_message (4929726371602896039) -->
+ <skip />
+ <!-- no translation found for image_message (5450245866521896891) -->
+ <skip />
+ <string name="settings_title">"Instellingen van Gears"</string>
+ <string name="privacy_policy">"U kunt het privacybeleid van de site raadplegen voor meer informatie over hoe uw locatie wordt gebruikt."</string>
+ <!-- no translation found for settings_storage_title (7089119630457156408) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_on (2733295483569432111) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_off (1338039396087898813) -->
+ <skip />
+ <!-- no translation found for settings_location_title (4953062923509886651) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_on (1496219621849158879) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_off (5216466051810596189) -->
+ <skip />
+ <!-- no translation found for settings_remove_site (1822247070226589958) -->
+ <skip />
+ <!-- no translation found for settings_empty (2008952224378583146) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove_title (5360857296753219635) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove (585560170733446898) -->
+ <skip />
+ <!-- no translation found for storage_notification (6534213782873968255) -->
+ <skip />
+ <!-- no translation found for storage_notification_alwaysdeny (5473208033665783513) -->
+ <skip />
+ <!-- no translation found for location_notification (3495804750470905804) -->
+ <skip />
+ <!-- no translation found for location_notification_alwaysdeny (6184925953783312629) -->
+ <skip />
+ <!-- no translation found for shortcut_notification (1659700220868444568) -->
+ <skip />
+ <!-- no translation found for permission_button_alwaysdeny (3837055994905214848) -->
+ <skip />
+ <!-- no translation found for permission_button_allow (7301874925704148242) -->
+ <skip />
+ <!-- no translation found for permission_button_deny (104712269725153018) -->
+ <skip />
+ <!-- no translation found for shortcut_button_alwaysdeny (4172277731325126605) -->
+ <skip />
+ <string name="shortcut_button_allow">"OK"</string>
+ <string name="shortcut_button_deny">"Annuleren"</string>
+ <string name="settings_button_allow">"Toepassen"</string>
+ <string name="settings_button_deny">"Annuleren"</string>
+ <string name="filepicker_button_allow">"OK"</string>
+ <string name="filepicker_button_deny">"Annuleren"</string>
+ <string name="filepicker_path">"pad:"</string>
+ <string name="filepicker_no_files_selected">"Er zijn geen bestanden geselecteerd"</string>
+ <string name="filepicker_one_file_selected">"Eén bestand geselecteerd"</string>
+ <string name="filepicker_some_files_selected">"bestanden geselecteerd"</string>
+ <string name="remove">"Verwijderen"</string>
+ <string name="local_storage">"Lokale opslag"</string>
+ <string name="allowed">"Toegestaan"</string>
+ <string name="denied">"Geweigerd"</string>
+ <string name="unrecognized_dialog_message">"Dialoogtype wordt niet herkend"</string>
+ <string name="default_button">"OK"</string>
+ <string name="zoom_overview_button_text">"Overzicht"</string>
+</resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
new file mode 100644
index 00000000..6523df1a
--- /dev/null
+++ b/res/values-pl/strings.xml
@@ -0,0 +1,334 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name">"Internet"</string>
+ <string name="tab_bookmarks">"Zakładki"</string>
+ <string name="tab_most_visited">"Najczęściej odwiedzane"</string>
+ <string name="tab_history">"Historia"</string>
+ <string name="added_to_bookmarks">"Dodano do zakładek"</string>
+ <string name="removed_from_bookmarks">"Usunięto z zakładek"</string>
+ <string name="sign_in_to">"Zaloguj się w witrynie <xliff:g id="HOSTNAME">%s1</xliff:g> „<xliff:g id="REALM">%s2</xliff:g>”"</string>
+ <string name="username">"Nazwa"</string>
+ <string name="password">"Hasło"</string>
+ <string name="action">"Zaloguj się"</string>
+ <string name="cancel">"Anuluj"</string>
+ <string name="ok">"OK"</string>
+ <string name="matches_found">" dopasowania"</string>
+ <string name="zero">"Nie"</string>
+ <string name="page_info">"Informacje o stronie"</string>
+ <string name="page_info_view">"Informacje o stronie"</string>
+ <string name="page_info_address">"Adres:"</string>
+ <string name="ssl_warnings_header">"Są problemy z certyfikatem zabezpieczeń tej strony."</string>
+ <string name="ssl_continue">"Dalej"</string>
+ <string name="security_warning">"Ostrzeżenie zabezpieczeń"</string>
+ <string name="view_certificate">"Wyświetl certyfikat"</string>
+ <string name="ssl_untrusted">"Ten certyfikat nie pochodzi od zaufanego urzędu."</string>
+ <string name="ssl_mismatch">"Nazwa witryny nie odpowiada nazwie podanej w certyfikacie."</string>
+ <string name="ssl_expired">"Ten certyfikat wygasł."</string>
+ <string name="ssl_not_yet_valid">"Ten certyfikat nie jest jeszcze prawidłowy."</string>
+ <string name="ssl_certificate">"Certyfikat zabezpieczeń"</string>
+ <string name="ssl_certificate_is_valid">"Ten certyfikat jest prawidłowy."</string>
+ <string name="issued_to">"Otrzymujący:"</string>
+ <string name="common_name">"Nazwa pospolita:"</string>
+ <string name="org_name">"Organizacja:"</string>
+ <string name="org_unit">"Jednostka organizacyjna:"</string>
+ <string name="issued_by">"Wystawca:"</string>
+ <string name="validity_period">"Poprawność:"</string>
+ <string name="issued_on">"Data wystawienia:"</string>
+ <string name="expires_on">"Wygasa:"</string>
+ <string name="retrieving_creds_dlg_msg">"Pobieranie szczegółów logowania…"</string>
+ <string name="close">"Zamknij"</string>
+ <string name="close_window">"To okno zostanie zamknięte."</string>
+ <string name="stopping">"Trwa zatrzymywanie…"</string>
+ <string name="stop">"Zatrzymaj"</string>
+ <string name="reload">"Odśwież"</string>
+ <string name="forward">"Dalej"</string>
+ <string name="save">"OK"</string>
+ <string name="do_not_save">"Anuluj"</string>
+ <string name="location">"Adres"</string>
+ <string name="name">"Nazwa"</string>
+ <string name="http">"http://"</string>
+ <string name="save_to_bookmarks">"Utwórz zakładkę"</string>
+ <string name="edit_bookmark">"Edytuj zakładkę"</string>
+ <string name="create_shortcut_bookmark">"Dodaj skrót do strony głównej"</string>
+ <string name="open_bookmark">"Otwórz"</string>
+ <string name="remove_bookmark">"Usuń zakładkę"</string>
+ <string name="remove_history_item">"Usuń z historii"</string>
+ <string name="bookmark_saved">"Zapisano na liście zakładek."</string>
+ <string name="bookmark_needs_title">"Zakładka musi mieć nazwę."</string>
+ <string name="bookmark_needs_url">"Zakładka musi zawierać adres."</string>
+ <string name="empty_bookmark">"Nie można utworzyć pustej zakładki."</string>
+ <string name="bookmark_url_not_valid">"Adres URL jest nieprawidłowy."</string>
+ <string name="delete_bookmark">"Usuń"</string>
+ <string name="bookmark_page">"Dodaj do zakładek ostatnio wyświetlaną stronę"</string>
+ <string name="current_page">"z "</string>
+ <string name="delete_bookmark_warning">"Zakładka „<xliff:g id="BOOKMARK">%s</xliff:g>” zostanie usunięta."</string>
+ <string name="open_in_new_window">"Otwórz w nowym oknie"</string>
+ <string name="new_window">"Nowe okno"</string>
+ <string name="goto_dot">"Idź"</string>
+ <string name="find_dot">"Znajdź na stronie"</string>
+ <string name="select_dot">"Zaznacz tekst"</string>
+ <string name="view_tabs">"Przegląd okien"</string>
+ <string name="view_tabs_condensed">"Okna"</string>
+ <string name="tab_picker_title">"Bieżące okna"</string>
+ <string name="tab_picker_view_tab">"Wyświetl"</string>
+ <string name="tab_picker_new_tab">"Nowe okno"</string>
+ <string name="tab_picker_remove_tab">"Zamknij"</string>
+ <string name="tab_picker_bookmark">"Utwórz zakładkę"</string>
+ <string name="tab_picker_send_url">"Prześlij link"</string>
+ <string name="bookmarks">"Zakładki"</string>
+ <string name="shortcut_bookmark">"Utwórz zakładkę"</string>
+ <string name="history">"Historia"</string>
+ <string name="menu_view_download">"Pobrane pliki"</string>
+ <string name="share_page">"Udostępnij stronę"</string>
+ <string name="contextmenu_openlink">"Otwórz"</string>
+ <string name="contextmenu_openlink_newwindow">"Otwórz w nowym oknie"</string>
+ <string name="contextmenu_bookmark_thislink">"Dodaj link do zakładek"</string>
+ <string name="contextmenu_savelink">"Zapisz link"</string>
+ <string name="contextmenu_sharelink">"Udostępnij link"</string>
+ <string name="contextmenu_copy">"Kopiuj"</string>
+ <string name="contextmenu_copylink">"Kopiuj adres URL linku"</string>
+ <string name="contextmenu_download_image">"Zapisz obraz"</string>
+ <string name="contextmenu_view_image">"Wyświetl obraz"</string>
+ <string name="contextmenu_dial_dot">"Wybierz numer…"</string>
+ <string name="contextmenu_add_contact">"Dodaj kontakt"</string>
+ <string name="contextmenu_send_mail">"Wyślij wiadomość e-mail"</string>
+ <string name="contextmenu_map">"Mapa"</string>
+ <string name="clear">"Wyczyść"</string>
+ <string name="replace">"Zastąp adres"</string>
+ <string name="browser_bookmarks_page_bookmarks_text">"Zakładki"</string>
+ <string name="menu_preferences">"Ustawienia"</string>
+ <string name="pref_content_title">"Ustawienia zawartości stron"</string>
+ <string name="pref_content_load_images">"Wczytuj obrazy"</string>
+ <string name="pref_content_load_images_summary">"Wyświetlaj obrazy na stronach WWW"</string>
+ <string name="pref_content_block_popups">"Blokuj wyskakujące okna"</string>
+ <string name="pref_content_javascript">"Włącz skrypty JavaScript"</string>
+ <string name="pref_content_open_in_background">"Otwórz w tle"</string>
+ <string name="pref_content_open_in_background_summary">"Nowe okna są otwierane w tle"</string>
+ <string name="pref_content_homepage">"Ustaw stronę główną"</string>
+ <string name="pref_content_autofit">"Autodopasowanie stron"</string>
+ <string name="pref_content_autofit_summary">"Formatuj strony WWW, aby mieściły się na ekranie"</string>
+ <string name="pref_privacy_title">"Ustawienia prywatności"</string>
+ <string name="pref_privacy_clear_cache">"Wyczyść pamięć podręczną"</string>
+ <string name="pref_privacy_clear_cache_summary">"Usuń całą zawartość stron z pamięci podręcznej"</string>
+ <string name="pref_privacy_clear_cache_dlg">"Pamięć podręczna zostanie wyczyszczona."</string>
+ <string name="pref_privacy_clear_cookies">"Wyczyść wszystkie cookies"</string>
+ <string name="pref_privacy_clear_cookies_summary">"Wyczyść pliki cookie przeglądarki"</string>
+ <string name="pref_privacy_clear_cookies_dlg">"Wszystkie pliki cookie zostaną usunięte."</string>
+ <string name="pref_privacy_clear_history">"Wyczyść historię"</string>
+ <string name="pref_privacy_clear_history_summary">"Wyczyść historię przeglądania"</string>
+ <string name="pref_privacy_clear_history_dlg">"Historia przeglądania zostanie wyczyszczona."</string>
+ <string name="pref_privacy_clear_form_data">"Wyczyść dane formularzy"</string>
+ <string name="pref_privacy_clear_form_data_summary">"Usuń wszystkie zapisane dane formularzy"</string>
+ <string name="pref_privacy_clear_form_data_dlg">"Wszystkie zapamiętane dane formularzy zostaną wyczyszczone."</string>
+ <string name="pref_privacy_clear_passwords">"Wyczyść hasła"</string>
+ <string name="pref_privacy_clear_passwords_summary">"Wyczyść wszystkie zapamiętane hasła"</string>
+ <string name="pref_privacy_clear_passwords_dlg">"Wszystkie zapamiętane hasła zostaną wyczyszczone."</string>
+ <string name="pref_security_title">"Ustawienia zabezpieczeń"</string>
+ <string name="pref_security_remember_passwords">"Zapamiętuj hasła"</string>
+ <string name="pref_security_remember_passwords_summary">"Zapamiętuj nazwy użytkownika i hasła do stron WWW"</string>
+ <string name="pref_security_save_form_data">"Zapamiętuj wpisane dane"</string>
+ <string name="pref_security_save_form_data_summary">"Zapamiętuj do późniejszego użycia dane, które wpisuję w formularzach"</string>
+ <string name="pref_security_show_security_warning">"Wyświetlaj ostrzeżenia"</string>
+ <string name="pref_security_show_security_warning_summary">"Wyświetl ostrzeżenie w razie problemów z zabezpieczeniami strony"</string>
+ <string name="pref_security_accept_cookies">"Akceptuj pliki cookie"</string>
+ <string name="pref_security_accept_cookies_summary">"Zezwalaj witrynom na zapis i odczyt danych plików „cookie”."</string>
+ <string name="pref_text_size">"Ustaw rozmiar tekstu"</string>
+ <string-array name="pref_text_size_choices">
+ <item>"Bardzo mały"</item>
+ <item>"Mały"</item>
+ <item>"Normalny"</item>
+ <item>"Duży"</item>
+ <item>"Bardzo duży"</item>
+ </string-array>
+ <string name="pref_text_size_dialogtitle">"Rozmiar tekstu"</string>
+ <string name="pref_extras_title">"Ustawienia zaawansowane"</string>
+ <string name="pref_extras_gears_enable">"Włącz technologię Gears"</string>
+ <string name="pref_extras_gears_enable_summary">"Aplikacje rozszerzające zakres funkcji przeglądarki"</string>
+ <string name="pref_extras_gears_settings">"Gears – ustawienia"</string>
+ <string name="pref_plugin_installed">"Lista dodatków plug-in"</string>
+ <string name="pref_plugin_installed_empty_list">"Brak zainstalowanych dodatków plug-in."</string>
+ <string name="pref_extras_gears_settings_summary">"Aplikacje rozszerzające zakres funkcji przeglądarki"</string>
+ <string name="pref_extras_reset_default">"Przywróć wartości domyślne"</string>
+ <string name="pref_extras_reset_default_summary">"Wyczyść wszystkie dane przeglądarki i przywróć wszystkie ustawienia domyślne"</string>
+ <string name="pref_extras_reset_default_dlg">"Wszystkie dane przeglądarki zostaną wyczyszczone, a ustawienia powrócą do wartości domyślnych."</string>
+ <string name="pref_extras_reset_default_dlg_title">"Przywróć wartości domyślne"</string>
+ <string name="pref_development_title">"Debugowanie"</string>
+ <string name="pref_development_viewport">"Use wide viewport"</string>
+ <string name="pref_development_single_column_rendering">"Single column rendering"</string>
+ <string name="pref_development_normal_rendering">"Normal rendering"</string>
+ <string name="pref_development_trace">"Enable tracing"</string>
+ <string name="pref_development_nav_dump">"Enable nav cache dump"</string>
+ <string name="pref_development_search_url">"Ustaw adres URL wyszukiwania"</string>
+ <string name="pref_development_uastring">"UAString"</string>
+ <string-array name="pref_development_ua_choices">
+ <item>"Android"</item>
+ <item>"Desktop"</item>
+ <item>"iPhone"</item>
+ </string-array>
+ <string-array name="pref_development_ua_values">
+ <item>"0"</item>
+ <item>"1"</item>
+ <item>"2"</item>
+ </string-array>
+ <string name="pref_default_text_encoding">"Ustaw kodowanie tekstu"</string>
+ <string-array name="pref_default_text_encoding_choices">
+ <item>"Latin-1 (ISO-8859-1)"</item>
+ <item>"Unicode (UTF-8)"</item>
+ <item>"Japoński (ISO-2022-JP)"</item>
+ <item>"Japoński (SHIFT_JIS)"</item>
+ <item>"Japoński (EUC-JP)"</item>
+ </string-array>
+ <string name="pref_default_text_encoding_dialogtitle">"Kodowanie tekstu"</string>
+ <string name="pref_default_text_encoding_default">"Latin-1"</string>
+ <string name="browserFrameRedirect">"Przekieruj"</string>
+ <string name="browserFrame307Post">"Ta strona WWW jest przekierowywana. Czy wysłać wpisane dane formularza do nowej lokalizacji?"</string>
+ <string name="browserFrameNetworkErrorLabel">"Problem z łącznością danych"</string>
+ <string name="browserFrameFileErrorLabel">"Problem z plikiem"</string>
+ <string name="browserFrameFormResubmitLabel">"Potwierdzenie"</string>
+ <string name="browserFrameFormResubmitMessage">"Strona, którą próbujesz wyświetlić, zawiera dane, które już przesłano („POSTDATA”). Jeśli wyślesz te dane ponownie, wszelkie operacje wykonane przez formularz na tej stronie (np. wyszukiwanie lub zakup online) zostaną powtórzone."</string>
+ <string name="loadSuspendedTitle">"Brak połączenia z siecią"</string>
+ <string name="loadSuspended">"Ładowanie strony będzie kontynuowane po przywróceniu połączenia."</string>
+ <string name="clear_history">"Wyczyść historię"</string>
+ <string name="browser_history">"Ostatnio odwiedzane strony"</string>
+ <string name="empty_history">"Historia przeglądarki jest pusta."</string>
+ <string name="add_new_bookmark">"Dodaj do zakładek..."</string>
+ <string name="no_database">"Brak bazy danych!"</string>
+ <string name="search_hint">"Wpisz adres WWW"</string>
+ <string name="search_button_text">"Otwórz"</string>
+ <string name="attention">"Uwaga"</string>
+ <string name="popup_window_attempt">"Ta strona próbuje otworzyć okienko wyskakujące."</string>
+ <string name="allow">"Zezwól"</string>
+ <string name="block">"Zablokuj"</string>
+ <string name="too_many_windows_dialog_title">"Osiągnięto limit liczby okien"</string>
+ <string name="too_many_windows_dialog_message">"Nie można otworzyć nowego okna, ponieważ jest już otwarta maksymalna liczba okien."</string>
+ <string name="too_many_subwindows_dialog_title">"Okienko wyskakujące jest już otwarte"</string>
+ <string name="too_many_subwindows_dialog_message">"Nie można otworzyć nowego okienka wyskakującego, ponieważ otwarte może być tylko jedno w danej chwili."</string>
+ <string name="download_title">"Historia pobierania"</string>
+ <string name="download_unknown_filename">"&lt;Nieznane&gt;"</string>
+ <string name="download_menu_open">"Otwórz"</string>
+ <string name="download_menu_clear">"Usuń z listy"</string>
+ <string name="download_menu_cancel">"Anuluj pobieranie"</string>
+ <string name="download_menu_cancel_all">"Anuluj pobieranie wszystkich plików"</string>
+ <string name="download_menu_clear_all">"Wyczyść listę"</string>
+ <string name="download_clear_dlg_title">"Wyczyść"</string>
+ <string name="download_clear_dlg_msg">"Wszystkie pozycje zostaną usunięte z listy i z pamięci podręcznej przeglądarki."</string>
+ <string name="download_cancel_dlg_title">"Anuluj wszystkie pobierania"</string>
+ <string name="download_cancel_dlg_msg">"Wszystkie pobierane pliki (<xliff:g id="DOWNLOAD_COUNT">%d</xliff:g>) zostaną anulowane i usunięte z historii pobierania."</string>
+ <string name="download_file_error_dlg_title">"Brak miejsca"</string>
+ <string name="download_file_error_dlg_msg">"Nie można pobrać pliku <xliff:g id="FILENAME">%s</xliff:g>."\n" Zwolnij trochę miejsca w telefonie i spróbuj ponownie."</string>
+ <string name="download_failed_generic_dlg_title">"Pobieranie nie powiodło się"</string>
+ <string name="download_no_sdcard_dlg_title">"Brak karty SD"</string>
+ <string name="download_no_sdcard_dlg_msg">"Do pobrania pliku <xliff:g id="FILENAME">%s</xliff:g> potrzebna jest karta SD."</string>
+ <string name="download_sdcard_busy_dlg_title">"Karta SD jest niedostępna"</string>
+ <!-- no translation found for download_sdcard_busy_dlg_msg (3473883538192835204) -->
+ <skip />
+ <string name="download_no_application">"Nie można znaleźć pliku do otwarcia tego programu."</string>
+ <string name="retry">"Spróbuj ponownie"</string>
+ <string name="no_downloads">"Historia pobierania jest pusta."</string>
+ <string name="download_error">"Pobieranie nie powiodło się."</string>
+ <string name="download_success">"Pobieranie pliku <xliff:g id="FILE">%s</xliff:g> zakończone."</string>
+ <string name="download_running">"Trwa pobieranie…"</string>
+ <string name="download_pending">"Rozpoczynanie pobierania"</string>
+ <string name="download_pending_network">"Oczekiwanie na połączenie danych…"</string>
+ <string name="download_running_paused">"Oczekiwanie na połączenie danych..."</string>
+ <string name="download_canceled">"Pobieranie anulowane."</string>
+ <string name="download_not_acceptable">"Pobranie niemożliwe. Ta zawartość nie jest obsługiwana przez ten telefon."</string>
+ <string name="download_file_error">"Nie można ukończyć pobierania. Za mało miejsca."</string>
+ <string name="download_length_required">"Pobieranie niemożliwe. Nie można ustalić rozmiaru elementu."</string>
+ <string name="download_precondition_failed">"Pobieranie zostało przerwane. Nie można go wznowić."</string>
+ <string name="activity_instrumentation_test_runner">"Uruchamianie testów przeglądarki"</string>
+ <string name="search_google">"Szukanie Google"</string>
+ <string name="permlab_readHistoryBookmarks">"odczytywanie historii i zakładek przeglądarki"</string>
+ <string name="permdesc_readHistoryBookmarks">"Zezwala aplikacji na odczytanie wszystkich adresów URL odwiedzonych przy użyciu przeglądarki oraz wszystkich zakładek przeglądarki."</string>
+ <string name="permlab_writeHistoryBookmarks">"zapisać historię i zakładki przeglądarki"</string>
+ <string name="permdesc_writeHistoryBookmarks">"Pozwala aplikacji na zmianę historii lub zakładek przeglądarki zapisanych w telefonie. Szkodliwe aplikacje mogą to wykorzystać, aby usunąć lub zmienić dane przeglądarki."</string>
+ <!-- no translation found for query_data_prompt (5474381240981604223) -->
+ <skip />
+ <!-- no translation found for query_data_message (8789381063185445197) -->
+ <skip />
+ <!-- no translation found for location_prompt (226262202057302423) -->
+ <skip />
+ <!-- no translation found for location_message (1729456751935683242) -->
+ <skip />
+ <!-- no translation found for shortcut_prompt (437193299088893596) -->
+ <skip />
+ <!-- no translation found for shortcut_message (4793042709293755892) -->
+ <skip />
+ <string name="settings_message">"W poniższej tabeli przedstawiono uprawnienia udzielone każdej z witryn próbujących użyć Gears."</string>
+ <!-- no translation found for filepicker_message (4929726371602896039) -->
+ <skip />
+ <!-- no translation found for image_message (5450245866521896891) -->
+ <skip />
+ <string name="settings_title">"Gears – ustawienia"</string>
+ <string name="privacy_policy">"Przeczytaj zasady zachowania poufności informacji witryny, aby dowiedzieć się, jak będą użyte informacje o twojej lokalizacji."</string>
+ <!-- no translation found for settings_storage_title (7089119630457156408) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_on (2733295483569432111) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_off (1338039396087898813) -->
+ <skip />
+ <!-- no translation found for settings_location_title (4953062923509886651) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_on (1496219621849158879) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_off (5216466051810596189) -->
+ <skip />
+ <!-- no translation found for settings_remove_site (1822247070226589958) -->
+ <skip />
+ <!-- no translation found for settings_empty (2008952224378583146) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove_title (5360857296753219635) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove (585560170733446898) -->
+ <skip />
+ <!-- no translation found for storage_notification (6534213782873968255) -->
+ <skip />
+ <!-- no translation found for storage_notification_alwaysdeny (5473208033665783513) -->
+ <skip />
+ <!-- no translation found for location_notification (3495804750470905804) -->
+ <skip />
+ <!-- no translation found for location_notification_alwaysdeny (6184925953783312629) -->
+ <skip />
+ <!-- no translation found for shortcut_notification (1659700220868444568) -->
+ <skip />
+ <!-- no translation found for permission_button_alwaysdeny (3837055994905214848) -->
+ <skip />
+ <!-- no translation found for permission_button_allow (7301874925704148242) -->
+ <skip />
+ <!-- no translation found for permission_button_deny (104712269725153018) -->
+ <skip />
+ <!-- no translation found for shortcut_button_alwaysdeny (4172277731325126605) -->
+ <skip />
+ <string name="shortcut_button_allow">"OK"</string>
+ <string name="shortcut_button_deny">"Anuluj"</string>
+ <string name="settings_button_allow">"Zastosuj"</string>
+ <string name="settings_button_deny">"Anuluj"</string>
+ <string name="filepicker_button_allow">"OK"</string>
+ <string name="filepicker_button_deny">"Anuluj"</string>
+ <string name="filepicker_path">"ścieżka:"</string>
+ <string name="filepicker_no_files_selected">"Nie wybrano żadnych plików"</string>
+ <string name="filepicker_one_file_selected">"Wybrano jeden plik"</string>
+ <string name="filepicker_some_files_selected">"plików wybrano"</string>
+ <string name="remove">"Usuń"</string>
+ <string name="local_storage">"Pamięć lokalna"</string>
+ <string name="allowed">"Zezwolono"</string>
+ <string name="denied">"Odmówiono"</string>
+ <string name="unrecognized_dialog_message">"Nierozpoznany typ okna dialogowego"</string>
+ <string name="default_button">"OK"</string>
+ <string name="zoom_overview_button_text">"Widok ogólny"</string>
+</resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
new file mode 100644
index 00000000..4b4b677b
--- /dev/null
+++ b/res/values-ru/strings.xml
@@ -0,0 +1,334 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name">"Браузер"</string>
+ <string name="tab_bookmarks">"Закладки"</string>
+ <string name="tab_most_visited">"Часто посещаемые"</string>
+ <string name="tab_history">"История"</string>
+ <string name="added_to_bookmarks">"Добавлено в закладки"</string>
+ <string name="removed_from_bookmarks">"Удалено из закладок"</string>
+ <string name="sign_in_to">"Регистрация в <xliff:g id="HOSTNAME">%s1</xliff:g> \"<xliff:g id="REALM">%s2</xliff:g>\""</string>
+ <string name="username">"Название"</string>
+ <string name="password">"Пароль"</string>
+ <string name="action">"Войти"</string>
+ <string name="cancel">"Отмена"</string>
+ <string name="ok">"ОК"</string>
+ <string name="matches_found">" совпадений(я)"</string>
+ <string name="zero">"Нет"</string>
+ <string name="page_info">"Сведения о странице"</string>
+ <string name="page_info_view">"Просмотреть сведения о странице"</string>
+ <string name="page_info_address">"Адрес:"</string>
+ <string name="ssl_warnings_header">"Есть проблемы с сертификатом безопасности для этого сайта."</string>
+ <string name="ssl_continue">"Продолжить"</string>
+ <string name="security_warning">"Предупреждение о безопасности"</string>
+ <string name="view_certificate">"Просмотреть сертификат"</string>
+ <string name="ssl_untrusted">"Этот сертификат получен из ненадежного центра сертификации."</string>
+ <string name="ssl_mismatch">"Название сайта не соответствует названию сертификата."</string>
+ <string name="ssl_expired">"Срок действия сертификата истек."</string>
+ <string name="ssl_not_yet_valid">"Этот сертификат пока не действителен."</string>
+ <string name="ssl_certificate">"Сертификат безопасности"</string>
+ <string name="ssl_certificate_is_valid">"Это действительный сертификат."</string>
+ <string name="issued_to">"Кому выдан:"</string>
+ <string name="common_name">"Обычное название:"</string>
+ <string name="org_name">"Организация:"</string>
+ <string name="org_unit">"Подразделение:"</string>
+ <string name="issued_by">"Кем выдан:"</string>
+ <string name="validity_period">"Срок действия:"</string>
+ <string name="issued_on">"Когда выдан:"</string>
+ <string name="expires_on">"Действителен до:"</string>
+ <string name="retrieving_creds_dlg_msg">"Получение сведений о входе…"</string>
+ <string name="close">"Закрыть"</string>
+ <string name="close_window">"Это окно будето закрыто."</string>
+ <string name="stopping">"Остановка загрузки…"</string>
+ <string name="stop">"Остановить"</string>
+ <string name="reload">"Обновить"</string>
+ <string name="forward">"Вперед"</string>
+ <string name="save">"ОК"</string>
+ <string name="do_not_save">"Отмена"</string>
+ <string name="location">"Адрес"</string>
+ <string name="name">"Название"</string>
+ <string name="http">"http://"</string>
+ <string name="save_to_bookmarks">"Добавить в закладки"</string>
+ <string name="edit_bookmark">"Изменить закладку"</string>
+ <string name="create_shortcut_bookmark">"Добавить ярлык на главный экран"</string>
+ <string name="open_bookmark">"Открыть"</string>
+ <string name="remove_bookmark">"Удалить закладку"</string>
+ <string name="remove_history_item">"Удалить из истории"</string>
+ <string name="bookmark_saved">"Сохранено в заклкдках."</string>
+ <string name="bookmark_needs_title">"У закладки должно быть название."</string>
+ <string name="bookmark_needs_url">"У закладки должен быть адрес."</string>
+ <string name="empty_bookmark">"Невозможно создать пустую закладку."</string>
+ <string name="bookmark_url_not_valid">"URL недействителен."</string>
+ <string name="delete_bookmark">"Удаление"</string>
+ <string name="bookmark_page">"Добавить в закладки последнюю просмотренную страницу"</string>
+ <string name="current_page">"из "</string>
+ <string name="delete_bookmark_warning">"Закладка \"<xliff:g id="BOOKMARK">%s</xliff:g>\" будет удалена."</string>
+ <string name="open_in_new_window">"Открыть в новом окне"</string>
+ <string name="new_window">"Новое окно"</string>
+ <string name="goto_dot">"Выбрать"</string>
+ <string name="find_dot">"Найти на странице"</string>
+ <string name="select_dot">"Выбрать текст"</string>
+ <string name="view_tabs">"Обзор окон"</string>
+ <string name="view_tabs_condensed">"Окна"</string>
+ <string name="tab_picker_title">"Текущие окна"</string>
+ <string name="tab_picker_view_tab">"Просмотреть"</string>
+ <string name="tab_picker_new_tab">"Новое окно"</string>
+ <string name="tab_picker_remove_tab">"Закрыть"</string>
+ <string name="tab_picker_bookmark">"Добавить в закладки"</string>
+ <string name="tab_picker_send_url">"Отправить ссылку"</string>
+ <string name="bookmarks">"Закладки"</string>
+ <string name="shortcut_bookmark">"Закладка"</string>
+ <string name="history">"История"</string>
+ <string name="menu_view_download">"Загрузки"</string>
+ <string name="share_page">"Отправить страницу"</string>
+ <string name="contextmenu_openlink">"Открыть"</string>
+ <string name="contextmenu_openlink_newwindow">"Открыть в новом окне"</string>
+ <string name="contextmenu_bookmark_thislink">"Добавить в закладки"</string>
+ <string name="contextmenu_savelink">"Сохранить ссылку"</string>
+ <string name="contextmenu_sharelink">"Отправить ссылку"</string>
+ <string name="contextmenu_copy">"Копировать"</string>
+ <string name="contextmenu_copylink">"Копировать ссылку"</string>
+ <string name="contextmenu_download_image">"Сохранить изображение"</string>
+ <string name="contextmenu_view_image">"Просмотреть изображение"</string>
+ <string name="contextmenu_dial_dot">"Вызвать…"</string>
+ <string name="contextmenu_add_contact">"Добавить в адресную книгу"</string>
+ <string name="contextmenu_send_mail">"Отправить сообщение электронной почты"</string>
+ <string name="contextmenu_map">"На карте"</string>
+ <string name="clear">"Очистка"</string>
+ <string name="replace">"Заменить"</string>
+ <string name="browser_bookmarks_page_bookmarks_text">"Закладки"</string>
+ <string name="menu_preferences">"Настройки"</string>
+ <string name="pref_content_title">"Настройки содержания страниц"</string>
+ <string name="pref_content_load_images">"Загружать изображения"</string>
+ <string name="pref_content_load_images_summary">"Показывать изображения на страницах"</string>
+ <string name="pref_content_block_popups">"Заблокировать всплывающие окна"</string>
+ <string name="pref_content_javascript">"Разрешить JavaScript"</string>
+ <string name="pref_content_open_in_background">"Открывать в фоновом режиме"</string>
+ <string name="pref_content_open_in_background_summary">"Новые окна открываются позади текущего"</string>
+ <string name="pref_content_homepage">"Выбрать главную страницу"</string>
+ <string name="pref_content_autofit">"Автоподбор размеров страниц"</string>
+ <string name="pref_content_autofit_summary">"Форматировать веб-страницы по размеру экрана"</string>
+ <string name="pref_privacy_title">"Настройки конфиденциальности"</string>
+ <string name="pref_privacy_clear_cache">"Очистить кэш"</string>
+ <string name="pref_privacy_clear_cache_summary">"Удалить все кэшированные страницы"</string>
+ <string name="pref_privacy_clear_cache_dlg">"Кэш будет очищен."</string>
+ <string name="pref_privacy_clear_cookies">"Удалить все данные cookie"</string>
+ <string name="pref_privacy_clear_cookies_summary">"Удалить все файлы cookie браузера"</string>
+ <string name="pref_privacy_clear_cookies_dlg">"Все cookie будут удалены."</string>
+ <string name="pref_privacy_clear_history">"Очистить историю"</string>
+ <string name="pref_privacy_clear_history_summary">"Очистить историю посещений браузера"</string>
+ <string name="pref_privacy_clear_history_dlg">"История посещений браузера будет удалена."</string>
+ <string name="pref_privacy_clear_form_data">"Удалить данные форм"</string>
+ <string name="pref_privacy_clear_form_data_summary">"Удалить все сохраненные данные форм"</string>
+ <string name="pref_privacy_clear_form_data_dlg">"Все сохраненные данные форм будут удалены."</string>
+ <string name="pref_privacy_clear_passwords">"Удалить пароли"</string>
+ <string name="pref_privacy_clear_passwords_summary">"Удалить все сохраненные пароли"</string>
+ <string name="pref_privacy_clear_passwords_dlg">"Все сохраненные пароли будут удалены."</string>
+ <string name="pref_security_title">"Настройки безопасности"</string>
+ <string name="pref_security_remember_passwords">"Запоминать пароли"</string>
+ <string name="pref_security_remember_passwords_summary">"Сохранять имена пользователя и пароли для веб-сайтов"</string>
+ <string name="pref_security_save_form_data">"Запоминать данные форм"</string>
+ <string name="pref_security_save_form_data_summary">"Запоминать данные форм для дальнейшего использования"</string>
+ <string name="pref_security_show_security_warning">"Показывать предупреждения о безопасности"</string>
+ <string name="pref_security_show_security_warning_summary">"Показывать предупреждение при наличии проблем с безопасностью сайта"</string>
+ <string name="pref_security_accept_cookies">"Разрешить cookie"</string>
+ <string name="pref_security_accept_cookies_summary">"Разрешить сайтам сохранять и считывать данные cookie"</string>
+ <string name="pref_text_size">"Выбрать размер текста"</string>
+ <string-array name="pref_text_size_choices">
+ <item>"Очень мелкий"</item>
+ <item>"Мелкий"</item>
+ <item>"Обычный"</item>
+ <item>"Крупный"</item>
+ <item>"Очень крупный"</item>
+ </string-array>
+ <string name="pref_text_size_dialogtitle">"Размер текста"</string>
+ <string name="pref_extras_title">"Дополнительные настройки"</string>
+ <string name="pref_extras_gears_enable">"Включить Gears"</string>
+ <string name="pref_extras_gears_enable_summary">"Приложения, расширяющие возможности браузера"</string>
+ <string name="pref_extras_gears_settings">"Настройки Gears"</string>
+ <string name="pref_plugin_installed">"Список плагинов"</string>
+ <string name="pref_plugin_installed_empty_list">"Нет установленных плагинов."</string>
+ <string name="pref_extras_gears_settings_summary">"Приложения, расширяющие возможности браузера"</string>
+ <string name="pref_extras_reset_default">"Восстановить значения по умолчанию"</string>
+ <string name="pref_extras_reset_default_summary">"Удалить все данные браузера и восстановить настройки по умолчанию"</string>
+ <string name="pref_extras_reset_default_dlg">"Все данные бразуера будут удалены, а для настроек будут восстановлены значения по умолчанию."</string>
+ <string name="pref_extras_reset_default_dlg_title">"Восстановить значения по умолчанию"</string>
+ <string name="pref_development_title">"Отладка"</string>
+ <string name="pref_development_viewport">"Use wide viewport"</string>
+ <string name="pref_development_single_column_rendering">"Single column rendering"</string>
+ <string name="pref_development_normal_rendering">"Normal rendering"</string>
+ <string name="pref_development_trace">"Enable tracing"</string>
+ <string name="pref_development_nav_dump">"Enable nav cache dump"</string>
+ <string name="pref_development_search_url">"Set search URL"</string>
+ <string name="pref_development_uastring">"UAString"</string>
+ <string-array name="pref_development_ua_choices">
+ <item>"Android"</item>
+ <item>"Desktop"</item>
+ <item>"iPhone"</item>
+ </string-array>
+ <string-array name="pref_development_ua_values">
+ <item>"0"</item>
+ <item>"1"</item>
+ <item>"2"</item>
+ </string-array>
+ <string name="pref_default_text_encoding">"Выбрать кодировку текста"</string>
+ <string-array name="pref_default_text_encoding_choices">
+ <item>"Латиница-1 (ISO-8859-1)"</item>
+ <item>"Юникод (UTF-8)"</item>
+ <item>"Японская (ISO-2022-JP)"</item>
+ <item>"Японская (SHIFT_JIS)"</item>
+ <item>"Японская (EUC-JP)"</item>
+ </string-array>
+ <string name="pref_default_text_encoding_dialogtitle">"Кодировка текста"</string>
+ <string name="pref_default_text_encoding_default">"Латиница-1"</string>
+ <string name="browserFrameRedirect">"Перенаправление"</string>
+ <string name="browserFrame307Post">"С этой веб-страницы выполняется перенаправление. Отправить введенные данные формы по новому адресу?"</string>
+ <string name="browserFrameNetworkErrorLabel">"Неполадка подключения для передачи данных"</string>
+ <string name="browserFrameFileErrorLabel">"Проблема с файлом"</string>
+ <string name="browserFrameFormResubmitLabel">"Подтверждение"</string>
+ <string name="browserFrameFormResubmitMessage">"Страница, которую вы пытаетесь просмотреть, содержит отправленные ранее данные (\"POSTDATA\"). При повторной отправке данных выполняемое формой действие (например поиск или онлайн-покупка) будет повторено."</string>
+ <string name="loadSuspendedTitle">"Нет сетевого подключения"</string>
+ <string name="loadSuspended">"Загрузка страницы будет продолжена после восстановления подключения."</string>
+ <string name="clear_history">"Очистить историю"</string>
+ <string name="browser_history">"Недавно посещенные страницы"</string>
+ <string name="empty_history">"История бразуера пуста."</string>
+ <string name="add_new_bookmark">"Добавить закладку…"</string>
+ <string name="no_database">"Нет базы данных!"</string>
+ <string name="search_hint">"Введите веб-адрес"</string>
+ <string name="search_button_text">"Перейти"</string>
+ <string name="attention">"Внимание"</string>
+ <string name="popup_window_attempt">"Этот сайт пытается открыть всплывающее окно."</string>
+ <string name="allow">"Разрешить"</string>
+ <string name="block">"Заблокировать"</string>
+ <string name="too_many_windows_dialog_title">"Предельное количество окон"</string>
+ <string name="too_many_windows_dialog_message">"Открыть новое окно невозможно, потому что открыто максимальное количество окон."</string>
+ <string name="too_many_subwindows_dialog_title">"Всплывающее окно уже открыто"</string>
+ <string name="too_many_subwindows_dialog_message">"Не удалось открыть всплывающее окно, потому что нельзя открыть больше одного такого окна одновременно."</string>
+ <string name="download_title">"История загрузок"</string>
+ <string name="download_unknown_filename">"&lt;Неизвестно&gt;"</string>
+ <string name="download_menu_open">"Открыть"</string>
+ <string name="download_menu_clear">"Удалить из списка"</string>
+ <string name="download_menu_cancel">"Отменить загрузку"</string>
+ <string name="download_menu_cancel_all">"Отменить все загрузки"</string>
+ <string name="download_menu_clear_all">"Очистить список"</string>
+ <string name="download_clear_dlg_title">"Очистка"</string>
+ <string name="download_clear_dlg_msg">"Все элементы будут удалены из списка и кэша браузера."</string>
+ <string name="download_cancel_dlg_title">"Отмена загрузок"</string>
+ <string name="download_cancel_dlg_msg">"Все загрузки (<xliff:g id="DOWNLOAD_COUNT">%d</xliff:g>) будут отменены и удалены из истории загрузок."</string>
+ <string name="download_file_error_dlg_title">"Недостаточно места"</string>
+ <string name="download_file_error_dlg_msg">"Не удалось загрузить <xliff:g id="FILENAME">%s</xliff:g>."\n"Освободите пространство на телефоне и повторите попытку."</string>
+ <string name="download_failed_generic_dlg_title">"Загрузка не удалась"</string>
+ <string name="download_no_sdcard_dlg_title">"Нет карты SD"</string>
+ <string name="download_no_sdcard_dlg_msg">"Для загрузки файла <xliff:g id="FILENAME">%s</xliff:g> нужна карта SD."</string>
+ <string name="download_sdcard_busy_dlg_title">"Карта SD недоступна"</string>
+ <!-- no translation found for download_sdcard_busy_dlg_msg (3473883538192835204) -->
+ <skip />
+ <string name="download_no_application">"Не удалось найти приложение для открытия этого файла."</string>
+ <string name="retry">"Повторить"</string>
+ <string name="no_downloads">"История загрузок пуста."</string>
+ <string name="download_error">"Загрузка безуспешна."</string>
+ <string name="download_success">"Загрузка файла <xliff:g id="FILE">%s</xliff:g> завершена."</string>
+ <string name="download_running">"Выполняется загрузка…"</string>
+ <string name="download_pending">"Загрузка начинается…"</string>
+ <string name="download_pending_network">"Ожидание подключения для передачи данных…"</string>
+ <string name="download_running_paused">"Ожидание подключения для передачи данных…"</string>
+ <string name="download_canceled">"Загрузка отменена."</string>
+ <string name="download_not_acceptable">"Загрузка невозможна. Содержание не поддерживается телефоном."</string>
+ <string name="download_file_error">"Невозможно завершить загрузку. Недостаточно места."</string>
+ <string name="download_length_required">"Загрузка невозможна. Не удается определить размер элемента."</string>
+ <string name="download_precondition_failed">"Загрузка прервана и не может быть продолжена."</string>
+ <string name="activity_instrumentation_test_runner">"Проверка браузера"</string>
+ <string name="search_google">"Поиск в Google"</string>
+ <string name="permlab_readHistoryBookmarks">"считывать историю и закладки браузера"</string>
+ <string name="permdesc_readHistoryBookmarks">"Позволяет приложению считывать все URL, открытые через браузер, и все закладки браузера."</string>
+ <string name="permlab_writeHistoryBookmarks">"записывать историю и закладки браузера"</string>
+ <string name="permdesc_writeHistoryBookmarks">"Позволяет приложению изменять сохраненные в телефоне историю и закладки браузера. Вредоносное ПО сможет стирать или изменять данные браузера."</string>
+ <!-- no translation found for query_data_prompt (5474381240981604223) -->
+ <skip />
+ <!-- no translation found for query_data_message (8789381063185445197) -->
+ <skip />
+ <!-- no translation found for location_prompt (226262202057302423) -->
+ <skip />
+ <!-- no translation found for location_message (1729456751935683242) -->
+ <skip />
+ <!-- no translation found for shortcut_prompt (437193299088893596) -->
+ <skip />
+ <!-- no translation found for shortcut_message (4793042709293755892) -->
+ <skip />
+ <string name="settings_message">"В таблице ниже показаны разрешения, которые вы дали сайтам, пытавшимся использовать Gears."</string>
+ <!-- no translation found for filepicker_message (4929726371602896039) -->
+ <skip />
+ <!-- no translation found for image_message (5450245866521896891) -->
+ <skip />
+ <string name="settings_title">"Настройки Gears"</string>
+ <string name="privacy_policy">"Прочитайте политику конфиденциальности сайта, чтобы узнать, как будут использованы данные о вашем местоположении."</string>
+ <!-- no translation found for settings_storage_title (7089119630457156408) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_on (2733295483569432111) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_off (1338039396087898813) -->
+ <skip />
+ <!-- no translation found for settings_location_title (4953062923509886651) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_on (1496219621849158879) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_off (5216466051810596189) -->
+ <skip />
+ <!-- no translation found for settings_remove_site (1822247070226589958) -->
+ <skip />
+ <!-- no translation found for settings_empty (2008952224378583146) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove_title (5360857296753219635) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove (585560170733446898) -->
+ <skip />
+ <!-- no translation found for storage_notification (6534213782873968255) -->
+ <skip />
+ <!-- no translation found for storage_notification_alwaysdeny (5473208033665783513) -->
+ <skip />
+ <!-- no translation found for location_notification (3495804750470905804) -->
+ <skip />
+ <!-- no translation found for location_notification_alwaysdeny (6184925953783312629) -->
+ <skip />
+ <!-- no translation found for shortcut_notification (1659700220868444568) -->
+ <skip />
+ <!-- no translation found for permission_button_alwaysdeny (3837055994905214848) -->
+ <skip />
+ <!-- no translation found for permission_button_allow (7301874925704148242) -->
+ <skip />
+ <!-- no translation found for permission_button_deny (104712269725153018) -->
+ <skip />
+ <!-- no translation found for shortcut_button_alwaysdeny (4172277731325126605) -->
+ <skip />
+ <string name="shortcut_button_allow">"ОК"</string>
+ <string name="shortcut_button_deny">"Отмена"</string>
+ <string name="settings_button_allow">"Применить"</string>
+ <string name="settings_button_deny">"Отмена"</string>
+ <string name="filepicker_button_allow">"ОК"</string>
+ <string name="filepicker_button_deny">"Отмена"</string>
+ <string name="filepicker_path">"путь:"</string>
+ <string name="filepicker_no_files_selected">"Файлы не выбраны"</string>
+ <string name="filepicker_one_file_selected">"Выбран один файл"</string>
+ <string name="filepicker_some_files_selected">"файлов выбрано"</string>
+ <string name="remove">"Удалить"</string>
+ <string name="local_storage">"Локальное хранение"</string>
+ <string name="allowed">"Разрешено"</string>
+ <string name="denied">"Запрещено"</string>
+ <string name="unrecognized_dialog_message">"Неизвестный тип диалога"</string>
+ <string name="default_button">"ОК"</string>
+ <string name="zoom_overview_button_text">"Обзор"</string>
+</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
new file mode 100644
index 00000000..a55556b0
--- /dev/null
+++ b/res/values-zh-rCN/strings.xml
@@ -0,0 +1,334 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name">"浏览器"</string>
+ <string name="tab_bookmarks">"书签"</string>
+ <string name="tab_most_visited">"访问最多的网站"</string>
+ <string name="tab_history">"历史记录"</string>
+ <string name="added_to_bookmarks">"添加到书签"</string>
+ <string name="removed_from_bookmarks">"从书签中删除"</string>
+ <string name="sign_in_to">"登录至 <xliff:g id="HOSTNAME">%s1</xliff:g>“<xliff:g id="REALM">%s2</xliff:g>”"</string>
+ <string name="username">"名称"</string>
+ <string name="password">"密码"</string>
+ <string name="action">"登录"</string>
+ <string name="cancel">"取消"</string>
+ <string name="ok">"确定"</string>
+ <string name="matches_found">" 匹配项"</string>
+ <string name="zero">"否"</string>
+ <string name="page_info">"页面信息"</string>
+ <string name="page_info_view">"查看页面信息"</string>
+ <string name="page_info_address">"地址:"</string>
+ <string name="ssl_warnings_header">"该网站的安全证书有问题。"</string>
+ <string name="ssl_continue">"继续"</string>
+ <string name="security_warning">"安全警告"</string>
+ <string name="view_certificate">"查看证书"</string>
+ <string name="ssl_untrusted">"该证书并非来自可信的授权机构。"</string>
+ <string name="ssl_mismatch">"网站的名称与证书上的名称不一致。"</string>
+ <string name="ssl_expired">"该证书已过期。"</string>
+ <string name="ssl_not_yet_valid">"该证书尚未生效。"</string>
+ <string name="ssl_certificate">"安全证书"</string>
+ <string name="ssl_certificate_is_valid">"该证书有效。"</string>
+ <string name="issued_to">"发布位置:"</string>
+ <string name="common_name">"常用名称:"</string>
+ <string name="org_name">"组织:"</string>
+ <string name="org_unit">"组织单位:"</string>
+ <string name="issued_by">"发布者:"</string>
+ <string name="validity_period">"有效性:"</string>
+ <string name="issued_on">"发布时间:"</string>
+ <string name="expires_on">"有效期至:"</string>
+ <string name="retrieving_creds_dlg_msg">"正在检索登录详细信息..."</string>
+ <string name="close">"关闭"</string>
+ <string name="close_window">"该窗口会关闭。"</string>
+ <string name="stopping">"正在停止..."</string>
+ <string name="stop">"停止"</string>
+ <string name="reload">"刷新"</string>
+ <string name="forward">"前进"</string>
+ <string name="save">"确定"</string>
+ <string name="do_not_save">"取消"</string>
+ <string name="location">"位置"</string>
+ <string name="name">"名称"</string>
+ <string name="http">"http://"</string>
+ <string name="save_to_bookmarks">"书签"</string>
+ <string name="edit_bookmark">"编辑书签"</string>
+ <string name="create_shortcut_bookmark">"向“主页”添加快捷方式"</string>
+ <string name="open_bookmark">"打开"</string>
+ <string name="remove_bookmark">"删除书签"</string>
+ <string name="remove_history_item">"从历史记录中删除"</string>
+ <string name="bookmark_saved">"保存至书签。"</string>
+ <string name="bookmark_needs_title">"书签必须具有名称。"</string>
+ <string name="bookmark_needs_url">"书签必须具有位置。"</string>
+ <string name="empty_bookmark">"无法创建空书签。"</string>
+ <string name="bookmark_url_not_valid">"网址无效。"</string>
+ <string name="delete_bookmark">"删除"</string>
+ <string name="bookmark_page">"将上次查看过的页面加为书签"</string>
+ <string name="current_page">"来源 "</string>
+ <string name="delete_bookmark_warning">"会删除书签“<xliff:g id="BOOKMARK">%s</xliff:g>”。"</string>
+ <string name="open_in_new_window">"在新窗口中打开"</string>
+ <string name="new_window">"新窗口"</string>
+ <string name="goto_dot">"开始"</string>
+ <string name="find_dot">"在页面上查找"</string>
+ <string name="select_dot">"选择文本"</string>
+ <string name="view_tabs">"窗口概述"</string>
+ <string name="view_tabs_condensed">"窗口"</string>
+ <string name="tab_picker_title">"当前窗口"</string>
+ <string name="tab_picker_view_tab">"查看"</string>
+ <string name="tab_picker_new_tab">"新窗口"</string>
+ <string name="tab_picker_remove_tab">"关闭"</string>
+ <string name="tab_picker_bookmark">"书签"</string>
+ <string name="tab_picker_send_url">"共享链接"</string>
+ <string name="bookmarks">"书签"</string>
+ <string name="shortcut_bookmark">"书签"</string>
+ <string name="history">"历史记录"</string>
+ <string name="menu_view_download">"下载"</string>
+ <string name="share_page">"共享页面"</string>
+ <string name="contextmenu_openlink">"打开"</string>
+ <string name="contextmenu_openlink_newwindow">"在新窗口中打开"</string>
+ <string name="contextmenu_bookmark_thislink">"书签链接"</string>
+ <string name="contextmenu_savelink">"保存链接"</string>
+ <string name="contextmenu_sharelink">"共享链接"</string>
+ <string name="contextmenu_copy">"复制"</string>
+ <string name="contextmenu_copylink">"复制链接网址"</string>
+ <string name="contextmenu_download_image">"保存图片"</string>
+ <string name="contextmenu_view_image">"查看图片"</string>
+ <string name="contextmenu_dial_dot">"拔号..."</string>
+ <string name="contextmenu_add_contact">"添加联系人"</string>
+ <string name="contextmenu_send_mail">"发送电子邮件"</string>
+ <string name="contextmenu_map">"地图"</string>
+ <string name="clear">"清除"</string>
+ <string name="replace">"替换"</string>
+ <string name="browser_bookmarks_page_bookmarks_text">"书签"</string>
+ <string name="menu_preferences">"设置"</string>
+ <string name="pref_content_title">"页面内容设置"</string>
+ <string name="pref_content_load_images">"载入图片"</string>
+ <string name="pref_content_load_images_summary">"在网页上显示图片"</string>
+ <string name="pref_content_block_popups">"阻止弹出窗口"</string>
+ <string name="pref_content_javascript">"启用 JavaScript"</string>
+ <string name="pref_content_open_in_background">"在后台打开"</string>
+ <string name="pref_content_open_in_background_summary">"新窗口会在当前窗口后方打开"</string>
+ <string name="pref_content_homepage">"设置主页"</string>
+ <string name="pref_content_autofit">"自动适应页面"</string>
+ <string name="pref_content_autofit_summary">"调整网页格式以适合屏幕大小"</string>
+ <string name="pref_privacy_title">"隐私设置"</string>
+ <string name="pref_privacy_clear_cache">"清除缓存"</string>
+ <string name="pref_privacy_clear_cache_summary">"删除所有缓存的页面内容"</string>
+ <string name="pref_privacy_clear_cache_dlg">"会清除缓存。"</string>
+ <string name="pref_privacy_clear_cookies">"清除所有 Cookie 数据"</string>
+ <string name="pref_privacy_clear_cookies_summary">"清除所有浏览器 Cookie"</string>
+ <string name="pref_privacy_clear_cookies_dlg">"会清除所有 Cookie。"</string>
+ <string name="pref_privacy_clear_history">"清除历史记录"</string>
+ <string name="pref_privacy_clear_history_summary">"清除浏览器导航历史记录"</string>
+ <string name="pref_privacy_clear_history_dlg">"会清除浏览器导航历史记录。"</string>
+ <string name="pref_privacy_clear_form_data">"清除表单数据"</string>
+ <string name="pref_privacy_clear_form_data_summary">"清除所有已保存的表单数据"</string>
+ <string name="pref_privacy_clear_form_data_dlg">"会清除所有保存的表单数据。"</string>
+ <string name="pref_privacy_clear_passwords">"清除密码"</string>
+ <string name="pref_privacy_clear_passwords_summary">"清除所有保存的密码"</string>
+ <string name="pref_privacy_clear_passwords_dlg">"会清除所有保存的密码。"</string>
+ <string name="pref_security_title">"安全设置"</string>
+ <string name="pref_security_remember_passwords">"记住密码"</string>
+ <string name="pref_security_remember_passwords_summary">"保存用于网站的用户名和密码"</string>
+ <string name="pref_security_save_form_data">"记住表单数据"</string>
+ <string name="pref_security_save_form_data_summary">"记住我在表单中键入的数据以供以后使用"</string>
+ <string name="pref_security_show_security_warning">"显示安全警告"</string>
+ <string name="pref_security_show_security_warning_summary">"当网站的安全有问题时显示警告"</string>
+ <string name="pref_security_accept_cookies">"接受 Cookie"</string>
+ <string name="pref_security_accept_cookies_summary">"允许网站保存并读取“Cookie”数据"</string>
+ <string name="pref_text_size">"设置文字大小"</string>
+ <string-array name="pref_text_size_choices">
+ <item>"极小"</item>
+ <item>"小"</item>
+ <item>"正常"</item>
+ <item>"大"</item>
+ <item>"极大"</item>
+ </string-array>
+ <string name="pref_text_size_dialogtitle">"文字大小"</string>
+ <string name="pref_extras_title">"高级设置"</string>
+ <string name="pref_extras_gears_enable">"启用 Gears"</string>
+ <string name="pref_extras_gears_enable_summary">"扩展浏览器功能的应用程序"</string>
+ <string name="pref_extras_gears_settings">"Gears 设置"</string>
+ <string name="pref_plugin_installed">"插件列表"</string>
+ <string name="pref_plugin_installed_empty_list">"没有已安装的插件。"</string>
+ <string name="pref_extras_gears_settings_summary">"扩展浏览器功能的应用程序"</string>
+ <string name="pref_extras_reset_default">"重置为默认值"</string>
+ <string name="pref_extras_reset_default_summary">"清除所有浏览器数据并将所有设置重置为默认值"</string>
+ <string name="pref_extras_reset_default_dlg">"会清除所有浏览器数据并且设置会恢复为默认值。"</string>
+ <string name="pref_extras_reset_default_dlg_title">"重置为默认值"</string>
+ <string name="pref_development_title">"调试"</string>
+ <string name="pref_development_viewport">"使用宽视口"</string>
+ <string name="pref_development_single_column_rendering">"单列呈现"</string>
+ <string name="pref_development_normal_rendering">"正常呈现"</string>
+ <string name="pref_development_trace">"启用跟踪"</string>
+ <string name="pref_development_nav_dump">"启用导航缓存转储"</string>
+ <string name="pref_development_search_url">"设置搜索网址"</string>
+ <string name="pref_development_uastring">"UAString"</string>
+ <string-array name="pref_development_ua_choices">
+ <item>"Android"</item>
+ <item>"桌面"</item>
+ <item>"iPhone"</item>
+ </string-array>
+ <string-array name="pref_development_ua_values">
+ <item>"0"</item>
+ <item>"1"</item>
+ <item>"2"</item>
+ </string-array>
+ <string name="pref_default_text_encoding">"设置文字编码"</string>
+ <string-array name="pref_default_text_encoding_choices">
+ <item>"Latin-1 (ISO-8859-1)"</item>
+ <item>"Unicode (UTF-8)"</item>
+ <item>"Japanese (ISO-2022-JP)"</item>
+ <item>"Japanese (SHIFT_JIS)"</item>
+ <item>"Japanese (EUC-JP)"</item>
+ </string-array>
+ <string name="pref_default_text_encoding_dialogtitle">"文字编码"</string>
+ <string name="pref_default_text_encoding_default">"Latin-1"</string>
+ <string name="browserFrameRedirect">"重定向"</string>
+ <string name="browserFrame307Post">"该网页正被重定向。是否将您键入的表单数据重新发送到新位置?"</string>
+ <string name="browserFrameNetworkErrorLabel">"数据连接性问题"</string>
+ <string name="browserFrameFileErrorLabel">"文件有问题"</string>
+ <string name="browserFrameFormResubmitLabel">"确认"</string>
+ <string name="browserFrameFormResubmitMessage">"您尝试查看的页面中包含已经提交的数据(“POSTDATA”)。如果重新发送数据,系统会重复该页面上的表单所执行过的一切操作(如搜索或在线购买)。"</string>
+ <string name="loadSuspendedTitle">"没有网络连接"</string>
+ <string name="loadSuspended">"连接恢复后页面会继续载入。"</string>
+ <string name="clear_history">"清除历史记录"</string>
+ <string name="browser_history">"最近访问过的页面"</string>
+ <string name="empty_history">"浏览器历史记录为空。"</string>
+ <string name="add_new_bookmark">"添加书签..."</string>
+ <string name="no_database">"没有数据库!"</string>
+ <string name="search_hint">"键入网址"</string>
+ <string name="search_button_text">"转至"</string>
+ <string name="attention">"注意"</string>
+ <string name="popup_window_attempt">"该网站正试图打开一个弹出窗口。"</string>
+ <string name="allow">"允许"</string>
+ <string name="block">"阻止"</string>
+ <string name="too_many_windows_dialog_title">"已达到窗口限制"</string>
+ <string name="too_many_windows_dialog_message">"无法打开新窗口,因为您打开的窗口数已达到允许的最高数目。"</string>
+ <string name="too_many_subwindows_dialog_title">"弹出窗口已经打开"</string>
+ <string name="too_many_subwindows_dialog_message">"无法打开新的弹出窗口,因为不管何时一次都只能打开一个。"</string>
+ <string name="download_title">"下载历史记录"</string>
+ <string name="download_unknown_filename">"&lt;未知&gt;"</string>
+ <string name="download_menu_open">"打开"</string>
+ <string name="download_menu_clear">"从列表中清除"</string>
+ <string name="download_menu_cancel">"取消下载"</string>
+ <string name="download_menu_cancel_all">"取消所有下载"</string>
+ <string name="download_menu_clear_all">"清除列表"</string>
+ <string name="download_clear_dlg_title">"清除"</string>
+ <string name="download_clear_dlg_msg">"所有条目都会从列表中清除并会从浏览器缓存中删除。"</string>
+ <string name="download_cancel_dlg_title">"取消下载"</string>
+ <string name="download_cancel_dlg_msg">"所有 <xliff:g id="DOWNLOAD_COUNT">%d</xliff:g> 个下载都会取消并从下载历史记录中清除。"</string>
+ <string name="download_file_error_dlg_title">"空间不足"</string>
+ <string name="download_file_error_dlg_msg">"无法下载<xliff:g id="FILENAME">%s</xliff:g>。"\n"请释放手机中的一些空间并重试。"</string>
+ <string name="download_failed_generic_dlg_title">"下载不成功"</string>
+ <string name="download_no_sdcard_dlg_title">"无 SD 卡"</string>
+ <string name="download_no_sdcard_dlg_msg">"需要 SD 卡才能下载 <xliff:g id="FILENAME">%s</xliff:g>。"</string>
+ <string name="download_sdcard_busy_dlg_title">"SD 卡不可用"</string>
+ <!-- no translation found for download_sdcard_busy_dlg_msg (3473883538192835204) -->
+ <skip />
+ <string name="download_no_application">"无法找到可打开此文件的应用程序。"</string>
+ <string name="retry">"重试"</string>
+ <string name="no_downloads">"下载历史记录为空。"</string>
+ <string name="download_error">"下载不成功。"</string>
+ <string name="download_success">"<xliff:g id="FILE">%s</xliff:g>下载完成。"</string>
+ <string name="download_running">"正在下载..."</string>
+ <string name="download_pending">"正在开始下载..."</string>
+ <string name="download_pending_network">"正在等待数据连接..."</string>
+ <string name="download_running_paused">"正在等待数据连接..."</string>
+ <string name="download_canceled">"下载已取消。"</string>
+ <string name="download_not_acceptable">"无法下载。手机不支持该内容。"</string>
+ <string name="download_file_error">"无法完成下载。存储空间不足。"</string>
+ <string name="download_length_required">"无法下载。无法确定该项的大小。"</string>
+ <string name="download_precondition_failed">"下载中断,无法重新开始。"</string>
+ <string name="activity_instrumentation_test_runner">"浏览器测试运行器"</string>
+ <string name="search_google">"使用 Google 进行搜索"</string>
+ <string name="permlab_readHistoryBookmarks">"读取浏览器的历史记录和书签"</string>
+ <string name="permdesc_readHistoryBookmarks">"允许应用程序读取浏览器访问过的所有网址以及浏览器的所有书签。"</string>
+ <string name="permlab_writeHistoryBookmarks">"写入浏览器的历史记录和书签"</string>
+ <string name="permdesc_writeHistoryBookmarks">"允许应用程序修改您手机中存储的浏览器的历史记录或书签。恶意应用程序可能会利用此权限清除或修改您浏览器的数据。"</string>
+ <!-- no translation found for query_data_prompt (5474381240981604223) -->
+ <skip />
+ <!-- no translation found for query_data_message (8789381063185445197) -->
+ <skip />
+ <!-- no translation found for location_prompt (226262202057302423) -->
+ <skip />
+ <!-- no translation found for location_message (1729456751935683242) -->
+ <skip />
+ <!-- no translation found for shortcut_prompt (437193299088893596) -->
+ <skip />
+ <!-- no translation found for shortcut_message (4793042709293755892) -->
+ <skip />
+ <string name="settings_message">"下表显示了您授予尝试使用 Gears 的各个网站的权限。"</string>
+ <!-- no translation found for filepicker_message (4929726371602896039) -->
+ <skip />
+ <!-- no translation found for image_message (5450245866521896891) -->
+ <skip />
+ <string name="settings_title">"Gears 设置"</string>
+ <string name="privacy_policy">"阅读网站的隐私政策,以了解会如何使用您的位置。"</string>
+ <!-- no translation found for settings_storage_title (7089119630457156408) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_on (2733295483569432111) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_off (1338039396087898813) -->
+ <skip />
+ <!-- no translation found for settings_location_title (4953062923509886651) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_on (1496219621849158879) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_off (5216466051810596189) -->
+ <skip />
+ <!-- no translation found for settings_remove_site (1822247070226589958) -->
+ <skip />
+ <!-- no translation found for settings_empty (2008952224378583146) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove_title (5360857296753219635) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove (585560170733446898) -->
+ <skip />
+ <!-- no translation found for storage_notification (6534213782873968255) -->
+ <skip />
+ <!-- no translation found for storage_notification_alwaysdeny (5473208033665783513) -->
+ <skip />
+ <!-- no translation found for location_notification (3495804750470905804) -->
+ <skip />
+ <!-- no translation found for location_notification_alwaysdeny (6184925953783312629) -->
+ <skip />
+ <!-- no translation found for shortcut_notification (1659700220868444568) -->
+ <skip />
+ <!-- no translation found for permission_button_alwaysdeny (3837055994905214848) -->
+ <skip />
+ <!-- no translation found for permission_button_allow (7301874925704148242) -->
+ <skip />
+ <!-- no translation found for permission_button_deny (104712269725153018) -->
+ <skip />
+ <!-- no translation found for shortcut_button_alwaysdeny (4172277731325126605) -->
+ <skip />
+ <string name="shortcut_button_allow">"确定"</string>
+ <string name="shortcut_button_deny">"取消"</string>
+ <string name="settings_button_allow">"应用"</string>
+ <string name="settings_button_deny">"取消"</string>
+ <string name="filepicker_button_allow">"确定"</string>
+ <string name="filepicker_button_deny">"取消"</string>
+ <string name="filepicker_path">"路径:"</string>
+ <string name="filepicker_no_files_selected">"未选择文件"</string>
+ <string name="filepicker_one_file_selected">"选择了一个文件"</string>
+ <string name="filepicker_some_files_selected">"选择了文件"</string>
+ <string name="remove">"删除"</string>
+ <string name="local_storage">"本地存储"</string>
+ <string name="allowed">"允许"</string>
+ <string name="denied">"已拒绝"</string>
+ <string name="unrecognized_dialog_message">"未识别的对话类型"</string>
+ <string name="default_button">"确定"</string>
+ <string name="zoom_overview_button_text">"概览"</string>
+</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
new file mode 100644
index 00000000..3dd3ec78
--- /dev/null
+++ b/res/values-zh-rTW/strings.xml
@@ -0,0 +1,334 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name">"瀏覽器"</string>
+ <string name="tab_bookmarks">"書籤"</string>
+ <string name="tab_most_visited">"常用網頁"</string>
+ <string name="tab_history">"紀錄"</string>
+ <string name="added_to_bookmarks">"已加入書籤"</string>
+ <string name="removed_from_bookmarks">"已自書籤中移除"</string>
+ <string name="sign_in_to">"登入到 <xliff:g id="HOSTNAME">%s1</xliff:g>「<xliff:g id="REALM">%s2</xliff:g>」"</string>
+ <string name="username">"名字"</string>
+ <string name="password">"密碼"</string>
+ <string name="action">"登入"</string>
+ <string name="cancel">"取消"</string>
+ <string name="ok">"確定"</string>
+ <string name="matches_found">" 個符合項目"</string>
+ <string name="zero">"否"</string>
+ <string name="page_info">"頁面資訊"</string>
+ <string name="page_info_view">"檢視頁面資訊"</string>
+ <string name="page_info_address">"位址:"</string>
+ <string name="ssl_warnings_header">"此網站的安全性憑證有問題。"</string>
+ <string name="ssl_continue">"繼續"</string>
+ <string name="security_warning">"安全性警告"</string>
+ <string name="view_certificate">"檢視憑證"</string>
+ <string name="ssl_untrusted">"此憑證來自未信任的機構。"</string>
+ <string name="ssl_mismatch">"網站名稱與憑證不符。"</string>
+ <string name="ssl_expired">"此憑證已過期"</string>
+ <string name="ssl_not_yet_valid">"此憑證無效。"</string>
+ <string name="ssl_certificate">"安全憑證"</string>
+ <string name="ssl_certificate_is_valid">"這個憑證沒有問題。"</string>
+ <string name="issued_to">"發給:"</string>
+ <string name="common_name">"好記的名稱:"</string>
+ <string name="org_name">"組織:"</string>
+ <string name="org_unit">"組織單位:"</string>
+ <string name="issued_by">"發行者:"</string>
+ <string name="validity_period">"合法性:"</string>
+ <string name="issued_on">"發給:"</string>
+ <string name="expires_on">"有效日期:"</string>
+ <string name="retrieving_creds_dlg_msg">"取回登入資訊…"</string>
+ <string name="close">"關閉"</string>
+ <string name="close_window">"視窗即將關閉。"</string>
+ <string name="stopping">"停止中…"</string>
+ <string name="stop">"停止"</string>
+ <string name="reload">"重新整理"</string>
+ <string name="forward">"下一頁"</string>
+ <string name="save">"確定"</string>
+ <string name="do_not_save">"取消"</string>
+ <string name="location">"位置"</string>
+ <string name="name">"名字"</string>
+ <string name="http">"http://"</string>
+ <string name="save_to_bookmarks">"書籤"</string>
+ <string name="edit_bookmark">"編輯書籤"</string>
+ <string name="create_shortcut_bookmark">"新增捷徑至首頁"</string>
+ <string name="open_bookmark">"開啟"</string>
+ <string name="remove_bookmark">"刪除書籤"</string>
+ <string name="remove_history_item">"從紀錄中移除"</string>
+ <string name="bookmark_saved">"儲存至書籤。"</string>
+ <string name="bookmark_needs_title">"請輸入書籤名字。"</string>
+ <string name="bookmark_needs_url">"請選取書籤位置。"</string>
+ <string name="empty_bookmark">"無法建立空白書籤。"</string>
+ <string name="bookmark_url_not_valid">"網址無效。"</string>
+ <string name="delete_bookmark">"刪除"</string>
+ <string name="bookmark_page">"最後瀏覽的書籤頁面"</string>
+ <string name="current_page">"來源 "</string>
+ <string name="delete_bookmark_warning">"<xliff:g id="BOOKMARK">%s</xliff:g>"</string>
+ <string name="open_in_new_window">"在新視窗開啟"</string>
+ <string name="new_window">"新視窗"</string>
+ <string name="goto_dot">"前往"</string>
+ <string name="find_dot">"在頁面中尋找"</string>
+ <string name="select_dot">"選取文字"</string>
+ <string name="view_tabs">"視窗總覽"</string>
+ <string name="view_tabs_condensed">"視窗"</string>
+ <string name="tab_picker_title">"目前視窗"</string>
+ <string name="tab_picker_view_tab">"檢視"</string>
+ <string name="tab_picker_new_tab">"新視窗"</string>
+ <string name="tab_picker_remove_tab">"關閉"</string>
+ <string name="tab_picker_bookmark">"書籤"</string>
+ <string name="tab_picker_send_url">"分享連結"</string>
+ <string name="bookmarks">"書籤"</string>
+ <string name="shortcut_bookmark">"書籤"</string>
+ <string name="history">"紀錄"</string>
+ <string name="menu_view_download">"下載"</string>
+ <string name="share_page">"分享網頁"</string>
+ <string name="contextmenu_openlink">"開啟"</string>
+ <string name="contextmenu_openlink_newwindow">"在新視窗開啟"</string>
+ <string name="contextmenu_bookmark_thislink">"書籤連結"</string>
+ <string name="contextmenu_savelink">"儲存連結"</string>
+ <string name="contextmenu_sharelink">"分享連結"</string>
+ <string name="contextmenu_copy">"複製"</string>
+ <string name="contextmenu_copylink">"複製連結網址"</string>
+ <string name="contextmenu_download_image">"儲存圖片"</string>
+ <string name="contextmenu_view_image">"檢視圖片"</string>
+ <string name="contextmenu_dial_dot">"撥打…"</string>
+ <string name="contextmenu_add_contact">"新增連絡人"</string>
+ <string name="contextmenu_send_mail">"傳送電子郵件"</string>
+ <string name="contextmenu_map">"地圖"</string>
+ <string name="clear">"清除"</string>
+ <string name="replace">"取代"</string>
+ <string name="browser_bookmarks_page_bookmarks_text">"書籤"</string>
+ <string name="menu_preferences">"設定"</string>
+ <string name="pref_content_title">"頁面內容設定"</string>
+ <string name="pref_content_load_images">"載入圖片"</string>
+ <string name="pref_content_load_images_summary">"在網頁上顯示圖片"</string>
+ <string name="pref_content_block_popups">"阻擋快顯視窗"</string>
+ <string name="pref_content_javascript">"啟用 JavaScript"</string>
+ <string name="pref_content_open_in_background">"在背景開啟"</string>
+ <string name="pref_content_open_in_background_summary">"新分頁開在目前頁面後面"</string>
+ <string name="pref_content_homepage">"設定首頁"</string>
+ <string name="pref_content_autofit">"自動調整頁面"</string>
+ <string name="pref_content_autofit_summary">"將網頁調整至適合螢幕大小"</string>
+ <string name="pref_privacy_title">"隱私設定"</string>
+ <string name="pref_privacy_clear_cache">"清除快取"</string>
+ <string name="pref_privacy_clear_cache_summary">"刪除所有快取頁面內容"</string>
+ <string name="pref_privacy_clear_cache_dlg">"快取會被清除"</string>
+ <string name="pref_privacy_clear_cookies">"清除所有 cookie"</string>
+ <string name="pref_privacy_clear_cookies_summary">"清除所有 cookie"</string>
+ <string name="pref_privacy_clear_cookies_dlg">"所有 cookie 會被清除。"</string>
+ <string name="pref_privacy_clear_history">"清除紀錄"</string>
+ <string name="pref_privacy_clear_history_summary">"清除瀏覽記錄"</string>
+ <string name="pref_privacy_clear_history_dlg">"瀏覽記錄會被清除。"</string>
+ <string name="pref_privacy_clear_form_data">"清除表單資訊"</string>
+ <string name="pref_privacy_clear_form_data_summary">"清除所有表單"</string>
+ <string name="pref_privacy_clear_form_data_dlg">"所有儲存的資料會被清除。"</string>
+ <string name="pref_privacy_clear_passwords">"清除密碼"</string>
+ <string name="pref_privacy_clear_passwords_summary">"清除所有儲存密碼"</string>
+ <string name="pref_privacy_clear_passwords_dlg">"所有儲存密碼會被清除"</string>
+ <string name="pref_security_title">"安全性設定"</string>
+ <string name="pref_security_remember_passwords">"記住密碼"</string>
+ <string name="pref_security_remember_passwords_summary">"儲存網站使用者名稱與密碼"</string>
+ <string name="pref_security_save_form_data">"儲存表單資訊"</string>
+ <string name="pref_security_save_form_data_summary">"儲存輸入的表單資料"</string>
+ <string name="pref_security_show_security_warning">"顯示安全性警告"</string>
+ <string name="pref_security_show_security_warning_summary">"當網站安全性有問題時,顯示警告訊息"</string>
+ <string name="pref_security_accept_cookies">"接受 cookie"</string>
+ <string name="pref_security_accept_cookies_summary">"允許網站存取 cookie"</string>
+ <string name="pref_text_size">"設定文字大小"</string>
+ <string-array name="pref_text_size_choices">
+ <item>"極小"</item>
+ <item>"小"</item>
+ <item>"一般"</item>
+ <item>"大"</item>
+ <item>"特大"</item>
+ </string-array>
+ <string name="pref_text_size_dialogtitle">"文字大小"</string>
+ <string name="pref_extras_title">"進階設定"</string>
+ <string name="pref_extras_gears_enable">"啟用裝置"</string>
+ <string name="pref_extras_gears_enable_summary">"可擴充瀏覽器功能的應用程式"</string>
+ <string name="pref_extras_gears_settings">"裝置設定"</string>
+ <string name="pref_plugin_installed">"外掛程式清單"</string>
+ <string name="pref_plugin_installed_empty_list">"未安裝外掛程式。"</string>
+ <string name="pref_extras_gears_settings_summary">"可擴充瀏覽器功能的應用程式"</string>
+ <string name="pref_extras_reset_default">"回復初始設定"</string>
+ <string name="pref_extras_reset_default_summary">"清除瀏覽器所有資料,並回復初始設定"</string>
+ <string name="pref_extras_reset_default_dlg">"瀏覽器所有資料會被清除, 並回復初始設定。"</string>
+ <string name="pref_extras_reset_default_dlg_title">"回復初始設定"</string>
+ <string name="pref_development_title">"除錯"</string>
+ <string name="pref_development_viewport">"Use wide viewport"</string>
+ <string name="pref_development_single_column_rendering">"單欄模式"</string>
+ <string name="pref_development_normal_rendering">"Normal rendering"</string>
+ <string name="pref_development_trace">"Enable tracing"</string>
+ <string name="pref_development_nav_dump">"Enable nav cache dump"</string>
+ <string name="pref_development_search_url">"設定搜尋 URL"</string>
+ <string name="pref_development_uastring">"UAString"</string>
+ <string-array name="pref_development_ua_choices">
+ <item>"Android"</item>
+ <item>"桌面"</item>
+ <item>"iPhone"</item>
+ </string-array>
+ <string-array name="pref_development_ua_values">
+ <item>"0"</item>
+ <item>"1"</item>
+ <item>"2"</item>
+ </string-array>
+ <string name="pref_default_text_encoding">"設定文字編碼"</string>
+ <string-array name="pref_default_text_encoding_choices">
+ <item>"Latin-1 (ISO-8859-1)"</item>
+ <item>"Unicode (UTF-8)"</item>
+ <item>"Japanese (ISO-2022-JP)"</item>
+ <item>"Japanese (SHIFT_JIS)"</item>
+ <item>"Japanese (EUC-JP)"</item>
+ </string-array>
+ <string name="pref_default_text_encoding_dialogtitle">"文字編碼"</string>
+ <string name="pref_default_text_encoding_default">"Latin-1"</string>
+ <string name="browserFrameRedirect">"轉址"</string>
+ <string name="browserFrame307Post">"此網頁即將轉向其他頁面。您是否要將輸入的表單資料傳送到新頁面?"</string>
+ <string name="browserFrameNetworkErrorLabel">"資料連線問題"</string>
+ <string name="browserFrameFileErrorLabel">"檔案有問題"</string>
+ <string name="browserFrameFormResubmitLabel">"確認"</string>
+ <string name="browserFrameFormResubmitMessage">"您正在瀏覽的頁面包含已傳送的 (「POSTDATA」),若重新送出資料,此頁表單中的資料會重複 (例如:搜尋結果、線上購物狀態)。"</string>
+ <string name="loadSuspendedTitle">"沒有網路連線"</string>
+ <string name="loadSuspended">"連線恢復後,此頁面會繼續載入。"</string>
+ <string name="clear_history">"清除紀錄"</string>
+ <string name="browser_history">"最近瀏覽的頁面"</string>
+ <string name="empty_history">"瀏覽記錄是空的。"</string>
+ <string name="add_new_bookmark">"加入書籤…"</string>
+ <string name="no_database">"沒有資料!"</string>
+ <string name="search_hint">"輸入網頁位址"</string>
+ <string name="search_button_text">"前往"</string>
+ <string name="attention">"注意"</string>
+ <string name="popup_window_attempt">"此網站試圖開啟快顯視窗。"</string>
+ <string name="allow">"允許"</string>
+ <string name="block">"阻擋"</string>
+ <string name="too_many_windows_dialog_title">"已達視窗數量限制"</string>
+ <string name="too_many_windows_dialog_message">"已達最大視窗數量限制,無法開啟新視窗。"</string>
+ <string name="too_many_subwindows_dialog_title">"已開啟快顯視窗"</string>
+ <string name="too_many_subwindows_dialog_message">"無法開啟新的快顯視窗,同時只能有一個快顯視窗存在。"</string>
+ <string name="download_title">"下載記錄"</string>
+ <string name="download_unknown_filename">"&lt;未知&gt;"</string>
+ <string name="download_menu_open">"開啟"</string>
+ <string name="download_menu_clear">"清除表單列表"</string>
+ <string name="download_menu_cancel">"取消下載"</string>
+ <string name="download_menu_cancel_all">"取消所有下載"</string>
+ <string name="download_menu_clear_all">"清除清單"</string>
+ <string name="download_clear_dlg_title">"清除"</string>
+ <string name="download_clear_dlg_msg">"所有清單中的項目會被清除,並從快取中移除。"</string>
+ <string name="download_cancel_dlg_title">"取消下載"</string>
+ <string name="download_cancel_dlg_msg">"所有 (<xliff:g id="DOWNLOAD_COUNT">%d</xliff:g>) 下載會取消並從下載記錄中清除。"</string>
+ <string name="download_file_error_dlg_title">"超出空間"</string>
+ <string name="download_file_error_dlg_msg">"無法下載 <xliff:g id="FILENAME">%s</xliff:g>。"\n" 請清除手機上的空間或再試一次。"</string>
+ <string name="download_failed_generic_dlg_title">"下載失敗"</string>
+ <string name="download_no_sdcard_dlg_title">"沒有 SD 卡"</string>
+ <string name="download_no_sdcard_dlg_msg">"需要 SD 卡下載 <xliff:g id="FILENAME">%s</xliff:g>。"</string>
+ <string name="download_sdcard_busy_dlg_title">"無法使用 SD 卡"</string>
+ <!-- no translation found for download_sdcard_busy_dlg_msg (3473883538192835204) -->
+ <skip />
+ <string name="download_no_application">"找不到可以開啟這個檔案的應用程式。"</string>
+ <string name="retry">"重試"</string>
+ <string name="no_downloads">"下載記錄是空的。"</string>
+ <string name="download_error">"下載失敗"</string>
+ <string name="download_success">"<xliff:g id="FILE">%s</xliff:g> 已下載完成。"</string>
+ <string name="download_running">"下載中…"</string>
+ <string name="download_pending">"開始下載…"</string>
+ <string name="download_pending_network">"等待資料連線中…"</string>
+ <string name="download_running_paused">"等待資料連線中…"</string>
+ <string name="download_canceled">"下載已取消。"</string>
+ <string name="download_not_acceptable">"無法下載。電話不支援該內容。"</string>
+ <string name="download_file_error">"無法完成下載。儲存空間不足。"</string>
+ <string name="download_length_required">"無法下載。物件大小無法估計。"</string>
+ <string name="download_precondition_failed">"下載中斷。無法續傳。"</string>
+ <string name="activity_instrumentation_test_runner">"Browser Test Runner"</string>
+ <string name="search_google">"搜尋 Google"</string>
+ <string name="permlab_readHistoryBookmarks">"讀取瀏覽器的瀏覽記錄和書籤"</string>
+ <string name="permdesc_readHistoryBookmarks">"允許應用程式讀取瀏覽器造訪過的所有 URL 和瀏覽器所有的書籤。"</string>
+ <string name="permlab_writeHistoryBookmarks">"寫入瀏覽器的瀏覽記錄和書籤"</string>
+ <string name="permdesc_writeHistoryBookmarks">"允許應用程式修改儲存在電話上的瀏覽器記錄或書籤。惡意應用程式可能會使用此選項來清除或修改您瀏覽器的資料。"</string>
+ <!-- no translation found for query_data_prompt (5474381240981604223) -->
+ <skip />
+ <!-- no translation found for query_data_message (8789381063185445197) -->
+ <skip />
+ <!-- no translation found for location_prompt (226262202057302423) -->
+ <skip />
+ <!-- no translation found for location_message (1729456751935683242) -->
+ <skip />
+ <!-- no translation found for shortcut_prompt (437193299088893596) -->
+ <skip />
+ <!-- no translation found for shortcut_message (4793042709293755892) -->
+ <skip />
+ <string name="settings_message">"下表列出您為每個想要使用 Gears 之網站所授予的權限。"</string>
+ <!-- no translation found for filepicker_message (4929726371602896039) -->
+ <skip />
+ <!-- no translation found for image_message (5450245866521896891) -->
+ <skip />
+ <string name="settings_title">"裝置設定"</string>
+ <string name="privacy_policy">"讀取網站的隱私權政策,以瞭解網站將如何使用您的位置資訊。"</string>
+ <!-- no translation found for settings_storage_title (7089119630457156408) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_on (2733295483569432111) -->
+ <skip />
+ <!-- no translation found for settings_storage_subtitle_off (1338039396087898813) -->
+ <skip />
+ <!-- no translation found for settings_location_title (4953062923509886651) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_on (1496219621849158879) -->
+ <skip />
+ <!-- no translation found for settings_location_subtitle_off (5216466051810596189) -->
+ <skip />
+ <!-- no translation found for settings_remove_site (1822247070226589958) -->
+ <skip />
+ <!-- no translation found for settings_empty (2008952224378583146) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove_title (5360857296753219635) -->
+ <skip />
+ <!-- no translation found for settings_confirmation_remove (585560170733446898) -->
+ <skip />
+ <!-- no translation found for storage_notification (6534213782873968255) -->
+ <skip />
+ <!-- no translation found for storage_notification_alwaysdeny (5473208033665783513) -->
+ <skip />
+ <!-- no translation found for location_notification (3495804750470905804) -->
+ <skip />
+ <!-- no translation found for location_notification_alwaysdeny (6184925953783312629) -->
+ <skip />
+ <!-- no translation found for shortcut_notification (1659700220868444568) -->
+ <skip />
+ <!-- no translation found for permission_button_alwaysdeny (3837055994905214848) -->
+ <skip />
+ <!-- no translation found for permission_button_allow (7301874925704148242) -->
+ <skip />
+ <!-- no translation found for permission_button_deny (104712269725153018) -->
+ <skip />
+ <!-- no translation found for shortcut_button_alwaysdeny (4172277731325126605) -->
+ <skip />
+ <string name="shortcut_button_allow">"確定"</string>
+ <string name="shortcut_button_deny">"取消"</string>
+ <string name="settings_button_allow">"套用"</string>
+ <string name="settings_button_deny">"取消"</string>
+ <string name="filepicker_button_allow">"確定"</string>
+ <string name="filepicker_button_deny">"取消"</string>
+ <string name="filepicker_path">"路徑:"</string>
+ <string name="filepicker_no_files_selected">"未選取任何檔案"</string>
+ <string name="filepicker_one_file_selected">"已選取一個檔案"</string>
+ <string name="filepicker_some_files_selected">"選取的檔案"</string>
+ <string name="remove">"移除"</string>
+ <string name="local_storage">"本機儲存空間"</string>
+ <string name="allowed">"允許"</string>
+ <string name="denied">"遭拒"</string>
+ <string name="unrecognized_dialog_message">"無法辨識的對話類型"</string>
+ <string name="default_button">"確定"</string>
+ <string name="zoom_overview_button_text">"總覽"</string>
+</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
new file mode 100644
index 00000000..f34b6404
--- /dev/null
+++ b/res/values/colors.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/http_authentication_colors.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.
+*/
+-->
+<!-- FIXME: Change the name of this file! It is now being used generically
+ for the browser -->
+<resources>
+ <color name="username_text">#ffffffff</color>
+ <color name="username_edit">#ff000000</color>
+
+ <color name="password_text">#ffffffff</color>
+ <color name="password_edit">#ff000000</color>
+
+ <color name="ssl_text_label">#ffffffff</color>
+ <color name="ssl_text_value">#ffffffff</color>
+
+ <color name="white">#ffffffff</color>
+ <color name="black">#ff000000</color>
+ <color name="gray">#ffbbbbbb</color>
+ <color name="dark_gray">#ff777777</color>
+
+ <!-- Gears Dialogs -->
+ <color name="permission_border">#f1cc1d</color>
+ <color name="permission_background">#faefb8</color>
+ <color name="dialog_link">#ff0000ff</color>
+</resources>
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
new file mode 100644
index 00000000..d6056576
--- /dev/null
+++ b/res/values/strings.xml
@@ -0,0 +1,707 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- The name of the application. -->
+ <string name="application_name">Browser</string>
+
+ <!-- Name of tab containing bookmarks -->
+ <string name="tab_bookmarks">Bookmarks</string>
+ <!-- Name of tab containing the user's most visited websites, organized by number of visits -->
+ <string name="tab_most_visited">Most visited</string>
+ <!-- Name of tab containing the user's complete history, organized by time of last visit -->
+ <string name="tab_history">History</string>
+ <!-- Toast shown when a history item's star is clicked, converting it to a bookmark -->
+ <string name="added_to_bookmarks">Added to bookmarks</string>
+ <!-- Toast shown when a history item's star is clicked off, removing its bookmark -->
+ <string name="removed_from_bookmarks">Removed from bookmarks</string>
+ <!-- Sign-in dialog -->
+ <!-- This is used as the title of a dialog which is used to sign in to a secure site.
+ "%s1"is replaced with the host and "%s2" with realm -->
+ <string name="sign_in_to">Sign in to <xliff:g id="hostname">%s1</xliff:g> \"<xliff:g id="realm">%s2</xliff:g>\"</string>
+ <!-- Prompt for an input box that allows the user to enter their login name -->
+ <string name="username">Name</string>
+ <!-- Prompt for an input box that allows the user to enter their password -->
+ <string name="password">Password</string>
+ <!-- The label on the "sign in" button -->
+ <string name="action">Sign in</string>
+
+ <!-- Label for a cancel button. It is used for multiple cancel buttons in different contexts -->
+ <string name="cancel">Cancel</string>
+ <!-- Label for a confirm button. Used in multiple contexts. -->
+ <string name="ok">OK</string>
+
+ <!--The find dialog -->
+ <!-- Displayed on the Find Dialog, stating "X matches" where X is the
+ number of matches found in the current page -->
+ <string name="matches_found">\u0020matches</string>
+ <!-- Displayed on the Find Dialog, stating that there are "zero
+ matches" -->
+ <string name="zero">No</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
+ Page info screen for the current page -->
+ <string name="page_info_view">View page info</string>
+ <!-- Page Info dialog strings -->
+ <!-- Noun. The url for the current page. Displayed on the Page Info
+ screen. -->
+ <string name="page_info_address">Address:</string>
+
+ <!-- SSL Error dialogs -->
+ <string name="ssl_warnings_header">There are problems with the security certificate for this site.</string>
+ <!-- Label for a button on an SSL error dialog that lets the user continue
+ and view the page despite the error -->
+ <string name="ssl_continue">Continue</string>
+ <!-- Title for an SSL error dialog -->
+ <string name="security_warning">Security warning</string>
+ <!-- Label for a button on an SSL error dialog that lets the user view the
+ certificate -->
+ <string name="view_certificate">View certificate</string>
+
+ <!-- Message in an SSL Error dialog -->
+ <string name="ssl_untrusted">This certificate is not from a trusted authority.</string>
+ <!-- Message in an SSL Error dialog -->
+ <string name="ssl_mismatch">The name of the site does not match the name on the certificate.</string>
+ <!-- Message in an SSL Error dialog -->
+ <string name="ssl_expired">This certificate has expired.</string>
+ <!-- Message in an SSL Error dialog -->
+ <string name="ssl_not_yet_valid">This certificate is not valid yet.</string>
+
+ <!-- SSL Certificate dialogs -->
+ <!-- Title for an SSL Certificate dialog -->
+ <string name="ssl_certificate">Security certificate</string>
+ <!-- Message on an SSL Certificate dialog -->
+ <string name="ssl_certificate_is_valid">This certificate is valid.</string>
+ <!-- Label for an information field on an SSL Certificate Dialog -->
+ <string name="issued_to">Issued to:</string>
+ <!-- Label for an information field on an SSL Certificate Dialog -->
+ <string name="common_name">Common name:</string>
+ <!-- Label for an information field on an SSL Certificate Dialog -->
+ <string name="org_name">Organization:</string>
+ <!-- Label for an information field on an SSL Certificate Dialog -->
+ <string name="org_unit">Organizational unit:</string>
+ <!-- Label for an information field on an SSL Certificate Dialog -->
+ <string name="issued_by">Issued by:</string>
+ <!-- Label for an information field on an SSL Certificate Dialog -->
+ <string name="validity_period">Validity:</string>
+ <!-- Label for an information field on an SSL Certificate Dialog -->
+ <string name="issued_on">Issued on:</string>
+ <!-- Label for an information field on an SSL Certificate Dialog -->
+ <string name="expires_on">Expires on:</string>
+
+ <!-- Dialog that is shown while we are retrieving the login creds from the system -->
+ <string name="retrieving_creds_dlg_msg">Retrieving sign-in details\u2026</string>
+ <!-- Menu item in a context menu for closing a browser window. -->
+ <string name="close">Close</string>
+ <!-- Confirmation dialog message stating that a browser window will be closed -->
+ <string name="close_window">This window will be closed.</string>
+ <!-- Toast informing the user that loading has stopped for the current page. -->
+ <string name="stopping">Stopping\u2026</string>
+ <!-- Menu item to stop the current page from loading. -->
+ <string name="stop">Stop</string>
+ <!-- Menu item to reload or refresh the current page. -->
+ <string name="reload">Refresh</string>
+ <!-- Menu item to go to the next page (only useful if the user has previously hit back.) -->
+ <string name="forward">Forward</string>
+ <!-- Button label to confirm saving a bookmark. -->
+ <string name="save">OK</string>
+ <!-- Button label to cancel saving a bookmark. -->
+ <string name="do_not_save">Cancel</string>
+ <!-- Field label in Bookmark dialog box: refers to URL of the page to bookmark -->
+ <string name="location">Location</string>
+ <!-- Field label in Bookmark dialog box: title that the user wishes to use for the bookmark -->
+ <string name="name">Name</string>
+ <!-- Initial value in Location field in Bookmark dialog box -->
+ <string name="http">http://</string>
+ <!-- Menu item that opens a dialog to save a bookmark, initialized with the current page -->
+ <string name="save_to_bookmarks">Bookmark</string>
+ <!-- Menu item on the bookmarks page, to edit an existing bookmark -->
+ <string name="edit_bookmark">Edit bookmark</string>
+ <!-- Context menu item to create a shortcut to the bookmark on the desktop -->
+ <string name="create_shortcut_bookmark">Add shortcut to Home</string>
+ <!-- Context menu item to open the currently highlighted bookmark -->
+ <string name="open_bookmark">Open</string>
+ <!-- Menu item to remove the currently highlighted bookmark-->
+ <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>
+ <!-- Toast informing the user that their action to save a bookmark has succeeded -->
+ <string name="bookmark_saved">Saved to bookmarks.</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 -->
+ <string name="bookmark_needs_url">"Bookmark must have a location."</string>
+ <!-- Error that appears in the title of Bookmark dialog when user selects OK with empty Name & Location fields -->
+ <string name="empty_bookmark">"Cannot create empty bookmark."</string>
+ <!-- Error that appears in the title of Bookmark dialog when user selects OK with invalid URL -->
+ <string name="bookmark_url_not_valid">URL is not valid.</string>
+ <!-- Title of a confirmation dialog when the user deletes a bookmark-->
+ <string name="delete_bookmark">Delete</string>
+ <!-- Menu item in the page that displays all bookmarks. It brings up a
+ dialog that allows the user to bookmark the page that the browser is
+ currently on, but is not visible because the bookmarks page is
+ showing. -->
+ <string name="bookmark_page">Bookmark last-viewed page</string>
+ <!-- Summary text under the New Bookmark item on the Bookmarks screen.
+ Tells the user that if they select this item, it will bring up a
+ dialog to bookmark the page that the browser is currently viewing,
+ though it is not currently being displayed, since they are viewing
+ the bookmarks screen. -->
+ <string name="current_page">from\u0020</string>
+ <!-- Confirmation dialog message confirming that the user wishes to delete
+ the bookmark they selected for deletion -->
+ <string name="delete_bookmark_warning">Bookmark \"<xliff:g id="bookmark">%s</xliff:g>\" will be deleted.</string>
+ <!-- Context Menu item to open the selected link in a new window -->
+ <string name="open_in_new_window">Open in new window</string>
+ <!-- Appears in Current windows screen as label on blank + window; user
+ selects this window to open a new browser window -->
+ <string name="new_window">New window</string>
+ <!-- Menu item to open a dialog which allows the user to enter a url or do search-->
+ <string name="goto_dot">Go</string>
+ <!-- Menu item that opens up a dialog which allows the user to provide a
+ string and search for it on the page. -->
+ <string name="find_dot">Find on page</string>
+ <!-- Menu item to switch to text selection mode for copy and paste. -->
+ <string name="select_dot">Select text</string>
+ <!-- Menu item which opens a screen which shows all the currently open
+ windows, and also allows them to create a new one. -->
+ <string name="view_tabs">Window overview</string>
+ <!-- Menu item which opens a screen which shows all the currently open
+ windows, and also allows them to create a new one. This is a shorter
+ alternate version of "view_tabs"-->
+ <string name="view_tabs_condensed">Windows</string>
+ <!-- Title of current windows screen; appears in title bar -->
+ <string name="tab_picker_title">Current windows</string>
+ <!-- Context Menu item to view the selected window on the window picker
+ screen -->
+ <string name="tab_picker_view_tab">View</string>
+ <!-- Menu item on the window picker screen to open a new window -->
+ <string name="tab_picker_new_tab">New window</string>
+ <!-- Context Menu item to close the currently selected window -->
+ <string name="tab_picker_remove_tab">Close</string>
+ <!-- Context Menu item to bookmark the currently selected window -->
+ <string name="tab_picker_bookmark">Bookmark</string>
+ <!-- Context Menu item from the bookmarks page to share the selected
+ bookmark -->
+ <string name="tab_picker_send_url">Share link</string>
+ <!-- Menu item to open the bookmarks page. This is a shorter version that
+ is displayed with an icon -->
+ <string name="bookmarks">Bookmarks</string>
+ <!-- Secondary name for the Activity "BrowserBookmarksPage" -->
+ <string name="shortcut_bookmark">Bookmark</string>
+ <!-- Menu item to display the History of previously visited pages -->
+ <string name="history">History</string>
+ <!-- Menu item for viewing downloads from the browser -->
+ <string name="menu_view_download">Downloads</string>
+ <!-- Menu item -->
+ <string name="share_page">Share page</string>
+ <!-- Context Menu item open the currently selected link in the current
+ window.-->
+ <string name="contextmenu_openlink">Open</string>
+ <!-- Context Menu item to open the currently selected link in a new
+ window. -->
+ <string name="contextmenu_openlink_newwindow">Open in new window</string>
+ <!-- Context Menu item to create a bookmark for the url of the currently
+ selected link. -->
+ <string name="contextmenu_bookmark_thislink">Bookmark link</string>
+ <!-- Context Menu item to save the webpage referred to by this link to the
+ SD card (external storage) -->
+ <string name="contextmenu_savelink">Save link</string>
+ <!-- Context Menu item to send the url of the selected link to someone else,
+ via Gmail or another app -->
+ <string name="contextmenu_sharelink">Share link</string>
+ <!-- Context Menu item -->
+ <string name="contextmenu_copy">Copy</string>
+ <!-- Context Menu item to copy the url of the selected link to the
+ clipboard -->
+ <string name="contextmenu_copylink">Copy link URL</string>
+ <!-- Context Menu item to save the image to external storage -->
+ <string name="contextmenu_download_image">Save image</string>
+ <!-- Context Menu item to view the image by itself in the browser -->
+ <string name="contextmenu_view_image">View image</string>
+ <!-- Context Menu item to open the dialer app with the selected phone number
+ -->
+ <string name="contextmenu_dial_dot">Dial\u2026</string>
+ <!-- Context Menu item to add the selected phone number to the address book.
+ -->
+ <string name="contextmenu_add_contact">Add contact</string>
+ <!-- Context Menu item to send an email using the selected email address.-->
+ <string name="contextmenu_send_mail">Send email</string>
+ <!-- Context Menu item to show the currently selected address in the Maps
+ application -->
+ <string name="contextmenu_map">Map</string>
+
+ <!-- Used as the title of dialogs in the Browser Settings confirming that
+ the user wants to clear (cache, history, or cookie data) -->
+ <string name="clear">Clear</string>
+
+ <!-- Part of a sentence that says "Replace <url of existing bookmark> with
+ <url of new bookmark>?" -->
+ <string name="replace">Replace</string>
+
+ <!-- Title displayed when the bookmarks page is shown -->
+ <string name="browser_bookmarks_page_bookmarks_text">Bookmarks</string>
+
+
+ <!-- Settings screen strings -->
+ <!-- Menu item to open the Settings screen -->
+ <string name="menu_preferences">Settings</string>
+ <!-- Settings screen, section title -->
+ <string name="pref_content_title">Page content settings</string>
+ <!-- Settings label -->
+ <string name="pref_content_load_images">Load images</string>
+ <!-- Settings label -->
+ <string name="pref_content_load_images_summary">Display images on Web pages</string>
+ <!-- Settings label -->
+ <string name="pref_content_block_popups">Block pop-up windows</string>
+ <!-- Settings label -->
+ <string name="pref_content_javascript">Enable JavaScript</string>
+ <!-- Settings label -->
+ <string name="pref_content_open_in_background">Open in background</string>
+ <!-- Settings summary -->
+ <string name="pref_content_open_in_background_summary">New windows open behind the current one</string>
+ <!-- Settings label -->
+ <string name="pref_content_homepage">Set home page</string>
+ <!-- Settings label -->
+ <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 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>
+ <!-- Confirmation dialog message -->
+ <string name="pref_privacy_clear_cache_dlg">The cache will be cleared.</string>
+ <!-- Settings label -->
+ <string name="pref_privacy_clear_cookies">Clear all cookie data</string>
+ <!-- Settings summary -->
+ <string name="pref_privacy_clear_cookies_summary">Clear all the browser cookies</string>
+ <!-- Confirmation dialog message -->
+ <string name="pref_privacy_clear_cookies_dlg">All cookies will be cleared.</string>
+ <!-- Settings label -->
+ <string name="pref_privacy_clear_history">Clear history</string>
+ <!-- Settings summary -->
+ <string name="pref_privacy_clear_history_summary">Clear the browser navigation history</string>
+ <!-- Confirmation dialog message -->
+ <string name="pref_privacy_clear_history_dlg">The browser navigation history will be cleared.</string>
+ <!-- Settings label -->
+ <string name="pref_privacy_clear_form_data">Clear form data</string>
+ <!-- Settings summary -->
+ <string name="pref_privacy_clear_form_data_summary">Clear all the saved form data</string>
+ <!-- Confirmation dialog message -->
+ <string name="pref_privacy_clear_form_data_dlg">All saved form data will be cleared.</string>
+ <!-- Settings label -->
+ <string name="pref_privacy_clear_passwords">Clear passwords</string>
+ <!-- Settings summary -->
+ <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 screen, section title -->
+ <string name="pref_security_title">Security settings</string>
+ <!-- Settings label -->
+ <string name="pref_security_remember_passwords">Remember passwords</string>
+ <!-- Settings summary -->
+ <string name="pref_security_remember_passwords_summary">Save usernames and passwords for Web sites</string>
+ <!-- Settings label -->
+ <string name="pref_security_save_form_data">Remember form data</string>
+ <!-- Settings summary -->
+ <string name="pref_security_save_form_data_summary">Remember data I type in forms for later use</string>
+ <!-- Settings label -->
+ <string name="pref_security_show_security_warning">Show security warnings</string>
+ <!-- Settings summmary -->
+ <string name="pref_security_show_security_warning_summary">Show warning if there is a problem with a site\'s security</string>
+ <!-- Settings label -->
+ <string name="pref_security_accept_cookies">Accept cookies</string>
+ <!-- Settings summary -->
+ <string name="pref_security_accept_cookies_summary">Allow sites to save and read \"cookie\" data</string>
+ <!-- Settings label -->
+ <string name="pref_text_size">Set text size</string>
+ <!-- Settings text size options; appear in Text size dialog box -->
+ <string-array name="pref_text_size_choices">
+ <item>Tiny</item>
+ <item>Small</item>
+ <item>Normal</item>
+ <item>Large</item>
+ <item>Huge</item>
+ </string-array>
+ <!-- Title of dialog for setting the text size -->
+ <string name="pref_text_size_dialogtitle">Text size</string>
+ <!-- Do not translate -->
+ <string-array name="pref_text_size_values">
+ <item><xliff:g>SMALLEST</xliff:g></item>
+ <item><xliff:g>SMALLER</xliff:g></item>
+ <item><xliff:g>NORMAL</xliff:g></item>
+ <item><xliff:g>LARGER</xliff:g></item>
+ <item><xliff:g>LARGEST</xliff:g></item>
+ </string-array>
+ <!-- Settings screen, section title -->
+ <string name="pref_extras_title">Advanced settings</string>
+ <!-- Settings label -->
+ <string name="pref_extras_gears_enable">Enable Gears</string>
+ <!-- Settings summary -->
+ <string name="pref_extras_gears_enable_summary">Applications that extend browser functionality</string>
+ <!-- Settings label -->
+ <string name="pref_extras_gears_settings">Gears settings</string>
+ <!-- Settings summary -->
+ <string name="pref_plugin_installed">Plugins list</string>
+ <!-- Settings summary -->
+ <string name="pref_plugin_installed_empty_list">No installed plugins.</string>
+ <string name="pref_extras_gears_settings_summary">Applications that extend browser functionality</string>
+ <!-- Settings label -->
+ <string name="pref_extras_reset_default">Reset to default</string>
+ <!-- Settings summary -->
+ <string name="pref_extras_reset_default_summary">Clear all browser data and reset all settings to default</string>
+ <!-- Confirmation dialog message -->
+ <string name="pref_extras_reset_default_dlg">All browser data will be cleared and settings will revert to default values.</string>
+ <!-- Confirmation dialog title -->
+ <string name="pref_extras_reset_default_dlg_title">Reset to default</string>
+ <!-- Title for a group of settings -->
+ <string name="pref_development_title">Debug</string>
+ <!-- Do not tranlsate. Development option -->
+ <string name="pref_development_viewport">Use wide viewport</string>
+ <!-- Do not tranlsate. Development option -->
+ <string name="pref_development_single_column_rendering">Single column rendering</string>
+ <!-- Do not tranlsate. Development option -->
+ <string name="pref_development_normal_rendering">Normal rendering</string>
+ <!-- Do not tranlsate. Development option -->
+ <string name="pref_development_trace">Enable tracing</string>
+ <!-- Do not tranlsate. Development option -->
+ <string name="pref_development_nav_dump">Enable nav cache dump</string>
+ <!-- Do not tranlsate. Development option -->
+ <string name="pref_development_search_url">Set search URL</string>
+ <!-- Do not tranlsate. Development option -->
+ <string name="pref_development_uastring">UAString</string>
+ <!-- Do not tranlsate. Development option -->
+ <string-array name="pref_development_ua_choices">
+ <item>Android</item>
+ <item>Desktop</item>
+ <item>iPhone</item>
+ </string-array>
+ <!-- Do not tranlsate. Development option -->
+ <string-array name="pref_development_ua_values">
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ </string-array>
+ <!-- Settings screen, setting option name -->
+ <string name="pref_default_text_encoding">Set text encoding</string>
+ <!-- Options in the Default encoding dialog box -->
+ <string-array name="pref_default_text_encoding_choices">
+ <item>Latin-1 (ISO-8859-1)</item>
+ <item>Unicode (UTF-8)</item>
+ <item>Japanese (ISO-2022-JP)</item>
+ <item>Japanese (SHIFT_JIS)</item>
+ <item>Japanese (EUC-JP)</item>
+ <!-- TODO: We should add a lot more encodings. -->
+ </string-array>
+ <!-- Dialog box title -->
+ <string name="pref_default_text_encoding_dialogtitle">Text encoding</string>
+ <!-- Do not translate. Actual encoding values. Must be valid text encodings understandable from WebKit. -->
+ <string-array name="pref_default_text_encoding_values">
+ <item>Latin-1</item>
+ <item>UTF-8</item>
+ <item>ISO-2022-JP</item>
+ <item>SHIFT_JIS</item>
+ <item>EUC-JP</item>
+ </string-array>
+ <string name="pref_default_text_encoding_default">Latin-1</string>
+ <!-- Dialog box title -->
+ <string name="browserFrameRedirect">Redirect</string>
+ <!-- Dialog box message -->
+ <string name="browserFrame307Post">This Web page is being redirected. Resend your typed form data to the new location?</string>
+ <!-- Title for a dialog displayed when the browser has a data connectivity
+ problem -->
+ <string name="browserFrameNetworkErrorLabel">Data connectivity problem</string>
+ <!-- Title for a dialog displayed when the browser runs into a file label
+ error -->
+ <string name="browserFrameFileErrorLabel">Problem with file</string>
+ <!-- Dialog box title -->
+ <string name="browserFrameFormResubmitLabel">Confirm</string>
+ <!-- Dialog box message -->
+ <string name="browserFrameFormResubmitMessage">The page you are trying to view contains data that has already been submitted (\"POSTDATA\"). If you resend the data, any action the form on the page carried out (such as a search or online purchase) will be repeated.</string>
+ <!-- Dialog box title -->
+ <string name="loadSuspendedTitle">No network connection</string>
+ <!-- Dialog box message -->
+ <string name="loadSuspended">The page will continue loading after connection has been restored.</string>
+ <!-- Menu item -->
+ <string name="clear_history">Clear history</string>
+ <!-- History screen title; appears in title bar -->
+ <string name="browser_history">Recently-visited pages</string>
+ <!-- Appears on History screen if no history is found -->
+ <string name="empty_history">Browser history is empty.</string>
+ <!-- Displayed at the top of the bookmarks page. When clicked, it bookmarks the page the browser is currently showing -->
+ <string name="add_new_bookmark">Add bookmark\u2026</string>
+ <!-- Add bookmark dialog sets its title to this if we have no database.
+ This is an error case -->
+ <string name="no_database">No database!</string>
+
+ <!-- This string is for the browser "Go To" UI. -->
+ <!-- Do not translate. This string is not displayed in UI (no badge is selected for go to). -->
+ <string name="search_label">Browser</string>
+ <!-- This string is for the browser "Go To" UI. -->
+ <!-- This is the hint text shown in the Go To widget, before text is entered. -->
+ <string name="search_hint">Type Web address</string>
+ <!-- This string is for the browser "Go To" UI. -->
+ <!-- This is the button label in the "Go To" UI. -->
+ <string name="search_button_text">Go</string>
+
+ <!-- Pop-up window dialog -->
+ <!-- Title for a dialog informing the user that the site is attempting to
+ display a popup window -->
+ <string name="attention">Attention</string>
+ <!-- Message for a dialog informing the user that the site is attempting to
+ display a popup window -->
+ <string name="popup_window_attempt">This site is attempting to open a
+ pop-up window.</string>
+ <!-- Label for a button to allow a particular popup window to open -->
+ <string name="allow">Allow</string>
+ <!-- Label for a button to disallow a particular popup from opening -->
+ <string name="block">Block</string>
+
+ <!-- Title for a dialog informing the user that they cannot open any more
+ windows because the limit has been reached. -->
+ <string name="too_many_windows_dialog_title">Window limit reached</string>
+ <!-- Message in a dialog telling the user that they cannot open any more
+ windows because the limit has been reached. -->
+ <string name="too_many_windows_dialog_message">Could not open a new window because you have already opened the maximum number.</string>
+ <!-- Title for a dialog informing the user that they cannot open a subwindow
+ because only one can be open at a time. -->
+ <string name="too_many_subwindows_dialog_title">Pop-up already open</string>
+ <!-- Message for a dialog informing the user that they cannot open a
+ subwindow because only one can be open at a time. -->
+ <string name="too_many_subwindows_dialog_message">Could not open a new
+ pop-up window because only one can be open at any time.</string>
+
+ <!-- Download History UI strings -->
+ <string name="download_title">Download history</string>
+ <!-- Download history screen string-->
+ <string name="download_unknown_filename">&lt;Unknown&gt;</string>
+ <!-- Context menu item on Download history screen -->
+ <string name="download_menu_open">Open</string>
+ <!-- Context menu item on Download history screen -->
+ <string name="download_menu_clear">Clear from list</string>
+ <!-- Context menu item in Download history screen -->
+ <string name="download_menu_cancel">Cancel download</string>
+ <!-- Menu item -->
+ <string name="download_menu_cancel_all">Cancel all downloads</string>
+ <!-- Menu item -->
+ <string name="download_menu_clear_all">Clear list</string>
+ <!-- Confirmation dialog title -->
+ <string name="download_clear_dlg_title">Clear</string>
+ <!-- Confirmation dialog message -->
+ <string name="download_clear_dlg_msg">All items will be cleared from the list and removed from the browser cache.</string>
+ <!-- Confirmation dialog title -->
+ <string name="download_cancel_dlg_title">Cancel downloads</string>
+ <!-- Confirmation dialog message -->
+ <string name="download_cancel_dlg_msg">All <xliff:g id="download_count">%d</xliff:g> downloads will be canceled and cleared from the download history.</string>
+ <!-- Dialog title -->
+ <string name="download_file_error_dlg_title">Out of space</string>
+ <!-- Dialog message -->
+ <string name="download_file_error_dlg_msg"><xliff:g id="filename">%s</xliff:g> could not be downloaded.\nFree up some space on your phone and try again.</string>
+ <!-- Title for a dialog informing the user that the download has failed -->
+ <string name="download_failed_generic_dlg_title">Download unsuccessful</string>
+ <!-- Dialog title -->
+ <string name="download_no_sdcard_dlg_title">No SD card</string>
+ <!-- Dialog message -->
+ <string name="download_no_sdcard_dlg_msg">An SD card is required to download <xliff:g id="filename">%s</xliff:g>.</string>
+ <!-- Title for a dialog informing the user that the SD card used for
+ external storage is busy so they cannot download anything -->
+ <string name="download_sdcard_busy_dlg_title">SD card unavailable</string>
+ <!-- Message for a dialog informing the user that the SD card used for
+ external storage is busy so they cannot download anything. Informs
+ the user how to enable SD card storage -->
+ <string name="download_sdcard_busy_dlg_msg">The SD card is busy. To allow downloads, select \"Turn off USB storage\" in the notification.</string>
+
+ <!-- Message for a dialog informing the user that there is no application on
+ the phone that can open the file that was downloaded -->
+ <string name="download_no_application">No application can be found to open this file.</string>
+ <!-- Label for a button to re-attempt downloading a file -->
+ <string name="retry">Retry</string>
+ <!-- Appears in Download history screen if there is no history -->
+ <string name="no_downloads">Download history is empty.</string>
+ <!-- the following download_xxxx matches the download manager state, ie Downloads.Status -->
+ <string name="download_error">Download unsuccessful.</string>
+ <!-- Appears in Download history screen after an item has downloaded, included item size -->
+ <string name="download_success"><xliff:g id="file">%s</xliff:g> Download complete.</string>
+ <!-- Appears in Download history screen while an item is being downloaded -->
+ <string name="download_running">Downloading\u2026</string>
+ <!-- Message in the list of items which have received download requests
+ telling their status. This message states that the download has not
+ yet begun -->
+ <string name="download_pending">Starting download\u2026</string>
+ <!-- Message in the list of items which have received download requests
+ telling their status. This message states that the download is
+ waiting for a network connection to continue -->
+ <string name="download_pending_network">Waiting for data connection\u2026</string>
+ <!-- Message in the list of items which have received download requests
+ telling their status. This message states that the download has
+ paused to wait for a network connection -->
+ <string name="download_running_paused">Waiting for data connection\u2026</string>
+ <!-- Message in the list of items which have received download requests
+ telling their status. This message states that the download has
+ been canceled -->
+ <string name="download_canceled">Download canceled.</string>
+ <!-- Message in the list of items which have received download requests
+ telling their status. This message states that the download will
+ not happen because the content is not supported by the phone -->
+ <string name="download_not_acceptable">Cannot download. The content is not supported on the phone.</string>
+ <!-- Message in the list of items which have received download requests
+ telling their status. This message states that the download did not
+ finish because there is not enough storage for the file -->
+ <string name="download_file_error">Cannot finish download. There is not enough space.</string>
+ <!-- Message in the list of items which have received download requests
+ telling their status. This message states that the item cannot be
+ downloaded because it cannot determine the length of the item -->
+ <string name="download_length_required">Cannot download. The size of the item cannot be determined.</string>
+ <!-- Message in the list of items which have received download requests
+ telling their status. This message states that the download was
+ interrupted and cannot be resumed -->
+ <string name="download_precondition_failed">Download interrupted. It cannot be resumed.</string>
+
+ <string name="activity_instrumentation_test_runner">Browser Test Runner</string>
+ <!-- Do not translate. Testing only -->
+ <string name="activity_instrumentation_functional_test_runner">Browser Functional Test Runner</string>
+ <!-- Do not translate. Testing only -->
+ <string name="dump_nav">Dump navigation cache</string>
+
+ <!-- Bookmarks -->
+ <string-array name="bookmarks" translatable="false">
+ <item>Google</item>
+ <item>http://www.google.com/</item>
+ <item>Picasa Web Albums</item>
+ <item>http://picasaweb.google.com/m/viewer?source=androidclient</item>
+ <item>Yahoo!</item>
+ <item>http://www.yahoo.com/</item>
+ <item>MSN</item>
+ <item>http://www.msn.com/</item>
+ <item>MySpace</item>
+ <item>http://www.myspace.com/</item>
+ <item>Facebook</item>
+ <item>http://www.facebook.com/</item>
+ <item>Wikipedia</item>
+ <item>http://www.wikipedia.org/</item>
+ <item>eBay</item>
+ <item>http://www.ebay.com/</item>
+ <item>CNN</item>
+ <item>http://www.cnn.com/</item>
+ <item>New York Times</item>
+ <item>http://www.nytimes.com/</item>
+ <item>ESPN</item>
+ <item>http://espn.go.com/</item>
+ <item>Amazon</item>
+ <item>http://www.amazon.com/</item>
+ <item>Weather Channel</item>
+ <item>http://www.weather.com/</item>
+ <item>BBC</item>
+ <item>http://www.bbc.co.uk/</item>
+ </string-array>
+
+ <!-- Note that this is the standard search url. It uses the current locale for language -->
+ <!-- (%1$s) and country (%2$s) and shouldn't need to be replaced by locale or mcc selected -->
+ <!-- resources. -->
+ <!-- Any changes to this (especially in terms of adding/removing the MCC-specific flavors) -->
+ <!-- should be mirrored by changes in apps/GoogleSearch. -->
+ <!-- Do not translate. This string is an internal URI, not seen in the UI. -->
+ <string name="google_search_base" translatable="false">
+ http://www.google.com/m?hl=%1$s&amp;gl=%2$s&amp;</string>
+
+ <!-- Used in Goto to indicate Google search -->
+ <string name="search_google">Search Google</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether
+ they want to allow the application to do this. -->
+ <string name="permlab_readHistoryBookmarks">read Browser\'s history and bookmarks</string>
+ <!-- Description of an application permission, listed so the user can choose whether
+ they want to allow the application to do this. -->
+ <string name="permdesc_readHistoryBookmarks">Allows the application to read all
+ the URLs that the Browser has visited, and all of the Browser\'s bookmarks.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether
+ they want to allow the application to do this. -->
+ <string name="permlab_writeHistoryBookmarks">write Browser\'s history and bookmarks</string>
+ <!-- Description of an application permission, listed so the user can choose whether
+ they want to allow the application to do this. -->
+ <string name="permdesc_writeHistoryBookmarks">Allows an application to modify the
+ Browser\'s history or bookmarks stored on your phone. Malicious applications
+ can use this to erase or modify your Browser\'s data.</string>
+
+ <!-- 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="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>
+ <string name="shortcut_message">This web site would like to create a shortcut to this page on your home screen.</string>
+ <string name="settings_message">The table below shows the permissions you have granted to each site that has attempted to use Gears.</string>
+ <string name="filepicker_message">File Picker</string>
+ <string name="image_message">Choosen Image</string>
+
+ <string name="settings_title">Gears settings</string>
+ <string name="privacy_policy">Read the site\'s privacy policy to see how your location will be used.</string>
+ <string name="settings_storage_title">Allow Local Storage</string>
+ <string name="settings_storage_subtitle_on">This site can save information to your phone</string>
+ <string name="settings_storage_subtitle_off">This site cannot save information to your phone</string>
+
+ <string name="settings_location_title">Allow Location Detection</string>
+ <string name="settings_location_subtitle_on">This site can read your current location</string>
+ <string name="settings_location_subtitle_off">This site cannot read your current location</string>
+
+ <string name="settings_remove_site">Remove this site from Gears</string>
+ <string name="settings_empty">No permissions set</string>
+
+ <string name="settings_confirmation_remove_title">Remove this site</string>
+ <string name="settings_confirmation_remove">All permissions will be cleared, and any data stored on your phone by the site will be deleted.</string>
+
+ <string name="storage_notification">This site will store information on this phone.\n Change this setting in:\n Settings -> Gears Settings</string>
+ <string name="storage_notification_alwaysdeny">This site will never store information on this phone.\n Change this setting in:\n Settings -> Gears Settings</string>
+ <string name="location_notification">This site can access your current location.\n Change this setting in:\n Settings -> Gears Settings</string>
+ <string name="location_notification_alwaysdeny">This site will not access your current location.\n Change this setting in:\n Settings -> Gears Settings</string>
+ <string name="shortcut_notification">Shortcut created</string>
+
+ <string name="permission_button_alwaysdeny">Never Allow</string>
+ <string name="permission_button_allow">OK</string>
+ <string name="permission_button_deny">Cancel</string>
+
+ <string name="shortcut_button_alwaysdeny">Never Allow</string>
+ <string name="shortcut_button_allow">OK</string>
+ <string name="shortcut_button_deny">Cancel</string>
+
+ <string name="settings_button_allow">Apply</string>
+ <string name="settings_button_deny">Cancel</string>
+
+ <string name="filepicker_button_allow">OK</string>
+ <string name="filepicker_button_deny">Cancel</string>
+ <string name="filepicker_path">path:</string>
+ <string name="filepicker_no_files_selected">No files selected</string>
+ <string name="filepicker_one_file_selected">One file selected</string>
+ <string name="filepicker_some_files_selected">files selected</string>
+
+ <string name="remove">Remove</string>
+ <string name="local_storage">Local storage</string>
+ <string name="allowed">Allowed</string>
+ <string name="denied">Denied</string>
+
+ <string name="unrecognized_dialog_message">Unrecognized dialog type</string>
+ <string name="default_button">OK</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>
+</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
new file mode 100644
index 00000000..f20220f1
--- /dev/null
+++ b/res/values/styles.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ *
+ * Copyright 2006,2007,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.
+ */
+-->
+<resources>
+ <style name="BrowserTheme" parent="@android:Theme.Black">
+ <item name="android:windowBackground">@color/white</item>
+ <item name="android:colorBackground">#FFFFFFFF</item>
+ <item name="android:autoCompleteTextViewStyle">@style/AutoCompleteTextView</item>
+ </style>
+
+ <style name="AutoCompleteTextView">
+ <item name="android:focusable">true</item>
+ <item name="android:focusableInTouchMode">true</item>
+ <item name="android:clickable">true</item>
+ <item name="android:completionHintView">@android:layout/simple_dropdown_item_1line</item>
+ <item name="android:textAppearance">?android:attr/textAppearanceLargeInverse</item>
+ <item name="android:completionThreshold">2</item>
+ <item name="android:dropDownSelector">@android:drawable/list_selector_background</item>
+ <item name="android:popupBackground">@android:drawable/spinner_dropdown_background</item>
+ </style>
+
+ <style name="gears_button">
+ <item name="android:background">@drawable/gears_button</item>
+ </style>
+
+ <style name="FindDialog">
+ <item name="android:windowEnterAnimation">@anim/find_dialog_enter</item>
+ <item name="android:windowExitAnimation">@anim/find_dialog_exit</item>
+ </style>
+
+
+</resources>
+
diff --git a/res/values/themes.xml b/res/values/themes.xml
new file mode 100644
index 00000000..bb922dda
--- /dev/null
+++ b/res/values/themes.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<resources>
+ <style name="FindDialogTheme">
+ <item name="android:windowFrame">@null</item>
+ <item name="android:windowIsFloating">true</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:background">@null</item>
+ <item name="android:windowBackground">@null</item>
+ <item name="android:windowAnimationStyle">@style/FindDialog</item>
+ <item name="android:backgroundDimEnabled">false</item>
+ </style>
+</resources>
diff --git a/res/xml/browser_preferences.xml b/res/xml/browser_preferences.xml
new file mode 100644
index 00000000..cd403226
--- /dev/null
+++ b/res/xml/browser_preferences.xml
@@ -0,0 +1,174 @@
+<?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.
+-->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <PreferenceCategory
+ android:title="@string/pref_content_title">
+
+ <ListPreference
+ android:key="text_size"
+ android:title="@string/pref_text_size"
+ android:defaultValue="NORMAL"
+ android:entries="@array/pref_text_size_choices"
+ android:entryValues="@array/pref_text_size_values"
+ android:dialogTitle="@string/pref_text_size_dialogtitle" />
+
+ <ListPreference
+ android:key="default_text_encoding"
+ android:title="@string/pref_default_text_encoding"
+ android:defaultValue="@string/pref_default_text_encoding_default"
+ android:entries="@array/pref_default_text_encoding_choices"
+ android:entryValues="@array/pref_default_text_encoding_values"
+ android:dialogTitle="@string/pref_default_text_encoding_dialogtitle" />
+
+ <CheckBoxPreference
+ android:key="block_popup_windows"
+ android:defaultValue="true"
+ android:title="@string/pref_content_block_popups" />
+
+ <CheckBoxPreference
+ android:key="load_images"
+ android:defaultValue="true"
+ android:title="@string/pref_content_load_images"
+ android:summary="@string/pref_content_load_images_summary" />
+
+ <CheckBoxPreference
+ android:key="autofit_pages"
+ android:defaultValue="true"
+ android:title="@string/pref_content_autofit"
+ android:summary="@string/pref_content_autofit_summary" />
+
+ <CheckBoxPreference
+ android:key="enable_javascript"
+ android:defaultValue="true"
+ android:title="@string/pref_content_javascript" />
+
+ <CheckBoxPreference
+ android:key="open_in_background"
+ android:defaultValue="false"
+ android:title="@string/pref_content_open_in_background"
+ android:summary="@string/pref_content_open_in_background_summary" />
+
+ <com.android.browser.BrowserHomepagePreference
+ android:key="homepage"
+ android:title="@string/pref_content_homepage"
+ android:singleLine="true" />
+
+ </PreferenceCategory>
+
+ <PreferenceCategory
+ android:title="@string/pref_privacy_title">
+
+ <com.android.browser.BrowserYesNoPreference
+ android:key="privacy_clear_cache"
+ android:title="@string/pref_privacy_clear_cache"
+ android:summary="@string/pref_privacy_clear_cache_summary"
+ android:dialogMessage="@string/pref_privacy_clear_cache_dlg"
+ android:dialogTitle="@string/clear"
+ android:dialogIcon="@android:drawable/ic_dialog_alert" />
+
+ <com.android.browser.BrowserYesNoPreference
+ android:key="privacy_clear_history"
+ android:title="@string/pref_privacy_clear_history"
+ android:summary="@string/pref_privacy_clear_history_summary"
+ android:dialogMessage="@string/pref_privacy_clear_history_dlg"
+ android:dialogTitle="@string/clear"
+ android:dialogIcon="@android:drawable/ic_dialog_alert"/>
+
+ <CheckBoxPreference
+ android:key="accept_cookies"
+ android:defaultValue="true"
+ android:title="@string/pref_security_accept_cookies"
+ android:summary="@string/pref_security_accept_cookies_summary" />
+
+ <com.android.browser.BrowserYesNoPreference
+ android:key="privacy_clear_cookies"
+ android:title="@string/pref_privacy_clear_cookies"
+ android:summary="@string/pref_privacy_clear_cookies_summary"
+ android:dialogMessage="@string/pref_privacy_clear_cookies_dlg"
+ android:dialogTitle="@string/clear"
+ android:dialogIcon="@android:drawable/ic_dialog_alert"/>
+
+ <CheckBoxPreference
+ android:key="save_formdata"
+ android:defaultValue="true"
+ android:title="@string/pref_security_save_form_data"
+ android:summary="@string/pref_security_save_form_data_summary" />
+
+ <com.android.browser.BrowserYesNoPreference
+ android:key="privacy_clear_form_data"
+ android:title="@string/pref_privacy_clear_form_data"
+ android:summary="@string/pref_privacy_clear_form_data_summary"
+ android:dialogMessage="@string/pref_privacy_clear_form_data_dlg"
+ android:dialogTitle="@string/clear"
+ android:dialogIcon="@android:drawable/ic_dialog_alert"/>
+
+ </PreferenceCategory>
+
+ <PreferenceCategory
+ android:title="@string/pref_security_title">
+
+ <CheckBoxPreference
+ android:key="remember_passwords"
+ android:defaultValue="true"
+ android:title="@string/pref_security_remember_passwords"
+ android:summary="@string/pref_security_remember_passwords_summary" />
+
+ <com.android.browser.BrowserYesNoPreference
+ android:key="privacy_clear_passwords"
+ android:title="@string/pref_privacy_clear_passwords"
+ android:summary="@string/pref_privacy_clear_passwords_summary"
+ android:dialogMessage="@string/pref_privacy_clear_passwords_dlg"
+ android:dialogTitle="@string/clear"
+ android:dialogIcon="@android:drawable/ic_dialog_alert"/>
+
+ <CheckBoxPreference
+ android:key="show_security_warnings"
+ android:defaultValue="true"
+ android:title="@string/pref_security_show_security_warning"
+ android:summary="@string/pref_security_show_security_warning_summary" />
+
+
+ </PreferenceCategory>
+ <PreferenceCategory
+ android:title="@string/pref_extras_title">
+
+ <!-- This setting is named "Enable Gears" but is actually
+ tied to enabling all plugins. -->
+ <CheckBoxPreference
+ android:key="enable_plugins"
+ android:defaultValue="true"
+ android:title="@string/pref_extras_gears_enable"
+ android:summary="@string/pref_extras_gears_enable_summary" />
+
+ <Preference
+ android:key="gears_settings"
+ android:persistent="false"
+ android:dependency="enable_plugins"
+ android:title="@string/pref_extras_gears_settings"
+ android:summary="@string/pref_extras_gears_settings_summary" />
+
+ <com.android.browser.BrowserYesNoPreference
+ android:key="reset_default_preferences"
+ android:title="@string/pref_extras_reset_default"
+ android:summary="@string/pref_extras_reset_default_summary"
+ android:dialogMessage="@string/pref_extras_reset_default_dlg"
+ android:dialogTitle="@string/pref_extras_reset_default_dlg_title"
+ android:dialogIcon="@android:drawable/ic_dialog_alert" />
+ </PreferenceCategory>
+</PreferenceScreen>
diff --git a/res/xml/debug_preferences.xml b/res/xml/debug_preferences.xml
new file mode 100644
index 00000000..4f55f6b0
--- /dev/null
+++ b/res/xml/debug_preferences.xml
@@ -0,0 +1,67 @@
+<?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.
+-->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <PreferenceCategory
+ android:title="@string/pref_development_title"
+ android:key="debug_menu" >
+
+ <CheckBoxPreference
+ android:key="small_screen"
+ android:defaultValue="false"
+ android:title="@string/pref_development_single_column_rendering" />
+
+ <CheckBoxPreference
+ android:key="wide_viewport"
+ android:defaultValue="true"
+ android:title="@string/pref_development_viewport" />
+
+ <CheckBoxPreference
+ android:key="normal_layout"
+ android:defaultValue="false"
+ android:title="@string/pref_development_normal_rendering" />
+
+ <CheckBoxPreference
+ android:key="enable_tracing"
+ android:defaultValue="false"
+ android:title="@string/pref_development_trace" />
+
+ <CheckBoxPreference
+ android:key="enable_light_touch"
+ android:defaultValue="false"
+ android:title="Enable light touch" />
+
+ <CheckBoxPreference
+ android:key="enable_nav_dump"
+ android:defaultValue="false"
+ android:title="@string/pref_development_nav_dump" />
+
+ <com.android.browser.BrowserSearchpagePreference
+ android:key="search_url"
+ android:title="@string/pref_development_search_url"
+ android:singleLine="true" />
+
+ <ListPreference
+ android:key="user_agent"
+ android:title="@string/pref_development_uastring"
+ android:entries="@array/pref_development_ua_choices"
+ android:entryValues="@array/pref_development_ua_values"
+ android:defaultValue="0"/>
+
+ </PreferenceCategory>
+
+</PreferenceScreen>
diff --git a/res/xml/searchable.xml b/res/xml/searchable.xml
new file mode 100644
index 00000000..bb578c62
--- /dev/null
+++ b/res/xml/searchable.xml
@@ -0,0 +1,33 @@
+<?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.
+-->
+
+<!-- Note: This defines the UI for the "Go To" UI, which uses the search manager UI, but
+ is actually used to enter Uri's to load and display. -->
+
+<searchable xmlns:android="http://schemas.android.com/apk/res/android"
+ android:icon="@drawable/ic_search_category_browser"
+ android:label="@string/search_label"
+ android:hint="@string/search_hint"
+ android:searchButtonText="@string/search_button_text"
+ android:searchMode="queryRewriteFromData"
+ android:voiceSearchMode="showVoiceSearchButton|launchWebSearch"
+ android:inputType="textUri"
+ android:imeOptions="actionGo"
+
+ android:searchSuggestAuthority="browser"
+ android:searchSuggestSelection="url LIKE ?"
+ android:searchSuggestIntentAction="android.intent.action.VIEW"
+/>
diff --git a/src/com/android/browser/AddBookmarkPage.java b/src/com/android/browser/AddBookmarkPage.java
new file mode 100644
index 00000000..cf3fe709
--- /dev/null
+++ b/src/com/android/browser/AddBookmarkPage.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.browser;
+
+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;
+
+import java.util.Date;
+
+public class AddBookmarkPage extends Activity {
+
+ private final String LOGTAG = "Bookmarks";
+
+ private EditText mTitle;
+ private EditText mAddress;
+ private TextView mButton;
+ 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) {
+ if (save()) {
+ finish();
+ Toast.makeText(AddBookmarkPage.this, R.string.bookmark_saved,
+ Toast.LENGTH_LONG).show();
+ }
+ }
+ };
+
+ private View.OnClickListener mCancel = new View.OnClickListener() {
+ public void onClick(View v) {
+ finish();
+ }
+ };
+
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ requestWindowFeature(Window.FEATURE_LEFT_ICON);
+ setContentView(R.layout.browser_add_bookmark);
+ setTitle(R.string.save_to_bookmarks);
+ getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, R.drawable.ic_dialog_bookmark);
+
+ String title = null;
+ String url = null;
+ mMap = getIntent().getExtras();
+ if (mMap != null) {
+ Bundle b = mMap.getBundle("bookmark");
+ if (b != null) {
+ mMap = b;
+ mEditingExisting = true;
+ setTitle(R.string.edit_bookmark);
+ }
+ title = mMap.getString("title");
+ url = mMap.getString("url");
+ }
+
+ mTitle = (EditText) findViewById(R.id.title);
+ mTitle.setText(title);
+ mAddress = (EditText) findViewById(R.id.address);
+ mAddress.setText(url);
+
+
+ View.OnClickListener accept = mSaveBookmark;
+ mButton = (TextView) findViewById(R.id.OK);
+ mButton.setOnClickListener(accept);
+
+ mCancelButton = findViewById(R.id.cancel);
+ mCancelButton.setOnClickListener(mCancel);
+
+ if (!getWindow().getDecorView().isInTouchMode()) {
+ mButton.requestFocus();
+ }
+ }
+
+ /**
+ * Save the data to the database.
+ * Also, change the view to dialog stating
+ * that the webpage has been saved.
+ */
+ boolean save() {
+ String title = mTitle.getText().toString().trim();
+ String unfilteredUrl =
+ BrowserActivity.fixUrl(mAddress.getText().toString());
+ boolean emptyTitle = title.length() == 0;
+ boolean emptyUrl = unfilteredUrl.trim().length() == 0;
+ Resources r = getResources();
+ if (emptyTitle || emptyUrl) {
+ if (emptyTitle) {
+ mTitle.setError(r.getText(R.string.bookmark_needs_title));
+ }
+ if (emptyUrl) {
+ mAddress.setError(r.getText(R.string.bookmark_needs_url));
+ }
+ return false;
+ }
+ String url = unfilteredUrl;
+ if (!(url.startsWith("about:") || url.startsWith("data:") || url
+ .startsWith("file:"))) {
+ WebAddress address;
+ try {
+ address = new WebAddress(unfilteredUrl);
+ } catch (ParseException e) {
+ mAddress.setError(r.getText(R.string.bookmark_url_not_valid));
+ return false;
+ }
+ if (address.mHost.length() == 0) {
+ mAddress.setError(r.getText(R.string.bookmark_url_not_valid));
+ return false;
+ }
+ url = address.toString();
+ }
+ try {
+ if (mEditingExisting) {
+ mMap.putString("title", title);
+ mMap.putString("url", url);
+ 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();
+ setResult(RESULT_OK);
+ }
+ } catch (IllegalStateException e) {
+ setTitle(r.getText(R.string.no_database));
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/com/android/browser/AddNewBookmark.java b/src/com/android/browser/AddNewBookmark.java
new file mode 100644
index 00000000..a75d002e
--- /dev/null
+++ b/src/com/android/browser/AddNewBookmark.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package com.android.browser;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/**
+ * Custom layout for an item representing a bookmark in the browser.
+ */
+ // FIXME: Remove BrowserBookmarkItem
+class AddNewBookmark extends LinearLayout {
+
+ private TextView mTextView;
+ private TextView mUrlText;
+ private ImageView mImageView;
+
+ /**
+ * Instantiate a bookmark item, including a default favicon.
+ *
+ * @param context The application context for the item.
+ */
+ AddNewBookmark(Context context) {
+ super(context);
+
+ setWillNotDraw(false);
+ LayoutInflater factory = LayoutInflater.from(context);
+ factory.inflate(R.layout.add_new_bookmark, this);
+ mTextView = (TextView) findViewById(R.id.title);
+ mUrlText = (TextView) findViewById(R.id.url);
+ mImageView = (ImageView) findViewById(R.id.favicon);
+ }
+
+ /**
+ * Copy this BookmarkItem to item.
+ * @param item BookmarkItem to receive the info from this BookmarkItem.
+ */
+ /* package */ void copyTo(AddNewBookmark item) {
+ item.mTextView.setText(mTextView.getText());
+ item.mUrlText.setText(mUrlText.getText());
+ item.mImageView.setImageDrawable(mImageView.getDrawable());
+ }
+
+ /**
+ * Set the new url for the bookmark item.
+ * @param url The new url for the bookmark item.
+ */
+ /* package */ void setUrl(String url) {
+ mUrlText.setText(url);
+ }
+}
diff --git a/src/com/android/browser/BookmarkItem.java b/src/com/android/browser/BookmarkItem.java
new file mode 100644
index 00000000..a70dd4f7
--- /dev/null
+++ b/src/com/android/browser/BookmarkItem.java
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+package com.android.browser;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/**
+ * Custom layout for an item representing a bookmark in the browser.
+ */
+class BookmarkItem extends LinearLayout {
+
+ protected TextView mTextView;
+ protected TextView mUrlText;
+ protected ImageView mImageView;
+ protected String mUrl;
+
+ /**
+ * Instantiate a bookmark item, including a default favicon.
+ *
+ * @param context The application context for the item.
+ */
+ BookmarkItem(Context context) {
+ super(context);
+
+ LayoutInflater factory = LayoutInflater.from(context);
+ factory.inflate(R.layout.history_item, this);
+ mTextView = (TextView) findViewById(R.id.title);
+ mUrlText = (TextView) findViewById(R.id.url);
+ mImageView = (ImageView) findViewById(R.id.favicon);
+ View star = findViewById(R.id.star);
+ star.setVisibility(View.GONE);
+ }
+
+ /**
+ * Copy this BookmarkItem to item.
+ * @param item BookmarkItem to receive the info from this BookmarkItem.
+ */
+ /* package */ void copyTo(BookmarkItem item) {
+ item.mTextView.setText(mTextView.getText());
+ item.mUrlText.setText(mUrlText.getText());
+ item.mImageView.setImageDrawable(mImageView.getDrawable());
+ }
+
+ /**
+ * Return the name assigned to this bookmark item.
+ */
+ /* package */ String getName() {
+ return mTextView.getText().toString();
+ }
+
+ /**
+ * Return the TextView which holds the name of this bookmark item.
+ */
+ /* package */ TextView getNameTextView() {
+ return mTextView;
+ }
+
+ /* package */ String getUrl() {
+ return mUrl;
+ }
+
+ /**
+ * Set the favicon for this item.
+ *
+ * @param b The new bitmap for this item.
+ * If it is null, will use the default.
+ */
+ /* package */ void setFavicon(Bitmap b) {
+ if (b != null) {
+ mImageView.setImageBitmap(b);
+ } else {
+ mImageView.setImageResource(R.drawable.app_web_browser_sm);
+ }
+ }
+
+ /**
+ * Set the new name for the bookmark item.
+ *
+ * @param name The new name for the bookmark item.
+ */
+ /* package */ void setName(String name) {
+ mTextView.setText(name);
+ }
+
+ /**
+ * Set the new url for the bookmark item.
+ * @param url The new url for the bookmark item.
+ */
+ /* package */ void setUrl(String url) {
+ mUrlText.setText(url);
+ mUrl = url;
+ }
+}
diff --git a/src/com/android/browser/Browser.java b/src/com/android/browser/Browser.java
new file mode 100644
index 00000000..32cee670
--- /dev/null
+++ b/src/com/android/browser/Browser.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.browser;
+
+import android.util.Config;
+import android.util.Log;
+
+import android.app.Application;
+import android.content.Intent;
+import android.webkit.CookieManager;
+import android.webkit.CookieSyncManager;
+
+import dalvik.system.VMRuntime;
+
+public class Browser extends Application {
+
+ private final static String LOGTAG = "browser";
+
+ /**
+ * Specifies a heap utilization ratio that works better
+ * for the browser than the default ratio does.
+ */
+ private final static float TARGET_HEAP_UTILIZATION = 0.75f;
+
+ public Browser() {
+ }
+
+ public void onCreate() {
+ if (Config.LOGV)
+ Log.v(LOGTAG, "Browser.onCreate: this=" + this);
+ // Fix heap utilization for better heap size characteristics.
+ VMRuntime.getRuntime().setTargetHeapUtilization(
+ TARGET_HEAP_UTILIZATION);
+ // create CookieSyncManager with current Context
+ CookieSyncManager.createInstance(this);
+ // remove all expired cookies
+ CookieManager.getInstance().removeExpiredCookie();
+ }
+
+ static Intent createBrowserViewIntent() {
+ Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
+ return intent;
+ }
+}
+
diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java
new file mode 100644
index 00000000..53640e5e
--- /dev/null
+++ b/src/com/android/browser/BrowserActivity.java
@@ -0,0 +1,4567 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.browser;
+
+import com.google.android.googleapps.IGoogleLoginService;
+import com.google.android.googlelogin.GoogleLoginServiceConstants;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.app.SearchManager;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.DrawFilter;
+import android.graphics.Paint;
+import android.graphics.PaintFlagsDrawFilter;
+import android.graphics.Picture;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.graphics.drawable.PaintDrawable;
+import android.hardware.SensorListener;
+import android.hardware.SensorManager;
+import android.net.ConnectivityManager;
+import android.net.Uri;
+import android.net.WebAddress;
+import android.net.http.EventHandler;
+import android.net.http.SslCertificate;
+import android.net.http.SslError;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Debug;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.preference.PreferenceManager;
+import android.provider.Browser;
+import android.provider.Contacts;
+import android.provider.Downloads;
+import android.provider.MediaStore;
+import android.provider.Contacts.Intents.Insert;
+import android.text.IClipboard;
+import android.text.TextUtils;
+import android.text.format.DateFormat;
+import android.text.util.Regex;
+import android.util.Config;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.MenuItem.OnMenuItemClickListener;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.ScaleAnimation;
+import android.view.animation.TranslateAnimation;
+import android.webkit.CookieManager;
+import android.webkit.CookieSyncManager;
+import android.webkit.DownloadListener;
+import android.webkit.HttpAuthHandler;
+import android.webkit.SslErrorHandler;
+import android.webkit.URLUtil;
+import android.webkit.WebChromeClient;
+import android.webkit.WebHistoryItem;
+import android.webkit.WebIconDatabase;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Vector;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+public class BrowserActivity extends Activity
+ implements KeyTracker.OnKeyTracker,
+ View.OnCreateContextMenuListener,
+ DownloadListener {
+
+ private IGoogleLoginService mGls = null;
+ private ServiceConnection mGlsConnection = null;
+
+ private SensorManager mSensorManager = null;
+
+ /* Whitelisted webpages
+ private static HashSet<String> sWhiteList;
+
+ static {
+ sWhiteList = new HashSet<String>();
+ sWhiteList.add("cnn.com/");
+ sWhiteList.add("espn.go.com/");
+ sWhiteList.add("nytimes.com/");
+ sWhiteList.add("engadget.com/");
+ sWhiteList.add("yahoo.com/");
+ sWhiteList.add("msn.com/");
+ sWhiteList.add("amazon.com/");
+ sWhiteList.add("consumerist.com/");
+ sWhiteList.add("google.com/m/news");
+ }
+ */
+
+ private void setupHomePage() {
+ final Runnable getAccount = new Runnable() {
+ public void run() {
+ // Lower priority
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ // get the default home page
+ String homepage = mSettings.getHomePage();
+
+ try {
+ if (mGls == null) return;
+
+ String hostedUser = mGls.getAccount(GoogleLoginServiceConstants.PREFER_HOSTED);
+ String googleUser = mGls.getAccount(GoogleLoginServiceConstants.REQUIRE_GOOGLE);
+
+ // three cases:
+ //
+ // hostedUser == googleUser
+ // The device has only a google account
+ //
+ // hostedUser != googleUser
+ // The device has a hosted account and a google account
+ //
+ // hostedUser != null, googleUser == null
+ // The device has only a hosted account (so far)
+
+ // developers might have no accounts at all
+ if (hostedUser == null) return;
+
+ if (googleUser == null || !hostedUser.equals(googleUser)) {
+ String domain = hostedUser.substring(hostedUser.lastIndexOf('@')+1);
+ homepage = "http://www.google.com/m/a/" + domain + "?client=ms-" +
+ SystemProperties.get("persist.sys.com.google.clientid", "unknown");
+ }
+ } catch (RemoteException ignore) {
+ // Login service died; carry on
+ } catch (RuntimeException ignore) {
+ // Login service died; carry on
+ } finally {
+ finish(homepage);
+ }
+ }
+
+ private void finish(final String homepage) {
+ mHandler.post(new Runnable() {
+ public void run() {
+ mSettings.setHomePage(BrowserActivity.this, homepage);
+ resumeAfterCredentials();
+
+ // as this is running in a separate thread,
+ // BrowserActivity's onDestroy() may have been called,
+ // which also calls unbindService().
+ if (mGlsConnection != null) {
+ // we no longer need to keep GLS open
+ unbindService(mGlsConnection);
+ mGlsConnection = null;
+ }
+ } });
+ } };
+
+ final boolean[] done = { false };
+
+ // Open a connection to the Google Login Service. The first
+ // time the connection is established, set up the homepage depending on
+ // the account in a background thread.
+ mGlsConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ mGls = IGoogleLoginService.Stub.asInterface(service);
+ if (done[0] == false) {
+ done[0] = true;
+ Thread account = new Thread(getAccount);
+ account.setName("GLSAccount");
+ account.start();
+ }
+ }
+ public void onServiceDisconnected(ComponentName className) {
+ mGls = null;
+ }
+ };
+
+ bindService(GoogleLoginServiceConstants.SERVICE_INTENT,
+ mGlsConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ /**
+ * This class is in charge of installing pre-packaged plugins
+ * from the Browser assets directory to the user's data partition.
+ * Plugins are loaded from the "plugins" directory in the assets;
+ * Anything that is in this directory will be copied over to the
+ * user data partition in app_plugins.
+ */
+ private class CopyPlugins implements Runnable {
+ final static String TAG = "PluginsInstaller";
+ final static String ZIP_FILTER = "assets/plugins/";
+ final static String APK_PATH = "/system/app/Browser.apk";
+ final static String PLUGIN_EXTENSION = ".so";
+ final static String TEMPORARY_EXTENSION = "_temp";
+ final static String BUILD_INFOS_FILE = "build.prop";
+ final static String SYSTEM_BUILD_INFOS_FILE = "/system/"
+ + BUILD_INFOS_FILE;
+ final int BUFSIZE = 4096;
+ boolean mDoOverwrite = false;
+ String pluginsPath;
+ Context mContext;
+ File pluginsDir;
+ AssetManager manager;
+
+ public CopyPlugins (boolean overwrite, Context context) {
+ mDoOverwrite = overwrite;
+ mContext = context;
+ }
+
+ /**
+ * Returned a filtered list of ZipEntry.
+ * We list all the files contained in the zip and
+ * only returns the ones starting with the ZIP_FILTER
+ * path.
+ *
+ * @param zip the zip file used.
+ */
+ public Vector<ZipEntry> pluginsFilesFromZip(ZipFile zip) {
+ Vector<ZipEntry> list = new Vector<ZipEntry>();
+ Enumeration entries = zip.entries();
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = (ZipEntry) entries.nextElement();
+ if (entry.getName().startsWith(ZIP_FILTER)) {
+ list.add(entry);
+ }
+ }
+ return list;
+ }
+
+ /**
+ * Utility method to copy the content from an inputstream
+ * to a file output stream.
+ */
+ public void copyStreams(InputStream is, FileOutputStream fos) {
+ BufferedOutputStream os = null;
+ try {
+ byte data[] = new byte[BUFSIZE];
+ int count;
+ os = new BufferedOutputStream(fos, BUFSIZE);
+ while ((count = is.read(data, 0, BUFSIZE)) != -1) {
+ os.write(data, 0, count);
+ }
+ os.flush();
+ } catch (IOException e) {
+ Log.e(TAG, "Exception while copying: " + e);
+ } finally {
+ try {
+ if (os != null) {
+ os.close();
+ }
+ } catch (IOException e2) {
+ Log.e(TAG, "Exception while closing the stream: " + e2);
+ }
+ }
+ }
+
+ /**
+ * Returns a string containing the contents of a file
+ *
+ * @param file the target file
+ */
+ private String contentsOfFile(File file) {
+ String ret = null;
+ FileInputStream is = null;
+ try {
+ byte[] buffer = new byte[BUFSIZE];
+ int count;
+ is = new FileInputStream(file);
+ StringBuffer out = new StringBuffer();
+
+ while ((count = is.read(buffer, 0, BUFSIZE)) != -1) {
+ out.append(new String(buffer, 0, count));
+ }
+ ret = out.toString();
+ } catch (IOException e) {
+ Log.e(TAG, "Exception getting contents of file " + e);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e2) {
+ Log.e(TAG, "Exception while closing the file: " + e2);
+ }
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Utility method to initialize the user data plugins path.
+ */
+ public void initPluginsPath() {
+ BrowserSettings s = BrowserSettings.getInstance();
+ pluginsPath = s.getPluginsPath();
+ if (pluginsPath == null) {
+ s.loadFromDb(mContext);
+ pluginsPath = s.getPluginsPath();
+ }
+ if (Config.LOGV) {
+ Log.v(TAG, "Plugin path: " + pluginsPath);
+ }
+ }
+
+ /**
+ * Utility method to delete a file or a directory
+ *
+ * @param file the File to delete
+ */
+ public void deleteFile(File file) {
+ File[] files = file.listFiles();
+ if ((files != null) && files.length > 0) {
+ for (int i=0; i< files.length; i++) {
+ deleteFile(files[i]);
+ }
+ }
+ if (!file.delete()) {
+ Log.e(TAG, file.getPath() + " could not get deleted");
+ }
+ }
+
+ /**
+ * Clean the content of the plugins directory.
+ * We delete the directory, then recreate it.
+ */
+ public void cleanPluginsDirectory() {
+ if (Config.LOGV) {
+ Log.v(TAG, "delete plugins directory: " + pluginsPath);
+ }
+ File pluginsDirectory = new File(pluginsPath);
+ deleteFile(pluginsDirectory);
+ pluginsDirectory.mkdir();
+ }
+
+
+ /**
+ * Copy the SYSTEM_BUILD_INFOS_FILE file containing the
+ * informations about the system build to the
+ * BUILD_INFOS_FILE in the plugins directory.
+ */
+ public void copyBuildInfos() {
+ try {
+ if (Config.LOGV) {
+ Log.v(TAG, "Copy build infos to the plugins directory");
+ }
+ File buildInfoFile = new File(SYSTEM_BUILD_INFOS_FILE);
+ File buildInfoPlugins = new File(pluginsPath, BUILD_INFOS_FILE);
+ copyStreams(new FileInputStream(buildInfoFile),
+ new FileOutputStream(buildInfoPlugins));
+ } catch (IOException e) {
+ Log.e(TAG, "Exception while copying the build infos: " + e);
+ }
+ }
+
+ /**
+ * Returns true if the current system is newer than the
+ * system that installed the plugins.
+ * We determinate this by checking the build number of the system.
+ *
+ * At the end of the plugins copy operation, we copy the
+ * SYSTEM_BUILD_INFOS_FILE to the BUILD_INFOS_FILE.
+ * We then just have to load both and compare them -- if they
+ * are different the current system is newer.
+ *
+ * Loading and comparing the strings should be faster than
+ * creating a hash, the files being rather small. Extracting the
+ * version number would require some parsing which may be more
+ * brittle.
+ */
+ public boolean newSystemImage() {
+ try {
+ File buildInfoFile = new File(SYSTEM_BUILD_INFOS_FILE);
+ File buildInfoPlugins = new File(pluginsPath, BUILD_INFOS_FILE);
+ if (!buildInfoPlugins.exists()) {
+ if (Config.LOGV) {
+ Log.v(TAG, "build.prop in plugins directory " + pluginsPath
+ + " does not exist, therefore it's a new system image");
+ }
+ return true;
+ } else {
+ String buildInfo = contentsOfFile(buildInfoFile);
+ String buildInfoPlugin = contentsOfFile(buildInfoPlugins);
+ if (buildInfo == null || buildInfoPlugin == null
+ || buildInfo.compareTo(buildInfoPlugin) != 0) {
+ if (Config.LOGV) {
+ Log.v(TAG, "build.prop are different, "
+ + " therefore it's a new system image");
+ }
+ return true;
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Exc in newSystemImage(): " + e);
+ }
+ return false;
+ }
+
+ /**
+ * Check if the version of the plugins contained in the
+ * Browser assets is the same as the version of the plugins
+ * in the plugins directory.
+ * We simply iterate on every file in the assets/plugins
+ * and return false if a file listed in the assets does
+ * not exist in the plugins directory.
+ */
+ private boolean checkIsDifferentVersions() {
+ try {
+ ZipFile zip = new ZipFile(APK_PATH);
+ Vector<ZipEntry> files = pluginsFilesFromZip(zip);
+ int zipFilterLength = ZIP_FILTER.length();
+
+ Enumeration entries = files.elements();
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = (ZipEntry) entries.nextElement();
+ String path = entry.getName().substring(zipFilterLength);
+ File outputFile = new File(pluginsPath, path);
+ if (!outputFile.exists()) {
+ if (Config.LOGV) {
+ Log.v(TAG, "checkIsDifferentVersions(): extracted file "
+ + path + " does not exist, we have a different version");
+ }
+ return true;
+ }
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Exception in checkDifferentVersions(): " + e);
+ }
+ return false;
+ }
+
+ /**
+ * Copy every files from the assets/plugins directory
+ * to the app_plugins directory in the data partition.
+ * Once copied, we copy over the SYSTEM_BUILD_INFOS file
+ * in the plugins directory.
+ *
+ * NOTE: we directly access the content from the Browser
+ * package (it's a zip file) and do not use AssetManager
+ * as there is a limit of 1Mb (see Asset.h)
+ */
+ public void run() {
+ // Lower the priority
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ try {
+ if (pluginsPath == null) {
+ Log.e(TAG, "No plugins path found!");
+ return;
+ }
+
+ ZipFile zip = new ZipFile(APK_PATH);
+ Vector<ZipEntry> files = pluginsFilesFromZip(zip);
+ Vector<File> plugins = new Vector<File>();
+ int zipFilterLength = ZIP_FILTER.length();
+
+ Enumeration entries = files.elements();
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = (ZipEntry) entries.nextElement();
+ String path = entry.getName().substring(zipFilterLength);
+ File outputFile = new File(pluginsPath, path);
+ outputFile.getParentFile().mkdirs();
+
+ if (outputFile.exists() && !mDoOverwrite) {
+ if (Config.LOGV) {
+ Log.v(TAG, path + " already extracted.");
+ }
+ } else {
+ if (path.endsWith(PLUGIN_EXTENSION)) {
+ // We rename plugins to be sure a half-copied
+ // plugin is not loaded by the browser.
+ plugins.add(outputFile);
+ outputFile = new File(pluginsPath,
+ path + TEMPORARY_EXTENSION);
+ }
+ FileOutputStream fos = new FileOutputStream(outputFile);
+ if (Config.LOGV) {
+ Log.v(TAG, "copy " + entry + " to "
+ + pluginsPath + "/" + path);
+ }
+ copyStreams(zip.getInputStream(entry), fos);
+ }
+ }
+
+ // We now rename the .so we copied, once all their resources
+ // are safely copied over to the user data partition.
+ Enumeration elems = plugins.elements();
+ while (elems.hasMoreElements()) {
+ File renamedFile = (File) elems.nextElement();
+ File sourceFile = new File(renamedFile.getPath()
+ + TEMPORARY_EXTENSION);
+ if (Config.LOGV) {
+ Log.v(TAG, "rename " + sourceFile.getPath()
+ + " to " + renamedFile.getPath());
+ }
+ sourceFile.renameTo(renamedFile);
+ }
+
+ copyBuildInfos();
+
+ // Refresh the plugin list.
+ if (mTabControl.getCurrentWebView() != null) {
+ mTabControl.getCurrentWebView().refreshPlugins(false);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "IO Exception: " + e);
+ }
+ }
+ };
+
+ /**
+ * Copy the content of assets/plugins/ to the app_plugins directory
+ * in the data partition.
+ *
+ * This function is called every time the browser is started.
+ * We first check if the system image is newer than the one that
+ * copied the plugins (if there's plugins in the data partition).
+ * If this is the case, we then check if the versions are different.
+ * If they are different, we clean the plugins directory in the
+ * data partition, then start a thread to copy the plugins while
+ * the browser continue to load.
+ *
+ * @param overwrite if true overwrite the files even if they are
+ * already present (to let the user "reset" the plugins if needed).
+ */
+ private void copyPlugins(boolean overwrite) {
+ CopyPlugins copyPluginsFromAssets = new CopyPlugins(overwrite, this);
+ copyPluginsFromAssets.initPluginsPath();
+ if (copyPluginsFromAssets.newSystemImage()) {
+ if (copyPluginsFromAssets.checkIsDifferentVersions()) {
+ copyPluginsFromAssets.cleanPluginsDirectory();
+ Thread copyplugins = new Thread(copyPluginsFromAssets);
+ copyplugins.setName("CopyPlugins");
+ copyplugins.start();
+ }
+ }
+ }
+
+ private class ClearThumbnails extends AsyncTask<File, Void, Void> {
+ @Override
+ public Void doInBackground(File... files) {
+ if (files != null) {
+ for (File f : files) {
+ f.delete();
+ }
+ }
+ return null;
+ }
+ }
+
+ @Override public void onCreate(Bundle icicle) {
+ if (Config.LOGV) {
+ 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);
+
+ // test the browser in OpenGL
+ // requestWindowFeature(Window.FEATURE_OPENGL);
+
+ setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
+
+ mResolver = getContentResolver();
+
+ setBaseSearchUrl(PreferenceManager.getDefaultSharedPreferences(this)
+ .getString("search_url", ""));
+
+ //
+ // start MASF proxy service
+ //
+ //Intent proxyServiceIntent = new Intent();
+ //proxyServiceIntent.setComponent
+ // (new ComponentName(
+ // "com.android.masfproxyservice",
+ // "com.android.masfproxyservice.MasfProxyService"));
+ //startService(proxyServiceIntent, null);
+
+ mSecLockIcon = Resources.getSystem().getDrawable(
+ android.R.drawable.ic_secure);
+ mMixLockIcon = Resources.getSystem().getDrawable(
+ android.R.drawable.ic_partial_secure);
+ mGenericFavicon = getResources().getDrawable(
+ R.drawable.app_web_browser_sm);
+
+ mContentView = (FrameLayout) getWindow().getDecorView().findViewById(
+ com.android.internal.R.id.content);
+
+ // Create the tab control and our initial tab
+ mTabControl = new TabControl(this);
+
+ // Open the icon database and retain all the bookmark urls for favicons
+ retainIconsOnStartup();
+
+ // Keep a settings instance handy.
+ mSettings = BrowserSettings.getInstance();
+ mSettings.setTabControl(mTabControl);
+ mSettings.loadFromDb(this);
+
+ PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Browser");
+
+ if (!mTabControl.restoreState(icicle)) {
+ // clear up the thumbnail directory if we can't restore the state as
+ // none of the files in the directory are referenced any more.
+ new ClearThumbnails().execute(
+ mTabControl.getThumbnailDir().listFiles());
+ final Intent intent = getIntent();
+ final Bundle extra = intent.getExtras();
+ // Create an initial tab.
+ // If the intent is ACTION_VIEW and data is not null, the Browser is
+ // invoked to view the content by another application. In this case,
+ // the tab will be close when exit.
+ final TabControl.Tab t = mTabControl.createNewTab(
+ Intent.ACTION_VIEW.equals(intent.getAction()) &&
+ intent.getData() != null);
+ mTabControl.setCurrentTab(t);
+ // This is one of the only places we call attachTabToContentView
+ // without animating from the tab picker.
+ attachTabToContentView(t);
+ WebView webView = t.getWebView();
+ if (extra != null) {
+ int scale = extra.getInt(Browser.INITIAL_ZOOM_LEVEL, 0);
+ if (scale > 0 && scale <= 1000) {
+ webView.setInitialScale(scale);
+ }
+ }
+ // If we are not restoring from an icicle, then there is a high
+ // likely hood this is the first run. So, check to see if the
+ // homepage needs to be configured and copy any plugins from our
+ // asset directory to the data partition.
+ if ((extra == null || !extra.getBoolean("testing"))
+ && !mSettings.isLoginInitialized()) {
+ setupHomePage();
+ }
+ copyPlugins(true);
+
+ String url = getUrlFromIntent(intent);
+ if (url == null || url.length() == 0) {
+ if (mSettings.isLoginInitialized()) {
+ webView.loadUrl(mSettings.getHomePage());
+ } else {
+ waitForCredentials();
+ }
+ } else {
+ webView.loadUrl(url);
+ }
+ } else {
+ // TabControl.restoreState() will create a new tab even if
+ // restoring the state fails. Attach it to the view here since we
+ // are not animating from the tab picker.
+ attachTabToContentView(mTabControl.getCurrentTab());
+ }
+
+ /* enables registration for changes in network status from
+ http stack */
+ mNetworkStateChangedFilter = new IntentFilter();
+ mNetworkStateChangedFilter.addAction(
+ ConnectivityManager.CONNECTIVITY_ACTION);
+ mNetworkStateIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(
+ ConnectivityManager.CONNECTIVITY_ACTION)) {
+ boolean down = intent.getBooleanExtra(
+ ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
+ onNetworkToggle(!down);
+ }
+ }
+ };
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ TabControl.Tab current = mTabControl.getCurrentTab();
+ // When a tab is closed on exit, the current tab index is set to -1.
+ // Reset before proceed as Browser requires the current tab to be set.
+ if (current == null) {
+ // Try to reset the tab in case the index was incorrect.
+ current = mTabControl.getTab(0);
+ if (current == null) {
+ // No tabs at all so just ignore this intent.
+ return;
+ }
+ mTabControl.setCurrentTab(current);
+ attachTabToContentView(current);
+ resetTitleAndIcon(current.getWebView());
+ }
+ final String action = intent.getAction();
+ final int flags = intent.getFlags();
+ if (Intent.ACTION_MAIN.equals(action) ||
+ (flags & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
+ // just resume the browser
+ return;
+ }
+ if (Intent.ACTION_VIEW.equals(action)
+ || Intent.ACTION_SEARCH.equals(action)
+ || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
+ || Intent.ACTION_WEB_SEARCH.equals(action)) {
+ String url = getUrlFromIntent(intent);
+ if (url == null || url.length() == 0) {
+ url = mSettings.getHomePage();
+ }
+ if (Intent.ACTION_VIEW.equals(action) &&
+ (flags & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
+ // if FLAG_ACTIVITY_BROUGHT_TO_FRONT flag is on, the url will be
+ // opened in a new tab unless we have reached MAX_TABS. Then the
+ // url will be opened in the current tab. If a new tab is
+ // created, it will have "true" for exit on close.
+ openTabAndShow(url, null, true);
+ } else {
+ if ("about:debug".equals(url)) {
+ mSettings.toggleDebugSettings();
+ return;
+ }
+ // If the Window overview is up and we are not in the midst of
+ // an animation, animate away from the Window overview.
+ if (mTabOverview != null && mAnimationCount == 0) {
+ sendAnimateFromOverview(current, false, url,
+ TAB_OVERVIEW_DELAY, null);
+ } else {
+ // Get rid of the subwindow if it exists
+ dismissSubWindow(current);
+ current.getWebView().loadUrl(url);
+ }
+ }
+ }
+ }
+
+ private String getUrlFromIntent(Intent intent) {
+ String url = null;
+ if (intent != null) {
+ final String action = intent.getAction();
+ if (Intent.ACTION_VIEW.equals(action)) {
+ url = smartUrlFilter(intent.getData());
+ if (url != null && url.startsWith("content:")) {
+ /* Append mimetype so webview knows how to display */
+ String mimeType = intent.resolveType(getContentResolver());
+ if (mimeType != null) {
+ url += "?" + mimeType;
+ }
+ }
+ } else if (Intent.ACTION_SEARCH.equals(action)
+ || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
+ || Intent.ACTION_WEB_SEARCH.equals(action)) {
+ url = intent.getStringExtra(SearchManager.QUERY);
+ if (url != null) {
+ mLastEnteredUrl = url;
+ // Don't add Urls, just search terms.
+ // Urls will get added when the page is loaded.
+ if (!Regex.WEB_URL_PATTERN.matcher(url).matches()) {
+ Browser.updateVisitedHistory(mResolver, url, false);
+ }
+ // In general, we shouldn't modify URL from Intent.
+ // But currently, we get the user-typed URL from search box as well.
+ url = fixUrl(url);
+ url = smartUrlFilter(url);
+ String searchSource = "&source=android-" + GOOGLE_SEARCH_SOURCE_SUGGEST + "&";
+ if (url.contains(searchSource)) {
+ String source = null;
+ final Bundle appData = intent.getBundleExtra(SearchManager.APP_DATA);
+ if (appData != null) {
+ source = appData.getString(SearchManager.SOURCE);
+ }
+ if (TextUtils.isEmpty(source)) {
+ source = GOOGLE_SEARCH_SOURCE_UNKNOWN;
+ }
+ url = url.replace(searchSource, "&source=android-"+source+"&");
+ }
+ }
+ }
+ }
+ return url;
+ }
+
+ /* package */ static String fixUrl(String inUrl) {
+ if (inUrl.startsWith("http://") || inUrl.startsWith("https://"))
+ return inUrl;
+ if (inUrl.startsWith("http:") ||
+ inUrl.startsWith("https:")) {
+ if (inUrl.startsWith("http:/") || inUrl.startsWith("https:/")) {
+ inUrl = inUrl.replaceFirst("/", "//");
+ } else inUrl = inUrl.replaceFirst(":", "://");
+ }
+ return inUrl;
+ }
+
+ /**
+ * Looking for the pattern like this
+ *
+ * *
+ * * *
+ * *** * *******
+ * * *
+ * * *
+ * *
+ */
+ private final SensorListener mSensorListener = new SensorListener() {
+ private long mLastGestureTime;
+ private float[] mPrev = new float[3];
+ private float[] mPrevDiff = new float[3];
+ private float[] mDiff = new float[3];
+ private float[] mRevertDiff = new float[3];
+
+ public void onSensorChanged(int sensor, float[] values) {
+ boolean show = false;
+ float[] diff = new float[3];
+
+ for (int i = 0; i < 3; i++) {
+ diff[i] = values[i] - mPrev[i];
+ if (Math.abs(diff[i]) > 1) {
+ show = true;
+ }
+ if ((diff[i] > 1.0 && mDiff[i] < 0.2)
+ || (diff[i] < -1.0 && mDiff[i] > -0.2)) {
+ // start track when there is a big move, or revert
+ mRevertDiff[i] = mDiff[i];
+ mDiff[i] = 0;
+ } else if (diff[i] > -0.2 && diff[i] < 0.2) {
+ // reset when it is flat
+ mDiff[i] = mRevertDiff[i] = 0;
+ }
+ mDiff[i] += diff[i];
+ mPrevDiff[i] = diff[i];
+ mPrev[i] = values[i];
+ }
+
+ if (false) {
+ // only shows if we think the delta is big enough, in an attempt
+ // to detect "serious" moves left/right or up/down
+ Log.d("BrowserSensorHack", "sensorChanged " + sensor + " ("
+ + values[0] + ", " + values[1] + ", " + values[2] + ")"
+ + " diff(" + diff[0] + " " + diff[1] + " " + diff[2]
+ + ")");
+ Log.d("BrowserSensorHack", " mDiff(" + mDiff[0] + " "
+ + mDiff[1] + " " + mDiff[2] + ")" + " mRevertDiff("
+ + mRevertDiff[0] + " " + mRevertDiff[1] + " "
+ + mRevertDiff[2] + ")");
+ }
+
+ long now = android.os.SystemClock.uptimeMillis();
+ if (now - mLastGestureTime > 1000) {
+ mLastGestureTime = 0;
+
+ float y = mDiff[1];
+ float z = mDiff[2];
+ float ay = Math.abs(y);
+ float az = Math.abs(z);
+ float ry = mRevertDiff[1];
+ float rz = mRevertDiff[2];
+ float ary = Math.abs(ry);
+ float arz = Math.abs(rz);
+ boolean gestY = ay > 2.5f && ary > 1.0f && ay > ary;
+ boolean gestZ = az > 3.5f && arz > 1.0f && az > arz;
+
+ if ((gestY || gestZ) && !(gestY && gestZ)) {
+ WebView view = mTabControl.getCurrentWebView();
+
+ if (view != null) {
+ if (gestZ) {
+ if (z < 0) {
+ view.zoomOut();
+ } else {
+ view.zoomIn();
+ }
+ } else {
+ view.flingScroll(0, Math.round(y * 100));
+ }
+ }
+ mLastGestureTime = now;
+ }
+ }
+ }
+
+ public void onAccuracyChanged(int sensor, int accuracy) {
+ // TODO Auto-generated method stub
+
+ }
+ };
+
+ @Override protected void onResume() {
+ super.onResume();
+ if (Config.LOGV) {
+ Log.v(LOGTAG, "BrowserActivity.onResume: this=" + this);
+ }
+
+ if (!mActivityInPause) {
+ Log.e(LOGTAG, "BrowserActivity is already resumed.");
+ return;
+ }
+
+ mActivityInPause = false;
+ resumeWebView();
+
+ if (mWakeLock.isHeld()) {
+ mHandler.removeMessages(RELEASE_WAKELOCK);
+ mWakeLock.release();
+ }
+
+ if (mCredsDlg != null) {
+ if (!mHandler.hasMessages(CANCEL_CREDS_REQUEST)) {
+ // In case credential request never comes back
+ mHandler.sendEmptyMessageDelayed(CANCEL_CREDS_REQUEST, 6000);
+ }
+ }
+
+ registerReceiver(mNetworkStateIntentReceiver,
+ mNetworkStateChangedFilter);
+ WebView.enablePlatformNotifications();
+
+ if (mSettings.doFlick()) {
+ if (mSensorManager == null) {
+ mSensorManager = (SensorManager) getSystemService(
+ Context.SENSOR_SERVICE);
+ }
+ mSensorManager.registerListener(mSensorListener,
+ SensorManager.SENSOR_ACCELEROMETER,
+ SensorManager.SENSOR_DELAY_FASTEST);
+ } else {
+ mSensorManager = null;
+ }
+ }
+
+ /**
+ * onSaveInstanceState(Bundle map)
+ * onSaveInstanceState is called right before onStop(). The map contains
+ * the saved state.
+ */
+ @Override protected void onSaveInstanceState(Bundle outState) {
+ if (Config.LOGV) {
+ Log.v(LOGTAG, "BrowserActivity.onSaveInstanceState: this=" + this);
+ }
+ // the default implementation requires each view to have an id. As the
+ // browser handles the state itself and it doesn't use id for the views,
+ // don't call the default implementation. Otherwise it will trigger the
+ // warning like this, "couldn't save which view has focus because the
+ // focused view XXX has no id".
+
+ // Save all the tabs
+ mTabControl.saveState(outState);
+ }
+
+ @Override protected void onPause() {
+ super.onPause();
+
+ if (mActivityInPause) {
+ Log.e(LOGTAG, "BrowserActivity is already paused.");
+ return;
+ }
+
+ mActivityInPause = true;
+ if (mTabControl.getCurrentIndex() >= 0 && !pauseWebView()) {
+ mWakeLock.acquire();
+ mHandler.sendMessageDelayed(mHandler
+ .obtainMessage(RELEASE_WAKELOCK), WAKELOCK_TIMEOUT);
+ }
+
+ // Clear the credentials toast if it is up
+ if (mCredsDlg != null && mCredsDlg.isShowing()) {
+ mCredsDlg.dismiss();
+ }
+ mCredsDlg = null;
+
+ cancelStopToast();
+
+ // unregister network state listener
+ unregisterReceiver(mNetworkStateIntentReceiver);
+ WebView.disablePlatformNotifications();
+
+ if (mSensorManager != null) {
+ mSensorManager.unregisterListener(mSensorListener);
+ }
+ }
+
+ @Override protected void onDestroy() {
+ if (Config.LOGV) {
+ Log.v(LOGTAG, "BrowserActivity.onDestroy: this=" + this);
+ }
+ super.onDestroy();
+ // Remove the current tab and sub window
+ TabControl.Tab t = mTabControl.getCurrentTab();
+ dismissSubWindow(t);
+ removeTabFromContentView(t);
+ // Destroy all the tabs
+ mTabControl.destroy();
+ WebIconDatabase.getInstance().close();
+ if (mGlsConnection != null) {
+ unbindService(mGlsConnection);
+ mGlsConnection = null;
+ }
+
+ //
+ // stop MASF proxy service
+ //
+ //Intent proxyServiceIntent = new Intent();
+ //proxyServiceIntent.setComponent
+ // (new ComponentName(
+ // "com.android.masfproxyservice",
+ // "com.android.masfproxyservice.MasfProxyService"));
+ //stopService(proxyServiceIntent);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ if (mPageInfoDialog != null) {
+ mPageInfoDialog.dismiss();
+ showPageInfo(
+ mPageInfoView,
+ mPageInfoFromShowSSLCertificateOnError.booleanValue());
+ }
+ if (mSSLCertificateDialog != null) {
+ mSSLCertificateDialog.dismiss();
+ showSSLCertificate(
+ mSSLCertificateView);
+ }
+ if (mSSLCertificateOnErrorDialog != null) {
+ mSSLCertificateOnErrorDialog.dismiss();
+ showSSLCertificateOnError(
+ mSSLCertificateOnErrorView,
+ mSSLCertificateOnErrorHandler,
+ mSSLCertificateOnErrorError);
+ }
+ if (mHttpAuthenticationDialog != null) {
+ String title = ((TextView) mHttpAuthenticationDialog
+ .findViewById(com.android.internal.R.id.alertTitle)).getText()
+ .toString();
+ String name = ((TextView) mHttpAuthenticationDialog
+ .findViewById(R.id.username_edit)).getText().toString();
+ String password = ((TextView) mHttpAuthenticationDialog
+ .findViewById(R.id.password_edit)).getText().toString();
+ int focusId = mHttpAuthenticationDialog.getCurrentFocus()
+ .getId();
+ mHttpAuthenticationDialog.dismiss();
+ showHttpAuthentication(mHttpAuthHandler, null, null, title,
+ name, password, focusId);
+ }
+ if (mFindDialog != null && mFindDialog.isShowing()) {
+ mFindDialog.onConfigurationChanged(newConfig);
+ }
+ }
+
+ @Override public void onLowMemory() {
+ super.onLowMemory();
+ mTabControl.freeMemory();
+ }
+
+ private boolean resumeWebView() {
+ if ((!mActivityInPause && !mPageStarted) ||
+ (mActivityInPause && mPageStarted)) {
+ CookieSyncManager.getInstance().startSync();
+ WebView w = mTabControl.getCurrentWebView();
+ if (w != null) {
+ w.resumeTimers();
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private boolean pauseWebView() {
+ if (mActivityInPause && !mPageStarted) {
+ CookieSyncManager.getInstance().stopSync();
+ WebView w = mTabControl.getCurrentWebView();
+ if (w != null) {
+ w.pauseTimers();
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /*
+ * This function is called when we are launching for the first time. We
+ * are waiting for the login credentials before loading Google home
+ * pages. This way the user will be logged in straight away.
+ */
+ private void waitForCredentials() {
+ // Show a toast
+ mCredsDlg = new ProgressDialog(this);
+ mCredsDlg.setIndeterminate(true);
+ mCredsDlg.setMessage(getText(R.string.retrieving_creds_dlg_msg));
+ // If the user cancels the operation, then cancel the Google
+ // Credentials request.
+ mCredsDlg.setCancelMessage(mHandler.obtainMessage(CANCEL_CREDS_REQUEST));
+ mCredsDlg.show();
+
+ // We set a timeout for the retrieval of credentials in onResume()
+ // as that is when we have freed up some CPU time to get
+ // the login credentials.
+ }
+
+ /*
+ * If we have received the credentials or we have timed out and we are
+ * showing the credentials dialog, then it is time to move on.
+ */
+ private void resumeAfterCredentials() {
+ if (mCredsDlg == null) {
+ return;
+ }
+
+ // Clear the toast
+ if (mCredsDlg.isShowing()) {
+ mCredsDlg.dismiss();
+ }
+ mCredsDlg = null;
+
+ // Clear any pending timeout
+ mHandler.removeMessages(CANCEL_CREDS_REQUEST);
+
+ // Load the page
+ WebView w = mTabControl.getCurrentWebView();
+ if (w != null) {
+ w.loadUrl(mSettings.getHomePage());
+ }
+
+ // Update the settings, need to do this last as it can take a moment
+ // to persist the settings. In the mean time we could be loading
+ // content.
+ mSettings.setLoginInitialized(this);
+ }
+
+ // Open the icon database and retain all the icons for visited sites.
+ private void retainIconsOnStartup() {
+ final WebIconDatabase db = WebIconDatabase.getInstance();
+ db.open(getDir("icons", 0).getPath());
+ try {
+ Cursor c = Browser.getAllBookmarks(mResolver);
+ if (!c.moveToFirst()) {
+ c.deactivate();
+ return;
+ }
+ int urlIndex = c.getColumnIndex(Browser.BookmarkColumns.URL);
+ do {
+ String url = c.getString(urlIndex);
+ db.retainIconForPageUrl(url);
+ } while (c.moveToNext());
+ c.deactivate();
+ } catch (IllegalStateException e) {
+ Log.e(LOGTAG, "retainIconsOnStartup", e);
+ }
+ }
+
+ // Helper method for getting the top window.
+ WebView getTopWindow() {
+ return mTabControl.getCurrentTopWebView();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.browser, menu);
+ mMenu = menu;
+ updateInLoadMenuItems();
+ return true;
+ }
+
+ /**
+ * As the menu can be open when loading state changes
+ * we must manually update the state of the stop/reload menu
+ * item
+ */
+ private void updateInLoadMenuItems() {
+ if (mMenu == null) {
+ return;
+ }
+ MenuItem src = mInLoad ?
+ mMenu.findItem(R.id.stop_menu_id):
+ mMenu.findItem(R.id.reload_menu_id);
+ MenuItem dest = mMenu.findItem(R.id.stop_reload_menu_id);
+ dest.setIcon(src.getIcon());
+ dest.setTitle(src.getTitle());
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ // chording is not an issue with context menus, but we use the same
+ // options selector, so set mCanChord to true so we can access them.
+ mCanChord = true;
+ int id = item.getItemId();
+ final WebView webView = getTopWindow();
+ final HashMap hrefMap = new HashMap();
+ hrefMap.put("webview", webView);
+ final Message msg = mHandler.obtainMessage(
+ FOCUS_NODE_HREF, id, 0, hrefMap);
+ switch (id) {
+ // -- Browser context menu
+ case R.id.open_context_menu_id:
+ case R.id.open_newtab_context_menu_id:
+ case R.id.bookmark_context_menu_id:
+ case R.id.save_link_context_menu_id:
+ case R.id.share_link_context_menu_id:
+ case R.id.copy_link_context_menu_id:
+ webView.requestFocusNodeHref(msg);
+ break;
+
+ default:
+ // For other context menus
+ return onOptionsItemSelected(item);
+ }
+ mCanChord = false;
+ return true;
+ }
+
+ private Bundle createGoogleSearchSourceBundle(String source) {
+ Bundle bundle = new Bundle();
+ bundle.putString(SearchManager.SOURCE, source);
+ return bundle;
+ }
+
+ /**
+ * Overriding this forces the search key to launch global search. The difference
+ * is the final "true" which requests global search.
+ */
+ @Override
+ public boolean onSearchRequested() {
+ startSearch(null, false,
+ createGoogleSearchSourceBundle(GOOGLE_SEARCH_SOURCE_SEARCHKEY), true);
+ return true;
+ }
+
+ @Override
+ public void startSearch(String initialQuery, boolean selectInitialQuery,
+ Bundle appSearchData, boolean globalSearch) {
+ if (appSearchData == null) {
+ appSearchData = createGoogleSearchSourceBundle(GOOGLE_SEARCH_SOURCE_TYPE);
+ }
+ super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (!mCanChord) {
+ // The user has already fired a shortcut with this hold down of the
+ // menu key.
+ return false;
+ }
+ switch (item.getItemId()) {
+ // -- Main menu
+ case R.id.goto_menu_id: {
+ String url = getTopWindow().getUrl();
+ startSearch(mSettings.getHomePage().equals(url) ? null : url, true,
+ createGoogleSearchSourceBundle(GOOGLE_SEARCH_SOURCE_GOTO), false);
+ }
+ break;
+
+ case R.id.bookmarks_menu_id:
+ bookmarksOrHistoryPicker(false);
+ break;
+
+ case R.id.windows_menu_id:
+ if (mTabControl.getTabCount() == 1) {
+ openTabAndShow(mSettings.getHomePage(), null, false);
+ } else {
+ tabPicker(true, mTabControl.getCurrentIndex(), false);
+ }
+ break;
+
+ case R.id.stop_reload_menu_id:
+ if (mInLoad) {
+ stopLoading();
+ } else {
+ getTopWindow().reload();
+ }
+ break;
+
+ case R.id.back_menu_id:
+ getTopWindow().goBack();
+ break;
+
+ case R.id.forward_menu_id:
+ getTopWindow().goForward();
+ break;
+
+ case R.id.close_menu_id:
+ // Close the subwindow if it exists.
+ if (mTabControl.getCurrentSubWindow() != null) {
+ dismissSubWindow(mTabControl.getCurrentTab());
+ break;
+ }
+ final int currentIndex = mTabControl.getCurrentIndex();
+ final TabControl.Tab parent =
+ mTabControl.getCurrentTab().getParentTab();
+ int indexToShow = -1;
+ if (parent != null) {
+ indexToShow = mTabControl.getTabIndex(parent);
+ } else {
+ // Get the last tab in the list. If it is the current tab,
+ // subtract 1 more.
+ indexToShow = mTabControl.getTabCount() - 1;
+ if (currentIndex == indexToShow) {
+ indexToShow--;
+ }
+ }
+ switchTabs(currentIndex, indexToShow, true);
+ break;
+
+ case R.id.homepage_menu_id:
+ TabControl.Tab current = mTabControl.getCurrentTab();
+ if (current != null) {
+ dismissSubWindow(current);
+ current.getWebView().loadUrl(mSettings.getHomePage());
+ }
+ break;
+
+ case R.id.preferences_menu_id:
+ Intent intent = new Intent(this,
+ BrowserPreferencesPage.class);
+ startActivityForResult(intent, PREFERENCES_PAGE);
+ break;
+
+ case R.id.find_menu_id:
+ if (null == mFindDialog) {
+ mFindDialog = new FindDialog(this);
+ }
+ mFindDialog.setWebView(getTopWindow());
+ mFindDialog.show();
+ mMenuState = EMPTY_MENU;
+ break;
+
+ case R.id.select_text_id:
+ getTopWindow().emulateShiftHeld();
+ break;
+ case R.id.page_info_menu_id:
+ showPageInfo(mTabControl.getCurrentTab(), false);
+ break;
+
+ case R.id.classic_history_menu_id:
+ bookmarksOrHistoryPicker(true);
+ break;
+
+ case R.id.share_page_menu_id:
+ Browser.sendString(this, getTopWindow().getUrl());
+ break;
+
+ case R.id.dump_nav_menu_id:
+ getTopWindow().debugDump();
+ break;
+
+ case R.id.zoom_in_menu_id:
+ getTopWindow().zoomIn();
+ break;
+
+ case R.id.zoom_out_menu_id:
+ getTopWindow().zoomOut();
+ break;
+
+ case R.id.view_downloads_menu_id:
+ viewDownloads(null);
+ break;
+
+ // -- Tab menu
+ case R.id.view_tab_menu_id:
+ if (mTabListener != null && mTabOverview != null) {
+ int pos = mTabOverview.getContextMenuPosition(item);
+ mTabOverview.setCurrentIndex(pos);
+ mTabListener.onClick(pos);
+ }
+ break;
+
+ case R.id.remove_tab_menu_id:
+ if (mTabListener != null && mTabOverview != null) {
+ int pos = mTabOverview.getContextMenuPosition(item);
+ mTabListener.remove(pos);
+ }
+ break;
+
+ case R.id.new_tab_menu_id:
+ // No need to check for mTabOverview here since we are not
+ // dependent on it for a position.
+ if (mTabListener != null) {
+ // If the overview happens to be non-null, make the "New
+ // Tab" cell visible.
+ if (mTabOverview != null) {
+ mTabOverview.setCurrentIndex(ImageGrid.NEW_TAB);
+ }
+ mTabListener.onClick(ImageGrid.NEW_TAB);
+ }
+ break;
+
+ case R.id.bookmark_tab_menu_id:
+ if (mTabListener != null && mTabOverview != null) {
+ int pos = mTabOverview.getContextMenuPosition(item);
+ TabControl.Tab t = mTabControl.getTab(pos);
+ // Since we called populatePickerData for all of the
+ // tabs, getTitle and getUrl will return appropriate
+ // values.
+ Browser.saveBookmark(BrowserActivity.this, t.getTitle(),
+ t.getUrl());
+ }
+ break;
+
+ case R.id.history_tab_menu_id:
+ bookmarksOrHistoryPicker(true);
+ break;
+
+ case R.id.bookmarks_tab_menu_id:
+ bookmarksOrHistoryPicker(false);
+ break;
+
+ case R.id.properties_tab_menu_id:
+ if (mTabListener != null && mTabOverview != null) {
+ int pos = mTabOverview.getContextMenuPosition(item);
+ showPageInfo(mTabControl.getTab(pos), false);
+ }
+ break;
+
+ case R.id.window_one_menu_id:
+ case R.id.window_two_menu_id:
+ case R.id.window_three_menu_id:
+ case R.id.window_four_menu_id:
+ case R.id.window_five_menu_id:
+ case R.id.window_six_menu_id:
+ case R.id.window_seven_menu_id:
+ case R.id.window_eight_menu_id:
+ {
+ int menuid = item.getItemId();
+ for (int id = 0; id < WINDOW_SHORTCUT_ID_ARRAY.length; id++) {
+ if (WINDOW_SHORTCUT_ID_ARRAY[id] == menuid) {
+ TabControl.Tab desiredTab = mTabControl.getTab(id);
+ if (desiredTab != null &&
+ desiredTab != mTabControl.getCurrentTab()) {
+ switchTabs(mTabControl.getCurrentIndex(), id, false);
+ }
+ break;
+ }
+ }
+ }
+ break;
+
+ default:
+ if (!super.onOptionsItemSelected(item)) {
+ return false;
+ }
+ // Otherwise fall through.
+ }
+ mCanChord = false;
+ return true;
+ }
+
+ public void closeFind() {
+ mMenuState = R.id.MAIN_MENU;
+ }
+
+ @Override public boolean onPrepareOptionsMenu(Menu menu)
+ {
+ // This happens when the user begins to hold down the menu key, so
+ // allow them to chord to get a shortcut.
+ mCanChord = true;
+ // Note: setVisible will decide whether an item is visible; while
+ // setEnabled() will decide whether an item is enabled, which also means
+ // whether the matching shortcut key will function.
+ super.onPrepareOptionsMenu(menu);
+ switch (mMenuState) {
+ case R.id.TAB_MENU:
+ if (mCurrentMenuState != mMenuState) {
+ menu.setGroupVisible(R.id.MAIN_MENU, false);
+ menu.setGroupEnabled(R.id.MAIN_MENU, false);
+ menu.setGroupEnabled(R.id.MAIN_SHORTCUT_MENU, false);
+ menu.setGroupVisible(R.id.TAB_MENU, true);
+ menu.setGroupEnabled(R.id.TAB_MENU, true);
+ }
+ boolean newT = mTabControl.getTabCount() < TabControl.MAX_TABS;
+ final MenuItem tab = menu.findItem(R.id.new_tab_menu_id);
+ tab.setVisible(newT);
+ tab.setEnabled(newT);
+ break;
+ case EMPTY_MENU:
+ if (mCurrentMenuState != mMenuState) {
+ menu.setGroupVisible(R.id.MAIN_MENU, false);
+ menu.setGroupEnabled(R.id.MAIN_MENU, false);
+ menu.setGroupEnabled(R.id.MAIN_SHORTCUT_MENU, false);
+ menu.setGroupVisible(R.id.TAB_MENU, false);
+ menu.setGroupEnabled(R.id.TAB_MENU, false);
+ }
+ break;
+ default:
+ if (mCurrentMenuState != mMenuState) {
+ menu.setGroupVisible(R.id.MAIN_MENU, true);
+ menu.setGroupEnabled(R.id.MAIN_MENU, true);
+ menu.setGroupEnabled(R.id.MAIN_SHORTCUT_MENU, true);
+ menu.setGroupVisible(R.id.TAB_MENU, false);
+ menu.setGroupEnabled(R.id.TAB_MENU, false);
+ }
+ final WebView w = getTopWindow();
+ boolean canGoBack = false;
+ boolean canGoForward = false;
+ boolean isHome = false;
+ if (w != null) {
+ canGoBack = w.canGoBack();
+ canGoForward = w.canGoForward();
+ isHome = mSettings.getHomePage().equals(w.getUrl());
+ }
+ final MenuItem back = menu.findItem(R.id.back_menu_id);
+ back.setEnabled(canGoBack);
+
+ final MenuItem home = menu.findItem(R.id.homepage_menu_id);
+ home.setEnabled(!isHome);
+
+ menu.findItem(R.id.forward_menu_id)
+ .setEnabled(canGoForward);
+
+ // decide whether to show the share link option
+ PackageManager pm = getPackageManager();
+ Intent send = new Intent(Intent.ACTION_SEND);
+ send.setType("text/plain");
+ ResolveInfo ri = pm.resolveActivity(send, PackageManager.MATCH_DEFAULT_ONLY);
+ menu.findItem(R.id.share_page_menu_id).setVisible(ri != null);
+
+ // If there is only 1 window, the text will be "New window"
+ final MenuItem windows = menu.findItem(R.id.windows_menu_id);
+ windows.setTitleCondensed(mTabControl.getTabCount() > 1 ?
+ getString(R.string.view_tabs_condensed) :
+ getString(R.string.tab_picker_new_tab));
+
+ boolean isNavDump = mSettings.isNavDump();
+ final MenuItem nav = menu.findItem(R.id.dump_nav_menu_id);
+ nav.setVisible(isNavDump);
+ nav.setEnabled(isNavDump);
+ break;
+ }
+ mCurrentMenuState = mMenuState;
+ return true;
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenuInfo menuInfo) {
+ WebView webview = (WebView) v;
+ WebView.HitTestResult result = webview.getHitTestResult();
+ if (result == null) {
+ return;
+ }
+
+ int type = result.getType();
+ if (type == WebView.HitTestResult.UNKNOWN_TYPE) {
+ Log.w(LOGTAG,
+ "We should not show context menu when nothing is touched");
+ return;
+ }
+ if (type == WebView.HitTestResult.EDIT_TEXT_TYPE) {
+ // let TextView handles context menu
+ return;
+ }
+
+ // Note, http://b/issue?id=1106666 is requesting that
+ // an inflated menu can be used again. This is not available
+ // yet, so inflate each time (yuk!)
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.browsercontext, menu);
+
+ // Show the correct menu group
+ String extra = result.getExtra();
+ menu.setGroupVisible(R.id.PHONE_MENU,
+ type == WebView.HitTestResult.PHONE_TYPE);
+ menu.setGroupVisible(R.id.EMAIL_MENU,
+ type == WebView.HitTestResult.EMAIL_TYPE);
+ menu.setGroupVisible(R.id.GEO_MENU,
+ type == WebView.HitTestResult.GEO_TYPE);
+ menu.setGroupVisible(R.id.IMAGE_MENU,
+ type == WebView.HitTestResult.IMAGE_TYPE
+ || type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
+ menu.setGroupVisible(R.id.ANCHOR_MENU,
+ type == WebView.HitTestResult.SRC_ANCHOR_TYPE
+ || type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
+
+ // Setup custom handling depending on the type
+ switch (type) {
+ case WebView.HitTestResult.PHONE_TYPE:
+ menu.setHeaderTitle(Uri.decode(extra));
+ menu.findItem(R.id.dial_context_menu_id).setIntent(
+ new Intent(Intent.ACTION_VIEW, Uri
+ .parse(WebView.SCHEME_TEL + extra)));
+ Intent addIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+ addIntent.putExtra(Insert.PHONE, Uri.decode(extra));
+ addIntent.setType(Contacts.People.CONTENT_ITEM_TYPE);
+ menu.findItem(R.id.add_contact_context_menu_id).setIntent(
+ addIntent);
+ menu.findItem(R.id.copy_phone_context_menu_id).setOnMenuItemClickListener(
+ new Copy(extra));
+ break;
+
+ case WebView.HitTestResult.EMAIL_TYPE:
+ menu.setHeaderTitle(extra);
+ menu.findItem(R.id.email_context_menu_id).setIntent(
+ new Intent(Intent.ACTION_VIEW, Uri
+ .parse(WebView.SCHEME_MAILTO + extra)));
+ menu.findItem(R.id.copy_mail_context_menu_id).setOnMenuItemClickListener(
+ new Copy(extra));
+ break;
+
+ case WebView.HitTestResult.GEO_TYPE:
+ menu.setHeaderTitle(extra);
+ menu.findItem(R.id.map_context_menu_id).setIntent(
+ new Intent(Intent.ACTION_VIEW, Uri
+ .parse(WebView.SCHEME_GEO
+ + URLEncoder.encode(extra))));
+ menu.findItem(R.id.copy_geo_context_menu_id).setOnMenuItemClickListener(
+ new Copy(extra));
+ break;
+
+ case WebView.HitTestResult.SRC_ANCHOR_TYPE:
+ case WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE:
+ TextView titleView = (TextView) LayoutInflater.from(this)
+ .inflate(android.R.layout.browser_link_context_header,
+ null);
+ titleView.setText(extra);
+ menu.setHeaderView(titleView);
+ // decide whether to show the open link in new tab option
+ menu.findItem(R.id.open_newtab_context_menu_id).setVisible(
+ mTabControl.getTabCount() < TabControl.MAX_TABS);
+ PackageManager pm = getPackageManager();
+ Intent send = new Intent(Intent.ACTION_SEND);
+ send.setType("text/plain");
+ ResolveInfo ri = pm.resolveActivity(send, PackageManager.MATCH_DEFAULT_ONLY);
+ menu.findItem(R.id.share_link_context_menu_id).setVisible(ri != null);
+ if (type == WebView.HitTestResult.SRC_ANCHOR_TYPE) {
+ break;
+ }
+ // otherwise fall through to handle image part
+ case WebView.HitTestResult.IMAGE_TYPE:
+ if (type == WebView.HitTestResult.IMAGE_TYPE) {
+ menu.setHeaderTitle(extra);
+ }
+ menu.findItem(R.id.view_image_context_menu_id).setIntent(
+ new Intent(Intent.ACTION_VIEW, Uri.parse(extra)));
+ menu.findItem(R.id.download_context_menu_id).
+ setOnMenuItemClickListener(new Download(extra));
+ break;
+
+ default:
+ Log.w(LOGTAG, "We should not get here.");
+ break;
+ }
+ }
+
+ // Used by attachTabToContentView for the WebView's ZoomControl widget.
+ private static final FrameLayout.LayoutParams ZOOM_PARAMS =
+ new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ Gravity.BOTTOM);
+
+ // Attach the given tab to the content view.
+ private void attachTabToContentView(TabControl.Tab t) {
+ final WebView main = t.getWebView();
+ // Attach the main WebView.
+ mContentView.addView(main, COVER_SCREEN_PARAMS);
+ // Attach the Zoom control widget and hide it.
+ final View zoom = main.getZoomControls();
+ mContentView.addView(zoom, ZOOM_PARAMS);
+ zoom.setVisibility(View.GONE);
+ // Attach the sub window if necessary
+ attachSubWindow(t);
+ // Request focus on the top window.
+ t.getTopWindow().requestFocus();
+ }
+
+ // Attach a sub window to the main WebView of the given tab.
+ private void attachSubWindow(TabControl.Tab t) {
+ // If a sub window exists, attach it to the content view.
+ final WebView subView = t.getSubWebView();
+ if (subView != null) {
+ final View container = t.getSubWebViewContainer();
+ mContentView.addView(container, COVER_SCREEN_PARAMS);
+ subView.requestFocus();
+ }
+ }
+
+ // Remove the given tab from the content view.
+ private void removeTabFromContentView(TabControl.Tab t) {
+ // Remove the Zoom widget and the main WebView.
+ mContentView.removeView(t.getWebView().getZoomControls());
+ mContentView.removeView(t.getWebView());
+ // Remove the sub window if it exists.
+ if (t.getSubWebView() != null) {
+ mContentView.removeView(t.getSubWebViewContainer());
+ }
+ }
+
+ // Remove the sub window if it exists. Also called by TabControl when the
+ // user clicks the 'X' to dismiss a sub window.
+ /* package */ void dismissSubWindow(TabControl.Tab t) {
+ final WebView mainView = t.getWebView();
+ if (t.getSubWebView() != null) {
+ // Remove the container view and request focus on the main WebView.
+ mContentView.removeView(t.getSubWebViewContainer());
+ mainView.requestFocus();
+ // Tell the TabControl to dismiss the subwindow. This will destroy
+ // the WebView.
+ mTabControl.dismissSubWindow(t);
+ }
+ }
+
+ // Send the ANIMTE_FROM_OVERVIEW message after changing the current tab.
+ private void sendAnimateFromOverview(final TabControl.Tab tab,
+ final boolean newTab, final String url, final int delay,
+ final Message msg) {
+ // Set the current tab.
+ mTabControl.setCurrentTab(tab);
+ // Attach the WebView so it will layout.
+ attachTabToContentView(tab);
+ // Set the view to invisibile for now.
+ tab.getWebView().setVisibility(View.INVISIBLE);
+ // If there is a sub window, make it invisible too.
+ if (tab.getSubWebView() != null) {
+ tab.getSubWebViewContainer().setVisibility(View.INVISIBLE);
+ }
+ // Create our fake animating view.
+ final AnimatingView view = new AnimatingView(this, tab);
+ // Attach it to the view system and make in invisible so it will
+ // layout but not flash white on the screen.
+ mContentView.addView(view, COVER_SCREEN_PARAMS);
+ view.setVisibility(View.INVISIBLE);
+ // Send the animate message.
+ final HashMap map = new HashMap();
+ map.put("view", view);
+ // Load the url after the AnimatingView has captured the picture. This
+ // prevents any bad layout or bad scale from being used during
+ // animation.
+ if (url != null) {
+ dismissSubWindow(tab);
+ tab.getWebView().loadUrl(url);
+ }
+ map.put("msg", msg);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ ANIMATE_FROM_OVERVIEW, newTab ? 1 : 0, 0, map), delay);
+ // Increment the count to indicate that we are in an animation.
+ mAnimationCount++;
+ // Remove the listener so we don't get any more tab changes.
+ mTabOverview.setListener(null);
+ mTabListener = null;
+ // Make the menu empty until the animation completes.
+ mMenuState = EMPTY_MENU;
+
+ }
+
+ // 500ms animation with 800ms delay
+ private static final int TAB_ANIMATION_DURATION = 500;
+ private static final int TAB_OVERVIEW_DELAY = 800;
+
+ // Called by TabControl when a tab is requesting focus
+ /* package */ void showTab(TabControl.Tab t) {
+ // Disallow focus change during a tab animation.
+ if (mAnimationCount > 0) {
+ return;
+ }
+ int delay = 0;
+ if (mTabOverview == null) {
+ // Add a delay so the tab overview can be shown before the second
+ // animation begins.
+ delay = TAB_ANIMATION_DURATION + TAB_OVERVIEW_DELAY;
+ tabPicker(false, mTabControl.getTabIndex(t), false);
+ }
+ sendAnimateFromOverview(t, false, null, delay, null);
+ }
+
+ // This method does a ton of stuff. It will attempt to create a new tab
+ // if we haven't reached MAX_TABS. Otherwise it uses the current tab. If
+ // url isn't null, it will load the given url. If the tab overview is not
+ // showing, it will animate to the tab overview, create a new tab and
+ // animate away from it. After the animation completes, it will dispatch
+ // the given Message. If the tab overview is already showing (i.e. this
+ // method is called from TabListener.onClick(), the method will animate
+ // away from the tab overview.
+ private void openTabAndShow(String url, final Message msg,
+ boolean closeOnExit) {
+ final boolean newTab = mTabControl.getTabCount() != TabControl.MAX_TABS;
+ final TabControl.Tab currentTab = mTabControl.getCurrentTab();
+ if (newTab) {
+ int delay = 0;
+ // If the tab overview is up and there are animations, just load
+ // the url.
+ if (mTabOverview != null && mAnimationCount > 0) {
+ if (url != null) {
+ // We should not have a msg here since onCreateWindow
+ // checks the animation count and every other caller passes
+ // null.
+ assert msg == null;
+ // just dismiss the subwindow and load the given url.
+ dismissSubWindow(currentTab);
+ currentTab.getWebView().loadUrl(url);
+ }
+ } else {
+ // show mTabOverview if it is not there.
+ if (mTabOverview == null) {
+ // We have to delay the animation from the tab picker by the
+ // length of the tab animation. Add a delay so the tab
+ // overview can be shown before the second animation begins.
+ delay = TAB_ANIMATION_DURATION + TAB_OVERVIEW_DELAY;
+ tabPicker(false, ImageGrid.NEW_TAB, false);
+ }
+ // Animate from the Tab overview after any animations have
+ // finished.
+ sendAnimateFromOverview(mTabControl.createNewTab(closeOnExit),
+ true, url, delay, msg);
+ }
+ } else if (url != null) {
+ // We should not have a msg here.
+ assert msg == null;
+ if (mTabOverview != null && mAnimationCount == 0) {
+ sendAnimateFromOverview(currentTab, false, url,
+ TAB_OVERVIEW_DELAY, null);
+ } else {
+ // Get rid of the subwindow if it exists
+ dismissSubWindow(currentTab);
+ // Load the given url.
+ currentTab.getWebView().loadUrl(url);
+ }
+ }
+ }
+
+ private Animation createTabAnimation(final AnimatingView view,
+ final View cell, boolean scaleDown) {
+ final AnimationSet set = new AnimationSet(true);
+ final float scaleX = (float) cell.getWidth() / view.getWidth();
+ final float scaleY = (float) cell.getHeight() / view.getHeight();
+ if (scaleDown) {
+ set.addAnimation(new ScaleAnimation(1.0f, scaleX, 1.0f, scaleY));
+ set.addAnimation(new TranslateAnimation(0, cell.getLeft(), 0,
+ cell.getTop()));
+ } else {
+ set.addAnimation(new ScaleAnimation(scaleX, 1.0f, scaleY, 1.0f));
+ set.addAnimation(new TranslateAnimation(cell.getLeft(), 0,
+ cell.getTop(), 0));
+ }
+ set.setDuration(TAB_ANIMATION_DURATION);
+ set.setInterpolator(new DecelerateInterpolator());
+ return set;
+ }
+
+ // Animate to the tab overview. currentIndex tells us which position to
+ // animate to and newIndex is the position that should be selected after
+ // the animation completes.
+ // If remove is true, after the animation stops, a confirmation dialog will
+ // be displayed to the user.
+ private void animateToTabOverview(final int newIndex, final boolean remove,
+ final AnimatingView view) {
+ // Find the view in the ImageGrid allowing for the "New Tab" cell.
+ int position = mTabControl.getTabIndex(view.mTab);
+ if (!((ImageAdapter) mTabOverview.getAdapter()).maxedOut()) {
+ position++;
+ }
+
+ // Offset the tab position with the first visible position to get a
+ // number between 0 and 3.
+ position -= mTabOverview.getFirstVisiblePosition();
+
+ // Grab the view that we are going to animate to.
+ final View v = mTabOverview.getChildAt(position);
+
+ final Animation.AnimationListener l =
+ new Animation.AnimationListener() {
+ public void onAnimationStart(Animation a) {
+ mTabOverview.requestFocus();
+ // Clear the listener so we don't trigger a tab
+ // selection.
+ mTabOverview.setListener(null);
+ }
+ public void onAnimationRepeat(Animation a) {}
+ public void onAnimationEnd(Animation a) {
+ // We are no longer animating so decrement the count.
+ mAnimationCount--;
+ // Make the view GONE so that it will not draw between
+ // now and when the Runnable is handled.
+ view.setVisibility(View.GONE);
+ // Post a runnable since we can't modify the view
+ // hierarchy during this callback.
+ mHandler.post(new Runnable() {
+ public void run() {
+ // Remove the AnimatingView.
+ mContentView.removeView(view);
+ if (mTabOverview != null) {
+ // Make newIndex visible.
+ mTabOverview.setCurrentIndex(newIndex);
+ // Restore the listener.
+ mTabOverview.setListener(mTabListener);
+ // Change the menu to TAB_MENU if the
+ // ImageGrid is interactive.
+ if (mTabOverview.isLive()) {
+ mMenuState = R.id.TAB_MENU;
+ mTabOverview.requestFocus();
+ }
+ }
+ // If a remove was requested, remove the tab.
+ if (remove) {
+ // During a remove, the current tab has
+ // already changed. Remember the current one
+ // here.
+ final TabControl.Tab currentTab =
+ mTabControl.getCurrentTab();
+ // Remove the tab at newIndex from
+ // TabControl and the tab overview.
+ final TabControl.Tab tab =
+ mTabControl.getTab(newIndex);
+ mTabControl.removeTab(tab);
+ // Restore the current tab.
+ if (currentTab != tab) {
+ mTabControl.setCurrentTab(currentTab);
+ }
+ if (mTabOverview != null) {
+ mTabOverview.remove(newIndex);
+ // Make the current tab visible.
+ mTabOverview.setCurrentIndex(
+ mTabControl.getCurrentIndex());
+ }
+ }
+ }
+ });
+ }
+ };
+
+ // Do an animation if there is a view to animate to.
+ if (v != null) {
+ // Create our animation
+ final Animation anim = createTabAnimation(view, v, true);
+ anim.setAnimationListener(l);
+ // Start animating
+ view.startAnimation(anim);
+ } else {
+ // If something goes wrong and we didn't find a view to animate to,
+ // just do everything here.
+ l.onAnimationStart(null);
+ l.onAnimationEnd(null);
+ }
+ }
+
+ // Animate from the tab picker. The index supplied is the index to animate
+ // from.
+ private void animateFromTabOverview(final AnimatingView view,
+ final boolean newTab, final Message msg) {
+ // firstVisible is the first visible tab on the screen. This helps
+ // to know which corner of the screen the selected tab is.
+ int firstVisible = mTabOverview.getFirstVisiblePosition();
+ // tabPosition is the 0-based index of of the tab being opened
+ int tabPosition = mTabControl.getTabIndex(view.mTab);
+ if (!((ImageAdapter) mTabOverview.getAdapter()).maxedOut()) {
+ // Add one to make room for the "New Tab" cell.
+ tabPosition++;
+ }
+ // If this is a new tab, animate from the "New Tab" cell.
+ if (newTab) {
+ tabPosition = 0;
+ }
+ // Location corresponds to the four corners of the screen.
+ // A new tab or 0 is upper left, 0 for an old tab is upper
+ // right, 1 is lower left, and 2 is lower right
+ int location = tabPosition - firstVisible;
+
+ // Find the view at this location.
+ final View v = mTabOverview.getChildAt(location);
+
+ // Wait until the animation completes to replace the AnimatingView.
+ final Animation.AnimationListener l =
+ new Animation.AnimationListener() {
+ public void onAnimationStart(Animation a) {}
+ public void onAnimationRepeat(Animation a) {}
+ public void onAnimationEnd(Animation a) {
+ mHandler.post(new Runnable() {
+ public void run() {
+ mContentView.removeView(view);
+ // Dismiss the tab overview. If the cell at the
+ // given location is null, set the fade
+ // parameter to true.
+ dismissTabOverview(v == null);
+ TabControl.Tab t =
+ mTabControl.getCurrentTab();
+ mMenuState = R.id.MAIN_MENU;
+ // Resume regular updates.
+ t.getWebView().resumeTimers();
+ // Dispatch the message after the animation
+ // completes.
+ if (msg != null) {
+ msg.sendToTarget();
+ }
+ // The animation is done and the tab overview is
+ // gone so allow key events and other animations
+ // to begin.
+ mAnimationCount--;
+ // Reset all the title bar info.
+ resetTitle();
+ }
+ });
+ }
+ };
+
+ if (v != null) {
+ final Animation anim = createTabAnimation(view, v, false);
+ // Set the listener and start animating
+ anim.setAnimationListener(l);
+ view.startAnimation(anim);
+ // Make the view VISIBLE during the animation.
+ view.setVisibility(View.VISIBLE);
+ } else {
+ // Go ahead and do all the cleanup.
+ l.onAnimationEnd(null);
+ }
+ }
+
+ // Dismiss the tab overview applying a fade if needed.
+ private void dismissTabOverview(final boolean fade) {
+ if (fade) {
+ AlphaAnimation anim = new AlphaAnimation(1.0f, 0.0f);
+ anim.setDuration(500);
+ anim.startNow();
+ mTabOverview.startAnimation(anim);
+ }
+ // Just in case there was a problem with animating away from the tab
+ // overview
+ WebView current = mTabControl.getCurrentWebView();
+ if (current != null) {
+ current.setVisibility(View.VISIBLE);
+ } else {
+ Log.e(LOGTAG, "No current WebView in dismissTabOverview");
+ }
+ // Make the sub window container visible.
+ if (mTabControl.getCurrentSubWindow() != null) {
+ mTabControl.getCurrentTab().getSubWebViewContainer()
+ .setVisibility(View.VISIBLE);
+ }
+ mContentView.removeView(mTabOverview);
+ mTabOverview.clear();
+ mTabOverview = null;
+ mTabListener = null;
+ }
+
+ private void openTab(String url) {
+ if (mSettings.openInBackground()) {
+ TabControl.Tab t = mTabControl.createNewTab(false);
+ if (t != null) {
+ t.getWebView().loadUrl(url);
+ }
+ } else {
+ openTabAndShow(url, null, false);
+ }
+ }
+
+ private class Copy implements OnMenuItemClickListener {
+ private CharSequence mText;
+
+ public boolean onMenuItemClick(MenuItem item) {
+ copy(mText);
+ return true;
+ }
+
+ public Copy(CharSequence toCopy) {
+ mText = toCopy;
+ }
+ }
+
+ private class Download implements OnMenuItemClickListener {
+ private String mText;
+
+ public boolean onMenuItemClick(MenuItem item) {
+ onDownloadStartNoStream(mText, null, null, null, -1);
+ return true;
+ }
+
+ public Download(String toDownload) {
+ mText = toDownload;
+ }
+ }
+
+ private void copy(CharSequence text) {
+ try {
+ IClipboard clip = IClipboard.Stub.asInterface(ServiceManager.getService("clipboard"));
+ if (clip != null) {
+ clip.setClipboardText(text);
+ }
+ } catch (android.os.RemoteException e) {
+ Log.e(LOGTAG, "Copy failed", e);
+ }
+ }
+
+ /**
+ * Resets the browser title-view to whatever it must be (for example, if we
+ * load a page from history).
+ */
+ private void resetTitle() {
+ resetLockIcon();
+ resetTitleIconAndProgress();
+ }
+
+ /**
+ * Resets the browser title-view to whatever it must be
+ * (for example, if we had a loading error)
+ * When we have a new page, we call resetTitle, when we
+ * have to reset the titlebar to whatever it used to be
+ * (for example, if the user chose to stop loading), we
+ * call resetTitleAndRevertLockIcon.
+ */
+ /* package */ void resetTitleAndRevertLockIcon() {
+ revertLockIcon();
+ resetTitleIconAndProgress();
+ }
+
+ /**
+ * Reset the title, favicon, and progress.
+ */
+ private void resetTitleIconAndProgress() {
+ WebView current = mTabControl.getCurrentWebView();
+ if (current == null) {
+ return;
+ }
+ resetTitleAndIcon(current);
+ int progress = current.getProgress();
+ mInLoad = (progress != 100);
+ updateInLoadMenuItems();
+ mWebChromeClient.onProgressChanged(current, progress);
+ }
+
+ // Reset the title and the icon based on the given item.
+ private void resetTitleAndIcon(WebView view) {
+ WebHistoryItem item = view.copyBackForwardList().getCurrentItem();
+ if (item != null) {
+ setUrlTitle(item.getUrl(), item.getTitle());
+ setFavicon(item.getFavicon());
+ } else {
+ setUrlTitle(null, null);
+ setFavicon(null);
+ }
+ }
+
+ /**
+ * Sets a title composed of the URL and the title string.
+ * @param url The URL of the site being loaded.
+ * @param title The title of the site being loaded.
+ */
+ private void setUrlTitle(String url, String title) {
+ mUrl = url;
+ mTitle = title;
+
+ // While the tab overview is animating or being shown, block changes
+ // to the title.
+ if (mAnimationCount == 0 && mTabOverview == null) {
+ setTitle(buildUrlTitle(url, title));
+ }
+ }
+
+ /**
+ * Builds and returns the page title, which is some
+ * combination of the page URL and title.
+ * @param url The URL of the site being loaded.
+ * @param title The title of the site being loaded.
+ * @return The page title.
+ */
+ private String buildUrlTitle(String url, String title) {
+ String urlTitle = "";
+
+ if (url != null) {
+ String titleUrl = buildTitleUrl(url);
+
+ if (title != null && 0 < title.length()) {
+ if (titleUrl != null && 0 < titleUrl.length()) {
+ urlTitle = titleUrl + ": " + title;
+ } else {
+ urlTitle = title;
+ }
+ } else {
+ if (titleUrl != null) {
+ urlTitle = titleUrl;
+ }
+ }
+ }
+
+ return urlTitle;
+ }
+
+ /**
+ * @param url The URL to build a title version of the URL from.
+ * @return The title version of the URL or null if fails.
+ * The title version of the URL can be either the URL hostname,
+ * or the hostname with an "https://" prefix (for secure URLs),
+ * or an empty string if, for example, the URL in question is a
+ * file:// URL with no hostname.
+ */
+ private static String buildTitleUrl(String url) {
+ String titleUrl = null;
+
+ if (url != null) {
+ try {
+ // parse the url string
+ URL urlObj = new URL(url);
+ if (urlObj != null) {
+ titleUrl = "";
+
+ String protocol = urlObj.getProtocol();
+ String host = urlObj.getHost();
+
+ if (host != null && 0 < host.length()) {
+ titleUrl = host;
+ if (protocol != null) {
+ // if a secure site, add an "https://" prefix!
+ if (protocol.equalsIgnoreCase("https")) {
+ titleUrl = protocol + "://" + host;
+ }
+ }
+ }
+ }
+ } catch (MalformedURLException e) {}
+ }
+
+ return titleUrl;
+ }
+
+ // Set the favicon in the title bar.
+ private void setFavicon(Bitmap icon) {
+ // While the tab overview is animating or being shown, block changes to
+ // the favicon.
+ 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;
+ } 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);
+ }
+
+ /**
+ * Saves the current lock-icon state before resetting
+ * the lock icon. If we have an error, we may need to
+ * roll back to the previous state.
+ */
+ private void saveLockIcon() {
+ mPrevLockType = mLockIconType;
+ }
+
+ /**
+ * Reverts the lock-icon state to the last saved state,
+ * for example, if we had an error, and need to cancel
+ * the load.
+ */
+ private void revertLockIcon() {
+ mLockIconType = mPrevLockType;
+
+ if (Config.LOGV) {
+ Log.v(LOGTAG, "BrowserActivity.revertLockIcon:" +
+ " revert lock icon to " + mLockIconType);
+ }
+
+ updateLockIconImage(mLockIconType);
+ }
+
+ private void switchTabs(int indexFrom, int indexToShow, boolean remove) {
+ int delay = TAB_ANIMATION_DURATION + TAB_OVERVIEW_DELAY;
+ // Animate to the tab picker, remove the current tab, then
+ // animate away from the tab picker to the parent WebView.
+ tabPicker(false, indexFrom, remove);
+ // Change to the parent tab
+ final TabControl.Tab tab = mTabControl.getTab(indexToShow);
+ if (tab != null) {
+ sendAnimateFromOverview(tab, false, null, delay, null);
+ } else {
+ // Increment this here so that no other animations can happen in
+ // between the end of the tab picker transition and the beginning
+ // of openTabAndShow. This has a matching decrement in the handler
+ // of OPEN_TAB_AND_SHOW.
+ mAnimationCount++;
+ // Send a message to open a new tab.
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(OPEN_TAB_AND_SHOW,
+ mSettings.getHomePage()), delay);
+ }
+ }
+
+ private void goBackOnePageOrQuit() {
+ TabControl.Tab current = mTabControl.getCurrentTab();
+ if (current == null) {
+ /*
+ * Instead of finishing the activity, simply push this to the back
+ * of the stack and let ActivityManager to choose the foreground
+ * activity. As BrowserActivity is singleTask, it will be always the
+ * root of the task. So we can use either true or false for
+ * moveTaskToBack().
+ */
+ moveTaskToBack(true);
+ }
+ WebView w = current.getWebView();
+ if (w.canGoBack()) {
+ w.goBack();
+ } else {
+ // Check to see if we are closing a window that was created by
+ // another window. If so, we switch back to that window.
+ TabControl.Tab parent = current.getParentTab();
+ if (parent != null) {
+ switchTabs(mTabControl.getCurrentIndex(),
+ mTabControl.getTabIndex(parent), true);
+ } else {
+ if (current.closeOnExit()) {
+ if (mTabControl.getTabCount() == 1) {
+ finish();
+ return;
+ }
+ // call pauseWebView() now, we won't be able to call it in
+ // onPause() as the WebView won't be valid.
+ pauseWebView();
+ removeTabFromContentView(current);
+ mTabControl.removeTab(current);
+ }
+ /*
+ * Instead of finishing the activity, simply push this to the back
+ * of the stack and let ActivityManager to choose the foreground
+ * activity. As BrowserActivity is singleTask, it will be always the
+ * root of the task. So we can use either true or false for
+ * moveTaskToBack().
+ */
+ moveTaskToBack(true);
+ }
+ }
+ }
+
+ public KeyTracker.State onKeyTracker(int keyCode,
+ KeyEvent event,
+ KeyTracker.Stage stage,
+ int duration) {
+ // if onKeyTracker() is called after activity onStop()
+ // because of accumulated key events,
+ // we should ignore it as browser is not active any more.
+ WebView topWindow = getTopWindow();
+ if (topWindow == null)
+ return KeyTracker.State.NOT_TRACKING;
+
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ // During animations, block the back key so that other animations
+ // are not triggered and so that we don't end up destroying all the
+ // WebViews before finishing the animation.
+ if (mAnimationCount > 0) {
+ return KeyTracker.State.DONE_TRACKING;
+ }
+ if (stage == KeyTracker.Stage.LONG_REPEAT) {
+ bookmarksOrHistoryPicker(true);
+ return KeyTracker.State.DONE_TRACKING;
+ } else if (stage == KeyTracker.Stage.UP) {
+ // FIXME: Currently, we do not have a notion of the
+ // history picker for the subwindow, but maybe we
+ // should?
+ WebView subwindow = mTabControl.getCurrentSubWindow();
+ if (subwindow != null) {
+ if (subwindow.canGoBack()) {
+ subwindow.goBack();
+ } else {
+ dismissSubWindow(mTabControl.getCurrentTab());
+ }
+ } else {
+ goBackOnePageOrQuit();
+ }
+ return KeyTracker.State.DONE_TRACKING;
+ }
+ return KeyTracker.State.KEEP_TRACKING;
+ }
+ return KeyTracker.State.NOT_TRACKING;
+ }
+
+ @Override public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_MENU) {
+ mMenuIsDown = true;
+ }
+ boolean handled = mKeyTracker.doKeyDown(keyCode, event);
+ if (!handled) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_SPACE:
+ if (event.isShiftPressed()) {
+ getTopWindow().pageUp(false);
+ } else {
+ getTopWindow().pageDown(false);
+ }
+ handled = true;
+ break;
+
+ default:
+ break;
+ }
+ }
+ return handled || super.onKeyDown(keyCode, event);
+ }
+
+ @Override public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_MENU) {
+ mMenuIsDown = false;
+ }
+ return mKeyTracker.doKeyUp(keyCode, event) || super.onKeyUp(keyCode, event);
+ }
+
+ private void stopLoading() {
+ resetTitleAndRevertLockIcon();
+ WebView w = getTopWindow();
+ w.stopLoading();
+ mWebViewClient.onPageFinished(w, w.getUrl());
+
+ cancelStopToast();
+ mStopToast = Toast
+ .makeText(this, R.string.stopping, Toast.LENGTH_SHORT);
+ mStopToast.show();
+ }
+
+ private void cancelStopToast() {
+ if (mStopToast != null) {
+ mStopToast.cancel();
+ mStopToast = null;
+ }
+ }
+
+ // called by a non-UI thread to post the message
+ public void postMessage(int what, int arg1, int arg2, Object obj) {
+ mHandler.sendMessage(mHandler.obtainMessage(what, arg1, arg2, obj));
+ }
+
+ // public message ids
+ public final static int LOAD_URL = 1001;
+ public final static int STOP_LOAD = 1002;
+
+ // Message Ids
+ private static final int FOCUS_NODE_HREF = 102;
+ private static final int CANCEL_CREDS_REQUEST = 103;
+ private static final int ANIMATE_FROM_OVERVIEW = 104;
+ private static final int ANIMATE_TO_OVERVIEW = 105;
+ private static final int OPEN_TAB_AND_SHOW = 106;
+ private static final int CHECK_MEMORY = 107;
+ private static final int RELEASE_WAKELOCK = 108;
+
+ // Private handler for handling javascript and saving passwords
+ private Handler mHandler = new Handler() {
+
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case ANIMATE_FROM_OVERVIEW:
+ final HashMap map = (HashMap) msg.obj;
+ animateFromTabOverview((AnimatingView) map.get("view"),
+ msg.arg1 == 1, (Message) map.get("msg"));
+ break;
+
+ case ANIMATE_TO_OVERVIEW:
+ animateToTabOverview(msg.arg1, msg.arg2 == 1,
+ (AnimatingView) msg.obj);
+ break;
+
+ case OPEN_TAB_AND_SHOW:
+ // Decrement mAnimationCount before openTabAndShow because
+ // the method relies on the value being 0 to start the next
+ // animation.
+ mAnimationCount--;
+ openTabAndShow((String) msg.obj, null, false);
+ break;
+
+ case FOCUS_NODE_HREF:
+ String url = (String) msg.getData().get("url");
+ if (url == null || url.length() == 0) {
+ break;
+ }
+ HashMap focusNodeMap = (HashMap) msg.obj;
+ WebView view = (WebView) focusNodeMap.get("webview");
+ // Only apply the action if the top window did not change.
+ if (getTopWindow() != view) {
+ break;
+ }
+ switch (msg.arg1) {
+ case R.id.open_context_menu_id:
+ case R.id.view_image_context_menu_id:
+ loadURL(getTopWindow(), url);
+ break;
+ case R.id.open_newtab_context_menu_id:
+ openTab(url);
+ break;
+ case R.id.bookmark_context_menu_id:
+ Intent intent = new Intent(BrowserActivity.this,
+ AddBookmarkPage.class);
+ intent.putExtra("url", url);
+ startActivity(intent);
+ break;
+ case R.id.share_link_context_menu_id:
+ Browser.sendString(BrowserActivity.this, url);
+ break;
+ case R.id.copy_link_context_menu_id:
+ copy(url);
+ break;
+ case R.id.save_link_context_menu_id:
+ case R.id.download_context_menu_id:
+ onDownloadStartNoStream(url, null, null, null, -1);
+ break;
+ }
+ break;
+
+ case LOAD_URL:
+ loadURL(getTopWindow(), (String) msg.obj);
+ break;
+
+ case STOP_LOAD:
+ stopLoading();
+ break;
+
+ case CANCEL_CREDS_REQUEST:
+ resumeAfterCredentials();
+ break;
+
+ case CHECK_MEMORY:
+ // reschedule to check memory condition
+ mHandler.removeMessages(CHECK_MEMORY);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage
+ (CHECK_MEMORY), CHECK_MEMORY_INTERVAL);
+ checkMemory();
+ break;
+
+ case RELEASE_WAKELOCK:
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ break;
+ }
+ }
+ };
+
+ // -------------------------------------------------------------------------
+ // WebViewClient implementation.
+ //-------------------------------------------------------------------------
+
+ // Use in overrideUrlLoading
+ /* package */ final static String SCHEME_WTAI = "wtai://wp/";
+ /* package */ final static String SCHEME_WTAI_MC = "wtai://wp/mc;";
+ /* package */ final static String SCHEME_WTAI_SD = "wtai://wp/sd;";
+ /* package */ final static String SCHEME_WTAI_AP = "wtai://wp/ap;";
+
+ /* package */ WebViewClient getWebViewClient() {
+ return mWebViewClient;
+ }
+
+ private void updateIcon(String url, Bitmap icon) {
+ if (icon != null) {
+ BrowserBookmarksAdapter.updateBookmarkFavicon(mResolver,
+ url, icon);
+ }
+ setFavicon(icon);
+ }
+
+ private final WebViewClient mWebViewClient = new WebViewClient() {
+ @Override
+ public void onPageStarted(WebView view, String url, Bitmap favicon) {
+ resetLockIcon(url);
+ setUrlTitle(url, null);
+ // Call updateIcon instead of setFavicon so the bookmark
+ // database can be updated.
+ updateIcon(url, favicon);
+
+ if (mSettings.isTracing() == true) {
+ // FIXME: we should save the trace file somewhere other than data.
+ // I can't use "/tmp" as it competes for system memory.
+ File file = getDir("browserTrace", 0);
+ String baseDir = file.getPath();
+ if (!baseDir.endsWith(File.separator)) baseDir += File.separator;
+ String host;
+ try {
+ WebAddress uri = new WebAddress(url);
+ host = uri.mHost;
+ } catch (android.net.ParseException ex) {
+ host = "unknown_host";
+ }
+ host = host.replace('.', '_');
+ baseDir = baseDir + host;
+ file = new File(baseDir+".data");
+ if (file.exists() == true) {
+ file.delete();
+ }
+ file = new File(baseDir+".key");
+ if (file.exists() == true) {
+ file.delete();
+ }
+ mInTrace = true;
+ Debug.startMethodTracing(baseDir, 8 * 1024 * 1024);
+ }
+
+ // Performance probe
+ if (false) {
+ mStart = SystemClock.uptimeMillis();
+ mProcessStart = Process.getElapsedCpuTime();
+ long[] sysCpu = new long[7];
+ if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT, null,
+ sysCpu, null)) {
+ mUserStart = sysCpu[0] + sysCpu[1];
+ mSystemStart = sysCpu[2];
+ mIdleStart = sysCpu[3];
+ mIrqStart = sysCpu[4] + sysCpu[5] + sysCpu[6];
+ }
+ mUiStart = SystemClock.currentThreadTimeMillis();
+ }
+
+ if (!mPageStarted) {
+ mPageStarted = true;
+ // if onResume() has been called, resumeWebView() does nothing.
+ resumeWebView();
+ }
+
+ // reset sync timer to avoid sync starts during loading a page
+ CookieSyncManager.getInstance().resetSync();
+
+ mInLoad = true;
+ updateInLoadMenuItems();
+ if (!mIsNetworkUp) {
+ if ( mAlertDialog == null) {
+ mAlertDialog = new AlertDialog.Builder(BrowserActivity.this)
+ .setTitle(R.string.loadSuspendedTitle)
+ .setMessage(R.string.loadSuspended)
+ .setPositiveButton(R.string.ok, null)
+ .show();
+ }
+ if (view != null) {
+ view.setNetworkAvailable(false);
+ }
+ }
+
+ // schedule to check memory condition
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(CHECK_MEMORY),
+ CHECK_MEMORY_INTERVAL);
+ }
+
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ // Reset the title and icon in case we stopped a provisional
+ // load.
+ resetTitleAndIcon(view);
+
+ // Update the lock icon image only once we are done loading
+ updateLockIconImage(mLockIconType);
+
+ // Performance probe
+ if (false) {
+ long[] sysCpu = new long[7];
+ if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT, null,
+ sysCpu, null)) {
+ String uiInfo = "UI thread used "
+ + (SystemClock.currentThreadTimeMillis() - mUiStart)
+ + " ms";
+ if (Config.LOGD) {
+ Log.d(LOGTAG, uiInfo);
+ }
+ //The string that gets written to the log
+ String performanceString = "It took total "
+ + (SystemClock.uptimeMillis() - mStart)
+ + " ms clock time to load the page."
+ + "\nbrowser process used "
+ + (Process.getElapsedCpuTime() - mProcessStart)
+ + " ms, user processes used "
+ + (sysCpu[0] + sysCpu[1] - mUserStart) * 10
+ + " ms, kernel used "
+ + (sysCpu[2] - mSystemStart) * 10
+ + " ms, idle took " + (sysCpu[3] - mIdleStart) * 10
+ + " ms and irq took "
+ + (sysCpu[4] + sysCpu[5] + sysCpu[6] - mIrqStart)
+ * 10 + " ms, " + uiInfo;
+ if (Config.LOGD) {
+ Log.d(LOGTAG, performanceString + "\nWebpage: " + url);
+ }
+ if (url != null) {
+ // strip the url to maintain consistency
+ String newUrl = new String(url);
+ if (newUrl.startsWith("http://www.")) {
+ newUrl = newUrl.substring(11);
+ } else if (newUrl.startsWith("http://")) {
+ newUrl = newUrl.substring(7);
+ } else if (newUrl.startsWith("https://www.")) {
+ newUrl = newUrl.substring(12);
+ } else if (newUrl.startsWith("https://")) {
+ newUrl = newUrl.substring(8);
+ }
+ if (Config.LOGD) {
+ Log.d(LOGTAG, newUrl + " loaded");
+ }
+ /*
+ if (sWhiteList.contains(newUrl)) {
+ // The string that gets pushed to the statistcs
+ // service
+ performanceString = performanceString
+ + "\nWebpage: "
+ + newUrl
+ + "\nCarrier: "
+ + android.os.SystemProperties
+ .get("gsm.sim.operator.alpha");
+ if (mWebView != null
+ && mWebView.getContext() != null
+ && mWebView.getContext().getSystemService(
+ Context.CONNECTIVITY_SERVICE) != null) {
+ ConnectivityManager cManager =
+ (ConnectivityManager) mWebView
+ .getContext().getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ NetworkInfo nInfo = cManager
+ .getActiveNetworkInfo();
+ if (nInfo != null) {
+ performanceString = performanceString
+ + "\nNetwork Type: "
+ + nInfo.getType().toString();
+ }
+ }
+ Checkin.logEvent(mResolver,
+ Checkin.Events.Tag.WEBPAGE_LOAD,
+ performanceString);
+ Log.w(LOGTAG, "pushed to the statistics service");
+ }
+ */
+ }
+ }
+ }
+
+ if (mInTrace) {
+ mInTrace = false;
+ Debug.stopMethodTracing();
+ }
+
+ if (mPageStarted) {
+ mPageStarted = false;
+ // pauseWebView() will do nothing and return false if onPause()
+ // is not called yet.
+ if (pauseWebView()) {
+ if (mWakeLock.isHeld()) {
+ mHandler.removeMessages(RELEASE_WAKELOCK);
+ mWakeLock.release();
+ }
+ }
+ }
+
+ if (mInLoad) {
+ mInLoad = false;
+ updateInLoadMenuItems();
+ }
+
+ mHandler.removeMessages(CHECK_MEMORY);
+ checkMemory();
+ }
+
+ // return true if want to hijack the url to let another app to handle it
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ if (url.startsWith(SCHEME_WTAI)) {
+ // wtai://wp/mc;number
+ // number=string(phone-number)
+ if (url.startsWith(SCHEME_WTAI_MC)) {
+ Intent intent = new Intent(Intent.ACTION_VIEW,
+ Uri.parse(WebView.SCHEME_TEL +
+ url.substring(SCHEME_WTAI_MC.length())));
+ startActivity(intent);
+ return true;
+ }
+ // wtai://wp/sd;dtmf
+ // dtmf=string(dialstring)
+ if (url.startsWith(SCHEME_WTAI_SD)) {
+ // TODO
+ // only send when there is active voice connection
+ return false;
+ }
+ // wtai://wp/ap;number;name
+ // number=string(phone-number)
+ // name=string
+ if (url.startsWith(SCHEME_WTAI_AP)) {
+ // TODO
+ return false;
+ }
+ }
+
+ Uri uri;
+ try {
+ uri = Uri.parse(url);
+ } catch (IllegalArgumentException ex) {
+ return false;
+ }
+
+ // check whether other activities want to handle this url
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ intent.addCategory(Intent.CATEGORY_BROWSABLE);
+ try {
+ if (startActivityIfNeeded(intent, -1)) {
+ return true;
+ }
+ } catch (ActivityNotFoundException ex) {
+ // ignore the error. If no application can handle the URL,
+ // eg about:blank, assume the browser can handle it.
+ }
+
+ if (mMenuIsDown) {
+ openTab(url);
+ closeOptionsMenu();
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Updates the lock icon. This method is called when we discover another
+ * resource to be loaded for this page (for example, javascript). While
+ * we update the icon type, we do not update the lock icon itself until
+ * we are done loading, it is slightly more secure this way.
+ */
+ @Override
+ public void onLoadResource(WebView view, String url) {
+ if (url != null && url.length() > 0) {
+ // It is only if the page claims to be secure
+ // that we may have to update the lock:
+ if (mLockIconType == LOCK_ICON_SECURE) {
+ // If NOT a 'safe' url, change the lock to mixed content!
+ if (!(URLUtil.isHttpsUrl(url) || URLUtil.isDataUrl(url) || URLUtil.isAboutUrl(url))) {
+ mLockIconType = LOCK_ICON_MIXED;
+ if (Config.LOGV) {
+ Log.v(LOGTAG, "BrowserActivity.updateLockIcon:" +
+ " updated lock icon to " + mLockIconType + " due to " + url);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Show the dialog, asking the user if they would like to continue after
+ * an excessive number of HTTP redirects.
+ */
+ @Override
+ public void onTooManyRedirects(WebView view, final Message cancelMsg,
+ final Message continueMsg) {
+ new AlertDialog.Builder(BrowserActivity.this)
+ .setTitle(R.string.browserFrameRedirect)
+ .setMessage(R.string.browserFrame307Post)
+ .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ continueMsg.sendToTarget();
+ }})
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ cancelMsg.sendToTarget();
+ }})
+ .setOnCancelListener(new OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ cancelMsg.sendToTarget();
+ }})
+ .show();
+ }
+
+ /**
+ * Show a dialog informing the user of the network error reported by
+ * WebCore.
+ */
+ @Override
+ public void onReceivedError(WebView view, int errorCode,
+ String description, String failingUrl) {
+ if (errorCode != EventHandler.ERROR_LOOKUP &&
+ errorCode != EventHandler.ERROR_CONNECT &&
+ errorCode != EventHandler.ERROR_BAD_URL &&
+ errorCode != EventHandler.ERROR_UNSUPPORTED_SCHEME &&
+ errorCode != EventHandler.FILE_ERROR) {
+ new AlertDialog.Builder(BrowserActivity.this)
+ .setTitle((errorCode == EventHandler.FILE_NOT_FOUND_ERROR) ?
+ R.string.browserFrameFileErrorLabel :
+ R.string.browserFrameNetworkErrorLabel)
+ .setMessage(description)
+ .setPositiveButton(R.string.ok, null)
+ .show();
+ }
+ Log.e(LOGTAG, "onReceivedError code:"+errorCode+" "+description);
+
+ // We need to reset the title after an error.
+ resetTitleAndRevertLockIcon();
+ }
+
+ /**
+ * Check with the user if it is ok to resend POST data as the page they
+ * are trying to navigate to is the result of a POST.
+ */
+ @Override
+ public void onFormResubmission(WebView view, final Message dontResend,
+ final Message resend) {
+ new AlertDialog.Builder(BrowserActivity.this)
+ .setTitle(R.string.browserFrameFormResubmitLabel)
+ .setMessage(R.string.browserFrameFormResubmitMessage)
+ .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ resend.sendToTarget();
+ }})
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ dontResend.sendToTarget();
+ }})
+ .setOnCancelListener(new OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ dontResend.sendToTarget();
+ }})
+ .show();
+ }
+
+ /**
+ * Insert the url into the visited history database.
+ * @param url The url to be inserted.
+ * @param isReload True if this url is being reloaded.
+ * FIXME: Not sure what to do when reloading the page.
+ */
+ @Override
+ public void doUpdateVisitedHistory(WebView view, String url,
+ boolean isReload) {
+ if (url.regionMatches(true, 0, "about:", 0, 6)) {
+ return;
+ }
+ Browser.updateVisitedHistory(mResolver, url, true);
+ WebIconDatabase.getInstance().retainIconForPageUrl(url);
+ }
+
+ /**
+ * Displays SSL error(s) dialog to the user.
+ */
+ @Override
+ public void onReceivedSslError(
+ final WebView view, final SslErrorHandler handler, final SslError error) {
+
+ if (mSettings.showSecurityWarnings()) {
+ final LayoutInflater factory =
+ LayoutInflater.from(BrowserActivity.this);
+ final View warningsView =
+ factory.inflate(R.layout.ssl_warnings, null);
+ final LinearLayout placeholder =
+ (LinearLayout)warningsView.findViewById(R.id.placeholder);
+
+ if (error.hasError(SslError.SSL_UNTRUSTED)) {
+ LinearLayout ll = (LinearLayout)factory
+ .inflate(R.layout.ssl_warning, null);
+ ((TextView)ll.findViewById(R.id.warning))
+ .setText(R.string.ssl_untrusted);
+ placeholder.addView(ll);
+ }
+
+ if (error.hasError(SslError.SSL_IDMISMATCH)) {
+ LinearLayout ll = (LinearLayout)factory
+ .inflate(R.layout.ssl_warning, null);
+ ((TextView)ll.findViewById(R.id.warning))
+ .setText(R.string.ssl_mismatch);
+ placeholder.addView(ll);
+ }
+
+ if (error.hasError(SslError.SSL_EXPIRED)) {
+ LinearLayout ll = (LinearLayout)factory
+ .inflate(R.layout.ssl_warning, null);
+ ((TextView)ll.findViewById(R.id.warning))
+ .setText(R.string.ssl_expired);
+ placeholder.addView(ll);
+ }
+
+ if (error.hasError(SslError.SSL_NOTYETVALID)) {
+ LinearLayout ll = (LinearLayout)factory
+ .inflate(R.layout.ssl_warning, null);
+ ((TextView)ll.findViewById(R.id.warning))
+ .setText(R.string.ssl_not_yet_valid);
+ placeholder.addView(ll);
+ }
+
+ new AlertDialog.Builder(BrowserActivity.this)
+ .setTitle(R.string.security_warning)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setView(warningsView)
+ .setPositiveButton(R.string.ssl_continue,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ handler.proceed();
+ }
+ })
+ .setNeutralButton(R.string.view_certificate,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ showSSLCertificateOnError(view, handler, error);
+ }
+ })
+ .setNegativeButton(R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ handler.cancel();
+ BrowserActivity.this.resetTitleAndRevertLockIcon();
+ }
+ })
+ .setOnCancelListener(
+ new DialogInterface.OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ handler.cancel();
+ BrowserActivity.this.resetTitleAndRevertLockIcon();
+ }
+ })
+ .show();
+ } else {
+ handler.proceed();
+ }
+ }
+
+ /**
+ * Handles an HTTP authentication request.
+ *
+ * @param handler The authentication handler
+ * @param host The host
+ * @param realm The realm
+ */
+ @Override
+ public void onReceivedHttpAuthRequest(WebView view,
+ final HttpAuthHandler handler, final String host, final String realm) {
+ String username = null;
+ String password = null;
+
+ boolean reuseHttpAuthUsernamePassword =
+ handler.useHttpAuthUsernamePassword();
+
+ if (reuseHttpAuthUsernamePassword &&
+ (mTabControl.getCurrentWebView() != null)) {
+ String[] credentials =
+ mTabControl.getCurrentWebView()
+ .getHttpAuthUsernamePassword(host, realm);
+ if (credentials != null && credentials.length == 2) {
+ username = credentials[0];
+ password = credentials[1];
+ }
+ }
+
+ if (username != null && password != null) {
+ handler.proceed(username, password);
+ } else {
+ showHttpAuthentication(handler, host, realm, null, null, null, 0);
+ }
+ }
+
+ @Override
+ public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
+ if (mMenuIsDown) {
+ // only check shortcut key when MENU is held
+ return getWindow().isShortcutKey(event.getKeyCode(), event);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
+ if (view != mTabControl.getCurrentTopWebView()) {
+ return;
+ }
+ if (event.isDown()) {
+ BrowserActivity.this.onKeyDown(event.getKeyCode(), event);
+ } else {
+ BrowserActivity.this.onKeyUp(event.getKeyCode(), event);
+ }
+ }
+ };
+
+ //--------------------------------------------------------------------------
+ // WebChromeClient implementation
+ //--------------------------------------------------------------------------
+
+ /* package */ WebChromeClient getWebChromeClient() {
+ return mWebChromeClient;
+ }
+
+ private final WebChromeClient mWebChromeClient = new WebChromeClient() {
+ // Helper method to create a new tab or sub window.
+ private void createWindow(final boolean dialog, final Message msg) {
+ if (dialog) {
+ mTabControl.createSubWindow();
+ final TabControl.Tab t = mTabControl.getCurrentTab();
+ attachSubWindow(t);
+ WebView.WebViewTransport transport =
+ (WebView.WebViewTransport) msg.obj;
+ transport.setWebView(t.getSubWebView());
+ msg.sendToTarget();
+ } else {
+ final TabControl.Tab parent = mTabControl.getCurrentTab();
+ // openTabAndShow will dispatch the message after creating the
+ // new WebView. This will prevent another request from coming
+ // in during the animation.
+ openTabAndShow(null, msg, false);
+ parent.addChildTab(mTabControl.getCurrentTab());
+ WebView.WebViewTransport transport =
+ (WebView.WebViewTransport) msg.obj;
+ transport.setWebView(mTabControl.getCurrentWebView());
+ }
+ }
+
+ @Override
+ public boolean onCreateWindow(WebView view, final boolean dialog,
+ final boolean userGesture, final Message resultMsg) {
+ // Ignore these requests during tab animations or if the tab
+ // overview is showing.
+ if (mAnimationCount > 0 || mTabOverview != null) {
+ return false;
+ }
+ // Short-circuit if we can't create any more tabs or sub windows.
+ if (dialog && mTabControl.getCurrentSubWindow() != null) {
+ new AlertDialog.Builder(BrowserActivity.this)
+ .setTitle(R.string.too_many_subwindows_dialog_title)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setMessage(R.string.too_many_subwindows_dialog_message)
+ .setPositiveButton(R.string.ok, null)
+ .show();
+ return false;
+ } else if (mTabControl.getTabCount() >= TabControl.MAX_TABS) {
+ new AlertDialog.Builder(BrowserActivity.this)
+ .setTitle(R.string.too_many_windows_dialog_title)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setMessage(R.string.too_many_windows_dialog_message)
+ .setPositiveButton(R.string.ok, null)
+ .show();
+ return false;
+ }
+
+ // Short-circuit if this was a user gesture.
+ if (userGesture) {
+ // createWindow will call openTabAndShow for new Windows and
+ // that will call tabPicker which will increment
+ // mAnimationCount.
+ createWindow(dialog, resultMsg);
+ return true;
+ }
+
+ // Allow the popup and create the appropriate window.
+ final AlertDialog.OnClickListener allowListener =
+ new AlertDialog.OnClickListener() {
+ public void onClick(DialogInterface d,
+ int which) {
+ // Same comment as above for setting
+ // mAnimationCount.
+ createWindow(dialog, resultMsg);
+ // Since we incremented mAnimationCount while the
+ // dialog was up, we have to decrement it here.
+ mAnimationCount--;
+ }
+ };
+
+ // Block the popup by returning a null WebView.
+ final AlertDialog.OnClickListener blockListener =
+ new AlertDialog.OnClickListener() {
+ public void onClick(DialogInterface d, int which) {
+ resultMsg.sendToTarget();
+ // We are not going to trigger an animation so
+ // unblock keys and animation requests.
+ mAnimationCount--;
+ }
+ };
+
+ // Build a confirmation dialog to display to the user.
+ final AlertDialog d =
+ new AlertDialog.Builder(BrowserActivity.this)
+ .setTitle(R.string.attention)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setMessage(R.string.popup_window_attempt)
+ .setPositiveButton(R.string.allow, allowListener)
+ .setNegativeButton(R.string.block, blockListener)
+ .setCancelable(false)
+ .create();
+
+ // Show the confirmation dialog.
+ d.show();
+ // We want to increment mAnimationCount here to prevent a
+ // potential race condition. If the user allows a pop-up from a
+ // site and that pop-up then triggers another pop-up, it is
+ // possible to get the BACK key between here and when the dialog
+ // appears.
+ mAnimationCount++;
+ return true;
+ }
+
+ @Override
+ public void onCloseWindow(WebView window) {
+ final int currentIndex = mTabControl.getCurrentIndex();
+ final TabControl.Tab parent =
+ mTabControl.getCurrentTab().getParentTab();
+ if (parent != null) {
+ // JavaScript can only close popup window.
+ switchTabs(currentIndex, mTabControl.getTabIndex(parent), true);
+ }
+ }
+
+ @Override
+ public void onProgressChanged(WebView view, int newProgress) {
+ // 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 (newProgress == 100) {
+ // onProgressChanged() is called for sub-frame too while
+ // onPageFinished() is only called for the main frame. sync
+ // cookie and cache promptly here.
+ CookieSyncManager.getInstance().sync();
+ }
+ }
+
+ @Override
+ public void onReceivedTitle(WebView view, String title) {
+ String url = view.getOriginalUrl();
+
+ // here, if url is null, we want to reset the title
+ setUrlTitle(url, title);
+
+ if (url == null ||
+ url.length() >= SQLiteDatabase.SQLITE_MAX_LIKE_PATTERN_LENGTH) {
+ return;
+ }
+ if (url.startsWith("http://www.")) {
+ url = url.substring(11);
+ } else if (url.startsWith("http://")) {
+ url = url.substring(4);
+ }
+ try {
+ url = "%" + url;
+ String [] selArgs = new String[] { url };
+
+ String where = Browser.BookmarkColumns.URL + " LIKE ? AND "
+ + Browser.BookmarkColumns.BOOKMARK + " = 0";
+ Cursor c = mResolver.query(Browser.BOOKMARKS_URI,
+ Browser.HISTORY_PROJECTION, where, selArgs, null);
+ if (c.moveToFirst()) {
+ if (Config.LOGV) {
+ 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();
+ }
+ c.close();
+ } catch (IllegalStateException e) {
+ Log.e(LOGTAG, "BrowserActivity onReceived title", e);
+ } catch (SQLiteException ex) {
+ Log.e(LOGTAG, "onReceivedTitle() caught SQLiteException: ", ex);
+ }
+ }
+
+ @Override
+ public void onReceivedIcon(WebView view, Bitmap icon) {
+ updateIcon(view.getUrl(), icon);
+ }
+ };
+
+ /**
+ * Notify the host application a download should be done, or that
+ * the data should be streamed if a streaming viewer is available.
+ * @param url The full url to the content that should be downloaded
+ * @param contentDisposition Content-disposition http header, if
+ * present.
+ * @param mimetype The mimetype of the content reported by the server
+ * @param contentLength The file size reported by the server
+ */
+ public void onDownloadStart(String url, String userAgent,
+ String contentDisposition, String mimetype, long contentLength) {
+ // if we're dealing wih A/V content that's not explicitly marked
+ // for download, check if it's streamable.
+ if (contentDisposition == null
+ || !contentDisposition.regionMatches(true, 0, "attachment", 0, 10)) {
+ // query the package manager to see if there's a registered handler
+ // that matches.
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setDataAndType(Uri.parse(url), mimetype);
+ if (getPackageManager().resolveActivity(intent,
+ PackageManager.MATCH_DEFAULT_ONLY) != null) {
+ // someone knows how to handle this mime type with this scheme, don't download.
+ try {
+ startActivity(intent);
+ return;
+ } catch (ActivityNotFoundException ex) {
+ if (Config.LOGD) {
+ Log.d(LOGTAG, "activity not found for " + mimetype
+ + " over " + Uri.parse(url).getScheme(), ex);
+ }
+ // Best behavior is to fall back to a download in this case
+ }
+ }
+ }
+ onDownloadStartNoStream(url, userAgent, contentDisposition, mimetype, contentLength);
+ }
+
+ /**
+ * Notify the host application a download should be done, even if there
+ * is a streaming viewer available for thise type.
+ * @param url The full url to the content that should be downloaded
+ * @param contentDisposition Content-disposition http header, if
+ * present.
+ * @param mimetype The mimetype of the content reported by the server
+ * @param contentLength The file size reported by the server
+ */
+ /*package */ void onDownloadStartNoStream(String url, String userAgent,
+ String contentDisposition, String mimetype, long contentLength) {
+
+ String filename = URLUtil.guessFileName(url,
+ contentDisposition, mimetype);
+
+ // Check to see if we have an SDCard
+ String status = Environment.getExternalStorageState();
+ if (!status.equals(Environment.MEDIA_MOUNTED)) {
+ int title;
+ String msg;
+
+ // Check to see if the SDCard is busy, same as the music app
+ if (status.equals(Environment.MEDIA_SHARED)) {
+ msg = getString(R.string.download_sdcard_busy_dlg_msg);
+ title = R.string.download_sdcard_busy_dlg_title;
+ } else {
+ msg = getString(R.string.download_no_sdcard_dlg_msg, filename);
+ title = R.string.download_no_sdcard_dlg_title;
+ }
+
+ new AlertDialog.Builder(this)
+ .setTitle(title)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setMessage(msg)
+ .setPositiveButton(R.string.ok, null)
+ .show();
+ return;
+ }
+
+ // java.net.URI is a lot stricter than KURL so we have to undo
+ // KURL's percent-encoding and redo the encoding using java.net.URI.
+ URI uri = null;
+ try {
+ // Undo the percent-encoding that KURL may have done.
+ String newUrl = new String(URLUtil.decode(url.getBytes()));
+ // Parse the url into pieces
+ WebAddress w = new WebAddress(newUrl);
+ String frag = null;
+ String query = null;
+ String path = w.mPath;
+ // Break the path into path, query, and fragment
+ if (path.length() > 0) {
+ // Strip the fragment
+ int idx = path.lastIndexOf('#');
+ if (idx != -1) {
+ frag = path.substring(idx + 1);
+ path = path.substring(0, idx);
+ }
+ idx = path.lastIndexOf('?');
+ if (idx != -1) {
+ query = path.substring(idx + 1);
+ path = path.substring(0, idx);
+ }
+ }
+ uri = new URI(w.mScheme, w.mAuthInfo, w.mHost, w.mPort, path,
+ query, frag);
+ } catch (Exception e) {
+ Log.e(LOGTAG, "Could not parse url for download: " + url, e);
+ return;
+ }
+
+ // XXX: Have to use the old url since the cookies were stored using the
+ // old percent-encoded url.
+ 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,
+ getPackageName());
+ values.put(Downloads.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());
+ if (contentLength > 0) {
+ values.put(Downloads.TOTAL_BYTES, contentLength);
+ }
+ if (mimetype == null) {
+ // We must have long pressed on a link or image to download it. We
+ // are not sure of the mimetype in this case, so do a head request
+ new FetchUrlMimeType(this).execute(values);
+ } else {
+ final Uri contentUri =
+ getContentResolver().insert(Downloads.CONTENT_URI, values);
+ viewDownloads(contentUri);
+ }
+
+ }
+
+ /**
+ * Resets the lock icon. This method is called when we start a new load and
+ * know the url to be loaded.
+ */
+ private void resetLockIcon(String url) {
+ // Save the lock-icon state (we revert to it if the load gets cancelled)
+ saveLockIcon();
+
+ mLockIconType = LOCK_ICON_UNSECURE;
+ if (URLUtil.isHttpsUrl(url)) {
+ mLockIconType = LOCK_ICON_SECURE;
+ if (Config.LOGV) {
+ Log.v(LOGTAG, "BrowserActivity.resetLockIcon:" +
+ " reset lock icon to " + mLockIconType);
+ }
+ }
+
+ updateLockIconImage(LOCK_ICON_UNSECURE);
+ }
+
+ /**
+ * Resets the lock icon. This method is called when the icon needs to be
+ * reset but we do not know whether we are loading a secure or not secure
+ * page.
+ */
+ private void resetLockIcon() {
+ // Save the lock-icon state (we revert to it if the load gets cancelled)
+ saveLockIcon();
+
+ mLockIconType = LOCK_ICON_UNSECURE;
+
+ if (Config.LOGV) {
+ Log.v(LOGTAG, "BrowserActivity.resetLockIcon:" +
+ " reset lock icon to " + mLockIconType);
+ }
+
+ updateLockIconImage(LOCK_ICON_UNSECURE);
+ }
+
+ /**
+ * Updates the lock-icon image in the title-bar.
+ */
+ private void updateLockIconImage(int lockIconType) {
+ Drawable d = null;
+ if (lockIconType == LOCK_ICON_SECURE) {
+ d = mSecLockIcon;
+ } else if (lockIconType == LOCK_ICON_MIXED) {
+ d = mMixLockIcon;
+ }
+ // 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);
+ }
+ }
+
+ /**
+ * Displays a page-info dialog.
+ * @param tab The tab to show info about
+ * @param fromShowSSLCertificateOnError The flag that indicates whether
+ * this dialog was opened from the SSL-certificate-on-error dialog or
+ * not. This is important, since we need to know whether to return to
+ * the parent dialog or simply dismiss.
+ */
+ private void showPageInfo(final TabControl.Tab tab,
+ final boolean fromShowSSLCertificateOnError) {
+ final LayoutInflater factory = LayoutInflater
+ .from(this);
+
+ final View pageInfoView = factory.inflate(R.layout.page_info, null);
+
+ final WebView view = tab.getWebView();
+
+ String url = null;
+ String title = null;
+
+ if (view == null) {
+ url = tab.getUrl();
+ title = tab.getTitle();
+ } else if (view == mTabControl.getCurrentWebView()) {
+ // Use the cached title and url if this is the current WebView
+ url = mUrl;
+ title = mTitle;
+ } else {
+ url = view.getUrl();
+ title = view.getTitle();
+ }
+
+ if (url == null) {
+ url = "";
+ }
+ if (title == null) {
+ title = "";
+ }
+
+ ((TextView) pageInfoView.findViewById(R.id.address)).setText(url);
+ ((TextView) pageInfoView.findViewById(R.id.title)).setText(title);
+
+ mPageInfoView = tab;
+ mPageInfoFromShowSSLCertificateOnError = new Boolean(fromShowSSLCertificateOnError);
+
+ AlertDialog.Builder alertDialogBuilder =
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.page_info).setIcon(android.R.drawable.ic_dialog_info)
+ .setView(pageInfoView)
+ .setPositiveButton(
+ R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int whichButton) {
+ mPageInfoDialog = null;
+ mPageInfoView = null;
+ mPageInfoFromShowSSLCertificateOnError = null;
+
+ // if we came here from the SSL error dialog
+ if (fromShowSSLCertificateOnError) {
+ // go back to the SSL error dialog
+ showSSLCertificateOnError(
+ mSSLCertificateOnErrorView,
+ mSSLCertificateOnErrorHandler,
+ mSSLCertificateOnErrorError);
+ }
+ }
+ })
+ .setOnCancelListener(
+ new DialogInterface.OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ mPageInfoDialog = null;
+ mPageInfoView = null;
+ mPageInfoFromShowSSLCertificateOnError = null;
+
+ // if we came here from the SSL error dialog
+ if (fromShowSSLCertificateOnError) {
+ // go back to the SSL error dialog
+ showSSLCertificateOnError(
+ mSSLCertificateOnErrorView,
+ mSSLCertificateOnErrorHandler,
+ mSSLCertificateOnErrorError);
+ }
+ }
+ });
+
+ // if we have a main top-level page SSL certificate set or a certificate
+ // error
+ if (fromShowSSLCertificateOnError ||
+ (view != null && view.getCertificate() != null)) {
+ // add a 'View Certificate' button
+ alertDialogBuilder.setNeutralButton(
+ R.string.view_certificate,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int whichButton) {
+ mPageInfoDialog = null;
+ mPageInfoView = null;
+ mPageInfoFromShowSSLCertificateOnError = null;
+
+ // if we came here from the SSL error dialog
+ if (fromShowSSLCertificateOnError) {
+ // go back to the SSL error dialog
+ showSSLCertificateOnError(
+ mSSLCertificateOnErrorView,
+ mSSLCertificateOnErrorHandler,
+ mSSLCertificateOnErrorError);
+ } else {
+ // otherwise, display the top-most certificate from
+ // the chain
+ if (view.getCertificate() != null) {
+ showSSLCertificate(tab);
+ }
+ }
+ }
+ });
+ }
+
+ mPageInfoDialog = alertDialogBuilder.show();
+ }
+
+ /**
+ * Displays the main top-level page SSL certificate dialog
+ * (accessible from the Page-Info dialog).
+ * @param tab The tab to show certificate for.
+ */
+ private void showSSLCertificate(final TabControl.Tab tab) {
+ final View certificateView =
+ inflateCertificateView(tab.getWebView().getCertificate());
+ if (certificateView == null) {
+ return;
+ }
+
+ LayoutInflater factory = LayoutInflater.from(this);
+
+ final LinearLayout placeholder =
+ (LinearLayout)certificateView.findViewById(R.id.placeholder);
+
+ LinearLayout ll = (LinearLayout) factory.inflate(
+ R.layout.ssl_success, placeholder);
+ ((TextView)ll.findViewById(R.id.success))
+ .setText(R.string.ssl_certificate_is_valid);
+
+ mSSLCertificateView = tab;
+ mSSLCertificateDialog =
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.ssl_certificate).setIcon(
+ R.drawable.ic_dialog_browser_certificate_secure)
+ .setView(certificateView)
+ .setPositiveButton(R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int whichButton) {
+ mSSLCertificateDialog = null;
+ mSSLCertificateView = null;
+
+ showPageInfo(tab, false);
+ }
+ })
+ .setOnCancelListener(
+ new DialogInterface.OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ mSSLCertificateDialog = null;
+ mSSLCertificateView = null;
+
+ showPageInfo(tab, false);
+ }
+ })
+ .show();
+ }
+
+ /**
+ * Displays the SSL error certificate dialog.
+ * @param view The target web-view.
+ * @param handler The SSL error handler responsible for cancelling the
+ * connection that resulted in an SSL error or proceeding per user request.
+ * @param error The SSL error object.
+ */
+ private void showSSLCertificateOnError(
+ final WebView view, final SslErrorHandler handler, final SslError error) {
+
+ final View certificateView =
+ inflateCertificateView(error.getCertificate());
+ if (certificateView == null) {
+ return;
+ }
+
+ LayoutInflater factory = LayoutInflater.from(this);
+
+ final LinearLayout placeholder =
+ (LinearLayout)certificateView.findViewById(R.id.placeholder);
+
+ if (error.hasError(SslError.SSL_UNTRUSTED)) {
+ LinearLayout ll = (LinearLayout)factory
+ .inflate(R.layout.ssl_warning, placeholder);
+ ((TextView)ll.findViewById(R.id.warning))
+ .setText(R.string.ssl_untrusted);
+ }
+
+ if (error.hasError(SslError.SSL_IDMISMATCH)) {
+ LinearLayout ll = (LinearLayout)factory
+ .inflate(R.layout.ssl_warning, placeholder);
+ ((TextView)ll.findViewById(R.id.warning))
+ .setText(R.string.ssl_mismatch);
+ }
+
+ if (error.hasError(SslError.SSL_EXPIRED)) {
+ LinearLayout ll = (LinearLayout)factory
+ .inflate(R.layout.ssl_warning, placeholder);
+ ((TextView)ll.findViewById(R.id.warning))
+ .setText(R.string.ssl_expired);
+ }
+
+ if (error.hasError(SslError.SSL_NOTYETVALID)) {
+ LinearLayout ll = (LinearLayout)factory
+ .inflate(R.layout.ssl_warning, placeholder);
+ ((TextView)ll.findViewById(R.id.warning))
+ .setText(R.string.ssl_not_yet_valid);
+ }
+
+ mSSLCertificateOnErrorHandler = handler;
+ mSSLCertificateOnErrorView = view;
+ mSSLCertificateOnErrorError = error;
+ mSSLCertificateOnErrorDialog =
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.ssl_certificate).setIcon(
+ R.drawable.ic_dialog_browser_certificate_partially_secure)
+ .setView(certificateView)
+ .setPositiveButton(R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int whichButton) {
+ mSSLCertificateOnErrorDialog = null;
+ mSSLCertificateOnErrorView = null;
+ mSSLCertificateOnErrorHandler = null;
+ mSSLCertificateOnErrorError = null;
+
+ mWebViewClient.onReceivedSslError(
+ view, handler, error);
+ }
+ })
+ .setNeutralButton(R.string.page_info_view,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int whichButton) {
+ mSSLCertificateOnErrorDialog = null;
+
+ // do not clear the dialog state: we will
+ // need to show the dialog again once the
+ // user is done exploring the page-info details
+
+ showPageInfo(mTabControl.getTabFromView(view),
+ true);
+ }
+ })
+ .setOnCancelListener(
+ new DialogInterface.OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ mSSLCertificateOnErrorDialog = null;
+ mSSLCertificateOnErrorView = null;
+ mSSLCertificateOnErrorHandler = null;
+ mSSLCertificateOnErrorError = null;
+
+ mWebViewClient.onReceivedSslError(
+ view, handler, error);
+ }
+ })
+ .show();
+ }
+
+ /**
+ * Inflates the SSL certificate view (helper method).
+ * @param certificate The SSL certificate.
+ * @return The resultant certificate view with issued-to, issued-by,
+ * issued-on, expires-on, and possibly other fields set.
+ * If the input certificate is null, returns null.
+ */
+ private View inflateCertificateView(SslCertificate certificate) {
+ if (certificate == null) {
+ return null;
+ }
+
+ LayoutInflater factory = LayoutInflater.from(this);
+
+ View certificateView = factory.inflate(
+ R.layout.ssl_certificate, null);
+
+ // issued to:
+ SslCertificate.DName issuedTo = certificate.getIssuedTo();
+ if (issuedTo != null) {
+ ((TextView) certificateView.findViewById(R.id.to_common))
+ .setText(issuedTo.getCName());
+ ((TextView) certificateView.findViewById(R.id.to_org))
+ .setText(issuedTo.getOName());
+ ((TextView) certificateView.findViewById(R.id.to_org_unit))
+ .setText(issuedTo.getUName());
+ }
+
+ // issued by:
+ SslCertificate.DName issuedBy = certificate.getIssuedBy();
+ if (issuedBy != null) {
+ ((TextView) certificateView.findViewById(R.id.by_common))
+ .setText(issuedBy.getCName());
+ ((TextView) certificateView.findViewById(R.id.by_org))
+ .setText(issuedBy.getOName());
+ ((TextView) certificateView.findViewById(R.id.by_org_unit))
+ .setText(issuedBy.getUName());
+ }
+
+ // issued on:
+ String issuedOn = reformatCertificateDate(
+ certificate.getValidNotBefore());
+ ((TextView) certificateView.findViewById(R.id.issued_on))
+ .setText(issuedOn);
+
+ // expires on:
+ String expiresOn = reformatCertificateDate(
+ certificate.getValidNotAfter());
+ ((TextView) certificateView.findViewById(R.id.expires_on))
+ .setText(expiresOn);
+
+ return certificateView;
+ }
+
+ /**
+ * Re-formats the certificate date (Date.toString()) string to
+ * a properly localized date string.
+ * @return Properly localized version of the certificate date string and
+ * the original certificate date string if fails to localize.
+ * If the original string is null, returns an empty string "".
+ */
+ private String reformatCertificateDate(String certificateDate) {
+ String reformattedDate = null;
+
+ if (certificateDate != null) {
+ Date date = null;
+ try {
+ date = java.text.DateFormat.getInstance().parse(certificateDate);
+ } catch (ParseException e) {
+ date = null;
+ }
+
+ if (date != null) {
+ reformattedDate =
+ DateFormat.getDateFormat(this).format(date);
+ }
+ }
+
+ return reformattedDate != null ? reformattedDate :
+ (certificateDate != null ? certificateDate : "");
+ }
+
+ /**
+ * Displays an http-authentication dialog.
+ */
+ private void showHttpAuthentication(final HttpAuthHandler handler,
+ final String host, final String realm, final String title,
+ final String name, final String password, int focusId) {
+ LayoutInflater factory = LayoutInflater.from(this);
+ final View v = factory
+ .inflate(R.layout.http_authentication, null);
+ if (name != null) {
+ ((EditText) v.findViewById(R.id.username_edit)).setText(name);
+ }
+ if (password != null) {
+ ((EditText) v.findViewById(R.id.password_edit)).setText(password);
+ }
+
+ String titleText = title;
+ if (titleText == null) {
+ titleText = getText(R.string.sign_in_to).toString().replace(
+ "%s1", host).replace("%s2", realm);
+ }
+
+ mHttpAuthHandler = handler;
+ AlertDialog dialog = new AlertDialog.Builder(this)
+ .setTitle(titleText)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setView(v)
+ .setPositiveButton(R.string.action,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int whichButton) {
+ String nm = ((EditText) v
+ .findViewById(R.id.username_edit))
+ .getText().toString();
+ String pw = ((EditText) v
+ .findViewById(R.id.password_edit))
+ .getText().toString();
+ BrowserActivity.this.setHttpAuthUsernamePassword
+ (host, realm, nm, pw);
+ handler.proceed(nm, pw);
+ mHttpAuthenticationDialog = null;
+ mHttpAuthHandler = null;
+ }})
+ .setNegativeButton(R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int whichButton) {
+ handler.cancel();
+ BrowserActivity.this.resetTitleAndRevertLockIcon();
+ mHttpAuthenticationDialog = null;
+ mHttpAuthHandler = null;
+ }})
+ .setOnCancelListener(new DialogInterface.OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ handler.cancel();
+ BrowserActivity.this.resetTitleAndRevertLockIcon();
+ mHttpAuthenticationDialog = null;
+ mHttpAuthHandler = null;
+ }})
+ .create();
+ // Make the IME appear when the dialog is displayed if applicable.
+ dialog.getWindow().setSoftInputMode(
+ WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
+ dialog.show();
+ if (focusId != 0) {
+ dialog.findViewById(focusId).requestFocus();
+ } else {
+ v.findViewById(R.id.username_edit).requestFocus();
+ }
+ mHttpAuthenticationDialog = dialog;
+ }
+
+ public int getProgress() {
+ WebView w = mTabControl.getCurrentWebView();
+ if (w != null) {
+ return w.getProgress();
+ } else {
+ return 100;
+ }
+ }
+
+ /**
+ * Set HTTP authentication password.
+ *
+ * @param host The host for the password
+ * @param realm The realm for the password
+ * @param username The username for the password. If it is null, it means
+ * password can't be saved.
+ * @param password The password
+ */
+ public void setHttpAuthUsernamePassword(String host, String realm,
+ String username,
+ String password) {
+ WebView w = mTabControl.getCurrentWebView();
+ if (w != null) {
+ w.setHttpAuthUsernamePassword(host, realm, username, password);
+ }
+ }
+
+ /**
+ * connectivity manager says net has come or gone... inform the user
+ * @param up true if net has come up, false if net has gone down
+ */
+ public void onNetworkToggle(boolean up) {
+ if (up == mIsNetworkUp) {
+ return;
+ } else if (up) {
+ mIsNetworkUp = true;
+ if (mAlertDialog != null) {
+ mAlertDialog.cancel();
+ mAlertDialog = null;
+ }
+ } else {
+ mIsNetworkUp = false;
+ if (mInLoad && mAlertDialog == null) {
+ mAlertDialog = new AlertDialog.Builder(this)
+ .setTitle(R.string.loadSuspendedTitle)
+ .setMessage(R.string.loadSuspended)
+ .setPositiveButton(R.string.ok, null)
+ .show();
+ }
+ }
+ WebView w = mTabControl.getCurrentWebView();
+ if (w != null) {
+ w.setNetworkAvailable(up);
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode,
+ Intent intent) {
+ switch (requestCode) {
+ case COMBO_PAGE:
+ if (resultCode == RESULT_OK && intent != null) {
+ String data = intent.getAction();
+ Bundle extras = intent.getExtras();
+ if (extras != null && extras.getBoolean("new_window", false)) {
+ openTab(data);
+ } else {
+ final TabControl.Tab currentTab =
+ mTabControl.getCurrentTab();
+ // If the Window overview is up and we are not in the
+ // middle of an animation, animate away from it to the
+ // current tab.
+ if (mTabOverview != null && mAnimationCount == 0) {
+ sendAnimateFromOverview(currentTab, false, data,
+ TAB_OVERVIEW_DELAY, null);
+ } else {
+ dismissSubWindow(currentTab);
+ if (data != null && data.length() != 0) {
+ getTopWindow().loadUrl(data);
+ }
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ getTopWindow().requestFocus();
+ }
+
+ /*
+ * This method is called as a result of the user selecting the options
+ * menu to see the download window, or when a download changes state. It
+ * shows the download window ontop of the current window.
+ */
+ /* package */ void viewDownloads(Uri downloadRecord) {
+ Intent intent = new Intent(this,
+ BrowserDownloadPage.class);
+ intent.setData(downloadRecord);
+ startActivityForResult(intent, this.DOWNLOAD_PAGE);
+
+ }
+
+ /**
+ * Handle results from Tab Switcher mTabOverview tool
+ */
+ private class TabListener implements ImageGrid.Listener {
+ public void remove(int position) {
+ // Note: Remove is not enabled if we have only one tab.
+ if (Config.DEBUG && mTabControl.getTabCount() == 1) {
+ throw new AssertionError();
+ }
+
+ // Remember the current tab.
+ TabControl.Tab current = mTabControl.getCurrentTab();
+ final TabControl.Tab remove = mTabControl.getTab(position);
+ mTabControl.removeTab(remove);
+ // If we removed the current tab, use the tab at position - 1 if
+ // possible.
+ if (current == remove) {
+ // If the user removes the last tab, act like the New Tab item
+ // was clicked on.
+ if (mTabControl.getTabCount() == 0) {
+ current = mTabControl.createNewTab(false);
+ sendAnimateFromOverview(current, true,
+ mSettings.getHomePage(), TAB_OVERVIEW_DELAY, null);
+ } else {
+ final int index = position > 0 ? (position - 1) : 0;
+ current = mTabControl.getTab(index);
+ }
+ }
+
+ // The tab overview could have been dismissed before this method is
+ // called.
+ if (mTabOverview != null) {
+ // Remove the tab and change the index.
+ mTabOverview.remove(position);
+ mTabOverview.setCurrentIndex(mTabControl.getTabIndex(current));
+ }
+
+ // Only the current tab ensures its WebView is non-null. This
+ // implies that we are reloading the freed tab.
+ mTabControl.setCurrentTab(current);
+ }
+ public void onClick(int index) {
+ // Change the tab if necessary.
+ // Index equals ImageGrid.CANCEL when pressing back from the tab
+ // overview.
+ if (index == ImageGrid.CANCEL) {
+ index = mTabControl.getCurrentIndex();
+ // The current index is -1 if the current tab was removed.
+ if (index == -1) {
+ // Take the last tab as a fallback.
+ index = mTabControl.getTabCount() - 1;
+ }
+ }
+
+ // Clear all the data for tab picker so next time it will be
+ // recreated.
+ mTabControl.wipeAllPickerData();
+
+ // NEW_TAB means that the "New Tab" cell was clicked on.
+ if (index == ImageGrid.NEW_TAB) {
+ openTabAndShow(mSettings.getHomePage(), null, false);
+ } else {
+ sendAnimateFromOverview(mTabControl.getTab(index),
+ false, null, 0, null);
+ }
+ }
+ }
+
+ // A fake View that draws the WebView's picture with a fast zoom filter.
+ // The View is used in case the tab is freed during the animation because
+ // of low memory.
+ private static class AnimatingView extends View {
+ private static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG |
+ Paint.DITHER_FLAG | Paint.SUBPIXEL_TEXT_FLAG;
+ private static final DrawFilter sZoomFilter =
+ new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG);
+ private final Picture mPicture;
+ private final float mScale;
+ private final int mScrollX;
+ private final int mScrollY;
+ final TabControl.Tab mTab;
+
+ 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();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ canvas.save();
+ canvas.drawColor(Color.WHITE);
+ if (mPicture != null) {
+ canvas.setDrawFilter(sZoomFilter);
+ float scale = getWidth() * mScale;
+ canvas.scale(scale, scale);
+ canvas.translate(-mScrollX, -mScrollY);
+ canvas.drawPicture(mPicture);
+ }
+ canvas.restore();
+ }
+ }
+
+ /**
+ * Open the tab picker. This function will always use the current tab in
+ * its animation.
+ * @param stay boolean stating whether the tab picker is to remain open
+ * (in which case it needs a listener and its menu) or not.
+ * @param index The index of the tab to show as the selection in the tab
+ * overview.
+ * @param remove If true, the tab at index will be removed after the
+ * animation completes.
+ */
+ private void tabPicker(final boolean stay, final int index,
+ final boolean remove) {
+ if (mTabOverview != null) {
+ return;
+ }
+
+ int size = mTabControl.getTabCount();
+
+ TabListener l = null;
+ if (stay) {
+ l = mTabListener = new TabListener();
+ }
+ mTabOverview = new ImageGrid(this, stay, l);
+
+ for (int i = 0; i < size; i++) {
+ final TabControl.Tab t = mTabControl.getTab(i);
+ mTabControl.populatePickerData(t);
+ mTabOverview.add(t);
+ }
+
+ // Tell the tab overview to show the current tab, the tab overview will
+ // handle the "New Tab" case.
+ int currentIndex = mTabControl.getCurrentIndex();
+ mTabOverview.setCurrentIndex(currentIndex);
+
+ // Attach the tab overview.
+ mContentView.addView(mTabOverview, COVER_SCREEN_PARAMS);
+
+ // Create a fake AnimatingView to animate the WebView's picture.
+ final TabControl.Tab current = mTabControl.getCurrentTab();
+ final AnimatingView v = new AnimatingView(this, current);
+ mContentView.addView(v, COVER_SCREEN_PARAMS);
+ removeTabFromContentView(current);
+ // Pause timers to get the animation smoother.
+ current.getWebView().pauseTimers();
+
+ // Send a message so the tab picker has a chance to layout and get
+ // positions for all the cells.
+ mHandler.sendMessage(mHandler.obtainMessage(ANIMATE_TO_OVERVIEW,
+ index, remove ? 1 : 0, v));
+ // Setting this will indicate that we are animating to the overview. We
+ // set it here to prevent another request to animate from coming in
+ // between now and when ANIMATE_TO_OVERVIEW is handled.
+ 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);
+ // Make the menu empty until the animation completes.
+ mMenuState = EMPTY_MENU;
+ }
+
+ private void bookmarksOrHistoryPicker(boolean startWithHistory) {
+ WebView current = mTabControl.getCurrentWebView();
+ if (current == null) {
+ return;
+ }
+ Intent intent = new Intent(this,
+ CombinedBookmarkHistoryActivity.class);
+ String title = current.getTitle();
+ String url = current.getUrl();
+ // Just in case the user opens bookmarks before a page finishes loading
+ // so the current history item, and therefore the page, is null.
+ if (null == url) {
+ url = mLastEnteredUrl;
+ // This can happen.
+ if (null == url) {
+ url = mSettings.getHomePage();
+ }
+ }
+ // In case the web page has not yet received its associated title.
+ if (title == null) {
+ title = url;
+ }
+ intent.putExtra("title", title);
+ intent.putExtra("url", url);
+ intent.putExtra("maxTabsOpen",
+ mTabControl.getTabCount() >= TabControl.MAX_TABS);
+ if (startWithHistory) {
+ intent.putExtra(CombinedBookmarkHistoryActivity.STARTING_TAB,
+ CombinedBookmarkHistoryActivity.HISTORY_TAB);
+ }
+ startActivityForResult(intent, COMBO_PAGE);
+ }
+
+ // Called when loading from context menu or LOAD_URL message
+ private void loadURL(WebView view, String url) {
+ // In case the user enters nothing.
+ if (url != null && url.length() != 0 && view != null) {
+ url = smartUrlFilter(url);
+ if (!mWebViewClient.shouldOverrideUrlLoading(view, url)) {
+ view.loadUrl(url);
+ }
+ }
+ }
+
+ private void checkMemory() {
+ ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
+ ((ActivityManager) getSystemService(ACTIVITY_SERVICE))
+ .getMemoryInfo(mi);
+ // FIXME: mi.lowMemory is too aggressive, use (mi.availMem <
+ // mi.threshold) for now
+ // if (mi.lowMemory) {
+ if (mi.availMem < mi.threshold) {
+ Log.w(LOGTAG, "Browser is freeing memory now because: available="
+ + (mi.availMem / 1024) + "K threshold="
+ + (mi.threshold / 1024) + "K");
+ mTabControl.freeMemory();
+ }
+ }
+
+ private String smartUrlFilter(Uri inUri) {
+ if (inUri != null) {
+ return smartUrlFilter(inUri.toString());
+ }
+ return null;
+ }
+
+
+ // get window count
+
+ int getWindowCount(){
+ if(mTabControl != null){
+ return mTabControl.getTabCount();
+ }
+ return 0;
+ }
+
+ static final Pattern ACCEPTED_URI_SCHEMA = Pattern.compile(
+ "(?i)" + // switch on case insensitive matching
+ "(" + // begin group for schema
+ "(?:http|https|file):\\/\\/" +
+ "|(?:data|about|content|javascript):" +
+ ")" +
+ "(.*)" );
+
+ /**
+ * Attempts to determine whether user input is a URL or search
+ * terms. Anything with a space is passed to search.
+ *
+ * Converts to lowercase any mistakenly uppercased schema (i.e.,
+ * "Http://" converts to "http://"
+ *
+ * @return Original or modified URL
+ *
+ */
+ String smartUrlFilter(String url) {
+
+ String inUrl = url.trim();
+ boolean hasSpace = inUrl.indexOf(' ') != -1;
+
+ Matcher matcher = ACCEPTED_URI_SCHEMA.matcher(inUrl);
+ if (matcher.matches()) {
+ if (hasSpace) {
+ inUrl = inUrl.replace(" ", "%20");
+ }
+ // force scheme to lowercase
+ String scheme = matcher.group(1);
+ String lcScheme = scheme.toLowerCase();
+ if (!lcScheme.equals(scheme)) {
+ return lcScheme + matcher.group(2);
+ }
+ return inUrl;
+ }
+ if (hasSpace) {
+ // FIXME: quick search, need to be customized by setting
+ if (inUrl.length() > 2 && inUrl.charAt(1) == ' ') {
+ // FIXME: Is this the correct place to add to searches?
+ // what if someone else calls this function?
+ char char0 = inUrl.charAt(0);
+
+ if (char0 == 'g') {
+ Browser.addSearchUrl(mResolver, inUrl);
+ return composeSearchUrl(inUrl.substring(2));
+
+ } else if (char0 == 'w') {
+ Browser.addSearchUrl(mResolver, inUrl);
+ return URLUtil.composeSearchUrl(inUrl.substring(2),
+ QuickSearch_W,
+ QUERY_PLACE_HOLDER);
+
+ } else if (char0 == 'd') {
+ Browser.addSearchUrl(mResolver, inUrl);
+ return URLUtil.composeSearchUrl(inUrl.substring(2),
+ QuickSearch_D,
+ QUERY_PLACE_HOLDER);
+
+ } else if (char0 == 'l') {
+ Browser.addSearchUrl(mResolver, inUrl);
+ // FIXME: we need location in this case
+ return URLUtil.composeSearchUrl(inUrl.substring(2),
+ QuickSearch_L,
+ QUERY_PLACE_HOLDER);
+ }
+ }
+ } else {
+ if (Regex.WEB_URL_PATTERN.matcher(inUrl).matches()) {
+ return URLUtil.guessUrl(inUrl);
+ }
+ }
+
+ Browser.addSearchUrl(mResolver, inUrl);
+ return composeSearchUrl(inUrl);
+ }
+
+ /* package */ String composeSearchUrl(String search) {
+ return URLUtil.composeSearchUrl(search, QuickSearch_G,
+ QUERY_PLACE_HOLDER);
+ }
+
+ /* package */void setBaseSearchUrl(String url) {
+ if (url == null || url.length() == 0) {
+ /*
+ * get the google search url based on the SIM. Default is US. NOTE:
+ * This code uses resources to optionally select the search Uri,
+ * based on the MCC value from the SIM. The default string will most
+ * likely be fine. It is parameterized to accept info from the
+ * Locale, the language code is the first parameter (%1$s) and the
+ * country code is the second (%2$s). This code must function in the
+ * same way as a similar lookup in
+ * com.android.googlesearch.SuggestionProvider#onCreate(). If you
+ * change either of these functions, change them both. (The same is
+ * true for the underlying resource strings, which are stored in
+ * mcc-specific xml files.)
+ */
+ Locale l = Locale.getDefault();
+ QuickSearch_G = getResources().getString(
+ R.string.google_search_base, l.getLanguage(),
+ l.getCountry().toLowerCase())
+ + "client=ms-"
+ + SystemProperties.get("persist.sys.com.google.clientid", "unknown")
+ + "&source=android-" + GOOGLE_SEARCH_SOURCE_SUGGEST + "&q=%s";
+ } else {
+ QuickSearch_G = url;
+ }
+ }
+
+ private final static int LOCK_ICON_UNSECURE = 0;
+ private final static int LOCK_ICON_SECURE = 1;
+ private final static int LOCK_ICON_MIXED = 2;
+
+ private int mLockIconType = LOCK_ICON_UNSECURE;
+ private int mPrevLockType = LOCK_ICON_UNSECURE;
+
+ private BrowserSettings mSettings;
+ private TabControl mTabControl;
+ private ContentResolver mResolver;
+ private FrameLayout mContentView;
+ private ImageGrid mTabOverview;
+
+ // FIXME, temp address onPrepareMenu performance problem. When we move everything out of
+ // view, we should rewrite this.
+ private int mCurrentMenuState = 0;
+ private int mMenuState = R.id.MAIN_MENU;
+ private static final int EMPTY_MENU = -1;
+ private Menu mMenu;
+
+ private FindDialog mFindDialog;
+ // Used to prevent chording to result in firing two shortcuts immediately
+ // one after another. Fixes bug 1211714.
+ boolean mCanChord;
+
+ private boolean mInLoad;
+ private boolean mIsNetworkUp;
+
+ private boolean mPageStarted;
+ private boolean mActivityInPause = true;
+
+ private boolean mMenuIsDown;
+
+ private final KeyTracker mKeyTracker = new KeyTracker(this);
+
+ // As trackball doesn't send repeat down, we have to track it ourselves
+ private boolean mTrackTrackball;
+
+ private static boolean mInTrace;
+
+ // Performance probe
+ private static final int[] SYSTEM_CPU_FORMAT = new int[] {
+ Process.PROC_SPACE_TERM | Process.PROC_COMBINE,
+ Process.PROC_SPACE_TERM | Process.PROC_OUT_LONG, // 1: user time
+ Process.PROC_SPACE_TERM | Process.PROC_OUT_LONG, // 2: nice time
+ Process.PROC_SPACE_TERM | Process.PROC_OUT_LONG, // 3: sys time
+ Process.PROC_SPACE_TERM | Process.PROC_OUT_LONG, // 4: idle time
+ Process.PROC_SPACE_TERM | Process.PROC_OUT_LONG, // 5: iowait time
+ Process.PROC_SPACE_TERM | Process.PROC_OUT_LONG, // 6: irq time
+ Process.PROC_SPACE_TERM | Process.PROC_OUT_LONG // 7: softirq time
+ };
+
+ private long mStart;
+ private long mProcessStart;
+ private long mUserStart;
+ private long mSystemStart;
+ private long mIdleStart;
+ private long mIrqStart;
+
+ private long mUiStart;
+
+ private Drawable mMixLockIcon;
+ private Drawable mSecLockIcon;
+ private Drawable mGenericFavicon;
+
+ /* hold a ref so we can auto-cancel if necessary */
+ private AlertDialog mAlertDialog;
+
+ // Wait for credentials before loading google.com
+ private ProgressDialog mCredsDlg;
+
+ // The up-to-date URL and title (these can be different from those stored
+ // in WebView, since it takes some time for the information in WebView to
+ // get updated)
+ private String mUrl;
+ private String mTitle;
+
+ // As PageInfo has different style for landscape / portrait, we have
+ // to re-open it when configuration changed
+ private AlertDialog mPageInfoDialog;
+ private TabControl.Tab mPageInfoView;
+ // If the Page-Info dialog is launched from the SSL-certificate-on-error
+ // dialog, we should not just dismiss it, but should get back to the
+ // SSL-certificate-on-error dialog. This flag is used to store this state
+ private Boolean mPageInfoFromShowSSLCertificateOnError;
+
+ // as SSLCertificateOnError has different style for landscape / portrait,
+ // we have to re-open it when configuration changed
+ private AlertDialog mSSLCertificateOnErrorDialog;
+ private WebView mSSLCertificateOnErrorView;
+ private SslErrorHandler mSSLCertificateOnErrorHandler;
+ private SslError mSSLCertificateOnErrorError;
+
+ // as SSLCertificate has different style for landscape / portrait, we
+ // have to re-open it when configuration changed
+ private AlertDialog mSSLCertificateDialog;
+ private TabControl.Tab mSSLCertificateView;
+
+ // as HttpAuthentication has different style for landscape / portrait, we
+ // have to re-open it when configuration changed
+ private AlertDialog mHttpAuthenticationDialog;
+ private HttpAuthHandler mHttpAuthHandler;
+
+ /*package*/ static final FrameLayout.LayoutParams COVER_SCREEN_PARAMS =
+ new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.FILL_PARENT);
+ // We may provide UI to customize these
+ // Google search from the browser
+ static String QuickSearch_G;
+ // Wikipedia search
+ final static String QuickSearch_W = "http://en.wikipedia.org/w/index.php?search=%s&go=Go";
+ // Dictionary search
+ final static String QuickSearch_D = "http://dictionary.reference.com/search?q=%s";
+ // Google Mobile Local search
+ final static String QuickSearch_L = "http://www.google.com/m/search?site=local&q=%s&near=mountain+view";
+
+ final static String QUERY_PLACE_HOLDER = "%s";
+
+ // "source" parameter for Google search through search key
+ final static String GOOGLE_SEARCH_SOURCE_SEARCHKEY = "browser-key";
+ // "source" parameter for Google search through goto menu
+ final static String GOOGLE_SEARCH_SOURCE_GOTO = "browser-goto";
+ // "source" parameter for Google search through simplily type
+ final static String GOOGLE_SEARCH_SOURCE_TYPE = "browser-type";
+ // "source" parameter for Google search suggested by the browser
+ final static String GOOGLE_SEARCH_SOURCE_SUGGEST = "browser-suggest";
+ // "source" parameter for Google search from unknown source
+ final static String GOOGLE_SEARCH_SOURCE_UNKNOWN = "unknown";
+
+ private final static String LOGTAG = "browser";
+
+ private TabListener mTabListener;
+
+ private String mLastEnteredUrl;
+
+ private PowerManager.WakeLock mWakeLock;
+ private final static int WAKELOCK_TIMEOUT = 5 * 60 * 1000; // 5 minutes
+
+ private Toast mStopToast;
+
+ // 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
+ // there are animations in progress.
+ private int mAnimationCount;
+
+ // As the ids are dynamically created, we can't guarantee that they will
+ // be in sequence, so this static array maps ids to a window number.
+ final static private int[] WINDOW_SHORTCUT_ID_ARRAY =
+ { R.id.window_one_menu_id, R.id.window_two_menu_id, R.id.window_three_menu_id,
+ R.id.window_four_menu_id, R.id.window_five_menu_id, R.id.window_six_menu_id,
+ R.id.window_seven_menu_id, R.id.window_eight_menu_id };
+
+ // monitor platform changes
+ private IntentFilter mNetworkStateChangedFilter;
+ private BroadcastReceiver mNetworkStateIntentReceiver;
+
+ // activity requestCode
+ final static int COMBO_PAGE = 1;
+ final static int DOWNLOAD_PAGE = 2;
+ final static int PREFERENCES_PAGE = 3;
+
+ // 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
new file mode 100644
index 00000000..479dc0ef
--- /dev/null
+++ b/src/com/android/browser/BrowserBookmarksAdapter.java
@@ -0,0 +1,535 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.browser;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.database.DataSetObserver;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.Browser;
+import android.provider.Browser.BookmarkColumns;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.WebIconDatabase;
+import android.webkit.WebIconDatabase.IconListener;
+import android.widget.BaseAdapter;
+
+import java.io.ByteArrayOutputStream;
+
+class BrowserBookmarksAdapter extends BaseAdapter {
+
+ private final String LOGTAG = "Bookmarks";
+
+ private String mCurrentPage;
+ private Cursor mCursor;
+ private int mCount;
+ private String mLastWhereClause;
+ private String[] mLastSelectionArgs;
+ private String mLastOrderBy;
+ private BrowserBookmarksPage mBookmarksPage;
+ private ContentResolver mContentResolver;
+ private ChangeObserver mChangeObserver;
+ private DataSetObserver mDataSetObserver;
+ private boolean mDataValid;
+
+ // When true, this adapter is used to pick a bookmark to create a shortcut
+ private boolean mCreateShortcut;
+ private int mExtraOffset;
+
+ // Implementation of WebIconDatabase.IconListener
+ private class IconReceiver implements IconListener {
+ public void onReceivedIcon(String url, Bitmap icon) {
+ updateBookmarkFavicon(mContentResolver, url, icon);
+ }
+ }
+
+ // Instance of IconReceiver
+ private final IconReceiver mIconReceiver = new IconReceiver();
+
+ /**
+ * Create a new BrowserBookmarksAdapter.
+ * @param b BrowserBookmarksPage that instantiated this.
+ * Necessary so it will adjust its focus
+ * appropriately after a search.
+ */
+ public BrowserBookmarksAdapter(BrowserBookmarksPage b, String curPage) {
+ this(b, curPage, false);
+ }
+
+ /**
+ * Create a new BrowserBookmarksAdapter.
+ * @param b BrowserBookmarksPage that instantiated this.
+ * Necessary so it will adjust its focus
+ * appropriately after a search.
+ */
+ public BrowserBookmarksAdapter(BrowserBookmarksPage b, String curPage,
+ boolean createShortcut) {
+ mDataValid = false;
+ mCreateShortcut = createShortcut;
+ mExtraOffset = createShortcut ? 0 : 1;
+ mBookmarksPage = b;
+ mCurrentPage = b.getResources().getString(R.string.current_page) +
+ curPage;
+ mContentResolver = b.getContentResolver();
+ mLastOrderBy = Browser.BookmarkColumns.CREATED + " DESC";
+ mChangeObserver = new ChangeObserver();
+ mDataSetObserver = new MyDataSetObserver();
+ // FIXME: Should have a default sort order that the user selects.
+ search(null);
+ // FIXME: This requires another query of the database after the
+ // initial search(null). Can we optimize this?
+ Browser.requestAllIcons(mContentResolver,
+ Browser.BookmarkColumns.FAVICON + " is NULL AND " +
+ Browser.BookmarkColumns.BOOKMARK + " == 1", mIconReceiver);
+ }
+
+ /**
+ * Return a hashmap with one row's Title, Url, and favicon.
+ * @param position Position in the list.
+ * @return Bundle Stores title, url of row position, favicon, and id
+ * for the url. Return a blank map if position is out of
+ * range.
+ */
+ public Bundle getRow(int position) {
+ Bundle map = new Bundle();
+ if (position < mExtraOffset || position >= mCount) {
+ return map;
+ }
+ mCursor.moveToPosition(position- mExtraOffset);
+ String url = mCursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX);
+ map.putString(Browser.BookmarkColumns.TITLE,
+ mCursor.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX));
+ map.putString(Browser.BookmarkColumns.URL, url);
+ byte[] data = mCursor.getBlob(Browser.HISTORY_PROJECTION_FAVICON_INDEX);
+ if (data != null) {
+ map.putParcelable(Browser.BookmarkColumns.FAVICON,
+ BitmapFactory.decodeByteArray(data, 0, data.length));
+ }
+ map.putInt("id", mCursor.getInt(Browser.HISTORY_PROJECTION_ID_INDEX));
+ return map;
+ }
+
+ /**
+ * Update a row in the database with new information.
+ * Requeries the database if the information has changed.
+ * @param map Bundle storing id, title and url of new information
+ */
+ public void updateRow(Bundle map) {
+
+ // Find the record
+ int id = map.getInt("id");
+ int position = -1;
+ for (mCursor.moveToFirst(); !mCursor.isAfterLast(); mCursor.moveToNext()) {
+ if (mCursor.getInt(Browser.HISTORY_PROJECTION_ID_INDEX) == id) {
+ position = mCursor.getPosition();
+ break;
+ }
+ }
+ if (position < 0) {
+ return;
+ }
+
+ mCursor.moveToPosition(position);
+ ContentValues values = new ContentValues();
+ String title = map.getString(Browser.BookmarkColumns.TITLE);
+ if (!title.equals(mCursor
+ .getString(Browser.HISTORY_PROJECTION_TITLE_INDEX))) {
+ values.put(Browser.BookmarkColumns.TITLE, title);
+ }
+ String url = map.getString(Browser.BookmarkColumns.URL);
+ if (!url.equals(mCursor.
+ getString(Browser.HISTORY_PROJECTION_URL_INDEX))) {
+ values.put(Browser.BookmarkColumns.URL, url);
+ }
+ if (values.size() > 0
+ && mContentResolver.update(Browser.BOOKMARKS_URI, values,
+ "_id = " + id, null) != -1) {
+ refreshList();
+ }
+ }
+
+ /**
+ * Delete a row from the database. Requeries the database.
+ * Does nothing if the provided position is out of range.
+ * @param position Position in the list.
+ */
+ public void deleteRow(int position) {
+ if (position < mExtraOffset || position >= getCount()) {
+ return;
+ }
+ 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);
+ }
+ refreshList();
+ }
+
+ /**
+ * Delete all bookmarks from the db. Requeries the database.
+ * All bookmarks with become visited URLs or if never visited
+ * are removed
+ */
+ public void deleteAllRows() {
+ StringBuilder deleteIds = null;
+ StringBuilder convertIds = null;
+
+ for (mCursor.moveToFirst(); !mCursor.isAfterLast(); mCursor.moveToNext()) {
+ String url = mCursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX);
+ WebIconDatabase.getInstance().releaseIconForPageUrl(url);
+ int id = mCursor.getInt(Browser.HISTORY_PROJECTION_ID_INDEX);
+ int numVisits = mCursor.getInt(Browser.HISTORY_PROJECTION_VISITS_INDEX);
+ if (0 == numVisits) {
+ if (deleteIds == null) {
+ deleteIds = new StringBuilder();
+ deleteIds.append("( ");
+ } else {
+ deleteIds.append(" OR ( ");
+ }
+ deleteIds.append(BookmarkColumns._ID);
+ deleteIds.append(" = ");
+ deleteIds.append(id);
+ deleteIds.append(" )");
+ } else {
+ // It is no longer a bookmark, but it is still a visited site.
+ if (convertIds == null) {
+ convertIds = new StringBuilder();
+ convertIds.append("( ");
+ } else {
+ convertIds.append(" OR ( ");
+ }
+ convertIds.append(BookmarkColumns._ID);
+ convertIds.append(" = ");
+ convertIds.append(id);
+ convertIds.append(" )");
+ }
+ }
+
+ if (deleteIds != null) {
+ mContentResolver.delete(Browser.BOOKMARKS_URI, deleteIds.toString(),
+ null);
+ }
+ if (convertIds != null) {
+ ContentValues values = new ContentValues();
+ values.put(Browser.BookmarkColumns.BOOKMARK, 0);
+ mContentResolver.update(Browser.BOOKMARKS_URI, values,
+ convertIds.toString(), null);
+ }
+ refreshList();
+ }
+
+ /**
+ * Refresh list to recognize a change in the database.
+ */
+ public void refreshList() {
+ // FIXME: consider using requery().
+ // Need to do more work to get it to function though.
+ searchInternal(mLastWhereClause, mLastSelectionArgs, mLastOrderBy);
+ }
+
+ /**
+ * Search the database for bookmarks that match the input string.
+ * @param like String to use to search the database. Strings with spaces
+ * are treated as having multiple search terms using the
+ * OR operator. Search both the title and url.
+ */
+ public void search(String like) {
+ String whereClause = Browser.BookmarkColumns.BOOKMARK + " == 1";
+ String[] selectionArgs = null;
+ if (like != null) {
+ String[] likes = like.split(" ");
+ int count = 0;
+ boolean firstTerm = true;
+ StringBuilder andClause = new StringBuilder(256);
+ for (int j = 0; j < likes.length; j++) {
+ if (likes[j].length() > 0) {
+ if (firstTerm) {
+ firstTerm = false;
+ } else {
+ andClause.append(" OR ");
+ }
+ andClause.append(Browser.BookmarkColumns.TITLE
+ + " LIKE ? OR " + Browser.BookmarkColumns.URL
+ + " LIKE ? ");
+ count += 2;
+ }
+ }
+ if (count > 0) {
+ selectionArgs = new String[count];
+ count = 0;
+ for (int j = 0; j < likes.length; j++) {
+ if (likes[j].length() > 0) {
+ like = "%" + likes[j] + "%";
+ selectionArgs[count++] = like;
+ selectionArgs[count++] = like;
+ }
+ }
+ whereClause += " AND (" + andClause + ")";
+ }
+ }
+ searchInternal(whereClause, selectionArgs, mLastOrderBy);
+ }
+
+ /**
+ * Update the bookmark's favicon.
+ * @param cr The ContentResolver to use.
+ * @param url The url of the bookmark to update.
+ * @param favicon The favicon bitmap to write to the db.
+ */
+ /* package */ static void updateBookmarkFavicon(ContentResolver cr,
+ String url, Bitmap favicon) {
+ if (url == null || favicon == null) {
+ return;
+ }
+ // Strip the query.
+ int query = url.indexOf('?');
+ String noQuery = url;
+ if (query != -1) {
+ noQuery = url.substring(0, query);
+ }
+ url = noQuery + '?';
+ // Use noQuery to search for the base url (i.e. if the url is
+ // http://www.yahoo.com/?rs=1, search for http://www.yahoo.com)
+ // Use url to match the base url with other queries (i.e. if the url is
+ // http://www.google.com/m, search for
+ // http://www.google.com/m?some_query)
+ final String[] selArgs = new String[] { noQuery, url };
+ final String where = "(" + Browser.BookmarkColumns.URL + " == ? OR "
+ + Browser.BookmarkColumns.URL + " GLOB ? || '*') AND "
+ + Browser.BookmarkColumns.BOOKMARK + " == 1";
+ final String[] projection = new String[] { Browser.BookmarkColumns._ID };
+ 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();
+ favicon.compress(Bitmap.CompressFormat.PNG, 100, os);
+ values = new ContentValues();
+ values.put(Browser.BookmarkColumns.FAVICON, os.toByteArray());
+ }
+ cr.update(ContentUris.withAppendedId(Browser.BOOKMARKS_URI, c
+ .getInt(0)), values, null, null);
+ succeed = c.moveToNext();
+ }
+ c.close();
+ }
+
+ /**
+ * This sorts alphabetically, with non-capitalized titles before
+ * capitalized.
+ */
+ public void sortAlphabetical() {
+ searchInternal(mLastWhereClause, mLastSelectionArgs,
+ Browser.BookmarkColumns.TITLE + " COLLATE UNICODE ASC");
+ }
+
+ /**
+ * Internal function used in search, sort, and refreshList.
+ */
+ private void searchInternal(String whereClause, String[] selectionArgs,
+ String orderBy) {
+ if (mCursor != null) {
+ mCursor.unregisterContentObserver(mChangeObserver);
+ mCursor.unregisterDataSetObserver(mDataSetObserver);
+ mBookmarksPage.stopManagingCursor(mCursor);
+ mCursor.deactivate();
+ }
+
+ mLastWhereClause = whereClause;
+ mLastSelectionArgs = selectionArgs;
+ mLastOrderBy = orderBy;
+ mCursor = mContentResolver.query(
+ Browser.BOOKMARKS_URI,
+ Browser.HISTORY_PROJECTION,
+ whereClause,
+ selectionArgs,
+ orderBy);
+ mCursor.registerContentObserver(mChangeObserver);
+ mCursor.registerDataSetObserver(mDataSetObserver);
+ mBookmarksPage.startManagingCursor(mCursor);
+
+ mDataValid = true;
+ notifyDataSetChanged();
+
+ mCount = mCursor.getCount() + mExtraOffset;
+ }
+
+ /**
+ * How many items should be displayed in the list.
+ * @return Count of items.
+ */
+ public int getCount() {
+ if (mDataValid) {
+ return mCount;
+ } else {
+ return 0;
+ }
+ }
+
+ public boolean areAllItemsEnabled() {
+ return true;
+ }
+
+ public boolean isEnabled(int position) {
+ return true;
+ }
+
+ /**
+ * Get the data associated with the specified position in the list.
+ * @param position Index of the item whose data we want.
+ * @return The data at the specified position.
+ */
+ public Object getItem(int position) {
+ return null;
+ }
+
+ /**
+ * Get the row id associated with the specified position in the list.
+ * @param position Index of the item whose row id we want.
+ * @return The id of the item at the specified position.
+ */
+ public long getItemId(int position) {
+ return position;
+ }
+
+ /**
+ * Get a View that displays the data at the specified position
+ * in the list.
+ * @param position Index of the item whose view we want.
+ * @return A View corresponding to the data at the specified position.
+ */
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (!mDataValid) {
+ throw new IllegalStateException(
+ "this should only be called when the cursor is valid");
+ }
+ if (position < 0 || position > mCount) {
+ throw new AssertionError(
+ "BrowserBookmarksAdapter tried to get a view out of range");
+ }
+ if (position == 0 && !mCreateShortcut) {
+ AddNewBookmark b;
+ if (convertView instanceof AddNewBookmark) {
+ b = (AddNewBookmark) convertView;
+ } else {
+ b = new AddNewBookmark(mBookmarksPage);
+ }
+ b.setUrl(mCurrentPage);
+ return b;
+ }
+ if (convertView == null || convertView instanceof AddNewBookmark) {
+ convertView = new BookmarkItem(mBookmarksPage);
+ }
+ bind((BookmarkItem)convertView, position);
+ return convertView;
+ }
+
+ /**
+ * Return the title for this item in the list.
+ */
+ public String getTitle(int position) {
+ return getString(Browser.HISTORY_PROJECTION_TITLE_INDEX, position);
+ }
+
+ /**
+ * Return the Url for this item in the list.
+ */
+ public String getUrl(int position) {
+ return getString(Browser.HISTORY_PROJECTION_URL_INDEX, position);
+ }
+
+ /**
+ * Private helper function to return the title or url.
+ */
+ private String getString(int cursorIndex, int position) {
+ if (position < mExtraOffset || position > mCount) {
+ return "";
+ }
+ mCursor.moveToPosition(position- mExtraOffset);
+ return mCursor.getString(cursorIndex);
+ }
+
+ private void bind(BookmarkItem b, int position) {
+ mCursor.moveToPosition(position- mExtraOffset);
+
+ String title = mCursor.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX);
+ if (title.length() > BrowserSettings.MAX_TEXTVIEW_LEN) {
+ title = title.substring(0, BrowserSettings.MAX_TEXTVIEW_LEN);
+ }
+ b.setName(title);
+ String url = mCursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX);
+ if (url.length() > BrowserSettings.MAX_TEXTVIEW_LEN) {
+ url = url.substring(0, BrowserSettings.MAX_TEXTVIEW_LEN);
+ }
+ b.setUrl(url);
+ byte[] data = mCursor.getBlob(Browser.HISTORY_PROJECTION_FAVICON_INDEX);
+ if (data != null) {
+ b.setFavicon(BitmapFactory.decodeByteArray(data, 0, data.length));
+ } else {
+ b.setFavicon(null);
+ }
+ }
+
+ private class ChangeObserver extends ContentObserver {
+ public ChangeObserver() {
+ super(new Handler());
+ }
+
+ @Override
+ public boolean deliverSelfNotifications() {
+ return true;
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ refreshList();
+ }
+ }
+
+ private class MyDataSetObserver extends DataSetObserver {
+ @Override
+ public void onChanged() {
+ mDataValid = true;
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public void onInvalidated() {
+ mDataValid = false;
+ notifyDataSetInvalidated();
+ }
+ }
+}
diff --git a/src/com/android/browser/BrowserBookmarksPage.java b/src/com/android/browser/BrowserBookmarksPage.java
new file mode 100644
index 00000000..7708e8bf
--- /dev/null
+++ b/src/com/android/browser/BrowserBookmarksPage.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.browser;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.ServiceManager;
+import android.provider.Browser;
+import android.text.IClipboard;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.widget.AdapterView;
+import android.widget.ListView;
+
+/**
+ * View showing the user's bookmarks in the browser.
+ */
+public class BrowserBookmarksPage extends Activity implements
+ View.OnCreateContextMenuListener {
+
+ private BrowserBookmarksAdapter mBookmarksAdapter;
+ private static final int BOOKMARKS_SAVE = 1;
+ private boolean mMaxTabsOpen;
+ private BookmarkItem mContextHeader;
+ private AddNewBookmark mAddHeader;
+ private boolean mCanceled = false;
+ private boolean mCreateShortcut;
+ // XXX: There is no public string defining this intent so if Home changes
+ // the value, we have to update this string.
+ private static final String INSTALL_SHORTCUT =
+ "com.android.launcher.action.INSTALL_SHORTCUT";
+
+ private final static String LOGTAG = "browser";
+
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ // It is possible that the view has been canceled when we get to
+ // this point as back has a higher priority
+ if (mCanceled) {
+ return true;
+ }
+ AdapterView.AdapterContextMenuInfo i =
+ (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
+ // If we have no menu info, we can't tell which item was selected.
+ if (i == null) {
+ return true;
+ }
+
+ switch (item.getItemId()) {
+ case R.id.new_context_menu_id:
+ saveCurrentPage();
+ break;
+ case R.id.open_context_menu_id:
+ loadUrl(i.position);
+ break;
+ case R.id.edit_context_menu_id:
+ editBookmark(i.position);
+ break;
+ case R.id.shortcut_context_menu_id:
+ final Intent send = createShortcutIntent(getUrl(i.position),
+ getBookmarkTitle(i.position));
+ send.setAction(INSTALL_SHORTCUT);
+ sendBroadcast(send);
+ break;
+ case R.id.delete_context_menu_id:
+ displayRemoveBookmarkDialog(i.position);
+ break;
+ case R.id.new_window_context_menu_id:
+ openInNewWindow(i.position);
+ break;
+ case R.id.send_context_menu_id:
+ Browser.sendString(BrowserBookmarksPage.this, getUrl(i.position));
+ break;
+ case R.id.copy_url_context_menu_id:
+ copy(getUrl(i.position));
+
+ default:
+ return super.onContextItemSelected(item);
+ }
+ return true;
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenuInfo menuInfo) {
+ AdapterView.AdapterContextMenuInfo i =
+ (AdapterView.AdapterContextMenuInfo) menuInfo;
+
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.bookmarkscontext, menu);
+
+ if (0 == i.position) {
+ menu.setGroupVisible(R.id.CONTEXT_MENU, false);
+ if (mAddHeader == null) {
+ mAddHeader = new AddNewBookmark(BrowserBookmarksPage.this);
+ } else if (mAddHeader.getParent() != null) {
+ ((ViewGroup) mAddHeader.getParent()).
+ removeView(mAddHeader);
+ }
+ ((AddNewBookmark) i.targetView).copyTo(mAddHeader);
+ menu.setHeaderView(mAddHeader);
+ return;
+ }
+ menu.setGroupVisible(R.id.ADD_MENU, false);
+ BookmarkItem b = (BookmarkItem) i.targetView;
+ if (mContextHeader == null) {
+ mContextHeader = new BookmarkItem(BrowserBookmarksPage.this);
+ } else if (mContextHeader.getParent() != null) {
+ ((ViewGroup) mContextHeader.getParent()).
+ removeView(mContextHeader);
+ }
+ b.copyTo(mContextHeader);
+ menu.setHeaderView(mContextHeader);
+
+ if (mMaxTabsOpen) {
+ menu.findItem(R.id.new_window_context_menu_id).setVisible(
+ false);
+ }
+ }
+
+ /**
+ * Create a new BrowserBookmarksPage.
+ */
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ setContentView(R.layout.browser_bookmarks_page);
+ setTitle(R.string.browser_bookmarks_page_bookmarks_text);
+
+ if (Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction())) {
+ mCreateShortcut = true;
+ }
+
+ mBookmarksAdapter = new BrowserBookmarksAdapter(this,
+ getIntent().getStringExtra("url"), mCreateShortcut);
+ mMaxTabsOpen = getIntent().getBooleanExtra("maxTabsOpen", false);
+
+ ListView listView = (ListView) findViewById(R.id.list);
+ listView.setAdapter(mBookmarksAdapter);
+ listView.setDrawSelectorOnTop(false);
+ listView.setVerticalScrollBarEnabled(true);
+ listView.setOnItemClickListener(mListener);
+
+ if (!mCreateShortcut) {
+ listView.setOnCreateContextMenuListener(this);
+ }
+ }
+
+ private static final int SAVE_CURRENT_PAGE = 1000;
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == SAVE_CURRENT_PAGE) {
+ saveCurrentPage();
+ }
+ }
+ };
+
+ private AdapterView.OnItemClickListener mListener = new AdapterView.OnItemClickListener() {
+ public void onItemClick(AdapterView parent, View v, int position, long id) {
+ // It is possible that the view has been canceled when we get to
+ // this point as back has a higher priority
+ if (mCanceled) {
+ android.util.Log.e("browser", "item clicked when dismising");
+ return;
+ }
+ if (!mCreateShortcut) {
+ if (0 == position) {
+ // XXX: Work-around for a framework issue.
+ mHandler.sendEmptyMessage(SAVE_CURRENT_PAGE);
+ } else {
+ loadUrl(position);
+ }
+ } else {
+ final Intent intent = createShortcutIntent(getUrl(position),
+ getBookmarkTitle(position));
+ setResultToParent(RESULT_OK, intent);
+ finish();
+ }
+ }
+ };
+
+ private Intent createShortcutIntent(String url, String title) {
+ final Intent i = new Intent();
+ i.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent(Intent.ACTION_VIEW,
+ Uri.parse(url)));
+ i.putExtra(Intent.EXTRA_SHORTCUT_NAME, title);
+ i.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
+ Intent.ShortcutIconResource.fromContext(BrowserBookmarksPage.this,
+ R.drawable.ic_launcher_browser));
+ // Do not allow duplicate items
+ i.putExtra("duplicate", false);
+ return i;
+ }
+
+ private void saveCurrentPage() {
+ Intent i = new Intent(BrowserBookmarksPage.this,
+ AddBookmarkPage.class);
+ i.putExtras(getIntent());
+ startActivityForResult(i, BOOKMARKS_SAVE);
+ }
+
+ private void loadUrl(int position) {
+ Intent intent = (new Intent()).setAction(getUrl(position));
+ setResultToParent(RESULT_OK, intent);
+ finish();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ boolean result = super.onCreateOptionsMenu(menu);
+ if (!mCreateShortcut) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.bookmarks, menu);
+ return true;
+ }
+ return result;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.new_context_menu_id:
+ saveCurrentPage();
+ break;
+
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ return true;
+ }
+
+ private void openInNewWindow(int position) {
+ Bundle b = new Bundle();
+ b.putBoolean("new_window", true);
+ setResultToParent(RESULT_OK,
+ (new Intent()).setAction(getUrl(position)).putExtras(b));
+
+ finish();
+ }
+
+
+ private void editBookmark(int position) {
+ Intent intent = new Intent(BrowserBookmarksPage.this,
+ AddBookmarkPage.class);
+ intent.putExtra("bookmark", getRow(position));
+ startActivityForResult(intent, BOOKMARKS_SAVE);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode,
+ Intent data) {
+ switch(requestCode) {
+ case BOOKMARKS_SAVE:
+ if (resultCode == RESULT_OK) {
+ Bundle extras;
+ if (data != null && (extras = data.getExtras()) != null) {
+ // If there are extras, then we need to save
+ // the edited bookmark. This is done in updateRow()
+ String title = extras.getString("title");
+ String url = extras.getString("url");
+ if (title != null && url != null) {
+ mBookmarksAdapter.updateRow(extras);
+ }
+ } else {
+ // extras == null then a new bookmark was added to
+ // the database.
+ refreshList();
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void displayRemoveBookmarkDialog(int position) {
+ // Put up a dialog asking if the user really wants to
+ // delete the bookmark
+ final int deletePos = position;
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.delete_bookmark)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setMessage(getText(R.string.delete_bookmark_warning).toString().replace(
+ "%s", getBookmarkTitle(deletePos)))
+ .setPositiveButton(R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ deleteBookmark(deletePos);
+ }
+ })
+ .setNegativeButton(R.string.cancel, null)
+ .show();
+ }
+
+ /**
+ * Refresh the shown list after the database has changed.
+ */
+ public void refreshList() {
+ mBookmarksAdapter.refreshList();
+ }
+
+ /**
+ * Return a hashmap representing the currently highlighted row.
+ */
+ public Bundle getRow(int position) {
+ return mBookmarksAdapter.getRow(position);
+ }
+
+ /**
+ * Return the url of the currently highlighted row.
+ */
+ public String getUrl(int position) {
+ return mBookmarksAdapter.getUrl(position);
+ }
+
+ private void copy(CharSequence text) {
+ try {
+ IClipboard clip = IClipboard.Stub.asInterface(ServiceManager.getService("clipboard"));
+ if (clip != null) {
+ clip.setClipboardText(text);
+ }
+ } catch (android.os.RemoteException e) {
+ Log.e(LOGTAG, "Copy failed", e);
+ }
+ }
+
+ public String getBookmarkTitle(int position) {
+ return mBookmarksAdapter.getTitle(position);
+ }
+
+ /**
+ * Delete the currently highlighted row.
+ */
+ public void deleteBookmark(int position) {
+ mBookmarksAdapter.deleteRow(position);
+ }
+
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.isDown()) {
+ setResultToParent(RESULT_CANCELED, null);
+ mCanceled = true;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ // This Activity is generally a sub-Activity of CombinedHistoryActivity. In
+ // that situation, we need to pass our result code up to our parent.
+ // However, if someone calls this Activity directly, then this has no
+ // parent, and it needs to set it on itself.
+ private void setResultToParent(int resultCode, Intent data) {
+ Activity a = getParent() == null ? this : getParent();
+ a.setResult(resultCode, data);
+ }
+}
diff --git a/src/com/android/browser/BrowserDownloadAdapter.java b/src/com/android/browser/BrowserDownloadAdapter.java
new file mode 100644
index 00000000..38b83fe5
--- /dev/null
+++ b/src/com/android/browser/BrowserDownloadAdapter.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2007 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.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.drm.mobile1.DrmRawContent;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.provider.Downloads;
+import android.text.format.Formatter;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.ResourceCursorAdapter;
+import android.widget.TextView;
+
+import java.io.File;
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * This class is used to represent the data for the download list box. The only
+ * real work done by this class is to construct a custom view for the line
+ * items.
+ */
+public class BrowserDownloadAdapter extends ResourceCursorAdapter {
+
+ private int mFilenameColumnId;
+ private int mTitleColumnId;
+ private int mDescColumnId;
+ private int mStatusColumnId;
+ private int mTotalBytesColumnId;
+ private int mCurrentBytesColumnId;
+ private int mMimetypeColumnId;
+ private int mDateColumnId;
+
+ 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);
+ mCurrentBytesColumnId =
+ c.getColumnIndexOrThrow(Downloads.CURRENT_BYTES);
+ mMimetypeColumnId = c.getColumnIndexOrThrow(Downloads.MIMETYPE);
+ mDateColumnId = c.getColumnIndexOrThrow(Downloads.LAST_MODIFICATION);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ Resources r = context.getResources();
+
+ // Retrieve the icon for this download
+ String mimeType = cursor.getString(mMimetypeColumnId);
+ ImageView iv = (ImageView) view.findViewById(R.id.download_icon);
+ if (DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING.equalsIgnoreCase(mimeType)) {
+ iv.setImageResource(R.drawable.ic_launcher_drm_file);
+ } else if (mimeType == null) {
+ iv.setVisibility(View.INVISIBLE);
+ } else {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setDataAndType(Uri.fromParts("file", "", null), mimeType);
+ PackageManager pm = context.getPackageManager();
+ List<ResolveInfo> list = pm.queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ if (list.size() > 0) {
+ Drawable icon = list.get(0).activityInfo.loadIcon(pm);
+ iv.setImageDrawable(icon);
+ iv.setVisibility(View.VISIBLE);
+ } else {
+ iv.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ TextView tv = (TextView) view.findViewById(R.id.download_title);
+ String title = cursor.getString(mTitleColumnId);
+ if (title == null) {
+ String fullFilename = cursor.getString(mFilenameColumnId);
+ if (fullFilename == null) {
+ title = r.getString(R.string.download_unknown_filename);
+ } else {
+ // We have a filename, so we can build a title from that
+ title = new File(fullFilename).getName();
+ ContentValues values = new ContentValues();
+ values.put(Downloads.TITLE, title);
+ // assume "_id" is the first column for the cursor
+ context.getContentResolver().update(
+ ContentUris.withAppendedId(Downloads.CONTENT_URI,
+ cursor.getLong(0)), values, null, null);
+ }
+ }
+ tv.setText(title);
+
+ tv = (TextView) view.findViewById(R.id.domain);
+ tv.setText(cursor.getString(mDescColumnId));
+
+ long totalBytes = cursor.getLong(mTotalBytesColumnId);
+
+ int status = cursor.getInt(mStatusColumnId);
+ if (Downloads.isStatusCompleted(status)) { // Download stopped
+ View v = view.findViewById(R.id.progress_text);
+ v.setVisibility(View.GONE);
+
+ v = view.findViewById(R.id.download_progress);
+ v.setVisibility(View.GONE);
+
+ tv = (TextView) view.findViewById(R.id.complete_text);
+ tv.setVisibility(View.VISIBLE);
+ if (Downloads.isStatusError(status)) {
+ tv.setText(getErrorText(status));
+ } else {
+ tv.setText(r.getString(R.string.download_success,
+ Formatter.formatFileSize(mContext, totalBytes)));
+ }
+
+ long time = cursor.getLong(mDateColumnId);
+ Date d = new Date(time);
+ DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
+ tv = (TextView) view.findViewById(R.id.complete_date);
+ tv.setVisibility(View.VISIBLE);
+ tv.setText(df.format(d));
+
+ } else { // Download is still running
+ tv = (TextView) view.findViewById(R.id.progress_text);
+ tv.setVisibility(View.VISIBLE);
+
+ View progress = view.findViewById(R.id.download_progress);
+ progress.setVisibility(View.VISIBLE);
+
+ View v = view.findViewById(R.id.complete_date);
+ v.setVisibility(View.GONE);
+
+ v = view.findViewById(R.id.complete_text);
+ v.setVisibility(View.GONE);
+
+ if (status == Downloads.STATUS_PENDING) {
+ tv.setText(r.getText(R.string.download_pending));
+ } else if (status == Downloads.STATUS_PENDING_PAUSED) {
+ tv.setText(r.getText(R.string.download_pending_network));
+ } else {
+ ProgressBar pb = (ProgressBar) progress;
+
+ StringBuilder sb = new StringBuilder();
+ if (status == Downloads.STATUS_RUNNING) {
+ sb.append(r.getText(R.string.download_running));
+ } else {
+ sb.append(r.getText(R.string.download_running_paused));
+ }
+ if (totalBytes > 0) {
+ long currentBytes = cursor.getLong(mCurrentBytesColumnId);
+ int progressAmount = (int)(currentBytes * 100 / totalBytes);
+ sb.append(' ');
+ sb.append(progressAmount);
+ sb.append("% (");
+ sb.append(Formatter.formatFileSize(mContext, currentBytes));
+ sb.append("/");
+ sb.append(Formatter.formatFileSize(mContext, totalBytes));
+ sb.append(")");
+ pb.setIndeterminate(false);
+ pb.setProgress(progressAmount);
+ } else {
+ pb.setIndeterminate(true);
+ }
+ tv.setText(sb.toString());
+ }
+ }
+
+ }
+
+ /**
+ * Provide the resource id for the error string.
+ * @param status status of the download item
+ * @return resource id for the error string.
+ */
+ public static int getErrorText(int status) {
+ switch (status) {
+ case Downloads.STATUS_NOT_ACCEPTABLE:
+ return R.string.download_not_acceptable;
+
+ case Downloads.STATUS_LENGTH_REQUIRED:
+ return R.string.download_length_required;
+
+ case Downloads.STATUS_PRECONDITION_FAILED:
+ return R.string.download_precondition_failed;
+
+ case Downloads.STATUS_CANCELED:
+ return R.string.download_canceled;
+
+ case Downloads.STATUS_FILE_ERROR:
+ return R.string.download_file_error;
+
+ case Downloads.STATUS_BAD_REQUEST:
+ case Downloads.STATUS_UNKNOWN_ERROR:
+ default:
+ return R.string.download_error;
+ }
+ }
+}
diff --git a/src/com/android/browser/BrowserDownloadPage.java b/src/com/android/browser/BrowserDownloadPage.java
new file mode 100644
index 00000000..4397337b
--- /dev/null
+++ b/src/com/android/browser/BrowserDownloadPage.java
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2007 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.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.ContentValues;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.ContentUris;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Downloads;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MenuInflater;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.AdapterView;
+import android.widget.ListView;
+import android.widget.AdapterView.OnItemClickListener;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * View showing the user's current browser downloads
+ */
+public class BrowserDownloadPage extends Activity
+ implements View.OnCreateContextMenuListener, OnItemClickListener {
+
+ private ListView mListView;
+ private Cursor mDownloadCursor;
+ private BrowserDownloadAdapter mDownloadAdapter;
+ private int mStatusColumnId;
+ private int mIdColumnId;
+ private int mTitleColumnId;
+ private int mContextMenuPosition;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.browser_downloads_page);
+
+ 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);
+
+ 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},
+ 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);
+ mIdColumnId =
+ mDownloadCursor.getColumnIndexOrThrow(Downloads._ID);
+ mTitleColumnId =
+ mDownloadCursor.getColumnIndexOrThrow(Downloads.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);
+ mListView.setOnItemClickListener(this);
+
+ Intent intent = getIntent();
+ if (intent != null && intent.getData() != null) {
+ int position = checkStatus(
+ ContentUris.parseId(intent.getData()));
+ if (position >= 0) {
+ mListView.setSelection(position);
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ if (mDownloadCursor != null) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.downloadhistory, menu);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ boolean showCancel = getCancelableCount() > 0;
+ menu.findItem(R.id.download_menu_cancel_all).setEnabled(showCancel);
+
+ boolean showClear = getClearableCount() > 0;
+ menu.findItem(R.id.download_menu_clear_all).setEnabled(showClear);
+ return super.onPrepareOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.download_menu_cancel_all:
+ promptCancelAll();
+ return true;
+
+ case R.id.download_menu_clear_all:
+ promptClearList();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ mDownloadCursor.moveToPosition(mContextMenuPosition);
+ switch (item.getItemId()) {
+ case R.id.download_menu_open:
+ hideCompletedDownload();
+ openCurrentDownload();
+ return true;
+
+ case R.id.download_menu_clear:
+ case R.id.download_menu_cancel:
+ getContentResolver().delete(
+ ContentUris.withAppendedId(Downloads.CONTENT_URI,
+ mDownloadCursor.getLong(mIdColumnId)), null, null);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenuInfo menuInfo) {
+ if (mDownloadCursor != null) {
+ AdapterView.AdapterContextMenuInfo info =
+ (AdapterView.AdapterContextMenuInfo) menuInfo;
+ mDownloadCursor.moveToPosition(info.position);
+ mContextMenuPosition = info.position;
+ menu.setHeaderTitle(mDownloadCursor.getString(mTitleColumnId));
+
+ MenuInflater inflater = getMenuInflater();
+ int status = mDownloadCursor.getInt(mStatusColumnId);
+ if (Downloads.isStatusSuccess(status)) {
+ inflater.inflate(R.menu.downloadhistorycontextfinished, menu);
+ } else if (Downloads.isStatusError(status)) {
+ inflater.inflate(R.menu.downloadhistorycontextfailed, menu);
+ } else {
+ inflater.inflate(R.menu.downloadhistorycontextrunning, menu);
+ }
+ }
+ }
+
+ /**
+ * This function is called to check the status of the download and if it
+ * has an error show an error dialog.
+ * @param id Row id of the download to check
+ * @return position of item
+ */
+ int checkStatus(final long id) {
+ int position = -1;
+ for (mDownloadCursor.moveToFirst(); !mDownloadCursor.isAfterLast();
+ mDownloadCursor.moveToNext()) {
+ if (id == mDownloadCursor.getLong(mIdColumnId)) {
+ position = mDownloadCursor.getPosition();
+ break;
+ }
+
+ }
+ if (!mDownloadCursor.isAfterLast()) {
+ int status = mDownloadCursor.getInt(mStatusColumnId);
+ if (!Downloads.isStatusError(status)) {
+ return position;
+ }
+
+ if (status == Downloads.STATUS_FILE_ERROR) {
+ String title = mDownloadCursor.getString(mTitleColumnId);
+ if (title == null || title.length() == 0) {
+ title = getString(R.string.download_unknown_filename);
+ }
+ String msg = getString(R.string.download_file_error_dlg_msg,
+ title);
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.download_file_error_dlg_title)
+ .setIcon(android.R.drawable.ic_popup_disk_full)
+ .setMessage(msg)
+ .setPositiveButton(R.string.ok, null)
+ .setNegativeButton(R.string.retry,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int whichButton) {
+ resumeDownload(id);
+ }
+ })
+ .show();
+ } else {
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.download_failed_generic_dlg_title)
+ .setIcon(R.drawable.ssl_icon)
+ .setMessage(BrowserDownloadAdapter.getErrorText(status))
+ .setPositiveButton(R.string.ok, null)
+ .show();
+ }
+ }
+ return position;
+ }
+
+ /**
+ * Resume a given download
+ * @param id Row id of the download to resume
+ */
+ private void resumeDownload(final long id) {
+ // the relevant functionality doesn't exist in the download manager
+ }
+
+ /**
+ * Prompt the user if they would like to clear the download history
+ */
+ private void promptClearList() {
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.download_clear_dlg_title)
+ .setIcon(R.drawable.ssl_icon)
+ .setMessage(R.string.download_clear_dlg_msg)
+ .setPositiveButton(R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int whichButton) {
+ clearAllDownloads();
+ }
+ })
+ .setNegativeButton(R.string.cancel, null)
+ .show();
+ }
+
+ /**
+ * Return the number of items in the list that can be canceled.
+ * @return count
+ */
+ private int getCancelableCount() {
+ // Count the number of items that will be canceled.
+ int count = 0;
+ if (mDownloadCursor != null) {
+ for (mDownloadCursor.moveToFirst(); !mDownloadCursor.isAfterLast();
+ mDownloadCursor.moveToNext()) {
+ int status = mDownloadCursor.getInt(mStatusColumnId);
+ if (!Downloads.isStatusCompleted(status)) {
+ count++;
+ }
+ }
+ }
+
+ return count;
+ }
+
+ /**
+ * Prompt the user if they would like to clear the download history
+ */
+ private void promptCancelAll() {
+ int count = getCancelableCount();
+
+ // If there is nothing to do, just return
+ if (count == 0) {
+ return;
+ }
+
+ // Don't show the dialog if there is only one download
+ if (count == 1) {
+ cancelAllDownloads();
+ return;
+ }
+ String msg =
+ getString(R.string.download_cancel_dlg_msg, count);
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.download_cancel_dlg_title)
+ .setIcon(R.drawable.ssl_icon)
+ .setMessage(msg)
+ .setPositiveButton(R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int whichButton) {
+ cancelAllDownloads();
+ }
+ })
+ .setNegativeButton(R.string.cancel, null)
+ .show();
+ }
+
+ /**
+ * Cancel all downloads. As canceled downloads are not
+ * listed, we removed them from the db. Removing a download
+ * record, cancels the download.
+ */
+ private void cancelAllDownloads() {
+ if (mDownloadCursor.moveToFirst()) {
+ StringBuilder where = new StringBuilder();
+ boolean firstTime = true;
+ while (!mDownloadCursor.isAfterLast()) {
+ int status = mDownloadCursor.getInt(mStatusColumnId);
+ if (!Downloads.isStatusCompleted(status)) {
+ if (firstTime) {
+ firstTime = false;
+ } else {
+ where.append(" OR ");
+ }
+ where.append("( ");
+ where.append(Downloads._ID);
+ where.append(" = '");
+ where.append(mDownloadCursor.getLong(mIdColumnId));
+ where.append("' )");
+ }
+ mDownloadCursor.moveToNext();
+ }
+ if (!firstTime) {
+ getContentResolver().delete(Downloads.CONTENT_URI,
+ where.toString(), null);
+ }
+ }
+ }
+
+ private int getClearableCount() {
+ int count = 0;
+ if (mDownloadCursor.moveToFirst()) {
+ while (!mDownloadCursor.isAfterLast()) {
+ int status = mDownloadCursor.getInt(mStatusColumnId);
+ if (Downloads.isStatusCompleted(status)) {
+ count++;
+ }
+ mDownloadCursor.moveToNext();
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Clear all stopped downloads, ie canceled (though should not be
+ * there), error and success download items.
+ */
+ private void clearAllDownloads() {
+ if (mDownloadCursor.moveToFirst()) {
+ StringBuilder where = new StringBuilder();
+ boolean firstTime = true;
+ while (!mDownloadCursor.isAfterLast()) {
+ int status = mDownloadCursor.getInt(mStatusColumnId);
+ if (Downloads.isStatusCompleted(status)) {
+ if (firstTime) {
+ firstTime = false;
+ } else {
+ where.append(" OR ");
+ }
+ where.append("( ");
+ where.append(Downloads._ID);
+ where.append(" = '");
+ where.append(mDownloadCursor.getLong(mIdColumnId));
+ where.append("' )");
+ }
+ mDownloadCursor.moveToNext();
+ }
+ if (!firstTime) {
+ getContentResolver().delete(Downloads.CONTENT_URI,
+ where.toString(), null);
+ }
+ }
+ }
+
+ /**
+ * Open the content where the download db cursor currently is
+ */
+ private void openCurrentDownload() {
+ int filenameColumnId =
+ mDownloadCursor.getColumnIndexOrThrow(Downloads._DATA);
+ String filename = mDownloadCursor.getString(filenameColumnId);
+ int mimetypeColumnId =
+ mDownloadCursor.getColumnIndexOrThrow(Downloads.MIMETYPE);
+ String mimetype = mDownloadCursor.getString(mimetypeColumnId);
+ Uri path = Uri.parse(filename);
+ // If there is no scheme, then it must be a file
+ if (path.getScheme() == null) {
+ path = Uri.fromFile(new File(filename));
+ }
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setDataAndType(path, mimetype);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ try {
+ startActivity(intent);
+ } catch (ActivityNotFoundException ex) {
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.download_failed_generic_dlg_title)
+ .setIcon(R.drawable.ssl_icon)
+ .setMessage(R.string.download_no_application)
+ .setPositiveButton(R.string.ok, null)
+ .show();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see android.widget.AdapterView.OnItemClickListener#onItemClick(android.widget.AdapterView, android.view.View, int, long)
+ */
+ public void onItemClick(AdapterView parent, View view, int position,
+ long id) {
+ // Open the selected item
+ mDownloadCursor.moveToPosition(position);
+
+ hideCompletedDownload();
+
+ int status = mDownloadCursor.getInt(mStatusColumnId);
+ if (Downloads.isStatusSuccess(status)) {
+ // Open it if it downloaded successfully
+ openCurrentDownload();
+ } else {
+ // Check to see if there is an error.
+ checkStatus(id);
+ }
+ }
+
+ /**
+ * hides the notification for the download pointed by mDownloadCursor
+ * if the download has completed.
+ */
+ private void hideCompletedDownload() {
+ int status = mDownloadCursor.getInt(mStatusColumnId);
+
+ int visibilityColumn = mDownloadCursor.getColumnIndexOrThrow(Downloads.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);
+ 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
new file mode 100644
index 00000000..42ca8480
--- /dev/null
+++ b/src/com/android/browser/BrowserHistoryPage.java
@@ -0,0 +1,468 @@
+/*
+ * 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.
+ */
+
+package com.android.browser;
+
+import android.app.Activity;
+import android.app.ExpandableListActivity;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.database.DataSetObserver;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.ServiceManager;
+import android.provider.Browser;
+import android.text.IClipboard;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.webkit.DateSorter;
+import android.webkit.WebIconDatabase.IconListener;
+import android.widget.AdapterView;
+import android.widget.ExpandableListAdapter;
+import android.widget.ExpandableListView;
+import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
+import android.widget.TextView;
+
+import java.util.List;
+import java.util.Vector;
+
+/**
+ * Activity for displaying the browser's history, divided into
+ * days of viewing.
+ */
+public class BrowserHistoryPage extends ExpandableListActivity {
+ private HistoryAdapter mAdapter;
+ private DateSorter mDateSorter;
+ private boolean mMaxTabsOpen;
+
+ private final static String LOGTAG = "browser";
+
+ // Implementation of WebIconDatabase.IconListener
+ private class IconReceiver implements IconListener {
+ public void onReceivedIcon(String url, Bitmap icon) {
+ setListAdapter(mAdapter);
+ }
+ }
+ // Instance of IconReceiver
+ private final IconReceiver mIconReceiver = new IconReceiver();
+
+ /**
+ * Report back to the calling activity to load a site.
+ * @param url Site to load.
+ * @param newWindow True if the URL should be loaded in a new window
+ */
+ private void loadUrl(String url, boolean newWindow) {
+ Intent intent = new Intent().setAction(url);
+ if (newWindow) {
+ Bundle b = new Bundle();
+ b.putBoolean("new_window", true);
+ intent.putExtras(b);
+ }
+ setResultToParent(RESULT_OK, intent);
+ finish();
+ }
+
+ private void copy(CharSequence text) {
+ try {
+ IClipboard clip = IClipboard.Stub.asInterface(ServiceManager.getService("clipboard"));
+ if (clip != null) {
+ clip.setClipboardText(text);
+ }
+ } catch (android.os.RemoteException e) {
+ Log.e(LOGTAG, "Copy failed", e);
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setTitle(R.string.browser_history);
+
+ mDateSorter = new DateSorter(this);
+
+ mAdapter = new HistoryAdapter();
+ setListAdapter(mAdapter);
+ final ExpandableListView list = getExpandableListView();
+ list.setOnCreateContextMenuListener(this);
+ LayoutInflater factory = LayoutInflater.from(this);
+ View v = factory.inflate(R.layout.empty_history, null);
+ addContentView(v, new LayoutParams(LayoutParams.FILL_PARENT,
+ LayoutParams.FILL_PARENT));
+ list.setEmptyView(v);
+ // Do not post the runnable if there is nothing in the list.
+ if (list.getExpandableListAdapter().getGroupCount() > 0) {
+ list.post(new Runnable() {
+ public void run() {
+ // In case the history gets cleared before this event
+ // happens.
+ if (list.getExpandableListAdapter().getGroupCount() > 0) {
+ list.expandGroup(0);
+ }
+ }
+ });
+ }
+ mMaxTabsOpen = getIntent().getBooleanExtra("maxTabsOpen", false);
+ CombinedBookmarkHistoryActivity.getIconListenerSet(getContentResolver())
+ .addListener(mIconReceiver);
+
+ // initialize the result to canceled, so that if the user just presses
+ // back then it will have the correct result
+ setResultToParent(RESULT_CANCELED, null);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.history, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ menu.findItem(R.id.clear_history_menu_id).setVisible(Browser.canClearHistory(this.getContentResolver()));
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.clear_history_menu_id:
+ // FIXME: Need to clear the tab control in browserActivity
+ // as well
+ Browser.clearHistory(getContentResolver());
+ mAdapter.refreshData();
+ return true;
+
+ default:
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenuInfo menuInfo) {
+ ExpandableListContextMenuInfo i =
+ (ExpandableListContextMenuInfo) menuInfo;
+ // Do not allow a context menu to come up from the group views.
+ if (!(i.targetView instanceof HistoryItem)) {
+ return;
+ }
+
+ // Inflate the menu
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.historycontext, menu);
+
+ // Setup the header
+ menu.setHeaderTitle(((HistoryItem)i.targetView).getUrl());
+
+ // Only show open in new tab if we have not maxed out available tabs
+ menu.findItem(R.id.new_window_context_menu_id).setVisible(!mMaxTabsOpen);
+
+ // decide whether to show the share link option
+ PackageManager pm = getPackageManager();
+ Intent send = new Intent(Intent.ACTION_SEND);
+ send.setType("text/plain");
+ ResolveInfo ri = pm.resolveActivity(send, PackageManager.MATCH_DEFAULT_ONLY);
+ menu.findItem(R.id.share_link_context_menu_id).setVisible(ri != null);
+
+ super.onCreateContextMenu(menu, v, menuInfo);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ ExpandableListContextMenuInfo i =
+ (ExpandableListContextMenuInfo) item.getMenuInfo();
+ String url = ((HistoryItem)i.targetView).getUrl();
+ String title = ((HistoryItem)i.targetView).getName();
+ switch (item.getItemId()) {
+ case R.id.open_context_menu_id:
+ loadUrl(url, false);
+ return true;
+ case R.id.new_window_context_menu_id:
+ loadUrl(url, true);
+ return true;
+ case R.id.save_to_bookmarks_menu_id:
+ Browser.saveBookmark(this, title, url);
+ return true;
+ case R.id.share_link_context_menu_id:
+ Browser.sendString(this, url);
+ return true;
+ case R.id.copy_context_menu_id:
+ copy(url);
+ return true;
+ case R.id.delete_context_menu_id:
+ Browser.deleteFromHistory(getContentResolver(), url);
+ mAdapter.refreshData();
+ return true;
+ default:
+ break;
+ }
+ return super.onContextItemSelected(item);
+ }
+
+ @Override
+ public boolean onChildClick(ExpandableListView parent, View v,
+ int groupPosition, int childPosition, long id) {
+ if (v instanceof HistoryItem) {
+ loadUrl(((HistoryItem) v).getUrl(), false);
+ return true;
+ }
+ return false;
+ }
+
+ // This Activity is generally a sub-Activity of CombinedHistoryActivity. In
+ // that situation, we need to pass our result code up to our parent.
+ // However, if someone calls this Activity directly, then this has no
+ // parent, and it needs to set it on itself.
+ private void setResultToParent(int resultCode, Intent data) {
+ Activity a = getParent() == null ? this : getParent();
+ a.setResult(resultCode, data);
+ }
+
+ private class ChangeObserver extends ContentObserver {
+ public ChangeObserver() {
+ super(new Handler());
+ }
+
+ @Override
+ public boolean deliverSelfNotifications() {
+ return true;
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ mAdapter.refreshData();
+ }
+ }
+
+ private class HistoryAdapter implements ExpandableListAdapter {
+
+ // Array for each of our bins. Each entry represents how many items are
+ // in that bin.
+ 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;
+
+ HistoryAdapter() {
+ mObservers = new Vector<DataSetObserver>();
+
+ String whereClause = Browser.BookmarkColumns.VISITS + " > 0 ";
+ String orderBy = Browser.BookmarkColumns.DATE + " DESC";
+
+ mCursor = managedQuery(
+ Browser.BOOKMARKS_URI,
+ Browser.HISTORY_PROJECTION,
+ whereClause, null, orderBy);
+
+ buildMap();
+ mCursor.registerContentObserver(new ChangeObserver());
+ }
+
+ void refreshData() {
+ mCursor.requery();
+ buildMap();
+ for (DataSetObserver o : mObservers) {
+ o.onChanged();
+ }
+ }
+
+ private void buildMap() {
+ // The cursor is sorted by date
+ // The ItemMap will store the number of items in each bin.
+ int array[] = new int[DateSorter.DAY_COUNT];
+ // Zero out the array.
+ for (int j = 0; j < DateSorter.DAY_COUNT; j++) {
+ array[j] = 0;
+ }
+ mNumberOfBins = 0;
+ int dateIndex = -1;
+ if (mCursor.moveToFirst() && mCursor.getCount() > 0) {
+ while (!mCursor.isAfterLast()) {
+ long date = mCursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX);
+ int index = mDateSorter.getIndex(date);
+ if (index > dateIndex) {
+ mNumberOfBins++;
+ if (index == DateSorter.DAY_COUNT - 1) {
+ // We are already in the last bin, so it will
+ // include all the remaining items
+ array[index] = mCursor.getCount()
+ - mCursor.getPosition();
+ break;
+ }
+ dateIndex = index;
+ }
+ array[dateIndex]++;
+ mCursor.moveToNext();
+ }
+ }
+ mItemMap = array;
+ }
+
+ // This translates from a group position in the Adapter to a position in
+ // our array. This is necessary because some positions in the array
+ // have no history items, so we simply do not present those positions
+ // to the Adapter.
+ private int groupPositionToArrayPosition(int groupPosition) {
+ if (groupPosition < 0 || groupPosition >= DateSorter.DAY_COUNT) {
+ throw new AssertionError("group position out of range");
+ }
+ if (DateSorter.DAY_COUNT == mNumberOfBins || 0 == mNumberOfBins) {
+ // In the first case, we have exactly the same number of bins
+ // as our maximum possible, so there is no need to do a
+ // conversion
+ // The second statement is in case this method gets called when
+ // the array is empty, in which case the provided groupPosition
+ // will do fine.
+ return groupPosition;
+ }
+ int arrayPosition = -1;
+ while (groupPosition > -1) {
+ arrayPosition++;
+ if (mItemMap[arrayPosition] != 0) {
+ groupPosition--;
+ }
+ }
+ return arrayPosition;
+ }
+
+ public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
+ View convertView, ViewGroup parent) {
+ groupPosition = groupPositionToArrayPosition(groupPosition);
+ HistoryItem item;
+ if (null == convertView || !(convertView instanceof HistoryItem)) {
+ item = new HistoryItem(BrowserHistoryPage.this);
+ // Add padding on the left so it will be indented from the
+ // arrows on the group views.
+ item.setPadding(item.getPaddingLeft() + 10,
+ item.getPaddingTop(),
+ item.getPaddingRight(),
+ item.getPaddingBottom());
+ } else {
+ item = (HistoryItem) convertView;
+ }
+ int index = childPosition;
+ for (int i = 0; i < groupPosition; i++) {
+ index += mItemMap[i];
+ }
+ mCursor.moveToPosition(index);
+ item.setName(mCursor.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX));
+ String url = mCursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX);
+ item.setUrl(url);
+ item.setFavicon(CombinedBookmarkHistoryActivity.getIconListenerSet(
+ getContentResolver()).getFavicon(url));
+ item.setIsBookmark(1 ==
+ mCursor.getInt(Browser.HISTORY_PROJECTION_BOOKMARK_INDEX));
+ return item;
+ }
+
+ public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
+ groupPosition = groupPositionToArrayPosition(groupPosition);
+ TextView item;
+ if (null == convertView || !(convertView instanceof TextView)) {
+ LayoutInflater factory =
+ LayoutInflater.from(BrowserHistoryPage.this);
+ item = (TextView)
+ factory.inflate(R.layout.history_header, null);
+ } else {
+ item = (TextView) convertView;
+ }
+ item.setText(mDateSorter.getLabel(groupPosition));
+ return item;
+ }
+
+ public boolean areAllItemsEnabled() {
+ return true;
+ }
+
+ public boolean isChildSelectable(int groupPosition, int childPosition) {
+ return true;
+ }
+
+ public int getGroupCount() {
+ return mNumberOfBins;
+ }
+
+ public int getChildrenCount(int groupPosition) {
+ return mItemMap[groupPositionToArrayPosition(groupPosition)];
+ }
+
+ public Object getGroup(int groupPosition) {
+ return null;
+ }
+
+ public Object getChild(int groupPosition, int childPosition) {
+ return null;
+ }
+
+ public long getGroupId(int groupPosition) {
+ return groupPosition;
+ }
+
+ public long getChildId(int groupPosition, int childPosition) {
+ return (childPosition << 3) + groupPosition;
+ }
+
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ public void registerDataSetObserver(DataSetObserver observer) {
+ mObservers.add(observer);
+ }
+
+ public void unregisterDataSetObserver(DataSetObserver observer) {
+ mObservers.remove(observer);
+ }
+
+ public void onGroupExpanded(int groupPosition) {
+
+ }
+
+ public void onGroupCollapsed(int groupPosition) {
+
+ }
+
+ public long getCombinedChildId(long groupId, long childId) {
+ return childId;
+ }
+
+ public long getCombinedGroupId(long groupId) {
+ return groupId;
+ }
+
+ public boolean isEmpty() {
+ return mCursor.getCount() == 0;
+ }
+ }
+}
diff --git a/src/com/android/browser/BrowserHomepagePreference.java b/src/com/android/browser/BrowserHomepagePreference.java
new file mode 100644
index 00000000..d4708c30
--- /dev/null
+++ b/src/com/android/browser/BrowserHomepagePreference.java
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+package com.android.browser;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.preference.EditTextPreference;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.text.util.Regex;
+import android.util.AttributeSet;
+
+public class BrowserHomepagePreference extends EditTextPreference implements
+ TextWatcher {
+
+ public BrowserHomepagePreference(Context context, AttributeSet attrs,
+ int defStyle) {
+ super(context, attrs, defStyle);
+ getEditText().addTextChangedListener(this);
+ }
+
+ public BrowserHomepagePreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ getEditText().addTextChangedListener(this);
+ }
+
+ public BrowserHomepagePreference(Context context) {
+ super(context);
+ getEditText().addTextChangedListener(this);
+ }
+
+ public void afterTextChanged(Editable s) {
+ AlertDialog dialog = (AlertDialog) getDialog();
+ // This callback is called before the dialog has been fully constructed
+ 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());
+ }
+ }
+
+ public void beforeTextChanged(CharSequence s, int start, int count,
+ int after) {
+ }
+
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+}
diff --git a/src/com/android/browser/BrowserPluginList.java b/src/com/android/browser/BrowserPluginList.java
new file mode 100644
index 00000000..6689b0ec
--- /dev/null
+++ b/src/com/android/browser/BrowserPluginList.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+package com.android.browser;
+
+import android.app.ListActivity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.Plugin;
+import android.webkit.PluginList;
+import android.webkit.WebView;
+import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
+import android.widget.ListView;
+import android.widget.TextView;
+import java.util.ArrayList;
+import java.util.List;
+
+// Manages the list of installed (and loaded) plugins.
+public class BrowserPluginList extends ListActivity {
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ // The list of plugins can change under us, as the plugins are
+ // loaded and unloaded in a different thread. We make a copy
+ // of the list here.
+ List loadedPlugins = WebView.getPluginList().getList();
+ ArrayList localLoadedPluginList = new ArrayList();
+ synchronized (loadedPlugins) {
+ localLoadedPluginList.addAll(loadedPlugins);
+ }
+ setListAdapter(new ArrayAdapter(this,
+ android.R.layout.simple_list_item_1,
+ localLoadedPluginList));
+ setTitle(R.string.pref_plugin_installed);
+ // Add a text view to this ListActivity. This text view
+ // will be displayed when the list of plugins is empty.
+ TextView textView = new TextView(this);
+ textView.setId(android.R.id.empty);
+ textView.setText(R.string.pref_plugin_installed_empty_list);
+ addContentView(textView, new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+
+ }
+
+ @Override
+ public void onListItemClick(ListView l, View v, int position, long id) {
+ WebView.getPluginList().pluginClicked(this, position);
+ }
+}
diff --git a/src/com/android/browser/BrowserPreferencesPage.java b/src/com/android/browser/BrowserPreferencesPage.java
new file mode 100644
index 00000000..5d6795ba
--- /dev/null
+++ b/src/com/android/browser/BrowserPreferencesPage.java
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ */
+
+package com.android.browser;
+
+import java.util.List;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.preference.EditTextPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.webkit.WebView;
+import android.webkit.Plugin;
+
+public class BrowserPreferencesPage extends PreferenceActivity
+ implements Preference.OnPreferenceChangeListener,
+ Preference.OnPreferenceClickListener {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Load the XML preferences file
+ addPreferencesFromResource(R.xml.browser_preferences);
+
+ Preference e = findPreference(BrowserSettings.PREF_HOMEPAGE);
+ e.setOnPreferenceChangeListener(this);
+ e.setSummary(getPreferenceScreen().getSharedPreferences()
+ .getString(BrowserSettings.PREF_HOMEPAGE, null));
+
+ e = findPreference(BrowserSettings.PREF_EXTRAS_RESET_DEFAULTS);
+ e.setOnPreferenceChangeListener(this);
+
+ e = findPreference(BrowserSettings.PREF_TEXT_SIZE);
+ e.setOnPreferenceChangeListener(this);
+ e.setSummary(getVisualTextSizeName(
+ getPreferenceScreen().getSharedPreferences()
+ .getString(BrowserSettings.PREF_TEXT_SIZE, null)) );
+
+ e = findPreference(BrowserSettings.PREF_DEFAULT_TEXT_ENCODING);
+ e.setOnPreferenceChangeListener(this);
+
+ if (BrowserSettings.getInstance().showDebugSettings()) {
+ addPreferencesFromResource(R.xml.debug_preferences);
+ }
+
+ e = findPreference(BrowserSettings.PREF_GEARS_SETTINGS);
+ e.setOnPreferenceClickListener(this);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ // sync the shared preferences back to BrowserSettings
+ BrowserSettings.getInstance().syncSharedPreferences(
+ getPreferenceScreen().getSharedPreferences());
+ }
+
+ public boolean onPreferenceChange(Preference pref, Object objValue) {
+ if (pref.getKey().equals(BrowserSettings.PREF_EXTRAS_RESET_DEFAULTS)) {
+ Boolean value = (Boolean) objValue;
+ if (value.booleanValue() == true) {
+ finish();
+ }
+ } else if (pref.getKey().equals(BrowserSettings.PREF_HOMEPAGE)) {
+ String value = (String) objValue;
+ boolean needUpdate = value.indexOf(' ') != -1;
+ if (needUpdate) {
+ value = value.trim().replace(" ", "%20");
+ }
+ if (value.length() != 0 && Uri.parse(value).getScheme() == null) {
+ value = "http://" + value;
+ needUpdate = true;
+ }
+ // Set the summary value.
+ pref.setSummary(value);
+ if (needUpdate) {
+ // Update through the EditText control as it has a cached copy
+ // of the string and it will handle persisting the value
+ ((EditTextPreference) pref).setText(value);
+
+ // as we update the value above, we need to return false
+ // here so that setText() is not called by EditTextPref
+ // with the old value.
+ return false;
+ } else {
+ return true;
+ }
+ } else if (pref.getKey().equals(BrowserSettings.PREF_TEXT_SIZE)) {
+ pref.setSummary(getVisualTextSizeName((String) objValue));
+ return true;
+ } else if (pref.getKey().equals(
+ BrowserSettings.PREF_DEFAULT_TEXT_ENCODING)) {
+ pref.setSummary((String) objValue);
+ return true;
+ }
+
+ return false;
+ }
+
+ public boolean onPreferenceClick(Preference pref) {
+ if (pref.getKey().equals(BrowserSettings.PREF_GEARS_SETTINGS)) {
+ List<Plugin> loadedPlugins = WebView.getPluginList().getList();
+ for(Plugin p : loadedPlugins) {
+ if (p.getName().equals("gears")) {
+ p.dispatchClickEvent(this);
+ return true;
+ }
+ }
+
+ }
+ return true;
+ }
+
+ private CharSequence getVisualTextSizeName(String enumName) {
+ CharSequence[] visualNames =
+ getResources().getTextArray(R.array.pref_text_size_choices);
+ CharSequence[] enumNames =
+ getResources().getTextArray(R.array.pref_text_size_values);
+
+ // Sanity check
+ if (visualNames.length != enumNames.length) {
+ return "";
+ }
+
+ for (int i = 0; i < enumNames.length; i++) {
+ if (enumNames[i].equals(enumName)) {
+ return visualNames[i];
+ }
+ }
+
+ return "";
+ }
+}
diff --git a/src/com/android/browser/BrowserProvider.java b/src/com/android/browser/BrowserProvider.java
new file mode 100644
index 00000000..0c930c60
--- /dev/null
+++ b/src/com/android/browser/BrowserProvider.java
@@ -0,0 +1,654 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.browser;
+
+import android.app.ISearchManager;
+import android.app.SearchManager;
+import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.UriMatcher;
+import android.database.AbstractCursor;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.provider.Browser;
+import android.util.Log;
+import android.server.search.SearchableInfo;
+import android.text.util.Regex;
+
+public class BrowserProvider extends ContentProvider {
+
+ private SQLiteOpenHelper mOpenHelper;
+ private static final String sDatabaseName = "browser.db";
+ private static final String TAG = "BrowserProvider";
+ private static final String ORDER_BY = "visits DESC, date DESC";
+
+ private static final String[] TABLE_NAMES = new String[] {
+ "bookmarks", "searches"
+ };
+ private static final String[] SUGGEST_PROJECTION = new String[] {
+ "_id", "url", "title", "bookmark"
+ };
+ private static final String SUGGEST_SELECTION =
+ "url LIKE ? OR url LIKE ? OR url LIKE ? OR url LIKE ?";
+ private String[] SUGGEST_ARGS = new String[4];
+
+ // shared suggestion array index, make sure to match COLUMNS
+ private static final int SUGGEST_COLUMN_INTENT_ACTION_ID = 1;
+ private static final int SUGGEST_COLUMN_INTENT_DATA_ID = 2;
+ private static final int SUGGEST_COLUMN_TEXT_1_ID = 3;
+ private static final int SUGGEST_COLUMN_TEXT_2_ID = 4;
+ private static final int SUGGEST_COLUMN_ICON_1_ID = 5;
+ private static final int SUGGEST_COLUMN_ICON_2_ID = 6;
+ private static final int SUGGEST_COLUMN_QUERY_ID = 7;
+
+ // shared suggestion columns
+ private static final String[] COLUMNS = new String[] {
+ "_id",
+ SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
+ SearchManager.SUGGEST_COLUMN_INTENT_DATA,
+ SearchManager.SUGGEST_COLUMN_TEXT_1,
+ SearchManager.SUGGEST_COLUMN_TEXT_2,
+ SearchManager.SUGGEST_COLUMN_ICON_1,
+ SearchManager.SUGGEST_COLUMN_ICON_2,
+ SearchManager.SUGGEST_COLUMN_QUERY};
+
+ private static final int MAX_SUGGESTION_SHORT_ENTRIES = 3;
+ private static final int MAX_SUGGESTION_LONG_ENTRIES = 6;
+
+ // make sure that these match the index of TABLE_NAMES
+ private static final int URI_MATCH_BOOKMARKS = 0;
+ private static final int URI_MATCH_SEARCHES = 1;
+ // (id % 10) should match the table name index
+ private static final int URI_MATCH_BOOKMARKS_ID = 10;
+ private static final int URI_MATCH_SEARCHES_ID = 11;
+ //
+ private static final int URI_MATCH_SUGGEST = 20;
+
+ private static final UriMatcher URI_MATCHER;
+
+ static {
+ URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
+ URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_BOOKMARKS],
+ URI_MATCH_BOOKMARKS);
+ URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_BOOKMARKS] + "/#",
+ URI_MATCH_BOOKMARKS_ID);
+ URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_SEARCHES],
+ URI_MATCH_SEARCHES);
+ URI_MATCHER.addURI("browser", TABLE_NAMES[URI_MATCH_SEARCHES] + "/#",
+ URI_MATCH_SEARCHES_ID);
+ URI_MATCHER.addURI("browser", SearchManager.SUGGEST_URI_PATH_QUERY,
+ URI_MATCH_SUGGEST);
+ }
+
+ // 1 -> 2 add cache table
+ // 2 -> 3 update history table
+ // 3 -> 4 add passwords table
+ // 4 -> 5 add settings table
+ // 5 -> 6 ?
+ // 6 -> 7 ?
+ // 7 -> 8 drop proxy table
+ // 8 -> 9 drop settings table
+ // 9 -> 10 add form_urls and form_data
+ // 10 -> 11 add searches table
+ // 11 -> 12 modify cache table
+ // 12 -> 13 modify cache table
+ // 13 -> 14 correspond with Google Bookmarks schema
+ // 14 -> 15 move couple of tables to either browser private database or webview database
+ // 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;
+
+ public BrowserProvider() {
+ }
+
+
+ private static CharSequence replaceSystemPropertyInString(CharSequence srcString) {
+ StringBuffer sb = new StringBuffer();
+ int lastCharLoc = 0;
+ for (int i = 0; i < srcString.length(); ++i) {
+ char c = srcString.charAt(i);
+ if (c == '{') {
+ sb.append(srcString.subSequence(lastCharLoc, i));
+ lastCharLoc = i;
+ inner:
+ for (int j = i; j < srcString.length(); ++j) {
+ char k = srcString.charAt(j);
+ if (k == '}') {
+ String propertyKeyValue = srcString.subSequence(i + 1, j).toString();
+ // See if the propertyKeyValue specifies a default value
+ int defaultOffset = propertyKeyValue.indexOf(':');
+ if (defaultOffset == -1) {
+ sb.append(SystemProperties.get(propertyKeyValue));
+ } else {
+ String propertyKey = propertyKeyValue.substring(0, defaultOffset);
+ String defaultValue =
+ propertyKeyValue.substring(defaultOffset + 1,
+ propertyKeyValue.length());
+ sb.append(SystemProperties.get(propertyKey, defaultValue));
+ }
+ lastCharLoc = j + 1;
+ i = j;
+ break inner;
+ }
+ }
+ }
+ }
+ if (srcString.length() - lastCharLoc > 0) {
+ // Put on the tail, if there is one
+ sb.append(srcString.subSequence(lastCharLoc, srcString.length()));
+ }
+ return sb;
+ }
+
+ private static class DatabaseHelper extends SQLiteOpenHelper {
+ private Context mContext;
+
+ public DatabaseHelper(Context context) {
+ super(context, sDatabaseName, null, DATABASE_VERSION);
+ mContext = context;
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE bookmarks (" +
+ "_id INTEGER PRIMARY KEY," +
+ "title TEXT," +
+ "url TEXT," +
+ "visits INTEGER," +
+ "date LONG," +
+ "created LONG," +
+ "description TEXT," +
+ "bookmark INTEGER," +
+ "favicon BLOB DEFAULT NULL" +
+ ");");
+
+ final CharSequence[] bookmarks = mContext.getResources()
+ .getTextArray(R.array.bookmarks);
+ int size = bookmarks.length;
+ try {
+ for (int i = 0; i < size; i = i + 2) {
+ CharSequence bookmarkDestination = replaceSystemPropertyInString(bookmarks[i + 1]);
+ db.execSQL("INSERT INTO bookmarks (title, url, visits, " +
+ "date, created, bookmark)" + " VALUES('" +
+ bookmarks[i] + "', '" + bookmarkDestination +
+ "', 0, 0, 0, 1);");
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ }
+
+ db.execSQL("CREATE TABLE searches (" +
+ "_id INTEGER PRIMARY KEY," +
+ "search TEXT," +
+ "date LONG" +
+ ");");
+ }
+
+ @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");
+ if (oldVersion == 18) {
+ db.execSQL("DROP TABLE IF EXISTS labels");
+ } else {
+ db.execSQL("DROP TABLE IF EXISTS bookmarks");
+ db.execSQL("DROP TABLE IF EXISTS searches");
+ onCreate(db);
+ }
+ }
+ }
+
+ @Override
+ public boolean onCreate() {
+ mOpenHelper = new DatabaseHelper(getContext());
+ return true;
+ }
+
+ /*
+ * Subclass AbstractCursor so we can combine multiple Cursors and add
+ * "Google Search".
+ * Here are the rules.
+ * 1. We only have MAX_SUGGESTION_LONG_ENTRIES in the list plus
+ * "Google Search";
+ * 2. If bookmark/history entries are less than
+ * (MAX_SUGGESTION_SHORT_ENTRIES -1), we include Google suggest.
+ */
+ private class MySuggestionCursor extends AbstractCursor {
+ private Cursor mHistoryCursor;
+ private Cursor mSuggestCursor;
+ private int mHistoryCount;
+ private int mSuggestionCount;
+ private boolean mBeyondCursor;
+ private String mString;
+
+ public MySuggestionCursor(Cursor hc, Cursor sc, String string) {
+ mHistoryCursor = hc;
+ mSuggestCursor = sc;
+ mHistoryCount = hc.getCount();
+ mSuggestionCount = sc != null ? sc.getCount() : 0;
+ if (mSuggestionCount > (MAX_SUGGESTION_LONG_ENTRIES - mHistoryCount)) {
+ mSuggestionCount = MAX_SUGGESTION_LONG_ENTRIES - mHistoryCount;
+ }
+ mString = string;
+ mBeyondCursor = false;
+ }
+
+ @Override
+ public boolean onMove(int oldPosition, int newPosition) {
+ if (mHistoryCursor == null) {
+ return false;
+ }
+ if (mHistoryCount > newPosition) {
+ mHistoryCursor.moveToPosition(newPosition);
+ mBeyondCursor = false;
+ } else if (mHistoryCount + mSuggestionCount > newPosition) {
+ mSuggestCursor.moveToPosition(newPosition - mHistoryCount);
+ mBeyondCursor = false;
+ } else {
+ mBeyondCursor = true;
+ }
+ return true;
+ }
+
+ @Override
+ public int getCount() {
+ if (mString.length() > 0) {
+ return mHistoryCount + mSuggestionCount + 1;
+ } else {
+ return mHistoryCount + mSuggestionCount;
+ }
+ }
+
+ @Override
+ public String[] getColumnNames() {
+ return COLUMNS;
+ }
+
+ @Override
+ public String getString(int columnIndex) {
+ if ((mPos != -1 && mHistoryCursor != null)) {
+ switch(columnIndex) {
+ case SUGGEST_COLUMN_INTENT_ACTION_ID:
+ if (mHistoryCount > mPos) {
+ return Intent.ACTION_VIEW;
+ } else {
+ return Intent.ACTION_SEARCH;
+ }
+
+ case SUGGEST_COLUMN_INTENT_DATA_ID:
+ if (mHistoryCount > mPos) {
+ return mHistoryCursor.getString(1);
+ } else {
+ return null;
+ }
+
+ case SUGGEST_COLUMN_TEXT_1_ID:
+ if (mHistoryCount > mPos) {
+ return mHistoryCursor.getString(1);
+ } else if (!mBeyondCursor) {
+ return mSuggestCursor.getString(1);
+ } else {
+ return mString;
+ }
+
+ case SUGGEST_COLUMN_TEXT_2_ID:
+ if (mHistoryCount > mPos) {
+ return mHistoryCursor.getString(2);
+ } else if (!mBeyondCursor) {
+ return mSuggestCursor.getString(2);
+ } else {
+ return getContext().getString(R.string.search_google);
+ }
+
+ case SUGGEST_COLUMN_ICON_1_ID:
+ if (mHistoryCount > mPos) {
+ if (mHistoryCursor.getInt(3) == 1) {
+ return new Integer(
+ R.drawable.ic_search_category_bookmark)
+ .toString();
+ } else {
+ return new Integer(
+ R.drawable.ic_search_category_history)
+ .toString();
+ }
+ } else {
+ return new Integer(
+ R.drawable.ic_search_category_suggest)
+ .toString();
+ }
+
+ case SUGGEST_COLUMN_ICON_2_ID:
+ return new String("0");
+
+ case SUGGEST_COLUMN_QUERY_ID:
+ if (mHistoryCount > mPos) {
+ return null;
+ } else if (!mBeyondCursor) {
+ return mSuggestCursor.getString(3);
+ } else {
+ return mString;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public double getDouble(int column) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public float getFloat(int column) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getInt(int column) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long getLong(int column) {
+ if ((mPos != -1) && column == 0) {
+ return mPos; // use row# as the _Id
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public short getShort(int column) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isNull(int column) {
+ throw new UnsupportedOperationException();
+ }
+
+ // TODO Temporary change, finalize after jq's changes go in
+ public void deactivate() {
+ if (mHistoryCursor != null) {
+ mHistoryCursor.deactivate();
+ }
+ if (mSuggestCursor != null) {
+ mSuggestCursor.deactivate();
+ }
+ super.deactivate();
+ }
+
+ public boolean requery() {
+ return (mHistoryCursor != null ? mHistoryCursor.requery() : false) |
+ (mSuggestCursor != null ? mSuggestCursor.requery() : false);
+ }
+
+ // TODO Temporary change, finalize after jq's changes go in
+ public void close() {
+ super.close();
+ if (mHistoryCursor != null) {
+ mHistoryCursor.close();
+ mHistoryCursor = null;
+ }
+ if (mSuggestCursor != null) {
+ mSuggestCursor.close();
+ mSuggestCursor = null;
+ }
+ }
+ }
+
+ @Override
+ public Cursor query(Uri url, String[] projectionIn, String selection,
+ String[] selectionArgs, String sortOrder)
+ throws IllegalStateException {
+ SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+
+ int match = URI_MATCHER.match(url);
+ if (match == -1) {
+ throw new IllegalArgumentException("Unknown URL");
+ }
+
+ if (match == URI_MATCH_SUGGEST) {
+ String suggestSelection;
+ String [] myArgs;
+ if (selectionArgs[0] == null || selectionArgs[0].equals("")) {
+ suggestSelection = null;
+ myArgs = null;
+ } else {
+ String like = selectionArgs[0] + "%";
+ if (selectionArgs[0].startsWith("http")) {
+ myArgs = new String[1];
+ myArgs[0] = like;
+ suggestSelection = selection;
+ } else {
+ SUGGEST_ARGS[0] = "http://" + like;
+ SUGGEST_ARGS[1] = "http://www." + like;
+ SUGGEST_ARGS[2] = "https://" + like;
+ SUGGEST_ARGS[3] = "https://www." + like;
+ myArgs = SUGGEST_ARGS;
+ suggestSelection = SUGGEST_SELECTION;
+ }
+ }
+
+ Cursor c = db.query(TABLE_NAMES[URI_MATCH_BOOKMARKS],
+ SUGGEST_PROJECTION, suggestSelection, myArgs, null, null,
+ ORDER_BY,
+ (new Integer(MAX_SUGGESTION_LONG_ENTRIES)).toString());
+
+ if (Regex.WEB_URL_PATTERN.matcher(selectionArgs[0]).matches()) {
+ return new MySuggestionCursor(c, null, "");
+ } else {
+ // get Google suggest if there is still space in the list
+ if (myArgs != null && myArgs.length > 1
+ && c.getCount() < (MAX_SUGGESTION_SHORT_ENTRIES - 1)) {
+ ISearchManager sm = ISearchManager.Stub
+ .asInterface(ServiceManager
+ .getService(Context.SEARCH_SERVICE));
+ SearchableInfo si = null;
+ try {
+ // use the global search to get Google suggest provider
+ si = sm.getSearchableInfo(new ComponentName(
+ getContext(), "com.android.browser"), true);
+
+ // similar to the getSuggestions() in SearchDialog.java
+ StringBuilder uriStr = new StringBuilder("content://");
+ uriStr.append(si.getSuggestAuthority());
+ // if content path provided, insert it now
+ final String contentPath = si.getSuggestPath();
+ if (contentPath != null) {
+ uriStr.append('/');
+ uriStr.append(contentPath);
+ }
+ // append standard suggestion query path
+ uriStr.append('/' + SearchManager.SUGGEST_URI_PATH_QUERY);
+ // inject query, either as selection args or inline
+ String[] selArgs = null;
+ if (si.getSuggestSelection() != null) {
+ selArgs = new String[] {selectionArgs[0]};
+ } else {
+ uriStr.append('/');
+ uriStr.append(Uri.encode(selectionArgs[0]));
+ }
+
+ // finally, make the query
+ Cursor sc = getContext().getContentResolver().query(
+ Uri.parse(uriStr.toString()), null,
+ si.getSuggestSelection(), selArgs, null);
+
+ return new MySuggestionCursor(c, sc, selectionArgs[0]);
+ } catch (RemoteException e) {
+ }
+ }
+ return new MySuggestionCursor(c, null, selectionArgs[0]);
+ }
+ }
+
+ String[] projection = null;
+ if (projectionIn != null && projectionIn.length > 0) {
+ projection = new String[projectionIn.length + 1];
+ System.arraycopy(projectionIn, 0, projection, 0, projectionIn.length);
+ projection[projectionIn.length] = "_id AS _id";
+ }
+
+ StringBuilder whereClause = new StringBuilder(256);
+ if (match == URI_MATCH_BOOKMARKS_ID || match == URI_MATCH_SEARCHES_ID) {
+ whereClause.append("(_id = ").append(url.getPathSegments().get(1))
+ .append(")");
+ }
+
+ // Tack on the user's selection, if present
+ if (selection != null && selection.length() > 0) {
+ if (whereClause.length() > 0) {
+ whereClause.append(" AND ");
+ }
+
+ whereClause.append('(');
+ whereClause.append(selection);
+ whereClause.append(')');
+ }
+ Cursor c = db.query(TABLE_NAMES[match % 10], projection,
+ whereClause.toString(), selectionArgs, null, null, sortOrder,
+ null);
+ c.setNotificationUri(getContext().getContentResolver(), url);
+ return c;
+ }
+
+ @Override
+ public String getType(Uri url) {
+ int match = URI_MATCHER.match(url);
+ switch (match) {
+ case URI_MATCH_BOOKMARKS:
+ return "vnd.android.cursor.dir/bookmark";
+
+ case URI_MATCH_BOOKMARKS_ID:
+ return "vnd.android.cursor.item/bookmark";
+
+ case URI_MATCH_SEARCHES:
+ return "vnd.android.cursor.dir/searches";
+
+ case URI_MATCH_SEARCHES_ID:
+ return "vnd.android.cursor.item/searches";
+
+ case URI_MATCH_SUGGEST:
+ return SearchManager.SUGGEST_MIME_TYPE;
+
+ default:
+ throw new IllegalArgumentException("Unknown URL");
+ }
+ }
+
+ @Override
+ public Uri insert(Uri url, ContentValues initialValues) {
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+
+ int match = URI_MATCHER.match(url);
+ Uri uri = null;
+ switch (match) {
+ case URI_MATCH_BOOKMARKS: {
+ // Insert into the bookmarks table
+ long rowID = db.insert(TABLE_NAMES[URI_MATCH_BOOKMARKS], "url",
+ initialValues);
+ if (rowID > 0) {
+ uri = ContentUris.withAppendedId(Browser.BOOKMARKS_URI,
+ rowID);
+ }
+ break;
+ }
+
+ case URI_MATCH_SEARCHES: {
+ // Insert into the searches table
+ long rowID = db.insert(TABLE_NAMES[URI_MATCH_SEARCHES], "url",
+ initialValues);
+ if (rowID > 0) {
+ uri = ContentUris.withAppendedId(Browser.SEARCHES_URI,
+ rowID);
+ }
+ break;
+ }
+
+ default:
+ throw new IllegalArgumentException("Unknown URL");
+ }
+
+ if (uri == null) {
+ throw new IllegalArgumentException("Unknown URL");
+ }
+ getContext().getContentResolver().notifyChange(uri, null);
+ return uri;
+ }
+
+ @Override
+ public int delete(Uri url, String where, String[] whereArgs) {
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+
+ int match = URI_MATCHER.match(url);
+ if (match == -1 || match == URI_MATCH_SUGGEST) {
+ throw new IllegalArgumentException("Unknown URL");
+ }
+
+ if (match == URI_MATCH_BOOKMARKS_ID || match == URI_MATCH_SEARCHES_ID) {
+ StringBuilder sb = new StringBuilder();
+ if (where != null && where.length() > 0) {
+ sb.append("( ");
+ sb.append(where);
+ sb.append(" ) AND ");
+ }
+ sb.append("_id = ");
+ sb.append(url.getPathSegments().get(1));
+ where = sb.toString();
+ }
+
+ int count = db.delete(TABLE_NAMES[match % 10], where, whereArgs);
+ getContext().getContentResolver().notifyChange(url, null);
+ return count;
+ }
+
+ @Override
+ public int update(Uri url, ContentValues values, String where,
+ String[] whereArgs) {
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+
+ int match = URI_MATCHER.match(url);
+ if (match == -1 || match == URI_MATCH_SUGGEST) {
+ throw new IllegalArgumentException("Unknown URL");
+ }
+
+ if (match == URI_MATCH_BOOKMARKS_ID || match == URI_MATCH_SEARCHES_ID) {
+ StringBuilder sb = new StringBuilder();
+ if (where != null && where.length() > 0) {
+ sb.append("( ");
+ sb.append(where);
+ sb.append(" ) AND ");
+ }
+ sb.append("_id = ");
+ sb.append(url.getPathSegments().get(1));
+ where = sb.toString();
+ }
+
+ int ret = db.update(TABLE_NAMES[match % 10], values, where, whereArgs);
+ getContext().getContentResolver().notifyChange(url, null);
+ return ret;
+ }
+}
diff --git a/src/com/android/browser/BrowserSearchpagePreference.java b/src/com/android/browser/BrowserSearchpagePreference.java
new file mode 100644
index 00000000..09e89933
--- /dev/null
+++ b/src/com/android/browser/BrowserSearchpagePreference.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package com.android.browser;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.preference.EditTextPreference;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.text.util.Regex;
+import android.util.AttributeSet;
+
+public class BrowserSearchpagePreference extends EditTextPreference implements
+ TextWatcher {
+
+ public BrowserSearchpagePreference(Context context, AttributeSet attrs,
+ int defStyle) {
+ super(context, attrs, defStyle);
+ getEditText().addTextChangedListener(this);
+ }
+
+ public BrowserSearchpagePreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ getEditText().addTextChangedListener(this);
+ }
+
+ public BrowserSearchpagePreference(Context context) {
+ super(context);
+ getEditText().addTextChangedListener(this);
+ }
+
+ public void afterTextChanged(Editable s) {
+ AlertDialog dialog = (AlertDialog) getDialog();
+ // This callback is called before the dialog has been fully constructed
+ if (dialog != null) {
+ String string = s.toString();
+ int length = string.length();
+ int first = length > 0 ? string
+ .indexOf(BrowserActivity.QUERY_PLACE_HOLDER) : -1;
+ int last = length > 0 ? string
+ .lastIndexOf(BrowserActivity.QUERY_PLACE_HOLDER) : -1;
+ dialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(
+ length == 0 || (first > 0 && first == last));
+ }
+ }
+
+ public void beforeTextChanged(CharSequence s, int start, int count,
+ int after) {
+ }
+
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+}
diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java
new file mode 100644
index 00000000..d8c5186c
--- /dev/null
+++ b/src/com/android/browser/BrowserSettings.java
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2007 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.ContentResolver;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.os.SystemProperties;
+import android.view.WindowManager;
+import android.webkit.CacheManager;
+import android.webkit.CookieManager;
+import android.webkit.WebView;
+import android.webkit.WebViewDatabase;
+import android.webkit.WebIconDatabase;
+import android.webkit.WebSettings;
+import android.preference.PreferenceManager;
+import android.provider.Browser;
+
+import java.util.HashMap;
+import java.util.Observable;
+
+/*
+ * Package level class for storing various WebView and Browser settings. To use
+ * this class:
+ * BrowserSettings s = BrowserSettings.getInstance();
+ * s.addObserver(webView.getSettings());
+ * s.loadFromDb(context); // Only needed on app startup
+ * s.javaScriptEnabled = true;
+ * ... // set any other settings
+ * s.update(); // this will update all the observers
+ *
+ * To remove an observer:
+ * s.deleteObserver(webView.getSettings());
+ */
+class BrowserSettings extends Observable {
+
+ // Public variables for settings
+ // NOTE: these defaults need to be kept in sync with the XML
+ // until the performance of PreferenceManager.setDefaultValues()
+ // is improved.
+ private boolean loadsImagesAutomatically = true;
+ private boolean javaScriptEnabled = true;
+ private boolean pluginsEnabled = true;
+ private String pluginsPath; // default value set in loadFromDb().
+ private boolean javaScriptCanOpenWindowsAutomatically = false;
+ private boolean showSecurityWarnings = true;
+ private boolean rememberPasswords = true;
+ private boolean saveFormData = true;
+ private boolean openInBackground = false;
+ private String defaultTextEncodingName;
+ private String homeUrl = "http://www.google.com/m?client=ms-" +
+ SystemProperties.get("persist.sys.com.google.clientid", "unknown");
+ private boolean loginInitialized = false;
+ private boolean autoFitPage = true;
+ private boolean showDebugSettings = false;
+
+ // Development settings
+ public WebSettings.LayoutAlgorithm layoutAlgorithm =
+ WebSettings.LayoutAlgorithm.NARROW_COLUMNS;
+ private boolean useWideViewPort = true;
+ private int userAgent = 0;
+ private boolean tracing = false;
+ private boolean lightTouch = false;
+ private boolean navDump = false;
+ // Browser only settings
+ private boolean doFlick = false;
+
+ // Private preconfigured values
+ private static int minimumFontSize = 8;
+ private static int minimumLogicalFontSize = 8;
+ private static int defaultFontSize = 16;
+ private static int defaultFixedFontSize = 13;
+ private static WebSettings.TextSize textSize =
+ WebSettings.TextSize.NORMAL;
+
+ // Preference keys that are used outside this class
+ public final static String PREF_CLEAR_CACHE = "privacy_clear_cache";
+ public final static String PREF_CLEAR_COOKIES = "privacy_clear_cookies";
+ public final static String PREF_CLEAR_HISTORY = "privacy_clear_history";
+ public final static String PREF_HOMEPAGE = "homepage";
+ public final static String PREF_CLEAR_FORM_DATA =
+ "privacy_clear_form_data";
+ public final static String PREF_CLEAR_PASSWORDS =
+ "privacy_clear_passwords";
+ 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_TEXT_SIZE = "text_size";
+ public final static String PREF_DEFAULT_TEXT_ENCODING =
+ "default_text_encoding";
+
+ private static final String DESKTOP_USERAGENT = "Mozilla/5.0 (Macintosh; " +
+ "U; Intel Mac OS X 10_5_5; en-us) AppleWebKit/525.18 (KHTML, " +
+ "like Gecko) Version/3.1.2 Safari/525.20.1";
+
+ private static final String IPHONE_USERAGENT = "Mozilla/5.0 (iPhone; U; " +
+ "CPU iPhone OS 2_2 like Mac OS X; en-us) AppleWebKit/525.18.1 " +
+ "(KHTML, like Gecko) Version/3.1.1 Mobile/5G77 Safari/525.20";
+
+ // Value to truncate strings when adding them to a TextView within
+ // a ListView
+ public final static int MAX_TEXTVIEW_LEN = 80;
+
+ private TabControl mTabControl;
+
+ // Single instance of the BrowserSettings for use in the Browser app.
+ private static BrowserSettings sSingleton;
+
+ // Private map of WebSettings to Observer objects used when deleting an
+ // observer.
+ private HashMap<WebSettings,Observer> mWebSettingsToObservers =
+ new HashMap<WebSettings,Observer>();
+
+ /*
+ * An observer wrapper for updating a WebSettings object with the new
+ * settings after a call to BrowserSettings.update().
+ */
+ static class Observer implements java.util.Observer {
+ // Private WebSettings object that will be updated.
+ private WebSettings mSettings;
+
+ Observer(WebSettings w) {
+ mSettings = w;
+ }
+
+ public void update(Observable o, Object arg) {
+ BrowserSettings b = (BrowserSettings)o;
+ WebSettings s = mSettings;
+
+ s.setLayoutAlgorithm(b.layoutAlgorithm);
+ if (b.userAgent == 0) {
+ // use the default ua string
+ s.setUserAgentString(null);
+ } else if (b.userAgent == 1) {
+ s.setUserAgentString(DESKTOP_USERAGENT);
+ } else if (b.userAgent == 2) {
+ s.setUserAgentString(IPHONE_USERAGENT);
+ }
+ s.setUseWideViewPort(b.useWideViewPort);
+ s.setLoadsImagesAutomatically(b.loadsImagesAutomatically);
+ s.setJavaScriptEnabled(b.javaScriptEnabled);
+ s.setPluginsEnabled(b.pluginsEnabled);
+ s.setPluginsPath(b.pluginsPath);
+ s.setJavaScriptCanOpenWindowsAutomatically(
+ b.javaScriptCanOpenWindowsAutomatically);
+ s.setDefaultTextEncodingName(b.defaultTextEncodingName);
+ s.setMinimumFontSize(b.minimumFontSize);
+ s.setMinimumLogicalFontSize(b.minimumLogicalFontSize);
+ s.setDefaultFontSize(b.defaultFontSize);
+ s.setDefaultFixedFontSize(b.defaultFixedFontSize);
+ s.setNavDump(b.navDump);
+ s.setTextSize(b.textSize);
+ s.setLightTouchEnabled(b.lightTouch);
+ s.setSaveFormData(b.saveFormData);
+ s.setSavePassword(b.rememberPasswords);
+
+ // WebView inside Browser doesn't want initial focus to be set.
+ s.setNeedInitialFocus(false);
+ // Browser supports multiple windows
+ s.setSupportMultipleWindows(true);
+ // Turn off file access
+ s.setAllowFileAccess(false);
+ }
+ }
+
+ /**
+ * Load settings from the browser app's database.
+ * NOTE: Strings used for the preferences must match those specified
+ * in the browser_preferences.xml
+ * @param ctx A Context object used to query the browser's settings
+ * database. If the database exists, the saved settings will be
+ * stored in this BrowserSettings object. This will update all
+ * observers of this object.
+ */
+ public void loadFromDb(Context ctx) {
+ SharedPreferences p =
+ PreferenceManager.getDefaultSharedPreferences(ctx);
+
+ // Set the default value for the plugins path to the application's
+ // local directory.
+ pluginsPath = ctx.getDir("plugins", 0).getPath();
+
+ // Load the defaults from the xml
+ // This call is TOO SLOW, need to manually keep the defaults
+ // in sync
+ //PreferenceManager.setDefaultValues(ctx, R.xml.browser_preferences);
+ syncSharedPreferences(p);
+ }
+
+ /* package */ void syncSharedPreferences(SharedPreferences p) {
+ homeUrl =
+ p.getString(PREF_HOMEPAGE, homeUrl);
+ loadsImagesAutomatically = p.getBoolean("load_images",
+ loadsImagesAutomatically);
+ javaScriptEnabled = p.getBoolean("enable_javascript",
+ javaScriptEnabled);
+ pluginsEnabled = p.getBoolean("enable_plugins",
+ pluginsEnabled);
+ pluginsPath = p.getString("plugins_path", pluginsPath);
+ javaScriptCanOpenWindowsAutomatically = !p.getBoolean(
+ "block_popup_windows",
+ !javaScriptCanOpenWindowsAutomatically);
+ showSecurityWarnings = p.getBoolean("show_security_warnings",
+ showSecurityWarnings);
+ rememberPasswords = p.getBoolean("remember_passwords",
+ rememberPasswords);
+ saveFormData = p.getBoolean("save_formdata",
+ saveFormData);
+ boolean accept_cookies = p.getBoolean("accept_cookies",
+ CookieManager.getInstance().acceptCookie());
+ CookieManager.getInstance().setAcceptCookie(accept_cookies);
+ openInBackground = p.getBoolean("open_in_background", openInBackground);
+ loginInitialized = p.getBoolean("login_initialized", loginInitialized);
+ textSize = WebSettings.TextSize.valueOf(
+ p.getString(PREF_TEXT_SIZE, textSize.name()));
+ autoFitPage = p.getBoolean("autofit_pages", autoFitPage);
+ useWideViewPort = true; // use wide view port for either setting
+ if (autoFitPage) {
+ layoutAlgorithm = WebSettings.LayoutAlgorithm.NARROW_COLUMNS;
+ } else {
+ layoutAlgorithm = WebSettings.LayoutAlgorithm.NORMAL;
+ }
+ defaultTextEncodingName =
+ p.getString(PREF_DEFAULT_TEXT_ENCODING,
+ defaultTextEncodingName);
+
+ showDebugSettings =
+ p.getBoolean(PREF_DEBUG_SETTINGS, showDebugSettings);
+ // Debug menu items have precidence if the menu is visible
+ if (showDebugSettings) {
+ boolean small_screen = p.getBoolean("small_screen",
+ layoutAlgorithm ==
+ WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
+ if (small_screen) {
+ layoutAlgorithm = WebSettings.LayoutAlgorithm.SINGLE_COLUMN;
+ } else {
+ boolean normal_layout = p.getBoolean("normal_layout",
+ layoutAlgorithm == WebSettings.LayoutAlgorithm.NORMAL);
+ if (normal_layout) {
+ layoutAlgorithm = WebSettings.LayoutAlgorithm.NORMAL;
+ } else {
+ layoutAlgorithm =
+ WebSettings.LayoutAlgorithm.NARROW_COLUMNS;
+ }
+ }
+ useWideViewPort = p.getBoolean("wide_viewport", useWideViewPort);
+ tracing = p.getBoolean("enable_tracing", tracing);
+ lightTouch = p.getBoolean("enable_light_touch", lightTouch);
+ navDump = p.getBoolean("enable_nav_dump", navDump);
+ doFlick = p.getBoolean("enable_flick", doFlick);
+ userAgent = Integer.parseInt(p.getString("user_agent", "0"));
+ mTabControl.getBrowserActivity().setBaseSearchUrl(
+ p.getString("search_url", ""));
+ }
+ update();
+ }
+
+ public String getPluginsPath() {
+ return pluginsPath;
+ }
+
+ public String getHomePage() {
+ return homeUrl;
+ }
+
+ public void setHomePage(Context context, String url) {
+ Editor ed = PreferenceManager.
+ getDefaultSharedPreferences(context).edit();
+ ed.putString(PREF_HOMEPAGE, url);
+ ed.commit();
+ homeUrl = url;
+ }
+
+ public boolean isLoginInitialized() {
+ return loginInitialized;
+ }
+
+ public void setLoginInitialized(Context context) {
+ loginInitialized = true;
+ Editor ed = PreferenceManager.
+ getDefaultSharedPreferences(context).edit();
+ ed.putBoolean("login_initialized", loginInitialized);
+ ed.commit();
+ }
+
+ public WebSettings.TextSize getTextSize() {
+ return textSize;
+ }
+
+ public boolean openInBackground() {
+ return openInBackground;
+ }
+
+ public boolean showSecurityWarnings() {
+ return showSecurityWarnings;
+ }
+
+ public boolean isTracing() {
+ return tracing;
+ }
+
+ public boolean isLightTouch() {
+ return lightTouch;
+ }
+
+ public boolean isNavDump() {
+ return navDump;
+ }
+
+ public boolean doFlick() {
+ return doFlick;
+ }
+
+ public boolean showDebugSettings() {
+ return showDebugSettings;
+ }
+
+ public void toggleDebugSettings() {
+ showDebugSettings = !showDebugSettings;
+ navDump = showDebugSettings;
+ update();
+ }
+
+ /**
+ * Add a WebSettings object to the list of observers that will be updated
+ * when update() is called.
+ *
+ * @param s A WebSettings object that is strictly tied to the life of a
+ * WebView.
+ */
+ public Observer addObserver(WebSettings s) {
+ Observer old = mWebSettingsToObservers.get(s);
+ if (old != null) {
+ super.deleteObserver(old);
+ }
+ Observer o = new Observer(s);
+ mWebSettingsToObservers.put(s, o);
+ super.addObserver(o);
+ return o;
+ }
+
+ /**
+ * Delete the given WebSettings observer from the list of observers.
+ * @param s The WebSettings object to be deleted.
+ */
+ public void deleteObserver(WebSettings s) {
+ Observer o = mWebSettingsToObservers.get(s);
+ if (o != null) {
+ mWebSettingsToObservers.remove(s);
+ super.deleteObserver(o);
+ }
+ }
+
+ /*
+ * Package level method for obtaining a single app instance of the
+ * BrowserSettings.
+ */
+ /*package*/ static BrowserSettings getInstance() {
+ if (sSingleton == null ) {
+ sSingleton = new BrowserSettings();
+ }
+ return sSingleton;
+ }
+
+ /*
+ * Package level method for associating the BrowserSettings with TabControl
+ */
+ /* package */void setTabControl(TabControl tabControl) {
+ mTabControl = tabControl;
+ }
+
+ /*
+ * Update all the observers of the object.
+ */
+ /*package*/ void update() {
+ setChanged();
+ notifyObservers();
+ }
+
+ /*package*/ void clearCache(Context context) {
+ WebIconDatabase.getInstance().removeAllIcons();
+ if (mTabControl != null) {
+ WebView current = mTabControl.getCurrentWebView();
+ if (current != null) {
+ current.clearCache(true);
+ }
+ }
+ }
+
+ /*package*/ void clearCookies(Context context) {
+ CookieManager.getInstance().removeAllCookie();
+ }
+
+ /* package */void clearHistory(Context context) {
+ ContentResolver resolver = context.getContentResolver();
+ Browser.clearHistory(resolver);
+ Browser.clearSearches(resolver);
+ }
+
+ /* package */ void clearFormData(Context context) {
+ WebViewDatabase.getInstance(context).clearFormData();
+ if (mTabControl != null) {
+ mTabControl.getCurrentTopWebView().clearFormData();
+ }
+ }
+
+ /*package*/ void clearPasswords(Context context) {
+ WebViewDatabase db = WebViewDatabase.getInstance(context);
+ db.clearUsernamePassword();
+ db.clearHttpAuthUsernamePassword();
+ }
+
+ /*package*/ void resetDefaultPreferences(Context context) {
+ SharedPreferences p =
+ PreferenceManager.getDefaultSharedPreferences(context);
+ p.edit().clear().commit();
+ PreferenceManager.setDefaultValues(context, R.xml.browser_preferences,
+ true);
+ }
+
+ // Private constructor that does nothing.
+ private BrowserSettings() {
+ }
+}
diff --git a/src/com/android/browser/BrowserYesNoPreference.java b/src/com/android/browser/BrowserYesNoPreference.java
new file mode 100644
index 00000000..65cde71a
--- /dev/null
+++ b/src/com/android/browser/BrowserYesNoPreference.java
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+package com.android.browser;
+
+import com.android.internal.preference.YesNoPreference;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+class BrowserYesNoPreference extends YesNoPreference {
+
+ // This is the constructor called by the inflater
+ public BrowserYesNoPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onDialogClosed(boolean positiveResult) {
+ super.onDialogClosed(positiveResult);
+
+ if (positiveResult) {
+ setEnabled(false);
+
+ Context context = getContext();
+ if (BrowserSettings.PREF_CLEAR_CACHE.equals(getKey())) {
+ BrowserSettings.getInstance().clearCache(context);
+ } else if (BrowserSettings.PREF_CLEAR_COOKIES.equals(getKey())) {
+ BrowserSettings.getInstance().clearCookies(context);
+ } else if (BrowserSettings.PREF_CLEAR_HISTORY.equals(getKey())) {
+ BrowserSettings.getInstance().clearHistory(context);
+ } else if (BrowserSettings.PREF_CLEAR_FORM_DATA.equals(getKey())) {
+ BrowserSettings.getInstance().clearFormData(context);
+ } else if (BrowserSettings.PREF_CLEAR_PASSWORDS.equals(getKey())) {
+ BrowserSettings.getInstance().clearPasswords(context);
+ } else if (BrowserSettings.PREF_EXTRAS_RESET_DEFAULTS.equals(
+ getKey())) {
+ BrowserSettings.getInstance().resetDefaultPreferences(context);
+ setEnabled(true);
+ }
+ }
+ }
+}
diff --git a/src/com/android/browser/CombinedBookmarkHistoryActivity.java b/src/com/android/browser/CombinedBookmarkHistoryActivity.java
new file mode 100644
index 00000000..963f1792
--- /dev/null
+++ b/src/com/android/browser/CombinedBookmarkHistoryActivity.java
@@ -0,0 +1,124 @@
+/*
+ * 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.TabActivity;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.provider.Browser;
+import android.webkit.WebIconDatabase.IconListener;
+import android.widget.TabHost;
+import android.widget.TabHost.TabSpec;
+import android.view.Window;
+
+import java.util.HashMap;
+import java.util.Vector;
+
+public class CombinedBookmarkHistoryActivity extends TabActivity
+ implements TabHost.OnTabChangeListener {
+ /* package */ static String BOOKMARKS_TAB = "bookmark";
+ /* package */ static String VISITED_TAB = "visited";
+ /* package */ static String HISTORY_TAB = "history";
+ /* package */ static String STARTING_TAB = "tab";
+
+ static class IconListenerSet implements IconListener {
+ // Used to store favicons as we get them from the database
+ // FIXME: We use a different method to get the Favicons in
+ // BrowserBookmarksAdapter. They should probably be unified.
+ private HashMap<String, Bitmap> mUrlsToIcons;
+ private Vector<IconListener> mListeners;
+
+ public IconListenerSet() {
+ mUrlsToIcons = new HashMap<String, Bitmap>();
+ mListeners = new Vector<IconListener>();
+ }
+ public void onReceivedIcon(String url, Bitmap icon) {
+ mUrlsToIcons.put(url, icon);
+ for (IconListener listener : mListeners) {
+ listener.onReceivedIcon(url, icon);
+ }
+ }
+ public void addListener(IconListener listener) {
+ mListeners.add(listener);
+ }
+ public Bitmap getFavicon(String url) {
+ return (Bitmap) mUrlsToIcons.get(url);
+ }
+ }
+ private static IconListenerSet sIconListenerSet;
+ static IconListenerSet getIconListenerSet(ContentResolver cr) {
+ if (null == sIconListenerSet) {
+ sIconListenerSet = new IconListenerSet();
+ Browser.requestAllIcons(cr, null, sIconListenerSet);
+ }
+ return sIconListenerSet;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ setContentView(R.layout.tabs);
+ TabHost tabHost = getTabHost();
+ tabHost.setOnTabChangedListener(this);
+
+ Bundle extras = getIntent().getExtras();
+ Resources resources = getResources();
+
+ getIconListenerSet(getContentResolver());
+ Intent bookmarksIntent = new Intent(this, BrowserBookmarksPage.class);
+ bookmarksIntent.putExtras(extras);
+ tabHost.addTab(tabHost.newTabSpec(BOOKMARKS_TAB)
+ .setIndicator(resources.getString(R.string.tab_bookmarks),
+ resources.getDrawable(R.drawable.browser_bookmark_tab))
+ .setContent(bookmarksIntent));
+
+ Intent visitedIntent = new Intent(this, MostVisitedActivity.class);
+ visitedIntent.putExtras(extras);
+ tabHost.addTab(tabHost.newTabSpec(VISITED_TAB)
+ .setIndicator(resources.getString(R.string.tab_most_visited),
+ resources.getDrawable(R.drawable.browser_visited_tab))
+ .setContent(visitedIntent));
+
+ Intent historyIntent = new Intent(this, BrowserHistoryPage.class);
+ historyIntent.putExtras(extras);
+ tabHost.addTab(tabHost.newTabSpec(HISTORY_TAB)
+ .setIndicator(resources.getString(R.string.tab_history),
+ resources.getDrawable(R.drawable.
+ browser_history_tab)).setContent(historyIntent));
+
+ String defaultTab = extras.getString(STARTING_TAB);
+ if (defaultTab != null) {
+ tabHost.setCurrentTab(2);
+ }
+ }
+
+ // Copied from DialTacts Activity
+ /** {@inheritDoc} */
+ public void onTabChanged(String tabId) {
+ Activity activity = getLocalActivityManager().getActivity(tabId);
+ if (activity != null) {
+ activity.onWindowFocusChanged(true);
+ }
+ }
+
+
+}
diff --git a/src/com/android/browser/Dots.java b/src/com/android/browser/Dots.java
new file mode 100644
index 00000000..eb8d4934
--- /dev/null
+++ b/src/com/android/browser/Dots.java
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+package com.android.browser;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import java.util.Map;
+
+/**
+ * Displays a series of dots. The selected one is highlighted.
+ * No animations yet. Nothing fancy.
+ */
+class Dots extends LinearLayout {
+
+ private static final int MAX_DOTS = 8;
+ private int mSelected = -1;
+
+ public Dots(Context context) {
+ this(context, null);
+ }
+
+ public Dots(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ setGravity(Gravity.CENTER);
+ setPadding(0, 4, 0, 4);
+
+ LayoutParams lp =
+ new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT);
+
+ for (int i = 0; i < MAX_DOTS; i++) {
+ ImageView dotView = new ImageView(mContext);
+ dotView.setImageResource(R.drawable.page_indicator_unselected2);
+ addView(dotView, lp);
+ }
+ }
+
+ /**
+ * @param dotCount if less than 1 or greater than MAX_DOTS, Dots
+ * disappears
+ */
+ public void setDotCount(int dotCount) {
+ if (dotCount > 1 && dotCount <= MAX_DOTS) {
+ setVisibility(VISIBLE);
+ for (int i = 0; i < MAX_DOTS; i++) {
+ getChildAt(i).setVisibility(i < dotCount? VISIBLE : GONE);
+ }
+ } else {
+ setVisibility(GONE);
+ }
+ }
+
+ public void setSelected(int index) {
+ if (index < 0 || index >= MAX_DOTS) return;
+
+ if (mSelected >= 0) {
+ // Unselect old
+ ((ImageView)getChildAt(mSelected)).setImageResource(
+ R.drawable.page_indicator_unselected2);
+ }
+ ((ImageView)getChildAt(index)).setImageResource(R.drawable.page_indicator);
+ mSelected = index;
+ }
+}
diff --git a/src/com/android/browser/FakeWebView.java b/src/com/android/browser/FakeWebView.java
new file mode 100644
index 00000000..633b799d
--- /dev/null
+++ b/src/com/android/browser/FakeWebView.java
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+
+package com.android.browser;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Picture;
+import android.util.AttributeSet;
+import android.view.View;
+import android.webkit.WebView;
+import android.widget.ImageView;
+
+import android.util.Log;
+
+/**
+ * This class is used by ImageAdapter to draw a representation of each tab. It
+ * overrides ImageView so it can be used for the new tab image as well.
+ */
+public class FakeWebView extends ImageView {
+ private TabControl.Tab mTab;
+ private Picture mPicture;
+ private boolean mUsesResource;
+
+ private class Listener implements WebView.PictureListener {
+ public void onNewPicture(WebView view, Picture p) {
+ FakeWebView.this.mPicture = p;
+ FakeWebView.this.invalidate();
+ }
+ };
+
+ public FakeWebView(Context context) {
+ this(context, null);
+ }
+
+ public FakeWebView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public FakeWebView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mUsesResource) {
+ super.onDraw(canvas);
+ } else {
+ // Always draw white behind the picture just in case the picture
+ // draws nothing.
+ // FIXME: We used to draw white only when the WebView was null but
+ // sometimes the picture was empty. So now we always draw white. It
+ // would be nice to know if the picture is empty so we can avoid
+ // drawing white.
+ canvas.drawColor(Color.WHITE);
+ if (mTab != null) {
+ final WebView w = mTab.getTopWindow();
+ if (w != null) {
+ if (mPicture != null) {
+ canvas.save();
+ float scale = getWidth() * w.getScale() / w.getWidth();
+ canvas.scale(scale, scale);
+ canvas.translate(-w.getScrollX(), -w.getScrollY());
+ canvas.drawPicture(mPicture);
+ canvas.restore();
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setImageResource(int resId) {
+ mUsesResource = true;
+ mTab = null;
+ super.setImageResource(resId);
+ }
+
+ /**
+ * Set a WebView for this FakeWebView to represent.
+ * @param v WebView whose picture and other data will be used in onDraw.
+ */
+ public void setTab(TabControl.Tab t) {
+ mUsesResource = false;
+ mTab = t;
+ if (t != null && t.getWebView() != null) {
+ Listener l = new Listener();
+ if (t.getSubWebView() != null) {
+ t.getSubWebView().setPictureListener(l);
+ } else {
+ t.getWebView().setPictureListener(l);
+ }
+ mPicture = mTab.getTopWindow().capturePicture();
+ }
+ }
+}
diff --git a/src/com/android/browser/FetchUrlMimeType.java b/src/com/android/browser/FetchUrlMimeType.java
new file mode 100644
index 00000000..8578643b
--- /dev/null
+++ b/src/com/android/browser/FetchUrlMimeType.java
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+
+package com.android.browser;
+
+import android.content.ContentValues;
+import android.net.Uri;
+import android.net.http.AndroidHttpClient;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.Header;
+import org.apache.http.client.methods.HttpHead;
+
+import java.io.IOException;
+
+import android.os.AsyncTask;
+import android.provider.Downloads;
+import android.webkit.MimeTypeMap;
+import android.webkit.URLUtil;
+
+/**
+ * This class is used to pull down the http headers of a given URL so that
+ * we can analyse the mimetype and make any correction needed before we give
+ * the URL to the download manager. The ContentValues class holds the
+ * content that would be provided to the download manager, so that on
+ * completion of checking the mimetype, we can issue the download to
+ * the download manager.
+ * This operation is needed when the user long-clicks on a link or image and
+ * we don't know the mimetype. If the user just clicks on the link, we will
+ * do the same steps of correcting the mimetype down in
+ * android.os.webkit.LoadListener rather than handling it here.
+ *
+ */
+class FetchUrlMimeType extends AsyncTask<ContentValues, String, String> {
+
+ BrowserActivity mActivity;
+ ContentValues mValues;
+
+ public FetchUrlMimeType(BrowserActivity activity) {
+ mActivity = activity;
+ }
+
+ @Override
+ public String doInBackground(ContentValues... values) {
+ mValues = values[0];
+
+ // Check to make sure we have a URI to download
+ String uri = mValues.getAsString(Downloads.URI);
+ if (uri == null || uri.length() == 0) {
+ return null;
+ }
+
+ // User agent is likely to be null, though the AndroidHttpClient
+ // seems ok with that.
+ AndroidHttpClient client = AndroidHttpClient.newInstance(
+ mValues.getAsString(Downloads.USER_AGENT));
+ HttpHead request = new HttpHead(uri);
+
+ String cookie = mValues.getAsString(Downloads.COOKIE_DATA);
+ if (cookie != null && cookie.length() > 0) {
+ request.addHeader("Cookie", cookie);
+ }
+
+ String referer = mValues.getAsString(Downloads.REFERER);
+ if (referer != null && referer.length() > 0) {
+ request.addHeader("Referer", referer);
+ }
+
+ HttpResponse response;
+ Boolean succeeded = true;
+ String mimeType = null;
+ try {
+ response = client.execute(request);
+ // We could get a redirect here, but if we do lets let
+ // the download manager take care of it, and thus trust that
+ // the server sends the right mimetype
+ if (response.getStatusLine().getStatusCode() == 200) {
+ Header header = response.getFirstHeader("Content-Type");
+ if (header != null) {
+ mimeType = header.getValue();
+ final int semicolonIndex = mimeType.indexOf(';');
+ if (semicolonIndex != -1) {
+ mimeType = mimeType.substring(0, semicolonIndex);
+ }
+ }
+ }
+ } catch (IllegalArgumentException ex) {
+ request.abort();
+ } catch (IOException ex) {
+ request.abort();
+ } finally {
+ client.close();
+ }
+
+ return mimeType;
+ }
+
+ @Override
+ public void onPostExecute(String mimeType) {
+ if (mimeType != null) {
+ String url = mValues.getAsString(Downloads.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);
+ }
+ }
+ String filename = URLUtil.guessFileName(url,
+ null, mimeType);
+ mValues.put(Downloads.FILENAME_HINT, filename);
+ }
+
+ // Start the download
+ final Uri contentUri =
+ mActivity.getContentResolver().insert(Downloads.CONTENT_URI, mValues);
+ mActivity.viewDownloads(contentUri);
+ }
+
+}
diff --git a/src/com/android/browser/FindDialog.java b/src/com/android/browser/FindDialog.java
new file mode 100644
index 00000000..43cd1c40
--- /dev/null
+++ b/src/com/android/browser/FindDialog.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2007 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.Dialog;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.text.Editable;
+import android.text.Spannable;
+import android.text.TextWatcher;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
+import android.webkit.WebView;
+import android.widget.EditText;
+import android.widget.TextView;
+
+/* package */ class FindDialog extends Dialog implements TextWatcher {
+ private WebView mWebView;
+ private TextView mMatches;
+ private BrowserActivity mBrowserActivity;
+
+ // Views with which the user can interact.
+ private View mOk;
+ private EditText mEditText;
+ private View mNextButton;
+ private View mPrevButton;
+ private View mMatchesView;
+
+ private View.OnClickListener mFindListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ findNext();
+ }
+ };
+
+ private View.OnClickListener mFindCancelListener =
+ new View.OnClickListener() {
+ public void onClick(View v) {
+ dismiss();
+ }
+ };
+
+ private View.OnClickListener mFindPreviousListener =
+ new View.OnClickListener() {
+ public void onClick(View v) {
+ if (mWebView == null) {
+ throw new AssertionError("No WebView for FindDialog::onClick");
+ }
+ mWebView.findNext(false);
+ hideSoftInput();
+ }
+ };
+
+ /*
+ * Remove the soft keyboard from the screen.
+ */
+ private void hideSoftInput() {
+ InputMethodManager imm = (InputMethodManager)
+ mBrowserActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
+ }
+
+ private void disableButtons() {
+ mPrevButton.setEnabled(false);
+ mNextButton.setEnabled(false);
+ mPrevButton.setFocusable(false);
+ mNextButton.setFocusable(false);
+ }
+
+ /* package */ void setWebView(WebView webview) {
+ mWebView = webview;
+ }
+
+ /* package */ FindDialog(BrowserActivity context) {
+ super(context, R.style.FindDialogTheme);
+ mBrowserActivity = context;
+ setCanceledOnTouchOutside(true);
+ }
+
+ /* package */ void onConfigurationChanged(Configuration newConfig) {
+ // FIXME: Would like to call mWebView.findAll again, so that the
+ // matches would refresh, but the new picture has not yet been
+ // created, so it is too soon.
+ mEditText.getText().clear();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Window theWindow = getWindow();
+ theWindow.setGravity(Gravity.BOTTOM|Gravity.FILL_HORIZONTAL);
+
+ setContentView(R.layout.browser_find);
+
+ theWindow.setLayout(ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+
+ mEditText = (EditText) findViewById(R.id.edit);
+
+ View button = findViewById(R.id.next);
+ button.setOnClickListener(mFindListener);
+ mNextButton = button;
+
+ button = findViewById(R.id.previous);
+ button.setOnClickListener(mFindPreviousListener);
+ mPrevButton = button;
+
+ button = findViewById(R.id.done);
+ button.setOnClickListener(mFindCancelListener);
+ mOk = button;
+
+ mMatches = (TextView) findViewById(R.id.matches);
+ mMatchesView = findViewById(R.id.matches_view);
+ disableButtons();
+ theWindow.setSoftInputMode(
+ WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
+ }
+
+ public void dismiss() {
+ super.dismiss();
+ 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;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ private void findNext() {
+ if (mWebView == null) {
+ throw new AssertionError("No WebView for FindDialog::findNext");
+ }
+ mWebView.findNext(true);
+ hideSoftInput();
+ }
+
+ public void show() {
+ super.show();
+ mEditText.requestFocus();
+ mEditText.setText("");
+ Spannable span = (Spannable) mEditText.getText();
+ span.setSpan(this, 0, span.length(),
+ Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+ mMatches.setText(R.string.zero);
+ disableButtons();
+ }
+
+ // TextWatcher methods
+ public void beforeTextChanged(CharSequence s,
+ int start,
+ int count,
+ int after) {
+ }
+
+ public void onTextChanged(CharSequence s,
+ int start,
+ int before,
+ int count) {
+ if (mWebView == null) {
+ throw new AssertionError(
+ "No WebView for FindDialog::onTextChanged");
+ }
+ CharSequence find = mEditText.getText();
+ if (0 == find.length()) {
+ disableButtons();
+ mWebView.clearMatches();
+ mMatchesView.setVisibility(View.INVISIBLE);
+ } else {
+ mMatchesView.setVisibility(View.VISIBLE);
+ int found = mWebView.findAll(find.toString());
+ mMatches.setText(Integer.toString(found));
+ if (found < 2) {
+ disableButtons();
+ if (found == 0) {
+ mMatches.setText(R.string.zero);
+ }
+ } else {
+ mPrevButton.setFocusable(true);
+ mNextButton.setFocusable(true);
+ mPrevButton.setEnabled(true);
+ mNextButton.setEnabled(true);
+ }
+ }
+ }
+
+ public void afterTextChanged(Editable s) {
+ }
+}
diff --git a/src/com/android/browser/GearsBaseDialog.java b/src/com/android/browser/GearsBaseDialog.java
new file mode 100644
index 00000000..638ba273
--- /dev/null
+++ b/src/com/android/browser/GearsBaseDialog.java
@@ -0,0 +1,474 @@
+/*
+ * 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.
+ */
+
+package com.android.browser;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Handler;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.style.UnderlineSpan;
+import android.util.Log;
+import android.view.InflateException;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.lang.ClassCastException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Base dialog class for gears
+ */
+class GearsBaseDialog {
+
+ private static final String TAG = "GearsNativeDialog";
+ protected Handler mHandler;
+ protected Activity mActivity;
+ protected String mDialogArguments;
+
+ private Bitmap mIcon;
+ private final int MAX_ICON_SIZE = 64;
+ protected int mChoosenIconSize;
+
+ // Dialog closing types
+ public static final int CANCEL = 0;
+ public static final int ALWAYS_DENY = 1;
+ public static final int ALLOW = 2;
+ public static final int DENY = 3;
+ public static final int NEW_ICON = 4;
+ public static final int UPDATE_ICON = 5;
+ public static final int REQUEST_ICON = 6;
+ public static final int PAUSE_REQUEST_ICON = 7;
+ public static final int CLEAR_REQUEST_ICON = 8;
+
+ protected final String LOCAL_DATA_STRING = "localData";
+ protected final String LOCAL_STORAGE_STRING = "localStorage";
+ protected final String LOCATION_DATA_STRING = "locationData";
+
+ protected String mGearsVersion = "UNDEFINED";
+ protected boolean mDebug = false;
+
+ public GearsBaseDialog(Activity activity, Handler handler, String arguments) {
+ mActivity = activity;
+ mHandler = handler;
+ mDialogArguments = arguments;
+ }
+
+ Resources getResources() {
+ return mActivity.getResources();
+ }
+
+ Object getSystemService(String name) {
+ return mActivity.getSystemService(name);
+ }
+
+ View findViewById(int id) {
+ return mActivity.findViewById(id);
+ }
+
+ private String getString(int id) {
+ return mActivity.getString(id);
+ }
+
+ public void setDebug(boolean debug) {
+ mDebug = debug;
+ }
+
+ public void setGearsVersion(String version) {
+ mGearsVersion = version;
+ }
+
+ public String closeDialog(int closingType) {
+ return null;
+ }
+
+ /*
+ * Utility methods for setting up the dialogs elements
+ */
+
+ /**
+ * Inflate a given layout in a view (which has to be
+ * a ViewGroup, e.g. LinearLayout).
+ * This is used to share the basic dialog outline among
+ * the different dialog types.
+ */
+ void inflate(int layout, int viewID) {
+ LayoutInflater inflater = (LayoutInflater) getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ View view = findViewById(viewID);
+ if (view != null) {
+ try {
+ ViewGroup viewGroup = (ViewGroup) view;
+ inflater.inflate(layout, viewGroup);
+ } catch (ClassCastException e) {
+ String msg = "exception, the view (" + view + ")";
+ msg += " is not a ViewGroup";
+ Log.e(TAG, msg, e);
+ } catch (InflateException e) {
+ Log.e(TAG, "exception while inflating the layout", e);
+ }
+ } else {
+ String msg = "problem, trying to inflate a non-existent view";
+ msg += " (" + viewID + ")";
+ Log.e(TAG, msg);
+ }
+ }
+
+ /**
+ * Button setup.
+ * Set the button's text and its listener. If the text resource's id
+ * is 0, makes the button invisible.
+ */
+ void setupButton(int buttonRscID,
+ int rscString,
+ View.OnClickListener listener,
+ boolean isLink,
+ boolean requestFocus) {
+ View view = findViewById(buttonRscID);
+ if (view == null) {
+ return;
+ }
+
+ Button button = (Button) view;
+
+ if (rscString == 0) {
+ button.setVisibility(View.GONE);
+ } else {
+ CharSequence text = getString(rscString);
+ button.setText(text);
+ button.setOnClickListener(listener);
+ if (isLink) {
+ displayAsLink(button);
+ }
+ if (requestFocus) {
+ button.requestFocus();
+ }
+ }
+ }
+
+ /**
+ * Button setup: as the above method, except that 'isLink' and
+ * 'requestFocus' default to false.
+ */
+ void setupButton(int buttonRsc, int rsc,
+ View.OnClickListener listener) {
+ setupButton(buttonRsc, rsc, listener, false, false);
+ }
+
+ /**
+ * Utility method to setup the three dialog buttons.
+ */
+ void setupButtons(int alwaysDenyRsc, int allowRsc, int denyRsc) {
+ setupButton(R.id.button_alwaysdeny, alwaysDenyRsc,
+ new Button.OnClickListener() {
+ public void onClick(View v) {
+ mHandler.sendEmptyMessage(ALWAYS_DENY);
+ }
+ });
+
+ setupButton(R.id.button_allow, allowRsc,
+ new Button.OnClickListener() {
+ public void onClick(View v) {
+ mHandler.sendEmptyMessage(ALLOW);
+ }
+ });
+
+ setupButton(R.id.button_deny, denyRsc,
+ new Button.OnClickListener() {
+ public void onClick(View v) {
+ mHandler.sendEmptyMessage(DENY);
+ }
+ });
+ }
+
+ /**
+ * Display a button as an HTML link. Remove the background, set the
+ * text color to R.color.dialog_link and draw an underline
+ */
+ void displayAsLink(Button button) {
+ if (button == null) {
+ return;
+ }
+
+ CharSequence text = button.getText();
+ button.setBackgroundDrawable(null);
+ int color = getResources().getColor(R.color.dialog_link);
+ button.setTextColor(color);
+ SpannableString str = new SpannableString(text);
+ str.setSpan(new UnderlineSpan(), 0, str.length(),
+ Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+ button.setText(str);
+ button.setFocusable(false);
+ }
+
+ /**
+ * Utility method to set elements' text indicated in
+ * the dialogs' arguments.
+ */
+ void setLabel(JSONObject json, String name, int rsc) {
+ try {
+ if (json.has(name)) {
+ String text = json.getString(name);
+ View view = findViewById(rsc);
+ if (view != null && text != null) {
+ TextView textView = (TextView) view;
+ textView.setText(text);
+ textView.setVisibility(View.VISIBLE);
+ }
+ }
+ } catch (JSONException e) {
+ Log.e(TAG, "json exception", e);
+ }
+ }
+
+ /**
+ * Utility method to hide a view.
+ */
+ void hideView(View v, int rsc) {
+ if (rsc == 0) {
+ return;
+ }
+ View view;
+ if (v == null) {
+ view = findViewById(rsc);
+ } else {
+ view = v.findViewById(rsc);
+ }
+ if (view != null) {
+ view.setVisibility(View.GONE);
+ }
+ }
+
+ /**
+ * Utility method to show a view.
+ */
+ void showView(View v, int rsc) {
+ if (rsc == 0) {
+ return;
+ }
+ View view;
+ if (v == null) {
+ view = findViewById(rsc);
+ } else {
+ view = v.findViewById(rsc);
+ }
+ if (view != null) {
+ view.setVisibility(View.VISIBLE);
+ }
+ }
+
+ /**
+ * Utility method to set a text.
+ */
+ void setText(View v, int rsc, CharSequence text) {
+ if (rsc == 0) {
+ return;
+ }
+ View view = v.findViewById(rsc);
+ if (view != null) {
+ TextView textView = (TextView) view;
+ textView.setText(text);
+ textView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ /**
+ * Utility method to set a text.
+ */
+ void setText(View v, int rsc, int txtRsc) {
+ if (rsc == 0) {
+ return;
+ }
+ View view = v.findViewById(rsc);
+ if (view != null) {
+ TextView textView = (TextView) view;
+ if (txtRsc == 0) {
+ textView.setVisibility(View.GONE);
+ } else {
+ CharSequence text = getString(txtRsc);
+ textView.setText(text);
+ textView.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+ /**
+ * Utility class to download an icon in the background.
+ * Once done ask the UI thread to update the icon.
+ */
+ class IconDownload implements Runnable {
+ private String mUrlString;
+
+ IconDownload(String url) {
+ mUrlString = url;
+ }
+
+ public void run() {
+ if (mUrlString == null) {
+ return;
+ }
+ try {
+ URL url = new URL(mUrlString);
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ connection.setDoInput(true);
+ connection.connect();
+ int length = connection.getContentLength();
+ InputStream is = connection.getInputStream();
+ Bitmap customIcon = BitmapFactory.decodeStream(is);
+ if (customIcon != null) {
+ mIcon = customIcon;
+ mHandler.sendEmptyMessage(UPDATE_ICON);
+ }
+ } catch (ClassCastException e) {
+ Log.e(TAG, "Class cast exception (" + mUrlString + ")", e);
+ } catch (MalformedURLException e) {
+ Log.e(TAG, "Malformed url (" + mUrlString + ") ", e);
+ } catch (IOException e) {
+ Log.e(TAG, "Exception downloading icon (" + mUrlString + ") ", e);
+ }
+ }
+ }
+
+ /**
+ * Utility method to update the icon.
+ * Called on the UI thread.
+ */
+ public void updateIcon() {
+ if (mIcon == null) {
+ return;
+ }
+ View view = findViewById(R.id.origin_icon);
+ if (view != null) {
+ ImageView imageView = (ImageView) view;
+ imageView.setMaxHeight(MAX_ICON_SIZE);
+ imageView.setMaxWidth(MAX_ICON_SIZE);
+ imageView.setScaleType(ImageView.ScaleType.FIT_XY);
+ imageView.setImageBitmap(mIcon);
+ imageView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ /**
+ * Utility method to download an icon from a url and set
+ * it to the GUI element R.id.origin_icon.
+ * It is used both in the shortcut dialog and the
+ * permission dialog.
+ * The actual download is done in the background via
+ * IconDownload; once the icon is downlowded the UI is updated
+ * via updateIcon().
+ * The icon size is included in the layout with the choosen
+ * size, although not displayed, to limit text reflow once
+ * the icon is received.
+ */
+ void downloadIcon(String url) {
+ if (url == null) {
+ return;
+ }
+ View view = findViewById(R.id.origin_icon);
+ if (view != null) {
+ view.setMinimumWidth(mChoosenIconSize);
+ view.setMinimumHeight(mChoosenIconSize);
+ view.setVisibility(View.INVISIBLE);
+ }
+ Thread thread = new Thread(new IconDownload(url));
+ thread.start();
+ }
+
+ /**
+ * Utility method that get the dialogMessage
+ * and icon and ask the setupDialog(message,icon)
+ * method to set the values.
+ */
+ public void setupDialog() {
+ TextView dialogMessage = null;
+ ImageView icon = null;
+
+ View view = findViewById(R.id.dialog_message);
+ if (view != null) {
+ dialogMessage = (TextView) view;
+ }
+
+ View iconView = findViewById(R.id.icon);
+ if (iconView != null) {
+ icon = (ImageView) iconView;
+ }
+
+ if ((dialogMessage != null) && (icon != null)) {
+ setupDialog(dialogMessage, icon);
+ dialogMessage.setVisibility(View.VISIBLE);
+ }
+ }
+
+ /*
+ * Set the message and icon of the dialog
+ */
+ public void setupDialog(TextView message, ImageView icon) {
+ message.setText(R.string.unrecognized_dialog_message);
+ icon.setImageResource(R.drawable.ic_dialog_menu_generic);
+ message.setVisibility(View.VISIBLE);
+ }
+
+ /**
+ * Setup the dialog
+ * By default, just display a simple message.
+ */
+ public void setup() {
+ setupButtons(0, 0, R.string.default_button);
+ setupDialog();
+ }
+
+ /**
+ * Method called when the back button is pressed,
+ * allowing the dialog to intercept the default behaviour.
+ */
+ public boolean handleBackButton() {
+ return false;
+ }
+
+ /**
+ * Returns the resource string of the notification displayed
+ * after the dialog. By default, does not return one.
+ */
+ public int notification() {
+ return 0;
+ }
+
+ /**
+ * If a secondary dialog (e.g. a confirmation dialog) is created,
+ * GearsNativeDialog will call this method.
+ */
+ public Dialog onCreateDialog(int id) {
+ // This should be redefined by subclasses as needed.
+ return null;
+ }
+
+}
diff --git a/src/com/android/browser/GearsNativeDialog.java b/src/com/android/browser/GearsNativeDialog.java
new file mode 100644
index 00000000..ecf166df
--- /dev/null
+++ b/src/com/android/browser/GearsNativeDialog.java
@@ -0,0 +1,280 @@
+/*
+ * 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.
+ */
+
+package com.android.browser;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Config;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.Window;
+import android.widget.BaseAdapter;
+import android.widget.Toast;
+
+import android.webkit.gears.NativeDialog;
+
+import com.android.browser.GearsBaseDialog;
+import com.android.browser.GearsPermissionsDialog;
+import com.android.browser.GearsSettingsDialog;
+
+/**
+ * Native dialog Activity used by gears
+ * TODO: rename in GearsNativeDialogActivity
+ * @hide
+ */
+public class GearsNativeDialog extends Activity {
+
+ private static final String TAG = "GearsNativeDialog";
+
+ private String mDialogArguments;
+
+ private String mGearsVersion = null;
+
+ private boolean mDebug = false;
+
+ private int mDialogType;
+ private final int SETTINGS_DIALOG = 1;
+ private final int PERMISSION_DIALOG = 2;
+ private final int LOCATION_DIALOG = 3;
+
+ private final String VERSION_STRING = "version";
+ private final String SETTINGS_DIALOG_STRING = "settings_dialog";
+ private final String PERMISSION_DIALOG_STRING = "permissions_dialog";
+ private final String LOCATION_DIALOG_STRING = "locations_dialog";
+
+ private boolean mDialogDismissed = false;
+
+ GearsBaseDialog dialog;
+
+ // Handler for callbacks to the UI thread
+ final Handler mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ if (msg.what == GearsBaseDialog.NEW_ICON) {
+ BaseAdapter adapter = (BaseAdapter) msg.obj;
+ adapter.notifyDataSetChanged();
+ } else if (msg.what == GearsBaseDialog.UPDATE_ICON) {
+ dialog.updateIcon();
+ } else if (msg.what == GearsBaseDialog.ALWAYS_DENY) {
+ closeDialog(GearsBaseDialog.ALWAYS_DENY);
+ } else if (msg.what == GearsBaseDialog.ALLOW) {
+ closeDialog(GearsBaseDialog.ALLOW);
+ } else if (msg.what == GearsBaseDialog.DENY) {
+ closeDialog(GearsBaseDialog.DENY);
+ }
+ super.handleMessage(msg);
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ getArguments();
+ if (mDialogType == SETTINGS_DIALOG) {
+ setTheme(android.R.style.Theme);
+ }
+ super.onCreate(icicle);
+ if (mDialogType != SETTINGS_DIALOG) {
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ setContentView(R.layout.gears_dialog);
+ }
+
+ switch (mDialogType) {
+ case SETTINGS_DIALOG:
+ dialog = new GearsSettingsDialog(this, mHandler, mDialogArguments);
+ dialog.setGearsVersion(mGearsVersion);
+ break;
+ case PERMISSION_DIALOG:
+ dialog = new GearsPermissionsDialog(this, mHandler, mDialogArguments);
+ break;
+ case LOCATION_DIALOG:
+ dialog = new GearsPermissionsDialog(this, mHandler, mDialogArguments);
+ break;
+ default:
+ dialog = new GearsBaseDialog(this, mHandler, mDialogArguments);
+ }
+ dialog.setDebug(mDebug);
+ dialog.setup();
+ }
+
+ /**
+ * Get the arguments for the dialog
+ *
+ * The dialog needs a json string as an argument, as
+ * well as a dialogType. In debug mode the arguments
+ * are mocked.
+ */
+ private void getArguments() {
+ if (mDebug) {
+ mDialogType = LOCATION_DIALOG +1;
+ mockArguments();
+
+ return;
+ }
+
+ Intent intent = getIntent();
+ mDialogArguments = intent.getStringExtra("dialogArguments");
+ String dialogTypeString = intent.getStringExtra("dialogType");
+ if (dialogTypeString == null) {
+ return;
+ }
+
+ if (Config.LOGV) {
+ Log.v(TAG, "dialogtype: " + dialogTypeString);
+ }
+
+ if (dialogTypeString.equalsIgnoreCase(SETTINGS_DIALOG_STRING)) {
+ mDialogType = SETTINGS_DIALOG;
+ mGearsVersion = intent.getStringExtra(VERSION_STRING);
+ } else if (dialogTypeString.equalsIgnoreCase(PERMISSION_DIALOG_STRING)) {
+ mDialogType = PERMISSION_DIALOG;
+ } else if (dialogTypeString.equalsIgnoreCase(LOCATION_DIALOG_STRING)) {
+ mDialogType = LOCATION_DIALOG;
+ }
+ }
+
+ /**
+ * Utility method for debugging the dialog.
+ *
+ * Set mock arguments.
+ */
+ private void mockArguments() {
+
+ String argumentsPermissions = "{ locale: \"en-US\", "
+ + "origin: \"http://www.google.com\", dialogType: \"localData\","
+ + "customIcon: \"http://google-gears.googlecode.com/"
+ + "svn/trunk/gears/test/manual/shortcuts/32.png\","
+ + "customName: \"My Application\","
+ + "customMessage: \"Press the button to enable my "
+ + "application to run offline!\" };";
+
+ String argumentsPermissions2 = "{ locale: \"en-US\", "
+ + "origin: \"http://www.google.com\", dialogType: \"localData\" };";
+
+ String argumentsLocation = "{ locale: \"en-US\", "
+ + "origin: \"http://www.google.com\", dialogType: \"locationData\","
+ + "customIcon: \"http://google-gears.googlecode.com/"
+ + "svn/trunk/gears/test/manual/shortcuts/32.png\","
+ + "customName: \"My Application\","
+ + "customMessage: \"Press the button to enable my "
+ + "application to run offline!\" };";
+
+ String argumentsSettings = "{ locale: \"en-US\", permissions: [ { "
+ + "name: \"http://www.google.com\", "
+ + "localStorage: { permissionState: 0 }, "
+ + "locationData: { permissionState: 1 } }, "
+ + "{ name: \"http://www.aaronboodman.com\", "
+ + "localStorage: { permissionState: 1 }, "
+ + "locationData: { permissionState: 2 } }, "
+ + "{ name: \"http://www.evil.org\", "
+ + "localStorage: { permissionState: 2 }, "
+ + "locationData: { permissionState: 2 } } ] }";
+
+ switch (mDialogType) {
+ case PERMISSION_DIALOG:
+ mDialogArguments = argumentsPermissions;
+ break;
+ case LOCATION_DIALOG:
+ mDialogArguments = argumentsLocation;
+ break;
+ case SETTINGS_DIALOG:
+ mDialogArguments = argumentsSettings;
+ break;
+ }
+ }
+
+ /**
+ * Close the dialog and set the return string value.
+ */
+ private void closeDialog(int closingType) {
+ String ret = dialog.closeDialog(closingType);
+
+ if (mDebug) {
+ Log.v(TAG, "closeDialog ret value: " + ret);
+ }
+
+ NativeDialog.closeDialog(ret);
+ notifyEndOfDialog();
+ finish();
+
+ // If the dialog sets a notification, we display it.
+ int notification = dialog.notification();
+ if (notification != 0) {
+ Toast toast = Toast.makeText(this, notification, Toast.LENGTH_LONG);
+ toast.setGravity(Gravity.BOTTOM, 0, 0);
+ toast.show();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ // In case we reach this point without
+ // notifying NativeDialog, we do it now.
+ if (!mDialogDismissed) {
+ notifyEndOfDialog();
+ }
+ }
+
+ @Override
+ public void onPause(){
+ super.onPause();
+ if (!mDialogDismissed) {
+ closeDialog(GearsBaseDialog.CANCEL);
+ }
+ }
+
+ /**
+ * Signal to NativeDialog that we are done.
+ */
+ private void notifyEndOfDialog() {
+ NativeDialog.signalFinishedDialog();
+ mDialogDismissed = true;
+ }
+
+ /**
+ * 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 (!dialog.handleBackButton()) {
+ // if the dialog doesn't do anything with the back button
+ closeDialog(GearsBaseDialog.CANCEL);
+ }
+ return true; // event consumed
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ /**
+ * If the dialog call showDialog() on ourself, we let
+ * it handle the creation of this secondary dialog.
+ * It is used in GearsSettingsDialog, to create the confirmation
+ * dialog when the user click on "Remove this site from Gears"
+ */
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ return dialog.onCreateDialog(id);
+ }
+
+}
diff --git a/src/com/android/browser/GearsPermissions.java b/src/com/android/browser/GearsPermissions.java
new file mode 100644
index 00000000..e48e045d
--- /dev/null
+++ b/src/com/android/browser/GearsPermissions.java
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+
+package com.android.browser;
+
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * The permission mechanism works the following way:
+ *
+ * PermissionType allows to define a type of permission
+ * (e.g. localStorage/locationData), storing a name and a set of
+ * resource ids corresponding to the GUI resources.
+ *
+ * Permission defines an actual permission instance, with a type and a value.
+ *
+ * OriginPermissions holds an origin with a set of Permission objects
+ */
+class GearsPermissions {
+
+ private static final String TAG = "GearsPermissions";
+
+ /**
+ * Defines a type of permission
+ *
+ * Store the permission's name (used in the json result)
+ * Graphically, each permission is a label followed by two radio buttons.
+ * We store the resources ids here.
+ */
+ public static class PermissionType {
+ public static final int PERMISSION_NOT_SET = 0;
+ public static final int PERMISSION_ALLOWED = 1;
+ public static final int PERMISSION_DENIED = 2;
+
+ String mName;
+ int mTitleRsc;
+ int mSubtitleOnRsc;
+ int mSubtitleOffRsc;
+
+ PermissionType(String name) {
+ mName = name;
+ }
+
+ public void setResources(int titleRsc,
+ int subtitleOnRsc, int subtitleOffRsc) {
+ mTitleRsc = titleRsc;
+ mSubtitleOnRsc = subtitleOnRsc;
+ mSubtitleOffRsc = subtitleOffRsc;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public int getTitleRsc() {
+ return mTitleRsc;
+ }
+
+ public int getSubtitleOnRsc() {
+ return mSubtitleOnRsc;
+ }
+
+ public int getSubtitleOffRsc() {
+ return mSubtitleOffRsc;
+ }
+
+ }
+
+ /**
+ * Simple class to store an instance of a permission
+ *
+ * i.e. a permission type and a value
+ * Value can be either PERMISSION_NOT_SET,
+ * PERMISSION_ALLOWED or PERMISSION_DENIED
+ * (defined in PermissionType).
+ */
+ public static class Permission {
+ PermissionType mType;
+ int mValue;
+
+ Permission(PermissionType type, int value) {
+ mType = type;
+ mValue = value;
+ }
+
+ Permission(PermissionType type) {
+ mType = type;
+ mValue = 0;
+ }
+
+ public PermissionType getType() {
+ return mType;
+ }
+
+ public void setValue(int value) {
+ mValue = value;
+ }
+
+ public int getValue() {
+ return mValue;
+ }
+ }
+
+ /**
+ * Interface used by the GearsNativeDialog implementation
+ * to listen to changes in the permissions.
+ */
+ public interface PermissionsChangesListener {
+ public boolean setPermission(PermissionType type, int perm);
+ }
+
+ /**
+ * Holds the model for an origin -- each origin has a set of
+ * permissions.
+ */
+ public static class OriginPermissions {
+ HashMap<PermissionType, Permission> mPermissions;
+ String mOrigin;
+ public static PermissionsChangesListener mListener;
+
+ public static void setListener(PermissionsChangesListener listener) {
+ mListener = listener;
+ }
+
+ OriginPermissions(String anOrigin) {
+ mOrigin = anOrigin;
+ mPermissions = new HashMap<PermissionType, Permission>();
+ }
+
+ OriginPermissions(OriginPermissions perms) {
+ mOrigin = perms.getOrigin();
+ mPermissions = new HashMap<PermissionType, Permission>();
+ HashMap<PermissionType, Permission> permissions = perms.getPermissions();
+ Iterator<PermissionType> iterator = permissions.keySet().iterator();
+ while (iterator.hasNext()) {
+ Permission permission = permissions.get(iterator.next());
+ int value = permission.getValue();
+ setPermission(permission.getType(), value);
+ }
+ }
+
+ public String getOrigin() {
+ return mOrigin;
+ }
+
+ public HashMap<PermissionType, Permission> getPermissions() {
+ return mPermissions;
+ }
+
+ public int getPermission(PermissionType type) {
+ return mPermissions.get(type).getValue();
+ }
+
+ public void setPermission(PermissionType type, int perm) {
+ if (mPermissions.get(type) == null) {
+ Permission permission = new Permission(type, perm);
+ mPermissions.put(type, permission);
+ return;
+ }
+
+ if (mListener != null) {
+ mListener.setPermission(type, perm);
+ }
+
+ mPermissions.get(type).setValue(perm);
+ }
+
+ public void print() {
+ Log.v(TAG, "Permissions for " + mOrigin);
+ Iterator<PermissionType> iterator = mPermissions.keySet().iterator();
+ while (iterator.hasNext()) {
+ Permission permission = mPermissions.get(iterator.next());
+ String name = permission.getType().getName();
+ int value = permission.getValue();
+ Log.v(TAG, " " + name + ": " + value);
+ }
+ }
+ }
+
+}
diff --git a/src/com/android/browser/GearsPermissionsDialog.java b/src/com/android/browser/GearsPermissionsDialog.java
new file mode 100644
index 00000000..dbec363a
--- /dev/null
+++ b/src/com/android/browser/GearsPermissionsDialog.java
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+
+package com.android.browser;
+
+import android.app.Activity;
+import android.os.Handler;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Gears permission dialog
+ */
+class GearsPermissionsDialog extends GearsBaseDialog {
+
+ private static final String TAG = "GearsPermissionsDialog";
+
+ private String mDialogType;
+ private int mNotification = 0;
+
+ public GearsPermissionsDialog(Activity activity,
+ Handler handler,
+ String arguments) {
+ super (activity, handler, arguments);
+ }
+
+ public void setup() {
+ inflate(R.layout.gears_dialog_permission, R.id.panel_content);
+ setupButtons(R.string.permission_button_alwaysdeny,
+ R.string.permission_button_allow,
+ R.string.permission_button_deny);
+
+ try {
+ JSONObject json = new JSONObject(mDialogArguments);
+
+ if (json.has("dialogType")) {
+ mDialogType = json.getString("dialogType");
+ setupDialog();
+ }
+
+ if (!json.has("customName")) {
+ setLabel(json, "origin", R.id.origin_title);
+ View titleView = findViewById(R.id.origin_title);
+ if (titleView != null) {
+ TextView title = (TextView) titleView;
+ title.setGravity(Gravity.CENTER);
+ }
+ } else {
+ setLabel(json, "customName", R.id.origin_title);
+ setLabel(json, "origin", R.id.origin_subtitle);
+ setLabel(json, "customMessage", R.id.origin_message);
+ }
+
+ if (json.has("customIcon")) {
+ String iconUrl = json.getString("customIcon");
+ mChoosenIconSize = 32;
+ downloadIcon(iconUrl);
+ }
+
+ View msg = findViewById(R.id.permission_dialog_message);
+ if (msg != null) {
+ TextView dialogMessage = (TextView) msg;
+ if (mDialogType.equalsIgnoreCase(LOCAL_DATA_STRING)) {
+ dialogMessage.setText(R.string.query_data_message);
+ } else if (mDialogType.equalsIgnoreCase(LOCATION_DATA_STRING)) {
+ dialogMessage.setText(R.string.location_message);
+ }
+ }
+
+ } catch (JSONException e) {
+ Log.e(TAG, "JSON exception ", e);
+ }
+ }
+
+ public void setupDialog(TextView message, ImageView icon) {
+ if (mDialogType.equalsIgnoreCase(LOCAL_DATA_STRING)) {
+ message.setText(R.string.query_data_prompt);
+ icon.setImageResource(android.R.drawable.ic_popup_disk_full);
+ } else if (mDialogType.equalsIgnoreCase(LOCATION_DATA_STRING)) {
+ message.setText(R.string.location_prompt);
+ icon.setImageResource(R.drawable.ic_dialog_menu_generic);
+ }
+ }
+
+ public String closeDialog(int closingType) {
+ String ret = null;
+ switch (closingType) {
+ case ALWAYS_DENY:
+ ret = "{\"allow\": false, \"permanently\": true }";
+ if (mDialogType.equalsIgnoreCase(LOCAL_DATA_STRING)) {
+ mNotification = R.string.storage_notification_alwaysdeny;
+ } else if (mDialogType.equalsIgnoreCase(LOCATION_DATA_STRING)) {
+ mNotification = R.string.location_notification_alwaysdeny;
+ }
+ break;
+ case ALLOW:
+ ret = "{\"allow\": true, \"permanently\": true }";
+ if (mDialogType.equalsIgnoreCase(LOCAL_DATA_STRING)) {
+ mNotification = R.string.storage_notification;
+ } else if (mDialogType.equalsIgnoreCase(LOCATION_DATA_STRING)) {
+ mNotification = R.string.location_notification;
+ }
+ break;
+ case DENY:
+ ret = "{\"allow\": false, \"permanently\": false }";
+ break;
+ }
+ return ret;
+ }
+
+ public int notification() {
+ return mNotification;
+ }
+}
diff --git a/src/com/android/browser/GearsSettingsDialog.java b/src/com/android/browser/GearsSettingsDialog.java
new file mode 100644
index 00000000..5ea2342a
--- /dev/null
+++ b/src/com/android/browser/GearsSettingsDialog.java
@@ -0,0 +1,460 @@
+/*
+ * 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.
+ */
+
+package com.android.browser;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Handler;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.ImageView;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.RadioButton;
+import android.widget.TextView;
+
+import com.android.browser.GearsPermissions.OriginPermissions;
+import com.android.browser.GearsPermissions.Permission;
+import com.android.browser.GearsPermissions.PermissionsChangesListener;
+import com.android.browser.GearsPermissions.PermissionType;
+
+import java.util.Vector;
+import java.util.List;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Gears Settings dialog
+ */
+class GearsSettingsDialog extends GearsBaseDialog
+ implements PermissionsChangesListener {
+
+ private static final String TAG = "GearsPermissionsDialog";
+ private Vector<OriginPermissions> mSitesPermissions = null;
+ private Vector<OriginPermissions> mOriginalPermissions = null;
+ private Vector<OriginPermissions> mCurrentPermissions = null;
+
+ private Vector<PermissionType> mPermissions;
+ private static final int CONFIRMATION_REMOVE_DIALOG = 1;
+
+ // We declare the permissions globally to simplify the code
+ private final PermissionType LOCAL_STORAGE =
+ new PermissionType(LOCAL_STORAGE_STRING);
+ private final PermissionType LOCATION_DATA =
+ new PermissionType(LOCATION_DATA_STRING);
+
+ private boolean mChanges = false;
+
+ SettingsAdapter mListAdapter;
+
+ public GearsSettingsDialog(Activity activity,
+ Handler handler,
+ String arguments) {
+ super (activity, handler, arguments);
+ activity.setContentView(R.layout.gears_settings);
+ }
+
+ public void setup() {
+ // First let's add the permissions' resources
+ LOCAL_STORAGE.setResources(R.string.settings_storage_title,
+ R.string.settings_storage_subtitle_on,
+ R.string.settings_storage_subtitle_off);
+ LOCATION_DATA.setResources(R.string.settings_location_title,
+ R.string.settings_location_subtitle_on,
+ R.string.settings_location_subtitle_off);
+ // add the permissions to the list of permissions.
+ mPermissions = new Vector<PermissionType>();
+ mPermissions.add(LOCAL_STORAGE);
+ mPermissions.add(LOCATION_DATA);
+ OriginPermissions.setListener(this);
+
+
+ setupDialog();
+
+ // We manage the permissions using three vectors, mSitesPermissions,
+ // mOriginalPermissions and mCurrentPermissions.
+ // The dialog's arguments are parsed and a list of permissions is
+ // generated and stored in those three vectors.
+ // mOriginalPermissions is a separate copy and will not be modified;
+ // mSitesPermissions contains the current permissions _only_ --
+ // if an origin is removed, it is also removed from mSitesPermissions.
+ // Finally, mCurrentPermissions contains the current permissions and
+ // is a clone of mSitesPermissions, but removed sites aren't removed,
+ // their permissions are simply set to PERMISSION_NOT_SET. This
+ // allows us to easily generate the final difference between the
+ // original permissions and the final permissions, while directly
+ // using mSitesPermissions for the listView adapter (SettingsAdapter).
+
+ mSitesPermissions = new Vector<OriginPermissions>();
+ mOriginalPermissions = new Vector<OriginPermissions>();
+
+ try {
+ JSONObject json = new JSONObject(mDialogArguments);
+ if (json.has("permissions")) {
+ JSONArray jsonArray = json.getJSONArray("permissions");
+ for (int i = 0; i < jsonArray.length(); i++) {
+ JSONObject infos = jsonArray.getJSONObject(i);
+ String name = null;
+ int localStorage = PermissionType.PERMISSION_NOT_SET;
+ int locationData = PermissionType.PERMISSION_NOT_SET;
+ if (infos.has("name")) {
+ name = infos.getString("name");
+ }
+ if (infos.has(LOCAL_STORAGE_STRING)) {
+ JSONObject perm = infos.getJSONObject(LOCAL_STORAGE_STRING);
+ if (perm.has("permissionState")) {
+ localStorage = perm.getInt("permissionState");
+ }
+ }
+ if (infos.has(LOCATION_DATA_STRING)) {
+ JSONObject perm = infos.getJSONObject(LOCATION_DATA_STRING);
+ if (perm.has("permissionState")) {
+ locationData = perm.getInt("permissionState");
+ }
+ }
+ OriginPermissions perms = new OriginPermissions(name);
+ perms.setPermission(LOCAL_STORAGE, localStorage);
+ perms.setPermission(LOCATION_DATA, locationData);
+
+ mSitesPermissions.add(perms);
+ mOriginalPermissions.add(new OriginPermissions(perms));
+ }
+ }
+ } catch (JSONException e) {
+ Log.e(TAG, "JSON exception ", e);
+ }
+ mCurrentPermissions = (Vector<OriginPermissions>)mSitesPermissions.clone();
+
+ View listView = findViewById(R.id.sites_list);
+ if (listView != null) {
+ ListView list = (ListView) listView;
+ mListAdapter = new SettingsAdapter(mActivity, mSitesPermissions);
+ list.setAdapter(mListAdapter);
+ list.setScrollBarStyle(android.view.View.SCROLLBARS_OUTSIDE_INSET);
+ list.setOnItemClickListener(mListAdapter);
+ }
+ if (mDebug) {
+ printPermissions();
+ }
+ }
+
+ private void setMainTitle() {
+ String windowTitle = mActivity.getString(R.string.pref_extras_gears_settings);
+ mActivity.setTitle(windowTitle);
+ }
+
+ public void setupDialog() {
+ setMainTitle();
+ }
+
+ /**
+ * GearsPermissions.PermissionsChangesListener delegate
+ */
+ public boolean setPermission(PermissionType type, int perm) {
+ if (mChanges == false) {
+ mChanges = true;
+ }
+ return mChanges;
+ }
+
+ public boolean handleBackButton() {
+ return mListAdapter.backButtonPressed();
+ }
+
+ /**
+ * We use this to create a confirmation dialog when the user
+ * clicks on "remove this site from gears"
+ */
+ public Dialog onCreateDialog(int id) {
+ return new AlertDialog.Builder(mActivity)
+ .setTitle(R.string.settings_confirmation_remove_title)
+ .setMessage(R.string.settings_confirmation_remove)
+ .setPositiveButton(android.R.string.ok,
+ new AlertDialog.OnClickListener() {
+ public void onClick(DialogInterface dlg, int which) {
+ mListAdapter.removeCurrentSite();
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, null)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .create();
+ }
+
+ /**
+ * Adapter class for the list view in the settings dialog
+ *
+ * We first display a list of all the origins (sites), or
+ * a message saying that no permission is set if the list is empty.
+ * When the user click on one of the origin, we then display
+ * the list of the permissions existing for that origin.
+ * Each permission can be either allowed or denied by clicking
+ * on the checkbox.
+ * The last row is a special case, allowing to remove the entire origin.
+ */
+ class SettingsAdapter extends BaseAdapter
+ implements AdapterView.OnItemClickListener {
+ private Activity mContext;
+ private List mItems;
+ private OriginPermissions mCurrentSite;
+ private Vector mCurrentPermissions;
+ private int MAX_ROW_HEIGHT = 64;
+
+ SettingsAdapter(Activity context, List items) {
+ mContext = context;
+ mItems = items;
+ mCurrentSite = null;
+ }
+
+ public int getCount() {
+ if (mCurrentSite == null) {
+ int size = mItems.size();
+ if (size == 0) {
+ return 1;
+ } else {
+ return size;
+ }
+ }
+ return mCurrentPermissions.size() + 1;
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ private String shortName(String url) {
+ // We remove the http and https prefix
+ if (url.startsWith("http://")) {
+ return url.substring(7);
+ }
+ if (url.startsWith("https://")) {
+ return url.substring(8);
+ }
+ return url;
+ }
+
+ public Object getItem(int position) {
+ if (mCurrentSite == null) {
+ if (mItems.size() == 0) {
+ return null;
+ } else {
+ return mItems.get(position);
+ }
+ }
+ return mCurrentPermissions.get(position);
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View row = convertView;
+ if (row == null) { // no cached view, we create one
+ LayoutInflater inflater = (LayoutInflater) getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ row = inflater.inflate(R.layout.gears_settings_row, null);
+ }
+ row.setMinimumHeight(MAX_ROW_HEIGHT);
+
+ if (mCurrentSite == null) {
+ if (mItems.size() == 0) {
+ hideView(row, R.id.title);
+ hideView(row, R.id.subtitle);
+ hideView(row, R.id.checkbox);
+ hideView(row, R.id.icon);
+ setText(row, R.id.info, R.string.settings_empty);
+ } else {
+ hideView(row, R.id.subtitle);
+ hideView(row, R.id.info);
+ hideView(row, R.id.checkbox);
+ OriginPermissions perms = (OriginPermissions) mItems.get(position);
+ setText(row, R.id.title, shortName(perms.getOrigin()));
+ showView(row, R.id.icon);
+ }
+ } else {
+ if (position == getCount() - 1) {
+ // last position: "remove this site from gears"
+ hideView(row, R.id.subtitle);
+ hideView(row, R.id.info);
+ hideView(row, R.id.checkbox);
+ hideView(row, R.id.icon);
+ setText(row, R.id.title, R.string.settings_remove_site);
+ } else {
+ hideView(row, R.id.info);
+ hideView(row, R.id.icon);
+ showView(row, R.id.checkbox);
+
+ PermissionType type =
+ (PermissionType) mCurrentPermissions.get(position);
+ setText(row, R.id.title, type.getTitleRsc());
+
+ View checkboxView = row.findViewById(R.id.checkbox);
+ if (checkboxView != null) {
+ CheckBox checkbox = (CheckBox) checkboxView;
+ int perm = mCurrentSite.getPermission(type);
+ if (perm == PermissionType.PERMISSION_DENIED) {
+ setText(row, R.id.subtitle, type.getSubtitleOffRsc());
+ checkbox.setChecked(false);
+ } else {
+ setText(row, R.id.subtitle, type.getSubtitleOnRsc());
+ checkbox.setChecked(true);
+ }
+ }
+ }
+ }
+ return row;
+ }
+
+ public void removeCurrentSite() {
+ mCurrentSite.setPermission(LOCAL_STORAGE,
+ PermissionType.PERMISSION_NOT_SET);
+ mCurrentSite.setPermission(LOCATION_DATA,
+ PermissionType.PERMISSION_NOT_SET);
+ mSitesPermissions.remove(mCurrentSite);
+ mCurrentSite = null;
+ setMainTitle();
+ notifyDataSetChanged();
+ }
+
+ public void onItemClick(AdapterView<?> parent,
+ View view,
+ int position,
+ long id) {
+ if (mItems.size() == 0) {
+ return;
+ }
+ if (mCurrentSite == null) {
+ mCurrentSite = (OriginPermissions) mItems.get(position);
+ mCurrentPermissions = new Vector();
+ for (int i = 0; i < mPermissions.size(); i++) {
+ PermissionType type = mPermissions.get(i);
+ int perm = mCurrentSite.getPermission(type);
+ if (perm != PermissionType.PERMISSION_NOT_SET) {
+ mCurrentPermissions.add(type);
+ }
+ }
+ mContext.setTitle(shortName(mCurrentSite.getOrigin()));
+ } else {
+ if (position == getCount() - 1) { // last item (remove site)
+ // Ask the user to confirm
+ // If yes, removeCurrentSite() will be called via the dialog callback.
+ mActivity.showDialog(CONFIRMATION_REMOVE_DIALOG);
+ } else {
+ PermissionType type =
+ (PermissionType) mCurrentPermissions.get(position);
+ if (mCurrentSite.getPermission(type) ==
+ PermissionType.PERMISSION_ALLOWED) {
+ mCurrentSite.setPermission(type, PermissionType.PERMISSION_DENIED);
+ } else {
+ mCurrentSite.setPermission(type, PermissionType.PERMISSION_ALLOWED);
+ }
+ }
+ }
+ notifyDataSetChanged();
+ }
+
+ public boolean backButtonPressed() {
+ if (mCurrentSite != null) { // we intercept the back button
+ mCurrentSite = null;
+ setMainTitle();
+ notifyDataSetChanged();
+ return true;
+ }
+ return false;
+ }
+
+ }
+
+ /**
+ * Utility method used in debug mode to print the list of
+ * permissions (original values and current values).
+ */
+ public void printPermissions() {
+ Log.v(TAG, "Original Permissions: ");
+ for (int i = 0; i < mOriginalPermissions.size(); i++) {
+ OriginPermissions p = mOriginalPermissions.get(i);
+ p.print();
+ }
+ Log.v(TAG, "Current Permissions: ");
+ for (int i = 0; i < mSitesPermissions.size(); i++) {
+ OriginPermissions p = mSitesPermissions.get(i);
+ p.print();
+ }
+ }
+
+ /**
+ * Computes the difference between the original permissions and the
+ * current ones. Returns a json-formatted string.
+ * It is used by the Settings dialog.
+ */
+ public String computeDiff(boolean modif) {
+ String ret = null;
+ try {
+ JSONObject results = new JSONObject();
+ JSONArray permissions = new JSONArray();
+
+ for (int i = 0; modif && i < mOriginalPermissions.size(); i++) {
+ OriginPermissions original = mOriginalPermissions.get(i);
+ OriginPermissions current = mCurrentPermissions.get(i);
+ JSONObject permission = new JSONObject();
+ boolean modifications = false;
+
+ for (int j = 0; j < mPermissions.size(); j++) {
+ PermissionType type = mPermissions.get(j);
+
+ if (current.getPermission(type) != original.getPermission(type)) {
+ JSONObject state = new JSONObject();
+ state.put("permissionState", current.getPermission(type));
+ permission.put(type.getName(), state);
+ modifications = true;
+ }
+ }
+
+ if (modifications) {
+ permission.put("name", current.getOrigin());
+ permissions.put(permission);
+ }
+ }
+ results.put("modifiedOrigins", permissions);
+ ret = results.toString();
+ } catch (JSONException e) {
+ Log.e(TAG, "JSON exception ", e);
+ }
+ return ret;
+ }
+
+ public String closeDialog(int closingType) {
+ String ret = computeDiff(mChanges);
+
+ if (mDebug) {
+ printPermissions();
+ }
+
+ return ret;
+ }
+
+}
diff --git a/src/com/android/browser/HistoryItem.java b/src/com/android/browser/HistoryItem.java
new file mode 100644
index 00000000..55e43f03
--- /dev/null
+++ b/src/com/android/browser/HistoryItem.java
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+
+
+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.
+ */
+/* package */ class HistoryItem extends BookmarkItem {
+
+ private CompoundButton mStar; // Star for bookmarking
+ private CompoundButton.OnCheckedChangeListener mListener;
+ /**
+ * Create a new HistoryItem.
+ * @param context Context for this HistoryItem.
+ */
+ /* package */ HistoryItem(Context context) {
+ super(context);
+
+ mStar = (CompoundButton) findViewById(R.id.star);
+ mStar.setVisibility(View.VISIBLE);
+ 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();
+ } 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();
+ }
+ cursor.deactivate();
+ }
+ };
+ }
+
+ void copyTo(HistoryItem item) {
+ item.mTextView.setText(mTextView.getText());
+ item.mUrlText.setText(mUrlText.getText());
+ item.setIsBookmark(mStar.isChecked());
+ item.mImageView.setImageDrawable(mImageView.getDrawable());
+ }
+
+ /**
+ * Set whether or not this represents a bookmark, and make sure the star
+ * behaves appropriately.
+ */
+ void setIsBookmark(boolean isBookmark) {
+ mStar.setOnCheckedChangeListener(null);
+ mStar.setChecked(isBookmark);
+ mStar.setOnCheckedChangeListener(mListener);
+ }
+}
diff --git a/src/com/android/browser/ImageAdapter.java b/src/com/android/browser/ImageAdapter.java
new file mode 100644
index 00000000..b4c1209d
--- /dev/null
+++ b/src/com/android/browser/ImageAdapter.java
@@ -0,0 +1,291 @@
+/*
+ * 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.
+ */
+
+package com.android.browser;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.database.DataSetObserver;
+import android.graphics.Color;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.LayoutInflater;
+import android.webkit.WebView;
+import android.widget.ImageView;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+/**
+ * Adapter used by ImageGrid.
+ */
+public class ImageAdapter implements ListAdapter {
+
+ ArrayList<TabControl.Tab> mItems; // Items shown in the grid
+ private ArrayList<DataSetObserver> mDataObservers; // Data change listeners
+ private Context mContext; // Context to use to inflate views
+ private boolean mMaxedOut;
+ private ImageGrid mImageGrid;
+ private boolean mIsLive;
+ private int mTabHeight;
+
+ ImageAdapter(Context context, ImageGrid grid, boolean live) {
+ mContext = context;
+ mIsLive = live;
+ mItems = new ArrayList<TabControl.Tab>();
+ mImageGrid = grid;
+ mDataObservers = new ArrayList<DataSetObserver>();
+ }
+
+ void heightChanged(int newHeight) {
+ mTabHeight = newHeight;
+ }
+
+ /**
+ * Whether the adapter is at its limit, determined by TabControl.MAX_TABS
+ *
+ * @return True if the number of Tabs represented in this Adapter is at its
+ * maximum.
+ */
+ public boolean maxedOut() {
+ return mMaxedOut;
+ }
+
+ /**
+ * Clear the internal WebViews and remove their picture listeners.
+ */
+ public void clear() {
+ for (TabControl.Tab t : mItems) {
+ clearPictureListeners(t);
+ }
+ mItems.clear();
+ notifyObservers();
+ }
+
+ private void clearPictureListeners(TabControl.Tab t) {
+ if (t.getWebView() != null) {
+ t.getWebView().setPictureListener(null);
+ if (t.getSubWebView() != null) {
+ t.getSubWebView().setPictureListener(null);
+ }
+ }
+ }
+
+ /**
+ * Add a new window web page to the grid
+ *
+ * @param t The tab to display
+ */
+ public void add(TabControl.Tab t) {
+ if (mMaxedOut) {
+ return;
+ }
+ mItems.add(t);
+ notifyObservers();
+ if (mItems.size() == TabControl.MAX_TABS) {
+ mMaxedOut = true;
+ }
+ }
+
+ /**
+ * Remove a window from the list. At this point, the window
+ * has already gone. It just needs to be removed from the screen
+ *
+ * @param index window to remove
+ */
+ public void remove(int index) {
+ if (index >= 0 && index < mItems.size()) {
+ clearPictureListeners(mItems.remove(index));
+ notifyObservers();
+ mMaxedOut = false;
+ }
+ }
+
+ /* (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 <= mItems.size()) {
+ return true;
+ }
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see android.widget.Adapter#getCount()
+ */
+ public int getCount() {
+ // Include the New Window button if we have not reached the tab limit
+ if (!mMaxedOut) {
+ return mItems.size()+1;
+ }
+ return mItems.size();
+ }
+
+ /* (non-Javadoc)
+ * @see android.widget.Adapter#getItem(int)
+ */
+ public Object getItem(int position) {
+ if (!mMaxedOut) {
+ if (0 == position) {
+ return null;
+ }
+ return mItems.get(position);
+ }
+ return mItems.get(position);
+ }
+
+ /* (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.tabitem, null);
+ }
+ FakeWebView img = (FakeWebView) v.findViewById(R.id.icon);
+ ImageView close = (ImageView) v.findViewById(R.id.close);
+ TextView tv = (TextView) v.findViewById(R.id.label);
+
+ // position needs to be in the range of Tab indices.
+ if (!mMaxedOut) {
+ position--;
+ }
+
+ // Create the View for actual tabs
+ if (position != ImageGrid.NEW_TAB) {
+ TabControl.Tab t = mItems.get(position);
+ img.setTab(t);
+ tv.setText(t.getTitle());
+ // Do not put the 'X' if the tab picker isn't "live" (meaning the
+ // user cannot click on a tab)
+ if (!mIsLive) {
+ close.setVisibility(View.GONE);
+ } else {
+ close.setVisibility(View.VISIBLE);
+ final int pos = position;
+ close.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ ImageAdapter.this.confirmClose(pos);
+ }
+ });
+ }
+ } else {
+ img.setBackgroundColor(Color.BLACK);
+ img.setImageResource(R.drawable.ic_new_window);
+ img.setScaleType(ImageView.ScaleType.CENTER);
+ img.setPadding(0, 0, 0, 34);
+ tv.setText(R.string.new_window);
+ close.setVisibility(View.GONE);
+ }
+ ViewGroup.LayoutParams lp = img.getLayoutParams();
+ if (lp.height != mTabHeight) {
+ lp.height = mTabHeight;
+ img.requestLayout();
+ }
+ return v;
+ }
+
+ /*
+ * Pop a confirmation dialog to the user asking if they want to close this
+ * tab.
+ */
+ private void confirmClose(final int position) {
+ final ImageGrid.Listener l = mImageGrid.getListener();
+ if (l == null) {
+ return;
+ }
+ DialogInterface.OnClickListener confirm =
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int whichButton) {
+ l.remove(position);
+ }
+ };
+ new AlertDialog.Builder(mContext)
+ .setTitle(R.string.close)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setMessage(R.string.close_window)
+ .setPositiveButton(R.string.ok, confirm)
+ .setNegativeButton(R.string.cancel, null)
+ .show();
+ }
+
+ /* (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);
+ }
+
+ /**
+ * Notify all the observers that a change has happened.
+ */
+ void notifyObservers() {
+ for (DataSetObserver observer : mDataObservers) {
+ observer.onChanged();
+ }
+ }
+
+ 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/ImageGrid.java b/src/com/android/browser/ImageGrid.java
new file mode 100644
index 00000000..9eccb162
--- /dev/null
+++ b/src/com/android/browser/ImageGrid.java
@@ -0,0 +1,233 @@
+/*
+ * 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.
+ */
+
+package com.android.browser;
+
+import android.content.Context;
+import android.util.Config;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.KeyEvent;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnCreateContextMenuListener;
+import android.webkit.WebView;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.GridView;
+
+/**
+ * This class implements a Grid layout of Views for the Tab picker.
+ */
+class ImageGrid extends GridView implements OnItemClickListener,
+ OnCreateContextMenuListener {
+
+ private Listener mListener;
+ private ImageAdapter mAdapter;
+ private boolean mIsLive;
+ private static final int SPACING = 10;
+ public static final int CANCEL = -99;
+ public static final int NEW_TAB = -1;
+
+ /**
+ * Constructor
+ * @param context Context to use when inflating resources.
+ * @param live TRUE if the view can accept touch or click
+ * @param l Listener to respond to clicks etc.
+ */
+ public ImageGrid(Context context, boolean live, Listener l) {
+ super(context);
+
+ mIsLive = live;
+ if (live) {
+ setFocusable(true);
+ setFocusableInTouchMode(true);
+ setOnItemClickListener(this);
+ setOnCreateContextMenuListener(this);
+ }
+ mListener = l;
+
+ mAdapter = new ImageAdapter(context, this, live);
+ setAdapter(mAdapter);
+
+ setBackgroundColor(0xFF000000);
+
+ setVerticalSpacing(SPACING);
+ setHorizontalSpacing(SPACING);
+ setNumColumns(2);
+ setStretchMode(GridView.STRETCH_COLUMN_WIDTH);
+ setSelector(android.R.drawable.gallery_thumb);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ // We always consume the BACK key even if mListener is null or the
+ // ImageGrid is not "live." This prevents crashes during tab animations
+ // if the user presses BACK.
+ if ((event.getAction() == KeyEvent.ACTION_DOWN) &&
+ (event.getKeyCode() == KeyEvent.KEYCODE_BACK)) {
+ if (mListener != null && mIsLive) {
+ mListener.onClick(CANCEL);
+ invalidate();
+ }
+ return true;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ /**
+ * Called by BrowserActivity to add a new window to the tab picker.
+ * This does not happen dynamically, this only happens during view
+ * setup.
+ *
+ * @param v Webview of the tab to add
+ * @param name Web page title
+ * @param url URL of the webpage
+ */
+ public void add(TabControl.Tab t) {
+ mAdapter.add(t);
+ }
+
+ /**
+ * Called by BrowserActivity when a window has been removed from the
+ * tab list.
+ *
+ * @param index Window to remove, from 0 to MAX_TABS-1
+ */
+ public void remove(int index) {
+ if (Config.DEBUG && (index < 0 || index >= TabControl.MAX_TABS)) {
+ throw new AssertionError();
+ }
+ mAdapter.remove(index);
+ }
+
+ /**
+ * Request focus to initially set to a particular tab.
+ *
+ * @param startingIndex This is a Tab index from 0 - MAX_TABS-1 and does not
+ * include the "New Tab" cell.
+ */
+ public void setCurrentIndex(int startingIndex) {
+ if (!mAdapter.maxedOut()) {
+ startingIndex++;
+ }
+ setSelection(startingIndex);
+ }
+
+ public Listener getListener() {
+ return mListener;
+ }
+
+ public void setListener(Listener l) {
+ mListener = l;
+ }
+
+ /**
+ * Return true if the ImageGrid is live. This means that tabs can be chosen
+ * and the menu can be invoked.
+ */
+ public boolean isLive() {
+ return mIsLive;
+ }
+
+ /**
+ * Do some internal cleanup of the ImageGrid's adapter.
+ */
+ public void clear() {
+ mAdapter.clear();
+ }
+
+ /* (non-Javadoc)
+ * @see android.widget.AdapterView.OnItemClickListener#onItemClick(android.widget.AdapterView, android.view.View, int, long)
+ */
+ public void onItemClick(AdapterView parent, View v, int position, long id) {
+ if (!mAdapter.maxedOut()) {
+ position--;
+ }
+ // Position will be -1 for the "New Tab" cell.
+ if (mListener != null) {
+ mListener.onClick(position);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see android.view.View.OnCreateContextMenuListener#onCreateContextMenu(android.view.ContextMenu, android.view.View, java.lang.Object)
+ */
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenuInfo menuInfo) {
+ // Do not create the context menu if there is no listener or the Tab
+ // overview is not "live."
+ if (mListener == null || !mIsLive) {
+ return;
+ }
+ AdapterView.AdapterContextMenuInfo info =
+ (AdapterView.AdapterContextMenuInfo) menuInfo;
+ boolean maxed = mAdapter.maxedOut();
+ if (info.position > 0 || maxed) {
+ MenuInflater inflater = new MenuInflater(mContext);
+ inflater.inflate(R.menu.tabscontext, menu);
+ int position = info.position;
+ if (!maxed) {
+ position--;
+ }
+ menu.setHeaderTitle(mAdapter.mItems.get(position).getTitle());
+ }
+ }
+
+ // convert a context menu position to an actual tab position. Since context
+ // menus are not created for the "New Tab" cell, this will always return a
+ // valid tab position.
+ public int getContextMenuPosition(MenuItem menu) {
+ AdapterView.AdapterContextMenuInfo info =
+ (AdapterView.AdapterContextMenuInfo) menu.getMenuInfo();
+ int pos = info.position;
+ if (!mAdapter.maxedOut()) {
+ pos--;
+ }
+ return pos;
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ // Called when our orientation changes. Tell the adapter about the new
+ // size. Compute the individual tab height by taking the grid height
+ // and subtracting the SPACING. Then subtract the list padding twice
+ // (once for each tab on screen) and divide the remaining height by 2.
+ int tabHeight = (h - SPACING
+ - 2 * (getListPaddingTop() + getListPaddingBottom())) / 2;
+ mAdapter.heightChanged(tabHeight);
+ super.onSizeChanged(w, h, oldw, oldh);
+ }
+
+ /**
+ * Listener to be notified by behavior of ImageGrid.
+ */
+ public interface Listener {
+ /**
+ * Called when enter is pressed on the list.
+ * @param position The index of the selected image when
+ * enter is pressed.
+ */
+ void onClick(int position);
+
+ /**
+ * Called when remove is called on the grid.
+ */
+ void remove(int position);
+ }
+
+}
diff --git a/src/com/android/browser/KeyTracker.java b/src/com/android/browser/KeyTracker.java
new file mode 100644
index 00000000..344e4f85
--- /dev/null
+++ b/src/com/android/browser/KeyTracker.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.browser;
+
+import android.view.KeyEvent;
+import android.view.ViewConfiguration;
+
+class KeyTracker {
+
+ public enum Stage {
+ DOWN, //!< the key has just been pressed
+ SHORT_REPEAT, //!< repeated key, but duration is under the long-press threshold
+ LONG_REPEAT, //!< repeated key, but duration is over the long-press threshold
+ UP //!< the key is being released
+ }
+
+ public enum State {
+ KEEP_TRACKING, //!< return this to continue to track the key
+ DONE_TRACKING, //!< return this if you handled the key, but need not track it anymore
+ NOT_TRACKING //!< return this if you will not handle this key
+ }
+
+ public interface OnKeyTracker {
+
+ /** Called whenever there is a key event [down, short/long repeat, up]
+ @param keyCode The current keyCode (see KeyEvent class)
+ @param msg The message associated with the keyCode
+ @maram stage The state the key press is in [down, short/long repeat, up]
+ @param duration The number of milliseconds since this key was initially pressed
+ @return your state after seeing the key. If you return DONE_TRACKING or NOT_TRACKING,
+ you will not be called again for the lifetime of this key event.
+ */
+ public State onKeyTracker(int keyCode, KeyEvent event, Stage stage, int duration);
+ }
+
+ public KeyTracker(OnKeyTracker tracker) {
+ mTracker = tracker;
+ }
+
+ public boolean doKeyDown(int keyCode, KeyEvent event) {
+ long now = System.currentTimeMillis();
+ Stage stage = null;
+
+ // check if its a new/different key
+ if (mKeyCode != keyCode || event.getRepeatCount() == 0) {
+ mKeyCode = keyCode;
+ mStartMS = now;
+ stage = Stage.DOWN;
+ }
+ else if (mState == State.KEEP_TRACKING) {
+ stage = (now - mStartMS) >= LONG_PRESS_DURATION_MS ? Stage.LONG_REPEAT : Stage.SHORT_REPEAT;
+ }
+
+ if (stage != null) {
+ mEvent = event;
+ callTracker(stage, now);
+ }
+
+ return mState != State.NOT_TRACKING;
+ }
+
+ public boolean doKeyUp(int keyCode, KeyEvent event) {
+ boolean handled = false;
+
+ if (mState == State.KEEP_TRACKING && mKeyCode == keyCode) {
+ mEvent = event;
+ callTracker(Stage.UP, System.currentTimeMillis());
+ handled = mState != State.NOT_TRACKING;
+ }
+ mKeyCode = NOT_A_KEYCODE;
+ return handled;
+ }
+
+ private void callTracker(Stage stage, long now) {
+ mState = mTracker.onKeyTracker(mKeyCode, mEvent, stage, (int)(now - mStartMS));
+ }
+
+ private void dump() {
+ System.out.println(" key=" + mKeyCode + " dur=" + (System.currentTimeMillis() - mStartMS) +
+ " state=" + mState);
+ }
+
+ private int mKeyCode = NOT_A_KEYCODE;
+ private KeyEvent mEvent;
+ private long mStartMS;
+ private State mState;
+ private OnKeyTracker mTracker;
+
+ private static final int LONG_PRESS_DURATION_MS =
+ ViewConfiguration.getLongPressTimeout();
+ private static final int NOT_A_KEYCODE = -123456;
+}
+
diff --git a/src/com/android/browser/MostVisitedActivity.java b/src/com/android/browser/MostVisitedActivity.java
new file mode 100644
index 00000000..704ee279
--- /dev/null
+++ b/src/com/android/browser/MostVisitedActivity.java
@@ -0,0 +1,199 @@
+/*
+ * 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.ListActivity;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.database.DataSetObserver;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.Browser;
+import android.webkit.WebIconDatabase.IconListener;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.SimpleCursorAdapter;
+import android.widget.TextView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+
+import java.util.Vector;
+
+public class MostVisitedActivity extends ListActivity {
+
+ private MyAdapter mAdapter;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mAdapter = new MyAdapter();
+ CombinedBookmarkHistoryActivity.getIconListenerSet(getContentResolver())
+ .addListener(new IconReceiver());
+ setListAdapter(mAdapter);
+ ListView list = getListView();
+ LayoutInflater factory = LayoutInflater.from(this);
+ View v = factory.inflate(R.layout.empty_history, null);
+ addContentView(v, new LayoutParams(LayoutParams.FILL_PARENT,
+ LayoutParams.FILL_PARENT));
+ list.setEmptyView(v);
+ }
+
+ private class IconReceiver implements IconListener {
+ public void onReceivedIcon(String url, Bitmap icon) {
+ setListAdapter(mAdapter);
+ }
+ }
+
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ TextView tv = (TextView) v.findViewById(R.id.url);
+ String url = tv.getText().toString();
+ loadUrl(url, false);
+ }
+
+ private void loadUrl(String url, boolean newWindow) {
+ Intent intent = new Intent().setAction(url);
+ if (newWindow) {
+ Bundle b = new Bundle();
+ b.putBoolean("new_window", true);
+ intent.putExtras(b);
+ }
+ setResultToParent(RESULT_OK, intent);
+ finish();
+ }
+
+ private class MyAdapter implements ListAdapter {
+ 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;
+
+ MyAdapter() {
+ mObservers = new Vector<DataSetObserver>();
+ String[] projection = new String[] {
+ Browser.BookmarkColumns.URL,
+ Browser.BookmarkColumns.TITLE,
+ Browser.BookmarkColumns.BOOKMARK };
+ String whereClause = Browser.BookmarkColumns.VISITS + " != 0";
+ String orderBy = Browser.BookmarkColumns.VISITS + " DESC";
+ mCursor = managedQuery(Browser.BOOKMARKS_URI, projection,
+ whereClause, null, orderBy);
+ mCursor.registerContentObserver(new ChangeObserver());
+ }
+
+ private class ChangeObserver extends ContentObserver {
+ public ChangeObserver() {
+ super(new Handler());
+ }
+
+ @Override
+ public boolean deliverSelfNotifications() {
+ return true;
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ MyAdapter.this.refreshData();
+ }
+ }
+
+ void refreshData() {
+ mCursor.requery();
+ for (DataSetObserver o : mObservers) {
+ o.onChanged();
+ }
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ HistoryItem item;
+ if (null == convertView) {
+ item = new HistoryItem(MostVisitedActivity.this);
+ } else {
+ item = (HistoryItem) convertView;
+ }
+ mCursor.moveToPosition(position);
+ item.setName(mCursor.getString(mTitleIndex));
+ String url = mCursor.getString(mUrlIndex);
+ item.setUrl(url);
+ item.setFavicon(CombinedBookmarkHistoryActivity.getIconListenerSet(
+ getContentResolver()).getFavicon(url));
+ item.setIsBookmark(1 == mCursor.getInt(mBookmarkIndex));
+ return item;
+ }
+
+ public boolean areAllItemsEnabled() {
+ return true;
+ }
+
+ public boolean isEnabled(int position) {
+ return true;
+ }
+
+ public int getCount() {
+ return mCursor.getCount();
+ }
+
+ public Object getItem(int position) {
+ return null;
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ // Always a HistoryItem
+ public int getItemViewType(int position) {
+ return 0;
+ }
+
+ public int getViewTypeCount() {
+ return 1;
+ }
+
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ public void registerDataSetObserver(DataSetObserver observer) {
+ mObservers.add(observer);
+ }
+
+ public void unregisterDataSetObserver(DataSetObserver observer) {
+ mObservers.remove(observer);
+ }
+
+ public boolean isEmpty() {
+ return getCount() == 0;
+ }
+ }
+
+ // This Activity is generally a sub-Activity of CombinedHistoryActivity. In
+ // that situation, we need to pass our result code up to our parent.
+ // However, if someone calls this Activity directly, then this has no
+ // parent, and it needs to set it on itself.
+ private void setResultToParent(int resultCode, Intent data) {
+ Activity a = getParent() == null ? this : getParent();
+ a.setResult(resultCode, data);
+ }
+}
+
diff --git a/src/com/android/browser/TabControl.java b/src/com/android/browser/TabControl.java
new file mode 100644
index 00000000..2f393028
--- /dev/null
+++ b/src/com/android/browser/TabControl.java
@@ -0,0 +1,937 @@
+/*
+ * Copyright (C) 2007 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.net.http.SslError;
+import android.os.Bundle;
+import android.os.Message;
+import android.util.Config;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnClickListener;
+import android.webkit.HttpAuthHandler;
+import android.webkit.JsPromptResult;
+import android.webkit.JsResult;
+import android.webkit.SslErrorHandler;
+import android.webkit.WebBackForwardList;
+import android.webkit.WebChromeClient;
+import android.webkit.WebHistoryItem;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Vector;
+
+class TabControl {
+ // Log Tag
+ private static final String LOGTAG = "TabControl";
+ // Maximum number of tabs.
+ static final int MAX_TABS = 8;
+ // Static instance of an empty callback.
+ private static final WebViewClient mEmptyClient =
+ new WebViewClient();
+ // Instance of BackgroundChromeClient for background tabs.
+ private final BackgroundChromeClient mBackgroundChromeClient =
+ new BackgroundChromeClient();
+ // Private array of WebViews that are used as tabs.
+ private ArrayList<Tab> mTabs = new ArrayList<Tab>(MAX_TABS);
+ // Queue of most recently viewed tabs.
+ private ArrayList<Tab> mTabQueue = new ArrayList<Tab>(MAX_TABS);
+ // Current position in mTabs.
+ private int mCurrentTab = -1;
+ // A private instance of BrowserActivity to interface with when adding and
+ // switching between tabs.
+ private final BrowserActivity mActivity;
+ // Inflation service for making subwindows.
+ private final LayoutInflater mInflateService;
+ // Subclass of WebViewClient used in subwindows to notify the main
+ // WebViewClient of certain WebView activities.
+ private class SubWindowClient extends WebViewClient {
+ // The main WebViewClient.
+ private final WebViewClient mClient;
+
+ SubWindowClient(WebViewClient client) {
+ mClient = client;
+ }
+ @Override
+ public void doUpdateVisitedHistory(WebView view, String url,
+ boolean isReload) {
+ mClient.doUpdateVisitedHistory(view, url, isReload);
+ }
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ return mClient.shouldOverrideUrlLoading(view, url);
+ }
+ @Override
+ public void onReceivedSslError(WebView view, SslErrorHandler handler,
+ SslError error) {
+ mClient.onReceivedSslError(view, handler, error);
+ }
+ @Override
+ public void onReceivedHttpAuthRequest(WebView view,
+ HttpAuthHandler handler, String host, String realm) {
+ mClient.onReceivedHttpAuthRequest(view, handler, host, realm);
+ }
+ @Override
+ public void onFormResubmission(WebView view, Message dontResend,
+ Message resend) {
+ mClient.onFormResubmission(view, dontResend, resend);
+ }
+ @Override
+ public void onReceivedError(WebView view, int errorCode,
+ String description, String failingUrl) {
+ mClient.onReceivedError(view, errorCode, description, failingUrl);
+ }
+ }
+ // Subclass of WebChromeClient to display javascript dialogs.
+ private class SubWindowChromeClient extends WebChromeClient {
+ // This subwindow's tab.
+ private final Tab mTab;
+ // The main WebChromeClient.
+ private final WebChromeClient mClient;
+
+ SubWindowChromeClient(Tab t, WebChromeClient client) {
+ mTab = t;
+ mClient = client;
+ }
+ @Override
+ public void onProgressChanged(WebView view, int newProgress) {
+ mClient.onProgressChanged(view, newProgress);
+ }
+ @Override
+ public boolean onCreateWindow(WebView view, boolean dialog,
+ boolean userGesture, android.os.Message resultMsg) {
+ return mClient.onCreateWindow(view, dialog, userGesture, resultMsg);
+ }
+ @Override
+ public void onCloseWindow(WebView window) {
+ if (Config.DEBUG && window != mTab.mSubView) {
+ throw new AssertionError("Can't close the window");
+ }
+ mActivity.dismissSubWindow(mTab);
+ }
+ }
+ // Background WebChromeClient for focusing tabs
+ private class BackgroundChromeClient extends WebChromeClient {
+ @Override
+ public void onRequestFocus(WebView view) {
+ Tab t = getTabFromView(view);
+ if (t != getCurrentTab()) {
+ mActivity.showTab(t);
+ }
+ }
+ }
+
+ /**
+ * Private class for maintaining Tabs with a main WebView and a subwindow.
+ */
+ public class Tab {
+ // Main WebView
+ private WebView mMainView;
+ // Subwindow WebView
+ private WebView mSubView;
+ // Subwindow container
+ private View mSubViewContainer;
+ // Subwindow callback
+ private SubWindowClient mSubViewClient;
+ // Subwindow chrome callback
+ private SubWindowChromeClient mSubViewChromeClient;
+ // Saved bundle for when we are running low on memory. It contains the
+ // information needed to restore the WebView if the user goes back to
+ // the tab.
+ private Bundle mSavedState;
+ // Extra saved information for displaying the tab in the picker.
+ private String mUrl;
+ private String mTitle;
+
+ // Parent Tab. This is the Tab that created this Tab, or null
+ // if the Tab was created by the UI
+ private Tab mParentTab;
+ // Tab that constructed by this Tab. This is used when this
+ // Tab is destroyed, it clears all mParentTab values in the
+ // children.
+ private Vector<Tab> mChildTabs;
+
+ private Boolean mCloseOnExit;
+
+ // Construct a new tab
+ private Tab(WebView w, boolean closeOnExit) {
+ mMainView = w;
+ mCloseOnExit = closeOnExit;
+ }
+
+ /**
+ * Return the top window of this tab; either the subwindow if it is not
+ * null or the main window.
+ * @return The top window of this tab.
+ */
+ public WebView getTopWindow() {
+ if (mSubView != null) {
+ return mSubView;
+ }
+ return mMainView;
+ }
+
+ /**
+ * Return the main window of this tab. Note: if a tab is freed in the
+ * background, this can return null. It is only guaranteed to be
+ * non-null for the current tab.
+ * @return The main WebView of this tab.
+ */
+ public WebView getWebView() {
+ return mMainView;
+ }
+
+ /**
+ * Return the subwindow of this tab or null if there is no subwindow.
+ * @return The subwindow of this tab or null.
+ */
+ public WebView getSubWebView() {
+ return mSubView;
+ }
+
+ /**
+ * Return the subwindow container of this tab or null if there is no
+ * subwindow.
+ * @return The subwindow's container View.
+ */
+ public View getSubWebViewContainer() {
+ return mSubViewContainer;
+ }
+
+ /**
+ * Get the url of this tab. Valid after calling populatePickerData, but
+ * before calling wipePickerData, or if the webview has been destroyed.
+ *
+ * @return The WebView's url or null.
+ */
+ public String getUrl() {
+ return mUrl;
+ }
+
+ /**
+ * Get the title of this tab. Valid after calling populatePickerData,
+ * but before calling wipePickerData, or if the webview has been
+ * destroyed. If the url has no title, use the url instead.
+ *
+ * @return The WebView's title (or url) or null.
+ */
+ public String getTitle() {
+ return mTitle;
+ }
+
+ private void setParentTab(Tab parent) {
+ mParentTab = parent;
+ // This tab may have been freed due to low memory. If that is the
+ // case, the parent tab index is already saved. If we are changing
+ // that index (most likely due to removing the parent tab) we must
+ // update the parent tab index in the saved Bundle.
+ if (mSavedState != null) {
+ if (parent == null) {
+ mSavedState.remove(PARENTTAB);
+ } else {
+ mSavedState.putInt(PARENTTAB, getTabIndex(parent));
+ }
+ }
+ }
+
+ /**
+ * When a Tab is created through the content of another Tab, then
+ * we associate the Tabs.
+ * @param child the Tab that was created from this Tab
+ */
+ public void addChildTab(Tab child) {
+ if (mChildTabs == null) {
+ mChildTabs = new Vector<Tab>();
+ }
+ mChildTabs.add(child);
+ child.setParentTab(this);
+ }
+
+ private void removeFromTree() {
+ // detach the children
+ if (mChildTabs != null) {
+ for(Tab t : mChildTabs) {
+ t.setParentTab(null);
+ }
+ }
+
+ // Find myself in my parent list
+ if (mParentTab != null) {
+ mParentTab.mChildTabs.remove(this);
+ }
+ }
+
+ /**
+ * If this Tab was created through another Tab, then this method
+ * returns that Tab.
+ * @return the Tab parent or null
+ */
+ public Tab getParentTab() {
+ return mParentTab;
+ }
+
+ /**
+ * Return whether this tab should be closed when it is backing out of
+ * the first page.
+ * @return TRUE if this tab should be closed when exit.
+ */
+ public boolean closeOnExit() {
+ return mCloseOnExit;
+ }
+ };
+
+ // Directory to store thumbnails for each WebView.
+ private final File mThumbnailDir;
+
+ /**
+ * Construct a new TabControl object that interfaces with the given
+ * BrowserActivity instance.
+ * @param activity A BrowserActivity instance that TabControl will interface
+ * with.
+ */
+ TabControl(BrowserActivity activity) {
+ mActivity = activity;
+ mInflateService =
+ ((LayoutInflater) activity.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE));
+ mThumbnailDir = activity.getDir("thumbnails", 0);
+ }
+
+ File getThumbnailDir() {
+ return mThumbnailDir;
+ }
+
+ BrowserActivity getBrowserActivity() {
+ return mActivity;
+ }
+
+ /**
+ * Return the current tab's main WebView. This will always return the main
+ * WebView for a given tab and not a subwindow.
+ * @return The current tab's WebView.
+ */
+ WebView getCurrentWebView() {
+ Tab t = getTab(mCurrentTab);
+ if (t == null) {
+ return null;
+ }
+ return t.mMainView;
+ }
+
+ /**
+ * Return the current tab's top-level WebView. This can return a subwindow
+ * if one exists.
+ * @return The top-level WebView of the current tab.
+ */
+ WebView getCurrentTopWebView() {
+ Tab t = getTab(mCurrentTab);
+ if (t == null) {
+ return null;
+ }
+ return t.mSubView != null ? t.mSubView : t.mMainView;
+ }
+
+ /**
+ * Return the current tab's subwindow if it exists.
+ * @return The subwindow of the current tab or null if it doesn't exist.
+ */
+ WebView getCurrentSubWindow() {
+ Tab t = getTab(mCurrentTab);
+ if (t == null) {
+ return null;
+ }
+ return t.mSubView;
+ }
+
+ /**
+ * Return the tab at the specified index.
+ * @return The Tab for the specified index or null if the tab does not
+ * exist.
+ */
+ Tab getTab(int index) {
+ if (index >= 0 && index < mTabs.size()) {
+ return mTabs.get(index);
+ }
+ return null;
+ }
+
+ /**
+ * Return the current tab.
+ * @return The current tab.
+ */
+ Tab getCurrentTab() {
+ return getTab(mCurrentTab);
+ }
+
+ /**
+ * Return the current tab index.
+ * @return The current tab index
+ */
+ int getCurrentIndex() {
+ return mCurrentTab;
+ }
+
+ /**
+ * Given a Tab, find it's index
+ * @param Tab to find
+ * @return index of Tab or -1 if not found
+ */
+ int getTabIndex(Tab tab) {
+ return mTabs.indexOf(tab);
+ }
+
+ /**
+ * Create a new tab and display the new tab immediately.
+ * @return The newly createTab or null if we have reached the maximum
+ * number of open tabs.
+ */
+ Tab createNewTab(boolean closeOnExit) {
+ int size = mTabs.size();
+ // Return false if we have maxed out on tabs
+ if (MAX_TABS == size) {
+ return null;
+ }
+ // Create a new WebView
+ WebView w = new WebView(mActivity);
+ w.setMapTrackballToArrowKeys(false); // use trackball directly
+ // Add this WebView to the settings observer list and update the
+ // settings
+ final BrowserSettings s = BrowserSettings.getInstance();
+ s.addObserver(w.getSettings()).update(s, null);
+ // Create a new tab and add it to the tab list
+ Tab t = new Tab(w, closeOnExit);
+ mTabs.add(t);
+ return t;
+ }
+
+ /**
+ * Remove the tab from the list. If the tab is the current tab shown, the
+ * last created tab will be shown.
+ * @param t The tab to be removed.
+ */
+ boolean removeTab(Tab t) {
+ if (t == null) {
+ return false;
+ }
+ // Only remove the tab if it is the current one.
+ if (getCurrentTab() == t) {
+ putTabInBackground(t);
+ }
+
+ // Only destroy the WebView if it still exists.
+ if (t.mMainView != null) {
+ // Take down the sub window.
+ dismissSubWindow(t);
+ // Remove the WebView's settings from the BrowserSettings list of
+ // observers.
+ BrowserSettings.getInstance().deleteObserver(
+ t.mMainView.getSettings());
+ // Destroy the main view and subview
+ t.mMainView.destroy();
+ t.mMainView = null;
+ }
+ // clear it's references to parent and children
+ t.removeFromTree();
+
+ // Remove it from our list of tabs.
+ mTabs.remove(t);
+
+ // The tab indices have shifted, update all the saved state so we point
+ // to the correct index.
+ for (Tab tab : mTabs) {
+ if (tab.mChildTabs != null) {
+ for (Tab child : tab.mChildTabs) {
+ child.setParentTab(tab);
+ }
+ }
+ }
+
+
+ // This tab may have been pushed in to the background and then closed.
+ // If the saved state contains a picture file, delete the file.
+ if (t.mSavedState != null) {
+ if (t.mSavedState.containsKey("picture")) {
+ new File(t.mSavedState.getString("picture")).delete();
+ }
+ }
+
+ // Remove it from the queue of viewed tabs.
+ mTabQueue.remove(t);
+ mCurrentTab = -1;
+ return true;
+ }
+
+ /**
+ * Clear the back/forward list for all the current tabs.
+ */
+ void clearHistory() {
+ int size = getTabCount();
+ for (int i = 0; i < size; i++) {
+ Tab t = mTabs.get(i);
+ // TODO: if a tab is freed due to low memory, its history is not
+ // cleared here.
+ if (t.mMainView != null) {
+ t.mMainView.clearHistory();
+ }
+ if (t.mSubView != null) {
+ t.mSubView.clearHistory();
+ }
+ }
+ }
+
+ /**
+ * Destroy all the tabs and subwindows
+ */
+ void destroy() {
+ BrowserSettings s = BrowserSettings.getInstance();
+ for (Tab t : mTabs) {
+ if (t.mMainView != null) {
+ dismissSubWindow(t);
+ s.deleteObserver(t.mMainView.getSettings());
+ t.mMainView.destroy();
+ t.mMainView = null;
+ }
+ }
+ mTabs.clear();
+ mTabQueue.clear();
+ }
+
+ /**
+ * Returns the number of tabs created.
+ * @return The number of tabs created.
+ */
+ int getTabCount() {
+ return mTabs.size();
+ }
+
+ // Used for saving and restoring each Tab
+ private static final String WEBVIEW = "webview";
+ private static final String NUMTABS = "numTabs";
+ private static final String CURRTAB = "currentTab";
+ private static final String CURRURL = "currentUrl";
+ private static final String CURRTITLE = "currentTitle";
+ private static final String CLOSEONEXIT = "closeonexit";
+ private static final String PARENTTAB = "parentTab";
+
+ /**
+ * Save the state of all the Tabs.
+ * @param outState The Bundle to save the state to.
+ */
+ void saveState(Bundle outState) {
+ final int numTabs = getTabCount();
+ outState.putInt(NUMTABS, numTabs);
+ final int index = getCurrentIndex();
+ outState.putInt(CURRTAB, (index >= 0 && index < numTabs) ? index : 0);
+ for (int i = 0; i < numTabs; i++) {
+ final Tab t = getTab(i);
+ if (saveState(t)) {
+ outState.putBundle(WEBVIEW + i, t.mSavedState);
+ }
+ }
+ }
+
+ /**
+ * Restore the state of all the tabs.
+ * @param inState The saved state of all the tabs.
+ * @return True if there were previous tabs that were restored. False if
+ * there was no saved state or restoring the state failed.
+ */
+ boolean restoreState(Bundle inState) {
+ final int numTabs = (inState == null)
+ ? -1 : inState.getInt(NUMTABS, -1);
+ if (numTabs == -1) {
+ return false;
+ } else {
+ final int currentTab = inState.getInt(CURRTAB, -1);
+ for (int i = 0; i < numTabs; i++) {
+ if (i == currentTab) {
+ Tab t = createNewTab(false);
+ // Me must set the current tab before restoring the state
+ // so that all the client classes are set.
+ setCurrentTab(t);
+ if (!restoreState(inState.getBundle(WEBVIEW + i), t)) {
+ Log.w(LOGTAG, "Fail in restoreState, load home page.");
+ t.mMainView.loadUrl(BrowserSettings.getInstance()
+ .getHomePage());
+ }
+ } else {
+ // Create a new tab and don't restore the state yet, add it
+ // to the tab list
+ Tab t = new Tab(null, false);
+ t.mSavedState = inState.getBundle(WEBVIEW + i);
+ if (t.mSavedState != null) {
+ t.mUrl = t.mSavedState.getString(CURRURL);
+ t.mTitle = t.mSavedState.getString(CURRTITLE);
+ }
+ mTabs.add(t);
+ mTabQueue.add(t);
+ }
+ }
+ // Rebuild the tree of tabs. Do this after all tabs have been
+ // created/restored so that the parent tab exists.
+ for (int i = 0; i < numTabs; i++) {
+ final Bundle b = inState.getBundle(WEBVIEW + i);
+ final Tab t = getTab(i);
+ if (b != null && t != null) {
+ final int parentIndex = b.getInt(PARENTTAB, -1);
+ if (parentIndex != -1) {
+ final Tab parent = getTab(parentIndex);
+ if (parent != null) {
+ parent.addChildTab(t);
+ }
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Free the memory in this order, 1) free the background tab; 2) free the
+ * WebView cache;
+ */
+ void freeMemory() {
+ // free the least frequently used background tab
+ Tab t = getLeastUsedTab();
+ if (t != null) {
+ Log.w(LOGTAG, "Free a tab in the browser");
+ freeTab(t);
+ // force a gc
+ System.gc();
+ return;
+ }
+
+ // free the WebView cache
+ Log.w(LOGTAG, "Free WebView cache");
+ WebView view = getCurrentWebView();
+ if (view != null) {
+ view.clearCache(false);
+ }
+ // force a gc
+ System.gc();
+ }
+
+ private Tab getLeastUsedTab() {
+ // Don't do anything if we only have 1 tab.
+ if (getTabCount() == 1) {
+ return null;
+ }
+
+ // Rip through the queue starting at the beginning and teardown the
+ // next available tab.
+ Tab t = null;
+ int i = 0;
+ final int queueSize = mTabQueue.size();
+ if (queueSize == 0) {
+ return null;
+ }
+ do {
+ t = mTabQueue.get(i++);
+ } while (i < queueSize && t != null && t.mMainView == null);
+
+ // Don't do anything if the last remaining tab is the current one.
+ if (t == getCurrentTab()) {
+ return null;
+ }
+
+ return t;
+ }
+
+ private void freeTab(Tab t) {
+ // Store the WebView's state.
+ saveState(t);
+
+ // Tear down the tab.
+ dismissSubWindow(t);
+ // Remove the WebView's settings from the BrowserSettings list of
+ // observers.
+ BrowserSettings.getInstance().deleteObserver(t.mMainView.getSettings());
+ t.mMainView.destroy();
+ t.mMainView = null;
+ }
+
+ /**
+ * Create a new subwindow unless a subwindow already exists.
+ * @return True if a new subwindow was created. False if one already exists.
+ */
+ void createSubWindow() {
+ Tab t = getTab(mCurrentTab);
+ if (t != null && t.mSubView == null) {
+ final View v = mInflateService.inflate(R.layout.browser_subwindow, null);
+ final WebView w = (WebView) v.findViewById(R.id.webview);
+ w.setMapTrackballToArrowKeys(false); // use trackball directly
+ final SubWindowClient subClient =
+ new SubWindowClient(mActivity.getWebViewClient());
+ final SubWindowChromeClient subChromeClient =
+ new SubWindowChromeClient(t,
+ mActivity.getWebChromeClient());
+ w.setWebViewClient(subClient);
+ w.setWebChromeClient(subChromeClient);
+ w.setDownloadListener(mActivity);
+ w.setOnCreateContextMenuListener(mActivity);
+ final BrowserSettings s = BrowserSettings.getInstance();
+ s.addObserver(w.getSettings()).update(s, null);
+ t.mSubView = w;
+ t.mSubViewClient = subClient;
+ t.mSubViewChromeClient = subChromeClient;
+ // FIXME: I really hate having to know the name of the view
+ // containing the webview.
+ t.mSubViewContainer = v.findViewById(R.id.subwindow_container);
+ final ImageButton cancel =
+ (ImageButton) v.findViewById(R.id.subwindow_close);
+ cancel.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ subChromeClient.onCloseWindow(w);
+ }
+ });
+ }
+ }
+
+ /**
+ * Show the tab that contains the given WebView.
+ * @param view The WebView used to find the tab.
+ */
+ Tab getTabFromView(WebView view) {
+ final int size = getTabCount();
+ for (int i = 0; i < size; i++) {
+ final Tab t = getTab(i);
+ if (t.mSubView == view || t.mMainView == view) {
+ return t;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Put the current tab in the background and set newTab as the current tab.
+ * @param newTab The new tab. If newTab is null, the current tab is not
+ * set.
+ */
+ boolean setCurrentTab(Tab newTab) {
+ Tab current = getTab(mCurrentTab);
+ if (current == newTab) {
+ return true;
+ }
+ if (current != null) {
+ // Remove the current WebView and the container of the subwindow
+ putTabInBackground(current);
+ }
+
+ if (newTab == null) {
+ return false;
+ }
+
+ // Move the newTab to the end of the queue
+ int index = mTabQueue.indexOf(newTab);
+ if (index != -1) {
+ mTabQueue.remove(index);
+ }
+ mTabQueue.add(newTab);
+
+ WebView mainView;
+ WebView subView;
+
+ // Display the new current tab
+ mCurrentTab = mTabs.indexOf(newTab);
+ mainView = newTab.mMainView;
+ boolean needRestore = (mainView == null);
+ if (needRestore) {
+ // Same work as in createNewTab() except don't do new Tab()
+ newTab.mMainView = mainView = new WebView(mActivity);
+ mainView.setMapTrackballToArrowKeys(false); // use t-ball directly
+
+ // Add this WebView to the settings observer list and update the
+ // settings
+ final BrowserSettings s = BrowserSettings.getInstance();
+ s.addObserver(mainView.getSettings()).update(s, null);
+ }
+ mainView.setWebViewClient(mActivity.getWebViewClient());
+ mainView.setWebChromeClient(mActivity.getWebChromeClient());
+ mainView.setOnCreateContextMenuListener(mActivity);
+ mainView.setDownloadListener(mActivity);
+ // 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);
+ }
+ if (needRestore) {
+ // Have to finish setCurrentTab work before calling restoreState
+ if (!restoreState(newTab.mSavedState, newTab)) {
+ mainView.loadUrl(BrowserSettings.getInstance().getHomePage());
+ }
+ }
+ return true;
+ }
+
+ /*
+ * 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);
+ }
+ }
+
+ /*
+ * Dismiss the subwindow for the given tab.
+ */
+ void dismissSubWindow(Tab t) {
+ if (t != null && t.mSubView != null) {
+ BrowserSettings.getInstance().deleteObserver(
+ t.mSubView.getSettings());
+ t.mSubView.destroy();
+ t.mSubView = null;
+ t.mSubViewContainer = null;
+ }
+ }
+
+ /**
+ * Ensure that Tab t has a title, url, and favicon.
+ * @param t Tab to populate.
+ */
+ /* package */ void populatePickerData(Tab t) {
+ if (t == null || t.mMainView == null) {
+ return;
+ }
+ // FIXME: The only place we cared about subwindow was for
+ // bookmarking (i.e. not when saving state). Was this deliberate?
+ final WebBackForwardList list = t.mMainView.copyBackForwardList();
+ final WebHistoryItem item =
+ list != null ? list.getCurrentItem() : null;
+ populatePickerData(t, item);
+ }
+
+ // Populate the picker data
+ private void populatePickerData(Tab t, WebHistoryItem item) {
+ if (item != null) {
+ t.mUrl = item.getUrl();
+ t.mTitle = item.getTitle();
+ if (t.mTitle == null) {
+ t.mTitle = t.mUrl;
+ }
+ }
+ }
+
+ /**
+ * Clean up the data for all tabs.
+ */
+ /* package */ void wipeAllPickerData() {
+ int size = getTabCount();
+ for (int i = 0; i < size; i++) {
+ final Tab t = getTab(i);
+ if (t != null && t.mSavedState == null) {
+ t.mUrl = null;
+ t.mTitle = null;
+ }
+ }
+ }
+
+ /*
+ * Save the state for an individual tab.
+ */
+ private boolean saveState(Tab t) {
+ if (t != null) {
+ final WebView w = t.mMainView;
+ // If the WebView is null it means we ran low on memory and we
+ // already stored the saved state in mSavedState.
+ if (w == null) {
+ return true;
+ }
+ final Bundle b = new Bundle();
+ final WebBackForwardList list = w.saveState(b);
+ if (list != null) {
+ final File f = new File(mThumbnailDir, w.hashCode()
+ + "_pic.save");
+ if (w.savePicture(b, f)) {
+ b.putString("picture", f.getPath());
+ }
+ }
+
+ // Store some extra info for displaying the tab in the picker.
+ final WebHistoryItem item =
+ list != null ? list.getCurrentItem() : null;
+ populatePickerData(t, item);
+ if (t.mUrl != null) {
+ b.putString(CURRURL, t.mUrl);
+ }
+ if (t.mTitle != null) {
+ b.putString(CURRTITLE, t.mTitle);
+ }
+ b.putBoolean(CLOSEONEXIT, t.mCloseOnExit);
+
+ // Remember the parent tab so the relationship can be restored.
+ if (t.mParentTab != null) {
+ b.putInt(PARENTTAB, getTabIndex(t.mParentTab));
+ }
+
+ // Remember the saved state.
+ t.mSavedState = b;
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ * Restore the state of the tab.
+ */
+ private boolean restoreState(Bundle b, Tab t) {
+ if (b == null) {
+ return false;
+ }
+ final WebView w = t.mMainView;
+ final WebBackForwardList list = w.restoreState(b);
+ if (list == null) {
+ return false;
+ }
+ if (b.containsKey("picture")) {
+ final File f = new File(b.getString("picture"));
+ w.restorePicture(b, f);
+ f.delete();
+ }
+ t.mSavedState = null;
+ t.mUrl = null;
+ t.mTitle = null;
+ t.mCloseOnExit = b.getBoolean(CLOSEONEXIT);
+ return true;
+ }
+}