diff --git a/res/drawable/bg_gadget_error.9.png b/res/drawable/bg_gadget_error.9.png new file mode 100644 index 0000000000..507742471f Binary files /dev/null and b/res/drawable/bg_gadget_error.9.png differ diff --git a/res/drawable/ic_launcher_alarmclock.png b/res/drawable/ic_launcher_alarmclock.png deleted file mode 100755 index 30ff2671a2..0000000000 Binary files a/res/drawable/ic_launcher_alarmclock.png and /dev/null differ diff --git a/res/drawable/ic_launcher_application.png b/res/drawable/ic_launcher_application.png new file mode 100644 index 0000000000..75024841d3 Binary files /dev/null and b/res/drawable/ic_launcher_application.png differ diff --git a/res/drawable/ic_launcher_empty.png b/res/drawable/ic_launcher_empty.png new file mode 100644 index 0000000000..59bb6c5060 Binary files /dev/null and b/res/drawable/ic_launcher_empty.png differ diff --git a/res/drawable/ic_launcher_gadget.png b/res/drawable/ic_launcher_gadget.png new file mode 100644 index 0000000000..cb40a1988b Binary files /dev/null and b/res/drawable/ic_launcher_gadget.png differ diff --git a/res/drawable/wallpaper.jpg b/res/drawable/wallpaper.jpg deleted file mode 100644 index d19b642825..0000000000 Binary files a/res/drawable/wallpaper.jpg and /dev/null differ diff --git a/res/drawable/wallpaper_beach.jpg b/res/drawable/wallpaper_beach.jpg index c059cc3db9..bdcece08c6 100644 Binary files a/res/drawable/wallpaper_beach.jpg and b/res/drawable/wallpaper_beach.jpg differ diff --git a/res/drawable/wallpaper_beach_small.jpg b/res/drawable/wallpaper_beach_small.jpg index 3508509357..46051cc4e7 100644 Binary files a/res/drawable/wallpaper_beach_small.jpg and b/res/drawable/wallpaper_beach_small.jpg differ diff --git a/res/drawable/wallpaper_blue.jpg b/res/drawable/wallpaper_blue.jpg index 032f598189..0edd6bda8b 100644 Binary files a/res/drawable/wallpaper_blue.jpg and b/res/drawable/wallpaper_blue.jpg differ diff --git a/res/drawable/wallpaper_blue_small.jpg b/res/drawable/wallpaper_blue_small.jpg index e987c69dc7..5cf585a0e1 100644 Binary files a/res/drawable/wallpaper_blue_small.jpg and b/res/drawable/wallpaper_blue_small.jpg differ diff --git a/res/drawable/wallpaper_green.jpg b/res/drawable/wallpaper_green.jpg index 89b308b148..36c9e08d75 100644 Binary files a/res/drawable/wallpaper_green.jpg and b/res/drawable/wallpaper_green.jpg differ diff --git a/res/drawable/wallpaper_green_small.jpg b/res/drawable/wallpaper_green_small.jpg index 16790d2a08..d7c28ec1e3 100644 Binary files a/res/drawable/wallpaper_green_small.jpg and b/res/drawable/wallpaper_green_small.jpg differ diff --git a/res/drawable/wallpaper_grey.jpg b/res/drawable/wallpaper_grey.jpg index 7898568cfd..ca48e6afc9 100644 Binary files a/res/drawable/wallpaper_grey.jpg and b/res/drawable/wallpaper_grey.jpg differ diff --git a/res/drawable/wallpaper_grey_small.jpg b/res/drawable/wallpaper_grey_small.jpg index 12ca3e22b7..1734d2e966 100644 Binary files a/res/drawable/wallpaper_grey_small.jpg and b/res/drawable/wallpaper_grey_small.jpg differ diff --git a/res/drawable/wallpaper_jellyfish.jpg b/res/drawable/wallpaper_jellyfish.jpg index 659228b1ed..f32739992d 100644 Binary files a/res/drawable/wallpaper_jellyfish.jpg and b/res/drawable/wallpaper_jellyfish.jpg differ diff --git a/res/drawable/wallpaper_jellyfish_small.jpg b/res/drawable/wallpaper_jellyfish_small.jpg index e140597c86..4e5d134643 100644 Binary files a/res/drawable/wallpaper_jellyfish_small.jpg and b/res/drawable/wallpaper_jellyfish_small.jpg differ diff --git a/res/drawable/wallpaper_lake.jpg b/res/drawable/wallpaper_lake.jpg index 5ba522f57b..874bb3b163 100644 Binary files a/res/drawable/wallpaper_lake.jpg and b/res/drawable/wallpaper_lake.jpg differ diff --git a/res/drawable/wallpaper_lake_small.jpg b/res/drawable/wallpaper_lake_small.jpg index 179b26a057..36d56ccc7b 100644 Binary files a/res/drawable/wallpaper_lake_small.jpg and b/res/drawable/wallpaper_lake_small.jpg differ diff --git a/res/drawable/wallpaper_mountain.jpg b/res/drawable/wallpaper_mountain.jpg index 137617793a..af4a3b1e43 100644 Binary files a/res/drawable/wallpaper_mountain.jpg and b/res/drawable/wallpaper_mountain.jpg differ diff --git a/res/drawable/wallpaper_mountain_small.jpg b/res/drawable/wallpaper_mountain_small.jpg index 2416474882..e2d5ac2651 100644 Binary files a/res/drawable/wallpaper_mountain_small.jpg and b/res/drawable/wallpaper_mountain_small.jpg differ diff --git a/res/drawable/wallpaper_path.jpg b/res/drawable/wallpaper_path.jpg index f4cb02ce0c..c8d610e505 100644 Binary files a/res/drawable/wallpaper_path.jpg and b/res/drawable/wallpaper_path.jpg differ diff --git a/res/drawable/wallpaper_path_small.jpg b/res/drawable/wallpaper_path_small.jpg index a1af4cdd86..f47d7a74f8 100644 Binary files a/res/drawable/wallpaper_path_small.jpg and b/res/drawable/wallpaper_path_small.jpg differ diff --git a/res/drawable/wallpaper_pink.jpg b/res/drawable/wallpaper_pink.jpg index 1b9f226e05..74403e5529 100644 Binary files a/res/drawable/wallpaper_pink.jpg and b/res/drawable/wallpaper_pink.jpg differ diff --git a/res/drawable/wallpaper_pink_small.jpg b/res/drawable/wallpaper_pink_small.jpg index 7b7ad587e6..42b17709fb 100644 Binary files a/res/drawable/wallpaper_pink_small.jpg and b/res/drawable/wallpaper_pink_small.jpg differ diff --git a/res/drawable/wallpaper_ripples.jpg b/res/drawable/wallpaper_ripples.jpg index 8624c860f3..4d740a41d3 100644 Binary files a/res/drawable/wallpaper_ripples.jpg and b/res/drawable/wallpaper_ripples.jpg differ diff --git a/res/drawable/wallpaper_ripples_small.jpg b/res/drawable/wallpaper_ripples_small.jpg index ce07d38af2..c690d9c6a8 100644 Binary files a/res/drawable/wallpaper_ripples_small.jpg and b/res/drawable/wallpaper_ripples_small.jpg differ diff --git a/res/drawable/wallpaper_road.jpg b/res/drawable/wallpaper_road.jpg index 88611a4ec7..3aaa639545 100644 Binary files a/res/drawable/wallpaper_road.jpg and b/res/drawable/wallpaper_road.jpg differ diff --git a/res/drawable/wallpaper_road_small.jpg b/res/drawable/wallpaper_road_small.jpg index 0b0c390dba..6bf401aaf0 100644 Binary files a/res/drawable/wallpaper_road_small.jpg and b/res/drawable/wallpaper_road_small.jpg differ diff --git a/res/drawable/wallpaper_snow_leopard.jpg b/res/drawable/wallpaper_snow_leopard.jpg index 0d33157a59..ffb5d0991e 100644 Binary files a/res/drawable/wallpaper_snow_leopard.jpg and b/res/drawable/wallpaper_snow_leopard.jpg differ diff --git a/res/drawable/wallpaper_snow_leopard_small.jpg b/res/drawable/wallpaper_snow_leopard_small.jpg index ec976aad98..99db5c6931 100644 Binary files a/res/drawable/wallpaper_snow_leopard_small.jpg and b/res/drawable/wallpaper_snow_leopard_small.jpg differ diff --git a/res/drawable/wallpaper_sunrise.jpg b/res/drawable/wallpaper_sunrise.jpg index 4ee8d5dde3..e00f45ca78 100644 Binary files a/res/drawable/wallpaper_sunrise.jpg and b/res/drawable/wallpaper_sunrise.jpg differ diff --git a/res/drawable/wallpaper_sunrise_small.jpg b/res/drawable/wallpaper_sunrise_small.jpg index aadd980efe..248b7e78fe 100644 Binary files a/res/drawable/wallpaper_sunrise_small.jpg and b/res/drawable/wallpaper_sunrise_small.jpg differ diff --git a/res/drawable/wallpaper_sunset.jpg b/res/drawable/wallpaper_sunset.jpg index bcfd8d4bd4..bb2af6ed29 100644 Binary files a/res/drawable/wallpaper_sunset.jpg and b/res/drawable/wallpaper_sunset.jpg differ diff --git a/res/drawable/wallpaper_sunset_small.jpg b/res/drawable/wallpaper_sunset_small.jpg index eff8f0da2e..d5e5add39c 100644 Binary files a/res/drawable/wallpaper_sunset_small.jpg and b/res/drawable/wallpaper_sunset_small.jpg differ diff --git a/res/drawable/wallpaper_zanzibar.jpg b/res/drawable/wallpaper_zanzibar.jpg index fb133741eb..75d175de13 100644 Binary files a/res/drawable/wallpaper_zanzibar.jpg and b/res/drawable/wallpaper_zanzibar.jpg differ diff --git a/res/drawable/wallpaper_zanzibar_small.jpg b/res/drawable/wallpaper_zanzibar_small.jpg index 7307a73985..700a7fee1e 100644 Binary files a/res/drawable/wallpaper_zanzibar_small.jpg and b/res/drawable/wallpaper_zanzibar_small.jpg differ diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml index 11a81fd8ee..e92c8dc18f 100644 --- a/res/layout-land/launcher.xml +++ b/res/layout-land/launcher.xml @@ -16,7 +16,6 @@ - + android:bottomOffset="7dip" + android:handle="@+id/all_apps" + android:content="@+id/content"> - + - + android:topOffset="5dip" + android:bottomOffset="7dip" + android:handle="@+id/all_apps" + android:content="@+id/content"> - + + + + diff --git a/res/layout/create_shortcut_list.xml b/res/layout/create_shortcut_list.xml index 63ca2b8795..aa1673375b 100644 --- a/res/layout/create_shortcut_list.xml +++ b/res/layout/create_shortcut_list.xml @@ -17,11 +17,10 @@ ** limitations under the License. */ --> - diff --git a/res/layout/gadget_error.xml b/res/layout/gadget_error.xml new file mode 100644 index 0000000000..bde07afd06 --- /dev/null +++ b/res/layout/gadget_error.xml @@ -0,0 +1,29 @@ + + + + diff --git a/res/layout/widget_search.xml b/res/layout/widget_search.xml index 27e591db99..dcc83a5bb1 100644 --- a/res/layout/widget_search.xml +++ b/res/layout/widget_search.xml @@ -36,7 +36,7 @@ android:singleLine="true" android:selectAllOnFocus="true" android:completionThreshold="1" - android:inputType="textAutoComplete|textSearchString" + android:inputType="textAutoComplete|textSearch" /> + + diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index c5afda2ad8..d7d98e9ef1 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -28,14 +28,24 @@ "Přidat na plochu" "Aplikace" "Zástupce" + + + + "Složka Live" "Miniaplikace" + + "Tapeta" "Složka" "Hodiny" "Rámeček fotografie" "Vyhledávání" "Na této ploše již není místo." + + + + "Přidat" "Tapeta" "Hledat" @@ -50,4 +60,6 @@ "zápis nastavení a odkazů plochy" "Povoluje aplikaci změnit nastavení a odkazy plochy." "Vyhledávání Google" + + diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index 3f0c42a619..f8c4b61191 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -28,14 +28,24 @@ "Zur Startseite hinzufügen" "Anwendung" "Verknüpfung" + + + + "Live-Ordner" "Widget" + + "Hintergrund" "Ordner" "Uhr" "Bildrahmen" "Suchen" "Auf der Startseite ist kein Platz mehr vorhanden." + + + + "Hinzufügen" "Hintergrund" "Suchen" @@ -50,4 +60,6 @@ "Einstellungen und Shortcuts für Startseite schreiben" "Ermöglicht einer Anwendung, die Einstellungen und Shortcuts auf der Startseite zu ändern." "Google-Suche" + + diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 161616dcfb..96dbfa23db 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -28,14 +28,24 @@ "Añadir a pantalla de página principal" "Aplicación" "Acceso directo" + + + + "Carpeta activa" "Widget" + + "Papel tapiz" "Carpeta" "Reloj" "Picture frame" "Búsqueda de Google" "No queda espacio en esta pantalla de página principal." + + + + "Añadir" "Papel tapiz" "Búsqueda de Google" @@ -50,4 +60,6 @@ "escribir información de accesos directos y de configuración de la página principal" "Permite que una aplicación modifique la configuración y los accesos directos de la página principal." "Búsqueda de Google" + + diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index effdc7aff7..3d14a22a9f 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -28,14 +28,24 @@ "Ajouter à l\'écran d\'accueil" "Application" "Raccourci" + + + + "Dossier live" "Widget" + + "Arrière-plan" "Dossier" "Horloge" "Cadre d\'image" "Rechercher" "Plus d\'espace libre sur l\'écran Accueil." + + + + "Ajouter" "Arrière-plan" "Rechercher" @@ -50,4 +60,6 @@ "écrire les paramètres de la page d\'accueil et des raccourcis" "Permet à une application de modifier les paramètres et les raccourcis de la page d\'accueil." "Recherche Google" + + diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index d38fa30c35..8c79d13c39 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -28,14 +28,24 @@ "Aggiungi a schermata Home" "Applicazione" "Collegamento" + + + + "Cartella dinamica" "Widget" + + "Sfondo" "Cartella" "Orologio" "Cornice immagini" "Ricerca" "Spazio nella schermata Home esaurito." + + + + "Aggiungi" "Sfondo" "Cerca" @@ -50,4 +60,6 @@ "creare impostazioni e collegamenti in Home" "Consente a un\'applicazione di modificare le impostazioni e i collegamenti in Home." "Ricerca Google" + + diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index c3b1ff8c4b..0e7531408c 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -28,14 +28,24 @@ "ホーム画面に追加" "アプリケーション" "ショートカット" + + + + "ライブフォルダ" "ウィジェット" + + "壁紙" "フォルダ" "時計" "写真フレーム" "検索" "このホーム画面には空きスペースがありません。" + + + + "追加" "壁紙" "検索" @@ -50,4 +60,6 @@ "ホームの設定とショートカットの書き込み" "ホームの設定とショートカットの変更をアプリケーションに許可します。" "Google検索" + + diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml new file mode 100644 index 0000000000..29cb53a24a --- /dev/null +++ b/res/values-ko/strings.xml @@ -0,0 +1,65 @@ + + + + "홈" + "폴더" + "배경화면 선택" + "배경화면 설정" + "배경화면 갤러리" + "전화기에 설치되어 있지 않은 응용프로그램입니다." + "폴더 이름" + "폴더 이름 바꾸기" + "확인" + "취소" + "홈 화면에 추가" + "응용프로그램" + "바로가기" + + + + + "라이브 폴더" + "위젯" + + + "배경화면" + "폴더" + "시계" + "사진 프레임" + "검색" + "홈 화면에 더 이상 공간이 없습니다." + + + + + "추가" + "배경화면" + "검색" + "알림" + "설정" + "바로가기 설치" + "응용프로그램이 사용자의 작업 없이 바로가기를 추가할 수 있도록 합니다." + "바로가기 제거" + "응용프로그램이 사용자의 작업 없이 바로가기를 제거할 수 있도록 합니다." + "홈 설정 및 바로가기 읽기" + "응용프로그램이 홈에 있는 설정 및 바로가기를 읽을 수 있습니다." + "홈 설정 및 바로가기 쓰기" + "응용프로그램이 홈에 있는 설정 및 바로가기를 변경할 수 있습니다." + "Google 검색" + + + diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml new file mode 100644 index 0000000000..c485e6b179 --- /dev/null +++ b/res/values-nb/strings.xml @@ -0,0 +1,65 @@ + + + + "Hjem" + "Mappe" + "Velg bakgrunnsbilde fra" + "Velg bakgrunnsbilde" + "Bildegalleri" + "Applikasjonen er ikke installert." + "Mappenavn" + "Gi nytt navn til mappe" + "OK" + "Avbryt" + "Legg til skrivebord" + "Applikasjon" + "Snarvei" + + + + + "Aktiv mappe" + "Skrivebordselement" + + + "Bakgrunnsbilde" + "Mappe" + "Klokke" + "Bilderamme" + "Søk" + "Ikke nok plass på skrivebordet." + + + + + "Legg til" + "Bakgrunnsbilde" + "Søk" + "Varslinger" + "Innstillinger" + "shortcuts" + "Allows an application to add shortcuts without user intervention." + "uninstall shortcuts" + "Allows an application to remove shortcuts without user intervention." + "read Home settings and shortcuts" + "Allows an application to read the settings and shortcuts in Home." + "write Home settings and shortcuts" + "Allows an application to change the settings and shortcuts in Home." + "Google-søk" + + + diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index 9e55d8f44f..bcb13ee415 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -28,14 +28,24 @@ "Toevoegen aan startpagina" "Toepassing" "Snelkoppeling" + + + + "Live map" "Widget" + + "Achtergrond" "Map" "Klok" "Fotolijstje" "Zoeken" "Er is geen ruimte meer op dit startscherm." + + + + "Toevoegen" "Achtergrond" "Zoeken" @@ -50,4 +60,6 @@ "instellingen en snelkoppelingen voor de startpagina schrijven" "Hiermee kan een toepassing de instellingen en snelkoppelingen op de startpagina wijzigen." "Zoeken met Google" + + diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index 935695434e..f5d4ca9d02 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -15,7 +15,7 @@ --> - "Strona główna" + "Sieć" "Folder" "Wybierz tapetę" "Ustaw tapetę" @@ -28,14 +28,24 @@ "Dodaj do strony głównej" "Aplikacja" "Skrót" + + + + "Folder Live" "Widget" + + "Tapeta" "Folder" "Zegar" "Ramka obrazu" "Szukaj" "Brak miejsca na tej stronie głównej" + + + + "Dodaj" "Tapeta" "Szukaj" @@ -50,4 +60,6 @@ "zapisywanie ustawień i skrótów strony głównej" "Umożliwia aplikacji zmianę ustawień i skrótów strony głównej." "Szukaj w Google" + + diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 9bb4787c1f..d720e3b408 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -28,14 +28,24 @@ "Добавление на главный экран" "Приложение" "Ярлык" + + + + "Динамическая папка" "Виджет" + + "Фоновый рисунок" "Папка" "Часы" "Рамка для картинки" "Поиск" "На главном экране больше нет места." + + + + "Добавить" "Фоновый рисунок" "Искать" @@ -50,4 +60,6 @@ "записывать ярлыки и настройки главного экрана" "Позволяет приложению изменять настройки и ярлыки на главном экране." "Поиск Google" + + diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 72ffcf42a5..ec751399e5 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -28,14 +28,24 @@ "添加到“主页”屏幕" "应用程序" "快捷键" + + + + "活动的文件夹" "小工具" + + "壁纸" "文件夹" "时钟" "相框" "搜索" "该“主页”屏幕上没有多余空间。" + + + + "添加" "壁纸" "搜索" @@ -50,4 +60,6 @@ "写入“主页”设置和快捷键" "允许应用程序更改“主页”中的设置和快捷键。" "Google 搜索" + + diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index 35db6220c6..aa4f951fa8 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -28,14 +28,24 @@ "新增至首頁畫面" "應用程式" "捷徑" + + + + "使用中的資料夾" "Widget" + + "桌布" "資料夾" "時鐘" "相框" "搜尋" "首頁已無空間" + + + + "新增" "桌布" "搜尋" @@ -50,4 +60,6 @@ "寫入首頁設定和捷徑" "允許應用程式變更首頁中的設定和捷徑。" "Google 搜尋" + + diff --git a/res/values/colors.xml b/res/values/colors.xml index d4051f0dbd..4a2aba375a 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -22,4 +22,6 @@ #EB191919 #B2191919 #A5FF0000 + + #fccc diff --git a/res/values/strings.xml b/res/values/strings.xml index c1d3455ae7..ff417987f6 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -52,9 +52,16 @@ Application Shortcut + + Search + + Folder + Live folder Widget + + Gadget Wallpaper @@ -68,6 +75,11 @@ No more room on this Home screen. + + Select shortcut + + Select live folder + @@ -101,5 +113,8 @@ This translation SHOULD MATCH the string "search_hint" which is found in GoogleSearch/res/values/strings.xml --> Google Search - + + + Problem loading gadget + diff --git a/src/com/android/launcher/AddAdapter.java b/src/com/android/launcher/AddAdapter.java index 8eebe39d98..14107084ee 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 mItems = new ArrayList(); + + 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_GADGET = 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 mList; - - Group(String name) { - mName = name; - mList = new ArrayList(); - } - - 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 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_gadget, ITEM_SEARCH)); + + mItems.add(new ListItem(res, R.string.group_gadgets, + R.drawable.ic_launcher_gadget, ITEM_GADGET)); + + 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 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 findTargetsForIntent(Intent intent, PackageManager packageManager) { - List 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= 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 @@ public class CellLayout extends ViewGroup { 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 @@ public class CellLayout extends ViewGroup { cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE; cellInfo.maxVacantSpanY = Integer.MIN_VALUE; cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE; - cellInfo.vacantCells = new ArrayList(); + 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 @@ public class CellLayout extends ViewGroup { } 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 @@ public class CellLayout extends ViewGroup { cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE; cellInfo.maxVacantSpanY = Integer.MIN_VALUE; cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE; - cellInfo.vacantCells = new ArrayList(); 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++) { @@ -634,6 +658,26 @@ public class CellLayout extends ViewGroup { 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 + * @param cellInfo {@link CellInfo} to fill with calculated span parameters + */ + public void rectToCell(int width, int height, CellInfo cellInfo) { + // 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 + cellInfo.spanX = (width + smallerSize) / smallerSize; + cellInfo.spanY = (height + smallerSize) / smallerSize; + } /** * Find the first vacant cell, if there is one. @@ -811,12 +855,54 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { } 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 +918,27 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { int screen; boolean valid; - ArrayList vacantCells; + final ArrayList vacantCells = new ArrayList(VacantCell.POOL_LIMIT); int maxVacantSpanX; int maxVacantSpanXSpanY; int maxVacantSpanY; int maxVacantSpanYSpanX; + final Rect current = new Rect(); + + private void clearVacantCells() { + final ArrayList 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(); + clearVacantCells(); return; } @@ -855,26 +951,40 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { 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 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 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 +994,14 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { 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 798cf0de8c..6f67884db2 100644 --- a/src/com/android/launcher/DeleteZone.java +++ b/src/com/android/launcher/DeleteZone.java @@ -97,6 +97,12 @@ public class DeleteZone extends ImageView implements DropTarget, DragController. final UserFolderInfo userFolderInfo = (UserFolderInfo)item; LauncherModel.deleteUserFolderContentsFromDatabase(mLauncher, userFolderInfo); model.removeUserFolder(userFolderInfo); + } else if (item instanceof LauncherGadgetInfo) { + final LauncherGadgetInfo launcherGadgetInfo = (LauncherGadgetInfo)item; + final LauncherGadgetHost gadgetHost = mLauncher.getGadgetHost(); + if (gadgetHost != null) { + gadgetHost.deleteGadgetId(launcherGadgetInfo.gadgetId); + } } LauncherModel.deleteItemFromDatabase(mLauncher, item); } diff --git a/src/com/android/launcher/DragLayer.java b/src/com/android/launcher/DragLayer.java index 56140dd076..aa6615a22a 100644 --- a/src/com/android/launcher/DragLayer.java +++ b/src/com/android/launcher/DragLayer.java @@ -320,40 +320,33 @@ public class DragLayer extends FrameLayout implements DragController { 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 61745dda0e..8899f44aa8 100644 --- a/src/com/android/launcher/ItemInfo.java +++ b/src/com/android/launcher/ItemInfo.java @@ -38,10 +38,8 @@ class ItemInfo { /** * 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_GADGET}. */ int itemType; diff --git a/src/com/android/launcher/Launcher.java b/src/com/android/launcher/Launcher.java index 465400b8d8..293abe808a 100644 --- a/src/com/android/launcher/Launcher.java +++ b/src/com/android/launcher/Launcher.java @@ -24,16 +24,21 @@ import android.app.SearchManager; 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.gadget.GadgetInfo; +import android.gadget.GadgetManager; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; @@ -64,16 +69,15 @@ import android.view.ViewGroup; 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 java.lang.ref.WeakReference; import java.util.ArrayList; @@ -86,8 +90,6 @@ public final class Launcher extends Activity implements View.OnClickListener, On private static final boolean PROFILE_STARTUP = 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 +100,12 @@ public final class Launcher extends Activity implements View.OnClickListener, On 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_GADGET = 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_GADGET = 9; static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate"; @@ -148,10 +153,6 @@ public final class Launcher extends Activity implements View.OnClickListener, On 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 +165,12 @@ public final class Launcher extends Activity implements View.OnClickListener, On private DragLayer mDragLayer; private Workspace mWorkspace; - + + private GadgetManager mGadgetManager; + private LauncherGadgetHost mGadgetHost; + + private static final int GADGET_HOST_ID = 1024; + private CellLayout.CellInfo mAddItemCellInfo; private CellLayout.CellInfo mMenuAddInfo; private final int[] mCellCoordinates = new int[2]; @@ -189,6 +195,11 @@ public final class Launcher extends Activity implements View.OnClickListener, On protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mInflater = getLayoutInflater(); + + mGadgetManager = GadgetManager.getInstance(this); + mGadgetHost = new LauncherGadgetHost(this, GADGET_HOST_ID); + + // TODO: figure out if this is first launch and correctly clear GadgetHost database if (PROFILE_STARTUP) { android.os.Debug.startMethodTracing("/sdcard/launcher"); @@ -223,6 +234,18 @@ public final class Launcher extends Activity implements View.OnClickListener, On Selection.setSelection(mDefaultKeySsb, 0); } + @Override + protected void onStart() { + super.onStart(); + mGadgetHost.startListening(); + } + + @Override + protected void onStop() { + super.onStop(); + mGadgetHost.stopListening(); + } + private void checkForLocaleChange() { final SharedPreferences preferences = getSharedPreferences(PREFERENCES, MODE_PRIVATE); final Configuration configuration = getResources().getConfiguration(); @@ -283,20 +306,42 @@ public final class Launcher extends Activity implements View.OnClickListener, On @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); + 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_GADGET: + addGadget(data); + break; + case REQUEST_CREATE_GADGET: + completeAddGadget(data, mAddItemCellInfo, !mDesktopLocked); + break; + } + } else if (requestCode == REQUEST_PICK_GADGET && + resultCode == RESULT_CANCELED && data != null) { + // Clean up the gadgetId if we canceled + int gadgetId = data.getIntExtra(GadgetManager.EXTRA_GADGET_ID, -1); + if (gadgetId != -1) { + mGadgetHost.deleteGadgetId(gadgetId); } } mWaitingForResult = false; @@ -398,7 +443,6 @@ public final class Launcher extends Activity implements View.OnClickListener, On mRestoring = true; } - boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false); if (renameFolder) { long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID); @@ -439,10 +483,6 @@ public final class Launcher extends Activity implements View.OnClickListener, On grid.setTextFilterEnabled(true); grid.setDragger(dragLayer); grid.setLauncher(this); - if (sOpenGlEnabled) { - grid.setScrollingCacheEnabled(false); - grid.setFadingEdgeLength(0); - } workspace.setOnLongClickListener(this); workspace.setDragger(dragLayer); @@ -456,18 +496,6 @@ public final class Launcher extends Activity implements View.OnClickListener, On 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 +535,42 @@ public final class Launcher extends Activity implements View.OnClickListener, On 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) { + cellInfo.screen = mWorkspace.getCurrentScreen(); + // 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, mAddItemCellInfo); + } + } + /** * Add a shortcut to the workspace. * @@ -535,6 +594,74 @@ public final class Launcher extends Activity implements View.OnClickListener, On } } + + /** + * Add a gadget to the workspace. + * + * @param data The intent describing the gadgetId. + * @param cellInfo The position on screen where to create the shortcut. + * @param insertAtFirst + */ + private void completeAddGadget(Intent data, CellLayout.CellInfo cellInfo, + boolean insertAtFirst) { + + Bundle extras = data.getExtras(); + int gadgetId = extras.getInt(GadgetManager.EXTRA_GADGET_ID, -1); + + Log.d(LOG_TAG, "dumping extras content="+extras.toString()); + + GadgetInfo gadgetInfo = mGadgetManager.getGadgetInfo(gadgetId); + + // Calculate the grid spans needed to fit this gadget + CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen); + layout.rectToCell(gadgetInfo.minWidth, gadgetInfo.minHeight, cellInfo); + + // Try finding open space on Launcher screen + final int[] xy = mCellCoordinates; + if (!findSlot(cellInfo, xy, cellInfo.spanX, cellInfo.spanY)) return; + + // Build Launcher-specific Gadget info and save to database + LauncherGadgetInfo launcherInfo = new LauncherGadgetInfo(gadgetId); + launcherInfo.spanX = cellInfo.spanX; + launcherInfo.spanY = cellInfo.spanY; + + LauncherModel.addItemToDatabase(this, launcherInfo, + LauncherSettings.Favorites.CONTAINER_DESKTOP, + mWorkspace.getCurrentScreen(), xy[0], xy[1], false); + + if (!mRestoring) { + sModel.addDesktopItem(launcherInfo); + + // Perform actual inflation because we're live + launcherInfo.hostView = mGadgetHost.createView(this, gadgetId, gadgetInfo); + + launcherInfo.hostView.setGadget(gadgetId, gadgetInfo); + launcherInfo.hostView.setTag(launcherInfo); + + mWorkspace.addInCurrentScreen(launcherInfo.hostView, xy[0], xy[1], + launcherInfo.spanX, launcherInfo.spanY, insertAtFirst); + } else if (sModel.isDesktopLoaded()) { + sModel.addDesktopItem(launcherInfo); + } + + // Request fresh update if we needed to config this gadget to + // remove the stale UPDATE from the initial bind + if (!extras.containsKey(SKIP_CONFIG) || true) { + // TODO: move this down into GadgetManager to prevent abuse? (anyone + // could force a specific gadget into the ground through flooding) + Log.d(LOG_TAG, "requesting new gadget update because we had a config step"); + Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION); + intent.putExtra(GadgetManager.EXTRA_GADGET_IDS, new int[] { gadgetId }); + intent.setComponent(gadgetInfo.provider); + sendBroadcast(intent); + } + + } + + public LauncherGadgetHost getGadgetHost() { + return mGadgetHost; + } + static ApplicationInfo addShortcut(Context context, Intent data, CellLayout.CellInfo cellInfo, boolean notify) { @@ -584,107 +711,6 @@ public final class Launcher extends Activity implements View.OnClickListener, On 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); @@ -871,10 +897,51 @@ public final class Launcher extends Activity implements View.OnClickListener, On private void removeShortcutsForPackage(String packageName) { if (packageName != null && packageName.length() > 0) { - android.util.Log.d(LOG_TAG, packageName); mWorkspace.removeShortcutsForPackage(packageName); } } + + static final String SKIP_CONFIG = "skip_config"; + + void addGadget(Intent data) { + int gadgetId = data.getIntExtra(GadgetManager.EXTRA_GADGET_ID, -1); + GadgetInfo gadget = mGadgetManager.getGadgetInfo(gadgetId); + + if (gadget.configure != null) { + // Launch over to configure gadget, if needed + Intent intent = new Intent(GadgetManager.GADGET_CONFIGURE_ACTION); + intent.setComponent(gadget.configure); + intent.putExtra(GadgetManager.EXTRA_GADGET_ID, gadgetId); + + startActivityForResult(intent, REQUEST_CREATE_GADGET); + } else { + // Otherwise just add it + Log.d(LOG_TAG, "dumping extras content="+data.getExtras().toString()); + data.putExtra(SKIP_CONFIG, true); + Log.d(LOG_TAG, "dumping extras content="+data.getExtras().toString()); + onActivityResult(REQUEST_CREATE_GADGET, 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); @@ -964,39 +1031,6 @@ public final class Launcher extends Activity implements View.OnClickListener, On 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 findSlot(CellLayout.CellInfo cellInfo, int[] xy, int spanX, int spanY) { if (!cellInfo.findCellForSpan(xy, spanX, spanY)) { boolean[] occupied = mSavedState != null ? @@ -1136,6 +1170,18 @@ public final class Launcher extends Activity implements View.OnClickListener, On 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); + + finishButton.setOnClickListener(new android.widget.Button.OnClickListener() { + public void onClick(View v) { + finish(); + } + }); + } count = shortcuts.size(); @@ -1176,11 +1222,41 @@ public final class Launcher extends Activity implements View.OnClickListener, On workspace.addInScreen(newLiveFolder, item.screen, item.cellX, item.cellY, 1, 1, !desktopLocked); break; - default: + case LauncherSettings.Favorites.ITEM_TYPE_GADGET: + final LauncherGadgetInfo launcherInfo = (LauncherGadgetInfo) item; + + final int gadgetId = launcherInfo.gadgetId; + GadgetInfo gadgetInfo = mGadgetManager.getGadgetInfo(gadgetId); + launcherInfo.hostView = mGadgetHost.createView(this, gadgetId, gadgetInfo); + + Log.d(LOG_TAG, "about to setGadget during desktop bind"); + launcherInfo.hostView.setGadget(gadgetId, gadgetInfo); + launcherInfo.hostView.setTag(launcherInfo); + + workspace.addInScreen(launcherInfo.hostView, item.screen, item.cellX, + item.cellY, item.spanX, item.spanY, !desktopLocked); + + // Now that we've bound the item, request an update for it + if (gadgetInfo != null) { + if (gadgetInfo.provider != null) { + Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION); + intent.putExtra(GadgetManager.EXTRA_GADGET_IDS, new int[] { gadgetId }); + intent.setComponent(gadgetInfo.provider); + sendBroadcast(intent); + } + } + + break; + 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; } } @@ -1226,17 +1302,6 @@ public final class Launcher extends Activity implements View.OnClickListener, On 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; - } - DragController getDragController() { return mDragLayer; } @@ -1263,6 +1328,11 @@ public final class Launcher extends Activity implements View.OnClickListener, On 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); } } @@ -1508,24 +1578,24 @@ public final class Launcher extends Activity implements View.OnClickListener, On * 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 +1609,6 @@ public final class Launcher extends Activity implements View.OnClickListener, On 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 +1619,76 @@ public final class Launcher extends Activity implements View.OnClickListener, On 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_GADGET: { + int gadgetId = Launcher.this.mGadgetHost.allocateGadgetId(); + + Intent pickIntent = new Intent(GadgetManager.GADGET_PICK_ACTION); + pickIntent.putExtra(GadgetManager.EXTRA_HOST_ID, GADGET_HOST_ID); + pickIntent.putExtra(GadgetManager.EXTRA_GADGET_ID, gadgetId); + startActivityForResult(pickIntent, REQUEST_PICK_GADGET); + 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(); + dismissDialog(DIALOG_CREATE_SHORTCUT); + break; + } + + case AddAdapter.ITEM_WALLPAPER: { + startWallpaper(); + break; + } + + } + + } } } @@ -1569,13 +1698,20 @@ public final class Launcher extends Activity implements View.OnClickListener, On 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; + 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) { + sModel.loadApplications(false, Launcher.this, false); + } else { + sModel.loadUserItems(false, Launcher.this, false, true); + } } } diff --git a/src/com/android/launcher/LauncherGadgetHost.java b/src/com/android/launcher/LauncherGadgetHost.java new file mode 100644 index 0000000000..4f7e8f2f2c --- /dev/null +++ b/src/com/android/launcher/LauncherGadgetHost.java @@ -0,0 +1,131 @@ +/* + * 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.content.Context; +import android.gadget.GadgetHost; +import android.gadget.GadgetHostView; +import android.gadget.GadgetInfo; +import android.graphics.Color; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.widget.TextView; + +/** + * Specific {@link GadgetHost} that creates our {@link LauncherGadgetHostView} which correctly + * captures all long-press events. This ensures that users can always pick up and move gadgets. + */ +public class LauncherGadgetHost extends GadgetHost { + public LauncherGadgetHost(Context context, int hostId) { + super(context, hostId); + } + + protected GadgetHostView onCreateView(Context context, int gadgetId, GadgetInfo gadget) { + return new LauncherGadgetHostView(context); + } + + /** + * {@inheritDoc} + */ + public class LauncherGadgetHostView extends GadgetHostView { + static final String TAG = "LauncherGadgetHostView"; + + private boolean mHasPerformedLongPress; + + private CheckForLongPress mPendingCheckForLongPress; + + private LayoutInflater mInflater; + + public LauncherGadgetHostView(Context context) { + super(context); + + mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + // Prepare our default transition animations + setAnimateFirstView(true); + setInAnimation(context, android.R.anim.fade_in); + setOutAnimation(context, android.R.anim.fade_out); + } + + @Override + protected View getErrorView() { + return mInflater.inflate(R.layout.gadget_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 Gadget + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: { + postCheckForLongClick(); + break; + } + + case MotionEvent.ACTION_UP: { + 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/LauncherGadgetInfo.java b/src/com/android/launcher/LauncherGadgetInfo.java new file mode 100644 index 0000000000..65fa7742f3 --- /dev/null +++ b/src/com/android/launcher/LauncherGadgetInfo.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.content.ContentValues; +import android.gadget.GadgetHostView; + +/** + * Represents a gadget, which just contains an identifier. + */ +class LauncherGadgetInfo extends ItemInfo { + + /** + * Identifier for this gadget when talking with {@link GadgetManager} for updates. + */ + int gadgetId; + + /** + * View that holds this gadget after it's been created. This view isn't created + * until Launcher knows it's needed. + */ + GadgetHostView hostView = null; + + LauncherGadgetInfo(int gadgetId) { + itemType = LauncherSettings.Favorites.ITEM_TYPE_GADGET; + this.gadgetId = gadgetId; + } + + @Override + void onAddToDatabase(ContentValues values) { + super.onAddToDatabase(values); + values.put(LauncherSettings.Favorites.GADGET_ID, gadgetId); + } + + @Override + public String toString() { + return Integer.toString(gadgetId); + } +} diff --git a/src/com/android/launcher/LauncherModel.java b/src/com/android/launcher/LauncherModel.java index 314a502d4e..5d0179663b 100644 --- a/src/com/android/launcher/LauncherModel.java +++ b/src/com/android/launcher/LauncherModel.java @@ -272,7 +272,8 @@ public class LauncherModel { try { while (c.moveToNext()) { try { - if (c.getInt(itemTypeIndex) != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { + if (c.getInt(itemTypeIndex) != + LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { continue; } @@ -374,15 +375,19 @@ public class LauncherModel { 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 gadgetIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.GADGET_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; + LauncherGadgetInfo gadgetInfo = null; int container; long id; Intent intent; @@ -494,41 +499,44 @@ public class LauncherModel { 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(); - 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); - - desktopItems.add(widgetInfo); + 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); + + desktopItems.add(widgetInfo); + break; + case LauncherSettings.Favorites.ITEM_TYPE_GADGET: + // Read all Launcher-specific gadget details + int gadgetId = c.getInt(gadgetIdIndex); + gadgetInfo = new LauncherGadgetInfo(gadgetId); + gadgetInfo.id = c.getLong(idIndex); + gadgetInfo.screen = c.getInt(screenIndex); + gadgetInfo.cellX = c.getInt(cellXIndex); + gadgetInfo.cellY = c.getInt(cellYIndex); + gadgetInfo.spanX = c.getInt(spanXIndex); + gadgetInfo.spanY = c.getInt(spanYIndex); + + container = c.getInt(containerIndex); + if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) { + Log.e(Launcher.LOG_TAG, "Gadget found where container " + + "!= CONTAINER_DESKTOP -- ignoring!"); + continue; + } + gadgetInfo.container = c.getInt(containerIndex); + + desktopItems.add(gadgetInfo); break; } } catch (Exception e) { @@ -972,7 +980,7 @@ public class LauncherModel { 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 a3e529d328..47db647e4a 100644 --- a/src/com/android/launcher/LauncherProvider.java +++ b/src/com/android/launcher/LauncherProvider.java @@ -29,6 +29,7 @@ import android.database.sqlite.SQLiteOpenHelper; 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; @@ -49,7 +50,8 @@ public class LauncherProvider extends ContentProvider { private static final String LOG_TAG = "LauncherSettingsProvider"; private static final String DATABASE_NAME = "launcher.db"; - private static final int DATABASE_VERSION = 1; + + private static final int DATABASE_VERSION = 2; static final String AUTHORITY = "com.android.launcher.settings"; @@ -187,6 +189,7 @@ public class LauncherProvider extends ContentProvider { "spanX INTEGER," + "spanY INTEGER," + "itemType INTEGER," + + "gadgetId INTEGER NOT NULL DEFAULT -1," + "isShortcut INTEGER," + "iconType INTEGER," + "iconPackage TEXT," + @@ -196,6 +199,11 @@ public class LauncherProvider extends ContentProvider { "displayMode INTEGER" + ");"); + // TODO: During first database creation, trigger wipe of any gadgets that + // might have been left around during a wipe-data. +// GadgetManager gadgetManager = GadgetManager.getInstance(mContext); + + if (!convertDatabase(db)) { // Populate favorites table with initial favorites loadFavorites(db, DEFAULT_FAVORITES_PATH); @@ -206,7 +214,7 @@ public class LauncherProvider extends ContentProvider { 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; @@ -261,6 +269,7 @@ public class LauncherProvider extends ContentProvider { 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.GADGET_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,11 +299,31 @@ public class LauncherProvider extends ContentProvider { @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"); - - db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES); - onCreate(db); + int version = oldVersion; + if (version == 1) { + // upgrade 1 -> 2 added gadgetId column + db.beginTransaction(); + try { + // TODO: convert any existing widgets for search and clock + // this might involve a FORCE_ADD_GADGET permission in GadgetManager that + // Launcher could then use to add these gadgets without user interaction + db.execSQL("ALTER TABLE favorites " + + "ADD COLUMN gadgetId INTEGER NOT NULL DEFAULT -1;"); + db.setTransactionSuccessful(); + version = 2; + } catch (SQLException ex) { + // Old version remains, which means we wipe old data + Log.e(LOG_TAG, ex.getMessage(), ex); + } finally { + db.endTransaction(); + } + } + + if (version != DATABASE_VERSION) { + Log.w(LOG_TAG, "Destroying all old data."); + db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES); + onCreate(db); + } } @@ -370,20 +399,7 @@ public class LauncherProvider extends ContentProvider { } 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,6 +412,9 @@ public class LauncherProvider extends ContentProvider { values.put(LauncherSettings.Favorites.SPANX, 4); values.put(LauncherSettings.Favorites.SPANY, 1); db.insert(TABLE_FAVORITES, null, values); + + // TODO: automatically add clock and search gadget to + // default locations. this might need the FORCE permission mentioned above return i; } diff --git a/src/com/android/launcher/LauncherSettings.java b/src/com/android/launcher/LauncherSettings.java index c5dfd1ff02..77bdc73fea 100644 --- a/src/com/android/launcher/LauncherSettings.java +++ b/src/com/android/launcher/LauncherSettings.java @@ -146,6 +146,11 @@ class LauncherSettings { */ static final int ITEM_TYPE_LIVE_FOLDER = 3; + /** + * The favorite is a gadget + */ + static final int ITEM_TYPE_GADGET = 4; + /** * The favorite is a clock */ @@ -161,6 +166,13 @@ class LauncherSettings { */ static final int ITEM_TYPE_WIDGET_PHOTO_FRAME = 1002; + /** + * The gadgetId of the gadget + * + *

Type: INTEGER

+ */ + static final String GADGET_ID = "gadgetId"; + /** * 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 diff --git a/src/com/android/launcher/PhotoFrame.java b/src/com/android/launcher/PhotoFrame.java deleted file mode 100644 index 1151322270..0000000000 --- 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 41d6562c7a..449caf9976 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; @@ -59,6 +57,8 @@ import android.widget.TextView; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemSelectedListener; +import java.util.List; + public class Search extends LinearLayout implements OnClickListener, OnKeyListener, OnLongClickListener, TextWatcher, OnItemClickListener, OnItemSelectedListener { @@ -66,6 +66,7 @@ public class Search extends LinearLayout implements OnClickListener, OnKeyListen private AutoCompleteTextView mSearchText; private ImageButton mGoButton; + private ImageButton mVoiceButton; private OnLongClickListener mLongClickListener; // Support for suggestions @@ -76,6 +77,9 @@ public class Search extends LinearLayout implements OnClickListener, OnKeyListen private String mSuggestionQuery = null; private int mItemSelected = -1; + // For voice searching + private Intent mVoiceSearchIntent; + private Rect mTempRect = new Rect(); /** @@ -86,6 +90,10 @@ public class Search extends LinearLayout implements OnClickListener, OnKeyListen */ 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 +102,14 @@ public class Search extends LinearLayout implements OnClickListener, OnKeyListen 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"); + } } } @@ -206,7 +222,7 @@ public class Search extends LinearLayout implements OnClickListener, OnKeyListen 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 +259,14 @@ public class Search extends LinearLayout implements OnClickListener, OnKeyListen @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 +291,29 @@ public class Search extends LinearLayout implements OnClickListener, OnKeyListen 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 +329,36 @@ public class Search extends LinearLayout implements OnClickListener, OnKeyListen 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(); + List list = pm.queryIntentActivities(mVoiceSearchIntent, + PackageManager.MATCH_DEFAULT_ONLY); + voiceSearchVisible = list.size() > 0; + } + + // 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); diff --git a/src/com/android/launcher/WallpaperChooser.java b/src/com/android/launcher/WallpaperChooser.java index afbe6f352b..1eb8d0cb3f 100644 --- a/src/com/android/launcher/WallpaperChooser.java +++ b/src/com/android/launcher/WallpaperChooser.java @@ -123,9 +123,6 @@ public class WallpaperChooser extends Activity implements AdapterView.OnItemSele final String[] extras = resources.getStringArray(R.array.extra_wallpapers); final String packageName = getApplication().getPackageName(); - final ArrayList images = mImages; - final ArrayList thumbs = mThumbs; - for (String extra : extras) { int res = resources.getIdentifier(extra, "drawable", packageName); if (res != 0) { @@ -133,8 +130,8 @@ public class WallpaperChooser extends Activity implements AdapterView.OnItemSele "drawable", packageName); if (thumbRes != 0) { - images.add(res); - thumbs.add(res); + mThumbs.add(res); + mImages.add(res); } } } @@ -148,7 +145,7 @@ public class WallpaperChooser extends Activity implements AdapterView.OnItemSele 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/Workspace.java b/src/com/android/launcher/Workspace.java index 0ca1b5af68..510dad490e 100644 --- a/src/com/android/launcher/Workspace.java +++ b/src/com/android/launcher/Workspace.java @@ -89,6 +89,8 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag private boolean mAllowLongPress; private boolean mLocked; + private int mTouchSlop; + /** * Used to inflate the Workspace from XML. * @@ -126,6 +128,8 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag mPaint = new Paint(); mPaint.setDither(false); + + mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); } /** @@ -626,8 +630,8 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag */ 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; @@ -1155,6 +1159,10 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag } } } + + // TODO: remove gadgets when gadgetmanager tells us they're gone +// void removeGadgetsForProvider() { +// } void moveToDefaultScreen() { snapToScreen(mDefaultScreen);