Merge commit 'remotes/korg/cupcake' into merge
diff --git a/Android.mk b/Android.mk
index 61ca3a3..b95e3d8 100644
--- a/Android.mk
+++ b/Android.mk
@@ -17,7 +17,7 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := user eng development
+LOCAL_MODULE_TAGS := user
LOCAL_SRC_FILES := $(call all-subdir-java-files)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9668483..272f7c1 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -20,7 +20,8 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.launcher"
- android:sharedUserId="android.uid.shared">
+ android:sharedUserId="android.uid.shared"
+ android:sharedUserLabel="@string/application_name">
<permission
android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
@@ -69,7 +70,8 @@
android:launchMode="singleTask"
android:clearTaskOnLaunch="true"
android:stateNotNeeded="true"
- android:theme="@style/Theme">
+ android:theme="@style/Theme"
+ android:windowSoftInputMode="stateUnspecified|adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME"/>
diff --git a/res/drawable/bg_appwidget_error.9.png b/res/drawable/bg_appwidget_error.9.png
new file mode 100644
index 0000000..5077424
--- /dev/null
+++ b/res/drawable/bg_appwidget_error.9.png
Binary files differ
diff --git a/res/drawable/clock_dial.png b/res/drawable/clock_dial.png
deleted file mode 100644
index eda3d17..0000000
--- a/res/drawable/clock_dial.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/clock_hour.png b/res/drawable/clock_hour.png
deleted file mode 100644
index fcfd948..0000000
--- a/res/drawable/clock_hour.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/clock_minute.png b/res/drawable/clock_minute.png
deleted file mode 100644
index afc0a3f..0000000
--- a/res/drawable/clock_minute.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/ic_launcher_alarmclock.png b/res/drawable/ic_launcher_alarmclock.png
deleted file mode 100755
index 30ff267..0000000
--- a/res/drawable/ic_launcher_alarmclock.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/ic_launcher_application.png b/res/drawable/ic_launcher_application.png
new file mode 100644
index 0000000..7502484
--- /dev/null
+++ b/res/drawable/ic_launcher_application.png
Binary files differ
diff --git a/res/drawable/ic_launcher_appwidget.png b/res/drawable/ic_launcher_appwidget.png
new file mode 100644
index 0000000..cb40a19
--- /dev/null
+++ b/res/drawable/ic_launcher_appwidget.png
Binary files differ
diff --git a/res/drawable/ic_launcher_empty.png b/res/drawable/ic_launcher_empty.png
new file mode 100644
index 0000000..59bb6c5
--- /dev/null
+++ b/res/drawable/ic_launcher_empty.png
Binary files differ
diff --git a/res/drawable/ic_search_gadget.png b/res/drawable/ic_search_widget.png
similarity index 100%
rename from res/drawable/ic_search_gadget.png
rename to res/drawable/ic_search_widget.png
Binary files differ
diff --git a/res/drawable/picture_frame.9.png b/res/drawable/picture_frame.9.png
deleted file mode 100644
index b153260..0000000
--- a/res/drawable/picture_frame.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/texture_brushed_steel.png b/res/drawable/texture_brushed_steel.png
new file mode 100644
index 0000000..73b3dfe
--- /dev/null
+++ b/res/drawable/texture_brushed_steel.png
Binary files differ
diff --git a/res/drawable/wallpaper.jpg b/res/drawable/wallpaper.jpg
deleted file mode 100644
index d19b642..0000000
--- a/res/drawable/wallpaper.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable/wallpaper_beach.jpg b/res/drawable/wallpaper_beach.jpg
index c059cc3..b502092 100644
--- a/res/drawable/wallpaper_beach.jpg
+++ b/res/drawable/wallpaper_beach.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_beach_small.jpg b/res/drawable/wallpaper_beach_small.jpg
index 3508509..46051cc 100644
--- a/res/drawable/wallpaper_beach_small.jpg
+++ b/res/drawable/wallpaper_beach_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_blue.jpg b/res/drawable/wallpaper_blue.jpg
index 032f598..0edd6bd 100644
--- a/res/drawable/wallpaper_blue.jpg
+++ b/res/drawable/wallpaper_blue.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_blue_small.jpg b/res/drawable/wallpaper_blue_small.jpg
index e987c69..5cf585a 100644
--- a/res/drawable/wallpaper_blue_small.jpg
+++ b/res/drawable/wallpaper_blue_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_green.jpg b/res/drawable/wallpaper_green.jpg
index 89b308b..36c9e08 100644
--- a/res/drawable/wallpaper_green.jpg
+++ b/res/drawable/wallpaper_green.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_green_small.jpg b/res/drawable/wallpaper_green_small.jpg
index 16790d2..d7c28ec 100644
--- a/res/drawable/wallpaper_green_small.jpg
+++ b/res/drawable/wallpaper_green_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_grey.jpg b/res/drawable/wallpaper_grey.jpg
index 7898568..ca48e6a 100644
--- a/res/drawable/wallpaper_grey.jpg
+++ b/res/drawable/wallpaper_grey.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_grey_small.jpg b/res/drawable/wallpaper_grey_small.jpg
index 12ca3e2..1734d2e 100644
--- a/res/drawable/wallpaper_grey_small.jpg
+++ b/res/drawable/wallpaper_grey_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_jellyfish_small.jpg b/res/drawable/wallpaper_jellyfish_small.jpg
index e140597..4e5d134 100644
--- a/res/drawable/wallpaper_jellyfish_small.jpg
+++ b/res/drawable/wallpaper_jellyfish_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_lake.jpg b/res/drawable/wallpaper_lake.jpg
deleted file mode 100644
index 5ba522f..0000000
--- a/res/drawable/wallpaper_lake.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable/wallpaper_lake_small.jpg b/res/drawable/wallpaper_lake_small.jpg
index 179b26a..36d56cc 100644
--- a/res/drawable/wallpaper_lake_small.jpg
+++ b/res/drawable/wallpaper_lake_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_mountain_small.jpg b/res/drawable/wallpaper_mountain_small.jpg
index 2416474..e2d5ac2 100644
--- a/res/drawable/wallpaper_mountain_small.jpg
+++ b/res/drawable/wallpaper_mountain_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_path.jpg b/res/drawable/wallpaper_path.jpg
index f4cb02c..e1c98c0 100644
--- a/res/drawable/wallpaper_path.jpg
+++ b/res/drawable/wallpaper_path.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_path_small.jpg b/res/drawable/wallpaper_path_small.jpg
index a1af4cd..f47d7a7 100644
--- a/res/drawable/wallpaper_path_small.jpg
+++ b/res/drawable/wallpaper_path_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_pink.jpg b/res/drawable/wallpaper_pink.jpg
index 1b9f226..74403e5 100644
--- a/res/drawable/wallpaper_pink.jpg
+++ b/res/drawable/wallpaper_pink.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_pink_small.jpg b/res/drawable/wallpaper_pink_small.jpg
index 7b7ad58..42b1770 100644
--- a/res/drawable/wallpaper_pink_small.jpg
+++ b/res/drawable/wallpaper_pink_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_ripples.jpg b/res/drawable/wallpaper_ripples.jpg
deleted file mode 100644
index 8624c86..0000000
--- a/res/drawable/wallpaper_ripples.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable/wallpaper_ripples_small.jpg b/res/drawable/wallpaper_ripples_small.jpg
deleted file mode 100644
index ce07d38..0000000
--- a/res/drawable/wallpaper_ripples_small.jpg
+++ /dev/null
Binary files differ
diff --git a/res/drawable/wallpaper_road.jpg b/res/drawable/wallpaper_road.jpg
index 88611a4..3aaa639 100644
--- a/res/drawable/wallpaper_road.jpg
+++ b/res/drawable/wallpaper_road.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_snow_leopard.jpg b/res/drawable/wallpaper_snow_leopard.jpg
index 0d33157..bcbc51e 100644
--- a/res/drawable/wallpaper_snow_leopard.jpg
+++ b/res/drawable/wallpaper_snow_leopard.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_snow_leopard_small.jpg b/res/drawable/wallpaper_snow_leopard_small.jpg
index ec976aa..99db5c6 100644
--- a/res/drawable/wallpaper_snow_leopard_small.jpg
+++ b/res/drawable/wallpaper_snow_leopard_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_sunrise_small.jpg b/res/drawable/wallpaper_sunrise_small.jpg
index aadd980..248b7e7 100644
--- a/res/drawable/wallpaper_sunrise_small.jpg
+++ b/res/drawable/wallpaper_sunrise_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_sunset_small.jpg b/res/drawable/wallpaper_sunset_small.jpg
index eff8f0d..d5e5add 100644
--- a/res/drawable/wallpaper_sunset_small.jpg
+++ b/res/drawable/wallpaper_sunset_small.jpg
Binary files differ
diff --git a/res/drawable/wallpaper_zanzibar_small.jpg b/res/drawable/wallpaper_zanzibar_small.jpg
index 7307a73..700a7fe 100644
--- a/res/drawable/wallpaper_zanzibar_small.jpg
+++ b/res/drawable/wallpaper_zanzibar_small.jpg
Binary files differ
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index 11a81fd..2b262c3 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -16,7 +16,6 @@
<com.android.launcher.DragLayer
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
android:id="@+id/drag_layer"
@@ -37,15 +36,15 @@
</com.android.launcher.Workspace>
- <com.android.internal.widget.SlidingDrawer
+ <SlidingDrawer
android:id="@+id/drawer"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal"
- androidprv:bottomOffset="7px"
- androidprv:handle="@+id/all_apps"
- androidprv:content="@+id/content">
+ android:bottomOffset="7dip"
+ android:handle="@+id/all_apps"
+ android:content="@+id/content">
<com.android.launcher.HandleView
android:id="@id/all_apps"
@@ -67,7 +66,7 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent"
- android:background="@color/grid_dark_background"
+ launcher:texture="@drawable/texture_brushed_steel"
android:scrollbarStyle="outsideInset"
android:drawSelectorOnTop="false"
@@ -81,7 +80,7 @@
android:verticalSpacing="10dip"
android:numColumns="5" />
- </com.android.internal.widget.SlidingDrawer>
+ </SlidingDrawer>
<com.android.launcher.DeleteZone
android:id="@+id/delete_zone"
diff --git a/res/layout-port/application_boxed.xml b/res/layout-port/application_boxed.xml
index 63d2254..e71a2e2 100644
--- a/res/layout-port/application_boxed.xml
+++ b/res/layout-port/application_boxed.xml
@@ -22,7 +22,7 @@
android:paddingTop="5dip"
android:paddingBottom="2dip"
android:drawablePadding="0dip"
-
+
android:textSize="13dip"
android:maxLines="2"
android:ellipsize="marquee"
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index 167331e..0c249a3 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -16,7 +16,6 @@
<com.android.launcher.DragLayer
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
android:id="@+id/drag_layer"
@@ -37,15 +36,15 @@
</com.android.launcher.Workspace>
- <com.android.internal.widget.SlidingDrawer
+ <SlidingDrawer
android:id="@+id/drawer"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
- androidprv:topOffset="5px"
- androidprv:bottomOffset="7px"
- androidprv:handle="@+id/all_apps"
- androidprv:content="@+id/content">
+ android:topOffset="5dip"
+ android:bottomOffset="7dip"
+ android:handle="@+id/all_apps"
+ android:content="@+id/content">
<com.android.launcher.HandleView
android:id="@id/all_apps"
@@ -67,7 +66,7 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent"
- android:background="@color/grid_dark_background"
+ launcher:texture="@drawable/texture_brushed_steel"
android:scrollbarStyle="outsideInset"
android:drawSelectorOnTop="false"
@@ -81,7 +80,7 @@
android:verticalSpacing="10dip"
android:numColumns="4" />
- </com.android.internal.widget.SlidingDrawer>
+ </SlidingDrawer>
<com.android.launcher.DeleteZone
android:id="@+id/delete_zone"
diff --git a/res/layout/add_list_item.xml b/res/layout/add_list_item.xml
new file mode 100644
index 0000000..f276511
--- /dev/null
+++ b/res/layout/add_list_item.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:textAppearance="?android:attr/textAppearanceLargeInverse"
+ android:gravity="center_vertical"
+ android:drawablePadding="14dip"
+ android:paddingLeft="15dip"
+ android:paddingRight="15dip" />
diff --git a/res/layout/appwidget_error.xml b/res/layout/appwidget_error.xml
new file mode 100644
index 0000000..03d4ae4
--- /dev/null
+++ b/res/layout/appwidget_error.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="10dip"
+ android:paddingBottom="10dip"
+ android:paddingLeft="20dip"
+ android:paddingRight="20dip"
+ android:gravity="center"
+ android:background="@drawable/bg_appwidget_error"
+ android:textAppearance="?android:attr/textAppearanceMediumInverse"
+ android:textColor="@color/appwidget_error_color"
+ android:text="@string/gadget_error_text"
+ />
diff --git a/res/layout/create_shortcut_list.xml b/res/layout/create_shortcut_list.xml
index 63ca2b8..aa16733 100644
--- a/res/layout/create_shortcut_list.xml
+++ b/res/layout/create_shortcut_list.xml
@@ -17,11 +17,10 @@
** limitations under the License.
*/
-->
-<ExpandableListView xmlns:android="http://schemas.android.com/apk/res/android"
+<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
- android:layout_marginTop="5px"
+ android:layout_marginTop="5dip"
android:cacheColorHint="@null"
- android:childDivider="@android:drawable/divider_horizontal_bright"
android:divider="@android:drawable/divider_horizontal_bright"
android:scrollbars="vertical" />
diff --git a/res/layout/widget_clock.xml b/res/layout/widget_clock.xml
deleted file mode 100644
index b934e90..0000000
--- a/res/layout/widget_clock.xml
+++ /dev/null
@@ -1,22 +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.
--->
-
-<AnalogClock xmlns:android="http://schemas.android.com/apk/res/android"
- android:dial="@drawable/clock_dial"
- android:hand_hour="@drawable/clock_hour"
- android:hand_minute="@drawable/clock_minute"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent" />
diff --git a/res/layout/widget_photo_frame.xml b/res/layout/widget_photo_frame.xml
deleted file mode 100644
index 9fe3b80..0000000
--- a/res/layout/widget_photo_frame.xml
+++ /dev/null
@@ -1,22 +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.
--->
-
-<com.android.launcher.PhotoFrame xmlns:android="http://schemas.android.com/apk/res/android"
- android:scaleType="center"
- android:cropToPadding="true"
- android:background="@drawable/picture_frame"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent" />
diff --git a/res/layout/widget_search.xml b/res/layout/widget_search.xml
index 27e591d..209716d 100644
--- a/res/layout/widget_search.xml
+++ b/res/layout/widget_search.xml
@@ -26,7 +26,7 @@
android:layout_height="wrap_content"
android:src="@drawable/google_logo" />
- <AutoCompleteTextView
+ <com.android.launcher.SearchAutoCompleteTextView
android:id="@+id/input"
android:layout_width="0dip"
android:layout_weight="1"
@@ -36,7 +36,8 @@
android:singleLine="true"
android:selectAllOnFocus="true"
android:completionThreshold="1"
- android:inputType="textAutoComplete|textSearchString"
+ android:inputType="textAutoComplete"
+ android:imeOptions="actionSearch"
/>
<ImageButton android:id="@+id/search_go_btn"
@@ -46,4 +47,10 @@
android:src="@*android:drawable/ic_btn_search"
/>
+ <ImageButton android:id="@+id/search_voice_btn"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@android:drawable/ic_btn_speak_now"
+ />
+
</com.android.launcher.Search>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index c5afda2..dea039f 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -28,6 +28,8 @@
<string name="menu_item_add_item">"Přidat na plochu"</string>
<string name="group_applications">"Aplikace"</string>
<string name="group_shortcuts">"Zástupce"</string>
+ <string name="group_search">"Hledat"</string>
+ <string name="group_folder">"Složka"</string>
<string name="group_live_folders">"Složka Live"</string>
<string name="group_widgets">"Miniaplikace"</string>
<string name="group_wallpapers">"Tapeta"</string>
@@ -36,6 +38,8 @@
<string name="add_photo_frame">"Rámeček fotografie"</string>
<string name="add_search">"Vyhledávání"</string>
<string name="out_of_space">"Na této ploše již není místo."</string>
+ <string name="title_select_shortcut">"Vyberte zástupce"</string>
+ <string name="title_select_live_folder">"Vyberte složku Live"</string>
<string name="menu_add">"Přidat"</string>
<string name="menu_wallpaper">"Tapeta"</string>
<string name="menu_search">"Hledat"</string>
@@ -50,4 +54,6 @@
<string name="permlab_write_settings">"zápis nastavení a odkazů plochy"</string>
<string name="permdesc_write_settings">"Povoluje aplikaci změnit nastavení a odkazy plochy."</string>
<string name="search_hint">"Vyhledávání Google"</string>
+ <!-- no translation found for gadget_error_text (8359351016167075858) -->
+ <skip />
</resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 3f0c42a..c405852 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -28,6 +28,8 @@
<string name="menu_item_add_item">"Zur Startseite hinzufügen"</string>
<string name="group_applications">"Anwendung"</string>
<string name="group_shortcuts">"Verknüpfung"</string>
+ <string name="group_search">"Suchen"</string>
+ <string name="group_folder">"Ordner"</string>
<string name="group_live_folders">"Live-Ordner"</string>
<string name="group_widgets">"Widget"</string>
<string name="group_wallpapers">"Hintergrund"</string>
@@ -36,6 +38,8 @@
<string name="add_photo_frame">"Bildrahmen"</string>
<string name="add_search">"Suchen"</string>
<string name="out_of_space">"Auf der Startseite ist kein Platz mehr vorhanden."</string>
+ <string name="title_select_shortcut">"Tastenkürzel auswählen"</string>
+ <string name="title_select_live_folder">"Live-Ordner auswählen"</string>
<string name="menu_add">"Hinzufügen"</string>
<string name="menu_wallpaper">"Hintergrund"</string>
<string name="menu_search">"Suchen"</string>
@@ -50,4 +54,6 @@
<string name="permlab_write_settings">"Einstellungen und Shortcuts für Startseite schreiben"</string>
<string name="permdesc_write_settings">"Ermöglicht einer Anwendung, die Einstellungen und Shortcuts auf der Startseite zu ändern."</string>
<string name="search_hint">"Google-Suche"</string>
+ <!-- no translation found for gadget_error_text (8359351016167075858) -->
+ <skip />
</resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 161616d..20d2605 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -17,9 +17,9 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="application_name">"Página principal"</string>
<string name="folder_name">"Carpeta"</string>
- <string name="chooser_wallpaper">"Seleccionar papel tapiz de"</string>
- <string name="wallpaper_instructions">"Establecer papel tapiz"</string>
- <string name="pick_wallpaper">"Galería de papel tapiz"</string>
+ <string name="chooser_wallpaper">"Seleccionar fondo de pantalla de"</string>
+ <string name="wallpaper_instructions">"Establecer fondo de pantalla"</string>
+ <string name="pick_wallpaper">"Galería de fondo de pantalla"</string>
<string name="activity_not_found">"La aplicación no está instalada en el teléfono."</string>
<string name="rename_folder_label">"Nombre de carpeta"</string>
<string name="rename_folder_title">"Cambiar nombre de carpeta"</string>
@@ -28,19 +28,23 @@
<string name="menu_item_add_item">"Añadir a pantalla de página principal"</string>
<string name="group_applications">"Aplicación"</string>
<string name="group_shortcuts">"Acceso directo"</string>
+ <string name="group_search">"Búsqueda"</string>
+ <string name="group_folder">"Carpeta"</string>
<string name="group_live_folders">"Carpeta activa"</string>
<string name="group_widgets">"Widget"</string>
- <string name="group_wallpapers">"Papel tapiz"</string>
+ <string name="group_wallpapers">"Fondo de pantalla"</string>
<string name="add_folder">"Carpeta"</string>
<string name="add_clock">"Reloj"</string>
<string name="add_photo_frame">"Picture frame"</string>
<string name="add_search">"Búsqueda de Google"</string>
<string name="out_of_space">"No queda espacio en esta pantalla de página principal."</string>
+ <string name="title_select_shortcut">"Seleccionar acceso directo"</string>
+ <string name="title_select_live_folder">"Seleccionar carpeta activa"</string>
<string name="menu_add">"Añadir"</string>
- <string name="menu_wallpaper">"Papel tapiz"</string>
- <string name="menu_search">"Búsqueda de Google"</string>
+ <string name="menu_wallpaper">"Fondo de pantalla"</string>
+ <string name="menu_search">"Buscar con Google"</string>
<string name="menu_notifications">"Notificaciones"</string>
- <string name="menu_settings">"Configuración"</string>
+ <string name="menu_settings">"Ajustes"</string>
<string name="permlab_install_shortcut">"instalar accesos directos"</string>
<string name="permdesc_install_shortcut">"Permite que una aplicación añada accesos directos sin intervención del usuario."</string>
<string name="permlab_uninstall_shortcut">"desinstalar accesos directos"</string>
@@ -50,4 +54,6 @@
<string name="permlab_write_settings">"escribir información de accesos directos y de configuración de la página principal"</string>
<string name="permdesc_write_settings">"Permite que una aplicación modifique la configuración y los accesos directos de la página principal."</string>
<string name="search_hint">"Búsqueda de Google"</string>
+ <!-- no translation found for gadget_error_text (8359351016167075858) -->
+ <skip />
</resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index effdc7a..c01ef47 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -17,9 +17,9 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="application_name">"Accueil"</string>
<string name="folder_name">"Dossier"</string>
- <string name="chooser_wallpaper">"Sélectionner l\'arrière-plan à partir de"</string>
- <string name="wallpaper_instructions">"Configurer l\'arrière-plan"</string>
- <string name="pick_wallpaper">"Galerie des arrière-plans"</string>
+ <string name="chooser_wallpaper">"Sélectionner à partir de..."</string>
+ <string name="wallpaper_instructions">"Sélectionner"</string>
+ <string name="pick_wallpaper">"Galerie"</string>
<string name="activity_not_found">"L\'application n\'est pas installée sur votre téléphone."</string>
<string name="rename_folder_label">"Nom du dossier"</string>
<string name="rename_folder_title">"Renommer le dossier"</string>
@@ -28,14 +28,18 @@
<string name="menu_item_add_item">"Ajouter à l\'écran d\'accueil"</string>
<string name="group_applications">"Application"</string>
<string name="group_shortcuts">"Raccourci"</string>
- <string name="group_live_folders">"Dossier live"</string>
+ <string name="group_search">"Recherche"</string>
+ <string name="group_folder">"Dossier"</string>
+ <string name="group_live_folders">"Dossier actif"</string>
<string name="group_widgets">"Widget"</string>
<string name="group_wallpapers">"Arrière-plan"</string>
<string name="add_folder">"Dossier"</string>
<string name="add_clock">"Horloge"</string>
<string name="add_photo_frame">"Cadre d\'image"</string>
<string name="add_search">"Rechercher"</string>
- <string name="out_of_space">"Plus d\'espace libre sur l\'écran Accueil."</string>
+ <string name="out_of_space">"Plus d\'espace libre sur l\'écran d\'accueil."</string>
+ <string name="title_select_shortcut">"Sélectionner un raccourci"</string>
+ <string name="title_select_live_folder">"Sélectionner dossier actif"</string>
<string name="menu_add">"Ajouter"</string>
<string name="menu_wallpaper">"Arrière-plan"</string>
<string name="menu_search">"Rechercher"</string>
@@ -45,9 +49,11 @@
<string name="permdesc_install_shortcut">"Permet à une application d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur."</string>
<string name="permlab_uninstall_shortcut">"désinstaller les raccourcis"</string>
<string name="permdesc_uninstall_shortcut">"Permet à une application de supprimer les raccourcis sans l\'intervention de l\'utilisateur."</string>
- <string name="permlab_read_settings">"lire les paramètres et les raccourcis de la page d\'accueil"</string>
+ <string name="permlab_read_settings">"Lire les paramètres et les raccourcis de la page d\'accueil"</string>
<string name="permdesc_read_settings">"Permet à une application de lire les paramètres et raccourcis de la page d\'accueil."</string>
- <string name="permlab_write_settings">"écrire les paramètres de la page d\'accueil et des raccourcis"</string>
+ <string name="permlab_write_settings">"Enregistrer les paramètres de la page d\'accueil et des raccourcis"</string>
<string name="permdesc_write_settings">"Permet à une application de modifier les paramètres et les raccourcis de la page d\'accueil."</string>
<string name="search_hint">"Recherche Google"</string>
+ <!-- no translation found for gadget_error_text (8359351016167075858) -->
+ <skip />
</resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index d38fa30..47c6f9f 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -27,7 +27,9 @@
<string name="cancel_action">"Annulla"</string>
<string name="menu_item_add_item">"Aggiungi a schermata Home"</string>
<string name="group_applications">"Applicazione"</string>
- <string name="group_shortcuts">"Collegamento"</string>
+ <string name="group_shortcuts">"Scorciatoia"</string>
+ <string name="group_search">"Ricerca"</string>
+ <string name="group_folder">"Cartella"</string>
<string name="group_live_folders">"Cartella dinamica"</string>
<string name="group_widgets">"Widget"</string>
<string name="group_wallpapers">"Sfondo"</string>
@@ -36,18 +38,22 @@
<string name="add_photo_frame">"Cornice immagini"</string>
<string name="add_search">"Ricerca"</string>
<string name="out_of_space">"Spazio nella schermata Home esaurito."</string>
+ <string name="title_select_shortcut">"Seleziona collegamento"</string>
+ <string name="title_select_live_folder">"Seleziona cartella dinamica"</string>
<string name="menu_add">"Aggiungi"</string>
<string name="menu_wallpaper">"Sfondo"</string>
<string name="menu_search">"Cerca"</string>
<string name="menu_notifications">"Notifiche"</string>
<string name="menu_settings">"Impostazioni"</string>
- <string name="permlab_install_shortcut">"aggiungere collegamenti"</string>
- <string name="permdesc_install_shortcut">"Consente a un\'applicazione di aggiungere collegamenti automaticamente."</string>
- <string name="permlab_uninstall_shortcut">"eliminare collegamenti"</string>
- <string name="permdesc_uninstall_shortcut">"Consente a un\'applicazione di rimuovere collegamenti automaticamente."</string>
- <string name="permlab_read_settings">"leggere impostazioni e collegamenti in Home"</string>
- <string name="permdesc_read_settings">"Consente a un\'applicazione di leggere le impostazioni e i collegamenti in Home."</string>
- <string name="permlab_write_settings">"creare impostazioni e collegamenti in Home"</string>
- <string name="permdesc_write_settings">"Consente a un\'applicazione di modificare le impostazioni e i collegamenti in Home."</string>
+ <string name="permlab_install_shortcut">"aggiungere scorciatorie"</string>
+ <string name="permdesc_install_shortcut">"Consente a un\'applicazione di aggiungere scorciatoie automaticamente."</string>
+ <string name="permlab_uninstall_shortcut">"eliminare scorciatoie"</string>
+ <string name="permdesc_uninstall_shortcut">"Consente a un\'applicazione di rimuovere scorciatoie automaticamente."</string>
+ <string name="permlab_read_settings">"leggere impostazioni e scorciatoie in Home"</string>
+ <string name="permdesc_read_settings">"Consente a un\'applicazione di leggere le impostazioni e le scorciatoie in Home."</string>
+ <string name="permlab_write_settings">"creare impostazioni e scorciatoie in Home"</string>
+ <string name="permdesc_write_settings">"Consente a un\'applicazione di modificare le impostazioni e le scorciatoie in Home."</string>
<string name="search_hint">"Ricerca Google"</string>
+ <!-- no translation found for gadget_error_text (8359351016167075858) -->
+ <skip />
</resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index c3b1ff8..cf71c65 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -28,6 +28,8 @@
<string name="menu_item_add_item">"ホーム画面に追加"</string>
<string name="group_applications">"アプリケーション"</string>
<string name="group_shortcuts">"ショートカット"</string>
+ <string name="group_search">"検索"</string>
+ <string name="group_folder">"フォルダ"</string>
<string name="group_live_folders">"ライブフォルダ"</string>
<string name="group_widgets">"ウィジェット"</string>
<string name="group_wallpapers">"壁紙"</string>
@@ -35,19 +37,23 @@
<string name="add_clock">"時計"</string>
<string name="add_photo_frame">"写真フレーム"</string>
<string name="add_search">"検索"</string>
- <string name="out_of_space">"このホーム画面には空きスペースがありません。"</string>
+ <string name="out_of_space">"ホーム画面に空きスペースがありません。"</string>
+ <string name="title_select_shortcut">"ショートカットを選択"</string>
+ <string name="title_select_live_folder">"ライブフォルダを選択"</string>
<string name="menu_add">"追加"</string>
<string name="menu_wallpaper">"壁紙"</string>
<string name="menu_search">"検索"</string>
<string name="menu_notifications">"通知"</string>
<string name="menu_settings">"設定"</string>
<string name="permlab_install_shortcut">"ショートカットのインストール"</string>
- <string name="permdesc_install_shortcut">"ユーザー操作なしで、ショートカットをアプリケーションで追加できるようにします。"</string>
+ <string name="permdesc_install_shortcut">"ユーザー操作なしでショートカットの追加をアプリケーションに許可します。"</string>
<string name="permlab_uninstall_shortcut">"ショートカットのアンインストール"</string>
- <string name="permdesc_uninstall_shortcut">"ユーザー操作なしで、ショートカットをアプリケーションで削除できるようにします。"</string>
- <string name="permlab_read_settings">"ホームの設定とショートカットの読み取り"</string>
+ <string name="permdesc_uninstall_shortcut">"ユーザー操作なしでショートカットの削除をアプリケーションに許可します。"</string>
+ <string name="permlab_read_settings">"ホーム設定とショートカットの読み取り"</string>
<string name="permdesc_read_settings">"ホームの設定とショートカットの読み取りをアプリケーションに許可します。"</string>
<string name="permlab_write_settings">"ホームの設定とショートカットの書き込み"</string>
<string name="permdesc_write_settings">"ホームの設定とショートカットの変更をアプリケーションに許可します。"</string>
<string name="search_hint">"Google検索"</string>
+ <!-- no translation found for gadget_error_text (8359351016167075858) -->
+ <skip />
</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
new file mode 100644
index 0000000..75ad6d8
--- /dev/null
+++ b/res/values-ko/strings.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name">"홈"</string>
+ <string name="folder_name">"폴더"</string>
+ <string name="chooser_wallpaper">"배경화면 선택"</string>
+ <string name="wallpaper_instructions">"배경화면 설정"</string>
+ <string name="pick_wallpaper">"배경화면 갤러리"</string>
+ <string name="activity_not_found">"전화기에 설치되어 있지 않은 응용프로그램입니다."</string>
+ <string name="rename_folder_label">"폴더 이름"</string>
+ <string name="rename_folder_title">"폴더 이름 바꾸기"</string>
+ <string name="rename_action">"확인"</string>
+ <string name="cancel_action">"취소"</string>
+ <string name="menu_item_add_item">"홈 화면에 추가"</string>
+ <string name="group_applications">"응용프로그램"</string>
+ <string name="group_shortcuts">"바로가기"</string>
+ <string name="group_search">"검색"</string>
+ <string name="group_folder">"폴더"</string>
+ <string name="group_live_folders">"라이브 폴더"</string>
+ <string name="group_widgets">"위젯"</string>
+ <string name="group_wallpapers">"배경화면"</string>
+ <string name="add_folder">"폴더"</string>
+ <string name="add_clock">"시계"</string>
+ <string name="add_photo_frame">"사진 프레임"</string>
+ <string name="add_search">"검색"</string>
+ <string name="out_of_space">"홈 화면에 더 이상 공간이 없습니다."</string>
+ <string name="title_select_shortcut">"바로가기 선택"</string>
+ <string name="title_select_live_folder">"라이브 폴더 선택"</string>
+ <string name="menu_add">"추가"</string>
+ <string name="menu_wallpaper">"배경화면"</string>
+ <string name="menu_search">"검색"</string>
+ <string name="menu_notifications">"알림"</string>
+ <string name="menu_settings">"설정"</string>
+ <string name="permlab_install_shortcut">"바로가기 설치"</string>
+ <string name="permdesc_install_shortcut">"응용프로그램이 사용자의 작업 없이 바로가기를 추가할 수 있도록 합니다."</string>
+ <string name="permlab_uninstall_shortcut">"바로가기 제거"</string>
+ <string name="permdesc_uninstall_shortcut">"응용프로그램이 사용자의 작업 없이 바로가기를 제거할 수 있도록 합니다."</string>
+ <string name="permlab_read_settings">"홈 설정 및 바로가기 읽기"</string>
+ <string name="permdesc_read_settings">"응용프로그램이 홈에 있는 설정 및 바로가기를 읽을 수 있습니다."</string>
+ <string name="permlab_write_settings">"홈 설정 및 바로가기 쓰기"</string>
+ <string name="permdesc_write_settings">"응용프로그램이 홈에 있는 설정 및 바로가기를 변경할 수 있습니다."</string>
+ <string name="search_hint">"Google 검색"</string>
+ <!-- no translation found for gadget_error_text (8359351016167075858) -->
+ <skip />
+</resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
new file mode 100644
index 0000000..b14a5da
--- /dev/null
+++ b/res/values-nb/strings.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name">"Hjem"</string>
+ <string name="folder_name">"Mappe"</string>
+ <string name="chooser_wallpaper">"Velg bakgrunnsbilde fra"</string>
+ <string name="wallpaper_instructions">"Velg bakgrunnsbilde"</string>
+ <string name="pick_wallpaper">"Bildegalleri"</string>
+ <string name="activity_not_found">"Applikasjonen er ikke installert."</string>
+ <string name="rename_folder_label">"Mappenavn"</string>
+ <string name="rename_folder_title">"Gi nytt navn til mappe"</string>
+ <string name="rename_action">"OK"</string>
+ <string name="cancel_action">"Avbryt"</string>
+ <string name="menu_item_add_item">"Legg til skrivebord"</string>
+ <string name="group_applications">"Applikasjon"</string>
+ <string name="group_shortcuts">"Snarvei"</string>
+ <string name="group_search">"Søk"</string>
+ <string name="group_folder">"Mappe"</string>
+ <string name="group_live_folders">"Aktiv mappe"</string>
+ <string name="group_widgets">"Skrivebordselement"</string>
+ <string name="group_wallpapers">"Bakgrunnsbilde"</string>
+ <string name="add_folder">"Mappe"</string>
+ <string name="add_clock">"Klokke"</string>
+ <string name="add_photo_frame">"Bilderamme"</string>
+ <string name="add_search">"Søk"</string>
+ <string name="out_of_space">"Ikke nok plass på skrivebordet."</string>
+ <string name="title_select_shortcut">"Velg snarvei"</string>
+ <string name="title_select_live_folder">"Velg aktiv mappe"</string>
+ <string name="menu_add">"Legg til"</string>
+ <string name="menu_wallpaper">"Bakgrunnsbilde"</string>
+ <string name="menu_search">"Søk"</string>
+ <string name="menu_notifications">"Varslinger"</string>
+ <string name="menu_settings">"Innstillinger"</string>
+ <string name="permlab_install_shortcut">"installere snarveier"</string>
+ <string name="permdesc_install_shortcut">"Lar applikasjonen legge til snarveier uten å involvere brukeren."</string>
+ <string name="permlab_uninstall_shortcut">"avinstallere snarveier"</string>
+ <string name="permdesc_uninstall_shortcut">"Lar applikasjonen fjerne snarveier uten å involvere brukeren."</string>
+ <string name="permlab_read_settings">"lese skrivebordsinnstillinger og -snarveier"</string>
+ <string name="permdesc_read_settings">"Lar applikasjonen lese innstillinger og snarveier fra skrivebordet."</string>
+ <string name="permlab_write_settings">"skrive skrivebordsinnstillinger og -snarveier"</string>
+ <string name="permdesc_write_settings">"Lar applikasjonen endre innstillinger og snarveier på skrivebordet."</string>
+ <string name="search_hint">"Google-søk"</string>
+ <!-- no translation found for gadget_error_text (8359351016167075858) -->
+ <skip />
+</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 9e55d8f..e4959c0 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -28,6 +28,8 @@
<string name="menu_item_add_item">"Toevoegen aan startpagina"</string>
<string name="group_applications">"Toepassing"</string>
<string name="group_shortcuts">"Snelkoppeling"</string>
+ <string name="group_search">"Zoeken"</string>
+ <string name="group_folder">"Map"</string>
<string name="group_live_folders">"Live map"</string>
<string name="group_widgets">"Widget"</string>
<string name="group_wallpapers">"Achtergrond"</string>
@@ -36,6 +38,8 @@
<string name="add_photo_frame">"Fotolijstje"</string>
<string name="add_search">"Zoeken"</string>
<string name="out_of_space">"Er is geen ruimte meer op dit startscherm."</string>
+ <string name="title_select_shortcut">"Snelkoppeling selecteren"</string>
+ <string name="title_select_live_folder">"Live map selecteren"</string>
<string name="menu_add">"Toevoegen"</string>
<string name="menu_wallpaper">"Achtergrond"</string>
<string name="menu_search">"Zoeken"</string>
@@ -49,5 +53,7 @@
<string name="permdesc_read_settings">"Hiermee kan een toepassing de instellingen en snelkoppelingen op de startpagina lezen."</string>
<string name="permlab_write_settings">"instellingen en snelkoppelingen voor de startpagina schrijven"</string>
<string name="permdesc_write_settings">"Hiermee kan een toepassing de instellingen en snelkoppelingen op de startpagina wijzigen."</string>
- <string name="search_hint">"Zoeken met Google"</string>
+ <string name="search_hint">"Google Zoeken"</string>
+ <!-- no translation found for gadget_error_text (8359351016167075858) -->
+ <skip />
</resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 9356954..99718cb 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -15,7 +15,7 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="application_name">"Strona główna"</string>
+ <string name="application_name">"Sieć"</string>
<string name="folder_name">"Folder"</string>
<string name="chooser_wallpaper">"Wybierz tapetę"</string>
<string name="wallpaper_instructions">"Ustaw tapetę"</string>
@@ -28,14 +28,18 @@
<string name="menu_item_add_item">"Dodaj do strony głównej"</string>
<string name="group_applications">"Aplikacja"</string>
<string name="group_shortcuts">"Skrót"</string>
- <string name="group_live_folders">"Folder Live"</string>
+ <string name="group_search">"Wyszukiwarka"</string>
+ <string name="group_folder">"Folder"</string>
+ <string name="group_live_folders">"Folder aktywny"</string>
<string name="group_widgets">"Widget"</string>
<string name="group_wallpapers">"Tapeta"</string>
<string name="add_folder">"Folder"</string>
<string name="add_clock">"Zegar"</string>
<string name="add_photo_frame">"Ramka obrazu"</string>
- <string name="add_search">"Szukaj"</string>
+ <string name="add_search">"Wyszukiwarka"</string>
<string name="out_of_space">"Brak miejsca na tej stronie głównej"</string>
+ <string name="title_select_shortcut">"Wybierz skrót"</string>
+ <string name="title_select_live_folder">"Wybierz folder aktywny"</string>
<string name="menu_add">"Dodaj"</string>
<string name="menu_wallpaper">"Tapeta"</string>
<string name="menu_search">"Szukaj"</string>
@@ -50,4 +54,6 @@
<string name="permlab_write_settings">"zapisywanie ustawień i skrótów strony głównej"</string>
<string name="permdesc_write_settings">"Umożliwia aplikacji zmianę ustawień i skrótów strony głównej."</string>
<string name="search_hint">"Szukaj w Google"</string>
+ <!-- no translation found for gadget_error_text (8359351016167075858) -->
+ <skip />
</resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 9bb4787..ab6120e 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -28,6 +28,8 @@
<string name="menu_item_add_item">"Добавление на главный экран"</string>
<string name="group_applications">"Приложение"</string>
<string name="group_shortcuts">"Ярлык"</string>
+ <string name="group_search">"Поиск"</string>
+ <string name="group_folder">"Папка"</string>
<string name="group_live_folders">"Динамическая папка"</string>
<string name="group_widgets">"Виджет"</string>
<string name="group_wallpapers">"Фоновый рисунок"</string>
@@ -36,6 +38,8 @@
<string name="add_photo_frame">"Рамка для картинки"</string>
<string name="add_search">"Поиск"</string>
<string name="out_of_space">"На главном экране больше нет места."</string>
+ <string name="title_select_shortcut">"Выберите ярлык"</string>
+ <string name="title_select_live_folder">"Выберите активную папку"</string>
<string name="menu_add">"Добавить"</string>
<string name="menu_wallpaper">"Фоновый рисунок"</string>
<string name="menu_search">"Искать"</string>
@@ -50,4 +54,6 @@
<string name="permlab_write_settings">"записывать ярлыки и настройки главного экрана"</string>
<string name="permdesc_write_settings">"Позволяет приложению изменять настройки и ярлыки на главном экране."</string>
<string name="search_hint">"Поиск Google"</string>
+ <!-- no translation found for gadget_error_text (8359351016167075858) -->
+ <skip />
</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 72ffcf4..f5d929d 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -28,6 +28,8 @@
<string name="menu_item_add_item">"添加到“主页”屏幕"</string>
<string name="group_applications">"应用程序"</string>
<string name="group_shortcuts">"快捷键"</string>
+ <string name="group_search">"搜索"</string>
+ <string name="group_folder">"文件夹"</string>
<string name="group_live_folders">"活动的文件夹"</string>
<string name="group_widgets">"小工具"</string>
<string name="group_wallpapers">"壁纸"</string>
@@ -36,6 +38,8 @@
<string name="add_photo_frame">"相框"</string>
<string name="add_search">"搜索"</string>
<string name="out_of_space">"该“主页”屏幕上没有多余空间。"</string>
+ <string name="title_select_shortcut">"选择快捷键"</string>
+ <string name="title_select_live_folder">"选择活动文件夹"</string>
<string name="menu_add">"添加"</string>
<string name="menu_wallpaper">"壁纸"</string>
<string name="menu_search">"搜索"</string>
@@ -50,4 +54,6 @@
<string name="permlab_write_settings">"写入“主页”设置和快捷键"</string>
<string name="permdesc_write_settings">"允许应用程序更改“主页”中的设置和快捷键。"</string>
<string name="search_hint">"Google 搜索"</string>
+ <!-- no translation found for gadget_error_text (8359351016167075858) -->
+ <skip />
</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 35db622..326f2a2 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -28,6 +28,8 @@
<string name="menu_item_add_item">"新增至首頁畫面"</string>
<string name="group_applications">"應用程式"</string>
<string name="group_shortcuts">"捷徑"</string>
+ <string name="group_search">"搜尋"</string>
+ <string name="group_folder">"資料夾"</string>
<string name="group_live_folders">"使用中的資料夾"</string>
<string name="group_widgets">"Widget"</string>
<string name="group_wallpapers">"桌布"</string>
@@ -36,6 +38,8 @@
<string name="add_photo_frame">"相框"</string>
<string name="add_search">"搜尋"</string>
<string name="out_of_space">"首頁已無空間"</string>
+ <string name="title_select_shortcut">"選取捷徑"</string>
+ <string name="title_select_live_folder">"選取作用中資料夾"</string>
<string name="menu_add">"新增"</string>
<string name="menu_wallpaper">"桌布"</string>
<string name="menu_search">"搜尋"</string>
@@ -50,4 +54,6 @@
<string name="permlab_write_settings">"寫入首頁設定和捷徑"</string>
<string name="permdesc_write_settings">"允許應用程式變更首頁中的設定和捷徑。"</string>
<string name="search_hint">"Google 搜尋"</string>
+ <!-- no translation found for gadget_error_text (8359351016167075858) -->
+ <skip />
</resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 87f5b78..ab545aa 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -69,4 +69,11 @@
<attr name="direction" />
</declare-styleable>
+ <!-- AllAppsGridView specific attributes. These attributes are used to customize
+ the list of all apps in XML files. -->
+ <declare-styleable name="AllAppsGridView">
+ <!-- The background texture. -->
+ <attr name="texture" format="reference" />
+ </declare-styleable>
+
</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index d4051f0..5574944 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -22,4 +22,6 @@
<color name="grid_dark_background">#EB191919</color>
<color name="bubble_dark_background">#B2191919</color>
<color name="delete_color_filter">#A5FF0000</color>
+
+ <color name="appwidget_error_color">#fccc</color>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c1d3455..a7945cb 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -52,8 +52,13 @@
<string name="group_applications">Application</string>
<!-- Options in "Add to Home" dialog box; Title of the group containing the list of all shortcuts -->
<string name="group_shortcuts">Shortcut</string>
+ <!-- Options in "Add to Home" dialog box; Title of the search gadget -->
+ <string name="group_search">Search</string>
+ <!-- Options in "Add to Home" dialog box; Title of the folder gadget -->
+ <string name="group_folder">Folder</string>
+ <!-- Options in "Add to Home" dialog box; Title of the group containing the list of all live folders -->
<string name="group_live_folders">Live folder</string>
- <!-- Options in "Add to Home" dialog box; Title of the group containing the list of all widgets -->
+ <!-- Options in "Add to Home" dialog box; Title of the group containing the list of all widgets/gadgets -->
<string name="group_widgets">Widget</string>
<!-- Options in "Add to Home" dialog box; Title of the group containing the list of apps that can set the wallpaper-->
<string name="group_wallpapers">Wallpaper</string>
@@ -68,6 +73,11 @@
<!-- Error message when user has filled a home screen, possibly not used -->
<string name="out_of_space">No more room on this Home screen.</string>
+ <!-- Title of dialog when user is selecting shortcut to add to homescreen -->
+ <string name="title_select_shortcut">Select shortcut</string>
+ <!-- Title of dialog when user is selecting live folder to add to homescreen -->
+ <string name="title_select_live_folder">Select live folder</string>
+
<!-- Menus items: -->
<skip />
<!-- Verb, menu item used to add an item on the desktop -->
@@ -101,5 +111,8 @@
This translation SHOULD MATCH the string "search_hint" which is found in
GoogleSearch/res/values/strings.xml -->
<string name="search_hint">Google Search</string>
-
+
+ <!-- Text to show user in place of a gadget when we can't display it properly -->
+ <string name="gadget_error_text">Problem loading widget</string>
+
</resources>
diff --git a/src/com/android/launcher/AddAdapter.java b/src/com/android/launcher/AddAdapter.java
index 8eebe39..cbcb338 100644
--- a/src/com/android/launcher/AddAdapter.java
+++ b/src/com/android/launcher/AddAdapter.java
@@ -16,456 +16,111 @@
package com.android.launcher;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.BaseAdapter;
import android.widget.TextView;
-import android.widget.BaseExpandableListAdapter;
-import android.graphics.drawable.Drawable;
-import android.provider.LiveFolders;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
/**
- * Shows a list of all the items that can be added to the workspace.
+ * Adapter showing the types of items that can be added to a {@link Workspace}.
*/
-public final class AddAdapter extends BaseExpandableListAdapter {
- private static final int GROUP_APPLICATIONS = 0;
- private static final int GROUP_SHORTCUTS = 1;
- private static final int GROUP_WIDGETS = 2;
- private static final int GROUP_LIVE_FOLDERS = 3;
- private static final int GROUP_WALLPAPERS = 4;
-
- private final Intent mCreateShortcutIntent;
- private final Intent mCreateLiveFolderIntent;
- private Intent mSetWallpaperIntent;
+public class AddAdapter extends BaseAdapter {
+
+ private final Launcher mLauncher;
private final LayoutInflater mInflater;
- private Launcher mLauncher;
- private Group[] mGroups;
-
- /**
- * Abstract class representing one thing that can be added
- */
- public abstract class AddAction implements Runnable {
- protected final Context mContext;
-
- AddAction(Context context) {
- mContext = context;
- }
-
- Drawable getIcon(int resource) {
- return mContext.getResources().getDrawable(resource);
- }
-
- public abstract void bindView(View v);
- }
-
- /**
- * Class representing an action that will create set the wallpaper.
- */
- public class SetWallpaperAction extends CreateShortcutAction {
- SetWallpaperAction(Context context, ResolveInfo info) {
- super(context, info);
- }
-
- public void run() {
- Intent intent = new Intent(mSetWallpaperIntent);
- ActivityInfo activityInfo = mInfo.activityInfo;
- intent.setComponent(new ComponentName(activityInfo.applicationInfo.packageName,
- activityInfo.name));
- mLauncher.startActivity(intent);
- }
- }
+
+ private final ArrayList<ListItem> mItems = new ArrayList<ListItem>();
+
+ public static final int ITEM_APPLICATION = 0;
+ public static final int ITEM_SHORTCUT = 1;
+ public static final int ITEM_SEARCH = 2;
+ public static final int ITEM_APPWIDGET = 3;
+ public static final int ITEM_LIVE_FOLDER = 4;
+ public static final int ITEM_FOLDER = 5;
+ public static final int ITEM_WALLPAPER = 6;
/**
- * Class representing an action that will create a specific type
- * of shortcut
+ * Specific item in our list.
*/
- public class CreateShortcutAction extends AddAction {
+ public class ListItem {
+ public final CharSequence text;
+ public final Drawable image;
+ public final int actionTag;
- ResolveInfo mInfo;
- private CharSequence mLabel;
- private Drawable mIcon;
-
- CreateShortcutAction(Context context, ResolveInfo info) {
- super(context);
- mInfo = info;
- }
-
- @Override
- public void bindView(View view) {
- ResolveInfo info = mInfo;
- TextView text = (TextView) view;
-
- PackageManager pm = mLauncher.getPackageManager();
-
- if (mLabel == null) {
- mLabel = info.loadLabel(pm);
- if (mLabel == null) {
- mLabel = info.activityInfo.name;
- }
+ public ListItem(Resources res, int textResourceId, int imageResourceId, int actionTag) {
+ text = res.getString(textResourceId);
+ if (imageResourceId != -1) {
+ image = res.getDrawable(imageResourceId);
+ } else {
+ image = null;
}
-
- if (mIcon == null) {
- mIcon = Utilities.createIconThumbnail(info.loadIcon(pm), mContext);
- }
-
- text.setText(mLabel);
- text.setCompoundDrawablesWithIntrinsicBounds(mIcon, null, null, null);
- }
-
- public void run() {
- Intent intent = new Intent(mCreateShortcutIntent);
- ActivityInfo activityInfo = mInfo.activityInfo;
- intent.setComponent(new ComponentName(activityInfo.applicationInfo.packageName,
- activityInfo.name));
- mLauncher.addShortcut(intent);
- }
- }
-
- /**
- * Class representing an action that will create a specific type
- * of live folder
- */
- public class CreateLiveFolderAction extends CreateShortcutAction {
- CreateLiveFolderAction(Context context, ResolveInfo info) {
- super(context, info);
- }
-
- @Override
- public void run() {
- Intent intent = new Intent(mCreateLiveFolderIntent);
- ActivityInfo activityInfo = mInfo.activityInfo;
- intent.setComponent(new ComponentName(activityInfo.applicationInfo.packageName,
- activityInfo.name));
- mLauncher.addLiveFolder(intent);
- }
- }
-
- /**
- * Class representing an action that will add a folder
- */
- public class CreateFolderAction extends AddAction {
- private Drawable mIcon;
-
- CreateFolderAction(Context context) {
- super(context);
- }
-
- @Override
- public void bindView(View view) {
- TextView text = (TextView) view;
- text.setText(R.string.add_folder);
- if (mIcon == null) mIcon = getIcon(R.drawable.ic_launcher_folder);
- text.setCompoundDrawablesWithIntrinsicBounds(mIcon, null, null, null);
- }
-
- public void run() {
- mLauncher.addFolder();
- }
- }
-
- /**
- * Class representing an action that will add a folder
- */
- public class CreateClockAction extends AddAction {
-
- CreateClockAction(Context context) {
- super(context);
- }
-
- @Override
- public void bindView(View view) {
- TextView text = (TextView) view;
- text.setText(R.string.add_clock);
- Drawable icon = getIcon(R.drawable.ic_launcher_alarmclock);
- text.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
- }
-
- public void run() {
- mLauncher.addClock();
- }
- }
-
- /**
- * Class representing an action that will add a PhotoFrame
- */
- public class CreatePhotoFrameAction extends AddAction {
- private Drawable mIcon;
-
- CreatePhotoFrameAction(Context context) {
- super(context);
- }
-
- @Override
- public void bindView(View view) {
- TextView text = (TextView) view;
- text.setText(R.string.add_photo_frame);
- if (mIcon == null) mIcon = getIcon(R.drawable.ic_launcher_gallery);
- text.setCompoundDrawablesWithIntrinsicBounds(mIcon, null, null, null);
- }
-
- public void run() {
- mLauncher.getPhotoForPhotoFrame();
- }
- }
-
-
- /**
- * Class representing an action that will add a Search widget
- */
- public class CreateSearchAction extends AddAction {
- private Drawable mIcon;
-
- CreateSearchAction(Context context) {
- super(context);
- }
-
- @Override
- public void bindView(View view) {
- TextView text = (TextView) view;
- text.setText(R.string.add_search);
- if (mIcon == null) mIcon = getIcon(R.drawable.ic_search_gadget);
- text.setCompoundDrawablesWithIntrinsicBounds(mIcon, null, null, null);
- }
-
- public void run() {
- mLauncher.addSearch();
+ this.actionTag = actionTag;
}
}
- private class Group {
- private String mName;
- private ArrayList<AddAction> mList;
-
- Group(String name) {
- mName = name;
- mList = new ArrayList<AddAction>();
- }
-
- void add(AddAction action) {
- mList.add(action);
- }
-
- int size() {
- return mList.size();
- }
-
- String getName() {
- return mName;
- }
-
- void run(int position) {
- mList.get(position).run();
- }
-
- void bindView(int childPosition, View view) {
- mList.get(childPosition).bindView(view);
- }
-
- public Object get(int childPosition) {
- return mList.get(childPosition);
- }
- }
-
- private class ApplicationsGroup extends Group {
- private final Launcher mLauncher;
- private final ArrayList<ApplicationInfo> mApplications;
-
- ApplicationsGroup(Launcher launcher, String name) {
- super(name);
- mLauncher = launcher;
- mApplications = Launcher.getModel().getApplications();
- }
-
- @Override
- int size() {
- return mApplications == null ? 0 : mApplications.size();
- }
-
- @Override
- void add(AddAction action) {
- }
-
- @Override
- void run(int position) {
- final ApplicationInfo info = mApplications.get(position);
- mLauncher.addApplicationShortcut(info);
- }
-
- @Override
- void bindView(int childPosition, View view) {
- TextView text = (TextView) view.findViewById(R.id.title);
-
- final ApplicationInfo info = mApplications.get(childPosition);
- text.setText(info.title);
- if (!info.filtered) {
- info.icon = Utilities.createIconThumbnail(info.icon, mLauncher);
- info.filtered = true;
- }
- text.setCompoundDrawablesWithIntrinsicBounds(info.icon, null, null, null);
- }
-
- @Override
- public Object get(int childPosition) {
- return mApplications.get(childPosition);
- }
- }
-
- public AddAdapter(Launcher launcher, boolean forFolder) {
- mCreateShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
- mCreateShortcutIntent.setComponent(null);
-
- mCreateLiveFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER);
- mCreateLiveFolderIntent.setComponent(null);
-
- mSetWallpaperIntent = new Intent(Intent.ACTION_SET_WALLPAPER);
- mSetWallpaperIntent.setComponent(null);
-
+ public AddAdapter(Launcher launcher) {
+ super();
+
mLauncher = launcher;
- mInflater = (LayoutInflater) launcher.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mInflater = (LayoutInflater) mLauncher.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ // Create default actions
+ Resources res = launcher.getResources();
+
+ mItems.add(new ListItem(res, R.string.group_applications,
+ R.drawable.ic_launcher_application, ITEM_APPLICATION));
+
+ mItems.add(new ListItem(res, R.string.group_shortcuts,
+ R.drawable.ic_launcher_empty, ITEM_SHORTCUT));
+
+ mItems.add(new ListItem(res, R.string.group_search,
+ R.drawable.ic_search_widget, ITEM_SEARCH));
+
+ mItems.add(new ListItem(res, R.string.group_widgets,
+ R.drawable.ic_launcher_appwidget, ITEM_APPWIDGET));
+
+ mItems.add(new ListItem(res, R.string.group_live_folders,
+ R.drawable.ic_launcher_empty, ITEM_LIVE_FOLDER));
+
+ mItems.add(new ListItem(res, R.string.group_folder,
+ R.drawable.ic_launcher_folder, ITEM_FOLDER));
+
+ mItems.add(new ListItem(res, R.string.group_wallpapers,
+ R.drawable.ic_launcher_gallery, ITEM_WALLPAPER));
- mGroups = new Group[forFolder ? 2 : 5];
- final Group[] groups = mGroups;
- groups[GROUP_APPLICATIONS] = new ApplicationsGroup(mLauncher,
- mLauncher.getString(R.string.group_applications));
- groups[GROUP_SHORTCUTS] = new Group(mLauncher.getString(R.string.group_shortcuts));
- groups[GROUP_LIVE_FOLDERS] = new Group(mLauncher.getString(R.string.group_live_folders));
+ }
- if (!forFolder) {
- groups[GROUP_WALLPAPERS] = new Group(mLauncher.getString(R.string.group_wallpapers));
- groups[GROUP_SHORTCUTS].add(new CreateFolderAction(launcher));
- groups[GROUP_WIDGETS] = new Group(mLauncher.getString(R.string.group_widgets));
-
- final Group widgets = groups[GROUP_WIDGETS];
- widgets.add(new CreateClockAction(launcher));
- widgets.add(new CreatePhotoFrameAction(launcher));
- widgets.add(new CreateSearchAction(launcher));
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ListItem item = (ListItem) getItem(position);
+
+ if (convertView == null) {
+ convertView = mInflater.inflate(R.layout.add_list_item, parent, false);
}
- PackageManager packageManager = launcher.getPackageManager();
-
- List<ResolveInfo> list = findTargetsForIntent(mCreateShortcutIntent, packageManager);
- if (list != null && list.size() > 0) {
- int count = list.size();
- final Group shortcuts = groups[GROUP_SHORTCUTS];
- for (int i = 0; i < count; i++) {
- ResolveInfo resolveInfo = list.get(i);
- shortcuts.add(new CreateShortcutAction(launcher, resolveInfo));
- }
- }
-
- list = findTargetsForIntent(mCreateLiveFolderIntent, packageManager);
- if (list != null && list.size() > 0) {
- int count = list.size();
- final Group shortcuts = groups[GROUP_LIVE_FOLDERS];
- for (int i = 0; i < count; i++) {
- ResolveInfo resolveInfo = list.get(i);
- shortcuts.add(new CreateLiveFolderAction(launcher, resolveInfo));
- }
- }
-
- list = findTargetsForIntent(mSetWallpaperIntent, packageManager);
- if (list != null && list.size() > 0) {
- int count = list.size();
- final Group shortcuts = groups[GROUP_WALLPAPERS];
- for (int i = 0; i < count; i++) {
- ResolveInfo resolveInfo = list.get(i);
- shortcuts.add(new SetWallpaperAction(launcher, resolveInfo));
- }
- }
+ TextView textView = (TextView) convertView;
+ textView.setTag(item);
+ textView.setText(item.text);
+ textView.setCompoundDrawablesWithIntrinsicBounds(item.image, null, null, null);
+
+ return convertView;
}
- private List<ResolveInfo> findTargetsForIntent(Intent intent, PackageManager packageManager) {
- List<ResolveInfo> list = packageManager.queryIntentActivities(intent,
- PackageManager.MATCH_DEFAULT_ONLY);
- if (list != null) {
- int count = list.size();
- if (count > 1) {
- // Only display the first matches that are either of equal
- // priority or have asked to be default options.
- ResolveInfo firstInfo = list.get(0);
- for (int i=1; i<count; i++) {
- ResolveInfo resolveInfo = list.get(i);
- if (firstInfo.priority != resolveInfo.priority ||
- firstInfo.isDefault != resolveInfo.isDefault) {
- while (i < count) {
- list.remove(i);
- count--;
- }
- }
- }
- Collections.sort(list, new ResolveInfo.DisplayNameComparator(packageManager));
- }
- }
- return list;
+ public int getCount() {
+ return mItems.size();
}
- public int getGroupCount() {
- return mGroups.length;
+ public Object getItem(int position) {
+ return mItems.get(position);
}
- public int getChildrenCount(int groupPosition) {
- return mGroups[groupPosition].size();
+ public long getItemId(int position) {
+ return position;
}
-
- public Object getGroup(int groupPosition) {
- return mGroups[groupPosition].getName();
- }
-
- public Object getChild(int groupPosition, int childPosition) {
- return mGroups[groupPosition].get(childPosition);
- }
-
- public long getGroupId(int groupPosition) {
- return groupPosition;
- }
-
- public long getChildId(int groupPosition, int childPosition) {
- return (groupPosition << 16) | childPosition;
- }
-
- public boolean hasStableIds() {
- return true;
- }
-
- public View getGroupView(int groupPosition, boolean isExpanded,
- View convertView, ViewGroup parent) {
- View view;
- if (convertView == null) {
- view = mInflater.inflate(R.layout.create_shortcut_group_item, parent, false);
- } else {
- view = convertView;
- }
- ((TextView) view).setText(mGroups[groupPosition].getName());
- return view;
- }
-
- public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
- View convertView, ViewGroup parent) {
- View view;
- if (convertView == null) {
- view = mInflater.inflate(R.layout.create_shortcut_list_item, parent, false);
- } else {
- view = convertView;
- }
- mGroups[groupPosition].bindView(childPosition, view);
- return view;
- }
-
- public boolean isChildSelectable(int groupPosition, int childPosition) {
- return true;
- }
-
- void performAction(int groupPosition, int childPosition) {
- mGroups[groupPosition].run(childPosition);
- }
+
}
diff --git a/src/com/android/launcher/AllAppsGridView.java b/src/com/android/launcher/AllAppsGridView.java
index a898c1a..b8f7902 100644
--- a/src/com/android/launcher/AllAppsGridView.java
+++ b/src/com/android/launcher/AllAppsGridView.java
@@ -19,25 +19,46 @@
import android.widget.GridView;
import android.widget.AdapterView;
import android.content.Context;
+import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
+import android.graphics.BitmapFactory;
+import android.graphics.Bitmap;
+import android.graphics.Paint;
+import android.graphics.Canvas;
public class AllAppsGridView extends GridView implements AdapterView.OnItemClickListener,
AdapterView.OnItemLongClickListener, DragSource {
private DragController mDragger;
private Launcher mLauncher;
+ private Bitmap mTexture;
+ private Paint mPaint;
+ private int mTextureWidth;
+ private int mTextureHeight;
public AllAppsGridView(Context context) {
super(context);
}
public AllAppsGridView(Context context, AttributeSet attrs) {
- super(context, attrs);
+ this(context, attrs, com.android.internal.R.attr.gridViewStyle);
}
public AllAppsGridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AllAppsGridView, defStyle, 0);
+ final int textureId = a.getResourceId(R.styleable.AllAppsGridView_texture, 0);
+ if (textureId != 0) {
+ mTexture = BitmapFactory.decodeResource(getResources(), textureId);
+ mTextureWidth = mTexture.getWidth();
+ mTextureHeight = mTexture.getHeight();
+
+ mPaint = new Paint();
+ mPaint.setDither(false);
+ }
+ a.recycle();
}
@Override
@@ -46,6 +67,32 @@
setOnItemLongClickListener(this);
}
+ @Override
+ public void draw(Canvas canvas) {
+ final Bitmap texture = mTexture;
+ final Paint paint = mPaint;
+
+ final int width = getWidth();
+ final int height = getHeight();
+
+ final int textureWidth = mTextureWidth;
+ final int textureHeight = mTextureHeight;
+
+ int x = 0;
+ int y;
+
+ while (x < width) {
+ y = 0;
+ while (y < height) {
+ canvas.drawBitmap(texture, x, y, paint);
+ y += textureHeight;
+ }
+ x += textureWidth;
+ }
+
+ super.draw(canvas);
+ }
+
public void onItemClick(AdapterView parent, View v, int position, long id) {
ApplicationInfo app = (ApplicationInfo) parent.getItemAtPosition(position);
mLauncher.startActivitySafely(app.intent);
diff --git a/src/com/android/launcher/BubbleTextView.java b/src/com/android/launcher/BubbleTextView.java
index f2c31e9..3782454 100644
--- a/src/com/android/launcher/BubbleTextView.java
+++ b/src/com/android/launcher/BubbleTextView.java
@@ -40,6 +40,9 @@
private boolean mBackgroundSizeChanged;
private Drawable mBackground;
+ private float mCornerRadius;
+ private float mPaddingH;
+ private float mPaddingV;
public BubbleTextView(Context context) {
super(context);
@@ -64,6 +67,12 @@
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(getContext().getResources().getColor(R.color.bubble_dark_background));
+
+ final float scale = getContext().getResources().getDisplayMetrics().density;
+ mCornerRadius = CORNER_RADIUS * scale;
+ mPaddingH = PADDING_H * scale;
+ //noinspection PointlessArithmeticExpression
+ mPaddingV = PADDING_V * scale;
}
@Override
@@ -114,11 +123,11 @@
final int left = getCompoundPaddingLeft();
final int top = getExtendedPaddingTop();
- rect.set(left + layout.getLineLeft(0) - PADDING_H,
- top + layout.getLineTop(0) - PADDING_V,
- Math.min(left + layout.getLineRight(0) + PADDING_H, mScrollX + mRight - mLeft),
- top + layout.getLineBottom(0) + PADDING_V);
- canvas.drawRoundRect(rect, CORNER_RADIUS, CORNER_RADIUS, mPaint);
+ rect.set(left + layout.getLineLeft(0) - mPaddingH,
+ top + layout.getLineTop(0) - mPaddingV,
+ Math.min(left + layout.getLineRight(0) + mPaddingH, mScrollX + mRight - mLeft),
+ top + layout.getLineBottom(0) + mPaddingV);
+ canvas.drawRoundRect(rect, mCornerRadius, mCornerRadius, mPaint);
super.draw(canvas);
}
diff --git a/src/com/android/launcher/CellLayout.java b/src/com/android/launcher/CellLayout.java
index 02646bf..91f0420 100644
--- a/src/com/android/launcher/CellLayout.java
+++ b/src/com/android/launcher/CellLayout.java
@@ -56,6 +56,8 @@
private RectF mDragRect = new RectF();
+ private boolean mDirtyTag;
+
public CellLayout(Context context) {
this(context, null);
}
@@ -157,6 +159,7 @@
cellInfo.spanY = lp.cellVSpan;
cellInfo.valid = true;
found = true;
+ mDirtyTag = false;
break;
}
}
@@ -181,10 +184,12 @@
cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < xCount &&
cellXY[1] < yCount && !occupied[cellXY[0]][cellXY[1]];
- if (cellInfo.valid) {
- findIntersectingVacantCells(cellInfo, cellXY[0], cellXY[1],
- xCount, yCount, occupied);
- }
+ // Instead of finding the interesting vacant cells here, wait until a
+ // caller invokes getTag() to retrieve the result. Finding the vacant
+ // cells is a bit expensive and can generate many new objects, it's
+ // therefore better to defer it until we know we actually need it.
+
+ mDirtyTag = true;
}
setTag(cellInfo);
} else if (action == MotionEvent.ACTION_UP) {
@@ -194,12 +199,31 @@
cellInfo.spanX = 0;
cellInfo.spanY = 0;
cellInfo.valid = false;
+ mDirtyTag = false;
setTag(cellInfo);
}
return false;
}
+ @Override
+ public CellInfo getTag() {
+ final CellInfo info = (CellInfo) super.getTag();
+ if (mDirtyTag && info.valid) {
+ final boolean portrait = mPortrait;
+ final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
+ final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
+
+ final boolean[][] occupied = mOccupied;
+ findOccupiedCells(xCount, yCount, occupied);
+
+ findIntersectingVacantCells(info, info.cellX, info.cellY, xCount, yCount, occupied);
+
+ mDirtyTag = false;
+ }
+ return info;
+ }
+
private static void findIntersectingVacantCells(CellInfo cellInfo, int x, int y,
int xCount, int yCount, boolean[][] occupied) {
@@ -207,14 +231,15 @@
cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE;
cellInfo.maxVacantSpanY = Integer.MIN_VALUE;
cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE;
- cellInfo.vacantCells = new ArrayList<CellInfo.VacantCell>();
+ cellInfo.clearVacantCells();
if (occupied[x][y]) {
return;
}
- Rect current = new Rect(x, y, x, y);
- findVacantCell(current, xCount, yCount, occupied, cellInfo);
+ cellInfo.current.set(x, y, x, y);
+
+ findVacantCell(cellInfo.current, xCount, yCount, occupied, cellInfo);
}
private static void findVacantCell(Rect current, int xCount, int yCount, boolean[][] occupied,
@@ -256,7 +281,7 @@
}
private static void addVacantCell(Rect current, CellInfo cellInfo) {
- CellInfo.VacantCell cell = new CellInfo.VacantCell();
+ CellInfo.VacantCell cell = CellInfo.VacantCell.acquire();
cell.cellX = current.left;
cell.cellY = current.top;
cell.spanX = current.right - current.left + 1;
@@ -317,10 +342,9 @@
cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE;
cellInfo.maxVacantSpanY = Integer.MIN_VALUE;
cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE;
- cellInfo.vacantCells = new ArrayList<CellInfo.VacantCell>();
cellInfo.screen = mCellInfo.screen;
- Rect current = new Rect();
+ Rect current = cellInfo.current;
for (int x = 0; x < xCount; x++) {
for (int y = 0; y < yCount; y++) {
@@ -333,16 +357,10 @@
}
cellInfo.valid = cellInfo.vacantCells.size() > 0;
- if (cellInfo.valid) {
- int[] xy = new int[2];
- if (cellInfo.findCellForSpan(xy, 1, 1)) {
- cellInfo.cellX = xy[0];
- cellInfo.cellY = xy[1];
- cellInfo.spanY = 1;
- cellInfo.spanX = 1;
- }
- }
+ // Assume the caller will perform their own cell searching, otherwise we
+ // risk causing an unnecessary rebuild after findCellForSpan()
+
return cellInfo;
}
@@ -634,6 +652,26 @@
dragRect.set(x, y, x + width, y + height);
}
+
+ /**
+ * Computes the required horizontal and vertical cell spans to always
+ * fit the given rectangle.
+ *
+ * @param width Width in pixels
+ * @param height Height in pixels
+ */
+ public int[] rectToCell(int width, int height) {
+ // Always assume we're working with the smallest span to make sure we
+ // reserve enough space in both orientations.
+ int actualWidth = mCellWidth + mWidthGap;
+ int actualHeight = mCellHeight + mHeightGap;
+ int smallerSize = Math.min(actualWidth, actualHeight);
+
+ // Always round up to next largest cell
+ int spanX = (width + smallerSize) / smallerSize;
+ int spanY = (height + smallerSize) / smallerSize;
+ return new int[] { spanX, spanY };
+ }
/**
* Find the first vacant cell, if there is one.
@@ -811,12 +849,54 @@
}
static final class CellInfo implements ContextMenu.ContextMenuInfo {
+ /**
+ * See View.AttachInfo.InvalidateInfo for futher explanations about
+ * the recycling mechanism. In this case, we recycle the vacant cells
+ * instances because up to several hundreds can be instanciated when
+ * the user long presses an empty cell.
+ */
static final class VacantCell {
int cellX;
int cellY;
int spanX;
int spanY;
+ // We can create up to 523 vacant cells on a 4x4 grid, 100 seems
+ // like a reasonable compromise given the size of a VacantCell and
+ // the fact that the user is not likely to touch an empty 4x4 grid
+ // very often
+ private static final int POOL_LIMIT = 100;
+ private static final Object sLock = new Object();
+
+ private static int sAcquiredCount = 0;
+ private static VacantCell sRoot;
+
+ private VacantCell next;
+
+ static VacantCell acquire() {
+ synchronized (sLock) {
+ if (sRoot == null) {
+ return new VacantCell();
+ }
+
+ VacantCell info = sRoot;
+ sRoot = info.next;
+ sAcquiredCount--;
+
+ return info;
+ }
+ }
+
+ void release() {
+ synchronized (sLock) {
+ if (sAcquiredCount < POOL_LIMIT) {
+ sAcquiredCount++;
+ next = sRoot;
+ sRoot = this;
+ }
+ }
+ }
+
@Override
public String toString() {
return "VacantCell[x=" + cellX + ", y=" + cellY + ", spanX=" + spanX +
@@ -832,17 +912,27 @@
int screen;
boolean valid;
- ArrayList<VacantCell> vacantCells;
+ final ArrayList<VacantCell> vacantCells = new ArrayList<VacantCell>(VacantCell.POOL_LIMIT);
int maxVacantSpanX;
int maxVacantSpanXSpanY;
int maxVacantSpanY;
int maxVacantSpanYSpanX;
+ final Rect current = new Rect();
+
+ private void clearVacantCells() {
+ final ArrayList<VacantCell> list = vacantCells;
+ final int count = list.size();
+
+ for (int i = 0; i < count; i++) list.get(i).release();
+
+ list.clear();
+ }
void findVacantCellsFromOccupied(boolean[] occupied, int xCount, int yCount) {
if (cellX < 0 || cellY < 0) {
maxVacantSpanX = maxVacantSpanXSpanY = Integer.MIN_VALUE;
maxVacantSpanY = maxVacantSpanYSpanX = Integer.MIN_VALUE;
- vacantCells = new ArrayList<VacantCell>();
+ clearVacantCells();
return;
}
@@ -855,26 +945,40 @@
CellLayout.findIntersectingVacantCells(this, cellX, cellY, xCount, yCount, unflattened);
}
+ /**
+ * This method can be called only once! Calling #findVacantCellsFromOccupied will
+ * restore the ability to call this method.
+ *
+ * Finds the upper-left coordinate of the first rectangle in the grid that can
+ * hold a cell of the specified dimensions.
+ *
+ * @param cellXY The array that will contain the position of a vacant cell if such a cell
+ * can be found.
+ * @param spanX The horizontal span of the cell we want to find.
+ * @param spanY The vertical span of the cell we want to find.
+ *
+ * @return True if a vacant cell of the specified dimension was found, false otherwise.
+ */
boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
- if (vacantCells == null) {
- return false;
- }
+ final ArrayList<VacantCell> list = vacantCells;
+ final int count = list.size();
+
+ boolean found = false;
if (this.spanX >= spanX && this.spanY >= spanY) {
cellXY[0] = cellX;
cellXY[1] = cellY;
- return true;
+ found = true;
}
- final ArrayList<VacantCell> list = vacantCells;
- final int count = list.size();
// Look for an exact match first
for (int i = 0; i < count; i++) {
VacantCell cell = list.get(i);
if (cell.spanX == spanX && cell.spanY == spanY) {
cellXY[0] = cell.cellX;
cellXY[1] = cell.cellY;
- return true;
+ found = true;
+ break;
}
}
@@ -884,11 +988,14 @@
if (cell.spanX >= spanX && cell.spanY >= spanY) {
cellXY[0] = cell.cellX;
cellXY[1] = cell.cellY;
- return true;
+ found = true;
+ break;
}
}
- return false;
+ clearVacantCells();
+
+ return found;
}
@Override
diff --git a/src/com/android/launcher/DeleteZone.java b/src/com/android/launcher/DeleteZone.java
index 798cf0d..7f92c23 100644
--- a/src/com/android/launcher/DeleteZone.java
+++ b/src/com/android/launcher/DeleteZone.java
@@ -85,7 +85,11 @@
final LauncherModel model = Launcher.getModel();
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
- model.removeDesktopItem(item);
+ if (item instanceof LauncherAppWidgetInfo) {
+ model.removeDesktopAppWidget((LauncherAppWidgetInfo) item);
+ } else {
+ model.removeDesktopItem(item);
+ }
} else {
if (source instanceof UserFolder) {
final UserFolder userFolder = (UserFolder) source;
@@ -97,6 +101,12 @@
final UserFolderInfo userFolderInfo = (UserFolderInfo)item;
LauncherModel.deleteUserFolderContentsFromDatabase(mLauncher, userFolderInfo);
model.removeUserFolder(userFolderInfo);
+ } else if (item instanceof LauncherAppWidgetInfo) {
+ final LauncherAppWidgetInfo launcherAppWidgetInfo = (LauncherAppWidgetInfo) item;
+ final LauncherAppWidgetHost appWidgetHost = mLauncher.getAppWidgetHost();
+ if (appWidgetHost != null) {
+ appWidgetHost.deleteAppWidgetId(launcherAppWidgetInfo.appWidgetId);
+ }
}
LauncherModel.deleteItemFromDatabase(mLauncher, item);
}
diff --git a/src/com/android/launcher/DragLayer.java b/src/com/android/launcher/DragLayer.java
index 56140dd..b542de6 100644
--- a/src/com/android/launcher/DragLayer.java
+++ b/src/com/android/launcher/DragLayer.java
@@ -32,6 +32,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.KeyEvent;
+import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
/**
@@ -127,6 +128,8 @@
private int mAnimationType;
private int mAnimationState = ANIMATION_STATE_DONE;
+ private InputMethodManager mInputMethodManager;
+
/**
* Used to create a new DragLayer from XML.
*
@@ -144,7 +147,14 @@
if (PROFILE_DRAWING_DURING_DRAG) {
android.os.Debug.startMethodTracing("Launcher");
}
-
+
+ // Hide soft keyboard, if visible
+ if (mInputMethodManager == null) {
+ mInputMethodManager = (InputMethodManager)
+ getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+ }
+ mInputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
+
if (mListener != null) {
mListener.onDragStart(v, source, dragInfo, dragAction);
}
@@ -320,40 +330,33 @@
break;
case MotionEvent.ACTION_MOVE:
- if (Launcher.sOpenGlEnabled) {
- mLastMotionX = x;
- mLastMotionY = y;
+ final int scrollX = mScrollX;
+ final int scrollY = mScrollY;
- invalidate();
- } else {
- final int scrollX = mScrollX;
- final int scrollY = mScrollY;
+ final float touchX = mTouchOffsetX;
+ final float touchY = mTouchOffsetY;
- final float touchX = mTouchOffsetX;
- final float touchY = mTouchOffsetY;
+ final int offsetX = mBitmapOffsetX;
+ final int offsetY = mBitmapOffsetY;
- final int offsetX = mBitmapOffsetX;
- final int offsetY = mBitmapOffsetY;
+ int left = (int) (scrollX + mLastMotionX - touchX - offsetX);
+ int top = (int) (scrollY + mLastMotionY - touchY - offsetY);
- int left = (int) (scrollX + mLastMotionX - touchX - offsetX);
- int top = (int) (scrollY + mLastMotionY - touchY - offsetY);
+ final Bitmap dragBitmap = mDragBitmap;
+ final int width = dragBitmap.getWidth();
+ final int height = dragBitmap.getHeight();
- final Bitmap dragBitmap = mDragBitmap;
- final int width = dragBitmap.getWidth();
- final int height = dragBitmap.getHeight();
+ final Rect rect = mRect;
+ rect.set(left - 1, top - 1, left + width + 1, top + height + 1);
- final Rect rect = mRect;
- rect.set(left - 1, top - 1, left + width + 1, top + height + 1);
+ mLastMotionX = x;
+ mLastMotionY = y;
- mLastMotionX = x;
- mLastMotionY = y;
+ left = (int) (scrollX + x - touchX - offsetX);
+ top = (int) (scrollY + y - touchY - offsetY);
- left = (int) (scrollX + x - touchX - offsetX);
- top = (int) (scrollY + y - touchY - offsetY);
-
- rect.union(left - 1, top - 1, left + width + 1, top + height + 1);
- invalidate(rect);
- }
+ rect.union(left - 1, top - 1, left + width + 1, top + height + 1);
+ invalidate(rect);
final int[] coordinates = mDropCoordinates;
DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates);
diff --git a/src/com/android/launcher/ItemInfo.java b/src/com/android/launcher/ItemInfo.java
index 61745dd..51449a7 100644
--- a/src/com/android/launcher/ItemInfo.java
+++ b/src/com/android/launcher/ItemInfo.java
@@ -38,10 +38,8 @@
/**
* One of {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION},
* {@link LauncherSettings.Favorites#ITEM_TYPE_SHORTCUT},
- * {@link LauncherSettings.Favorites#ITEM_TYPE_USER_FOLDER},
- * {@link LauncherSettings.Favorites#ITEM_TYPE_WIDGET_CLOCK},
- * {@link LauncherSettings.Favorites#ITEM_TYPE_WIDGET_SEARCH} or
- * {@link LauncherSettings.Favorites#ITEM_TYPE_WIDGET_PHOTO_FRAME},
+ * {@link LauncherSettings.Favorites#ITEM_TYPE_USER_FOLDER}, or
+ * {@link LauncherSettings.Favorites#ITEM_TYPE_APPWIDGET}.
*/
int itemType;
diff --git a/src/com/android/launcher/Launcher.java b/src/com/android/launcher/Launcher.java
index 465400b..17f16a7 100644
--- a/src/com/android/launcher/Launcher.java
+++ b/src/com/android/launcher/Launcher.java
@@ -24,17 +24,21 @@
import android.app.StatusBarManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.graphics.Bitmap;
+import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
@@ -64,15 +68,16 @@
import android.view.WindowManager;
import android.view.View.OnLongClickListener;
import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
import android.widget.EditText;
-import android.widget.ExpandableListView;
-import android.widget.ImageView;
+import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.GridView;
+import android.widget.SlidingDrawer;
import android.app.IWallpaperService;
-
-import com.android.internal.widget.SlidingDrawer;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -82,12 +87,12 @@
*/
public final class Launcher extends Activity implements View.OnClickListener, OnLongClickListener {
static final String LOG_TAG = "Launcher";
+ static final boolean LOGD = false;
private static final boolean PROFILE_STARTUP = false;
+ private static final boolean PROFILE_DRAWER = false;
private static final boolean DEBUG_USER_INTERFACE = false;
- private static final boolean REMOVE_SHORTCUT_ON_PACKAGE_REMOVE = false;
-
private static final int WALLPAPER_SCREENS_SPAN = 2;
private static final int MENU_GROUP_ADD = 1;
@@ -98,9 +103,12 @@
private static final int MENU_SETTINGS = MENU_NOTIFICATIONS + 1;
private static final int REQUEST_CREATE_SHORTCUT = 1;
- private static final int REQUEST_CHOOSE_PHOTO = 2;
- private static final int REQUEST_UPDATE_PHOTO = 3;
private static final int REQUEST_CREATE_LIVE_FOLDER = 4;
+ private static final int REQUEST_CREATE_APPWIDGET = 5;
+ private static final int REQUEST_PICK_APPLICATION = 6;
+ private static final int REQUEST_PICK_SHORTCUT = 7;
+ private static final int REQUEST_PICK_LIVE_FOLDER = 8;
+ private static final int REQUEST_PICK_APPWIDGET = 9;
static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate";
@@ -144,14 +152,10 @@
// Type: long
private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id";
- private static LauncherModel sModel;
+ private static final LauncherModel sModel = new LauncherModel();
private static Bitmap sWallpaper;
- // Indicates whether the OpenGL pipeline was enabled, either through
- // USE_OPENGL_BY_DEFAULT or the system property launcher.opengl
- static boolean sOpenGlEnabled;
-
private static final Object sLock = new Object();
private static int sScreen = DEFAULT_SCREN;
@@ -164,7 +168,12 @@
private DragLayer mDragLayer;
private Workspace mWorkspace;
-
+
+ private AppWidgetManager mAppWidgetManager;
+ private LauncherAppWidgetHost mAppWidgetHost;
+
+ static final int APPWIDGET_HOST_ID = 1024;
+
private CellLayout.CellInfo mAddItemCellInfo;
private CellLayout.CellInfo mMenuAddInfo;
private final int[] mCellCoordinates = new int[2];
@@ -172,6 +181,7 @@
private SlidingDrawer mDrawer;
private TransitionDrawable mHandleIcon;
+ private HandleView mHandleView;
private AllAppsGridView mAllAppsGrid;
private boolean mDesktopLocked = true;
@@ -185,11 +195,18 @@
private boolean mWaitingForResult;
private boolean mLocaleChanged;
+ private Bundle mSavedInstanceState;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mInflater = getLayoutInflater();
-
+
+ mAppWidgetManager = AppWidgetManager.getInstance(this);
+
+ mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
+ mAppWidgetHost.startListening();
+
if (PROFILE_STARTUP) {
android.os.Debug.startMethodTracing("/sdcard/launcher");
}
@@ -197,10 +214,6 @@
checkForLocaleChange();
setWallpaperDimension();
- if (sModel == null) {
- sModel = new LauncherModel();
- }
-
setContentView(R.layout.launcher);
setupViews();
@@ -260,8 +273,9 @@
}
private void startLoaders() {
- sModel.loadApplications(true, this, mLocaleChanged);
- sModel.loadUserItems(!mLocaleChanged, this, mLocaleChanged, true);
+ boolean loadApplications = sModel.loadApplications(true, this, mLocaleChanged);
+ sModel.loadUserItems(!mLocaleChanged, this, mLocaleChanged, loadApplications);
+
mRestoring = false;
}
@@ -283,20 +297,42 @@
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ // The pattern used here is that a user PICKs a specific application,
+ // which, depending on the target, might need to CREATE the actual target.
+
+ // For example, the user would PICK_SHORTCUT for "Music playlist", and we
+ // launch over to the Music app to actually CREATE_SHORTCUT.
+
if (resultCode == RESULT_OK && mAddItemCellInfo != null) {
switch (requestCode) {
+ case REQUEST_PICK_APPLICATION:
+ completeAddApplication(this, data, mAddItemCellInfo, !mDesktopLocked);
+ break;
+ case REQUEST_PICK_SHORTCUT:
+ addShortcut(data);
+ break;
case REQUEST_CREATE_SHORTCUT:
completeAddShortcut(data, mAddItemCellInfo, !mDesktopLocked);
break;
- case REQUEST_CHOOSE_PHOTO:
- completeAddPhotoFrame(data, mAddItemCellInfo);
- break;
- case REQUEST_UPDATE_PHOTO:
- completeUpdatePhotoFrame(data, mAddItemCellInfo);
+ case REQUEST_PICK_LIVE_FOLDER:
+ addLiveFolder(data);
break;
case REQUEST_CREATE_LIVE_FOLDER:
completeAddLiveFolder(data, mAddItemCellInfo, !mDesktopLocked);
break;
+ case REQUEST_PICK_APPWIDGET:
+ addAppWidget(data);
+ break;
+ case REQUEST_CREATE_APPWIDGET:
+ completeAddAppWidget(data, mAddItemCellInfo, !mDesktopLocked);
+ break;
+ }
+ } else if (requestCode == REQUEST_PICK_APPWIDGET &&
+ resultCode == RESULT_CANCELED && data != null) {
+ // Clean up the appWidgetId if we canceled
+ int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
+ if (appWidgetId != -1) {
+ mAppWidgetHost.deleteAppWidgetId(appWidgetId);
}
}
mWaitingForResult = false;
@@ -312,19 +348,21 @@
}
@Override
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- boolean handled = super.onKeyUp(keyCode, event);
- if (keyCode == KeyEvent.KEYCODE_SEARCH) {
- handled = mWorkspace.snapToSearch();
- if (handled) closeDrawer(true);
- }
- return handled;
+ protected void onPause() {
+ super.onPause();
+ closeDrawer(false);
+ }
+
+ private boolean acceptFilter() {
+ final InputMethodManager inputManager = (InputMethodManager)
+ getSystemService(Context.INPUT_METHOD_SERVICE);
+ return !inputManager.isFullscreenMode();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
boolean handled = super.onKeyDown(keyCode, event);
- if (!handled && keyCode != KeyEvent.KEYCODE_ENTER) {
+ if (!handled && acceptFilter() && keyCode != KeyEvent.KEYCODE_ENTER) {
boolean gotKey = TextKeyListener.getInstance().onKeyDown(mWorkspace, mDefaultKeySsb,
keyCode, event);
if (gotKey && mDefaultKeySsb != null && mDefaultKeySsb.length() > 0) {
@@ -398,7 +436,6 @@
mRestoring = true;
}
-
boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false);
if (renameFolder) {
long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID);
@@ -425,9 +462,9 @@
final DeleteZone deleteZone = (DeleteZone) dragLayer.findViewById(R.id.delete_zone);
- final HandleView handleIcon = (HandleView) drawer.findViewById(R.id.all_apps);
- handleIcon.setLauncher(this);
- mHandleIcon = (TransitionDrawable) handleIcon.getDrawable();
+ mHandleView = (HandleView) drawer.findViewById(R.id.all_apps);
+ mHandleView.setLauncher(this);
+ mHandleIcon = (TransitionDrawable) mHandleView.getDrawable();
mHandleIcon.setCrossFadeEnabled(true);
drawer.lock();
@@ -439,10 +476,6 @@
grid.setTextFilterEnabled(true);
grid.setDragger(dragLayer);
grid.setLauncher(this);
- if (sOpenGlEnabled) {
- grid.setScrollingCacheEnabled(false);
- grid.setFadingEdgeLength(0);
- }
workspace.setOnLongClickListener(this);
workspace.setDragger(dragLayer);
@@ -451,23 +484,11 @@
deleteZone.setLauncher(this);
deleteZone.setDragController(dragLayer);
- deleteZone.setHandle(handleIcon);
+ deleteZone.setHandle(mHandleView);
dragLayer.setIgnoredDropTarget(grid);
dragLayer.setDragScoller(workspace);
dragLayer.setDragListener(deleteZone);
-
- if (DEBUG_USER_INTERFACE) {
- android.widget.Button finishButton = new android.widget.Button(this);
- finishButton.setText("Finish");
- workspace.addInScreen(finishButton, 1, 0, 0, 1, 1);
-
- finishButton.setOnClickListener(new android.widget.Button.OnClickListener() {
- public void onClick(View v) {
- finish();
- }
- });
- }
}
/**
@@ -507,11 +528,44 @@
return favorite;
}
- void addApplicationShortcut(ApplicationInfo info) {
- mAddItemCellInfo.screen = mWorkspace.getCurrentScreen();
- mWorkspace.addApplicationShortcut(info, mAddItemCellInfo);
- }
+ /**
+ * Add an application shortcut to the workspace.
+ *
+ * @param data The intent describing the application.
+ * @param cellInfo The position on screen where to create the shortcut.
+ */
+ void completeAddApplication(Context context, Intent data, CellLayout.CellInfo cellInfo,
+ boolean insertAtFirst) {
+ cellInfo.screen = mWorkspace.getCurrentScreen();
+ if (!findSingleSlot(cellInfo)) return;
+ // Find details for this application
+ ComponentName component = data.getComponent();
+ PackageManager packageManager = context.getPackageManager();
+ ActivityInfo activityInfo = null;
+ try {
+ activityInfo = packageManager.getActivityInfo(component, 0 /* no flags */);
+ } catch (NameNotFoundException e) {
+ Log.e(LOG_TAG, "Couldn't find ActivityInfo for selected application", e);
+ }
+
+ if (activityInfo != null) {
+ ApplicationInfo itemInfo = new ApplicationInfo();
+
+ itemInfo.title = activityInfo.loadLabel(packageManager);
+ if (itemInfo.title == null) {
+ itemInfo.title = activityInfo.name;
+ }
+
+ itemInfo.setActivity(component, Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ itemInfo.icon = activityInfo.loadIcon(packageManager);
+ itemInfo.container = ItemInfo.NO_ID;
+
+ mWorkspace.addApplicationShortcut(itemInfo, cellInfo, insertAtFirst);
+ }
+ }
+
/**
* Add a shortcut to the workspace.
*
@@ -521,8 +575,9 @@
*/
private void completeAddShortcut(Intent data, CellLayout.CellInfo cellInfo,
boolean insertAtFirst) {
-
cellInfo.screen = mWorkspace.getCurrentScreen();
+ if (!findSingleSlot(cellInfo)) return;
+
final ApplicationInfo info = addShortcut(this, data, cellInfo, false);
if (!mRestoring) {
@@ -535,6 +590,60 @@
}
}
+
+ /**
+ * Add a widget to the workspace.
+ *
+ * @param data The intent describing the appWidgetId.
+ * @param cellInfo The position on screen where to create the widget.
+ */
+ private void completeAddAppWidget(Intent data, CellLayout.CellInfo cellInfo,
+ boolean insertAtFirst) {
+
+ Bundle extras = data.getExtras();
+ int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
+
+ Log.d(LOG_TAG, "dumping extras content="+extras.toString());
+
+ AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
+
+ // Calculate the grid spans needed to fit this widget
+ CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen);
+ int[] spans = layout.rectToCell(appWidgetInfo.minWidth, appWidgetInfo.minHeight);
+
+ // Try finding open space on Launcher screen
+ final int[] xy = mCellCoordinates;
+ if (!findSlot(cellInfo, xy, spans[0], spans[1])) return;
+
+ // Build Launcher-specific widget info and save to database
+ LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId);
+ launcherInfo.spanX = spans[0];
+ launcherInfo.spanY = spans[1];
+
+ LauncherModel.addItemToDatabase(this, launcherInfo,
+ LauncherSettings.Favorites.CONTAINER_DESKTOP,
+ mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
+
+ if (!mRestoring) {
+ sModel.addDesktopAppWidget(launcherInfo);
+
+ // Perform actual inflation because we're live
+ launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+
+ launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
+ launcherInfo.hostView.setTag(launcherInfo);
+
+ mWorkspace.addInCurrentScreen(launcherInfo.hostView, xy[0], xy[1],
+ launcherInfo.spanX, launcherInfo.spanY, insertAtFirst);
+ } else if (sModel.isDesktopLoaded()) {
+ sModel.addDesktopAppWidget(launcherInfo);
+ }
+ }
+
+ public LauncherAppWidgetHost getAppWidgetHost() {
+ return mAppWidgetHost;
+ }
+
static ApplicationInfo addShortcut(Context context, Intent data,
CellLayout.CellInfo cellInfo, boolean notify) {
@@ -584,107 +693,6 @@
return info;
}
- /**
- * Add a PhotFrame to the workspace.
- *
- * @param data The intent describing the photo.
- * @param cellInfo The position on screen where to create the shortcut.
- */
- private void completeAddPhotoFrame(Intent data, CellLayout.CellInfo cellInfo) {
- final Bundle extras = data.getExtras();
- if (extras != null) {
- Bitmap photo = extras.getParcelable("data");
-
- Widget info = Widget.makePhotoFrame();
- info.photo = photo;
-
- final int[] xy = mCellCoordinates;
- if (!findSlot(cellInfo, xy, info.spanX, info.spanY)) return;
-
- LauncherModel.addItemToDatabase(this, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
- mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
-
- if (!mRestoring) {
- sModel.addDesktopItem(info);
-
- final PhotoFrame view = (PhotoFrame) mInflater.inflate(info.layoutResource, null);
- view.setImageBitmap(photo);
- view.setTag(info);
-
- mWorkspace.addInCurrentScreen(view, xy[0], xy[1], info.spanX, info.spanY);
- } else if (sModel.isDesktopLoaded()) {
- sModel.addDesktopItem(info);
- }
- }
- }
-
- /**
- * Updates a workspace PhotoFrame.
- *
- * @param data The intent describing the photo.
- * @param cellInfo The position on screen of the PhotoFrame to update.
- */
- private void completeUpdatePhotoFrame(Intent data, CellLayout.CellInfo cellInfo) {
- final Bundle extras = data.getExtras();
- if (extras != null) {
- Widget info;
- Bitmap photo = extras.getParcelable("data");
-
- if (!mRestoring) {
- final CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen);
- final PhotoFrame view = (PhotoFrame) layout.findCell(cellInfo.cellX, cellInfo.cellY,
- cellInfo.spanX, cellInfo.spanY, null);
- view.setImageBitmap(photo);
- info = (Widget) view.getTag();
- } else {
- info = LauncherModel.getPhotoFrameInfo(this, cellInfo.screen,
- cellInfo.cellX, cellInfo.cellY);
- }
-
- info.photo = photo;
- LauncherModel.updateItemInDatabase(this, info);
- }
- }
-
- /**
- * Starts a new Intent to let the user update the PhotoFrame defined by the
- * specified Widget.
- *
- * @param widget The Widget info defining which PhotoFrame to update.
- */
- void updatePhotoFrame(Widget widget) {
- CellLayout.CellInfo info = new CellLayout.CellInfo();
- info.screen = widget.screen;
- info.cellX = widget.cellX;
- info.cellY = widget.cellY;
- info.spanX = widget.spanX;
- info.spanY = widget.spanY;
- mAddItemCellInfo = info;
-
- startActivityForResult(createPhotoPickIntent(), Launcher.REQUEST_UPDATE_PHOTO);
- }
-
- /**
- * Creates an Intent used to let the user pick a photo for a PhotoFrame.
- *
- * @return The Intent to pick a photo suited for a PhotoFrame.
- */
- private static Intent createPhotoPickIntent() {
- // TODO: Move this method to PhotoFrame?
- // TODO: get these values from constants somewhere
- // TODO: Adjust the PhotoFrame's image size to avoid on the fly scaling
- Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
- intent.setType("image/*");
- intent.putExtra("crop", "true");
- intent.putExtra("aspectX", 1);
- intent.putExtra("aspectY", 1);
- intent.putExtra("outputX", 192);
- intent.putExtra("outputY", 192);
- intent.putExtra("noFaceDetection", true);
- intent.putExtra("return-data", true);
- return intent;
- }
-
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
@@ -730,6 +738,12 @@
}
@Override
+ protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ // Do not call super here
+ mSavedInstanceState = savedInstanceState;
+ }
+
+ @Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getCurrentScreen());
@@ -776,6 +790,12 @@
mDestroyed = true;
super.onDestroy();
+
+ try {
+ mAppWidgetHost.stopListening();
+ } catch (NullPointerException ex) {
+ Log.w(LOG_TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex);
+ }
TextKeyListener.getInstance().release();
@@ -853,9 +873,7 @@
startWallpaper();
return true;
case MENU_SEARCH:
- if (!mWorkspace.snapToSearch()) {
- onSearchRequested();
- }
+ onSearchRequested();
return true;
case MENU_NOTIFICATIONS:
showNotifications();
@@ -865,16 +883,63 @@
return super.onOptionsItemSelected(item);
}
+ @Override
+ public boolean onSearchRequested() {
+ if (mWorkspace.snapToSearch()) {
+ closeDrawer(true); // search widget: get drawer out of the way
+ return true;
+ } else {
+ return super.onSearchRequested(); // no search widget: use system search UI
+ }
+ }
+
private void addItems() {
showAddDialog(mMenuAddInfo);
}
private void removeShortcutsForPackage(String packageName) {
if (packageName != null && packageName.length() > 0) {
- android.util.Log.d(LOG_TAG, packageName);
mWorkspace.removeShortcutsForPackage(packageName);
}
}
+
+ void addAppWidget(Intent data) {
+ // TODO: catch bad widget exception when sent
+ int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
+ AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
+
+ if (appWidget.configure != null) {
+ // Launch over to configure widget, if needed
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
+ intent.setComponent(appWidget.configure);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+
+ startActivityForResult(intent, REQUEST_CREATE_APPWIDGET);
+ } else {
+ // Otherwise just add it
+ onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data);
+ }
+ }
+
+ void addSearch() {
+ final Widget info = Widget.makeSearch();
+ final CellLayout.CellInfo cellInfo = mAddItemCellInfo;
+
+ final int[] xy = mCellCoordinates;
+ final int spanX = info.spanX;
+ final int spanY = info.spanY;
+
+ if (!findSlot(cellInfo, xy, spanX, spanY)) return;
+
+ sModel.addDesktopItem(info);
+ LauncherModel.addItemToDatabase(this, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
+ mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
+
+ final View view = mInflater.inflate(info.layoutResource, null);
+ view.setTag(info);
+
+ mWorkspace.addInCurrentScreen(view, xy[0], xy[1], info.spanX, spanY);
+ }
void addShortcut(Intent intent) {
startActivityForResult(intent, REQUEST_CREATE_SHORTCUT);
@@ -884,28 +949,32 @@
startActivityForResult(intent, REQUEST_CREATE_LIVE_FOLDER);
}
- void addFolder() {
+ void addFolder(boolean insertAtFirst) {
UserFolderInfo folderInfo = new UserFolderInfo();
folderInfo.title = getText(R.string.folder_name);
- int cellX = mAddItemCellInfo.cellX;
- int cellY = mAddItemCellInfo.cellY;
+
+ CellLayout.CellInfo cellInfo = mAddItemCellInfo;
+ cellInfo.screen = mWorkspace.getCurrentScreen();
+ if (!findSingleSlot(cellInfo)) return;
// Update the model
LauncherModel.addItemToDatabase(this, folderInfo, LauncherSettings.Favorites.CONTAINER_DESKTOP,
- mWorkspace.getCurrentScreen(), cellX, cellY, false);
+ mWorkspace.getCurrentScreen(), cellInfo.cellX, cellInfo.cellY, false);
sModel.addDesktopItem(folderInfo);
sModel.addFolder(folderInfo);
// Create the view
FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
(ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), folderInfo);
- mWorkspace.addInCurrentScreen(newFolder, cellX, cellY, 1, 1);
+ mWorkspace.addInCurrentScreen(newFolder,
+ cellInfo.cellX, cellInfo.cellY, 1, 1, insertAtFirst);
}
-
+
private void completeAddLiveFolder(Intent data, CellLayout.CellInfo cellInfo,
boolean insertAtFirst) {
-
cellInfo.screen = mWorkspace.getCurrentScreen();
+ if (!findSingleSlot(cellInfo)) return;
+
final LiveFolderInfo info = addLiveFolder(this, data, cellInfo, false);
if (!mRestoring) {
@@ -964,37 +1033,14 @@
return info;
}
- void getPhotoForPhotoFrame() {
- startActivityForResult(createPhotoPickIntent(), REQUEST_CHOOSE_PHOTO);
- }
-
- void addClock() {
- final Widget info = Widget.makeClock();
- addWidget(info);
- }
-
- void addSearch() {
- final Widget info = Widget.makeSearch();
- addWidget(info);
- }
-
- private void addWidget(final Widget info) {
- final CellLayout.CellInfo cellInfo = mAddItemCellInfo;
-
- final int[] xy = mCellCoordinates;
- final int spanX = info.spanX;
- final int spanY = info.spanY;
-
- if (!findSlot(cellInfo, xy, spanX, spanY)) return;
-
- sModel.addDesktopItem(info);
- LauncherModel.addItemToDatabase(this, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
- mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
-
- final View view = mInflater.inflate(info.layoutResource, null);
- view.setTag(info);
-
- mWorkspace.addInCurrentScreen(view, xy[0], xy[1], info.spanX, spanY);
+ private boolean findSingleSlot(CellLayout.CellInfo cellInfo) {
+ final int[] xy = new int[2];
+ if (findSlot(cellInfo, xy, 1, 1)) {
+ cellInfo.cellX = xy[0];
+ cellInfo.cellY = xy[1];
+ return true;
+ }
+ return false;
}
private boolean findSlot(CellLayout.CellInfo cellInfo, int[] xy, int spanX, int spanY) {
@@ -1117,9 +1163,9 @@
void onDesktopItemsLoaded() {
if (mDestroyed) return;
-
+android.util.Log.d("Home", "setting grid adapter");
+ mAllAppsGrid.setAdapter(sModel.getApplicationsAdapter());
bindDesktopItems();
- mAllAppsGrid.setAdapter(Launcher.getModel().getApplicationsAdapter());
}
/**
@@ -1127,7 +1173,8 @@
*/
private void bindDesktopItems() {
final ArrayList<ItemInfo> shortcuts = sModel.getDesktopItems();
- if (shortcuts == null) {
+ final ArrayList<LauncherAppWidgetInfo> appWidgets = sModel.getDesktopAppWidgets();
+ if (shortcuts == null || appWidgets == null) {
return;
}
@@ -1136,20 +1183,30 @@
for (int i = 0; i < count; i++) {
((ViewGroup) workspace.getChildAt(i)).removeAllViewsInLayout();
}
+
+ if (DEBUG_USER_INTERFACE) {
+ android.widget.Button finishButton = new android.widget.Button(this);
+ finishButton.setText("Finish");
+ workspace.addInScreen(finishButton, 1, 0, 0, 1, 1);
- count = shortcuts.size();
+ finishButton.setOnClickListener(new android.widget.Button.OnClickListener() {
+ public void onClick(View v) {
+ finish();
+ }
+ });
+ }
- final DesktopItemsBinder binder = new DesktopItemsBinder(this, shortcuts);
- binder.obtainMessage(DesktopItemsBinder.MESSAGE_BIND_ITEMS, 0, count).sendToTarget();
+ final DesktopBinder binder = new DesktopBinder(this, shortcuts, appWidgets);
+ binder.startBindingItems();
}
- private void bindItems(Launcher.DesktopItemsBinder binder,
+ private void bindItems(Launcher.DesktopBinder binder,
ArrayList<ItemInfo> shortcuts, int start, int count) {
final Workspace workspace = mWorkspace;
final boolean desktopLocked = mDesktopLocked;
- final int end = Math.min(start + DesktopItemsBinder.ITEMS_COUNT, count);
+ final int end = Math.min(start + DesktopBinder.ITEMS_COUNT, count);
int i = start;
for ( ; i < end; i++) {
@@ -1176,11 +1233,16 @@
workspace.addInScreen(newLiveFolder, item.screen, item.cellX, item.cellY, 1, 1,
!desktopLocked);
break;
- default:
+ case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
+ final int screen = workspace.getCurrentScreen();
+ final View view = mInflater.inflate(R.layout.widget_search,
+ (ViewGroup) workspace.getChildAt(screen), false);
+
final Widget widget = (Widget) item;
- final View view = createWidget(mInflater, widget);
view.setTag(widget);
+
workspace.addWidget(view, widget, !desktopLocked);
+ break;
}
}
@@ -1188,14 +1250,17 @@
if (end >= count) {
finishBindDesktopItems();
+ binder.startBindingAppWidgets();
} else {
- binder.obtainMessage(DesktopItemsBinder.MESSAGE_BIND_ITEMS, i, count).sendToTarget();
+ binder.obtainMessage(DesktopBinder.MESSAGE_BIND_ITEMS, i, count).sendToTarget();
}
}
private void finishBindDesktopItems() {
if (mSavedState != null) {
- mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
+ if (!mWorkspace.hasFocus()) {
+ mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
+ }
final long[] userFolders = mSavedState.getLongArray(RUNTIME_STATE_USER_FOLDERS);
if (userFolders != null) {
@@ -1208,35 +1273,67 @@
final Folder openFolder = mWorkspace.getOpenFolder();
if (openFolder != null) {
openFolder.requestFocus();
- } else {
- mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
}
}
final boolean allApps = mSavedState.getBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, false);
if (allApps) {
mDrawer.open();
- mDrawer.requestFocus();
}
mSavedState = null;
}
+ if (mSavedInstanceState != null) {
+ super.onRestoreInstanceState(mSavedInstanceState);
+ mSavedInstanceState = null;
+ }
+
+ if (mDrawer.isOpened() && !mDrawer.hasFocus()) {
+ mDrawer.requestFocus();
+ }
+
mDesktopLocked = false;
mDrawer.unlock();
}
- private View createWidget(LayoutInflater inflater, Widget widget) {
- final Workspace workspace = mWorkspace;
- final int screen = workspace.getCurrentScreen();
- View v = inflater.inflate(widget.layoutResource,
- (ViewGroup) workspace.getChildAt(screen), false);
- if (widget.itemType == LauncherSettings.Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME) {
- ((ImageView)v).setImageBitmap(widget.photo);
- }
- return v;
- }
+ private void bindAppWidgets(Launcher.DesktopBinder binder,
+ ArrayList<LauncherAppWidgetInfo> appWidgets, int start, int count) {
+ final Workspace workspace = mWorkspace;
+ final boolean desktopLocked = mDesktopLocked;
+
+ final int end = Math.min(start + DesktopBinder.APPWIDGETS_COUNT, count);
+ int i = start;
+
+ for ( ; i < end; i++) {
+ final LauncherAppWidgetInfo item = appWidgets.get(i);
+
+ final int appWidgetId = item.appWidgetId;
+ final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
+ item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+
+ if (LOGD) Log.d(LOG_TAG, String.format("about to setAppWidget for id=%d, info=%s", appWidgetId, appWidgetInfo));
+
+ item.hostView.setAppWidget(appWidgetId, appWidgetInfo);
+ item.hostView.setTag(item);
+
+ workspace.addInScreen(item.hostView, item.screen, item.cellX,
+ item.cellY, item.spanX, item.spanY, !desktopLocked);
+ }
+
+ workspace.requestLayout();
+
+ if (end >= count) {
+ finishBindDesktopAppWidgets();
+ } else {
+ binder.obtainMessage(DesktopBinder.MESSAGE_BIND_APPWIDGETS, i, count).sendToTarget();
+ }
+ }
+
+ private void finishBindDesktopAppWidgets() {
+ }
+
DragController getDragController() {
return mDragLayer;
}
@@ -1263,6 +1360,11 @@
startActivity(intent);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+ } catch (SecurityException e) {
+ Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+ Log.e(LOG_TAG, "Launcher does not have the permission to launch " + intent +
+ ". Make sure to create a MAIN intent-filter for the corresponding activity " +
+ "or use the exported attribute for this activity.", e);
}
}
@@ -1355,13 +1457,14 @@
// This happens when long clicking an item with the dpad/trackball
if (cellInfo == null) {
- return false;
+ return true;
}
if (mWorkspace.allowLongPress()) {
if (cellInfo.cell == null) {
if (cellInfo.valid) {
// User long pressed on empty space
+ mWorkspace.setAllowLongPress(false);
showAddDialog(cellInfo);
}
} else {
@@ -1382,10 +1485,22 @@
mDrawer.close();
}
+ View getDrawerHandle() {
+ return mHandleView;
+ }
+
boolean isDrawerDown() {
return !mDrawer.isMoving() && !mDrawer.isOpened();
}
+ boolean isDrawerUp() {
+ return mDrawer.isOpened() && !mDrawer.isMoving();
+ }
+
+ boolean isDrawerMoving() {
+ return mDrawer.isMoving();
+ }
+
Workspace getWorkspace() {
return mWorkspace;
}
@@ -1508,24 +1623,24 @@
* Displays the shortcut creation dialog and launches, if necessary, the
* appropriate activity.
*/
- private class CreateShortcut implements ExpandableListView.OnChildClickListener,
- DialogInterface.OnCancelListener, ExpandableListView.OnGroupExpandListener {
+ private class CreateShortcut implements AdapterView.OnItemClickListener,
+ DialogInterface.OnCancelListener {
private AddAdapter mAdapter;
- private ExpandableListView mList;
-
+ private ListView mList;
+
Dialog createDialog() {
mWaitingForResult = true;
- mAdapter = new AddAdapter(Launcher.this, false);
+
+ mAdapter = new AddAdapter(Launcher.this);
final AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this);
builder.setTitle(getString(R.string.menu_item_add_item));
builder.setIcon(0);
- mList = (ExpandableListView)
+ mList = (ListView)
View.inflate(Launcher.this, R.layout.create_shortcut_list, null);
mList.setAdapter(mAdapter);
- mList.setOnChildClickListener(this);
- mList.setOnGroupExpandListener(this);
+ mList.setOnItemClickListener(this);
builder.setView(mList);
builder.setInverseBackgroundForced(true);
@@ -1539,13 +1654,6 @@
return dialog;
}
- public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
- int childPosition, long id) {
- mAdapter.performAction(groupPosition, childPosition);
- cleanup();
- return true;
- }
-
public void onCancel(DialogInterface dialog) {
mWaitingForResult = false;
cleanup();
@@ -1556,10 +1664,75 @@
dismissDialog(DIALOG_CREATE_SHORTCUT);
}
- public void onGroupExpand(int groupPosition) {
- long packged = ExpandableListView.getPackedPositionForGroup(groupPosition);
- int position = mList.getFlatListPosition(packged);
- mList.setSelectionFromTop(position, 0);
+ public void onItemClick(AdapterView parent, View view, int position, long id) {
+ // handle which item was clicked based on position
+ // this will launch off pick intent
+
+ Object tag = view.getTag();
+ if (tag instanceof AddAdapter.ListItem) {
+ AddAdapter.ListItem item = (AddAdapter.ListItem) tag;
+ cleanup();
+ switch (item.actionTag) {
+ case AddAdapter.ITEM_APPLICATION: {
+ Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+ mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+
+ Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
+ pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent);
+ startActivityForResult(pickIntent, REQUEST_PICK_APPLICATION);
+ break;
+ }
+
+ case AddAdapter.ITEM_SHORTCUT: {
+ Intent shortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
+
+ Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
+ pickIntent.putExtra(Intent.EXTRA_INTENT, shortcutIntent);
+ pickIntent.putExtra(Intent.EXTRA_TITLE,
+ getText(R.string.title_select_shortcut));
+ startActivityForResult(pickIntent, REQUEST_PICK_SHORTCUT);
+ break;
+ }
+
+ case AddAdapter.ITEM_SEARCH: {
+ addSearch();
+ break;
+ }
+
+ case AddAdapter.ITEM_APPWIDGET: {
+ int appWidgetId = Launcher.this.mAppWidgetHost.allocateAppWidgetId();
+
+ Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
+ pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+ startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET);
+ break;
+ }
+
+ case AddAdapter.ITEM_LIVE_FOLDER: {
+ Intent liveFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER);
+
+ Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
+ pickIntent.putExtra(Intent.EXTRA_INTENT, liveFolderIntent);
+ pickIntent.putExtra(Intent.EXTRA_TITLE,
+ getText(R.string.title_select_live_folder));
+ startActivityForResult(pickIntent, REQUEST_PICK_LIVE_FOLDER);
+ break;
+ }
+
+ case AddAdapter.ITEM_FOLDER: {
+ addFolder(!mDesktopLocked);
+ dismissDialog(DIALOG_CREATE_SHORTCUT);
+ break;
+ }
+
+ case AddAdapter.ITEM_WALLPAPER: {
+ startWallpaper();
+ break;
+ }
+
+ }
+
+ }
}
}
@@ -1569,13 +1742,24 @@
private class ApplicationsIntentReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- //noinspection ConstantConditions
- if (REMOVE_SHORTCUT_ON_PACKAGE_REMOVE &&
- Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
- removeShortcutsForPackage(intent.getData().getSchemeSpecificPart());
+ boolean reloadWorkspace = false;
+android.util.Log.d("Home", "application intent received: " + intent.getAction());
+android.util.Log.d("Home", " --> " + intent.getData());
+ if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
+ if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ removeShortcutsForPackage(intent.getData().getSchemeSpecificPart());
+ } else {
+ reloadWorkspace = true;
+ }
}
removeDialog(DIALOG_CREATE_SHORTCUT);
- sModel.loadApplications(false, Launcher.this, false);
+ if (!reloadWorkspace) {
+android.util.Log.d("Home", " --> loading apps");
+ sModel.loadApplications(false, Launcher.this, false);
+ } else {
+android.util.Log.d("Home", " --> loading workspace");
+ sModel.loadUserItems(false, Launcher.this, false, true);
+ }
}
}
@@ -1637,48 +1821,93 @@
public void onDrawerOpened() {
if (!mOpen) {
mHandleIcon.reverseTransition(150);
+
+ final Rect bounds = mWorkspace.mDrawerBounds;
+ offsetBoundsToDragLayer(bounds, mAllAppsGrid);
+
mOpen = true;
}
}
+ private void offsetBoundsToDragLayer(Rect bounds, View view) {
+ view.getDrawingRect(bounds);
+
+ while (view != mDragLayer) {
+ bounds.offset(view.getLeft(), view.getTop());
+ view = (View) view.getParent();
+ }
+ }
+
public void onDrawerClosed() {
if (mOpen) {
mHandleIcon.reverseTransition(150);
+ mWorkspace.mDrawerBounds.setEmpty();
mOpen = false;
}
+
mAllAppsGrid.setSelection(0);
mAllAppsGrid.clearTextFilter();
- mWorkspace.clearChildrenCache();
}
public void onScrollStarted() {
- mWorkspace.enableChildrenCache();
+ if (PROFILE_DRAWER) {
+ android.os.Debug.startMethodTracing("/sdcard/launcher-drawer");
+ }
+
+ mWorkspace.mDrawerContentWidth = mAllAppsGrid.getWidth();
+ mWorkspace.mDrawerContentHeight = mAllAppsGrid.getHeight();
}
public void onScrollEnded() {
+ if (PROFILE_DRAWER) {
+ android.os.Debug.stopMethodTracing();
+ }
}
}
- private static class DesktopItemsBinder extends Handler {
+ private static class DesktopBinder extends Handler {
static final int MESSAGE_BIND_ITEMS = 0x1;
+ static final int MESSAGE_BIND_APPWIDGETS = 0x2;
// Number of items to bind in every pass
static final int ITEMS_COUNT = 6;
+ static final int APPWIDGETS_COUNT = 1;
private final ArrayList<ItemInfo> mShortcuts;
+ private final ArrayList<LauncherAppWidgetInfo> mAppWidgets;
private final WeakReference<Launcher> mLauncher;
- DesktopItemsBinder(Launcher launcher, ArrayList<ItemInfo> shortcuts) {
+ DesktopBinder(Launcher launcher, ArrayList<ItemInfo> shortcuts,
+ ArrayList<LauncherAppWidgetInfo> appWidgets) {
+
mLauncher = new WeakReference<Launcher>(launcher);
mShortcuts = shortcuts;
+ mAppWidgets = appWidgets;
+ }
+
+ public void startBindingItems() {
+ obtainMessage(MESSAGE_BIND_ITEMS, 0, mShortcuts.size()).sendToTarget();
}
+ public void startBindingAppWidgets() {
+ obtainMessage(MESSAGE_BIND_APPWIDGETS, 0, mAppWidgets.size()).sendToTarget();
+ }
+
@Override
public void handleMessage(Message msg) {
+ Launcher launcher = mLauncher.get();
+ if (launcher == null) {
+ return;
+ }
+
switch (msg.what) {
- case MESSAGE_BIND_ITEMS:
- Launcher launcher = mLauncher.get();
- if (launcher != null) launcher.bindItems(this, mShortcuts, msg.arg1, msg.arg2);
+ case MESSAGE_BIND_ITEMS: {
+ launcher.bindItems(this, mShortcuts, msg.arg1, msg.arg2);
break;
+ }
+ case MESSAGE_BIND_APPWIDGETS: {
+ launcher.bindAppWidgets(this, mAppWidgets, msg.arg1, msg.arg2);
+ break;
+ }
}
}
}
diff --git a/src/com/android/launcher/LauncherAppWidgetHost.java b/src/com/android/launcher/LauncherAppWidgetHost.java
new file mode 100644
index 0000000..22fd5b6
--- /dev/null
+++ b/src/com/android/launcher/LauncherAppWidgetHost.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher;
+
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+
+/**
+ * Specific {@link AppWidgetHost} that creates our {@link LauncherAppWidgetHostView}
+ * which correctly captures all long-press events. This ensures that users can
+ * always pick up and move widgets.
+ */
+public class LauncherAppWidgetHost extends AppWidgetHost {
+ public LauncherAppWidgetHost(Context context, int hostId) {
+ super(context, hostId);
+ }
+
+ @Override
+ protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
+ AppWidgetProviderInfo appWidget) {
+ return new LauncherAppWidgetHostView(context);
+ }
+}
diff --git a/src/com/android/launcher/LauncherAppWidgetHostView.java b/src/com/android/launcher/LauncherAppWidgetHostView.java
new file mode 100644
index 0000000..1e21a19
--- /dev/null
+++ b/src/com/android/launcher/LauncherAppWidgetHostView.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher;
+
+import android.appwidget.AppWidgetHostView;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+/**
+ * {@inheritDoc}
+ */
+public class LauncherAppWidgetHostView extends AppWidgetHostView {
+ private boolean mHasPerformedLongPress;
+
+ private CheckForLongPress mPendingCheckForLongPress;
+
+ private LayoutInflater mInflater;
+
+ public LauncherAppWidgetHostView(Context context) {
+ super(context);
+ mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ @Override
+ protected View getErrorView() {
+ return mInflater.inflate(R.layout.appwidget_error, this, false);
+ }
+
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ // Consume any touch events for ourselves after longpress is triggered
+ if (mHasPerformedLongPress) {
+ mHasPerformedLongPress = false;
+ return true;
+ }
+
+ // Watch for longpress events at this level to make sure
+ // users can always pick up this widget
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN: {
+ postCheckForLongClick();
+ break;
+ }
+
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mHasPerformedLongPress = false;
+ if (mPendingCheckForLongPress != null) {
+ removeCallbacks(mPendingCheckForLongPress);
+ }
+ break;
+ }
+
+ // Otherwise continue letting touch events fall through to children
+ return false;
+ }
+
+ class CheckForLongPress implements Runnable {
+ private int mOriginalWindowAttachCount;
+
+ public void run() {
+ if ((mParent != null) && hasWindowFocus()
+ && mOriginalWindowAttachCount == getWindowAttachCount()
+ && !mHasPerformedLongPress) {
+ if (performLongClick()) {
+ mHasPerformedLongPress = true;
+ }
+ }
+ }
+
+ public void rememberWindowAttachCount() {
+ mOriginalWindowAttachCount = getWindowAttachCount();
+ }
+ }
+
+ private void postCheckForLongClick() {
+ mHasPerformedLongPress = false;
+
+ if (mPendingCheckForLongPress == null) {
+ mPendingCheckForLongPress = new CheckForLongPress();
+ }
+ mPendingCheckForLongPress.rememberWindowAttachCount();
+ postDelayed(mPendingCheckForLongPress, ViewConfiguration.getLongPressTimeout());
+ }
+}
diff --git a/src/com/android/launcher/LauncherAppWidgetInfo.java b/src/com/android/launcher/LauncherAppWidgetInfo.java
new file mode 100644
index 0000000..3b5f08e
--- /dev/null
+++ b/src/com/android/launcher/LauncherAppWidgetInfo.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher;
+
+import android.appwidget.AppWidgetHostView;
+import android.content.ContentValues;
+
+/**
+ * Represents a widget, which just contains an identifier.
+ */
+class LauncherAppWidgetInfo extends ItemInfo {
+
+ /**
+ * Identifier for this widget when talking with {@link AppWidgetManager} for updates.
+ */
+ int appWidgetId;
+
+ /**
+ * View that holds this widget after it's been created. This view isn't created
+ * until Launcher knows it's needed.
+ */
+ AppWidgetHostView hostView = null;
+
+ LauncherAppWidgetInfo(int appWidgetId) {
+ itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
+ this.appWidgetId = appWidgetId;
+ }
+
+ @Override
+ void onAddToDatabase(ContentValues values) {
+ super.onAddToDatabase(values);
+ values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
+ }
+
+ @Override
+ public String toString() {
+ return Integer.toString(appWidgetId);
+ }
+}
diff --git a/src/com/android/launcher/LauncherModel.java b/src/com/android/launcher/LauncherModel.java
index 314a502..70b4c10 100644
--- a/src/com/android/launcher/LauncherModel.java
+++ b/src/com/android/launcher/LauncherModel.java
@@ -33,7 +33,6 @@
import android.os.Process;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Comparator;
@@ -44,33 +43,41 @@
/**
* Maintains in-memory state of the Launcher. It is expected that there should be only one
* LauncherModel object held in a static. Also provide APIs for updating the database state
- * for the Launcher
+ * for the Launcher.
*/
public class LauncherModel {
private static final int UI_NOTIFICATION_RATE = 4;
private static final int DEFAULT_APPLICATIONS_NUMBER = 42;
private static final long APPLICATION_NOT_RESPONDING_TIMEOUT = 5000;
+ private static final int INITIAL_ICON_CACHE_CAPACITY = 50;
- private final Collator sCollator = Collator.getInstance();
+ private static final boolean DEBUG = false;
+
+ private static final Collator sCollator = Collator.getInstance();
private boolean mApplicationsLoaded;
private boolean mDesktopItemsLoaded;
private ArrayList<ItemInfo> mDesktopItems;
+ private ArrayList<LauncherAppWidgetInfo> mDesktopAppWidgets;
private HashMap<Long, FolderInfo> mFolders;
private ArrayList<ApplicationInfo> mApplications;
private ApplicationsAdapter mApplicationsAdapter;
private ApplicationsLoader mApplicationsLoader;
private DesktopItemsLoader mDesktopItemsLoader;
- private Thread mLoader;
- private Thread mDesktopLoader;
+ private Thread mApplicationsLoaderThread;
+ private Thread mDesktopLoaderThread;
- void abortLoaders() {
+ private final HashMap<ComponentName, ApplicationInfo> mAppInfoCache =
+ new HashMap<ComponentName, ApplicationInfo>(INITIAL_ICON_CACHE_CAPACITY);
+
+ synchronized void abortLoaders() {
if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
mApplicationsLoader.stop();
mApplicationsLoaded = false;
}
+
if (mDesktopItemsLoader != null && mDesktopItemsLoader.isRunning()) {
mDesktopItemsLoader.stop();
mDesktopItemsLoaded = false;
@@ -78,41 +85,72 @@
}
/**
- * Loads the list of installed applications in mApplications.
+ * Drop our cache of components to their lables & icons. We do
+ * this from Launcher when applications are added/removed. It's a
+ * bit overkill, but it's a rare operation anyway.
*/
- void loadApplications(boolean isLaunching, Launcher launcher, boolean localeChanged) {
+ synchronized void dropApplicationCache() {
+ mAppInfoCache.clear();
+ }
+
+ /**
+ * Loads the list of installed applications in mApplications.
+ *
+ * @return true if the applications loader must be started
+ * (see startApplicationsLoader()), false otherwise.
+ */
+ synchronized boolean loadApplications(boolean isLaunching, Launcher launcher,
+ boolean localeChanged) {
+android.util.Log.d("Home", "load applications");
if (isLaunching && mApplicationsLoaded && !localeChanged) {
mApplicationsAdapter = new ApplicationsAdapter(launcher, mApplications);
- return;
+android.util.Log.d("Home", " --> applications loaded, return");
+ return false;
}
+ waitForApplicationsLoader();
+
+ if (localeChanged) {
+ dropApplicationCache();
+ }
+
if (mApplicationsAdapter == null || isLaunching || localeChanged) {
- mApplicationsAdapter = new ApplicationsAdapter(launcher,
- mApplications = new ArrayList<ApplicationInfo>(DEFAULT_APPLICATIONS_NUMBER));
- }
-
- if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
- mApplicationsLoader.stop();
- // Wait for the currently running thread to finish, this can take a little
- // time but it should be well below the timeout limit
- try {
- mLoader.join(APPLICATION_NOT_RESPONDING_TIMEOUT);
- } catch (InterruptedException e) {
- // Empty
- }
+ mApplications = new ArrayList<ApplicationInfo>(DEFAULT_APPLICATIONS_NUMBER);
+ mApplicationsAdapter = new ApplicationsAdapter(launcher, mApplications);
}
mApplicationsLoaded = false;
if (!isLaunching) {
startApplicationsLoader(launcher);
+ return false;
+ }
+
+ return true;
+ }
+
+ private synchronized void waitForApplicationsLoader() {
+ if (mApplicationsLoader != null && mApplicationsLoader.isRunning()) {
+ android.util.Log.d("Home", " --> wait for applications loader");
+
+ mApplicationsLoader.stop();
+ // Wait for the currently running thread to finish, this can take a little
+ // time but it should be well below the timeout limit
+ try {
+ mApplicationsLoaderThread.join(APPLICATION_NOT_RESPONDING_TIMEOUT);
+ } catch (InterruptedException e) {
+ // EMpty
+ }
}
}
- private void startApplicationsLoader(Launcher launcher) {
+ private synchronized void startApplicationsLoader(Launcher launcher) {
+android.util.Log.d("Home", " --> starting applications loader");
+ waitForApplicationsLoader();
+
mApplicationsLoader = new ApplicationsLoader(launcher);
- mLoader = new Thread(mApplicationsLoader, "Applications Loader");
- mLoader.start();
+ mApplicationsLoaderThread = new Thread(mApplicationsLoader, "Applications Loader");
+ mApplicationsLoaderThread.start();
}
private class ApplicationsLoader implements Runnable {
@@ -149,36 +187,39 @@
final int count = apps.size();
final ApplicationsAdapter applicationList = mApplicationsAdapter;
- ChangeNotifier action = new ChangeNotifier(applicationList);
+ ChangeNotifier action = new ChangeNotifier(applicationList, true);
+ final HashMap<ComponentName, ApplicationInfo> appInfoCache = mAppInfoCache;
for (int i = 0; i < count && !mStopped; i++) {
- ApplicationInfo application = new ApplicationInfo();
ResolveInfo info = apps.get(i);
-
- application.title = info.loadLabel(manager);
- if (application.title == null) {
- application.title = info.activityInfo.name;
- }
- application.setActivity(new ComponentName(
+ ComponentName componentName = new ComponentName(
info.activityInfo.applicationInfo.packageName,
- info.activityInfo.name),
- Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- application.icon = info.activityInfo.loadIcon(manager);
- application.container = ItemInfo.NO_ID;
-
- action.add(application);
- }
-
- action.sort(new Comparator<ApplicationInfo>() {
- public final int compare(ApplicationInfo a, ApplicationInfo b) {
- return sCollator.compare(a.title.toString(), b.title.toString());
+ info.activityInfo.name);
+ ApplicationInfo application = appInfoCache.get(componentName);
+ if (application == null) {
+ application = new ApplicationInfo();
+ application.title = info.loadLabel(manager);
+ if (application.title == null) {
+ application.title = info.activityInfo.name;
+ }
+ application.setActivity(componentName,
+ Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ application.container = ItemInfo.NO_ID;
+ application.icon = info.activityInfo.loadIcon(manager);
+ if (DEBUG) {
+ Log.d(Launcher.LOG_TAG, "Loaded ApplicationInfo for " + componentName);
+ }
+ appInfoCache.put(componentName, application);
}
- });
- if (!mStopped) {
- launcher.runOnUiThread(action);
+ if (action.add(application)) {
+ launcher.runOnUiThread(action);
+ action = new ChangeNotifier(applicationList, false);
+ }
}
+
+ launcher.runOnUiThread(action);
}
if (!mStopped) {
@@ -188,51 +229,66 @@
}
}
- private static class ChangeNotifier implements Runnable {
+ private static class ChangeNotifier implements Runnable, Comparator<ApplicationInfo> {
private final ApplicationsAdapter mApplicationList;
- private ArrayList<ApplicationInfo> mBuffer;
+ private final ArrayList<ApplicationInfo> mBuffer;
- ChangeNotifier(ApplicationsAdapter applicationList) {
+ private boolean mFirst = true;
+
+ ChangeNotifier(ApplicationsAdapter applicationList, boolean first) {
mApplicationList = applicationList;
+ mFirst = first;
mBuffer = new ArrayList<ApplicationInfo>(UI_NOTIFICATION_RATE);
}
public void run() {
- final ArrayList<ApplicationInfo> buffer = mBuffer;
final ApplicationsAdapter applicationList = mApplicationList;
+
+ if (mFirst) {
+ applicationList.setNotifyOnChange(false);
+ applicationList.clear();
+ mFirst = false;
+ }
+
+ final ArrayList<ApplicationInfo> buffer = mBuffer;
final int count = buffer.size();
- applicationList.clear();
for (int i = 0; i < count; i++) {
applicationList.setNotifyOnChange(false);
applicationList.add(buffer.get(i));
}
- applicationList.notifyDataSetChanged();
buffer.clear();
+
+ applicationList.sort(this);
+ applicationList.notifyDataSetChanged();
}
- void add(ApplicationInfo application) {
- mBuffer.add(application);
+ boolean add(ApplicationInfo application) {
+ final ArrayList<ApplicationInfo> buffer = mBuffer;
+ buffer.add(application);
+ return buffer.size() >= UI_NOTIFICATION_RATE;
}
- void sort(Comparator<ApplicationInfo> comparator) {
- Collections.sort(mBuffer, comparator);
+ public final int compare(ApplicationInfo a, ApplicationInfo b) {
+ return sCollator.compare(a.title.toString(), b.title.toString());
}
}
boolean isDesktopLoaded() {
- return mDesktopItems != null && mDesktopItemsLoaded;
+ return mDesktopItems != null && mDesktopAppWidgets != null && mDesktopItemsLoaded;
}
-
+
/**
* Loads all of the items on the desktop, in folders, or in the dock.
* These can be apps, shortcuts or widgets
*/
void loadUserItems(boolean isLaunching, Launcher launcher, boolean localeChanged,
boolean loadApplications) {
+android.util.Log.d("Home", "loading user items");
- if (isLaunching && mDesktopItems != null && mDesktopItemsLoaded) {
+ if (isLaunching && isDesktopLoaded()) {
+android.util.Log.d("Home", " --> items loaded, return");
if (loadApplications) startApplicationsLoader(launcher);
// We have already loaded our data from the DB
launcher.onDesktopItemsLoaded();
@@ -244,16 +300,17 @@
// Wait for the currently running thread to finish, this can take a little
// time but it should be well below the timeout limit
try {
- mDesktopLoader.join(APPLICATION_NOT_RESPONDING_TIMEOUT);
+ mDesktopLoaderThread.join(APPLICATION_NOT_RESPONDING_TIMEOUT);
} catch (InterruptedException e) {
// Empty
}
}
+android.util.Log.d("Home", " --> starting workspace loader");
mDesktopItemsLoaded = false;
mDesktopItemsLoader = new DesktopItemsLoader(launcher, localeChanged, loadApplications);
- mDesktopLoader = new Thread(mDesktopItemsLoader, "Desktop Items Loader");
- mDesktopLoader.start();
+ mDesktopLoaderThread = new Thread(mDesktopItemsLoader, "Desktop Items Loader");
+ mDesktopLoaderThread.start();
}
private static void updateShortcutLabels(ContentResolver resolver, PackageManager manager) {
@@ -272,7 +329,8 @@
try {
while (c.moveToNext()) {
try {
- if (c.getInt(itemTypeIndex) != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+ if (c.getInt(itemTypeIndex) !=
+ LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
continue;
}
@@ -357,9 +415,11 @@
}
mDesktopItems = new ArrayList<ItemInfo>();
+ mDesktopAppWidgets = new ArrayList<LauncherAppWidgetInfo>();
mFolders = new HashMap<Long, FolderInfo>();
final ArrayList<ItemInfo> desktopItems = mDesktopItems;
+ final ArrayList<LauncherAppWidgetInfo> desktopAppWidgets = mDesktopAppWidgets;
final Cursor c = contentResolver.query(
LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
@@ -374,15 +434,19 @@
final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
+ final int appWidgetIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.APPWIDGET_ID);
final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
+ final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
+ final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
ApplicationInfo info;
String intentDescription;
- Widget widgetInfo = null;
+ Widget widgetInfo;
+ LauncherAppWidgetInfo appWidgetInfo;
int container;
long id;
Intent intent;
@@ -494,41 +558,44 @@
break;
}
break;
- case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_CLOCK:
case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
- case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME:
- switch (itemType) {
- case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_CLOCK:
- widgetInfo = Widget.makeClock();
- break;
- case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH:
- widgetInfo = Widget.makeSearch();
- break;
- case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME:
- widgetInfo = Widget.makePhotoFrame();
- byte[] data = c.getBlob(iconIndex);
- if (data != null) {
- widgetInfo.photo =
- BitmapFactory.decodeByteArray(data, 0, data.length);
- }
- break;
+ widgetInfo = Widget.makeSearch();
+
+ container = c.getInt(containerIndex);
+ if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ Log.e(Launcher.LOG_TAG, "Widget found where container "
+ + "!= CONTAINER_DESKTOP ignoring!");
+ continue;
}
- if (widgetInfo != null) {
- container = c.getInt(containerIndex);
- if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
- Log.e(Launcher.LOG_TAG, "Widget found where container "
- + "!= CONTAINER_DESKTOP -- ignoring!");
- continue;
- }
- widgetInfo.id = c.getLong(idIndex);
- widgetInfo.screen = c.getInt(screenIndex);
- widgetInfo.container = container;
- widgetInfo.cellX = c.getInt(cellXIndex);
- widgetInfo.cellY = c.getInt(cellYIndex);
+ widgetInfo.id = c.getLong(idIndex);
+ widgetInfo.screen = c.getInt(screenIndex);
+ widgetInfo.container = container;
+ widgetInfo.cellX = c.getInt(cellXIndex);
+ widgetInfo.cellY = c.getInt(cellYIndex);
- desktopItems.add(widgetInfo);
+ desktopItems.add(widgetInfo);
+ break;
+ case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+ // Read all Launcher-specific widget details
+ int appWidgetId = c.getInt(appWidgetIdIndex);
+ appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
+ appWidgetInfo.id = c.getLong(idIndex);
+ appWidgetInfo.screen = c.getInt(screenIndex);
+ appWidgetInfo.cellX = c.getInt(cellXIndex);
+ appWidgetInfo.cellY = c.getInt(cellYIndex);
+ appWidgetInfo.spanX = c.getInt(spanXIndex);
+ appWidgetInfo.spanY = c.getInt(spanYIndex);
+
+ container = c.getInt(containerIndex);
+ if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ Log.e(Launcher.LOG_TAG, "Widget found where container "
+ + "!= CONTAINER_DESKTOP -- ignoring!");
+ continue;
}
+ appWidgetInfo.container = c.getInt(containerIndex);
+
+ desktopAppWidgets.add(appWidgetInfo);
break;
}
} catch (Exception e) {
@@ -578,7 +645,7 @@
break;
default:
liveFolderInfo.icon =
- launcher.getResources().getDrawable(R.drawable.ic_launcher_folder);
+ launcher.getResources().getDrawable(R.drawable.ic_launcher_folder);
}
}
@@ -586,7 +653,7 @@
* Finds the user folder defined by the specified id.
*
* @param id The id of the folder to look for.
- *
+ *
* @return A UserFolderInfo if the folder exists or null otherwise.
*/
FolderInfo findFolderById(long id) {
@@ -635,8 +702,10 @@
mApplicationsAdapter = null;
unbindAppDrawables(mApplications);
unbindDrawables(mDesktopItems);
+ unbindAppWidgetHostViews(mDesktopAppWidgets);
+ unbindCachedIconDrawables();
}
-
+
/**
* Remove the callback for the cached drawables or we leak the previous
* Home screen on orientation change.
@@ -650,11 +719,12 @@
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
((ApplicationInfo)item).icon.setCallback(null);
+ break;
}
}
}
}
-
+
/**
* Remove the callback for the cached drawables or we leak the previous
* Home screen on orientation change.
@@ -669,31 +739,54 @@
}
/**
- * @return The current list of applications
+ * Remove any {@link LauncherAppWidgetHostView} references in our widgets.
*/
- public ArrayList<ApplicationInfo> getApplications() {
- return mApplications;
+ private void unbindAppWidgetHostViews(ArrayList<LauncherAppWidgetInfo> appWidgets) {
+ if (appWidgets != null) {
+ final int count = appWidgets.size();
+ for (int i = 0; i < count; i++) {
+ LauncherAppWidgetInfo launcherInfo = appWidgets.get(i);
+ launcherInfo.hostView = null;
+ }
+ }
+ }
+
+ /**
+ * Remove the callback for the cached drawables or we leak the previous
+ * Home screen on orientation change.
+ */
+ private void unbindCachedIconDrawables() {
+ for (ApplicationInfo appInfo : mAppInfoCache.values()) {
+ appInfo.icon.setCallback(null);
+ }
}
/**
* @return The current list of applications
*/
- public ApplicationsAdapter getApplicationsAdapter() {
+ ApplicationsAdapter getApplicationsAdapter() {
return mApplicationsAdapter;
}
/**
* @return The current list of desktop items
*/
- public ArrayList<ItemInfo> getDesktopItems() {
+ ArrayList<ItemInfo> getDesktopItems() {
return mDesktopItems;
}
+
+ /**
+ * @return The current list of desktop items
+ */
+ ArrayList<LauncherAppWidgetInfo> getDesktopAppWidgets() {
+ return mDesktopAppWidgets;
+ }
/**
* Add an item to the desktop
* @param info
*/
- public void addDesktopItem(ItemInfo info) {
+ void addDesktopItem(ItemInfo info) {
// TODO: write to DB; also check that folder has been added to folders list
mDesktopItems.add(info);
}
@@ -702,12 +795,26 @@
* Remove an item from the desktop
* @param info
*/
- public void removeDesktopItem(ItemInfo info) {
+ void removeDesktopItem(ItemInfo info) {
// TODO: write to DB; figure out if we should remove folder from folders list
mDesktopItems.remove(info);
}
/**
+ * Add a widget to the desktop
+ */
+ void addDesktopAppWidget(LauncherAppWidgetInfo info) {
+ mDesktopAppWidgets.add(info);
+ }
+
+ /**
+ * Remove a widget from the desktop
+ */
+ void removeDesktopAppWidget(LauncherAppWidgetInfo info) {
+ mDesktopAppWidgets.remove(info);
+ }
+
+ /**
* Make an ApplicationInfo object for an application
*/
private static ApplicationInfo getApplicationInfo(PackageManager manager, Intent intent) {
@@ -886,37 +993,6 @@
return null;
}
- static Widget getPhotoFrameInfo(Context context, int screen, int cellX, int cellY) {
- final ContentResolver cr = context.getContentResolver();
- Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
- null, "screen=? and cellX=? and cellY=? and itemType=?",
- new String[] { String.valueOf(screen), String.valueOf(cellX), String.valueOf(cellY),
- String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME) }, null);
-
- try {
- if (c.moveToFirst()) {
- final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ID);
- final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
- final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
- final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
- final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
-
- Widget widgetInfo = Widget.makePhotoFrame();
- widgetInfo.id = c.getLong(idIndex);
- widgetInfo.screen = c.getInt(screenIndex);
- widgetInfo.container = c.getInt(containerIndex);
- widgetInfo.cellX = c.getInt(cellXIndex);
- widgetInfo.cellY = c.getInt(cellYIndex);
-
- return widgetInfo;
- }
- } finally {
- c.close();
- }
-
- return null;
- }
-
/**
* Add an item to the database in a specified container. Sets the container, screen, cellX and
* cellY fields of the item. Also assigns an ID to the item.
@@ -972,7 +1048,7 @@
final ContentResolver cr = context.getContentResolver();
cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
- cr.delete(LauncherSettings.Favorites.CONTENT_URI, LauncherSettings.Favorites.CONTAINER + "=" + info.id,
- null);
+ cr.delete(LauncherSettings.Favorites.CONTENT_URI,
+ LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
}
}
diff --git a/src/com/android/launcher/LauncherProvider.java b/src/com/android/launcher/LauncherProvider.java
index a3e529d..5cd7a0f 100644
--- a/src/com/android/launcher/LauncherProvider.java
+++ b/src/com/android/launcher/LauncherProvider.java
@@ -16,6 +16,7 @@
package com.android.launcher;
+import android.appwidget.AppWidgetHost;
import android.content.ContentProvider;
import android.content.Context;
import android.content.ContentValues;
@@ -29,6 +30,7 @@
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.database.Cursor;
+import android.database.SQLException;
import android.util.Log;
import android.util.Xml;
import android.net.Uri;
@@ -40,18 +42,25 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.util.ArrayList;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import com.android.internal.util.XmlUtils;
+import com.android.launcher.LauncherSettings.Favorites;
public class LauncherProvider extends ContentProvider {
- private static final String LOG_TAG = "LauncherSettingsProvider";
+ private static final String LOG_TAG = "LauncherProvider";
+ private static final boolean LOGD = true;
private static final String DATABASE_NAME = "launcher.db";
- private static final int DATABASE_VERSION = 1;
+
+ private static final int DATABASE_VERSION = 3;
static final String AUTHORITY = "com.android.launcher.settings";
+
+ static final String EXTRA_BIND_SOURCES = "com.android.launcher.settings.bindsources";
+ static final String EXTRA_BIND_TARGETS = "com.android.launcher.settings.bindtargets";
static final String TABLE_FAVORITES = "favorites";
static final String PARAMETER_NOTIFY = "notify";
@@ -168,14 +177,18 @@
private static final String ATTRIBUTE_Y = "y";
private final Context mContext;
+ private final AppWidgetHost mAppWidgetHost;
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
mContext = context;
+ mAppWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
}
@Override
public void onCreate(SQLiteDatabase db) {
+ if (LOGD) Log.d(LOG_TAG, "creating new launcher database");
+
db.execSQL("CREATE TABLE favorites (" +
"_id INTEGER PRIMARY KEY," +
"title TEXT," +
@@ -187,6 +200,7 @@
"spanX INTEGER," +
"spanY INTEGER," +
"itemType INTEGER," +
+ "appWidgetId INTEGER NOT NULL DEFAULT -1," +
"isShortcut INTEGER," +
"iconType INTEGER," +
"iconPackage TEXT," +
@@ -196,6 +210,11 @@
"displayMode INTEGER" +
");");
+ // Database was just created, so wipe any previous widgets
+ if (mAppWidgetHost != null) {
+ mAppWidgetHost.deleteHost();
+ }
+
if (!convertDatabase(db)) {
// Populate favorites table with initial favorites
loadFavorites(db, DEFAULT_FAVORITES_PATH);
@@ -203,10 +222,11 @@
}
private boolean convertDatabase(SQLiteDatabase db) {
+ if (LOGD) Log.d(LOG_TAG, "converting database from an older format, but not onUpgrade");
boolean converted = false;
final Uri uri = Uri.parse("content://" + Settings.AUTHORITY +
- "/favorites?notify=true");
+ "/old_favorites?notify=true");
final ContentResolver resolver = mContext.getContentResolver();
Cursor cursor = null;
@@ -228,6 +248,12 @@
resolver.delete(uri, null, null);
}
}
+
+ if (converted) {
+ // Convert widgets from this import into widgets
+ if (LOGD) Log.d(LOG_TAG, "converted and now triggering widget upgrade");
+ convertWidgets(db);
+ }
return converted;
}
@@ -261,6 +287,7 @@
values.put(LauncherSettings.Favorites.ICON_RESOURCE, c.getString(iconResourceIndex));
values.put(LauncherSettings.Favorites.CONTAINER, c.getInt(containerIndex));
values.put(LauncherSettings.Favorites.ITEM_TYPE, c.getInt(itemTypeIndex));
+ values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1);
values.put(LauncherSettings.Favorites.SCREEN, c.getInt(screenIndex));
values.put(LauncherSettings.Favorites.CELLX, c.getInt(cellXIndex));
values.put(LauncherSettings.Favorites.CELLY, c.getInt(cellYIndex));
@@ -290,14 +317,130 @@
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- Log.w(LOG_TAG, "Upgrading database from version " + oldVersion + " to " +
- newVersion + ", which will destroy all old data");
+ if (LOGD) Log.d(LOG_TAG, "onUpgrade triggered");
+
+ int version = oldVersion;
+ if (version < 3) {
+ // upgrade 1,2 -> 3 added appWidgetId column
+ db.beginTransaction();
+ try {
+ // Insert new column for holding appWidgetIds
+ db.execSQL("ALTER TABLE favorites " +
+ "ADD COLUMN appWidgetId INTEGER NOT NULL DEFAULT -1;");
+ db.setTransactionSuccessful();
+ version = 3;
+ } catch (SQLException ex) {
+ // Old version remains, which means we wipe old data
+ Log.e(LOG_TAG, ex.getMessage(), ex);
+ } finally {
+ db.endTransaction();
+ }
+
+ // Convert existing widgets only if table upgrade was successful
+ if (version == 3) {
+ convertWidgets(db);
+ }
+ }
+
+ if (version != DATABASE_VERSION) {
+ Log.w(LOG_TAG, "Destroying all old data.");
+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
+ onCreate(db);
+ }
+ }
+
+ /**
+ * Upgrade existing clock and photo frame widgets into their new widget
+ * equivalents. This method allocates appWidgetIds, and then hands off to
+ * LauncherAppWidgetBinder to finish the actual binding.
+ */
+ private void convertWidgets(SQLiteDatabase db) {
+ final int[] bindSources = new int[] {
+ Favorites.ITEM_TYPE_WIDGET_CLOCK,
+ Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME,
+ };
+
+ final ArrayList<ComponentName> bindTargets = new ArrayList<ComponentName>();
+ bindTargets.add(new ComponentName("com.android.alarmclock",
+ "com.android.alarmclock.AnalogAppWidgetProvider"));
+ bindTargets.add(new ComponentName("com.android.camera",
+ "com.android.camera.PhotoAppWidgetProvider"));
+
+ final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE, bindSources);
+
+ Cursor c = null;
+ boolean allocatedAppWidgets = false;
+
+ db.beginTransaction();
+ try {
+ // Select and iterate through each matching widget
+ c = db.query(TABLE_FAVORITES, new String[] { Favorites._ID },
+ selectWhere, null, null, null, null);
+
+ if (LOGD) Log.d(LOG_TAG, "found upgrade cursor count="+c.getCount());
+
+ final ContentValues values = new ContentValues();
+ while (c != null && c.moveToNext()) {
+ long favoriteId = c.getLong(0);
+
+ // Allocate and update database with new appWidgetId
+ try {
+ int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
+
+ if (LOGD) Log.d(LOG_TAG, "allocated appWidgetId="+appWidgetId+" for favoriteId="+favoriteId);
+
+ values.clear();
+ values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
+
+ // Original widgets might not have valid spans when upgrading
+ values.put(LauncherSettings.Favorites.SPANX, 2);
+ values.put(LauncherSettings.Favorites.SPANY, 2);
- db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
- onCreate(db);
+ String updateWhere = Favorites._ID + "=" + favoriteId;
+ db.update(TABLE_FAVORITES, values, updateWhere, null);
+
+ allocatedAppWidgets = true;
+ } catch (RuntimeException ex) {
+ Log.e(LOG_TAG, "Problem allocating appWidgetId", ex);
+ }
+ }
+
+ db.setTransactionSuccessful();
+ } catch (SQLException ex) {
+ Log.w(LOG_TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
+ } finally {
+ db.endTransaction();
+ if (c != null) {
+ c.close();
+ }
+ }
+
+ // If any appWidgetIds allocated, then launch over to binder
+ if (allocatedAppWidgets) {
+ launchAppWidgetBinder(bindSources, bindTargets);
+ }
}
-
+ /**
+ * Launch the widget binder that walks through the Launcher database,
+ * binding any matching widgets to the corresponding targets. We can't
+ * bind ourselves because our parent process can't obtain the
+ * BIND_APPWIDGET permission.
+ */
+ private void launchAppWidgetBinder(int[] bindSources, ArrayList<ComponentName> bindTargets) {
+ final Intent intent = new Intent();
+ intent.setComponent(new ComponentName("com.android.settings",
+ "com.android.settings.LauncherAppWidgetBinder"));
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ final Bundle extras = new Bundle();
+ extras.putIntArray(EXTRA_BIND_SOURCES, bindSources);
+ extras.putParcelableArrayList(EXTRA_BIND_TARGETS, bindTargets);
+ intent.putExtras(extras);
+
+ mContext.startActivity(intent);
+ }
+
/**
* Loads the default set of favorite packages from an xml file.
*
@@ -370,20 +513,7 @@
} catch (IOException e) {
Log.w(LOG_TAG, "Got exception parsing favorites.", e);
}
-
- // Add a clock
- values.clear();
- values.put(LauncherSettings.Favorites.CONTAINER,
- LauncherSettings.Favorites.CONTAINER_DESKTOP);
- values.put(LauncherSettings.Favorites.ITEM_TYPE,
- LauncherSettings.Favorites.ITEM_TYPE_WIDGET_CLOCK);
- values.put(LauncherSettings.Favorites.SCREEN, 1);
- values.put(LauncherSettings.Favorites.CELLX, 1);
- values.put(LauncherSettings.Favorites.CELLY, 0);
- values.put(LauncherSettings.Favorites.SPANX, 2);
- values.put(LauncherSettings.Favorites.SPANY, 2);
- db.insert(TABLE_FAVORITES, null, values);
-
+
// Add a search box
values.clear();
values.put(LauncherSettings.Favorites.CONTAINER,
@@ -396,11 +526,63 @@
values.put(LauncherSettings.Favorites.SPANX, 4);
values.put(LauncherSettings.Favorites.SPANY, 1);
db.insert(TABLE_FAVORITES, null, values);
+
+ final int[] bindSources = new int[] {
+ Favorites.ITEM_TYPE_WIDGET_CLOCK,
+ };
+
+ final ArrayList<ComponentName> bindTargets = new ArrayList<ComponentName>();
+ bindTargets.add(new ComponentName("com.android.alarmclock",
+ "com.android.alarmclock.AnalogAppWidgetProvider"));
+
+ boolean allocatedAppWidgets = false;
+
+ // Try binding to an analog clock widget
+ try {
+ int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
+
+ values.clear();
+ values.put(LauncherSettings.Favorites.CONTAINER,
+ LauncherSettings.Favorites.CONTAINER_DESKTOP);
+ values.put(LauncherSettings.Favorites.ITEM_TYPE,
+ LauncherSettings.Favorites.ITEM_TYPE_WIDGET_CLOCK);
+ values.put(LauncherSettings.Favorites.SCREEN, 1);
+ values.put(LauncherSettings.Favorites.CELLX, 1);
+ values.put(LauncherSettings.Favorites.CELLY, 0);
+ values.put(LauncherSettings.Favorites.SPANX, 2);
+ values.put(LauncherSettings.Favorites.SPANY, 2);
+ values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
+ db.insert(TABLE_FAVORITES, null, values);
+
+ allocatedAppWidgets = true;
+ } catch (RuntimeException ex) {
+ Log.e(LOG_TAG, "Problem allocating appWidgetId", ex);
+ }
+ // If any appWidgetIds allocated, then launch over to binder
+ if (allocatedAppWidgets) {
+ launchAppWidgetBinder(bindSources, bindTargets);
+ }
+
return i;
}
}
+ /**
+ * Build a query string that will match any row where the column matches
+ * anything in the values list.
+ */
+ static String buildOrWhereString(String column, int[] values) {
+ StringBuilder selectWhere = new StringBuilder();
+ for (int i = values.length - 1; i >= 0; i--) {
+ selectWhere.append(column).append("=").append(values[i]);
+ if (i > 0) {
+ selectWhere.append(" OR ");
+ }
+ }
+ return selectWhere.toString();
+ }
+
static class SqlArguments {
public final String table;
public final String where;
diff --git a/src/com/android/launcher/LauncherSettings.java b/src/com/android/launcher/LauncherSettings.java
index c5dfd1f..60ea0df 100644
--- a/src/com/android/launcher/LauncherSettings.java
+++ b/src/com/android/launcher/LauncherSettings.java
@@ -24,7 +24,8 @@
*/
class LauncherSettings {
/**
- * Favorites.
+ * Favorites. When changing these values, be sure to update
+ * {@link com.android.settings.LauncherAppWidgetBinder} as needed.
*/
static final class Favorites implements BaseColumns {
/**
@@ -147,6 +148,11 @@
static final int ITEM_TYPE_LIVE_FOLDER = 3;
/**
+ * The favorite is a widget
+ */
+ static final int ITEM_TYPE_APPWIDGET = 4;
+
+ /**
* The favorite is a clock
*/
static final int ITEM_TYPE_WIDGET_CLOCK = 1000;
@@ -162,6 +168,13 @@
static final int ITEM_TYPE_WIDGET_PHOTO_FRAME = 1002;
/**
+ * The appWidgetId of the widget
+ *
+ * <P>Type: INTEGER</P>
+ */
+ static final String APPWIDGET_ID = "appWidgetId";
+
+ /**
* Indicates whether this favorite is an application-created shortcut or not.
* If the value is 0, the favorite is not an application-created shortcut, if the
* value is 1, it is an application-created shortcut.
diff --git a/src/com/android/launcher/LiveFolder.java b/src/com/android/launcher/LiveFolder.java
index 37b98e0..5d727f8 100644
--- a/src/com/android/launcher/LiveFolder.java
+++ b/src/com/android/launcher/LiveFolder.java
@@ -24,8 +24,14 @@
import android.widget.AdapterView;
import android.net.Uri;
import android.provider.LiveFolders;
+import android.os.AsyncTask;
+import android.database.Cursor;
+
+import java.lang.ref.WeakReference;
public class LiveFolder extends Folder {
+ private AsyncTask<LiveFolderInfo,Void,Cursor> mLoadingTask;
+
public LiveFolder(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -66,7 +72,10 @@
void bind(FolderInfo info) {
super.bind(info);
- setContentAdapter(new LiveFolderAdapter(mLauncher, (LiveFolderInfo) info));
+ if (mLoadingTask != null && mLoadingTask.getStatus() == AsyncTask.Status.RUNNING) {
+ mLoadingTask.cancel(true);
+ }
+ mLoadingTask = new FolderLoadingTask(this).execute((LiveFolderInfo) info);
}
@Override
@@ -78,6 +87,42 @@
@Override
void onClose() {
super.onClose();
+ if (mLoadingTask != null && mLoadingTask.getStatus() == AsyncTask.Status.RUNNING) {
+ mLoadingTask.cancel(true);
+ }
((LiveFolderAdapter) mContent.getAdapter()).cleanup();
}
+
+ static class FolderLoadingTask extends AsyncTask<LiveFolderInfo, Void, Cursor> {
+ private final WeakReference<LiveFolder> mFolder;
+ private LiveFolderInfo mInfo;
+
+ FolderLoadingTask(LiveFolder folder) {
+ mFolder = new WeakReference<LiveFolder>(folder);
+ }
+
+ protected Cursor doInBackground(LiveFolderInfo... params) {
+ final LiveFolder folder = mFolder.get();
+ if (folder != null) {
+ mInfo = params[0];
+ return LiveFolderAdapter.query(folder.mLauncher, mInfo);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Cursor cursor) {
+ if (!isCancelled()) {
+ if (cursor != null) {
+ final LiveFolder folder = mFolder.get();
+ if (folder != null) {
+ final Launcher launcher = folder.mLauncher;
+ folder.setContentAdapter(new LiveFolderAdapter(launcher, mInfo, cursor));
+ }
+ }
+ } else if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
}
diff --git a/src/com/android/launcher/LiveFolderAdapter.java b/src/com/android/launcher/LiveFolderAdapter.java
index 01db5a6..4a5c077 100644
--- a/src/com/android/launcher/LiveFolderAdapter.java
+++ b/src/com/android/launcher/LiveFolderAdapter.java
@@ -45,8 +45,8 @@
new HashMap<Long, SoftReference<Drawable>>();
private final Launcher mLauncher;
- LiveFolderAdapter(Launcher launcher, LiveFolderInfo info) {
- super(launcher, query(launcher, info), true);
+ LiveFolderAdapter(Launcher launcher, LiveFolderInfo info, Cursor cursor) {
+ super(launcher, cursor, true);
mIsList = info.displayMode == LiveFolders.DISPLAY_MODE_LIST;
mInflater = LayoutInflater.from(launcher);
mLauncher = launcher;
@@ -54,8 +54,9 @@
mLauncher.startManagingCursor(getCursor());
}
- private static Cursor query(Context context, LiveFolderInfo info) {
- return context.getContentResolver().query(info.uri, null, null, null, LiveFolders.NAME + " ASC");
+ static Cursor query(Context context, LiveFolderInfo info) {
+ return context.getContentResolver().query(info.uri, null, null,
+ null, LiveFolders.NAME + " ASC");
}
public View newView(Context context, Cursor cursor, ViewGroup parent) {
@@ -178,10 +179,13 @@
}
mCustomIcons.clear();
- try {
- getCursor().close();
- } finally {
- mLauncher.stopManagingCursor(getCursor());
+ final Cursor cursor = getCursor();
+ if (cursor != null) {
+ try {
+ cursor.close();
+ } finally {
+ mLauncher.stopManagingCursor(cursor);
+ }
}
}
diff --git a/src/com/android/launcher/PhotoFrame.java b/src/com/android/launcher/PhotoFrame.java
deleted file mode 100644
index 1151322..0000000
--- a/src/com/android/launcher/PhotoFrame.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.ImageView;
-
-
-/**
- * Desktop widget that holds a user folder
- *
- */
-public class PhotoFrame extends ImageView implements OnClickListener {
-
- public PhotoFrame(Context context, AttributeSet attrs) {
- super(context, attrs);
- setClickable(true);
- setOnClickListener(this);
- setWillNotCacheDrawing(true);
- }
-
- public void onClick(View v) {
- ((Launcher) mContext).updatePhotoFrame((Widget) getTag());
- }
-}
diff --git a/src/com/android/launcher/Search.java b/src/com/android/launcher/Search.java
index 41d6562..97dcd98 100644
--- a/src/com/android/launcher/Search.java
+++ b/src/com/android/launcher/Search.java
@@ -16,8 +16,6 @@
package com.android.launcher;
-import java.util.List;
-
import android.app.ISearchManager;
import android.app.SearchManager;
import android.content.ActivityNotFoundException;
@@ -62,10 +60,11 @@
public class Search extends LinearLayout implements OnClickListener, OnKeyListener,
OnLongClickListener, TextWatcher, OnItemClickListener, OnItemSelectedListener {
- private final String TAG = "SearchGadget";
+ private final String TAG = "SearchWidget";
private AutoCompleteTextView mSearchText;
private ImageButton mGoButton;
+ private ImageButton mVoiceButton;
private OnLongClickListener mLongClickListener;
// Support for suggestions
@@ -76,7 +75,11 @@
private String mSuggestionQuery = null;
private int mItemSelected = -1;
+ // For voice searching
+ private Intent mVoiceSearchIntent;
+
private Rect mTempRect = new Rect();
+ private boolean mRestoreFocus = false;
/**
* Used to inflate the Workspace from XML.
@@ -86,6 +89,10 @@
*/
public Search(Context context, AttributeSet attrs) {
super(context, attrs);
+
+ mVoiceSearchIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
+ mVoiceSearchIntent.putExtra(android.speech.RecognizerIntent.EXTRA_LANGUAGE_MODEL,
+ android.speech.RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
}
/**
@@ -94,6 +101,14 @@
public void onClick(View v) {
if (v == mGoButton) {
query();
+ } else if (v == mVoiceButton) {
+ try {
+ getContext().startActivity(mVoiceSearchIntent);
+ } catch (ActivityNotFoundException ex) {
+ // Should not happen, since we check the availability of
+ // voice search before showing the button. But just in case...
+ Log.w(TAG, "Could not find voice search activity");
+ }
}
}
@@ -154,7 +169,26 @@
getContext().startActivity(launcher);
}
-
+
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ if (!hasWindowFocus && hasFocus()) {
+ mRestoreFocus = true;
+ }
+
+ super.onWindowFocusChanged(hasWindowFocus);
+
+ if (hasWindowFocus && mRestoreFocus) {
+ if (isInTouchMode()) {
+ final AutoCompleteTextView searchText = mSearchText;
+ searchText.setSelectAllOnFocus(false);
+ searchText.requestFocusFromTouch();
+ searchText.setSelectAllOnFocus(true);
+ }
+ mRestoreFocus = false;
+ }
+ }
+
/**
* Implements TextWatcher (for EditText)
*/
@@ -206,7 +240,7 @@
return true;
}
}
- } else if (v == mGoButton) {
+ } else if (v == mGoButton || v == mVoiceButton) {
boolean handled = false;
if (!event.isSystem() &&
(keyCode != KeyEvent.KEYCODE_DPAD_UP) &&
@@ -243,7 +277,14 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
- requestFocusFromTouch();
+ // Request focus unless the user tapped on the voice search button
+ final int x = (int) ev.getX();
+ final int y = (int) ev.getY();
+ final Rect frame = mTempRect;
+ mVoiceButton.getHitRect(frame);
+ if (!frame.contains(x, y)) {
+ requestFocusFromTouch();
+ }
return super.onInterceptTouchEvent(ev);
}
@@ -268,26 +309,29 @@
mSearchText.addTextChangedListener(this);
mGoButton = (ImageButton) findViewById(R.id.search_go_btn);
+ mVoiceButton = (ImageButton) findViewById(R.id.search_voice_btn);
mGoButton.setOnClickListener(this);
+ mVoiceButton.setOnClickListener(this);
mGoButton.setOnKeyListener(this);
+ mVoiceButton.setOnKeyListener(this);
mSearchText.setOnLongClickListener(this);
mGoButton.setOnLongClickListener(this);
+ mVoiceButton.setOnLongClickListener(this);
// disable the button since we start out w/empty input
mGoButton.setEnabled(false);
mGoButton.setFocusable(false);
+ configureSearchableInfo();
configureSuggestions();
+ configureVoiceSearchButton();
}
- /** The rest of the class deals with providing search suggestions */
-
/**
- * Set up the suggestions provider mechanism
+ * Read the searchable info from the search manager
*/
- private void configureSuggestions() {
- // get SearchableInfo
+ private void configureSearchableInfo() {
ISearchManager sms;
SearchableInfo searchable;
sms = ISearchManager.Stub.asInterface(ServiceManager.getService(Context.SEARCH_SERVICE));
@@ -303,6 +347,36 @@
return;
}
mSearchable = searchable;
+ }
+
+ /**
+ * If appropriate & available, configure voice search
+ *
+ * Note: Because the home screen search widget is always web search, we only check for
+ * getVoiceSearchLaunchWebSearch() modes. We don't support the alternate form of app-specific
+ * voice search.
+ */
+ private void configureVoiceSearchButton() {
+ boolean voiceSearchVisible = false;
+ if (mSearchable.getVoiceSearchEnabled() && mSearchable.getVoiceSearchLaunchWebSearch()) {
+ // Enable the voice search button if there is an activity that can handle it
+ PackageManager pm = getContext().getPackageManager();
+ ResolveInfo ri = pm.resolveActivity(mVoiceSearchIntent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ voiceSearchVisible = ri != null;
+ }
+
+ // finally, set visible state of voice search button, as appropriate
+ mVoiceButton.setVisibility(voiceSearchVisible ? View.VISIBLE : View.GONE);
+ }
+
+ /** The rest of the class deals with providing search suggestions */
+
+ /**
+ * Set up the suggestions provider mechanism
+ */
+ private void configureSuggestions() {
+ // get SearchableInfo
mSearchText.setOnItemClickListener(this);
mSearchText.setOnItemSelectedListener(this);
@@ -315,6 +389,18 @@
}
/**
+ * Remove internal cursor references when detaching from window which
+ * prevents {@link Context} leaks.
+ */
+ @Override
+ public void onDetachedFromWindow() {
+ if (mSuggestionsAdapter != null) {
+ mSuggestionsAdapter.changeCursor(null);
+ mSuggestionsAdapter = null;
+ }
+ }
+
+ /**
* Implements OnItemClickListener
*/
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
@@ -443,7 +529,11 @@
" returned exception" + e.toString());
}
}
-
+
+ SearchAutoCompleteTextView getSearchInputField() {
+ return (SearchAutoCompleteTextView) mSearchText;
+ }
+
/**
* This class provides the filtering-based interface to suggestions providers.
* It is hardwired in a couple of places to support GoogleSearch - for example, it supports
diff --git a/src/com/android/launcher/SearchAutoCompleteTextView.java b/src/com/android/launcher/SearchAutoCompleteTextView.java
new file mode 100644
index 0000000..e25a8f1
--- /dev/null
+++ b/src/com/android/launcher/SearchAutoCompleteTextView.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher;
+
+import android.widget.AutoCompleteTextView;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.graphics.Rect;
+import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
+import android.app.Activity;
+
+/**
+ * This class is not for the faint of heart. Home works in the pan & scan
+ * soft input mode. However, this mode gets rid of the soft keyboard on rotation,
+ * which is a probelm when the Search widget has focus. This special class
+ * changes Home's soft input method temporarily as long as the Search widget holds
+ * the focus. This way, the soft keyboard remains after rotation.
+ */
+public class SearchAutoCompleteTextView extends AutoCompleteTextView {
+ private boolean mShowKeyboard;
+
+ private Handler mLoseFocusHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ if (msg.what == 1 && !hasFocus()) {
+ // Hide the soft keyboard when the search widget loses the focus
+ InputMethodManager.peekInstance().hideSoftInputFromWindow(getWindowToken(), 0);
+ }
+ }
+ };
+
+ public SearchAutoCompleteTextView(Context context) {
+ super(context);
+ }
+
+ public SearchAutoCompleteTextView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public SearchAutoCompleteTextView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
+ super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+
+ final WindowManager.LayoutParams lp = ((Activity) getContext()).getWindow().getAttributes();
+ if (gainFocus) {
+ lp.softInputMode =
+ (lp.softInputMode & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) |
+ WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
+ } else {
+ //noinspection PointlessBitwiseExpression
+ lp.softInputMode =
+ (lp.softInputMode & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) |
+ WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED;
+ // If we don't immediately gain focus, we want to hide the IME.
+ mLoseFocusHandler.sendEmptyMessage(1);
+ }
+
+ if (getWindowToken() != null) {
+ final WindowManager manager = (WindowManager)
+ getContext().getSystemService(Context.WINDOW_SERVICE);
+ manager.updateViewLayout(getRootView(), lp);
+
+ if (mShowKeyboard) {
+ if (getContext().getResources().getConfiguration().hardKeyboardHidden ==
+ Configuration.HARDKEYBOARDHIDDEN_YES) {
+ InputMethodManager inputManager = (InputMethodManager)
+ getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+ inputManager.showSoftInput(this, 0);
+ }
+ mShowKeyboard = false;
+ }
+ }
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ super.onWindowFocusChanged(hasWindowFocus);
+ // See Workspace#focusOnSearch()
+ setFocusableInTouchMode(false);
+ }
+
+ void showKeyboardOnNextFocus() {
+ mShowKeyboard = true;
+ }
+}
diff --git a/src/com/android/launcher/WallpaperChooser.java b/src/com/android/launcher/WallpaperChooser.java
index afbe6f3..9ac922c 100644
--- a/src/com/android/launcher/WallpaperChooser.java
+++ b/src/com/android/launcher/WallpaperChooser.java
@@ -50,7 +50,6 @@
R.drawable.wallpaper_path_small,
R.drawable.wallpaper_sunrise_small,
R.drawable.wallpaper_mountain_small,
- R.drawable.wallpaper_ripples_small,
R.drawable.wallpaper_road_small,
R.drawable.wallpaper_jellyfish_small,
R.drawable.wallpaper_zanzibar_small,
@@ -61,14 +60,13 @@
};
private static final Integer[] IMAGE_IDS = {
- R.drawable.wallpaper_lake,
+ com.android.internal.R.drawable.default_wallpaper,
R.drawable.wallpaper_sunset,
R.drawable.wallpaper_beach,
R.drawable.wallpaper_snow_leopard,
R.drawable.wallpaper_path,
R.drawable.wallpaper_sunrise,
R.drawable.wallpaper_mountain,
- R.drawable.wallpaper_ripples,
R.drawable.wallpaper_road,
R.drawable.wallpaper_jellyfish,
R.drawable.wallpaper_zanzibar,
@@ -123,9 +121,6 @@
final String[] extras = resources.getStringArray(R.array.extra_wallpapers);
final String packageName = getApplication().getPackageName();
- final ArrayList<Integer> images = mImages;
- final ArrayList<Integer> thumbs = mThumbs;
-
for (String extra : extras) {
int res = resources.getIdentifier(extra, "drawable", packageName);
if (res != 0) {
@@ -133,8 +128,8 @@
"drawable", packageName);
if (thumbRes != 0) {
- images.add(res);
- thumbs.add(res);
+ mThumbs.add(thumbRes);
+ mImages.add(res);
}
}
}
@@ -148,7 +143,7 @@
public void onItemSelected(AdapterView parent, View v, int position, long id) {
final ImageView view = mImageView;
- Bitmap b = BitmapFactory.decodeResource(getResources(), IMAGE_IDS[position], mOptions);
+ Bitmap b = BitmapFactory.decodeResource(getResources(), mImages.get(position), mOptions);
view.setImageBitmap(b);
// Help the GC
diff --git a/src/com/android/launcher/Widget.java b/src/com/android/launcher/Widget.java
index 8812522..4f246cc 100644
--- a/src/com/android/launcher/Widget.java
+++ b/src/com/android/launcher/Widget.java
@@ -20,32 +20,11 @@
import android.graphics.Bitmap;
/**
- * Represents one instance of a Launcher widget (clock, search, photo frame).
- *
+ * Represents one instance of a Launcher widget, such as search.
*/
class Widget extends ItemInfo {
-
int layoutResource;
- Bitmap photo;
- static Widget makeClock() {
- Widget w = new Widget();
- w.itemType = LauncherSettings.Favorites.ITEM_TYPE_WIDGET_CLOCK;
- w.spanX = 2;
- w.spanY = 2;
- w.layoutResource = R.layout.widget_clock;
- return w;
- }
-
- static Widget makePhotoFrame() {
- Widget w = new Widget();
- w.itemType = LauncherSettings.Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME;
- w.spanX = 2;
- w.spanY = 2;
- w.layoutResource = R.layout.widget_photo_frame;
- return w;
- }
-
static Widget makeSearch() {
Widget w = new Widget();
w.itemType = LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH;
@@ -54,11 +33,4 @@
w.layoutResource = R.layout.widget_search;
return w;
}
-
- @Override
- void onAddToDatabase(ContentValues values) {
- super.onAddToDatabase(values);
- writeBitmap(values, photo);
- }
-
}
diff --git a/src/com/android/launcher/Workspace.java b/src/com/android/launcher/Workspace.java
index 0ca1b5a..bc5347e 100644
--- a/src/com/android/launcher/Workspace.java
+++ b/src/com/android/launcher/Workspace.java
@@ -17,12 +17,15 @@
package com.android.launcher;
import android.content.Context;
+import android.content.Intent;
+import android.content.ComponentName;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Rect;
+import android.graphics.Region;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
@@ -89,6 +92,13 @@
private boolean mAllowLongPress;
private boolean mLocked;
+ private int mTouchSlop;
+
+ final Rect mDrawerBounds = new Rect();
+ final Rect mClipBounds = new Rect();
+ int mDrawerContentHeight;
+ int mDrawerContentWidth;
+
/**
* Used to inflate the Workspace from XML.
*
@@ -126,6 +136,8 @@
mPaint = new Paint();
mPaint.setDither(false);
+
+ mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
/**
@@ -433,6 +445,29 @@
@Override
protected void dispatchDraw(Canvas canvas) {
+ boolean restore = false;
+
+ // If the all apps drawer is open and the drawing region for the workspace
+ // is contained within the drawer's bounds, we skip the drawing. This requires
+ // the drawer to be fully opaque.
+ if (mLauncher.isDrawerUp()) {
+ final Rect clipBounds = mClipBounds;
+ canvas.getClipBounds(clipBounds);
+ clipBounds.offset(-mScrollX, -mScrollY);
+ if (mDrawerBounds.contains(clipBounds)) {
+ return;
+ }
+ } else if (mLauncher.isDrawerMoving()) {
+ restore = true;
+ canvas.save(Canvas.CLIP_SAVE_FLAG);
+
+ final View view = mLauncher.getDrawerHandle();
+ final int top = view.getTop() + view.getHeight();
+
+ canvas.clipRect(mScrollX, top, mScrollX + mDrawerContentWidth,
+ top + mDrawerContentHeight, Region.Op.DIFFERENCE);
+ }
+
float x = mScrollX * mWallpaperOffset;
if (x + mWallpaperWidth < mRight - mLeft) {
x = mRight - mLeft - mWallpaperWidth;
@@ -464,6 +499,10 @@
}
}
}
+
+ if (restore) {
+ canvas.restore();
+ }
}
@Override
@@ -626,8 +665,8 @@
*/
final int xDiff = (int) Math.abs(x - mLastMotionX);
final int yDiff = (int) Math.abs(y - mLastMotionY);
- final int touchSlop = ViewConfiguration.getTouchSlop();
-
+
+ final int touchSlop = mTouchSlop;
boolean xMoved = xDiff > touchSlop;
boolean yMoved = yDiff > touchSlop;
@@ -669,6 +708,7 @@
// Release the drag
clearChildrenCache();
mTouchState = TOUCH_STATE_REST;
+ mAllowLongPress = false;
break;
}
@@ -835,11 +875,16 @@
}
void addApplicationShortcut(ApplicationInfo info, CellLayout.CellInfo cellInfo) {
+ addApplicationShortcut(info, cellInfo, false);
+ }
+
+ void addApplicationShortcut(ApplicationInfo info, CellLayout.CellInfo cellInfo,
+ boolean insertAtFirst) {
final CellLayout layout = (CellLayout) getChildAt(cellInfo.screen);
final int[] result = new int[2];
layout.cellToPoint(cellInfo.cellX, cellInfo.cellY, result);
- onDropExternal(result[0], result[1], info, layout);
+ onDropExternal(result[0], result[1], info, layout, insertAtFirst);
}
public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo) {
@@ -878,6 +923,11 @@
}
private void onDropExternal(int x, int y, Object dragInfo, CellLayout cellLayout) {
+ onDropExternal(x, y, dragInfo, cellLayout, false);
+ }
+
+ private void onDropExternal(int x, int y, Object dragInfo, CellLayout cellLayout,
+ boolean insertAtFirst) {
// Drag from somewhere else
ItemInfo info = (ItemInfo) dragInfo;
@@ -901,7 +951,7 @@
throw new IllegalStateException("Unknown item type: " + info.itemType);
}
- cellLayout.addView(view);
+ cellLayout.addView(view, insertAtFirst ? 0 : -1);
view.setOnLongClickListener(mLongClickListener);
cellLayout.onDropChild(view, x, y);
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
@@ -979,12 +1029,12 @@
/**
* Find a search widget on the given screen
*/
- private View findSearchWidget(CellLayout screen) {
+ private Search findSearchWidget(CellLayout screen) {
final int count = screen.getChildCount();
for (int i = 0; i < count; i++) {
View v = screen.getChildAt(i);
if (v instanceof Search) {
- return v;
+ return (Search) v;
}
}
return null;
@@ -995,9 +1045,30 @@
* if there is one. Also clears the current search selection so we don't
*/
private boolean focusOnSearch(int screen) {
- CellLayout currentScreen = (CellLayout)getChildAt(screen);
- Search searchWidget = (Search)findSearchWidget(currentScreen);
+ CellLayout currentScreen = (CellLayout) getChildAt(screen);
+ final Search searchWidget = findSearchWidget(currentScreen);
if (searchWidget != null) {
+ // This is necessary when focus on search is requested from the menu
+ // If the workspace was not in touch mode before the menu is invoked
+ // and the user clicks "Search" by touching the menu item, the following
+ // happens:
+ //
+ // - We request focus from touch on the search widget
+ // - The search widget gains focus
+ // - The window focus comes back to Home's window
+ // - The touch mode change is propagated to Home's window
+ // - The search widget is not focusable in touch mode and ViewRoot
+ // clears its focus
+ //
+ // Forcing focusable in touch mode ensures the search widget will
+ // keep the focus no matter what happens.
+ //
+ // Note: the search input field disables focusable in touch mode
+ // after the window gets the focus back, see SearchAutoCompleteTextView
+ final SearchAutoCompleteTextView input = searchWidget.getSearchInputField();
+ input.setFocusableInTouchMode(true);
+ input.showKeyboardOnNextFocus();
+
if (isInTouchMode()) {
searchWidget.requestFocusFromTouch();
} else {
@@ -1124,6 +1195,14 @@
public boolean allowLongPress() {
return mAllowLongPress;
}
+
+ /**
+ * Set true to allow long-press events to be triggered, usually checked by
+ * {@link Launcher} to accept or block dpad-initiated long-presses.
+ */
+ public void setAllowLongPress(boolean allowLongPress) {
+ mAllowLongPress = allowLongPress;
+ }
void removeShortcutsForPackage(String packageName) {
final ArrayList<View> childrenToRemove = new ArrayList<View>();
@@ -1138,7 +1217,13 @@
Object tag = view.getTag();
if (tag instanceof ApplicationInfo) {
ApplicationInfo info = (ApplicationInfo) tag;
- if (packageName.equals(info.intent.getComponent().getPackageName())) {
+ // We need to check for ACTION_MAIN otherwise getComponent() might
+ // return null for some shortcuts (for instance, for shortcuts to
+ // web pages.)
+ final Intent intent = info.intent;
+ final ComponentName name = intent.getComponent();
+ if (Intent.ACTION_MAIN.equals(intent.getAction()) &&
+ name != null && packageName.equals(name.getPackageName())) {
model.removeDesktopItem(info);
LauncherModel.deleteItemFromDatabase(mLauncher, info);
childrenToRemove.add(view);
@@ -1155,6 +1240,10 @@
}
}
}
+
+ // TODO: remove widgets when appwidgetmanager tells us they're gone
+// void removeAppWidgetsForProvider() {
+// }
void moveToDefaultScreen() {
snapToScreen(mDefaultScreen);