diff --git a/AndroidManifest.xml b/AndroidManifest.xml index e81c4c96a4..272f7c12c4 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -70,7 +70,8 @@ android:launchMode="singleTask" android:clearTaskOnLaunch="true" android:stateNotNeeded="true" - android:theme="@style/Theme"> + android:theme="@style/Theme" + android:windowSoftInputMode="stateUnspecified|adjustPan"> diff --git a/res/drawable/texture_brushed_steel.png b/res/drawable/texture_brushed_steel.png new file mode 100644 index 0000000000..73b3dfe63e Binary files /dev/null and b/res/drawable/texture_brushed_steel.png differ diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml index e92c8dc18f..2b262c37ee 100644 --- a/res/layout-land/launcher.xml +++ b/res/layout-land/launcher.xml @@ -66,7 +66,7 @@ android:layout_width="fill_parent" android:layout_height="fill_parent" - android:background="@color/grid_dark_background" + launcher:texture="@drawable/texture_brushed_steel" android:scrollbarStyle="outsideInset" android:drawSelectorOnTop="false" diff --git a/res/layout-port/application_boxed.xml b/res/layout-port/application_boxed.xml index 63d2254b4e..e71a2e2d93 100644 --- a/res/layout-port/application_boxed.xml +++ b/res/layout-port/application_boxed.xml @@ -22,7 +22,7 @@ android:paddingTop="5dip" android:paddingBottom="2dip" android:drawablePadding="0dip" - + android:textSize="13dip" android:maxLines="2" android:ellipsize="marquee" diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml index 1ba524b4dd..0c249a3444 100644 --- a/res/layout-port/launcher.xml +++ b/res/layout-port/launcher.xml @@ -66,7 +66,7 @@ android:layout_width="fill_parent" android:layout_height="fill_parent" - android:background="@color/grid_dark_background" + launcher:texture="@drawable/texture_brushed_steel" android:scrollbarStyle="outsideInset" android:drawSelectorOnTop="false" diff --git a/res/layout/widget_search.xml b/res/layout/widget_search.xml index dcc83a5bb1..209716d529 100644 --- a/res/layout/widget_search.xml +++ b/res/layout/widget_search.xml @@ -26,7 +26,7 @@ android:layout_height="wrap_content" android:src="@drawable/google_logo" /> - "Složka" "Složka Live" "Miniaplikace" - "Gadget" "Tapeta" "Složka" "Hodiny" @@ -55,5 +54,6 @@ "zápis nastavení a odkazů plochy" "Povoluje aplikaci změnit nastavení a odkazy plochy." "Vyhledávání Google" - "Při načítání gadgetu došlo k problému" + + diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index 2b144da99d..c40585215a 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -32,7 +32,6 @@ "Ordner" "Live-Ordner" "Widget" - "Gadget" "Hintergrund" "Ordner" "Uhr" @@ -55,5 +54,6 @@ "Einstellungen und Shortcuts für Startseite schreiben" "Ermöglicht einer Anwendung, die Einstellungen und Shortcuts auf der Startseite zu ändern." "Google-Suche" - "Problem beim Laden von Gadget" + + diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 0cacbdafd5..20d2605be0 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -28,11 +28,10 @@ "Añadir a pantalla de página principal" "Aplicación" "Acceso directo" - "Buscar" + "Búsqueda" "Carpeta" "Carpeta activa" "Widget" - "Gadget" "Fondo de pantalla" "Carpeta" "Reloj" @@ -43,7 +42,7 @@ "Seleccionar carpeta activa" "Añadir" "Fondo de pantalla" - "Búsqueda de Google" + "Buscar con Google" "Notificaciones" "Ajustes" "instalar accesos directos" @@ -55,5 +54,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" - "Problema la cargar gadget" + + diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index c256109224..c01ef47d2f 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -17,9 +17,9 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "Accueil" "Dossier" - "Sélectionner l\'arrière-plan à partir de" - "Configurer l\'arrière-plan" - "Galerie des arrière-plans" + "Sélectionner à partir de..." + "Sélectionner" + "Galerie" "L\'application n\'est pas installée sur votre téléphone." "Nom du dossier" "Renommer le dossier" @@ -30,17 +30,16 @@ "Raccourci" "Recherche" "Dossier" - "Dossier live" + "Dossier actif" "Widget" - "Gadgets" "Arrière-plan" "Dossier" "Horloge" "Cadre d\'image" "Rechercher" - "Plus d\'espace libre sur l\'écran Accueil." + "Plus d\'espace libre sur l\'écran d\'accueil." "Sélectionner un raccourci" - "Sélectionner Live Folder" + "Sélectionner dossier actif" "Ajouter" "Arrière-plan" "Rechercher" @@ -50,10 +49,11 @@ "Permet à une application d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur." "désinstaller les raccourcis" "Permet à une application de supprimer les raccourcis sans l\'intervention de l\'utilisateur." - "lire les paramètres et les raccourcis de la page d\'accueil" + "Lire les paramètres et les raccourcis de la page d\'accueil" "Permet à une application de lire les paramètres et raccourcis de la page d\'accueil." - "écrire les paramètres de la page d\'accueil et des raccourcis" + "Enregistrer 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" - "Problème lors du chargement du gadget" + + diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index a6895f9936..47c6f9f798 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -27,12 +27,11 @@ "Annulla" "Aggiungi a schermata Home" "Applicazione" - "Collegamento" - "Cerca" + "Scorciatoia" + "Ricerca" "Cartella" "Cartella dinamica" "Widget" - "Gadget" "Sfondo" "Cartella" "Orologio" @@ -46,14 +45,15 @@ "Cerca" "Notifiche" "Impostazioni" - "aggiungere collegamenti" - "Consente a un\'applicazione di aggiungere collegamenti automaticamente." - "eliminare collegamenti" - "Consente a un\'applicazione di rimuovere collegamenti automaticamente." - "leggere impostazioni e collegamenti in Home" - "Consente a un\'applicazione di leggere le impostazioni e i collegamenti in Home." - "creare impostazioni e collegamenti in Home" - "Consente a un\'applicazione di modificare le impostazioni e i collegamenti in Home." + "aggiungere scorciatorie" + "Consente a un\'applicazione di aggiungere scorciatoie automaticamente." + "eliminare scorciatoie" + "Consente a un\'applicazione di rimuovere scorciatoie automaticamente." + "leggere impostazioni e scorciatoie in Home" + "Consente a un\'applicazione di leggere le impostazioni e le scorciatoie in Home." + "creare impostazioni e scorciatoie in Home" + "Consente a un\'applicazione di modificare le impostazioni e le scorciatoie in Home." "Ricerca Google" - "Errore durante caricamento del gadget" + + diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index 4c848828d8..cf71c65a9d 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -32,7 +32,6 @@ "フォルダ" "ライブフォルダ" "ウィジェット" - "ガジェット" "壁紙" "フォルダ" "時計" @@ -55,5 +54,6 @@ "ホームの設定とショートカットの書き込み" "ホームの設定とショートカットの変更をアプリケーションに許可します。" "Google検索" - "ガジェットをロードできません" + + diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index 65f2079727..75ad6d8a69 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -32,7 +32,6 @@ "폴더" "라이브 폴더" "위젯" - "가젯" "배경화면" "폴더" "시계" @@ -55,5 +54,6 @@ "홈 설정 및 바로가기 쓰기" "응용프로그램이 홈에 있는 설정 및 바로가기를 변경할 수 있습니다." "Google 검색" - "가젯 로드 중 문제 발생" + + diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index c34acf387e..b14a5da7b7 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -32,7 +32,6 @@ "Mappe" "Aktiv mappe" "Skrivebordselement" - "Gadget" "Bakgrunnsbilde" "Mappe" "Klokke" @@ -55,5 +54,6 @@ "skrive skrivebordsinnstillinger og -snarveier" "Lar applikasjonen endre innstillinger og snarveier på skrivebordet." "Google-søk" - "Problem under lasting av gadget" + + diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index 301d2747e1..e4959c0db7 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -32,7 +32,6 @@ "Map" "Live map" "Widget" - "Gadget" "Achtergrond" "Map" "Klok" @@ -54,6 +53,7 @@ "Hiermee kan een toepassing de instellingen en snelkoppelingen op de startpagina lezen." "instellingen en snelkoppelingen voor de startpagina schrijven" "Hiermee kan een toepassing de instellingen en snelkoppelingen op de startpagina wijzigen." - "Zoeken met Google" - "Probleem bij het laden van gadget" + "Google Zoeken" + + diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index adb5785a27..99718cb160 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -28,16 +28,15 @@ "Dodaj do strony głównej" "Aplikacja" "Skrót" - "Szukaj" + "Wyszukiwarka" "Folder" - "Folder Live" + "Folder aktywny" "Widget" - "Gadżet" "Tapeta" "Folder" "Zegar" "Ramka obrazu" - "Szukaj" + "Wyszukiwarka" "Brak miejsca na tej stronie głównej" "Wybierz skrót" "Wybierz folder aktywny" @@ -55,5 +54,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" - "Wystąpił problem podczas ładowania gadżetu" + + diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index d2ac07dccd..ab6120e9bf 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -32,7 +32,6 @@ "Папка" "Динамическая папка" "Виджет" - "Гаджет" "Фоновый рисунок" "Папка" "Часы" @@ -55,5 +54,6 @@ "записывать ярлыки и настройки главного экрана" "Позволяет приложению изменять настройки и ярлыки на главном экране." "Поиск Google" - "Проблема загрузки гаджета" + + diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 8ea7b3f6bc..f5d929d16f 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -32,7 +32,6 @@ "文件夹" "活动的文件夹" "小工具" - "小工具" "壁纸" "文件夹" "时钟" @@ -55,5 +54,6 @@ "写入“主页”设置和快捷键" "允许应用程序更改“主页”中的设置和快捷键。" "Google 搜索" - "载入小工具时出现问题" + + diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index b6559ffd6b..326f2a232f 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -32,7 +32,6 @@ "資料夾" "使用中的資料夾" "Widget" - "小工具" "桌布" "資料夾" "時鐘" @@ -55,5 +54,6 @@ "寫入首頁設定和捷徑" "允許應用程式變更首頁中的設定和捷徑。" "Google 搜尋" - "載入小工具時發生問題" + + diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 87f5b788b4..ab545aa764 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -69,4 +69,11 @@ + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index ff417987f6..a7945cba7f 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -58,10 +58,8 @@ Folder Live folder - + Widget - - Gadget Wallpaper @@ -115,6 +113,6 @@ Google Search - Problem loading gadget + Problem loading widget diff --git a/src/com/android/launcher/AddAdapter.java b/src/com/android/launcher/AddAdapter.java index 14107084ee..18b36d5e22 100644 --- a/src/com/android/launcher/AddAdapter.java +++ b/src/com/android/launcher/AddAdapter.java @@ -82,7 +82,7 @@ public class AddAdapter extends BaseAdapter { 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, + mItems.add(new ListItem(res, R.string.group_widgets, R.drawable.ic_launcher_gadget, ITEM_GADGET)); mItems.add(new ListItem(res, R.string.group_live_folders, diff --git a/src/com/android/launcher/AllAppsGridView.java b/src/com/android/launcher/AllAppsGridView.java index a898c1a029..b8f79025fd 100644 --- a/src/com/android/launcher/AllAppsGridView.java +++ b/src/com/android/launcher/AllAppsGridView.java @@ -19,25 +19,46 @@ package com.android.launcher; import android.widget.GridView; import android.widget.AdapterView; import android.content.Context; +import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.View; +import android.graphics.BitmapFactory; +import android.graphics.Bitmap; +import android.graphics.Paint; +import android.graphics.Canvas; public class AllAppsGridView extends GridView implements AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener, DragSource { private DragController mDragger; private Launcher mLauncher; + private Bitmap mTexture; + private Paint mPaint; + private int mTextureWidth; + private int mTextureHeight; public AllAppsGridView(Context context) { super(context); } public AllAppsGridView(Context context, AttributeSet attrs) { - super(context, attrs); + this(context, attrs, com.android.internal.R.attr.gridViewStyle); } public AllAppsGridView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AllAppsGridView, defStyle, 0); + final int textureId = a.getResourceId(R.styleable.AllAppsGridView_texture, 0); + if (textureId != 0) { + mTexture = BitmapFactory.decodeResource(getResources(), textureId); + mTextureWidth = mTexture.getWidth(); + mTextureHeight = mTexture.getHeight(); + + mPaint = new Paint(); + mPaint.setDither(false); + } + a.recycle(); } @Override @@ -46,6 +67,32 @@ public class AllAppsGridView extends GridView implements AdapterView.OnItemClick setOnItemLongClickListener(this); } + @Override + public void draw(Canvas canvas) { + final Bitmap texture = mTexture; + final Paint paint = mPaint; + + final int width = getWidth(); + final int height = getHeight(); + + final int textureWidth = mTextureWidth; + final int textureHeight = mTextureHeight; + + int x = 0; + int y; + + while (x < width) { + y = 0; + while (y < height) { + canvas.drawBitmap(texture, x, y, paint); + y += textureHeight; + } + x += textureWidth; + } + + super.draw(canvas); + } + public void onItemClick(AdapterView parent, View v, int position, long id) { ApplicationInfo app = (ApplicationInfo) parent.getItemAtPosition(position); mLauncher.startActivitySafely(app.intent); diff --git a/src/com/android/launcher/DeleteZone.java b/src/com/android/launcher/DeleteZone.java index 6f67884db2..f31a206c5d 100644 --- a/src/com/android/launcher/DeleteZone.java +++ b/src/com/android/launcher/DeleteZone.java @@ -85,7 +85,11 @@ public class DeleteZone extends ImageView implements DropTarget, DragController. final LauncherModel model = Launcher.getModel(); if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { - model.removeDesktopItem(item); + if (item instanceof LauncherGadgetInfo) { + model.removeDesktopGadget((LauncherGadgetInfo) item); + } else { + model.removeDesktopItem(item); + } } else { if (source instanceof UserFolder) { final UserFolder userFolder = (UserFolder) source; diff --git a/src/com/android/launcher/DragLayer.java b/src/com/android/launcher/DragLayer.java index aa6615a22a..b542de62aa 100644 --- a/src/com/android/launcher/DragLayer.java +++ b/src/com/android/launcher/DragLayer.java @@ -32,6 +32,7 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.KeyEvent; +import android.view.inputmethod.InputMethodManager; import android.widget.FrameLayout; /** @@ -127,6 +128,8 @@ public class DragLayer extends FrameLayout implements DragController { private int mAnimationType; private int mAnimationState = ANIMATION_STATE_DONE; + private InputMethodManager mInputMethodManager; + /** * Used to create a new DragLayer from XML. * @@ -144,7 +147,14 @@ public class DragLayer extends FrameLayout implements DragController { if (PROFILE_DRAWING_DURING_DRAG) { android.os.Debug.startMethodTracing("Launcher"); } - + + // Hide soft keyboard, if visible + if (mInputMethodManager == null) { + mInputMethodManager = (InputMethodManager) + getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + } + mInputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0); + if (mListener != null) { mListener.onDragStart(v, source, dragInfo, dragAction); } diff --git a/src/com/android/launcher/Launcher.java b/src/com/android/launcher/Launcher.java index 58fcd5a28b..e88e55ebf5 100644 --- a/src/com/android/launcher/Launcher.java +++ b/src/com/android/launcher/Launcher.java @@ -40,6 +40,7 @@ import android.database.ContentObserver; import android.gadget.GadgetProviderInfo; import android.gadget.GadgetManager; import android.graphics.Bitmap; +import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; @@ -86,6 +87,7 @@ import java.util.ArrayList; */ public final class Launcher extends Activity implements View.OnClickListener, OnLongClickListener { static final String LOG_TAG = "Launcher"; + static final boolean LOGD = false; private static final boolean PROFILE_STARTUP = false; private static final boolean DEBUG_USER_INTERFACE = false; @@ -169,7 +171,7 @@ public final class Launcher extends Activity implements View.OnClickListener, On private GadgetManager mGadgetManager; private LauncherGadgetHost mGadgetHost; - private static final int GADGET_HOST_ID = 1024; + static final int GADGET_HOST_ID = 1024; private CellLayout.CellInfo mAddItemCellInfo; private CellLayout.CellInfo mMenuAddInfo; @@ -191,6 +193,8 @@ public final class Launcher extends Activity implements View.OnClickListener, On private boolean mWaitingForResult; private boolean mLocaleChanged; + private Bundle mSavedInstanceState; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -201,8 +205,6 @@ public final class Launcher extends Activity implements View.OnClickListener, On mGadgetHost = new LauncherGadgetHost(this, GADGET_HOST_ID); mGadgetHost.startListening(); - // TODO: figure out if this is first launch and correctly clear GadgetHost database - if (PROFILE_STARTUP) { android.os.Debug.startMethodTracing("/sdcard/launcher"); } @@ -355,15 +357,11 @@ public final class Launcher extends Activity implements View.OnClickListener, On } return handled; } + private boolean acceptFilter() { - final Configuration configuration = getResources().getConfiguration(); - final boolean keyboardShowing = configuration.keyboardHidden != - Configuration.KEYBOARDHIDDEN_YES; - final boolean hasKeyboard = configuration.keyboard != Configuration.KEYBOARD_NOKEYS; final InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - return (hasKeyboard && keyboardShowing) || - (!hasKeyboard && !inputManager.isFullscreenMode()); + return !inputManager.isFullscreenMode(); } @Override @@ -633,7 +631,7 @@ public final class Launcher extends Activity implements View.OnClickListener, On mWorkspace.getCurrentScreen(), xy[0], xy[1], false); if (!mRestoring) { - sModel.addDesktopItem(launcherInfo); + sModel.addDesktopGadget(launcherInfo); // Perform actual inflation because we're live launcherInfo.hostView = mGadgetHost.createView(this, gadgetId, gadgetInfo); @@ -644,7 +642,7 @@ public final class Launcher extends Activity implements View.OnClickListener, On mWorkspace.addInCurrentScreen(launcherInfo.hostView, xy[0], xy[1], launcherInfo.spanX, launcherInfo.spanY, insertAtFirst); } else if (sModel.isDesktopLoaded()) { - sModel.addDesktopItem(launcherInfo); + sModel.addDesktopGadget(launcherInfo); } } @@ -745,6 +743,12 @@ public final class Launcher extends Activity implements View.OnClickListener, On } } + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + // Do not call super here + mSavedInstanceState = savedInstanceState; + } + @Override protected void onSaveInstanceState(Bundle outState) { outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getCurrentScreen()); @@ -793,7 +797,11 @@ public final class Launcher extends Activity implements View.OnClickListener, On super.onDestroy(); - mGadgetHost.stopListening(); + try { + mGadgetHost.stopListening(); + } catch (NullPointerException ex) { + Log.w(LOG_TAG, "problem while stopping GadgetHost during Launcher destruction", ex); + } TextKeyListener.getInstance().release(); @@ -1156,8 +1164,8 @@ public final class Launcher extends Activity implements View.OnClickListener, On void onDesktopItemsLoaded() { if (mDestroyed) return; - bindDesktopItems(); mAllAppsGrid.setAdapter(Launcher.getModel().getApplicationsAdapter()); + bindDesktopItems(); } /** @@ -1165,7 +1173,8 @@ public final class Launcher extends Activity implements View.OnClickListener, On */ private void bindDesktopItems() { final ArrayList shortcuts = sModel.getDesktopItems(); - if (shortcuts == null) { + final ArrayList gadgets = sModel.getDesktopGadgets(); + if (shortcuts == null || gadgets == null) { return; } @@ -1187,19 +1196,17 @@ public final class Launcher extends Activity implements View.OnClickListener, On }); } - count = shortcuts.size(); - - final DesktopItemsBinder binder = new DesktopItemsBinder(this, shortcuts); - binder.obtainMessage(DesktopItemsBinder.MESSAGE_BIND_ITEMS, 0, count).sendToTarget(); + final DesktopBinder binder = new DesktopBinder(this, shortcuts, gadgets); + binder.startBindingItems(); } - private void bindItems(Launcher.DesktopItemsBinder binder, + private void bindItems(Launcher.DesktopBinder binder, ArrayList shortcuts, int start, int count) { final Workspace workspace = mWorkspace; final boolean desktopLocked = mDesktopLocked; - final int end = Math.min(start + DesktopItemsBinder.ITEMS_COUNT, count); + final int end = Math.min(start + DesktopBinder.ITEMS_COUNT, count); int i = start; for ( ; i < end; i++) { @@ -1226,21 +1233,6 @@ public final class Launcher extends Activity implements View.OnClickListener, On workspace.addInScreen(newLiveFolder, item.screen, item.cellX, item.cellY, 1, 1, !desktopLocked); break; - case LauncherSettings.Favorites.ITEM_TYPE_GADGET: - final LauncherGadgetInfo launcherInfo = (LauncherGadgetInfo) item; - - final int gadgetId = launcherInfo.gadgetId; - GadgetProviderInfo gadgetInfo = mGadgetManager.getGadgetInfo(gadgetId); - launcherInfo.hostView = mGadgetHost.createView(this, gadgetId, gadgetInfo); - - Log.d(LOG_TAG, "about to setGadget for id="+gadgetId+", info="+gadgetInfo); - launcherInfo.hostView.setGadget(gadgetId, gadgetInfo); - launcherInfo.hostView.setTag(launcherInfo); - - workspace.addInScreen(launcherInfo.hostView, item.screen, item.cellX, - item.cellY, item.spanX, item.spanY, !desktopLocked); - - break; case LauncherSettings.Favorites.ITEM_TYPE_WIDGET_SEARCH: final int screen = workspace.getCurrentScreen(); final View view = mInflater.inflate(R.layout.widget_search, @@ -1258,14 +1250,17 @@ public final class Launcher extends Activity implements View.OnClickListener, On if (end >= count) { finishBindDesktopItems(); + binder.startBindingGadgets(); } else { - binder.obtainMessage(DesktopItemsBinder.MESSAGE_BIND_ITEMS, i, count).sendToTarget(); + binder.obtainMessage(DesktopBinder.MESSAGE_BIND_ITEMS, i, count).sendToTarget(); } } private void finishBindDesktopItems() { if (mSavedState != null) { - mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus(); + if (!mWorkspace.hasFocus()) { + mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus(); + } final long[] userFolders = mSavedState.getLongArray(RUNTIME_STATE_USER_FOLDERS); if (userFolders != null) { @@ -1278,24 +1273,67 @@ public final class Launcher extends Activity implements View.OnClickListener, On final Folder openFolder = mWorkspace.getOpenFolder(); if (openFolder != null) { openFolder.requestFocus(); - } else { - mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus(); } } final boolean allApps = mSavedState.getBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, false); if (allApps) { mDrawer.open(); - mDrawer.requestFocus(); } mSavedState = null; } + if (mSavedInstanceState != null) { + super.onRestoreInstanceState(mSavedInstanceState); + mSavedInstanceState = null; + } + + if (mDrawer.isOpened() && !mDrawer.hasFocus()) { + mDrawer.requestFocus(); + } + mDesktopLocked = false; mDrawer.unlock(); } + private void bindGadgets(Launcher.DesktopBinder binder, + ArrayList gadgets, int start, int count) { + + final Workspace workspace = mWorkspace; + final boolean desktopLocked = mDesktopLocked; + + final int end = Math.min(start + DesktopBinder.GADGETS_COUNT, count); + int i = start; + + for ( ; i < end; i++) { + final LauncherGadgetInfo item = gadgets.get(i); + + final int gadgetId = item.gadgetId; + final GadgetProviderInfo gadgetInfo = mGadgetManager.getGadgetInfo(gadgetId); + item.hostView = mGadgetHost.createView(this, gadgetId, gadgetInfo); + + if (LOGD) Log.d(LOG_TAG, String.format("about to setGadget for id=%d, info=%s", gadgetId, gadgetInfo)); + + item.hostView.setGadget(gadgetId, gadgetInfo); + item.hostView.setTag(item); + + workspace.addInScreen(item.hostView, item.screen, item.cellX, + item.cellY, item.spanX, item.spanY, !desktopLocked); + } + + workspace.requestLayout(); + + if (end >= count) { + finishBindDesktopGadgets(); + } else { + binder.obtainMessage(DesktopBinder.MESSAGE_BIND_GADGETS, i, count).sendToTarget(); + } + } + + private void finishBindDesktopGadgets() { + } + DragController getDragController() { return mDragLayer; } @@ -1419,7 +1457,7 @@ public final class Launcher extends Activity implements View.OnClickListener, On // This happens when long clicking an item with the dpad/trackball if (cellInfo == null) { - return false; + return true; } if (mWorkspace.allowLongPress()) { @@ -1450,6 +1488,10 @@ public final class Launcher extends Activity implements View.OnClickListener, On return !mDrawer.isMoving() && !mDrawer.isOpened(); } + boolean isDrawerUp() { + return mDrawer.isOpened() && !mDrawer.isMoving(); + } + Workspace getWorkspace() { return mWorkspace; } @@ -1766,6 +1808,16 @@ public final class Launcher extends Activity implements View.OnClickListener, On public void onDrawerOpened() { if (!mOpen) { mHandleIcon.reverseTransition(150); + final Rect bounds = mWorkspace.mDrawerBounds; + + View view = mAllAppsGrid; + view.getDrawingRect(bounds); + + while (view != mDragLayer) { + bounds.offset(view.getLeft(), view.getTop()); + view = (View) view.getParent(); + } + mOpen = true; } } @@ -1773,41 +1825,63 @@ public final class Launcher extends Activity implements View.OnClickListener, On public void onDrawerClosed() { if (mOpen) { mHandleIcon.reverseTransition(150); + mWorkspace.mDrawerBounds.setEmpty(); mOpen = false; } mAllAppsGrid.setSelection(0); mAllAppsGrid.clearTextFilter(); - mWorkspace.clearChildrenCache(); } public void onScrollStarted() { - mWorkspace.enableChildrenCache(); } public void onScrollEnded() { } } - private static class DesktopItemsBinder extends Handler { + private static class DesktopBinder extends Handler { static final int MESSAGE_BIND_ITEMS = 0x1; + static final int MESSAGE_BIND_GADGETS = 0x2; // Number of items to bind in every pass static final int ITEMS_COUNT = 6; + static final int GADGETS_COUNT = 1; private final ArrayList mShortcuts; + private final ArrayList mGadgets; private final WeakReference mLauncher; - DesktopItemsBinder(Launcher launcher, ArrayList shortcuts) { + DesktopBinder(Launcher launcher, ArrayList shortcuts, + ArrayList gadgets) { + mLauncher = new WeakReference(launcher); mShortcuts = shortcuts; + mGadgets = gadgets; + } + + public void startBindingItems() { + obtainMessage(MESSAGE_BIND_ITEMS, 0, mShortcuts.size()).sendToTarget(); } + public void startBindingGadgets() { + obtainMessage(MESSAGE_BIND_GADGETS, 0, mGadgets.size()).sendToTarget(); + } + @Override public void handleMessage(Message msg) { + Launcher launcher = mLauncher.get(); + if (launcher == null) { + return; + } + switch (msg.what) { - case MESSAGE_BIND_ITEMS: - Launcher launcher = mLauncher.get(); - if (launcher != null) launcher.bindItems(this, mShortcuts, msg.arg1, msg.arg2); + case MESSAGE_BIND_ITEMS: { + launcher.bindItems(this, mShortcuts, msg.arg1, msg.arg2); break; + } + case MESSAGE_BIND_GADGETS: { + launcher.bindGadgets(this, mGadgets, msg.arg1, msg.arg2); + break; + } } } } diff --git a/src/com/android/launcher/LauncherGadgetHostView.java b/src/com/android/launcher/LauncherGadgetHostView.java index dd098aac23..2b5f7f7f46 100644 --- a/src/com/android/launcher/LauncherGadgetHostView.java +++ b/src/com/android/launcher/LauncherGadgetHostView.java @@ -58,13 +58,13 @@ public class LauncherGadgetHostView extends GadgetHostView { break; } - case MotionEvent.ACTION_UP: { + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: mHasPerformedLongPress = false; if (mPendingCheckForLongPress != null) { removeCallbacks(mPendingCheckForLongPress); } break; - } } // Otherwise continue letting touch events fall through to children diff --git a/src/com/android/launcher/LauncherModel.java b/src/com/android/launcher/LauncherModel.java index 783eef2db9..40b5402c0b 100644 --- a/src/com/android/launcher/LauncherModel.java +++ b/src/com/android/launcher/LauncherModel.java @@ -57,6 +57,7 @@ public class LauncherModel { private boolean mDesktopItemsLoaded; private ArrayList mDesktopItems; + private ArrayList mDesktopGadgets; private HashMap mFolders; private ArrayList mApplications; @@ -202,6 +203,7 @@ public class LauncherModel { final ApplicationsAdapter applicationList = mApplicationList; final int count = buffer.size(); + applicationList.setNotifyOnChange(false); applicationList.clear(); for (int i = 0; i < count; i++) { applicationList.setNotifyOnChange(false); @@ -222,7 +224,7 @@ public class LauncherModel { } boolean isDesktopLoaded() { - return mDesktopItems != null && mDesktopItemsLoaded; + return mDesktopItems != null && mDesktopGadgets != null && mDesktopItemsLoaded; } /** @@ -232,7 +234,7 @@ public class LauncherModel { void loadUserItems(boolean isLaunching, Launcher launcher, boolean localeChanged, boolean loadApplications) { - if (isLaunching && mDesktopItems != null && mDesktopItemsLoaded) { + if (isLaunching && isDesktopLoaded()) { if (loadApplications) startApplicationsLoader(launcher); // We have already loaded our data from the DB launcher.onDesktopItemsLoaded(); @@ -358,9 +360,11 @@ public class LauncherModel { } mDesktopItems = new ArrayList(); + mDesktopGadgets = new ArrayList(); mFolders = new HashMap(); final ArrayList desktopItems = mDesktopItems; + final ArrayList desktopGadgets = mDesktopGadgets; final Cursor c = contentResolver.query( LauncherSettings.Favorites.CONTENT_URI, null, null, null, null); @@ -386,8 +390,8 @@ public class LauncherModel { ApplicationInfo info; String intentDescription; - Widget widgetInfo = null; - LauncherGadgetInfo gadgetInfo = null; + Widget widgetInfo; + LauncherGadgetInfo gadgetInfo; int container; long id; Intent intent; @@ -536,7 +540,7 @@ public class LauncherModel { } gadgetInfo.container = c.getInt(containerIndex); - desktopItems.add(gadgetInfo); + desktopGadgets.add(gadgetInfo); break; } } catch (Exception e) { @@ -643,6 +647,7 @@ public class LauncherModel { mApplicationsAdapter = null; unbindAppDrawables(mApplications); unbindDrawables(mDesktopItems); + unbindGadgetHostViews(mDesktopGadgets); } /** @@ -658,6 +663,7 @@ public class LauncherModel { case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: ((ApplicationInfo)item).icon.setCallback(null); + break; } } } @@ -676,6 +682,19 @@ public class LauncherModel { } } + /** + * Remove any {@link LauncherGadgetHostView} references in our gadgets. + */ + private void unbindGadgetHostViews(ArrayList gadgets) { + if (gadgets != null) { + final int count = gadgets.size(); + for (int i = 0; i < count; i++) { + LauncherGadgetInfo launcherInfo = gadgets.get(i); + launcherInfo.hostView = null; + } + } + } + /** * @return The current list of applications */ @@ -696,6 +715,13 @@ public class LauncherModel { public ArrayList getDesktopItems() { return mDesktopItems; } + + /** + * @return The current list of desktop items + */ + public ArrayList getDesktopGadgets() { + return mDesktopGadgets; + } /** * Add an item to the desktop @@ -715,6 +741,20 @@ public class LauncherModel { mDesktopItems.remove(info); } + /** + * Add a gadget to the desktop + */ + public void addDesktopGadget(LauncherGadgetInfo info) { + mDesktopGadgets.add(info); + } + + /** + * Remove a gadget from the desktop + */ + public void removeDesktopGadget(LauncherGadgetInfo info) { + mDesktopGadgets.remove(info); + } + /** * Make an ApplicationInfo object for an application */ diff --git a/src/com/android/launcher/LauncherProvider.java b/src/com/android/launcher/LauncherProvider.java index 47db647e4a..539d5ae449 100644 --- a/src/com/android/launcher/LauncherProvider.java +++ b/src/com/android/launcher/LauncherProvider.java @@ -30,6 +30,7 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.database.Cursor; import android.database.SQLException; +import android.gadget.GadgetHost; import android.util.Log; import android.util.Xml; import android.net.Uri; @@ -41,19 +42,25 @@ import java.io.FileReader; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.ArrayList; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import com.android.internal.util.XmlUtils; +import com.android.launcher.LauncherSettings.Favorites; public class LauncherProvider extends ContentProvider { - private static final String LOG_TAG = "LauncherSettingsProvider"; + private static final String LOG_TAG = "LauncherProvider"; + private static final boolean LOGD = true; private static final String DATABASE_NAME = "launcher.db"; private static final int DATABASE_VERSION = 2; static final String AUTHORITY = "com.android.launcher.settings"; + + static final String EXTRA_BIND_SOURCES = "com.android.launcher.settings.bindsources"; + static final String EXTRA_BIND_TARGETS = "com.android.launcher.settings.bindtargets"; static final String TABLE_FAVORITES = "favorites"; static final String PARAMETER_NOTIFY = "notify"; @@ -170,14 +177,18 @@ public class LauncherProvider extends ContentProvider { private static final String ATTRIBUTE_Y = "y"; private final Context mContext; + private final GadgetHost mGadgetHost; DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); mContext = context; + mGadgetHost = new GadgetHost(context, Launcher.GADGET_HOST_ID); } @Override public void onCreate(SQLiteDatabase db) { + if (LOGD) Log.d(LOG_TAG, "creating new launcher database"); + db.execSQL("CREATE TABLE favorites (" + "_id INTEGER PRIMARY KEY," + "title TEXT," + @@ -199,11 +210,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); - - + // Database was just created, so wipe any previous gadgets + if (mGadgetHost != null) { + mGadgetHost.deleteHost(); + } + if (!convertDatabase(db)) { // Populate favorites table with initial favorites loadFavorites(db, DEFAULT_FAVORITES_PATH); @@ -211,6 +222,7 @@ public class LauncherProvider extends ContentProvider { } private boolean convertDatabase(SQLiteDatabase db) { + if (LOGD) Log.d(LOG_TAG, "converting database from an older format, but not onUpgrade"); boolean converted = false; final Uri uri = Uri.parse("content://" + Settings.AUTHORITY + @@ -236,6 +248,12 @@ public class LauncherProvider extends ContentProvider { resolver.delete(uri, null, null); } } + + if (converted) { + // Convert widgets from this import into gadgets + if (LOGD) Log.d(LOG_TAG, "converted and now triggering widget upgrade"); + convertWidgets(db); + } return converted; } @@ -299,16 +317,16 @@ public class LauncherProvider extends ContentProvider { @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + if (LOGD) Log.d(LOG_TAG, "onUpgrade triggered"); + 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 + // Insert new column for holding gadgetIds db.execSQL("ALTER TABLE favorites " + - "ADD COLUMN gadgetId INTEGER NOT NULL DEFAULT -1;"); + "ADD COLUMN gadgetId INTEGER NOT NULL DEFAULT -1;"); db.setTransactionSuccessful(); version = 2; } catch (SQLException ex) { @@ -317,6 +335,11 @@ public class LauncherProvider extends ContentProvider { } finally { db.endTransaction(); } + + // Convert existing widgets only if table upgrade was successful + if (version == 2) { + convertWidgets(db); + } } if (version != DATABASE_VERSION) { @@ -325,8 +348,99 @@ public class LauncherProvider extends ContentProvider { onCreate(db); } } + + /** + * Upgrade existing clock and photo frame widgets into their new gadget + * equivalents. This method allocates gadgetIds, and then hands off to + * LauncherGadgetBinder to finish the actual binding. + */ + private void convertWidgets(SQLiteDatabase db) { + final int[] bindSources = new int[] { + Favorites.ITEM_TYPE_WIDGET_CLOCK, + Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME, + }; + + final ArrayList bindTargets = new ArrayList(); + bindTargets.add(new ComponentName("com.android.alarmclock", + "com.android.alarmclock.AnalogGadgetProvider")); + bindTargets.add(new ComponentName("com.android.camera", + "com.android.camera.PhotoGadgetProvider")); + + final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE, bindSources); + + Cursor c = null; + boolean allocatedGadgets = false; + + db.beginTransaction(); + try { + // Select and iterate through each matching widget + c = db.query(TABLE_FAVORITES, new String[] { Favorites._ID }, + selectWhere, null, null, null, null); + + if (LOGD) Log.d(LOG_TAG, "found upgrade cursor count="+c.getCount()); + + final ContentValues values = new ContentValues(); + while (c != null && c.moveToNext()) { + long favoriteId = c.getLong(0); + + // Allocate and update database with new gadgetId + try { + int gadgetId = mGadgetHost.allocateGadgetId(); + + if (LOGD) Log.d(LOG_TAG, "allocated gadgetId="+gadgetId+" for favoriteId="+favoriteId); + + values.clear(); + values.put(LauncherSettings.Favorites.GADGET_ID, gadgetId); + + // Original widgets might not have valid spans when upgrading + values.put(LauncherSettings.Favorites.SPANX, 2); + values.put(LauncherSettings.Favorites.SPANY, 2); + String updateWhere = Favorites._ID + "=" + favoriteId; + db.update(TABLE_FAVORITES, values, updateWhere, null); + + allocatedGadgets = true; + } catch (RuntimeException ex) { + Log.e(LOG_TAG, "Problem allocating gadgetId", ex); + } + } + + db.setTransactionSuccessful(); + } catch (SQLException ex) { + Log.w(LOG_TAG, "Problem while allocating gadgetIds for existing widgets", ex); + } finally { + db.endTransaction(); + if (c != null) { + c.close(); + } + } + + // If any gadgetIds allocated, then launch over to binder + if (allocatedGadgets) { + launchGadgetBinder(bindSources, bindTargets); + } + } + /** + * Launch the gadget binder that walks through the Launcher database, + * binding any matching widgets to the corresponding targets. We can't + * bind ourselves because our parent process can't obtain the + * BIND_GADGET permission. + */ + private void launchGadgetBinder(int[] bindSources, ArrayList bindTargets) { + final Intent intent = new Intent(); + intent.setComponent(new ComponentName("com.android.settings", + "com.android.settings.LauncherGadgetBinder")); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + final Bundle extras = new Bundle(); + extras.putIntArray(EXTRA_BIND_SOURCES, bindSources); + extras.putParcelableArrayList(EXTRA_BIND_TARGETS, bindTargets); + intent.putExtras(extras); + + mContext.startActivity(intent); + } + /** * Loads the default set of favorite packages from an xml file. * @@ -413,13 +527,62 @@ public class LauncherProvider extends ContentProvider { 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 + final int[] bindSources = new int[] { + Favorites.ITEM_TYPE_WIDGET_CLOCK, + }; + + final ArrayList bindTargets = new ArrayList(); + bindTargets.add(new ComponentName("com.android.alarmclock", + "com.android.alarmclock.AnalogGadgetProvider")); + + boolean allocatedGadgets = false; + + // Try binding to an analog clock gadget + try { + int gadgetId = mGadgetHost.allocateGadgetId(); + + values.clear(); + values.put(LauncherSettings.Favorites.CONTAINER, + LauncherSettings.Favorites.CONTAINER_DESKTOP); + values.put(LauncherSettings.Favorites.ITEM_TYPE, + LauncherSettings.Favorites.ITEM_TYPE_WIDGET_CLOCK); + values.put(LauncherSettings.Favorites.SCREEN, 1); + values.put(LauncherSettings.Favorites.CELLX, 1); + values.put(LauncherSettings.Favorites.CELLY, 0); + values.put(LauncherSettings.Favorites.SPANX, 2); + values.put(LauncherSettings.Favorites.SPANY, 2); + values.put(LauncherSettings.Favorites.GADGET_ID, gadgetId); + db.insert(TABLE_FAVORITES, null, values); + + allocatedGadgets = true; + } catch (RuntimeException ex) { + Log.e(LOG_TAG, "Problem allocating gadgetId", ex); + } + // If any gadgetIds allocated, then launch over to binder + if (allocatedGadgets) { + launchGadgetBinder(bindSources, bindTargets); + } + return i; } } + /** + * Build a query string that will match any row where the column matches + * anything in the values list. + */ + static String buildOrWhereString(String column, int[] values) { + StringBuilder selectWhere = new StringBuilder(); + for (int i = values.length - 1; i >= 0; i--) { + selectWhere.append(column).append("=").append(values[i]); + if (i > 0) { + selectWhere.append(" OR "); + } + } + return selectWhere.toString(); + } + static class SqlArguments { public final String table; public final String where; diff --git a/src/com/android/launcher/LauncherSettings.java b/src/com/android/launcher/LauncherSettings.java index 77bdc73fea..461cca4fe4 100644 --- a/src/com/android/launcher/LauncherSettings.java +++ b/src/com/android/launcher/LauncherSettings.java @@ -24,7 +24,8 @@ import android.net.Uri; */ class LauncherSettings { /** - * Favorites. + * Favorites. When changing these values, be sure to update + * {@link com.android.settings.LauncherGadgetBinder} as needed. */ static final class Favorites implements BaseColumns { /** diff --git a/src/com/android/launcher/LiveFolder.java b/src/com/android/launcher/LiveFolder.java index 37b98e0f7b..5d727f830d 100644 --- a/src/com/android/launcher/LiveFolder.java +++ b/src/com/android/launcher/LiveFolder.java @@ -24,8 +24,14 @@ import android.view.View; import android.widget.AdapterView; import android.net.Uri; import android.provider.LiveFolders; +import android.os.AsyncTask; +import android.database.Cursor; + +import java.lang.ref.WeakReference; public class LiveFolder extends Folder { + private AsyncTask mLoadingTask; + public LiveFolder(Context context, AttributeSet attrs) { super(context, attrs); } @@ -66,7 +72,10 @@ public class LiveFolder extends Folder { void bind(FolderInfo info) { super.bind(info); - setContentAdapter(new LiveFolderAdapter(mLauncher, (LiveFolderInfo) info)); + if (mLoadingTask != null && mLoadingTask.getStatus() == AsyncTask.Status.RUNNING) { + mLoadingTask.cancel(true); + } + mLoadingTask = new FolderLoadingTask(this).execute((LiveFolderInfo) info); } @Override @@ -78,6 +87,42 @@ public class LiveFolder extends Folder { @Override void onClose() { super.onClose(); + if (mLoadingTask != null && mLoadingTask.getStatus() == AsyncTask.Status.RUNNING) { + mLoadingTask.cancel(true); + } ((LiveFolderAdapter) mContent.getAdapter()).cleanup(); } + + static class FolderLoadingTask extends AsyncTask { + private final WeakReference mFolder; + private LiveFolderInfo mInfo; + + FolderLoadingTask(LiveFolder folder) { + mFolder = new WeakReference(folder); + } + + protected Cursor doInBackground(LiveFolderInfo... params) { + final LiveFolder folder = mFolder.get(); + if (folder != null) { + mInfo = params[0]; + return LiveFolderAdapter.query(folder.mLauncher, mInfo); + } + return null; + } + + @Override + protected void onPostExecute(Cursor cursor) { + if (!isCancelled()) { + if (cursor != null) { + final LiveFolder folder = mFolder.get(); + if (folder != null) { + final Launcher launcher = folder.mLauncher; + folder.setContentAdapter(new LiveFolderAdapter(launcher, mInfo, cursor)); + } + } + } else if (cursor != null) { + cursor.close(); + } + } + } } diff --git a/src/com/android/launcher/LiveFolderAdapter.java b/src/com/android/launcher/LiveFolderAdapter.java index 71ed85d343..4a5c077c89 100644 --- a/src/com/android/launcher/LiveFolderAdapter.java +++ b/src/com/android/launcher/LiveFolderAdapter.java @@ -45,8 +45,8 @@ class LiveFolderAdapter extends CursorAdapter { new HashMap>(); private final Launcher mLauncher; - LiveFolderAdapter(Launcher launcher, LiveFolderInfo info) { - super(launcher, query(launcher, info), true); + LiveFolderAdapter(Launcher launcher, LiveFolderInfo info, Cursor cursor) { + super(launcher, cursor, true); mIsList = info.displayMode == LiveFolders.DISPLAY_MODE_LIST; mInflater = LayoutInflater.from(launcher); mLauncher = launcher; @@ -54,8 +54,9 @@ class LiveFolderAdapter extends CursorAdapter { mLauncher.startManagingCursor(getCursor()); } - private static Cursor query(Context context, LiveFolderInfo info) { - return context.getContentResolver().query(info.uri, null, null, null, LiveFolders.NAME + " ASC"); + static Cursor query(Context context, LiveFolderInfo info) { + return context.getContentResolver().query(info.uri, null, null, + null, LiveFolders.NAME + " ASC"); } public View newView(Context context, Cursor cursor, ViewGroup parent) { diff --git a/src/com/android/launcher/Search.java b/src/com/android/launcher/Search.java index d33fd69fa3..522a432620 100644 --- a/src/com/android/launcher/Search.java +++ b/src/com/android/launcher/Search.java @@ -57,8 +57,6 @@ 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 { @@ -81,6 +79,7 @@ public class Search extends LinearLayout implements OnClickListener, OnKeyListen private Intent mVoiceSearchIntent; private Rect mTempRect = new Rect(); + private boolean mRestoreFocus = false; /** * Used to inflate the Workspace from XML. @@ -170,7 +169,26 @@ public class Search extends LinearLayout implements OnClickListener, OnKeyListen getContext().startActivity(launcher); } - + + @Override + public void onWindowFocusChanged(boolean hasWindowFocus) { + if (!hasWindowFocus && hasFocus()) { + mRestoreFocus = true; + } + + super.onWindowFocusChanged(hasWindowFocus); + + if (hasWindowFocus && mRestoreFocus) { + if (isInTouchMode()) { + final AutoCompleteTextView searchText = mSearchText; + searchText.setSelectAllOnFocus(false); + searchText.requestFocusFromTouch(); + searchText.setSelectAllOnFocus(true); + } + mRestoreFocus = false; + } + } + /** * Implements TextWatcher (for EditText) */ @@ -370,6 +388,18 @@ public class Search extends LinearLayout implements OnClickListener, OnKeyListen mSearchText.setAdapter(mSuggestionsAdapter); } + /** + * Remove internal cursor references when detaching from window which + * prevents {@link Context} leaks. + */ + @Override + public void onDetachedFromWindow() { + if (mSuggestionsAdapter != null) { + mSuggestionsAdapter.changeCursor(null); + mSuggestionsAdapter = null; + } + } + /** * Implements OnItemClickListener */ diff --git a/src/com/android/launcher/SearchAutoCompleteTextView.java b/src/com/android/launcher/SearchAutoCompleteTextView.java new file mode 100644 index 0000000000..18a464a5c8 --- /dev/null +++ b/src/com/android/launcher/SearchAutoCompleteTextView.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher; + +import android.widget.AutoCompleteTextView; +import android.content.Context; +import android.util.AttributeSet; +import android.graphics.Rect; +import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; +import android.app.Activity; + +/** + * This class is not for the faint of heart. Home works in the pan & scan + * soft input mode. However, this mode gets rid of the soft keyboard on rotation, + * which is a probelm when the Search widget has focus. This special class + * changes Home's soft input method temporarily as long as the Search widget holds + * the focus. This way, the soft keyboard remains after rotation. + */ +public class SearchAutoCompleteTextView extends AutoCompleteTextView { + public SearchAutoCompleteTextView(Context context) { + super(context); + } + + public SearchAutoCompleteTextView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public SearchAutoCompleteTextView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { + super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); + + final WindowManager.LayoutParams lp = ((Activity) getContext()).getWindow().getAttributes(); + if (gainFocus) { + lp.softInputMode = + (lp.softInputMode & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) | + WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED; + } else { + //noinspection PointlessBitwiseExpression + lp.softInputMode = + (lp.softInputMode & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) | + WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED; + + // Hide the soft keyboard when the search widget loses the focus + InputMethodManager.peekInstance().hideSoftInputFromWindow(getWindowToken(), 0); + } + + final WindowManager manager = (WindowManager) + getContext().getSystemService(Context.WINDOW_SERVICE); + manager.updateViewLayout(getRootView(), lp); + } +} diff --git a/src/com/android/launcher/Workspace.java b/src/com/android/launcher/Workspace.java index 6d8eef5f1a..d93fe1436e 100644 --- a/src/com/android/launcher/Workspace.java +++ b/src/com/android/launcher/Workspace.java @@ -17,6 +17,8 @@ package com.android.launcher; import android.content.Context; +import android.content.Intent; +import android.content.ComponentName; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -91,6 +93,9 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag private int mTouchSlop; + final Rect mDrawerBounds = new Rect(); + final Rect mClipBounds = new Rect(); + /** * Used to inflate the Workspace from XML. * @@ -437,6 +442,18 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag @Override protected void dispatchDraw(Canvas canvas) { + // If the all apps drawer is open and the drawing region for the workspace + // is contained within the drawer's bounds, we skip the drawing. This requires + // the drawer to be fully opaque. + if (mLauncher.isDrawerUp()) { + final Rect clipBounds = mClipBounds; + canvas.getClipBounds(clipBounds); + clipBounds.offset(-mScrollX, -mScrollY); + if (mDrawerBounds.contains(clipBounds)) { + return; + } + } + float x = mScrollX * mWallpaperOffset; if (x + mWallpaperWidth < mRight - mLeft) { x = mRight - mLeft - mWallpaperWidth; @@ -673,6 +690,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag // Release the drag clearChildrenCache(); mTouchState = TOUCH_STATE_REST; + mAllowLongPress = false; break; } @@ -1152,7 +1170,13 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag Object tag = view.getTag(); if (tag instanceof ApplicationInfo) { ApplicationInfo info = (ApplicationInfo) tag; - if (packageName.equals(info.intent.getComponent().getPackageName())) { + // We need to check for ACTION_MAIN otherwise getComponent() might + // return null for some shortcuts (for instance, for shortcuts to + // web pages.) + final Intent intent = info.intent; + final ComponentName name = intent.getComponent(); + if (Intent.ACTION_MAIN.equals(intent.getAction()) && + name != null && packageName.equals(name.getPackageName())) { model.removeDesktopItem(info); LauncherModel.deleteItemFromDatabase(mLauncher, info); childrenToRemove.add(view);