am 539fde68: am f6915fbb: Add more choices to about:debug UAString setting
Merge commit '539fde6807f0d9a5a86dc09ce362a2dc9ba0bc10'
* commit '539fde6807f0d9a5a86dc09ce362a2dc9ba0bc10':
Add more choices to about:debug UAString setting
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 7e98019..b46a41f 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -184,6 +184,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/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/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/drawable/button_selected.png b/res/drawable/button_selected.png
new file mode 100644
index 0000000..4fd1aa3
--- /dev/null
+++ b/res/drawable/button_selected.png
Binary files differ
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/ic_arrow_left.png b/res/drawable/ic_arrow_left.png
new file mode 100644
index 0000000..58fd2ca
--- /dev/null
+++ b/res/drawable/ic_arrow_left.png
Binary files differ
diff --git a/res/drawable/ic_arrow_right.png b/res/drawable/ic_arrow_right.png
new file mode 100644
index 0000000..aaf3fde
--- /dev/null
+++ b/res/drawable/ic_arrow_right.png
Binary files differ
diff --git a/res/drawable/ic_menu.png b/res/drawable/ic_menu.png
new file mode 100644
index 0000000..520b2c3
--- /dev/null
+++ b/res/drawable/ic_menu.png
Binary files differ
diff --git a/res/drawable/ic_menu_downloads.png b/res/drawable/ic_menu_downloads.png
new file mode 100644
index 0000000..fff5022
--- /dev/null
+++ b/res/drawable/ic_menu_downloads.png
Binary files differ
diff --git a/res/drawable/ic_menu_find.png b/res/drawable/ic_menu_find.png
new file mode 100644
index 0000000..4d96348
--- /dev/null
+++ b/res/drawable/ic_menu_find.png
Binary files differ
diff --git a/res/drawable/ic_menu_pageinfo.png b/res/drawable/ic_menu_pageinfo.png
new file mode 100644
index 0000000..c04f0e3
--- /dev/null
+++ b/res/drawable/ic_menu_pageinfo.png
Binary files differ
diff --git a/res/drawable/ic_menu_settings.png b/res/drawable/ic_menu_settings.png
new file mode 100644
index 0000000..7a642d6
--- /dev/null
+++ b/res/drawable/ic_menu_settings.png
Binary files differ
diff --git a/res/drawable/ic_menu_share.png b/res/drawable/ic_menu_share.png
new file mode 100644
index 0000000..ea2b672
--- /dev/null
+++ b/res/drawable/ic_menu_share.png
Binary files differ
diff --git a/res/drawable/ic_pages.png b/res/drawable/ic_pages.png
new file mode 100644
index 0000000..fba4651
--- /dev/null
+++ b/res/drawable/ic_pages.png
Binary files differ
diff --git a/res/drawable/ic_reload.png b/res/drawable/ic_reload.png
new file mode 100644
index 0000000..ec0c238
--- /dev/null
+++ b/res/drawable/ic_reload.png
Binary files differ
diff --git a/res/drawable/ic_star.png b/res/drawable/ic_star.png
new file mode 100644
index 0000000..20a40de
--- /dev/null
+++ b/res/drawable/ic_star.png
Binary files differ
diff --git a/res/drawable/ic_stop.png b/res/drawable/ic_stop.png
new file mode 100644
index 0000000..7ee56e9
--- /dev/null
+++ b/res/drawable/ic_stop.png
Binary files differ
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/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/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/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_xlarge.xml b/res/layout/title_bar_xlarge.xml
new file mode 100644
index 0000000..3070b8b
--- /dev/null
+++ b/res/layout/title_bar_xlarge.xml
@@ -0,0 +1,118 @@
+<?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="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"
+ 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"
+ />
+
+ <LinearLayout android:id="@+id/title_bg"
+ android:background="@drawable/textfield_stroke"
+ android:layout_width="0dip"
+ android:layout_weight="1.0"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="6dip"
+ 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"
+ />
+ </LinearLayout>
+ <ImageButton android:id="@+id/stop"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginRight="6dip"
+ android:src="@drawable/ic_stop"
+ 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>
+
+ <!-- Should show translucent over the webpage -->
+ <ProgressBar android:id="@+id/progress_horizontal"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="match_parent"
+ android:layout_height="14dip"
+ android:max="100"
+ />
+
+</LinearLayout>
diff --git a/res/menu-xlarge/browser.xml b/res/menu-xlarge/browser.xml
new file mode 100644
index 0000000..2de278f
--- /dev/null
+++ b/res/menu-xlarge/browser.xml
@@ -0,0 +1,95 @@
+<?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" />
+ <!-- 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..da36b73 100644
--- a/res/menu/browser.xml
+++ b/res/menu/browser.xml
@@ -41,21 +41,23 @@
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" />
<!-- followings are debug only -->
<item android:id="@+id/dump_nav_menu_id"
@@ -91,16 +93,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/strings.xml b/res/values/strings.xml
index 07c2eb9..da75762 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-->
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..104a495 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);
@@ -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..aeb8c46 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;
@@ -133,6 +123,7 @@
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.Vector;
public class BrowserActivity extends Activity
implements View.OnCreateContextMenuListener, DownloadListener {
@@ -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,10 +210,25 @@
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;
+
+ if (mXLargeScreenSize) {
+ mTitleBar = new TitleBarXLarge(this);
+ 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);
+ }
// Create the tab control and our initial tab
mTabControl = new TabControl(this);
@@ -305,9 +317,7 @@
}
if (permissionOk) {
PluginManager.getInstance(BrowserActivity.this)
- .refreshPlugins(
- Intent.ACTION_PACKAGE_ADDED
- .equals(action));
+ .refreshPlugins(true);
}
}
}
@@ -612,6 +622,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 +684,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 +709,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
@@ -827,6 +843,7 @@
}
private void showFakeTitleBar() {
+ if (mXLargeScreenSize) return;
if (mFakeTitleBar.getParent() == null && mActiveTabsPage == null
&& !mActivityInPause) {
WebView mainView = mTabControl.getCurrentWebView();
@@ -834,6 +851,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 +892,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();
@@ -1113,12 +1137,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 +1171,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 +1288,7 @@
*/
/* package */ void removeActiveTabPage(boolean needToAttach) {
mContentView.removeView(mActiveTabsPage);
+ mTitleBar.setVisibility(View.VISIBLE);
mActiveTabsPage = null;
mMenuState = R.id.MAIN_MENU;
if (needToAttach) {
@@ -1271,6 +1297,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 +1347,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 +1355,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 +1400,9 @@
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();
- break;
case R.id.page_info_menu_id:
showPageInfo(mTabControl.getCurrentTab(), false);
break;
@@ -1394,7 +1421,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 +1478,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 +1586,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 +1618,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 +1645,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 +1702,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 +1769,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 +1798,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 +1928,7 @@
return true;
}
+ @Override
public void run() {
Drawable oldWallpaper = BrowserActivity.this.getWallpaper();
try {
@@ -1919,7 +2033,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 +2078,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 +2096,7 @@
}
mTabControl.setCurrentTab(mTabControl.getTab(currentIndex));
resetTitleIconAndProgress();
+ updateLockIconToLatest();
}
/* package */ void goBackOnePageOrQuit() {
@@ -2169,9 +2288,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 +2314,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 +2322,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 +2360,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 +2408,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 +2485,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 +2499,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 +2509,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 +2550,7 @@
onProgressChanged(view, INITIAL_PROGRESS);
mDidStopLoad = false;
if (!mIsNetworkUp) createAndShowNetworkDialog();
-
+ closeDialogs();
if (mSettings.isTracing()) {
String host;
try {
@@ -2560,6 +2648,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 +2669,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 +2715,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 +2732,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 +2757,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 +2865,120 @@
* 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 mediaSourceKey = "source";
+ final String mediaSourceValueCamera = "camera";
+ final String mediaSourceValueGallery = "gallery";
+ final String mediaSourceValueCamcorder = "camcorder";
+
+ // media source can be 'gallery' or 'camera' or 'camcorder'
+ 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);
+
+ 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(mediaSourceValueGallery)) {
+ // Specified gallery 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(mediaSourceValueGallery)) {
+ // Specified gallery as the source, so don't want to consider the camcorder.
+ addCamcorderIntent = false;
+ }
+ } 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 +3165,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 +3182,9 @@
d = mMixLockIcon;
}
mTitleBar.setLock(d);
- mFakeTitleBar.setLock(d);
+ if (!mXLargeScreenSize) {
+ mFakeTitleBar.setLock(d);
+ }
}
/**
@@ -3468,8 +3700,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 +3739,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(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 +3794,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 +3823,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 +4044,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 +4061,7 @@
}
// Executes on the UI thread
+ @Override
protected void onPostExecute(Set<String> installedPackages) {
addPackageNames(installedPackages);
}
@@ -3770,6 +4090,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 +4204,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..320c438 100644
--- a/src/com/android/browser/BrowserBookmarksPage.java
+++ b/src/com/android/browser/BrowserBookmarksPage.java
@@ -74,10 +74,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 +104,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) {
@@ -438,8 +432,7 @@
loadUrl(position);
}
} else {
- final Intent intent = createShortcutIntent(position);
- setResultToParent(RESULT_OK, intent);
+ setResultToParent(RESULT_OK, createShortcutIntent(position));
finish();
}
}
@@ -449,99 +442,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() {
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..1eec52b 100644
--- a/src/com/android/browser/BrowserProvider.java
+++ b/src/com/android/browser/BrowserProvider.java
@@ -465,7 +465,7 @@
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;
@@ -846,8 +846,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")
diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java
index 769dfca..ad1d1c0 100644
--- a/src/com/android/browser/BrowserSettings.java
+++ b/src/com/android/browser/BrowserSettings.java
@@ -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/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 b5369ae..99925dc 100644
--- a/src/com/android/browser/DownloadTouchIcon.java
+++ b/src/com/android/browser/DownloadTouchIcon.java
@@ -24,10 +24,11 @@
import android.graphics.BitmapFactory;
import android.net.http.AndroidHttpClient;
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.HttpResponse;
import org.apache.http.client.methods.HttpGet;
@@ -42,9 +43,16 @@
private Cursor mCursor;
private final String mOriginalUrl;
private final String mUrl;
- private final String mUserAgent;
+ private final String mUserAgent; // Sites may serve a different icon to different UAs
+ private Message mMessage;
+
/* 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, ContentResolver cr, WebView view) {
mTab = tab;
mContentResolver = cr;
@@ -54,6 +62,13 @@
mUserAgent = view.getSettings().getUserAgentString();
}
+ /**
+ * 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(ContentResolver cr, String url) {
mTab = null;
mContentResolver = cr;
@@ -62,15 +77,32 @@
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(Message msg, String userAgent) {
+ mMessage = msg;
+ 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);
HttpGet request = new HttpGet(url);
// Follow redirects
@@ -78,7 +110,6 @@
try {
HttpResponse response = client.execute(request);
-
if (response.getStatusLine().getStatusCode() == 200) {
HttpEntity entity = response.getEntity();
if (entity != null) {
@@ -86,7 +117,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);
+ }
}
}
}
@@ -98,9 +134,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 d2fed22..f7438da 100644
--- a/src/com/android/browser/Tab.java
+++ b/src/com/android/browser/Tab.java
@@ -87,7 +87,7 @@
// The Geolocation permissions prompt
private GeolocationPermissionsPrompt mGeolocationPermissionsPrompt;
// Main WebView wrapper
- private View mContainer;
+ private LinearLayout mContainer;
// Main WebView
private WebView mMainView;
// Subwindow container
@@ -1041,6 +1041,16 @@
}
@Override
+ public void onSelectionDone() {
+ if (mInForeground) mActivity.closeDialogs();
+ }
+
+ @Override
+ public void onSelectionStart() {
+ if (mInForeground) mActivity.showSelectDialog();
+ }
+
+ @Override
public void onShowCustomView(View view,
WebChromeClient.CustomViewCallback callback) {
if (mInForeground) mActivity.onShowCustomView(view, callback);
@@ -1185,9 +1195,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);
}
@@ -1220,9 +1230,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 +1331,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 +1442,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 +1451,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 +1490,7 @@
*/
void dismissSubWindow() {
if (mSubView != null) {
+ mActivity.closeDialogs();
BrowserSettings.getInstance().deleteObserver(
mSubView.getSettings());
mSubView.destroy();
@@ -1493,6 +1515,7 @@
void removeSubWindow(ViewGroup content) {
if (mSubView != null) {
content.removeView(mSubViewContainer);
+ mActivity.closeDialogs();
}
}
@@ -1551,6 +1574,7 @@
(FrameLayout) mContainer.findViewById(R.id.webview_wrapper);
wrapper.removeView(mMainView);
content.removeView(mContainer);
+ mActivity.closeDialogs();
removeSubWindow(content);
}
@@ -1824,6 +1848,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 +1859,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();
@@ -1946,4 +1975,36 @@
}
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);
+ }
}
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..3d234e8
--- /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;
+
+ private 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..0d799a8
--- /dev/null
+++ b/src/com/android/browser/TitleBarXLarge.java
@@ -0,0 +1,170 @@
+/*
+ * 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.res.Resources;
+import android.graphics.drawable.Animatable;
+import android.graphics.drawable.Drawable;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.MenuInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.android.common.speech.LoggingEvents;
+
+/**
+ * This class represents a title bar for a particular "tab" or "window" in the
+ * browser.
+ */
+public class TitleBarXLarge extends TitleBarBase {
+ private Drawable mCircularProgress;
+ private ProgressBar mHorizontalProgress;
+ private Drawable mStopDrawable;
+ private Drawable mReloadDrawable;
+ private boolean mInLoad;
+ private BrowserActivity mBrowserActivity;
+
+ private final View mBackButton;
+ private final View mForwardButton;
+ private final View mStar;
+ private final View mMenu;
+ private final ImageView mStopButton;
+ private final TextView mTitle;
+ private final View mAllButton;
+
+ public TitleBarXLarge(BrowserActivity context) {
+ super(context);
+ Resources resources = context.getResources();
+ LayoutInflater factory = LayoutInflater.from(context);
+ factory.inflate(R.layout.title_bar_xlarge, this);
+ mBrowserActivity = context;
+
+ mTitle = (TextView) findViewById(R.id.title);
+ mTitle.setCompoundDrawablePadding(5);
+ mTitle.setLongClickable(true);
+
+ mLockIcon = (ImageView) findViewById(R.id.lock);
+ mFavicon = (ImageView) findViewById(R.id.favicon);
+ mStopButton = (ImageView) findViewById(R.id.stop);
+ mStopDrawable = mStopButton.getDrawable();
+ mReloadDrawable = resources.getDrawable(R.drawable.ic_reload);
+
+ mAllButton = (ImageView) findViewById(R.id.all_btn);
+ mCircularProgress = (Drawable) resources.getDrawable(
+ com.android.internal.R.drawable.search_spinner);
+ DisplayMetrics metrics = resources.getDisplayMetrics();
+ int iconDimension = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, 20f, metrics);
+ mCircularProgress.setBounds(0, 0, iconDimension, iconDimension);
+ mHorizontalProgress = (ProgressBar) findViewById(
+ R.id.progress_horizontal);
+ mHorizontalProgress.setProgressDrawable(
+ resources.getDrawable(R.drawable.progress));
+
+ // FIXME: 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 (mStopButton == v) {
+ if (mInLoad) {
+ mBrowserActivity.stopLoading();
+ } else {
+ mBrowserActivity.getTopWindow().reload();
+ }
+ } else if (mTitle == v) {
+ mBrowserActivity.editUrl();
+ } else if (mAllButton == v) {
+ // FIXME: Show the new bookmarks/windows view.
+ mBrowserActivity.bookmarksOrHistoryPicker(false);
+ }
+ }
+ };
+ mBackButton.setOnClickListener(listener);
+ mForwardButton.setOnClickListener(listener);
+ mStar.setOnClickListener(listener);
+ mStopButton.setOnClickListener(listener);
+ mTitle.setOnClickListener(listener);
+ mAllButton.setOnClickListener(listener);
+ mMenu.setOnClickListener(listener);
+ }
+
+ @Override
+ public void createContextMenu(ContextMenu menu) {
+ MenuInflater inflater = mBrowserActivity.getMenuInflater();
+ inflater.inflate(R.menu.title_context, menu);
+ mBrowserActivity.onCreateContextMenu(menu, this, null);
+ }
+
+ /**
+ * Update the progress, from 0 to 100.
+ */
+ /* package */ void setProgress(int newProgress) {
+ if (newProgress >= mHorizontalProgress.getMax()) {
+ mTitle.setCompoundDrawables(null, null, null, null);
+ ((Animatable) mCircularProgress).stop();
+ mHorizontalProgress.setVisibility(View.GONE);
+ mInLoad = false;
+ mStopButton.setImageDrawable(mReloadDrawable);
+ } else {
+ mHorizontalProgress.setProgress(newProgress);
+ 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)
+ mTitle.setCompoundDrawables(null, null, mCircularProgress,
+ null);
+ ((Animatable) mCircularProgress).start();
+ mHorizontalProgress.setVisibility(View.VISIBLE);
+ mInLoad = true;
+ mStopButton.setImageDrawable(mStopDrawable);
+ }
+ }
+ }
+
+ /**
+ * Update the text displayed in the title bar.
+ * @param title String to display. If null, the loading string will be
+ * shown.
+ */
+ /* package */ void setDisplayTitle(String title) {
+ if (title == null) {
+ mTitle.setText(R.string.title_bar_loading);
+ } else {
+ mTitle.setText(title);
+ }
+ }
+
+}
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/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);
}
}