am 9acca15d: (-s ours) am 96f9a830: Import revised translations. DO NOT MERGE
Merge commit '9acca15d763d11b4aa4f1c2366aeec4f1e6a34d7'
* commit '9acca15d763d11b4aa4f1c2366aeec4f1e6a34d7':
Import revised translations. DO NOT MERGE
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 7e98019..ac2b317 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -84,6 +84,16 @@
<data android:mimeType="application/xhtml+xml"/>
<data android:mimeType="application/vnd.wap.xhtml+xml"/>
</intent-filter>
+ <!-- For viewing saved web archives. -->
+ <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:scheme="file" />
+ <data android:mimeType="application/x-webarchive-xml"/>
+ </intent-filter>
<!-- We are also the main entry point of the browser. -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -184,6 +194,10 @@
</intent-filter>
</activity>
+ <activity android:name="SaveToHomescreenDialog" android:label="Save to homescreen" android:theme="@android:style/Theme.Dialog"
+ android:configChanges="orientation|keyboardHidden" android:windowSoftInputMode="stateHidden">
+ </activity>
+
<!--receiver android:name=".widget.BookmarkWidgetProvider" android:label="@string/bookmarks">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
diff --git a/res/anim/find_dialog_enter.xml b/res/anim/dialog_enter.xml
similarity index 82%
rename from res/anim/find_dialog_enter.xml
rename to res/anim/dialog_enter.xml
index 5e597a4..6fbcb9e 100644
--- a/res/anim/find_dialog_enter.xml
+++ b/res/anim/dialog_enter.xml
@@ -16,6 +16,6 @@
<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" />
+ <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/dialog_exit.xml
similarity index 82%
rename from res/anim/find_dialog_exit.xml
rename to res/anim/dialog_exit.xml
index 854abd0..9845849 100644
--- a/res/anim/find_dialog_exit.xml
+++ b/res/anim/dialog_exit.xml
@@ -16,7 +16,7 @@
<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" />
+ <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-hdpi/button_selected.png b/res/drawable-hdpi/button_selected.png
new file mode 100644
index 0000000..752deda
--- /dev/null
+++ b/res/drawable-hdpi/button_selected.png
Binary files differ
diff --git a/res/drawable-hdpi/default_video_poster.png b/res/drawable-hdpi/default_video_poster.png
new file mode 100644
index 0000000..9bbaf9c
--- /dev/null
+++ b/res/drawable-hdpi/default_video_poster.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_arrow_left.png b/res/drawable-hdpi/ic_arrow_left.png
new file mode 100644
index 0000000..bcbe3f6
--- /dev/null
+++ b/res/drawable-hdpi/ic_arrow_left.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_arrow_right.png b/res/drawable-hdpi/ic_arrow_right.png
new file mode 100644
index 0000000..6095533
--- /dev/null
+++ b/res/drawable-hdpi/ic_arrow_right.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_btn_copy.png b/res/drawable-hdpi/ic_btn_copy.png
new file mode 100644
index 0000000..04fda7f
--- /dev/null
+++ b/res/drawable-hdpi/ic_btn_copy.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_btn_find.png b/res/drawable-hdpi/ic_btn_find.png
new file mode 100755
index 0000000..20e1fbc
--- /dev/null
+++ b/res/drawable-hdpi/ic_btn_find.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_btn_select_all.png b/res/drawable-hdpi/ic_btn_select_all.png
new file mode 100644
index 0000000..839915b
--- /dev/null
+++ b/res/drawable-hdpi/ic_btn_select_all.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_btn_share.png b/res/drawable-hdpi/ic_btn_share.png
new file mode 100644
index 0000000..44db9b1
--- /dev/null
+++ b/res/drawable-hdpi/ic_btn_share.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu.png b/res/drawable-hdpi/ic_menu.png
new file mode 100644
index 0000000..00f784f
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_downloads.png b/res/drawable-hdpi/ic_menu_downloads.png
new file mode 100644
index 0000000..c85f6a2
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_downloads.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_find.png b/res/drawable-hdpi/ic_menu_find.png
new file mode 100644
index 0000000..17ac694
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_find.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_pageinfo.png b/res/drawable-hdpi/ic_menu_pageinfo.png
new file mode 100644
index 0000000..293a021
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_pageinfo.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_settings.png b/res/drawable-hdpi/ic_menu_settings.png
new file mode 100644
index 0000000..46be101
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_settings.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_share.png b/res/drawable-hdpi/ic_menu_share.png
new file mode 100644
index 0000000..4b69736
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_share.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_pages.png b/res/drawable-hdpi/ic_pages.png
new file mode 100644
index 0000000..3fc2e46
--- /dev/null
+++ b/res/drawable-hdpi/ic_pages.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_reload.png b/res/drawable-hdpi/ic_reload.png
new file mode 100644
index 0000000..ebad6da
--- /dev/null
+++ b/res/drawable-hdpi/ic_reload.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_star.png b/res/drawable-hdpi/ic_star.png
new file mode 100644
index 0000000..666c670
--- /dev/null
+++ b/res/drawable-hdpi/ic_star.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_stop.png b/res/drawable-hdpi/ic_stop.png
new file mode 100644
index 0000000..0f1337f
--- /dev/null
+++ b/res/drawable-hdpi/ic_stop.png
Binary files differ
diff --git a/res/drawable-hdpi/pattern_carbon_fiber_dark.png b/res/drawable-hdpi/pattern_carbon_fiber_dark.png
new file mode 100644
index 0000000..cc8e0c5
--- /dev/null
+++ b/res/drawable-hdpi/pattern_carbon_fiber_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/progress_stop.png b/res/drawable-hdpi/progress_stop.png
new file mode 100644
index 0000000..aacece2
--- /dev/null
+++ b/res/drawable-hdpi/progress_stop.png
Binary files differ
diff --git a/res/drawable-hdpi/tab_selected_bg.9.png b/res/drawable-hdpi/tab_selected_bg.9.png
new file mode 100644
index 0000000..4e0f740
--- /dev/null
+++ b/res/drawable-hdpi/tab_selected_bg.9.png
Binary files differ
diff --git a/res/drawable-hdpi/tab_unselected_bg.9.png b/res/drawable-hdpi/tab_unselected_bg.9.png
new file mode 100644
index 0000000..9ab7c56
--- /dev/null
+++ b/res/drawable-hdpi/tab_unselected_bg.9.png
Binary files differ
diff --git a/res/drawable-mdpi/button_selected.png b/res/drawable-mdpi/button_selected.png
new file mode 100644
index 0000000..4fd1aa3
--- /dev/null
+++ b/res/drawable-mdpi/button_selected.png
Binary files differ
diff --git a/res/drawable/default_video_poster.png b/res/drawable-mdpi/default_video_poster.png
similarity index 100%
rename from res/drawable/default_video_poster.png
rename to res/drawable-mdpi/default_video_poster.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_arrow_left.png b/res/drawable-mdpi/ic_arrow_left.png
new file mode 100644
index 0000000..58fd2ca
--- /dev/null
+++ b/res/drawable-mdpi/ic_arrow_left.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_arrow_right.png b/res/drawable-mdpi/ic_arrow_right.png
new file mode 100644
index 0000000..aaf3fde
--- /dev/null
+++ b/res/drawable-mdpi/ic_arrow_right.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_btn_copy.png b/res/drawable-mdpi/ic_btn_copy.png
new file mode 100644
index 0000000..04fda7f
--- /dev/null
+++ b/res/drawable-mdpi/ic_btn_copy.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_btn_find.png b/res/drawable-mdpi/ic_btn_find.png
new file mode 100755
index 0000000..20e1fbc
--- /dev/null
+++ b/res/drawable-mdpi/ic_btn_find.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_btn_select_all.png b/res/drawable-mdpi/ic_btn_select_all.png
new file mode 100644
index 0000000..839915b
--- /dev/null
+++ b/res/drawable-mdpi/ic_btn_select_all.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_btn_share.png b/res/drawable-mdpi/ic_btn_share.png
new file mode 100644
index 0000000..44db9b1
--- /dev/null
+++ b/res/drawable-mdpi/ic_btn_share.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu.png b/res/drawable-mdpi/ic_menu.png
new file mode 100644
index 0000000..520b2c3
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_downloads.png b/res/drawable-mdpi/ic_menu_downloads.png
new file mode 100644
index 0000000..fff5022
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_downloads.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_find.png b/res/drawable-mdpi/ic_menu_find.png
new file mode 100644
index 0000000..4d96348
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_find.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_pageinfo.png b/res/drawable-mdpi/ic_menu_pageinfo.png
new file mode 100644
index 0000000..c04f0e3
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_pageinfo.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_settings.png b/res/drawable-mdpi/ic_menu_settings.png
new file mode 100644
index 0000000..7a642d6
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_settings.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_share.png b/res/drawable-mdpi/ic_menu_share.png
new file mode 100644
index 0000000..ea2b672
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_share.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_pages.png b/res/drawable-mdpi/ic_pages.png
new file mode 100644
index 0000000..fba4651
--- /dev/null
+++ b/res/drawable-mdpi/ic_pages.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_reload.png b/res/drawable-mdpi/ic_reload.png
new file mode 100644
index 0000000..ec0c238
--- /dev/null
+++ b/res/drawable-mdpi/ic_reload.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_star.png b/res/drawable-mdpi/ic_star.png
new file mode 100644
index 0000000..20a40de
--- /dev/null
+++ b/res/drawable-mdpi/ic_star.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_stop.png b/res/drawable-mdpi/ic_stop.png
new file mode 100644
index 0000000..7ee56e9
--- /dev/null
+++ b/res/drawable-mdpi/ic_stop.png
Binary files differ
diff --git a/res/drawable/pattern_carbon_fiber_dark.png b/res/drawable-mdpi/pattern_carbon_fiber_dark.png
similarity index 100%
rename from res/drawable/pattern_carbon_fiber_dark.png
rename to res/drawable-mdpi/pattern_carbon_fiber_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/progress_stop.png b/res/drawable-mdpi/progress_stop.png
new file mode 100644
index 0000000..a85f987
--- /dev/null
+++ b/res/drawable-mdpi/progress_stop.png
Binary files differ
diff --git a/res/drawable-mdpi/tab_selected_bg.9.png b/res/drawable-mdpi/tab_selected_bg.9.png
new file mode 100644
index 0000000..5e6b1ed
--- /dev/null
+++ b/res/drawable-mdpi/tab_selected_bg.9.png
Binary files differ
diff --git a/res/drawable-mdpi/tab_unselected_bg.9.png b/res/drawable-mdpi/tab_unselected_bg.9.png
new file mode 100644
index 0000000..c19443a
--- /dev/null
+++ b/res/drawable-mdpi/tab_unselected_bg.9.png
Binary files differ
diff --git a/res/anim/find_dialog_enter.xml b/res/drawable/browserbarbutton.xml
similarity index 62%
copy from res/anim/find_dialog_enter.xml
copy to res/drawable/browserbarbutton.xml
index 5e597a4..35a6f48 100644
--- a/res/anim/find_dialog_enter.xml
+++ b/res/drawable/browserbarbutton.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 The Android Open Source Project
+<!-- Copyright (C) 2010 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.
@@ -14,8 +14,8 @@
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>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true"
+ android:drawable="@drawable/button_selected" />
+ <item android:state_pressed="false" android:drawable="@drawable/clear" />
+</selector>
diff --git a/res/anim/find_dialog_enter.xml b/res/drawable/clear.xml
similarity index 62%
copy from res/anim/find_dialog_enter.xml
copy to res/drawable/clear.xml
index 5e597a4..5973f5c 100644
--- a/res/anim/find_dialog_enter.xml
+++ b/res/drawable/clear.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 The Android Open Source Project
+<!-- Copyright (C) 2010 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.
@@ -14,8 +14,10 @@
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>
+<!-- Used by browserbarbutton to show a clear background for the non pressed
+ state -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="#00000000"/>
+ <padding android:left="9dp" android:top="9dp"
+ android:right="9dp" android:bottom="9dp" />
+</shape>
diff --git a/res/drawable/progress.xml b/res/drawable/progress.xml
new file mode 100644
index 0000000..dd7c375
--- /dev/null
+++ b/res/drawable/progress.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:id="@android:id/background">
+ <shape>
+ <solid android:color="#ffffffff"/>
+ </shape>
+ </item>
+ <item android:id="@android:id/progress">
+ <clip>
+ <shape>
+ <solid android:color="#ff94b73f"/>
+ </shape>
+ </clip>
+ </item>
+
+</layer-list>
diff --git a/res/anim/find_dialog_enter.xml b/res/drawable/tab_unselected.xml
similarity index 62%
copy from res/anim/find_dialog_enter.xml
copy to res/drawable/tab_unselected.xml
index 5e597a4..1f22ae1 100644
--- a/res/anim/find_dialog_enter.xml
+++ b/res/drawable/tab_unselected.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 The Android Open Source Project
+<!-- Copyright (C) 2010 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.
@@ -14,8 +14,9 @@
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>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="#00000000"/>
+ <stroke android:width="3dp" android:color="#ff404040"/>
+ <padding android:left="9dp" android:top="9dp"
+ android:right="9dp" android:bottom="9dp" />
+</shape>
diff --git a/res/anim/find_dialog_enter.xml b/res/drawable/textfield_nostroke.xml
similarity index 62%
copy from res/anim/find_dialog_enter.xml
copy to res/drawable/textfield_nostroke.xml
index 5e597a4..2945056 100644
--- a/res/anim/find_dialog_enter.xml
+++ b/res/drawable/textfield_nostroke.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 The Android Open Source Project
+<!-- Copyright (C) 2010 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.
@@ -14,8 +14,9 @@
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>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="#ffd0d0d0"/>
+ <stroke android:width="1dp" android:color="#ff94b73f"/>
+ <padding android:left="9dp" android:top="9dp"
+ android:right="9dp" android:bottom="9dp" />
+</shape>
diff --git a/res/anim/find_dialog_enter.xml b/res/drawable/textfield_stroke.xml
similarity index 62%
copy from res/anim/find_dialog_enter.xml
copy to res/drawable/textfield_stroke.xml
index 5e597a4..4d4c74e 100644
--- a/res/anim/find_dialog_enter.xml
+++ b/res/drawable/textfield_stroke.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 The Android Open Source Project
+<!-- Copyright (C) 2010 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.
@@ -14,8 +14,9 @@
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>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="#ffffffff"/>
+ <stroke android:width="3dp" android:color="#ff94b73f"/>
+ <padding android:left="9dp" android:top="9dp"
+ android:right="9dp" android:bottom="9dp" />
+</shape>
diff --git a/res/layout-land/title_bar_tabbed.xml b/res/layout-land/title_bar_tabbed.xml
new file mode 100644
index 0000000..853dbeb
--- /dev/null
+++ b/res/layout-land/title_bar_tabbed.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?>
+ <!--
+ Copyright 2010, 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/tabbedtitleland"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingLeft="6dip"
+ android:paddingRight="6dip"
+ android:background="#ffdddddd">
+ <ImageButton
+ android:id="@+id/back"
+ android:src="@drawable/ic_arrow_left"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginRight="6dip"
+ android:background="@drawable/browserbarbutton" />
+ <ImageButton
+ android:id="@+id/forward"
+ android:src="@drawable/ic_arrow_right"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginRight="6dip"
+ android:background="@drawable/browserbarbutton" />
+ <ImageButton
+ android:id="@+id/star"
+ android:src="@drawable/ic_star"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginRight="6dip"
+ android:background="@drawable/browserbarbutton" />
+ <com.android.browser.TabScrollView
+ android:id="@+id/tabs"
+ android:layout_width="0dip"
+ android:layout_weight="1.0"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" />
+ <com.android.browser.UrlInputView
+ android:id="@+id/editurl"
+ android:layout_width="0dip"
+ android:layout_weight="1.0"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="3dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/black"
+ android:gravity="center_vertical"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:lines="1"
+ android:scrollHorizontally="true"
+ android:visibility="gone"
+ android:background="@drawable/textfield_stroke"
+ android:inputType="textUri"
+ android:imeOptions="actionGo" />
+ <ImageButton
+ android:id="@+id/newtab"
+ android:src="@drawable/ic_menu_new_window"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginRight="6dip"
+ android:background="@drawable/browserbarbutton" />
+ <ImageButton
+ android:id="@+id/menu"
+ android:src="@drawable/ic_menu"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginRight="6dip"
+ android:background="@drawable/browserbarbutton" />
+ <ImageButton
+ android:id="@+id/all_btn"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:scaleType="center"
+ android:background="@drawable/browserbarbutton"
+ android:src="@drawable/ic_pages" />
+</LinearLayout>
diff --git a/res/layout/bookmark_thumbnail.xml b/res/layout/bookmark_thumbnail.xml
index 1f017d0..363e632 100644
--- a/res/layout/bookmark_thumbnail.xml
+++ b/res/layout/bookmark_thumbnail.xml
@@ -35,7 +35,7 @@
<LinearLayout android:id="@+id/holder"
android:layout_height="match_parent"
android:layout_width="match_parent"
- android:orientation="horizontal"
+ android:orientation="vertical"
android:background="#99000000"
android:gravity="center"
android:layout_alignBottom="@+id/thumb"
diff --git a/res/layout/browser_add_bookmark_const_url.xml b/res/layout/browser_add_bookmark_const_url.xml
new file mode 100644
index 0000000..c6603f4
--- /dev/null
+++ b/res/layout/browser_add_bookmark_const_url.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ >
+
+ <ImageView android:id="@+id/titleDivider"
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:scaleType="fitXY"
+ android:gravity="fill_horizontal"
+ android:src="@drawable/dialog_divider_horizontal_light"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="10dip"/>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingTop="5dip"
+ android:paddingBottom="13dip"
+ android:paddingLeft="20dip"
+ android:paddingRight="20dip" >
+
+ <TextView
+ android:id="@+id/titleText"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/name"
+ android:gravity="left"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <EditText
+ android:id="@+id/title"
+ android:layout_height="wrap_content"
+ android:layout_width="250dip"
+ android:gravity="fill_horizontal"
+ android:inputType="textCapSentences"
+ android:selectAllOnFocus="true"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_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_select.xml b/res/layout/browser_select.xml
new file mode 100644
index 0000000..b30be8d
--- /dev/null
+++ b/res/layout/browser_select.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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/selectControls"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="5dip"
+ android:paddingLeft="4dip"
+ android:paddingRight="4dip"
+ android:paddingBottom="1dip"
+ android:background="@android:drawable/bottom_bar">
+ <ImageButton
+ android:src="@drawable/ic_btn_copy"
+ android:id="@+id/copy"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
+ <ImageButton
+ android:src="@drawable/ic_btn_share"
+ android:id="@+id/share"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
+ <ImageButton
+ android:src="@drawable/ic_btn_select_all"
+ android:id="@+id/select_all"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
+ <ImageButton
+ android:src="@drawable/ic_btn_find"
+ android:id="@+id/find"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
+ <LinearLayout
+ android:layout_height="fill_parent"
+ android:layout_width="fill_parent"
+ android:layout_weight="1"
+ />
+
+ <ImageButton
+ android:src="@drawable/ic_btn_close_panel"
+ android:id="@+id/done"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+</LinearLayout>
+
diff --git a/res/layout/browser_subwindow.xml b/res/layout/browser_subwindow.xml
index 76d72d5..adf3284 100644
--- a/res/layout/browser_subwindow.xml
+++ b/res/layout/browser_subwindow.xml
@@ -23,6 +23,7 @@
android:layout_height="match_parent"
android:padding="10dip" >
<LinearLayout
+ android:id="@+id/inner_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
diff --git a/res/layout/custom_screen.xml b/res/layout/custom_screen.xml
index 90dc324..525f30c 100644
--- a/res/layout/custom_screen.xml
+++ b/res/layout/custom_screen.xml
@@ -22,6 +22,7 @@
android:layout_height="match_parent"
/>
<LinearLayout android:orientation="vertical"
+ android:id="@+id/vertical_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
diff --git a/res/layout/simple_dropdown_item_2line.xml b/res/layout/simple_dropdown_item_2line.xml
new file mode 100644
index 0000000..8b955ec
--- /dev/null
+++ b/res/layout/simple_dropdown_item_2line.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/layout/simple_spinner_item.xml
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:baselineAligned="false">
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/icon1"
+ android:scaleType="center"
+ android:paddingLeft="2dip"
+ android:paddingRight="2dip" />
+ <TwoLineListItem
+ android:paddingTop="2dip"
+ android:paddingBottom="2dip"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:mode="twoLine">
+ <TextView
+ android:id="@android:id/text1"
+ style="?android:attr/dropDownItemStyle"
+ android:textAppearance="?android:attr/textAppearanceLargeInverse"
+ android:singleLine="true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ <TextView
+ android:id="@android:id/text2"
+ style="?android:attr/dropDownItemStyle"
+ android:textAppearance="?android:attr/textAppearanceSmallInverse"
+ android:textColor="#323232"
+ android:singleLine="true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/text1"
+ android:layout_alignLeft="@android:id/text1" />
+ </TwoLineListItem>
+</LinearLayout>
diff --git a/res/layout/tab_title.xml b/res/layout/tab_title.xml
new file mode 100644
index 0000000..28b553d
--- /dev/null
+++ b/res/layout/tab_title.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+ <!--
+ Copyright 2010, 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="0dip"
+ android:layout_weight="1.0"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@+id/favicon"
+ android:layout_width="20dip"
+ android:layout_height="20dip"
+ android:layout_marginLeft="3dip" />
+ <ImageView
+ android:id="@+id/lock"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="6dip"
+ android:visibility="gone" />
+ <TextView
+ android:id="@+id/title"
+ android:layout_height="wrap_content"
+ android:layout_width="0dip"
+ android:layout_weight="1.0"
+ android:layout_marginLeft="3dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/black"
+ android:gravity="center_vertical"
+ android:singleLine="true"
+ android:ellipsize="end" />
+ <com.android.browser.CircularProgressView
+ android:id="@+id/stop"
+ android:layout_width="36dip"
+ android:layout_height="36dip"
+ android:background="@null"
+ android:src="@drawable/progress_stop" />
+ <ImageView
+ android:id="@+id/close"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="6dip"
+ android:src="@drawable/btn_close_window" />
+</LinearLayout>
diff --git a/res/layout/title_bar.xml b/res/layout/title_bar.xml
index 9f0cb51..d6a77e0 100644
--- a/res/layout/title_bar.xml
+++ b/res/layout/title_bar.xml
@@ -90,7 +90,7 @@
android:layout_marginRight="-5dip"
android:scaleType="center"
android:background="@drawable/btn_bookmark"
- android:src="@drawable/ic_btn_bookmarks"
+ android:src="@drawable/ic_list_bookmark"
/>
</LinearLayout>
</LinearLayout>
diff --git a/res/layout/title_bar_tabbed.xml b/res/layout/title_bar_tabbed.xml
new file mode 100644
index 0000000..fc786e0
--- /dev/null
+++ b/res/layout/title_bar_tabbed.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+ <!--
+ Copyright 2010, 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/tabbedtitleport"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:background="#ffdddddd">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <com.android.browser.TabScrollView
+ android:id="@+id/tabs"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1.0"
+ android:orientation="horizontal" />
+ <ImageButton
+ android:id="@+id/newtab"
+ android:src="@drawable/ic_menu_new_window"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:background="@drawable/browserbarbutton" />
+ </LinearLayout>
+ <LinearLayout
+ android:id="@+id/urlbar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingLeft="6dip"
+ android:paddingRight="6dip">
+ <ImageButton
+ android:id="@+id/back"
+ android:src="@drawable/ic_arrow_left"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginRight="6dip"
+ android:background="@drawable/browserbarbutton" />
+ <ImageButton
+ android:id="@+id/forward"
+ android:src="@drawable/ic_arrow_right"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginRight="6dip"
+ android:background="@drawable/browserbarbutton" />
+ <ImageButton
+ android:id="@+id/star"
+ android:src="@drawable/ic_star"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginRight="6dip"
+ android:background="@drawable/browserbarbutton" />
+ <com.android.browser.UrlInputView
+ android:id="@+id/editurl"
+ android:layout_width="0dip"
+ android:layout_weight="1.0"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="3dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/black"
+ android:gravity="center_vertical"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:lines="1"
+ android:scrollHorizontally="true"
+ android:visibility="gone"
+ android:background="@drawable/textfield_nostroke"
+ android:inputType="textUri"
+ android:imeOptions="actionGo" />
+ <ImageButton
+ android:id="@+id/menu"
+ android:src="@drawable/ic_menu"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginRight="6dip"
+ android:background="@drawable/browserbarbutton" />
+ <ImageButton
+ android:id="@+id/all_btn"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:scaleType="center"
+ android:background="@drawable/browserbarbutton"
+ android:src="@drawable/ic_pages" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/res/menu-xlarge/browser.xml b/res/menu-xlarge/browser.xml
new file mode 100644
index 0000000..0fc20be
--- /dev/null
+++ b/res/menu-xlarge/browser.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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/new_tab_menu_id"
+ android:title="@string/new_tab"
+ android:icon="@drawable/ic_menu_new_window"
+ android:alphabeticShortcut="n" />
+ <item android:id="@+id/active_tabs_menu_id"
+ android:title="@string/active_tabs"
+ android:icon="@drawable/ic_menu_windows"
+ android:alphabeticShortcut="t" />
+ <item android:id="@+id/find_menu_id"
+ android:title="@string/find_dot"
+ android:icon="@drawable/ic_menu_find"
+ android:alphabeticShortcut="f" />
+ <item android:id="@+id/share_page_menu_id"
+ android:title="@string/share_page"
+ android:icon="@drawable/ic_menu_share"
+ android:alphabeticShortcut="s" />
+ <item android:id="@+id/page_info_menu_id"
+ android:title="@string/page_info"
+ android:icon="@drawable/ic_menu_pageinfo"
+ android:alphabeticShortcut="g" />
+ <item android:id="@+id/view_downloads_menu_id"
+ android:title="@string/menu_view_download"
+ android:icon="@drawable/ic_menu_downloads"
+ android:alphabeticShortcut="d" />
+ <item android:id="@+id/preferences_menu_id"
+ android:title="@string/menu_preferences"
+ android:icon="@drawable/ic_menu_settings"
+ android:alphabeticShortcut="p" />
+ <item android:id="@+id/save_webarchive_menu_id"
+ android:title="@string/menu_save_webarchive" />
+ <!-- followings are debug only -->
+ <item android:id="@+id/dump_nav_menu_id"
+ android:title="@string/dump_nav"
+ android:visible="false" />
+ <item android:id="@+id/dump_counters_menu_id"
+ android:title="@string/dump_counters"
+ android:visible="false" />
+ </group>
+ <group android:id="@+id/MAIN_SHORTCUT_MENU" android:visible="false">
+ <item android:id="@+id/homepage_menu_id"
+ android:alphabeticShortcut=" " />
+ <item android:id="@+id/classic_history_menu_id"
+ android:alphabeticShortcut="h" />
+ <item android:id="@+id/zoom_in_menu_id"
+ android:alphabeticShortcut="i" />
+ <item android:id="@+id/zoom_out_menu_id"
+ android:alphabeticShortcut="o" />
+ <item android:id="@+id/window_one_menu_id"
+ android:alphabeticShortcut="1" />
+ <item android:id="@+id/window_two_menu_id"
+ android:alphabeticShortcut="2" />
+ <item android:id="@+id/window_three_menu_id"
+ android:alphabeticShortcut="3" />
+ <item android:id="@+id/window_four_menu_id"
+ android:alphabeticShortcut="4" />
+ <item android:id="@+id/window_five_menu_id"
+ android:alphabeticShortcut="5" />
+ <item android:id="@+id/window_six_menu_id"
+ android:alphabeticShortcut="6" />
+ <item android:id="@+id/window_seven_menu_id"
+ android:alphabeticShortcut="7" />
+ <item android:id="@+id/window_eight_menu_id"
+ android:alphabeticShortcut="8" />
+ <item android:id="@+id/back_menu_id"
+ android:alphabeticShortcut="j" />
+ <item android:id="@+id/forward_menu_id"
+ android:alphabeticShortcut="k" />
+ <item android:id="@+id/bookmarks_menu_id"
+ android:alphabeticShortcut="b" />
+ <item android:id="@+id/add_bookmark_menu_id"
+ android:alphabeticShortcut="a" />
+ <item android:id="@+id/stop_reload_menu_id"
+ android:alphabeticShortcut="r" />
+ <item android:id="@+id/goto_menu_id"
+ android:alphabeticShortcut="l" />
+ <item android:id="@+id/close_menu_id"
+ android:alphabeticShortcut="w" />
+ </group>
+</menu>
diff --git a/res/menu/browser.xml b/res/menu/browser.xml
index 4793c21..adb1bad 100644
--- a/res/menu/browser.xml
+++ b/res/menu/browser.xml
@@ -41,22 +41,26 @@
android:alphabeticShortcut="a" />
<item android:id="@+id/find_menu_id"
android:title="@string/find_dot"
+ android:icon="@drawable/ic_menu_find"
android:alphabeticShortcut="f" />
- <item android:id="@+id/select_text_id"
- android:title="@string/select_dot"
- android:alphabeticShortcut="e" />
- <item android:id="@+id/page_info_menu_id"
- android:title="@string/page_info"
- android:alphabeticShortcut="g" />
<item android:id="@+id/share_page_menu_id"
android:title="@string/share_page"
+ android:icon="@drawable/ic_menu_share"
android:alphabeticShortcut="s" />
+ <item android:id="@+id/page_info_menu_id"
+ android:title="@string/page_info"
+ android:icon="@drawable/ic_menu_pageinfo"
+ android:alphabeticShortcut="g" />
<item android:id="@+id/view_downloads_menu_id"
android:title="@string/menu_view_download"
+ android:icon="@drawable/ic_menu_downloads"
android:alphabeticShortcut="d" />
<item android:id="@+id/preferences_menu_id"
android:title="@string/menu_preferences"
+ android:icon="@drawable/ic_menu_settings"
android:alphabeticShortcut="p" />
+ <item android:id="@+id/save_webarchive_menu_id"
+ android:title="@string/menu_save_webarchive" />
<!-- followings are debug only -->
<item android:id="@+id/dump_nav_menu_id"
android:title="@string/dump_nav"
@@ -91,16 +95,10 @@
<item android:id="@+id/window_eight_menu_id"
android:alphabeticShortcut="8" />
<item android:id="@+id/back_menu_id"
- android:title="@string/back"
- android:drawable="@*android:drawable/ic_menu_back"
android:alphabeticShortcut="j" />
<item android:id="@+id/goto_menu_id"
- android:title="@string/goto_dot"
- android:alphabeticShortcut="l"
- android:icon="@android:drawable/ic_menu_search"/>
+ android:alphabeticShortcut="l" />
<item android:id="@+id/close_menu_id"
- android:icon="@drawable/ic_btn_close_panel"
- android:title="@string/tab_picker_remove_tab"
android:alphabeticShortcut="w" />
</group>
<!-- these items are toggled in and out of @+id/stop_reload_menu_id -->
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index e9df9f4..7efb620 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -38,7 +38,7 @@
<item quantity="few" msgid="5544267486978946555">"<xliff:g id="NUMBER">%d</xliff:g> Treffer"</item>
<item quantity="other" msgid="6616125067364315405">"<xliff:g id="NUMBER">%d</xliff:g> Treffer"</item>
</plurals>
- <string name="title_bar_loading" msgid="7438217780834640678">"Wird geladen..."</string>
+ <string name="title_bar_loading" msgid="7438217780834640678">"Ladevorgang läuft..."</string>
<string name="page_info" msgid="4048529256302257195">"Seiteninfo"</string>
<string name="page_info_view" msgid="5303490449842635158">"Seiteninfo anzeigen"</string>
<string name="page_info_address" msgid="2222306609532903254">"Adresse:"</string>
@@ -112,7 +112,7 @@
<string name="contextmenu_copylink" msgid="5153657160294534270">"Link-URL kopieren"</string>
<string name="contextmenu_download_image" msgid="4243829645180686912">"Bild speichern"</string>
<string name="contextmenu_view_image" msgid="3870625602053600905">"Bild anzeigen"</string>
- <string name="contextmenu_set_wallpaper" msgid="3691902960115350686">"Als Hintergrund festlegen"</string>
+ <string name="contextmenu_set_wallpaper" msgid="3691902960115350686">"Als Hintergrundbild festlegen"</string>
<string name="contextmenu_dial_dot" msgid="5856550683415933806">"Wählen..."</string>
<string name="contextmenu_add_contact" msgid="3183511922223645716">"Kontakt hinzufügen"</string>
<string name="contextmenu_send_mail" msgid="1014513374828775660">"E-Mail senden"</string>
@@ -289,5 +289,5 @@
<string name="website_settings_clear_all_dialog_message" msgid="6150502090601476333">"Alle Websitedaten und Standortberechtigungen werden gelöscht."</string>
<string name="website_settings_clear_all_dialog_ok_button" msgid="6401582240627669431">"Alle Daten löschen"</string>
<string name="website_settings_clear_all_dialog_cancel_button" msgid="1896757051856611674">"Abbrechen"</string>
- <string name="progress_dialog_setting_wallpaper" msgid="4871900779338536674">"Hintergrund wird eingestellt..."</string>
+ <string name="progress_dialog_setting_wallpaper" msgid="4871900779338536674">"Hintergrundbild wird eingestellt..."</string>
</resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 02b06b1..d69b315 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -22,7 +22,7 @@
<string name="active_tabs" msgid="3050623868203544623">"Windows"</string>
<string name="tab_bookmarks" msgid="2305793036003473653">"Marcadores"</string>
<string name="tab_most_visited" msgid="1077402532455000703">"Más visitados"</string>
- <string name="tab_history" msgid="1979267558744613746">"Historial "</string>
+ <string name="tab_history" msgid="1979267558744613746">"Historial"</string>
<string name="added_to_bookmarks" msgid="1020224130695956728">"Agregado a marcadores"</string>
<string name="removed_from_bookmarks" msgid="6063705902028438800">"Suprimido de los marcadores"</string>
<string name="sign_in_to" msgid="5939425800148759165">"Iniciar sesión en <xliff:g id="HOSTNAME">%s1</xliff:g> \"<xliff:g id="REALM">%s2</xliff:g>\""</string>
@@ -226,17 +226,17 @@
<string name="popup_window_attempt" msgid="2673111696288657989">"Este sitio está intentando abrir una ventana emergente."</string>
<string name="allow" msgid="1157313689171991335">"Permitir"</string>
<string name="block" msgid="9172175889884707800">"Bloquear"</string>
- <string name="too_many_windows_dialog_title" msgid="5175503564948906442">"Se alcanzó el límite de la ventana "</string>
+ <string name="too_many_windows_dialog_title" msgid="5175503564948906442">"Se alcanzó el límite de la ventana"</string>
<string name="too_many_windows_dialog_message" msgid="1398571800233959583">"No se ha podido abrir una ventana nueva porque ya has abierto el máximo permitido."</string>
<string name="too_many_subwindows_dialog_title" msgid="3805453941587725944">"Ventana emergente ya abierta"</string>
<string name="too_many_subwindows_dialog_message" msgid="5827289829907966657">"No es posible abrir una ventana emergente nueva porque sólo puede abrirse una por vez."</string>
<string name="download_title" msgid="2122874021047565594">"Historial de descarga"</string>
<string name="download_unknown_filename" msgid="4013465542563652175">"<Desconocido>"</string>
<string name="download_menu_open" msgid="4888327480367757513">"Abrir"</string>
- <string name="download_menu_clear" msgid="6264454531553418124">"Borrar de la lista\n "</string>
+ <string name="download_menu_clear" msgid="6264454531553418124">"Borrar de la lista"</string>
<string name="download_menu_delete" msgid="8815502136393894148">"Eliminar"</string>
<string name="download_menu_cancel" msgid="2545333007601851574">"Cancelar descarga"</string>
- <string name="download_menu_cancel_all" msgid="2136550823151999166">"Cancelar todas las descargas\n "</string>
+ <string name="download_menu_cancel_all" msgid="2136550823151999166">"Cancelar todas las descargas"</string>
<string name="download_cancel_dlg_title" msgid="8909108500262799748">"Cancelar descargas"</string>
<string name="download_cancel_dlg_msg" msgid="6285389170052357797">"Las <xliff:g id="DOWNLOAD_COUNT">%d</xliff:g> descargas se cancelarán y se borrarán del historial de descarga."</string>
<string name="download_delete_file" msgid="5330036497843073249">"El archivo se eliminará"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 6e19b2e..b976eeb 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -62,7 +62,7 @@
<string name="expires_on" msgid="8061200430557020704">"Дата окончания действия:"</string>
<string name="stopping" msgid="4839698519340302982">"Остановка..."</string>
<string name="stop" msgid="5687251076030630074">"Стоп"</string>
- <string name="reload" msgid="8585220783228408062">"Обновить"</string>
+ <string name="reload" msgid="8585220783228408062">"Обновление"</string>
<string name="back" msgid="8414603107175713668">"Назад"</string>
<string name="forward" msgid="4288210890526641577">"Вперед"</string>
<string name="save" msgid="5922311934992468496">"ОК"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index bbb698c..b4dafb1 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -90,7 +90,7 @@
<string name="switch_to_thumbnails" msgid="5493351529609043151">"Miniatyrvy"</string>
<string name="switch_to_list" msgid="8900531247982121055">"Listvy"</string>
<string name="current_page" msgid="7510129573681663135">"från "</string>
- <string name="delete_bookmark_warning" msgid="758043186202032205">"Bokmärket <xliff:g id="BOOKMARK">%s</xliff:g> tas bort. "</string>
+ <string name="delete_bookmark_warning" msgid="758043186202032205">"Bokmärket <xliff:g id="BOOKMARK">%s</xliff:g> tas bort."</string>
<string name="open_in_new_window" msgid="6596775546468054510">"Öppna i nytt fönster"</string>
<string name="goto_dot" msgid="3895839050522602723">"Kör"</string>
<string name="find_dot" msgid="6259312434696611957">"Sök på sidan"</string>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 07c2eb9..0dcfa9e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -50,17 +50,16 @@
<!-- Label for a confirm button. Used in multiple contexts. -->
<string name="ok">OK</string>
- <!-- Displayed on the Find dialog to display the number of matches
- found in the current page. -->
+ <!-- Displayed on the Find dialog when there are no matches -->
+ <string name="no_matches">No matches</string>
+
+ <!-- Displayed on the Find dialog to display the index of the highlighted
+ match and total number of matches found in the current page. -->
<plurals name="matches_found">
- <!-- Case of no matches -->
- <item quantity="zero">No matches</item>
<!-- Case of one match -->
<item quantity="one">1 match</item>
- <!-- Case of "few" (two) matches -->
- <item quantity="few"><xliff:g id="number" example="2">%d</xliff:g> matches</item>
- <!-- Case of several matches -->
- <item quantity="other"><xliff:g id="number" example="137">%d</xliff:g> matches</item>
+ <!-- Case of multiple total matches -->
+ <item quantity="other"><xliff:g id="index" example="2">%d</xliff:g> of <xliff:g id="total" example="137">%d</xliff:g></item>
</plurals>
<!-- Displayed on the title bar while the page is loading -->
@@ -138,12 +137,13 @@
<string name="name">Name</string>
<!-- Initial value in Location field in Bookmark dialog box -->
<string name="http">http://</string>
- <!-- Menu item that opens a dialog to save a bookmark, initialized with the current page -->
- <string name="save_to_bookmarks">Add bookmark</string>
+ <!-- Menu item that opens a dialog to save a bookmark for the current page, also displayed as
+ the title of the dialog used for adding a bookmark -->
+ <string name="save_to_bookmarks">Add to Bookmarks</string>
<!-- Menu item on the bookmarks page, to edit an existing bookmark -->
<string name="edit_bookmark">Edit bookmark</string>
<!-- Context menu item to create a shortcut to the bookmark on the desktop -->
- <string name="create_shortcut_bookmark">Add shortcut to Home</string>
+ <string name="create_shortcut_bookmark">Add to Home</string>
<!-- Context menu item to open the currently highlighted bookmark -->
<string name="open_bookmark">Open</string>
<!-- Menu item to remove the currently highlighted bookmark-->
@@ -217,6 +217,12 @@
<string name="copy_page_url">Copy page url</string>
<!-- Menu item -->
<string name="share_page">Share page</string>
+ <!-- Menu item for saving a page as a web archive. -->
+ <string name="menu_save_webarchive">Save as Web Archive</string>
+ <!-- Toast informing the user that the page has been saved. -->
+ <string name="webarchive_saved">Web archive saved.</string>
+ <!-- Toast informing the user that saving the page has failed. -->
+ <string name="webarchive_failed">Failed to save web archive.</string>
<!-- Context Menu item open the currently selected link in the current
window.-->
<string name="contextmenu_openlink">Open</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 4779aa1..2e8510a 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -30,11 +30,6 @@
<item name="android:windowContentOverlay">@null</item>
</style>
- <style name="FindDialog">
- <item name="android:windowEnterAnimation">@anim/find_dialog_enter</item>
- <item name="android:windowExitAnimation">@anim/find_dialog_exit</item>
- </style>
-
<style name="TitleBar">
<item name="android:windowEnterAnimation">@anim/title_bar_enter</item>
<item name="android:windowExitAnimation">@anim/title_bar_exit</item>
diff --git a/res/values/themes.xml b/res/values/themes.xml
deleted file mode 100644
index bb922dd..0000000
--- a/res/values/themes.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources>
- <style name="FindDialogTheme">
- <item name="android:windowFrame">@null</item>
- <item name="android:windowIsFloating">true</item>
- <item name="android:windowIsTranslucent">true</item>
- <item name="android:windowNoTitle">true</item>
- <item name="android:background">@null</item>
- <item name="android:windowBackground">@null</item>
- <item name="android:windowAnimationStyle">@style/FindDialog</item>
- <item name="android:backgroundDimEnabled">false</item>
- </style>
-</resources>
diff --git a/src/com/android/browser/ActiveTabsPage.java b/src/com/android/browser/ActiveTabsPage.java
index 2de7787..52828b3 100644
--- a/src/com/android/browser/ActiveTabsPage.java
+++ b/src/com/android/browser/ActiveTabsPage.java
@@ -20,6 +20,7 @@
import android.graphics.Bitmap;
import android.os.Handler;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
@@ -32,6 +33,7 @@
import android.widget.TextView;
public class ActiveTabsPage extends LinearLayout {
+ private static final String LOGTAG = "TabPicker";
private final BrowserActivity mBrowserActivity;
private final LayoutInflater mFactory;
private final TabControl mControl;
@@ -152,7 +154,19 @@
(ImageView) convertView.findViewById(R.id.favicon);
View close = convertView.findViewById(R.id.close);
Tab tab = mControl.getTab(position);
+ if (tab.getWebView() == null) {
+ // This means that populatePickerData will have to use the
+ // saved state.
+ Log.w(LOGTAG, "Tab " + position + " has a null WebView and "
+ + (tab.getSavedState() == null ? "null" : "non-null")
+ + " saved state ");
+ }
tab.populatePickerData();
+ if (tab.getTitle() == null || tab.getTitle().length() == 0) {
+ Log.w(LOGTAG, "Tab " + position + " has no title. "
+ + "Check above in the Logs to see whether it has a "
+ + "null WebView or null WebHistoryItem");
+ }
title.setText(tab.getTitle());
url.setText(tab.getUrl());
Bitmap icon = tab.getFavicon();
diff --git a/src/com/android/browser/AddBookmarkPage.java b/src/com/android/browser/AddBookmarkPage.java
index 1104d5e..10c91f8 100644
--- a/src/com/android/browser/AddBookmarkPage.java
+++ b/src/com/android/browser/AddBookmarkPage.java
@@ -51,6 +51,7 @@
private String mTouchIconUrl;
private Bitmap mThumbnail;
private String mOriginalUrl;
+ private boolean mIsUrlEditable = true;
// Message IDs
private static final int SAVE_BOOKMARK = 100;
@@ -74,13 +75,24 @@
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
requestWindowFeature(Window.FEATURE_LEFT_ICON);
- setContentView(R.layout.browser_add_bookmark);
+
+ mMap = getIntent().getExtras();
+ if (mMap != null) {
+ mIsUrlEditable = mMap.getBoolean("url_editable", true);
+ }
+
+ if (mIsUrlEditable) {
+ setContentView(R.layout.browser_add_bookmark);
+ } else {
+ setContentView(R.layout.browser_add_bookmark_const_url);
+ }
+
setTitle(R.string.save_to_bookmarks);
getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, R.drawable.ic_list_bookmark);
String title = null;
String url = null;
- mMap = getIntent().getExtras();
+
if (mMap != null) {
Bundle b = mMap.getBundle("bookmark");
if (b != null) {
@@ -96,8 +108,11 @@
mTitle = (EditText) findViewById(R.id.title);
mTitle.setText(title);
- mAddress = (EditText) findViewById(R.id.address);
- mAddress.setText(url);
+
+ if (mIsUrlEditable) {
+ mAddress = (EditText) findViewById(R.id.address);
+ mAddress.setText(url);
+ }
View.OnClickListener accept = mSaveBookmark;
mButton = (TextView) findViewById(R.id.OK);
@@ -135,7 +150,7 @@
final ContentResolver cr = getContentResolver();
Bookmarks.addBookmark(null, cr, url, title, thumbnail, true);
if (touchIconUrl != null) {
- new DownloadTouchIcon(cr, url).execute(mTouchIconUrl);
+ new DownloadTouchIcon(AddBookmarkPage.this, cr, url).execute(mTouchIconUrl);
}
mMessage.arg1 = 1;
} catch (IllegalStateException e) {
@@ -173,8 +188,14 @@
createHandler();
String title = mTitle.getText().toString().trim();
- String unfilteredUrl =
- BrowserActivity.fixUrl(mAddress.getText().toString());
+ String unfilteredUrl;
+ if (mIsUrlEditable) {
+ unfilteredUrl =
+ BrowserActivity.fixUrl(mAddress.getText().toString());
+ } else {
+ unfilteredUrl = mOriginalUrl;
+ }
+
boolean emptyTitle = title.length() == 0;
boolean emptyUrl = unfilteredUrl.trim().length() == 0;
Resources r = getResources();
@@ -183,9 +204,15 @@
mTitle.setError(r.getText(R.string.bookmark_needs_title));
}
if (emptyUrl) {
- mAddress.setError(r.getText(R.string.bookmark_needs_url));
+ if (mIsUrlEditable) {
+ mAddress.setError(r.getText(R.string.bookmark_needs_url));
+ } else {
+ Toast.makeText(AddBookmarkPage.this, R.string.bookmark_needs_url,
+ Toast.LENGTH_LONG).show();
+ }
+ return false;
}
- return false;
+
}
String url = unfilteredUrl.trim();
try {
@@ -200,7 +227,12 @@
// can't save their bookmark. If it was null, we'll assume
// they meant http when we parse it in the WebAddress class.
if (scheme != null) {
- mAddress.setError(r.getText(R.string.bookmark_cannot_save_url));
+ if (mIsUrlEditable) {
+ mAddress.setError(r.getText(R.string.bookmark_cannot_save_url));
+ } else {
+ Toast.makeText(AddBookmarkPage.this, R.string.bookmark_cannot_save_url,
+ Toast.LENGTH_LONG).show();
+ }
return false;
}
WebAddress address;
@@ -216,7 +248,12 @@
}
}
} catch (URISyntaxException e) {
- mAddress.setError(r.getText(R.string.bookmark_url_not_valid));
+ if (mIsUrlEditable) {
+ mAddress.setError(r.getText(R.string.bookmark_url_not_valid));
+ } else {
+ Toast.makeText(AddBookmarkPage.this, R.string.bookmark_url_not_valid,
+ Toast.LENGTH_LONG).show();
+ }
return false;
}
diff --git a/src/com/android/browser/BookmarkUtils.java b/src/com/android/browser/BookmarkUtils.java
new file mode 100644
index 0000000..0fdad15
--- /dev/null
+++ b/src/com/android/browser/BookmarkUtils.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2010 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.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.net.Uri;
+import android.provider.Browser;
+import android.util.Log;
+
+class BookmarkUtils {
+ private final static String LOGTAG = "BookmarkUtils";
+
+ // XXX: There is no public string defining this intent so if Home changes the value, we
+ // have to update this string.
+ private static final String INSTALL_SHORTCUT = "com.android.launcher.action.INSTALL_SHORTCUT";
+
+ enum BookmarkIconType {
+ ICON_INSTALLABLE_WEB_APP, // Icon for an installable web app (launches WebAppRuntime).
+ ICON_HOME_SHORTCUT // Icon for a shortcut on the home screen (launches Browser).
+ };
+
+ /**
+ * Creates an icon to be associated with this bookmark. If available, the apple touch icon
+ * will be used, else we draw our own depending on the type of "bookmark" being created.
+ */
+ static Bitmap createIcon(Context context, Bitmap touchIcon, Bitmap favicon,
+ BookmarkIconType type) {
+ int iconDimension = context.getResources().getDimensionPixelSize(
+ android.R.dimen.app_icon_size);
+
+ Bitmap bm = Bitmap.createBitmap(iconDimension, iconDimension, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bm);
+ Rect iconBounds = new Rect(0, 0, bm.getWidth(), bm.getHeight());
+
+ // Use the apple-touch-icon if available
+ if (touchIcon != null) {
+ drawTouchIconToCanvas(touchIcon, canvas, iconBounds);
+ } else {
+ // No touch icon so create our own.
+ // Set the background based on the type of shortcut (either webapp or home shortcut).
+ Bitmap icon = getIconBackground(context, type);
+
+ if (icon != null) {
+ // Now draw the correct icon background into our new bitmap.
+ canvas.drawBitmap(icon, null, iconBounds, null);
+ }
+
+ // If we have a favicon, overlay it in a nice rounded white box on top of the
+ // background.
+ if (favicon != null) {
+ drawFaviconToCanvas(favicon, canvas, iconBounds,
+ context.getResources().getDisplayMetrics().density);
+ }
+ }
+ return bm;
+ }
+
+ /**
+ * Convenience method for creating an intent that will add a shortcut to the home screen.
+ */
+ static Intent createAddToHomeIntent(Context context, String url, String title,
+ Bitmap touchIcon, Bitmap favicon) {
+ Intent i = new Intent(INSTALL_SHORTCUT);
+ Intent shortcutIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ long urlHash = url.hashCode();
+ long uniqueId = (urlHash << 32) | shortcutIntent.hashCode();
+ shortcutIntent.putExtra(Browser.EXTRA_APPLICATION_ID, Long.toString(uniqueId));
+ i.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
+ i.putExtra(Intent.EXTRA_SHORTCUT_NAME, title);
+ i.putExtra(Intent.EXTRA_SHORTCUT_ICON, createIcon(context, touchIcon, favicon,
+ BookmarkIconType.ICON_HOME_SHORTCUT));
+
+ // Do not allow duplicate items
+ i.putExtra("duplicate", false);
+ return i;
+ }
+
+ private static Bitmap getIconBackground(Context context, BookmarkIconType type) {
+ if (type == BookmarkIconType.ICON_HOME_SHORTCUT) {
+ // Want to create a shortcut icon on the homescreen, so the icon
+ // background is the red bookmark.
+ return BitmapFactory.decodeResource(context.getResources(),
+ R.drawable.ic_launcher_shortcut_browser_bookmark);
+ } else if (type == BookmarkIconType.ICON_INSTALLABLE_WEB_APP) {
+ // Use the web browser icon as the background for the icon for an installable
+ // web app.
+ return BitmapFactory.decodeResource(context.getResources(),
+ R.drawable.ic_launcher_browser);
+ }
+ return null;
+ }
+
+ private static void drawTouchIconToCanvas(Bitmap touchIcon, Canvas canvas, Rect iconBounds) {
+ Rect src = new Rect(0, 0, touchIcon.getWidth(), touchIcon.getHeight());
+
+ // Paint used for scaling the bitmap and drawing the rounded rect.
+ Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ paint.setFilterBitmap(true);
+ canvas.drawBitmap(touchIcon, src, iconBounds, paint);
+
+ // Construct a path from a round rect. This will allow drawing with
+ // an inverse fill so we can punch a hole using the round rect.
+ Path path = new Path();
+ path.setFillType(Path.FillType.INVERSE_WINDING);
+ RectF rect = new RectF(iconBounds);
+ rect.inset(1, 1);
+ path.addRoundRect(rect, 8f, 8f, Path.Direction.CW);
+
+ // Reuse the paint and clear the outside of the rectangle.
+ paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+ canvas.drawPath(path, paint);
+ }
+
+ private static void drawFaviconToCanvas(Bitmap favicon, Canvas canvas, Rect iconBounds,
+ float density) {
+ // Make a Paint for the white background rectangle and for
+ // filtering the favicon.
+ Paint p = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+ p.setStyle(Paint.Style.FILL_AND_STROKE);
+ p.setColor(Color.WHITE);
+
+ // Create a rectangle that is slightly wider than the favicon
+ final float iconSize = 16 * density; // 16x16 favicon
+ final float padding = 2 * density; // white padding around icon
+ final float rectSize = iconSize + 2 * padding;
+ final float x = iconBounds.exactCenterX() - (rectSize / 2);
+ // Note: Subtract 2 dip from the y position since the box is
+ // slightly higher than center. Use padding since it is already
+ // 2 * density.
+ final float y = iconBounds.exactCenterY() - (rectSize / 2) - padding;
+ RectF r = new RectF(x, y, x + rectSize, y + rectSize);
+
+ // Draw a white rounded rectangle behind the favicon
+ canvas.drawRoundRect(r, 2, 2, p);
+
+ // Draw the favicon in the same rectangle as the rounded
+ // rectangle but inset by the padding
+ // (results in a 16x16 favicon).
+ r.inset(padding, padding);
+ canvas.drawBitmap(favicon, null, r, p);
+ }
+
+};
diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java
index 5e55789..8afd3b4 100644
--- a/src/com/android/browser/BrowserActivity.java
+++ b/src/com/android/browser/BrowserActivity.java
@@ -38,13 +38,11 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
-import android.database.DatabaseUtils;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Picture;
import android.graphics.PixelFormat;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
@@ -64,14 +62,13 @@
import android.os.SystemClock;
import android.provider.Browser;
import android.provider.ContactsContract;
-import android.provider.ContactsContract.Intents.Insert;
import android.provider.Downloads;
import android.provider.MediaStore;
+import android.provider.ContactsContract.Intents.Insert;
import android.speech.RecognizerResultsIntent;
import android.text.IClipboard;
import android.text.TextUtils;
import android.text.format.DateFormat;
-import android.util.AttributeSet;
import android.util.Log;
import android.util.Patterns;
import android.view.ContextMenu;
@@ -87,6 +84,7 @@
import android.view.WindowManager;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.MenuItem.OnMenuItemClickListener;
+import android.view.accessibility.AccessibilityManager;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.DownloadListener;
@@ -104,12 +102,6 @@
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.AccountManagerFuture;
-import android.accounts.AuthenticatorException;
-import android.accounts.OperationCanceledException;
-import android.accounts.AccountManagerCallback;
import com.android.common.Search;
import com.android.common.speech.LoggingEvents;
@@ -119,11 +111,9 @@
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
-import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
-import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -131,6 +121,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -171,6 +162,8 @@
*/
private FrameLayout mBrowserFrameLayout;
+ private boolean mXLargeScreenSize;
+
@Override
public void onCreate(Bundle icicle) {
if (LOGV_ENABLED) {
@@ -186,7 +179,11 @@
BitmapFactory.setDefaultConfig(Bitmap.Config.ARGB_8888);
}
- setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
+ if (AccessibilityManager.getInstance(this).isEnabled()) {
+ setDefaultKeyMode(DEFAULT_KEYS_DISABLE);
+ } else {
+ setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
+ }
mResolver = getContentResolver();
@@ -213,14 +210,31 @@
mCustomViewContainer = (FrameLayout) mBrowserFrameLayout
.findViewById(R.id.fullscreen_custom_content);
frameLayout.addView(mBrowserFrameLayout, COVER_SCREEN_PARAMS);
- mTitleBar = new TitleBar(this);
- // mTitleBar will be always shown in the fully loaded mode
- mTitleBar.setProgress(100);
- mFakeTitleBar = new TitleBar(this);
+ mXLargeScreenSize = (getResources().getConfiguration().screenLayout
+ & Configuration.SCREENLAYOUT_SIZE_MASK)
+ == Configuration.SCREENLAYOUT_SIZE_XLARGE;
// Create the tab control and our initial tab
mTabControl = new TabControl(this);
+
+ if (mXLargeScreenSize) {
+ mTitleBar = new TitleBarXLarge(this, mTabControl);
+ LinearLayout layout = (LinearLayout) mBrowserFrameLayout.
+ findViewById(R.id.vertical_layout);
+ layout.addView(mTitleBar, 0, new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+ } else {
+ mTitleBar = new TitleBar(this);
+ // mTitleBar will be always be shown in the fully loaded mode on
+ // phone
+ mTitleBar.setProgress(100);
+ // Fake title bar is not needed in xlarge layout
+ mFakeTitleBar = new TitleBar(this);
+ }
+
+
// Open the icon database and retain all the bookmark urls for favicons
retainIconsOnStartup();
@@ -305,9 +319,7 @@
}
if (permissionOk) {
PluginManager.getInstance(BrowserActivity.this)
- .refreshPlugins(
- Intent.ACTION_PACKAGE_ADDED
- .equals(action));
+ .refreshPlugins(true);
}
}
}
@@ -358,6 +370,15 @@
attachTabToContentView(mTabControl.getCurrentTab());
}
+ // Delete old thumbnails to save space
+ File dir = mTabControl.getThumbnailDir();
+ if (dir.exists()) {
+ for (String child : dir.list()) {
+ File f = new File(dir, child);
+ f.delete();
+ }
+ }
+
// Read JavaScript flags if it exists.
String jsFlags = mSettings.getJsFlags();
if (jsFlags.trim().length() != 0) {
@@ -612,6 +633,7 @@
final ContentResolver cr = mResolver;
final String newUrl = url;
new AsyncTask<Void, Void, Void>() {
+ @Override
protected Void doInBackground(Void... unused) {
Browser.updateVisitedHistory(cr, newUrl, false);
Browser.addSearchUrl(cr, newUrl);
@@ -673,6 +695,7 @@
final ContentResolver cr = mResolver;
final String newUrl = url;
new AsyncTask<Void, Void, Void>() {
+ @Override
protected Void doInBackground(Void... unused) {
Browser.updateVisitedHistory(cr, newUrl, false);
return null;
@@ -697,17 +720,21 @@
}
/* package */ void showVoiceTitleBar(String title) {
mTitleBar.setInVoiceMode(true);
- mFakeTitleBar.setInVoiceMode(true);
-
mTitleBar.setDisplayTitle(title);
- mFakeTitleBar.setDisplayTitle(title);
+
+ if (!mXLargeScreenSize) {
+ mFakeTitleBar.setInVoiceMode(true);
+ mFakeTitleBar.setDisplayTitle(title);
+ }
}
/* package */ void revertVoiceTitleBar() {
mTitleBar.setInVoiceMode(false);
- mFakeTitleBar.setInVoiceMode(false);
-
mTitleBar.setDisplayTitle(mUrl);
- mFakeTitleBar.setDisplayTitle(mUrl);
+
+ if (!mXLargeScreenSize) {
+ mFakeTitleBar.setInVoiceMode(false);
+ mFakeTitleBar.setDisplayTitle(mUrl);
+ }
}
/* package */ static String fixUrl(String inUrl) {
// FIXME: Converting the url to lower case
@@ -826,7 +853,14 @@
return true;
}
+ private void rebuildTitleBar() {
+ if (mXLargeScreenSize) {
+ ((TitleBarXLarge) mTitleBar).rebuildLayout();
+ }
+ }
+
private void showFakeTitleBar() {
+ if (mXLargeScreenSize) return;
if (mFakeTitleBar.getParent() == null && mActiveTabsPage == null
&& !mActivityInPause) {
WebView mainView = mTabControl.getCurrentWebView();
@@ -834,6 +868,13 @@
if (mainView == null) {
return;
}
+ // Do not need to check for null, since the current tab will have
+ // at least a main WebView, or we would have returned above.
+ if (dialogIsUp()) {
+ // Do not show the fake title bar, which would cover up the
+ // find or select dialog.
+ return;
+ }
WindowManager manager
= (WindowManager) getSystemService(Context.WINDOW_SERVICE);
@@ -868,7 +909,7 @@
}
private void hideFakeTitleBar() {
- if (mFakeTitleBar.getParent() == null) return;
+ if (mXLargeScreenSize || mFakeTitleBar.getParent() == null) return;
WindowManager.LayoutParams params = (WindowManager.LayoutParams)
mFakeTitleBar.getLayoutParams();
WebView mainView = mTabControl.getCurrentWebView();
@@ -1027,6 +1068,7 @@
showHttpAuthentication(mHttpAuthHandler, null, null, title,
name, password, focusId);
}
+ rebuildTitleBar();
}
@Override
@@ -1113,12 +1155,14 @@
if (mMenu == null) {
return;
}
+ MenuItem dest = mMenu.findItem(R.id.stop_reload_menu_id);
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());
+ mMenu.findItem(R.id.reload_menu_id);
+ if (src != null) {
+ dest.setIcon(src.getIcon());
+ dest.setTitle(src.getTitle());
+ }
}
@Override
@@ -1145,7 +1189,6 @@
break;
// -- 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:
@@ -1263,6 +1306,7 @@
*/
/* package */ void removeActiveTabPage(boolean needToAttach) {
mContentView.removeView(mActiveTabsPage);
+ mTitleBar.setVisibility(View.VISIBLE);
mActiveTabsPage = null;
mMenuState = R.id.MAIN_MENU;
if (needToAttach) {
@@ -1271,6 +1315,22 @@
getTopWindow().requestFocus();
}
+ private WebView showDialog(WebDialog dialog) {
+ // Need to do something special for Tablet
+ Tab tab = mTabControl.getCurrentTab();
+ if (tab.getSubWebView() == null) {
+ // If the find or select is being performed on the main webview,
+ // remove the embedded title bar.
+ WebView mainView = tab.getWebView();
+ if (mainView != null) {
+ mainView.setEmbeddedTitleBar(null);
+ }
+ }
+ hideFakeTitleBar();
+ mMenuState = EMPTY_MENU;
+ return tab.showDialog(dialog);
+ }
+
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (!mCanChord) {
@@ -1305,6 +1365,7 @@
case R.id.active_tabs_menu_id:
mActiveTabsPage = new ActiveTabsPage(this, mTabControl);
removeTabFromContentView(mTabControl.getCurrentTab());
+ mTitleBar.setVisibility(View.GONE);
hideFakeTitleBar();
mContentView.addView(mActiveTabsPage, COVER_SCREEN_PARAMS);
mActiveTabsPage.requestFocus();
@@ -1312,14 +1373,7 @@
break;
case R.id.add_bookmark_menu_id:
- Intent i = new Intent(BrowserActivity.this,
- AddBookmarkPage.class);
- WebView w = getTopWindow();
- i.putExtra("url", w.getUrl());
- i.putExtra("title", w.getTitle());
- i.putExtra("touch_icon_url", w.getTouchIconUrl());
- i.putExtra("thumbnail", createScreenshot(w));
- startActivity(i);
+ bookmarkCurrentPage();
break;
case R.id.stop_reload_menu_id:
@@ -1364,18 +1418,26 @@
break;
case R.id.find_menu_id:
- if (null == mFindDialog) {
- mFindDialog = new FindDialog(this);
- }
- mFindDialog.setWebView(getTopWindow());
- mFindDialog.show();
- getTopWindow().setFindIsUp(true);
- mMenuState = EMPTY_MENU;
+ showFindDialog();
break;
- case R.id.select_text_id:
- getTopWindow().emulateShiftHeld();
+ case R.id.save_webarchive_menu_id:
+ if (LOGD_ENABLED) {
+ Log.d(LOGTAG, "Save as Web Archive");
+ }
+ String directory = getExternalFilesDir(null).getAbsolutePath() + File.separator;
+ getTopWindow().saveWebArchive(directory, true, new ValueCallback<String>() {
+ @Override
+ public void onReceiveValue(String value) {
+ if (value != null) {
+ Toast.makeText(BrowserActivity.this, R.string.webarchive_saved, Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(BrowserActivity.this, R.string.webarchive_failed, Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
break;
+
case R.id.page_info_menu_id:
showPageInfo(mTabControl.getCurrentTab(), false);
break;
@@ -1394,7 +1456,8 @@
currentTab.populatePickerData();
sharePage(this, currentTab.getTitle(),
currentTab.getUrl(), currentTab.getFavicon(),
- createScreenshot(currentTab.getWebView()));
+ createScreenshot(currentTab.getWebView(), getDesiredThumbnailWidth(this),
+ getDesiredThumbnailHeight(this)));
break;
case R.id.dump_nav_menu_id:
@@ -1450,8 +1513,74 @@
return true;
}
- public void closeFind() {
+ /* package */ void bookmarkCurrentPage() {
+ Intent i = new Intent(BrowserActivity.this,
+ AddBookmarkPage.class);
+ WebView w = getTopWindow();
+ i.putExtra("url", w.getUrl());
+ i.putExtra("title", w.getTitle());
+ i.putExtra("touch_icon_url", w.getTouchIconUrl());
+ i.putExtra("thumbnail", createScreenshot(w, getDesiredThumbnailWidth(this),
+ getDesiredThumbnailHeight(this)));
+ i.putExtra("url_editable", false);
+ startActivity(i);
+ }
+
+ private boolean dialogIsUp() {
+ return null != mFindDialog && mFindDialog.isVisible() ||
+ null != mSelectDialog && mSelectDialog.isVisible();
+ }
+
+ private boolean closeDialog(WebDialog dialog) {
+ if (null == dialog || !dialog.isVisible()) return false;
+ Tab currentTab = mTabControl.getCurrentTab();
+ currentTab.closeDialog(dialog);
+ dialog.dismiss();
+ return true;
+ }
+
+ /*
+ * Remove the find dialog or select dialog.
+ */
+ public void closeDialogs() {
+ if (!(closeDialog(mFindDialog) || closeDialog(mSelectDialog))) return;
+ if (!mXLargeScreenSize) {
+ // If the Find was being performed in the main WebView, replace the
+ // embedded title bar.
+ Tab currentTab = mTabControl.getCurrentTab();
+ if (currentTab.getSubWebView() == null) {
+ WebView mainView = currentTab.getWebView();
+ if (mainView != null) {
+ mainView.setEmbeddedTitleBar(mTitleBar);
+ }
+ }
+ }
mMenuState = R.id.MAIN_MENU;
+ if (mInLoad) {
+ // The title bar was hidden, because otherwise it would cover up the
+ // find or select dialog. Now that the dialog has been removed,
+ // show the fake title bar once again.
+ showFakeTitleBar();
+ }
+ }
+
+ public void showFindDialog() {
+ if (null == mFindDialog) {
+ mFindDialog = new FindDialog(this);
+ }
+ showDialog(mFindDialog).setFindIsUp(true);
+ }
+
+ public void setFindDialogText(String text) {
+ mFindDialog.setText(text);
+ }
+
+ public void showSelectDialog() {
+ if (null == mSelectDialog) {
+ mSelectDialog = new SelectDialog(this);
+ }
+ showDialog(mSelectDialog).setUpSelect();
+ mSelectDialog.hideSoftInput();
}
@Override
@@ -1492,11 +1621,11 @@
final MenuItem home = menu.findItem(R.id.homepage_menu_id);
home.setEnabled(!isHome);
- menu.findItem(R.id.forward_menu_id)
- .setEnabled(canGoForward);
+ final MenuItem forward = menu.findItem(R.id.forward_menu_id);
+ forward.setEnabled(canGoForward);
- menu.findItem(R.id.new_tab_menu_id).setEnabled(
- mTabControl.canCreateNewTab());
+ final MenuItem newtab = menu.findItem(R.id.new_tab_menu_id);
+ newtab.setEnabled(mTabControl.canCreateNewTab());
// decide whether to show the share link option
PackageManager pm = getPackageManager();
@@ -1524,7 +1653,7 @@
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
- if (v instanceof TitleBar) {
+ if (v instanceof TitleBarBase) {
return;
}
WebView webview = (WebView) v;
@@ -1551,7 +1680,7 @@
inflater.inflate(R.menu.browsercontext, menu);
// Show the correct menu group
- String extra = result.getExtra();
+ final String extra = result.getExtra();
menu.setGroupVisible(R.id.PHONE_MENU,
type == WebView.HitTestResult.PHONE_TYPE);
menu.setGroupVisible(R.id.EMAIL_MENU,
@@ -1608,8 +1737,23 @@
titleView.setText(extra);
menu.setHeaderView(titleView);
// decide whether to show the open link in new tab option
- menu.findItem(R.id.open_newtab_context_menu_id).setVisible(
- mTabControl.canCreateNewTab());
+ boolean showNewTab = mTabControl.canCreateNewTab();
+ MenuItem newTabItem
+ = menu.findItem(R.id.open_newtab_context_menu_id);
+ newTabItem.setVisible(showNewTab);
+ if (showNewTab) {
+ newTabItem.setOnMenuItemClickListener(
+ new MenuItem.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ final Tab parent = mTabControl.getCurrentTab();
+ final Tab newTab = openTab(extra);
+ if (newTab != parent) {
+ parent.addChildTab(newTab);
+ }
+ return true;
+ }
+ });
+ }
menu.findItem(R.id.bookmark_context_menu_id).setVisible(
Bookmarks.urlHasAcceptableScheme(extra));
PackageManager pm = getPackageManager();
@@ -1660,8 +1804,10 @@
ViewGroup.LayoutParams.WRAP_CONTENT));
}
- WebView view = t.getWebView();
- view.setEmbeddedTitleBar(mTitleBar);
+ if (!mXLargeScreenSize){
+ WebView view = t.getWebView();
+ view.setEmbeddedTitleBar(mTitleBar);
+ }
if (t.isInVoiceSearchMode()) {
showVoiceTitleBar(t.getVoiceDisplayTitle());
} else {
@@ -1687,9 +1833,11 @@
mErrorConsoleContainer.removeView(errorConsole);
}
- WebView view = t.getWebView();
- if (view != null) {
- view.setEmbeddedTitleBar(null);
+ if (!mXLargeScreenSize) {
+ WebView view = t.getWebView();
+ if (view != null) {
+ view.setEmbeddedTitleBar(null);
+ }
}
}
@@ -1815,6 +1963,7 @@
return true;
}
+ @Override
public void run() {
Drawable oldWallpaper = BrowserActivity.this.getWallpaper();
try {
@@ -1919,7 +2068,9 @@
// If we are in voice search mode, the title has already been set.
if (mTabControl.getCurrentTab().isInVoiceSearchMode()) return;
mTitleBar.setDisplayTitle(url);
- mFakeTitleBar.setDisplayTitle(url);
+ if (!mXLargeScreenSize) {
+ mFakeTitleBar.setDisplayTitle(url);
+ }
}
/**
@@ -1962,7 +2113,9 @@
// Set the favicon in the title bar.
void setFavicon(Bitmap icon) {
mTitleBar.setFavicon(icon);
- mFakeTitleBar.setFavicon(icon);
+ if (!mXLargeScreenSize) {
+ mFakeTitleBar.setFavicon(icon);
+ }
}
/**
@@ -1978,6 +2131,7 @@
}
mTabControl.setCurrentTab(mTabControl.getTab(currentIndex));
resetTitleIconAndProgress();
+ updateLockIconToLatest();
}
/* package */ void goBackOnePageOrQuit() {
@@ -2169,9 +2323,12 @@
static final int UPDATE_BOOKMARK_THUMBNAIL = 108;
+ private static final int TOUCH_ICON_DOWNLOADED = 109;
+
// Private handler for handling javascript and saving passwords
private Handler mHandler = new Handler() {
+ @Override
public void handleMessage(Message msg) {
switch (msg.what) {
case FOCUS_NODE_HREF:
@@ -2192,13 +2349,6 @@
case R.id.view_image_context_menu_id:
loadUrlFromContext(getTopWindow(), url);
break;
- case R.id.open_newtab_context_menu_id:
- final Tab parent = mTabControl.getCurrentTab();
- final Tab newTab = openTab(url);
- if (newTab != parent) {
- parent.addChildTab(newTab);
- }
- break;
case R.id.bookmark_context_menu_id:
Intent intent = new Intent(BrowserActivity.this,
AddBookmarkPage.class);
@@ -2207,41 +2357,8 @@
startActivity(intent);
break;
case R.id.share_link_context_menu_id:
- // See if this site has been visited before
- StringBuilder sb = new StringBuilder(
- Browser.BookmarkColumns.URL + " = ");
- DatabaseUtils.appendEscapedSQLString(sb, url);
- Cursor c = mResolver.query(Browser.BOOKMARKS_URI,
- Browser.HISTORY_PROJECTION,
- sb.toString(),
- null,
+ sharePage(BrowserActivity.this, title, url, null,
null);
- if (c.moveToFirst()) {
- // The site has been visited before, so grab the
- // info from the database.
- Bitmap favicon = null;
- Bitmap thumbnail = null;
- String linkTitle = c.getString(Browser.
- HISTORY_PROJECTION_TITLE_INDEX);
- byte[] data = c.getBlob(Browser.
- HISTORY_PROJECTION_FAVICON_INDEX);
- if (data != null) {
- favicon = BitmapFactory.decodeByteArray(
- data, 0, data.length);
- }
- data = c.getBlob(Browser.
- HISTORY_PROJECTION_THUMBNAIL_INDEX);
- if (data != null) {
- thumbnail = BitmapFactory.decodeByteArray(
- data, 0, data.length);
- }
- sharePage(BrowserActivity.this,
- linkTitle, url, favicon, thumbnail);
- } else {
- Browser.sendString(BrowserActivity.this, url,
- getString(
- R.string.choosertitle_sharevia));
- }
break;
case R.id.copy_link_context_menu_id:
copy(url);
@@ -2278,6 +2395,14 @@
updateScreenshot(view);
}
break;
+
+ case TOUCH_ICON_DOWNLOADED:
+ Bundle b = msg.getData();
+ showSaveToHomescreenDialog(b.getString("url"),
+ b.getString("title"),
+ (Bitmap) b.getParcelable("touchIcon"),
+ (Bitmap) b.getParcelable("favicon"));
+ break;
}
}
};
@@ -2318,7 +2443,8 @@
// draw, but the API for that (WebViewCore.pictureReady()) is not
// currently accessible here.
- final Bitmap bm = createScreenshot(view);
+ final Bitmap bm = createScreenshot(view, getDesiredThumbnailWidth(this),
+ getDesiredThumbnailHeight(this));
if (bm == null) {
return;
}
@@ -2394,13 +2520,12 @@
return THUMBNAIL_HEIGHT;
}
- private Bitmap createScreenshot(WebView view) {
+ private Bitmap createScreenshot(WebView view, int width, int height) {
Picture thumbnail = view.capturePicture();
if (thumbnail == null) {
return null;
}
- Bitmap bm = Bitmap.createBitmap(getDesiredThumbnailWidth(this),
- getDesiredThumbnailHeight(this), Bitmap.Config.RGB_565);
+ Bitmap bm = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bm);
// May need to tweak these values to determine what is the
// best scale factor
@@ -2409,8 +2534,7 @@
float scaleFactorX = 1.0f;
float scaleFactorY = 1.0f;
if (thumbnailWidth > 0) {
- scaleFactorX = (float) getDesiredThumbnailWidth(this) /
- (float)thumbnailWidth;
+ scaleFactorX = (float) width / (float)thumbnailWidth;
} else {
return null;
}
@@ -2420,8 +2544,7 @@
// If the device is in landscape and the page is shorter
// than the height of the view, stretch the thumbnail to fill the
// space.
- scaleFactorY = (float) getDesiredThumbnailHeight(this) /
- (float)thumbnailHeight;
+ scaleFactorY = (float) height / (float)thumbnailHeight;
} else {
// In the portrait case, this looks nice.
scaleFactorY = scaleFactorX;
@@ -2462,7 +2585,7 @@
onProgressChanged(view, INITIAL_PROGRESS);
mDidStopLoad = false;
if (!mIsNetworkUp) createAndShowNetworkDialog();
-
+ closeDialogs();
if (mSettings.isTracing()) {
String host;
try {
@@ -2560,6 +2683,18 @@
}
}
+ private void closeEmptyChildTab() {
+ Tab current = mTabControl.getCurrentTab();
+ if (current != null
+ && current.getWebView().copyBackForwardList().getSize() == 0) {
+ Tab parent = current.getParentTab();
+ if (parent != null) {
+ switchToTab(mTabControl.getTabIndex(parent));
+ closeTab(current);
+ }
+ }
+ }
+
boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith(SCHEME_WTAI)) {
// wtai://wp/mc;number
@@ -2569,6 +2704,11 @@
Uri.parse(WebView.SCHEME_TEL +
url.substring(SCHEME_WTAI_MC.length())));
startActivity(intent);
+ // before leaving BrowserActivity, close the empty child tab.
+ // If a new tab is created through JavaScript open to load this
+ // url, we would like to close it as we will load this url in a
+ // different Activity.
+ closeEmptyChildTab();
return true;
}
// wtai://wp/sd;dtmf
@@ -2610,6 +2750,11 @@
.parse("market://search?q=pname:" + packagename));
intent.addCategory(Intent.CATEGORY_BROWSABLE);
startActivity(intent);
+ // before leaving BrowserActivity, close the empty child tab.
+ // If a new tab is created through JavaScript open to load this
+ // url, we would like to close it as we will load this url in a
+ // different Activity.
+ closeEmptyChildTab();
return true;
} else {
return false;
@@ -2622,6 +2767,11 @@
intent.setComponent(null);
try {
if (startActivityIfNeeded(intent, -1)) {
+ // before leaving BrowserActivity, close the empty child tab.
+ // If a new tab is created through JavaScript open to load this
+ // url, we would like to close it as we will load this url in a
+ // different Activity.
+ closeEmptyChildTab();
return true;
}
} catch (ActivityNotFoundException ex) {
@@ -2642,7 +2792,14 @@
// -------------------------------------------------------------------------
void onProgressChanged(WebView view, int newProgress) {
- mFakeTitleBar.setProgress(newProgress);
+ if (mXLargeScreenSize) {
+ mTitleBar.setProgress(newProgress);
+ } else {
+ // On the phone, the fake title bar will always cover up the
+ // regular title bar (or the regular one is offscreen), so only the
+ // fake title bar needs to change its progress
+ mFakeTitleBar.setProgress(newProgress);
+ }
if (newProgress == 100) {
// onProgressChanged() may continue to be called after the main
@@ -2743,15 +2900,138 @@
* The Object used to inform the WebView of the file to upload.
*/
private ValueCallback<Uri> mUploadMessage;
+ private String mCameraFilePath;
- void openFileChooser(ValueCallback<Uri> uploadMsg) {
- if (mUploadMessage != null) return;
+ void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
+
+ final String imageMimeType = "image/*";
+ final String videoMimeType = "video/*";
+ final String audioMimeType = "audio/*";
+ final String mediaSourceKey = "capture";
+ final String mediaSourceValueCamera = "camera";
+ final String mediaSourceValueFileSystem = "filesystem";
+ final String mediaSourceValueCamcorder = "camcorder";
+ final String mediaSourceValueMicrophone = "microphone";
+
+ // media source can be 'filesystem' or 'camera' or 'camcorder' or 'microphone'.
+ String mediaSource = "";
+
+ // We add the camera intent if there was no accept type (or '*/*' or 'image/*').
+ boolean addCameraIntent = true;
+ // We add the camcorder intent if there was no accept type (or '*/*' or 'video/*').
+ boolean addCamcorderIntent = true;
+
+ if (mUploadMessage != null) {
+ // Already a file picker operation in progress.
+ return;
+ }
+
mUploadMessage = uploadMsg;
+
+ // Parse the accept type.
+ String params[] = acceptType.split(";");
+ String mimeType = params[0];
+
+ for (String p : params) {
+ String[] keyValue = p.split("=");
+ if (keyValue.length == 2) {
+ // Process key=value parameters.
+ if (mediaSourceKey.equals(keyValue[0])) {
+ mediaSource = keyValue[1];
+ }
+ }
+ }
+
+ // This intent will display the standard OPENABLE file picker.
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
- i.setType("*/*");
- BrowserActivity.this.startActivityForResult(Intent.createChooser(i,
- getString(R.string.choose_upload)), FILE_SELECTED);
+
+ // Create an intent to add to the standard file picker that will
+ // capture an image from the camera. We'll combine this intent with
+ // the standard OPENABLE picker unless the web developer specifically
+ // requested the camera or gallery be opened by passing a parameter
+ // in the accept type.
+ Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+ File externalDataDir = Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DCIM);
+ File cameraDataDir = new File(externalDataDir.getAbsolutePath() +
+ File.separator + "browser-photos");
+ cameraDataDir.mkdirs();
+ mCameraFilePath = cameraDataDir.getAbsolutePath() + File.separator +
+ System.currentTimeMillis() + ".jpg";
+ cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(mCameraFilePath)));
+
+ Intent camcorderIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
+
+ Intent soundRecIntent = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
+
+ if (mimeType.equals(imageMimeType)) {
+ i.setType(imageMimeType);
+ addCamcorderIntent = false;
+ if (mediaSource.equals(mediaSourceValueCamera)) {
+ // Specified 'image/*' and requested the camera, so go ahead and launch the camera
+ // directly.
+ BrowserActivity.this.startActivityForResult(cameraIntent, FILE_SELECTED);
+ return;
+ } else if (mediaSource.equals(mediaSourceValueFileSystem)) {
+ // Specified filesytem as the source, so don't want to consider the camera.
+ addCameraIntent = false;
+ }
+ } else if (mimeType.equals(videoMimeType)) {
+ i.setType(videoMimeType);
+ addCameraIntent = false;
+ // The camcorder saves it's own file and returns it to us in the intent, so
+ // we don't need to generate one here.
+ mCameraFilePath = null;
+
+ if (mediaSource.equals(mediaSourceValueCamcorder)) {
+ // Specified 'video/*' and requested the camcorder, so go ahead and launch the
+ // camcorder directly.
+ BrowserActivity.this.startActivityForResult(camcorderIntent, FILE_SELECTED);
+ return;
+ } else if (mediaSource.equals(mediaSourceValueFileSystem)) {
+ // Specified filesystem as the source, so don't want to consider the camcorder.
+ addCamcorderIntent = false;
+ }
+ } else if (mimeType.equals(audioMimeType)) {
+ i.setType(audioMimeType);
+ addCameraIntent = false;
+ addCamcorderIntent = false;
+ if (mediaSource.equals(mediaSourceValueMicrophone)) {
+ // Specified 'audio/*' and requested microphone, so go ahead and launch the sound
+ // recorder.
+ BrowserActivity.this.startActivityForResult(soundRecIntent, FILE_SELECTED);
+ return;
+ }
+ // On a default system, there is no single option to open an audio "gallery". Both the
+ // sound recorder and music browser respond to the OPENABLE/audio/* intent unlike the
+ // image/* and video/* OPENABLE intents where the image / video gallery are the only
+ // respondants (and so the user is not prompted by default).
+ } else {
+ i.setType("*/*");
+ }
+
+ // Combine the chooser and the extra choices (like camera or camcorder)
+ Intent chooser = new Intent(Intent.ACTION_CHOOSER);
+ chooser.putExtra(Intent.EXTRA_INTENT, i);
+
+ Vector<Intent> extraInitialIntents = new Vector<Intent>(0);
+
+ if (addCameraIntent) {
+ extraInitialIntents.add(cameraIntent);
+ }
+
+ if (addCamcorderIntent) {
+ extraInitialIntents.add(camcorderIntent);
+ }
+
+ if (extraInitialIntents.size() > 0) {
+ Intent[] extraIntents = new Intent[extraInitialIntents.size()];
+ chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, extraInitialIntents.toArray(extraIntents));
+ }
+
+ chooser.putExtra(Intent.EXTRA_TITLE, getString(R.string.choose_upload));
+ BrowserActivity.this.startActivityForResult(chooser, FILE_SELECTED);
}
// -------------------------------------------------------------------------
@@ -2938,7 +3218,10 @@
* Update the lock icon to correspond to our latest state.
*/
private void updateLockIconToLatest() {
- updateLockIconImage(mTabControl.getCurrentTab().getLockIconType());
+ Tab t = mTabControl.getCurrentTab();
+ if (t != null) {
+ updateLockIconImage(t.getLockIconType());
+ }
}
/**
@@ -2952,7 +3235,9 @@
d = mMixLockIcon;
}
mTitleBar.setLock(d);
- mFakeTitleBar.setLock(d);
+ if (!mXLargeScreenSize) {
+ mFakeTitleBar.setLock(d);
+ }
}
/**
@@ -3468,8 +3753,25 @@
if (null == mUploadMessage) break;
Uri result = intent == null || resultCode != RESULT_OK ? null
: intent.getData();
+
+ // As we ask the camera to save the result of the user taking
+ // a picture, the camera application does not return anything other
+ // than RESULT_OK. So we need to check whether the file we expected
+ // was written to disk in the in the case that we
+ // did not get an intent returned but did get a RESULT_OK. If it was,
+ // we assume that this result has came back from the camera.
+ if (result == null && intent == null && resultCode == RESULT_OK) {
+ File cameraFile = new File(mCameraFilePath);
+ if (cameraFile.exists()) {
+ result = Uri.fromFile(cameraFile);
+ // Broadcast to the media scanner that we have a new photo
+ // so it will be added into the gallery for the user.
+ sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, result));
+ }
+ }
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
+ mCameraFilePath = null;
break;
default:
break;
@@ -3490,6 +3792,47 @@
}
+ /* package*/ void promptAddOrInstallBookmark() {
+ final Tab current = mTabControl.getCurrentTab();
+ Resources resources = getResources();
+ CharSequence[] choices = {
+ resources.getString(R.string.save_to_bookmarks),
+ resources.getString(R.string.create_shortcut_bookmark)
+ };
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.add_new_bookmark);
+ builder.setItems(choices, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int item) {
+ if (item == 0) {
+ bookmarkCurrentPage();
+ } else if (item == 1) {
+ current.populatePickerData();
+ String touchIconUrl = mTabControl.getCurrentWebView().getTouchIconUrl();
+ if (touchIconUrl != null) {
+ // Download the touch icon for this site then save it to the
+ // homescreen.
+ Bundle b = new Bundle();
+ b.putString("url", current.getUrl());
+ b.putString("title", current.getTitle());
+ b.putParcelable("favicon", current.getFavicon());
+ Message msg = mHandler.obtainMessage(TOUCH_ICON_DOWNLOADED);
+ msg.setData(b);
+ new DownloadTouchIcon(BrowserActivity.this, msg,
+ mTabControl.getCurrentWebView().getSettings()
+ .getUserAgentString()).execute(touchIconUrl);
+ } else {
+ // add to homescreen, can do it immediately as there is no touch
+ // icon.
+ showSaveToHomescreenDialog(current.getUrl(), current.getTitle(),
+ null, current.getFavicon());
+ }
+ }
+ }
+ });
+ builder.create().show();
+ }
+
/**
* Open the Go page.
* @param startWithHistory If true, open starting on the history tab.
@@ -3504,7 +3847,8 @@
CombinedBookmarkHistoryActivity.class);
String title = current.getTitle();
String url = current.getUrl();
- Bitmap thumbnail = createScreenshot(current);
+ Bitmap thumbnail = createScreenshot(current, getDesiredThumbnailWidth(this),
+ getDesiredThumbnailHeight(this));
// Just in case the user opens bookmarks before a page finishes loading
// so the current history item, and therefore the page, is null.
@@ -3532,6 +3876,33 @@
startActivityForResult(intent, COMBO_PAGE);
}
+ private void showSaveToHomescreenDialog(String url, String title, Bitmap touchIcon,
+ Bitmap favicon) {
+ Intent intent = new Intent(this, SaveToHomescreenDialog.class);
+
+ // Just in case the user tries to save 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("favicon", favicon);
+ intent.putExtra("touchIcon", touchIcon);
+ startActivity(intent);
+ }
+
+
// Called when loading from context menu or LOAD_URL message
private void loadUrlFromContext(WebView view, String url) {
// In case the user enters nothing.
@@ -3726,6 +4097,7 @@
private void getInstalledPackages() {
AsyncTask<Void, Void, Set<String> > task =
new AsyncTask<Void, Void, Set<String> >() {
+ @Override
protected Set<String> doInBackground(Void... unused) {
Set<String> installedPackages = new HashSet<String>();
PackageManager pm = BrowserActivity.this.getPackageManager();
@@ -3742,6 +4114,7 @@
}
// Executes on the UI thread
+ @Override
protected void onPostExecute(Set<String> installedPackages) {
addPackageNames(installedPackages);
}
@@ -3770,6 +4143,7 @@
private Menu mMenu;
private FindDialog mFindDialog;
+ private SelectDialog mSelectDialog;
// Used to prevent chording to result in firing two shortcuts immediately
// one after another. Fixes bug 1211714.
boolean mCanChord;
@@ -3883,7 +4257,7 @@
private Toast mStopToast;
- private TitleBar mTitleBar;
+ private TitleBarBase mTitleBar;
private LinearLayout mErrorConsoleContainer = null;
private boolean mShouldShowErrorConsole = false;
diff --git a/src/com/android/browser/BrowserBookmarksPage.java b/src/com/android/browser/BrowserBookmarksPage.java
index 7560c78..6af14e8 100644
--- a/src/com/android/browser/BrowserBookmarksPage.java
+++ b/src/com/android/browser/BrowserBookmarksPage.java
@@ -23,16 +23,6 @@
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
@@ -53,6 +43,7 @@
import android.widget.GridView;
import android.widget.ListView;
import android.widget.Toast;
+import android.widget.AdapterView.OnItemClickListener;
/*package*/ enum BookmarkViewMode { NONE, GRID, LIST }
/**
@@ -74,10 +65,6 @@
private boolean mMostVisited;
private View mEmptyView;
private int mIconSize;
- // XXX: There is no public string defining this intent so if Home changes
- // the value, we have to update this string.
- private static final String INSTALL_SHORTCUT =
- "com.android.launcher.action.INSTALL_SHORTCUT";
private final static String LOGTAG = "browser";
private final static String PREF_BOOKMARK_VIEW_MODE = "pref_bookmark_view_mode";
@@ -108,9 +95,7 @@
editBookmark(i.position);
break;
case R.id.shortcut_context_menu_id:
- final Intent send = createShortcutIntent(i.position);
- send.setAction(INSTALL_SHORTCUT);
- sendBroadcast(send);
+ sendBroadcast(createShortcutIntent(i.position));
break;
case R.id.delete_context_menu_id:
if (mMostVisited) {
@@ -275,13 +260,14 @@
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... unused) {
- BrowserBookmarksAdapter adapter = new BrowserBookmarksAdapter(
- BrowserBookmarksPage.this,
- url,
- title,
- thumbnail,
- createShortcut,
- mostVisited);
+ BrowserBookmarksAdapter adapter =
+ new BrowserBookmarksAdapter(
+ BrowserBookmarksPage.this,
+ url,
+ title,
+ thumbnail,
+ createShortcut,
+ mostVisited);
mHandler.obtainMessage(ADAPTER_CREATED, adapter).sendToTarget();
return null;
}
@@ -358,7 +344,7 @@
}
listView.setDrawSelectorOnTop(false);
listView.setVerticalScrollBarEnabled(true);
- listView.setOnItemClickListener(mListener);
+ listView.setOnItemClickListener(mListListener);
if (mMostVisited) {
listView.setEmptyView(mEmptyView);
}
@@ -422,7 +408,7 @@
}
};
- private AdapterView.OnItemClickListener mListener = new AdapterView.OnItemClickListener() {
+ private OnItemClickListener mListener = new 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
@@ -438,8 +424,29 @@
loadUrl(position);
}
} else {
- final Intent intent = createShortcutIntent(position);
- setResultToParent(RESULT_OK, intent);
+ setResultToParent(RESULT_OK, createShortcutIntent(position));
+ finish();
+ }
+ }
+ };
+
+ private OnItemClickListener mListListener = new 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(LOGTAG, "item clicked when dismissing");
+ return;
+ }
+ if (!mCreateShortcut) {
+ if (0 == position && !mMostVisited) {
+ // XXX: Work-around for a framework issue.
+ mHandler.sendEmptyMessage(SAVE_CURRENT_PAGE);
+ } else {
+ loadUrl(position);
+ }
+ } else {
+ setResultToParent(RESULT_OK, createShortcutIntent(position));
finish();
}
}
@@ -449,99 +456,8 @@
String url = getUrl(position);
String title = getBookmarkTitle(position);
Bitmap touchIcon = getTouchIcon(position);
-
- final Intent i = new Intent();
- final Intent shortcutIntent = new Intent(Intent.ACTION_VIEW,
- Uri.parse(url));
- long urlHash = url.hashCode();
- long uniqueId = (urlHash << 32) | shortcutIntent.hashCode();
- shortcutIntent.putExtra(Browser.EXTRA_APPLICATION_ID,
- Long.toString(uniqueId));
- i.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
- i.putExtra(Intent.EXTRA_SHORTCUT_NAME, title);
- // Use the apple-touch-icon if available
- if (touchIcon != null) {
- // Make a copy so we can modify the pixels. We can't use
- // createScaledBitmap or copy since they will preserve the config
- // and lose the ability to add alpha.
- Bitmap bm = Bitmap.createBitmap(mIconSize, mIconSize,
- Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bm);
- Rect src = new Rect(0, 0, touchIcon.getWidth(),
- touchIcon.getHeight());
- Rect dest = new Rect(0, 0, bm.getWidth(), bm.getHeight());
-
- // Paint used for scaling the bitmap and drawing the rounded rect.
- Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
- paint.setFilterBitmap(true);
- canvas.drawBitmap(touchIcon, src, dest, paint);
-
- // Construct a path from a round rect. This will allow drawing with
- // an inverse fill so we can punch a hole using the round rect.
- Path path = new Path();
- path.setFillType(Path.FillType.INVERSE_WINDING);
- RectF rect = new RectF(0, 0, bm.getWidth(), bm.getHeight());
- rect.inset(1, 1);
- path.addRoundRect(rect, 8f, 8f, Path.Direction.CW);
-
- // Reuse the paint and clear the outside of the rectangle.
- paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
- canvas.drawPath(path, paint);
-
- i.putExtra(Intent.EXTRA_SHORTCUT_ICON, bm);
- } else {
- Bitmap favicon = getFavicon(position);
- if (favicon == null) {
- i.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
- Intent.ShortcutIconResource.fromContext(
- BrowserBookmarksPage.this,
- R.drawable.ic_launcher_shortcut_browser_bookmark));
- } else {
- Bitmap icon = BitmapFactory.decodeResource(getResources(),
- R.drawable.ic_launcher_shortcut_browser_bookmark_icon);
-
- // Make a copy of the regular icon so we can modify the pixels.
- Bitmap copy = icon.copy(Bitmap.Config.ARGB_8888, true);
- Canvas canvas = new Canvas(copy);
-
- // Make a Paint for the white background rectangle and for
- // filtering the favicon.
- Paint p = new Paint(Paint.ANTI_ALIAS_FLAG
- | Paint.FILTER_BITMAP_FLAG);
- p.setStyle(Paint.Style.FILL_AND_STROKE);
- p.setColor(Color.WHITE);
-
- final float density =
- getResources().getDisplayMetrics().density;
- // Create a rectangle that is slightly wider than the favicon
- final float iconSize = 16 * density; // 16x16 favicon
- final float padding = 2 * density; // white padding around icon
- final float rectSize = iconSize + 2 * padding;
-
- final Rect iconBounds =
- new Rect(0, 0, icon.getWidth(), icon.getHeight());
- final float x = iconBounds.exactCenterX() - (rectSize / 2);
- // Note: Subtract 2 dip from the y position since the box is
- // slightly higher than center. Use padding since it is already
- // 2 * density.
- final float y = iconBounds.exactCenterY() - (rectSize / 2)
- - padding;
- RectF r = new RectF(x, y, x + rectSize, y + rectSize);
-
- // Draw a white rounded rectangle behind the favicon
- canvas.drawRoundRect(r, 2, 2, p);
-
- // Draw the favicon in the same rectangle as the rounded
- // rectangle but inset by the padding
- // (results in a 16x16 favicon).
- r.inset(padding, padding);
- canvas.drawBitmap(favicon, null, r, p);
- i.putExtra(Intent.EXTRA_SHORTCUT_ICON, copy);
- }
- }
- // Do not allow duplicate items
- i.putExtra("duplicate", false);
- return i;
+ Bitmap favicon = getFavicon(position);
+ return BookmarkUtils.createAddToHomeIntent(this, url, title, touchIcon, favicon);
}
private void saveCurrentPage() {
@@ -756,4 +672,5 @@
resultCode, data);
}
}
+
}
diff --git a/src/com/android/browser/BrowserDownloadAdapter.java b/src/com/android/browser/BrowserDownloadAdapter.java
index 0f8f721..f22c9fe 100644
--- a/src/com/android/browser/BrowserDownloadAdapter.java
+++ b/src/com/android/browser/BrowserDownloadAdapter.java
@@ -26,6 +26,7 @@
import android.drm.mobile1.DrmRawContent;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Handler;
import android.provider.Downloads;
import android.text.format.Formatter;
import android.view.LayoutInflater;
@@ -55,8 +56,9 @@
private int mMimetypeColumnId;
private int mDateColumnId;
- public BrowserDownloadAdapter(Context context, Cursor c, int index) {
- super(context, c, index);
+ public BrowserDownloadAdapter(Context context, Cursor c, int index,
+ Handler handler) {
+ super(context, c, index, handler);
mTitleColumnId = c.getColumnIndexOrThrow(Downloads.Impl.COLUMN_TITLE);
mDescColumnId = c.getColumnIndexOrThrow(Downloads.Impl.COLUMN_DESCRIPTION);
mStatusColumnId = c.getColumnIndexOrThrow(Downloads.Impl.COLUMN_STATUS);
diff --git a/src/com/android/browser/BrowserDownloadPage.java b/src/com/android/browser/BrowserDownloadPage.java
index 18faf8b..bbf1191 100644
--- a/src/com/android/browser/BrowserDownloadPage.java
+++ b/src/com/android/browser/BrowserDownloadPage.java
@@ -63,6 +63,7 @@
// Only meaningful while a ContentObserver is registered. The ContextMenu
// will be reopened on this View.
private View mSelectedView;
+ private Handler mHandler;
private final static String LOGTAG = "BrowserDownloadPage";
@Override
@@ -85,7 +86,7 @@
Downloads.Impl._DATA,
Downloads.Impl.COLUMN_MIME_TYPE},
null, Downloads.Impl.COLUMN_LAST_MODIFICATION + " DESC");
-
+ mHandler = new Handler();
// only attach everything to the listbox if we can access
// the download database. Otherwise, just show it empty
if (mDownloadCursor != null) {
@@ -99,7 +100,7 @@
// Create a list "controller" for the data
mDownloadAdapter = new BrowserDownloadAdapter(this,
mDownloadCursor, mDownloadCursor.getColumnIndexOrThrow(
- Downloads.Impl.COLUMN_LAST_MODIFICATION));
+ Downloads.Impl.COLUMN_LAST_MODIFICATION), mHandler);
setListAdapter(mDownloadAdapter);
mListView.setOnCreateContextMenuListener(this);
@@ -241,8 +242,8 @@
*/
private class ChangeObserver extends ContentObserver {
private final Uri mTrack;
- public ChangeObserver(Uri track) {
- super(new Handler());
+ public ChangeObserver(Uri track, Handler handler) {
+ super(handler);
mTrack = track;
}
@@ -313,7 +314,7 @@
getContentResolver().unregisterContentObserver(
mContentObserver);
}
- mContentObserver = new ChangeObserver(track);
+ mContentObserver = new ChangeObserver(track, mHandler);
mSelectedView = v;
getContentResolver().registerContentObserver(track, false,
mContentObserver);
diff --git a/src/com/android/browser/BrowserHistoryPage.java b/src/com/android/browser/BrowserHistoryPage.java
index 23080f8..0281087 100644
--- a/src/com/android/browser/BrowserHistoryPage.java
+++ b/src/com/android/browser/BrowserHistoryPage.java
@@ -25,7 +25,10 @@
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
import android.os.ServiceManager;
import android.provider.Browser;
import android.text.IClipboard;
@@ -92,47 +95,75 @@
}
}
+ private static final int ADAPTER_CREATED = 1000;
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case ADAPTER_CREATED:
+ mAdapter = (HistoryAdapter) msg.obj;
+ setListAdapter(mAdapter);
+ final ExpandableListView list = getExpandableListView();
+ // Add an empty view late, so it does not claim an empty
+ // history before the adapter is present
+ View v = new ViewStub(BrowserHistoryPage.this,
+ R.layout.empty_history);
+ addContentView(v, new LayoutParams(
+ LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT));
+ list.setEmptyView(v);
+ list.setOnCreateContextMenuListener(
+ BrowserHistoryPage.this);
+ // Do not post the runnable if there is nothing in the list.
+ if (list.getExpandableListAdapter().getGroupCount() > 0) {
+ list.post(new Runnable() {
+ public void run() {
+ // In case the history gets cleared before this
+ // event happens
+ if (list.getExpandableListAdapter()
+ .getGroupCount() > 0) {
+ list.expandGroup(0);
+ }
+ }
+ });
+ }
+ break;
+ }
+ }
+ };
+
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setTitle(R.string.browser_history);
- final String whereClause = Browser.BookmarkColumns.VISITS + " > 0"
- // In AddBookmarkPage, where we save new bookmarks, we add
- // three visits to newly created bookmarks, so that
- // bookmarks that have not been visited will show up in the
- // most visited, and higher in the goto search box.
- // However, this puts the site in the history, unless we
- // ignore sites with a DATE of 0, which the next line does.
- + " AND " + Browser.BookmarkColumns.DATE + " > 0";
- final String orderBy = Browser.BookmarkColumns.DATE + " DESC";
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... unused) {
+ final String whereClause = Browser.BookmarkColumns.VISITS
+ + " > 0"
+ // In AddBookmarkPage, where we save new bookmarks, we
+ // add three visits to newly created bookmarks, so that
+ // bookmarks that have not been visited will show up in
+ // the most visited, and higher in the goto search box.
+ // However, this puts the site in the history, unless
+ // we ignore sites with a DATE of 0, which the next
+ // line does.
+ + " AND " + Browser.BookmarkColumns.DATE + " > 0";
+ final String orderBy = Browser.BookmarkColumns.DATE + " DESC";
- Cursor cursor = managedQuery(
- Browser.BOOKMARKS_URI,
- Browser.HISTORY_PROJECTION,
- whereClause, null, orderBy);
+ Cursor cursor = managedQuery(
+ Browser.BOOKMARKS_URI,
+ Browser.HISTORY_PROJECTION,
+ whereClause, null, orderBy);
- mAdapter = new HistoryAdapter(this, cursor,
- Browser.HISTORY_PROJECTION_DATE_INDEX);
- setListAdapter(mAdapter);
- final ExpandableListView list = getExpandableListView();
- list.setOnCreateContextMenuListener(this);
- View v = new ViewStub(this, R.layout.empty_history);
- addContentView(v, new LayoutParams(LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT));
- list.setEmptyView(v);
- // Do not post the runnable if there is nothing in the list.
- if (list.getExpandableListAdapter().getGroupCount() > 0) {
- list.post(new Runnable() {
- public void run() {
- // In case the history gets cleared before this event
- // happens.
- if (list.getExpandableListAdapter().getGroupCount() > 0) {
- list.expandGroup(0);
- }
- }
- });
- }
+ HistoryAdapter adapter = new HistoryAdapter(
+ BrowserHistoryPage.this, cursor,
+ Browser.HISTORY_PROJECTION_DATE_INDEX, mHandler);
+ mHandler.obtainMessage(ADAPTER_CREATED, adapter).sendToTarget();
+ return null;
+ }
+ }.execute();
mDisableNewWindow = getIntent().getBooleanExtra("disable_new_window",
false);
@@ -153,6 +184,7 @@
@Override
protected void onDestroy() {
+ mHandler.removeCallbacksAndMessages(null);
super.onDestroy();
CombinedBookmarkHistoryActivity.getIconListenerSet()
.removeListener(mIconReceiver);
@@ -181,7 +213,7 @@
// CombinedBookmarkHistoryActivity
((CombinedBookmarkHistoryActivity) getParent())
.removeParentChildRelationShips();
- mAdapter.refreshData();
+ if (mAdapter != null) mAdapter.refreshData();
return true;
default:
@@ -265,7 +297,7 @@
return true;
case R.id.delete_context_menu_id:
Browser.deleteFromHistory(getContentResolver(), url);
- mAdapter.refreshData();
+ if (mAdapter != null) mAdapter.refreshData();
return true;
case R.id.homepage_context_menu_id:
BrowserSettings.getInstance().setHomePage(this, url);
@@ -297,8 +329,9 @@
}
private class HistoryAdapter extends DateSortedExpandableListAdapter {
- HistoryAdapter(Context context, Cursor cursor, int index) {
- super(context, cursor, index);
+ HistoryAdapter(Context context, Cursor cursor, int index,
+ Handler handler) {
+ super(context, cursor, index, handler);
}
diff --git a/src/com/android/browser/BrowserProvider.java b/src/com/android/browser/BrowserProvider.java
index bf1f9d5..7064180 100644
--- a/src/com/android/browser/BrowserProvider.java
+++ b/src/com/android/browser/BrowserProvider.java
@@ -29,8 +29,7 @@
import android.content.SharedPreferences;
import android.content.UriMatcher;
import android.content.SharedPreferences.Editor;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
import android.database.AbstractCursor;
import android.database.ContentObserver;
import android.database.Cursor;
@@ -47,8 +46,6 @@
import android.text.TextUtils;
import android.util.Log;
import android.util.Patterns;
-import android.util.TypedValue;
-
import java.io.File;
import java.io.FilenameFilter;
@@ -91,6 +88,17 @@
private static final int SUGGEST_COLUMN_QUERY_ID = 8;
private static final int SUGGEST_COLUMN_INTENT_EXTRA_DATA = 9;
+ // how many suggestions will be shown in dropdown
+ // 0..SHORT: filled by browser db
+ private static final int MAX_SUGGEST_SHORT_SMALL = 3;
+ // SHORT..LONG: filled by search suggestions
+ private static final int MAX_SUGGEST_LONG_SMALL = 6;
+
+ // large screen size shows more
+ private static final int MAX_SUGGEST_SHORT_LARGE = 6;
+ private static final int MAX_SUGGEST_LONG_LARGE = 9;
+
+
// shared suggestion columns
private static final String[] COLUMNS = new String[] {
"_id",
@@ -104,10 +112,6 @@
SearchManager.SUGGEST_COLUMN_QUERY,
SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA};
- private static final int MAX_SUGGESTION_SHORT_ENTRIES = 3;
- private static final int MAX_SUGGESTION_LONG_ENTRIES = 6;
- private static final String MAX_SUGGESTION_LONG_ENTRIES_STRING =
- Integer.valueOf(MAX_SUGGESTION_LONG_ENTRIES).toString();
// make sure that these match the index of TABLE_NAMES
private static final int URI_MATCH_BOOKMARKS = 0;
@@ -167,6 +171,9 @@
private SearchManager mSearchManager;
+ private int mMaxSuggestionShortSize;
+ private int mMaxSuggestionLongSize;
+
public BrowserProvider() {
}
@@ -350,6 +357,20 @@
@Override
public boolean onCreate() {
final Context context = getContext();
+ boolean xlargeScreenSize = (context.getResources().getConfiguration().screenLayout
+ & Configuration.SCREENLAYOUT_SIZE_MASK)
+ == Configuration.SCREENLAYOUT_SIZE_XLARGE;
+ boolean isPortrait = (context.getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_PORTRAIT);
+
+
+ if (xlargeScreenSize && isPortrait) {
+ mMaxSuggestionLongSize = MAX_SUGGEST_LONG_LARGE;
+ mMaxSuggestionShortSize = MAX_SUGGEST_SHORT_LARGE;
+ } else {
+ mMaxSuggestionLongSize = MAX_SUGGEST_LONG_SMALL;
+ mMaxSuggestionShortSize = MAX_SUGGEST_SHORT_SMALL;
+ }
mOpenHelper = new DatabaseHelper(context);
mBackupManager = new BackupManager(context);
// we added "picasa web album" into default bookmarks for version 19.
@@ -465,10 +486,10 @@
public MySuggestionCursor(Cursor hc, Cursor sc, String string) {
mHistoryCursor = hc;
mSuggestCursor = sc;
- mHistoryCount = hc.getCount();
+ mHistoryCount = hc != null ? hc.getCount() : 0;
mSuggestionCount = sc != null ? sc.getCount() : 0;
- if (mSuggestionCount > (MAX_SUGGESTION_LONG_ENTRIES - mHistoryCount)) {
- mSuggestionCount = MAX_SUGGESTION_LONG_ENTRIES - mHistoryCount;
+ if (mSuggestionCount > (mMaxSuggestionLongSize - mHistoryCount)) {
+ mSuggestionCount = mMaxSuggestionLongSize - mHistoryCount;
}
mString = string;
mIncludeWebSearch = string.length() > 0;
@@ -682,6 +703,7 @@
}
// TODO Temporary change, finalize after jq's changes go in
+ @Override
public void deactivate() {
if (mHistoryCursor != null) {
mHistoryCursor.deactivate();
@@ -692,12 +714,14 @@
super.deactivate();
}
+ @Override
public boolean requery() {
return (mHistoryCursor != null ? mHistoryCursor.requery() : false) |
(mSuggestCursor != null ? mSuggestCursor.requery() : false);
}
// TODO Temporary change, finalize after jq's changes go in
+ @Override
public void close() {
super.close();
if (mHistoryCursor != null) {
@@ -762,12 +786,15 @@
public ResultsCursor(ArrayList<String> results) {
mResults = results;
}
+ @Override
public int getCount() { return mResults.size(); }
+ @Override
public String[] getColumnNames() {
return RESULTS_COLUMNS;
}
+ @Override
public String getString(int column) {
switch (column) {
case RESULT_ACTION_ID:
@@ -789,24 +816,30 @@
return null;
}
}
+ @Override
public short getShort(int column) {
throw new UnsupportedOperationException();
}
+ @Override
public int getInt(int column) {
throw new UnsupportedOperationException();
}
+ @Override
public long getLong(int column) {
if ((mPos != -1) && column == 0) {
return mPos; // use row# as the _id
}
throw new UnsupportedOperationException();
}
+ @Override
public float getFloat(int column) {
throw new UnsupportedOperationException();
}
+ @Override
public double getDouble(int column) {
throw new UnsupportedOperationException();
}
+ @Override
public boolean isNull(int column) {
throw new UnsupportedOperationException();
}
@@ -846,8 +879,7 @@
String suggestSelection;
String [] myArgs;
if (selectionArgs[0] == null || selectionArgs[0].equals("")) {
- suggestSelection = null;
- myArgs = null;
+ return new MySuggestionCursor(null, null, "");
} else {
String like = selectionArgs[0] + "%";
if (selectionArgs[0].startsWith("http")
@@ -869,7 +901,7 @@
Cursor c = db.query(TABLE_NAMES[URI_MATCH_BOOKMARKS],
SUGGEST_PROJECTION, suggestSelection, myArgs, null, null,
- ORDER_BY, MAX_SUGGESTION_LONG_ENTRIES_STRING);
+ ORDER_BY, Integer.toString(mMaxSuggestionLongSize));
if (match == URI_MATCH_BOOKMARKS_SUGGEST
|| Patterns.WEB_URL.matcher(selectionArgs[0]).matches()) {
@@ -878,7 +910,7 @@
// get Google suggest if there is still space in the list
if (myArgs != null && myArgs.length > 1
&& mSearchableInfo != null
- && c.getCount() < (MAX_SUGGESTION_SHORT_ENTRIES - 1)) {
+ && c.getCount() < (mMaxSuggestionShortSize - 1)) {
Cursor sc = mSearchManager.getSuggestions(mSearchableInfo, selectionArgs[0]);
return new MySuggestionCursor(c, sc, selectionArgs[0]);
}
@@ -1121,4 +1153,10 @@
}
}
+ public static Cursor getBookmarksSuggestions(ContentResolver cr, String constraint) {
+ Uri uri = Uri.parse("content://browser/" + SearchManager.SUGGEST_URI_PATH_QUERY);
+ return cr.query(uri, SUGGEST_PROJECTION, SUGGEST_SELECTION,
+ new String[] { constraint }, ORDER_BY);
+ }
+
}
diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java
index 769dfca..94bed2d 100644
--- a/src/com/android/browser/BrowserSettings.java
+++ b/src/com/android/browser/BrowserSettings.java
@@ -106,8 +106,8 @@
private boolean showConsole = true;
// Private preconfigured values
- private static int minimumFontSize = 8;
- private static int minimumLogicalFontSize = 8;
+ private static int minimumFontSize = 1;
+ private static int minimumLogicalFontSize = 1;
private static int defaultFontSize = 16;
private static int defaultFixedFontSize = 13;
private static WebSettings.TextSize textSize =
@@ -219,6 +219,9 @@
s.setNeedInitialFocus(false);
// Browser supports multiple windows
s.setSupportMultipleWindows(true);
+ // enable smooth transition for better performance during panning or
+ // zooming
+ s.setEnableSmoothTransition(true);
// HTML5 API flags
s.setAppCacheEnabled(b.appCacheEnabled);
diff --git a/src/com/android/browser/CircularProgressView.java b/src/com/android/browser/CircularProgressView.java
new file mode 100644
index 0000000..48f293a
--- /dev/null
+++ b/src/com/android/browser/CircularProgressView.java
@@ -0,0 +1,140 @@
+
+/*
+ * Copyright (C) 2010 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.Paint;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.widget.ImageButton;
+
+/**
+ *
+ */
+public class CircularProgressView extends ImageButton {
+
+ private static final int[] ALPHAS = {
+ 64, 96, 128, 160, 192, 192, 160, 128, 96, 64
+ };
+
+ // 100 ms delay between frames, 10fps
+ private static int ALPHA_REFRESH_DELAY = 100;
+
+ private int mEndAngle;
+ private int mProgress;
+ private Paint mPaint;
+ private int mAlpha;
+ private boolean mAnimated;
+ private RectF mRect;
+ private int mMaxProgress;
+
+ /**
+ * @param context
+ * @param attrs
+ * @param defStyle
+ */
+ public CircularProgressView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init(context);
+ }
+
+ /**
+ * @param context
+ * @param attrs
+ */
+ public CircularProgressView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ /**
+ * @param context
+ */
+ public CircularProgressView(Context context) {
+ super(context);
+ init(context);
+ }
+
+ private void init(Context ctx) {
+ mEndAngle = 0;
+ mProgress = 0;
+ mMaxProgress = 100;
+ mPaint = new Paint();
+ mPaint.setAntiAlias(true);
+ mPaint.setColor(Color.BLACK);
+ mRect = new RectF();
+ }
+
+ void setMaxProgress(int max) {
+ mMaxProgress = max;
+ }
+
+ private synchronized boolean isAnimated() {
+ return mAnimated;
+ }
+
+ private synchronized void setAnimated(boolean animated) {
+ mAnimated = animated;
+ }
+
+ void setProgress(int progress) {
+ mProgress = progress;
+ mEndAngle = 360 * progress / mMaxProgress;
+ invalidate();
+ if (!isAnimated() && (progress > 0) && (progress < mMaxProgress)) {
+ setAnimated(true);
+ mAlpha = 0;
+ post(new Runnable() {
+ @Override
+ public void run() {
+ if (isAnimated()) {
+ mAlpha = (mAlpha + 1) % ALPHAS.length;
+ mPaint.setAlpha(ALPHAS[mAlpha]);
+ invalidate();
+ postDelayed(this, ALPHA_REFRESH_DELAY);
+ }
+ }
+ });
+ } else if ((progress <= 0) || (progress >= mMaxProgress)) {
+ setAnimated(false);
+ }
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ int w = getWidth();
+ int h = getHeight();
+ float cx = w * 0.5f;
+ float cy = h * 0.5f;
+ mRect.set(0, 0, w, h);
+ if ((mProgress > 0) && (mProgress < mMaxProgress)) {
+ Path p = new Path();
+ p.moveTo(cx, cy);
+ p.lineTo(cx, 0);
+ p.arcTo(mRect, 270, mEndAngle);
+ p.lineTo(cx, cy);
+ int state = canvas.save();
+ canvas.drawPath(p, mPaint);
+ canvas.restoreToCount(state);
+ }
+ super.onDraw(canvas);
+ }
+
+}
diff --git a/src/com/android/browser/DateSortedExpandableListAdapter.java b/src/com/android/browser/DateSortedExpandableListAdapter.java
index 1d04493..f8261d8 100644
--- a/src/com/android/browser/DateSortedExpandableListAdapter.java
+++ b/src/com/android/browser/DateSortedExpandableListAdapter.java
@@ -51,8 +51,8 @@
private Context mContext;
private class ChangeObserver extends ContentObserver {
- public ChangeObserver() {
- super(new Handler());
+ public ChangeObserver(Handler handler) {
+ super(handler);
}
@Override
@@ -67,13 +67,13 @@
}
public DateSortedExpandableListAdapter(Context context, Cursor cursor,
- int dateIndex) {
+ int dateIndex, Handler handler) {
mContext = context;
mDateSorter = new DateSorter(context);
mObservers = new Vector<DataSetObserver>();
mCursor = cursor;
mIdIndex = cursor.getColumnIndexOrThrow(BaseColumns._ID);
- cursor.registerContentObserver(new ChangeObserver());
+ cursor.registerContentObserver(new ChangeObserver(handler));
mDateIndex = dateIndex;
buildMap();
}
diff --git a/src/com/android/browser/DownloadTouchIcon.java b/src/com/android/browser/DownloadTouchIcon.java
index 14404ff..765d288 100644
--- a/src/com/android/browser/DownloadTouchIcon.java
+++ b/src/com/android/browser/DownloadTouchIcon.java
@@ -16,6 +16,7 @@
package com.android.browser;
+import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -25,10 +26,11 @@
import android.net.http.AndroidHttpClient;
import android.net.Proxy;
import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Message;
import android.provider.Browser;
import android.webkit.WebView;
-
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
@@ -45,10 +47,17 @@
private Cursor mCursor;
private final String mOriginalUrl;
private final String mUrl;
- private final String mUserAgent;
- private final BrowserActivity mActivity;
+ private final String mUserAgent; // Sites may serve a different icon to different UAs
+ private Message mMessage;
+
+ private final Activity mActivity;
/* package */ Tab mTab;
+ /**
+ * Use this ctor to store the touch icon in the bookmarks database for
+ * the originalUrl so we take account of redirects. Used when the user
+ * bookmarks a page from outside the bookmarks activity.
+ */
public DownloadTouchIcon(Tab tab, BrowserActivity activity, ContentResolver cr, WebView view) {
mTab = tab;
mActivity = activity;
@@ -59,24 +68,49 @@
mUserAgent = view.getSettings().getUserAgentString();
}
- public DownloadTouchIcon(ContentResolver cr, String url) {
+ /**
+ * Use this ctor to download the touch icon and update the bookmarks database
+ * entry for the given url. Used when the user creates a bookmark from
+ * within the bookmarks activity and there haven't been any redirects.
+ * TODO: Would be nice to set the user agent here so that there is no
+ * potential for the three different ctors here to return different icons.
+ */
+ public DownloadTouchIcon(AddBookmarkPage activity, ContentResolver cr, String url) {
mTab = null;
- mActivity = null;
+ mActivity = activity;
mContentResolver = cr;
mOriginalUrl = null;
mUrl = url;
mUserAgent = null;
}
+ /**
+ * Use this ctor to not store the touch icon in a database, rather add it to
+ * the passed Message's data bundle with the key "touchIcon" and then send
+ * the message.
+ */
+ public DownloadTouchIcon(BrowserActivity activity, Message msg, String userAgent) {
+ mMessage = msg;
+ mActivity = activity;
+ mContentResolver = null;
+ mOriginalUrl = null;
+ mUrl = null;
+ mUserAgent = userAgent;
+ }
+
@Override
public Void doInBackground(String... values) {
- mCursor = BrowserBookmarksAdapter.queryBookmarksForUrl(mContentResolver,
- mOriginalUrl, mUrl, true);
- if (mCursor != null && mCursor.getCount() > 0) {
- String url = values[0];
+ if (mContentResolver != null) {
+ mCursor = BrowserBookmarksAdapter.queryBookmarksForUrl(mContentResolver,
+ mOriginalUrl, mUrl, true);
+ }
- AndroidHttpClient client = AndroidHttpClient.newInstance(
- mUserAgent);
+ boolean inBookmarksDatabase = mCursor != null && mCursor.getCount() > 0;
+
+ String url = values[0];
+
+ if (inBookmarksDatabase || mMessage != null) {
+ AndroidHttpClient client = AndroidHttpClient.newInstance(mUserAgent);
HttpHost httpHost = Proxy.getPreferredHttpHost(mActivity, url);
if (httpHost != null) {
ConnRouteParams.setDefaultProxy(client.getParams(), httpHost);
@@ -89,7 +123,6 @@
try {
HttpResponse response = client.execute(request);
-
if (response.getStatusLine().getStatusCode() == 200) {
HttpEntity entity = response.getEntity();
if (entity != null) {
@@ -97,7 +130,12 @@
if (content != null) {
Bitmap icon = BitmapFactory.decodeStream(
content, null, null);
- storeIcon(icon);
+ if (inBookmarksDatabase) {
+ storeIcon(icon);
+ } else if (mMessage != null) {
+ Bundle b = mMessage.getData();
+ b.putParcelable("touchIcon", icon);
+ }
}
}
}
@@ -109,9 +147,15 @@
client.close();
}
}
+
if (mCursor != null) {
mCursor.close();
}
+
+ if (mMessage != null) {
+ mMessage.sendToTarget();
+ }
+
return null;
}
diff --git a/src/com/android/browser/FindDialog.java b/src/com/android/browser/FindDialog.java
index 45c8016..9d0ac4b 100644
--- a/src/com/android/browser/FindDialog.java
+++ b/src/com/android/browser/FindDialog.java
@@ -16,27 +16,25 @@
package com.android.browser;
-import android.app.Dialog;
import android.content.Context;
-import android.os.Bundle;
import android.text.Editable;
+import android.text.Selection;
import android.text.Spannable;
import android.text.TextWatcher;
import android.view.Gravity;
import android.view.KeyEvent;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
+import android.view.animation.AnimationUtils;
import android.view.inputmethod.InputMethodManager;
import android.webkit.WebView;
import android.widget.EditText;
+import android.widget.LinearLayout;
import android.widget.TextView;
-/* package */ class FindDialog extends Dialog implements TextWatcher {
- private WebView mWebView;
+/* package */ class FindDialog extends WebDialog implements TextWatcher {
private TextView mMatches;
- private BrowserActivity mBrowserActivity;
// Views with which the user can interact.
private EditText mEditText;
@@ -44,39 +42,30 @@
private View mPrevButton;
private View mMatchesView;
+ // When the dialog is opened up with old text, enter needs to be pressed
+ // (or the text needs to be changed) before WebView.findAll can be called.
+ // Once it has been called, enter should move to the next match.
+ private boolean mMatchesFound;
+ private int mNumberOfMatches;
+
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 =
+ private View.OnClickListener mFindPreviousListener =
new View.OnClickListener() {
public void onClick(View v) {
if (mWebView == null) {
throw new AssertionError("No WebView for FindDialog::onClick");
}
mWebView.findNext(false);
+ updateMatchesString();
hideSoftInput();
}
};
- /*
- * Remove the soft keyboard from the screen.
- */
- private void hideSoftInput() {
- InputMethodManager imm = (InputMethodManager)
- mBrowserActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
- }
-
private void disableButtons() {
mPrevButton.setEnabled(false);
mNextButton.setEnabled(false);
@@ -84,28 +73,13 @@
mNextButton.setFocusable(false);
}
- /* package */ void setWebView(WebView webview) {
- mWebView = webview;
- }
-
/* package */ FindDialog(BrowserActivity context) {
- super(context, R.style.FindDialogTheme);
- mBrowserActivity = context;
- setCanceledOnTouchOutside(true);
- }
+ super(context);
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ LayoutInflater factory = LayoutInflater.from(context);
+ factory.inflate(R.layout.browser_find, this);
- Window theWindow = getWindow();
- theWindow.setGravity(Gravity.BOTTOM|Gravity.FILL_HORIZONTAL);
-
- setContentView(R.layout.browser_find);
-
- theWindow.setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT);
-
+ addCancel();
mEditText = (EditText) findViewById(R.id.edit);
View button = findViewById(R.id.next);
@@ -116,29 +90,59 @@
button.setOnClickListener(mFindPreviousListener);
mPrevButton = button;
- button = findViewById(R.id.done);
- button.setOnClickListener(mFindCancelListener);
-
mMatches = (TextView) findViewById(R.id.matches);
mMatchesView = findViewById(R.id.matches_view);
disableButtons();
- theWindow.setSoftInputMode(
- WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
+
}
-
+
+ /**
+ * Called by BrowserActivity.closeDialog. Start the animation to hide
+ * the dialog, inform the WebView that the dialog is being dismissed,
+ * and hide the soft keyboard.
+ */
public void dismiss() {
super.dismiss();
- mBrowserActivity.closeFind();
mWebView.notifyFindDialogDismissed();
+ hideSoftInput();
+ }
+
+ @Override
+ public boolean dispatchKeyEventPreIme(KeyEvent event) {
+ if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+ KeyEvent.DispatcherState state = getKeyDispatcherState();
+ if (state != null) {
+ int action = event.getAction();
+ if (KeyEvent.ACTION_DOWN == action
+ && event.getRepeatCount() == 0) {
+ state.startTracking(event, this);
+ return true;
+ } else if (KeyEvent.ACTION_UP == action
+ && !event.isCanceled() && state.isTracking(event)) {
+ mBrowserActivity.closeDialogs();
+ return true;
+ }
+ }
+ }
+ return super.dispatchKeyEventPreIme(event);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
- if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER
- && event.getAction() == KeyEvent.ACTION_UP
- && mEditText.hasFocus()) {
- findNext();
- return true;
+ int keyCode = event.getKeyCode();
+ if (event.getAction() == KeyEvent.ACTION_UP) {
+ if (keyCode == KeyEvent.KEYCODE_ENTER
+ && mEditText.hasFocus()) {
+ if (mMatchesFound) {
+ findNext();
+ } else {
+ findAll();
+ // Set the selection to the end.
+ Spannable span = (Spannable) mEditText.getText();
+ Selection.setSelection(span, span.length());
+ }
+ return true;
+ }
}
return super.dispatchKeyEvent(event);
}
@@ -148,18 +152,26 @@
throw new AssertionError("No WebView for FindDialog::findNext");
}
mWebView.findNext(true);
+ updateMatchesString();
hideSoftInput();
}
public void show() {
super.show();
+ // In case the matches view is showing from a previous search
+ mMatchesView.setVisibility(View.INVISIBLE);
+ mMatchesFound = false;
+ // This text is only here to ensure that mMatches has a height.
+ mMatches.setText("0");
mEditText.requestFocus();
- mEditText.setText("");
Spannable span = (Spannable) mEditText.getText();
- span.setSpan(this, 0, span.length(),
- Spannable.SPAN_INCLUSIVE_INCLUSIVE);
- setMatchesFound(0);
+ int length = span.length();
+ Selection.setSelection(span, 0, length);
+ span.setSpan(this, 0, length, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
disableButtons();
+ InputMethodManager imm = (InputMethodManager)
+ mBrowserActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(mEditText, 0);
}
// TextWatcher methods
@@ -173,9 +185,13 @@
int start,
int before,
int count) {
+ findAll();
+ }
+
+ private void findAll() {
if (mWebView == null) {
throw new AssertionError(
- "No WebView for FindDialog::onTextChanged");
+ "No WebView for FindDialog::findAll");
}
CharSequence find = mEditText.getText();
if (0 == find.length()) {
@@ -184,14 +200,16 @@
mMatchesView.setVisibility(View.INVISIBLE);
} else {
mMatchesView.setVisibility(View.VISIBLE);
- mWebView.setFindDialogHeight(
- getWindow().getDecorView().getHeight());
int found = mWebView.findAll(find.toString());
+ mMatchesFound = true;
setMatchesFound(found);
if (found < 2) {
disableButtons();
if (found == 0) {
- setMatchesFound(0);
+ // Cannot use getQuantityString, which ignores the "zero"
+ // quantity.
+ mMatches.setText(mBrowserActivity.getResources().getString(
+ R.string.no_matches));
}
} else {
mPrevButton.setFocusable(true);
@@ -203,8 +221,21 @@
}
private void setMatchesFound(int found) {
+ mNumberOfMatches = found;
+ updateMatchesString();
+ }
+
+ public void setText(String text) {
+ mEditText.setText(text);
+ findAll();
+ }
+
+ private void updateMatchesString() {
+ // Note: updateMatchesString is only called by methods that have already
+ // checked mWebView for null.
String template = mBrowserActivity.getResources().
- getQuantityString(R.plurals.matches_found, found, found);
+ getQuantityString(R.plurals.matches_found, mNumberOfMatches,
+ mWebView.findIndex() + 1, mNumberOfMatches);
mMatches.setText(template);
}
diff --git a/src/com/android/browser/SaveToHomescreenDialog.java b/src/com/android/browser/SaveToHomescreenDialog.java
new file mode 100644
index 0000000..15f0aea
--- /dev/null
+++ b/src/com/android/browser/SaveToHomescreenDialog.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2010 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.ContentResolver;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.net.ParseException;
+import android.net.Uri;
+import android.net.WebAddress;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.provider.Browser;
+import android.util.Log;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Date;
+
+public class SaveToHomescreenDialog extends Activity {
+
+ private EditText mTitle;
+ private String mUrl;
+ private Bitmap mFavicon;
+ private Bitmap mTouchIcon;
+
+ private View.OnClickListener mOk = new View.OnClickListener() {
+ public void onClick(View v) {
+ if (save()) {
+ finish();
+ }
+ }
+ };
+
+ 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_const_url);
+ setTitle(R.string.create_shortcut_bookmark);
+ getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
+ R.drawable.ic_list_bookmark);
+
+ String title = null;
+ String url = null;
+ Bundle map = getIntent().getExtras();
+ if (map != null) {
+ title = map.getString("title");
+ }
+
+ mUrl = map.getString("url");
+ mFavicon = (Bitmap)map.getParcelable("favicon");
+ mTouchIcon = (Bitmap)map.getParcelable("touchIcon");
+
+ Bitmap icon = BookmarkUtils.createIcon(this, mTouchIcon, mFavicon,
+ BookmarkUtils.BookmarkIconType.ICON_HOME_SHORTCUT);
+ getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, new BitmapDrawable(icon));
+
+ mTitle = (EditText) findViewById(R.id.title);
+ mTitle.setText(title);
+
+ Button okButton = (Button) findViewById(R.id.OK);
+ okButton.setOnClickListener(mOk);
+
+ Button cancelButton = (Button) findViewById(R.id.cancel);
+ cancelButton.setOnClickListener(mCancel);
+
+ if (!getWindow().getDecorView().isInTouchMode()) {
+ okButton.requestFocus();
+ }
+ }
+
+ /**
+ * Parse the data entered in the dialog and send an intent to create an
+ * icon on the homescreen.
+ */
+ private boolean save() {
+ String title = mTitle.getText().toString().trim();
+ String unfilteredUrl = BrowserActivity.fixUrl(mUrl);
+ if (title.length() == 0) {
+ mTitle.setError(getResources().getText(R.string.bookmark_needs_title));
+ return false;
+ }
+
+ String url = unfilteredUrl.trim();
+
+ sendBroadcast(BookmarkUtils.createAddToHomeIntent(this, url, title,
+ mTouchIcon, mFavicon));
+ setResult(RESULT_OK);
+ return true;
+ }
+}
diff --git a/src/com/android/browser/SelectDialog.java b/src/com/android/browser/SelectDialog.java
new file mode 100644
index 0000000..461127a
--- /dev/null
+++ b/src/com/android/browser/SelectDialog.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2010 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.provider.Browser;
+import android.view.LayoutInflater;
+import android.view.View;
+
+/* package */ class SelectDialog extends WebDialog {
+ private View mCopyButton;
+ private View mSelectAllButton;
+ private View mShareButton;
+ private View mFindButton;
+
+ SelectDialog(BrowserActivity context) {
+ super(context);
+ LayoutInflater factory = LayoutInflater.from(context);
+ factory.inflate(R.layout.browser_select, this);
+ addCancel();
+
+ mCopyButton = findViewById(R.id.copy);
+ mCopyButton.setOnClickListener(mCopyListener);
+ mSelectAllButton = findViewById(R.id.select_all);
+ mSelectAllButton.setOnClickListener(mSelectAllListener);
+ mShareButton = findViewById(R.id.share);
+ mShareButton.setOnClickListener(mShareListener);
+ mFindButton = findViewById(R.id.find);
+ mFindButton.setOnClickListener(mFindListener);
+ }
+
+ private View.OnClickListener mCopyListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ mWebView.copySelection();
+ mBrowserActivity.closeDialogs();
+ }
+ };
+
+ private View.OnClickListener mSelectAllListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ mWebView.selectAll();
+ }
+ };
+
+ private View.OnClickListener mShareListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ String selection = mWebView.getSelection();
+ Browser.sendString(mBrowserActivity, selection);
+ mBrowserActivity.closeDialogs();
+ }
+ };
+
+ private View.OnClickListener mFindListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ String selection = mWebView.getSelection();
+ mBrowserActivity.closeDialogs();
+ mBrowserActivity.showFindDialog();
+ mBrowserActivity.setFindDialogText(selection);
+ }
+ };
+
+ /**
+ * Called by BrowserActivity.closeDialog. Start the animation to hide
+ * the dialog, and inform the WebView that the dialog is being dismissed.
+ */
+ @Override
+ public void dismiss() {
+ super.dismiss();
+ mWebView.notifySelectDialogDismissed();
+ }
+
+}
diff --git a/src/com/android/browser/Tab.java b/src/com/android/browser/Tab.java
index 5795b05..1d9482b 100644
--- a/src/com/android/browser/Tab.java
+++ b/src/com/android/browser/Tab.java
@@ -16,21 +16,13 @@
package com.android.browser;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.Map;
-import java.util.Vector;
-
import android.app.AlertDialog;
import android.app.SearchManager;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.DialogInterface;
-import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
+import android.content.DialogInterface.OnCancelListener;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
@@ -71,8 +63,17 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.android.browser.TabControl.TabChangeListener;
import com.android.common.speech.LoggingEvents;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Vector;
+
/**
* Class for maintaining Tabs with a main WebView and a subwindow.
*/
@@ -87,7 +88,7 @@
// The Geolocation permissions prompt
private GeolocationPermissionsPrompt mGeolocationPermissionsPrompt;
// Main WebView wrapper
- private View mContainer;
+ private LinearLayout mContainer;
// Main WebView
private WebView mMainView;
// Subwindow container
@@ -151,7 +152,6 @@
static final String CURRTAB = "currentTab";
static final String CURRURL = "currentUrl";
static final String CURRTITLE = "currentTitle";
- static final String CURRPICTURE = "currentPicture";
static final String CLOSEONEXIT = "closeonexit";
static final String PARENTTAB = "parentTab";
static final String APPID = "appid";
@@ -503,6 +503,9 @@
if (mInForeground) {
mActivity.onPageStarted(view, url, favicon);
}
+ if (getTabChangeListener() != null) {
+ getTabChangeListener().onPageStarted(Tab.this);
+ }
}
@Override
@@ -524,6 +527,9 @@
if (mInForeground) {
mActivity.onPageFinished(view, url);
}
+ if (getTabChangeListener() != null) {
+ getTabChangeListener().onPageFinished(Tab.this);
+ }
}
// return true if want to hijack the url to let another app to handle it
@@ -673,6 +679,7 @@
final ContentResolver cr = mActivity.getContentResolver();
final String newUrl = url;
new AsyncTask<Void, Void, Void>() {
+ @Override
protected Void doInBackground(Void... unused) {
Browser.updateVisitedHistory(cr, newUrl, true);
return null;
@@ -949,6 +956,9 @@
if (mInForeground) {
mActivity.onProgressChanged(view, newProgress);
}
+ if (getTabChangeListener() != null) {
+ getTabChangeListener().onProgress(Tab.this, newProgress);
+ }
}
@Override
@@ -958,11 +968,16 @@
// here, if url is null, we want to reset the title
mActivity.setUrlTitle(pageUrl, title);
}
+ TabChangeListener tcl = getTabChangeListener();
+ if (tcl != null) {
+ tcl.onUrlAndTitle(Tab.this, pageUrl,title);
+ }
if (pageUrl == null || pageUrl.length()
>= SQLiteDatabase.SQLITE_MAX_LIKE_PATTERN_LENGTH) {
return;
}
new AsyncTask<Void, Void, Void>() {
+ @Override
protected Void doInBackground(Void... unused) {
// See if we can find the current url in our history
// database and add the new title to it.
@@ -1021,6 +1036,9 @@
if (mInForeground) {
mActivity.setFavicon(icon);
}
+ if (getTabChangeListener() != null) {
+ getTabChangeListener().onFavicon(Tab.this, icon);
+ }
}
@Override
@@ -1041,6 +1059,16 @@
}
@Override
+ public void onSelectionDone(WebView view) {
+ if (mInForeground) mActivity.closeDialogs();
+ }
+
+ @Override
+ public void onSelectionStart(WebView view) {
+ if (mInForeground) mActivity.showSelectDialog();
+ }
+
+ @Override
public void onShowCustomView(View view,
WebChromeClient.CustomViewCallback callback) {
if (mInForeground) mActivity.onShowCustomView(view, callback);
@@ -1185,9 +1213,9 @@
}
@Override
- public void openFileChooser(ValueCallback<Uri> uploadMsg) {
+ public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
if (mInForeground) {
- mActivity.openFileChooser(uploadMsg);
+ mActivity.openFileChooser(uploadMsg, acceptType);
} else {
uploadMsg.onReceiveValue(null);
}
@@ -1199,10 +1227,12 @@
@Override
public void getVisitedHistory(final ValueCallback<String[]> callback) {
AsyncTask<Void, Void, String[]> task = new AsyncTask<Void, Void, String[]>() {
+ @Override
public String[] doInBackground(Void... unused) {
return Browser.getVisitedHistory(mActivity
.getContentResolver());
}
+ @Override
public void onPostExecute(String[] result) {
callback.onReceiveValue(result);
};
@@ -1220,9 +1250,18 @@
private static class SubWindowClient extends WebViewClient {
// The main WebViewClient.
private final WebViewClient mClient;
+ private final BrowserActivity mBrowserActivity;
- SubWindowClient(WebViewClient client) {
+ SubWindowClient(WebViewClient client, BrowserActivity activity) {
mClient = client;
+ mBrowserActivity = activity;
+ }
+ @Override
+ public void onPageStarted(WebView view, String url, Bitmap favicon) {
+ // Unlike the others, do not call mClient's version, which would
+ // change the progress bar. However, we do want to remove the
+ // find or select dialog.
+ mBrowserActivity.closeDialogs();
}
@Override
public void doUpdateVisitedHistory(WebView view, String url,
@@ -1312,7 +1351,7 @@
// The tab consists of a container view, which contains the main
// WebView, as well as any other UI elements associated with the tab.
- mContainer = mInflateService.inflate(R.layout.tab, null);
+ mContainer = (LinearLayout) mInflateService.inflate(R.layout.tab, null);
mDownloadListener = new DownloadListener() {
public void onDownloadStart(String url, String userAgent,
@@ -1423,6 +1462,7 @@
*/
boolean createSubWindow() {
if (mSubView == null) {
+ mActivity.closeDialogs();
mSubViewContainer = mInflateService.inflate(
R.layout.browser_subwindow, null);
mSubView = (WebView) mSubViewContainer.findViewById(R.id.webview);
@@ -1431,7 +1471,8 @@
mSubView.setMapTrackballToArrowKeys(false);
// Enable the built-in zoom
mSubView.getSettings().setBuiltInZoomControls(true);
- mSubView.setWebViewClient(new SubWindowClient(mWebViewClient));
+ mSubView.setWebViewClient(new SubWindowClient(mWebViewClient,
+ mActivity));
mSubView.setWebChromeClient(new SubWindowChromeClient(
mWebChromeClient));
// Set a different DownloadListener for the mSubView, since it will
@@ -1469,6 +1510,7 @@
*/
void dismissSubWindow() {
if (mSubView != null) {
+ mActivity.closeDialogs();
BrowserSettings.getInstance().deleteObserver(
mSubView.getSettings());
mSubView.destroy();
@@ -1493,6 +1535,7 @@
void removeSubWindow(ViewGroup content) {
if (mSubView != null) {
content.removeView(mSubViewContainer);
+ mActivity.closeDialogs();
}
}
@@ -1551,6 +1594,7 @@
(FrameLayout) mContainer.findViewById(R.id.webview_wrapper);
wrapper.removeView(mMainView);
content.removeView(mContainer);
+ mActivity.closeDialogs();
removeSubWindow(content);
}
@@ -1824,6 +1868,9 @@
// FIXME: The only place we cared about subwindow was for
// bookmarking (i.e. not when saving state). Was this deliberate?
final WebBackForwardList list = mMainView.copyBackForwardList();
+ if (list == null) {
+ Log.w(LOGTAG, "populatePickerData called and WebBackForwardList is null");
+ }
final WebHistoryItem item = list != null ? list.getCurrentItem() : null;
populatePickerData(item);
}
@@ -1832,7 +1879,9 @@
// WebView.
private void populatePickerData(WebHistoryItem item) {
mPickerData = new PickerData();
- if (item != null) {
+ if (item == null) {
+ Log.w(LOGTAG, "populatePickerData called with a null WebHistoryItem");
+ } else {
mPickerData.mUrl = item.getUrl();
mPickerData.mTitle = item.getTitle();
mPickerData.mFavicon = item.getFavicon();
@@ -1883,17 +1932,6 @@
mSavedState = new Bundle();
final WebBackForwardList list = mMainView.saveState(mSavedState);
- if (list != null) {
- final File f = new File(mActivity.getTabControl().getThumbnailDir(),
- mMainView.hashCode() + "_pic.save");
- if (mMainView.savePicture(mSavedState, f)) {
- mSavedState.putString(CURRPICTURE, f.getPath());
- } else {
- // if savePicture returned false, we can't trust the contents,
- // and it may be large, so we delete it right away
- f.delete();
- }
- }
// Store some extra info for displaying the tab in the picker.
final WebHistoryItem item = list != null ? list.getCurrentItem() : null;
@@ -1939,11 +1977,47 @@
if (list == null) {
return false;
}
- if (b.containsKey(CURRPICTURE)) {
- final File f = new File(b.getString(CURRPICTURE));
- mMainView.restorePicture(b, f);
- f.delete();
- }
return true;
}
+
+ /*
+ * Opens the find and select text dialogs. Called by BrowserActivity.
+ */
+ WebView showDialog(WebDialog dialog) {
+ LinearLayout container;
+ WebView view;
+ if (mSubView != null) {
+ view = mSubView;
+ container = (LinearLayout) mSubViewContainer.findViewById(
+ R.id.inner_container);
+ } else {
+ view = mMainView;
+ container = mContainer;
+ }
+ dialog.show();
+ container.addView(dialog, 0, new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+ dialog.setWebView(view);
+ return view;
+ }
+
+ /*
+ * Close the find or select dialog. Called by BrowserActivity.closeDialog.
+ */
+ void closeDialog(WebDialog dialog) {
+ // The dialog may be attached to the subwindow. Ensure that the
+ // correct parent has it removed.
+ LinearLayout parent = (LinearLayout) dialog.getParent();
+ if (parent != null) parent.removeView(dialog);
+ }
+
+ /**
+ * always get the TabChangeListener form the tab control
+ * @return the TabControl change listener
+ */
+ private TabChangeListener getTabChangeListener() {
+ return mActivity.getTabControl().getTabChangeListener();
+ }
+
}
diff --git a/src/com/android/browser/TabControl.java b/src/com/android/browser/TabControl.java
index 7cd2ccb..0fbdd7a 100644
--- a/src/com/android/browser/TabControl.java
+++ b/src/com/android/browser/TabControl.java
@@ -132,7 +132,7 @@
int getCurrentIndex() {
return mCurrentTab;
}
-
+
/**
* Given a Tab, find it's index
* @param Tab to find
@@ -167,6 +167,9 @@
mTabs.add(t);
// Initially put the tab in the background.
t.putInBackground();
+ if (mTabChangeListener != null) {
+ mTabChangeListener.onNewTab(t);
+ }
return t;
}
@@ -229,17 +232,11 @@
}
}
- // This tab may have been pushed in to the background and then closed.
- // If the saved state contains a picture file, delete the file.
- Bundle savedState = t.getSavedState();
- if (savedState != null) {
- if (savedState.containsKey(Tab.CURRPICTURE)) {
- new File(savedState.getString(Tab.CURRPICTURE)).delete();
- }
- }
-
// Remove it from the queue of viewed tabs.
mTabQueue.remove(t);
+ if (mTabChangeListener != null) {
+ mTabChangeListener.onRemoveTab(t);
+ }
return true;
}
@@ -626,6 +623,47 @@
mainView.loadUrl(BrowserSettings.getInstance().getHomePage());
}
}
+ if (mTabChangeListener != null) {
+ mTabChangeListener.onCurrentTab(newTab);
+ }
return true;
}
+
+ interface TabChangeListener {
+
+ public void onNewTab(Tab tab);
+
+ public void onRemoveTab(Tab tab);
+
+ public void onCurrentTab(Tab tab);
+
+ public void onProgress(Tab tab, int progress);
+
+ public void onUrlAndTitle(Tab tab, String url, String title);
+
+ public void onFavicon(Tab tab, Bitmap favicon);
+
+ public void onPageStarted(Tab tab);
+
+ public void onPageFinished(Tab tab);
+
+ }
+
+ private TabChangeListener mTabChangeListener;
+
+ /**
+ * register the TabChangeListener with the tab control
+ * @param listener
+ */
+ void setOnTabChangeListener(TabChangeListener listener) {
+ mTabChangeListener = listener;
+ }
+
+ /**
+ * get the current TabChangeListener (used by the tabs)
+ */
+ TabChangeListener getTabChangeListener() {
+ return mTabChangeListener;
+ }
+
}
diff --git a/src/com/android/browser/TabScrollView.java b/src/com/android/browser/TabScrollView.java
new file mode 100644
index 0000000..6d8b91b
--- /dev/null
+++ b/src/com/android/browser/TabScrollView.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2010 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.View;
+import android.widget.HorizontalScrollView;
+import android.widget.LinearLayout;
+
+/**
+ * custom view for displaying tabs in the tabbed title bar
+ */
+public class TabScrollView extends HorizontalScrollView {
+
+ private Context mContext;
+
+ private LinearLayout mContentView;
+
+ private int mSelected;
+
+ /**
+ * @param context
+ * @param attrs
+ * @param defStyle
+ */
+ public TabScrollView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init(context);
+ }
+
+ /**
+ * @param context
+ * @param attrs
+ */
+ public TabScrollView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ /**
+ * @param context
+ */
+ public TabScrollView(Context context) {
+ super(context);
+ init(context);
+ }
+
+ private void init(Context ctx) {
+ mContext = ctx;
+ setHorizontalScrollBarEnabled(false);
+ mContentView = new LinearLayout(mContext);
+ mContentView.setOrientation(LinearLayout.HORIZONTAL);
+ mContentView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT));
+ addView(mContentView);
+ mSelected = -1;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ ensureChildVisible(getSelectedTab());
+ }
+
+ void setSelectedTab(int position) {
+ View v = getSelectedTab();
+ if (v != null) {
+ v.setSelected(false);
+ }
+ mSelected = position;
+ v = getSelectedTab();
+ if (v != null) {
+ v.setSelected(true);
+ }
+ requestLayout();
+ }
+
+ View getSelectedTab() {
+ if ((mSelected >= 0) && (mSelected < mContentView.getChildCount())) {
+ return mContentView.getChildAt(mSelected);
+ } else {
+ return null;
+ }
+ }
+
+ void clearTabs() {
+ mContentView.removeAllViews();
+ }
+
+ void addTab(View tab) {
+ mContentView.addView(tab);
+ tab.setSelected(false);
+ }
+
+ void addTab(View tab, int pos) {
+ mContentView.addView(tab, pos);
+ tab.setSelected(false);
+ }
+
+ void removeTab(View tab) {
+ mContentView.removeView(tab);
+ }
+
+ void ensureChildVisible(View child) {
+ if (child != null) {
+ int childl = child.getLeft();
+ int childr = childl + child.getWidth();
+ int viewl = getScrollX();
+ int viewr = viewl + getWidth();
+ if (childl < viewl) {
+ // need scrolling to left
+ scrollTo(childl, 0);
+ } else if (childr > viewr) {
+ // need scrolling to right
+ scrollTo(childr - viewr + viewl, 0);
+ }
+ }
+ }
+
+
+}
diff --git a/src/com/android/browser/TitleBar.java b/src/com/android/browser/TitleBar.java
index dc4979b..f45025d 100644
--- a/src/com/android/browser/TitleBar.java
+++ b/src/com/android/browser/TitleBar.java
@@ -21,14 +21,8 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.Rect;
import android.graphics.drawable.Animatable;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.PaintDrawable;
import android.os.Handler;
import android.os.Message;
import android.speech.RecognizerIntent;
@@ -45,7 +39,6 @@
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -55,21 +48,16 @@
* This class represents a title bar for a particular "tab" or "window" in the
* browser.
*/
-public class TitleBar extends LinearLayout {
+public class TitleBar extends TitleBarBase {
private TextView mTitle;
- private Drawable mCloseDrawable;
private ImageView mRtButton;
private Drawable mCircularProgress;
private ProgressBar mHorizontalProgress;
- private ImageView mFavicon;
- private ImageView mLockIcon;
private ImageView mStopButton;
private Drawable mBookmarkDrawable;
private Drawable mVoiceDrawable;
private boolean mInLoad;
private BrowserActivity mBrowserActivity;
- private Drawable mGenericFavicon;
- private int mIconDimension;
private View mTitleBg;
private MyHandler mHandler;
private Intent mVoiceSearchIntent;
@@ -84,7 +72,7 @@
private static int LONG_PRESS = 1;
public TitleBar(BrowserActivity context) {
- super(context, null);
+ super(context);
mHandler = new MyHandler();
LayoutInflater factory = LayoutInflater.from(context);
factory.inflate(R.layout.title_bar, this);
@@ -107,13 +95,11 @@
TypedValue.COMPLEX_UNIT_DIP, 8f, metrics);
mRightMargin = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 6f, metrics);
- mIconDimension = (int) TypedValue.applyDimension(
+ int iconDimension = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 20f, metrics);
- mCircularProgress.setBounds(0, 0, mIconDimension, mIconDimension);
+ mCircularProgress.setBounds(0, 0, iconDimension, iconDimension);
mHorizontalProgress = (ProgressBar) findViewById(
R.id.progress_horizontal);
- mGenericFavicon = context.getResources().getDrawable(
- R.drawable.app_web_browser_sm);
mVoiceSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
mVoiceSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
@@ -219,7 +205,7 @@
} else if (mInLoad) {
mBrowserActivity.stopLoading();
} else {
- mBrowserActivity.bookmarksOrHistoryPicker(false);
+ mBrowserActivity.promptAddOrInstallBookmark();
}
button.setPressed(false);
} else if (mTitleBg.isPressed()) {
@@ -248,25 +234,6 @@
}
/**
- * Set a new Bitmap for the Favicon.
- */
- /* package */ void setFavicon(Bitmap icon) {
- Drawable[] array = new Drawable[3];
- array[0] = new PaintDrawable(Color.BLACK);
- PaintDrawable p = new PaintDrawable(Color.WHITE);
- array[1] = p;
- if (icon == null) {
- array[2] = mGenericFavicon;
- } else {
- array[2] = new BitmapDrawable(icon);
- }
- LayerDrawable d = new LayerDrawable(array);
- d.setLayerInset(1, 1, 1, 1, 1);
- d.setLayerInset(2, 2, 2, 2, 2);
- mFavicon.setImageDrawable(d);
- }
-
- /**
* Change the TitleBar to or from voice mode. If there is no package to
* handle voice search, the TitleBar cannot be set to voice mode.
*/
@@ -302,18 +269,6 @@
}
/**
- * Set the Drawable for the lock icon, or null to hide it.
- */
- /* package */ void setLock(Drawable d) {
- if (null == d) {
- mLockIcon.setVisibility(View.GONE);
- } else {
- mLockIcon.setImageDrawable(d);
- mLockIcon.setVisibility(View.VISIBLE);
- }
- }
-
- /**
* Update the progress, from 0 to 100.
*/
/* package */ void setProgress(int newProgress) {
@@ -374,11 +329,4 @@
}
}
}
-
- /* package */ void setToTabPicker() {
- mTitle.setText(R.string.tab_picker_title);
- setFavicon(null);
- setLock(null);
- mHorizontalProgress.setVisibility(View.GONE);
- }
}
diff --git a/src/com/android/browser/TitleBarBase.java b/src/com/android/browser/TitleBarBase.java
new file mode 100644
index 0000000..7016dc0
--- /dev/null
+++ b/src/com/android/browser/TitleBarBase.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 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.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+/**
+ * Base class for a title bar used by the browser.
+ */
+public class TitleBarBase extends LinearLayout {
+ // These need to be set by the subclass.
+ protected ImageView mFavicon;
+ protected ImageView mLockIcon;
+
+ protected Drawable mGenericFavicon;
+
+ public TitleBarBase(Context context) {
+ super(context, null);
+ mGenericFavicon = context.getResources().getDrawable(
+ R.drawable.app_web_browser_sm);
+ }
+
+ /* package */ void setProgress(int newProgress) {}
+ /* package */ void setDisplayTitle(String title) {}
+
+ /* package */ void setLock(Drawable d) {
+ assert mLockIcon != null;
+ if (null == d) {
+ mLockIcon.setVisibility(View.GONE);
+ } else {
+ mLockIcon.setImageDrawable(d);
+ mLockIcon.setVisibility(View.VISIBLE);
+ }
+ }
+
+ /* package */ void setFavicon(Bitmap icon) {
+ assert mFavicon != null;
+ Drawable[] array = new Drawable[3];
+ array[0] = new PaintDrawable(Color.BLACK);
+ PaintDrawable p = new PaintDrawable(Color.WHITE);
+ array[1] = p;
+ if (icon == null) {
+ array[2] = mGenericFavicon;
+ } else {
+ array[2] = new BitmapDrawable(icon);
+ }
+ LayerDrawable d = new LayerDrawable(array);
+ d.setLayerInset(1, 1, 1, 1, 1);
+ d.setLayerInset(2, 2, 2, 2, 2);
+ mFavicon.setImageDrawable(d);
+ }
+
+ /* package */ void setInVoiceMode(boolean inVoiceMode) {}
+
+}
diff --git a/src/com/android/browser/TitleBarXLarge.java b/src/com/android/browser/TitleBarXLarge.java
new file mode 100644
index 0000000..fd6d67b
--- /dev/null
+++ b/src/com/android/browser/TitleBarXLarge.java
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 2010 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.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+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.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.MenuInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.browser.TabControl.TabChangeListener;
+import com.android.browser.UrlInputView.UrlInputListener;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * tabbed title bar for xlarge screen browser
+ */
+public class TitleBarXLarge extends TitleBarBase
+ implements TabChangeListener, UrlInputListener {
+
+ private static final int PROGRESS_MAX = 100;
+
+ private static final int TAB_WIDTH_SELECTED = 400;
+ private static final int TAB_WIDTH_UNSELECTED = 150;
+
+ private BrowserActivity mBrowserActivity;
+ private Drawable mStopDrawable;
+ private Drawable mReloadDrawable;
+ private Drawable mSelectedBackground;
+ private Drawable mUnselectedBackground;
+
+ private View mBackButton;
+ private View mForwardButton;
+ private View mStar;
+ private View mMenu;
+ private View mAllButton;
+ private TabScrollView mTabs;
+ private View mNewButton;
+ private TabControl mControl;
+ private UrlInputView mUrlView;
+
+ private boolean mIsInLandscape;
+ private Map<Tab, TabViewData> mTabMap;
+
+ private float mDensityScale;
+
+ public TitleBarXLarge(BrowserActivity context, TabControl tabcontrol) {
+ super(context);
+ mDensityScale = context.getResources().getDisplayMetrics().density;
+ mTabMap = new HashMap<Tab, TabViewData>();
+ mBrowserActivity = context;
+ mControl = tabcontrol;
+ Resources resources = context.getResources();
+ mSelectedBackground = resources.getDrawable(R.drawable.tab_selected_bg);
+ mUnselectedBackground = resources.getDrawable(R.drawable.tab_unselected_bg);
+ mStopDrawable = resources.getDrawable(R.drawable.progress_stop);
+ mReloadDrawable = resources.getDrawable(R.drawable.ic_reload);
+ rebuildLayout(context, true);
+ // register the tab change listener
+ mControl.setOnTabChangeListener(this);
+ }
+
+ void rebuildLayout() {
+ rebuildLayout(mBrowserActivity, false);
+ }
+
+ private void rebuildLayout(Context context, boolean rebuildData) {
+ removeAllViews();
+ LayoutInflater factory = LayoutInflater.from(context);
+ factory.inflate(R.layout.title_bar_tabbed, this);
+
+ mTabs = (TabScrollView) findViewById(R.id.tabs);
+ mNewButton = findViewById(R.id.newtab);
+ mUrlView = (UrlInputView) findViewById(R.id.editurl);
+ mAllButton = findViewById(R.id.all_btn);
+ // TODO: Change enabled states based on whether you can go
+ // back/forward. Probably should be done inside onPageStarted.
+ mBackButton = findViewById(R.id.back);
+ mForwardButton = findViewById(R.id.forward);
+ mStar = findViewById(R.id.star);
+ mMenu = findViewById(R.id.menu);
+ View.OnClickListener listener = new View.OnClickListener() {
+ public void onClick(View v) {
+ if (mBackButton == v) {
+ mBrowserActivity.getTopWindow().goBack();
+ } else if (mForwardButton == v) {
+ mBrowserActivity.getTopWindow().goForward();
+ } else if (mStar == v) {
+ mBrowserActivity.promptAddOrInstallBookmark();
+ } else if (mMenu == v) {
+ mBrowserActivity.openOptionsMenu();
+ } else if (mAllButton == v) {
+ // TODO: Show the new bookmarks/windows view.
+ mBrowserActivity.bookmarksOrHistoryPicker(false);
+ } else if (mNewButton == v) {
+ mBrowserActivity.openTabToHomePage();
+ }
+ }
+ };
+ mBackButton.setOnClickListener(listener);
+ mForwardButton.setOnClickListener(listener);
+ mStar.setOnClickListener(listener);
+ mAllButton.setOnClickListener(listener);
+ mMenu.setOnClickListener(listener);
+ mNewButton.setOnClickListener(listener);
+
+ mIsInLandscape = mBrowserActivity.getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE;
+ mUrlView.setVisibility(mIsInLandscape ? View.GONE : View.VISIBLE);
+ mUrlView.setUrlInputListener(this);
+ buildTabs(rebuildData);
+ // ensure title bar state
+ onCurrentTab(mControl.getCurrentTab());
+ }
+
+ void showUrlEditor(TabViewData tabdata) {
+ mUrlView.setVisibility(View.VISIBLE);
+ if (mIsInLandscape) {
+ mTabs.setVisibility(View.GONE);
+ mUrlView.requestFocus();
+ mUrlView.forceIme();
+ }
+ }
+
+ void hideUrlEditor() {
+ Tab tab = mControl.getCurrentTab();
+ if (mIsInLandscape) {
+ mUrlView.setVisibility(View.GONE);
+ mTabs.setVisibility(View.VISIBLE);
+ } else {
+ // portrait mode
+ mUrlView.setText(tab.getWebView().getUrl());
+ }
+ tab.getWebView().requestFocus();
+ }
+
+
+ // UrlInputListener implementation
+
+ @Override
+ public void onAction(String text) {
+ hideUrlEditor();
+ Intent i = new Intent();
+ i.setAction(Intent.ACTION_SEARCH);
+ i.putExtra(SearchManager.QUERY, text);
+ mBrowserActivity.onNewIntent(i);
+ }
+
+ @Override
+ public void onDismiss() {
+ hideUrlEditor();
+ }
+
+ @Override
+ public void createContextMenu(ContextMenu menu) {
+ MenuInflater inflater = mBrowserActivity.getMenuInflater();
+ inflater.inflate(R.menu.title_context, menu);
+ mBrowserActivity.onCreateContextMenu(menu, this, null);
+ }
+
+ @Override
+ /* package */ void setLock(Drawable d) {
+ // TODO: handle in tab specific callback
+ }
+
+ @Override
+ /* package */ void setFavicon(Bitmap icon) {
+ // this is handled in the tab specific callback
+ }
+
+ /**
+ * Update the progress, from 0 to 100.
+ */
+ @Override
+ /* package */ void setProgress(int newProgress) {
+ // this is handled in tab specific callback
+ }
+
+ @Override
+ /* package */ void setDisplayTitle(String title) {
+ // this is done in tab specific callback
+ }
+
+ private void buildTabs(boolean needsRebuilding) {
+ mTabs.clearTabs();
+ for (int i = 0; i < mControl.getTabCount(); i++) {
+ Tab tab = mControl.getTab(i);
+ TabViewData data = buildTab(needsRebuilding, tab);
+ TabView tv = buildView(data);
+ }
+ mTabs.setSelectedTab(mControl.getCurrentIndex());
+ }
+
+ private TabViewData buildTab(boolean needsRebuilding, Tab tab) {
+ TabViewData data = null;
+ if (needsRebuilding) {
+ data = new TabViewData(tab);
+ mTabMap.put(tab, data);
+ } else {
+ data = mTabMap.get(tab);
+ }
+ return data;
+ }
+
+ private TabView buildView(final TabViewData data) {
+ TabView tv = new TabView(mBrowserActivity, data);
+ tv.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mTabs.getSelectedTab() == v) {
+ showUrlEditor(data);
+ } else {
+ int ix = mControl.getTabIndex(data.mTab);
+ mTabs.setSelectedTab(ix);
+ mBrowserActivity.switchToTab(ix);
+ }
+ }
+ });
+ mTabs.addTab(tv);
+ return tv;
+ }
+
+ /**
+ * the views used in the tab bar
+ */
+ class TabView extends LinearLayout {
+
+ TabViewData mTabData;
+ View mTabContent;
+ TextView mTitle;
+ ImageView mIconView;
+ ImageView mLock;
+ CircularProgressView mStop;
+ ImageView mClose;
+ boolean mSelected;
+ boolean mInLoad;
+
+ /**
+ * @param context
+ */
+ public TabView(Context context, TabViewData tab) {
+ super(context);
+ mTabData = tab;
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ mTabContent = inflater.inflate(R.layout.tab_title, this);
+ mTitle = (TextView) mTabContent.findViewById(R.id.title);
+ mIconView = (ImageView) mTabContent.findViewById(R.id.favicon);
+ mLock = (ImageView) mTabContent.findViewById(R.id.lock);
+ mStop = (CircularProgressView) mTabContent.findViewById(R.id.stop);
+ mStop.setMaxProgress(PROGRESS_MAX);
+ mClose = (ImageView) mTabContent.findViewById(R.id.close);
+ mClose.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ closeTab();
+ }
+ });
+ mStop.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mInLoad) {
+ mBrowserActivity.stopLoading();
+ } else {
+ mBrowserActivity.getTopWindow().reload();
+ }
+ }
+ });
+ mSelected = false;
+ mInLoad = false;
+ // update the status
+ updateFromData();
+ }
+
+ private void updateFromData() {
+ mTabData.mTabView = this;
+ if (mTabData.mUrl != null) {
+ setDisplayTitle(mTabData.mUrl);
+ }
+ if (mTabData.mTitle != null) {
+ setDisplayTitle(mTabData.mTitle);
+ }
+ setProgress(mTabData.mProgress);
+ if (mTabData.mIcon != null) {
+ setFavicon(mTabData.mIcon);
+ }
+ if (mTabData.mLock != null) {
+ setLock(mTabData.mLock);
+ }
+ }
+
+ @Override
+ public void setSelected(boolean selected) {
+ mSelected = selected;
+ mStop.setVisibility(mSelected ? View.VISIBLE : View.GONE);
+ mIconView.setVisibility(mSelected ? View.VISIBLE : View.GONE);
+ super.setSelected(selected);
+ setBackgroundDrawable(selected ? mSelectedBackground
+ : mUnselectedBackground);
+ setLayoutParams(new LayoutParams(selected ?
+ (int) (TAB_WIDTH_SELECTED * mDensityScale)
+ : (int) (TAB_WIDTH_UNSELECTED * mDensityScale),
+ LayoutParams.WRAP_CONTENT));
+ }
+
+ void setDisplayTitle(String title) {
+ mTitle.setText(title);
+ }
+
+ void setFavicon(Drawable d) {
+ mIconView.setImageDrawable(d);
+ }
+
+ void setLock(Drawable d) {
+ if (null == d) {
+ mLock.setVisibility(View.GONE);
+ } else {
+ mLock.setImageDrawable(d);
+ mLock.setVisibility(View.VISIBLE);
+ }
+ }
+
+ void setTitleCompoundDrawables(Drawable left, Drawable top,
+ Drawable right, Drawable bottom) {
+ mTitle.setCompoundDrawables(left, top, right, bottom);
+ }
+
+ void setProgress(int newProgress) {
+ mStop.setProgress(newProgress);
+ if (newProgress >= PROGRESS_MAX) {
+ mInLoad = false;
+ mStop.setImageDrawable(mReloadDrawable);
+ } else {
+ if (!mInLoad && getWindowToken() != null) {
+ // checking the window token lets us be sure that we
+ // are attached to a window before starting the animation,
+ // preventing a potential race condition
+ // (fix for bug http://b/2115736)
+ mInLoad = true;
+ mStop.setImageDrawable(mStopDrawable);
+ }
+ }
+ }
+
+ private void closeTab() {
+ if (mTabData.mTab == mControl.getCurrentTab()) {
+ mBrowserActivity.closeCurrentWindow();
+ } else {
+ mBrowserActivity.closeTab(mTabData.mTab);
+ }
+ }
+
+ }
+
+ /**
+ * class to store tab state within the title bar
+ */
+ class TabViewData {
+
+ Tab mTab;
+ TabView mTabView;
+ int mProgress;
+ Drawable mIcon;
+ Drawable mLock;
+ String mTitle;
+ String mUrl;
+
+ TabViewData(Tab tab) {
+ mTab = tab;
+ }
+
+ void setUrlAndTitle(String url, String title) {
+ mUrl = url;
+ mTitle = title;
+ if (mTabView != null) {
+ if (title != null) {
+ mTabView.setDisplayTitle(title);
+ } else if (url != null) {
+ mTabView.setDisplayTitle(url);
+ }
+ }
+ }
+
+ void setProgress(int newProgress) {
+ mProgress = newProgress;
+ if (mTabView != null) {
+ mTabView.setProgress(mProgress);
+ }
+ }
+
+ void setFavicon(Bitmap icon) {
+ Drawable[] array = new Drawable[3];
+ array[0] = new PaintDrawable(Color.BLACK);
+ array[1] = new PaintDrawable(Color.WHITE);
+ if (icon == null) {
+ array[2] = mGenericFavicon;
+ } else {
+ array[2] = new BitmapDrawable(icon);
+ }
+ LayerDrawable d = new LayerDrawable(array);
+ d.setLayerInset(1, 1, 1, 1, 1);
+ d.setLayerInset(2, 2, 2, 2, 2);
+ mIcon = d;
+ if (mTabView != null) {
+ mTabView.setFavicon(mIcon);
+ }
+ }
+
+ }
+
+ // TabChangeListener implementation
+
+ @Override
+ public void onCurrentTab(Tab tab) {
+ mTabs.setSelectedTab(mControl.getCurrentIndex());
+ TabViewData tvd = mTabMap.get(tab);
+ if (tvd != null) {
+ if (tvd.mUrl != null) {
+ mUrlView.setText(tvd.mUrl);
+ }
+ setProgress(tvd.mProgress);
+ }
+ }
+
+ @Override
+ public void onFavicon(Tab tab, Bitmap favicon) {
+ TabViewData tvd = mTabMap.get(tab);
+ if (tvd != null) {
+ tvd.setFavicon(favicon);
+ }
+ }
+
+ @Override
+ public void onNewTab(Tab tab) {
+ TabViewData tvd = buildTab(true, tab);
+ buildView(tvd);
+ }
+
+ @Override
+ public void onProgress(Tab tab, int progress) {
+ TabViewData tvd = mTabMap.get(tab);
+ if (tvd != null) {
+ tvd.setProgress(progress);
+ }
+ if (tab == mControl.getCurrentTab()) {
+ setProgress(progress);
+ }
+ }
+
+ @Override
+ public void onRemoveTab(Tab tab) {
+ TabViewData tvd = mTabMap.get(tab);
+ TabView tv = tvd.mTabView;
+ if (tv != null) {
+ mTabs.removeTab(tv);
+ }
+ mTabMap.remove(tab);
+ }
+
+ @Override
+ public void onUrlAndTitle(Tab tab, String url, String title) {
+ TabViewData tvd = mTabMap.get(tab);
+ if (tvd != null) {
+ tvd.setUrlAndTitle(url, title);
+ }
+ if ((url != null) && (tab == mControl.getCurrentTab())) {
+ mUrlView.setText(url);
+ }
+ }
+
+ @Override
+ public void onPageFinished(Tab tab) {
+ }
+
+ @Override
+ public void onPageStarted(Tab tab) {
+ }
+
+}
diff --git a/src/com/android/browser/UrlInputView.java b/src/com/android/browser/UrlInputView.java
new file mode 100644
index 0000000..3841257
--- /dev/null
+++ b/src/com/android/browser/UrlInputView.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2010 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.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
+import android.widget.AutoCompleteTextView;
+import android.widget.CursorAdapter;
+import android.widget.Filterable;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.AdapterView.OnItemClickListener;
+
+/**
+ * url/search input view
+ * handling suggestions
+ */
+public class UrlInputView extends AutoCompleteTextView {
+
+ private UrlInputListener mListener;
+ private InputMethodManager mInputManager;
+ private SuggestionsAdapter mAdapter;
+ private Drawable mFocusDrawable;
+ private Drawable mNoFocusDrawable;
+
+
+ public UrlInputView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init(context);
+ }
+
+ public UrlInputView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ public UrlInputView(Context context) {
+ super(context);
+ init(context);
+ }
+
+ private void init(Context ctx) {
+ mFocusDrawable = ctx.getResources().getDrawable(R.drawable.textfield_stroke);
+ mNoFocusDrawable = ctx.getResources().getDrawable(R.drawable.textfield_nostroke);
+ mInputManager = (InputMethodManager) ctx.getSystemService(Context.INPUT_METHOD_SERVICE);
+ setOnEditorActionListener(new OnEditorActionListener() {
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ finishInput(getText().toString());
+ return true;
+ }
+ });
+ setOnFocusChangeListener(new OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ setBackgroundDrawable(hasFocus ? mFocusDrawable : mNoFocusDrawable);
+ }
+ });
+ final ContentResolver cr = mContext.getContentResolver();
+ mAdapter = new SuggestionsAdapter(mContext,
+ BrowserProvider.getBookmarksSuggestions(cr, null));
+ setAdapter(mAdapter);
+ setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ String url = mAdapter.getViewString(view);
+ finishInput(url);
+ }
+ });
+ setSelectAllOnFocus(true);
+ }
+
+ public void setUrlInputListener(UrlInputListener listener) {
+ mListener = listener;
+ }
+
+ public void forceIme() {
+ mInputManager.showSoftInput(this, 0);
+ }
+
+ private void finishInput(String url) {
+ this.dismissDropDown();
+ mInputManager.hideSoftInputFromWindow(getWindowToken(), 0);
+ if (url == null) {
+ mListener.onDismiss();
+ } else {
+ mListener.onAction(url);
+ }
+
+ }
+
+ @Override
+ public boolean onKeyPreIme(int keyCode, KeyEvent evt) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ // catch back key in order to do slightly more cleanup than usual
+ finishInput(null);
+ return true;
+ }
+ return super.onKeyPreIme(keyCode, evt);
+ }
+
+ interface UrlInputListener {
+
+ public void onDismiss();
+
+ public void onAction(String text);
+
+ }
+
+ /**
+ * adapter used by suggestion dropdown
+ */
+ class SuggestionsAdapter extends CursorAdapter implements Filterable {
+
+ private Cursor mLastCursor;
+ private ContentResolver mContent;
+ private int mIndexText1;
+ private int mIndexText2;
+ private int mIndexIcon;
+
+ public SuggestionsAdapter(Context context, Cursor c) {
+ super(context, c);
+ mContent = context.getContentResolver();
+ mIndexText1 = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1);
+ mIndexText2 = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2_URL);
+ mIndexIcon = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1);
+ }
+
+ public String getViewString(View view) {
+ TextView tv2 = (TextView) view.findViewById(android.R.id.text2);
+ if (tv2.getText().length() > 0) {
+ return tv2.getText().toString();
+ } else {
+ TextView tv1 = (TextView) view.findViewById(android.R.id.text1);
+ return tv1.getText().toString();
+ }
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ final LayoutInflater inflater = LayoutInflater.from(context);
+ final View view = inflater.inflate(
+ R.layout.simple_dropdown_item_2line, parent, false);
+ bindView(view, context, cursor);
+ return view;
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ TextView tv1 = (TextView) view.findViewById(android.R.id.text1);
+ TextView tv2 = (TextView) view.findViewById(android.R.id.text2);
+ ImageView ic1 = (ImageView) view.findViewById(R.id.icon1);
+ tv1.setText(cursor.getString(mIndexText1));
+ String url = cursor.getString(mIndexText2);
+ tv2.setText((url != null) ? url : "");
+ // assume an id
+ try {
+ int id = Integer.parseInt(cursor.getString(mIndexIcon));
+ Drawable d = context.getResources().getDrawable(id);
+ ic1.setImageDrawable(d);
+ } catch (NumberFormatException nfx) {
+ }
+ }
+
+ @Override
+ public String convertToString(Cursor cursor) {
+ return cursor.getString(mIndexText1);
+ }
+
+ @Override
+ public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
+ if (getFilterQueryProvider() != null) {
+ return getFilterQueryProvider().runQuery(constraint);
+ }
+ mLastCursor = BrowserProvider.getBookmarksSuggestions(mContent,
+ (constraint != null) ? constraint.toString() : null);
+ return mLastCursor;
+ }
+
+ }
+
+}
diff --git a/src/com/android/browser/WebDialog.java b/src/com/android/browser/WebDialog.java
new file mode 100644
index 0000000..9995e8f
--- /dev/null
+++ b/src/com/android/browser/WebDialog.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 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.View;
+import android.view.animation.AnimationUtils;
+import android.view.inputmethod.InputMethodManager;
+import android.webkit.WebView;
+import android.widget.LinearLayout;
+
+/* package */ class WebDialog extends LinearLayout {
+ protected WebView mWebView;
+ protected BrowserActivity mBrowserActivity;
+ private boolean mIsVisible;
+
+ /* package */ WebDialog(BrowserActivity context) {
+ super(context);
+ mBrowserActivity = context;
+ }
+
+ /* dialogs that have cancel buttons can optionally share code by including a
+ * view with an id of 'done'.
+ */
+ protected void addCancel() {
+ View button = findViewById(R.id.done);
+ if (button != null) button.setOnClickListener(mCancelListener);
+ }
+
+ private View.OnClickListener mCancelListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ mBrowserActivity.closeDialogs();
+ }
+ };
+
+ protected void dismiss() {
+ startAnimation(AnimationUtils.loadAnimation(mBrowserActivity,
+ R.anim.dialog_exit));
+ mIsVisible = false;
+ }
+
+ /*
+ * Remove the soft keyboard from the screen.
+ */
+ protected void hideSoftInput() {
+ InputMethodManager imm = (InputMethodManager)
+ mBrowserActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(mWebView.getWindowToken(), 0);
+ }
+
+ protected boolean isVisible() {
+ return mIsVisible;
+ }
+
+ /* package */ void setWebView(WebView webview) {
+ mWebView = webview;
+ }
+
+ protected void show() {
+ startAnimation(AnimationUtils.loadAnimation(mBrowserActivity,
+ R.anim.dialog_enter));
+ mIsVisible = true;
+ }
+
+}
diff --git a/tests/src/com/android/browser/PopularUrlsTest.java b/tests/src/com/android/browser/PopularUrlsTest.java
index b1760e9..547cb9f 100644
--- a/tests/src/com/android/browser/PopularUrlsTest.java
+++ b/tests/src/com/android/browser/PopularUrlsTest.java
@@ -307,7 +307,6 @@
public void write() throws IOException {
FileWriter output = null;
- OutputStreamWriter writer = null;
if (mFile.exists()) {
mFile.delete();
}
@@ -317,14 +316,8 @@
output.write(page + newLine);
output.write(url + newLine);
} finally {
- try {
- if (writer != null) {
- writer.close();
- }
- } finally {
- if (output != null) {
- output.close();
- }
+ if (output != null) {
+ output.close();
}
}
}
@@ -394,6 +387,7 @@
if (mStatus.getIsRecovery()) {
Log.e(TAG, "Recovering after crash: " + iterator.next());
+ mStatus.incrementPage();
}
while (mStatus.getIteration() < loopCount) {
diff --git a/tests/src/com/android/browser/TestWebChromeClient.java b/tests/src/com/android/browser/TestWebChromeClient.java
index d78eaed..53f8db3 100644
--- a/tests/src/com/android/browser/TestWebChromeClient.java
+++ b/tests/src/com/android/browser/TestWebChromeClient.java
@@ -195,7 +195,7 @@
/** {@inheritDoc} */
@Override
- public void openFileChooser(ValueCallback<Uri> uploadFile) {
- mWrappedClient.openFileChooser(uploadFile);
+ public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType) {
+ mWrappedClient.openFileChooser(uploadFile, acceptType);
}
}