summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commitba6d7b853c32ad6c3be26c443daa61f322bcfdc2 (patch)
tree1bb508b3b9cecf0b9f69271d4c4e953a5b5556f5
downloadandroid_packages_apps_Gello-ba6d7b853c32ad6c3be26c443daa61f322bcfdc2.tar.gz
android_packages_apps_Gello-ba6d7b853c32ad6c3be26c443daa61f322bcfdc2.tar.bz2
android_packages_apps_Gello-ba6d7b853c32ad6c3be26c443daa61f322bcfdc2.zip
Initial Contribution
-rw-r--r--Android.mk17
-rw-r--r--AndroidManifest.xml185
-rw-r--r--MODULE_LICENSE_APACHE20
-rw-r--r--NOTICE190
-rw-r--r--assets/plugins/gears-0.4.23.0/permissions_dialog.html2857
-rw-r--r--assets/plugins/gears-0.4.23.0/settings_dialog.html2750
-rw-r--r--assets/plugins/gears-0.4.23.0/shortcuts_dialog.html2907
-rw-r--r--assets/plugins/gears.sobin0 -> 1174896 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/dialog_frame.9.pngbin0 -> 694 bytes
-rwxr-xr-xres/drawable/fav_icn_background.pngbin0 -> 186 bytes
-rw-r--r--res/drawable/ic_browser_done.pngbin0 -> 4337 bytes
-rwxr-xr-xres/drawable/ic_browser_down.pngbin0 -> 764 bytes
-rwxr-xr-xres/drawable/ic_browser_up.pngbin0 -> 795 bytes
-rwxr-xr-xres/drawable/ic_dialog_alert.pngbin0 -> 3645 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_info.pngbin0 -> 3593 bytes
-rwxr-xr-xres/drawable/ic_launcher_browser.pngbin0 -> 3318 bytes
-rw-r--r--res/drawable/ic_launcher_drm_file.pngbin0 -> 2738 bytes
-rw-r--r--res/drawable/ic_menu_back.pngbin0 -> 1237 bytes
-rw-r--r--res/drawable/ic_menu_bookmark.pngbin0 -> 1080 bytes
-rw-r--r--res/drawable/ic_menu_clear_playlist.pngbin0 -> 2281 bytes
-rw-r--r--res/drawable/ic_menu_forward.pngbin0 -> 1228 bytes
-rw-r--r--res/drawable/ic_menu_goto.pngbin0 -> 1636 bytes
-rw-r--r--res/drawable/ic_menu_home.pngbin0 -> 2048 bytes
-rw-r--r--res/drawable/ic_menu_recent_history.pngbin0 -> 2647 bytes
-rw-r--r--res/drawable/ic_menu_refresh.pngbin0 -> 2450 bytes
-rw-r--r--res/drawable/ic_menu_stop.pngbin0 -> 1930 bytes
-rw-r--r--res/drawable/ic_menu_windows.pngbin0 -> 1244 bytes
-rw-r--r--res/drawable/ic_new_window.pngbin0 -> 3010 bytes
-rw-r--r--res/drawable/ic_search_browser.pngbin0 -> 2216 bytes
-rw-r--r--res/drawable/ic_search_category_browser.pngbin0 -> 2395 bytes
-rw-r--r--res/drawable/map.xml23
-rw-r--r--res/drawable/mapfocused.pngbin0 -> 3367 bytes
-rw-r--r--res/drawable/mapunfocused.pngbin0 -> 3340 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/drawable/urlbar_background.9.pngbin0 -> 303 bytes
-rw-r--r--res/layout-land/http_authentication.xml73
-rw-r--r--res/layout-land/page_info.xml66
-rw-r--r--res/layout-land/ssl_certificate.xml266
-rw-r--r--res/layout/add_new_bookmark.xml52
-rw-r--r--res/layout/bookmark_item.xml57
-rw-r--r--res/layout/browser_add_bookmark.xml101
-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.xml128
-rw-r--r--res/layout/browser_subwindow.xml50
-rw-r--r--res/layout/cache_cleared.xml31
-rw-r--r--res/layout/empty_history.xml25
-rw-r--r--res/layout/history_item.xml42
-rw-r--r--res/layout/http_authentication.xml72
-rw-r--r--res/layout/js_prompt.xml38
-rw-r--r--res/layout/no_downloads.xml25
-rw-r--r--res/layout/page_info.xml67
-rw-r--r--res/layout/search_bar.xml73
-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.xml54
-rw-r--r--res/menu/bookmarks.xml21
-rw-r--r--res/menu/bookmarkscontext.xml36
-rw-r--r--res/menu/browser.xml105
-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.xml217
-rw-r--r--res/values-de-rDE/strings.xml270
-rw-r--r--res/values-es-rUS/strings.xml270
-rw-r--r--res/values-fr-rFR/strings.xml270
-rw-r--r--res/values-it-rIT/strings.xml270
-rw-r--r--res/values-zh-rTW/strings.xml270
-rw-r--r--res/values/colors.xml37
-rw-r--r--res/values/strings.xml506
-rw-r--r--res/values/styles.xml39
-rw-r--r--res/xml/browser_preferences.xml164
-rw-r--r--res/xml/debug_preferences.xml62
-rw-r--r--res/xml/searchable.xml27
-rw-r--r--src/com/android/browser/AddBookmarkPage.java195
-rw-r--r--src/com/android/browser/AddNewBookmark.java72
-rw-r--r--src/com/android/browser/BookmarkItem.java105
-rw-r--r--src/com/android/browser/Browser.java59
-rw-r--r--src/com/android/browser/BrowserActivity.java4532
-rw-r--r--src/com/android/browser/BrowserBookmarksAdapter.java522
-rw-r--r--src/com/android/browser/BrowserBookmarksPage.java356
-rw-r--r--src/com/android/browser/BrowserDownloadAdapter.java221
-rw-r--r--src/com/android/browser/BrowserDownloadPage.java470
-rw-r--r--src/com/android/browser/BrowserHistoryPage.java377
-rw-r--r--src/com/android/browser/BrowserPluginList.java66
-rw-r--r--src/com/android/browser/BrowserPreferencesPage.java141
-rw-r--r--src/com/android/browser/BrowserProvider.java566
-rw-r--r--src/com/android/browser/BrowserSettings.java431
-rw-r--r--src/com/android/browser/BrowserYesNoPreference.java56
-rw-r--r--src/com/android/browser/Dots.java83
-rw-r--r--src/com/android/browser/FakeWebView.java106
-rw-r--r--src/com/android/browser/FindDialog.java243
-rw-r--r--src/com/android/browser/GearsDialog.java124
-rw-r--r--src/com/android/browser/GearsDialogService.java96
-rw-r--r--src/com/android/browser/HistoryItem.java126
-rw-r--r--src/com/android/browser/IGearsDialogService.aidl6
-rw-r--r--src/com/android/browser/ImageAdapter.java298
-rw-r--r--src/com/android/browser/ImageGrid.java239
-rw-r--r--src/com/android/browser/KeyTracker.java107
-rw-r--r--src/com/android/browser/TabControl.java840
118 files changed, 23988 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 00000000..f8cfafec
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,17 @@
+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) \
+ src/com/android/browser/IGearsDialogService.aidl
+
+LOCAL_PACKAGE_NAME := Browser
+
+include $(BUILD_PACKAGE)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100644
index 00000000..6bf400de
--- /dev/null
+++ b/AndroidManifest.xml
@@ -0,0 +1,185 @@
+<!--
+/* //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"/>
+
+ <application android:name="Browser"
+ android:label="Browser"
+ 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" />
+ <activity android:name="BrowserActivity" android:label="Browser"
+ 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" />
+ <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="BrowserBookmarksPage" android:label="@string/bookmarks"
+ android:launchMode="singleTop" android:configChanges="orientation|keyboardHidden">
+ </activity>
+
+ <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=""
+ 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>
+
+ <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">
+ <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/plugins/gears-0.4.23.0/permissions_dialog.html b/assets/plugins/gears-0.4.23.0/permissions_dialog.html
new file mode 100644
index 00000000..a332f786
--- /dev/null
+++ b/assets/plugins/gears-0.4.23.0/permissions_dialog.html
@@ -0,0 +1,2857 @@
+<!DOCTYPE html>
+
+<!--
+Copyright 2007, The Android Open Source Project
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ 3. Neither the name of Google Inc. nor the names of its contributors may be
+ used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<html>
+<head>
+ <style type="text/css">
+
+
+/*
+Copyright 2007, The Android Open Source Project
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ 3. Neither the name of Google Inc. nor the names of its contributors may be
+ used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+body, html {
+ background-color:white;
+ padding:0;
+ margin:0;
+ height:100%;
+ overflow:hidden;
+ cursor:default;
+
+ /*
+ Makes the text unselectable in mozilla. See html_diaog.js for IE
+ implementation.
+ */
+ -moz-user-select:none;
+}
+
+body, td, input, select, button {
+ font-family:arial,sans-serif;
+ font-size:13px;
+}
+
+p {
+ margin:0;
+ margin-bottom:1em;
+}
+
+a {
+ color:blue;
+ text-decoration:underline;
+}
+
+button {
+ /*
+ It would be better to express margins in ems, but it causes layout
+ artifacts (presumably because of rounding errors) when combined with
+ the -moz-border-radius property used in button.css on Firefox 2.
+
+ Also: bizarely, if I try to use the more natural margin-left:10px, I get
+ a bug where the buttons bounce on IE when you hover over them.
+ */
+ position:relative;
+ margin-right:7px;
+ left:7px;
+}
+
+#head {
+ padding:1em;
+}
+
+#foot {
+
+ /*
+ On Android we do not use position:absolute due to some
+ rendering limitations (absolute positioning works, but
+ the automatic resizing of dialog is confused by the
+ variable content and the display is wrong, while if we
+ remove the absolute positioning, #foot is back in the
+ flow and the dialog displays correctly)
+ */
+
+ background:white;
+ bottom:0;
+ width:100%;
+}
+
+#button-row {
+ margin:1em;
+}
+
+.accesskey {
+ text-decoration: underline;
+
+ /* IE CSS extension */
+ accelerator: true;
+}
+
+ #content {
+ margin:0 1em;
+ }
+
+ #yellowbox-right {
+ padding-left:1em;
+ padding-right:1em;
+ }
+
+
+
+ #checkbox-row {
+ margin-top:1em;
+
+ margin-bottom:1em;
+
+ }
+
+ .header-icon {
+ margin-right:0.5em;
+ display:none;
+ }
+
+ #custom-icon {
+ display:none;
+ margin-left:1em;
+ }
+
+ #custom-name {
+ font-weight:bold;
+ font-size:1.1em;
+ display:none;
+ }
+
+ #origin {
+ display:none;
+ }
+
+ #custom-message {
+
+ margin-top:6px;
+
+ display:none;
+ }
+
+ #origin-only {
+ font-weight:bold;
+ font-size:1.1em;
+ display:none;
+ }
+
+ #yellowbox {
+ margin:4px 0 4px 0;
+ border:solid #f1cc1d;
+ border-width:1px 0;
+ background:#faefb8;
+ }
+
+ #yellowbox-inner {
+ /* pseudo-rounded corners */
+ margin:0 -1px;
+ border:solid #f1cc1d;
+ border-width:0 1px;
+
+ padding:1em 0;
+
+ }
+
+ #yellowbox p {
+
+ padding:0 1em;
+
+ }
+
+ #permissions-help {
+ margin:4px;
+ display:none;
+ }
+ </style>
+</head>
+<body>
+ <div id="strings" style="display:none">
+ <div id="string-allow"></div>
+ <div id="string-allow-accesskey"></div>
+ <div id="string-deny"></div>
+ <div id="string-deny-accesskey"></div>
+ <div id="string-cancel"></div>
+ <div id="string-trust-site-accesskey"></div>
+ </div>
+
+ <!--
+ PIE only works with one window, and we are in a modal dialog.
+ Using window.open(this.href) replaces the content of the current dialog,
+ which is annoying when no back button is displayed...
+ The workaround is to embed directly a short explanation text, and
+ hide/show the div container for the help and the settings dialog.
+ -->
+
+ <div id="permissions-help">
+ <h2>Information</h2>
+ <p>
+ <span id="string-description"></span>
+ </p>
+ <ul>
+ <li><span id="string-localserver-desc"></span></li>
+ <li><span id="string-database-desc"></span></li>
+ <li><span id="string-workerpool-desc"></span></li>
+ </ul>
+ <a href="#" onclick="showHelp(false); return false;">Go back</a>
+ </div>
+
+
+ <div id="head">
+ <table width="100%" cellpadding="0" cellspacing="0" border="0">
+ <tr>
+ <td valign="top">
+ <!-- On IE Mobile, we can reliably hide or show div elements.
+ Showing or hiding image elements is like playing the Russian roulette:
+ it is potentially lethal. -->
+ <div id="icon-local-data" class="header-icon">
+
+ <img width="32" height="32" src="data:image/png;base64,
+iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0
+d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABLNJREFUeNrEV1toHFUY/ua2
+9yTYpKEJxIRisJJWY+1L82KtLaYPtRE1IERSFYxVKEqLoH2QPFjxhr5ozUuD
+iIZWkdQgWgu2IsQH26TFeklqAxpzaUjcZJPdzVzX/5zdmb3NdlYM5od/z5mz
+Z/7/O//1jJBKpbCeJGKdSf7g/Y5+Gg+6/Tk1HYJpWbBMExaNeXM+mjRaMAwd
+mqZB13XOTU2VaL6tOlfUZeJdh547s1QEgCl/rLMCPp/AF+bmLNTWpg1z+GiY
+C2QKbOGGnoJumDQ3aZ5eUzUVyUQS8XgciUQcrXf60d1VSc8pApbC98OJ1mjU
+unDivQNFIBgAUq7kLJnEEp8piuJpwr33V2D3rhC3ypmhGzj92Z/0XiAjOkWy
+gfa9Vfj6XIxAmBdo8e4iAM5QsCzLsieAurogWu+q5PPRKwmEw2EC4MvIsJA+
+oEggbsHA6flWuGrKnDhN1r+ygCjKzn5Z9iEYCkLx+QtkSgRCcg/C/wpAkrIA
+JALjJ+WKXAygZBawH0HImloQDOe5HBcwAPZ+keYMtCQpfE0QbJnyzQHklwPJ
+eS7fBaIDRqZ3RMmWIXqWmzSAPIS681wOgGvXLQwOJXgW/DZuOBbIk+ltgXx/
+xVessgFcn9AxNp506oQiK1Q3BC6DahPPAM8YyN0QDvud+ZNdsyVeUzIcvCk4
+FvmKT/J2gSBkAUQqpP+3FxQWov4PL62pgie67ykHQL6Jnnl2cE2UU6PzbLhi
+FofsUpLz6eoXn3K2aW7sZwz3vYPV5SWPM5aWLXtVKkZL05M4/2YvUqYBdWUZ
+c+O/IFBRiV+/+pzqgICxb4aw56VX0bBjp1up8nZBbhCWIi2+gurGJqiRMGJT
+E1igthusjEBPJpFcXiSQf6HB5b1C2fV1m5z59Mys7QIph4vJT6eNz9+AQX3f
+1DUkFxeRiEZhGgYx3RFWNb6nRLEulN2U4Y0MjOhUKpsLiPl3uO9d1GxuptMm
+yPxj2PZQF9oOvUhKa6DRRUSURYwM9POYKDZBkezGDG9mxUTMxqJYFLGTF3/A
+qac6sbowDx/1+ejkH9j/xglsffBRNN/3ADr7BqAEK8gqBqYuX8THjx/gQVkc
+53myhQxr7PbjmQXs5H7yOzO9QM2m9vaWvP/v2NcBi65olmGhqn5TuVnAruJ/
+s94v2oFis1sGqBSApqFRBizx51z6/btzHIDso04YCLkGoYtsg3jBMwhZWu05
+dhzaqgp1OYZITQ2+PHYY186f5e4ZPPI0FibGyToWQtUbsbPnCNp6XvAKQkZx
+4kRZdYCZvL33LZzsuBcbGm+FqSbx7esvU0ZopFhPG5N+2nqe53FRRh1gbyza
+F8YSl1KX/hcIkKl1WKRUlMmk1HJTVoqfPlKzwbva59/5osz8TimemZ3HyOhV
+ProRM78vUoVkLEbxEEftllZsaX+Y8l+l248IJRQpXQeY721OUyLjgiy82Zk5
+jI7+RLnh3jiq6hvQfepspu7HsPvoK3x96/5HcOmTk9z8bI+r/mIXLGSCMAug
+pWUbtm/fwRdGRq+UNGZhgLH42Nf7dlkdX1VVe2HeNj8HSJ9LJb8N15roO/Kj
+46/9eJAAWA6A9f48/0eAAQASLt1W3nvcdQAAAABJRU5ErkJggg==
+ ">
+
+ </div>
+ <div id="icon-locationdata" class="header-icon">
+
+ <img width="32" height="32" src="data:image/png;base64,
+iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsT
+AAALEwEAmpwYAAAAIGNIUk0AAG4nAABzrwAA/oMAAH9bAABMGwAA+PYAADHr
+AAAXyBit77wAAAXuSURBVHja7JdLbFxnFcd/3+M+Zjz2jCe168QTx2lw0rRV
+E4PapGqgXhDRpkhTsUCAqDQLFjyE2kUjsUGGJcomG8tdQOsKNggEKZQuKEhu
+FYqAFjshNLaT+DUO8sw44/G8PHPn3rks5tp1wqNjCMqGTzr6pPOd+53/eX7n
+Ct/3uZdLco/X/wGI3X5wbnggCjwHjACDAXsRmAQunJ1a3vifATg3PJAEJqTW
+Ma/hoQyFMhRCSDzXxXMaBSB1dmr59XbvVLtQPiq18XLv4cO2UyrjN320qdGm
+iRkOEYpGsTs77Xql8oXTfV28tbrx9l0DcG54IKkM8+WDJ5+knFulsraOMlvW
+K9NEmSbaMjE7Ogh3d7NZ2Bg5fX/X9FurG7N3KwnPD544RaNWpriapWfoEI89
+/2ViiQTR/n6GP/9F4oOHkFpjd3XRM3QY4Hw7F+t2rDc7IoNWJExm9jJew+XI
+6U/TMzTEvkePIZWBkAq7K8aVX/4cpU26+qLkFxcHzw0PJD8qH9rxwPHuxADr
+KwtU1gpIrTA6wggpKedyFFbSCKkwQmGUNpCGgTIMuvcPABy/GyE47rl11pfT
+gM/To98mti9B7to1/jjxCpd++mPyC/NEeu/n5Fe+jtIGSptYndG2AOg2ABTK
+uSy1YgVlKMxwB0JKaqUSytAoramXywghMSwTZbTI9zyAwt0AsFjbKCOUQmrF
+n374Gp/40vMcOPEEAoEyDBIff4xmw+Xqb95AahNlWGwWN7Ya1H8dggsIH6kl
+UisKK2nWrl9DCMnBJz/F4BOfRAhJKZuhXiyiDRN8KKws4zb9X5imJXeQCAjT
+tNoDcHZq+RIwid9EaYXSmsraGl6jwcLv3mHh3Ys0XZdyLhtYb5K9NkPDqb89
+ejV7BTAAM9iNwOsSEKZptRUCgBe9hjvpuV7M7ND87dIU2ZkPUJaNYYfIz99A
+22EMu4N8eomVy+9vLFUbo0Bsh0IfaAJ1YBNoAF5bjSjwQqpequA3faTWSG20
+yk4byCDxnEqZxT9c5Oam+9L3F9dvAvftoJ5g7wbsoAuLtp/joKGkiqtZhJQt
+EIFipVtxn5v8NfMV57vjC/nLQC+wJ/BCFOgK9m4gtBX+Xc0DZ6eWX/ObzQvF
+TA6lzaDmDZRhcvMv0xSqtXd+sLj+28DCrXirHa+uH4SgDniA/58MJKlqPl9w
+Nqsoo9V0qut5cos3Sj9KF74XGCV3KPQAN4j7LSADlALe7ieiYOA4v55Ot7xg
+mKzO/JVMzf3JyqZbCaz1A3KBSqB0OaBbQQL6jlP/6CoYH0seC+K4vby16vTm
+m9fxGi5COhTXcxSO9Yc/++DeFEKglfA7Ow0vFJLXXdd785WJqaUAjO849fYm
+ovGx5CiQikRCg5FIqOXPpofve0ilWPvVLEapCyElVXOd/FAfjuMRCmkOHLDZ
+t9eiVHZIpzdBcB74zte+8fpGWyPZ+Fjy1Xg8mjp54lH6+uIBeI+52QVsW7F/
+oIfc3Co/++YbeI7Dx756klwDenstYt2KcEhw9MEwQniUSnUu/j5PJuNMAyN3
+gpD/XHksdeaZEfbu7dnm12p1stlb9PZ2A9AzdB9WpwBb0gjbPP54D6dO9fDI
+w1EKBZd6vQlAJKJ4+nScQw/Yx4HJ8bFk9F96YHws+WqiP5raE49hmsaOXPKp
+1eq4rkckYuPjg+/zwcSfqVYbJD73CH19diALlYqL1mBZYpsHPumVGpls+TZP
+iNst70499dQJpNiqnibQxG04XL06x9GjB9Fa4NME38PbdAAPFZJAE99vfeO6
+HrOzZY4csVCquX2PZbm8936ZG/ONbRDiQ+Xx1LNnPoNpSfC9AEAr6ZaXlihX
+yjz00EHw3QCAuy2zJbeVK+Bx5UqRaJckkZA75FrnF9+tcmPenQZG1PhY8oV4
+fM+3nj1zBtMyArQfut5zG8zMzHHogQFse+f57XJ38iwTFhbr9PfrfzjfnxCs
+Zry+SsVfFeNjySQwcWetb08sWrInHiaTLe+6ZcaiNoWN2r8TeU7c69/zvw8A
+aBuA3Xz76MYAAAAASUVORK5CYII=
+ ">
+
+ </div>
+ <!-- Some browsers automatically focus the first focusable item. We
+ don't want anything focused, so we add this fake item. -->
+ <a href="#" id="focus-thief"></a>
+ </td>
+ <td width="100%" valign="middle">
+ <div id="local-data" style="display:none">
+ <span id="string-query-data"></span>
+ </div>
+ <div id="location-data" style="display:none">
+ <span id="string-query-location"></span>
+ </div>
+ </td>
+ </tr>
+ </table>
+ </div>
+
+ <div id="content">
+ <div id="yellowbox">
+ <div id="yellowbox-inner">
+ <table width="100%" cellpadding="0" cellspacing="0" border="0">
+ <tr>
+ <td id="yellowbox-left" valign="top">
+ <img id="custom-icon" width="0" height="0" src="blank.gif">
+ </td>
+ <td id="yellowbox-right" width="100%" valign="middle">
+ <div id="custom-name"></div>
+ <div id="origin"></div>
+ <div id="origin-only"></div>
+ <div id="custom-message"></div>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </div>
+
+ <div id="string-location-privacy-statement" style="display:none; padding-top:3px;"></div>
+
+ <p id="checkbox-row" style="display:none">
+ <table width="100%" cellspacing="0" cellpadding="0" border="0">
+ <tr>
+ <td valign="middle">
+ <input type="checkbox" id="unlock" accesskey="T"
+ onclick="updateAllowButtonEnabledState()">
+ </td>
+ <td valign="middle">
+ <label for="unlock">
+ &nbsp;<span id="string-trust-site"></span>
+ </label>
+ </td>
+ </tr>
+ </table>
+ </p>
+
+ </div>
+
+ <div id="foot">
+
+ <div id="button-row">
+ <table cellpadding="0" cellspacing="0" border="0">
+ <tr>
+
+
+ <td width="100%" valign="middle">
+
+ <a href="#" onclick="denyAccessPermanently(); return false;">
+
+ <span id="string-never-allow-link"></span></a>
+
+ </td>
+
+
+ <td nowrap="true" align="right" valign="middle">
+ <button id="allow-button" class="custom"
+ onclick="allowAccessPermanently(); return false;"></button
+ ><button id="deny-button" class="custom"
+ onclick="denyAccessTemporarily(); return false;"></button>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </div>
+
+
+</body>
+<!--
+We include all files through m4 as the HTML dialog implementation on
+PocketIE does not support callbacks for loading external JavaScript files.
+TODO: find a better way to include scripts for PIE
+-->
+<script>
+/*
+Copyright (c) 2005 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+//Array.prototype.______array = '______array';
+
+var JSON = {
+ org: 'http://www.JSON.org',
+ copyright: '(c)2005 JSON.org',
+ license: 'http://www.crockford.com/JSON/license.html',
+
+ stringify: function (arg) {
+ var c, i, l, s = '', v;
+
+ switch (typeof arg) {
+ case 'object':
+ if (arg) {
+ if (arg.constructor == Array.prototype.constructor) {
+ for (i = 0; i < arg.length; ++i) {
+ v = this.stringify(arg[i]);
+ if (s) {
+ s += ',';
+ }
+ s += v;
+ }
+ return '[' + s + ']';
+ } else if (typeof arg.toString != 'undefined') {
+ for (i in arg) {
+ v = arg[i];
+ if (typeof v != 'undefined' && typeof v != 'function') {
+ v = this.stringify(v);
+ if (s) {
+ s += ',';
+ }
+ s += this.stringify(i) + ':' + v;
+ }
+ }
+ return '{' + s + '}';
+ }
+ }
+ return 'null';
+ case 'number':
+ return isFinite(arg) ? String(arg) : 'null';
+ case 'string':
+ l = arg.length;
+ s = '"';
+ for (i = 0; i < l; i += 1) {
+ c = arg.charAt(i);
+ if (c >= ' ') {
+ if (c == '\\' || c == '"') {
+ s += '\\';
+ }
+ s += c;
+ } else {
+ switch (c) {
+ case '\b':
+ s += '\\b';
+ break;
+ case '\f':
+ s += '\\f';
+ break;
+ case '\n':
+ s += '\\n';
+ break;
+ case '\r':
+ s += '\\r';
+ break;
+ case '\t':
+ s += '\\t';
+ break;
+ default:
+ c = c.charCodeAt();
+ s += '\\u00' + Math.floor(c / 16).toString(16) +
+ (c % 16).toString(16);
+ }
+ }
+ }
+ return s + '"';
+ case 'boolean':
+ return String(arg);
+ default:
+ return 'null';
+ }
+ },
+ parse: function (text) {
+ var at = 0;
+ var ch = ' ';
+
+ function error(m) {
+ throw {
+ name: 'JSONError',
+ message: m,
+ at: at - 1,
+ text: text
+ };
+ }
+
+ function next() {
+ ch = text.charAt(at);
+ at += 1;
+ return ch;
+ }
+
+ function white() {
+ while (ch) {
+ if (ch <= ' ') {
+ next();
+ } else if (ch == '/') {
+ switch (next()) {
+ case '/':
+ while (next() && ch != '\n' && ch != '\r') {}
+ break;
+ case '*':
+ next();
+ for (;;) {
+ if (ch) {
+ if (ch == '*') {
+ if (next() == '/') {
+ next();
+ break;
+ }
+ } else {
+ next();
+ }
+ } else {
+ error("Unterminated comment");
+ }
+ }
+ break;
+ default:
+ error("Syntax error");
+ }
+ } else {
+ break;
+ }
+ }
+ }
+
+ function string() {
+ var i, s = '', t, u;
+
+ if (ch == '"') {
+outer: while (next()) {
+ if (ch == '"') {
+ next();
+ return s;
+ } else if (ch == '\\') {
+ switch (next()) {
+ case 'b':
+ s += '\b';
+ break;
+ case 'f':
+ s += '\f';
+ break;
+ case 'n':
+ s += '\n';
+ break;
+ case 'r':
+ s += '\r';
+ break;
+ case 't':
+ s += '\t';
+ break;
+ case 'u':
+ u = 0;
+ for (i = 0; i < 4; i += 1) {
+ t = parseInt(next(), 16);
+ if (!isFinite(t)) {
+ break outer;
+ }
+ u = u * 16 + t;
+ }
+ s += String.fromCharCode(u);
+ break;
+ default:
+ s += ch;
+ }
+ } else {
+ s += ch;
+ }
+ }
+ }
+ error("Bad string");
+ }
+
+ function array() {
+ var a = [];
+
+ if (ch == '[') {
+ next();
+ white();
+ if (ch == ']') {
+ next();
+ return a;
+ }
+ while (ch) {
+ a.push(value());
+ white();
+ if (ch == ']') {
+ next();
+ return a;
+ } else if (ch != ',') {
+ break;
+ }
+ next();
+ white();
+ }
+ }
+ error("Bad array");
+ }
+
+ function object() {
+ var k, o = {};
+
+ if (ch == '{') {
+ next();
+ white();
+ if (ch == '}') {
+ next();
+ return o;
+ }
+ while (ch) {
+ k = string();
+ white();
+ if (ch != ':') {
+ break;
+ }
+ next();
+ o[k] = value();
+ white();
+ if (ch == '}') {
+ next();
+ return o;
+ } else if (ch != ',') {
+ break;
+ }
+ next();
+ white();
+ }
+ }
+ error("Bad object");
+ }
+
+ function number() {
+ var n = '', v;
+ if (ch == '-') {
+ n = '-';
+ next();
+ }
+ while (ch >= '0' && ch <= '9') {
+ n += ch;
+ next();
+ }
+ if (ch == '.') {
+ n += '.';
+ while (next() && ch >= '0' && ch <= '9') {
+ n += ch;
+ }
+ }
+ if (ch == 'e' || ch == 'E') {
+ n += 'e';
+ next();
+ if (ch == '-' || ch == '+') {
+ n += ch;
+ next();
+ }
+ while (ch >= '0' && ch <= '9') {
+ n += ch;
+ next();
+ }
+ }
+ v = +n;
+ if (!isFinite(v)) {
+ ////error("Bad number");
+ } else {
+ return v;
+ }
+ }
+
+ function word() {
+ switch (ch) {
+ case 't':
+ if (next() == 'r' && next() == 'u' && next() == 'e') {
+ next();
+ return true;
+ }
+ break;
+ case 'f':
+ if (next() == 'a' && next() == 'l' && next() == 's' &&
+ next() == 'e') {
+ next();
+ return false;
+ }
+ break;
+ case 'n':
+ if (next() == 'u' && next() == 'l' && next() == 'l') {
+ next();
+ return null;
+ }
+ break;
+ }
+ error("Syntax error");
+ }
+
+ function value() {
+ white();
+ switch (ch) {
+ case '{':
+ return object();
+ case '[':
+ return array();
+ case '"':
+ return string();
+ case '-':
+ return number();
+ default:
+ return ch >= '0' && ch <= '9' ? number() : word();
+ }
+ }
+
+ return value();
+ }
+};
+
+// Copyright 2008, The Android Open Source Project
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// 3. Neither the name of Google Inc. nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//=============================================================================
+// Various basic JavaScript utilities.
+//=============================================================================
+
+/**
+ * Check that the type is not undefined (we do it here as on
+ * some devices typeof returns unknown instead of undefined...).
+ * We have to pass the evaluation of (typeof elem) (i.e., a string)
+ * to the function rather than simply (element) -- passing an
+ * undefined object would make the function crash on PIE.
+ */
+function isDefined(type) {
+ return type != 'undefined' && type != 'unknown';
+}
+
+/**
+ * Returns true if val is a function.
+ */
+function isFunction(val) {
+ return typeof val == "function";
+}
+
+// TODO(aa): more basic type checking here.
+
+
+/**
+ * Creates a 'bound' function that calls the current function with a preset
+ * 'this' object and set of arguments.
+ */
+Function.prototype.bind = function(obj) {
+ var preArgs = Array.prototype.slice.call(arguments, 1);
+ var self = this;
+ return function() {
+ var postArgs = Array.prototype.slice.call(arguments, 0);
+ var totalArgs = preArgs.concat(postArgs);
+ return self.apply(obj, totalArgs);
+ }
+};
+
+/**
+ * Creates a partially applied function that calls the current function with
+ * a preset set of arguments.
+ */
+Function.prototype.partial = function() {
+ var args = Array.prototype.slice.call(arguments, 0);
+ return this.bind(null, args);
+};
+
+/**
+ * Binds all the methods in obj to obj.
+ */
+function bindMethods(obj) {
+ for (var p in obj) {
+ if (isFunction(obj[p])) {
+ obj[p] = obj[p].bind(obj);
+ }
+ }
+}
+
+
+/**
+ * Trim leading and trailing whitespace from a string.
+ */
+String.prototype.trim = function(str) {
+ return this.replace(/^\s+/, "").replace(/\s+$/, "");
+};
+
+
+/**
+ * Find the index of a given element in an array. Returns -1 if the item does
+ * not exist.
+ */
+Array.prototype.indexOf = function(item) {
+ for (var i = 0; i < this.length; i++) {
+ if (this[i] === item) {
+ return i;
+ }
+ }
+ return -1;
+};
+
+/**
+ * Makes a deep copy of an object.
+ */
+function cloneObject(original) {
+ var newObject = new Object();
+ for (i in original) {
+ if (typeof original[i] == 'object') {
+ newObject[i] = cloneObject(original[i]);
+ }
+ else {
+ newObject[i] = original[i];
+ }
+ }
+ newObject.length = original.length;
+ return newObject;
+}
+
+var browser = {};
+
+(function() {
+ var ua = navigator.userAgent;
+ browser.opera = typeof opera != "undefined";
+ browser.ie = !browser.opera && ua.indexOf("MSIE") > -1;
+ browser.ie_mobile = (ua.indexOf("Windows CE") > -1) &&
+ (ua.indexOf("MSIE") > -1);
+ browser.webkit = ua.indexOf("WebKit") > -1;
+ // WebKit also gives product == "gecko".
+ browser.mozilla = !browser.webkit && navigator.product == "Gecko";
+ browser.safari = ua.indexOf("Safari") > -1;
+ browser.mac = navigator.platform.indexOf("Mac") > -1;
+ browser.linux = navigator.platform.indexOf("Linux") > -1;
+ browser.windows = navigator.platform.indexOf("Win") > -1;
+ browser.android = ua.indexOf("Android") > -1;
+ if (browser.android) {
+ browser.safari = false;
+ }
+ // TODO(aa): Add detection for more browsers, as necessary.
+})();
+
+// Copyright 2008, The Android Open Source Project
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// 3. Neither the name of Google Inc. nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//=============================================================================
+// DOM manipulation utilities.
+// Requires: base.js
+//=============================================================================
+
+var dom = {};
+
+/**
+ * Provides a cross-browser way of getting an element by its id
+ */
+dom.getElementById = function(id) {
+ if (isDefined(typeof document.getElementById)) {
+ return document.getElementById(id);
+ } else if (isDefined(typeof document.all)) {
+ return document.all[id];
+ }
+ throw new Error("Failed to get element by ID.");
+};
+
+/**
+ * Gets the position and dimensions of an element in pixels in relation
+ * to the window origin.
+ */
+dom.getPosition = function(elem) {
+ var ret = { 'left' : 0,
+ 'top' : 0,
+ 'width' : elem.width,
+ 'height' : elem.height };
+
+ if (!elem.offsetParent) return ret;
+
+ var left = 0;
+ var top = 0;
+ while (elem) {
+ left += elem.offsetLeft;
+ top += elem.offsetTop;
+ elem = elem.offsetParent
+ }
+ ret.left = left;
+ ret.top = top;
+ return ret;
+};
+
+/**
+ * Returns the height of the inside of the window.
+ */
+dom.getWindowInnerHeight = function() {
+ if (isDefined(typeof window.innerHeight)) { // Firefox
+ return window.innerHeight;
+ } else if (isDefined(typeof document.body.offsetHeight)) { // IE
+ return document.body.offsetHeight;
+ }
+
+ throw new Error("Could not get windowInnerHeight.");
+};
+
+/**
+ * Add an event listener to an element cross-browser.
+ */
+dom.addEvent = function(element, eventName, handler) {
+ var wrapper = dom._callEventHandler.bind(dom, handler);
+
+ if (isDefined(typeof element.addEventListener)) {
+ // Standards-compatible browsers
+ element.addEventListener(eventName, wrapper, false);
+ } else if (isDefined(typeof element.attachEvent)) {
+ // IE
+ element.attachEvent("on" + eventName, wrapper);
+ } else {
+ throw new Error('Failed to attach event.');
+ }
+};
+
+/**
+ * Private helper for calling event handlers compatibly across browsers.
+ */
+dom._callEventHandler = function(handler, e) {
+ // Older versions of IE don't pass event to the handler.
+ if (!e) e = window.event;
+
+ // TODO(aa): Normalize event object properties.
+
+ return handler(e);
+};
+
+/**
+ * Gets the CSS classes for an element.
+ */
+dom.getClasses = function(elm) {
+ return elm.className.split(/\s+/);
+};
+
+/**
+ * Check is an element has a particular CSS class name.
+ */
+dom.hasClass = function(elm, className) {
+ return this.getClasses(elm).indexOf(className) > -1;
+};
+
+/**
+ * Adds a CSS class to an element. Do nothing if the class already exists.
+ */
+dom.addClass = function(elm, className) {
+ var classes = this.getClasses(elm);
+ if (classes.indexOf(className) > -1) {
+ return;
+ }
+ classes.push(className);
+ elm.className = classes.join(" ");
+};
+
+/**
+ * Removes a CSS class from an element. Do nothing if the class doesn't exist.
+ */
+dom.removeClass = function(elm, className) {
+ var classes = this.getClasses(elm);
+ var index = classes.indexOf(className);
+ if (index == -1) {
+ return;
+ }
+ classes.splice(index, 1);
+ elm.className = classes.join(" ");
+};
+
+/**
+ * Gets the text (non-html) content of an element.
+ */
+dom.getTextContent = function(elm) {
+ if (isDefined(typeof elm.textContent)) {
+ return elm.textContent;
+ } else if (isDefined(typeof elm.innerText)) {
+ return elm.innerText;
+ } else {
+ throw new Error("Could not find a property to get text content.");
+ }
+};
+
+/**
+ * Sets the text (non-html) contents of an element.
+ */
+dom.setTextContent = function(elm, text) {
+ if (isDefined(typeof elm.textContent)) {
+ elm.textContent = text;
+ } else if (isDefined(typeof elm.innerText)) {
+ elm.innerText = text;
+ } else {
+ throw new Error("Could not find a property to set text content.");
+ }
+};
+
+// Copyright 2007, The Android Open Source Project
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// 3. Neither the name of Google Inc. nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * Initialize the base functionality of the dialog.
+ */
+function initDialog() {
+ if (!browser.ie_mobile) {
+ dom.addEvent(document, "keyup", handleKeyUp);
+ } else {
+ var buttonRowElem = null;
+ if (window.pie_dialog.IsSmartPhone()) {
+ buttonRowElem = dom.getElementById("button-row-smartphone");
+ } else {
+ buttonRowElem = dom.getElementById("button-row");
+ }
+ if (buttonRowElem) {
+ buttonRowElem.style.display = 'block';
+ }
+ }
+ if (browser.ie_mobile) {
+ window.pie_dialog.SetScriptContext(window);
+ window.pie_dialog.ResizeDialog();
+ }
+}
+
+/**
+ * Set the label of an input button, and optionally, its accesskey.
+ */
+function setButtonLabel(textID, elemID, accessKeyID) {
+ var textElem = dom.getElementById(textID);
+ var buttonElem = dom.getElementById(elemID);
+ if (textElem == null || buttonElem == null) {
+ return;
+ }
+
+ var accessKeyElem = null;
+ if (isDefined(typeof accessKeyID)) {
+ accessKeyElem = dom.getElementById(accessKeyID);
+ }
+
+ // This function works for two different kinds of buttons. Simple buttons
+ // based on the <input type="button"> tag, and custom buttons based on a
+ // <button> tag with the css class "custom".
+ // Note that on Windows Mobile 5, the tagName property is not defined.
+ // We can therefore safely assume that the converse is also true:
+ // if tagName is not defined, then the platform must be Windows Mobile 5.
+ if (!isDefined(typeof buttonElem.tagName) ||
+ buttonElem.tagName.toLowerCase() == "input") {
+ buttonElem.value = dom.getTextContent(textElem).trim();
+ if (accessKeyElem != null) {
+ buttonElem.accessKey = dom.getTextContent(accessKeyElem).trim();
+ }
+ } else if (buttonElem.tagName.toLowerCase() == "button") {
+ var text = dom.getTextContent(textElem).trim();
+
+ if (accessKeyElem != null) {
+ // Some browsers use the accessKey attribute of the the anchor tag.
+ var accessKey = dom.getTextContent(accessKeyElem).trim();
+ buttonElem.accessKey = accessKey;
+
+ // Find the first matching character in the text and mark it.
+ // Note: this form of String.replace() only replaces the first occurence.
+ text = text.replace(accessKey,
+ "<span class='accesskey'>" + accessKey + "</span>");
+ }
+
+ buttonElem.innerHTML = text;
+ } else {
+ throw new Error("Unexpected button tag name: " + buttonElem.tagName);
+ }
+}
+
+/**
+ * Allows a dialog to do custom layout when the window changes sizes. The
+ * provided function will be called with the height of the content area when the
+ * dialog should relayout.
+ */
+function initCustomLayout(layoutFunction) {
+ function doLayout() {
+ layoutFunction(getContentHeight());
+ }
+
+ doLayout();
+
+ // We do an additional layout in onload because sometimes things aren't
+ // stabilized when the first doLayout() is called above.
+ dom.addEvent(window, "load", doLayout);
+ dom.addEvent(window, "resize", doLayout);
+
+ // Mozilla doesn't fire continuous events during resize, so if we want to get
+ // somewhat smooth resizing, we need to run our own timer loop. This still
+ // doesn't look perfect, because the timer goes off out of sync with the
+ // browser's internal reflow, but I think it's better than nothing.
+ // TODO(aa): Keep looking for a way to get an event for each reflow, like IE.
+ if (browser.mozilla) {
+ var lastHeight = -1;
+
+ function maybeDoLayout() {
+ var currentHeight = dom.getWindowInnerHeight();
+ if (currentHeight != lastHeight) {
+ lastHeight = currentHeight;
+ doLayout();
+ }
+ }
+
+ window.setInterval(maybeDoLayout, 30);
+ }
+}
+
+/**
+ * Get the JSON-formatted arguments that were passed by the caller.
+ */
+function getArguments() {
+ var argsString;
+ if (browser.ie_mobile) {
+ argsString = window.pie_dialog.GetDialogArguments();
+ } else if (browser.ie) {
+ argsString = window.external.GetDialogArguments();
+ } else if (browser.mozilla) {
+ argsString = getFirefoxArguments(window.arguments[0]);
+ } else if (browser.safari) {
+ argsString = window.gears_dialogArguments;
+ } else if (browser.android) {
+ argsString = "" + window.bridge.getDialogArguments();
+ }
+
+ if (typeof argsString == "string") {
+ return JSON.parse(argsString);
+ } else {
+ return null;
+ }
+}
+
+/**
+ * Helper used by getArguments(). Getting the arguments in the right type is
+ * more involved in Firefox.
+ */
+function getFirefoxArguments(windowArguments) {
+ var Ci = Components.interfaces;
+ windowArguments.QueryInterface(Ci.nsIProperties);
+ var supportsString =
+ windowArguments.get("dialogArguments", Ci.nsISupportsString);
+ return supportsString.data;
+}
+
+/**
+ * Close the dialog, sending the specified result back to the caller.
+ */
+function saveAndClose(resultObject) {
+ var resultString = JSON.stringify(resultObject);
+ if (browser.ie_mobile) {
+ window.pie_dialog.CloseDialog(resultString);
+ } else if (browser.ie) {
+ window.external.CloseDialog(resultString);
+ } else if (browser.mozilla) {
+ saveFirefoxResults(resultString);
+ window.close();
+ } else if (browser.safari) {
+ window.gears_dialog.setResults(resultString);
+ } else if (browser.android) {
+ window.bridge.closeDialog(resultString);
+ }
+}
+
+/**
+ * Helper used by endDialog() for Firefox.
+ */
+function saveFirefoxResults(resultString) {
+ var Cc = Components.classes;
+ var Ci = Components.interfaces;
+
+ var props = window.arguments[0].QueryInterface(Ci.nsIProperties);
+ var supportsString = Cc["@mozilla.org/supports-string;1"]
+ .createInstance(Ci.nsISupportsString);
+
+ supportsString.data = resultString;
+ props.set("dialogResult", supportsString);
+}
+
+/**
+ * Returns the height of the content area of the dialog.
+ */
+function getContentHeight() {
+ var head = dom.getElementById("head");
+ var foot = dom.getElementById("foot");
+ return dom.getWindowInnerHeight() - head.offsetHeight - foot.offsetHeight;
+}
+
+/**
+ * For some reason ESC doesn't work on HTML dialogs in either Firefox or IE. So
+ * we implement it manually.
+ */
+function handleKeyUp(e) {
+ var ESC_KEY_CODE = 27;
+
+ if (e.keyCode == ESC_KEY_CODE) {
+ saveAndClose(null);
+ }
+}
+
+/**
+ * Disables a button in the right way, whether it is normal or custom.
+ */
+function disableButton(buttonElem) {
+ buttonElem.disabled = true;
+
+ if (browser.ie_mobile) {
+ window.pie_dialog.SetButtonEnabled(false);
+ }
+
+ if (!isDefined(typeof buttonElem.tagName) ||
+ buttonElem.tagName.toLowerCase() == "input") {
+ buttonElem.style.color = "gray";
+ } else if (buttonElem.tagName.toLowerCase() == "button") {
+ dom.addClass(buttonElem, "disabled");
+ } else {
+ throw new Error("Unexpected tag name: " + buttonElem.tagName);
+ }
+}
+
+/**
+ * Enables a button in the right way, whether it is normal or custom.
+ */
+function enableButton(buttonElem) {
+ buttonElem.disabled = false;
+
+ if (browser.ie_mobile) {
+ window.pie_dialog.SetButtonEnabled(true);
+ }
+
+ if (!isDefined(typeof buttonElem.tagName) ||
+ buttonElem.tagName.toLowerCase() == "input") {
+ buttonElem.style.color = "black";
+ } else if (buttonElem.tagName.toLowerCase() == "button") {
+ dom.removeClass(buttonElem, "disabled");
+ } else {
+ throw new Error("Unexpected tag name: " + buttonElem.tagName);
+ }
+}
+
+/**
+ * Returns a wrapped domain (useful for small screens dialogs,
+ * e.g. windows mobile devices)
+ */
+function wrapDomain(str) {
+ if (!browser.ie_mobile) {
+ return str;
+ }
+
+ var max = 20;
+ var url;
+ var scheme_start = str.indexOf("://");
+ var scheme = "";
+
+ if (scheme_start != -1) {
+ scheme = str.substring(0, scheme_start);
+ scheme += "://";
+ // there's automatically an hyphenation
+ // point used by the browser after http://
+ // so we only have to hyphenate the
+ // rest of the string
+ url = str.substring(scheme.length);
+ } else {
+ url = str;
+ }
+
+ // We hyphenate the string on every dot
+ var components = url.split(".");
+ if (components.length < 1) {
+ return str;
+ }
+
+ var content = components[0];
+ var len = content.length;
+ for (var i=1; i < components.length; i++) {
+ var elem = components[i];
+ content += ".";
+ len++;
+ if (len + elem.length > max) {
+ content += "<br>";
+ len = 0;
+ }
+ content += elem;
+ len += elem.length;
+ }
+ return scheme + content;
+}
+
+/**
+ * Resizes the dialog to fit the content size.
+ */
+function resizeDialogToFitContent(opt_minDialogInnerHeight) {
+ // This does not work on PIE (no height measurement)
+ if (browser.ie_mobile) {
+ window.pie_dialog.ResizeDialog();
+ return;
+ }
+ // window.resize() is not working on Android, we bail out.
+ if (browser.android) {
+ return;
+ }
+
+ // Resize the window to fit
+ var contentDiv = dom.getElementById("content");
+ var contentHeightProvided = getContentHeight();
+ var contentHeightDesired = contentDiv.offsetHeight;
+
+ var dialogHeightProvided = dom.getWindowInnerHeight();
+ var dialogHeightDesired =
+ dialogHeightProvided + (contentHeightDesired - contentHeightProvided);
+
+ var minDialogHeight = opt_minDialogInnerHeight || 0;
+ var maxDialogHeight = 400; // arbitrary max height for a dialog to resize to
+
+ var targetHeight = Math.max(dialogHeightDesired, minDialogHeight);
+ targetHeight = Math.min(dialogHeightDesired, maxDialogHeight);
+
+ if (targetHeight != dialogHeightProvided) {
+ var dy = targetHeight - dialogHeightProvided;
+ window.resizeBy(0, dy);
+ }
+}
+
+/**
+ * Safari running on OSX 10.4 can't load images dynamically from the network,
+ * this helper function calls gears-specific code to work around this
+ * limitation.
+ */
+function loadImage(elem, image_url) {
+ if (browser.safari) {
+ var position = dom.getPosition(elem);
+ window.gears_dialog.loadImageIntoElement(image_url, position.top,
+ position.left,
+ position.width,
+ position.height);
+ } else {
+ elem.src = image_url;
+ }
+}
+
+// Copyright 2008, The Android Open Source Project
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// 3. Neither the name of Google Inc. nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//=============================================================================
+// Implements the behavior of our custom buttons. There is no native support in
+// IE for rounded corners on HTML elements, so this script also implements a
+// hack to simulate them in that browser.
+//
+// Requires: base.js, dom.js
+//=============================================================================
+
+/**
+ * Constructor.
+ */
+function CustomButton(htmlButton) {
+ bindMethods(this);
+
+ this._focused = false;
+ this._htmlButton = htmlButton;
+
+ if (!document.all) { // not IE
+ dom.addEvent(htmlButton, "mouseover", this._handleMouseOver);
+ dom.addEvent(htmlButton, "mouseout", this._handleMouseOut);
+ } else { // IE
+ // mouseover/mouseout behave badly with nested elements in IE. Luckily,
+ // mouseenter/mouseleave are provided and have the equivalent semantics.
+ dom.addEvent(htmlButton, "mouseenter", this._handleMouseOver);
+ dom.addEvent(htmlButton, "mouseleave", this._handleMouseOut);
+
+ // We also handle focus and blur in IE so that we can update the corners
+ // to match the black border IE provides.
+ dom.addEvent(htmlButton, "focusin", this._handleFocus);
+ dom.addEvent(htmlButton, "blur", this._handleBlur);
+
+ dom.addClass(htmlButton, "ie");
+ this._createCorners();
+ }
+
+ // Limit button widths to a minimum of 60px. They look funny skinnier.
+ if (htmlButton.offsetWidth < 60) {
+ htmlButton.style.width = "60px";
+ }
+
+ htmlButton.style.visibility = "visible";
+}
+
+/**
+ * Initializes all the custom buttons on the page.
+ */
+CustomButton.initializeAll = function() {
+ var buttons = document.getElementsByTagName("button");
+ for (var i = 0, button; button = buttons[i]; i++) {
+ if (dom.hasClass(button, "custom")) {
+ new CustomButton(button);
+ }
+ }
+};
+
+/**
+ * Highlights the button on mouseover. Note that this gets called multiple
+ * times while the mouse goes over sub elements inside the button.
+ */
+CustomButton.prototype._handleMouseOver = function() {
+ if (!this._htmlButton.disabled && !this._focused) {
+ dom.addClass(this._htmlButton, "hover");
+ this._setCornerColor("blue");
+ }
+};
+
+/**
+ * Undoes the highlighting of the button on mouseout. Note that this gets called
+ * multiple times when the mouse moves out of sub elements, even if it is still
+ * over the button. That is OK, as long as there is immediately a mouseover
+ * event.
+ */
+CustomButton.prototype._handleMouseOut = function() {
+ if (!this._htmlButton.disabled && !this._focused) {
+ dom.removeClass(this._htmlButton, "hover");
+ this._setCornerColor("grey");
+ }
+};
+
+/**
+ * Highlights the button on focus. Currently only used for IE.
+ * TODO(aa): The black highlight looks cool. Consider for other browsers.
+ */
+CustomButton.prototype._handleFocus = function() {
+ dom.removeClass(this._htmlButton, "hover");
+ this._setCornerColor("black");
+ this._focused = true;
+};
+
+/**
+ * Undoes the highlighting of the button on blur.
+ */
+CustomButton.prototype._handleBlur = function() {
+ this._setCornerColor("grey");
+ this._focused = false;
+};
+
+/**
+ * Helper to create the graphics that make the button have rounded corners in
+ * IE.
+ */
+CustomButton.prototype._createCorners = function() {
+ // Making the button position:relative makes it possible to position things
+ // inside it relative to it's border.
+ this._htmlButton.style.position = "relative";
+
+ var verticalEdges = ["left", "right"];
+ var horizontalEdges = ["top", "bottom"];
+ var colors = ["grey", "blue", "black"];
+
+ for (var i = 0; i < verticalEdges.length; i++) {
+ for (var j = 0; j < horizontalEdges.length; j++) {
+ for (var k = 0; k < colors.length; k++) {
+ var img = document.createElement("img");
+ img.src = "button_corner_" + colors[k] + ".gif";
+ img.color = colors[k];
+ img.style.position = "absolute";
+ img.style[verticalEdges[i]] = "-2px";
+ img.style[horizontalEdges[j]] = "-2px";
+ img.style.filter =
+ "progid:DXImageTransform.Microsoft.BasicImage(Rotation=" +
+ (i + j) + ")";
+ this._htmlButton.appendChild(img);
+ }
+ }
+ }
+
+ this._setCornerColor("grey");
+};
+
+/**
+ * Sets the color of the corners in IE by changing the stack order of the corner
+ * images.
+ */
+CustomButton.prototype._setCornerColor = function(newColor) {
+ var imgs = this._htmlButton.getElementsByTagName("img");
+ for (var i = 0, img; img = imgs[i]; i++) {
+ img.style.zIndex = Number(img.color == newColor);
+ }
+};
+
+var localized_strings = {
+ "el": {
+ "string-workerpool-desc": "Εκτέλεση ασύγχρονου JavaScript για βελτίωση απόκρισης εφαρμογών",
+ "string-allow": "Να επιτρέπεται",
+ "string-query-data": "Ο παρακάτω ιστότοπος επιθυμεί να αποθηκεύει πληροφορίες στον υπολογιστή σας χρησιμοποιώντας το Gears.",
+ "string-deny-accesskey": "D",
+ "string-query-location": "Ο παρακάτω ιστότοπος επιθυμεί να αποκτήσει πρόσβαση σε πληροφορίες σχετικά με την τοποθεσία σας μέσω του Gears.",
+ "string-html-title": "Προειδοποίηση ασφαλείας Gears",
+ "string-description": "Το Gears είναι μια επέκταση προγράμματος περιήγησης ανοιχτής προέλευσης που επιτρέπει τις εφαρμογές ιστού να παρέχουν λειτουργίες εκτός σύνδεσης χρησιμοποιώντας τα ακόλουθα JavaScript API:",
+ "string-database-desc": "Αποθήκευση δεδομένων τοπικά σε μια σχεσιακή βάση δεδομένων με δυνατότητα πλήρους αναζήτησης",
+ "string-allow-accesskey": "A",
+ "string-cancel": "Άκυρο",
+ "string-never-allow-link": "Να μην επιτρέπεται ποτέ αυτός ο ιστότοπος",
+ "string-deny": "Απόρριψη",
+ "string-never-allow-link-wince": "Να μην επιτρέπεται ποτέ",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Αποθήκευση και διανομή πόρων εφαρμογών τοπικά",
+ "string-location-privacy-statement": "Διαβάστε την πολιτική για θέματα ιδιωτικού περιεχομένου του ιστότοπου για να δείτε πώς θα χρησιμοποιηθεί η τοποθεσία σας.",
+ "string-trust-site": "Εμπιστεύομαι αυτόν τον ιστότοπο. Να επιτρέπεται να χρησιμοποιεί το Gears."
+ },
+ "gu": {
+ "string-workerpool-desc": "એપ્લિકેશનની રિસ્પોંસિવનેસ વધારવા માટે અસિંક્રોનસ JavaScript ચલાવો",
+ "string-allow": "મંજૂરી આપો",
+ "string-query-data": "નીચેની વેબસાઇટ Gearsનો ઉપયોગ કરીને તમારા કમ્પ્યુટર પર માહિતી સંગ્રહિત કરવા માંગે છે.",
+ "string-deny-accesskey": "D",
+ "string-query-location": "નીચેની વેબસાઇટ Gearsનો ઉપયોગ કરીને તમારા સ્થાન વિશેની માહિતી એક્સેસ કરવા માંગે છે.",
+ "string-html-title": "Gears સુરક્ષા ચેતવણી",
+ "string-description": "Gears એક ઓપન સોર્સ બ્રાઉઝર એક્સ્ટેંશન છે જે નીચેની JavaScript APIsનો ઉપયોગ કરીને ઓફલાઇન ફંક્શનાલિટી આપવા વેબ એપ્લિકેશંસને એનેબલ કરે છે.",
+ "string-database-desc": "ડેટાને સ્થાનિક રીતે સંપૂર્ણ રીતે શોધી શકાય તેવા રિલેશનલ ડેટાબેસમાં સ્ટોર કરો",
+ "string-allow-accesskey": "A",
+ "string-cancel": "રદ કરો",
+ "string-never-allow-link": "આ સાઇટને ક્યારેય મંજૂરી આપશો નહીં",
+ "string-deny": "નકારો",
+ "string-never-allow-link-wince": "તેને ક્યારેય મંજૂર કરશો નહીં",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "એપ્લિકેશન સાધનોને સ્થાનિક રીતે સ્ટોર અને સર્વ કરો",
+ "string-location-privacy-statement": "તમારા સ્થળનો ઉપયોગ કેવી રીતે થશે તે જોવા સાઇટની ગોપનીયતા નીતિ વાંચો.",
+ "string-trust-site": "આ સાઇટ પર મને વિષ્વાસ છે. તેને Gearsનો ઉપયોગ કરવાની મંજુરી આપો."
+ },
+ "zh-TW": {
+ "string-workerpool-desc": "執行非同步 JavaScript 以提升應用程式的回應速度",
+ "string-allow": "允許",
+ "string-query-data": "下方的網站要使用 Gears 在您的電腦上儲存資訊。",
+ "string-deny-accesskey": "D",
+ "string-query-location": "下方的網站要使用 Gears 存取您位置的相關資訊。",
+ "string-html-title": "Gears 安全性警告",
+ "string-description": "Gears 是一種開放程式碼的瀏覽器擴充功能,它透過下列 JavaScript API 為網路應用程式提供離線功能:",
+ "string-database-desc": "在本機將資料儲存到完全可搜尋的關聯式資料庫中",
+ "string-allow-accesskey": "A",
+ "string-cancel": "取消",
+ "string-never-allow-link": "永不允許這個網站",
+ "string-deny": "拒絕",
+ "string-never-allow-link-wince": "永不允許",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "在本機儲存並提供應用程式資源",
+ "string-location-privacy-statement": "閱讀網站的隱私權政策以查看網站會如何使用您的位置。",
+ "string-trust-site": "我相信這個網站。 讓它使用 Gears。"
+ },
+ "vi": {
+ "string-workerpool-desc": "Chạy JavaScript không đồng bộ để tăng khả năng phản hồi của ứng dụng",
+ "string-allow": "Cho phép",
+ "string-query-data": "Trang web dưới đây muốn lưu trữ thông tin trên máy tính của bạn bằng cách sử dụng Gears.",
+ "string-deny-accesskey": "T",
+ "string-query-location": "Trang web dưới đây muốn truy cập vào thông tin về vị trí của bạn bằng cách sử dụng Gears.",
+ "string-html-title": "Cảnh báo Bảo mật của Gears",
+ "string-description": "Gears là một tiện ích bổ sung nguồn mở của trình duyệt cho phép ứng dụng web cung cấp tính năng ngoại tuyến bằng cách sử dụng các JavaScript API dưới đây:",
+ "string-database-desc": "Lưu dữ liệu cục bộ trong cơ sở dữ liệu quan hệ có thể tìm kiếm đầy đủ",
+ "string-allow-accesskey": "P",
+ "string-cancel": "Huỷ",
+ "string-never-allow-link": "Không bao giờ cho phép trang web này",
+ "string-deny": "Từ chối",
+ "string-never-allow-link-wince": "Không bao giờ cho phép Gears trên trang web này",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Lưu trữ và phục vụ các tài nguyên ứng dụng cục bộ",
+ "string-location-privacy-statement": "Đọc chính sách bảo mật của trang để xem vị trí của bạn sẽ được sử dụng thế nào.",
+ "string-trust-site": "Tôi tin cậy trang này. Cho phép trang web này sử dụng Gears."
+ },
+ "ca": {
+ "string-workerpool-desc": "Executar JavaScript asíncron per millorar la sensibilitat de les aplicacions",
+ "string-allow": "Permet",
+ "string-query-data": "El lloc web següent vol emmagatzemar informació al vostre ordinador mitjançant Gears.",
+ "string-deny-accesskey": "D",
+ "string-query-location": "El lloc web següent vol accedir a informació sobre la vostra ubicació mitjançant Gears.",
+ "string-html-title": "Advertència de seguretat de Gears",
+ "string-description": "Gear és una extensió de navegador de codi obert que permet que les aplicacions web proporcionin funcionalitat fora de línia mitjançant les següents API de JavaScript:",
+ "string-database-desc": "Desar dades localment en una base de dades relacional amb funció de cerca completa",
+ "string-allow-accesskey": "P",
+ "string-cancel": "Cancel·la",
+ "string-never-allow-link": "No permetis mai aquest lloc",
+ "string-deny": "Denega",
+ "string-never-allow-link-wince": "No permetre'l mai",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Desar i servir recursos d'aplicacions localment",
+ "string-location-privacy-statement": "Llegiu la política de privadesa del lloc per veure com es farà servir la vostra ubicació.",
+ "string-trust-site": "Confio en aquest lloc. Permet que utilitzi Gears."
+ },
+ "it": {
+ "string-workerpool-desc": "Eseguire codice JavaScript in modo asincrono per migliorare la velocità di risposta dell'applicazione",
+ "string-allow": "Consenti",
+ "string-query-data": "Il sito Web riportato di seguito vuole memorizzare le informazioni relative al computer che utilizza Google Gears.",
+ "string-deny-accesskey": "D",
+ "string-query-location": "Il sito Web riportato di seguito vuole accedere alle informazioni relative alla posizione di utilizzo di Google Gears.",
+ "string-html-title": "Avviso di protezione di Google Gears",
+ "string-description": "Google Gears è un'estensione browser open source che permette alle applicazioni Web di fornire funzionalità offline tramite le seguenti API JavaScript:",
+ "string-database-desc": "Memorizzare i dati localmente in un database relazionale dotato di ricerca integrata",
+ "string-allow-accesskey": "A",
+ "string-cancel": "Annulla",
+ "string-never-allow-link": "Non consentire mai l'accesso a questo sito",
+ "string-deny": "Nega",
+ "string-never-allow-link-wince": "Non consentirlo mai",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Memorizzare e fornire localmente le risorse dell'applicazione",
+ "string-location-privacy-statement": "Leggi le norme sulla privacy del sito per vedere come verrà utilizzata la tua posizione.",
+ "string-trust-site": "Considero questo sito attendibile. Consenti al sito di utilizzare Google Gears."
+ },
+ "kn": {
+ "string-workerpool-desc": "ಅಪ್ಲಿಕೇಶನ್ ಪ್ರತಿಕ್ರಿಯೆಯನ್ನು ಉತ್ತಮಪಡಿಸಲು ಏಸಿಂಕ್ರೋನಸ್ ಜಾವಾಸ್ಕ್ರಿಪ್ಟ್ ಚಲಿಸಿ",
+ "string-allow": "ಅನುಮತಿಸು",
+ "string-query-data": "ಈ ಕೆಳಗಿನ ವೆಬ್ ಸೈಟ್ Gears ಬಳಸಿ ನಿಮ್ಮ ಕಂಪ್ಯೂಟರಿನಲ್ಲಿ ಮಾಹಿತಿಯನ್ನು ಇರಿಸಲು ಬಯಸುತ್ತಿದೆ.",
+ "string-deny-accesskey": "D",
+ "string-query-location": "ಈ ಕೆಳಗಿನ ವೆಬ್ ಸೈಟ್ Gears ಬಳಸಿ ನಿಮ್ಮ ಸ್ಥಳದ ಕುರಿತು ಮಾಹಿತಿ ಪಡೆದುಕೊಳ್ಳಲು ಬಯಸುತ್ತಿದೆ.",
+ "string-html-title": "Gears ರಕ್ಷಣಾ ಎಚ್ಚರಿಕೆ",
+ "string-description": "Gears ಒಂದು ಓಪನ್ ಸೋರ್ಸ್ ಬ್ರೌಸರ್ ವಿಸ್ತರಣೆಯಾಗಿದ್ದು, ಅದು ವೆಬ್ ಅಪ್ಲಿಕೇಶನ್ ಗಳಿಗೆ ಈ ಕೆಳಗಿನ JavaScript API ಗಳನ್ನು ಬಳಸಿ ಆಫ್ಲೈನ್ ಫಂಕ್ಷನಾಲಿಟಿಯನ್ನು ನೀಡಲು ಸಾಧ್ಯವಾಗಿಸುತ್ತದೆ.",
+ "string-database-desc": "ಪೂರ್ಣವಾಗಿ ಹುಡುಕಬಹುದಾದ ಸಾಂಬಂಧಿಕ ಡೇಟಾಬೇಸ್‌ನಲ್ಲಿ ಸ್ಥಳೀಯವಾಗಿ ಡೇಟಾವನ್ನು ಸಂಗ್ರಹಿಸಿ",
+ "string-allow-accesskey": "A",
+ "string-cancel": "ರದ್ದುಮಾಡು",
+ "string-never-allow-link": "ಈ ಸೈಟನ್ನು ಎಂದಿಗೂ ಅನುಮತಿಸಬೇಡಿ",
+ "string-deny": "ನಿರಾಕರಿಸು",
+ "string-never-allow-link-wince": "ಅದನ್ನು ಎಂದಿಗೂ ಅನುಮತಿಸಬೇಡಿ",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "ಸ್ಥಳೀಯವಾಗಿ ಅಪ್ಲಿಕೇಶನ್ ಮೂಲಗಳನ್ನು ಸಂಗ್ರಹಿಸಿ ಮತ್ತು ಒದಗಿಸಿ",
+ "string-location-privacy-statement": "ನಿಮ್ಮ ಸ್ಥಳವನ್ನು ಹೇಗೆ ಉಪಯೋಗಿಸಲಾಗುತ್ತದೆ ಎಂದು ತಿಳಿಯಲು ಈ ಸೈಟ್ ನ ಗೌಪ್ಯತಾ ನೀತಿಯನ್ನು ಓದಿ.",
+ "string-trust-site": "ಈ ಸೈಟನ್ನು ನಾನು ನಂಬುತ್ತೇನೆ. ಇದಕ್ಕೆ Gears ಉಪಯೋಗಿಸಲು ಅನುಮತಿಸು."
+ },
+ "ar": {
+ "string-workerpool-desc": "تشغيل جافا سكريبت غير متزامنة لتحسين سرعة استجابة التطبيقات",
+ "string-allow": "سماح",
+ "string-query-data": "يرغب موقع الويب أدناه في تخزين معلومات على موقعك باستخدام Gears.",
+ "string-deny-accesskey": "ف",
+ "string-query-location": "يرغب موقع الويب أدناه في الوصول إلى معلومات حول موقعك باستخدام Gears.",
+ "string-html-title": "تحذير أمان خاص بـ Gears",
+ "string-description": "Gears هو ملحق متصفح مفتوح المصدر يتيح لتطبيقات الويب إمكانية القيام بوظائفها في حالة عدم الاتصال باستخدام واجهات برمجة التطبيقات (API) التالية المعدة باستخدام جافا سكريبت:",
+ "string-database-desc": "تخزين البيانات محليًا في قاعدة بيانات ارتباطية قابلة للبحث بشكل كامل",
+ "string-allow-accesskey": "س",
+ "string-cancel": "إلغاء",
+ "string-never-allow-link": "عدم السماح لهذا الموقع مطلقًا",
+ "string-deny": "رفض",
+ "string-never-allow-link-wince": "عدم السماح بـ Gears مطلقًا",
+ "string-trust-site-accesskey": "موثوق",
+ "string-localserver-desc": "تخزين موارد التطبيقات وإتاحتها محليًا",
+ "string-location-privacy-statement": "قم بقراءة سياسة خصوصية الموقع لمعرفة كيف سيتم استخدام موقعك.",
+ "string-trust-site": "أثق في هذا الموقع. وأرجو السماح له باستخدام Gears."
+ },
+ "ml": {
+ "string-workerpool-desc": "അപ്ലിക്കേഷന്‍ പ്രതികരണം അഭിവൃദ്ധിപ്പെടുത്താന്‍ ഏകകാലികമല്ലാത്ത ജാവാസ്ക്രിപ്റ്റ് പ്രവര്‍ത്തിപ്പിക്കുക",
+ "string-allow": "അനുവദിക്കൂ",
+ "string-query-data": "താഴെക്കാണുന്ന വെബ്സൈറ്റ് നിങ്ങളുടെ കമ്പ്യൂട്ടറില് വിവരങ്ങള് Gears ഉപയോഗിച്ച് ശേഖരിക്കാനാഗ്രഹിക്കുന്നു.",
+ "string-deny-accesskey": "D",
+ "string-query-location": "താഴെക്കാണുന്ന വെബ്സൈറ്റ് നിങ്ങളുടെ മേഖലയെക്കുറിച്ചുള്ള വിവരങ്ങള് Gears ഉപയോഗിച്ച് ആക്സസ്സ് ചെയ്യാനാഗ്രഹിക്കുന്നു.",
+ "string-html-title": "Gears സുരക്ഷാ മുന്നറിയിപ്പ്",
+ "string-description": "Gears ഒരു തുറന്ന ഉറവിട ബ്രൌസര്‍ എക്സ്റ്റന്ഷനാണ്, അത് JavaScript APIs ഉപയോഗിച്ച് വെബ് അപ്ളിക്കേഷന് ഓഫ്ലൈന് ക്രിയാത്മകത നല്കുന്നു:",
+ "string-database-desc": "പൂര്‍ണമായും തിരയാവുന്ന ആനുപാതികമായ ഡാറ്റാബേസില്‍ ഡാറ്റ സംഭരിക്കുക",
+ "string-allow-accesskey": "A",
+ "string-cancel": "റദ്ദാക്കൂ",
+ "string-never-allow-link": "ഈ സൈറ്റിനെ ഒരിക്കലും അനുവദിക്കരുത്",
+ "string-deny": "നിരസിക്കൂ",
+ "string-never-allow-link-wince": "ഇതൊരിക്കലും അനുവദിക്കരുത്",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "അപ്ലിക്കേഷന്‍ വിഭവങ്ങള്‍ പ്രാദേശികമായി സംഭരിച്ച് പ്രയോജനപ്പെടുത്തൂ",
+ "string-location-privacy-statement": "നിങ്ങളുടെ ലൊക്കേഷന് എങ്ങനെയാണ് ഉപയോഗിച്ചിരിക്കുന്നതെന്ന് കാണാന് സൈറ്റിന്റെ സ്വകാര്യതാ നയം വായിക്കുക.",
+ "string-trust-site": "ഞാനീ സൈറ്റിനെ വിശ്വസിക്കുന്നു. ഇതിനെ Gears ഉപയോഗിക്കാന് അനുവദിക്കുക."
+ },
+ "cs": {
+ "string-workerpool-desc": "Spouštět asynchronní JavaScript ke zlepšení odezvy aplikace",
+ "string-allow": "Povolit",
+ "string-query-data": "Následující webová stránka chce pomocí aplikace Gears uložit informace do vašeho počítače.",
+ "string-deny-accesskey": "Z",
+ "string-query-location": "Následující webová stránka se pomocí aplikace Gears pokouší získat informace o vaší lokalitě.",
+ "string-html-title": "Výstraha zabezpečení aplikace Gears",
+ "string-description": "Gears je rozšíření typu open source pro webové prohlížeče, které umožňuje webovým aplikacím pracovat v režimu offline pomocí následujících rozhraní API prostředí JavaScript:",
+ "string-database-desc": "Ukládat data lokálně v relační databázi, ve které lze vyhledávat",
+ "string-allow-accesskey": "P",
+ "string-cancel": "Zrušit",
+ "string-never-allow-link": "Nikdy nepovolit tyto stránky",
+ "string-deny": "Zamítnout",
+ "string-never-allow-link-wince": "Nikdy nepovolit tuto aplikaci",
+ "string-trust-site-accesskey": "d",
+ "string-localserver-desc": "Ukládat a nabízet zdroje aplikací lokálně",
+ "string-location-privacy-statement": "Přečtěte si zásady ochrany osobních údajů tohoto webu, kde zjistíte, jakým způsobem budou použity informace o vaší lokalitě.",
+ "string-trust-site": "Důvěřuji tomuto webu. Umožnit používání aplikace Gears."
+ },
+ "et": {
+ "string-workerpool-desc": "Käivita asünkroonne JavaScript, et rakenduse töö kiirust parandada",
+ "string-allow": "Luba",
+ "string-query-data": "Alljärgnev veebisait soovib Gearsi kasutades salvestada teavet teie arvutisse.",
+ "string-deny-accesskey": "K",
+ "string-query-location": "Alljärgnev veebisait soovib Gearsi kasutades juurdepääsu teabele teie asukoha kohta.",
+ "string-html-title": "Gearsi turvahoiatus",
+ "string-description": "Gears on avatud lähtekoodiga brauserilaiendus, mis võimaldab veebirakendustel võrguühenduseta töötada, kasutades järgmisi JavaScripti rakendusliideseid:",
+ "string-database-desc": "Salvestage andmed lokaalselt otsingu täisfunktsiooniga relatsioonandmebaasi",
+ "string-allow-accesskey": "L",
+ "string-cancel": "Tühista",
+ "string-never-allow-link": "Ära luba seda saiti kunagi",
+ "string-deny": "Keeldu",
+ "string-never-allow-link-wince": "Ära luba seda kunagi",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Salvestage ja kasutage rakenduse vahendeid lokaalselt",
+ "string-location-privacy-statement": "Kui soovite teada, kuidas teie asukohateavet kasutatakse, lugege saidi privaatsuspõhimõtteid.",
+ "string-trust-site": "Usaldan seda saiti. Luba sellel Gearsi kasutada."
+ },
+ "id": {
+ "string-workerpool-desc": "Menjalankan JavaScript asinkron untuk meningkatkan respons aplikasi",
+ "string-allow": "OK",
+ "string-query-data": "Website berikut ingin menyimpan informasi di komputer Anda dengan menggunakan Gears.",
+ "string-deny-accesskey": "D",
+ "string-query-location": "Website berikut ingin mengakses informasi tentang lokasi Anda dengan menggunakan Gears.",
+ "string-html-title": "Peringatan Keamanan Gears",
+ "string-description": "Gears adalah ekstensi browser open source yang memungkinkan aplikasi web untuk menyediakan fungsionalitas offline dengan menggunakan JavaScript API:",
+ "string-database-desc": "Menyimpan data secara lokal pada basis data relasional yang dapat cari",
+ "string-allow-accesskey": "A",
+ "string-cancel": "Batalkan",
+ "string-never-allow-link": "Jangan pernah izinkan situs ini",
+ "string-deny": "Tolak",
+ "string-never-allow-link-wince": "Jangan pernah mengizinkannya",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Menyimpan dan menggunakan sumber daya aplikasi secara lokal",
+ "string-location-privacy-statement": "Baca kebijakan privasi situs untuk melihat bagaimana lokasi Anda akan digunakan.",
+ "string-trust-site": "Saya mempercayai situs ini. Izinkan untuk menggunakan Gears."
+ },
+ "es": {
+ "string-workerpool-desc": "Ejecutar JavaScript asíncrono para mejorar la capacidad de respuesta de la aplicación",
+ "string-allow": "Permitir",
+ "string-query-data": "El siguiente sitio Web desea almacenar información en tu equipo utilizando Gears.",
+ "string-deny-accesskey": "D",
+ "string-query-location": "El siguiente sitio web desea acceder a información sobre tu ubicación con Gears.",
+ "string-html-title": "Advertencia de seguridad Gears",
+ "string-description": "Gears es una extensión del navegador de software libre que permite a las aplicaciones web funcionar sin conexión a través de las siguientes interfaces de programación de aplicaciones (API) de Javascript:",
+ "string-database-desc": "Almacenar datos localmente en una base de datos relacional que admite búsquedas completas",
+ "string-allow-accesskey": "P",
+ "string-cancel": "Cancelar",
+ "string-never-allow-link": "No permitir nunca este sitio",
+ "string-deny": "Denegar",
+ "string-never-allow-link-wince": "No permitirlo nunca",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Almacenar y proporcionar recursos de aplicaciones de forma local.",
+ "string-location-privacy-statement": "Lee la política de privacidad de este sitio para ver cómo se utilizará tu ubicación.",
+ "string-trust-site": "Confío en este sitio. Permitir utilizar Gears."
+ },
+ "en-GB": {
+ "string-workerpool-desc": "Run asynchronous JavaScript to improve application responsiveness",
+ "string-allow": "Allow",
+ "string-query-data": "The website below wants to store information on your computer using Gears.",
+ "string-deny-accesskey": "D",
+ "string-query-location": "The website below wants to access information about your location using Gears.",
+ "string-html-title": "Gears Security Warning",
+ "string-description": "Gears is an open source browser extension that enables web applications to provide offline functionality using the following JavaScript APIs:",
+ "string-database-desc": "Store data locally in a fully-searchable relational database",
+ "string-allow-accesskey": "A",
+ "string-cancel": "Cancel",
+ "string-never-allow-link": "Never allow this site",
+ "string-deny": "Deny",
+ "string-never-allow-link-wince": "Never allow",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Store and serve application resources locally",
+ "string-location-privacy-statement": "Read the site's privacy policy to see how your location will be used.",
+ "string-trust-site": "I trust this site. Allow it to use Gears."
+ },
+ "ru": {
+ "string-workerpool-desc": "Запустить асинхронный JavaScript, чтобы ускорить работу приложения",
+ "string-allow": "Разрешить",
+ "string-query-data": "Указанный ниже веб-сайт запрашивает разрешение на хранение информации на компьютере с помощью Gears.",
+ "string-deny-accesskey": "D",
+ "string-query-location": "Указанный ниже веб-сайт запрашивает разрешение на получение информации о местонахождении с помощью Gears.",
+ "string-html-title": "Предупреждение безопасности Gears",
+ "string-description": "Gears – это расширение для браузера с открытым исходным кодом, позволяющее веб-приложениям работать в автономном режиме с помощью следующих API JavaScript:",
+ "string-database-desc": "Сохраняйте данные на компьютере пользователя в виде реляционной БД с возможностью поиска по ним",
+ "string-allow-accesskey": "A",
+ "string-cancel": "Отмена",
+ "string-never-allow-link": "Никогда не разрешать этому сайту",
+ "string-deny": "Запретить",
+ "string-never-allow-link-wince": "Никогда не разрешать",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Сохраняйте и размещайте ресурсы приложений на локальном компьютере",
+ "string-location-privacy-statement": "Для того чтобы узнать о том, как будет использована информация о местонахождении, ознакомьтесь с политикой конфиденциальности сайта.",
+ "string-trust-site": "Я доверяю этому сайту. Разрешить использование Gears."
+ },
+ "nl": {
+ "string-workerpool-desc": "Asynchroon JavaScript uitvoeren om ontvankelijkheid van toepassing te verbeteren",
+ "string-allow": "Toestaan",
+ "string-query-data": "De website hieronder probeert via Gears informatie op uw computer op te slaan.",
+ "string-deny-accesskey": "D",
+ "string-query-location": "De website hieronder probeert via Gears informatie te krijgen over uw locatie.",
+ "string-html-title": "Beveiligingswaarschuwing voor Gears",
+ "string-description": "Gears is een browseruitbreiding met open broncode waarmee webtoepassingen offline functionaliteit kunnen bieden met de volgende JavaScript-API's:",
+ "string-database-desc": "Gegevens lokaal opslaan in een volledig doorzoekbare relationele database",
+ "string-allow-accesskey": "A",
+ "string-cancel": "Annuleren",
+ "string-never-allow-link": "Deze site nooit toestaan",
+ "string-deny": "Weigeren",
+ "string-never-allow-link-wince": "Nooit toestaan",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Toepassingsbronnen lokaal opslaan en aanbieden",
+ "string-location-privacy-statement": "Lees het privacybeleid op de site voor informatie over het gebruik van uw locatie.",
+ "string-trust-site": "Ik vertrouw deze site. Sta deze site toe Gears te gebruiken."
+ },
+ "no": {
+ "string-workerpool-desc": "Kjør asynkront JavaScript for å forbedre programmets respons",
+ "string-allow": "Tillat",
+ "string-query-data": "Nettstedet nedenfor ønsker å bruke Gears til å lagre informasjon om datamaskinen.",
+ "string-deny-accesskey": "V",
+ "string-query-location": "Nettstedet nedenfor ønsker å bruke Gears for å få tilgang på informasjon om hvor du befinner deg.",
+ "string-html-title": "Sikkerhetsadvarsel for Gears",
+ "string-description": "Gears er en nettleserutvidelse med åpen kildekode som lar nettprogrammer fungere i frakoblet modus ved hjelp av følgende JavaScript API-er:",
+ "string-database-desc": "Lagre data lokalt i en fullt søkbar relasjonsdatabase",
+ "string-allow-accesskey": "T",
+ "string-cancel": "Avbryt",
+ "string-never-allow-link": "Avvis alltid dette nettstedet",
+ "string-deny": "Avvis",
+ "string-never-allow-link-wince": "Avvis alltid",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Lagre og bruk programressurser lokalt",
+ "string-location-privacy-statement": "Les retningslinjene for personvern for nettstedet for å se hvordan området brukes.",
+ "string-trust-site": "Jeg stoler på dette nettstedet. Tillat at det bruker Gears."
+ },
+ "tr": {
+ "string-workerpool-desc": "Uygulamanın daha iyi yanıt vermesi için asenkron olarak Javascript yürütür",
+ "string-allow": "İzin ver",
+ "string-query-data": "Aşağıdaki web sitesi Gears'ı kullanarak bilgisayarınızda bilgi depolamak istiyor.",
+ "string-deny-accesskey": "R",
+ "string-query-location": "Aşağıdaki web sitesi Gears'ı kullanarak konumunuzla ilgili bilgilere erişmek istiyor.",
+ "string-html-title": "Gears Güvenlik Uyarısı",
+ "string-description": "Gears, web uygulamalarının aşağıdaki JavaScript API'larını kullanarak çevrimdışı çalışmasına olanak tanıyan açık kaynaklı bir tarayıcı uzantısıdır:",
+ "string-database-desc": "Verileri tamamen aratılabilir ilişkisel veritabanında yerel olarak sakla",
+ "string-allow-accesskey": "V",
+ "string-cancel": "İptal",
+ "string-never-allow-link": "Bu siteye asla izin verme",
+ "string-deny": "Reddet",
+ "string-never-allow-link-wince": "Asla izin verme",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Uygulama kaynaklarını yerel olarak saklar ve sunar",
+ "string-location-privacy-statement": "Konumunuzun nasıl kullanılacağını öğrenmek için sitenin gizlilik politikasını okuyun.",
+ "string-trust-site": "Bu siteye güveniyorum. Gears'ı kullanmasına izin ver."
+ },
+ "lv": {
+ "string-workerpool-desc": "Palaidiet asinhrono JavaScript, lai uzlabotu lietojumprogrammas atbildēšanas iespējas",
+ "string-allow": "Atļaut",
+ "string-query-data": "Tālāk minētā vietne vēlas saglabāt informāciju par jūsu datoru, lietojot Gears.",
+ "string-deny-accesskey": "D",
+ "string-query-location": "Tālāk minētā vietne vēlas piekļūt informācijai par jūsu atrašanās vietu, lietojot Gears.",
+ "string-html-title": "Gears drošības brīdinājums",
+ "string-description": "Gears ir atklāta pirmkoda pārlūkprogramma, ar kuru tīmekļa lietojumprogrammas nodrošina funkcionalitāti bezsaistē, izmantojot šādas JavaScript API:",
+ "string-database-desc": "Datu glabāšana lokāli pilnībā pārmeklējamā relāciju datu bāzē",
+ "string-allow-accesskey": "A",
+ "string-cancel": "Atcelt",
+ "string-never-allow-link": "Nekad neļaut piekļūt šai vietnei",
+ "string-deny": "Aizliegt",
+ "string-never-allow-link-wince": "Nekad to neatļaut",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Lietojumprogrammu resursu saglabāšana un apkalpošana lokāli",
+ "string-location-privacy-statement": "Izlasiet vietnes konfidencialitātes politiku, lai redzētu, kā tiks izmantota jūsu atrašanās vieta.",
+ "string-trust-site": "Es uzticos šai vietnei. Atļaujiet tai lietot Gears."
+ },
+ "lt": {
+ "string-workerpool-desc": "Padidinkite programos jautrumą paleisdami asinchroninę „Javascript“",
+ "string-allow": "Leisti",
+ "string-query-data": "Žemiau nurodyta interneto svetainė bando išsaugoti informaciją apie jūsų vietovę naudojant „Gears“.",
+ "string-deny-accesskey": "D",
+ "string-query-location": "Žemiau nurodyta interneto svetainė bando prieiti prie informacijos apie jūsų vietovę naudojant „Gears“.",
+ "string-html-title": "„Gears“ įspėjimas apie saugą",
+ "string-description": "„Gears“ – tai atvirojo kodo naršyklės plėtinys, leidžiantis žiniatinklio programoms naudoti atjungties funkcines galimybes su šiais „JavaScript“ API:",
+ "string-database-desc": "Išsaugokite duomenis vietoje, sąryšinėje duomenų bazėje su veikiančia paieškos funkcija",
+ "string-allow-accesskey": "A",
+ "string-cancel": "Atšaukti",
+ "string-never-allow-link": "Niekada neleisti šios svetainės",
+ "string-deny": "Atmesti",
+ "string-never-allow-link-wince": "Niekada neleisti",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Saugokite ir prižiūrėkite programos išteklius vietoje",
+ "string-location-privacy-statement": "Perskaitykite svetainės privatumo politiką, kad sužinotumėte, kaip bus naudojama jūsų vietovė.",
+ "string-trust-site": "Pasitikiu šia svetaine. Leisti jai naudoti „Gears“."
+ },
+ "th": {
+ "string-workerpool-desc": "เรียกใช้ JavaScript อะซิงโครนัสเพื่อปรับปรุงการตอบสนองของแอปพลิเคชัน",
+ "string-allow": "อนุญาต",
+ "string-query-data": "เว็บไซต์ต่อไปนี้ต้องการเก็บข้อมูลในคอมพิวเตอร์ของคุณโดยใช้ Gears",
+ "string-deny-accesskey": "ป",
+ "string-query-location": "เว็บไซต์ต่อไปนี้ต้องการเข้าถึงข้อมูลเกี่ยวกับตำแหน่งของคุณโดยใช้ Gears",
+ "string-html-title": "คำเตือนด้านความปลอดภัยของ Gears",
+ "string-description": "Gears เป็นส่วนขยายเบราว์เซอร์แบบโอเพ่นซอร์สที่ช่วยให้แอปพลิเคชันทางเว็บมีการทำงานแบบออฟไลน์โดยใช้ JavaScript API ต่อไปนี้:",
+ "string-database-desc": "จัดเก็บข้อมูลในตัวเครื่องในฐานข้อมูลที่สัมพันธ์กันและสามารถค้นหาได้อย่างสมบูรณ์",
+ "string-allow-accesskey": "อ",
+ "string-cancel": "ยกเลิก",
+ "string-never-allow-link": "ไม่อนุญาตไซต์นี้",
+ "string-deny": "ปฏิเสธ",
+ "string-never-allow-link-wince": "ไม่อนุญาต",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "จัดเก็บและรองรับทรัพยากรของแอปพลิเคชันในเครื่อง",
+ "string-location-privacy-statement": "อ่านนโยบายส่วนบุคคลของไซต์เพื่อดูว่าตำแหน่งของคุณจะถูกนำมาใช้อย่างไร",
+ "string-trust-site": "ฉันเชื่อถือไซต์นี้ อนุญาตให้ใช้ Gears"
+ },
+ "ro": {
+ "string-workerpool-desc": "Rulaţi JavaScript asincron, pentru a îmbunătăţi viteza de răspuns a aplicaţiei",
+ "string-allow": "Permiteţi",
+ "string-query-data": "Site-ul Web de mai jos doreşte să salveze informaţii pe computerul dvs. utilizând Gears.",
+ "string-deny-accesskey": "R",
+ "string-query-location": "Site-ul Web de mai jos doreşte să acceseze informaţii despre locaţia dvs. utilizând Gears.",
+ "string-html-title": "Avertizare de securitate Gears",
+ "string-description": "Gears este o extensie de browser cu sursă liberă care permite aplicaţiilor Web să ofere funcţionalitate offline, utilizând următoarele API-uri JavaScript:",
+ "string-database-desc": "Stocaţi date local într-o bază de date relaţională în care se pot efectua căutări complete",
+ "string-allow-accesskey": "P",
+ "string-cancel": "Anulaţi",
+ "string-never-allow-link": "Nu permiteţi niciodată acest site",
+ "string-deny": "Respingeţi",
+ "string-never-allow-link-wince": "Nu permiteţi niciodată",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Stocaţi şi livraţi local resurse ale aplicaţiei",
+ "string-location-privacy-statement": "Citiţi politica de confidenţialitate a site-ului pentru a afla cum va fi utilizată locaţia dvs.",
+ "string-trust-site": "Am încredere în acest site. Îi permit să utilizeze Gears."
+ },
+ "is": {
+ "string-workerpool-desc": "Keyra ósamstillt JavaScript til að bæta svörun forrits",
+ "string-allow": "Leyfa",
+ "string-query-data": "Vefsvæðið hér fyrir neðan vill fá aðgang að upplýsingum um tölvuna þína með því að nota Gears.",
+ "string-deny-accesskey": "F",
+ "string-query-location": "Vefsvæðið hér fyrir neðan vill fá aðgang að upplýsingum um staðsetninguna þína með því að nota Gears.",
+ "string-html-title": "Öryggisviðvörun frá Gears",
+ "string-description": "Gears er vafraviðbót með opnum kóða sem gerir vefforritum kleift að vinna án nettengingar með eftirfarandi JavaScript forritaskilum:",
+ "string-database-desc": "Geyma gögn staðbundið í töflugagnagrunni sem hægt er að leita í",
+ "string-allow-accesskey": "L",
+ "string-cancel": "Hætta við",
+ "string-never-allow-link": "Aldrei leyfa þetta svæði",
+ "string-deny": "Hafna",
+ "string-never-allow-link-wince": "Aldrei leyfa það",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Geyma og vinna staðbundið með tilföng fyrir forrit",
+ "string-location-privacy-statement": "Lestu persónuverndarstefnu svæðisins til þess að sjá hvernig staðsetningin þín verður notuð.",
+ "string-trust-site": "Ég treysti þessu vefsvæði. Það má nota Gears."
+ },
+ "fil": {
+ "string-workerpool-desc": "Magpatakbo ng hindi magkakasabay na JavaScript upang mapagbuti ang kakayahang tumugon ng application",
+ "string-allow": "Payagan",
+ "string-query-data": "Nais lagyan ng impormasyon ang website sa ibaba sa iyong computer gamit ang Mga Gear.",
+ "string-deny-accesskey": "T",
+ "string-query-location": "Nais i-access ng website sa ibaba ang impormasyon tungkol sa iyong lokasyon gamit ang mga Gear.",
+ "string-html-title": "Babala sa Seguridad ng Mga Gear",
+ "string-description": "Ang mga Gear ay isang open source browser extension na pinagana ng mga web application ng offline na pagpapaandar gamit ang sumusunod na Mga JavaScript API:",
+ "string-database-desc": "I-imbak ang data nang lokal sa isang ganap na mahahanapan na relational database",
+ "string-allow-accesskey": "P",
+ "string-cancel": "Ikansela",
+ "string-never-allow-link": "Huwag kailanman payagan ang site na ito",
+ "string-deny": "Tanggihan",
+ "string-never-allow-link-wince": "Huwag kailanman ito papayagan",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Mag-imbak at maghatid nang lokal ng mga mapagkukunan ng application",
+ "string-location-privacy-statement": "Basahin ang patakaran sa privacy ng site upang makita kung paano magagamit ang iyong lokasyon.",
+ "string-trust-site": "Pinagkakatiwalaan ko ang site na ito. Pinahihintulutan na gamitin ang mga Gear na ito."
+ },
+ "ta": {
+ "string-workerpool-desc": "பயன்பாட்டின் செயல்திறனை மேம்படுத்த ஒத்திசையாத JavaScript ஐ இயக்கவும்.",
+ "string-allow": "அனுமதி",
+ "string-query-data": "கீழே உள்ள வலைத்தளம் Gears ஐப் பயன்படுத்தி உங்கள் கணினியில் தகவலைச் சேமிக்க நினைக்கிறது.",
+ "string-deny-accesskey": "D",
+ "string-query-location": "கீழே உள்ள வலைத்தளம் Gears ஐப் பயன்படுத்தி உங்கள் இருப்பிடம் பற்றிய தகவலை அணுக நினைக்கிறது.",
+ "string-html-title": "Gears பாதுகாப்பு எச்சரிக்கை",
+ "string-description": "Gears ஒரு ஓப்பன் சோர்ஸ் உலாவி விரிவாக்கல், பின்வரும் JavaScript APIகளைப் பயன்படுத்தி ஆஃப்லைனில் வலை பயன்பாடுகளை அணுக உதவுகிறது:",
+ "string-database-desc": "தரவுகளை உங்கள் கணினியிலேயே, முழுமையாகத் தேடப்படக்கூடிய, ரிலேஷனல் டேடாபேஸில் சேமிக்கவும்",
+ "string-allow-accesskey": "A",
+ "string-cancel": "ரத்துசெய்",
+ "string-never-allow-link": "இந்த தளத்தை எப்போதும் அனுமதிக்காதே",
+ "string-deny": "மறு",
+ "string-never-allow-link-wince": "எப்போதும் அனுமதிக்காதே",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "பயன்பாட்டின் ஆதாரங்களை உங்கள் கணினியில் சேமிக்கவும் மற்றும் வழங்கவும்",
+ "string-location-privacy-statement": "உங்கள் இருப்பிடத்தை எங்கு உபயோகப்படுத்தப்படும் என்று காண தளங்களின் தனியுரிமை கொள்கையைப் படிக்கவும்.",
+ "string-trust-site": "நான் இந்த தளத்தை நம்புகிறேன். Gears ஐப் பயன்படுத்த அனுமதிக்கவும்."
+ },
+ "fr": {
+ "string-workerpool-desc": "Exécuter JavaScript asynchrone pour améliorer le temps de réponse de l'application",
+ "string-allow": "Autoriser",
+ "string-query-data": "Le site Web ci-dessous souhaite stocker des informations sur votre ordinateur à l'aide de Google Gears.",
+ "string-deny-accesskey": "R",
+ "string-query-location": "Le site Web ci-dessous souhaite accéder aux informations sur votre emplacement à l'aide de Google Gears.",
+ "string-html-title": "Avertissement de sécurité de Google Gears",
+ "string-description": "Google Gears est une extension open source de navigateur qui permet aux applications Web de fournir des services hors ligne, grâce aux API JavaScript suivantes :",
+ "string-database-desc": "Enregistrer des données localement dans une base de données relationnelle avec une fonction de recherche",
+ "string-allow-accesskey": "A",
+ "string-cancel": "Annuler",
+ "string-never-allow-link": "Ne jamais autoriser ce site",
+ "string-deny": "Refuser",
+ "string-never-allow-link-wince": "Ne jamais l'autoriser",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Stocker et distribuer localement des ressources de l'application",
+ "string-location-privacy-statement": "Lisez la politique de confidentialité du site pour savoir comment votre emplacement va être utilisé.",
+ "string-trust-site": "J'ai confiance en ce site. L'autoriser à utiliser Google Gears."
+ },
+ "bg": {
+ "string-workerpool-desc": "Изпълнявайте асинхронен JavaScript, за да подобрите скоростта на работа на приложенията",
+ "string-allow": "Разрешаване",
+ "string-query-data": "Уебсайтът по-долу иска да съхрани информация на компютъра ви чрез Gears.",
+ "string-deny-accesskey": "D",
+ "string-query-location": "Уебсайтът по-долу иска да получи достъп до информацията за вашето местоположение чрез Gears.",
+ "string-html-title": "Предупреждение за защита на Gears",
+ "string-description": "Gears е разширение за браузъри с отворен код, което позволява на уеб приложенията да функционират в офлайн режим чрез следните приложни програмни интерфейси на JavaScript:",
+ "string-database-desc": "Съхранявайте информация локално в база от сродни данни с възможност за пълно търсене в тях",
+ "string-allow-accesskey": "A",
+ "string-cancel": "Отказ",
+ "string-never-allow-link": "Не давам разрешението си за този сайт",
+ "string-deny": "Забраняване",
+ "string-never-allow-link-wince": "Не се разрешава изобщо",
+ "string-trust-site-accesskey": "й",
+ "string-localserver-desc": "Съхранявайте и използайте локално ресурсите на приложенията",
+ "string-location-privacy-statement": "Прочетете декларацията за поверителност на сайта, за да разберете как ще се използва информацията за местоположението ви.",
+ "string-trust-site": "Имам доверие на този сайт. Нека да може да използва Gears."
+ },
+ "uk": {
+ "string-workerpool-desc": "Запустіть асинхронний сценарій Javascript, щоб підвищити швидкість реагування програми",
+ "string-allow": "Дозволити",
+ "string-query-data": "Нижченаведений веб-сайт намагається зберегти інформацію на Вашому комп’ютері за допомогою Gears.",
+ "string-deny-accesskey": "D",
+ "string-query-location": "Нижченаведений веб-сайт намагається отримати доступ до інформації про Ваше розташування за допомогою Gears.",
+ "string-html-title": "Попередження системи безпеки Gears",
+ "string-description": "Gears – це розширення веб-браузера з відкритим кодом, яке дозволяє веб-програмам працювати в автономному режимі за допомогою таких інтерфейсів API JavaScript:",
+ "string-database-desc": "Дані зберігаються локально у відповідній базі даних із повною підтримкою функцій пошуку",
+ "string-allow-accesskey": "A",
+ "string-cancel": "Скасувати",
+ "string-never-allow-link": "Ніколи не дозволяти цьому веб-сайту",
+ "string-deny": "Відхилити",
+ "string-never-allow-link-wince": "Ніколи не дозволяти",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Локальне збереження та обслуговування програмних ресурсів",
+ "string-location-privacy-statement": "Ознайомтеся з політикою конфіденційності веб-сайту, щоб дізнатися, як буде використовуватися Ваше розташування.",
+ "string-trust-site": "Я довіряю цьому сайту. Дозволити йому використовувати Gears."
+ },
+ "hr": {
+ "string-workerpool-desc": "Pokrenite asinkroni JavaScript kako biste poboljšali odziv aplikacije",
+ "string-allow": "Dopusti",
+ "string-query-data": "Dolje navedena web-lokacija želi pomoću Gearsa pohraniti informacije na Vaše računalo.",
+ "string-deny-accesskey": "O",
+ "string-query-location": "Dolje navedena web-lokacija želi pristupiti informacijama o vašoj lokaciji pomoću Gearsa.",
+ "string-html-title": "Gearsovo upozorenje o sigurnosti",
+ "string-description": "Gears je pregledničko proširenje otvorenog izvornog koda koje web-aplikacijama omogućuje izvanmrežno funkcioniranje pomoću sljedećih API-ja za JavaScript:",
+ "string-database-desc": "Podatke spremi lokalno u potpuno pretraživoj relacijskoj bazi podataka",
+ "string-allow-accesskey": "A",
+ "string-cancel": "Odustani",
+ "string-never-allow-link": "Nikad ne dopuštaj ovu web lokaciju",
+ "string-deny": "Odbij",
+ "string-never-allow-link-wince": "Nikad ne dopuštaj",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Izvore aplikacije pohrani i poslužuj lokalno",
+ "string-location-privacy-statement": "Pročitajte pravila o zaštiti privatnosti te web-lokacije da biste saznali kako će se koristiti Vaša lokacija.",
+ "string-trust-site": "Ovu web-lokaciju smatram pouzdanom. Dopusti joj da koristi Gears."
+ },
+ "bn": {
+ "string-workerpool-desc": "প্রয়োগ প্রতিক্রিয়া উন্নত করতে অ্যাসিঙ্ক্রোনাস জাভাস্ক্রিপ্ট রান করুন",
+ "string-allow": "মঞ্জুরি দিন",
+ "string-query-data": "নিচের ওয়েবসাইটটি আপনার কম্পিউটারে তথ্য সঞ্চয় করতে চায় Gears ব্যহার করে ׀",
+ "string-deny-accesskey": "D",
+ "string-query-location": "নিচের ওয়েবসাইটটি আপনার Gears ব্যবহার করার অবস্থানের সম্পর্কে তথ্য অ্যাকসেস করতে চায় ׀",
+ "string-html-title": "Gears নিরাপত্তা সতর্কবাণী",
+ "string-description": "Gears একটি মুক্ত স্রোত ব্রাউসার যা ওয়েব অ্যাপ্লিকেশান প্রদান করে JavaScript APIs ব্যবহার করে অফলাইন ফাংশানালিটি প্রদান করে :",
+ "string-database-desc": "একটি পূর্ণ-সন্ধানযোগ্য সম্পর্কযুক্ত ডেটাবেসে স্থানীয়ভাবে ডেটা সঞ্চয় করুন",
+ "string-allow-accesskey": "A",
+ "string-cancel": "বাতিল",
+ "string-never-allow-link": "কখনই এই সাইটটির মঞ্জুরি দেবেন না",
+ "string-deny": "অস্বীকার",
+ "string-never-allow-link-wince": "এটিকে কখনই অনুমতি দেয় না",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "প্রয়োগ সম্পদ স্থানীয়ভানে সঞ্চয় এবং পরিবেশন করুন",
+ "string-location-privacy-statement": "এই সাইটের গোপনীয়তা নীতি পড়ুন আপনার অবস্থান কি ভাবে ব্যবহার করা হবে তা দেখতে׀",
+ "string-trust-site": "আমি এই সাইটে বিশ্বাস করি׀ Gears ব্যবহার করতে অনুমতি দিন ׀"
+ },
+ "en-US": {
+ "string-workerpool-desc": "Run asynchronous JavaScript to improve application responsiveness",
+ "string-allow": "Allow",
+ "string-query-data": "The website below wants to store information on your computer using Gears.",
+ "string-deny-accesskey": "D",
+ "string-query-location": "The website below wants to access information about your location using Gears.",
+ "string-html-title": "Gears Security Warning",
+ "string-description": "Gears is an open source browser extension that enables web applications to provide offline functionality using the following JavaScript APIs:",
+ "string-database-desc": "Store data locally in a fully-searchable relational database",
+ "string-allow-accesskey": "A",
+ "string-cancel": "Cancel",
+ "string-never-allow-link": "Never allow this site",
+ "string-deny": "Deny",
+ "string-never-allow-link-wince": "Never allow it",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Store and serve application resources locally",
+ "string-location-privacy-statement": "Read the site's privacy policy to see how your location will be used.",
+ "string-trust-site": "I trust this site. Allow it to use Gears."
+ },
+ "da": {
+ "string-workerpool-desc": "Kør asynkron JavaScript for at forbedre programmernes svartid",
+ "string-allow": "Tillad",
+ "string-query-data": "Nedenstående websted ønsker at gemme oplysninger på din computer ved hjælp af Gears.",
+ "string-deny-accesskey": "F",
+ "string-query-location": "Nedenstående websted ønsker at få adgang til oplysninger om din placering ved hjælp af Gears.",
+ "string-html-title": "Gears-sikkerhedsadvarsel",
+ "string-description": "Gears er en open source-browserudvidelse, som gør det muligt for webprogrammer at stille offlinefunktioner til rådighed ved hjælp af følgende JavaScript-API'er:",
+ "string-database-desc": "Gem data lokalt i en fuldt søgbar relationel database",
+ "string-allow-accesskey": "I",
+ "string-cancel": "Annuller",
+ "string-never-allow-link": "Tillad aldrig dette websted",
+ "string-deny": "Afvis",
+ "string-never-allow-link-wince": "Giv aldrig tilladelse",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Gem og servicer applikationsressourcer lokalt",
+ "string-location-privacy-statement": "Læs webstedets politik til beskyttelse af personlige oplysninger for at få mere at vide om, hvordan din placering vil blive brugt.",
+ "string-trust-site": "Jeg har tillid til dette websted. Giv tilladelse til, at det bruger Gears."
+ },
+ "fa": {
+ "string-workerpool-desc": "JavaScript ناهمزمان را برای بهبود پاسخگویی برنامه اجرا کنید.",
+ "string-allow": "مجاز",
+ "string-query-data": "وب سایت زیر می خواهد با استفاده از Gears اطلاعات را در رایانه شما ذخیره کند.",
+ "string-deny-accesskey": "رد كردن",
+ "string-query-location": "وب سایت زیر می خواهد با استفاده از Gears به اطلاعاتی در مورد موقعیت شما دست یابد.",
+ "string-html-title": "اخطار امنیتی Gears",
+ "string-description": "Gears یک پسوند مرورگر منبع باز است که با استفاده از برنامه های JavaScript زیر امکان عملکرد برنامه های وب در حالت آفلاین را فراهم می سازد:",
+ "string-database-desc": "داده ها را در یک پایگاه داده مربوطه که کاملاً قابل جستجو باشد به صورت محلی ذخیره کنید",
+ "string-allow-accesskey": "مجاز",
+ "string-cancel": "لغو",
+ "string-never-allow-link": "هرگز به این سایت اجازه داده نشود",
+ "string-deny": "رد",
+ "string-never-allow-link-wince": "هرگز اجازه داده نشود",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "منابع برنامه را به صورت محلی ذخیره و استفاده کنید",
+ "string-location-privacy-statement": "برای اطلاع از نحوه استفاده از موقعیت خود، سیاست حفظ حقوق خصوصی سایت را مطالعه کنید.",
+ "string-trust-site": "من به این سایت اعتماد دارم. به آن اجازه دهید از Gears استفاده کند."
+ },
+ "hi": {
+ "string-workerpool-desc": "अनुप्रयोग की प्रतिक्रियात्मकता में सुधार लाने के लिए एसिंक्रॉनस JavaScript चलाएँ",
+ "string-allow": "अनुमति दें",
+ "string-query-data": "नीचे दी गई वेबसाइट Gears का उपयोग कर आपके कंप्यूटर पर जानकारी संग्रहीत करना चाहती है.",
+ "string-deny-accesskey": "इंकार करें",
+ "string-query-location": "नीचे दी गई वेबसाइट Gears का उपयोग कर आपकी स्थिति के बारे में जानकारी पर पहुँचना चाहती है.",
+ "string-html-title": "Gears सुरक्षा चेतावनी",
+ "string-description": "Gears एक खुला स्रोत ब्राउज़र एक्सटेंशन है जो वेब अनुप्रयोगों को निम्नलिखित JavaScript APIs का उपयोग कर ऑफ़लाइन कार्यक्षमताएँ उपलब्ध कराने देता है:",
+ "string-database-desc": "डेटा को स्थानीय रूप से एक पूर्ण रूप से खोजने योग्य रिलेशनल डेटाबेस में संचित करें",
+ "string-allow-accesskey": "अनुमति दें",
+ "string-cancel": "रद्द करें",
+ "string-never-allow-link": "इस साइट को कभी अनुमति न दें",
+ "string-deny": "इंकार करें",
+ "string-never-allow-link-wince": "उसे कभी अनुमति न दें",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "अनुप्रयोग संसाधन स्थानीय रूप से संचित और सर्व करें",
+ "string-location-privacy-statement": "यह देखने के लिए कि आपकी स्थिति का किस प्रकार उपयोग किया जाएगा साइट की गोपनीयता नीति पढ़ें.",
+ "string-trust-site": "मैं इस साइट पर भरोसा करता हूँ. इसे Gears का उपयोग करने दें."
+ },
+ "pt-BR": {
+ "string-workerpool-desc": "Executar o JavaScript assíncrono para melhorar a resposta do aplicativo",
+ "string-allow": "Permitir",
+ "string-query-data": "O site abaixo deseja armazenar informações sobre o computador que usa o Gears.",
+ "string-deny-accesskey": "D",
+ "string-query-location": "O site abaixo deseja acessar informações sobre o local que usa o Gears.",
+ "string-html-title": "Aviso de segurança do Gears",
+ "string-description": "O Gears é uma extensão de navegador de código aberto que permite que aplicativos da web forneçam funcionalidades off-line utilizando estas APIs em JavaScript:",
+ "string-database-desc": "Armazenar os dados em um banco de dados relacional local completamente pesquisável",
+ "string-allow-accesskey": "P",
+ "string-cancel": "Cancelar",
+ "string-never-allow-link": "Nunca permitir este site",
+ "string-deny": "Negar",
+ "string-never-allow-link-wince": "Nunca permiti-lo",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Armazenar e atender a recursos de aplicativo localmente",
+ "string-location-privacy-statement": "Leia as políticas de privacidade dos sites para ver como seu local será usado.",
+ "string-trust-site": "Este site é confiável. Permita que ele use o Gears."
+ },
+ "fi": {
+ "string-workerpool-desc": "Paranna sovellusten suorituskykyä käyttämällä asynkronista JavaScriptiä",
+ "string-allow": "Salli",
+ "string-query-data": "Alla oleva sivusto haluaa tallentaa tietoja tietokoneellesi käyttämällä Gearsia.",
+ "string-deny-accesskey": "D",
+ "string-query-location": "Alla oleva sivusto haluaa päästä sijaintitietoihisi käyttämällä Gearsia.",
+ "string-html-title": "Gears-tietoturvavaroitus",
+ "string-description": "Gears on avoimen lähdekoodin selainlaajennus, jonka avulla verkkosovelluksia voi käyttää offline-tilassa seuraavilla JavaScript API -liittymillä:",
+ "string-database-desc": "Tallenna tietoja paikallisesti täysin haettaviin relaatiotietokantoihin",
+ "string-allow-accesskey": "A",
+ "string-cancel": "Peruuta",
+ "string-never-allow-link": "Älä koskaan salli tätä sivustoa",
+ "string-deny": "Estä",
+ "string-never-allow-link-wince": "Älä koskaan salli",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Tallenna ja käytä sovellusten resurssitiedostoja paikallisesti",
+ "string-location-privacy-statement": "Lukemalla tämän sivuston tietosuojakäytännön näet, miten sijaintiasi käytetään.",
+ "string-trust-site": "Luotan tähän sivustoon. Anna sen käyttää Gearsia."
+ },
+ "hu": {
+ "string-workerpool-desc": "Aszinkron JavaScript futtatása az alkalmazás jobb kezelhetősége érdekében",
+ "string-allow": "Engedélyezés",
+ "string-query-data": "Az alábbi webhely a Szinkron szolgáltatás használatával adatokat szeretne tárolni az Ön számítógépén.",
+ "string-deny-accesskey": "T",
+ "string-query-location": "Az alábbi webhely a Szinkron szolgáltatás használatával adatokat szeretne megtudni az Ön helyéről.",
+ "string-html-title": "Biztonsági figyelmeztetés a Szinkrontól",
+ "string-description": "A Szinkron nyílt forráskódú böngészőbővítmény, amely lehetővé teszi a webes alkalmazások számára, hogy kapcsolaton kívüli funkciókat kínáljanak a következő JavaScript API-k segítségével:",
+ "string-database-desc": "Adatok helyi tárolása egy teljes mértékben kereshető relációs adatbázisban",
+ "string-allow-accesskey": "E",
+ "string-cancel": "Mégse",
+ "string-never-allow-link": "Soha ne engedélyezze ezt a webhelyet",
+ "string-deny": "Tiltás",
+ "string-never-allow-link-wince": "Soha ne engedélyezze",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Alkalmazás-erőforrások helyi tárolása és kiszolgálása",
+ "string-location-privacy-statement": "Olvassa el a webhely adatvédelmi szabályzatát, hogy megtudja, hogyan kezelik a felhasználók helyét.",
+ "string-trust-site": "Megbízom ebben a webhelyben. Használhatja a Szinkron szolgáltatást."
+ },
+ "ja": {
+ "string-workerpool-desc": "Asynchronous JavaScript により、アプリケーションのレスポンスを向上",
+ "string-allow": "許可",
+ "string-query-data": "下記のウェブサイトは、Gears を使用してパソコンに情報を保存します。",
+ "string-deny-accesskey": "D",
+ "string-query-location": "下記のウェブサイトは、Gears を使用して場所に関する情報にアクセスします。",
+ "string-html-title": "Gears のセキュリティ警告",
+ "string-description": "Gears は、ウェブ アプリケーションの機能をオフラインで使えるようにするオープン ソースのブラウザ拡張ツールです。使用している JavaScript API は、次のとおりです。",
+ "string-database-desc": "検索できるローカルのリレーショナル データベースにデータを保存",
+ "string-allow-accesskey": "A",
+ "string-cancel": "キャンセル",
+ "string-never-allow-link": "このサイトを常に拒否する",
+ "string-deny": "拒否",
+ "string-never-allow-link-wince": "常に拒否する",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "アプリケーション リソースをクライアント PC 側で格納して提供",
+ "string-location-privacy-statement": "場所の使用方法については、サイトのプライバシー ポリシーをご覧ください。",
+ "string-trust-site": "このサイトを信頼します。Gears の使用を許可します。"
+ },
+ "he": {
+ "string-workerpool-desc": "הפעל Javascript אסינכרוני כדי לשפר את היענות היישום",
+ "string-allow": "אפשר",
+ "string-query-data": "אתר האינטרנט שלהלן מבקש לאחסן באמצעות Gears מידע במחשב שלך.",
+ "string-deny-accesskey": "ד",
+ "string-query-location": "אתר האינטרנט שלהלן מבקש גישה באמצעות Gears למידע לגבי המיקום שלך.",
+ "string-html-title": "אזהרת אבטחה של Gears",
+ "string-description": "Gears הוא הרחבת דפדפן בעלת קוד פתוח. ההרחבה מאפשרת ליישומי אינטרנט לספק פונקציונליות באופן לא מקוון באמצעות ממשקי JavaScript API הבאים:",
+ "string-database-desc": "אחסן מידע באופן מקומי במסד נתונים טבלאי הניתן לחיפוש במלואו",
+ "string-allow-accesskey": "א",
+ "string-cancel": "ביטול",
+ "string-never-allow-link": "לעולם אין לאפשר אתר זה",
+ "string-deny": "דחה",
+ "string-never-allow-link-wince": "לעולם אל תאפשר שירות זה",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "אחסן משאבי יישומים והגש אותם באופן מקומי",
+ "string-location-privacy-statement": "עיין בהצהרת הפרטיות של האתר כדי לראות את אופן השימוש במיקומך.",
+ "string-trust-site": "אני נותן אמון באתר זה. אפשר לו להשתמש ב-Gears."
+ },
+ "te": {
+ "string-workerpool-desc": "అప్లికేషన్ ప్రతిస్పందనా స్వభావాన్ని పెంచడానికి అసమకాలీకరించిన జావాస్క్రిప్ట్‌ను రన్ చెయ్యండి",
+ "string-allow": "అనుమతించు",
+ "string-query-data": "Gears ఉపయోగించి ఈ క్రింది నున్న వెబ్సైట్ మీ కంప్యుటర్లో సమాచారాన్ని నిల్వ చెయ్యలనుకుంటుంది.",
+ "string-deny-accesskey": "D",
+ "string-query-location": "Gears ఉపయోగించి ఈ క్రింది నున్న వెబ్సైట్ మీ స్థానం గురించి సమాచారాన్ని ఆక్సెస్ చెయ్యలనుకుంటుంది.",
+ "string-html-title": "Gears భద్రత హెచ్చెరిక",
+ "string-description": "Gears అనేది ఒక ఓపెన్ సోర్స్ బ్రౌజర్ ఎక్స్ టెన్షన్. ఇది ఈ క్రింది JavaScript API లని ఉపయోగించి వెబ్ అప్లికేషన్ల కోసం ఆఫ్లైన్ ఉపయోగాన్ని ఎనేబుల్ చేస్తుంది:",
+ "string-database-desc": "డేటాను స్థానికంగా శోధించదగిన సంబంధిత డేటాబేస్‌లో నిల్వ చెయ్యండి",
+ "string-allow-accesskey": "A",
+ "string-cancel": "రద్దు చెయ్యి",
+ "string-never-allow-link": "ఈ సైట్‌ను ఎప్పుడూ అనుమతించవద్దు",
+ "string-deny": "తిరస్కరించు",
+ "string-never-allow-link-wince": "దీనిని ఎప్పుడూ అనుమతించవద్దు",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "అప్లికేషన్ రిసోర్సులను స్థానికంగా నిల్వ చేసి, అందించు",
+ "string-location-privacy-statement": "ఈ సైట్ యొక్క గోప్యతా విధానాన్ని చదివి మీ స్థానం ఎలా ఉపయోగించబడుతుందో తెలుసుకోండి .",
+ "string-trust-site": "నేను ఈ సైట్ని నమ్ముతున్నాను. దానిని Gears ఉపయోగించడానికి అనుమతించు."
+ },
+ "pt-PT": {
+ "string-workerpool-desc": "Executar Javascript assíncrono para melhorar a capacidade de resposta das aplicações",
+ "string-allow": "Permitir",
+ "string-query-data": "O Web site abaixo deseja armazenar informação no seu computador através do Gears.",
+ "string-deny-accesskey": "E",
+ "string-query-location": "O Web site abaixo deseja aceder a informação acerca da sua localização através do Gears.",
+ "string-html-title": "Aviso de segurança do Gears",
+ "string-description": "O Gears é uma extensão de browser de software livre que permite às aplicações Web fornecerem a funcionalidade off-line através da utilização das seguintes APIs de JavaScript:",
+ "string-database-desc": "Armazenar dados localmente numa base de dados relacional totalmente pesquisável",
+ "string-allow-accesskey": "P",
+ "string-cancel": "Cancelar",
+ "string-never-allow-link": "Nunca permitir este site",
+ "string-deny": "Negar",
+ "string-never-allow-link-wince": "Nunca permitir",
+ "string-trust-site-accesskey": "c",
+ "string-localserver-desc": "Armazenar e servir localmente recursos da aplicação",
+ "string-location-privacy-statement": "Leia a política de privacidade do site para saber como a sua localização vai ser usada.",
+ "string-trust-site": "Confio neste site. Permitir que utilize o Gears."
+ },
+ "sr": {
+ "string-workerpool-desc": "Покрените асинхрони JavaScript како бисте побољшали реакције апликације",
+ "string-allow": "Дозволи",
+ "string-query-data": "Веб сајт испод жели да ускладишти информације на вашем рачунару помоћу услуге Gears.",
+ "string-deny-accesskey": "X",
+ "string-query-location": "Веб сајт испод жели да приступи информацијама о вашој локацији помоћу услуге Gears.",
+ "string-html-title": "Gears безбедносно упозорење",
+ "string-description": "Gears је проширење прегледача отвореног кода којe веб апликацијама омогућава функционисање ван мреже помоћу следећих JavaScript API-ја:",
+ "string-database-desc": "Складиштите податке локално у потпуно претраживој релационој бази података",
+ "string-allow-accesskey": "M",
+ "string-cancel": "Откажи",
+ "string-never-allow-link": "Никада не дозволи овом сајту",
+ "string-deny": "Забрани приступ",
+ "string-never-allow-link-wince": "Никада не дозволи",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Складиштите и пружајте ресурсе апликације локално",
+ "string-location-privacy-statement": "Прочитајте политику приватности сајта да бисте видели како ће се ваша локација користити.",
+ "string-trust-site": "Имам поверења у овај сајт. Дозволи му да користи Gears."
+ },
+ "ko": {
+ "string-workerpool-desc": "비동기 자바스크립트를 실행하여 응용 프로그램 응답성 개선",
+ "string-allow": "허용",
+ "string-query-data": "다음 웹사이트가 Gears를 사용하여 컴퓨터에 정보를 저장하려고 합니다.",
+ "string-deny-accesskey": "D",
+ "string-query-location": "다음 웹사이트가 Gears를 사용하여 사용자의 위치 정보에 액세스하려고 합니다.",
+ "string-html-title": "Gears 보안경고",
+ "string-description": "Gears는 다음과 같은 자바스크립트 API를 통해 오프라인에서 웹 애플리케이션을 사용할 수 있게 해 주는 오픈 소스 브라우저 확장 프로그램입니다.",
+ "string-database-desc": "완벽한 검색이 가능한 관계형 데이터베이스의 형태로 데이터를 로컬 컴퓨터에 저장",
+ "string-allow-accesskey": "A",
+ "string-cancel": "취소",
+ "string-never-allow-link": "사이트를 허용하지 않음",
+ "string-deny": "금지",
+ "string-never-allow-link-wince": "허용하지 않음",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "로컬 컴퓨터에서 애플리케이션 리소스 저장 및 사용",
+ "string-location-privacy-statement": "위치가 사용되는 방법을 보려면 사이트의 개인정보 보호정책을 읽어 보세요.",
+ "string-trust-site": "이 사이트를 신뢰합니다. Gears 사용을 허용합니다."
+ },
+ "sv": {
+ "string-workerpool-desc": "Kör asynkron JavaScript som förbättrar programmets responsivitet",
+ "string-allow": "Tillåt",
+ "string-query-data": "Webbplatsen nedan vill ha tillgång till information om din plats som använder Gears.",
+ "string-deny-accesskey": "E",
+ "string-query-location": "Webbplatsen nedan will ha tillgång till information om din plats som använder Gears.",
+ "string-html-title": "Säkerhetsvarning i Gears",
+ "string-description": "Gears är ett webbläsartillägg med öppen källkod som gör att webbprogram kan ge offlinefunktionalitet med följande JavaScript-API:er:",
+ "string-database-desc": "Spara data lokalt i en helt sökbar relationsdatabas",
+ "string-allow-accesskey": "T",
+ "string-cancel": "Avbryt",
+ "string-never-allow-link": "Tillåt aldrig den här webbplatsen",
+ "string-deny": "Neka",
+ "string-never-allow-link-wince": "Tillåt aldrig",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Spara och hantera programresurser lokalt",
+ "string-location-privacy-statement": "Läs webbplatsens sekretesspolicy för att se hur din plats kommer att användas.",
+ "string-trust-site": "Jag litar på den här platsen Tillåt den använda Gears."
+ },
+ "ur": {
+ "string-workerpool-desc": "اپلی کیشن کا رد عمل بہتر بنانے کیلئے مطابقت پذیر جاوا اسکرپٹ چلائیں",
+ "string-allow": "اجازت دیں",
+ "string-query-data": "ذیل کی ویب سائٹ گیئرز کا استعمال کرتے ہوئے آپ کے کمپیوٹر پر معلومات جمع کرنا چاہتی ہے۔",
+ "string-deny-accesskey": "D",
+ "string-query-location": "ذیل کی ویب سائٹ گیئرز کا استعمال کرتے ہوئے آپ کے مقام سے متعلق معلومات تک رسائی حاصل کرنا چاہتی ہے۔",
+ "string-html-title": "گیئرز کی تحفظاتی تنبیہ",
+ "string-description": "گیئرز براؤزر کی ایک کھلے وسائل والی توسیع ہے جو ویب اطلاقات کو درج ذیل جاوا اسکرپٹ اطلاقات کا استعمال کرتے ہوئے آف لائن کارکردگی فراہم کرنے کے قابل بناتا ہے:",
+ "string-database-desc": "ایک پوری طرح قابل تلاش متعلقہ ڈیٹا بیس",
+ "string-allow-accesskey": "A",
+ "string-cancel": "منسوخ",
+ "string-never-allow-link": "کبھی بھی اس سائٹ کی اجازت نہ دیں۔",
+ "string-deny": "مسترد",
+ "string-never-allow-link-wince": "کبھی بھی اس کی اجازت نہ دیں",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "اطلاقی وسائل کو مقامی طور پر جمع اور استعمال کریں",
+ "string-location-privacy-statement": "یہ دیکھنے کیلئے کہ آپ کے جائے وقوع کا استعمال کس طرح کیا جائے گا، اس سائٹ کی رازداری کی پالیسی کو پڑھیں۔",
+ "string-trust-site": "میں اس سائٹ پر اعتبار کرتا ہوں۔ اسے گیئرز استعمال کرنے کی اجازت دیں۔"
+ },
+ "sk": {
+ "string-workerpool-desc": "Spustiť asynchrónny Javascript na zlepšenie reakcie aplikácie",
+ "string-allow": "Povoliť",
+ "string-query-data": "Nižšie uvedená webová lokalita chce použiť rozšírenie Gears na uchovávanie informácií vo vašom počítači.",
+ "string-deny-accesskey": "K",
+ "string-query-location": "Nižšie uvedená webová lokalita chce použiť rozšírenie Gears na získanie prístupu k informáciám o vašom umiestnení.",
+ "string-html-title": "Varovanie zabezpečenia rozšírenia Gears",
+ "string-description": "Gears je rozšírenie prehliadača typu open source, ktoré umožňuje webovým aplikáciám poskytovať funkcie v režime offline prostredníctvom nasledujúcich rozhraní JavaScript API:",
+ "string-database-desc": "Uložiť informácie lokálne v relačnej databáze, s možnosťou úplného prehľadávania",
+ "string-allow-accesskey": "P",
+ "string-cancel": "Zrušiť",
+ "string-never-allow-link": "Nikdy nepovoliť túto lokalitu",
+ "string-deny": "Zakázať",
+ "string-never-allow-link-wince": "Nikdy nepovoliť",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Uložiť a sprístupniť zdroje aplikácie lokálne",
+ "string-location-privacy-statement": "Ak chcete zistiť, na aké účely sa použijú informácie o vašom umiestnení, prečítajte si zásady ochrany osobných údajov pre túto lokalitu.",
+ "string-trust-site": "Považujem túto lokalitu za dôveryhodnú. Povoliť, aby mohla používať rozšírenie Gears."
+ },
+ "zh-CN": {
+ "string-workerpool-desc": "运行异步 JavaScript 以提高应用程序响应速度",
+ "string-allow": "允许",
+ "string-query-data": "下列网站要使用 Gears 在您的计算机上存储信息。",
+ "string-deny-accesskey": "D",
+ "string-query-location": "下列网站要使用 Gears 访问有关您的位置的信息。",
+ "string-html-title": "Gears 安全警告",
+ "string-description": "Gears 是一种开放源代码的浏览器扩展程序,通过该扩展程序网络应用程序可以使用以下 JavaScript API 提供脱机功能:",
+ "string-database-desc": "在本地将数据存储到可全面搜索的关系数据库中",
+ "string-allow-accesskey": "A",
+ "string-cancel": "取消",
+ "string-never-allow-link": "永不允许该网站",
+ "string-deny": "拒绝",
+ "string-never-allow-link-wince": "永不允许",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "在本地存储和提供应用程序资源",
+ "string-location-privacy-statement": "阅读网站的隐私政策,了解该网站会如何使用您的位置。",
+ "string-trust-site": "我信任该网站。 允许其使用 Gears。"
+ },
+ "de": {
+ "string-workerpool-desc": "Asynchrones JavaScript zum Verbessern der Anwendungsreaktion ausführen",
+ "string-allow": "Zulassen",
+ "string-query-data": "Die untenstehende Website möchte über Gears Informationen auf Ihrem Computer speichern.",
+ "string-deny-accesskey": "V",
+ "string-query-location": "Die untenstehende Website fordert über Gears Zugriffsinformationen zu Ihrem Speicherort an.",
+ "string-html-title": "Gears-Sicherheitswarnung",
+ "string-description": "Gears ist eine Open-Source-Browsererweiterung, mit der Webanwendungen unter Verwendung der folgenden JavaScript-APIs Offline-Funktionen anbieten können:",
+ "string-database-desc": "Daten lokal in einer voll durchsuchbaren relationalen Datenbank speichern",
+ "string-allow-accesskey": "Z",
+ "string-cancel": "Abbrechen",
+ "string-never-allow-link": "Diese Website nie zulassen",
+ "string-deny": "Verweigern",
+ "string-never-allow-link-wince": "Nie zulassen",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Anwendungsressourcen lokal speichern und bereitstellen",
+ "string-location-privacy-statement": "Lesen Sie die Datenschutzrichtlinien für die Site, um zu erfahren, wie Ihr Speicherort verwendet wird.",
+ "string-trust-site": "Ich vertraue dieser Site. Verwendung von Gears zulassen."
+ },
+ "pl": {
+ "string-workerpool-desc": "Uruchom asynchroniczny skrypt JavaScript, aby usprawnić działanie aplikacji",
+ "string-allow": "Zezwalaj",
+ "string-query-data": "Poniższa witryna internetowa chce przechowywać informacje na Twoim komputerze za pośrednictwem wtyczki Gears.",
+ "string-deny-accesskey": "P",
+ "string-query-location": "Poniższa witryna internetowa chce uzyskać dostęp do informacji o Twojej lokalizacji za pośrednictwem wtyczki Gears.",
+ "string-html-title": "Ostrzeżenie wtyczki Gears dotyczące zabezpieczeń",
+ "string-description": "Wtyczka Gears to rozszerzenie przeglądarki typu open source, które umożliwia aplikacjom internetowym funkcjonowanie w trybie offline przy użyciu następujących interfejsów API języka JavaScript:",
+ "string-database-desc": "Przechowuj dane lokalnie w relacyjnej bazie danych z możliwością pełnego przeszukiwania",
+ "string-allow-accesskey": "Z",
+ "string-cancel": "Anuluj",
+ "string-never-allow-link": "Nigdy nie zezwalaj na tę witrynę",
+ "string-deny": "Odmów",
+ "string-never-allow-link-wince": "Nigdy nie zezwalaj",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Przechowuj zasoby aplikacji i obsługuj je lokalnie",
+ "string-location-privacy-statement": "Zapoznaj się z polityką prywatności witryny, aby zobaczyć, do czego zostaną wykorzystane informacje o Twojej lokalizacji.",
+ "string-trust-site": "Ufam tej witrynie. Zezwól jej na korzystanie z wtyczki Gears."
+ },
+ "ms": {
+ "string-workerpool-desc": "Jalankan JavaScript tak segerak untuk meningkatkan kesambutan aplikasi",
+ "string-allow": "Benarkan",
+ "string-query-data": "Laman web di bawah mahu menyimpan maklumat pada komputer anda dengan menggunakan Gear.",
+ "string-deny-accesskey": "L",
+ "string-query-location": "Laman web di bawah mahu mengakses maklumat mengenai lokasi anda dengan menggunakan Gear.",
+ "string-html-title": "Amaran Keselamatan Gear",
+ "string-description": "Gear adalah lanjutan penyemak imbas sumber terbuka yang membolehkan aplikasi web untuk menyediakan kefungsian luar talian menggunakan JavaScript APIs yang berikut:",
+ "string-database-desc": "Simpan data setempat dalam pangkalan data hubungan boleh cari sepenuhnya",
+ "string-allow-accesskey": "N",
+ "string-cancel": "Batal",
+ "string-never-allow-link": "Jangan sekali-kali benarkan laman ini",
+ "string-deny": "Tolak",
+ "string-never-allow-link-wince": "Jangan sekali-kali benarkannya",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Simpan dan berikan sumber aplikasi setempat",
+ "string-location-privacy-statement": "Baca dasar privasi tapak untuk melihat bagaimana lokasi anda akan digunakan.",
+ "string-trust-site": "Saya percaya tapak ini. Benarkannya untuk menggunakan Gear."
+ },
+ "sl": {
+ "string-workerpool-desc": "Zaženi asinhroni javascript, da bo odzivnost aplikacij boljša",
+ "string-allow": "Dovoli",
+ "string-query-data": "To spletno mesto želi shraniti informacije o računalniku, v katerem se uporablja program Gears.",
+ "string-deny-accesskey": "R",
+ "string-query-location": "To spletno mesto želi omogočiti dostop do informacij o lokaciji, ki uporablja program Gears.",
+ "string-html-title": "Varnostno opozorilo programa Gears",
+ "string-description": "Gears je odprtokodna razširitev brskalnika, ki z uporabo teh programskih vmesnikov z JavaScriptom omogoča delovanje spletnih programov brez povezave:",
+ "string-database-desc": "Lokalno shranjevanje podatkov v relacijsko podatkovno zbirko, po kateri je mogoče iskati",
+ "string-allow-accesskey": "A",
+ "string-cancel": "Prekliči",
+ "string-never-allow-link": "Nikoli ne dovoli tega spletnega mesta",
+ "string-deny": "Zavrni",
+ "string-never-allow-link-wince": "Nikoli ne dovoli",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "Lokalno shranjevanje in omogočanje virov za aplikacije",
+ "string-location-privacy-statement": "Če želite izvedeti, kako bo uporabljena vaša lokacija, preberite pravilnik o zasebnosti, ki velja za to spletno mesto.",
+ "string-trust-site": "Temu spletnemu mestu zaupam. Dovoli mu uporabo programa Gears."
+ },
+ "mr": {
+ "string-workerpool-desc": "अनुप्रयोगाच्या प्रतिसादात्मकतेत सुधारणा करण्यासाठी असिंक्रोनस JavaScript चालवा",
+ "string-allow": "अनुमती द्या",
+ "string-query-data": "खालील वेबसाइट Gears वापरुन आपल्या संगणकावर माहिती संचयित करू इच्छित आहे.",
+ "string-deny-accesskey": "D",
+ "string-query-location": "खालील वेबसाइट Gears वापरुन आपल्या स्थानाबद्दलच्या माहितीमध्ये प्रवेश करू इच्छिते.",
+ "string-html-title": "Gears सुरक्षा चेतावणी",
+ "string-description": "Gears एक ओपन सोर्स ब्राउझर विस्तार आहे जो ऑफलाइन कार्यक्षमता प्रदान करण्यासाठी खालील JavaScript APIs वापरुन वेब अनुप्रयोग सक्षम करतोः",
+ "string-database-desc": "संपूर्ण-शोध करण्यायोग्य संबंधित डेटाबेस मध्ये स्थानिक रुपात डेटा संग्रहित करा",
+ "string-allow-accesskey": "A",
+ "string-cancel": "रद्द करा",
+ "string-never-allow-link": "या साइटला कधीही अनुमती देऊ नये",
+ "string-deny": "अमान्य करा",
+ "string-never-allow-link-wince": "यास कधीही परवानगी देऊ नये",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "अनुप्रयोग स्रोतांना स्थानिक रुपात संग्रहित आणि सर्व्ह करा",
+ "string-location-privacy-statement": "आपले स्थान कसे वापरले जाते हे जाणून घेण्यासाठी साउटचे गोपनीयता धोरण वाचा.",
+ "string-trust-site": "माझा या साइटवर विश्वास आहे. तिला Gears वापरण्याची परवानगी द्या."
+ },
+ "or": {
+ "string-workerpool-desc": "ଅନୁପ୍ରୟୋଗ ଶୀଘ୍ର ପ୍ରତିକ୍ରିୟା କରିବାକୁ ଏକ ଏସିଂକ୍ରୋନିୟସ୍ JavaScript ଚଲାନ୍ତୁ",
+ "string-allow": "ଅନୁମତି",
+ "string-query-data": "Gears ବ୍ୟବହାର କରି ନିମ୍ନ ୱେବସାଇଟ୍ ଆପଣଙ୍କ କମ୍ପ୍ୟୁଟରରେ ସୂଚନା ଗଚ୍ଛିତ କରିବାକୁ ଚାହୁଁଛି |",
+ "string-deny-accesskey": "D",
+ "string-query-location": "Gears ବ୍ୟବହାର କରି ନିମ୍ନ ୱେବସାଇଟ୍ ଆପଣଙ୍କ ଅବସ୍ଥାନ ସମ୍ବନ୍ଧରେ ସୂଚନା ଆସେସ୍ କରିବାକୁ ଚାହୁଁଛି |",
+ "string-html-title": "Gears ସୁରକ୍ଷା ଚେତାବନୀ",
+ "string-description": "Gears ଏକ ଖୋଲା ଉତ୍ସ ବ୍ରାଉଜର୍ ବିସ୍ତାର , ଯାହା ଅଫଲାଇନ୍ କାର୍ଯ୍ୟକ୍ଷମତା ପ୍ରଦାନ କରିବାକୁ ନିମ୍ନ JavaScript APIs ବ୍ୟବହାର କରି ୱେବ୍ ଅନୁପ୍ରୟୋଗ ସକ୍ଷମ କରେ",
+ "string-database-desc": "ପୂର୍ଣ୍ଣ-ସନ୍ଧାନ ଯୋଗ୍ୟ ସମ୍ପର୍କିତ ଡାଟାବେସ୍‌‌ରେ ଡାଟା ସ୍ଥାନୀୟ ଭାବରେ ଗଚ୍ଛିତ କରନ୍ତୁ",
+ "string-allow-accesskey": "A",
+ "string-cancel": "ବାତିଲ୍",
+ "string-never-allow-link": "କେବେ ମଧ୍ୟ ଏହି ସାଇଟ୍‌କୁ ଅନୁମତି କରନ୍ତୁ ନାହିଁ",
+ "string-deny": "ଅଗ୍ରାହ୍ୟ",
+ "string-never-allow-link-wince": "ଏହାକୁ କେବେ ମଧ୍ୟ ଅନୁମତି କରନ୍ତୁ ନାହିଁ",
+ "string-trust-site-accesskey": "t",
+ "string-localserver-desc": "ଅନୁପ୍ରୟୋଗ ଉତ୍ସଗୁଡିକୁ ସ୍ଥାନୀୟ ଭାବରେ ଗଚ୍ଛିତ ଏବଂ ପରିବେଷିତ କରନ୍ତୁ",
+ "string-location-privacy-statement": "ଆଣଙ୍କର ଅବସ୍ଥାନ କିପରି ବ୍ୟବହୃତ ହେବ ଦେଖିବାକୁ ଏହି ସାଇଟର ଗୋପନୀୟତା ନୀତି ପଢନ୍ତୁ |",
+ "string-trust-site": "ମୁଁ ଏହି ସାଇଟକୁ ବିଶ୍ବାସ କରେ,ଏହାକୁ Gears ବ୍ୟବହାର କରିବାକୁ ଅନୁମତି ପ୍ରଦାନ କରନ୍ତୁ |"
+ }
+};
+
+// Insert all localized strings for the specified locale into the div or span
+// matching the id.
+function loadI18nStrings(locale) {
+ var rtl_languages = ['he', 'ar', 'fa', 'ur'];
+
+ if (!locale) {
+ locale = 'en-US';
+ } else {
+ if (!localized_strings[locale]) {
+ // For xx-YY locales, determine what the base locale is.
+ var base_locale = locale.split('-')[0];
+
+ if (localized_strings[base_locale]) {
+ locale = base_locale;
+ } else {
+ locale = 'en-US';
+ }
+ }
+ }
+
+ var strings = localized_strings[locale];
+
+ // If the specified locale is an right to left language, change the direction
+ // of the page.
+ for (index in rtl_languages) {
+ if (locale == rtl_languages[index]) {
+ document.body.dir = "rtl";
+ break;
+ }
+ }
+
+ // Copy each string to the proper UI element, if it exists.
+ for (name in strings) {
+ if (name == 'string-html-title') {
+ if (!browser.ie_mobile) {
+ // IE Mobile dialogs don't have a title bar.
+ // Furthermore, document.title is undefined in IE Mobile on WinMo 5.
+ // It's also impossible to add properties to the window object.
+ // (see http://code.google.com/apis/gears/mobile.html)
+ document.title = strings[name];
+ }
+ } else {
+ var element = dom.getElementById(name);
+ if (element) {
+ element.innerHTML = strings[name];
+ }
+ }
+ }
+}
+
+
+</script>
+
+<script>
+ var debug = false;
+ initDialog();
+
+ initWarning();
+
+ setButtonLabel("string-allow", "allow-button", "string-allow-accesskey");
+ setButtonLabel("string-deny", "deny-button", "string-deny-accesskey");
+
+ // Highlight the access key in the 'trust' text.
+ // TODO(zork): Cause this to be based on the localized string, and figure out
+ // how to programmatically set the access key for the checkbox.
+ var trustTextElement = dom.getElementById("string-trust-site");
+ text = trustTextElement.innerHTML;
+ text = text.replace(/t/, "<span class='accesskey'>t</span>");
+ trustTextElement.innerHTML = text;
+
+ if (!browser.ie_mobile) {
+ CustomButton.initializeAll();
+ }
+
+ if (browser.ie_mobile) {
+ var allowText = dom.getElementById("string-allow");
+ if (allowText) {
+ window.pie_dialog.SetButton(allowText.innerText, "allowAccessPermanently();");
+ window.pie_dialog.SetButtonEnabled(true);
+ }
+ var cancelText = dom.getElementById("string-cancel");
+ if (cancelText) {
+ window.pie_dialog.SetCancelButton(cancelText.innerText);
+ }
+ // Focus allow button by default.
+ dom.getElementById('allow-button').focus();
+ }
+
+ function setTextContent(elem, content) {
+ if (isDefined(typeof document.createTextNode)) {
+ elem.appendChild(document.createTextNode(content));
+ } else {
+ elem.innerText = content;
+ }
+ }
+
+ function showHelp(show) {
+ if (browser.ie_mobile) {
+ var elemSettings = dom.getElementById("permissions-settings");
+ var elemHelp = dom.getElementById("permissions-help");
+ if (show) {
+ elemSettings.style.display = 'none';
+ elemHelp.style.display = 'block';
+ window.pie_dialog.SetButton("Back", "showHelp(false);");
+ window.pie_dialog.SetButtonEnabled(true);
+ } else {
+ elemSettings.style.display = 'block';
+ elemHelp.style.display = 'none';
+ window.pie_dialog.SetButton("Allow", "allowAccessPermanently();");
+ }
+ window.pie_dialog.ResizeDialog();
+ } else {
+ window.open('http://gears.google.com/?action=help');
+ }
+ }
+
+ function initWarning() {
+ var args;
+ if (debug) {
+ // Handy for debugging layout:
+ args = {
+ 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!"
+ };
+ } else {
+ // The arguments to this dialog are a single string, see PermissionsDialog
+ args = getArguments();
+ }
+
+ loadI18nStrings(args.locale);
+ var origin = args['origin']; // required parameter
+ var dialogType = args['dialogType']; // required parameter
+ var customIcon = args['customIcon'];
+ var customName = args['customName'];
+ var customMessage = args['customMessage'];
+
+ var elem;
+
+ elem = dom.getElementById("icon");
+ if (dialogType == "localData") {
+ elem = dom.getElementById("icon-local-data");
+ elem.style.display = "block";
+ elem = dom.getElementById("local-data");
+ elem.style.display = "block";
+ } else if (dialogType == "locationData") {
+ elem = dom.getElementById("icon-locationdata");
+ elem.style.display = "block";
+ elem = dom.getElementById("location-data");
+ elem.style.display = "block";
+ elem = dom.getElementById("string-location-privacy-statement");
+ elem.style.display = "block";
+ }
+
+ if (!customName) {
+ elem = dom.getElementById("origin-only");
+ elem.style.display = "block";
+ if (browser.ie_mobile) {
+ elem.innerHTML = wrapDomain(origin);
+ } else {
+ setTextContent(elem, origin);
+ }
+
+ // When all we have is the origin, we lay it out centered because that
+ // looks nicer. This is also what the original dialog did, which did not
+ // support the extra name, icon, or message.
+ if (!browser.ie_mobile && !customIcon && !customMessage) {
+ elem.setAttribute("align", "center");
+ }
+ } else {
+ elem = dom.getElementById("origin");
+ elem.style.display = "block";
+ setTextContent(elem, origin);
+ }
+
+ if (customIcon) {
+ elem = dom.getElementById("custom-icon");
+ elem.style.display = "inline";
+ elem.height = 32;
+ elem.width = 32;
+ loadImage(elem, customIcon);
+ }
+
+ if (customName) {
+ elem = dom.getElementById("custom-name");
+ elem.style.display = "block";
+ setTextContent(elem, customName);
+ }
+
+ if (customMessage) {
+ elem = dom.getElementById("custom-message");
+ elem.style.display = "block";
+ setTextContent(elem, customMessage);
+ }
+
+ if (!browser.ie_mobile) {
+ // Set up the checkbox to toggle the enabledness of the Allow button.
+ dom.getElementById("unlock").onclick = updateAllowButtonEnabledState;
+ dom.getElementById("checkbox-row").style.display = 'block';
+ updateAllowButtonEnabledState();
+ }
+ resizeDialogToFitContent();
+ }
+
+
+ function updateAllowButtonEnabledState() {
+ var allowButton = dom.getElementById("allow-button");
+ var unlockCheckbox = dom.getElementById("unlock");
+
+ if (unlockCheckbox.checked) {
+ enableButton(allowButton);
+ } else {
+ disableButton(allowButton);
+ }
+ }
+
+ // Note: The structure that the following functions pass is coupled to the
+ // code in PermissionsDialog that processes it.
+ function allowAccessPermanently() {
+ saveAndClose({"allow": true, "permanently": true});
+ }
+
+ function denyAccessTemporarily() {
+ saveAndClose({"allow": false, "permanently": false});
+ }
+
+ function denyAccessPermanently() {
+ saveAndClose({"allow": false, "permanently": true });
+ }
+</script>
+</html>
diff --git a/assets/plugins/gears-0.4.23.0/settings_dialog.html b/assets/plugins/gears-0.4.23.0/settings_dialog.html
new file mode 100644
index 00000000..e70e790e
--- /dev/null
+++ b/assets/plugins/gears-0.4.23.0/settings_dialog.html
@@ -0,0 +1,2750 @@
+<!DOCTYPE html>
+
+<!--
+Copyright 2007, The Android Open Source Project
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ 3. Neither the name of Google Inc. nor the names of its contributors may be
+ used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<html>
+<head>
+ <style type="text/css">
+
+
+/*
+Copyright 2007, The Android Open Source Project
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ 3. Neither the name of Google Inc. nor the names of its contributors may be
+ used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+body, html {
+ background-color:white;
+ padding:0;
+ margin:0;
+ height:100%;
+ overflow:hidden;
+ cursor:default;
+
+ /*
+ Makes the text unselectable in mozilla. See html_diaog.js for IE
+ implementation.
+ */
+ -moz-user-select:none;
+}
+
+body, td, input, select, button {
+ font-family:arial,sans-serif;
+ font-size:13px;
+}
+
+p {
+ margin:0;
+ margin-bottom:1em;
+}
+
+a {
+ color:blue;
+ text-decoration:underline;
+}
+
+button {
+ /*
+ It would be better to express margins in ems, but it causes layout
+ artifacts (presumably because of rounding errors) when combined with
+ the -moz-border-radius property used in button.css on Firefox 2.
+
+ Also: bizarely, if I try to use the more natural margin-left:10px, I get
+ a bug where the buttons bounce on IE when you hover over them.
+ */
+ position:relative;
+ margin-right:7px;
+ left:7px;
+}
+
+#head {
+ padding:1em;
+}
+
+#foot {
+
+ /*
+ On Android we do not use position:absolute due to some
+ rendering limitations (absolute positioning works, but
+ the automatic resizing of dialog is confused by the
+ variable content and the display is wrong, while if we
+ remove the absolute positioning, #foot is back in the
+ flow and the dialog displays correctly)
+ */
+
+ background:white;
+ bottom:0;
+ width:100%;
+}
+
+#button-row {
+ margin:1em;
+}
+
+.accesskey {
+ text-decoration: underline;
+
+ /* IE CSS extension */
+ accelerator: true;
+}
+
+ h1 {
+ font-size:1.2em;
+ margin:0;
+ }
+
+ h2 {
+ font-size:1.1em;
+ margin:0;
+ }
+
+ /* TODO(aa): Move into standard stylesheet if used in all dialogs? */
+ #icon {
+ margin-right:0.5em;
+ }
+
+ #content {
+ overflow-x:hidden;
+ overflow-y:auto;
+
+ margin:0 1em;
+
+ }
+
+ #content table {
+ border: 1px #ccc;
+ /* Only set the sides and bottom border of the table because
+ border-colapse doesn't work on WinCE. */
+ border-style: none solid;
+ border-collapse: collapse;
+ /* A bug in Mozilla with border-collapse:collapse along with the overflow
+ settings we have on the scroll element causes the left border to be
+ placed 1px to the left, making the element invisible. */
+ margin-left:1px;
+ }
+
+ #content td {
+
+ padding: 4px;
+
+ }
+
+ #content table.permissions {
+ border-bottom-style: solid;
+ }
+
+ #version {
+ font-size:0.8em;
+ font-style:italic;
+ }
+
+ #content td.left {
+ width: 100%;
+
+ padding-left: 20px;
+
+ }
+
+ #content td.right {
+
+ padding-right: 20px;
+
+ }
+
+ .app-icon {
+ width: 30px;
+ }
+
+ .app-name {
+ width: 150px;
+ }
+
+ .app-origin {
+ width: 70px;
+ }
+
+
+
+ td.origin-name {
+ font-weight: bold;
+ width: 100%;
+ }
+
+
+ div.radio-buttons {
+
+ width: 150px;
+
+ }
+
+
+
+
+ </style>
+</head>
+<body>
+ <div id="strings" style="display:none;">
+ <div id="string-cancel"></div>
+ <div id="string-cancel-accesskey"></div>
+ <div id="string-apply"></div>
+ <div id="string-apply-accesskey"></div>
+ <div id="string-nosites"></div>
+ <div id="string-allowed"></div>
+ <div id="string-denied"></div>
+ <div id="string-local-storage"></div>
+ <div id="string-location-data"></div>
+ <div id="string-remove"></div>
+ </div>
+
+ <div id="head">
+ <table width="100%" cellpadding="0" cellspacing="0" border="0">
+ <tr>
+ <td valign="middle">
+
+ <img id="icon" src="data:image/png;base64,
+iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAJPElEQVR4Xq3V
+WWyc1d0G8Oecd515Z/csHsfxntiOE8eOMY6dxFkokJCQlACBhEpsvWgLRkAL
+IhRxEYrSpp/6XYCgUokKBQEl0ABR0wrShBAI2R1nM3HseB+Px2PPPu/Mu3aE
+uKkoUhPzuzhXRzp/PUfPOcQ0TcxGrOcJ9Jz6F4YmTNq2yF4r5436aFyrlyz8
+nyQxH6m7aTdETxO+D8UssSxF/0gGc4NCpc3u2VtctvBNb5HjN71XUw8bxAHd
+pPiGkQeg/PADDI5lMZ3QYRUZL2MpqRVIyhpwGwj62OdTObo1l405svEBd9/h
+J7ae3Nd1p6rIP+wA5/tzuKExgKDfsZm32KHIU6AUaGnwiD5b5u3R0787OXVu
+x0kbF3t7ZuyzPZ/ve2HjfySI65SdPOQf7z/UNM87pPg8zi32wI0/J8oINE1F
+NgewLOBxW8GwxnyTUWCqcSycHyT7Du99V9XY1rX37Lh43QPoagrhS689zRnp
+X/p9btjcZUB+CL29vThwPD6USOnHrRbGqCixNLc3ueuK3BpMQ0U0oaJvONvd
+uvaGoVklkJm+6DbU2Aa3rxxy7CL0VBxj4Qz++s/I4ebF1VvKAyRiaEmkMobt
+bO/0Gy0NRZu9bh5XBifVxqXbnm/p2JiZVQtmJs5WhcZHq+XkEAhlYJrAF2cS
+qUX1pV13bVgSqa+yo6bMgSInl57XdOdDM0l9kFIKniWcwDP6rFtQUn/f6QTt
+uPd0z2AMJoGqGYjMqD1Vc8j5zEw/qJGG3UrBsQwMNpjgqHZW1w3MKbYiFTm1
+StOU2Q3Ai3bcdt9LH4iuusczsgxKCSwiYxLCQMmnwbAUDEMg5/PgRDec3qqc
+yXpQ4rejrjj0zME9j+/IyUnrrGrIsDwal3f1GAbReI7FgiqpeTqWWjQxJePK
+cAY9lxO4MmrCJc6UOt3BDoAFIQbmV3mF7qN7num/3F2CAnb610+CwITAsjAB
+GIYBAkBgKN4ZDiFhAttqq4TpqamNcU0PEULHeUJDnEAUs0P2e0saYeaGsLjO
+bjvfl3pDUWnX0IT8JYMcOprLmsTc0T/yzopyNRsFy7CIzOQQy3kO2ezeQRSQ
+qVvbEc2r2D00DsUwANNEhdWCUknEuyMT6GpvRTCT3GparW/LeQXTyWT0QjT9
+SXEt9+jSX1Xvd7iql+ZiF8CyDBQNmJzO64ZJj9ntLt3lCTaLNp89E+3BmXMD
+hcPVzNh4zLQGOh976oW9f0YBSy0WvD86CcNThLtrKkloZIhSm0PvHp/A1rpq
+lCiy17TbdwY710C72IOiaNR7MqFsuJjVXyoOp6Qq/TIEjiKb08FxHMqDFkY3
+yTLK2UGQQiJ0Bf/4fFQfjJa8sG7zI++1+eYYHo9nCN8iXdWlyIHgyboqsJry
+cljROmKqtreIY/9uJVgkSLaHi5evXKGHQ1Cv9oNQin2FZC5l0i0ttzrifAU5
+FovpvpEJ+YjLzup1VVL7onk2QRQYUGLi4pUYDp7zPvvcrv07BUH47mcUZXg8
+2rYEpqY8JFTUPFKzoKG5UhJ3uIo8J731Da/PWd65AqExGCODoIQAuoYGlw1B
+VthOjpEJPYpVGVNbdvOaus6GGtvqyLSyqvdqdoQS8s21TEZzkGyuE98e/h20
+JeAFL8ttiondjrllQDyGIlGA3+GgFkPH1Imv0NdzFgPxJCJZGQalqHLYsCLg
+vosH+ay4j+9sH3PO0z6e+onLZBzzq8RjMLXtn345irc+upr97Pj48bLy8iF8
+D/Liwhp0+jyBSUU52Oh2LbCYOhiOg6pqOBdL4Eg0poRy6gkLy0SKRaGy2W1v
+bnTZITEMcgB0wsM0ga8GRkFX+G9pvW/xp4qsMOevpFc6JYTL6n98ua55vc5y
+Iv4bsmvxfDS6nYjnlc2VkuWdCqedVxUVR6fj2DM8fpwThF8sdDnPcLoKllJr
+QtXur7NL/9fmcVpFlgWVJBT24FTv1zjjLt39xAcf/5S3cPhfUZFSHJ6YQoVV
+PMKKYpSxSgjJORyYmBoRrNI9j61ZeWZzZSmaXHYEBD7b5nG8GlO0rlAub6LA
+SKehxGOAaMHZLw5u7jt11IdrQP82FkFtSTEsBEGbw+knDidGUhlEFfW9wgM0
+XCynYWMoGl0OgFKEZQUNDuktWTcuEYsFBRhMpvHOhcvZ2k13Ple1uDmNa0C3
+LF6A1ZXl0FRlk3tuOavJWaQ0DQ6OO52MxzE2GQGTzYJwPFoDPlxKpgGYikTJ
+JPUUIUcp9g+OZEtuWXf/s7tff8XqcMi4Bux6K+tNDg8+6F+waAcnSchenIRL
+FGDjGL+mqIjBBCQRNosFJ0NhhFUNIsdxAqU+wenGcH8/ZkTp/PYXd77P8zyu
+FQ0lM09xbvcuR209lKEBUBOolKwICMKDedOkeZb75qV89UIf3uwbwl1zi2GD
+2e4sK68DAQopoaS6Jlzk8+N60LCifjAZS0wo3SdBkgmYlMAncmj3OpvSmvYa
+SyCUFwdg6jp+VlOGeRxtopL0srN+AZcfHYbf5USq50xw7PLXDK4DObS6FZNy
+fr+TZdZRSuEROBT6DoYQjGRljGVz3VaG+cjDMqMCpcvsc+Zs8Ta12JT+y1CH
+BwGGweHxMOTWZY9s2/WHV0S7A9eCnLh5KXoTmTVnYsmtGU2HT+SDLW77+haP
+CzaHA9l0BjlNhVDkg628HILNDvlqP5SxYXAcjwIcn0ng9RPdZ188cLitbvkK
+BdeAPTA5g1a34yDgOFiIHIQQDKayXQ6W/e1ClrWypgE7z4ElJrIDV3ApPImR
+VBYCz6FC0iHrBj4eGE413n3v02WNixVcI7bRaYMBoMJmRR4motk8av2+l6Zj
+sdWKad5htYjQczkkpiI4MDmNL6LxbsUw/1Ik8nmJYe6ITk8vkWoX7ChU8FPu
+OlrA/P+SevhFHj6bhKDVgq/jCUQNwENJkUmwXgCg6QaOzSTx4Wj4PZ/TuX5b
+bfWXQYJTvK69Wbduw/sP7Pz9AZc/YOA6sGo2AxMA1Q0IFCghJj4cGERpqf/I
+J+HIJRNmOSVUOh9LxtxWy/YHW5cotkgImqZC1/K4/dHHrtrm1+J6UXHZKlg7
+b4Jl+SqwHStRe/NatN++EcFVPzrfcOvaVk/nmhaho7Dctn7TA1u3Xg3Mmwd/
+RyduXL8BbbdvAmuxYjb+DcasAaYvg7ldAAAAAElFTkSuQmCC
+ ">
+
+ <!-- Some browsers automatically focus the first focusable item. We
+ don't want anything focused, so we add this fake item. -->
+ <a href="#" id="focus-thief"></a>
+ </td>
+ <td width="100%" valign="middle">
+ <h1>
+ <span id="string-gears-settings"></span>
+ </h1>
+ </td>
+ </tr>
+ </table>
+ </div>
+ <div id="content">
+ <p>
+ <span id="string-permissions-description"></span>
+ </p>
+ <div id='div-permissions'>
+ </div>
+ <br>
+ <br>
+ <div id="string-version"></div>
+ </div>
+ <div id="foot">
+ <div id="button-row">
+ <table width="100%" cellpadding="0" cellspacing="0" border="0">
+ <tr>
+
+ <td nowrap="true" align="right" valign="middle">
+ <button id="confirm-button" class="custom"
+ onclick="saveAndClose(calculateDiff()); return false;"
+ ></button
+ ><button id="cancel-button" accesskey="C" class="custom"
+ onclick="saveAndClose(null); return false;"
+ ></button>
+ </td>
+
+ </tr>
+ </table>
+ </div>
+ </div>
+
+
+</body>
+
+<script>
+/*
+Copyright (c) 2005 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+//Array.prototype.______array = '______array';
+
+var JSON = {
+ org: 'http://www.JSON.org',
+ copyright: '(c)2005 JSON.org',
+ license: 'http://www.crockford.com/JSON/license.html',
+
+ stringify: function (arg) {
+ var c, i, l, s = '', v;
+
+ switch (typeof arg) {
+ case 'object':
+ if (arg) {
+ if (arg.constructor == Array.prototype.constructor) {
+ for (i = 0; i < arg.length; ++i) {
+ v = this.stringify(arg[i]);
+ if (s) {
+ s += ',';
+ }
+ s += v;
+ }
+ return '[' + s + ']';
+ } else if (typeof arg.toString != 'undefined') {
+ for (i in arg) {
+ v = arg[i];
+ if (typeof v != 'undefined' && typeof v != 'function') {
+ v = this.stringify(v);
+ if (s) {
+ s += ',';
+ }
+ s += this.stringify(i) + ':' + v;
+ }
+ }
+ return '{' + s + '}';
+ }
+ }
+ return 'null';
+ case 'number':
+ return isFinite(arg) ? String(arg) : 'null';
+ case 'string':
+ l = arg.length;
+ s = '"';
+ for (i = 0; i < l; i += 1) {
+ c = arg.charAt(i);
+ if (c >= ' ') {
+ if (c == '\\' || c == '"') {
+ s += '\\';
+ }
+ s += c;
+ } else {
+ switch (c) {
+ case '\b':
+ s += '\\b';
+ break;
+ case '\f':
+ s += '\\f';
+ break;
+ case '\n':
+ s += '\\n';
+ break;
+ case '\r':
+ s += '\\r';
+ break;
+ case '\t':
+ s += '\\t';
+ break;
+ default:
+ c = c.charCodeAt();
+ s += '\\u00' + Math.floor(c / 16).toString(16) +
+ (c % 16).toString(16);
+ }
+ }
+ }
+ return s + '"';
+ case 'boolean':
+ return String(arg);
+ default:
+ return 'null';
+ }
+ },
+ parse: function (text) {
+ var at = 0;
+ var ch = ' ';
+
+ function error(m) {
+ throw {
+ name: 'JSONError',
+ message: m,
+ at: at - 1,
+ text: text
+ };
+ }
+
+ function next() {
+ ch = text.charAt(at);
+ at += 1;
+ return ch;
+ }
+
+ function white() {
+ while (ch) {
+ if (ch <= ' ') {
+ next();
+ } else if (ch == '/') {
+ switch (next()) {
+ case '/':
+ while (next() && ch != '\n' && ch != '\r') {}
+ break;
+ case '*':
+ next();
+ for (;;) {
+ if (ch) {
+ if (ch == '*') {
+ if (next() == '/') {
+ next();
+ break;
+ }
+ } else {
+ next();
+ }
+ } else {
+ error("Unterminated comment");
+ }
+ }
+ break;
+ default:
+ error("Syntax error");
+ }
+ } else {
+ break;
+ }
+ }
+ }
+
+ function string() {
+ var i, s = '', t, u;
+
+ if (ch == '"') {
+outer: while (next()) {
+ if (ch == '"') {
+ next();
+ return s;
+ } else if (ch == '\\') {
+ switch (next()) {
+ case 'b':
+ s += '\b';
+ break;
+ case 'f':
+ s += '\f';
+ break;
+ case 'n':
+ s += '\n';
+ break;
+ case 'r':
+ s += '\r';
+ break;
+ case 't':
+ s += '\t';
+ break;
+ case 'u':
+ u = 0;
+ for (i = 0; i < 4; i += 1) {
+ t = parseInt(next(), 16);
+ if (!isFinite(t)) {
+ break outer;
+ }
+ u = u * 16 + t;
+ }
+ s += String.fromCharCode(u);
+ break;
+ default:
+ s += ch;
+ }
+ } else {
+ s += ch;
+ }
+ }
+ }
+ error("Bad string");
+ }
+
+ function array() {
+ var a = [];
+
+ if (ch == '[') {
+ next();
+ white();
+ if (ch == ']') {
+ next();
+ return a;
+ }
+ while (ch) {
+ a.push(value());
+ white();
+ if (ch == ']') {
+ next();
+ return a;
+ } else if (ch != ',') {
+ break;
+ }
+ next();
+ white();
+ }
+ }
+ error("Bad array");
+ }
+
+ function object() {
+ var k, o = {};
+
+ if (ch == '{') {
+ next();
+ white();
+ if (ch == '}') {
+ next();
+ return o;
+ }
+ while (ch) {
+ k = string();
+ white();
+ if (ch != ':') {
+ break;
+ }
+ next();
+ o[k] = value();
+ white();
+ if (ch == '}') {
+ next();
+ return o;
+ } else if (ch != ',') {
+ break;
+ }
+ next();
+ white();
+ }
+ }
+ error("Bad object");
+ }
+
+ function number() {
+ var n = '', v;
+ if (ch == '-') {
+ n = '-';
+ next();
+ }
+ while (ch >= '0' && ch <= '9') {
+ n += ch;
+ next();
+ }
+ if (ch == '.') {
+ n += '.';
+ while (next() && ch >= '0' && ch <= '9') {
+ n += ch;
+ }
+ }
+ if (ch == 'e' || ch == 'E') {
+ n += 'e';
+ next();
+ if (ch == '-' || ch == '+') {
+ n += ch;
+ next();
+ }
+ while (ch >= '0' && ch <= '9') {
+ n += ch;
+ next();
+ }
+ }
+ v = +n;
+ if (!isFinite(v)) {
+ ////error("Bad number");
+ } else {
+ return v;
+ }
+ }
+
+ function word() {
+ switch (ch) {
+ case 't':
+ if (next() == 'r' && next() == 'u' && next() == 'e') {
+ next();
+ return true;
+ }
+ break;
+ case 'f':
+ if (next() == 'a' && next() == 'l' && next() == 's' &&
+ next() == 'e') {
+ next();
+ return false;
+ }
+ break;
+ case 'n':
+ if (next() == 'u' && next() == 'l' && next() == 'l') {
+ next();
+ return null;
+ }
+ break;
+ }
+ error("Syntax error");
+ }
+
+ function value() {
+ white();
+ switch (ch) {
+ case '{':
+ return object();
+ case '[':
+ return array();
+ case '"':
+ return string();
+ case '-':
+ return number();
+ default:
+ return ch >= '0' && ch <= '9' ? number() : word();
+ }
+ }
+
+ return value();
+ }
+};
+
+// Copyright 2008, The Android Open Source Project
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// 3. Neither the name of Google Inc. nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//=============================================================================
+// Various basic JavaScript utilities.
+//=============================================================================
+
+/**
+ * Check that the type is not undefined (we do it here as on
+ * some devices typeof returns unknown instead of undefined...).
+ * We have to pass the evaluation of (typeof elem) (i.e., a string)
+ * to the function rather than simply (element) -- passing an
+ * undefined object would make the function crash on PIE.
+ */
+function isDefined(type) {
+ return type != 'undefined' && type != 'unknown';
+}
+
+/**
+ * Returns true if val is a function.
+ */
+function isFunction(val) {
+ return typeof val == "function";
+}
+
+// TODO(aa): more basic type checking here.
+
+
+/**
+ * Creates a 'bound' function that calls the current function with a preset
+ * 'this' object and set of arguments.
+ */
+Function.prototype.bind = function(obj) {
+ var preArgs = Array.prototype.slice.call(arguments, 1);
+ var self = this;
+ return function() {
+ var postArgs = Array.prototype.slice.call(arguments, 0);
+ var totalArgs = preArgs.concat(postArgs);
+ return self.apply(obj, totalArgs);
+ }
+};
+
+/**
+ * Creates a partially applied function that calls the current function with
+ * a preset set of arguments.
+ */
+Function.prototype.partial = function() {
+ var args = Array.prototype.slice.call(arguments, 0);
+ return this.bind(null, args);
+};
+
+/**
+ * Binds all the methods in obj to obj.
+ */
+function bindMethods(obj) {
+ for (var p in obj) {
+ if (isFunction(obj[p])) {
+ obj[p] = obj[p].bind(obj);
+ }
+ }
+}
+
+
+/**
+ * Trim leading and trailing whitespace from a string.
+ */
+String.prototype.trim = function(str) {
+ return this.replace(/^\s+/, "").replace(/\s+$/, "");
+};
+
+
+/**
+ * Find the index of a given element in an array. Returns -1 if the item does
+ * not exist.
+ */
+Array.prototype.indexOf = function(item) {
+ for (var i = 0; i < this.length; i++) {
+ if (this[i] === item) {
+ return i;
+ }
+ }
+ return -1;
+};
+
+/**
+ * Makes a deep copy of an object.
+ */
+function cloneObject(original) {
+ var newObject = new Object();
+ for (i in original) {
+ if (typeof original[i] == 'object') {
+ newObject[i] = cloneObject(original[i]);
+ }
+ else {
+ newObject[i] = original[i];
+ }
+ }
+ newObject.length = original.length;
+ return newObject;
+}
+
+var browser = {};
+
+(function() {
+ var ua = navigator.userAgent;
+ browser.opera = typeof opera != "undefined";
+ browser.ie = !browser.opera && ua.indexOf("MSIE") > -1;
+ browser.ie_mobile = (ua.indexOf("Windows CE") > -1) &&
+ (ua.indexOf("MSIE") > -1);
+ browser.webkit = ua.indexOf("WebKit") > -1;
+ // WebKit also gives product == "gecko".
+ browser.mozilla = !browser.webkit && navigator.product == "Gecko";
+ browser.safari = ua.indexOf("Safari") > -1;
+ browser.mac = navigator.platform.indexOf("Mac") > -1;
+ browser.linux = navigator.platform.indexOf("Linux") > -1;
+ browser.windows = navigator.platform.indexOf("Win") > -1;
+ browser.android = ua.indexOf("Android") > -1;
+ if (browser.android) {
+ browser.safari = false;
+ }
+ // TODO(aa): Add detection for more browsers, as necessary.
+})();
+
+// Copyright 2008, The Android Open Source Project
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// 3. Neither the name of Google Inc. nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//=============================================================================
+// DOM manipulation utilities.
+// Requires: base.js
+//=============================================================================
+
+var dom = {};
+
+/**
+ * Provides a cross-browser way of getting an element by its id
+ */
+dom.getElementById = function(id) {
+ if (isDefined(typeof document.getElementById)) {
+ return document.getElementById(id);
+ } else if (isDefined(typeof document.all)) {
+ return document.all[id];
+ }
+ throw new Error("Failed to get element by ID.");
+};
+
+/**
+ * Gets the position and dimensions of an element in pixels in relation
+ * to the window origin.
+ */
+dom.getPosition = function(elem) {
+ var ret = { 'left' : 0,
+ 'top' : 0,
+ 'width' : elem.width,
+ 'height' : elem.height };
+
+ if (!elem.offsetParent) return ret;
+
+ var left = 0;
+ var top = 0;
+ while (elem) {
+ left += elem.offsetLeft;
+ top += elem.offsetTop;
+ elem = elem.offsetParent
+ }
+ ret.left = left;
+ ret.top = top;
+ return ret;
+};
+
+/**
+ * Returns the height of the inside of the window.
+ */
+dom.getWindowInnerHeight = function() {
+ if (isDefined(typeof window.innerHeight)) { // Firefox
+ return window.innerHeight;
+ } else if (isDefined(typeof document.body.offsetHeight)) { // IE
+ return document.body.offsetHeight;
+ }
+
+ throw new Error("Could not get windowInnerHeight.");
+};
+
+/**
+ * Add an event listener to an element cross-browser.
+ */
+dom.addEvent = function(element, eventName, handler) {
+ var wrapper = dom._callEventHandler.bind(dom, handler);
+
+ if (isDefined(typeof element.addEventListener)) {
+ // Standards-compatible browsers
+ element.addEventListener(eventName, wrapper, false);
+ } else if (isDefined(typeof element.attachEvent)) {
+ // IE
+ element.attachEvent("on" + eventName, wrapper);
+ } else {
+ throw new Error('Failed to attach event.');
+ }
+};
+
+/**
+ * Private helper for calling event handlers compatibly across browsers.
+ */
+dom._callEventHandler = function(handler, e) {
+ // Older versions of IE don't pass event to the handler.
+ if (!e) e = window.event;
+
+ // TODO(aa): Normalize event object properties.
+
+ return handler(e);
+};
+
+/**
+ * Gets the CSS classes for an element.
+ */
+dom.getClasses = function(elm) {
+ return elm.className.split(/\s+/);
+};
+
+/**
+ * Check is an element has a particular CSS class name.
+ */
+dom.hasClass = function(elm, className) {
+ return this.getClasses(elm).indexOf(className) > -1;
+};
+
+/**
+ * Adds a CSS class to an element. Do nothing if the class already exists.
+ */
+dom.addClass = function(elm, className) {
+ var classes = this.getClasses(elm);
+ if (classes.indexOf(className) > -1) {
+ return;
+ }
+ classes.push(className);
+ elm.className = classes.join(" ");
+};
+
+/**
+ * Removes a CSS class from an element. Do nothing if the class doesn't exist.
+ */
+dom.removeClass = function(elm, className) {
+ var classes = this.getClasses(elm);
+ var index = classes.indexOf(className);
+ if (index == -1) {
+ return;
+ }
+ classes.splice(index, 1);
+ elm.className = classes.join(" ");
+};
+
+/**
+ * Gets the text (non-html) content of an element.
+ */
+dom.getTextContent = function(elm) {
+ if (isDefined(typeof elm.textContent)) {
+ return elm.textContent;
+ } else if (isDefined(typeof elm.innerText)) {
+ return elm.innerText;
+ } else {
+ throw new Error("Could not find a property to get text content.");
+ }
+};
+
+/**
+ * Sets the text (non-html) contents of an element.
+ */
+dom.setTextContent = function(elm, text) {
+ if (isDefined(typeof elm.textContent)) {
+ elm.textContent = text;
+ } else if (isDefined(typeof elm.innerText)) {
+ elm.innerText = text;
+ } else {
+ throw new Error("Could not find a property to set text content.");
+ }
+};
+
+// Copyright 2007, The Android Open Source Project
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// 3. Neither the name of Google Inc. nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * Initialize the base functionality of the dialog.
+ */
+function initDialog() {
+ if (!browser.ie_mobile) {
+ dom.addEvent(document, "keyup", handleKeyUp);
+ } else {
+ var buttonRowElem = null;
+ if (window.pie_dialog.IsSmartPhone()) {
+ buttonRowElem = dom.getElementById("button-row-smartphone");
+ } else {
+ buttonRowElem = dom.getElementById("button-row");
+ }
+ if (buttonRowElem) {
+ buttonRowElem.style.display = 'block';
+ }
+ }
+ if (browser.ie_mobile) {
+ window.pie_dialog.SetScriptContext(window);
+ window.pie_dialog.ResizeDialog();
+ }
+}
+
+/**
+ * Set the label of an input button, and optionally, its accesskey.
+ */
+function setButtonLabel(textID, elemID, accessKeyID) {
+ var textElem = dom.getElementById(textID);
+ var buttonElem = dom.getElementById(elemID);
+ if (textElem == null || buttonElem == null) {
+ return;
+ }
+
+ var accessKeyElem = null;
+ if (isDefined(typeof accessKeyID)) {
+ accessKeyElem = dom.getElementById(accessKeyID);
+ }
+
+ // This function works for two different kinds of buttons. Simple buttons
+ // based on the <input type="button"> tag, and custom buttons based on a
+ // <button> tag with the css class "custom".
+ // Note that on Windows Mobile 5, the tagName property is not defined.
+ // We can therefore safely assume that the converse is also true:
+ // if tagName is not defined, then the platform must be Windows Mobile 5.
+ if (!isDefined(typeof buttonElem.tagName) ||
+ buttonElem.tagName.toLowerCase() == "input") {
+ buttonElem.value = dom.getTextContent(textElem).trim();
+ if (accessKeyElem != null) {
+ buttonElem.accessKey = dom.getTextContent(accessKeyElem).trim();
+ }
+ } else if (buttonElem.tagName.toLowerCase() == "button") {
+ var text = dom.getTextContent(textElem).trim();
+
+ if (accessKeyElem != null) {
+ // Some browsers use the accessKey attribute of the the anchor tag.
+ var accessKey = dom.getTextContent(accessKeyElem).trim();
+ buttonElem.accessKey = accessKey;
+
+ // Find the first matching character in the text and mark it.
+ // Note: this form of String.replace() only replaces the first occurence.
+ text = text.replace(accessKey,
+ "<span class='accesskey'>" + accessKey + "</span>");
+ }
+
+ buttonElem.innerHTML = text;
+ } else {
+ throw new Error("Unexpected button tag name: " + buttonElem.tagName);
+ }
+}
+
+/**
+ * Allows a dialog to do custom layout when the window changes sizes. The
+ * provided function will be called with the height of the content area when the
+ * dialog should relayout.
+ */
+function initCustomLayout(layoutFunction) {
+ function doLayout() {
+ layoutFunction(getContentHeight());
+ }
+
+ doLayout();
+
+ // We do an additional layout in onload because sometimes things aren't
+ // stabilized when the first doLayout() is called above.
+ dom.addEvent(window, "load", doLayout);
+ dom.addEvent(window, "resize", doLayout);
+
+ // Mozilla doesn't fire continuous events during resize, so if we want to get
+ // somewhat smooth resizing, we need to run our own timer loop. This still
+ // doesn't look perfect, because the timer goes off out of sync with the
+ // browser's internal reflow, but I think it's better than nothing.
+ // TODO(aa): Keep looking for a way to get an event for each reflow, like IE.
+ if (browser.mozilla) {
+ var lastHeight = -1;
+
+ function maybeDoLayout() {
+ var currentHeight = dom.getWindowInnerHeight();
+ if (currentHeight != lastHeight) {
+ lastHeight = currentHeight;
+ doLayout();
+ }
+ }
+
+ window.setInterval(maybeDoLayout, 30);
+ }
+}
+
+/**
+ * Get the JSON-formatted arguments that were passed by the caller.
+ */
+function getArguments() {
+ var argsString;
+ if (browser.ie_mobile) {
+ argsString = window.pie_dialog.GetDialogArguments();
+ } else if (browser.ie) {
+ argsString = window.external.GetDialogArguments();
+ } else if (browser.mozilla) {
+ argsString = getFirefoxArguments(window.arguments[0]);
+ } else if (browser.safari) {
+ argsString = window.gears_dialogArguments;
+ } else if (browser.android) {
+ argsString = "" + window.bridge.getDialogArguments();
+ }
+
+ if (typeof argsString == "string") {
+ return JSON.parse(argsString);
+ } else {
+ return null;
+ }
+}
+
+/**
+ * Helper used by getArguments(). Getting the arguments in the right type is
+ * more involved in Firefox.
+ */
+function getFirefoxArguments(windowArguments) {
+ var Ci = Components.interfaces;
+ windowArguments.QueryInterface(Ci.nsIProperties);
+ var supportsString =
+ windowArguments.get("dialogArguments", Ci.nsISupportsString);
+ return supportsString.data;
+}
+
+/**
+ * Close the dialog, sending the specified result back to the caller.
+ */
+function saveAndClose(resultObject) {
+ var resultString = JSON.stringify(resultObject);
+ if (browser.ie_mobile) {
+ window.pie_dialog.CloseDialog(resultString);
+ } else if (browser.ie) {
+ window.external.CloseDialog(resultString);
+ } else if (browser.mozilla) {
+ saveFirefoxResults(resultString);
+ window.close();
+ } else if (browser.safari) {
+ window.gears_dialog.setResults(resultString);
+ } else if (browser.android) {
+ window.bridge.closeDialog(resultString);
+ }
+}
+
+/**
+ * Helper used by endDialog() for Firefox.
+ */
+function saveFirefoxResults(resultString) {
+ var Cc = Components.classes;
+ var Ci = Components.interfaces;
+
+ var props = window.arguments[0].QueryInterface(Ci.nsIProperties);
+ var supportsString = Cc["@mozilla.org/supports-string;1"]
+ .createInstance(Ci.nsISupportsString);
+
+ supportsString.data = resultString;
+ props.set("dialogResult", supportsString);
+}
+
+/**
+ * Returns the height of the content area of the dialog.
+ */
+function getContentHeight() {
+ var head = dom.getElementById("head");
+ var foot = dom.getElementById("foot");
+ return dom.getWindowInnerHeight() - head.offsetHeight - foot.offsetHeight;
+}
+
+/**
+ * For some reason ESC doesn't work on HTML dialogs in either Firefox or IE. So
+ * we implement it manually.
+ */
+function handleKeyUp(e) {
+ var ESC_KEY_CODE = 27;
+
+ if (e.keyCode == ESC_KEY_CODE) {
+ saveAndClose(null);
+ }
+}
+
+/**
+ * Disables a button in the right way, whether it is normal or custom.
+ */
+function disableButton(buttonElem) {
+ buttonElem.disabled = true;
+
+ if (browser.ie_mobile) {
+ window.pie_dialog.SetButtonEnabled(false);
+ }
+
+ if (!isDefined(typeof buttonElem.tagName) ||
+ buttonElem.tagName.toLowerCase() == "input") {
+ buttonElem.style.color = "gray";
+ } else if (buttonElem.tagName.toLowerCase() == "button") {
+ dom.addClass(buttonElem, "disabled");
+ } else {
+ throw new Error("Unexpected tag name: " + buttonElem.tagName);
+ }
+}
+
+/**
+ * Enables a button in the right way, whether it is normal or custom.
+ */
+function enableButton(buttonElem) {
+ buttonElem.disabled = false;
+
+ if (browser.ie_mobile) {
+ window.pie_dialog.SetButtonEnabled(true);
+ }
+
+ if (!isDefined(typeof buttonElem.tagName) ||
+ buttonElem.tagName.toLowerCase() == "input") {
+ buttonElem.style.color = "black";
+ } else if (buttonElem.tagName.toLowerCase() == "button") {
+ dom.removeClass(buttonElem, "disabled");
+ } else {
+ throw new Error("Unexpected tag name: " + buttonElem.tagName);
+ }
+}
+
+/**
+ * Returns a wrapped domain (useful for small screens dialogs,
+ * e.g. windows mobile devices)
+ */
+function wrapDomain(str) {
+ if (!browser.ie_mobile) {
+ return str;
+ }
+
+ var max = 20;
+ var url;
+ var scheme_start = str.indexOf("://");
+ var scheme = "";
+
+ if (scheme_start != -1) {
+ scheme = str.substring(0, scheme_start);
+ scheme += "://";
+ // there's automatically an hyphenation
+ // point used by the browser after http://
+ // so we only have to hyphenate the
+ // rest of the string
+ url = str.substring(scheme.length);
+ } else {
+ url = str;
+ }
+
+ // We hyphenate the string on every dot
+ var components = url.split(".");
+ if (components.length < 1) {
+ return str;
+ }
+
+ var content = components[0];
+ var len = content.length;
+ for (var i=1; i < components.length; i++) {
+ var elem = components[i];
+ content += ".";
+ len++;
+ if (len + elem.length > max) {
+ content += "<br>";
+ len = 0;
+ }
+ content += elem;
+ len += elem.length;
+ }
+ return scheme + content;
+}
+
+/**
+ * Resizes the dialog to fit the content size.
+ */
+function resizeDialogToFitContent(opt_minDialogInnerHeight) {
+ // This does not work on PIE (no height measurement)
+ if (browser.ie_mobile) {
+ window.pie_dialog.ResizeDialog();
+ return;
+ }
+ // window.resize() is not working on Android, we bail out.
+ if (browser.android) {
+ return;
+ }
+
+ // Resize the window to fit
+ var contentDiv = dom.getElementById("content");
+ var contentHeightProvided = getContentHeight();
+ var contentHeightDesired = contentDiv.offsetHeight;
+
+ var dialogHeightProvided = dom.getWindowInnerHeight();
+ var dialogHeightDesired =
+ dialogHeightProvided + (contentHeightDesired - contentHeightProvided);
+
+ var minDialogHeight = opt_minDialogInnerHeight || 0;
+ var maxDialogHeight = 400; // arbitrary max height for a dialog to resize to
+
+ var targetHeight = Math.max(dialogHeightDesired, minDialogHeight);
+ targetHeight = Math.min(dialogHeightDesired, maxDialogHeight);
+
+ if (targetHeight != dialogHeightProvided) {
+ var dy = targetHeight - dialogHeightProvided;
+ window.resizeBy(0, dy);
+ }
+}
+
+/**
+ * Safari running on OSX 10.4 can't load images dynamically from the network,
+ * this helper function calls gears-specific code to work around this
+ * limitation.
+ */
+function loadImage(elem, image_url) {
+ if (browser.safari) {
+ var position = dom.getPosition(elem);
+ window.gears_dialog.loadImageIntoElement(image_url, position.top,
+ position.left,
+ position.width,
+ position.height);
+ } else {
+ elem.src = image_url;
+ }
+}
+
+// Copyright 2008, The Android Open Source Project
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// 3. Neither the name of Google Inc. nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//=============================================================================
+// Implements the behavior of our custom buttons. There is no native support in
+// IE for rounded corners on HTML elements, so this script also implements a
+// hack to simulate them in that browser.
+//
+// Requires: base.js, dom.js
+//=============================================================================
+
+/**
+ * Constructor.
+ */
+function CustomButton(htmlButton) {
+ bindMethods(this);
+
+ this._focused = false;
+ this._htmlButton = htmlButton;
+
+ if (!document.all) { // not IE
+ dom.addEvent(htmlButton, "mouseover", this._handleMouseOver);
+ dom.addEvent(htmlButton, "mouseout", this._handleMouseOut);
+ } else { // IE
+ // mouseover/mouseout behave badly with nested elements in IE. Luckily,
+ // mouseenter/mouseleave are provided and have the equivalent semantics.
+ dom.addEvent(htmlButton, "mouseenter", this._handleMouseOver);
+ dom.addEvent(htmlButton, "mouseleave", this._handleMouseOut);
+
+ // We also handle focus and blur in IE so that we can update the corners
+ // to match the black border IE provides.
+ dom.addEvent(htmlButton, "focusin", this._handleFocus);
+ dom.addEvent(htmlButton, "blur", this._handleBlur);
+
+ dom.addClass(htmlButton, "ie");
+ this._createCorners();
+ }
+
+ // Limit button widths to a minimum of 60px. They look funny skinnier.
+ if (htmlButton.offsetWidth < 60) {
+ htmlButton.style.width = "60px";
+ }
+
+ htmlButton.style.visibility = "visible";
+}
+
+/**
+ * Initializes all the custom buttons on the page.
+ */
+CustomButton.initializeAll = function() {
+ var buttons = document.getElementsByTagName("button");
+ for (var i = 0, button; button = buttons[i]; i++) {
+ if (dom.hasClass(button, "custom")) {
+ new CustomButton(button);
+ }
+ }
+};
+
+/**
+ * Highlights the button on mouseover. Note that this gets called multiple
+ * times while the mouse goes over sub elements inside the button.
+ */
+CustomButton.prototype._handleMouseOver = function() {
+ if (!this._htmlButton.disabled && !this._focused) {
+ dom.addClass(this._htmlButton, "hover");
+ this._setCornerColor("blue");
+ }
+};
+
+/**
+ * Undoes the highlighting of the button on mouseout. Note that this gets called
+ * multiple times when the mouse moves out of sub elements, even if it is still
+ * over the button. That is OK, as long as there is immediately a mouseover
+ * event.
+ */
+CustomButton.prototype._handleMouseOut = function() {
+ if (!this._htmlButton.disabled && !this._focused) {
+ dom.removeClass(this._htmlButton, "hover");
+ this._setCornerColor("grey");
+ }
+};
+
+/**
+ * Highlights the button on focus. Currently only used for IE.
+ * TODO(aa): The black highlight looks cool. Consider for other browsers.
+ */
+CustomButton.prototype._handleFocus = function() {
+ dom.removeClass(this._htmlButton, "hover");
+ this._setCornerColor("black");
+ this._focused = true;
+};
+
+/**
+ * Undoes the highlighting of the button on blur.
+ */
+CustomButton.prototype._handleBlur = function() {
+ this._setCornerColor("grey");
+ this._focused = false;
+};
+
+/**
+ * Helper to create the graphics that make the button have rounded corners in
+ * IE.
+ */
+CustomButton.prototype._createCorners = function() {
+ // Making the button position:relative makes it possible to position things
+ // inside it relative to it's border.
+ this._htmlButton.style.position = "relative";
+
+ var verticalEdges = ["left", "right"];
+ var horizontalEdges = ["top", "bottom"];
+ var colors = ["grey", "blue", "black"];
+
+ for (var i = 0; i < verticalEdges.length; i++) {
+ for (var j = 0; j < horizontalEdges.length; j++) {
+ for (var k = 0; k < colors.length; k++) {
+ var img = document.createElement("img");
+ img.src = "button_corner_" + colors[k] + ".gif";
+ img.color = colors[k];
+ img.style.position = "absolute";
+ img.style[verticalEdges[i]] = "-2px";
+ img.style[horizontalEdges[j]] = "-2px";
+ img.style.filter =
+ "progid:DXImageTransform.Microsoft.BasicImage(Rotation=" +
+ (i + j) + ")";
+ this._htmlButton.appendChild(img);
+ }
+ }
+ }
+
+ this._setCornerColor("grey");
+};
+
+/**
+ * Sets the color of the corners in IE by changing the stack order of the corner
+ * images.
+ */
+CustomButton.prototype._setCornerColor = function(newColor) {
+ var imgs = this._htmlButton.getElementsByTagName("img");
+ for (var i = 0, img; img = imgs[i]; i++) {
+ img.style.zIndex = Number(img.color == newColor);
+ }
+};
+
+var localized_strings = {
+ "el": {
+ "string-denied": "Δεν επιτρέπονται",
+ "string-apply-accesskey": "A",
+ "string-remove": "Κατάργηση",
+ "string-gears-settings": "Ρυθμίσεις Gears",
+ "string-apply": "Εφαρμογή",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Άκυρο",
+ "string-html-title": "Ρυθμίσεις Gears",
+ "string-allowed": "Επιτρέπονται",
+ "string-local-storage": "Τοπικός αποθηκευτικός χώρος",
+ "string-nosites": "Δεν υπάρχουν ιστότοποι.",
+ "string-permissions-description": "Ο παρακάτω πίνακας δείχνει τα δικαιώματα που χορηγήσατε σε κάθε ιστότοπο που επιχείρησε να χρησιμοποιήσει το Gears.",
+ "string-location-data": "Τοποθεσία",
+ "string-version": "Έκδοση Gears 0.4.23.0"
+ },
+ "gu": {
+ "string-denied": "નકારેલું",
+ "string-apply-accesskey": "A",
+ "string-remove": "દૂર કરો",
+ "string-gears-settings": "Gears સેટિંગ્સ",
+ "string-apply": "લાગુ કરો",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "રદ કરો",
+ "string-html-title": "Gears સેટિંગ્સ",
+ "string-allowed": "મંજુર કરેલ",
+ "string-local-storage": "સ્થાનિક સ્ટોરેજ",
+ "string-nosites": "કોઇ સાઇટ્સ નથીં.",
+ "string-permissions-description": "નીચેનુ કોષ્ટક દર્શાવે છે કે તમે જુદી-જુદી સાઇટ્સને કઇ પરવાનગીઓ આપેલી છે કે જેમણે Gearsનો ઉપયોગ કરવાનો પ્રયત્ન કર્યો છે.",
+ "string-location-data": "સ્થાન",
+ "string-version": "Gears સંસ્કરણ 0.4.23.0"
+ },
+ "zh-TW": {
+ "string-denied": "遭拒",
+ "string-apply-accesskey": "A",
+ "string-remove": "移除",
+ "string-gears-settings": "Gears 設定",
+ "string-apply": "套用",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "取消",
+ "string-html-title": "Gears 設定",
+ "string-allowed": "允許",
+ "string-local-storage": "本機儲存",
+ "string-nosites": "沒有網站。",
+ "string-permissions-description": "下表顯示您授予嘗試使用 Gears 之各個網站的權限。",
+ "string-location-data": "位置",
+ "string-version": "Gears 版本 0.4.23.0"
+ },
+ "vi": {
+ "string-denied": "Bị từ chối",
+ "string-apply-accesskey": "P",
+ "string-remove": "Xoá",
+ "string-gears-settings": "Cài đặt Gears",
+ "string-apply": "Áp dụng",
+ "string-cancel-accesskey": "H",
+ "string-cancel": "Huỷ",
+ "string-html-title": "Cài đặt Gears",
+ "string-allowed": "Được phép",
+ "string-local-storage": "Bộ nhớ cục bộ",
+ "string-nosites": "Không có trang web nào.",
+ "string-permissions-description": "Bảng dưới đây hiển thị các quyền bạn cấp cho mỗi trang đã cố gắng sử dụng Gears.",
+ "string-location-data": "Vị trí",
+ "string-version": "Gears phiên bản 0.4.23.0"
+ },
+ "ca": {
+ "string-denied": "Denegat",
+ "string-apply-accesskey": "P",
+ "string-remove": "Elimina",
+ "string-gears-settings": "Configuració de Gears",
+ "string-apply": "Aplica",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Cancel·la",
+ "string-html-title": "Configuració de Gears",
+ "string-allowed": "Permès",
+ "string-local-storage": "Emmagatzematge local",
+ "string-nosites": "Cap lloc.",
+ "string-permissions-description": "La taula següent mostra els permisos que teniu a cada lloc que ha intentat utilizar Gears.",
+ "string-location-data": "Ubicació",
+ "string-version": "Versió de Gears 0.4.23.0"
+ },
+ "it": {
+ "string-denied": "Non consentito",
+ "string-apply-accesskey": "A",
+ "string-remove": "Rimuovi",
+ "string-gears-settings": "Impostazioni di Google Gears",
+ "string-apply": "Applica",
+ "string-cancel-accesskey": "A",
+ "string-cancel": "Annulla",
+ "string-html-title": "Impostazioni di Google Gears",
+ "string-allowed": "Consentito",
+ "string-local-storage": "Archiviazione locale",
+ "string-nosites": "Nessun sito.",
+ "string-permissions-description": "La tabella riportata di seguito mostra le autorizzazioni concesse ai siti che hanno tentato di utilizzare Google Gears.",
+ "string-location-data": "Posizione",
+ "string-version": "Versione di Google Gears 0.4.23.0"
+ },
+ "kn": {
+ "string-denied": "ನಿರಾಕರಿಸಲಾಗಿದೆ",
+ "string-apply-accesskey": "A",
+ "string-remove": "ತೆಗೆಯಿರಿ",
+ "string-gears-settings": "Gears ಸೆಟ್ಟಿಂಗ್ಸ್",
+ "string-apply": "ಅನ್ವಯಿಸಿ",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "ರದ್ದುಮಾಡು",
+ "string-html-title": "Gears ಸೆಟ್ಟಿಂಗ್ಸ್",
+ "string-allowed": "ಅನುಮತಿಸಲಾಗಿದೆ",
+ "string-local-storage": "ಸ್ಥಳೀಯ ಶೇಖರಣೆ",
+ "string-nosites": "ಸೈಟುಗಳಿಲ್ಲ.",
+ "string-permissions-description": "ಕೆಳಗಿನ ಟೇಬಲ್ Gears ಬಳಸಲು ಪ್ರಯತ್ನಿಸಿದ ಪ್ರತಿಯೊಂದು ಸೈಟ್ ಗೂ ನೀವು ನೀಡಿದ ಅನುಮತಿಗಳನ್ನು ತೋರಿಸುತ್ತಿದೆ.",
+ "string-location-data": "ಸ್ಥಳ",
+ "string-version": "0.4.23.0 Gears ಆವೃತ್ತಿ"
+ },
+ "ar": {
+ "string-denied": "مرفوض",
+ "string-apply-accesskey": "س",
+ "string-remove": "إزالة",
+ "string-gears-settings": "إعدادات Gears",
+ "string-apply": "تطبيق",
+ "string-cancel-accesskey": "غ",
+ "string-cancel": "إلغاء",
+ "string-html-title": "إعدادات Gears",
+ "string-allowed": "مسموح",
+ "string-local-storage": "تخزين محلي",
+ "string-nosites": "لا توجد مواقع.",
+ "string-permissions-description": "يوضح الجدول أدناه الأذونات التي قمت بمنحها لكل موقع حاول استخدام Gears.",
+ "string-location-data": "الموقع",
+ "string-version": "Gears الإصدار 0.4.23.0"
+ },
+ "ml": {
+ "string-denied": "തടഞ്ഞു",
+ "string-apply-accesskey": "A",
+ "string-remove": "നീക്കംചെയ്യൂ",
+ "string-gears-settings": "Gears ക്രമീകരണങ്ങള്",
+ "string-apply": "അപേക്ഷിക്കുക",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "റദ്ദാക്കൂ",
+ "string-html-title": "Gears ക്രമീകരണങ്ങള്",
+ "string-allowed": "അനുവദിച്ചു",
+ "string-local-storage": "പ്രാദേശിക ശേഖരം",
+ "string-nosites": "സൈറ്റുകളൊന്നുമില്ല.",
+ "string-permissions-description": "താഴെക്കാണുന്ന പട്ടിക Gears ഉപയോഗിക്കാന് ശ്രമിച്ച ഓരോ സൈറ്റിലേക്കും അനുവദിച്ചവ കാണിക്കുന്നു.",
+ "string-location-data": "ലൊക്കേഷന്",
+ "string-version": "Gears പതിപ്പ് 0.4.23.0"
+ },
+ "cs": {
+ "string-denied": "Zamítnuto",
+ "string-apply-accesskey": "P",
+ "string-remove": "Odebrat",
+ "string-gears-settings": "Nastavení aplikace Gears",
+ "string-apply": "Použít",
+ "string-cancel-accesskey": "R",
+ "string-cancel": "Zrušit",
+ "string-html-title": "Nastavení aplikace Gears",
+ "string-allowed": "Povoleno",
+ "string-local-storage": "Místní úložiště",
+ "string-nosites": "Žádné weby.",
+ "string-permissions-description": "Následující tabulka zobrazuje oprávnění udělená každému webu, který se pokusil použít aplikaci Gears.",
+ "string-location-data": "Lokalita",
+ "string-version": "Gears verze 0.4.23.0"
+ },
+ "et": {
+ "string-denied": "Keelatud",
+ "string-apply-accesskey": "L",
+ "string-remove": "Eemalda",
+ "string-gears-settings": "Gearsi seaded",
+ "string-apply": "Rakenda",
+ "string-cancel-accesskey": "T",
+ "string-cancel": "Tühista",
+ "string-html-title": "Gearsi seaded",
+ "string-allowed": "Lubatud",
+ "string-local-storage": "Kohalik salvestamine",
+ "string-nosites": "Saidid puuduvad.",
+ "string-permissions-description": "Alljärgnev tabel näitab lubasid, mille olete igale Gearsi kasutada püüdnud saidile andnud.",
+ "string-location-data": "Asukoht",
+ "string-version": "Gearsi versioon 0.4.23.0"
+ },
+ "id": {
+ "string-denied": "Ditolak",
+ "string-apply-accesskey": "A",
+ "string-remove": "Hapus",
+ "string-gears-settings": "Pengaturan Gears",
+ "string-apply": "Terapkan",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Batalkan",
+ "string-html-title": "Pengaturan Gears",
+ "string-allowed": "Diizinkan",
+ "string-local-storage": "Penyimpanan lokal",
+ "string-nosites": "Tiada situs.",
+ "string-permissions-description": "Tabel berikut menunjukkan izin yang telah Anda berikan ke setiap situs yang berusaha menggunakan Gears.",
+ "string-location-data": "Lokasi",
+ "string-version": "Versi Gears 0.4.23.0"
+ },
+ "es": {
+ "string-denied": "Denegado",
+ "string-apply-accesskey": "P",
+ "string-remove": "Quitar",
+ "string-gears-settings": "Configuración Gears",
+ "string-apply": "Aplicar",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Cancelar",
+ "string-html-title": "Configuración Gears",
+ "string-allowed": "Permitido",
+ "string-local-storage": "Almacenamiento local",
+ "string-nosites": "No hay sitios.",
+ "string-permissions-description": "La siguiente tabla muestra las autorizaciones que has concedido a cada sitio en el que has intentado usar Gears.",
+ "string-location-data": "Ubicación",
+ "string-version": "Versión Gears 0.4.23.0"
+ },
+ "en-GB": {
+ "string-denied": "Denied",
+ "string-apply-accesskey": "A",
+ "string-remove": "Remove",
+ "string-gears-settings": "Gears Settings",
+ "string-apply": "Apply",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Cancel",
+ "string-html-title": "Gears Settings",
+ "string-allowed": "Allowed",
+ "string-local-storage": "Local storage",
+ "string-nosites": "No sites.",
+ "string-permissions-description": "The table below shows the permissions you have granted to each site that has attempted to use Gears.",
+ "string-location-data": "Location",
+ "string-version": "Gears version 0.4.23.0"
+ },
+ "ru": {
+ "string-denied": "Отказано",
+ "string-apply-accesskey": "A",
+ "string-remove": "Удалить",
+ "string-gears-settings": "Настройки Gears",
+ "string-apply": "Применить",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Отмена",
+ "string-html-title": "Настройки Gears",
+ "string-allowed": "Разрешено",
+ "string-local-storage": "Локальное хранилище",
+ "string-nosites": "Нет сайтов.",
+ "string-permissions-description": "В таблице ниже перечислены разрешения, предоставленные каждому сайту, который пытался воспользоваться Gears.",
+ "string-location-data": "Местонахождение",
+ "string-version": "Gears версии 0.4.23.0"
+ },
+ "nl": {
+ "string-denied": "Geweigerd",
+ "string-apply-accesskey": "A",
+ "string-remove": "Verwijderen",
+ "string-gears-settings": "Instellingen voor Gears",
+ "string-apply": "Toepassen",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Annuleren",
+ "string-html-title": "Instellingen voor Gears",
+ "string-allowed": "Toegestaan",
+ "string-local-storage": "Lokale opslag",
+ "string-nosites": "Geen sites.",
+ "string-permissions-description": "Voor elke site die heeft geprobeerd Gears te gebruiken, worden in de tabel hieronder de machtigingen weergegeven die u aan deze site hebt verleend.",
+ "string-location-data": "Locatie",
+ "string-version": "Gears-versie 0.4.23.0"
+ },
+ "no": {
+ "string-denied": "Ikke tillatt",
+ "string-apply-accesskey": "T",
+ "string-remove": "Fjern",
+ "string-gears-settings": "Innstillinger for Gears",
+ "string-apply": "Bruk",
+ "string-cancel-accesskey": "A",
+ "string-cancel": "Avbryt",
+ "string-html-title": "Innstillinger for Gears",
+ "string-allowed": "Tillatt",
+ "string-local-storage": "Lokal lagring",
+ "string-nosites": "Ingen steder.",
+ "string-permissions-description": "Tabellen nedenfor viser tillatelsene du har gitt til hvert nettsted som har forsøkt å bruke Gears.",
+ "string-location-data": "Område",
+ "string-version": "Gears versjon 0.4.23.0"
+ },
+ "tr": {
+ "string-denied": "Reddedildi",
+ "string-apply-accesskey": "V",
+ "string-remove": "Kaldır",
+ "string-gears-settings": "Gears Ayarları",
+ "string-apply": "Uygula",
+ "string-cancel-accesskey": "a",
+ "string-cancel": "İptal",
+ "string-html-title": "Gears Ayarları",
+ "string-allowed": "İzin verildi",
+ "string-local-storage": "Yerel depolama",
+ "string-nosites": "Site yok.",
+ "string-permissions-description": "Aşağıdaki tablo Gears kullanan her site için sağladığınız izinleri gösterir.",
+ "string-location-data": "Konum",
+ "string-version": "Gears sürüm 0.4.23.0"
+ },
+ "lv": {
+ "string-denied": "Neatļauts",
+ "string-apply-accesskey": "A",
+ "string-remove": "Noņemt",
+ "string-gears-settings": "Gears iestatījumi",
+ "string-apply": "Lietot",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Atcelt",
+ "string-html-title": "Gears iestatījumi",
+ "string-allowed": "Atļauts",
+ "string-local-storage": "Vietējā atmiņa",
+ "string-nosites": "Nav vietņu.",
+ "string-permissions-description": "Tālāk minētā tabula rāda atļaujas, kuras esat piešķīris katrai vietnei, kas mēģināja lietot Gears.",
+ "string-location-data": "Atrašanās vieta",
+ "string-version": "Gears versija 0.4.23.0"
+ },
+ "lt": {
+ "string-denied": "Neleidžiama",
+ "string-apply-accesskey": "A",
+ "string-remove": "Pašalinti",
+ "string-gears-settings": "„Gears“ nuostatos",
+ "string-apply": "Taikyti",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Atšaukti",
+ "string-html-title": "„Gears“ nuostatos",
+ "string-allowed": "Leidžiama",
+ "string-local-storage": "Vietinė saugykla",
+ "string-nosites": "Nėra svetainių.",
+ "string-permissions-description": "Žemiau pateikta lentelėje rodomi leidimai, kuriuos suteikėte kiekvienai svetainei, bandžiusiai naudoti „Gears“.",
+ "string-location-data": "Vietovė",
+ "string-version": "„Gears“ versija 0.4.23.0"
+ },
+ "th": {
+ "string-denied": "ปฏิเสธ",
+ "string-apply-accesskey": "อ",
+ "string-remove": "ลบ",
+ "string-gears-settings": "การตั้งค่า Gears",
+ "string-apply": "ใช้",
+ "string-cancel-accesskey": "ย",
+ "string-cancel": "ยกเลิก",
+ "string-html-title": "การตั้งค่า Gears",
+ "string-allowed": "อนุญาต",
+ "string-local-storage": "พื้นที่เก็บข้อมูลในเครื่อง",
+ "string-nosites": "ไม่มีไซต์",
+ "string-permissions-description": "ตารางต่อไปนี้แสดงสิทธิ์ที่คุณให้แก่แต่ละไซต์ที่พยายามใช้ Gears",
+ "string-location-data": "ตำแหน่ง",
+ "string-version": "Gears เวอร์ชัน 0.4.23.0"
+ },
+ "ro": {
+ "string-denied": "Refuzat",
+ "string-apply-accesskey": "P",
+ "string-remove": "Eliminaţi",
+ "string-gears-settings": "Setări Gears",
+ "string-apply": "Aplicaţi",
+ "string-cancel-accesskey": "A",
+ "string-cancel": "Anulaţi",
+ "string-html-title": "Setări Gears",
+ "string-allowed": "Permis",
+ "string-local-storage": "Stocare locală",
+ "string-nosites": "Niciun site.",
+ "string-permissions-description": "Tabelul de mai jos afişează permisiunile pe care le-aţi acordat fiecărui site care a încercat să utilizeze Gears.",
+ "string-location-data": "Locaţie",
+ "string-version": "Versiune Gears 0.4.23.0"
+ },
+ "is": {
+ "string-denied": "Hafnað",
+ "string-apply-accesskey": "L",
+ "string-remove": "Fjarlægja",
+ "string-gears-settings": "Stillingar Gears",
+ "string-apply": "Nota",
+ "string-cancel-accesskey": "H",
+ "string-cancel": "Hætta við",
+ "string-html-title": "Stillingar Gears",
+ "string-allowed": "Leyft",
+ "string-local-storage": "Staðbundin geymsla",
+ "string-nosites": "Engin vefsvæði.",
+ "string-permissions-description": "Taflan hér fyrir neðan sýnir þær heimildir sem þú hefur veitt hverju svæði sem hefur reynt að nota Gears.",
+ "string-location-data": "Staðsetning",
+ "string-version": "Gears útgáfa 0.4.23.0"
+ },
+ "fil": {
+ "string-denied": "Tinganggihan",
+ "string-apply-accesskey": "P",
+ "string-remove": "Alisin",
+ "string-gears-settings": "Mga Setting ng Gear",
+ "string-apply": "Ilapat",
+ "string-cancel-accesskey": "I",
+ "string-cancel": "Ikansela",
+ "string-html-title": "Mga Setting ng Gear",
+ "string-allowed": "Pinahintulutan",
+ "string-local-storage": "Lokal na imbakan",
+ "string-nosites": "Walang mga site.",
+ "string-permissions-description": "Ang talahanayan sa ibaba ay nagpapakita ng mga pahintulot na naibigay mo sa bawat site na sinubukang gumamit ng Mga Gear.",
+ "string-location-data": "Lokasyon",
+ "string-version": "bersyon ng Mga Gear 0.4.23.0"
+ },
+ "ta": {
+ "string-denied": "அனுமதி மறுத்தது",
+ "string-apply-accesskey": "A",
+ "string-remove": "அகற்று",
+ "string-gears-settings": "Gears அமைப்புகள்",
+ "string-apply": "பயன்படுத்து",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "ரத்துசெய்",
+ "string-html-title": "Gears அமைப்புகள்",
+ "string-allowed": "அனுமதிக்கப்பட்டது",
+ "string-local-storage": "உள்ளக சேமிப்பிடம்",
+ "string-nosites": "தளங்கள் இல்லை.",
+ "string-permissions-description": "கீழே உள்ள அட்டவணை Gears ஐப் பயன்படுத்த ஒவ்வொரு தளங்களுக்கும் நீங்கள் அளித்த அனுமதிகளை காட்டுகிறது.",
+ "string-location-data": "இருப்பிடம்",
+ "string-version": "Gears பதிப்பு 0.4.23.0"
+ },
+ "fr": {
+ "string-denied": "Refusé",
+ "string-apply-accesskey": "A",
+ "string-remove": "Supprimer",
+ "string-gears-settings": "Paramètres de Google Gears",
+ "string-apply": "Appliquer",
+ "string-cancel-accesskey": "U",
+ "string-cancel": "Annuler",
+ "string-html-title": "Paramètres de Google Gears",
+ "string-allowed": "Autorisé",
+ "string-local-storage": "Stockage local",
+ "string-nosites": "Aucun site.",
+ "string-permissions-description": "Le tableau ci-dessous montre les autorisations que vous avez accordées à chaque site ayant tenté d'utiliser Google Gears.",
+ "string-location-data": "Emplacement",
+ "string-version": "Version 0.4.23.0 de Google Gears"
+ },
+ "bg": {
+ "string-denied": "Отказан",
+ "string-apply-accesskey": "A",
+ "string-remove": "Премахване",
+ "string-gears-settings": "Настройки на Gears",
+ "string-apply": "Прилагане",
+ "string-cancel-accesskey": "С",
+ "string-cancel": "Отказ",
+ "string-html-title": "Настройки на Gears",
+ "string-allowed": "Разрешен",
+ "string-local-storage": "Локално хранилище",
+ "string-nosites": "Няма сайтове.",
+ "string-permissions-description": "В таблицата по-долу са показани дадените от вас разрешения на всеки сайт, който се е опитал да използва Gears.",
+ "string-location-data": "Местоположение",
+ "string-version": "Версия 0.4.23.0 на Gears"
+ },
+ "uk": {
+ "string-denied": "Відхилено",
+ "string-apply-accesskey": "A",
+ "string-remove": "Видалити",
+ "string-gears-settings": "Налаштування Gears",
+ "string-apply": "Застосувати",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Скасувати",
+ "string-html-title": "Налаштування Gears",
+ "string-allowed": "Дозволено",
+ "string-local-storage": "Збереження на локальному диску",
+ "string-nosites": "Сайти відсутні.",
+ "string-permissions-description": "У нижченаведеній таблиці наведено дозволи, надані кожному сайту, який намагається використовувати Gears.",
+ "string-location-data": "Розташування",
+ "string-version": "Gears версії 0.4.23.0"
+ },
+ "hr": {
+ "string-denied": "Odbijeno",
+ "string-apply-accesskey": "A",
+ "string-remove": "Ukloni",
+ "string-gears-settings": "Postavke Gearsa",
+ "string-apply": "Primijeni",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Odustani",
+ "string-html-title": "Postavke Gearsa",
+ "string-allowed": "Dopušteno",
+ "string-local-storage": "Lokalna pohrana",
+ "string-nosites": "Nema web-lokacija.",
+ "string-permissions-description": "U dolje navedenoj tablici navedene su dozvole koje ste dali svakoj web-lokaciji koja je pokušala koristiti Gears.",
+ "string-location-data": "Lokacija",
+ "string-version": "Verzija Gearsa 0.4.23.0"
+ },
+ "bn": {
+ "string-denied": "অস্বীকৃত",
+ "string-apply-accesskey": "A",
+ "string-remove": "সরান",
+ "string-gears-settings": "Gears সেটিংস",
+ "string-apply": "প্রয়োগ",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "বাতিল",
+ "string-html-title": "Gears সেটিংস",
+ "string-allowed": "অনুমতিপ্রাপ্ত",
+ "string-local-storage": "স্থানিয় স্টোরেজ",
+ "string-nosites": "কোন সাইটগুলি ׀",
+ "string-permissions-description": "নিচের সারণিটির অনুসারে আপনি প্রত্যেকটি সাইটটিকে অনুমতি দিয়েছেন যা Gears ব্যবহার করতে চেয়েছে ׀",
+ "string-location-data": "অবস্থান",
+ "string-version": "Gears সংস্করণ 0.4.23.0"
+ },
+ "en-US": {
+ "string-denied": "Denied",
+ "string-apply-accesskey": "A",
+ "string-remove": "Remove",
+ "string-gears-settings": "Gears Settings",
+ "string-apply": "Apply",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Cancel",
+ "string-html-title": "Gears Settings",
+ "string-allowed": "Allowed",
+ "string-local-storage": "Local storage",
+ "string-nosites": "No sites.",
+ "string-permissions-description": "The table below shows the permissions you have granted to each site that has attempted to use Gears.",
+ "string-location-data": "Location",
+ "string-version": "Gears version 0.4.23.0"
+ },
+ "da": {
+ "string-denied": "Afvist",
+ "string-apply-accesskey": "I",
+ "string-remove": "Fjern",
+ "string-gears-settings": "Indstillinger til Gears",
+ "string-apply": "Anvend",
+ "string-cancel-accesskey": "A",
+ "string-cancel": "Annuller",
+ "string-html-title": "Indstillinger til Gears",
+ "string-allowed": "Tilladt",
+ "string-local-storage": "Lokalt lager",
+ "string-nosites": "Ingen websteder.",
+ "string-permissions-description": "Nedenstående tabel indeholder en oversigt over de tilladelser, du har givet til de websteder, der har forsøgt at få adgang til at bruge Gears.",
+ "string-location-data": "Placering",
+ "string-version": "Gears-version PRODUKT_VERSION"
+ },
+ "fa": {
+ "string-denied": "مردود",
+ "string-apply-accesskey": "مجاز",
+ "string-remove": "حذف",
+ "string-gears-settings": "تنظیمات Gears",
+ "string-apply": "اعمال",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "لغو",
+ "string-html-title": "تنظیمات Gears",
+ "string-allowed": "مجاز",
+ "string-local-storage": "مکان ذخیره",
+ "string-nosites": "سایتی وجود ندارد.",
+ "string-permissions-description": "جدول زیر مجوزهای شما برای دستیابی به سایتهایی که از Gears استفاده می کند را نشان می دهد.",
+ "string-location-data": "موقعیت",
+ "string-version": "Gears نسخه 0.4.23.0"
+ },
+ "hi": {
+ "string-denied": "अस्वीकृत",
+ "string-apply-accesskey": "अनुमति दें",
+ "string-remove": "निकालें",
+ "string-gears-settings": "Gears सेटिंग्स",
+ "string-apply": "लागू करें",
+ "string-cancel-accesskey": "रद्द करें",
+ "string-cancel": "रद्द करें",
+ "string-html-title": "Gears सेटिंग्स",
+ "string-allowed": "स्वीकार्य",
+ "string-local-storage": "स्थानीय संग्रहण",
+ "string-nosites": "कोई साइट नहीं.",
+ "string-permissions-description": "नीचे दी गई टेबल वे अनुमतियाँ दिखाती है जो आपने उन साइटों को दी है जिन्होंने Gears का उपयोग करने का प्रयास किया है.",
+ "string-location-data": "स्थिति",
+ "string-version": "Gears संस्करण 0.4.23.0"
+ },
+ "pt-BR": {
+ "string-denied": "Negado",
+ "string-apply-accesskey": "P",
+ "string-remove": "Remover",
+ "string-gears-settings": "Configurações do Gears",
+ "string-apply": "Aplicar",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Cancelar",
+ "string-html-title": "Configurações do Gears",
+ "string-allowed": "Permitido",
+ "string-local-storage": "Armazenamento local",
+ "string-nosites": "Nenhum site.",
+ "string-permissions-description": "A tabela abaixo mostra as permissões concedidas por você a cada site que tentou usar o Gears.",
+ "string-location-data": "Local",
+ "string-version": "Gears versão 0.4.23.0"
+ },
+ "fi": {
+ "string-denied": "Estetty",
+ "string-apply-accesskey": "A",
+ "string-remove": "Poista",
+ "string-gears-settings": "Gearsin asetukset",
+ "string-apply": "Ota käyttöön",
+ "string-cancel-accesskey": "P",
+ "string-cancel": "Peruuta",
+ "string-html-title": "Gearsin asetukset",
+ "string-allowed": "Sallittu",
+ "string-local-storage": "Paikallinen tallennus",
+ "string-nosites": "Ei sivustoja.",
+ "string-permissions-description": "Oheisesta taulukosta näet luvat, jotka olet myöntänyt jokaiselle sivustolle, joka on yrittänyt käyttää Gearsia.",
+ "string-location-data": "Sijainti",
+ "string-version": "Gearsin versio 0.4.23.0"
+ },
+ "hu": {
+ "string-denied": "Megtagadva",
+ "string-apply-accesskey": "E",
+ "string-remove": "Eltávolítás",
+ "string-gears-settings": "A Szinkron beállításai",
+ "string-apply": "OK",
+ "string-cancel-accesskey": "M",
+ "string-cancel": "Mégse",
+ "string-html-title": "A Szinkron beállításai",
+ "string-allowed": "Engedélyezve",
+ "string-local-storage": "Helyi tárolás",
+ "string-nosites": "Nincsenek webhelyek.",
+ "string-permissions-description": "Az alábbi táblázat azt mutatja, hogy a Szinkron használatát megkísérlő webhelyeknek milyen engedélyeket adott.",
+ "string-location-data": "Hely",
+ "string-version": "A Szinkron verziószáma: 0.4.23.0"
+ },
+ "ja": {
+ "string-denied": "拒否",
+ "string-apply-accesskey": "A",
+ "string-remove": "削除",
+ "string-gears-settings": "Gears の設定",
+ "string-apply": "適用",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "キャンセル",
+ "string-html-title": "Gears の設定",
+ "string-allowed": "許可",
+ "string-local-storage": "ローカル保存",
+ "string-nosites": "サイトがありません。",
+ "string-permissions-description": "下記の表は、Gears を使用しようとした各サイトに与えた権限を示しています。",
+ "string-location-data": "場所",
+ "string-version": "Gears バージョン 0.4.23.0"
+ },
+ "he": {
+ "string-denied": "נדחה",
+ "string-apply-accesskey": "א",
+ "string-remove": "הסר",
+ "string-gears-settings": "הגדרות Gears",
+ "string-apply": "החל",
+ "string-cancel-accesskey": "ביטול",
+ "string-cancel": "ביטול",
+ "string-html-title": "הגדרות Gears",
+ "string-allowed": "מותר",
+ "string-local-storage": "אחסון מקומי",
+ "string-nosites": "ללא אתרים.",
+ "string-permissions-description": "הטבלה שלהלן מראה את ההרשאות שהענקת לכל אתר שניסה להשתמש ב-Gears.",
+ "string-location-data": "מיקום",
+ "string-version": "Gears בגרסה 0.4.23.0"
+ },
+ "te": {
+ "string-denied": "అనుమతి లేని",
+ "string-apply-accesskey": "A",
+ "string-remove": "తొలగించు",
+ "string-gears-settings": "Gears సెట్టింగులు",
+ "string-apply": "వర్తించు",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "రద్దు చెయ్యి",
+ "string-html-title": "Gears సెట్టింగులు",
+ "string-allowed": "అనుమతించబడిన",
+ "string-local-storage": "స్థానిక నిల్వ",
+ "string-nosites": "సైట్లు లేవు.",
+ "string-permissions-description": "ఈ క్రింది పట్టిక, మీరు ఏ సైట్లకి Gears ఉపయోగించడానికి అనుమతి ఇచ్చారో, చూపిస్తుంది.",
+ "string-location-data": "స్థానం",
+ "string-version": "0.4.23.0 Gears వర్షన్"
+ },
+ "pt-PT": {
+ "string-denied": "Negado",
+ "string-apply-accesskey": "P",
+ "string-remove": "Remover",
+ "string-gears-settings": "Definições do Gears",
+ "string-apply": "Aplicar",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Cancelar",
+ "string-html-title": "Definições do Gears",
+ "string-allowed": "Permitido",
+ "string-local-storage": "Armazenamento local",
+ "string-nosites": "Não existem sites.",
+ "string-permissions-description": "A tabela abaixo mostra as autorizações que concedeu a cada site que tentou utilizar o Gears.",
+ "string-location-data": "Localização",
+ "string-version": "Versão 0.4.23.0 do Gears"
+ },
+ "sr": {
+ "string-denied": "Забрањен приступ",
+ "string-apply-accesskey": "M",
+ "string-remove": "Уклони",
+ "string-gears-settings": "Gears подешавања",
+ "string-apply": "Примени",
+ "string-cancel-accesskey": "О",
+ "string-cancel": "Откажи",
+ "string-html-title": "Gears подешавања",
+ "string-allowed": "Дозвољен приступ",
+ "string-local-storage": "Локални простор за складиштење",
+ "string-nosites": "Ниједан сајт.",
+ "string-permissions-description": "Табела испод приказује дозволе које сте одобрили сваком сајту који је покушао да користи Gears.",
+ "string-location-data": "Локација",
+ "string-version": "Gears верзија 0.4.23.0"
+ },
+ "ko": {
+ "string-denied": "거부됨",
+ "string-apply-accesskey": "A",
+ "string-remove": "삭제",
+ "string-gears-settings": "Gears 설정",
+ "string-apply": "적용",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "취소",
+ "string-html-title": "Gears 설정",
+ "string-allowed": "허용",
+ "string-local-storage": "로컬 저장소",
+ "string-nosites": "사이트가 없습니다.",
+ "string-permissions-description": "다음 표는 Gears를 사용하려 시도했던 각 사이트에 대해 승인받은 사용권한을 보여줍니다.",
+ "string-location-data": "위치",
+ "string-version": "Gears 버전 0.4.23.0"
+ },
+ "sv": {
+ "string-denied": "Nekad",
+ "string-apply-accesskey": "T",
+ "string-remove": "Ta bort",
+ "string-gears-settings": "Inställningar i Gears",
+ "string-apply": "Tillämpa",
+ "string-cancel-accesskey": "A",
+ "string-cancel": "Avbryt",
+ "string-html-title": "Inställningar i Gears",
+ "string-allowed": "Tillåten",
+ "string-local-storage": "Lokal lagring",
+ "string-nosites": "Inga webbplatser",
+ "string-permissions-description": "Tabellen nedan visar beviljade tillstånd för varje webbplats som har försökt att använda Gears.",
+ "string-location-data": "Plats",
+ "string-version": "Gears version 0.4.23.0"
+ },
+ "ur": {
+ "string-denied": "مسترد",
+ "string-apply-accesskey": "A",
+ "string-remove": "حذف کریں",
+ "string-gears-settings": "گیئرز کی ترتیبات",
+ "string-apply": "اطلاق کریں",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "منسوخ",
+ "string-html-title": "گیئرز کی ترتیبات",
+ "string-allowed": "اجازت ہے",
+ "string-local-storage": "ذخیرہ کرنے کی مقامی جگہ",
+ "string-nosites": "کوئی سائٹ نہیں۔",
+ "string-permissions-description": "ذیل کا جدول ان اجازتوں کو دکھاتا ہے جو آپ نے ان سائٹوں کیلئے دی ہیں جنہوں نے گیئرز کے استعمال کی کوشش کی تھی۔",
+ "string-location-data": "جائے وقوع",
+ "string-version": "گیئرز ورژن0.4.23.0"
+ },
+ "sk": {
+ "string-denied": "Odmietnuté",
+ "string-apply-accesskey": "P",
+ "string-remove": "Odstrániť",
+ "string-gears-settings": "Nastavenia rozšírenia Gears",
+ "string-apply": "Použiť",
+ "string-cancel-accesskey": "R",
+ "string-cancel": "Zrušiť",
+ "string-html-title": "Nastavenia rozšírenia Gears",
+ "string-allowed": "Povolené",
+ "string-local-storage": "Lokálny ukladací priestor",
+ "string-nosites": "Žiadne lokality.",
+ "string-permissions-description": "V nižšie uvedenej tabuľke sú uvedené povolenia, ktoré máte udelené pre všetky lokality, ktoré sa pokúsili použiť rozšírenie Gears.",
+ "string-location-data": "Umiestnenie",
+ "string-version": "Gears, verzia 0.4.23.0"
+ },
+ "zh-CN": {
+ "string-denied": "已拒绝",
+ "string-apply-accesskey": "A",
+ "string-remove": "删除",
+ "string-gears-settings": "Gears 设置",
+ "string-apply": "应用",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "取消",
+ "string-html-title": "Gears 设置",
+ "string-allowed": "允许",
+ "string-local-storage": "本地存储",
+ "string-nosites": "没有网站。",
+ "string-permissions-description": "下表显示了您向试图使用 Gears 的每个网站授予了哪些权限。",
+ "string-location-data": "位置",
+ "string-version": "Gears 版本 0.4.23.0"
+ },
+ "de": {
+ "string-denied": "Verweigert",
+ "string-apply-accesskey": "Z",
+ "string-remove": "Entfernen",
+ "string-gears-settings": "Gears-Einstellungen",
+ "string-apply": "Anwenden",
+ "string-cancel-accesskey": "A",
+ "string-cancel": "Abbrechen",
+ "string-html-title": "Gears-Einstellungen",
+ "string-allowed": "Zulässig",
+ "string-local-storage": "Lokaler Speicherplatz",
+ "string-nosites": "Keine Sites.",
+ "string-permissions-description": "Die untenstehende Tabelle zeigt die Berechtigungen an, die Sie jeder Site gewährt haben, die versucht hat, Gears zu verwenden.",
+ "string-location-data": "Speicherort",
+ "string-version": "Gears-Version PRODUKT_VERSION"
+ },
+ "pl": {
+ "string-denied": "Zabroniono",
+ "string-apply-accesskey": "Z",
+ "string-remove": "Usuń",
+ "string-gears-settings": "Ustawienia wtyczki Gears",
+ "string-apply": "Zastosuj",
+ "string-cancel-accesskey": "A",
+ "string-cancel": "Anuluj",
+ "string-html-title": "Ustawienia wtyczki Gears",
+ "string-allowed": "Zezwolono",
+ "string-local-storage": "Przechowywanie danych lokalnie",
+ "string-nosites": "Brak witryn.",
+ "string-permissions-description": "Poniższa tabela zawiera uprawnienia, jakie przyznano każdej z witryn próbujących użyć wtyczki Gears.",
+ "string-location-data": "Lokalizacja",
+ "string-version": "Gears 0.4.23.0"
+ },
+ "ms": {
+ "string-denied": "Ditolak",
+ "string-apply-accesskey": "N",
+ "string-remove": "Keluarkan",
+ "string-gears-settings": "Tetapan Gear",
+ "string-apply": "Gunakan",
+ "string-cancel-accesskey": "B",
+ "string-cancel": "Batal",
+ "string-html-title": "Tetapan Gear",
+ "string-allowed": "Dibenarkan",
+ "string-local-storage": "Penyimpanan setempat",
+ "string-nosites": "Tiada tapak.",
+ "string-permissions-description": "Jadual di bawah menunjukkan kebenaran yang telah anda berikan kepada setiap tapak yang telah cuba menggunakan Gear.",
+ "string-location-data": "Lokasi",
+ "string-version": "Versi Gear 0.4.23.0"
+ },
+ "sl": {
+ "string-denied": "Zavrnjeno",
+ "string-apply-accesskey": "A",
+ "string-remove": "Odstrani",
+ "string-gears-settings": "Nastavitve programa Gears",
+ "string-apply": "Uporabi",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Prekliči",
+ "string-html-title": "Nastavitve programa Gears",
+ "string-allowed": "Dovoljeno",
+ "string-local-storage": "Lokalna shramba",
+ "string-nosites": "Ni spletnih mest.",
+ "string-permissions-description": "V spodnji tabeli so prikazana dovoljenja, ki ste jih podelili vsem spletnim mestom, ki so poskusila uporabljati program Gears.",
+ "string-location-data": "Lokacija",
+ "string-version": "Različica programa Gears 0.4.23.0"
+ },
+ "mr": {
+ "string-denied": "अननुमत",
+ "string-apply-accesskey": "A",
+ "string-remove": "काढा",
+ "string-gears-settings": "Gears सेटिंग्ज",
+ "string-apply": "लागू करा",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "रद्द करा",
+ "string-html-title": "Gears सेटिंग्ज",
+ "string-allowed": "अनुमत",
+ "string-local-storage": "स्थानिक संचय",
+ "string-nosites": "साइट नाहीत.",
+ "string-permissions-description": "निम्न सारणी Gears वापरण्याचा प्रयत्न केलेल्या प्रत्येक साइटकरिता आपल्याकडे किती परवानग्या आहेत हे दर्शविते.",
+ "string-location-data": "स्थान",
+ "string-version": "Gears आवृत्ती 0.4.23.0"
+ },
+ "or": {
+ "string-denied": "ମନାକରନ୍ତୁ",
+ "string-apply-accesskey": "A",
+ "string-remove": "ଅପସାରଣ",
+ "string-gears-settings": "Gears ସେଟିଂସମୂହ",
+ "string-apply": "ଲାଗୁ କରନ୍ତୁ",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "ବାତିଲ୍",
+ "string-html-title": "Gears ସେଟିଂସମୂହ",
+ "string-allowed": "ଅନୁମତି ପ୍ରାପ୍ତ କରନ୍ତୁ",
+ "string-local-storage": "ସ୍ଥାନୀୟ ସଂଞ୍ଚୟ",
+ "string-nosites": "କୌଣସି ସାଇଟମାନ ନାହିଁ |",
+ "string-permissions-description": "ନିମ୍ନ ସାରଣୀ ଆପଣ ପ୍ରତ୍ୟେକ ସାଇଟକୁ ପ୍ରଦାନ କରିଥିବା ସ୍ବୀକୃତି ଦର୍ଶାଉଛି,ଯାହା Gears ବ୍ୟବହାର କରିବାକୁ ପଦକ୍ଷେପ ନେଇଛି |",
+ "string-location-data": "ସ୍ଥାନ",
+ "string-version": "Gears ସଂସ୍କରଣ 0.4.23.0"
+ }
+};
+
+// Insert all localized strings for the specified locale into the div or span
+// matching the id.
+function loadI18nStrings(locale) {
+ var rtl_languages = ['he', 'ar', 'fa', 'ur'];
+
+ if (!locale) {
+ locale = 'en-US';
+ } else {
+ if (!localized_strings[locale]) {
+ // For xx-YY locales, determine what the base locale is.
+ var base_locale = locale.split('-')[0];
+
+ if (localized_strings[base_locale]) {
+ locale = base_locale;
+ } else {
+ locale = 'en-US';
+ }
+ }
+ }
+
+ var strings = localized_strings[locale];
+
+ // If the specified locale is an right to left language, change the direction
+ // of the page.
+ for (index in rtl_languages) {
+ if (locale == rtl_languages[index]) {
+ document.body.dir = "rtl";
+ break;
+ }
+ }
+
+ // Copy each string to the proper UI element, if it exists.
+ for (name in strings) {
+ if (name == 'string-html-title') {
+ if (!browser.ie_mobile) {
+ // IE Mobile dialogs don't have a title bar.
+ // Furthermore, document.title is undefined in IE Mobile on WinMo 5.
+ // It's also impossible to add properties to the window object.
+ // (see http://code.google.com/apis/gears/mobile.html)
+ document.title = strings[name];
+ }
+ } else {
+ var element = dom.getElementById(name);
+ if (element) {
+ element.innerHTML = strings[name];
+ }
+ }
+ }
+}
+
+
+</script>
+<script>
+ var debug = false;
+ var permissions;
+ var originalPermissions;
+
+ // Must match values in permission_db.h
+ var PERMISSION_NOT_SET = 0;
+ var PERMISSION_ALLOWED = 1;
+ var PERMISSION_DENIED = 2;
+
+ if (debug && browser.ie_mobile) {
+ // Handy for debugging layout.
+ // Remember to remove the pie_dialog object tag above when debugging on
+ // WinMo 5, otherwise the assignement below will fail with "the object does
+ // not support this property or method".
+ pie_dialog = new Object();
+ pie_dialog.IsSmartPhone = function() { return false; };
+ pie_dialog.SetCancelButton = function() {};
+ pie_dialog.SetButton = function() {};
+ pie_dialog.SetButtonEnabled = function() {};
+ pie_dialog.SetScriptContext = function() {};
+ pie_dialog.ResizeDialog = function() {};
+ }
+
+ initDialog();
+
+ initSettings();
+
+ setButtonLabel("string-cancel", "cancel-button", "string-cancel-accesskey");
+ setButtonLabel("string-apply", "confirm-button", "string-apply-accesskey");
+
+ if (!browser.ie_mobile) {
+ CustomButton.initializeAll();
+ if (!browser.android) {
+ initCustomLayout(layoutSettings);
+ }
+ } else {
+ var applyText = dom.getElementById("string-apply");
+ if (applyText) {
+ window.pie_dialog.SetButton(applyText.innerText,
+ "saveAndClose(calculateDiff());");
+ }
+ var cancelText = dom.getElementById("string-cancel");
+ if (cancelText) {
+ window.pie_dialog.SetCancelButton(cancelText.innerText);
+ }
+ window.pie_dialog.SetButtonEnabled(true);
+ }
+
+ // Start out with only cancel enabled, just for clarity.
+ disableButton(dom.getElementById("confirm-button"));
+
+ function initSettings() {
+ var args;
+
+ if (debug) {
+ // Handy for debugging layout:
+ var args = {
+ locale: "en-US",
+ permissions: [
+ {
+ name: "http://www.google.com",
+ localStorage: { permissionState: 1 },
+ locationData: { permissionState: 0 }
+ },
+ {
+ name: "http://www.aaronboodman.com",
+ localStorage: { permissionState: 1 },
+ locationData: { permissionState: 1 }
+ },
+ {
+ name: "http://www.evil.org",
+ localStorage: { permissionState: 2 },
+ locationData: { permissionState: 2 }
+ }
+ ]
+ };
+ } else {
+ args = getArguments();
+ }
+ loadI18nStrings(args.locale);
+ permissions = args.permissions;
+ originalPermissions = cloneObject(permissions);
+
+ updatePermissionsList();
+ }
+
+ function updatePermissionsList() {
+ var table = dom.getElementById('div-permissions');
+
+ var content = "";
+ var isFirstOrigin = true;
+ for (var index = 0; index < permissions.length; index++) {
+ content += renderOrigin(index, permissions[index], isFirstOrigin);
+ isFirstOrigin = false;
+ }
+ if (content == "") {
+ content = "<table style=\"border-style: solid;\"><tbody>";
+ content += "<tr><td class=\"left\"><em>";
+ var noSitesText = dom.getElementById("string-nosites");
+ if (noSitesText) {
+ if (isDefined(typeof noSitesText.innerText)) {
+ content += noSitesText.innerText;
+ } else {
+ content += noSitesText.textContent;
+ }
+ }
+ content += "</em></td><td></td></tr></tbody></table>";
+ }
+ table.innerHTML = content;
+ }
+
+ function renderOrigin(index, originData, isFirstOrigin) {
+ var localStorageContent =
+ renderLocalStorage(index, originData.localStorage);
+ var locationDataContent =
+ renderLocationData(index, originData.locationData);
+
+ // Defend against an origin having no data for any permission class.
+ if (localStorageContent == "" && locationDataContent == "") {
+ return "";
+ }
+ var content = "<table";
+ if (isFirstOrigin) {
+ content += " style=\"border-top-style: solid;\"";
+ }
+ content += "><tbody>";
+ content += "<tr><td class=\"origin-name\">";
+ content += wrapDomain(originData.name);
+ content += "</td><td><a href='#' onclick='handleRemoveClick(";
+ content += index;
+ content += ");'>";
+ content += getString("string-remove");
+ content += "</a></td>";
+ content += "</td></tr></tbody></table>";
+
+ content += "<table class=\"permissions\"><tbody>";
+ if (localStorageContent != "") {
+ content += "<tr>";
+ content += localStorageContent;
+ content += "</tr>";
+ }
+ if (locationDataContent != "") {
+ content += "<tr>";
+ content += locationDataContent;
+ content += "</tr>";
+ }
+ content += "</td></tr></tbody></table>";
+ return content;
+ }
+
+ function renderLocalStorage(index, data) {
+ if (data == null || data.permissionState == PERMISSION_NOT_SET) {
+ return "";
+ }
+ var content = "";
+ content += "<td class=\"left\">";
+ content += getString("string-local-storage");
+ content += "</td><td class=\"right\">";
+ content += renderRadioButtons(index, "localStorage",
+ data.permissionState);
+ content += "</td>";
+ // Permission class-specific content goes here;
+ return content;
+ }
+
+ function renderLocationData(index, data) {
+ if (data == null || data.permissionState == PERMISSION_NOT_SET) {
+ return "";
+ }
+ var content = "";
+ content += "<td class=\"left\">";
+ content += getString("string-location-data");
+ content += "</td><td class=\"right\">";
+ content += renderRadioButtons(index, "locationData",
+ data.permissionState);
+ content += "</td>";
+ // Permission class-specific content goes here;
+ return content;
+ }
+
+ function getString(stringId) {
+ var textElement = dom.getElementById(stringId);
+ if (!isDefined(typeof textElement)) {
+ return "";
+ }
+ return dom.getTextContent(textElement);
+ }
+
+ function renderRadioButtons(index, permissionClass, permissionState) {
+ var radioButtonName = getRadioButtonName(index, permissionClass);
+ var content = "<div class=\"radio-buttons\">";
+ content += "<input type=\"radio\" name=\"" + radioButtonName + "\"";
+ content += " id=\"" + radioButtonName + "-allow\"";
+
+ if (permissionState == PERMISSION_ALLOWED) {
+ content += "checked=\"true\"";
+ }
+ content += " onclick='handleRadioClick(" + index + ", \"" +
+ permissionClass + "\", " + PERMISSION_ALLOWED + ");'>";
+ content += "</input>";
+ content += "<label for=\"" + radioButtonName + "-allow\">";
+ content += getString("string-allowed");
+ content += "</label>";
+
+ content += "<input type=\"radio\" name=\"" + radioButtonName + "\"";
+ content += " id=\"" + radioButtonName + "-deny\"";
+
+ if (permissionState == PERMISSION_DENIED) {
+ content += "checked=\"true\"";
+ }
+ content += " onclick='handleRadioClick(" + index + ", \"" +
+ permissionClass + "\", " + PERMISSION_DENIED + ");'>";
+ content += "</input>";
+ content += "<label for=\"" + radioButtonName + "-deny\">";
+ content += getString("string-denied");
+ content += "</label>";
+ content += "</div>";
+ return content;
+ }
+
+ function getRadioButtonName(index, permissionClass) {
+ return index + "-" + permissionClass + "-radio";
+ }
+
+ function handleRadioClick(index, permissionClass, permissionState) {
+ updatePermission(index, permissionClass, permissionState);
+
+ // Return false to prevent the default link action (scrolling up to top of
+ // page in this case).
+ return false;
+ }
+
+ function handleRemoveClick(index) {
+ updatePermission(index, "localStorage", PERMISSION_NOT_SET);
+ updatePermission(index, "locationData", PERMISSION_NOT_SET);
+ updatePermissionsList();
+
+ // Return false to prevent the default link action (scrolling up to top of
+ // page in this case).
+ return false;
+ }
+
+ function updatePermission(index, permissionClass, permissionState) {
+ if (permissions[index][permissionClass]) {
+ if (permissions[index][permissionClass].permissionState !=
+ permissionState) {
+ permissions[index][permissionClass].permissionState =
+ permissionState;
+ enableButton(dom.getElementById("confirm-button"));
+ }
+ }
+ }
+
+ function layoutSettings(contentHeight) {
+ var content = dom.getElementById("content");
+
+ content.style.height = Math.max(contentHeight, 0) + "px";
+
+ // If a scrollbar is going to be visible, then add some padding between it
+ // and the inner right edge of the content. We don't want to have this in
+ // all the time or else there will be double-padding when the scrollbar
+ // isn't visible.
+ if (content.scrollHeight > contentHeight) {
+ content.style.paddingRight = "1em";
+ } else {
+ content.style.paddingRight = "";
+ }
+ }
+
+ function calculateDiff() {
+ var permissionClasses = ["localStorage", "locationData"];
+ var result = { "modifiedOrigins": [] };
+ for (var index = 0; index < originalPermissions.length; index++) {
+ var originalOriginData = originalPermissions[index];
+ var originData = permissions[index];
+ // Defend against a mis-match between the origins in each list.
+ if (isDefined(typeof originData) &&
+ originData.name == originalOriginData.name) {
+ var modifiedOriginData = new Object();
+ var modified = false;
+ for (var i = 0; i < permissionClasses.length; i++) {
+ var permissionClass = permissionClasses[i];
+ // The data may not include entries for all permission classes.
+ if (isDefined(typeof originalOriginData[permissionClass])) {
+ var originalState =
+ originalOriginData[permissionClass]["permissionState"];
+ var state = originData[permissionClass]["permissionState"];
+ if (originalState != state) {
+ modified = true;
+ modifiedOriginData[permissionClass] = new Object();
+ modifiedOriginData[permissionClass]["permissionState"] = state;
+ }
+ }
+ }
+ if (modified) {
+ modifiedOriginData.name = originData.name;
+ result.modifiedOrigins.push(modifiedOriginData);
+ }
+ }
+ }
+ return result;
+ }
+
+</script>
+</html>
diff --git a/assets/plugins/gears-0.4.23.0/shortcuts_dialog.html b/assets/plugins/gears-0.4.23.0/shortcuts_dialog.html
new file mode 100644
index 00000000..fc68120e
--- /dev/null
+++ b/assets/plugins/gears-0.4.23.0/shortcuts_dialog.html
@@ -0,0 +1,2907 @@
+<!DOCTYPE html>
+
+<!--
+Copyright 2007, The Android Open Source Project
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ 3. Neither the name of Google Inc. nor the names of its contributors may be
+ used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<html>
+<head>
+ <style type="text/css">
+
+
+/*
+Copyright 2007, The Android Open Source Project
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ 3. Neither the name of Google Inc. nor the names of its contributors may be
+ used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+body, html {
+ background-color:white;
+ padding:0;
+ margin:0;
+ height:100%;
+ overflow:hidden;
+ cursor:default;
+
+ /*
+ Makes the text unselectable in mozilla. See html_diaog.js for IE
+ implementation.
+ */
+ -moz-user-select:none;
+}
+
+body, td, input, select, button {
+ font-family:arial,sans-serif;
+ font-size:13px;
+}
+
+p {
+ margin:0;
+ margin-bottom:1em;
+}
+
+a {
+ color:blue;
+ text-decoration:underline;
+}
+
+button {
+ /*
+ It would be better to express margins in ems, but it causes layout
+ artifacts (presumably because of rounding errors) when combined with
+ the -moz-border-radius property used in button.css on Firefox 2.
+
+ Also: bizarely, if I try to use the more natural margin-left:10px, I get
+ a bug where the buttons bounce on IE when you hover over them.
+ */
+ position:relative;
+ margin-right:7px;
+ left:7px;
+}
+
+#head {
+ padding:1em;
+}
+
+#foot {
+
+ /*
+ On Android we do not use position:absolute due to some
+ rendering limitations (absolute positioning works, but
+ the automatic resizing of dialog is confused by the
+ variable content and the display is wrong, while if we
+ remove the absolute positioning, #foot is back in the
+ flow and the dialog displays correctly)
+ */
+
+ background:white;
+ bottom:0;
+ width:100%;
+}
+
+#button-row {
+ margin:1em;
+}
+
+.accesskey {
+ text-decoration: underline;
+
+ /* IE CSS extension */
+ accelerator: true;
+}
+
+ #icon {
+ margin-right:0.5em;
+ }
+
+ #content {
+ margin:0 1em;
+ padding-bottom:1em;
+ }
+
+ #scroll td {
+ padding-top:0;
+ }
+
+ #scroll img {
+ margin-right:1em;
+ }
+
+ #highlightbox {
+ /* for pseudo-rounded corners, also see highlightbox-inner */
+ margin:0 1px;
+ border:solid #e8e8e8;
+ border-width:1px 0;
+ background:#f8f8f8;
+ }
+
+ #highlightbox-inner {
+ margin:0 -1px;
+ border:solid #e8e8e8;
+ border-width:0 1px;
+ padding:1em;
+ }
+
+ #shortcut-name {
+ margin-bottom:2px;
+ font-weight: bold;
+ }
+
+ #shortcut-description {
+ /* We turn this on only when there is a description */
+ display:none;
+ }
+
+ #locations {
+ margin-top:1.2em;
+ display:none;
+ }
+
+ #locations table {
+ margin-top:-2px;
+ margin-left:0.45em;
+ }
+
+ </style>
+</head>
+<body>
+ <!-- This div contains strings that are conditionally used in the UI -->
+ <div id="strings" style="display:none;">
+ <!-- window titles -->
+ <div id="string-title-default"></div>
+ <div id="string-title-simple"></div>
+
+ <!-- headers -->
+ <div id="string-header-desktop"></div>
+ <div id="string-header-wince"></div>
+
+ <!-- buttons -->
+ <div id="string-ok"></div>
+ <div id="string-ok-accesskey"></div>
+ <div id="string-cancel"></div>
+ <div id="string-cancel-accesskey"></div>
+ <div id="string-yes"></div>
+ <div id="string-yes-accesskey"></div>
+ <div id="string-no"></div>
+ <div id="string-no-accesskey"></div>
+ <div id="string-never-allow"></div>
+ <div id="string-never-allow-wince"></div>
+ </div>
+
+ <div id="head">
+ <table width="100%" cellpadding="0" cellspacing="0" border="0">
+ <tr>
+ <td valign="top">
+
+ <img id="icon" src="data:image/png;base64,
+iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAJPElEQVR4Xq3V
+WWyc1d0G8Oecd515Z/csHsfxntiOE8eOMY6dxFkokJCQlACBhEpsvWgLRkAL
+IhRxEYrSpp/6XYCgUokKBQEl0ABR0wrShBAI2R1nM3HseB+Px2PPPu/Mu3aE
+uKkoUhPzuzhXRzp/PUfPOcQ0TcxGrOcJ9Jz6F4YmTNq2yF4r5436aFyrlyz8
+nyQxH6m7aTdETxO+D8UssSxF/0gGc4NCpc3u2VtctvBNb5HjN71XUw8bxAHd
+pPiGkQeg/PADDI5lMZ3QYRUZL2MpqRVIyhpwGwj62OdTObo1l405svEBd9/h
+J7ae3Nd1p6rIP+wA5/tzuKExgKDfsZm32KHIU6AUaGnwiD5b5u3R0787OXVu
+x0kbF3t7ZuyzPZ/ve2HjfySI65SdPOQf7z/UNM87pPg8zi32wI0/J8oINE1F
+NgewLOBxW8GwxnyTUWCqcSycHyT7Du99V9XY1rX37Lh43QPoagrhS689zRnp
+X/p9btjcZUB+CL29vThwPD6USOnHrRbGqCixNLc3ueuK3BpMQ0U0oaJvONvd
+uvaGoVklkJm+6DbU2Aa3rxxy7CL0VBxj4Qz++s/I4ebF1VvKAyRiaEmkMobt
+bO/0Gy0NRZu9bh5XBifVxqXbnm/p2JiZVQtmJs5WhcZHq+XkEAhlYJrAF2cS
+qUX1pV13bVgSqa+yo6bMgSInl57XdOdDM0l9kFIKniWcwDP6rFtQUn/f6QTt
+uPd0z2AMJoGqGYjMqD1Vc8j5zEw/qJGG3UrBsQwMNpjgqHZW1w3MKbYiFTm1
+StOU2Q3Ai3bcdt9LH4iuusczsgxKCSwiYxLCQMmnwbAUDEMg5/PgRDec3qqc
+yXpQ4rejrjj0zME9j+/IyUnrrGrIsDwal3f1GAbReI7FgiqpeTqWWjQxJePK
+cAY9lxO4MmrCJc6UOt3BDoAFIQbmV3mF7qN7num/3F2CAnb610+CwITAsjAB
+GIYBAkBgKN4ZDiFhAttqq4TpqamNcU0PEULHeUJDnEAUs0P2e0saYeaGsLjO
+bjvfl3pDUWnX0IT8JYMcOprLmsTc0T/yzopyNRsFy7CIzOQQy3kO2ezeQRSQ
+qVvbEc2r2D00DsUwANNEhdWCUknEuyMT6GpvRTCT3GparW/LeQXTyWT0QjT9
+SXEt9+jSX1Xvd7iql+ZiF8CyDBQNmJzO64ZJj9ntLt3lCTaLNp89E+3BmXMD
+hcPVzNh4zLQGOh976oW9f0YBSy0WvD86CcNThLtrKkloZIhSm0PvHp/A1rpq
+lCiy17TbdwY710C72IOiaNR7MqFsuJjVXyoOp6Qq/TIEjiKb08FxHMqDFkY3
+yTLK2UGQQiJ0Bf/4fFQfjJa8sG7zI++1+eYYHo9nCN8iXdWlyIHgyboqsJry
+cljROmKqtreIY/9uJVgkSLaHi5evXKGHQ1Cv9oNQin2FZC5l0i0ttzrifAU5
+FovpvpEJ+YjLzup1VVL7onk2QRQYUGLi4pUYDp7zPvvcrv07BUH47mcUZXg8
+2rYEpqY8JFTUPFKzoKG5UhJ3uIo8J731Da/PWd65AqExGCODoIQAuoYGlw1B
+VthOjpEJPYpVGVNbdvOaus6GGtvqyLSyqvdqdoQS8s21TEZzkGyuE98e/h20
+JeAFL8ttiondjrllQDyGIlGA3+GgFkPH1Imv0NdzFgPxJCJZGQalqHLYsCLg
+vosH+ay4j+9sH3PO0z6e+onLZBzzq8RjMLXtn345irc+upr97Pj48bLy8iF8
+D/Liwhp0+jyBSUU52Oh2LbCYOhiOg6pqOBdL4Eg0poRy6gkLy0SKRaGy2W1v
+bnTZITEMcgB0wsM0ga8GRkFX+G9pvW/xp4qsMOevpFc6JYTL6n98ua55vc5y
+Iv4bsmvxfDS6nYjnlc2VkuWdCqedVxUVR6fj2DM8fpwThF8sdDnPcLoKllJr
+QtXur7NL/9fmcVpFlgWVJBT24FTv1zjjLt39xAcf/5S3cPhfUZFSHJ6YQoVV
+PMKKYpSxSgjJORyYmBoRrNI9j61ZeWZzZSmaXHYEBD7b5nG8GlO0rlAub6LA
+SKehxGOAaMHZLw5u7jt11IdrQP82FkFtSTEsBEGbw+knDidGUhlEFfW9wgM0
+XCynYWMoGl0OgFKEZQUNDuktWTcuEYsFBRhMpvHOhcvZ2k13Ple1uDmNa0C3
+LF6A1ZXl0FRlk3tuOavJWaQ0DQ6OO52MxzE2GQGTzYJwPFoDPlxKpgGYikTJ
+JPUUIUcp9g+OZEtuWXf/s7tff8XqcMi4Bux6K+tNDg8+6F+waAcnSchenIRL
+FGDjGL+mqIjBBCQRNosFJ0NhhFUNIsdxAqU+wenGcH8/ZkTp/PYXd77P8zyu
+FQ0lM09xbvcuR209lKEBUBOolKwICMKDedOkeZb75qV89UIf3uwbwl1zi2GD
+2e4sK68DAQopoaS6Jlzk8+N60LCifjAZS0wo3SdBkgmYlMAncmj3OpvSmvYa
+SyCUFwdg6jp+VlOGeRxtopL0srN+AZcfHYbf5USq50xw7PLXDK4DObS6FZNy
+fr+TZdZRSuEROBT6DoYQjGRljGVz3VaG+cjDMqMCpcvsc+Zs8Ta12JT+y1CH
+BwGGweHxMOTWZY9s2/WHV0S7A9eCnLh5KXoTmTVnYsmtGU2HT+SDLW77+haP
+CzaHA9l0BjlNhVDkg628HILNDvlqP5SxYXAcjwIcn0ng9RPdZ188cLitbvkK
+BdeAPTA5g1a34yDgOFiIHIQQDKayXQ6W/e1ClrWypgE7z4ElJrIDV3ApPImR
+VBYCz6FC0iHrBj4eGE413n3v02WNixVcI7bRaYMBoMJmRR4motk8av2+l6Zj
+sdWKad5htYjQczkkpiI4MDmNL6LxbsUw/1Ik8nmJYe6ITk8vkWoX7ChU8FPu
+OlrA/P+SevhFHj6bhKDVgq/jCUQNwENJkUmwXgCg6QaOzSTx4Wj4PZ/TuX5b
+bfWXQYJTvK69Wbduw/sP7Pz9AZc/YOA6sGo2AxMA1Q0IFCghJj4cGERpqf/I
+J+HIJRNmOSVUOh9LxtxWy/YHW5cotkgImqZC1/K4/dHHrtrm1+J6UXHZKlg7
+b4Jl+SqwHStRe/NatN++EcFVPzrfcOvaVk/nmhaho7Dctn7TA1u3Xg3Mmwd/
+RyduXL8BbbdvAmuxYjb+DcasAaYvg7ldAAAAAElFTkSuQmCC
+ ">
+
+ <!-- Some browsers automatically focus the first focusable item. We
+ don't want anything focused, so we add this fake item. -->
+ <a href="#" id="focus-thief"></a>
+ </td>
+ <td id="header" width="100%" valign="middle"></td>
+ </tr>
+ </table>
+ </div>
+
+ <div id="content">
+ <div id="highlightbox">
+ <div id="highlightbox-inner">
+ <div id="scroll">
+ <table cellpadding="0" cellspacing="0" border="0">
+ <tr>
+ <td valign="top"><img id="shortcut-icon" width="32" height="32" src="blank.gif"></td>
+ <td align="left" width="100%" valign="middle">
+ <div id="shortcut-name"></div
+ ><div id="shortcut-description"></div>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </div>
+ </div>
+
+ <div id="locations">
+ <p><span id="string-location-query"></span></p>
+ <div id="locations-windows">
+ <table cellpadding="0" cellspacing="2" border="0">
+ <tr>
+ <!-- NOTE: Confusingly, onclick also gets fired when the checkbox changes via keyboard press. -->
+ <!-- NOTE: The values in the checkboxes correspond to the SHORTCUT_LOCATION_* bitmasks defined in desktop.cc. -->
+ <td valign="middle"><input type="checkbox" id="location-desktop" value="1" onclick="resetConfirmDisabledState()"></td>
+ <td valign="middle"><label for="location-desktop"><span id="string-desktop"></span></label></td>
+ </tr>
+ <tr>
+ <td valign="middle"><input type="checkbox" id="location-startmenu" value="4" onclick="resetConfirmDisabledState()"></td>
+ <td valign="middle"><label for="location-startmenu"><span id="string-startmenu"></span></label></td>
+ </tr>
+ <tr>
+ <td valign="middle"><input type="checkbox" id="location-quicklaunch" value="2" onclick="resetConfirmDisabledState()"></td>
+ <td valign="middle"><label for="location-quicklaunch"><span id="string-quicklaunch"></span></label></td>
+ </tr>
+ </table>
+ </div>
+ <!-- TODO(aa): Support more locations on other platforms, such as dock
+ and applications on OSX? -->
+ </div>
+ </div>
+
+ <div id="foot">
+
+
+ <div id="button-row">
+ <table cellpadding="0" cellspacing="0" border="0">
+ <tr>
+
+ <td width="100%" valign="middle">
+ <a href="#" onclick="denyShortcutPermanently(); return false;" id="deny-permanently-link"></a>
+ </td>
+ <td nowrap="true" align="right" valign="middle">
+ <button id="allow-button" class="custom"
+ onclick="allowShortcutsTemporarily(); return false;"></button
+ ><button id="deny-button" class="custom"
+ onclick="denyShortcutsTemporarily(); return false;"></button>
+ </td>
+
+ </tr>
+ </table>
+ </div>
+ </div>
+
+</body>
+<!--
+We include all files through m4 as the HTML dialog implementation on
+PocketIE does not support callbacks for loading external JavaScript files.
+TODO: find a better way to include scripts for PIE
+-->
+<script>
+/*
+Copyright (c) 2005 JSON.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The Software shall be used for Good, not Evil.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+//Array.prototype.______array = '______array';
+
+var JSON = {
+ org: 'http://www.JSON.org',
+ copyright: '(c)2005 JSON.org',
+ license: 'http://www.crockford.com/JSON/license.html',
+
+ stringify: function (arg) {
+ var c, i, l, s = '', v;
+
+ switch (typeof arg) {
+ case 'object':
+ if (arg) {
+ if (arg.constructor == Array.prototype.constructor) {
+ for (i = 0; i < arg.length; ++i) {
+ v = this.stringify(arg[i]);
+ if (s) {
+ s += ',';
+ }
+ s += v;
+ }
+ return '[' + s + ']';
+ } else if (typeof arg.toString != 'undefined') {
+ for (i in arg) {
+ v = arg[i];
+ if (typeof v != 'undefined' && typeof v != 'function') {
+ v = this.stringify(v);
+ if (s) {
+ s += ',';
+ }
+ s += this.stringify(i) + ':' + v;
+ }
+ }
+ return '{' + s + '}';
+ }
+ }
+ return 'null';
+ case 'number':
+ return isFinite(arg) ? String(arg) : 'null';
+ case 'string':
+ l = arg.length;
+ s = '"';
+ for (i = 0; i < l; i += 1) {
+ c = arg.charAt(i);
+ if (c >= ' ') {
+ if (c == '\\' || c == '"') {
+ s += '\\';
+ }
+ s += c;
+ } else {
+ switch (c) {
+ case '\b':
+ s += '\\b';
+ break;
+ case '\f':
+ s += '\\f';
+ break;
+ case '\n':
+ s += '\\n';
+ break;
+ case '\r':
+ s += '\\r';
+ break;
+ case '\t':
+ s += '\\t';
+ break;
+ default:
+ c = c.charCodeAt();
+ s += '\\u00' + Math.floor(c / 16).toString(16) +
+ (c % 16).toString(16);
+ }
+ }
+ }
+ return s + '"';
+ case 'boolean':
+ return String(arg);
+ default:
+ return 'null';
+ }
+ },
+ parse: function (text) {
+ var at = 0;
+ var ch = ' ';
+
+ function error(m) {
+ throw {
+ name: 'JSONError',
+ message: m,
+ at: at - 1,
+ text: text
+ };
+ }
+
+ function next() {
+ ch = text.charAt(at);
+ at += 1;
+ return ch;
+ }
+
+ function white() {
+ while (ch) {
+ if (ch <= ' ') {
+ next();
+ } else if (ch == '/') {
+ switch (next()) {
+ case '/':
+ while (next() && ch != '\n' && ch != '\r') {}
+ break;
+ case '*':
+ next();
+ for (;;) {
+ if (ch) {
+ if (ch == '*') {
+ if (next() == '/') {
+ next();
+ break;
+ }
+ } else {
+ next();
+ }
+ } else {
+ error("Unterminated comment");
+ }
+ }
+ break;
+ default:
+ error("Syntax error");
+ }
+ } else {
+ break;
+ }
+ }
+ }
+
+ function string() {
+ var i, s = '', t, u;
+
+ if (ch == '"') {
+outer: while (next()) {
+ if (ch == '"') {
+ next();
+ return s;
+ } else if (ch == '\\') {
+ switch (next()) {
+ case 'b':
+ s += '\b';
+ break;
+ case 'f':
+ s += '\f';
+ break;
+ case 'n':
+ s += '\n';
+ break;
+ case 'r':
+ s += '\r';
+ break;
+ case 't':
+ s += '\t';
+ break;
+ case 'u':
+ u = 0;
+ for (i = 0; i < 4; i += 1) {
+ t = parseInt(next(), 16);
+ if (!isFinite(t)) {
+ break outer;
+ }
+ u = u * 16 + t;
+ }
+ s += String.fromCharCode(u);
+ break;
+ default:
+ s += ch;
+ }
+ } else {
+ s += ch;
+ }
+ }
+ }
+ error("Bad string");
+ }
+
+ function array() {
+ var a = [];
+
+ if (ch == '[') {
+ next();
+ white();
+ if (ch == ']') {
+ next();
+ return a;
+ }
+ while (ch) {
+ a.push(value());
+ white();
+ if (ch == ']') {
+ next();
+ return a;
+ } else if (ch != ',') {
+ break;
+ }
+ next();
+ white();
+ }
+ }
+ error("Bad array");
+ }
+
+ function object() {
+ var k, o = {};
+
+ if (ch == '{') {
+ next();
+ white();
+ if (ch == '}') {
+ next();
+ return o;
+ }
+ while (ch) {
+ k = string();
+ white();
+ if (ch != ':') {
+ break;
+ }
+ next();
+ o[k] = value();
+ white();
+ if (ch == '}') {
+ next();
+ return o;
+ } else if (ch != ',') {
+ break;
+ }
+ next();
+ white();
+ }
+ }
+ error("Bad object");
+ }
+
+ function number() {
+ var n = '', v;
+ if (ch == '-') {
+ n = '-';
+ next();
+ }
+ while (ch >= '0' && ch <= '9') {
+ n += ch;
+ next();
+ }
+ if (ch == '.') {
+ n += '.';
+ while (next() && ch >= '0' && ch <= '9') {
+ n += ch;
+ }
+ }
+ if (ch == 'e' || ch == 'E') {
+ n += 'e';
+ next();
+ if (ch == '-' || ch == '+') {
+ n += ch;
+ next();
+ }
+ while (ch >= '0' && ch <= '9') {
+ n += ch;
+ next();
+ }
+ }
+ v = +n;
+ if (!isFinite(v)) {
+ ////error("Bad number");
+ } else {
+ return v;
+ }
+ }
+
+ function word() {
+ switch (ch) {
+ case 't':
+ if (next() == 'r' && next() == 'u' && next() == 'e') {
+ next();
+ return true;
+ }
+ break;
+ case 'f':
+ if (next() == 'a' && next() == 'l' && next() == 's' &&
+ next() == 'e') {
+ next();
+ return false;
+ }
+ break;
+ case 'n':
+ if (next() == 'u' && next() == 'l' && next() == 'l') {
+ next();
+ return null;
+ }
+ break;
+ }
+ error("Syntax error");
+ }
+
+ function value() {
+ white();
+ switch (ch) {
+ case '{':
+ return object();
+ case '[':
+ return array();
+ case '"':
+ return string();
+ case '-':
+ return number();
+ default:
+ return ch >= '0' && ch <= '9' ? number() : word();
+ }
+ }
+
+ return value();
+ }
+};
+
+// Copyright 2008, The Android Open Source Project
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// 3. Neither the name of Google Inc. nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//=============================================================================
+// Various basic JavaScript utilities.
+//=============================================================================
+
+/**
+ * Check that the type is not undefined (we do it here as on
+ * some devices typeof returns unknown instead of undefined...).
+ * We have to pass the evaluation of (typeof elem) (i.e., a string)
+ * to the function rather than simply (element) -- passing an
+ * undefined object would make the function crash on PIE.
+ */
+function isDefined(type) {
+ return type != 'undefined' && type != 'unknown';
+}
+
+/**
+ * Returns true if val is a function.
+ */
+function isFunction(val) {
+ return typeof val == "function";
+}
+
+// TODO(aa): more basic type checking here.
+
+
+/**
+ * Creates a 'bound' function that calls the current function with a preset
+ * 'this' object and set of arguments.
+ */
+Function.prototype.bind = function(obj) {
+ var preArgs = Array.prototype.slice.call(arguments, 1);
+ var self = this;
+ return function() {
+ var postArgs = Array.prototype.slice.call(arguments, 0);
+ var totalArgs = preArgs.concat(postArgs);
+ return self.apply(obj, totalArgs);
+ }
+};
+
+/**
+ * Creates a partially applied function that calls the current function with
+ * a preset set of arguments.
+ */
+Function.prototype.partial = function() {
+ var args = Array.prototype.slice.call(arguments, 0);
+ return this.bind(null, args);
+};
+
+/**
+ * Binds all the methods in obj to obj.
+ */
+function bindMethods(obj) {
+ for (var p in obj) {
+ if (isFunction(obj[p])) {
+ obj[p] = obj[p].bind(obj);
+ }
+ }
+}
+
+
+/**
+ * Trim leading and trailing whitespace from a string.
+ */
+String.prototype.trim = function(str) {
+ return this.replace(/^\s+/, "").replace(/\s+$/, "");
+};
+
+
+/**
+ * Find the index of a given element in an array. Returns -1 if the item does
+ * not exist.
+ */
+Array.prototype.indexOf = function(item) {
+ for (var i = 0; i < this.length; i++) {
+ if (this[i] === item) {
+ return i;
+ }
+ }
+ return -1;
+};
+
+/**
+ * Makes a deep copy of an object.
+ */
+function cloneObject(original) {
+ var newObject = new Object();
+ for (i in original) {
+ if (typeof original[i] == 'object') {
+ newObject[i] = cloneObject(original[i]);
+ }
+ else {
+ newObject[i] = original[i];
+ }
+ }
+ newObject.length = original.length;
+ return newObject;
+}
+
+var browser = {};
+
+(function() {
+ var ua = navigator.userAgent;
+ browser.opera = typeof opera != "undefined";
+ browser.ie = !browser.opera && ua.indexOf("MSIE") > -1;
+ browser.ie_mobile = (ua.indexOf("Windows CE") > -1) &&
+ (ua.indexOf("MSIE") > -1);
+ browser.webkit = ua.indexOf("WebKit") > -1;
+ // WebKit also gives product == "gecko".
+ browser.mozilla = !browser.webkit && navigator.product == "Gecko";
+ browser.safari = ua.indexOf("Safari") > -1;
+ browser.mac = navigator.platform.indexOf("Mac") > -1;
+ browser.linux = navigator.platform.indexOf("Linux") > -1;
+ browser.windows = navigator.platform.indexOf("Win") > -1;
+ browser.android = ua.indexOf("Android") > -1;
+ if (browser.android) {
+ browser.safari = false;
+ }
+ // TODO(aa): Add detection for more browsers, as necessary.
+})();
+
+// Copyright 2008, The Android Open Source Project
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// 3. Neither the name of Google Inc. nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//=============================================================================
+// DOM manipulation utilities.
+// Requires: base.js
+//=============================================================================
+
+var dom = {};
+
+/**
+ * Provides a cross-browser way of getting an element by its id
+ */
+dom.getElementById = function(id) {
+ if (isDefined(typeof document.getElementById)) {
+ return document.getElementById(id);
+ } else if (isDefined(typeof document.all)) {
+ return document.all[id];
+ }
+ throw new Error("Failed to get element by ID.");
+};
+
+/**
+ * Gets the position and dimensions of an element in pixels in relation
+ * to the window origin.
+ */
+dom.getPosition = function(elem) {
+ var ret = { 'left' : 0,
+ 'top' : 0,
+ 'width' : elem.width,
+ 'height' : elem.height };
+
+ if (!elem.offsetParent) return ret;
+
+ var left = 0;
+ var top = 0;
+ while (elem) {
+ left += elem.offsetLeft;
+ top += elem.offsetTop;
+ elem = elem.offsetParent
+ }
+ ret.left = left;
+ ret.top = top;
+ return ret;
+};
+
+/**
+ * Returns the height of the inside of the window.
+ */
+dom.getWindowInnerHeight = function() {
+ if (isDefined(typeof window.innerHeight)) { // Firefox
+ return window.innerHeight;
+ } else if (isDefined(typeof document.body.offsetHeight)) { // IE
+ return document.body.offsetHeight;
+ }
+
+ throw new Error("Could not get windowInnerHeight.");
+};
+
+/**
+ * Add an event listener to an element cross-browser.
+ */
+dom.addEvent = function(element, eventName, handler) {
+ var wrapper = dom._callEventHandler.bind(dom, handler);
+
+ if (isDefined(typeof element.addEventListener)) {
+ // Standards-compatible browsers
+ element.addEventListener(eventName, wrapper, false);
+ } else if (isDefined(typeof element.attachEvent)) {
+ // IE
+ element.attachEvent("on" + eventName, wrapper);
+ } else {
+ throw new Error('Failed to attach event.');
+ }
+};
+
+/**
+ * Private helper for calling event handlers compatibly across browsers.
+ */
+dom._callEventHandler = function(handler, e) {
+ // Older versions of IE don't pass event to the handler.
+ if (!e) e = window.event;
+
+ // TODO(aa): Normalize event object properties.
+
+ return handler(e);
+};
+
+/**
+ * Gets the CSS classes for an element.
+ */
+dom.getClasses = function(elm) {
+ return elm.className.split(/\s+/);
+};
+
+/**
+ * Check is an element has a particular CSS class name.
+ */
+dom.hasClass = function(elm, className) {
+ return this.getClasses(elm).indexOf(className) > -1;
+};
+
+/**
+ * Adds a CSS class to an element. Do nothing if the class already exists.
+ */
+dom.addClass = function(elm, className) {
+ var classes = this.getClasses(elm);
+ if (classes.indexOf(className) > -1) {
+ return;
+ }
+ classes.push(className);
+ elm.className = classes.join(" ");
+};
+
+/**
+ * Removes a CSS class from an element. Do nothing if the class doesn't exist.
+ */
+dom.removeClass = function(elm, className) {
+ var classes = this.getClasses(elm);
+ var index = classes.indexOf(className);
+ if (index == -1) {
+ return;
+ }
+ classes.splice(index, 1);
+ elm.className = classes.join(" ");
+};
+
+/**
+ * Gets the text (non-html) content of an element.
+ */
+dom.getTextContent = function(elm) {
+ if (isDefined(typeof elm.textContent)) {
+ return elm.textContent;
+ } else if (isDefined(typeof elm.innerText)) {
+ return elm.innerText;
+ } else {
+ throw new Error("Could not find a property to get text content.");
+ }
+};
+
+/**
+ * Sets the text (non-html) contents of an element.
+ */
+dom.setTextContent = function(elm, text) {
+ if (isDefined(typeof elm.textContent)) {
+ elm.textContent = text;
+ } else if (isDefined(typeof elm.innerText)) {
+ elm.innerText = text;
+ } else {
+ throw new Error("Could not find a property to set text content.");
+ }
+};
+
+// Copyright 2007, The Android Open Source Project
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// 3. Neither the name of Google Inc. nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * Initialize the base functionality of the dialog.
+ */
+function initDialog() {
+ if (!browser.ie_mobile) {
+ dom.addEvent(document, "keyup", handleKeyUp);
+ } else {
+ var buttonRowElem = null;
+ if (window.pie_dialog.IsSmartPhone()) {
+ buttonRowElem = dom.getElementById("button-row-smartphone");
+ } else {
+ buttonRowElem = dom.getElementById("button-row");
+ }
+ if (buttonRowElem) {
+ buttonRowElem.style.display = 'block';
+ }
+ }
+ if (browser.ie_mobile) {
+ window.pie_dialog.SetScriptContext(window);
+ window.pie_dialog.ResizeDialog();
+ }
+}
+
+/**
+ * Set the label of an input button, and optionally, its accesskey.
+ */
+function setButtonLabel(textID, elemID, accessKeyID) {
+ var textElem = dom.getElementById(textID);
+ var buttonElem = dom.getElementById(elemID);
+ if (textElem == null || buttonElem == null) {
+ return;
+ }
+
+ var accessKeyElem = null;
+ if (isDefined(typeof accessKeyID)) {
+ accessKeyElem = dom.getElementById(accessKeyID);
+ }
+
+ // This function works for two different kinds of buttons. Simple buttons
+ // based on the <input type="button"> tag, and custom buttons based on a
+ // <button> tag with the css class "custom".
+ // Note that on Windows Mobile 5, the tagName property is not defined.
+ // We can therefore safely assume that the converse is also true:
+ // if tagName is not defined, then the platform must be Windows Mobile 5.
+ if (!isDefined(typeof buttonElem.tagName) ||
+ buttonElem.tagName.toLowerCase() == "input") {
+ buttonElem.value = dom.getTextContent(textElem).trim();
+ if (accessKeyElem != null) {
+ buttonElem.accessKey = dom.getTextContent(accessKeyElem).trim();
+ }
+ } else if (buttonElem.tagName.toLowerCase() == "button") {
+ var text = dom.getTextContent(textElem).trim();
+
+ if (accessKeyElem != null) {
+ // Some browsers use the accessKey attribute of the the anchor tag.
+ var accessKey = dom.getTextContent(accessKeyElem).trim();
+ buttonElem.accessKey = accessKey;
+
+ // Find the first matching character in the text and mark it.
+ // Note: this form of String.replace() only replaces the first occurence.
+ text = text.replace(accessKey,
+ "<span class='accesskey'>" + accessKey + "</span>");
+ }
+
+ buttonElem.innerHTML = text;
+ } else {
+ throw new Error("Unexpected button tag name: " + buttonElem.tagName);
+ }
+}
+
+/**
+ * Allows a dialog to do custom layout when the window changes sizes. The
+ * provided function will be called with the height of the content area when the
+ * dialog should relayout.
+ */
+function initCustomLayout(layoutFunction) {
+ function doLayout() {
+ layoutFunction(getContentHeight());
+ }
+
+ doLayout();
+
+ // We do an additional layout in onload because sometimes things aren't
+ // stabilized when the first doLayout() is called above.
+ dom.addEvent(window, "load", doLayout);
+ dom.addEvent(window, "resize", doLayout);
+
+ // Mozilla doesn't fire continuous events during resize, so if we want to get
+ // somewhat smooth resizing, we need to run our own timer loop. This still
+ // doesn't look perfect, because the timer goes off out of sync with the
+ // browser's internal reflow, but I think it's better than nothing.
+ // TODO(aa): Keep looking for a way to get an event for each reflow, like IE.
+ if (browser.mozilla) {
+ var lastHeight = -1;
+
+ function maybeDoLayout() {
+ var currentHeight = dom.getWindowInnerHeight();
+ if (currentHeight != lastHeight) {
+ lastHeight = currentHeight;
+ doLayout();
+ }
+ }
+
+ window.setInterval(maybeDoLayout, 30);
+ }
+}
+
+/**
+ * Get the JSON-formatted arguments that were passed by the caller.
+ */
+function getArguments() {
+ var argsString;
+ if (browser.ie_mobile) {
+ argsString = window.pie_dialog.GetDialogArguments();
+ } else if (browser.ie) {
+ argsString = window.external.GetDialogArguments();
+ } else if (browser.mozilla) {
+ argsString = getFirefoxArguments(window.arguments[0]);
+ } else if (browser.safari) {
+ argsString = window.gears_dialogArguments;
+ } else if (browser.android) {
+ argsString = "" + window.bridge.getDialogArguments();
+ }
+
+ if (typeof argsString == "string") {
+ return JSON.parse(argsString);
+ } else {
+ return null;
+ }
+}
+
+/**
+ * Helper used by getArguments(). Getting the arguments in the right type is
+ * more involved in Firefox.
+ */
+function getFirefoxArguments(windowArguments) {
+ var Ci = Components.interfaces;
+ windowArguments.QueryInterface(Ci.nsIProperties);
+ var supportsString =
+ windowArguments.get("dialogArguments", Ci.nsISupportsString);
+ return supportsString.data;
+}
+
+/**
+ * Close the dialog, sending the specified result back to the caller.
+ */
+function saveAndClose(resultObject) {
+ var resultString = JSON.stringify(resultObject);
+ if (browser.ie_mobile) {
+ window.pie_dialog.CloseDialog(resultString);
+ } else if (browser.ie) {
+ window.external.CloseDialog(resultString);
+ } else if (browser.mozilla) {
+ saveFirefoxResults(resultString);
+ window.close();
+ } else if (browser.safari) {
+ window.gears_dialog.setResults(resultString);
+ } else if (browser.android) {
+ window.bridge.closeDialog(resultString);
+ }
+}
+
+/**
+ * Helper used by endDialog() for Firefox.
+ */
+function saveFirefoxResults(resultString) {
+ var Cc = Components.classes;
+ var Ci = Components.interfaces;
+
+ var props = window.arguments[0].QueryInterface(Ci.nsIProperties);
+ var supportsString = Cc["@mozilla.org/supports-string;1"]
+ .createInstance(Ci.nsISupportsString);
+
+ supportsString.data = resultString;
+ props.set("dialogResult", supportsString);
+}
+
+/**
+ * Returns the height of the content area of the dialog.
+ */
+function getContentHeight() {
+ var head = dom.getElementById("head");
+ var foot = dom.getElementById("foot");
+ return dom.getWindowInnerHeight() - head.offsetHeight - foot.offsetHeight;
+}
+
+/**
+ * For some reason ESC doesn't work on HTML dialogs in either Firefox or IE. So
+ * we implement it manually.
+ */
+function handleKeyUp(e) {
+ var ESC_KEY_CODE = 27;
+
+ if (e.keyCode == ESC_KEY_CODE) {
+ saveAndClose(null);
+ }
+}
+
+/**
+ * Disables a button in the right way, whether it is normal or custom.
+ */
+function disableButton(buttonElem) {
+ buttonElem.disabled = true;
+
+ if (browser.ie_mobile) {
+ window.pie_dialog.SetButtonEnabled(false);
+ }
+
+ if (!isDefined(typeof buttonElem.tagName) ||
+ buttonElem.tagName.toLowerCase() == "input") {
+ buttonElem.style.color = "gray";
+ } else if (buttonElem.tagName.toLowerCase() == "button") {
+ dom.addClass(buttonElem, "disabled");
+ } else {
+ throw new Error("Unexpected tag name: " + buttonElem.tagName);
+ }
+}
+
+/**
+ * Enables a button in the right way, whether it is normal or custom.
+ */
+function enableButton(buttonElem) {
+ buttonElem.disabled = false;
+
+ if (browser.ie_mobile) {
+ window.pie_dialog.SetButtonEnabled(true);
+ }
+
+ if (!isDefined(typeof buttonElem.tagName) ||
+ buttonElem.tagName.toLowerCase() == "input") {
+ buttonElem.style.color = "black";
+ } else if (buttonElem.tagName.toLowerCase() == "button") {
+ dom.removeClass(buttonElem, "disabled");
+ } else {
+ throw new Error("Unexpected tag name: " + buttonElem.tagName);
+ }
+}
+
+/**
+ * Returns a wrapped domain (useful for small screens dialogs,
+ * e.g. windows mobile devices)
+ */
+function wrapDomain(str) {
+ if (!browser.ie_mobile) {
+ return str;
+ }
+
+ var max = 20;
+ var url;
+ var scheme_start = str.indexOf("://");
+ var scheme = "";
+
+ if (scheme_start != -1) {
+ scheme = str.substring(0, scheme_start);
+ scheme += "://";
+ // there's automatically an hyphenation
+ // point used by the browser after http://
+ // so we only have to hyphenate the
+ // rest of the string
+ url = str.substring(scheme.length);
+ } else {
+ url = str;
+ }
+
+ // We hyphenate the string on every dot
+ var components = url.split(".");
+ if (components.length < 1) {
+ return str;
+ }
+
+ var content = components[0];
+ var len = content.length;
+ for (var i=1; i < components.length; i++) {
+ var elem = components[i];
+ content += ".";
+ len++;
+ if (len + elem.length > max) {
+ content += "<br>";
+ len = 0;
+ }
+ content += elem;
+ len += elem.length;
+ }
+ return scheme + content;
+}
+
+/**
+ * Resizes the dialog to fit the content size.
+ */
+function resizeDialogToFitContent(opt_minDialogInnerHeight) {
+ // This does not work on PIE (no height measurement)
+ if (browser.ie_mobile) {
+ window.pie_dialog.ResizeDialog();
+ return;
+ }
+ // window.resize() is not working on Android, we bail out.
+ if (browser.android) {
+ return;
+ }
+
+ // Resize the window to fit
+ var contentDiv = dom.getElementById("content");
+ var contentHeightProvided = getContentHeight();
+ var contentHeightDesired = contentDiv.offsetHeight;
+
+ var dialogHeightProvided = dom.getWindowInnerHeight();
+ var dialogHeightDesired =
+ dialogHeightProvided + (contentHeightDesired - contentHeightProvided);
+
+ var minDialogHeight = opt_minDialogInnerHeight || 0;
+ var maxDialogHeight = 400; // arbitrary max height for a dialog to resize to
+
+ var targetHeight = Math.max(dialogHeightDesired, minDialogHeight);
+ targetHeight = Math.min(dialogHeightDesired, maxDialogHeight);
+
+ if (targetHeight != dialogHeightProvided) {
+ var dy = targetHeight - dialogHeightProvided;
+ window.resizeBy(0, dy);
+ }
+}
+
+/**
+ * Safari running on OSX 10.4 can't load images dynamically from the network,
+ * this helper function calls gears-specific code to work around this
+ * limitation.
+ */
+function loadImage(elem, image_url) {
+ if (browser.safari) {
+ var position = dom.getPosition(elem);
+ window.gears_dialog.loadImageIntoElement(image_url, position.top,
+ position.left,
+ position.width,
+ position.height);
+ } else {
+ elem.src = image_url;
+ }
+}
+
+// Copyright 2008, The Android Open Source Project
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// 3. Neither the name of Google Inc. nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//=============================================================================
+// Implements the behavior of our custom buttons. There is no native support in
+// IE for rounded corners on HTML elements, so this script also implements a
+// hack to simulate them in that browser.
+//
+// Requires: base.js, dom.js
+//=============================================================================
+
+/**
+ * Constructor.
+ */
+function CustomButton(htmlButton) {
+ bindMethods(this);
+
+ this._focused = false;
+ this._htmlButton = htmlButton;
+
+ if (!document.all) { // not IE
+ dom.addEvent(htmlButton, "mouseover", this._handleMouseOver);
+ dom.addEvent(htmlButton, "mouseout", this._handleMouseOut);
+ } else { // IE
+ // mouseover/mouseout behave badly with nested elements in IE. Luckily,
+ // mouseenter/mouseleave are provided and have the equivalent semantics.
+ dom.addEvent(htmlButton, "mouseenter", this._handleMouseOver);
+ dom.addEvent(htmlButton, "mouseleave", this._handleMouseOut);
+
+ // We also handle focus and blur in IE so that we can update the corners
+ // to match the black border IE provides.
+ dom.addEvent(htmlButton, "focusin", this._handleFocus);
+ dom.addEvent(htmlButton, "blur", this._handleBlur);
+
+ dom.addClass(htmlButton, "ie");
+ this._createCorners();
+ }
+
+ // Limit button widths to a minimum of 60px. They look funny skinnier.
+ if (htmlButton.offsetWidth < 60) {
+ htmlButton.style.width = "60px";
+ }
+
+ htmlButton.style.visibility = "visible";
+}
+
+/**
+ * Initializes all the custom buttons on the page.
+ */
+CustomButton.initializeAll = function() {
+ var buttons = document.getElementsByTagName("button");
+ for (var i = 0, button; button = buttons[i]; i++) {
+ if (dom.hasClass(button, "custom")) {
+ new CustomButton(button);
+ }
+ }
+};
+
+/**
+ * Highlights the button on mouseover. Note that this gets called multiple
+ * times while the mouse goes over sub elements inside the button.
+ */
+CustomButton.prototype._handleMouseOver = function() {
+ if (!this._htmlButton.disabled && !this._focused) {
+ dom.addClass(this._htmlButton, "hover");
+ this._setCornerColor("blue");
+ }
+};
+
+/**
+ * Undoes the highlighting of the button on mouseout. Note that this gets called
+ * multiple times when the mouse moves out of sub elements, even if it is still
+ * over the button. That is OK, as long as there is immediately a mouseover
+ * event.
+ */
+CustomButton.prototype._handleMouseOut = function() {
+ if (!this._htmlButton.disabled && !this._focused) {
+ dom.removeClass(this._htmlButton, "hover");
+ this._setCornerColor("grey");
+ }
+};
+
+/**
+ * Highlights the button on focus. Currently only used for IE.
+ * TODO(aa): The black highlight looks cool. Consider for other browsers.
+ */
+CustomButton.prototype._handleFocus = function() {
+ dom.removeClass(this._htmlButton, "hover");
+ this._setCornerColor("black");
+ this._focused = true;
+};
+
+/**
+ * Undoes the highlighting of the button on blur.
+ */
+CustomButton.prototype._handleBlur = function() {
+ this._setCornerColor("grey");
+ this._focused = false;
+};
+
+/**
+ * Helper to create the graphics that make the button have rounded corners in
+ * IE.
+ */
+CustomButton.prototype._createCorners = function() {
+ // Making the button position:relative makes it possible to position things
+ // inside it relative to it's border.
+ this._htmlButton.style.position = "relative";
+
+ var verticalEdges = ["left", "right"];
+ var horizontalEdges = ["top", "bottom"];
+ var colors = ["grey", "blue", "black"];
+
+ for (var i = 0; i < verticalEdges.length; i++) {
+ for (var j = 0; j < horizontalEdges.length; j++) {
+ for (var k = 0; k < colors.length; k++) {
+ var img = document.createElement("img");
+ img.src = "button_corner_" + colors[k] + ".gif";
+ img.color = colors[k];
+ img.style.position = "absolute";
+ img.style[verticalEdges[i]] = "-2px";
+ img.style[horizontalEdges[j]] = "-2px";
+ img.style.filter =
+ "progid:DXImageTransform.Microsoft.BasicImage(Rotation=" +
+ (i + j) + ")";
+ this._htmlButton.appendChild(img);
+ }
+ }
+ }
+
+ this._setCornerColor("grey");
+};
+
+/**
+ * Sets the color of the corners in IE by changing the stack order of the corner
+ * images.
+ */
+CustomButton.prototype._setCornerColor = function(newColor) {
+ var imgs = this._htmlButton.getElementsByTagName("img");
+ for (var i = 0, img; img = imgs[i]; i++) {
+ img.style.zIndex = Number(img.color == newColor);
+ }
+};
+
+var localized_strings = {
+ "el": {
+ "string-no": "Όχι",
+ "string-header-desktop": "Αυτός ο ιστότοπος θέλει να δημιουργήσει μια συντόμευση στον υπολογιστή σας. Θέλετε να το επιτρέψετε αυτό;",
+ "string-no-accesskey": "Ο",
+ "string-header-wince": "Αυτός ο ιστότοπος θέλει να δημιουργήσει μια συντόμευση στη λίστα προγραμμάτων. Θέλετε να το επιτρέψετε;",
+ "string-title-simple": "Δημιουργία συντομεύσεων εφαρμογών",
+ "string-ok-accesskey": "O",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Άκυρο",
+ "string-startmenu": "Μενού έναρξης",
+ "string-yes": "Ναι",
+ "string-never-allow": "Να μην επιτρέπεται ποτέ αυτή η συντόμευση",
+ "string-yes-accesskey": "Ν",
+ "string-quicklaunch": "Γραμμή γρήγορης εκκίνησης",
+ "string-never-allow-wince": "Να μην επιτρέπεται ποτέ",
+ "string-desktop": "Επιφάνεια εργασίας",
+ "string-title-default": "Gears - Δημιουργία συντόμευσης στην επιφάνεια εργασίας",
+ "string-location-query": "Δημιουργία συντομεύσεων στις παρακάτω θέσεις:"
+ },
+ "gu": {
+ "string-no": "નહીં",
+ "string-header-desktop": "આ વેબસાઇટને તમારા કમ્પ્યુટર પર એક શોર્ટકટ બનાવવો છે શું આની તમારે મંજુરી આપવી છે?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "આ વેબસાઇટ આપની પ્રોગ્રામોની સૂચિમાં શૉર્ટકટ્સ બનાવવા માંગે છે. શું આપ આને મંજુરી આપવા માંગો છો?",
+ "string-title-simple": "એપ્લિકેશન શોર્ટકટ બનાવો",
+ "string-ok-accesskey": "O",
+ "string-ok": "ઓકે",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "રદ કરો",
+ "string-startmenu": "પ્રારંભ મેનુ",
+ "string-yes": "હા",
+ "string-never-allow": "આ શૉર્ટકટને ક્યારેય મંજૂર કરશો નહીં",
+ "string-yes-accesskey": "Y",
+ "string-quicklaunch": "ક્વિક લોંચ બાર",
+ "string-never-allow-wince": "ક્યારેય મંજૂર કરશો નહીં",
+ "string-desktop": "ડેસ્કટોપ",
+ "string-title-default": "Gears - ડેસ્ક્ટોપ શોર્ટકટ બનાવો",
+ "string-location-query": "નીચેના સ્થાનો પર શોર્ટકટ્સ બનાવો"
+ },
+ "zh-TW": {
+ "string-no": "否",
+ "string-header-desktop": "這個網站要在您的電腦上建立捷徑。 您是否允許這個動作?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "這個網站要在您的程式清單中建立捷徑。 您是否允許這個動作?",
+ "string-title-simple": "建立應用程式捷徑",
+ "string-ok-accesskey": "O",
+ "string-ok": "確定",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "取消",
+ "string-startmenu": "開始功能表",
+ "string-yes": "是",
+ "string-never-allow": "永不允許這個捷徑",
+ "string-yes-accesskey": "Y",
+ "string-quicklaunch": "快速啟動列",
+ "string-never-allow-wince": "永不允許",
+ "string-desktop": "桌面",
+ "string-title-default": "Gears - 建立桌面捷徑",
+ "string-location-query": "在下列位置建立捷徑:"
+ },
+ "vi": {
+ "string-no": "Không",
+ "string-header-desktop": "Trang web này muốn tạo một lối tắt trên màn hình của bạn. Bạn có cho phép tác vụ này không?",
+ "string-no-accesskey": "K",
+ "string-header-wince": "Trang web này muốn tạo lối tắt trong danh sách chương trình của bạn. Bạn có cho phép tác vụ này không?",
+ "string-title-simple": "Tạp Lối tắt cho Ứng dụng",
+ "string-ok-accesskey": "O",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "H",
+ "string-cancel": "Huỷ",
+ "string-startmenu": "Trình đơn bắt đầu",
+ "string-yes": "Có",
+ "string-never-allow": "Không bao giờ cho phép lối tắt này",
+ "string-yes-accesskey": "C",
+ "string-quicklaunch": "Thanh khởi động nhanh",
+ "string-never-allow-wince": "Không bao giờ cho phép",
+ "string-desktop": "Màn hình",
+ "string-title-default": "Gears - Tạo Lối tắt trên Màn hình",
+ "string-location-query": "Tạo lối tắt trong các vị trí sau:"
+ },
+ "ca": {
+ "string-no": "No",
+ "string-header-desktop": "Aquest lloc web vol crear una drecera al vostre ordinador. Ho voleu permetre?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "Aquest lloc web vol crear una drecera a la vostra llista de programes. Ho voleu permetre?",
+ "string-title-simple": "Creeu dreceres d'aplicacions",
+ "string-ok-accesskey": "O",
+ "string-ok": "D'acord",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Cancel·la",
+ "string-startmenu": "Menú Inici",
+ "string-yes": "Sí",
+ "string-never-allow": "No permetre mai aquesta drecera",
+ "string-yes-accesskey": "S",
+ "string-quicklaunch": "Barra d'inici ràpid",
+ "string-never-allow-wince": "No permetre mai",
+ "string-desktop": "Escriptori",
+ "string-title-default": "Gears - Crea una drecera a l'escriptori",
+ "string-location-query": "Crea dreceres a les ubicacions següents:"
+ },
+ "it": {
+ "string-no": "No",
+ "string-header-desktop": "Questo sito web vuole creare un collegamento sul tuo computer. Consentire l'operazione?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "Questo sito Web vuole creare sul tuo desktop un collegamento nel tuo elenco di programmi. Consentire l'operazione?",
+ "string-title-simple": "Crea collegamenti applicazione",
+ "string-ok-accesskey": "O",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "A",
+ "string-cancel": "Annulla",
+ "string-startmenu": "Menu Start",
+ "string-yes": "Sì",
+ "string-never-allow": "Non consentire mai questo collegamento",
+ "string-yes-accesskey": "S",
+ "string-quicklaunch": "Barra Avvio veloce",
+ "string-never-allow-wince": "Non consentire mai",
+ "string-desktop": "Desktop",
+ "string-title-default": "Google Gears - Crea collegamento sul desktop",
+ "string-location-query": "Crea collegamenti nelle seguenti posizioni:"
+ },
+ "kn": {
+ "string-no": "ಇಲ್ಲ",
+ "string-header-desktop": "ಈ ವೆಬ್ ಸೈಟ್ ನಿಮ್ಮ ಕಂಪ್ಯೂಟರಿನಲ್ಲಿ ಒಂದು ಶಾರ್ಟ್ ಕಟ್ ರಚಿಸಲು ಬಯಸುತ್ತದೆ. ಇದನ್ನು ನೀವು ಅನುಮತಿಸಲು ಬಯಸುತ್ತೀರಾ?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "ನಿಮ್ಮ ಕಾರ್ಯಕ್ರಮಗಳ ಪಟ್ಟಿಯಲ್ಲಿ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ರಚಿಸಲು ಈ ವೆಬ್‌ಸೈಟ್ ಬಯಸುತ್ತದೆ. ನೀವು ಇದನ್ನು ಅನುಮತಿಸಲು ಬಯಸುತ್ತೀರಾ?",
+ "string-title-simple": "ಅಪ್ಲಿಕೇಶನ್ ಶಾರ್ಟ್ ಕಟ್ ಗಳನ್ನು ರಚಿಸಿ",
+ "string-ok-accesskey": "O",
+ "string-ok": "ಸರಿ",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "ರದ್ದುಮಾಡು",
+ "string-startmenu": "ಪ್ರಾರಂಭ ಮೆನು",
+ "string-yes": "ಹೌದು",
+ "string-never-allow": "ಈ ಶಾರ್ಟ್‌ಕಟ್‌ನ್ನು ಎಂದಿಗೂ ಅನುಮತಿಸಬೇಡಿ",
+ "string-yes-accesskey": "Y",
+ "string-quicklaunch": "ಕ್ವಿಕ್ ಲಾಂಚ್ ಬಾರ್",
+ "string-never-allow-wince": "ಎಂದಿಗೂ ಅನುಮತಿಸಬೇಡಿ",
+ "string-desktop": "ಡೆಸ್ಕ್ ಟಾಪ್",
+ "string-title-default": "Gears - ಡೆಸ್ಕ್ ಟಾಪ್ ಶಾರ್ಟ್ ಕಟ್ ರಚಿಸಿ",
+ "string-location-query": "ಈ ಕೆಳಗಿನ ಸ್ಥಳಗಳಲ್ಲಿ ಶಾರ್ಟ್ ಕಟ್ ಗಳನ್ನು ರಚಿಸಿ:"
+ },
+ "ar": {
+ "string-no": "لا",
+ "string-header-desktop": "يتطلب موقع الويب هذا إنشاء اختصار على الكمبيوتر الخاص بك. هل تريد السماح بذلك؟",
+ "string-no-accesskey": "ل",
+ "string-header-wince": "يرغب موقع الويب هذا في إنشاء اختصار في قائمة البرامج لديك. هل ترغب في السماح له بذلك؟",
+ "string-title-simple": "إنشاء اختصارات للتطبيقات",
+ "string-ok-accesskey": "م",
+ "string-ok": "موافق",
+ "string-cancel-accesskey": "غ",
+ "string-cancel": "إلغاء",
+ "string-startmenu": "القائمة ابدأ",
+ "string-yes": "نعم",
+ "string-never-allow": "عدم السماح بهذا الاختصار مطلقًا",
+ "string-yes-accesskey": "ن",
+ "string-quicklaunch": "شريط التشغيل السريع",
+ "string-never-allow-wince": "عدم السماح مطلقًا",
+ "string-desktop": "سطح المكتب",
+ "string-title-default": "Gears - إنشاء اختصار لسطح المكتب",
+ "string-location-query": "قم بإنشاء اختصارات في المواقع التالية:"
+ },
+ "ml": {
+ "string-no": "ഇല്ല",
+ "string-header-desktop": "ഈ വെബ്സൈറ്റ് നിങ്ങളുടെ കമ്പ്യൂട്ടരില് ഒരു കുറുക്കുവഴി സൃഷ്ടിക്കാന് ആവശ്യപ്പെടുന്നു. ഇതനുവദിക്കാന് നിങ്ങള് ആഗ്രഹിക്കുന്നുണ്ടോ?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "പ്രോഗ്രാമുകളുടെ പട്ടികയില്‍ ഒരു കുറുക്കുവഴി സൃഷ്ടിക്കാന്‍ ഈ വെബ്സൈറ്റ് ആവശ്യപ്പെടുന്നു. ഇതനുവദിക്കാന്‍ നിങ്ങള്‍ ആഗ്രഹിക്കുന്നുണ്ടോ?",
+ "string-title-simple": "അപ്ലിക്കേഷന്‍ കുറുക്കുവഴികള്‍ സൃഷ്ടിക്കുക",
+ "string-ok-accesskey": "O",
+ "string-ok": "ശരി",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "റദ്ദാക്കൂ",
+ "string-startmenu": "തുടങ്ങുക മെനു",
+ "string-yes": "അതെ",
+ "string-never-allow": "ഈ കുറുക്കുവഴി ഒരിക്കലും അനുവദിക്കരുത്",
+ "string-yes-accesskey": "Y",
+ "string-quicklaunch": "ദ്രുത ലോഞ്ച് ബാര്‍",
+ "string-never-allow-wince": "ഒരിക്കലും അനുവദിക്കരുത്",
+ "string-desktop": "ഡെസ്ക്ടോപ്",
+ "string-title-default": "Gears - ഡെസ്ക്ടോപ് കുറുക്കുവഴി സൃഷ്ടിക്കുക",
+ "string-location-query": "താഴെക്കാണുന്ന ലോക്കഷനുകളില് കുറുക്കുവഴികള് സൃഷ്ടിക്കുക"
+ },
+ "cs": {
+ "string-no": "Ne",
+ "string-header-desktop": "Tato webová stránka chce vytvořit zástupce ve vašem počítači. Chcete tuto akci povolit?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "Tento web chce vytvořit zástupce v seznamu programů. Chcete tuto akci povolit?",
+ "string-title-simple": "Vytvořit zástupce aplikace",
+ "string-ok-accesskey": "O",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "R",
+ "string-cancel": "Zrušit",
+ "string-startmenu": "Nabídka Start",
+ "string-yes": "Ano",
+ "string-never-allow": "Nikdy nepovolit tohoto zástupce",
+ "string-yes-accesskey": "A",
+ "string-quicklaunch": "Panel rychlého spuštění",
+ "string-never-allow-wince": "Nikdy nepovolit",
+ "string-desktop": "Plocha",
+ "string-title-default": "Gears – vytvořit zástupce na ploše",
+ "string-location-query": "Vytvořit zástupce v následujících umístěních:"
+ },
+ "et": {
+ "string-no": "Ei",
+ "string-header-desktop": "See veebilehekülg tahab teie arvutisse otseteed luua. Kas soovite seda lubada?",
+ "string-no-accesskey": "E",
+ "string-header-wince": "Veebisait soovib teie programmiloendisse luua otsetee. Kas lubate seda?",
+ "string-title-simple": "Loo rakenduse otseteed",
+ "string-ok-accesskey": "O",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "T",
+ "string-cancel": "Tühista",
+ "string-startmenu": "Start-menüü",
+ "string-yes": "Jah",
+ "string-never-allow": "Ära luba seda otseteed kunagi",
+ "string-yes-accesskey": "J",
+ "string-quicklaunch": "Kiirkäivitusriba",
+ "string-never-allow-wince": "Ära luba kunagi",
+ "string-desktop": "Töölaud",
+ "string-title-default": "Gears – loo töölaua otsetee",
+ "string-location-query": "Loo otseteed järgmistesse asukohtadesse:"
+ },
+ "id": {
+ "string-no": "Tidak",
+ "string-header-desktop": "Website ini ingin membuat pintasan pada komputer Anda. Anda ingin mengizinkannya?",
+ "string-no-accesskey": "T",
+ "string-header-wince": "Situs web ini ingin membuat pintasan dalam daftar program Anda. Apakah Anda mengizinkannya?",
+ "string-title-simple": "Buat Pintasan Aplikasi",
+ "string-ok-accesskey": "O",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Batalkan",
+ "string-startmenu": "Menu Mulai",
+ "string-yes": "Ya",
+ "string-never-allow": "Jangan pernah mengizinkan pintasan ini",
+ "string-yes-accesskey": "Y",
+ "string-quicklaunch": "Bilah luncur cepat",
+ "string-never-allow-wince": "Jangan pernah mengizinkan",
+ "string-desktop": "Desktop",
+ "string-title-default": "Gears - Buat Jalan Pintas Desktop",
+ "string-location-query": "Buat pintasan di lokasi berikut ini:"
+ },
+ "es": {
+ "string-no": "No",
+ "string-header-desktop": "Este sitio web desea crear un acceso directo en tu equipo. ¿Quieres permitirlo?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "Este sitio web desea crear un acceso directo en tu lista de programas. ¿Quieres permitirlo?",
+ "string-title-simple": "Crear accesos directos de la aplicación",
+ "string-ok-accesskey": "A",
+ "string-ok": "Aceptar",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Cancelar",
+ "string-startmenu": "Menú Inicio",
+ "string-yes": "Sí",
+ "string-never-allow": "No permitir nunca este acceso directo",
+ "string-yes-accesskey": "S",
+ "string-quicklaunch": "Barra de inicio rápida",
+ "string-never-allow-wince": "No permitir nunca",
+ "string-desktop": "Escritorio",
+ "string-title-default": "Gears - Crear acceso directo en el escritorio",
+ "string-location-query": "Crear accesos directos en las siguientes ubicaciones:"
+ },
+ "en-GB": {
+ "string-no": "No",
+ "string-header-desktop": "This website wants to create a shortcut on your computer. Do you want to allow this?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "This website wants to create the shortcuts listed below in your list of programmes. Do you want to allow this?",
+ "string-title-simple": "Create Application Shortcuts",
+ "string-ok-accesskey": "O",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Cancel",
+ "string-startmenu": "Start menu",
+ "string-yes": "Yes",
+ "string-never-allow": "Never allow this shortcut",
+ "string-yes-accesskey": "Y",
+ "string-quicklaunch": "Quick launch bar",
+ "string-never-allow-wince": "Never allow",
+ "string-desktop": "Desktop",
+ "string-title-default": "Gears - Create Desktop Shortcut",
+ "string-location-query": "Create shortcuts in the following locations:"
+ },
+ "ru": {
+ "string-no": "Нет",
+ "string-header-desktop": "Этот веб-сайт собирается создать ярлык на вашем компьютере. Разрешить?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "Этот веб-сайт пытается создать ярлык в вашем списке программ. Разрешить?",
+ "string-title-simple": "Создать ярлыки для приложений",
+ "string-ok-accesskey": "O",
+ "string-ok": "ОК",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Отмена",
+ "string-startmenu": "Меню \"Пуск\"",
+ "string-yes": "Да",
+ "string-never-allow": "Никогда не разрешать создание этого ярлыка",
+ "string-yes-accesskey": "Y",
+ "string-quicklaunch": "Панель быстрого запуска",
+ "string-never-allow-wince": "Никогда",
+ "string-desktop": "Рабочий стол",
+ "string-title-default": "Gears — создать ярлык на рабочем столе",
+ "string-location-query": "Создать ярлыки со следующим размещением:"
+ },
+ "nl": {
+ "string-no": "Nee",
+ "string-header-desktop": "Deze website wil een snelkoppeling maken op je computer. Vind je dat goed?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "Deze website wil een snelkoppeling maken in uw programmalijst. Wilt u dit toestaan?",
+ "string-title-simple": "Toepassingssnelkoppelingen maken",
+ "string-ok-accesskey": "O",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Annuleren",
+ "string-startmenu": "Startmenu",
+ "string-yes": "Ja",
+ "string-never-allow": "Deze snelkoppeling nooit toestaan",
+ "string-yes-accesskey": "J",
+ "string-quicklaunch": "Werkbalk Snel starten",
+ "string-never-allow-wince": "Nooit toestaan",
+ "string-desktop": "Bureaublad",
+ "string-title-default": "Gears - Snelkoppeling op bureaublad maken",
+ "string-location-query": "Snelkoppelingen maken op de volgende locaties:"
+ },
+ "no": {
+ "string-no": "Nei",
+ "string-header-desktop": "Dette nettstedet ønsker å opprette en snarvei på datamaskinen. Vil du tillate dette?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "Dette nettstedet vil opprette en snarvei i listen over programmer. Vil du tillate dette?",
+ "string-title-simple": "Opprett snarveier til programmer",
+ "string-ok-accesskey": "O",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "A",
+ "string-cancel": "Avbryt",
+ "string-startmenu": "Start-meny",
+ "string-yes": "Ja",
+ "string-never-allow": "Avvis alltid denne snarveien",
+ "string-yes-accesskey": "J",
+ "string-quicklaunch": "Hurtigstartlinje",
+ "string-never-allow-wince": "Avvis alltid",
+ "string-desktop": "Skrivebord",
+ "string-title-default": "Gears – Opprett snarvei på skrivebordet",
+ "string-location-query": "Opprett snarveier på følgende plasseringer:"
+ },
+ "tr": {
+ "string-no": "Hayır",
+ "string-header-desktop": "Bu web sitesi bilgisayarınızda bir kısayol oluşturmak istiyor. Buna izin vermek istiyor musunuz?",
+ "string-no-accesskey": "H",
+ "string-header-wince": "Bu web sitesi, programlar listenizde bir kısayol oluşturmak istiyor. Buna izin vermek istiyor musunuz?",
+ "string-title-simple": "Uygulama Kısayolları Oluştur",
+ "string-ok-accesskey": "T",
+ "string-ok": "Tamam",
+ "string-cancel-accesskey": "a",
+ "string-cancel": "İptal",
+ "string-startmenu": "Başlat menüsü",
+ "string-yes": "Evet",
+ "string-never-allow": "Bu kısayola asla izin verme",
+ "string-yes-accesskey": "E",
+ "string-quicklaunch": "Hızlı başlat çubuğu",
+ "string-never-allow-wince": "Asla izin verme",
+ "string-desktop": "Masaüstü",
+ "string-title-default": "Gears - Masaüstü Kısayolu Oluştur",
+ "string-location-query": "Kısayolları aşağıdaki konumlarda oluştur:"
+ },
+ "lv": {
+ "string-no": "Nē",
+ "string-header-desktop": "Šī vietne vēlas izveidot datorā saīsni. Vai vēlaties to atļaut?",
+ "string-no-accesskey": "Nē",
+ "string-header-wince": "Šī vietne jūsu programmu sarakstā gatavojas izveidot īsceļu. Vai atļausit?",
+ "string-title-simple": "Izveidot lietojumprogrammu saīsnes",
+ "string-ok-accesskey": "O",
+ "string-ok": "Labi",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Atcelt",
+ "string-startmenu": "Izvēlne Sākt",
+ "string-yes": "Jā",
+ "string-never-allow": "Nekad neatļaut šī īsceļa izveidi",
+ "string-yes-accesskey": "Jā",
+ "string-quicklaunch": "Ātrās palaišanas josla",
+ "string-never-allow-wince": "Nekad neatļaut",
+ "string-desktop": "Datora darbvirsma",
+ "string-title-default": "Gears — darbvirsmas saīsnes izveide",
+ "string-location-query": "Izveidot saīsnes šādās vietās:"
+ },
+ "lt": {
+ "string-no": "Ne",
+ "string-header-desktop": "Ši svetainė sukurs šaukinį jūsų kompiuteryje. Ar leisti?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "Ši svetainė sukurs šaukinį programų sąraše. Ar leisti?",
+ "string-title-simple": "Sukurti programos šaukinius",
+ "string-ok-accesskey": "O",
+ "string-ok": "Gerai",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Atšaukti",
+ "string-startmenu": "Pradinis meniu",
+ "string-yes": "Taip",
+ "string-never-allow": "Niekada neleisti šio šaukinio",
+ "string-yes-accesskey": "T",
+ "string-quicklaunch": "Sparčiosios paleisties juosta",
+ "string-never-allow-wince": "Niekada neleisti",
+ "string-desktop": "Darbalaukis",
+ "string-title-default": "„Gears“ – sukurti darbalaukio šaukinį",
+ "string-location-query": "Sukurti šaukinius šiose vietose:"
+ },
+ "th": {
+ "string-no": "ไม่",
+ "string-header-desktop": "เว็บไซต์นี้ต้องการสร้างทางลัดบนเดสก์ท็อปของคุณ คุณต้องการอนุญาตหรือไม่",
+ "string-no-accesskey": "ม",
+ "string-header-wince": "เว็บไซต์นี้ต้องการสร้างทางลัดที่ปรากฏในรายการโปรแกรมของคุณ คุณต้องการอนุญาตหรือไม่",
+ "string-title-simple": "สร้างทางลัดของแอปพลิเคชัน",
+ "string-ok-accesskey": "ต",
+ "string-ok": "ตกลง",
+ "string-cancel-accesskey": "ย",
+ "string-cancel": "ยกเลิก",
+ "string-startmenu": "เมนู Start",
+ "string-yes": "ใช่",
+ "string-never-allow": "ไม่อนุญาตให้สร้างทางลัดนี้",
+ "string-yes-accesskey": "ช",
+ "string-quicklaunch": "แถบ Quick Launch",
+ "string-never-allow-wince": "ไม่อนุญาต",
+ "string-desktop": "เดสก์ท็อป",
+ "string-title-default": "Gears - สร้างทางลัดบนเดสก์ท็อป",
+ "string-location-query": "สร้างทางลัดในตำแหน่งต่อไปนี้:"
+ },
+ "ro": {
+ "string-no": "Nu",
+ "string-header-desktop": "Acest site Web doreşte să creeze o comandă rapidă pe computerul dvs. Permiteţi acest lucru?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "Acest site Web doreşte să creeze o comandă rapidă în lista dvs. de programe. Permiteţi acest lucru?",
+ "string-title-simple": "Creaţi comenzi rapide pentru aplicaţii",
+ "string-ok-accesskey": "O",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "A",
+ "string-cancel": "Anulaţi",
+ "string-startmenu": "Meniul Start",
+ "string-yes": "Da",
+ "string-never-allow": "Nu permiteţi niciodată această comandă rapidă",
+ "string-yes-accesskey": "D",
+ "string-quicklaunch": "Bara Lansare rapidă",
+ "string-never-allow-wince": "Nu permiteţi niciodată",
+ "string-desktop": "Desktop",
+ "string-title-default": "Gears – Creaţi comandă rapidă de pe desktop",
+ "string-location-query": "Creaţi comenzi rapide în următoarele locaţii:"
+ },
+ "is": {
+ "string-no": "Nei",
+ "string-header-desktop": "Þetta vefsvæði óskar eftir að búa til flýtivísun á tölvunni hjá þér. Viltu leyfa það?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "Þetta vefsvæði óskar eftir að búa til flýtivísun í forritalistanum hjá þér. Viltu leyfa það?",
+ "string-title-simple": "Útbúa flýtivísanir fyrir forrit",
+ "string-ok-accesskey": "O",
+ "string-ok": "Í lagi",
+ "string-cancel-accesskey": "H",
+ "string-cancel": "Hætta við",
+ "string-startmenu": "Upphafsvalmynd",
+ "string-yes": "Já",
+ "string-never-allow": "Aldrei heimila þessa flýtivísun",
+ "string-yes-accesskey": "J",
+ "string-quicklaunch": "Stika fyrir flýtiræsingu",
+ "string-never-allow-wince": "Leyfa aldrei",
+ "string-desktop": "Skjáborð",
+ "string-title-default": "Gears - Setja flýtivísun á skjáborð",
+ "string-location-query": "Útbúa flýtivísanir á eftirfarandi staðsetningum:"
+ },
+ "fil": {
+ "string-no": "Hindi",
+ "string-header-desktop": "Nais ng website na ito na lumikha ng isang shortcut sa iyong computer. Nais mo bang payagan ito?",
+ "string-no-accesskey": "H",
+ "string-header-wince": "Nais ng website na ito na lumikha ng isang shortcut sa iyong listahan ng mga program. Nais mo ba itong payagan?",
+ "string-title-simple": "Lumikha ng mga Shortcut ng Application",
+ "string-ok-accesskey": "K",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "I",
+ "string-cancel": "Ikansela",
+ "string-startmenu": "Start menu",
+ "string-yes": "Oo",
+ "string-never-allow": "Huwag kailanman payagan ang shortcut na ito",
+ "string-yes-accesskey": "O",
+ "string-quicklaunch": "Quick launch bar",
+ "string-never-allow-wince": "Huwag kailanman papayagan",
+ "string-desktop": "Desktop",
+ "string-title-default": "Mga Gear - Lumikha ng Shortcut sa Desktop",
+ "string-location-query": "Lumikha ng mga shortcut sa mga sumusunod na lokasyon:"
+ },
+ "ta": {
+ "string-no": "இல்லை",
+ "string-header-desktop": "இந்தத் தளம் உங்கள் கணினியில் குறுக்கு வழியை உருவாக்க விரும்புகிறது. நீங்கள் இதை அனுமதிக்க வேண்டுமா?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "உங்கள் நிரல்களின் பட்டியலில் ஒரு குறுக்குவழியை உருவாக்க இந்த வலைத்தளம் முயற்சிக்கிறது. இதனை அனுமதிக்க விரும்புகிறீர்களா?",
+ "string-title-simple": "அப்ளிகேஷன் குறுக்குவழியை உருவாக்கு",
+ "string-ok-accesskey": "O",
+ "string-ok": "சரி",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "ரத்துசெய்",
+ "string-startmenu": "ஸ்டார்ட் மெனு",
+ "string-yes": "ஆம்",
+ "string-never-allow": "இந்த குறுக்குவழியை எப்போதும் அனுமதிக்க வேண்டாம்",
+ "string-yes-accesskey": "Y",
+ "string-quicklaunch": "குவிக் லான்ச் பார்",
+ "string-never-allow-wince": "எப்போதும் அனுமதிக்காதே",
+ "string-desktop": "டெஸ்க்டாப்",
+ "string-title-default": "Gears - டெஸ்க்டாப் குறுக்குவழியை உருவாக்கு",
+ "string-location-query": "பின்வரும் இடத்தில் குறுக்கு வழியை உருவாக்கவும்:"
+ },
+ "fr": {
+ "string-no": "Non",
+ "string-header-desktop": "Autorisez-vous la création par ce site Web d'un raccourci sur votre ordinateur ?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "Ce site Web souhaite créer un raccourci dans votre liste de programmes. Autorisez-vous cette opération ?",
+ "string-title-simple": "Créer des raccourcis d'applications",
+ "string-ok-accesskey": "K",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "U",
+ "string-cancel": "Annuler",
+ "string-startmenu": "Menu Démarrer",
+ "string-yes": "Oui",
+ "string-never-allow": "Ne jamais autoriser ce raccourci",
+ "string-yes-accesskey": "O",
+ "string-quicklaunch": "Barre de lancement rapide",
+ "string-never-allow-wince": "Ne jamais autoriser",
+ "string-desktop": "Bureau",
+ "string-title-default": "Google Gears - Créer un raccourci sur le Bureau",
+ "string-location-query": "Créer des raccourcis aux emplacements suivants :"
+ },
+ "bg": {
+ "string-no": "Не",
+ "string-header-desktop": "Прави се опит за създаване на пряк път към този уебсайт на компютъра ви. Разрешавате ли?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "Този уебсайт иска да създаде пряк път в списъка ви с програми. Разрешавате ли?",
+ "string-title-simple": "Създаване на преки пътища към приложението",
+ "string-ok-accesskey": "О",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "С",
+ "string-cancel": "Отказ",
+ "string-startmenu": "Меню „Старт“",
+ "string-yes": "Да",
+ "string-never-allow": "Създаването на този пряк път не се разрешава изобщо",
+ "string-yes-accesskey": "Y",
+ "string-quicklaunch": "Лента за бързо стартиране",
+ "string-never-allow-wince": "Не се разрешава изобщо",
+ "string-desktop": "Работен плот",
+ "string-title-default": "Gears - Създаване на пряк път на работния плот",
+ "string-location-query": "Преки пътища да бъдат създадени на следните места:"
+ },
+ "uk": {
+ "string-no": "Ні",
+ "string-header-desktop": "Цей веб-сайт хоче створити ярлик на Вашому комп’ютері. Дозволити?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "Цей веб-сайт намагається створити ярлик у списку програм. Дозволити це?",
+ "string-title-simple": "Створіть ярлики програм",
+ "string-ok-accesskey": "O",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Скасувати",
+ "string-startmenu": "Меню \"Пуск\"",
+ "string-yes": "Так",
+ "string-never-allow": "Ніколи не дозволяти створювати цей ярлик",
+ "string-yes-accesskey": "Y",
+ "string-quicklaunch": "Панель \"Швидкий запуск\"",
+ "string-never-allow-wince": "Ніколи не дозволяти",
+ "string-desktop": "Робочий стіл",
+ "string-title-default": "Gears – Створення ярликів на робочому столі",
+ "string-location-query": "Створіть ярлики у таких розташуваннях:"
+ },
+ "hr": {
+ "string-no": "Ne",
+ "string-header-desktop": "Ova web-lokacija želi stvoriti prečac na Vašem računalu. Dopuštate li taj postupak?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "Ova web-lokacija želi stvoriti prečac na Vašem popisu programa. Dopuštate li taj postupak?",
+ "string-title-simple": "Stvori prečace za aplikacije",
+ "string-ok-accesskey": "K",
+ "string-ok": "U redu",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Odustani",
+ "string-startmenu": "Izbornik Start",
+ "string-yes": "Da",
+ "string-never-allow": "Nikad ne dopuštaj ovaj prečac",
+ "string-yes-accesskey": "D",
+ "string-quicklaunch": "Traka za brzo pokretanje",
+ "string-never-allow-wince": "Ne dopuštaj",
+ "string-desktop": "Radna površina",
+ "string-title-default": "Gears – Napravi prečac na radnoj površini",
+ "string-location-query": "Stvori prečace na sljedećim mjestima:"
+ },
+ "bn": {
+ "string-no": "না",
+ "string-header-desktop": "এই ওয়েবসাইটটা আপনার কম্পিউটারে একটি শর্টকার্ট তৈরি করতে চায় ׀ আপনি কি এটার অনুমতি দিতে চান?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "এই ওয়েবসাইটটি আপনার প্রোগ্রামের তালিকাতে একটি শর্টকাট তৈরি করতে চায়৷ আপনি কি এটি মঞ্জুরি দিতে চান?",
+ "string-title-simple": "অ্যাপ্লিকেশান শর্টকার্ট তৈরি করুন",
+ "string-ok-accesskey": "O",
+ "string-ok": "ওকে",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "বাতিল",
+ "string-startmenu": "সূচনা মেনু",
+ "string-yes": "হ্যাঁ",
+ "string-never-allow": "কখনই এই শর্টকাটটির মঞ্জুরি দেবেন না",
+ "string-yes-accesskey": "Y",
+ "string-quicklaunch": "দ্রুত লঞ্চ দণ্ড",
+ "string-never-allow-wince": "কখনই মঞ্জুরি দেবেন না",
+ "string-desktop": "ডেস্কটপ",
+ "string-title-default": "Gears- ডেস্কটপ শর্টকাট তৈরি করুন",
+ "string-location-query": "এই যায়গাগুলিতে জন্য শর্টকার্টগুলি তৈরি করুন:"
+ },
+ "en-US": {
+ "string-no": "No",
+ "string-header-desktop": "This website wants to create a shortcut on your computer. Do you want to allow this?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "This website wants to create a shortcut in your list of programs. Do you want to allow this?",
+ "string-title-simple": "Create Application Shortcuts",
+ "string-ok-accesskey": "O",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Cancel",
+ "string-startmenu": "Start menu",
+ "string-yes": "Yes",
+ "string-never-allow": "Never allow this shortcut",
+ "string-yes-accesskey": "Y",
+ "string-quicklaunch": "Quick launch bar",
+ "string-never-allow-wince": "Never allow",
+ "string-desktop": "Desktop",
+ "string-title-default": "Gears - Create Desktop Shortcut",
+ "string-location-query": "Create shortcuts in the following locations:"
+ },
+ "da": {
+ "string-no": "Nej",
+ "string-header-desktop": "Dette websted ønsker at oprette en genvej på din computer. Vil du tillade det?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "Dette websted ønsker at oprette en genvej i din liste med programmer. Vil du tillade det?",
+ "string-title-simple": "Opret genveje til programmer",
+ "string-ok-accesskey": "O",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "A",
+ "string-cancel": "Annuller",
+ "string-startmenu": "Startmenu",
+ "string-yes": "Ja",
+ "string-never-allow": "Tillad aldrig denne genvej",
+ "string-yes-accesskey": "J",
+ "string-quicklaunch": "Værktøjslinjen Hurtig start",
+ "string-never-allow-wince": "Giv aldrig tilladelse",
+ "string-desktop": "Skrivebord",
+ "string-title-default": "Gears – opret genvej på skrivebordet",
+ "string-location-query": "Opret genveje følgende steder:"
+ },
+ "fa": {
+ "string-no": "خیر",
+ "string-header-desktop": "این وب سایت می خواهد میانبری در رایانه شما ایجاد کند. آیا اجازه می دهید؟",
+ "string-no-accesskey": "N",
+ "string-header-wince": "این وب سایت می خواهد یك میانبر در ليست برنامه های شما ایجاد کند. آیا اجازه می دهید؟",
+ "string-title-simple": "ايجاد ميانبرهای برنامه",
+ "string-ok-accesskey": "O",
+ "string-ok": "تأیید",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "لغو",
+ "string-startmenu": "منوی شروع",
+ "string-yes": "بله",
+ "string-never-allow": "هرگز اجازه ندهید این میانبر ایجاد شود.",
+ "string-yes-accesskey": "Y",
+ "string-quicklaunch": "ميله راه اندازی سريع",
+ "string-never-allow-wince": "هرگز اجازه داده نشود",
+ "string-desktop": "دسک تاپ",
+ "string-title-default": "Gears- ایجاد میانبر در دسک تاپ",
+ "string-location-query": "ایجاد میانبر در محل های زیر:"
+ },
+ "hi": {
+ "string-no": "नहीं",
+ "string-header-desktop": "यह वेबसाइट आपके कंप्यूटर पर एक शॉर्टकट बनाना चाहती है. क्या आप इसकी अनुमति देना चाहते हैं?",
+ "string-no-accesskey": "नहीं",
+ "string-header-wince": "यह वेबसाइट आपके प्रोग्रामों की सूची में शॉर्टकट बनाना चाहती है. क्या आप इसकी अनुमति देना चाहते हैं?",
+ "string-title-simple": "अनुप्रयोग शार्टकट बनाएँ",
+ "string-ok-accesskey": "ठीक",
+ "string-ok": "ठीक",
+ "string-cancel-accesskey": "रद्द करें",
+ "string-cancel": "रद्द करें",
+ "string-startmenu": "प्रारंभ मेनू",
+ "string-yes": "हाँ",
+ "string-never-allow": "इस शॉर्टकट को कभी अनुमति न दें",
+ "string-yes-accesskey": "हाँ",
+ "string-quicklaunch": "त्वरित लॉन्च पट्टिका",
+ "string-never-allow-wince": "कभी अनुमति न दें",
+ "string-desktop": "डेस्कटॉप",
+ "string-title-default": "Gears - डेस्कटॉप शॉर्टकट बनाएँ",
+ "string-location-query": "निम्न स्थानों पर शॉर्टकट बनाएँ:"
+ },
+ "pt-BR": {
+ "string-no": "Não",
+ "string-header-desktop": "Este site deseja criar um atalho no seu computador. Gostaria de permitir isso?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "Este site deseja criar um atalho na sua lista de programas. Deseja permitir isso?",
+ "string-title-simple": "Criar atalhos do aplicativo",
+ "string-ok-accesskey": "O",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Cancelar",
+ "string-startmenu": "Menu Iniciar",
+ "string-yes": "Sim",
+ "string-never-allow": "Nunca permitir este atalho",
+ "string-yes-accesskey": "Y",
+ "string-quicklaunch": "Barra de inicialização rápida",
+ "string-never-allow-wince": "Nunca permitir",
+ "string-desktop": "Área de trabalho",
+ "string-title-default": "Gears - Criar atalho na área de trabalho",
+ "string-location-query": "Criar atalhos nos seguintes locais:"
+ },
+ "fi": {
+ "string-no": "Ei",
+ "string-header-desktop": "Tämä sivusto haluaa luoda pikakuvakkeen tietokoneellesi. Sallitko pikakuvakkeen luomisen?",
+ "string-no-accesskey": "E",
+ "string-header-wince": "Tämä sivusto haluaa luoda pikakuvakkeen ohjelmaluetteloosi. Sallitko pikakuvakkeen luomisen?",
+ "string-title-simple": "Luo sovelluksista pikakuvakkeita",
+ "string-ok-accesskey": "O",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "P",
+ "string-cancel": "Peruuta",
+ "string-startmenu": "Aloitusvalikko",
+ "string-yes": "Kyllä",
+ "string-never-allow": "Älä koskaan salli tätä pikakuvaketta",
+ "string-yes-accesskey": "K",
+ "string-quicklaunch": "Pikakäynnistyspalkki",
+ "string-never-allow-wince": "Älä koskaan salli",
+ "string-desktop": "Työpöytä",
+ "string-title-default": "Gears - Luo pikakuvake työpöydälle",
+ "string-location-query": "Luo pikakuvakkeet seuraaviin kohteisiin:"
+ },
+ "hu": {
+ "string-no": "Nem",
+ "string-header-desktop": "Ez a webhely parancsikont szeretne létrehozni a számítógépen. Engedélyezi ezt?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "Ez a webhely egy parancsikont kíván létrehozni a programlistában. Engedélyezi ezt?",
+ "string-title-simple": "Alkalmazásindító parancsikonok létrehozása",
+ "string-ok-accesskey": "O",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "M",
+ "string-cancel": "Mégse",
+ "string-startmenu": "Start menü",
+ "string-yes": "Igen",
+ "string-never-allow": "Soha ne engedélyezze ezt a parancsikont",
+ "string-yes-accesskey": "I",
+ "string-quicklaunch": "Gyorsindítás eszköztár",
+ "string-never-allow-wince": "Soha ne engedélyezze",
+ "string-desktop": "Asztal",
+ "string-title-default": "Szinkron - Asztali parancsikon létrehozása",
+ "string-location-query": "Parancsikonok létrehozása a következő helyeken:"
+ },
+ "ja": {
+ "string-no": "いいえ",
+ "string-header-desktop": "デスクトップにこのサイトのショートカットが作成されます。許可しますか?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "プログラム一覧にこのサイトのショートカットが作成されます。許可しますか?",
+ "string-title-simple": "アプリケーション ショートカットを作成",
+ "string-ok-accesskey": "O",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "キャンセル",
+ "string-startmenu": "スタート メニュー",
+ "string-yes": "はい",
+ "string-never-allow": "このショートカットを常に拒否する",
+ "string-yes-accesskey": "Y",
+ "string-quicklaunch": "クイック起動バー",
+ "string-never-allow-wince": "許可しない",
+ "string-desktop": "デスクトップ",
+ "string-title-default": "Gears - デスクトップ ショートカットを作成",
+ "string-location-query": "ショートカットを作成する場所"
+ },
+ "he": {
+ "string-no": "לא",
+ "string-header-desktop": "אתר אינטרנט זה רוצה ליצור קיצור דרך במחשב שלך. האם ברצונך לאפשר זאת?",
+ "string-no-accesskey": "לא",
+ "string-header-wince": "אתר אינטרנט זה מעוניין ליצור קיצור דרך ברשימת התוכניות שלך. האם ברצונך לאפשר זאת?",
+ "string-title-simple": "צור קיצורי דרך ליישומים",
+ "string-ok-accesskey": "אישור",
+ "string-ok": "אישור",
+ "string-cancel-accesskey": "ביטול",
+ "string-cancel": "ביטול",
+ "string-startmenu": "תפריט 'התחל'",
+ "string-yes": "כן",
+ "string-never-allow": "לעולם אל תאפשר קיצור דרך זה",
+ "string-yes-accesskey": "כן",
+ "string-quicklaunch": "סרגל הפעלה מהירה",
+ "string-never-allow-wince": "לעולם אל תאפשר",
+ "string-desktop": "שולחן עבודה",
+ "string-title-default": "Gears - צור קיצור דרך בשולחן העבודה",
+ "string-location-query": "צור קיצורי דרך במיקומים הבאים:"
+ },
+ "te": {
+ "string-no": "వద్దు",
+ "string-header-desktop": "ఈ వెబ్ సైట్ మీ కమ్ప్యుటర్ పైన షార్ట్ కట్స్ సృష్టించాలని అనుకుంటుంది . ఇది మీరు కొనసాగించాలని అనుకుంటున్నారా?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "ఈ వెబ్‌సైట్ మీ ప్రోగ్రామ్‌ల జాబితాలో ఒక సత్వర మార్గాన్ని సృష్టించదలిచింది. మీరు దీనిని అనుమతించదలిచారా?",
+ "string-title-simple": "అప్ప్లికేషన్ల షార్ట్కట్లు సృష్టించు",
+ "string-ok-accesskey": "O",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "రద్దు చెయ్యి",
+ "string-startmenu": "స్టార్ట్ మెన్యు",
+ "string-yes": "అవును",
+ "string-never-allow": "ఈ సత్వర మార్గాన్ని ఎప్పుడూ అనుమతించవద్దు",
+ "string-yes-accesskey": "Y",
+ "string-quicklaunch": "శీఘ్ర లాంచ్ బార్",
+ "string-never-allow-wince": "ఎప్పుడూ అనుమతించవద్దు",
+ "string-desktop": "డెస్క్ టాప్",
+ "string-title-default": "Gears -డెస్క్ టాప్ షార్ట్ కట్ని సృష్టించు",
+ "string-location-query": "ఈ లోకేషన్స్ లో షార్ట్ కట్స్ సృష్టించండి:"
+ },
+ "pt-PT": {
+ "string-no": "Não",
+ "string-header-desktop": "Este Web site pretende criar um atalho no seu computador. Pretende permitir esta acção?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "Este Web site pretende criar um atalho na sua lista de programas. Pretende permitir esta acção?",
+ "string-title-simple": "Criar atalhos para as aplicações",
+ "string-ok-accesskey": "O",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Cancelar",
+ "string-startmenu": "Menu Iniciar",
+ "string-yes": "Sim",
+ "string-never-allow": "Nunca permitir este atalho",
+ "string-yes-accesskey": "S",
+ "string-quicklaunch": "Barra de iniciação rápida",
+ "string-never-allow-wince": "Nunca permitir",
+ "string-desktop": "Ambiente de trabalho",
+ "string-title-default": "Gears - Criar atalho no ambiente de trabalho",
+ "string-location-query": "Criar atalhos nas seguintes localizações:"
+ },
+ "sr": {
+ "string-no": "Не",
+ "string-header-desktop": "Овај веб сајт жели да направи пречицу на вашем рачунару. Да ли желите да то дозволите?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "Овај веб сајт жели да направи пречицу на листи програма. Да ли желите да то дозволите?",
+ "string-title-simple": "Креирај пречице апликација",
+ "string-ok-accesskey": "P",
+ "string-ok": "Потврди",
+ "string-cancel-accesskey": "О",
+ "string-cancel": "Откажи",
+ "string-startmenu": "Старт мени",
+ "string-yes": "Да",
+ "string-never-allow": "Никада не дозволи ову пречицу",
+ "string-yes-accesskey": "D",
+ "string-quicklaunch": "Трака за брзо покретање",
+ "string-never-allow-wince": "Никада не дозволи",
+ "string-desktop": "Радна површина",
+ "string-title-default": "Gears - направи пречицу на радној површини",
+ "string-location-query": "Hаправи пречице на следећим локацијама:"
+ },
+ "ko": {
+ "string-no": "아니요",
+ "string-header-desktop": "웹사이트에서 사용자 컴퓨터에 바로가기를 만들려고 합니다. 허용하시겠습니까?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "웹사이트에서 프로그램 목록에 바로가기를 만들려고 합니다. 바로가기를 만드시겠습니까?",
+ "string-title-simple": "애플리케이션 바로가기 만들기",
+ "string-ok-accesskey": "O",
+ "string-ok": "확인",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "취소",
+ "string-startmenu": "시작 메뉴",
+ "string-yes": "예",
+ "string-never-allow": "바로가기를 허용하지 않음",
+ "string-yes-accesskey": "Y",
+ "string-quicklaunch": "빠른실행 표시줄",
+ "string-never-allow-wince": "허용 안함",
+ "string-desktop": "바탕화면",
+ "string-title-default": "Gears - 바탕화면 바로가기 만들기",
+ "string-location-query": "바로가기 생성 위치:"
+ },
+ "sv": {
+ "string-no": "Nej",
+ "string-header-desktop": "Webbplatsen försöker skapa en genväg i datorn. Vill du tillåta det?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "Den här webbplatsen vill skapa en genväg i din programlista. Vill du tillåta det?",
+ "string-title-simple": "Skapa programgenvägar",
+ "string-ok-accesskey": "O",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "A",
+ "string-cancel": "Avbryt",
+ "string-startmenu": "Start-meny",
+ "string-yes": "Ja",
+ "string-never-allow": "Tillåt aldrig den här genvägen",
+ "string-yes-accesskey": "J",
+ "string-quicklaunch": "Fältet Snabbstart",
+ "string-never-allow-wince": "Tillåt aldrig",
+ "string-desktop": "Skrivbord",
+ "string-title-default": "Gears - Skapa genväg på skrivbordet",
+ "string-location-query": "Skapa genvägar på följande platser:"
+ },
+ "ur": {
+ "string-no": "نہیں",
+ "string-header-desktop": "یہ ویب سائٹ آپ کے کمپیوٹر پر ایک شارٹ کٹ بنانا چاہتی ہے۔ کیا آپ اس کی اجازت دیں گے؟",
+ "string-no-accesskey": "نہیں",
+ "string-header-wince": "یہ ویب سائٹ آپ کے پروگرامز کی فہرست میں درج ذیل شارٹ کٹ بنانا چاہتی ہے۔ کیا آپ اس کی اجازت دیں گے؟",
+ "string-title-simple": "اطلاق کا شارٹ کٹ بنائیں",
+ "string-ok-accesskey": "O",
+ "string-ok": "ٹھیک ہے",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "منسوخ",
+ "string-startmenu": "اسٹارٹ مینو",
+ "string-yes": "ہاں",
+ "string-never-allow": "کبھی بھی اس شارٹ کٹ کی اجازت نہ دیں۔",
+ "string-yes-accesskey": "ہاں",
+ "string-quicklaunch": "فوری شروع کرنے کا بار",
+ "string-never-allow-wince": "کبھی بھی اجازت نہ دیں",
+ "string-desktop": "ڈیسک ٹاپ",
+ "string-title-default": "گیئرز - ڈیسک ٹاپ شارٹ کٹ بنائیں",
+ "string-location-query": "درج ذیل مقامات پر شارٹ کٹ بنائیں:"
+ },
+ "sk": {
+ "string-no": "Nie",
+ "string-header-desktop": "Táto webová lokalita chce vytvoriť odkaz v počítači. Chcete to povoliť?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "Táto webová lokalita chce vytvoriť odkaz v zozname programov. Chcete to povoliť?",
+ "string-title-simple": "Vytvorte odkazy na aplikácie",
+ "string-ok-accesskey": "K",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "R",
+ "string-cancel": "Zrušiť",
+ "string-startmenu": "Ponuka Štart",
+ "string-yes": "Áno",
+ "string-never-allow": "Nikdy nepovoliť tento odkaz",
+ "string-yes-accesskey": "O",
+ "string-quicklaunch": "Panel s nástrojmi Rýchly prístup",
+ "string-never-allow-wince": "Nikdy nepovoliť",
+ "string-desktop": "Pracovná plocha",
+ "string-title-default": "Gears - Vytvoriť odkaz na pracovnej ploche",
+ "string-location-query": "Vytvoriť odkazy na nasledujúcich miestach:"
+ },
+ "zh-CN": {
+ "string-no": "否",
+ "string-header-desktop": "该网站要在您的计算机上创建快捷键。 是否允许该操作?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "该网站要在您的程序列表中创建快捷方式。 是否允许该操作?",
+ "string-title-simple": "创建应用程序快捷键",
+ "string-ok-accesskey": "O",
+ "string-ok": "确定",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "取消",
+ "string-startmenu": "开始菜单",
+ "string-yes": "是",
+ "string-never-allow": "永不允许该快捷方式",
+ "string-yes-accesskey": "Y",
+ "string-quicklaunch": "快速启动栏",
+ "string-never-allow-wince": "永不允许",
+ "string-desktop": "桌面",
+ "string-title-default": "Gears - 创建桌面快捷方式",
+ "string-location-query": "在以下位置创建快捷键:"
+ },
+ "de": {
+ "string-no": "Nein",
+ "string-header-desktop": "Für diese Website soll eine Verknüpfung auf Ihrem Computer erstellt werden. Möchten Sie dies zulassen?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "Für diese Website soll eine Verknüpfung in Ihrer Programmliste erstellt werden. Möchten Sie dies zulassen?",
+ "string-title-simple": "Anwendungsverknüpfungen erstellen",
+ "string-ok-accesskey": "O",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "A",
+ "string-cancel": "Abbrechen",
+ "string-startmenu": "Startmenü",
+ "string-yes": "Ja",
+ "string-never-allow": "Diese Verknüpfung nie zulassen",
+ "string-yes-accesskey": "J",
+ "string-quicklaunch": "Schnellstartleiste",
+ "string-never-allow-wince": "Nie zulassen",
+ "string-desktop": "Desktop",
+ "string-title-default": "Gears - Desktopverknüpfung erstellen",
+ "string-location-query": "Verknüpfungen unter folgenden Speicherpfaden erstellen:"
+ },
+ "pl": {
+ "string-no": "Nie",
+ "string-header-desktop": "Ta witryna internetowa chce utworzyć skrót na Twoim komputerze. Czy chcesz na to zezwolić?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "Ta witryna chce utworzyć skrót na Twojej liście programów. Czy chcesz na to zezwolić?",
+ "string-title-simple": "Utwórz skróty do aplikacji",
+ "string-ok-accesskey": "O",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "A",
+ "string-cancel": "Anuluj",
+ "string-startmenu": "Menu Start",
+ "string-yes": "Tak",
+ "string-never-allow": "Nigdy nie zezwalaj na ten skrót",
+ "string-yes-accesskey": "T",
+ "string-quicklaunch": "Pasek szybkiego uruchamiania",
+ "string-never-allow-wince": "Nigdy nie zezwalaj",
+ "string-desktop": "Pulpit",
+ "string-title-default": "Gears — utwórz skrót na pulpicie",
+ "string-location-query": "Utwórz skróty w następujących lokalizacjach:"
+ },
+ "ms": {
+ "string-no": "Tidak",
+ "string-header-desktop": "Laman web ini mahu membuat pintasan pada komputer anda. Adakah anda membenarkannya?",
+ "string-no-accesskey": "T",
+ "string-header-wince": "Laman web ini mahu membuat pintasan dalam senarai atur cara anda. Adakah anda hendak membenarkannya?",
+ "string-title-simple": "Buat Pintasan Aplikasi",
+ "string-ok-accesskey": "O",
+ "string-ok": "OK",
+ "string-cancel-accesskey": "B",
+ "string-cancel": "Batal",
+ "string-startmenu": "Menu mula",
+ "string-yes": "Ya",
+ "string-never-allow": "Jangan sekali-kali benarkan pintasan ini",
+ "string-yes-accesskey": "Y",
+ "string-quicklaunch": "Bar lancar cepat",
+ "string-never-allow-wince": "Jangan sekali-kali benarkan",
+ "string-desktop": "Desktop",
+ "string-title-default": "Gear - Buat Pintasan Desktop",
+ "string-location-query": "Buat pintasan dalam lokasi yang berikut:"
+ },
+ "sl": {
+ "string-no": "Ne",
+ "string-header-desktop": "To spletno mesto želi v vašem računalniku ustvariti bližnjico. Ali želite to dejanje dovoliti?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "To spletno mesto skuša na seznamu programov ustvariti bližnjico. Ali želite to dejanje dovoliti?",
+ "string-title-simple": "Ustvarite bližnjice za programe",
+ "string-ok-accesskey": "O",
+ "string-ok": "V redu",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "Prekliči",
+ "string-startmenu": "Meni »Start«",
+ "string-yes": "Da",
+ "string-never-allow": "Nikoli ne dovoli te bližnjice",
+ "string-yes-accesskey": "D",
+ "string-quicklaunch": "Vrstica za hitri zagon",
+ "string-never-allow-wince": "Nikoli ne dovoli",
+ "string-desktop": "Namizje",
+ "string-title-default": "Gears – Ustvarjanje bližnjice na namizju",
+ "string-location-query": "Ustvari bližnjice na teh mestih:"
+ },
+ "mr": {
+ "string-no": "नाही",
+ "string-header-desktop": "ही वेबसाइट आपल्या संगणकावर शॉर्टकट तयार करू इच्छिते. यास आपली अनुमती आहे?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "ही वेबसाइट आपल्या प्रोग्राम्स सूचीमध्ये एक शॉर्टकट तयार करू इच्छित आहे. आपण यास परवानगी देऊ इच्छिता काय?",
+ "string-title-simple": "एप्लिकेशन शॉर्टकट तयार करा",
+ "string-ok-accesskey": "O",
+ "string-ok": "ओके",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "रद्द करा",
+ "string-startmenu": "स्टार्ट मेनू",
+ "string-yes": "होय",
+ "string-never-allow": "या शॉर्टकटला कधीही परवानगी देऊ नये",
+ "string-yes-accesskey": "Y",
+ "string-quicklaunch": "क्विक लाँच बार",
+ "string-never-allow-wince": "कधीही परवानगी देऊ नये",
+ "string-desktop": "डेस्कटॉप",
+ "string-title-default": "Gears - डेस्कटॉप शॉर्टकट तयार करा",
+ "string-location-query": "खालील स्थानांवर शॉर्टकट तयार करा:"
+ },
+ "or": {
+ "string-no": "ନା",
+ "string-header-desktop": "ଏହି ୱେବସାଇଟ୍ ଆପଣଙ୍କ କମ୍ପ୍ୟୁଟରରେ ଏକ ସର୍ଟକଟ ସୃଷ୍ଟି କରିବାକୁ ଚାହୁଁଛି ଆପଣ ଏହାକୁ ଅନୁମତି ଦେବାକୁ ଚାହୁଁଛନ୍ତି ?",
+ "string-no-accesskey": "N",
+ "string-header-wince": "ଏହି ୱେବସାଇଟ୍ ଆପଣଙ୍କର ପ୍ରୋଗ୍ରାମ୍ ତାଲିକାରେ ଏକ ସର୍ଟକଟ୍ ସୃଷ୍ଟି କରିବାକୁ ଚାହୁଁଛି । ଆପଣ ଏହାକୁ ଅନୁମତି କରିବାକୁ ଚାହୁଁଛନ୍ତି କି?",
+ "string-title-simple": "ଅନୁପ୍ରୟୋଗ ସର୍ଟକଟଗୁଡିକ ସୃଷ୍ଟି କରନ୍ତୁ",
+ "string-ok-accesskey": "O",
+ "string-ok": "ଓକେ",
+ "string-cancel-accesskey": "C",
+ "string-cancel": "ବାତିଲ୍",
+ "string-startmenu": "ଷ୍ଟାର୍ଟ ମେନୁ",
+ "string-yes": "ହଁ",
+ "string-never-allow": "ଏହି ସର୍ଟକଟ୍ କେବେ ମଧ୍ୟ ଅନୁମତି କରନ୍ତୁ ନାହିଁ",
+ "string-yes-accesskey": "Y",
+ "string-quicklaunch": "କୁଇକ୍ ଲଞ୍ଚ୍ ବାର୍",
+ "string-never-allow-wince": "କେବେ ମଧ୍ୟ ଅନୁମତି କରନ୍ତୁ ନାହିଁ",
+ "string-desktop": "ଡେସ୍କଟପ୍",
+ "string-title-default": "Gears - ଡେସ୍କଟପ୍ ସର୍ଟକଟ୍ ସୃଷ୍ଟି କରନ୍ତୁ",
+ "string-location-query": "ନିମ୍ନ ଅବସ୍ଥାନ ଗୁଡିକରେ ସର୍ଟକଟ ସୃଷ୍ଟି କରନ୍ତୁ:"
+ }
+};
+
+// Insert all localized strings for the specified locale into the div or span
+// matching the id.
+function loadI18nStrings(locale) {
+ var rtl_languages = ['he', 'ar', 'fa', 'ur'];
+
+ if (!locale) {
+ locale = 'en-US';
+ } else {
+ if (!localized_strings[locale]) {
+ // For xx-YY locales, determine what the base locale is.
+ var base_locale = locale.split('-')[0];
+
+ if (localized_strings[base_locale]) {
+ locale = base_locale;
+ } else {
+ locale = 'en-US';
+ }
+ }
+ }
+
+ var strings = localized_strings[locale];
+
+ // If the specified locale is an right to left language, change the direction
+ // of the page.
+ for (index in rtl_languages) {
+ if (locale == rtl_languages[index]) {
+ document.body.dir = "rtl";
+ break;
+ }
+ }
+
+ // Copy each string to the proper UI element, if it exists.
+ for (name in strings) {
+ if (name == 'string-html-title') {
+ if (!browser.ie_mobile) {
+ // IE Mobile dialogs don't have a title bar.
+ // Furthermore, document.title is undefined in IE Mobile on WinMo 5.
+ // It's also impossible to add properties to the window object.
+ // (see http://code.google.com/apis/gears/mobile.html)
+ document.title = strings[name];
+ }
+ } else {
+ var element = dom.getElementById(name);
+ if (element) {
+ element.innerHTML = strings[name];
+ }
+ }
+ }
+}
+
+
+</script>
+
+<script>
+ var debug = false;
+ var dialogMinHeight = 200;
+
+ // We currently only support custom locations for desktop windows.
+ var locationsEnabled = browser.windows && !browser.ie_mobile;
+
+ var args;
+
+ if (debug) {
+ // Handy for debugging layout:
+ if (browser.ie_mobile) {
+ window.pie_dialog = new Object();
+ window.pie_dialog.IsSmartPhone = function() { return false; };
+ window.pie_dialog.SetCancelButton = function() {};
+ window.pie_dialog.SetButton = function() {};
+ window.pie_dialog.SetButtonEnabled = function() {};
+ window.pie_dialog.SetScriptContext = function() {};
+ window.pie_dialog.ResizeDialog = function() {};
+ }
+
+ args = {
+ locale: "en-US",
+ style: "simple",
+ name: "My Application",
+ link: "http://www.google.com/",
+ description: "This application does things does things!",
+ // description: "This application does things does things that do things that do things that do things that do things that do things that do things that do things that do things that do things that do things that do things that do things that do things that do things that do things that do things that do things that do things.",
+ icon16x16: "http://google-gears.googlecode.com/svn/trunk/gears/test/manual/shortcuts/16.png",
+ icon32x32: "http://google-gears.googlecode.com/svn/trunk/gears/test/manual/shortcuts/32.png",
+ icon48x48: "http://google-gears.googlecode.com/svn/trunk/gears/test/manual/shortcuts/48.png",
+ icon128x128: "http://google-gears.googlecode.com/svn/trunk/gears/test/manual/shortcuts/128.png"
+ };
+ } else {
+ args = getArguments();
+ }
+
+ loadI18nStrings(args.locale);
+
+ initDialog();
+ initLayout();
+ initShortcuts(args);
+ resizeDialogToFitContent(dialogMinHeight);
+
+ function initLayout() {
+ if (locationsEnabled) {
+ dom.getElementById("locations").style.display = "block";
+ }
+
+ // Set up the rest of the layout. The details vary based on the layout style
+ // and the user agent (currently "simple" style only supported by desktop
+ // Gears).
+ if (isDefined(typeof args.style) && args.style == "simple") {
+ initSimpleStyle();
+ } else if (browser.ie_mobile) {
+ initPieStyle();
+ } else {
+ initDefaultStyle();
+ }
+
+ resetConfirmDisabledState();
+ }
+
+ function initSimpleStyle() {
+ document.title =
+ dom.getTextContent(dom.getElementById("string-title-simple"));
+ dom.getElementById("icon").parentNode.style.display = "none";
+ dom.getElementById("header").style.display = "none";
+ dom.getElementById("head").style.paddingBottom = "0";
+ dom.getElementById("deny-permanently-link").style.display = "none";
+ setButtonLabel("string-ok", "allow-button", "string-ok-accesskey");
+ setButtonLabel("string-cancel", "deny-button", "string-cancel-accesskey");
+
+ // Also check the desktop checkbox by default
+ dom.getElementById("location-desktop").checked = true;
+
+ // Turn on the buttons. They are turned off by default for CustomButton, but
+ // simple style does not use custom button.
+ var buttons = document.getElementsByTagName("button");
+ for (var i = 0, button; button = buttons[i]; i++) {
+ button.className = "";
+ button.style.visibility = "visible";
+ }
+ }
+
+ function initPieStyle() {
+ // For PIE, we don't set a window title.
+ setElementContents("string-header-wince", "header");
+
+ if (window.pie_dialog.IsSmartPhone()) {
+ // On softkey-only devices we only use a regular link to trigger
+ // the "never-allow" action. For "allow" and "deny" we use only
+ // the softkey lables.
+ setElementContents("string-never-allow-wince", "deny-permanently-link");
+ } else {
+ // For touchscreen devices, we use buttons for all actions. Additionally,
+ // we also set the sofkey labels.
+ setButtonLabel("string-yes", "allow-button", "string-yes-accesskey");
+ setButtonLabel("string-no", "deny-button", "string-no-accesskey");
+ setButtonLabel("string-never-allow-wince", "deny-permanently-button");
+ }
+ // Set the softkey labels for both softkey-only and touchscreen UIs.
+ window.pie_dialog.SetButton(dom.getElementById("string-yes").innerText,
+ "allowShortcutsTemporarily();");
+ window.pie_dialog.SetCancelButton(dom.getElementById("string-no").innerText);
+ // On PIE, the allow button is disabled by default.
+ // Why not just remove the disabled attribute?
+ // Because on WinCE, we need to also call
+ // window.pie_dialog.SetButtonEnabled(true); , which enables the right
+ // softkey button.
+ enableButton(dom.getElementById("allow-button"));
+ }
+
+ function initDefaultStyle() {
+ document.title =
+ dom.getTextContent(dom.getElementById("string-title-default"));
+ setElementContents("string-header-desktop", "header");
+ setButtonLabel("string-yes", "allow-button", "string-yes-accesskey");
+ setButtonLabel("string-no", "deny-button", "string-no-accesskey");
+ setElementContents("string-never-allow", "deny-permanently-link");
+
+ CustomButton.initializeAll();
+ }
+
+ /**
+ * Populate the shortcuts UI based on the data passed in from C++.
+ */
+ function initShortcuts(shortcutData) {
+ var iconElement = dom.getElementById("shortcut-icon");
+ var nameElement = dom.getElementById("shortcut-name");
+ var descriptionElement = dom.getElementById("shortcut-description");
+
+ loadImage(iconElement, pickIconToRender(shortcutData));
+ dom.setTextContent(nameElement, shortcutData.name);
+
+ if (isDefined(typeof shortcutData.description) &&
+ shortcutData.description) {
+ dom.setTextContent(descriptionElement, shortcutData.description);
+ descriptionElement.style.display = "block";
+ }
+
+ preloadIcons(shortcutData);
+ }
+
+ /**
+ * Picks the best icon to render in the UI.
+ */
+ function pickIconToRender(shortcutData) {
+ if (shortcutData.icon32x32) { // ideal size
+ return shortcutData.icon32x32;
+ } else if (shortcutData.icon48x48) { // better to pick one that is too big
+ return shortcutData.icon48x48;
+ } else if (shortcutData.icon128x128) {
+ return shortcutData.icon128x128;
+ } else {
+ return shortcutData.icon16x16; // pick too small icon last
+ }
+ }
+
+ /**
+ * Preloads the icons for a shortcut so that later when they are requested
+ * from C++, they will be fast.
+ */
+ function preloadIcons(shortcutData) {
+ for (var prop in shortcutData) {
+ if (prop.indexOf("icon") == 0) {
+ var img = new Image();
+ img.src = shortcutData[prop];
+ }
+ }
+ }
+
+ /**
+ * Sets the confirm button's disabled state depending on whether there is
+ * at least one checkbox checked.
+ */
+ function resetConfirmDisabledState() {
+ if (!locationsEnabled) {
+ return;
+ }
+
+ var allowButton = dom.getElementById("allow-button");
+ var checkboxes =
+ dom.getElementById("locations").getElementsByTagName("input");
+
+ for (var i = 0, checkbox; checkbox = checkboxes[i]; i++) {
+ if (checkbox.checked) {
+ enableButton(allowButton);
+ return;
+ }
+ }
+
+ disableButton(allowButton);
+ }
+
+ /**
+ * Called when the user clicks the allow button.
+ */
+ function allowShortcutsTemporarily() {
+ var result = {
+ allow: true,
+ locations: 0
+ };
+
+ if (locationsEnabled) {
+ var checkboxes =
+ dom.getElementById("locations").getElementsByTagName("input");
+ for (var i = 0, checkbox; checkbox = checkboxes[i]; i++) {
+ if (checkbox.checked) {
+ result.locations |= parseInt(checkbox.value);
+ }
+ }
+ }
+ saveAndClose(result);
+ }
+
+ /**
+ * Called when the user clicks the no button.
+ */
+ function denyShortcutsTemporarily() {
+ saveAndClose(null); // default behavior is to deny all
+ }
+
+ /**
+ * Called when the user clicks the "Never allow this shortcut" link.
+ */
+ function denyShortcutPermanently() {
+ saveAndClose({
+ allow: false
+ });
+ }
+
+ /**
+ * Set the contents of the element specified by destID from from that of the
+ * element specified by sourceID.
+ */
+ function setElementContents(sourceID, destID) {
+ var sourceElem = dom.getElementById(sourceID);
+ var destElem = dom.getElementById(destID);
+ if (isDefined(typeof sourceElem) && isDefined(typeof destElem)) {
+ destElem.innerHTML = sourceElem.innerHTML;
+ }
+ }
+</script>
+</html>
diff --git a/assets/plugins/gears.so b/assets/plugins/gears.so
new file mode 100644
index 00000000..1d0ba0e8
--- /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/dialog_frame.9.png b/res/drawable/dialog_frame.9.png
new file mode 100644
index 00000000..3d41e2f1
--- /dev/null
+++ b/res/drawable/dialog_frame.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/ic_browser_done.png b/res/drawable/ic_browser_done.png
new file mode 100644
index 00000000..3f300720
--- /dev/null
+++ b/res/drawable/ic_browser_done.png
Binary files differ
diff --git a/res/drawable/ic_browser_down.png b/res/drawable/ic_browser_down.png
new file mode 100755
index 00000000..e3747a76
--- /dev/null
+++ b/res/drawable/ic_browser_down.png
Binary files differ
diff --git a/res/drawable/ic_browser_up.png b/res/drawable/ic_browser_up.png
new file mode 100755
index 00000000..6ea5a813
--- /dev/null
+++ b/res/drawable/ic_browser_up.png
Binary files differ
diff --git a/res/drawable/ic_dialog_alert.png b/res/drawable/ic_dialog_alert.png
new file mode 100755
index 00000000..0a7de047
--- /dev/null
+++ b/res/drawable/ic_dialog_alert.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_info.png b/res/drawable/ic_dialog_info.png
new file mode 100755
index 00000000..e8b02299
--- /dev/null
+++ b/res/drawable/ic_dialog_info.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..63f5d8fb
--- /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_back.png b/res/drawable/ic_menu_back.png
new file mode 100644
index 00000000..5ce50ebf
--- /dev/null
+++ b/res/drawable/ic_menu_back.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_clear_playlist.png b/res/drawable/ic_menu_clear_playlist.png
new file mode 100644
index 00000000..750db627
--- /dev/null
+++ b/res/drawable/ic_menu_clear_playlist.png
Binary files differ
diff --git a/res/drawable/ic_menu_forward.png b/res/drawable/ic_menu_forward.png
new file mode 100644
index 00000000..0936fac4
--- /dev/null
+++ b/res/drawable/ic_menu_forward.png
Binary files differ
diff --git a/res/drawable/ic_menu_goto.png b/res/drawable/ic_menu_goto.png
new file mode 100644
index 00000000..40183ebc
--- /dev/null
+++ b/res/drawable/ic_menu_goto.png
Binary files differ
diff --git a/res/drawable/ic_menu_home.png b/res/drawable/ic_menu_home.png
new file mode 100644
index 00000000..34943f66
--- /dev/null
+++ b/res/drawable/ic_menu_home.png
Binary files differ
diff --git a/res/drawable/ic_menu_recent_history.png b/res/drawable/ic_menu_recent_history.png
new file mode 100644
index 00000000..4ccae5d1
--- /dev/null
+++ b/res/drawable/ic_menu_recent_history.png
Binary files differ
diff --git a/res/drawable/ic_menu_refresh.png b/res/drawable/ic_menu_refresh.png
new file mode 100644
index 00000000..77d70dd4
--- /dev/null
+++ b/res/drawable/ic_menu_refresh.png
Binary files differ
diff --git a/res/drawable/ic_menu_stop.png b/res/drawable/ic_menu_stop.png
new file mode 100644
index 00000000..4fc825cd
--- /dev/null
+++ b/res/drawable/ic_menu_stop.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_browser.png b/res/drawable/ic_search_browser.png
new file mode 100644
index 00000000..98761177
--- /dev/null
+++ b/res/drawable/ic_search_browser.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/map.xml b/res/drawable/map.xml
new file mode 100644
index 00000000..84d59039
--- /dev/null
+++ b/res/drawable/map.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/drawable/checkbox.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.
+*/
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true" android:drawable="@drawable/mapfocused"/>
+ <item android:drawable="@drawable/mapunfocused"/>
+</selector>
diff --git a/res/drawable/mapfocused.png b/res/drawable/mapfocused.png
new file mode 100644
index 00000000..83efcac2
--- /dev/null
+++ b/res/drawable/mapfocused.png
Binary files differ
diff --git a/res/drawable/mapunfocused.png b/res/drawable/mapunfocused.png
new file mode 100644
index 00000000..736dc686
--- /dev/null
+++ b/res/drawable/mapunfocused.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/drawable/urlbar_background.9.png b/res/drawable/urlbar_background.9.png
new file mode 100644
index 00000000..38583b39
--- /dev/null
+++ b/res/drawable/urlbar_background.9.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..f08dd7de
--- /dev/null
+++ b/res/layout-land/page_info.xml
@@ -0,0 +1,66 @@
+<?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: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>
+
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..c8eb2207
--- /dev/null
+++ b/res/layout/add_new_bookmark.xml
@@ -0,0 +1,52 @@
+<?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:orientation="horizontal"
+ android:paddingTop="0dip"
+ android:paddingBottom="0dip"
+ >
+ <ImageView android:id="@+id/favicon"
+ android:layout_width="48dip"
+ android:layout_height="48dip"
+ android:layout_marginRight="6dip"
+ android:focusable="false"
+ android:src="@android:drawable/ic_menu_add"
+ android:scaleType="fitCenter"
+ />
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center_vertical"
+ >
+ <TextView android:id="@+id/title"
+ android:text="@string/add_new_bookmark"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+ <TextView android:id="@+id/url"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:maxLines="1"
+ android:scrollHorizontally="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+ </LinearLayout>
+</LinearLayout>
diff --git a/res/layout/bookmark_item.xml b/res/layout/bookmark_item.xml
new file mode 100644
index 00000000..7d544476
--- /dev/null
+++ b/res/layout/bookmark_item.xml
@@ -0,0 +1,57 @@
+<?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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="10dip"
+ android:paddingTop="5dip"
+ android:paddingBottom="5dip"
+ >
+
+ <!-- note: to center icon vertically, replace the alignParentTop
+ below with android:layout_centerVertical="true" -->
+
+ <ImageView android:id="@+id/favicon"
+ android:layout_width="20dip"
+ android:layout_height="20dip"
+ android:focusable="false"
+ android:padding="2dip"
+ android:layout_marginTop="4dip"
+ android:layout_marginRight="6dip"
+ android:layout_alignParentTop="true"
+ android:background="@drawable/fav_icn_background"
+ android:src="@drawable/app_web_browser_sm"
+ android:layout_alignParentLeft="true"
+ />
+ <TextView android:id="@+id/title"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:maxLines="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@+id/favicon"
+ 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:layout_below="@+id/title"
+ android:layout_toRightOf="@+id/favicon"
+ android:ellipsize="end"
+ />
+</RelativeLayout>
diff --git a/res/layout/browser_add_bookmark.xml b/res/layout/browser_add_bookmark.xml
new file mode 100644
index 00000000..085a1183
--- /dev/null
+++ b/res/layout/browser_add_bookmark.xml
@@ -0,0 +1,101 @@
+<?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:scrollHorizontally="true"
+ android:autoText="false"
+ android:capitalize="sentences"
+ android:gravity="fill_horizontal"
+ 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:scrollHorizontally="true"
+ android:autoText="false"
+ android:capitalize="none"
+ android:hint="@string/http"
+ android:gravity="fill_horizontal"
+ 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..b1da8f19
--- /dev/null
+++ b/res/layout/browser_find.xml
@@ -0,0 +1,128 @@
+<?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:background="@android:drawable/menu_full_frame"
+ android:paddingTop="12dip"
+ >
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginLeft="6dip"
+ android:layout_marginRight="6dip"
+ >
+ <ImageButton android:id="@+id/done"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_browser_done"
+ />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/done"
+ android:layout_gravity="center_horizontal"
+ android:textColor="?android:attr/textColorPrimaryInverse"
+ />
+ </LinearLayout>
+
+ <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:autoText="false"
+ android:capitalize="none"
+ android:hint="@string/find_dot"
+ />
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:orientation="horizontal"
+ android:layout_gravity="center_horizontal"
+ android:paddingTop="1dip"
+ >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="3dip"
+ android:text="@string/matches_found"
+ android:textColor="?android:attr/textColorPrimaryInverse"
+ />
+ <TextView android:id="@+id/matches"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/zero"
+ android:textColor="?android:attr/textColorPrimaryInverse"
+ />
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginRight="6dip"
+ >
+ <ImageButton
+ android:src="@drawable/ic_browser_up"
+ android:id="@+id/previous"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/prev"
+ android:layout_gravity="center_horizontal"
+ android:textColor="?android:attr/textColorPrimaryInverse"
+ />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginRight="6dip"
+ >
+ <ImageButton
+ android:src="@drawable/ic_browser_down"
+ android:id="@+id/next"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/next"
+ android:layout_gravity="center_horizontal"
+ android:textColor="?android:attr/textColorPrimaryInverse"
+ />
+ </LinearLayout>
+
+</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/cache_cleared.xml b/res/layout/cache_cleared.xml
new file mode 100644
index 00000000..a65183cf
--- /dev/null
+++ b/res/layout/cache_cleared.xml
@@ -0,0 +1,31 @@
+<!--
+/*
+ * 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.
+ */
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="50dip"
+ android:layout_marginTop="50dip"
+ android:layout_marginRight="50dip"
+ android:layout_marginBottom="50dip"
+ android:text="@string/cache_cleared"
+ />
+</LinearLayout>
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/history_item.xml b/res/layout/history_item.xml
new file mode 100644
index 00000000..1d51ad1c
--- /dev/null
+++ b/res/layout/history_item.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="64dip"
+ android:orientation="vertical"
+ android:paddingRight="6dip"
+ android:paddingTop="8dip"
+ android:paddingLeft="4dip"
+ android:background="@color/translucent_white"
+ >
+ <TextView android:id="@+id/title"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:maxLines="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:drawablePadding="4dip"
+ />
+ <TextView android:id="@+id/url"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:maxLines="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="24dip"
+ android:ellipsize="end"
+ />
+</LinearLayout>
diff --git a/res/layout/http_authentication.xml b/res/layout/http_authentication.xml
new file mode 100644
index 00000000..125095ee
--- /dev/null
+++ b/res/layout/http_authentication.xml
@@ -0,0 +1,72 @@
+<?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:autoText="false"
+ android:capitalize="none"
+ 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:autoText="false"
+ android:capitalize="none"
+ android:gravity="fill_horizontal"
+ android:layout_weight="1"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="12dip"
+ android:password="true" />
+</TableLayout>
diff --git a/res/layout/js_prompt.xml b/res/layout/js_prompt.xml
new file mode 100644
index 00000000..9ab9d09f
--- /dev/null
+++ b/res/layout/js_prompt.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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ >
+
+ <TextView android:id="@+id/message"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ />
+
+ <EditText android:id="@+id/value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textStyle="bold"
+ android:selectAllOnFocus="true"
+ android:scrollHorizontally="true"
+ android:layout_marginTop="6dip"
+ />
+
+</LinearLayout>
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..b10b72a6
--- /dev/null
+++ b/res/layout/page_info.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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ 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>
diff --git a/res/layout/search_bar.xml b/res/layout/search_bar.xml
new file mode 100644
index 00000000..fe5647e4
--- /dev/null
+++ b/res/layout/search_bar.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/res/layout/SearchBar.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.
+*/
+-->
+ <!-- Outer layout defines the entire search bar at the top of the screen -->
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/search_bar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="15dip"
+ android:paddingRight="15dip"
+ android:paddingTop="10dip"
+ android:paddingBottom="20dip"
+ android:baselineAligned="false"
+ android:background="@android:drawable/alert_dark_frame" >
+
+ <!-- TextView or ImageView at top shows searched application -->
+ <!-- Code will display one-or-the-other (or neither) -->
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="3dip">
+
+ <TextView
+ android:id="@+id/search_badge_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </FrameLayout>
+
+ <!-- Inner layout contains the button(s) and EditText -->
+ <LinearLayout
+ android:id="@+id/search_edit_frame"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:baselineAligned="false" >
+
+ <EditText
+ android:id="@+id/search_src_text"
+ android:layout_height="wrap_content"
+ android:layout_width="0dip"
+ android:layout_weight="1.0"
+ android:paddingLeft="6dip"
+ android:paddingRight="6dip"
+ android:singleLine="true"
+ android:textSize="14sp" />
+
+ <Button
+ android:id="@+id/search_go_btn"
+ android:text="@android:string/search_go"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+
+ </LinearLayout>
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..9edd6141
--- /dev/null
+++ b/res/layout/tabitem.xml
@@ -0,0 +1,54 @@
+<?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:layout_height="198dip"
+ android:orientation="vertical"
+ android:padding="4dip"
+ android:background="@android:drawable/gallery_thumb">
+
+ <com.android.browser.FakeWebView android:id="@+id/icon"
+ android:background="@color/black"
+ android:layout_width="145dip"
+ android:layout_height="190dip" />
+
+ <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="12dip"
+ android:paddingBottom="10dip"
+ android:paddingLeft="10dip"
+ android:paddingRight="10dip"
+ android:src="@android:drawable/btn_dialog" />
+
+</FrameLayout>
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..383b3b66
--- /dev/null
+++ b/res/menu/bookmarkscontext.xml
@@ -0,0 +1,36 @@
+<?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/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..9f30ad18
--- /dev/null
+++ b/res/menu/browser.xml
@@ -0,0 +1,105 @@
+<?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="@drawable/ic_menu_goto" />
+ <item android:id="@+id/search_menu_id"
+ android:title="@string/search"
+ 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/back_menu_id"
+ android:title="@string/back"
+ android:alphabeticShortcut="j"
+ android:icon="@drawable/ic_menu_back" />
+ <item android:id="@+id/close_menu_id"
+ android:title="@string/close"
+ android:alphabeticShortcut="w"
+ android:icon="@android:drawable/ic_menu_close_clear_cancel" />
+ <item android:id="@+id/forward_menu_id"
+ android:title="@string/forward"
+ android:alphabeticShortcut="k" />
+ <item android:id="@+id/homepage_menu_id"
+ android:title="@string/homepage"
+ android:alphabeticShortcut="&#32;"
+ android:icon="@drawable/ic_menu_home" />
+ <item android:id="@+id/classic_history_menu_id"
+ android:icon="@drawable/ic_menu_recent_history"
+ android:alphabeticShortcut="h"
+ android:title="@string/history" />
+ <item android:id="@+id/view_downloads_menu_id"
+ android:title="@string/menu_view_download" />
+<!--
+ Disabling the find option for version 1.0
+ <item android:id="@+id/find_menu_id"
+ android:title="@string/find_dot"
+ android:alphabeticShortcut="f" />
+-->
+ <item android:id="@+id/page_info_menu_id"
+ android:title="@string/page_info" />
+ <item android:id="@+id/bookmark_page_menu_id"
+ android:title="@string/menu_bookmark_page"/>
+ <item android:id="@+id/share_page_menu_id"
+ android:title="@string/share_page"/>
+ <item android:id="@+id/flip_orientation_menu_id"
+ android:title="@string/menu_flip_orientation" />
+ <item android:id="@+id/zoom_menu_id"
+ android:title="@string/menu_zoom" />
+ <item android:id="@+id/preferences_menu_id"
+ android:title="@string/menu_preferences" />
+ <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/dump_nav_menu_id"
+ android:title="@string/dump_nav"
+ android:visible="false" />
+ </group>
+ <group android:id="@+id/TAB_MENU">
+ <item android:id="@+id/new_tab_menu_id"
+ android:title="@string/tab_picker_new_tab"
+ android:icon="@android:drawable/ic_menu_add" />
+ <item android:id="@+id/bookmarks_tab_menu_id"
+ android:title="@string/bookmarks"
+ android:icon="@drawable/ic_menu_bookmark" />
+ <item android:id="@+id/history_tab_menu_id"
+ android:title="@string/history"
+ android:icon="@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="@drawable/ic_menu_stop"
+ android:visible="false" />
+ <item android:id="@+id/reload_menu_id"
+ android:title="@string/reload"
+ android:icon="@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..d1d68099
--- /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="@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..2471227d
--- /dev/null
+++ b/res/values-cs/strings.xml
@@ -0,0 +1,217 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="action">Přihlásit se</string>
+ <string name="activity_instrumentation_functional_test_runner">Spouštěč testu funkcí prohlížeče</string>
+ <string name="activity_instrumentation_test_runner">Spouštěč testu prohlížeče</string>
+ <string name="add_new_bookmark">Nová záložka</string>
+ <string name="back">Zpět</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="bookmark_saved">Uloženo do záložek</string>
+ <string name="bookmark_url_not_valid">Adresa URL je neplatná!</string>
+ <string name="bookmarks">Záložky</string>
+ <string name="browserFrame307Post">Tato webová stránka se nyní přesměruje. Chcete znovu odeslat zadaná data formuláře do nového umístění?</string>
+ <string name="browserFrameFileErrorLabel">Chyba souboru</string>
+ <string name="browserFrameFormResubmitLabel">Potvrdit</string>
+ <string name="browserFrameFormResubmitMessage">Stránka, kterou chcete zobrazit, obsahuje data POSTDATA. Pokud znovu odešlete data, zopakují se všechny činnosti provedené formulářem (např. hledání nebo nakupování online).</string>
+ <string name="browserFrameNetworkErrorLabel">Chyba sítě</string>
+ <string name="browserFrameRedirect">Přesměrovat</string>
+ <string name="browser_bookmarks_page_bookmarks_text">Záložky</string>
+ <string name="browser_history">Historie prohlížeče</string>
+ <string name="cache_cleared">Mezipaměť vymazána</string>
+ <string name="cancel">Storno</string>
+ <string name="clear">Vymazat</string>
+ <string name="close">Zavřít</string>
+ <string name="common_name">Obecné jméno:</string>
+ <string name="contextmenu_bookmark_thislink">Přidat odkaz do záložek</string>
+ <string name="contextmenu_copylink">Kopírovat odkaz</string>
+ <string name="contextmenu_download_image">Stáhnout obrázek</string>
+ <string name="contextmenu_openlink">Otevřít odkaz</string>
+ <string name="contextmenu_openlink_newwindow">Otevřít v novém okně</string>
+ <string name="current_page">Aktuální stránka: </string>
+ <string name="delete">Odstranit</string>
+ <string name="delete_all">Odstranit vše</string>
+ <string name="delete_all_bookmarks">Odstranit všechny záložky?</string>
+ <string name="delete_all_bookmarks_warning">Odstranit všechny záložky?</string>
+ <string name="delete_bookmark">Odstranit záložku?</string>
+ <string name="delete_bookmark_warning">Odstranit \"<xliff:g id="bookmark">%s</xliff:g>\"?</string>
+ <string name="do_not_save">Neukládat</string>
+ <string name="done">Hotovo</string>
+ <string name="download_cancel_dlg_msg">Všech <xliff:g id="download_count">%d</xliff:g> souborů ke stažení bude zrušeno a vymazáno z historie stahování</string>
+ <string name="download_cancel_dlg_title">Zrušit všechny soubory ke stažení?</string>
+ <string name="download_canceled">Zrušeno</string>
+ <string name="download_clear_dlg_msg">Všechny položky budou vymazány ze seznamu a odebrány z mezipaměti prohlížeče.</string>
+ <string name="download_clear_dlg_title">Vymazat historii stahování?</string>
+ <string name="download_error">Chyba</string>
+ <string name="download_file_error">Stahování nelze dokončit; není dostatek volného místa.</string>
+ <string name="download_file_error_dlg_msg">Soubor <xliff:g id="filename">%s</xliff:g> nelze stáhnout!\nUvolněte místo a akci zopakujte</string>
+ <string name="download_file_error_dlg_title">V zařízení je nedostatek volného místa!</string>
+ <string name="download_length_required">Nelze stáhnout; nelze zjistit velikost obsahu</string>
+ <string name="download_menu_cancel">Zrušit stahování</string>
+ <string name="download_menu_cancel_all">Zrušit stahování</string>
+ <string name="download_menu_clear">Vymazat ze seznamu</string>
+ <string name="download_menu_clear_all">Vymazat seznam</string>
+ <string name="download_menu_open">Otevřít</string>
+ <string name="download_no_sdcard_dlg_msg">Ke stažení souboru <xliff:g id="filename">%s</xliff:g> je požadována karta SD</string>
+ <string name="download_no_sdcard_dlg_title">Karta SD chybí</string>
+ <string name="download_not_acceptable">Nelze stáhnout; obsah není podporován</string>
+ <string name="download_pending">Zahájení stahování\u2026</string>
+ <string name="download_pending_network">Čekání na síť\u2026</string>
+ <string name="download_precondition_failed">Stahování bylo přerušeno a nelze jej obnovit</string>
+ <string name="download_running">Stahování </string>
+ <string name="download_running_paused">Čekání na síť\u2026 </string>
+ <string name="download_success">Stahování souboru <xliff:g id="file">%s</xliff:g> dokončeno</string>
+ <string name="download_title">Historie stahování</string>
+ <string name="download_unknown_filename">&lt;Neznámý&gt;</string>
+ <string name="dump_nav">Výpis mezipaměti navigace</string>
+ <string name="edit_bookmark">Upravit záložku</string>
+ <string name="empty_bookmark">"Nelze vytvořit prázdnou záložku!"</string>
+ <string name="find_dot">Najít\u2026</string>
+ <string name="forward">Předat dál</string>
+ <string name="goto_dot">Přejít\u2026</string>
+ <string name="history">Historie</string>
+ <string name="history_picker_view_web_page">Zobrazit webovou stránku</string>
+ <string name="homepage">Domovská stránka</string>
+ <string name="homepage_set">Domovská stránka aktualizována</string>
+ <string name="http">http://</string>
+ <string name="info_base">Název:\n <xliff:g id="title">%s1</xliff:g> \n\nAdresa URL: \n <xliff:g id="url">%s2</xliff:g></string>
+ <string name="issued_by">Vystavitel:</string>
+ <string name="issued_to">Vystaveno pro:</string>
+ <string name="js_dialog_title_default">JavaScript</string>
+ <string name="js_dialog_title_prefix">Stránka na adrese</string>
+ <string name="js_dialog_title_suffix">uvádí:</string>
+ <string name="loadSuspended">Načítání stránky je pozastaveno a bude pokračovat po obnovení připojení</string>
+ <string name="loadSuspendedTitle">Žádné pokrytí sítí!</string>
+ <string name="location">Umístění</string>
+ <string name="matches_found">Nalezené výsledky:</string>
+ <string name="menu_preferences">Nastavení</string>
+ <string name="menu_use_as_homepage">Nastavit jako domovskou stránku</string>
+ <string name="menu_view_download">Zobrazit soubory ke stažení</string>
+ <string name="menu_zoom">Lupa</string>
+ <string name="name">Název</string>
+ <string name="new_window">Nové okno</string>
+ <string name="next">Další</string>
+ <string name="no_database">Žádná databáze!</string>
+ <string name="ok">OK</string>
+ <string name="oma_error_failed_to_download">Stažení média se nezdařilo</string>
+ <string name="oma_error_insufficient_memory">Nedostatek paměti! Zkontrolujte kartu SD</string>
+ <string name="oma_error_invalid_descriptor">Neplatný popisovač stahování</string>
+ <string name="oma_error_non_acceptable_content">Obsah není podporován</string>
+ <string name="oma_label_action">Stáhnout</string>
+ <string name="oma_label_cancel">Konec</string>
+ <string name="oma_label_description">Popis:</string>
+ <string name="oma_label_download_error">Chyba stahování:</string>
+ <string name="oma_label_exit">Konec</string>
+ <string name="oma_label_name">Název:</string>
+ <string name="oma_label_proceed">Pokračovat</string>
+ <string name="oma_label_size">Velikost:</string>
+ <string name="oma_label_type">Typ:</string>
+ <string name="oma_label_vendor">Výrobce:</string>
+ <string name="oma_label_want_to_download">Chcete tento obsah stáhnout?</string>
+ <string name="oma_status_attribute_mismatch">Neshoda atributů</string>
+ <string name="oma_status_device_aborted">Zařízení přerušeno</string>
+ <string name="oma_status_insufficient_memory">Nedostatek paměti</string>
+ <string name="oma_status_invalid_descriptor">Neplatný popisovač</string>
+ <string name="oma_status_invalid_version">Neplatná verze</string>
+ <string name="oma_status_loader_error">Chyba zavaděče</string>
+ <string name="oma_status_loss_of_service">Ztráta služby</string>
+ <string name="oma_status_non_acceptable_content">Nepřijatelný obsah</string>
+ <string name="oma_status_success">Úspěch</string>
+ <string name="oma_warning_one_or_more_types_not_supported">Nejméně jedna kategorie obsahu není podporována.</string>
+ <string name="open_bookmark">Zobrazit webovou stránku</string>
+ <string name="open_in_new_window">Otevřít v novém okně</string>
+ <string name="org_name">Organizace:</string>
+ <string name="org_unit">Organizační jednotka:</string>
+ <string name="override">Nahradit</string>
+ <string name="override_message">Chcete nahradit existující záložku?</string>
+ <string name="password">Heslo</string>
+ <string name="pref_content_block_popups">Blokovat automaticky otevíraná okna</string>
+ <string name="pref_content_javascript">Povolit JavaScript</string>
+ <string name="pref_content_load_images">Načítat obrázky</string>
+ <string name="pref_content_load_images_summary">Obrázky budou načítány s webovými stránkami</string>
+ <string name="pref_content_open_in_background">Otevřít na pozadí</string>
+ <string name="pref_content_open_in_background_summary">Otevře nová okna na pozadí</string>
+ <string name="pref_content_title">Nastavení obsahu stránky</string>
+ <string name="pref_development_nav_dump">Povolit výpis mezipaměti navigace</string>
+ <string name="pref_development_normal_rendering">Normální zobrazování</string>
+ <string name="pref_development_single_column_rendering">Zobrazování v jednom sloupci</string>
+ <string name="pref_development_title">Ladit</string>
+ <string name="pref_development_trace">Povolit trasování</string>
+ <string name="pref_development_track">Povolit trackball</string>
+ <string name="pref_development_uastring">Řetězec Desktop UA</string>
+ <string name="pref_development_viewport">Používat širokou pracovní oblast</string>
+ <string name="pref_extras_reset_default">Obnovit nastavení</string>
+ <string name="pref_extras_reset_default_dlg">Obnovit výchozí nastavení?</string>
+ <string name="pref_extras_reset_default_summary">Obnovit výchozí nastavení všech hodnot</string>
+ <string name="pref_extras_title">Doplňky</string>
+ <string name="pref_plugin_enable">Povolit moduly plug-in</string>
+ <string name="pref_plugin_enable_summary">Povolí webům používat dostupné moduly plug-in</string>
+ <string name="pref_plugin_installed">Nainstalované moduly plug-in</string>
+ <string name="pref_plugin_installed_empty_list">Žádné nainstalované moduly plug-in</string>
+ <string name="pref_plugin_installed_summary">Zobrazit aktuálně nainstalované moduly plug-in</string>
+ <string name="pref_plugin_title">Moduly plug-in</string>
+ <string name="pref_privacy_clear_cache">Vymazat mezipaměť</string>
+ <string name="pref_privacy_clear_cache_dlg">Vymazat mezipaměť?</string>
+ <string name="pref_privacy_clear_cache_summary">Vymaže mezipaměť prohlížeče</string>
+ <string name="pref_privacy_clear_cookies">Vymazat soubory cookie</string>
+ <string name="pref_privacy_clear_cookies_dlg">Vymazat všechny soubory cookie?</string>
+ <string name="pref_privacy_clear_cookies_summary">Vymaže všechny soubory cookie prohlížeče</string>
+ <string name="pref_privacy_clear_form_data">Vymazat data formulářů</string>
+ <string name="pref_privacy_clear_form_data_dlg">Vymazat uložená data formulářů?</string>
+ <string name="pref_privacy_clear_form_data_summary">Vymaže všechna uložená data formulářů</string>
+ <string name="pref_privacy_clear_history">Vymazat historii</string>
+ <string name="pref_privacy_clear_history_dlg">Vymazat historii prohlížeče?</string>
+ <string name="pref_privacy_clear_history_summary">Vymaže historii navigace prohlížeče</string>
+ <string name="pref_privacy_clear_passwords">Vymazat hesla</string>
+ <string name="pref_privacy_clear_passwords_dlg">Vymazat všechna uložená hesla?</string>
+ <string name="pref_privacy_clear_passwords_summary">Vymaže všechna uložená hesla</string>
+ <string name="pref_privacy_title">Nastavení ochrany osobních údajů</string>
+ <string name="pref_security_accept_cookies">Přijímat soubory cookie</string>
+ <string name="pref_security_accept_cookies_summary">Povolí prohlížeči pracovat se soubory cookie</string>
+ <string name="pref_security_remember_passwords">Zapamatovat hesla</string>
+ <string name="pref_security_remember_passwords_summary">Povolí zapamatování uživatelských jmen a hesel na webech</string>
+ <string name="pref_security_save_form_data">Zapamatovat data formulářů</string>
+ <string name="pref_security_save_form_data_summary">Povolí zapamatování údajů zadaných do formulářů k pozdějšímu použití</string>
+ <string name="pref_security_show_security_warning">Zobrazovat upozornění zabezpečení</string>
+ <string name="pref_security_show_security_warning_summary">Zobrazí upozornění v případě potíží se zabezpečeným webem</string>
+ <string name="pref_security_title">Nastavení zabezpečení</string>
+ <string name="pref_text_size">Velikost textu</string>
+ <string name="pref_text_size_dialogtitle">Velikost textu</string>
+ <string name="pref_text_size_summary">Vyberte velikost textu</string>
+ <string name="prev">Předchozí</string>
+ <string name="question_mark">\?</string>
+ <string name="reload">Aktualizovat</string>
+ <string name="remove_bookmark">Odstranit záložku</string>
+ <string name="replace">Nahradit</string>
+ <string name="retry">Opakovat</string>
+ <string name="save">Uložit</string>
+ <string name="save_to_bookmarks">Uložit do záložek</string>
+ <string name="search_label">Prohlížeč</string>
+ <string name="security_warning">Upozornění zabezpečení</string>
+ <string name="sign_in_to">Přihlásit k <xliff:g id="hostname">%s1</xliff:g> \"<xliff:g id="realm">%s2</xliff:g>\"</string>
+ <string name="ssl_certificate">Certifikát</string>
+ <string name="ssl_certificate_is_valid">Tento certifikát je platný</string>
+ <string name="ssl_continue">Pokračovat</string>
+ <string name="ssl_expired">Platnost tohoto certifikátu vypršela</string>
+ <string name="ssl_mismatch">Neshoda názvu hostitele</string>
+ <string name="ssl_not_yet_valid">Tento certifikát ještě není platný</string>
+ <string name="ssl_untrusted">Tento certifikát nepochází od důvěryhodného certifikačního úřadu</string>
+ <string name="ssl_warnings_header">Došlo k potížím s certifikátem zabezpečení tohoto webu</string>
+ <string name="stop">Zastavit</string>
+ <string name="stopping">Zastavení\u2026</string>
+ <string name="tab_picker_bookmark">Záložka</string>
+ <string name="tab_picker_new_tab">Otevřít nové okno</string>
+ <string name="tab_picker_properties">Vlastnosti</string>
+ <string name="tab_picker_remove_tab">Zavřít</string>
+ <string name="tab_picker_send_url">Odeslat adresu URL</string>
+ <string name="tab_picker_title">Aktuální okna</string>
+ <string name="tab_picker_view_tab">Zobrazit</string>
+ <string name="username">Jméno</string>
+ <string name="view_certificate">Zobrazit certifikát</string>
+ <string name="view_tabs">Přehled okna</string>
+ <string name="view_tabs_condensed">Okna</string>
+ <string name="visual_history">Vizuální historie</string>
+ <string name="with">s</string>
+ <string name="zero">0</string>
+</resources>
diff --git a/res/values-de-rDE/strings.xml b/res/values-de-rDE/strings.xml
new file mode 100644
index 00000000..beff7cd1
--- /dev/null
+++ b/res/values-de-rDE/strings.xml
@@ -0,0 +1,270 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="action">Anmelden</string>
+ <string name="activity_instrumentation_functional_test_runner">Testlauf Browserfunktion</string>
+ <string name="activity_instrumentation_test_runner">Testlauf Browserfunktion</string>
+ <string name="add_new_bookmark">Neues Lesezeichen</string>
+ <string name="allow">Zulassen</string>
+ <string name="attention">Achtung</string>
+ <string name="back">Zurück</string>
+ <string name="block">Blockieren</string>
+ <string name="bookmark_needs_title">"Lesezeichen müssen einen Namen besitzen."</string>
+ <string name="bookmark_needs_url">"Lesezeichen müssen auf einen Ort weisen."</string>
+ <string name="bookmark_page">Zuletzt angesehene Seite mit Lesezeichen versehen</string>
+ <string name="bookmark_saved">Unter Lesezeichen gespeichert.</string>
+ <string name="bookmark_url_not_valid">URL ist nicht gültig.</string>
+ <string name="bookmarks">Lesezeichen</string>
+ <string name="browserFrame307Post">Für diese Webseite gibt es einen anderen Ort. Möchten Sie die im Formzlar eingegebenen Daten zu diesem Ort senden?</string>
+ <string name="browserFrameFileErrorLabel">Problem mit der Datei</string>
+ <string name="browserFrameFormResubmitLabel">Bestätigen</string>
+ <string name="browserFrameFormResubmitMessage">Die Seite, die Sie ansehen möchten, enthält Daten, die bereits übertragen wurden (\"POSTDATA\"). Wenn Sie die Daten erneut senden, werden alle Aktionen, die vom Formular auf der Seite veranlasst werden (z. B. eine Suche oder ein Online-Kauf) wiederholt.</string>
+ <string name="browserFrameNetworkErrorLabel">Problem mit Datenverbindung</string>
+ <string name="browserFrameRedirect">Umleiten</string>
+ <string name="browser_bookmarks_page_bookmarks_text">Lesezeichen</string>
+ <string name="browser_history">Zuletzt besuchte Seiten</string>
+ <string name="cache_cleared">Cache gelöscht</string>
+ <string name="cancel">Abbrechen</string>
+ <string name="clear">Löschen</string>
+ <string name="clear_history">Verlauf löschen</string>
+ <string name="close">Schließen</string>
+ <string name="close_window">Dieses Fenster wird geschlossen.</string>
+ <string name="common_name">Allgemeiner Name:</string>
+ <string name="contextmenu_add_contact">Kontakt hinzufügen</string>
+ <string name="contextmenu_bookmark_thislink">Lesezeichen Verknüpfung</string>
+ <string name="contextmenu_copy">Kopieren</string>
+ <string name="contextmenu_copylink">URL Verknüpfung kopieren</string>
+ <string name="contextmenu_dial_dot">Wählen\u2026</string>
+ <string name="contextmenu_download_image">Bild speichern</string>
+ <string name="contextmenu_javascript">JavaScript</string>
+ <string name="contextmenu_map">Landkarte</string>
+ <string name="contextmenu_openlink">Öffnen</string>
+ <string name="contextmenu_openlink_newwindow">In neuem Fenster öffnen</string>
+ <string name="contextmenu_savelink">Verknüpfung speichern</string>
+ <string name="contextmenu_send_mail">E-Mail senden</string>
+ <string name="contextmenu_sharelink">Verknüpfung freigeben</string>
+ <string name="contextmenu_view_image">Bild anzeigen</string>
+ <string name="current_page">Aktuelle Seite:\u0020</string>
+ <string name="delete">Löschen</string>
+ <string name="delete_bookmark">Löschen</string>
+ <string name="delete_bookmark_warning">Lesezeichen \"<xliff:g id="bookmark">%s</xliff:g>\" wird gelöscht.</string>
+ <string name="do_not_save">Abbrechen</string>
+ <string name="done">Fertig</string>
+ <string name="download_cancel_dlg_msg">Alle <xliff:g id="download_count">%d</xliff:g> Downloads werden abgebrochen und aus dem Verlauf des Herunterladens entfernt.</string>
+ <string name="download_cancel_dlg_title">Herunterladen abbrechen</string>
+ <string name="download_canceled">Herunterladen abgebrochen.</string>
+ <string name="download_clear_dlg_msg">Alle Elemente werden in der Liste gelöscht und aus dem Browser-Cache (Zwischenspeicher) entfernt.</string>
+ <string name="download_clear_dlg_title">Löschen</string>
+ <string name="download_error">Herunterladen nicht erfolgreich.</string>
+ <string name="download_failed_generic_dlg_title">Herunterladen nicht erfolgreich</string>
+ <string name="download_file_error">Herunterladen kann nicht komplett durchgeführt werden. Der freie Speicherplatz reicht nicht aus.</string>
+ <string name="download_file_error_dlg_msg"><xliff:g id="filename">%s</xliff:g> konnte nicht heruntergeladen werden.\nStellen Sie zusätzlichen Speicher auf dem Telefon bereit und versuchen Sie es erneut.</string>
+ <string name="download_file_error_dlg_title">Speicherplatz reicht nicht aus</string>
+ <string name="download_length_required">Herunterladen kann nicht durchgeführt werden. Die Größe des Elementes kann nicht ermittelt werden.</string>
+ <string name="download_menu_cancel">Herunterladen abbrechen</string>
+ <string name="download_menu_cancel_all">Alle Herunterladungen abbrechen</string>
+ <string name="download_menu_clear">Aus Liste löschen</string>
+ <string name="download_menu_clear_all">Liste löschen</string>
+ <string name="download_menu_open">Öffnen</string>
+ <string name="download_no_application">Es gibt keine Anwendung zum Öffnen dieser Datei.</string>
+ <string name="download_no_sdcard_dlg_msg">Eine SD-Karte wird für das Herunterladen von <xliff:g id="filename">%s</xliff:g> benötigt.</string>
+ <string name="download_no_sdcard_dlg_title">Keine SD-Karte</string>
+ <string name="download_not_acceptable">Herunterladen kann nicht durchgeführt werden. Die Inhalte, die heruntergeladen werden sollen, werden vom Telefon nicht unterstützt.</string>
+ <string name="download_pending">Herunterladen wird gestartet\u2026</string>
+ <string name="download_pending_network">Warten auf Datenverbindung\u2026</string>
+ <string name="download_precondition_failed">Herunterladen wurde unterbrochen. Wiederaufnahme nicht möglich.</string>
+ <string name="download_running">Beim Herunterladen\u2026</string>
+ <string name="download_running_paused">Warten auf Datenverbindung\u2026</string>
+ <string name="download_success"><xliff:g id="file">%s</xliff:g> Herunterladen abgeschlossen.</string>
+ <string name="download_title">Verlauf des Herunterladens</string>
+ <string name="download_unknown_filename">&lt;Unbekannt&gt;</string>
+ <string name="dump_nav">Navigations-Cache löschen</string>
+ <string name="edit_bookmark">Lesezeichen bearbeiten</string>
+ <string name="empty_bookmark">"Es kann kein leeres Lesezeichen erstellt werden."</string>
+ <string name="empty_history">Browser-Verlauf ist leer.</string>
+ <string name="expires_on">Gültig bis:</string>
+ <string name="find_dot">Suchen auf Seite</string>
+ <string name="fingerprints">Fingerabdrücke:</string>
+ <string name="forward">Weiterleiten</string>
+ <string name="goto_dot">Gehe zu URL</string>
+ <string name="history">Verlauf</string>
+ <string name="history_picker_view_web_page">Öffnen</string>
+ <string name="homepage">Startseite</string>
+ <string name="homepage_set">Startseite aktualisiert</string>
+ <string name="http">http://</string>
+ <string name="info_base">Titel:\n <xliff:g id="title">%s1</xliff:g> \n\nURL: \n <xliff:g id="url">%s2</xliff:g></string>
+ <string name="issued_by">Herausgegeben von:</string>
+ <string name="issued_on">Herausgegeben am:</string>
+ <string name="issued_to">Herausgegeben an:</string>
+ <string name="js_dialog_before_unload">Diese Seite verlassen?\n\n<xliff:g id="message">%s</xliff:g>\n\nWählen Sie »OK« zur Fortsetzung oder »Abbrechen«, um auf aktueller Seite zu bleiben.</string>
+ <string name="js_dialog_title_default">JavaScript</string>
+ <string name="js_dialog_title_prefix">Die Seite auf</string>
+ <string name="js_dialog_title_suffix">gibt an:</string>
+ <string name="loadSuspended">Das Laden der Seite wird fortgesetzt, nachdem die Verbindung wiederhergestellt wurde.</string>
+ <string name="loadSuspendedTitle">Keine Netzverbindung</string>
+ <string name="location">Ort</string>
+ <string name="matches_found">Gefundene Übereinstimmungen:</string>
+ <string name="md5_fingerprint">MD5-Fingerabdruck:</string>
+ <string name="menu_bookmark_page">Lesezeichen für diese Seite</string>
+ <string name="menu_flip_orientation">Ausrichtung wechseln</string>
+ <string name="menu_preferences">Einstellungen</string>
+ <string name="menu_use_as_homepage">Als Startseite einstellen</string>
+ <string name="menu_view_download">Heruntergeladen</string>
+ <string name="menu_zoom">Zoom</string>
+ <string name="name">Name</string>
+ <string name="new_window">Neues Fenster</string>
+ <string name="next">Weiter</string>
+ <string name="no_database">Keine Datenbank!</string>
+ <string name="no_downloads">Verlauf des Herunterladens ist leer.</string>
+ <string name="ok">OK</string>
+ <string name="oma_error_failed_to_download">Medien können nicht heruntergeladen werden.</string>
+ <string name="oma_error_insufficient_memory">Speicher reicht nicht aus! Prüfen Sie die SD-Karte.</string>
+ <string name="oma_error_invalid_descriptor">Beschreibung des Herunterladen-Ziels ungültig. </string>
+ <string name="oma_error_non_acceptable_content">Inhalte werden nicht unterstützt.</string>
+ <string name="oma_label_action">Herunterladen</string>
+ <string name="oma_label_cancel">Beenden</string>
+ <string name="oma_label_description">Beschreibung:</string>
+ <string name="oma_label_download_error">Fehler beim Herunterladen:</string>
+ <string name="oma_label_exit">Beenden</string>
+ <string name="oma_label_name">Name:</string>
+ <string name="oma_label_proceed">Fortfahren</string>
+ <string name="oma_label_size">Größe:</string>
+ <string name="oma_label_type">Typ:</string>
+ <string name="oma_label_vendor">Anbieter:</string>
+ <string name="oma_label_want_to_download">Möchten Sie diese Inhalte herunterladen?</string>
+ <string name="oma_status_attribute_mismatch">Attribut passt nicht</string>
+ <string name="oma_status_device_aborted">Telefongespräch unterbrochen</string>
+ <string name="oma_status_insufficient_memory">Speicher reicht nicht aus</string>
+ <string name="oma_status_invalid_descriptor">Ungültige Beschreibung</string>
+ <string name="oma_status_invalid_version">Ungültige Version</string>
+ <string name="oma_status_loader_error">Ladefehler</string>
+ <string name="oma_status_loss_of_service">Verlust des Dienstes</string>
+ <string name="oma_status_non_acceptable_content">Nicht akzeptierbare Inhalte</string>
+ <string name="oma_status_success">Erfolgreich</string>
+ <string name="oma_warning_one_or_more_types_not_supported">Eine oder mehrere Inhaltskategorien werden nicht unterstützt.</string>
+ <string name="open_bookmark">Öffnen</string>
+ <string name="open_in_new_window">In neuem Fenster öffnen</string>
+ <string name="org_name">Organisation:</string>
+ <string name="org_unit">Organisatorische Einheit:</string>
+ <string name="override">Ersetzen</string>
+ <string name="override_message">Die bestehenden Lesezeichen werden ersetzt.</string>
+ <string name="page_info">Seiteninfo</string>
+ <string name="page_info_address">Adresse:</string>
+ <string name="page_info_cache_source">Cache-Quelle:</string>
+ <string name="page_info_encoding">Kodierung:</string>
+ <string name="page_info_expires">Gültig bis:</string>
+ <string name="page_info_modified">Geändert am:</string>
+ <string name="page_info_referring_url">Bezugs-URL:</string>
+ <string name="page_info_render_mode">Umsetzungsmodus:</string>
+ <string name="page_info_size">Größe:</string>
+ <string name="page_info_type">Typ:</string>
+ <string name="page_info_view">Seiteninfo anzeigen</string>
+ <string name="password">Kennwort</string>
+ <string name="popup_window_attempt">Diese Site versucht ein
+ Popup-Fenster zu öffnen.</string>
+ <string name="pref_content_autofit">Seiten automatisch einpassen</string>
+ <string name="pref_content_autofit_summary">Webseiten formatieren, um sie in den Bildschirm einzupassen</string>
+ <string name="pref_content_block_popups">Popup-Fenster blockieren</string>
+ <string name="pref_content_homepage">Als Startseite einstellen</string>
+ <string name="pref_content_javascript">JavaScript aktivieren</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_open_in_background">Im Hintergrund öffnen</string>
+ <string name="pref_content_open_in_background_summary">Neue Fenster hinter aktuellem Fenster öffnen</string>
+ <string name="pref_content_title">Einstellungen für Seiteninhalte</string>
+ <string name="pref_development_nav_dump">Löschen von Nav.-Cache aktivieren</string>
+ <string name="pref_development_normal_rendering">Normales Umsetzen</string>
+ <string name="pref_development_single_column_rendering">Umsetzen einer Spalte</string>
+ <string name="pref_development_title">Programmfehler entfernen</string>
+ <string name="pref_development_trace">Tracing aktivieren</string>
+ <string name="pref_development_track">Trackball aktivieren</string>
+ <string name="pref_development_uastring">UA (User Agent) String</string>
+ <string name="pref_development_viewport">Weite Darstellung im Browser (Viewport) verwenden</string>
+ <string name="pref_extras_gears_enable">Gears aktivieren</string>
+ <string name="pref_extras_gears_enable_summary">Anwendungen, die die Browser-Funktionalität erweitern</string>
+ <string name="pref_extras_gears_settings">Gears-Einstellungen</string>
+ <string name="pref_extras_gears_settings_summary">Anwendungen, die die Browser-Funktionalität erweitern</string>
+ <string name="pref_extras_reset_default">Standard wiederherstellen</string>
+ <string name="pref_extras_reset_default_dlg">Alle Browserdaten werden gelöscht und die Einstellungen werden auf die Standardwerte zurückgesetzt.</string>
+ <string name="pref_extras_reset_default_dlg_title">Standard wiederherstellen</string>
+ <string name="pref_extras_reset_default_summary">Alle Browserdaten löschen und Einstellungen auf Standardwerte zurücksetzen</string>
+ <string name="pref_extras_title">Erweiterte Einstellungen</string>
+ <string name="pref_plugin_installed_empty_listxx">Nicht installierte Plugins.</string>
+ <string name="pref_plugin_installed_summaryxx">Aktuell installierte Plugins anzeigen</string>
+ <string name="pref_privacy_clear_cache">Zwischenspeicher löschen</string>
+ <string name="pref_privacy_clear_cache_dlg">Der Zwischenspeicher wird gelöscht.</string>
+ <string name="pref_privacy_clear_cache_summary">Alle im Zwischenspeicher gespeicherten Seiteninhalte löschen</string>
+ <string name="pref_privacy_clear_cookies">Alle Cookie-Daten löschen</string>
+ <string name="pref_privacy_clear_cookies_dlg">Alle Cookies werden gelöscht.</string>
+ <string name="pref_privacy_clear_cookies_summary">Alle Browser-Cookies löschen</string>
+ <string name="pref_privacy_clear_form_data">Formulardaten löschen</string>
+ <string name="pref_privacy_clear_form_data_dlg">Alle gespeicherten Formulardaten werden gelöscht.</string>
+ <string name="pref_privacy_clear_form_data_summary">Alle gespeicherten Formulardaten löschen</string>
+ <string name="pref_privacy_clear_history">Verlauf löschen</string>
+ <string name="pref_privacy_clear_history_dlg">Der Navigationsverlauf des Browsers wird gelöscht.</string>
+ <string name="pref_privacy_clear_history_summary">Navigationsverlauf des Browsers löschen</string>
+ <string name="pref_privacy_clear_passwords">Kennworte löschen</string>
+ <string name="pref_privacy_clear_passwords_dlg">Alle gespeicherten Kennworte werden gelöscht.</string>
+ <string name="pref_privacy_clear_passwords_summary">Alle gespeicherten Kennworte löschen</string>
+ <string name="pref_privacy_title">Datenschutzeinstellungen</string>
+ <string name="pref_security_accept_cookies">Cookies akzeptieren</string>
+ <string name="pref_security_accept_cookies_summary">Seiten ermöglichen \"cookie\"-Daten zu speichern und zu lesen</string>
+ <string name="pref_security_remember_passwords">An Kennworte erinnern</string>
+ <string name="pref_security_remember_passwords_summary">Benutzernamen und Kennworte für Webseiten speichern</string>
+ <string name="pref_security_save_form_data">An Formulardaten erinnern</string>
+ <string name="pref_security_save_form_data_summary">An Daten erinnern, die in Formulare eingegeben wurden</string>
+ <string name="pref_security_show_security_warning">Sicherheitswarnungen anzeigen</string>
+ <string name="pref_security_show_security_warning_summary">Warnung anzeigen, wenn es ein Problem mit der Sicherheit einer Seite gibt</string>
+ <string name="pref_security_title">Sicherheitseinstellungen</string>
+ <string name="pref_text_size">Textgröße</string>
+ <string name="pref_text_size_dialogtitle">Textgröße</string>
+ <string name="pref_text_size_summary">Größe von Text auf Webseiten auswählen</string>
+ <string name="prev">Zurück</string>
+ <string name="question_mark">\?</string>
+ <string name="reload">Aktualisieren</string>
+ <string name="remove_bookmark">Lesezeichen löschen</string>
+ <string name="remove_history_item">Aus Verlauf entfernen</string>
+ <string name="replace">Ersetzen</string>
+ <string name="retrieving_creds_dlg_msg">Anmeldedetails abrufen\u2026</string>
+ <string name="retry">Wiederholen</string>
+ <string name="save">OK</string>
+ <string name="save_to_bookmarks">Lesezeichen Verknüpfung</string>
+ <string name="search">Suchen</string>
+ <string name="search_button_text">Gehe zu</string>
+ <string name="search_hint">Webadresse eingeben</string>
+ <string name="search_label">Browser</string>
+ <string name="security_warning">Sicherheitswarnung</string>
+ <string name="sha1_fingerprint">Fingerabdruck gemäß SHA1:</string>
+ <string name="share_page">Seite freigeben</string>
+ <string name="shortcut_bookmark">Lesezeichen</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="ssl_certificate">Sicherheitszertifikat</string>
+ <string name="ssl_certificate_is_valid">Dieses Zertifikat ist gültig.</string>
+ <string name="ssl_continue">Fortfahren</string>
+ <string name="ssl_expired">Dieses Zertifikat ist nicht mehr gültig.</string>
+ <string name="ssl_mismatch">Der Name der Seite stimmt nicht mit dem Namen des Zertifikats überein.</string>
+ <string name="ssl_not_yet_valid">Dieses Zertifikat ist noch nicht gültig.</string>
+ <string name="ssl_untrusted">Dieses Zertifikat wurde von keiner zuverlässigen Stelle ausgestellt.</string>
+ <string name="ssl_warnings_header">Es gibt Probleme mit dem Sicherheitszertifikat für diese Seite.</string>
+ <string name="stop">Anhalten</string>
+ <string name="stopping">Anhalten\u2026</string>
+ <string name="tab_picker_bookmark">Lesezeichen</string>
+ <string name="tab_picker_new_tab">Neues Fenster</string>
+ <string name="tab_picker_remove_tab">Schließen</string>
+ <string name="tab_picker_send_url">Verknüpfung freigeben</string>
+ <string name="tab_picker_title">Aktuelle Fenster</string>
+ <string name="tab_picker_view_tab">Ansicht</string>
+ <string name="too_many_subwindows_dialog_message">Neues Popup-Fenster
+ konnte nicht geöffnet werden, da nicht mehr als ein Fenster gleichzeitig geöffnet werden kann.</string>
+ <string name="too_many_subwindows_dialog_title">Pop-up-Fenster bereits geöffnet</string>
+ <string name="too_many_windows_dialog_message">Neues Fenster konnte nicht geöffnet werden, da bereits die maximale Anzahl von Fenstern geöffnet ist.</string>
+ <string name="too_many_windows_dialog_title">Maximale Fensteranzahl erreicht</string>
+ <string name="username">Name</string>
+ <string name="validity_period">Gültigkeit:</string>
+ <string name="view_certificate">Zertifikat anzeigen</string>
+ <string name="view_tabs">Fensterübersicht</string>
+ <string name="view_tabs_condensed">Fenster</string>
+ <string name="visual_history">Fensterverlauf</string>
+ <string name="with">mit</string>
+ <string name="zero">0</string>
+</resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
new file mode 100644
index 00000000..ae010b11
--- /dev/null
+++ b/res/values-es-rUS/strings.xml
@@ -0,0 +1,270 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="action">Inicio de sesión</string>
+ <string name="activity_instrumentation_functional_test_runner">Programa de ejecución de pruebas funcionales del explorador</string>
+ <string name="activity_instrumentation_test_runner">Programa de ejecución de pruebas del explorador</string>
+ <string name="add_new_bookmark">Nuevo marcador</string>
+ <string name="allow">Permitir</string>
+ <string name="attention">¡Atención!</string>
+ <string name="back">Atrás</string>
+ <string name="block">Bloquear</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="bookmark_page">Marcar la última página visualizada</string>
+ <string name="bookmark_saved">Guardada en marcadores.</string>
+ <string name="bookmark_url_not_valid">La dirección URL no es válida.</string>
+ <string name="bookmarks">Marcadores</string>
+ <string name="browserFrame307Post">Esta página Web está siendo redirigida. ¿Desea reenviar los datos introducidos en el formulario a la nueva ubicación?</string>
+ <string name="browserFrameFileErrorLabel">Problema con el archivo</string>
+ <string name="browserFrameFormResubmitLabel">Confirmar</string>
+ <string name="browserFrameFormResubmitMessage">La página que intenta ver contiene datos que ya se han enviado (\"POSTDATA\"). Si reenvía los datos, se repetirá cualquier acción que haya realizado el formulario (como una búsqueda o una compra en línea).</string>
+ <string name="browserFrameNetworkErrorLabel">Problema de conexión de datos</string>
+ <string name="browserFrameRedirect">Redireccionar</string>
+ <string name="browser_bookmarks_page_bookmarks_text">Marcadores</string>
+ <string name="browser_history">Páginas visitadas recientemente</string>
+ <string name="cache_cleared">Caché borrada</string>
+ <string name="cancel">Cancelar</string>
+ <string name="clear">Borrar</string>
+ <string name="clear_history">Borrar historial</string>
+ <string name="close">Cerrar</string>
+ <string name="close_window">Se cerrará esta ventana.</string>
+ <string name="common_name">Nombre común:</string>
+ <string name="contextmenu_add_contact">Agregar contacto</string>
+ <string name="contextmenu_bookmark_thislink">Marcar enlace</string>
+ <string name="contextmenu_copy">Copiar</string>
+ <string name="contextmenu_copylink">Copiar enlace URL</string>
+ <string name="contextmenu_dial_dot">Marcar\u2026</string>
+ <string name="contextmenu_download_image">Guardar imagen</string>
+ <string name="contextmenu_javascript">JavaScript</string>
+ <string name="contextmenu_map">Mapa</string>
+ <string name="contextmenu_openlink">Abrir</string>
+ <string name="contextmenu_openlink_newwindow">Abrir en una ventana nueva</string>
+ <string name="contextmenu_savelink">Guardar enlace</string>
+ <string name="contextmenu_send_mail">Enviar correo electrónico</string>
+ <string name="contextmenu_sharelink">Compartir enlace</string>
+ <string name="contextmenu_view_image">Ver imagen</string>
+ <string name="current_page">Página actual:\u0020</string>
+ <string name="delete">Eliminar</string>
+ <string name="delete_bookmark">Eliminar</string>
+ <string name="delete_bookmark_warning">El marcador \"<xliff:g id="bookmark">%s</xliff:g>\" se eliminará.</string>
+ <string name="do_not_save">Cancelar</string>
+ <string name="done">Listo</string>
+ <string name="download_cancel_dlg_msg">Todas las descargas de <xliff:g id="download_count">%d</xliff:g> se cancelarán y se borrarán del historial de descarga.</string>
+ <string name="download_cancel_dlg_title">Cancelar descargas</string>
+ <string name="download_canceled">Descarga cancelada.</string>
+ <string name="download_clear_dlg_msg">Todos los elementos se borrarán de la lista y se eliminarán de la caché del explorador.</string>
+ <string name="download_clear_dlg_title">Borrar</string>
+ <string name="download_error">Error en la descarga.</string>
+ <string name="download_failed_generic_dlg_title">Error en la descarga</string>
+ <string name="download_file_error">No se ha podido finalizar la descarga. No hay suficiente espacio. </string>
+ <string name="download_file_error_dlg_msg"><xliff:g id="filename">%s</xliff:g> no se ha podido descargar.\nLibere algo de espacio en su teléfono e inténtelo de nuevo.</string>
+ <string name="download_file_error_dlg_title">Sin espacio</string>
+ <string name="download_length_required">No se puede descargar. El tamaño del elemento no se puede determinar.</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">Borrar de la lista</string>
+ <string name="download_menu_clear_all">Borrar lista</string>
+ <string name="download_menu_open">Abrir</string>
+ <string name="download_no_application">No se ha encontrado ninguna aplicación al abrir este archivo. </string>
+ <string name="download_no_sdcard_dlg_msg">Se necesita una tarjeta SD para descargar <xliff:g id="filename">%s</xliff:g>.</string>
+ <string name="download_no_sdcard_dlg_title">Ninguna tarjeta SD</string>
+ <string name="download_not_acceptable">No se puede descargar. El contenido que se intenta descargar no es compatible con su teléfono. </string>
+ <string name="download_pending">Iniciando descarga\u2026</string>
+ <string name="download_pending_network">Esperando por conexión de datos\u2026</string>
+ <string name="download_precondition_failed">Descarga interrumpida. No se puede reiniciar. </string>
+ <string name="download_running">Descargando\u2026</string>
+ <string name="download_running_paused">Esperando por conexión de datos\u2026</string>
+ <string name="download_success"><xliff:g id="file">%s</xliff:g> Descarga completa. </string>
+ <string name="download_title">Historial de descargas</string>
+ <string name="download_unknown_filename">&lt;Desconocido&gt;</string>
+ <string name="dump_nav">Volcar caché de navegación</string>
+ <string name="edit_bookmark">Editar marcador</string>
+ <string name="empty_bookmark">"No se puede crear marcador vacío".</string>
+ <string name="empty_history">El historial del explorador está vacío.</string>
+ <string name="expires_on">Caduca el:</string>
+ <string name="find_dot">Encontrado en página</string>
+ <string name="fingerprints">Huellas digitales:</string>
+ <string name="forward">Desvío</string>
+ <string name="goto_dot">Ir a dirección URL</string>
+ <string name="history">Historial</string>
+ <string name="history_picker_view_web_page">Abrir</string>
+ <string name="homepage">Página de inicio</string>
+ <string name="homepage_set">Página de inicio actualizada</string>
+ <string name="http">http://</string>
+ <string name="info_base">Título:\n <xliff:g id="title">%s1</xliff:g> \n\nURL: \n <xliff:g id="url">%s2</xliff:g></string>
+ <string name="issued_by">Emitido por:</string>
+ <string name="issued_on">Emitido:</string>
+ <string name="issued_to">Emitido a:</string>
+ <string name="js_dialog_before_unload">¿Salir de esta página?\n\n<xliff:g id="message">%s</xliff:g>\n\nSeleccione Aceptar para continuar, o Cancelar para permanecer en la página actual.</string>
+ <string name="js_dialog_title_default">JavaScript</string>
+ <string name="js_dialog_title_prefix">La página en</string>
+ <string name="js_dialog_title_suffix">dice:</string>
+ <string name="loadSuspended">La página se seguirá cargando tras restablecer la conexión. </string>
+ <string name="loadSuspendedTitle">Sin conexión de red</string>
+ <string name="location">Ubicación</string>
+ <string name="matches_found">Coincidencias encontradas:</string>
+ <string name="md5_fingerprint">Huella digital MD5:</string>
+ <string name="menu_bookmark_page">Marcar página</string>
+ <string name="menu_flip_orientation">Orientación de volteo</string>
+ <string name="menu_preferences">Configuración</string>
+ <string name="menu_use_as_homepage">Establecer como página principal</string>
+ <string name="menu_view_download">Descargas</string>
+ <string name="menu_zoom">Zoom</string>
+ <string name="name">Nombre</string>
+ <string name="new_window">Nueva ventana</string>
+ <string name="next">Siguiente</string>
+ <string name="no_database">¡Ninguna base de datos!</string>
+ <string name="no_downloads">El historial de descarga está vacío.</string>
+ <string name="ok">Aceptar</string>
+ <string name="oma_error_failed_to_download">Incapaz de descargar medios. </string>
+ <string name="oma_error_insufficient_memory">¡Memoria insuficiente! Compruebe su tarjeta SD.</string>
+ <string name="oma_error_invalid_descriptor">Descriptor de descarga no válido.</string>
+ <string name="oma_error_non_acceptable_content">Contenido no compatible.</string>
+ <string name="oma_label_action">Descargar</string>
+ <string name="oma_label_cancel">Salir</string>
+ <string name="oma_label_description">Descripción:</string>
+ <string name="oma_label_download_error">Error de descarga:</string>
+ <string name="oma_label_exit">Salir</string>
+ <string name="oma_label_name">Nombre:</string>
+ <string name="oma_label_proceed">Continuar</string>
+ <string name="oma_label_size">Tamaño:</string>
+ <string name="oma_label_type">Tipo:</string>
+ <string name="oma_label_vendor">Proveedor:</string>
+ <string name="oma_label_want_to_download">¿Desea descargar este contenido?</string>
+ <string name="oma_status_attribute_mismatch">Atributos no coincidentes</string>
+ <string name="oma_status_device_aborted">Teléfono anulado</string>
+ <string name="oma_status_insufficient_memory">Memoria insuficiente</string>
+ <string name="oma_status_invalid_descriptor">Descriptor no válido</string>
+ <string name="oma_status_invalid_version">Versión no válida</string>
+ <string name="oma_status_loader_error">Error de cargador</string>
+ <string name="oma_status_loss_of_service">Pérdida de servicio</string>
+ <string name="oma_status_non_acceptable_content">Contenido inaceptable</string>
+ <string name="oma_status_success">Correcto</string>
+ <string name="oma_warning_one_or_more_types_not_supported">Una o más categorías de contenido no son compatibles.</string>
+ <string name="open_bookmark">Abrir</string>
+ <string name="open_in_new_window">Abrir en una ventana nueva</string>
+ <string name="org_name">Organización:</string>
+ <string name="org_unit">Departamento:</string>
+ <string name="override">Reemplazar</string>
+ <string name="override_message">El marcador existente será sustituido. </string>
+ <string name="page_info">Página de información</string>
+ <string name="page_info_address">Dirección:</string>
+ <string name="page_info_cache_source">Fuente de caché:</string>
+ <string name="page_info_encoding">Codificación:</string>
+ <string name="page_info_expires">Caduca:</string>
+ <string name="page_info_modified">Modificado:</string>
+ <string name="page_info_referring_url">URL de referencia:</string>
+ <string name="page_info_render_mode">Modo de presentación:</string>
+ <string name="page_info_size">Tamaño:</string>
+ <string name="page_info_type">Tipo:</string>
+ <string name="page_info_view">Ver página de información</string>
+ <string name="password">Contraseña</string>
+ <string name="popup_window_attempt">Este sitio está intentando abrir una
+ ventana emergente.</string>
+ <string name="pref_content_autofit">Ajuste automático de páginas</string>
+ <string name="pref_content_autofit_summary">Formatear páginas Web para ajustarse a la pantalla</string>
+ <string name="pref_content_block_popups">Bloquear ventanas emergentes</string>
+ <string name="pref_content_homepage">Configurar página principal</string>
+ <string name="pref_content_javascript">Habilitar JavaScript</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_open_in_background">Abrir en segundo plano</string>
+ <string name="pref_content_open_in_background_summary">Nueva ventana abierta detrás de la actual</string>
+ <string name="pref_content_title">Configuración del contenido de la página</string>
+ <string name="pref_development_nav_dump">Habilitar volcado de caché de navegación</string>
+ <string name="pref_development_normal_rendering">Presentación normal</string>
+ <string name="pref_development_single_column_rendering">Presentación en una sola columna</string>
+ <string name="pref_development_title">Depuración</string>
+ <string name="pref_development_trace">Habilitar trazado</string>
+ <string name="pref_development_track">Habilitar bola de seguimiento</string>
+ <string name="pref_development_uastring">UAString</string>
+ <string name="pref_development_viewport">Utilizar puerto de vista panorámica</string>
+ <string name="pref_extras_gears_enable">Habilitar Gears</string>
+ <string name="pref_extras_gears_enable_summary">Aplicaciones que amplían la funcionalidad del explorador</string>
+ <string name="pref_extras_gears_settings">Configuración de Gears</string>
+ <string name="pref_extras_gears_settings_summary">Aplicaciones que amplían la funcionalidad del explorador</string>
+ <string name="pref_extras_reset_default">Restablecer a predeterminados</string>
+ <string name="pref_extras_reset_default_dlg">Se borrarán todos los datos del explorador y los configuración retomarán los valores predeterminados de fábrica. </string>
+ <string name="pref_extras_reset_default_dlg_title">Restablecer a predeterminados</string>
+ <string name="pref_extras_reset_default_summary">Borrar todos los datos del explorador y restablecer a los valores predeterminados de fábrica</string>
+ <string name="pref_extras_title">Configuración avanzada</string>
+ <string name="pref_plugin_installed_empty_listxx">No hay ningún plug-in instalado.</string>
+ <string name="pref_plugin_installed_summaryxx">Ver plug-ins instalados actualmente</string>
+ <string name="pref_privacy_clear_cache">Borrar caché</string>
+ <string name="pref_privacy_clear_cache_dlg">Se borrará la caché.</string>
+ <string name="pref_privacy_clear_cache_summary">Eliminar todo el contenido de la página caché</string>
+ <string name="pref_privacy_clear_cookies">Borrar todos los datos de las cookies</string>
+ <string name="pref_privacy_clear_cookies_dlg">Se borrarán todas las cookies. </string>
+ <string name="pref_privacy_clear_cookies_summary">Borrar todas las cookies del explorador</string>
+ <string name="pref_privacy_clear_form_data">Borrar datos del formulario</string>
+ <string name="pref_privacy_clear_form_data_dlg">Se borrarán todos los datos del formulario guardados.</string>
+ <string name="pref_privacy_clear_form_data_summary">Borrar todos los datos del formulario guardados</string>
+ <string name="pref_privacy_clear_history">Borrar historial</string>
+ <string name="pref_privacy_clear_history_dlg">Se borrará el historial de navegación del explorador. </string>
+ <string name="pref_privacy_clear_history_summary">Borrar el historial de navegación del explorador</string>
+ <string name="pref_privacy_clear_passwords">Borrar contraseñas</string>
+ <string name="pref_privacy_clear_passwords_dlg">Se borrarán todas las contraseñas guardadas. </string>
+ <string name="pref_privacy_clear_passwords_summary">Borrar todas las contraseñas guardadas</string>
+ <string name="pref_privacy_title">Configuración de privacidad</string>
+ <string name="pref_security_accept_cookies">Aceptar cookies</string>
+ <string name="pref_security_accept_cookies_summary">Permite a los sitios guardar y leer los datos de la \"cookie\" </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 las páginas Web</string>
+ <string name="pref_security_save_form_data">Recordar datos del formulario</string>
+ <string name="pref_security_save_form_data_summary">Recordar los datos introducidos en los formularios para utilizarlos más adelante</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 de un sitio</string>
+ <string name="pref_security_title">Configuración de seguridad</string>
+ <string name="pref_text_size">Tamaño del texto</string>
+ <string name="pref_text_size_dialogtitle">Tamaño del texto</string>
+ <string name="pref_text_size_summary">Seleccionar el tamaño del texto en las páginas Web</string>
+ <string name="prev">Ant.</string>
+ <string name="question_mark">\?</string>
+ <string name="reload">Actualizar</string>
+ <string name="remove_bookmark">Eliminar marcador</string>
+ <string name="remove_history_item">Eliminar del historial</string>
+ <string name="replace">Reemplazar</string>
+ <string name="retrieving_creds_dlg_msg">Recuperar la información de acceso\u2026</string>
+ <string name="retry">Reintentar</string>
+ <string name="save">Aceptar</string>
+ <string name="save_to_bookmarks">Marcar enlace</string>
+ <string name="search">Buscar</string>
+ <string name="search_button_text">Ir</string>
+ <string name="search_hint">Escribir dirección Web</string>
+ <string name="search_label">Explorador</string>
+ <string name="security_warning">Advertencia de seguridad</string>
+ <string name="sha1_fingerprint">Huella digital SHA1:</string>
+ <string name="share_page">Compartir página</string>
+ <string name="shortcut_bookmark">Marcador</string>
+ <string name="sign_in_to">Iniciar sesión en <xliff:g id="hostname">%s1</xliff:g> \"<xliff:g id="realm">%s2</xliff:g>\"</string>
+ <string name="ssl_certificate">Certificado de seguridad</string>
+ <string name="ssl_certificate_is_valid">Este certificado es válido.</string>
+ <string name="ssl_continue">Continuar</string>
+ <string name="ssl_expired">Este certificado ha caducado.</string>
+ <string name="ssl_mismatch">El nombre del sitio no corresponde con el nombre del certificado. </string>
+ <string name="ssl_not_yet_valid">Este certificado aún no es válido.</string>
+ <string name="ssl_untrusted">Este certificado no es de una autoridad fiable.</string>
+ <string name="ssl_warnings_header">Hay problemas con el certificado de seguridad de este sitio.</string>
+ <string name="stop">Detener</string>
+ <string name="stopping">Deteniendo\u2026</string>
+ <string name="tab_picker_bookmark">Marcador</string>
+ <string name="tab_picker_new_tab">Nueva ventana</string>
+ <string name="tab_picker_remove_tab">Cerrar</string>
+ <string name="tab_picker_send_url">Compartir enlace</string>
+ <string name="tab_picker_title">Ventanas actuales</string>
+ <string name="tab_picker_view_tab">Ver</string>
+ <string name="too_many_subwindows_dialog_message">No se pudo abrir otra
+ ventana emergente ya que sólo se puede abrir una cada vez.</string>
+ <string name="too_many_subwindows_dialog_title">Ventana emergente ya abierta</string>
+ <string name="too_many_windows_dialog_message">No se pudo abrir otra ventana por haber alcanzado el número máximo de ventanas abiertas.</string>
+ <string name="too_many_windows_dialog_title">Límite de ventanas alcanzado</string>
+ <string name="username">Nombre</string>
+ <string name="validity_period">Validez:</string>
+ <string name="view_certificate">Ver certificado</string>
+ <string name="view_tabs">Vista general de la ventana</string>
+ <string name="view_tabs_condensed">Ventana</string>
+ <string name="visual_history">Historial de la ventana</string>
+ <string name="with">con</string>
+ <string name="zero">0</string>
+</resources>
diff --git a/res/values-fr-rFR/strings.xml b/res/values-fr-rFR/strings.xml
new file mode 100644
index 00000000..3ddce686
--- /dev/null
+++ b/res/values-fr-rFR/strings.xml
@@ -0,0 +1,270 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="action">Connexion</string>
+ <string name="activity_instrumentation_functional_test_runner">Exécuteur de test fonctionnel du navigateur</string>
+ <string name="activity_instrumentation_test_runner">Exécuteur de test du navigateur</string>
+ <string name="add_new_bookmark">Nouveau signet</string>
+ <string name="allow">Autoriser</string>
+ <string name="attention">Attention</string>
+ <string name="back">Retour</string>
+ <string name="block">Bloquer</string>
+ <string name="bookmark_needs_title">"Le signet doit avoir un nom."</string>
+ <string name="bookmark_needs_url">"Le signet doit avoir un emplacement."</string>
+ <string name="bookmark_page">Marquer la dernière page affichée</string>
+ <string name="bookmark_saved">Enregistrée dans les signets.</string>
+ <string name="bookmark_url_not_valid">URL non valide.</string>
+ <string name="bookmarks">Signets</string>
+ <string name="browserFrame307Post">Cette page Web est redirigée. Renvoyer vos données de formulaires tapées au nouvel emplacement ?</string>
+ <string name="browserFrameFileErrorLabel">Problème avec le fichier</string>
+ <string name="browserFrameFormResubmitLabel">Confirmer</string>
+ <string name="browserFrameFormResubmitMessage">La page que vous essayer d\'afficher contient des données qui ont été déjà soumises (\"POSTDATA\"). Si vous renvoyez les données, toute action effectuée par le formulaire sur la page (telle qu\'une recherche ou un achat en ligne) sera répétée.</string>
+ <string name="browserFrameNetworkErrorLabel">Problème de connectivité de données</string>
+ <string name="browserFrameRedirect">Rediriger</string>
+ <string name="browser_bookmarks_page_bookmarks_text">Signets</string>
+ <string name="browser_history">Pages récemment visitées</string>
+ <string name="cache_cleared">Cache effacée</string>
+ <string name="cancel">Annuler</string>
+ <string name="clear">Effacer</string>
+ <string name="clear_history">Effacer l\'historique</string>
+ <string name="close">Fermer</string>
+ <string name="close_window">Cette fenêtre sera fermée.</string>
+ <string name="common_name">Nom commun :</string>
+ <string name="contextmenu_add_contact">Ajouter un contact</string>
+ <string name="contextmenu_bookmark_thislink">Lien signet</string>
+ <string name="contextmenu_copy">Copier</string>
+ <string name="contextmenu_copylink">Copier le lien URL</string>
+ <string name="contextmenu_dial_dot">Numéroter\u2026</string>
+ <string name="contextmenu_download_image">Enregistrer l\'image</string>
+ <string name="contextmenu_javascript">JavaScript</string>
+ <string name="contextmenu_map">Carte</string>
+ <string name="contextmenu_openlink">Ouvrir</string>
+ <string name="contextmenu_openlink_newwindow">Ouvrir dans une nouvelle fenêtre</string>
+ <string name="contextmenu_savelink">Enregistrer le lien</string>
+ <string name="contextmenu_send_mail">Envoyer e-mail</string>
+ <string name="contextmenu_sharelink">Partager le lien</string>
+ <string name="contextmenu_view_image">Afficher l\'image</string>
+ <string name="current_page">Page actuelle :\u0020</string>
+ <string name="delete">Supprimer</string>
+ <string name="delete_bookmark">Supprimer</string>
+ <string name="delete_bookmark_warning">Les signets \"<xliff:g id="bookmark">%s</xliff:g>\" seront supprimés.</string>
+ <string name="do_not_save">Annuler</string>
+ <string name="done">Terminé</string>
+ <string name="download_cancel_dlg_msg">Tous les téléchargements <xliff:g id="download_count">%d</xliff:g> seront annulés et effacés de l\'historique des téléchargements.</string>
+ <string name="download_cancel_dlg_title">Annuler les téléchargements</string>
+ <string name="download_canceled">Téléchargement annulé.</string>
+ <string name="download_clear_dlg_msg">Tous les éléments seront effacés de la liste et supprimés de la cache du navigateur.</string>
+ <string name="download_clear_dlg_title">Effacer</string>
+ <string name="download_error">Échec du téléchargement.</string>
+ <string name="download_failed_generic_dlg_title">Échec du téléchargement</string>
+ <string name="download_file_error">Impossible de finir le téléchargement. L\'espace est insuffisant.</string>
+ <string name="download_file_error_dlg_msg"><xliff:g id="filename">%s</xliff:g> n\'a pas pu être téléchargé.\nLibérez de l\'espace sur votre téléphone et essayez à nouveau.</string>
+ <string name="download_file_error_dlg_title">Plus d\'espace</string>
+ <string name="download_length_required">Impossible de télécharger. La taille de l\'élément ne peut pas être déterminée.</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">Effacer de la liste</string>
+ <string name="download_menu_clear_all">Effacer la liste</string>
+ <string name="download_menu_open">Ouvrir</string>
+ <string name="download_no_application">Aucune application n\'est trouvée pour ouvrir ce fichier.</string>
+ <string name="download_no_sdcard_dlg_msg">Une carte SD est nécessaire pour télécharger <xliff:g id="filename">%s</xliff:g>.</string>
+ <string name="download_no_sdcard_dlg_title">Pas de carte SD</string>
+ <string name="download_not_acceptable">Impossible de télécharger. Le contenu en cours de téléchargement n\'est pas pris en charge sur le téléphone.</string>
+ <string name="download_pending">Début du téléchargement\u2026</string>
+ <string name="download_pending_network">Attente de la connexion de données\u2026</string>
+ <string name="download_precondition_failed">Téléchargement interrompu. Il ne peut pas être repris.</string>
+ <string name="download_running">Téléchargement\u2026</string>
+ <string name="download_running_paused">Attente de la connexion de données\u2026</string>
+ <string name="download_success"><xliff:g id="file">%s</xliff:g> Téléchargement terminé.</string>
+ <string name="download_title">Historique de téléchargement</string>
+ <string name="download_unknown_filename">&lt;Inconnu&gt;</string>
+ <string name="dump_nav">Décharger la cache de navigation</string>
+ <string name="edit_bookmark">Modifier le signet</string>
+ <string name="empty_bookmark">"Impossible de créer un signet vide."</string>
+ <string name="empty_history">L\'historique du navigateur est vide.</string>
+ <string name="expires_on">Expire le :</string>
+ <string name="find_dot">Rechercher sur la page</string>
+ <string name="fingerprints">Empreintes digitales :</string>
+ <string name="forward">Transférer</string>
+ <string name="goto_dot">Aller à URL</string>
+ <string name="history">Historique</string>
+ <string name="history_picker_view_web_page">Ouvrir</string>
+ <string name="homepage">Page d\'accueil</string>
+ <string name="homepage_set">Page d\'accueil mise à jour</string>
+ <string name="http">http://</string>
+ <string name="info_base">Titre :\n <xliff:g id="title">%s1</xliff:g> \n\nURL : \n <xliff:g id="url">%s2</xliff:g></string>
+ <string name="issued_by">Émis par :</string>
+ <string name="issued_on">Émis le :</string>
+ <string name="issued_to">Émis à :</string>
+ <string name="js_dialog_before_unload">Naviguer en quittant cette page ?\n\n<xliff:g id="message">%s</xliff:g>\n\nSélectionner OK pour continuer, ou Annuler pour rester sur la page actuelle.</string>
+ <string name="js_dialog_title_default">JavaScript</string>
+ <string name="js_dialog_title_prefix">La page à</string>
+ <string name="js_dialog_title_suffix">dit :</string>
+ <string name="loadSuspended">La page continuera son chargement après la restauration de la connexion.</string>
+ <string name="loadSuspendedTitle">Pas de connexion réseau</string>
+ <string name="location">Localisation</string>
+ <string name="matches_found">Correspondances trouvées :</string>
+ <string name="md5_fingerprint">Empreinte digitale MD5 :</string>
+ <string name="menu_bookmark_page">Marquer la page</string>
+ <string name="menu_flip_orientation">Retourner l\'orientation</string>
+ <string name="menu_preferences">Paramètres</string>
+ <string name="menu_use_as_homepage">Définir comme page d\'accueil</string>
+ <string name="menu_view_download">Téléchargements</string>
+ <string name="menu_zoom">Zoom</string>
+ <string name="name">Nom</string>
+ <string name="new_window">Nouvelle fenêtre</string>
+ <string name="next">Suivant</string>
+ <string name="no_database">Pas de base de données !</string>
+ <string name="no_downloads">L\'historique de téléchargement est vide.</string>
+ <string name="ok">OK</string>
+ <string name="oma_error_failed_to_download">Impossible de télécharger le média.</string>
+ <string name="oma_error_insufficient_memory">Mémoire insuffisante ! Vérifiez votre carte SD.</string>
+ <string name="oma_error_invalid_descriptor">Descripteur de téléchargement non valide.</string>
+ <string name="oma_error_non_acceptable_content">Contenu non pris en charge.</string>
+ <string name="oma_label_action">Télécharger</string>
+ <string name="oma_label_cancel">Quitter</string>
+ <string name="oma_label_description">Description :</string>
+ <string name="oma_label_download_error">Erreur de téléchargement :</string>
+ <string name="oma_label_exit">Quitter</string>
+ <string name="oma_label_name">Nom :</string>
+ <string name="oma_label_proceed">Continuer</string>
+ <string name="oma_label_size">Taille :</string>
+ <string name="oma_label_type">Type :</string>
+ <string name="oma_label_vendor">Vendeur :</string>
+ <string name="oma_label_want_to_download">Souhaitez-vous télécharger ce contenu ?</string>
+ <string name="oma_status_attribute_mismatch">Non concordance d\'attribut</string>
+ <string name="oma_status_device_aborted">Téléphone abandonné</string>
+ <string name="oma_status_insufficient_memory">Mémoire insuffisante</string>
+ <string name="oma_status_invalid_descriptor">Descripteur non valide</string>
+ <string name="oma_status_invalid_version">Version non valide</string>
+ <string name="oma_status_loader_error">Erreur de chargeur</string>
+ <string name="oma_status_loss_of_service">Perte de service</string>
+ <string name="oma_status_non_acceptable_content">Contenu non acceptable</string>
+ <string name="oma_status_success">Succès</string>
+ <string name="oma_warning_one_or_more_types_not_supported">Une ou plusieurs catégories de contenu ne sont pas prises en charge.</string>
+ <string name="open_bookmark">Ouvrir</string>
+ <string name="open_in_new_window">Ouvrir dans une nouvelle fenêtre</string>
+ <string name="org_name">Organisation :</string>
+ <string name="org_unit">Unité organisationnelle :</string>
+ <string name="override">Remplacer</string>
+ <string name="override_message">Le signet existant sera remplacé.</string>
+ <string name="page_info">Infos page</string>
+ <string name="page_info_address">Adresse :</string>
+ <string name="page_info_cache_source">Source cache :</string>
+ <string name="page_info_encoding">Codage :</string>
+ <string name="page_info_expires">Expire :</string>
+ <string name="page_info_modified">Modifié :</string>
+ <string name="page_info_referring_url">URL référente :</string>
+ <string name="page_info_render_mode">Mode de rendu :</string>
+ <string name="page_info_size">Taille :</string>
+ <string name="page_info_type">Type :</string>
+ <string name="page_info_view">Afficher les infos page</string>
+ <string name="password">Mot de passe</string>
+ <string name="popup_window_attempt">Ce site essaie d\'ouvrir une
+ fenêtre pop-up.</string>
+ <string name="pref_content_autofit">Auto ajuster pages</string>
+ <string name="pref_content_autofit_summary">Ajuster les pages Web à l\'écran</string>
+ <string name="pref_content_block_popups">Bloquer les fenêtres pop-up</string>
+ <string name="pref_content_homepage">Définir la page d\'accueil</string>
+ <string name="pref_content_javascript">Activer JavaScript</string>
+ <string name="pref_content_load_images">Charger les images</string>
+ <string name="pref_content_load_images_summary">Afficher les images sur les pages Web</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 l\'actuelle</string>
+ <string name="pref_content_title">Paramètres de contenu de la page</string>
+ <string name="pref_development_nav_dump">Activer décharge cache nav</string>
+ <string name="pref_development_normal_rendering">Rendu normal</string>
+ <string name="pref_development_single_column_rendering">Rendu en une colonne</string>
+ <string name="pref_development_title">Déboguer</string>
+ <string name="pref_development_trace">Activer le traçage</string>
+ <string name="pref_development_track">Activer la boute de commande</string>
+ <string name="pref_development_uastring">ChaîneUA</string>
+ <string name="pref_development_viewport">Utiliser une fenêtre d\'affichage large</string>
+ <string name="pref_extras_gears_enable">Activer les équipements</string>
+ <string name="pref_extras_gears_enable_summary">Applications étendent la fonctionnalité du navigateur</string>
+ <string name="pref_extras_gears_settings">Paramètre des équipements</string>
+ <string name="pref_extras_gears_settings_summary">Applications étendent la fonctionnalité du navigateur</string>
+ <string name="pref_extras_reset_default">Réinitialiser à défaut</string>
+ <string name="pref_extras_reset_default_dlg">Toutes les données du navigateur seront effacées et les paramètres retourneront aux valeurs par défaut.</string>
+ <string name="pref_extras_reset_default_dlg_title">Réinitialiser à défaut</string>
+ <string name="pref_extras_reset_default_summary">Effacer toutes les données du navigateur et réinitialiser les paramètres aux valeurs par défaut</string>
+ <string name="pref_extras_title">Paramètres avancés</string>
+ <string name="pref_plugin_installed_empty_listxx">Aucun plug-in installé.</string>
+ <string name="pref_plugin_installed_summaryxx">Afficher les plug-ins actuellement installés.</string>
+ <string name="pref_privacy_clear_cache">Effacer la cache</string>
+ <string name="pref_privacy_clear_cache_dlg">La cache va être effacée.</string>
+ <string name="pref_privacy_clear_cache_summary">Supprimer tout le contenu de page en cache</string>
+ <string name="pref_privacy_clear_cookies">Effacer toutes les données de cookies</string>
+ <string name="pref_privacy_clear_cookies_dlg">Tous les cookies vont être effacés.</string>
+ <string name="pref_privacy_clear_cookies_summary">Effacer tous les cookies du navigateur</string>
+ <string name="pref_privacy_clear_form_data">Effacer les données de formulaire</string>
+ <string name="pref_privacy_clear_form_data_dlg">Toutes les données de formulaire enregistrées vont être effacées.</string>
+ <string name="pref_privacy_clear_form_data_summary">Effacer toutes les données de formulaire enregistrées</string>
+ <string name="pref_privacy_clear_history">Effacer l\'historique</string>
+ <string name="pref_privacy_clear_history_dlg">L\'historique de navigation du navigateur sera effacé.</string>
+ <string name="pref_privacy_clear_history_summary">Effacer l\'historique de navigation du navigateur</string>
+ <string name="pref_privacy_clear_passwords">Effacer les mots de passe</string>
+ <string name="pref_privacy_clear_passwords_dlg">Tous les mots de passe enregistrés vont être effacés.</string>
+ <string name="pref_privacy_clear_passwords_summary">Effacer tous les mots de passe enregistrés</string>
+ <string name="pref_privacy_title">Paramètres de vie privée</string>
+ <string name="pref_security_accept_cookies">Accepter les cookies</string>
+ <string name="pref_security_accept_cookies_summary">Permettre aux sites d\'enregistrer et de lire les données de \"cookies\"</string>
+ <string name="pref_security_remember_passwords">Se souvenir des 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">Se souvenir des données de formulaire</string>
+ <string name="pref_security_save_form_data_summary">Se souvenir des données saisies dans les formulaires pour utilisation ultérieure</string>
+ <string name="pref_security_show_security_warning">Afficher les 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_title">Paramètres de sécurité</string>
+ <string name="pref_text_size">Taille du texte</string>
+ <string name="pref_text_size_dialogtitle">Taille du texte</string>
+ <string name="pref_text_size_summary">Sélectionnez la taille du texte sur les pages Web</string>
+ <string name="prev">Préc</string>
+ <string name="question_mark">\?</string>
+ <string name="reload">Actualiser</string>
+ <string name="remove_bookmark">Supprimer le signet</string>
+ <string name="remove_history_item">Supprimer de l\'historique</string>
+ <string name="replace">Remplacer</string>
+ <string name="retrieving_creds_dlg_msg">Récupération des détails de connexion\u2026</string>
+ <string name="retry">Réessayer</string>
+ <string name="save">OK</string>
+ <string name="save_to_bookmarks">Lien signet</string>
+ <string name="search">Recherche</string>
+ <string name="search_button_text">Aller</string>
+ <string name="search_hint">Entrer l\'adresse Web</string>
+ <string name="search_label">Navigateur</string>
+ <string name="security_warning">Avertissement de sécurité</string>
+ <string name="sha1_fingerprint">Empreinte digitale SHA1 :</string>
+ <string name="share_page">Partager page</string>
+ <string name="shortcut_bookmark">Signet</string>
+ <string name="sign_in_to">Connexion à <xliff:g id="hostname">%s1</xliff:g> \"<xliff:g id="realm">%s2</xliff:g>\"</string>
+ <string name="ssl_certificate">Certificat de sécurité</string>
+ <string name="ssl_certificate_is_valid">Ce certificat est valide.</string>
+ <string name="ssl_continue">Continuer</string>
+ <string name="ssl_expired">Ce certificat est expiré.</string>
+ <string name="ssl_mismatch">Le nom du site ne correspond pas au nom sur le certificat.</string>
+ <string name="ssl_not_yet_valid">Ce certificat n\'est pas encore valide.</string>
+ <string name="ssl_untrusted">Ce certificat n\'est pas d\'une autorité de confiance.</string>
+ <string name="ssl_warnings_header">Il y a des problèmes avec le certificat de sécurité de ce site.</string>
+ <string name="stop">Arrêter</string>
+ <string name="stopping">Arrêt\u2026</string>
+ <string name="tab_picker_bookmark">Signet</string>
+ <string name="tab_picker_new_tab">Nouvelle fenêtre</string>
+ <string name="tab_picker_remove_tab">Fermer</string>
+ <string name="tab_picker_send_url">Partager le lien</string>
+ <string name="tab_picker_title">Fenêtre actuelle</string>
+ <string name="tab_picker_view_tab">Afficher</string>
+ <string name="too_many_subwindows_dialog_message">Impossible d\'ouvrir une nouvelle
+ fenêtre pop-up car seulement une peut être ouverte à la fois.</string>
+ <string name="too_many_subwindows_dialog_title">Pop-up déjà ouverte</string>
+ <string name="too_many_windows_dialog_message">Impossible d\'ouvrir une nouvelle fenêtre parce que vous avez déjà ouvert le nombre maximum.</string>
+ <string name="too_many_windows_dialog_title">Limite de fenêtre atteinte</string>
+ <string name="username">Nom</string>
+ <string name="validity_period">Validité :</string>
+ <string name="view_certificate">Afficher le certificat</string>
+ <string name="view_tabs">Aperçu de fenêtre</string>
+ <string name="view_tabs_condensed">Fenêtre</string>
+ <string name="visual_history">Historique de fenêtre</string>
+ <string name="with">avec</string>
+ <string name="zero">0</string>
+</resources>
diff --git a/res/values-it-rIT/strings.xml b/res/values-it-rIT/strings.xml
new file mode 100644
index 00000000..6420cd6f
--- /dev/null
+++ b/res/values-it-rIT/strings.xml
@@ -0,0 +1,270 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="action">Accedi</string>
+ <string name="activity_instrumentation_functional_test_runner">Prova funzionamento del browser</string>
+ <string name="activity_instrumentation_test_runner">Prova verifica del browser</string>
+ <string name="add_new_bookmark">Nuovo segnalibro</string>
+ <string name="allow">Consenti</string>
+ <string name="attention">Attenzione</string>
+ <string name="back">Indietro</string>
+ <string name="block">Blocca</string>
+ <string name="bookmark_needs_title">"Il segnalibro deve avere un nome."</string>
+ <string name="bookmark_needs_url">"Il segnalibro deve avere un'ubicazione."</string>
+ <string name="bookmark_page">Pagina segnalibri visualizzati di recente</string>
+ <string name="bookmark_saved">Salvato nei segnalibri.</string>
+ <string name="bookmark_url_not_valid">URL non valido.</string>
+ <string name="bookmarks">Segnalibri</string>
+ <string name="browserFrame307Post">Reindirizzamento in corso della pagina Web. Reinviare i dati immessi nel modulo nella nuova ubicazione?</string>
+ <string name="browserFrameFileErrorLabel">Problema con il file</string>
+ <string name="browserFrameFormResubmitLabel">Conferma</string>
+ <string name="browserFrameFormResubmitMessage">La pagina che si desidera visualizzare contiene dati già inviati (\"POSTDATA\"). Se si rinviano i dati, verranno ripetute tutte le azioni sulla pagina, ad esempio ricerca o acquisto online.</string>
+ <string name="browserFrameNetworkErrorLabel">Problema di connessione dati</string>
+ <string name="browserFrameRedirect">Reindirizzamento</string>
+ <string name="browser_bookmarks_page_bookmarks_text">Segnalibri</string>
+ <string name="browser_history">Pagine visitate di recente</string>
+ <string name="cache_cleared">Cache svuotata</string>
+ <string name="cancel">Annulla</string>
+ <string name="clear">Cancella</string>
+ <string name="clear_history">Cancella cronologia</string>
+ <string name="close">Chiudi</string>
+ <string name="close_window">Questa finestra verrà chiusa.</string>
+ <string name="common_name">Nome comune:</string>
+ <string name="contextmenu_add_contact">Aggiungi contatto</string>
+ <string name="contextmenu_bookmark_thislink">Collegamento segnalibro</string>
+ <string name="contextmenu_copy">Copia</string>
+ <string name="contextmenu_copylink">Copia URL collegamento</string>
+ <string name="contextmenu_dial_dot">Componi\u2026</string>
+ <string name="contextmenu_download_image">Salva immagine</string>
+ <string name="contextmenu_javascript">JavaScript</string>
+ <string name="contextmenu_map">Mappa</string>
+ <string name="contextmenu_openlink">Apri</string>
+ <string name="contextmenu_openlink_newwindow">Apri in un'altra finestra</string>
+ <string name="contextmenu_savelink">Salva collegamento</string>
+ <string name="contextmenu_send_mail">Invia messaggio e-mail</string>
+ <string name="contextmenu_sharelink">Condividi collegamento</string>
+ <string name="contextmenu_view_image">Visualizza immagine</string>
+ <string name="current_page">Pagina attuale:\u0020</string>
+ <string name="delete">Elimina</string>
+ <string name="delete_bookmark">Elimina</string>
+ <string name="delete_bookmark_warning">Verrà eliminato il segnalibro \"<xliff:g id="bookmark">%s</xliff:g>\".</string>
+ <string name="do_not_save">Annulla</string>
+ <string name="done">Completato</string>
+ <string name="download_cancel_dlg_msg">Tutti i download <xliff:g id="download_count">%d</xliff:g> verranno annullati ed eliminati dalla cronologia di download.</string>
+ <string name="download_cancel_dlg_title">Annulla download</string>
+ <string name="download_canceled">Download annullato.</string>
+ <string name="download_clear_dlg_msg">Tutti gli elementi verranno cancellati dall'elenco e rimossi dalla cache del browser.</string>
+ <string name="download_clear_dlg_title">Cancella</string>
+ <string name="download_error">Download non riuscito.</string>
+ <string name="download_failed_generic_dlg_title">Download non riuscito</string>
+ <string name="download_file_error">Impossibile completare il download. Spazio insufficiente.</string>
+ <string name="download_file_error_dlg_msg">Impossibile scaricare <xliff:g id="filename">%s</xliff:g>.\nLiberare spazio sul telefono e riprovare.</string>
+ <string name="download_file_error_dlg_title">Spazio insufficiente</string>
+ <string name="download_length_required">Download non riuscito. Impossibile determinare le dimensioni dell'elemento.</string>
+ <string name="download_menu_cancel">Annulla download</string>
+ <string name="download_menu_cancel_all">Annulla tutti i download</string>
+ <string name="download_menu_clear">Cancella dall'elenco</string>
+ <string name="download_menu_clear_all">Cancella elenco</string>
+ <string name="download_menu_open">Apri</string>
+ <string name="download_no_application">Impossibile trovare un'applicazione per l'apertura del file.</string>
+ <string name="download_no_sdcard_dlg_msg">Per scaricare <xliff:g id="filename">%s</xliff:g>, è necessario utilizzare una scheda SD.</string>
+ <string name="download_no_sdcard_dlg_title">Nessuna scheda SD</string>
+ <string name="download_not_acceptable">Download non riuscito. Il contenuto che si sta tentando di scaricare non è supportato dal telefono.</string>
+ <string name="download_pending">Avvio del download in corso\u2026</string>
+ <string name="download_pending_network">In attesa della connessione dati\u2026</string>
+ <string name="download_precondition_failed">Download interrotto. Impossibile ripristinarlo.</string>
+ <string name="download_running">Scaricamento in corso\u2026</string>
+ <string name="download_running_paused">In attesa della connessione dati\u2026</string>
+ <string name="download_success"><xliff:g id="file">%s</xliff:g> download completato.</string>
+ <string name="download_title">Cronologia download</string>
+ <string name="download_unknown_filename">&lt;Sconosciuto&gt;</string>
+ <string name="dump_nav">Elimina cache di navigazione</string>
+ <string name="edit_bookmark">Modifica segnalibro</string>
+ <string name="empty_bookmark">"Impossibile creare un segnalibro vuoto."</string>
+ <string name="empty_history">La cronologia del browser è vuota.</string>
+ <string name="expires_on">Scade il:</string>
+ <string name="find_dot">Trova nella pagina</string>
+ <string name="fingerprints">Impronte digitali:</string>
+ <string name="forward">Inoltra</string>
+ <string name="goto_dot">Vai a URL</string>
+ <string name="history">Cronologia</string>
+ <string name="history_picker_view_web_page">Apri</string>
+ <string name="homepage">Home page</string>
+ <string name="homepage_set">Home page aggiornata</string>
+ <string name="http">http://</string>
+ <string name="info_base">Titolo:\n <xliff:g id="title">%s1</xliff:g> \n\nURL: \n <xliff:g id="url">%s2</xliff:g></string>
+ <string name="issued_by">Rilasciato da:</string>
+ <string name="issued_on">Rilasciato il:</string>
+ <string name="issued_to">Rilasciato a:</string>
+ <string name="js_dialog_before_unload">Uscire da questa pagina?\n\n<xliff:g id="message">%s</xliff:g>\n\nScegliere OK per continuare oppure Annulla per restare sulla pagina attuale.</string>
+ <string name="js_dialog_title_default">JavaScript</string>
+ <string name="js_dialog_title_prefix">Nella pagina a</string>
+ <string name="js_dialog_title_suffix">è riportato:</string>
+ <string name="loadSuspended">Il caricamento della pagina continuerà dopo il ripristino della connessione.</string>
+ <string name="loadSuspendedTitle">Nessuna connessione di rete</string>
+ <string name="location">Ubicazione</string>
+ <string name="matches_found">Corrispondenze trovate:</string>
+ <string name="md5_fingerprint">Impronta digitale MD5:</string>
+ <string name="menu_bookmark_page">Aggiungi pagina nei segnalibri</string>
+ <string name="menu_flip_orientation">Capovolgi orientamento</string>
+ <string name="menu_preferences">Impostazioni</string>
+ <string name="menu_use_as_homepage">Imposta come home page</string>
+ <string name="menu_view_download">Download</string>
+ <string name="menu_zoom">Zoom</string>
+ <string name="name">Nome</string>
+ <string name="new_window">Nuova finestra</string>
+ <string name="next">Avanti</string>
+ <string name="no_database">Nessun database</string>
+ <string name="no_downloads">La memoria di download è vuota.</string>
+ <string name="ok">OK</string>
+ <string name="oma_error_failed_to_download">Impossibile scaricare il supporto.</string>
+ <string name="oma_error_insufficient_memory">Memoria insufficiente. Controllare la scheda SD.</string>
+ <string name="oma_error_invalid_descriptor">Descrittore download non valido.</string>
+ <string name="oma_error_non_acceptable_content">Contenuto non supportato.</string>
+ <string name="oma_label_action">Download</string>
+ <string name="oma_label_cancel">Esci</string>
+ <string name="oma_label_description">Descrizione:</string>
+ <string name="oma_label_download_error">Errore di download:</string>
+ <string name="oma_label_exit">Esci</string>
+ <string name="oma_label_name">Nome:</string>
+ <string name="oma_label_proceed">Prosegui</string>
+ <string name="oma_label_size">Dimensione:</string>
+ <string name="oma_label_type">Tipo:</string>
+ <string name="oma_label_vendor">Fornitore:</string>
+ <string name="oma_label_want_to_download">Scaricare questo contenuto?</string>
+ <string name="oma_status_attribute_mismatch">Attributo non corrispondente</string>
+ <string name="oma_status_device_aborted">Telefonata interrotta</string>
+ <string name="oma_status_insufficient_memory">Memoria insufficiente</string>
+ <string name="oma_status_invalid_descriptor">Descrittore non valido</string>
+ <string name="oma_status_invalid_version">Versione non valida</string>
+ <string name="oma_status_loader_error">Errore nel caricatore</string>
+ <string name="oma_status_loss_of_service">Perdita di servizio</string>
+ <string name="oma_status_non_acceptable_content">Contenuto inacettabile</string>
+ <string name="oma_status_success">Riuscito</string>
+ <string name="oma_warning_one_or_more_types_not_supported">Una o più categorie di contenuto non sono supportate.</string>
+ <string name="open_bookmark">Apri</string>
+ <string name="open_in_new_window">Apri in un'altra finestra</string>
+ <string name="org_name">Organizzazione:</string>
+ <string name="org_unit">Unità organizzativa:</string>
+ <string name="override">Sostituisci</string>
+ <string name="override_message">Il segnalibro esistente verrà sostituito.</string>
+ <string name="page_info">Informazioni pagina</string>
+ <string name="page_info_address">Indirizzo:</string>
+ <string name="page_info_cache_source">Origine cache:</string>
+ <string name="page_info_encoding">Codifica:</string>
+ <string name="page_info_expires">Scade:</string>
+ <string name="page_info_modified">Modificato:</string>
+ <string name="page_info_referring_url">URL di riferimento:</string>
+ <string name="page_info_render_mode">Modalità rendering:</string>
+ <string name="page_info_size">Dimensione:</string>
+ <string name="page_info_type">Tipo:</string>
+ <string name="page_info_view">Visualizza informazioni pagina</string>
+ <string name="password">Password</string>
+ <string name="popup_window_attempt">Questo sito sta tentando di aprire una
+ finestra pop-up.</string>
+ <string name="pref_content_autofit">Adatta pagine automaticamente</string>
+ <string name="pref_content_autofit_summary">Formatta pagine Web per adattarle allo schermo</string>
+ <string name="pref_content_block_popups">Blocca finestre pop-up</string>
+ <string name="pref_content_homepage">Imposta home page</string>
+ <string name="pref_content_javascript">Abilita JavaScript</string>
+ <string name="pref_content_load_images">Carica immagini</string>
+ <string name="pref_content_load_images_summary">Visualizza immagini sulle pagine Web</string>
+ <string name="pref_content_open_in_background">Apri in secondo piano</string>
+ <string name="pref_content_open_in_background_summary">Nuove finestre aperte dietro quella corrente</string>
+ <string name="pref_content_title">Impostazioni contenuto pagina</string>
+ <string name="pref_development_nav_dump">Abilita nuova dump cache</string>
+ <string name="pref_development_normal_rendering">Rendering normale</string>
+ <string name="pref_development_single_column_rendering">Rendering singola colonna</string>
+ <string name="pref_development_title">Debug</string>
+ <string name="pref_development_trace">Abilita tracciatura</string>
+ <string name="pref_development_track">Abilita trackball</string>
+ <string name="pref_development_uastring">UAString</string>
+ <string name="pref_development_viewport">Usa visualizzazione ampia</string>
+ <string name="pref_extras_gears_enable">Abilita Gears</string>
+ <string name="pref_extras_gears_enable_summary">Applicazioni che estendono la funzionalità del browser</string>
+ <string name="pref_extras_gears_settings">Impostazioni Gears</string>
+ <string name="pref_extras_gears_settings_summary">Applicazioni che estendono la funzionalità del browser</string>
+ <string name="pref_extras_reset_default">Ripristina predefinito</string>
+ <string name="pref_extras_reset_default_dlg">Verranno cancellati tutti i dati del browser e verranno ripristinati i valori predefiniti.</string>
+ <string name="pref_extras_reset_default_dlg_title">Ripristina predefinito</string>
+ <string name="pref_extras_reset_default_summary">Cancella tutti i dati del browser e ripristina le impostazioni predefinite</string>
+ <string name="pref_extras_title">Impostazioni avanzate</string>
+ <string name="pref_plugin_installed_empty_listxx">Nessun plug-in installato.</string>
+ <string name="pref_plugin_installed_summaryxx">Visualizza plug-in attualmente installati</string>
+ <string name="pref_privacy_clear_cache">Svuota cache</string>
+ <string name="pref_privacy_clear_cache_dlg">La cache verrà svuotata.</string>
+ <string name="pref_privacy_clear_cache_summary">Elimina tutto il contenuto della pagina nella cache</string>
+ <string name="pref_privacy_clear_cookies">Cancella tutti i dati dei cookie</string>
+ <string name="pref_privacy_clear_cookies_dlg">Tutti i cookie verranno cancellati.</string>
+ <string name="pref_privacy_clear_cookies_summary">Cancella tutti i cookie del browser</string>
+ <string name="pref_privacy_clear_form_data">Cancella i dati del modulo</string>
+ <string name="pref_privacy_clear_form_data_dlg">Tutti i dati salvati nel modulo verranno cancellati.</string>
+ <string name="pref_privacy_clear_form_data_summary">Cancella tutti i dati salvati nel modulo</string>
+ <string name="pref_privacy_clear_history">Cancella cronologia</string>
+ <string name="pref_privacy_clear_history_dlg">Verrà cancellata la cronologia di navigazione del browser.</string>
+ <string name="pref_privacy_clear_history_summary">Cancella la cronologia di navigazione del browser</string>
+ <string name="pref_privacy_clear_passwords">Cancella password</string>
+ <string name="pref_privacy_clear_passwords_dlg">Tutte le password salvate verranno cancellate.</string>
+ <string name="pref_privacy_clear_passwords_summary">Cancella tutte le password salvate</string>
+ <string name="pref_privacy_title">Impostazioni privacy</string>
+ <string name="pref_security_accept_cookies">Accetta cookie</string>
+ <string name="pref_security_accept_cookies_summary">Consente ai siti di salvare e leggere i dati dei \"cookie\"</string>
+ <string name="pref_security_remember_passwords">Memorizza 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 modulo</string>
+ <string name="pref_security_save_form_data_summary">Memorizza i dati immessi nel modulo per utilizzarli in seguito</string>
+ <string name="pref_security_show_security_warning">Mostra avvisi sulla sicurezza</string>
+ <string name="pref_security_show_security_warning_summary">Mostra avviso se si verifica un problema con la sicurezza di un sito</string>
+ <string name="pref_security_title">Impostazioni protezione</string>
+ <string name="pref_text_size">Dimensioni testo</string>
+ <string name="pref_text_size_dialogtitle">Dimensioni testo</string>
+ <string name="pref_text_size_summary">Selezionare la dimensione del testo sulle pagine Web</string>
+ <string name="prev">Prec</string>
+ <string name="question_mark">\?</string>
+ <string name="reload">Aggiorna</string>
+ <string name="remove_bookmark">Elimina segnalibro</string>
+ <string name="remove_history_item">Rimuovi da cronologia</string>
+ <string name="replace">Sostituisci</string>
+ <string name="retrieving_creds_dlg_msg">Recupero in corso dei dettagli di accesso\u2026</string>
+ <string name="retry">Riprova</string>
+ <string name="save">OK</string>
+ <string name="save_to_bookmarks">Collegamento segnalibro</string>
+ <string name="search">Cerca</string>
+ <string name="search_button_text">Vai</string>
+ <string name="search_hint">Immetti indirizzo Web</string>
+ <string name="search_label">Browser</string>
+ <string name="security_warning">Avviso di sicurezza</string>
+ <string name="sha1_fingerprint">Impronta digitale SHA1:</string>
+ <string name="share_page">Condividi pagina</string>
+ <string name="shortcut_bookmark">Segnalibro</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="ssl_certificate">Certificato di sicurezza</string>
+ <string name="ssl_certificate_is_valid">Questo certificato è valido.</string>
+ <string name="ssl_continue">Continua</string>
+ <string name="ssl_expired">Questo certificato è scaduto.</string>
+ <string name="ssl_mismatch">Il nome del sito non corrisponde a quello del certificato.</string>
+ <string name="ssl_not_yet_valid">Questo certificato non è ancora valido.</string>
+ <string name="ssl_untrusted">Questo certificato non proviene da un'autorità attendibile.</string>
+ <string name="ssl_warnings_header">Si sono verificati problemi con il certificato di sicurezza per questo sito.</string>
+ <string name="stop">Interrompi</string>
+ <string name="stopping">Interruzione in corso\u2026</string>
+ <string name="tab_picker_bookmark">Segnalibro</string>
+ <string name="tab_picker_new_tab">Nuova finestra</string>
+ <string name="tab_picker_remove_tab">Chiudi</string>
+ <string name="tab_picker_send_url">Condividi collegamento</string>
+ <string name="tab_picker_title">Finestre attuali</string>
+ <string name="tab_picker_view_tab">Visualizza</string>
+ <string name="too_many_subwindows_dialog_message">Impossibile aprire una nuova
+ finestra pop-up. È possibile aprire una sola finestra alla volta.</string>
+ <string name="too_many_subwindows_dialog_title">Pop-up già aperta</string>
+ <string name="too_many_windows_dialog_message">Impossibile aprire un'altra finestra. È già stato aperto il numero massimo.</string>
+ <string name="too_many_windows_dialog_title">Limite finestra raggiunto</string>
+ <string name="username">Nome</string>
+ <string name="validity_period">Validità:</string>
+ <string name="view_certificate">Visualizza certificato</string>
+ <string name="view_tabs">Panoramica finestra</string>
+ <string name="view_tabs_condensed">Finestra</string>
+ <string name="visual_history">Cronologia finestre</string>
+ <string name="with">con</string>
+ <string name="zero">0</string>
+</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
new file mode 100644
index 00000000..fae76b6a
--- /dev/null
+++ b/res/values-zh-rTW/strings.xml
@@ -0,0 +1,270 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="action">登入</string>
+ <string name="activity_instrumentation_functional_test_runner">瀏覽器功能測試執行器</string>
+ <string name="activity_instrumentation_test_runner">瀏覽器測試執行器</string>
+ <string name="add_new_bookmark">新增書籤</string>
+ <string name="allow">允許</string>
+ <string name="attention">注意</string>
+ <string name="back">上一步</string>
+ <string name="block">封鎖</string>
+ <string name="bookmark_needs_title">"必須輸入書籤名稱。"</string>
+ <string name="bookmark_needs_url">"必須輸入書籤位置。"</string>
+ <string name="bookmark_page">將上次檢視的頁面加入書籤</string>
+ <string name="bookmark_saved">儲存到書籤。</string>
+ <string name="bookmark_url_not_valid">URL 無效。</string>
+ <string name="bookmarks">書籤</string>
+ <string name="browserFrame307Post">此網頁已經被重新導向。需要將輸入的表單資料重新傳送到新位置嗎?</string>
+ <string name="browserFrameFileErrorLabel">檔案發生問題</string>
+ <string name="browserFrameFormResubmitLabel">確認</string>
+ <string name="browserFrameFormResubmitMessage">正在嘗試檢視的頁面包含了已經提交的資料 (\"POSTDATA\")。如果需要重新傳送資料,則頁面上之表單所要執行 (例如搜尋或線上購買) 的任何動作將會被重複執行。</string>
+ <string name="browserFrameNetworkErrorLabel">資料連線能力發生問題</string>
+ <string name="browserFrameRedirect">重新導向</string>
+ <string name="browser_bookmarks_page_bookmarks_text">書籤</string>
+ <string name="browser_history">最近造訪的頁面</string>
+ <string name="cache_cleared">快取已經清除</string>
+ <string name="cancel">取消</string>
+ <string name="clear">清除</string>
+ <string name="clear_history">清除記錄</string>
+ <string name="close">關閉</string>
+ <string name="close_window">將會關閉此視窗。</string>
+ <string name="common_name">公用名稱:</string>
+ <string name="contextmenu_add_contact">新增連絡人</string>
+ <string name="contextmenu_bookmark_thislink">書籤連結</string>
+ <string name="contextmenu_copy">複製</string>
+ <string name="contextmenu_copylink">複製 URL 連結</string>
+ <string name="contextmenu_dial_dot">撥打\u2026</string>
+ <string name="contextmenu_download_image">儲存影像</string>
+ <string name="contextmenu_javascript">JavaScript</string>
+ <string name="contextmenu_map">地圖</string>
+ <string name="contextmenu_openlink">開啟</string>
+ <string name="contextmenu_openlink_newwindow">在新視窗中開啟</string>
+ <string name="contextmenu_savelink">儲存連結</string>
+ <string name="contextmenu_send_mail">傳送電子郵件</string>
+ <string name="contextmenu_sharelink">共用連結</string>
+ <string name="contextmenu_view_image">檢視影像</string>
+ <string name="current_page">目前的頁面:\u0020</string>
+ <string name="delete">刪除</string>
+ <string name="delete_bookmark">刪除</string>
+ <string name="delete_bookmark_warning">將會刪除 \"<xliff:g id="bookmark">%s</xliff:g>\" 書籤。</string>
+ <string name="do_not_save">取消</string>
+ <string name="done">完成</string>
+ <string name="download_cancel_dlg_msg">將會取消所有 <xliff:g id="download_count">%d</xliff:g> 的下載,並從下載記錄中清除。</string>
+ <string name="download_cancel_dlg_title">取消下載</string>
+ <string name="download_canceled">下載已經取消。</string>
+ <string name="download_clear_dlg_msg">將會從清單中清除所有的項目,並從瀏覽器快取中移除。</string>
+ <string name="download_clear_dlg_title">清除</string>
+ <string name="download_error">下載失敗。</string>
+ <string name="download_failed_generic_dlg_title">下載失敗</string>
+ <string name="download_file_error">無法完成下載。空間不足。</string>
+ <string name="download_file_error_dlg_msg">無法下載 <xliff:g id="filename">%s</xliff:g>。\n請釋放電話上的部分空間並再試一次。</string>
+ <string name="download_file_error_dlg_title">空間不足</string>
+ <string name="download_length_required">無法下載。無法判斷項目的大小。</string>
+ <string name="download_menu_cancel">取消下載</string>
+ <string name="download_menu_cancel_all">取消所有下載</string>
+ <string name="download_menu_clear">從清單中清除</string>
+ <string name="download_menu_clear_all">清除清單</string>
+ <string name="download_menu_open">開啟</string>
+ <string name="download_no_application">找不到應用程式開啟此檔案。</string>
+ <string name="download_no_sdcard_dlg_msg">需要 SD 卡才能下載 <xliff:g id="filename">%s</xliff:g>。</string>
+ <string name="download_no_sdcard_dlg_title">無 SD 卡</string>
+ <string name="download_not_acceptable">無法下載。電話不支援要下載的內容。</string>
+ <string name="download_pending">開始下載\u2026</string>
+ <string name="download_pending_network">正在等待資料連線\u2026</string>
+ <string name="download_precondition_failed">下載已經中斷。無法繼續。</string>
+ <string name="download_running">正在下載\u2026</string>
+ <string name="download_running_paused">正在等待資料連線\u2026</string>
+ <string name="download_success"><xliff:g id="file">%s</xliff:g> 下載完成。</string>
+ <string name="download_title">下載記錄</string>
+ <string name="download_unknown_filename">&lt;無法辨識&gt;</string>
+ <string name="dump_nav">刪除導覽快取</string>
+ <string name="edit_bookmark">編輯書籤</string>
+ <string name="empty_bookmark">"無法建立空白書籤。"</string>
+ <string name="empty_history">瀏覽器記錄空白。</string>
+ <string name="expires_on">到期日:</string>
+ <string name="find_dot">搜尋頁面</string>
+ <string name="fingerprints">指紋:</string>
+ <string name="forward">下一頁</string>
+ <string name="goto_dot">移至 URL</string>
+ <string name="history">記錄</string>
+ <string name="history_picker_view_web_page">開啟</string>
+ <string name="homepage">首頁</string>
+ <string name="homepage_set">首頁已經更新</string>
+ <string name="http">http://</string>
+ <string name="info_base">標題:\n <xliff:g id="title">%s1</xliff:g> \n\nURL:\n <xliff:g id="url">%s2</xliff:g></string>
+ <string name="issued_by">發行者:</string>
+ <string name="issued_on">發行日期:</string>
+ <string name="issued_to">發給:</string>
+ <string name="js_dialog_before_unload">瀏覽此頁面以外的網頁?\n\n<xliff:g id="message">%s</xliff:g>\n\n請選取確定繼續瀏覽,或選取取消停留在目前的頁面。</string>
+ <string name="js_dialog_title_default">JavaScript</string>
+ <string name="js_dialog_title_prefix">頁面</string>
+ <string name="js_dialog_title_suffix">指出:</string>
+ <string name="loadSuspended">此頁面將會在恢復連線之後繼續載入。</string>
+ <string name="loadSuspendedTitle">無網路連線</string>
+ <string name="location">位置</string>
+ <string name="matches_found">找到的符合項目:</string>
+ <string name="md5_fingerprint">MD5 指紋:</string>
+ <string name="menu_bookmark_page">將頁面加入書籤</string>
+ <string name="menu_flip_orientation">翻轉方向</string>
+ <string name="menu_preferences">設定</string>
+ <string name="menu_use_as_homepage">設定為首頁</string>
+ <string name="menu_view_download">下載</string>
+ <string name="menu_zoom">顯示比例</string>
+ <string name="name">名稱</string>
+ <string name="new_window">新增視窗</string>
+ <string name="next">下一步</string>
+ <string name="no_database">無資料庫!</string>
+ <string name="no_downloads">下載記錄空白。</string>
+ <string name="ok">確定</string>
+ <string name="oma_error_failed_to_download">無法下載媒體。</string>
+ <string name="oma_error_insufficient_memory">記憶體不足!請檢查 SD 卡。</string>
+ <string name="oma_error_invalid_descriptor">無效的下載描述符號。</string>
+ <string name="oma_error_non_acceptable_content">不支援此內容。</string>
+ <string name="oma_label_action">下載</string>
+ <string name="oma_label_cancel">結束</string>
+ <string name="oma_label_description">說明:</string>
+ <string name="oma_label_download_error">下載錯誤:</string>
+ <string name="oma_label_exit">結束</string>
+ <string name="oma_label_name">名稱:</string>
+ <string name="oma_label_proceed">繼續</string>
+ <string name="oma_label_size">大小:</string>
+ <string name="oma_label_type">類型:</string>
+ <string name="oma_label_vendor">廠商:</string>
+ <string name="oma_label_want_to_download">是否要下載此內容?</string>
+ <string name="oma_status_attribute_mismatch">屬性不相符</string>
+ <string name="oma_status_device_aborted">電話已經中止</string>
+ <string name="oma_status_insufficient_memory">記憶體不足</string>
+ <string name="oma_status_invalid_descriptor">無效的描述符號</string>
+ <string name="oma_status_invalid_version">無效的版本</string>
+ <string name="oma_status_loader_error">裝載程式錯誤</string>
+ <string name="oma_status_loss_of_service">服務中斷</string>
+ <string name="oma_status_non_acceptable_content">無法接受的內容</string>
+ <string name="oma_status_success">成功</string>
+ <string name="oma_warning_one_or_more_types_not_supported">不支援一或多個內容類別。</string>
+ <string name="open_bookmark">開啟</string>
+ <string name="open_in_new_window">在新視窗中開啟</string>
+ <string name="org_name">組織:</string>
+ <string name="org_unit">組織單位:</string>
+ <string name="override">取代</string>
+ <string name="override_message">將會取代現有書籤。</string>
+ <string name="page_info">頁面資訊</string>
+ <string name="page_info_address">網址:</string>
+ <string name="page_info_cache_source">快取來源:</string>
+ <string name="page_info_encoding">編碼:</string>
+ <string name="page_info_expires">到期日:</string>
+ <string name="page_info_modified">修改日期:</string>
+ <string name="page_info_referring_url">參照 URL:</string>
+ <string name="page_info_render_mode">顯示模式:</string>
+ <string name="page_info_size">大小:</string>
+ <string name="page_info_type">類型:</string>
+ <string name="page_info_view">檢視頁面資訊</string>
+ <string name="password">密碼</string>
+ <string name="popup_window_attempt">網站正在嘗試開啟
+ 快顯視窗。</string>
+ <string name="pref_content_autofit">自動調整為符合頁面</string>
+ <string name="pref_content_autofit_summary">將網頁格式化為符合畫面</string>
+ <string name="pref_content_block_popups">攔截快顯視窗</string>
+ <string name="pref_content_homepage">設定首頁</string>
+ <string name="pref_content_javascript">啟用 JavaScript</string>
+ <string name="pref_content_load_images">載入影像</string>
+ <string name="pref_content_load_images_summary">顯示網頁上的影像</string>
+ <string name="pref_content_open_in_background">在背景中開啟</string>
+ <string name="pref_content_open_in_background_summary">在目前的視窗後方開啟新視窗</string>
+ <string name="pref_content_title">頁面內容設定</string>
+ <string name="pref_development_nav_dump">啟用導覽快取刪除</string>
+ <string name="pref_development_normal_rendering">正常顯示</string>
+ <string name="pref_development_single_column_rendering">單一欄位顯示</string>
+ <string name="pref_development_title">偵錯</string>
+ <string name="pref_development_trace">啟用追蹤</string>
+ <string name="pref_development_track">啟用軌跡球</string>
+ <string name="pref_development_uastring">UAString</string>
+ <string name="pref_development_viewport">使用寬視圖</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_extras_gears_settings_summary">擴充瀏覽器功能的應用程式</string>
+ <string name="pref_extras_reset_default">重設成預設值</string>
+ <string name="pref_extras_reset_default_dlg">將會清除所有瀏覽器的資料,且設定也將會還原為預設值。</string>
+ <string name="pref_extras_reset_default_dlg_title">重設成預設值</string>
+ <string name="pref_extras_reset_default_summary">清除所有瀏覽器的資料,並將所有設定重設成預設值</string>
+ <string name="pref_extras_title">進階設定</string>
+ <string name="pref_plugin_installed_empty_listxx">尚未安裝外掛程式。</string>
+ <string name="pref_plugin_installed_summaryxx">檢視目前安裝的外掛程式</string>
+ <string name="pref_privacy_clear_cache">清除快取</string>
+ <string name="pref_privacy_clear_cache_dlg">將會清除快取。</string>
+ <string name="pref_privacy_clear_cache_summary">刪除所有快取的頁面內容</string>
+ <string name="pref_privacy_clear_cookies">清除所有 Cookie 的資料</string>
+ <string name="pref_privacy_clear_cookies_dlg">將會清除所有的 Cookie。</string>
+ <string name="pref_privacy_clear_cookies_summary">清除所有瀏覽器的 Cookie</string>
+ <string name="pref_privacy_clear_form_data">清除表單資料</string>
+ <string name="pref_privacy_clear_form_data_dlg">將會清除所有儲存的表單資料。</string>
+ <string name="pref_privacy_clear_form_data_summary">清除所有儲存的表單資料</string>
+ <string name="pref_privacy_clear_history">清除記錄</string>
+ <string name="pref_privacy_clear_history_dlg">將會清除瀏覽器的導覽記錄。</string>
+ <string name="pref_privacy_clear_history_summary">清除瀏覽器的導覽記錄</string>
+ <string name="pref_privacy_clear_passwords">清除密碼</string>
+ <string name="pref_privacy_clear_passwords_dlg">將會清除所有儲存的密碼。</string>
+ <string name="pref_privacy_clear_passwords_summary">清除所有儲存的密碼</string>
+ <string name="pref_privacy_title">隱私權設定</string>
+ <string name="pref_security_accept_cookies">接受 Cookie</string>
+ <string name="pref_security_accept_cookies_summary">允許網站儲存及讀取 \"Cookie\" 資料</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">記住表單中的 I 類型資料,以便稍後使用</string>
+ <string name="pref_security_show_security_warning">顯示安全性警告</string>
+ <string name="pref_security_show_security_warning_summary">如果網站的安全性發生問題,則顯示警告</string>
+ <string name="pref_security_title">安全性設定</string>
+ <string name="pref_text_size">文字大小</string>
+ <string name="pref_text_size_dialogtitle">文字大小</string>
+ <string name="pref_text_size_summary">選取網頁上的文字大小</string>
+ <string name="prev">上一頁</string>
+ <string name="question_mark">\?</string>
+ <string name="reload">重新整理</string>
+ <string name="remove_bookmark">刪除書籤</string>
+ <string name="remove_history_item">從記錄中移除</string>
+ <string name="replace">取代</string>
+ <string name="retrieving_creds_dlg_msg">正在擷取登入的詳細資料\u2026</string>
+ <string name="retry">重試</string>
+ <string name="save">確定</string>
+ <string name="save_to_bookmarks">書籤連結</string>
+ <string name="search">搜尋</string>
+ <string name="search_button_text">移至</string>
+ <string name="search_hint">輸入網址</string>
+ <string name="search_label">瀏覽器</string>
+ <string name="security_warning">安全性警告</string>
+ <string name="sha1_fingerprint">SHA1 指紋:</string>
+ <string name="share_page">共用頁面</string>
+ <string name="shortcut_bookmark">書籤</string>
+ <string name="sign_in_to">登入 <xliff:g id="hostname">%s1</xliff:g> \"<xliff:g id="realm">%s2</xliff:g>\"</string>
+ <string name="ssl_certificate">安全性憑證</string>
+ <string name="ssl_certificate_is_valid">有效的憑證。</string>
+ <string name="ssl_continue">繼續</string>
+ <string name="ssl_expired">此憑證已經過期。</string>
+ <string name="ssl_mismatch">網站名稱與憑證上的名稱不相符。</string>
+ <string name="ssl_not_yet_valid">無效的憑證。</string>
+ <string name="ssl_untrusted">此憑證不是來自信任的授權單位。</string>
+ <string name="ssl_warnings_header">此網站的安全性憑證發生問題。</string>
+ <string name="stop">停止</string>
+ <string name="stopping">正在停止\u2026</string>
+ <string name="tab_picker_bookmark">書籤</string>
+ <string name="tab_picker_new_tab">新增視窗</string>
+ <string name="tab_picker_remove_tab">關閉</string>
+ <string name="tab_picker_send_url">共用連結</string>
+ <string name="tab_picker_title">目前視窗</string>
+ <string name="tab_picker_view_tab">檢視</string>
+ <string name="too_many_subwindows_dialog_message">無法再開啟新的
+ 快顯視窗;在任何情況下一次只能開啟一個視窗。</string>
+ <string name="too_many_subwindows_dialog_title">已經開啟快顯視窗</string>
+ <string name="too_many_windows_dialog_message">無法開啟新的視窗;開啟的視窗數量已經達到上限。</string>
+ <string name="too_many_windows_dialog_title">已經達到視窗數量的上限</string>
+ <string name="username">名稱</string>
+ <string name="validity_period">有效性:</string>
+ <string name="view_certificate">檢視憑證</string>
+ <string name="view_tabs">視窗概觀</string>
+ <string name="view_tabs_condensed">視窗</string>
+ <string name="visual_history">視窗記錄</string>
+ <string name="with">含有</string>
+ <string name="zero">0</string>
+</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
new file mode 100644
index 00000000..71ed099d
--- /dev/null
+++ b/res/values/colors.xml
@@ -0,0 +1,37 @@
+<?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="translucent_white">#11ffffff</color>
+</resources>
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
new file mode 100644
index 00000000..cb66949c
--- /dev/null
+++ b/res/values/strings.xml
@@ -0,0 +1,506 @@
+<?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">
+ <!-- Sign-in dialog -->
+ <string name="sign_in_to">Sign in to <xliff:g id="hostname">%s1</xliff:g> \"<xliff:g id="realm">%s2</xliff:g>\"</string>
+ <string name="username">Name</string>
+ <string name="password">Password</string>
+ <string name="action">Sign in</string>
+ <!-- Button label -->
+ <string name="cancel">Cancel</string>
+ <!-- Button label -->
+ <string name="ok">OK</string>
+ <!-- Button label -->
+ <string name="done">Done</string>
+ <!-- Button label -->
+ <string name="prev">Prev</string>
+ <!-- Button label -->
+ <string name="next">Next</string>
+ <string name="matches_found">Matches found:</string>
+ <string name="zero">0</string>
+
+ <!-- Menu item -->
+ <string name="page_info">Page info</string>
+ <string name="page_info_view">View page info</string>
+ <!-- Page Info dialog strings -->
+ <string name="page_info_address">Address:</string>
+ <string name="page_info_type">Type:</string>
+ <string name="page_info_render_mode">Render mode:</string>
+ <string name="page_info_cache_source">Cache source:</string>
+ <string name="page_info_encoding">Encoding:</string>
+ <string name="page_info_size">Size:</string>
+ <string name="page_info_referring_url">Referring URL:</string>
+ <string name="page_info_modified">Modified:</string>
+ <string name="page_info_expires">Expires:</string>
+
+ <!-- SSL Error dialogs -->
+ <string name="ssl_warnings_header">There are problems with the security certificate for this site.</string>
+ <string name="ssl_continue">Continue</string>
+ <string name="security_warning">Security warning</string>
+ <string name="view_certificate">View certificate</string>
+
+ <string name="ssl_untrusted">This certificate is not from a trusted authority.</string>
+ <string name="ssl_mismatch">The name of the site does not match the name on the certificate.</string>
+ <string name="ssl_expired">This certificate has expired.</string>
+ <string name="ssl_not_yet_valid">This certificate is not valid yet.</string>
+
+ <!-- SSL Certificate dialogs -->
+ <string name="ssl_certificate">Security certificate</string>
+ <string name="ssl_certificate_is_valid">This certificate is valid.</string>
+ <string name="issued_to">Issued to:</string>
+ <string name="common_name">Common name:</string>
+ <string name="org_name">Organization:</string>
+ <string name="org_unit">Organizational unit:</string>
+ <string name="issued_by">Issued by:</string>
+ <string name="validity_period">Validity:</string>
+ <string name="issued_on">Issued on:</string>
+ <string name="expires_on">Expires on:</string>
+ <string name="fingerprints">Fingerprints:</string>
+ <string name="sha1_fingerprint">SHA1 fingerprint:</string>
+ <string name="md5_fingerprint">MD5 fingerprint:</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 -->
+ <string name="close">Close</string>
+ <!-- Confirmation dialog message -->
+ <string name="close_window">This window will be closed.</string>
+ <!-- Toast -->
+ <string name="stopping">Stopping\u2026</string>
+ <!-- Menu item -->
+ <string name="stop">Stop</string>
+ <!-- Menu item -->
+ <string name="reload">Refresh</string>
+ <!-- Menu item -->
+ <string name="back">Back</string>
+ <!-- Menu item -->
+ <string name="forward">Forward</string>
+ <!-- Button label -->
+ <string name="save">OK</string>
+ <!-- Button label -->
+ <string name="do_not_save">Cancel</string>
+ <!-- Field label in Bookmark dialog box -->
+ <string name="location">Location</string>
+ <!-- Field label in Bookmark dialog box -->
+ <string name="name">Name</string>
+ <!-- Initial value in Location field in Bookmark dialog box -->
+ <string name="http">http://</string>
+ <!-- Menu item -->
+ <string name="save_to_bookmarks">Bookmark link</string>
+ <!-- Menu item -->
+ <string name="edit_bookmark">Edit bookmark</string>
+ <string name="open_bookmark">Open</string>
+ <!-- Menu item -->
+ <string name="remove_bookmark">Delete bookmark</string>
+ <!-- Menu item -->
+ <string name="remove_history_item">Remove from history</string>
+ <!-- Toast -->
+ <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>
+ <!-- Confirmation dialog title -->
+ <string name="delete_bookmark">Delete</string>
+ <!-- Menu item -->
+ <string name="bookmark_page">Bookmark last-viewed page</string>
+ <!-- Summary text under the New Bookmark item on the Bookmarks screen -->
+ <string name="current_page">Current page:\u0020</string>
+ <!-- Confirmation dialog message -->
+ <string name="delete_bookmark_warning">Bookmark \"<xliff:g id="bookmark">%s</xliff:g>\" will be deleted.</string>
+ <string name="info_base">Title:\n <xliff:g id="title">%s1</xliff:g> \n\nURL: \n <xliff:g id="url">%s2</xliff:g></string>
+ <!-- Menu item -->
+ <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>
+ <string name="delete">Delete</string>
+ <!-- Menu item -->
+ <string name="goto_dot">Go to URL</string>
+ <string name="find_dot">Find on page</string>
+ <!-- Menu item -->
+ <string name="homepage">Home page</string>
+ <string name="view_tabs">Window overview</string>
+ <!-- Menu item -->
+ <string name="view_tabs_condensed">Window</string>
+ <!-- Title of current windows screen; appears in title bar -->
+ <string name="tab_picker_title">Current windows</string>
+ <!-- Menu item -->
+ <string name="tab_picker_view_tab">View</string>
+ <!-- Menu item -->
+ <string name="tab_picker_new_tab">New window</string>
+ <!-- Menu item -->
+ <string name="tab_picker_remove_tab">Close</string>
+ <!-- Menu item -->
+ <string name="tab_picker_bookmark">Bookmark</string>
+ <!-- Menu item -->
+ <string name="tab_picker_send_url">Share link</string>
+ <!-- Menu item -->
+ <string name="bookmarks">Bookmarks</string>
+ <string name="shortcut_bookmark">Bookmark</string>
+ <!-- Menu item -->
+ <string name="history">History</string>
+ <string name="visual_history">Window history</string>
+ <string name="menu_use_as_homepage">Set as home page</string>
+ <!-- Menu item -->
+ <string name="menu_view_download">Downloads</string>
+ <!-- Menu item -->
+ <string name="menu_bookmark_page">Bookmark page</string>
+ <!-- Menu item -->
+ <string name="share_page">Share page</string>
+ <string name="homepage_set">Home page updated</string>
+ <!-- Menu item -->
+ <string name="menu_zoom">Zoom</string>
+ <!-- Menu item -->
+ <string name="menu_flip_orientation">Flip orientation</string>
+ <!-- Menu item -->
+ <string name="contextmenu_openlink">Open</string>
+ <!-- Menu item -->
+ <string name="contextmenu_openlink_newwindow">Open in new window</string>
+ <!-- Menu item -->
+ <string name="contextmenu_bookmark_thislink">Bookmark link</string>
+ <!-- Menu item -->
+ <string name="contextmenu_savelink">Save link</string>
+ <!-- Menu item -->
+ <string name="contextmenu_sharelink">Share link</string>
+ <!-- Menu item -->
+ <string name="contextmenu_copy">Copy</string>
+ <!-- Menu item -->
+ <string name="contextmenu_copylink">Copy link URL</string>
+ <!-- Menu item -->
+ <string name="contextmenu_download_image">Save image</string>
+ <!-- Menu item -->
+ <string name="contextmenu_view_image">View image</string>
+ <!-- Menu item -->
+ <string name="contextmenu_dial_dot">Dial\u2026</string>
+ <!-- Menu item -->
+ <string name="contextmenu_add_contact">Add contact</string>
+ <!-- Menu item -->
+ <string name="contextmenu_send_mail">Send email</string>
+ <!-- Menu item -->
+ <string name="contextmenu_map">Map</string>
+ <!-- Menu item -->
+ <string name="contextmenu_javascript">JavaScript</string>
+
+ <string name="clear">Clear</string>
+ <string name="cache_cleared">Cache cleared</string>
+
+ <string name="override_message">The existing bookmark will be replaced.</string>
+ <string name="override">Replace</string>
+ <string name="replace">Replace</string>
+ <string name="with">with</string>
+ <string name="question_mark">\?</string>
+
+ <string name="browser_bookmarks_page_bookmarks_text">Bookmarks</string>
+
+ <string name="history_picker_view_web_page">Open</string>
+
+ <!-- Settings screen strings -->
+ <!-- Menu item -->
+ <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>
+ <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">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>
+ <!-- Dialog box title -->
+ <string name="pref_text_size_dialogtitle">Text size</string>
+ <string name="pref_text_size_summary">Select the size of text on Web pages</string>
+ <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_extras_gears_settings_summary">Applications that extend browser functionality</string>
+ <string name="pref_plugin_installed_summaryxx">View currently-installed plugins</string>
+ <string name="pref_plugin_installed_empty_listxx">No installed plugins.</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>
+ <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_track">Enable trackball</string>
+ <string name="pref_development_nav_dump">Enable nav cache dump</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>
+ <!-- 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>
+ <string name="browserFrameNetworkErrorLabel">Data connectivity problem</string>
+ <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>
+ <!-- Menu item -->
+ <string name="add_new_bookmark">New bookmark</string>
+ <string name="no_database">No database!</string>
+
+ <string name="search_label">Browser</string>
+ <string name="search_hint">Type Web address</string>
+ <!-- Menu item -->
+ <string name="search">Search</string>
+ <string name="search_button_text">Go</string>
+
+ <string name="js_dialog_title_prefix">The page at</string>
+ <string name="js_dialog_title_suffix">says:</string>
+ <string name="js_dialog_title_default">JavaScript</string>
+ <string name="js_dialog_before_unload">Navigate away from this page?\n\n<xliff:g id="message">%s</xliff:g>\n\nSelect OK to continue, or Cancel to stay on the current page.</string>
+
+ <!-- Pop-up window dialog -->
+ <string name="attention">Attention</string>
+ <string name="popup_window_attempt">This site is attempting to open a
+ pop-up window.</string>
+ <string name="allow">Allow</string>
+ <string name="block">Block</string>
+
+ <string name="too_many_windows_dialog_title">Window limit reached</string>
+ <string name="too_many_windows_dialog_message">Could not open a new window because you have already opened the maximum number.</string>
+ <string name="too_many_subwindows_dialog_title">Pop-up already open</string>
+ <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>
+ <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>
+ <string name="download_no_application">No application can be found to open this file.</string>
+ <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>
+ <string name="download_pending">Starting download\u2026</string>
+ <string name="download_pending_network">Waiting for data connection\u2026</string>
+ <string name="download_running_paused">Waiting for data connection\u2026</string>
+ <string name="download_canceled">Download canceled.</string>
+ <string name="download_not_acceptable">Cannot download. The content being downloaded is not supported on the phone.</string>
+ <string name="download_file_error">Cannot finish download. There is not enough space.</string>
+ <string name="download_length_required">Cannot download. The size of the item cannot be determined.</string>
+ <string name="download_precondition_failed">Download interrupted. It cannot be resumed.</string>
+
+ <!-- Oma Download Manager. Note: These oma_* messages are no longer used and so not revealed to the end-user-->
+ <string name="oma_label_want_to_download">Do you want to download this content?</string>
+ <string name="oma_label_download_error">Download error:</string>
+ <string name="oma_label_proceed">Proceed</string>
+ <string name="oma_label_action">Download</string>
+ <string name="oma_label_exit">Exit</string>
+ <string name="oma_label_cancel">Exit</string>
+
+ <string name="oma_label_name">Name:</string>
+ <string name="oma_label_vendor">Vendor:</string>
+ <string name="oma_label_size">Size:</string>
+ <string name="oma_label_type">Type:</string>
+ <string name="oma_label_description">Description:</string>
+
+ <string name="oma_error_insufficient_memory">Insufficient memory! Check your SD card.</string>
+ <string name="oma_error_invalid_descriptor">Invalid download descriptor.</string>
+ <string name="oma_error_non_acceptable_content">Content not supported.</string>
+ <string name="oma_error_failed_to_download">Unable to download media.</string>
+ <string name="oma_warning_one_or_more_types_not_supported">One or more content categories are not supported.</string>
+ <string name="oma_status_success">Success</string>
+ <string name="oma_status_insufficient_memory">Insufficient memory</string>
+ <string name="oma_status_loss_of_service">Loss of service</string>
+ <string name="oma_status_attribute_mismatch">Attribute mismatch</string>
+ <string name="oma_status_invalid_descriptor">Invalid descriptor</string>
+ <string name="oma_status_invalid_version">Invalid version</string>
+ <string name="oma_status_device_aborted">Phone aborted</string>
+ <string name="oma_status_non_acceptable_content">Unacceptable content</string>
+ <string name="oma_status_loader_error">Loader error</string>
+
+
+ <string name="activity_instrumentation_test_runner">Browser Test Runner</string>
+ <string name="activity_instrumentation_functional_test_runner">Browser Functional Test Runner</string>
+ <string name="dump_nav">Dump navigation cache</string>
+
+ <!-- Bookmarks -->
+ <string-array name="bookmarks">
+ <item>Google</item>
+ <item>http://www.google.com/</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>
+</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
new file mode 100644
index 00000000..3a5e4031
--- /dev/null
+++ b/res/values/styles.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/styles.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.
+*/
+-->
+<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/editbox_dropdown_dark_frame</item>
+ </style>
+
+</resources>
+
diff --git a/res/xml/browser_preferences.xml b/res/xml/browser_preferences.xml
new file mode 100644
index 00000000..08fd8357
--- /dev/null
+++ b/res/xml/browser_preferences.xml
@@ -0,0 +1,164 @@
+<?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" />
+
+ <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" />
+
+ <EditTextPreference
+ 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">
+
+ <CheckBoxPreference
+ android:key="enable_gears"
+ 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_gears"
+ 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..22b59978
--- /dev/null
+++ b/res/xml/debug_preferences.xml
@@ -0,0 +1,62 @@
+<?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" />
+
+ <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..2c159df9
--- /dev/null
+++ b/res/xml/searchable.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+
+<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: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..3fe38e82
--- /dev/null
+++ b/src/com/android/browser/AddBookmarkPage.java
@@ -0,0 +1,195 @@
+/*
+ * 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" };
+ private static final String WHERE_CLAUSE = "url = ? AND bookmark = 0";
+ 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 = mAddress.getText().toString();
+ boolean emptyTitle = title.length() == 0;
+ boolean emptyUrl = unfilteredUrl.trim().length() == 0;
+ Resources r = getResources();
+ if (emptyTitle) {
+ if (emptyUrl) {
+ setTitle(r.getText(R.string.empty_bookmark));
+ return false;
+ }
+ setTitle(r.getText(R.string.bookmark_needs_title));
+ return false;
+ }
+ if (emptyUrl) {
+ setTitle(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) {
+ setTitle(r.getText(R.string.bookmark_url_not_valid));
+ return false;
+ }
+ if (address.mHost.length() == 0) {
+ setTitle(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);
+ if (c.moveToFirst()) {
+ // This means we have been to this site, so convert the
+ // history item to a bookmark.
+ ContentValues map = new ContentValues();
+ 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 {
+ // Adding a bookmark for a site the user has not been to.
+ ContentValues map = new ContentValues();
+ 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);
+ map.put(Browser.BookmarkColumns.VISITS, 0);
+ 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..748c9c2f
--- /dev/null
+++ b/src/com/android/browser/AddNewBookmark.java
@@ -0,0 +1,72 @@
+/*
+ * 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) {
+ if (url.length() > BrowserSettings.MAX_TEXTVIEW_LEN) {
+ mUrlText.setText(url.substring(0, BrowserSettings.MAX_TEXTVIEW_LEN));
+ } else {
+ 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..0015eaf2
--- /dev/null
+++ b/src/com/android/browser/BookmarkItem.java
@@ -0,0 +1,105 @@
+/*
+ * 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.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+/**
+ * Custom layout for an item representing a bookmark in the browser.
+ */
+class BookmarkItem extends RelativeLayout {
+
+ private TextView mTextView;
+ private TextView mUrlText;
+ private ImageView mImageView;
+ private LayoutInflater mFactory;
+
+ /**
+ * Instantiate a bookmark item, including a default favicon.
+ *
+ * @param context The application context for the item.
+ */
+ BookmarkItem(Context context) {
+ super(context);
+
+ mFactory = LayoutInflater.from(context);
+ mFactory.inflate(R.layout.bookmark_item, 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(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 */ CharSequence getName() {
+ return mTextView.getText();
+ }
+
+ /**
+ * Return the TextView which holds the name of this bookmark item.
+ */
+ /* package */ TextView getNameTextView() {
+ return mTextView;
+ }
+
+ /**
+ * 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);
+ }
+}
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..fc4acfc9
--- /dev/null
+++ b/src/com/android/browser/BrowserActivity.java
@@ -0,0 +1,4532 @@
+/*
+ * 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.ActivityManager;
+import android.app.AlertDialog;
+import android.app.SearchManager;
+import android.app.ProgressDialog;
+import android.content.ActivityNotFoundException;
+import android.content.res.AssetManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+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.NetworkInfo;
+import android.net.Uri;
+import android.net.WebAddress;
+import android.net.http.EventHandler;
+import android.net.http.RequestQueue;
+import android.net.http.SslCertificate;
+import android.net.http.SslError;
+import android.os.Build;
+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.pim.DateFormat;
+import android.provider.Browser;
+import android.provider.Checkin;
+import android.provider.Contacts.Intents.Insert;
+import android.provider.Contacts;
+import android.provider.Downloads;
+import android.text.IClipboard;
+import android.text.util.Regex;
+import android.util.Config;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.MenuItem.OnMenuItemClickListener;
+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.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.AnimationUtils;
+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.JsPromptResult;
+import android.webkit.JsResult;
+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 com.google.android.googleapps.IGoogleLoginService;
+import com.google.android.googlelogin.GoogleLoginServiceConstants;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.net.MalformedURLException;
+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.HashSet;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.Vector;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
+
+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() {
+ // 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;
+ }
+ } 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;
+ new Thread(getAccount).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() {
+ 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 (mWebView != null)
+ mWebView.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();
+ new Thread(copyPluginsFromAssets).start();
+ }
+ }
+ }
+
+
+ @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();
+
+ //
+ // 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 = new FrameLayout(this);
+
+ setContentView(mContentView);
+
+ // 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)) {
+ final Intent intent = getIntent();
+ final Bundle extra = intent.getExtras();
+ // Create an initial tab.
+ final TabControl.Tab t = mTabControl.createNewTab();
+ mTabControl.setCurrentTab(t);
+ // This is one of the only places we call attachTabToContentView
+ // without animating from the tab picker.
+ attachTabToContentView(t);
+ mWebView = t.getWebView();
+ if (extra != null) {
+ int scale = extra.getInt(Browser.INITIAL_ZOOM_LEVEL, 0);
+ if (scale > 0 && scale <= 1000) {
+ mWebView.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()) {
+ mWebView.loadUrl(mSettings.getHomePage());
+ } else {
+ waitForCredentials();
+ }
+ } else {
+ mWebView.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());
+ mWebView = mTabControl.getCurrentWebView();
+ }
+
+ /* enables registration for changes in network status from
+ http stack */
+ mNetworkStateChangedFilter = new IntentFilter();
+ mNetworkStateChangedFilter.addAction(
+ RequestQueue.HTTP_NETWORK_STATE_CHANGED_INTENT);
+ mNetworkStateIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(
+ RequestQueue.HTTP_NETWORK_STATE_CHANGED_INTENT)) {
+ Boolean up = (Boolean)intent.getExtra(
+ RequestQueue.HTTP_NETWORK_STATE_UP);
+ onNetworkToggle(up);
+ }
+ }
+ };
+ setRequestedOrientation(mSettings.getOrientation());
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ if (mWebView == null) {
+ return;
+ }
+ 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)
+ || 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 and the
+ // url will be opened in the current tab
+ openTabAndShow(url, null);
+ } else {
+ if ("about:debug".equals(url)) {
+ mSettings.toggleDebugSettings();
+ return;
+ }
+ final TabControl.Tab currentTab = mTabControl.getCurrentTab();
+ // 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(currentTab, false, url,
+ TAB_OVERVIEW_DELAY, null);
+ } else {
+ // Get rid of the subwindow if it exists
+ dismissSubWindow(currentTab);
+ mWebView.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)
+ || Intent.ACTION_WEB_SEARCH.equals(action)) {
+ url = intent.getStringExtra(SearchManager.QUERY);
+ 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);
+ }
+ }
+ return url;
+ }
+
+ private 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 = mWebView;
+
+ 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 (!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);
+ }
+ }
+
+ @Override public void onLowMemory() {
+ super.onLowMemory();
+ mTabControl.freeMemory();
+ }
+
+ private boolean resumeWebView() {
+ if ((!mActivityInPause && !mPageStarted) ||
+ (mActivityInPause && mPageStarted)) {
+ CookieSyncManager.getInstance().startSync();
+ mWebView.resumeTimers();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private boolean pauseWebView() {
+ if (mActivityInPause && !mPageStarted) {
+ CookieSyncManager.getInstance().stopSync();
+ mWebView.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
+ mWebView.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();
+ 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:
+ Message msg = mHandler.obtainMessage(
+ FOCUS_NODE_HREF, id, 0);
+ WebView webview = getTopWindow();
+ msg.obj = webview;
+ webview.requestFocusNodeHref(msg);
+ break;
+
+ case R.id.download_context_menu_id:
+ case R.id.view_image_context_menu_id:
+ Message m = mHandler.obtainMessage(
+ FOCUS_NODE_HREF, id, 0);
+ WebView w = getTopWindow();
+ m.obj = w;
+ w.requestImageRef(m);
+ break;
+ default:
+ // For other context menus
+ return onOptionsItemSelected(item);
+ }
+ mCanChord = false;
+ return true;
+ }
+
+ /**
+ * 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, null, true);
+ return true;
+ }
+
+ @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();
+ // TODO: Activities are requested to call onSearchRequested, and to override
+ // that function in order to insert custom fields (e.g. the search query).
+ startSearch(mSettings.getHomePage().equals(url) ? null : url, true, null, false);
+ }
+ break;
+
+ case R.id.search_menu_id:
+ // launch using "global" search, which will bring up the Google search box
+ onSearchRequested();
+ break;
+
+ case R.id.bookmarks_menu_id:
+ bookmarksPicker();
+ break;
+
+ case R.id.windows_menu_id:
+ 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--;
+ }
+ }
+ removeTabAndShow(currentIndex, indexToShow);
+ break;
+
+ case R.id.homepage_menu_id:
+ dismissSubWindow(mTabControl.getCurrentTab());
+ mWebView.loadUrl(mSettings.getHomePage());
+ break;
+
+ case R.id.preferences_menu_id:
+ Intent intent = new Intent(this,
+ BrowserPreferencesPage.class);
+ startActivityForResult(intent, PREFERENCES_PAGE);
+ break;
+
+/*
+ Disable Find for version 1.0
+ case R.id.find_menu_id:
+ if (null == mFindDialog) {
+ mFindDialog = new FindDialog(this);
+ FrameLayout.LayoutParams lp =
+ new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ Gravity.BOTTOM);
+ mFindDialog.setLayoutParams(lp);
+ }
+ mFindDialog.setWebView(getTopWindow());
+ mContentView.addView(mFindDialog);
+ mFindDialog.show();
+ Animation anim =AnimationUtils.loadAnimation(this,
+ R.anim.find_dialog_enter);
+ mFindDialog.startAnimation(anim);
+ mMenuState = EMPTY_MENU;
+ break;
+*/
+
+ case R.id.page_info_menu_id:
+ showPageInfo(mWebView, false);
+ break;
+
+ case R.id.classic_history_menu_id: {
+ Intent i = new Intent(this, BrowserHistoryPage.class);
+ i.putExtra("maxTabsOpen",
+ mTabControl.getTabCount() >=
+ TabControl.MAX_TABS);
+ startActivityForResult(i, CLASSIC_HISTORY_PAGE);
+ }
+ break;
+
+ case R.id.bookmark_page_menu_id:
+ Browser.saveBookmark(this, getTopWindow().getTitle(),
+ getTopWindow().getUrl());
+ 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_menu_id:
+ // FIXME: Can we move this out of WebView? How does this work
+ // for a subwindow?
+ getTopWindow().invokeZoomPicker();
+ 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;
+
+ case R.id.flip_orientation_menu_id:
+ if (mSettings.getOrientation() !=
+ ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
+ mSettings.setOrientation(this,
+ ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ } else {
+ mSettings.setOrientation(this,
+ ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
+ }
+ setRequestedOrientation(mSettings.getOrientation());
+ 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: {
+ Intent i = new Intent(this, BrowserHistoryPage.class);
+ i.putExtra("maxTabsOpen",
+ mTabControl.getTabCount() >=
+ TabControl.MAX_TABS);
+ startActivityForResult(i, CLASSIC_HISTORY_PAGE);
+ }
+ break;
+
+ case R.id.bookmarks_tab_menu_id:
+ bookmarksPicker();
+ break;
+
+ case R.id.properties_tab_menu_id:
+ if (mTabListener != null && mTabOverview != null) {
+ int pos = mTabOverview.getContextMenuPosition(item);
+ TabControl.Tab t = mTabControl.getTab(pos);
+ // Use the tab's data for the page info dialog.
+ if (t.getWebView() != null) {
+ showPageInfo(t.getWebView(), false);
+ }
+ // FIXME: what should we display if the WebView is null?
+ }
+ break;
+
+ default:
+ if (!super.onOptionsItemSelected(item)) {
+ return false;
+ }
+ // Otherwise fall through.
+ }
+ mCanChord = false;
+ return true;
+ }
+
+ public void closeFind() {
+ Animation anim = AnimationUtils.loadAnimation(this,
+ R.anim.find_dialog_exit);
+ mFindDialog.startAnimation(anim);
+ mContentView.removeView(mFindDialog);
+ getTopWindow().requestFocus();
+ mMenuState = R.id.MAIN_MENU;
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ if (super.dispatchTouchEvent(event)) {
+ return true;
+ } else {
+ // We do not use the Dialog class because it places dialogs in the
+ // middle of the screen. It would take care of dismissing find if
+ // were using it, but we are doing it manually since we are not.
+ if (mFindDialog != null && mFindDialog.hasFocus()) {
+ mFindDialog.dismiss();
+ }
+ return false;
+ }
+ }
+
+ @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.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.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.setGroupVisible(R.id.TAB_MENU, false);
+ menu.setGroupEnabled(R.id.TAB_MENU, false);
+ }
+ final WebView w = getTopWindow();
+ boolean canGoBack = w.canGoBack();
+ final MenuItem back = menu.findItem(R.id.back_menu_id);
+ back.setVisible(canGoBack);
+ back.setEnabled(canGoBack);
+ final MenuItem close = menu.findItem(R.id.close_menu_id);
+ close.setVisible(!canGoBack);
+ close.setEnabled(!canGoBack);
+ final MenuItem flip =
+ menu.findItem(R.id.flip_orientation_menu_id);
+ boolean keyboardClosed =
+ getResources().getConfiguration().keyboardHidden ==
+ Configuration.KEYBOARDHIDDEN_YES;
+ flip.setEnabled(keyboardClosed);
+
+ boolean isHome = mSettings.getHomePage().equals(w.getUrl());
+ final MenuItem home = menu.findItem(R.id.homepage_menu_id);
+ home.setVisible(!isHome);
+ home.setEnabled(!isHome);
+
+ menu.findItem(R.id.forward_menu_id)
+ .setEnabled(w.canGoForward());
+
+ menu.findItem(R.id.zoom_in_menu_id).setVisible(false);
+ menu.findItem(R.id.zoom_out_menu_id).setVisible(false);
+
+ // decide whether to show the share link option
+ PackageManager pm = getPackageManager();
+ Intent send = new Intent(Intent.ACTION_SEND);
+ send.setType("text/plain");
+ List<ResolveInfo> list = pm.queryIntentActivities(send,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ menu.findItem(R.id.share_page_menu_id).setVisible(
+ list.size() > 0);
+
+ 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.IMAGE_ANCHOR_TYPE
+ || type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
+ menu.setGroupVisible(R.id.ANCHOR_MENU,
+ type == WebView.HitTestResult.ANCHOR_TYPE ||
+ type == WebView.HitTestResult.IMAGE_ANCHOR_TYPE
+ || 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(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,
+ Contacts.People.CONTENT_URI);
+ addIntent.putExtra(Insert.FULL_MODE, true);
+ addIntent.putExtra(Insert.PHONE, extra);
+ 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.ANCHOR_TYPE:
+ case WebView.HitTestResult.IMAGE_ANCHOR_TYPE:
+ case WebView.HitTestResult.SRC_ANCHOR_TYPE:
+ case WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE:
+ mTitleView = (TextView) LayoutInflater.from(this)
+ .inflate(android.R.layout.browser_link_context_header,
+ null);
+ menu.setHeaderView(mTitleView);
+ // 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);
+ if (type == WebView.HitTestResult.ANCHOR_TYPE
+ || type == WebView.HitTestResult.IMAGE_ANCHOR_TYPE){
+ menu.findItem(R.id.bookmark_context_menu_id).setVisible(
+ false);
+ menu.findItem(R.id.save_link_context_menu_id).setVisible(
+ false);
+ menu.findItem(R.id.copy_link_context_menu_id).setVisible(
+ false);
+ menu.findItem(R.id.share_link_context_menu_id).setVisible(
+ false);
+ mTitleView.setText(R.string.contextmenu_javascript);
+ break;
+ }
+ Message headerMessage = mHandler.obtainMessage(FOCUS_NODE_HREF,
+ HEADER_FLAG, 0);
+ headerMessage.obj = webview;
+ webview.requestFocusNodeHref(headerMessage);
+ // decide whether to show the share link option
+ PackageManager pm = getPackageManager();
+ Intent send = new Intent(Intent.ACTION_SEND);
+ send.setType("text/plain");
+ List<ResolveInfo> list = pm.queryIntentActivities(send,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ menu.findItem(R.id.share_link_context_menu_id).setVisible(
+ list.size() > 0);
+ if (type == WebView.HitTestResult.ANCHOR_TYPE) {
+ break;
+ }
+ //fall through
+
+ case WebView.HitTestResult.IMAGE_TYPE:
+ 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);
+ // Reset the current WebView.
+ mWebView = tab.getWebView();
+ // Set the view to invisibile for now.
+ mWebView.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);
+ map.put("url", 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.
+ if (mTabOverview != null) {
+ mTabOverview.setListener(null);
+ }
+ mTabListener = null;
+
+ }
+
+ // 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) {
+ 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);
+ mWebView.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(),
+ 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.
+ mWebView.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) {
+ if (mTabOverview == null) {
+ return;
+ }
+
+ // 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) {
+ if (mTabOverview != null) {
+ 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 String url, final Message msg) {
+ // mTabOverview may have been dismissed
+ if (mTabOverview == null) {
+ return;
+ }
+
+ // 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);
+
+ // Use a delay of 1 second in case we get a bad position
+ long delay = 1000;
+ boolean fade = false;
+
+ // Wait until the animation completes to load the url.
+ final Animation.AnimationListener l =
+ new Animation.AnimationListener() {
+ public void onAnimationStart(Animation a) {}
+ public void onAnimationRepeat(Animation a) {}
+ public void onAnimationEnd(Animation a) {
+ // The animation is done so allow key events and other
+ // animations to begin.
+ mAnimationCount--;
+ mHandler.post(new Runnable() {
+ public void run() {
+ if (v != null) {
+ mContentView.removeView(view);
+ mWebView.setVisibility(View.VISIBLE);
+ // Make the sub window container visible if
+ // there is one.
+ if (mTabControl.getCurrentSubWindow() != null) {
+ mTabControl.getCurrentTab()
+ .getSubWebViewContainer()
+ .setVisibility(View.VISIBLE);
+ }
+ }
+ if (url != null) {
+ // Dismiss the subwindow if one exists.
+ dismissSubWindow(
+ mTabControl.getCurrentTab());
+ mWebView.loadUrl(url);
+ }
+ mMenuState = R.id.MAIN_MENU;
+ // Resume regular updates.
+ mWebView.resumeTimers();
+ // Dispatch the message after the animation
+ // completes.
+ if (msg != null) {
+ msg.sendToTarget();
+ }
+ }
+ });
+ }
+ };
+
+ 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);
+ // Dismiss the tab overview after the animation completes.
+ delay = anim.getDuration();
+ } else {
+ // dismiss mTabOverview and have it fade out just in case we get a
+ // bad location.
+ fade = true;
+ // Go ahead and load the url.
+ l.onAnimationEnd(null);
+ }
+ // Reset all the title bar info.
+ resetTitle();
+ // Dismiss the tab overview either after the animation or after a
+ // second.
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ DISMISS_TAB_OVERVIEW, fade ? 1 : 0, 0), delay);
+ }
+
+ private void openTab(String url) {
+ if (mSettings.openInBackground()) {
+ TabControl.Tab t = mTabControl.createNewTab();
+ if (t != null) {
+ WebView w = t.getWebView();
+ w.loadUrl(url);
+ }
+ } else {
+ openTabAndShow(url, null);
+ }
+ }
+
+ private class Copy implements OnMenuItemClickListener {
+ private CharSequence mText;
+
+ public boolean onMenuItemClick(MenuItem item) {
+ copy(mText);
+ return true;
+ }
+
+ public Copy(CharSequence toCopy) {
+ mText = toCopy;
+ }
+ }
+
+ 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() {
+ resetTitleAndIcon(mWebView);
+ int progress = mWebView.getProgress();
+ mInLoad = (progress != 100);
+ updateInLoadMenuItems();
+ mWebChromeClient.onProgressChanged(mWebView, 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;
+
+ 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) {
+ 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 removeTabAndShow(int indexToRemove, int indexToShow) {
+ 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, indexToRemove, true);
+ // Change to the parent tab
+ final TabControl.Tab tab = mTabControl.getTab(indexToShow);
+ if (tab != null) {
+ sendAnimateFromOverview(tab, false, null, delay, null);
+ } else {
+ // Send a message to open a new tab.
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(OPEN_TAB_AND_SHOW,
+ mSettings.getHomePage()), delay);
+ }
+ }
+
+ private void goBackOnePageOrQuit() {
+ if (mWebView.canGoBack()) {
+ mWebView.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 = mTabControl.getCurrentTab().getParentTab();
+ if (parent != null) {
+ removeTabAndShow(mTabControl.getCurrentIndex(),
+ mTabControl.getTabIndex(parent));
+ } else {
+ /*
+ * 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.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 (mMenuState == R.id.MAIN_MENU){
+ 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 JS_CONFIRM = 101;
+ private static final int FOCUS_NODE_HREF = 102;
+ private static final int DISMISS_TAB_OVERVIEW = 103;
+ private static final int CANCEL_CREDS_REQUEST = 104;
+ private static final int ANIMATE_FROM_OVERVIEW = 105;
+ private static final int ANIMATE_TO_OVERVIEW = 106;
+ private static final int OPEN_TAB_AND_SHOW = 107;
+ private static final int CHECK_MEMORY = 108;
+ private static final int RELEASE_WAKELOCK = 109;
+
+ // Private handler for handling javascript and saving passwords
+ private Handler mHandler = new Handler() {
+
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case JS_CONFIRM:
+ JsResult res = (JsResult) msg.obj;
+ if (msg.arg1 == 0) {
+ res.cancel();
+ } else {
+ res.confirm();
+ }
+ break;
+
+ case DISMISS_TAB_OVERVIEW:
+ if (mTabOverview != null) {
+ if (msg.arg1 == 1) {
+ 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
+ mWebView.setVisibility(View.VISIBLE);
+ // Make the sub window container visible.
+ if (mTabControl.getCurrentSubWindow() != null) {
+ mTabControl.getCurrentTab().getSubWebViewContainer()
+ .setVisibility(View.VISIBLE);
+ }
+ mContentView.removeView(mTabOverview);
+ mTabOverview.clear();
+ // XXX: There are checks for mTabOverview throughout
+ // this file because this message can be received
+ // before it is expected. This is because we are not
+ // enforcing the order of animations properly. In order
+ // to get this right, we would need to rewrite a lot of
+ // the code to dispatch this messages after all
+ // animations have completed.
+ mTabOverview = null;
+ mTabListener = null;
+ }
+ break;
+
+ case ANIMATE_FROM_OVERVIEW:
+ final HashMap map = (HashMap) msg.obj;
+ animateFromTabOverview((AnimatingView) map.get("view"),
+ msg.arg1 == 1, (String) map.get("url"),
+ (Message) map.get("msg"));
+ break;
+
+ case ANIMATE_TO_OVERVIEW:
+ animateToTabOverview(msg.arg1, msg.arg2 == 1,
+ (AnimatingView) msg.obj);
+ break;
+
+ case OPEN_TAB_AND_SHOW:
+ openTabAndShow((String) msg.obj, null);
+ break;
+
+ case FOCUS_NODE_HREF:
+ String url = (String) msg.getData().get("url");
+ if (url == null || url.length() == 0) {
+ break;
+ }
+ WebView view = (WebView) msg.obj;
+ // Only apply the action if the top window did not change.
+ if (getTopWindow() != view) {
+ break;
+ }
+ switch (msg.arg1) {
+ case HEADER_FLAG:
+ mTitleView.setText(url);
+ break;
+ case R.id.open_context_menu_id:
+ case R.id.view_image_context_menu_id:
+ loadURL(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((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;
+ }
+ }
+ };
+
+ private static final int HEADER_FLAG = Integer.MIN_VALUE;
+ private TextView mTitleView = null;
+
+ // -------------------------------------------------------------------------
+ // 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 final WebViewClient mWebViewClient = new WebViewClient() {
+ @Override
+ public void onPageStarted(WebView view, String url, Bitmap favicon) {
+ resetLockIcon(url);
+ setUrlTitle(url, null);
+ // Call onReceivedIcon instead of setFavicon so the bookmark
+ // database can be updated.
+ mWebChromeClient.onReceivedIcon(view, 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();
+
+ // 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);
+ // Make the progress full.
+ getWindow().setFeatureInt(Window.FEATURE_PROGRESS, 10000);
+
+ // 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.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(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) {
+ String[] credentials =
+ mWebView.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 (event.isDown()) {
+ mKeyTracker.doKeyDown(event.getKeyCode(), event);
+ } else {
+ mKeyTracker.doKeyUp(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);
+ parent.addChildTab(mTabControl.getCurrentTab());
+ WebView.WebViewTransport transport =
+ (WebView.WebViewTransport) msg.obj;
+ transport.setWebView(mWebView);
+ }
+ }
+
+ @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(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(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(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.
+ removeTabAndShow(currentIndex, mTabControl.getTabIndex(parent));
+ }
+ }
+
+ @Override
+ public void onProgressChanged(WebView view, int newProgress) {
+ 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.getUrl();
+
+ // 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()) {
+ Log.d(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) {
+ if (icon != null) {
+ BrowserBookmarksAdapter.updateBookmarkFavicon(mResolver,
+ view.getUrl(), icon);
+ }
+ setFavicon(icon);
+ }
+
+ //----------------------------------------------------------------------
+ // JavaScript functions.
+ //----------------------------------------------------------------------
+
+ // Show an alert to the user.
+ @Override
+ public boolean onJsAlert(WebView view, String url, String message,
+ JsResult result) {
+ String title = url;
+ if (URLUtil.isDataUrl(url)) {
+ // For data: urls, we just display 'JavaScript' similar to
+ // Safari.
+ title = getString(R.string.js_dialog_title_default);
+ } else {
+ try {
+ URL aUrl = new URL(url);
+ // Example: "The page at 'http://www.mit.edu' says:"
+ title = getText(R.string.js_dialog_title_prefix)
+ + " '"
+ + (aUrl.getProtocol() + "://" + aUrl.getHost())
+ + "' "
+ + getText(R.string.js_dialog_title_suffix);
+ } catch (MalformedURLException ex) {
+ // do nothing. just use the url passed as the title
+ }
+ }
+ final JsResult res = result;
+ new AlertDialog.Builder(BrowserActivity.this)
+ .setTitle(title)
+ .setMessage(message)
+ .setPositiveButton(R.string.ok,
+ new AlertDialog.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int which) {
+ res.confirm();
+ }
+ })
+ .setCancelable(false)
+ .show();
+ return true;
+ }
+
+ @Override
+ public boolean onJsConfirm(WebView view, String url, String message,
+ final JsResult result) {
+ String title = url;
+ try {
+ URL aUrl = new URL(url);
+ // Example: "The page at 'http://www.mit.edu' says:"
+ title = getText(R.string.js_dialog_title_prefix)
+ + " '"
+ + (aUrl.getProtocol() + "://" + aUrl.getHost())
+ + "' "
+ + getText(R.string.js_dialog_title_suffix);
+ } catch (MalformedURLException ex) {
+ // do nothing. just use the url passed as the title
+ }
+ new AlertDialog.Builder(BrowserActivity.this)
+ .setTitle(title)
+ .setMessage(message)
+ .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ mHandler.obtainMessage(JS_CONFIRM, 1, 0, result).sendToTarget();
+ }})
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ mHandler.obtainMessage(JS_CONFIRM, 0, 0, result).sendToTarget();
+ }})
+ .show();
+ // Return true so WebView knows we will handle the confirm.
+ return true;
+ }
+
+ @Override
+ public boolean onJsPrompt(WebView view, String url, String message, String defaultValue,
+ final JsPromptResult result) {
+ String title = url;
+ try {
+ URL aUrl = new URL(url);
+ // Example: "The page at 'http://www.mit.edu' says:"
+ title = getText(R.string.js_dialog_title_prefix)
+ + " '"
+ + (aUrl.getProtocol() + "://" + aUrl.getHost())
+ + "' "
+ + getText(R.string.js_dialog_title_suffix);
+ } catch (MalformedURLException ex) {
+ // do nothing, just use the url passed as the title
+ }
+
+ final LayoutInflater factory = LayoutInflater.from(BrowserActivity.this);
+ final View v = factory.inflate(R.layout.js_prompt, null);
+ ((TextView)v.findViewById(R.id.message)).setText(message);
+ ((EditText)v.findViewById(R.id.value)).setText(defaultValue);
+
+ new AlertDialog.Builder(BrowserActivity.this)
+ .setTitle(title)
+ .setView(v)
+ .setPositiveButton(R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ String value = ((EditText)v.findViewById(R.id.value)).getText()
+ .toString();
+ result.confirm(value);
+ }
+ })
+ .setNegativeButton(R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ result.cancel();
+ }
+ })
+ .setOnCancelListener(
+ new DialogInterface.OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ result.cancel();
+ }
+ })
+ .show();
+
+ // Return true so WebView knows we will handle the prompt.
+ return true;
+ }
+
+ @Override
+ public boolean onJsBeforeUnload(WebView view, String url,
+ String message, final JsResult result) {
+ final String m =
+ getString(R.string.js_dialog_before_unload, message);
+ new AlertDialog.Builder(BrowserActivity.this)
+ .setMessage(m)
+ .setPositiveButton(R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int which) {
+ // Use JS_CONFIRM since it has the same
+ // behavior we want here.
+ mHandler.obtainMessage(JS_CONFIRM, 1, 0,
+ result).sendToTarget();
+ }})
+ .setNegativeButton(R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int which) {
+ // Use JS_CONFIRM since it has the same
+ // behavior we want here.
+ mHandler.obtainMessage(JS_CONFIRM, 0, 0,
+ result).sendToTarget();
+ }})
+ .show();
+ // Return true so WebView knows we will handle the dialog.
+ return true;
+ }
+ };
+
+ /**
+ * 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().queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY).size() != 0) {
+ // 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
+ */
+ public 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
+ if (!Environment.getExternalStorageState().
+ equals(Environment.MEDIA_MOUNTED)) {
+ String msg =
+ getString(R.string.download_no_sdcard_dlg_msg, filename);
+
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.download_no_sdcard_dlg_title)
+ .setIcon(R.drawable.ic_dialog_alert)
+ .setMessage(msg)
+ .setPositiveButton(R.string.ok, null)
+ .show();
+ return;
+ }
+
+ String cookies = CookieManager.getInstance().getCookie(url);
+
+ ContentValues values = new ContentValues();
+ values.put(Downloads.URI, url);
+ 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.parse(url).getHost());
+ if (contentLength > 0) {
+ values.put(Downloads.TOTAL_BYTES, contentLength);
+ }
+ 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;
+ }
+ getWindow().setFeatureDrawable(Window.FEATURE_RIGHT_ICON, d);
+ }
+
+ /**
+ * Displays a page-info dialog.
+ * @param view The target web-view.
+ * @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 WebView view,
+ final boolean fromShowSSLCertificateOnError) {
+ final LayoutInflater factory = LayoutInflater
+ .from(this);
+
+ final View pageInfoView = factory.inflate(R.layout.page_info, null);
+
+ String url = null;
+ String title = null;
+
+ // Use the cached title and url if this is the current WebView
+ if (view == mWebView) {
+ 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 = view;
+ mPageInfoFromShowSSLCertificateOnError = new Boolean(fromShowSSLCertificateOnError);
+
+ AlertDialog.Builder alertDialogBuilder =
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.page_info).setIcon(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.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(view);
+ }
+ }
+ }
+ });
+ }
+
+ mPageInfoDialog = alertDialogBuilder.show();
+ }
+
+ /**
+ * Displays the main top-level page SSL certificate dialog
+ * (accessible from the Page-Info dialog).
+ * @param view The target web-view.
+ */
+ private void showSSLCertificate(final WebView view) {
+ final View certificateView =
+ inflateCertificateView(mWebView.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 = view;
+ 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(view, false);
+ }
+ })
+ .setOnCancelListener(
+ new DialogInterface.OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ mSSLCertificateDialog = null;
+ mSSLCertificateView = null;
+
+ showPageInfo(view, 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(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;
+ mHttpAuthenticationDialog = new AlertDialog.Builder(this)
+ .setTitle(titleText)
+ .setIcon(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;
+ }})
+ .show();
+ if (focusId != 0) {
+ mHttpAuthenticationDialog.findViewById(focusId).requestFocus();
+ } else {
+ v.findViewById(R.id.username_edit).requestFocus();
+ }
+ }
+
+ public int getProgress() {
+ return mWebView.getProgress();
+ }
+
+ /**
+ * 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) {
+ mWebView.setHttpAuthUsernamePassword(host, realm, username, password);
+ }
+
+ /**
+ * http stack 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) {
+ if (mAlertDialog != null) {
+ mAlertDialog.cancel();
+ mAlertDialog = null;
+ }
+ } else {
+ if (mInLoad && mAlertDialog == null) {
+ mAlertDialog = new AlertDialog.Builder(this)
+ .setTitle(R.string.loadSuspendedTitle)
+ .setMessage(R.string.loadSuspended)
+ .setPositiveButton(R.string.ok, null)
+ .show();
+ }
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode,
+ Intent intent) {
+ switch (requestCode) {
+ case BOOKMARKS_PAGE:
+ case CLASSIC_HISTORY_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 {
+ // 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(mTabControl.getCurrentTab(),
+ false, data, TAB_OVERVIEW_DELAY, null);
+ } else {
+ 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.
+ */
+ private 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();
+ }
+
+ mTabControl.removeTab(mTabControl.getTab(position));
+ // 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(position);
+ } else {
+ position--;
+ }
+
+ // FIXME: This isn't really right. We don't have a current WebView
+ // since we are switching between tabs and haven't selected a new
+ // one. This just prevents a NPE in case the user hits home from the
+ // tab switcher.
+ int index = position;
+ if (index == ImageGrid.NEW_TAB) {
+ index = 0;
+ }
+ final TabControl.Tab t = mTabControl.getTab(index);
+ // Only the current tab ensures its WebView is non-null. This
+ // implies that we are reloading the freed tab.
+ mTabControl.setCurrentTab(t);
+ mWebView = t.getWebView();
+ }
+ 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();
+ BrowserActivity.this.getWindow().setFeatureInt(
+ Window.FEATURE_PROGRESS, Window.PROGRESS_VISIBILITY_ON);
+ BrowserActivity.this.mMenuState = EMPTY_MENU;
+
+ // NEW_TAB means that the "New Tab" cell was clicked on.
+ if (index == ImageGrid.NEW_TAB) {
+ openTabAndShow(mSettings.getHomePage(), null);
+ } 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);
+ 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++;
+ if (stay) {
+ getWindow().setFeatureDrawable(Window.FEATURE_LEFT_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 bookmarksPicker() {
+ Intent intent = new Intent(this,
+ BrowserBookmarksPage.class);
+ String title = mWebView.getTitle();
+ String url = mWebView.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);
+ startActivityForResult(intent, BOOKMARKS_PAGE);
+ }
+
+ // Called when loading from bookmarks or goto.
+ private void loadURL(String url) {
+ // In case the user enters nothing.
+ if (url != null && url.length() != 0) {
+ url = smartUrlFilter(url);
+ WebView w = getTopWindow();
+ if (!mWebViewClient.shouldOverrideUrlLoading(w, url)) {
+ w.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 inUrl) {
+
+ boolean hasSpace = inUrl.indexOf(' ') != -1;
+
+ if (!hasSpace) {
+ Matcher matcher = ACCEPTED_URI_SCHEMA.matcher(inUrl);
+ if (matcher.matches()) {
+ // 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 */static String composeSearchUrl(String search) {
+ return URLUtil.composeSearchUrl(search, QuickSearch_G,
+ QUERY_PLACE_HOLDER);
+ }
+
+ 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 WebView mWebView;
+ 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 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 WebView 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 WebView 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
+ final static String QuickSearch_G =
+ "http://www.google.com/m?client=ms-"
+ + SystemProperties.get("ro.com.google.clientid", "unknown")
+ + "&source=android-chrome&q=%s";
+ // 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";
+
+ private final static String QUERY_PLACE_HOLDER = "%s";
+
+ 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;
+
+ // monitor platform changes
+ private IntentFilter mNetworkStateChangedFilter;
+ private BroadcastReceiver mNetworkStateIntentReceiver;
+
+ // activity requestCode
+ final static int BOOKMARKS_PAGE = 1;
+ final static int CLASSIC_HISTORY_PAGE = 2;
+ final static int DOWNLOAD_PAGE = 3;
+ final static int PREFERENCES_PAGE = 4;
+
+ // the frenquency of checking whether system memory is low
+ final static int CHECK_MEMORY_INTERVAL = 30000; // 30 seconds
+}
diff --git a/src/com/android/browser/BrowserBookmarksAdapter.java b/src/com/android/browser/BrowserBookmarksAdapter.java
new file mode 100644
index 00000000..3b76e757
--- /dev/null
+++ b/src/com/android/browser/BrowserBookmarksAdapter.java
@@ -0,0 +1,522 @@
+/*
+ * 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;
+ }
+ final String[] selArgs = new String[] { url };
+ final String where = Browser.BookmarkColumns.URL + " == ? 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..f938ff9a
--- /dev/null
+++ b/src/com/android/browser/BrowserBookmarksPage.java
@@ -0,0 +1,356 @@
+/*
+ * 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.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;
+
+ 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.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("title"), 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 = new Intent();
+ intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent(Intent.ACTION_VIEW,
+ Uri.parse(getUrl(position))));
+ intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, getBookmarkTitle(position));
+ intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
+ Intent.ShortcutIconResource.fromContext(BrowserBookmarksPage.this,
+ R.drawable.ic_launcher_browser));
+ setResult(RESULT_OK, intent);
+ finish();
+ }
+ }
+ };
+
+ private void saveCurrentPage() {
+ Intent i = new Intent(BrowserBookmarksPage.this,
+ AddBookmarkPage.class);
+ i.putExtras(getIntent());
+ startActivityForResult(i, BOOKMARKS_SAVE);
+ }
+
+ private void loadUrl(int position) {
+ setResult(RESULT_OK, (new Intent()).setAction(getUrl(position)));
+ 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);
+ setResult(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(R.drawable.ssl_icon)
+ .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()) {
+ setResult(RESULT_CANCELED);
+ mCanceled = true;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+}
diff --git a/src/com/android/browser/BrowserDownloadAdapter.java b/src/com/android/browser/BrowserDownloadAdapter.java
new file mode 100644
index 00000000..b3e08f55
--- /dev/null
+++ b/src/com/android/browser/BrowserDownloadAdapter.java
@@ -0,0 +1,221 @@
+/*
+ * 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.Formatter;
+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.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.FILENAME);
+ 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.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_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..e2b11a69
--- /dev/null
+++ b/src/com/android/browser/BrowserDownloadPage.java
@@ -0,0 +1,470 @@
+/*
+ * 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.FILENAME, 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;
+
+ 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) {
+ Uri record = ContentUris.withAppendedId(Downloads.CONTENT_URI, id);
+ ContentValues values = new ContentValues();
+ values.put(Downloads.CONTROL, Downloads.CONTROL_RUN);
+ getContentResolver().update(record, values, null, null);
+ }
+
+ /**
+ * 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()) {
+ StringBuffer where = new StringBuffer();
+ 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()) {
+ StringBuffer where = new StringBuffer();
+ 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.FILENAME);
+ 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..30591263
--- /dev/null
+++ b/src/com/android/browser/BrowserHistoryPage.java
@@ -0,0 +1,377 @@
+/*
+ * 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.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.database.Cursor;
+import android.database.DataSetObserver;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+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.ListAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Vector;
+
+/**
+ * Activity for displaying the browser's history, divided into
+ * days of viewing.
+ */
+public class BrowserHistoryPage extends ListActivity {
+ private HistoryAdapter mAdapter;
+ private DateSorter mDateSorter;
+ private boolean mMaxTabsOpen;
+
+ private final static String LOGTAG = "browser";
+
+ // FIXME: Make this a part of Browser so we do not have more than one
+ // copy (other copy is in BrowserBookmarksAdapter).
+ // Used to store favicons as we get them from the database
+ private final HashMap<String, Bitmap> mUrlsToIcons =
+ new HashMap<String, Bitmap>();
+ // Implementation of WebIconDatabase.IconListener
+ private class IconReceiver implements IconListener {
+ public void onReceivedIcon(String url, Bitmap icon) {
+ mUrlsToIcons.put(url, 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);
+ }
+ setResult(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);
+ ListView list = getListView();
+ 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);
+
+ mMaxTabsOpen = getIntent().getBooleanExtra("maxTabsOpen", false);
+ Browser.requestAllIcons(getContentResolver(), null, mIconReceiver);
+
+ // initialize the result to canceled, so that if the user just presses
+ // back then it will have the correct result
+ setResult(RESULT_CANCELED);
+ }
+
+ @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) {
+ AdapterView.AdapterContextMenuInfo i =
+ (AdapterView.AdapterContextMenuInfo)
+ menuInfo;
+
+ // 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");
+ List<ResolveInfo> list = pm.queryIntentActivities(send,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ menu.findItem(R.id.share_link_context_menu_id).setVisible(
+ list.size() > 0);
+
+ super.onCreateContextMenu(menu, v, menuInfo);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ AdapterView.AdapterContextMenuInfo i =
+ (AdapterView.AdapterContextMenuInfo)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
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ if (v instanceof HistoryItem) {
+ loadUrl(((HistoryItem) v).getUrl(), false);
+ }
+ }
+
+ private class HistoryAdapter implements ListAdapter {
+
+ // Map of items. Negative values are labels, positive values
+ // and zero are cursor offsets.
+ int mItemMap[];
+ 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();
+ }
+
+ void refreshData() {
+ mCursor.requery();
+ buildMap();
+ for (DataSetObserver o : mObservers) {
+ o.onChanged();
+ }
+ }
+
+ public void buildMap() {
+ // The cursor is sorted by date
+ // Make one pass to build up the ItemMap with the inserted
+ // section separators.
+ int array[] = new int[mCursor.getCount() + DateSorter.DAY_COUNT];
+ int dateIndex = -1;
+ if (mCursor.moveToFirst() && mCursor.getCount() > 0) {
+ int itemIndex = 0;
+ while (!mCursor.isAfterLast()) {
+ long date = mCursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX);
+ int index = mDateSorter.getIndex(date);
+ if (index > dateIndex) {
+ dateIndex = index;
+ array[itemIndex] = dateIndex - DateSorter.DAY_COUNT;
+ itemIndex++;
+ }
+ array[itemIndex] = mCursor.getPosition();
+ itemIndex++;
+ mCursor.moveToNext();
+ }
+ } else {
+ // The db is empty, just add the heading for the first item
+ dateIndex = 0;
+ array[0] = dateIndex - DateSorter.DAY_COUNT;
+ }
+ // Compress the array as the trailing date sections may be
+ // empty
+ int extraEntries = DateSorter.DAY_COUNT - dateIndex - 1;
+ if (extraEntries > 0) {
+ int newArraySize = array.length - extraEntries;
+ mItemMap = new int[newArraySize];
+ System.arraycopy(array, 0, mItemMap, 0, newArraySize);
+ } else {
+ mItemMap = array;
+ }
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (mItemMap[position] < 0) {
+ return getHeaderView(position, convertView);
+ }
+ return getHistoryItem(position, convertView);
+ }
+
+ View getHistoryItem(int position, View convertView) {
+ HistoryItem item;
+ if (null == convertView || !(convertView instanceof HistoryItem)) {
+ item = new HistoryItem(BrowserHistoryPage.this);
+ } else {
+ item = (HistoryItem) convertView;
+ }
+ mCursor.moveToPosition(mItemMap[position]);
+ item.setName(mCursor.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX));
+ String url = mCursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX);
+ item.setUrl(url);
+ item.setFavicon((Bitmap) mUrlsToIcons.get(url));
+ return item;
+ }
+
+ View getHeaderView(int position, View convertView) {
+ TextView item;
+ if (null == convertView || !(convertView instanceof TextView)) {
+ LayoutInflater factory =
+ LayoutInflater.from(BrowserHistoryPage.this);
+ item = (TextView)
+ factory.inflate(android.R.layout.preference_category, null);
+ } else {
+ item = (TextView) convertView;
+ }
+ item.setText(mDateSorter.getLabel(
+ mItemMap[position] + DateSorter.DAY_COUNT));
+ return item;
+ }
+
+ public boolean areAllItemsEnabled() {
+ return false;
+ }
+
+ public boolean isEnabled(int position) {
+ return mItemMap[position] >= 0;
+ }
+
+ public int getCount() {
+ return mItemMap.length;
+ }
+
+ public Object getItem(int position) {
+ return null;
+ }
+
+ public long getItemId(int position) {
+ return mItemMap[position];
+ }
+
+ // 0 for TextView, 1 for HistoryItem
+ public int getItemViewType(int position) {
+ return mItemMap[position] < 0 ? 0 : 1;
+ }
+
+ public int getViewTypeCount() {
+ return 2;
+ }
+
+ 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() == 1;
+ }
+ }
+}
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..b8bc495c
--- /dev/null
+++ b/src/com/android/browser/BrowserPreferencesPage.java
@@ -0,0 +1,141 @@
+/*
+ * 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)) );
+
+ 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;
+
+ if (value.length() > 0) {
+ Uri path = Uri.parse(value);
+ if (path.getScheme() == null) {
+ value = "http://"+value;
+
+ pref.setSummary(value);
+
+ // 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;
+ }
+ }
+
+ pref.setSummary(value);
+ return true;
+ } else if (pref.getKey().equals(BrowserSettings.PREF_TEXT_SIZE)) {
+ pref.setSummary(getVisualTextSizeName((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..4f456e71
--- /dev/null
+++ b/src/com/android/browser/BrowserProvider.java
@@ -0,0 +1,566 @@
+/*
+ * 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.SearchManager;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.UriMatcher;
+import android.database.AbstractCursor;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.database.DataSetObserver;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.provider.Browser;
+import android.util.Log;
+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 = "date DESC";
+
+ private static final String[] TABLE_NAMES = new String[] {
+ "bookmarks", "searches"
+ };
+ private static final String[] SUGGEST_PROJECTION = new String [] {
+ "0 AS " + SearchManager.SUGGEST_COLUMN_FORMAT,
+ "url AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA,
+ "url AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
+ "title AS " + SearchManager.SUGGEST_COLUMN_TEXT_2,
+ "_id"
+ };
+ private static final String SUGGEST_SELECTION =
+ "url LIKE ? OR url LIKE ? OR url LIKE ? OR url LIKE ?";
+ private String[] SUGGEST_ARGS = new String[4];
+
+ // 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 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) {
+ db.execSQL("INSERT INTO bookmarks (title, url, visits, " +
+ "date, created, bookmark)" + " VALUES('" +
+ bookmarks[i] + "', '" + bookmarks[i + 1] +
+ "', 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 add "Google Search"
+ */
+ private class MySuggestionCursor extends AbstractCursor {
+ private Cursor mCursor;
+ private boolean mBeyondCursor;
+ private String mString;
+ private Uri mNotifyUri;
+ private ContentResolver mContentResolver;
+ private AbstractCursor.SelfContentObserver mObserver;
+ private final Object mObserverLock = new Object();
+
+ public MySuggestionCursor(Cursor c, String string) {
+ mCursor = c;
+ if (Regex.WEB_URL_PATTERN.matcher(string).matches()) {
+ mString = "";
+ } else {
+ mString = string;
+ }
+ mBeyondCursor = false;
+ }
+
+ public boolean onMove(int oldPosition, int newPosition) {
+ if (mCursor.getCount() == newPosition) {
+ mBeyondCursor = true;
+ } else {
+ mCursor.moveToPosition(newPosition);
+ mBeyondCursor = false;
+ }
+ return true;
+ }
+
+ public int getCount() {
+ if (mString.length() > 0) {
+ return mCursor.getCount() + 1;
+ } else {
+ return mCursor.getCount();
+ }
+ }
+
+ public boolean deleteRow() {
+ return !mBeyondCursor && mCursor.deleteRow();
+ }
+
+ public String[] getColumnNames() {
+ return mCursor.getColumnNames();
+ }
+
+ public int getColumnCount() {
+ return mCursor.getColumnCount();
+ }
+
+ public String getString(int columnIndex) {
+ if (!mBeyondCursor) {
+ return mCursor.getString(columnIndex);
+ }
+ switch (columnIndex) {
+ case 2: // SearchManager.SUGGEST_COLUMN_TEXT_1
+ return "Google Search for \"" + mString + "\"";
+ case 1: // SearchManager.SUGGEST_COLUMN_INTENT_DATA
+ return BrowserActivity.composeSearchUrl(mString);
+ case 3: // SearchManager.SUGGEST_COLUMN_TEXT_2
+ default:
+ return "";
+ }
+ }
+
+ public short getShort(int columnIndex) {
+ if (!mBeyondCursor) {
+ return mCursor.getShort(columnIndex);
+ }
+ if (0 == columnIndex) {
+ return 0;
+ }
+ return -1;
+ }
+
+ public int getInt(int columnIndex) {
+ if (!mBeyondCursor) {
+ return mCursor.getInt(columnIndex);
+ }
+ if (0 == columnIndex) {
+ return 0;
+ }
+ return -1;
+ }
+
+ public long getLong(int columnIndex) {
+ if (!mBeyondCursor) {
+ return mCursor.getLong(columnIndex);
+ }
+ if (0 == columnIndex) {
+ return 0;
+ }
+ return -1;
+ }
+
+ public float getFloat(int columnIndex) {
+ if (!mBeyondCursor) {
+ return mCursor.getFloat(columnIndex);
+ }
+ if (0 == columnIndex) {
+ return 0f;
+ }
+ return -1f;
+ }
+
+ public double getDouble(int columnIndex) {
+ if (!mBeyondCursor) {
+ return mCursor.getDouble(columnIndex);
+ }
+ if (0 == columnIndex) {
+ return 0.0;
+ }
+ return -1.0;
+ }
+
+ public boolean isNull(int columnIndex) {
+ return mCursor.isNull(columnIndex);
+ }
+
+ public boolean supportsUpdates() {
+ return false;
+ }
+
+ public boolean hasUpdates() {
+ return false;
+ }
+
+ public boolean updateString(int columnIndex, String value) {
+ return false;
+ }
+
+ public boolean updateShort(int columnIndex, short value) {
+ return false;
+ }
+
+ public boolean updateInt(int columnIndex, int value) {
+ return false;
+ }
+
+ public boolean updateLong(int columnIndex, long value) {
+ return false;
+ }
+
+ public boolean updateFloat(int columnIndex, float value) {
+ return false;
+ }
+
+ public boolean updateDouble(int columnIndex, double value) {
+ return false;
+ }
+
+ // TODO Temporary change, finalize after jq's changes go in
+ public void deactivate() {
+ if (mCursor != null) {
+ mCursor.deactivate();
+ }
+ super.deactivate();
+ }
+
+ public boolean requery() {
+ return mCursor.requery();
+ }
+
+ // TODO Temporary change, finalize after jq's changes go in
+ public void close() {
+ super.close();
+ if (mCursor != null) {
+ mCursor.close();
+ mCursor = null;
+ }
+ }
+
+ public void registerContentObserver(ContentObserver observer) {
+ super.registerContentObserver(observer);
+ }
+
+ public void unregisterContentObserver(ContentObserver observer) {
+ super.unregisterContentObserver(observer);
+ }
+
+ public void registerDataSetObserver(DataSetObserver observer) {
+ super.registerDataSetObserver(observer);
+ }
+
+ public void unregisterDataSetObserver(DataSetObserver observer) {
+ super.unregisterDataSetObserver(observer);
+ }
+
+ protected void onChange(boolean selfChange) {
+ synchronized (mObserverLock) {
+ super.onChange(selfChange);
+ if (mNotifyUri != null && selfChange) {
+ mContentResolver.notifyChange(mNotifyUri, mObserver);
+ }
+ }
+ }
+
+ public void setNotificationUri(ContentResolver cr, Uri uri) {
+ synchronized (mObserverLock) {
+ if (mObserver != null) {
+ cr.unregisterContentObserver(mObserver);
+ }
+ mObserver = new AbstractCursor.SelfContentObserver(this);
+ cr.registerContentObserver(uri, true, mObserver);
+ mCursor.setNotificationUri(cr, uri);
+ super.setNotificationUri(cr, uri);
+ mContentResolver = cr;
+ mNotifyUri = uri;
+ }
+ }
+
+ public boolean getWantsAllOnMoveCalls() {
+ return mCursor.getWantsAllOnMoveCalls();
+ }
+ }
+
+ @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] + "%";
+ 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;
+ }
+ // Suggestions are always performed with the default sort order:
+ // date ASC.
+ Cursor c = db.query(TABLE_NAMES[URI_MATCH_BOOKMARKS],
+ SUGGEST_PROJECTION, suggestSelection, myArgs, null, null,
+ ORDER_BY, null);
+ c.setNotificationUri(getContext().getContentResolver(), url);
+ return new MySuggestionCursor(c, 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/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java
new file mode 100644
index 00000000..b19c02e5
--- /dev/null
+++ b/src/com/android/browser/BrowserSettings.java
@@ -0,0 +1,431 @@
+/*
+ * 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.view.WindowManager;
+import android.webkit.CacheManager;
+import android.webkit.CookieManager;
+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";
+ private boolean loginInitialized = false;
+ private int orientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ 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";
+
+ // 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);
+ s.setUserAgent(b.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);
+ }
+ }
+
+ /**
+ * 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()));
+ orientation = p.getInt("orientation", orientation);
+ 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;
+ }
+
+ 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"));
+ }
+ 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 int getOrientation() {
+ return orientation;
+ }
+
+ public void setOrientation(Context context, int o) {
+ if (orientation == o) {
+ return;
+ }
+ orientation = o;
+ Editor ed = PreferenceManager.
+ getDefaultSharedPreferences(context).edit();
+ ed.putInt("orientation", orientation);
+ 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;
+ }
+
+ /**
+ * 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) {
+ mTabControl.getCurrentWebView().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);
+ // Delete back-forward list
+ if (mTabControl != null) {
+ mTabControl.clearHistory();
+ }
+ }
+
+ /* 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/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..200f86a0
--- /dev/null
+++ b/src/com/android/browser/FakeWebView.java
@@ -0,0 +1,106 @@
+/*
+ * 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 boolean mUsesResource;
+
+ private class Listener implements WebView.PictureListener {
+ public void onNewPicture(WebView view, Picture 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) {
+ Picture p = w.capturePicture();
+ canvas.save();
+ float scale = getWidth() * w.getScale() / w.getWidth();
+ canvas.scale(scale, scale);
+ canvas.translate(-w.getScrollX(), -w.getScrollY());
+ canvas.drawPicture(p);
+ 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();
+ t.getWebView().setPictureListener(l);
+ if (t.getSubWebView() != null) {
+ t.getSubWebView().setPictureListener(l);
+ }
+ }
+ }
+}
diff --git a/src/com/android/browser/FindDialog.java b/src/com/android/browser/FindDialog.java
new file mode 100644
index 00000000..42447e39
--- /dev/null
+++ b/src/com/android/browser/FindDialog.java
@@ -0,0 +1,243 @@
+/*
+ * 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.os.Handler;
+import android.os.Message;
+import android.text.Editable;
+import android.text.Spannable;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.LayoutInflater;
+import android.webkit.WebView;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/* package */ class FindDialog extends LinearLayout 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;
+
+ // Tags for messages to be sent to the handler.
+ private final static int FIND_RESPONSE = 0;
+ private final static int NUM_FOUND = 1;
+
+ 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");
+ }
+ // Find is disabled for version 1.0, so find methods on WebView are
+ // currently private.
+ //mWebView.findPrevious(mEditText.getText().toString(),
+ // mFindHandler.obtainMessage(FIND_RESPONSE));
+ }
+ };
+
+ private Handler mFindHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ if (NUM_FOUND == msg.what) {
+ mMatches.setText(Integer.toString(msg.arg1));
+ if (0 == msg.arg1) {
+ disableButtons();
+ } else {
+ mPrevButton.setFocusable(true);
+ mNextButton.setFocusable(true);
+ mPrevButton.setEnabled(true);
+ mNextButton.setEnabled(true);
+ }
+ }
+ }
+ };
+
+ private void disableButtons() {
+ mPrevButton.setEnabled(false);
+ mNextButton.setEnabled(false);
+ mPrevButton.setFocusable(false);
+ mNextButton.setFocusable(false);
+ }
+
+ public void setWebView(WebView webview) {
+ mWebView = webview;
+ }
+
+ /* package */ FindDialog(BrowserActivity context) {
+ super(context);
+ mBrowserActivity = context;
+ LayoutInflater factory = LayoutInflater.from(context);
+ factory.inflate(R.layout.browser_find, this);
+
+ setLayoutParams(new ViewGroup.LayoutParams(
+ 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);
+ disableButtons();
+ }
+
+ public void dismiss() {
+ mBrowserActivity.closeFind();
+ // If the nav buttons are highlighted, then there are matches
+ // highlighted in the WebView, and they should be cleared.
+ if (mPrevButton.isEnabled()) {
+ // Find is disabled for version 1.0, so find methods on WebView are
+ // currently private.
+ //mWebView.clearMatches();
+ }
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ // Make up and down find previous/next
+ int code = event.getKeyCode();
+ boolean up = event.getAction() == KeyEvent.ACTION_UP;
+ switch (code) {
+ case KeyEvent.KEYCODE_BACK:
+ if (up) {
+ dismiss();
+ }
+ return true;
+ case KeyEvent.KEYCODE_DPAD_UP:
+ if (event.getMetaState() != 0) {
+ break;
+ }
+ if (up) {
+ mFindPreviousListener.onClick(null);
+ }
+ return true;
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ if (event.getMetaState() != 0) {
+ break;
+ }
+ if (up) {
+ mFindListener.onClick(null);
+ }
+ return true;
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ case KeyEvent.KEYCODE_ENTER:
+ if (!mEditText.hasFocus()) {
+ break;
+ }
+ if (up) {
+ findNext();
+ }
+ return true;
+ default:
+ break;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ super.dispatchTouchEvent(ev);
+ // Return true so that BrowserActivity thinks we handled it and does
+ // not dismiss us.
+ return true;
+ }
+
+ private void findNext() {
+ if (mWebView == null) {
+ throw new AssertionError("No WebView for FindDialog::findNext");
+ }
+ // Find is disabled for version 1.0, so find methods on WebView are
+ // currently private.
+ //mWebView.findNext(mEditText.getText().toString(),
+ // mFindHandler.obtainMessage(FIND_RESPONSE));
+ }
+
+ public void 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) {
+ CharSequence find = mEditText.getText();
+ if (0 == find.length()) {
+ disableButtons();
+ // Find is disabled for version 1.0, so find methods on WebView are
+ // currently private.
+ //mWebView.clearMatches();
+ mMatches.setText(R.string.zero);
+ } else {
+ if (mWebView == null) {
+ throw new AssertionError(
+ "No WebView for FindDialog::onTextChanged");
+ }
+ // Find is disabled for version 1.0, so find methods on WebView are
+ // currently private.
+ //mWebView.findAll(find.toString(),
+ // mFindHandler.obtainMessage(NUM_FOUND));
+ }
+ }
+
+ public void afterTextChanged(Editable s) {
+ }
+}
diff --git a/src/com/android/browser/GearsDialog.java b/src/com/android/browser/GearsDialog.java
new file mode 100644
index 00000000..fd9e7624
--- /dev/null
+++ b/src/com/android/browser/GearsDialog.java
@@ -0,0 +1,124 @@
+/*
+ * 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.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.webkit.WebSettings;
+
+import android.view.KeyEvent;
+import android.view.ViewGroup.LayoutParams;
+
+import android.widget.LinearLayout;
+
+public class GearsDialog extends Activity {
+
+ private static final String TAG = "GearsDialog";
+
+ private WebView webview;
+
+ private String htmlContent;
+ private String dialogArguments;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ webview = new WebView(this);
+ webview.getSettings().setJavaScriptEnabled(true);
+ webview.addJavascriptInterface(this, "bridge");
+
+ setContentView(webview);
+ setTitle("Gears");
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ loadHTML();
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ Intent i = getIntent();
+ boolean inSettings = i.getBooleanExtra("inSettings", false);
+ // If we are called from the settings, we
+ // dismiss ourselve upon upon rotation
+ if (inSettings) {
+ GearsDialogService.signalFinishedDialog();
+ finish();
+ }
+ }
+
+ /**
+ * Load the HTML content in the WebView
+ */
+ private void loadHTML() {
+ Intent i = getIntent();
+ htmlContent = i.getStringExtra("htmlContent");
+ dialogArguments = i.getStringExtra("dialogArguments");
+ webview.loadDataWithBaseURL("", htmlContent, "text/html", "", "");
+ }
+
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.isDown()) {
+ GearsDialogService.signalFinishedDialog();
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ /**
+ * Returns a json-formatted string containing the information
+ * about the site.
+ * This method is accessible through the javascript bridge.
+ */
+ public String getDialogArguments() {
+ return dialogArguments;
+ }
+
+ /**
+ * Set the results string and closes the dialog.
+ * This method is accessible through the javascript bridge.
+ */
+ public void closeDialog(String results) {
+ GearsDialogService.closeDialog(results);
+ GearsDialogService.signalFinishedDialog();
+ finish();
+ }
+
+ /**
+ * Debug method outputting a message.
+ * This method is accessible through the javascript bridge.
+ */
+ public void log(String msg) {
+ Log.v(TAG, msg);
+ }
+}
diff --git a/src/com/android/browser/GearsDialogService.java b/src/com/android/browser/GearsDialogService.java
new file mode 100644
index 00000000..b92ff479
--- /dev/null
+++ b/src/com/android/browser/GearsDialogService.java
@@ -0,0 +1,96 @@
+/*
+ * 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.Service;
+import android.util.Log;
+import android.content.Intent;
+import android.os.IBinder;
+
+import android.content.Intent;
+import android.content.ContentValues;
+import android.content.ActivityNotFoundException;
+
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import java.lang.InterruptedException;
+
+public class GearsDialogService extends Service {
+ private static final String TAG = "GearsDialogService";
+ private final String DIALOG_PACKAGE = "com.android.browser";
+ private final String DIALOG_CLASS = DIALOG_PACKAGE + ".GearsDialog";
+
+ public static Lock lock = new ReentrantLock();
+ public static Condition dialogFinished = lock.newCondition();
+ public static String results = null;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (IGearsDialogService.class.getName().equals(intent.getAction())) {
+ return mBinder;
+ }
+ return null;
+ }
+
+ private final IGearsDialogService.Stub mBinder = new IGearsDialogService.Stub() {
+ public String showDialog(String htmlContent, String dialogArguments,
+ boolean inSettings) {
+ return GearsDialogService.this.showDialog(htmlContent, dialogArguments,
+ inSettings);
+ }
+ };
+
+ public static void closeDialog(String res) {
+ results = res;
+ }
+
+ public static void signalFinishedDialog() {
+ lock.lock();
+ dialogFinished.signal();
+ lock.unlock();
+ }
+
+ /**
+ * Show a 'true' modal dialog displaying html content, necessary
+ * for Gears. The method starts the GearsDialog activity, passing
+ * the necessary parameters to it, and then wait until the activity
+ * is finished. When the dialog closes, it sets the variable results.
+ */
+ public String showDialog(String htmlContent, String dialogArguments,
+ boolean inSettings) {
+ try {
+ Intent intent = new Intent();
+ intent.setClassName(DIALOG_PACKAGE, DIALOG_CLASS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra("htmlContent", htmlContent);
+ intent.putExtra("dialogArguments", dialogArguments);
+ intent.putExtra("inSettings", inSettings);
+ lock.lock();
+ startActivity(intent);
+ dialogFinished.await();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "exception e: " + e);
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "exception e: " + e);
+ } finally {
+ lock.unlock();
+ }
+ return results;
+ }
+}
diff --git a/src/com/android/browser/HistoryItem.java b/src/com/android/browser/HistoryItem.java
new file mode 100644
index 00000000..c83ced14
--- /dev/null
+++ b/src/com/android/browser/HistoryItem.java
@@ -0,0 +1,126 @@
+/*
+ * 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.graphics.Color;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.graphics.drawable.PaintDrawable;
+import android.view.LayoutInflater;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/**
+ * Layout representing a history item in the classic history viewer.
+ */
+/* package */ class HistoryItem extends LinearLayout {
+
+ private TextView mTitleView; // Truncated Title
+ private String mUrl; // Full Url
+ private TextView mUrlText; // Truncated Url
+
+ /**
+ * Create a new HistoryItem.
+ * @param context Context for this HistoryItem.
+ */
+ /* package */ HistoryItem(Context context) {
+ super(context);
+
+ setWillNotDraw(false);
+ LayoutInflater factory = LayoutInflater.from(context);
+ factory.inflate(R.layout.history_item, this);
+ mTitleView = (TextView) findViewById(R.id.title);
+ mUrlText = (TextView) findViewById(R.id.url);
+ }
+
+ void copyTo(HistoryItem item) {
+ item.mTitleView.setText(mTitleView.getText());
+ item.mUrlText.setText(mUrlText.getText());
+ }
+
+ /**
+ * Return the name of this HistoryItem.
+ * @return String name of this HistoryItem.
+ /
+ /* package */ String getName() {
+ return mTitleView.getText().toString();
+ }
+
+ /**
+ * Return the url of this HistoryItem.
+ * @return String url of this HistoryItem.
+ /
+ /* 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) {
+ Drawable[] array = new Drawable[2];
+ PaintDrawable p = new PaintDrawable(Color.WHITE);
+ p.setCornerRadius(3f);
+ array[0] = p;
+ if (b != null) {
+ array[1] = new BitmapDrawable(b);
+ } else {
+ array[1] = new BitmapDrawable(mContext.getResources().
+ openRawResource(R.drawable.app_web_browser_sm));
+ }
+ LayerDrawable d = new LayerDrawable(array);
+ d.setLayerInset(1, 2, 2, 2, 2);
+ d.setBounds(0, 0, 20, 20);
+ mTitleView.setCompoundDrawables(d, null, null, null);
+ }
+
+ /**
+ * Set the name for this HistoryItem.
+ * If the name is longer that BrowserSettings.MAX_TEXTVIEW_LEN characters,
+ * the name is truncated to BrowserSettings.MAX_TEXTVIEW_LEN characters.
+ * The History activity does not expose a UI element that can show the
+ * full title.
+ * @param name String representing new name for this HistoryItem.
+ */
+ /* package */ void setName(String name) {
+ if (name != null && name.length() > BrowserSettings.MAX_TEXTVIEW_LEN) {
+ name = name.substring(0, BrowserSettings.MAX_TEXTVIEW_LEN);
+ }
+ mTitleView.setText(name);
+ }
+
+ /**
+ * Set the url for this HistoryItem.
+ * @param url String representing new url for this HistoryItem.
+ */
+ /* package */ void setUrl(String url) {
+ mUrl = url;
+ // Truncate the url for the screen
+ if (url.length() > BrowserSettings.MAX_TEXTVIEW_LEN) {
+ mUrlText.setText(url.substring(0, BrowserSettings.MAX_TEXTVIEW_LEN));
+ } else {
+ mUrlText.setText(url);
+ }
+ }
+}
diff --git a/src/com/android/browser/IGearsDialogService.aidl b/src/com/android/browser/IGearsDialogService.aidl
new file mode 100644
index 00000000..02b30a2c
--- /dev/null
+++ b/src/com/android/browser/IGearsDialogService.aidl
@@ -0,0 +1,6 @@
+package com.android.browser;
+
+interface IGearsDialogService {
+ String showDialog(String htmlContent, String dialogArguments,
+ boolean inSettings);
+}
diff --git a/src/com/android/browser/ImageAdapter.java b/src/com/android/browser/ImageAdapter.java
new file mode 100644
index 00000000..e6eaa75a
--- /dev/null
+++ b/src/com/android/browser/ImageAdapter.java
@@ -0,0 +1,298 @@
+/*
+ * 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.DialogInterface;
+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.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.webkit.WebView;
+import android.widget.*;
+
+import java.util.ArrayList;
+
+/**
+ * Adapter used by ImageGrid.
+ */
+public class ImageAdapter implements ListAdapter {
+
+ ArrayList<TabControl.Tab> mItems; // Items shown in the grid
+ ArrayList<DataSetObserver> mDataObservers; // Data change listeners
+ Context mContext; // Context to use to inflate views
+ boolean mMaxedOut;
+ boolean mLandScape;
+ ImageGrid mImageGrid;
+ boolean mIsLive;
+
+ ImageAdapter(Context context, ImageGrid grid,
+ ArrayList<TabControl.Tab> items, boolean live) {
+ mContext = context;
+ mIsLive = live;
+ if (items == null) {
+ mItems = new ArrayList<TabControl.Tab>();
+ } else {
+ mItems = items;
+ if (items.size() == TabControl.MAX_TABS) {
+ mMaxedOut = true;
+ }
+ }
+ mImageGrid = grid;
+ mDataObservers = new ArrayList<DataSetObserver>();
+ mLandScape = context.getResources().getConfiguration().orientation ==
+ Configuration.ORIENTATION_LANDSCAPE;
+ }
+
+ /**
+ * 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' for a single tab or if the tab picker isn't
+ // "live" (meaning the user cannot click on a tab)
+ if (mItems.size() == 1 || !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);
+ }
+ if (mLandScape) {
+ ViewGroup.LayoutParams lp = img.getLayoutParams();
+ lp.width = 225;
+ lp.height = 120;
+ 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(R.drawable.ssl_icon)
+ .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..e0a5c898
--- /dev/null
+++ b/src/com/android/browser/ImageGrid.java
@@ -0,0 +1,239 @@
+/*
+ * 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;
+ 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);
+ }
+ if (Config.DEBUG && l == null) {
+ throw new AssertionError();
+ }
+ mListener = l;
+
+ mAdapter = new ImageAdapter(context, this, null, live);
+ setAdapter(mAdapter);
+
+ // android.R.color.window_background seems to return transparent?
+// setBackgroundColor(android.R.color.window_background);
+ setBackgroundColor(0xFF000000);
+
+ setPadding(0, 10, 0, 10);
+ setVerticalSpacing(10);
+ setHorizontalSpacing(10);
+ setNumColumns(2);
+ setStretchMode(GridView.STRETCH_COLUMN_WIDTH);
+ }
+
+ @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());
+
+ // If we only have one active tab left, don't add the remove option
+ if (mAdapter.mItems.size() <= 1) {
+ menu.findItem(R.id.remove_tab_menu_id).setVisible(false);
+ }
+ }
+ }
+
+ // 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. Replace the adapter with one
+ // that has the appropriate dimensions.
+ mAdapter = new ImageAdapter(mContext, this, mAdapter.mItems, mIsLive);
+ setAdapter(mAdapter);
+ 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/TabControl.java b/src/com/android/browser/TabControl.java
new file mode 100644
index 00000000..66adf3c0
--- /dev/null
+++ b/src/com/android/browser/TabControl.java
@@ -0,0 +1,840 @@
+/*
+ * 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.os.Bundle;
+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.JsPromptResult;
+import android.webkit.JsResult;
+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.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);
+ }
+ }
+ // 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);
+ }
+ // JavaScript functions
+ @Override
+ public boolean onJsAlert(WebView view, String url, String message,
+ JsResult result) {
+ return mClient.onJsAlert(view, url, message, result);
+ }
+ @Override
+ public boolean onJsConfirm(WebView view, String url, String message,
+ JsResult result) {
+ return mClient.onJsConfirm(view, url, message, result);
+ }
+ @Override
+ public boolean onJsPrompt(WebView view, String url, String message,
+ String initValue, JsPromptResult result) {
+ return mClient.onJsPrompt(view, url, message, initValue, result);
+ }
+ @Override
+ public boolean onJsBeforeUnload(WebView view, String url,
+ String message, JsResult result) {
+ return mClient.onJsBeforeUnload(view, url, message, result);
+ }
+ }
+ // 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;
+
+ // Construct a new tab
+ private Tab(WebView w) {
+ mMainView = w;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * 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;
+ }
+ };
+
+ /**
+ * 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));
+ }
+
+ /**
+ * 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() {
+ 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);
+ 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);
+ // 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";
+
+ /**
+ * 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();
+ // 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);
+ 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);
+ }
+ }
+ }
+ 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();
+ 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);
+
+ // 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);
+ }
+
+ // 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;
+ }
+ t.mSavedState = null;
+ t.mUrl = null;
+ t.mTitle = null;
+ return true;
+ }
+}