Home | History | Annotate | Download | only in launcher2
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.launcher2;
     18 
     19 import com.android.common.Search;
     20 
     21 import android.app.Activity;
     22 import android.app.AlertDialog;
     23 import android.app.Dialog;
     24 import android.app.SearchManager;
     25 import android.app.StatusBarManager;
     26 import android.app.WallpaperManager;
     27 import android.content.ActivityNotFoundException;
     28 import android.content.BroadcastReceiver;
     29 import android.content.ComponentName;
     30 import android.content.ContentResolver;
     31 import android.content.Context;
     32 import android.content.DialogInterface;
     33 import android.content.Intent;
     34 import android.content.Intent.ShortcutIconResource;
     35 import android.content.IntentFilter;
     36 import android.content.pm.ActivityInfo;
     37 import android.content.pm.PackageManager;
     38 import android.content.pm.ResolveInfo;
     39 import android.content.res.Configuration;
     40 import android.content.res.Resources;
     41 import android.content.res.TypedArray;
     42 import android.database.ContentObserver;
     43 import android.graphics.Bitmap;
     44 import android.graphics.Rect;
     45 import android.graphics.Canvas;
     46 import android.graphics.drawable.Drawable;
     47 import android.graphics.drawable.ColorDrawable;
     48 import android.net.Uri;
     49 import android.os.Bundle;
     50 import android.os.Handler;
     51 import android.os.Parcelable;
     52 import android.os.SystemClock;
     53 import android.os.SystemProperties;
     54 import android.provider.LiveFolders;
     55 import android.text.Selection;
     56 import android.text.SpannableStringBuilder;
     57 import android.text.TextUtils;
     58 import android.text.method.TextKeyListener;
     59 import android.util.Log;
     60 import android.view.Display;
     61 import android.view.HapticFeedbackConstants;
     62 import android.view.KeyEvent;
     63 import android.view.LayoutInflater;
     64 import android.view.Menu;
     65 import android.view.MenuItem;
     66 import android.view.View;
     67 import android.view.ViewGroup;
     68 import android.view.View.OnLongClickListener;
     69 import android.view.inputmethod.InputMethodManager;
     70 import android.widget.EditText;
     71 import android.widget.TextView;
     72 import android.widget.Toast;
     73 import android.widget.ImageView;
     74 import android.widget.PopupWindow;
     75 import android.widget.LinearLayout;
     76 import android.appwidget.AppWidgetManager;
     77 import android.appwidget.AppWidgetProviderInfo;
     78 
     79 import java.util.ArrayList;
     80 import java.util.List;
     81 import java.util.HashMap;
     82 import java.io.DataOutputStream;
     83 import java.io.FileNotFoundException;
     84 import java.io.IOException;
     85 import java.io.DataInputStream;
     86 
     87 import com.android.launcher.R;
     88 
     89 /**
     90  * Default launcher application.
     91  */
     92 public final class Launcher extends Activity
     93         implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
     94     static final String TAG = "Launcher";
     95     static final boolean LOGD = false;
     96 
     97     static final boolean PROFILE_STARTUP = false;
     98     static final boolean DEBUG_WIDGETS = false;
     99     static final boolean DEBUG_USER_INTERFACE = false;
    100 
    101     private static final int WALLPAPER_SCREENS_SPAN = 2;
    102 
    103     private static final int MENU_GROUP_ADD = 1;
    104     private static final int MENU_GROUP_WALLPAPER = MENU_GROUP_ADD + 1;
    105 
    106     private static final int MENU_ADD = Menu.FIRST + 1;
    107     private static final int MENU_WALLPAPER_SETTINGS = MENU_ADD + 1;
    108     private static final int MENU_SEARCH = MENU_WALLPAPER_SETTINGS + 1;
    109     private static final int MENU_NOTIFICATIONS = MENU_SEARCH + 1;
    110     private static final int MENU_SETTINGS = MENU_NOTIFICATIONS + 1;
    111 
    112     private static final int REQUEST_CREATE_SHORTCUT = 1;
    113     private static final int REQUEST_CREATE_LIVE_FOLDER = 4;
    114     private static final int REQUEST_CREATE_APPWIDGET = 5;
    115     private static final int REQUEST_PICK_APPLICATION = 6;
    116     private static final int REQUEST_PICK_SHORTCUT = 7;
    117     private static final int REQUEST_PICK_LIVE_FOLDER = 8;
    118     private static final int REQUEST_PICK_APPWIDGET = 9;
    119     private static final int REQUEST_PICK_WALLPAPER = 10;
    120 
    121     static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate";
    122 
    123     static final int SCREEN_COUNT = 5;
    124     static final int DEFAULT_SCREEN = 2;
    125     static final int NUMBER_CELLS_X = 4;
    126     static final int NUMBER_CELLS_Y = 4;
    127 
    128     static final int DIALOG_CREATE_SHORTCUT = 1;
    129     static final int DIALOG_RENAME_FOLDER = 2;
    130 
    131     private static final String PREFERENCES = "launcher.preferences";
    132 
    133     // Type: int
    134     private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen";
    135     // Type: boolean
    136     private static final String RUNTIME_STATE_ALL_APPS_FOLDER = "launcher.all_apps_folder";
    137     // Type: long
    138     private static final String RUNTIME_STATE_USER_FOLDERS = "launcher.user_folder";
    139     // Type: int
    140     private static final String RUNTIME_STATE_PENDING_ADD_SCREEN = "launcher.add_screen";
    141     // Type: int
    142     private static final String RUNTIME_STATE_PENDING_ADD_CELL_X = "launcher.add_cellX";
    143     // Type: int
    144     private static final String RUNTIME_STATE_PENDING_ADD_CELL_Y = "launcher.add_cellY";
    145     // Type: int
    146     private static final String RUNTIME_STATE_PENDING_ADD_SPAN_X = "launcher.add_spanX";
    147     // Type: int
    148     private static final String RUNTIME_STATE_PENDING_ADD_SPAN_Y = "launcher.add_spanY";
    149     // Type: int
    150     private static final String RUNTIME_STATE_PENDING_ADD_COUNT_X = "launcher.add_countX";
    151     // Type: int
    152     private static final String RUNTIME_STATE_PENDING_ADD_COUNT_Y = "launcher.add_countY";
    153     // Type: int[]
    154     private static final String RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS = "launcher.add_occupied_cells";
    155     // Type: boolean
    156     private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME = "launcher.rename_folder";
    157     // Type: long
    158     private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id";
    159 
    160     static final int APPWIDGET_HOST_ID = 1024;
    161 
    162     private static final Object sLock = new Object();
    163     private static int sScreen = DEFAULT_SCREEN;
    164 
    165     private final BroadcastReceiver mCloseSystemDialogsReceiver
    166             = new CloseSystemDialogsIntentReceiver();
    167     private final ContentObserver mWidgetObserver = new AppWidgetResetObserver();
    168 
    169     private LayoutInflater mInflater;
    170 
    171     private DragController mDragController;
    172     private Workspace mWorkspace;
    173 
    174     private AppWidgetManager mAppWidgetManager;
    175     private LauncherAppWidgetHost mAppWidgetHost;
    176 
    177     private CellLayout.CellInfo mAddItemCellInfo;
    178     private CellLayout.CellInfo mMenuAddInfo;
    179     private final int[] mCellCoordinates = new int[2];
    180     private FolderInfo mFolderInfo;
    181 
    182     private DeleteZone mDeleteZone;
    183     private HandleView mHandleView;
    184     private AllAppsView mAllAppsGrid;
    185 
    186     private Bundle mSavedState;
    187 
    188     private SpannableStringBuilder mDefaultKeySsb = null;
    189 
    190     private boolean mWorkspaceLoading = true;
    191 
    192     private boolean mPaused = true;
    193     private boolean mRestoring;
    194     private boolean mWaitingForResult;
    195 
    196     private Bundle mSavedInstanceState;
    197 
    198     private LauncherModel mModel;
    199     private IconCache mIconCache;
    200 
    201     private ArrayList<ItemInfo> mDesktopItems = new ArrayList<ItemInfo>();
    202     private static HashMap<Long, FolderInfo> mFolders = new HashMap<Long, FolderInfo>();
    203 
    204     private ImageView mPreviousView;
    205     private ImageView mNextView;
    206 
    207     // Hotseats (quick-launch icons next to AllApps)
    208     private static final int NUM_HOTSEATS = 2;
    209     private String[] mHotseatConfig = null;
    210     private Intent[] mHotseats = null;
    211     private Drawable[] mHotseatIcons = null;
    212     private CharSequence[] mHotseatLabels = null;
    213 
    214     @Override
    215     protected void onCreate(Bundle savedInstanceState) {
    216         super.onCreate(savedInstanceState);
    217 
    218         LauncherApplication app = ((LauncherApplication)getApplication());
    219         mModel = app.setLauncher(this);
    220         mIconCache = app.getIconCache();
    221         mDragController = new DragController(this);
    222         mInflater = getLayoutInflater();
    223 
    224         mAppWidgetManager = AppWidgetManager.getInstance(this);
    225         mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
    226         mAppWidgetHost.startListening();
    227 
    228         if (PROFILE_STARTUP) {
    229             android.os.Debug.startMethodTracing("/sdcard/launcher");
    230         }
    231 
    232         loadHotseats();
    233         checkForLocaleChange();
    234         setWallpaperDimension();
    235 
    236         setContentView(R.layout.launcher);
    237         setupViews();
    238 
    239         registerContentObservers();
    240 
    241         lockAllApps();
    242 
    243         mSavedState = savedInstanceState;
    244         restoreState(mSavedState);
    245 
    246         if (PROFILE_STARTUP) {
    247             android.os.Debug.stopMethodTracing();
    248         }
    249 
    250         if (!mRestoring) {
    251             mModel.startLoader(this, true);
    252         }
    253 
    254         // For handling default keys
    255         mDefaultKeySsb = new SpannableStringBuilder();
    256         Selection.setSelection(mDefaultKeySsb, 0);
    257 
    258         IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
    259         registerReceiver(mCloseSystemDialogsReceiver, filter);
    260     }
    261 
    262     private void checkForLocaleChange() {
    263         final LocaleConfiguration localeConfiguration = new LocaleConfiguration();
    264         readConfiguration(this, localeConfiguration);
    265 
    266         final Configuration configuration = getResources().getConfiguration();
    267 
    268         final String previousLocale = localeConfiguration.locale;
    269         final String locale = configuration.locale.toString();
    270 
    271         final int previousMcc = localeConfiguration.mcc;
    272         final int mcc = configuration.mcc;
    273 
    274         final int previousMnc = localeConfiguration.mnc;
    275         final int mnc = configuration.mnc;
    276 
    277         boolean localeChanged = !locale.equals(previousLocale) || mcc != previousMcc || mnc != previousMnc;
    278 
    279         if (localeChanged) {
    280             localeConfiguration.locale = locale;
    281             localeConfiguration.mcc = mcc;
    282             localeConfiguration.mnc = mnc;
    283 
    284             writeConfiguration(this, localeConfiguration);
    285             mIconCache.flush();
    286 
    287             loadHotseats();
    288         }
    289     }
    290 
    291     private static class LocaleConfiguration {
    292         public String locale;
    293         public int mcc = -1;
    294         public int mnc = -1;
    295     }
    296 
    297     private static void readConfiguration(Context context, LocaleConfiguration configuration) {
    298         DataInputStream in = null;
    299         try {
    300             in = new DataInputStream(context.openFileInput(PREFERENCES));
    301             configuration.locale = in.readUTF();
    302             configuration.mcc = in.readInt();
    303             configuration.mnc = in.readInt();
    304         } catch (FileNotFoundException e) {
    305             // Ignore
    306         } catch (IOException e) {
    307             // Ignore
    308         } finally {
    309             if (in != null) {
    310                 try {
    311                     in.close();
    312                 } catch (IOException e) {
    313                     // Ignore
    314                 }
    315             }
    316         }
    317     }
    318 
    319     private static void writeConfiguration(Context context, LocaleConfiguration configuration) {
    320         DataOutputStream out = null;
    321         try {
    322             out = new DataOutputStream(context.openFileOutput(PREFERENCES, MODE_PRIVATE));
    323             out.writeUTF(configuration.locale);
    324             out.writeInt(configuration.mcc);
    325             out.writeInt(configuration.mnc);
    326             out.flush();
    327         } catch (FileNotFoundException e) {
    328             // Ignore
    329         } catch (IOException e) {
    330             //noinspection ResultOfMethodCallIgnored
    331             context.getFileStreamPath(PREFERENCES).delete();
    332         } finally {
    333             if (out != null) {
    334                 try {
    335                     out.close();
    336                 } catch (IOException e) {
    337                     // Ignore
    338                 }
    339             }
    340         }
    341     }
    342 
    343     static int getScreen() {
    344         synchronized (sLock) {
    345             return sScreen;
    346         }
    347     }
    348 
    349     static void setScreen(int screen) {
    350         synchronized (sLock) {
    351             sScreen = screen;
    352         }
    353     }
    354 
    355     private void setWallpaperDimension() {
    356         WallpaperManager wpm = (WallpaperManager)getSystemService(WALLPAPER_SERVICE);
    357 
    358         Display display = getWindowManager().getDefaultDisplay();
    359         boolean isPortrait = display.getWidth() < display.getHeight();
    360 
    361         final int width = isPortrait ? display.getWidth() : display.getHeight();
    362         final int height = isPortrait ? display.getHeight() : display.getWidth();
    363         wpm.suggestDesiredDimensions(width * WALLPAPER_SCREENS_SPAN, height);
    364     }
    365 
    366     // Note: This doesn't do all the client-id magic that BrowserProvider does
    367     // in Browser. (http://b/2425179)
    368     private Uri getDefaultBrowserUri() {
    369         String url = getString(R.string.default_browser_url);
    370         if (url.indexOf("{CID}") != -1) {
    371             url = url.replace("{CID}", "android-google");
    372         }
    373         return Uri.parse(url);
    374     }
    375 
    376     // Load the Intent templates from arrays.xml to populate the hotseats. For
    377     // each Intent, if it resolves to a single app, use that as the launch
    378     // intent & use that app's label as the contentDescription. Otherwise,
    379     // retain the ResolveActivity so the user can pick an app.
    380     private void loadHotseats() {
    381         if (mHotseatConfig == null) {
    382             mHotseatConfig = getResources().getStringArray(R.array.hotseats);
    383             if (mHotseatConfig.length > 0) {
    384                 mHotseats = new Intent[mHotseatConfig.length];
    385                 mHotseatLabels = new CharSequence[mHotseatConfig.length];
    386                 mHotseatIcons = new Drawable[mHotseatConfig.length];
    387             } else {
    388                 mHotseats = null;
    389                 mHotseatIcons = null;
    390                 mHotseatLabels = null;
    391             }
    392 
    393             TypedArray hotseatIconDrawables = getResources().obtainTypedArray(R.array.hotseat_icons);
    394             for (int i=0; i<mHotseatConfig.length; i++) {
    395                 // load icon for this slot; currently unrelated to the actual activity
    396                 try {
    397                     mHotseatIcons[i] = hotseatIconDrawables.getDrawable(i);
    398                 } catch (ArrayIndexOutOfBoundsException ex) {
    399                     Log.w(TAG, "Missing hotseat_icons array item #" + i);
    400                     mHotseatIcons[i] = null;
    401                 }
    402             }
    403             hotseatIconDrawables.recycle();
    404         }
    405 
    406         PackageManager pm = getPackageManager();
    407         for (int i=0; i<mHotseatConfig.length; i++) {
    408             Intent intent = null;
    409             if (mHotseatConfig[i].equals("*BROWSER*")) {
    410                 // magic value meaning "launch user's default web browser"
    411                 // replace it with a generic web request so we can see if there is indeed a default
    412                 String defaultUri = getString(R.string.default_browser_url);
    413                 intent = new Intent(
    414                         Intent.ACTION_VIEW,
    415                         ((defaultUri != null)
    416                             ? Uri.parse(defaultUri)
    417                             : getDefaultBrowserUri())
    418                     ).addCategory(Intent.CATEGORY_BROWSABLE);
    419                 // note: if the user launches this without a default set, she
    420                 // will always be taken to the default URL above; this is
    421                 // unavoidable as we must specify a valid URL in order for the
    422                 // chooser to appear, and once the user selects something, that
    423                 // URL is unavoidably sent to the chosen app.
    424             } else {
    425                 try {
    426                     intent = Intent.parseUri(mHotseatConfig[i], 0);
    427                 } catch (java.net.URISyntaxException ex) {
    428                     Log.w(TAG, "Invalid hotseat intent: " + mHotseatConfig[i]);
    429                     // bogus; leave intent=null
    430                 }
    431             }
    432 
    433             if (intent == null) {
    434                 mHotseats[i] = null;
    435                 mHotseatLabels[i] = getText(R.string.activity_not_found);
    436                 continue;
    437             }
    438 
    439             if (LOGD) {
    440                 Log.d(TAG, "loadHotseats: hotseat " + i
    441                     + " initial intent=["
    442                     + intent.toUri(Intent.URI_INTENT_SCHEME)
    443                     + "]");
    444             }
    445 
    446             ResolveInfo bestMatch = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
    447             List<ResolveInfo> allMatches = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
    448             if (LOGD) {
    449                 Log.d(TAG, "Best match for intent: " + bestMatch);
    450                 Log.d(TAG, "All matches: ");
    451                 for (ResolveInfo ri : allMatches) {
    452                     Log.d(TAG, "  --> " + ri);
    453                 }
    454             }
    455             // did this resolve to a single app, or the resolver?
    456             if (allMatches.size() == 0 || bestMatch == null) {
    457                 // can't find any activity to handle this. let's leave the
    458                 // intent as-is and let Launcher show a toast when it fails
    459                 // to launch.
    460                 mHotseats[i] = intent;
    461 
    462                 // set accessibility text to "Not installed"
    463                 mHotseatLabels[i] = getText(R.string.activity_not_found);
    464             } else {
    465                 boolean found = false;
    466                 for (ResolveInfo ri : allMatches) {
    467                     if (bestMatch.activityInfo.name.equals(ri.activityInfo.name)
    468                         && bestMatch.activityInfo.applicationInfo.packageName
    469                             .equals(ri.activityInfo.applicationInfo.packageName)) {
    470                         found = true;
    471                         break;
    472                     }
    473                 }
    474 
    475                 if (!found) {
    476                     if (LOGD) Log.d(TAG, "Multiple options, no default yet");
    477                     // the bestMatch is probably the ResolveActivity, meaning the
    478                     // user has not yet selected a default
    479                     // so: we'll keep the original intent for now
    480                     mHotseats[i] = intent;
    481 
    482                     // set the accessibility text to "Select shortcut"
    483                     mHotseatLabels[i] = getText(R.string.title_select_shortcut);
    484                 } else {
    485                     // we have an app!
    486                     // now reconstruct the intent to launch it through the front
    487                     // door
    488                     ComponentName com = new ComponentName(
    489                         bestMatch.activityInfo.applicationInfo.packageName,
    490                         bestMatch.activityInfo.name);
    491                     mHotseats[i] = new Intent(Intent.ACTION_MAIN).setComponent(com);
    492 
    493                     // load the app label for accessibility
    494                     mHotseatLabels[i] = bestMatch.activityInfo.loadLabel(pm);
    495                 }
    496             }
    497 
    498             if (LOGD) {
    499                 Log.d(TAG, "loadHotseats: hotseat " + i
    500                     + " final intent=["
    501                     + ((mHotseats[i] == null)
    502                         ? "null"
    503                         : mHotseats[i].toUri(Intent.URI_INTENT_SCHEME))
    504                     + "] label=[" + mHotseatLabels[i]
    505                     + "]"
    506                     );
    507             }
    508         }
    509     }
    510 
    511     @Override
    512     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    513         mWaitingForResult = false;
    514 
    515         // The pattern used here is that a user PICKs a specific application,
    516         // which, depending on the target, might need to CREATE the actual target.
    517 
    518         // For example, the user would PICK_SHORTCUT for "Music playlist", and we
    519         // launch over to the Music app to actually CREATE_SHORTCUT.
    520 
    521         if (resultCode == RESULT_OK && mAddItemCellInfo != null) {
    522             switch (requestCode) {
    523                 case REQUEST_PICK_APPLICATION:
    524                     completeAddApplication(this, data, mAddItemCellInfo);
    525                     break;
    526                 case REQUEST_PICK_SHORTCUT:
    527                     processShortcut(data);
    528                     break;
    529                 case REQUEST_CREATE_SHORTCUT:
    530                     completeAddShortcut(data, mAddItemCellInfo);
    531                     break;
    532                 case REQUEST_PICK_LIVE_FOLDER:
    533                     addLiveFolder(data);
    534                     break;
    535                 case REQUEST_CREATE_LIVE_FOLDER:
    536                     completeAddLiveFolder(data, mAddItemCellInfo);
    537                     break;
    538                 case REQUEST_PICK_APPWIDGET:
    539                     addAppWidget(data);
    540                     break;
    541                 case REQUEST_CREATE_APPWIDGET:
    542                     completeAddAppWidget(data, mAddItemCellInfo);
    543                     break;
    544                 case REQUEST_PICK_WALLPAPER:
    545                     // We just wanted the activity result here so we can clear mWaitingForResult
    546                     break;
    547             }
    548         } else if ((requestCode == REQUEST_PICK_APPWIDGET ||
    549                 requestCode == REQUEST_CREATE_APPWIDGET) && resultCode == RESULT_CANCELED &&
    550                 data != null) {
    551             // Clean up the appWidgetId if we canceled
    552             int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
    553             if (appWidgetId != -1) {
    554                 mAppWidgetHost.deleteAppWidgetId(appWidgetId);
    555             }
    556         }
    557     }
    558 
    559     @Override
    560     protected void onResume() {
    561         super.onResume();
    562 
    563         mPaused = false;
    564 
    565         if (mRestoring) {
    566             mWorkspaceLoading = true;
    567             mModel.startLoader(this, true);
    568             mRestoring = false;
    569         }
    570     }
    571 
    572     @Override
    573     protected void onPause() {
    574         super.onPause();
    575         dismissPreview(mPreviousView);
    576         dismissPreview(mNextView);
    577         mDragController.cancelDrag();
    578     }
    579 
    580     @Override
    581     public Object onRetainNonConfigurationInstance() {
    582         // Flag the loader to stop early before switching
    583         mModel.stopLoader();
    584         mAllAppsGrid.surrender();
    585         return Boolean.TRUE;
    586     }
    587 
    588     // We can't hide the IME if it was forced open.  So don't bother
    589     /*
    590     @Override
    591     public void onWindowFocusChanged(boolean hasFocus) {
    592         super.onWindowFocusChanged(hasFocus);
    593 
    594         if (hasFocus) {
    595             final InputMethodManager inputManager = (InputMethodManager)
    596                     getSystemService(Context.INPUT_METHOD_SERVICE);
    597             WindowManager.LayoutParams lp = getWindow().getAttributes();
    598             inputManager.hideSoftInputFromWindow(lp.token, 0, new android.os.ResultReceiver(new
    599                         android.os.Handler()) {
    600                         protected void onReceiveResult(int resultCode, Bundle resultData) {
    601                             Log.d(TAG, "ResultReceiver got resultCode=" + resultCode);
    602                         }
    603                     });
    604             Log.d(TAG, "called hideSoftInputFromWindow from onWindowFocusChanged");
    605         }
    606     }
    607     */
    608 
    609     private boolean acceptFilter() {
    610         final InputMethodManager inputManager = (InputMethodManager)
    611                 getSystemService(Context.INPUT_METHOD_SERVICE);
    612         return !inputManager.isFullscreenMode();
    613     }
    614 
    615     @Override
    616     public boolean onKeyDown(int keyCode, KeyEvent event) {
    617         boolean handled = super.onKeyDown(keyCode, event);
    618         if (!handled && acceptFilter() && keyCode != KeyEvent.KEYCODE_ENTER) {
    619             boolean gotKey = TextKeyListener.getInstance().onKeyDown(mWorkspace, mDefaultKeySsb,
    620                     keyCode, event);
    621             if (gotKey && mDefaultKeySsb != null && mDefaultKeySsb.length() > 0) {
    622                 // something usable has been typed - start a search
    623                 // the typed text will be retrieved and cleared by
    624                 // showSearchDialog()
    625                 // If there are multiple keystrokes before the search dialog takes focus,
    626                 // onSearchRequested() will be called for every keystroke,
    627                 // but it is idempotent, so it's fine.
    628                 return onSearchRequested();
    629             }
    630         }
    631 
    632         // Eat the long press event so the keyboard doesn't come up.
    633         if (keyCode == KeyEvent.KEYCODE_MENU && event.isLongPress()) {
    634             return true;
    635         }
    636 
    637         return handled;
    638     }
    639 
    640     private String getTypedText() {
    641         return mDefaultKeySsb.toString();
    642     }
    643 
    644     private void clearTypedText() {
    645         mDefaultKeySsb.clear();
    646         mDefaultKeySsb.clearSpans();
    647         Selection.setSelection(mDefaultKeySsb, 0);
    648     }
    649 
    650     /**
    651      * Restores the previous state, if it exists.
    652      *
    653      * @param savedState The previous state.
    654      */
    655     private void restoreState(Bundle savedState) {
    656         if (savedState == null) {
    657             return;
    658         }
    659 
    660         final boolean allApps = savedState.getBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, false);
    661         if (allApps) {
    662             showAllApps(false);
    663         }
    664 
    665         final int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN, -1);
    666         if (currentScreen > -1) {
    667             mWorkspace.setCurrentScreen(currentScreen);
    668         }
    669 
    670         final int addScreen = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SCREEN, -1);
    671         if (addScreen > -1) {
    672             mAddItemCellInfo = new CellLayout.CellInfo();
    673             final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo;
    674             addItemCellInfo.valid = true;
    675             addItemCellInfo.screen = addScreen;
    676             addItemCellInfo.cellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X);
    677             addItemCellInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y);
    678             addItemCellInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X);
    679             addItemCellInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y);
    680             addItemCellInfo.findVacantCellsFromOccupied(
    681                     savedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS),
    682                     savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_X),
    683                     savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y));
    684             mRestoring = true;
    685         }
    686 
    687         boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false);
    688         if (renameFolder) {
    689             long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID);
    690             mFolderInfo = mModel.getFolderById(this, mFolders, id);
    691             mRestoring = true;
    692         }
    693     }
    694 
    695     /**
    696      * Finds all the views we need and configure them properly.
    697      */
    698     private void setupViews() {
    699         DragController dragController = mDragController;
    700 
    701         DragLayer dragLayer = (DragLayer) findViewById(R.id.drag_layer);
    702         dragLayer.setDragController(dragController);
    703 
    704         mAllAppsGrid = (AllAppsView)dragLayer.findViewById(R.id.all_apps_view);
    705         mAllAppsGrid.setLauncher(this);
    706         mAllAppsGrid.setDragController(dragController);
    707         ((View) mAllAppsGrid).setWillNotDraw(false); // We don't want a hole punched in our window.
    708         // Manage focusability manually since this thing is always visible
    709         ((View) mAllAppsGrid).setFocusable(false);
    710 
    711         mWorkspace = (Workspace) dragLayer.findViewById(R.id.workspace);
    712         final Workspace workspace = mWorkspace;
    713         workspace.setHapticFeedbackEnabled(false);
    714 
    715         DeleteZone deleteZone = (DeleteZone) dragLayer.findViewById(R.id.delete_zone);
    716         mDeleteZone = deleteZone;
    717 
    718         mHandleView = (HandleView) findViewById(R.id.all_apps_button);
    719         mHandleView.setLauncher(this);
    720         mHandleView.setOnClickListener(this);
    721         mHandleView.setOnLongClickListener(this);
    722 
    723         ImageView hotseatLeft = (ImageView) findViewById(R.id.hotseat_left);
    724         hotseatLeft.setContentDescription(mHotseatLabels[0]);
    725         hotseatLeft.setImageDrawable(mHotseatIcons[0]);
    726         ImageView hotseatRight = (ImageView) findViewById(R.id.hotseat_right);
    727         hotseatRight.setContentDescription(mHotseatLabels[1]);
    728         hotseatRight.setImageDrawable(mHotseatIcons[1]);
    729 
    730         mPreviousView = (ImageView) dragLayer.findViewById(R.id.previous_screen);
    731         mNextView = (ImageView) dragLayer.findViewById(R.id.next_screen);
    732 
    733         Drawable previous = mPreviousView.getDrawable();
    734         Drawable next = mNextView.getDrawable();
    735         mWorkspace.setIndicators(previous, next);
    736 
    737         mPreviousView.setHapticFeedbackEnabled(false);
    738         mPreviousView.setOnLongClickListener(this);
    739         mNextView.setHapticFeedbackEnabled(false);
    740         mNextView.setOnLongClickListener(this);
    741 
    742         workspace.setOnLongClickListener(this);
    743         workspace.setDragController(dragController);
    744         workspace.setLauncher(this);
    745 
    746         deleteZone.setLauncher(this);
    747         deleteZone.setDragController(dragController);
    748         deleteZone.setHandle(findViewById(R.id.all_apps_button_cluster));
    749 
    750         dragController.setDragScoller(workspace);
    751         dragController.setDragListener(deleteZone);
    752         dragController.setScrollView(dragLayer);
    753         dragController.setMoveTarget(workspace);
    754 
    755         // The order here is bottom to top.
    756         dragController.addDropTarget(workspace);
    757         dragController.addDropTarget(deleteZone);
    758     }
    759 
    760     @SuppressWarnings({"UnusedDeclaration"})
    761     public void previousScreen(View v) {
    762         if (!isAllAppsVisible()) {
    763             mWorkspace.scrollLeft();
    764         }
    765     }
    766 
    767     @SuppressWarnings({"UnusedDeclaration"})
    768     public void nextScreen(View v) {
    769         if (!isAllAppsVisible()) {
    770             mWorkspace.scrollRight();
    771         }
    772     }
    773 
    774     @SuppressWarnings({"UnusedDeclaration"})
    775     public void launchHotSeat(View v) {
    776         if (isAllAppsVisible()) return;
    777 
    778         int index = -1;
    779         if (v.getId() == R.id.hotseat_left) {
    780             index = 0;
    781         } else if (v.getId() == R.id.hotseat_right) {
    782             index = 1;
    783         }
    784 
    785         // reload these every tap; you never know when they might change
    786         loadHotseats();
    787         if (index >= 0 && index < mHotseats.length && mHotseats[index] != null) {
    788             Intent intent = mHotseats[index];
    789             startActivitySafely(
    790                 mHotseats[index],
    791                 "hotseat"
    792             );
    793         }
    794     }
    795 
    796     /**
    797      * Creates a view representing a shortcut.
    798      *
    799      * @param info The data structure describing the shortcut.
    800      *
    801      * @return A View inflated from R.layout.application.
    802      */
    803     View createShortcut(ShortcutInfo info) {
    804         return createShortcut(R.layout.application,
    805                 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info);
    806     }
    807 
    808     /**
    809      * Creates a view representing a shortcut inflated from the specified resource.
    810      *
    811      * @param layoutResId The id of the XML layout used to create the shortcut.
    812      * @param parent The group the shortcut belongs to.
    813      * @param info The data structure describing the shortcut.
    814      *
    815      * @return A View inflated from layoutResId.
    816      */
    817     View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) {
    818         TextView favorite = (TextView) mInflater.inflate(layoutResId, parent, false);
    819 
    820         favorite.setCompoundDrawablesWithIntrinsicBounds(null,
    821                 new FastBitmapDrawable(info.getIcon(mIconCache)),
    822                 null, null);
    823         favorite.setText(info.title);
    824         favorite.setTag(info);
    825         favorite.setOnClickListener(this);
    826 
    827         return favorite;
    828     }
    829 
    830     /**
    831      * Add an application shortcut to the workspace.
    832      *
    833      * @param data The intent describing the application.
    834      * @param cellInfo The position on screen where to create the shortcut.
    835      */
    836     void completeAddApplication(Context context, Intent data, CellLayout.CellInfo cellInfo) {
    837         cellInfo.screen = mWorkspace.getCurrentScreen();
    838         if (!findSingleSlot(cellInfo)) return;
    839 
    840         final ShortcutInfo info = mModel.getShortcutInfo(context.getPackageManager(),
    841                 data, context);
    842 
    843         if (info != null) {
    844             info.setActivity(data.getComponent(), Intent.FLAG_ACTIVITY_NEW_TASK |
    845                     Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
    846             info.container = ItemInfo.NO_ID;
    847             mWorkspace.addApplicationShortcut(info, cellInfo, isWorkspaceLocked());
    848         } else {
    849             Log.e(TAG, "Couldn't find ActivityInfo for selected application: " + data);
    850         }
    851     }
    852 
    853     /**
    854      * Add a shortcut to the workspace.
    855      *
    856      * @param data The intent describing the shortcut.
    857      * @param cellInfo The position on screen where to create the shortcut.
    858      */
    859     private void completeAddShortcut(Intent data, CellLayout.CellInfo cellInfo) {
    860         cellInfo.screen = mWorkspace.getCurrentScreen();
    861         if (!findSingleSlot(cellInfo)) return;
    862 
    863         final ShortcutInfo info = mModel.addShortcut(this, data, cellInfo, false);
    864 
    865         if (!mRestoring) {
    866             final View view = createShortcut(info);
    867             mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1,
    868                     isWorkspaceLocked());
    869         }
    870     }
    871 
    872 
    873     /**
    874      * Add a widget to the workspace.
    875      *
    876      * @param data The intent describing the appWidgetId.
    877      * @param cellInfo The position on screen where to create the widget.
    878      */
    879     private void completeAddAppWidget(Intent data, CellLayout.CellInfo cellInfo) {
    880         Bundle extras = data.getExtras();
    881         int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
    882 
    883         if (LOGD) Log.d(TAG, "dumping extras content=" + extras.toString());
    884 
    885         AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
    886 
    887         // Calculate the grid spans needed to fit this widget
    888         CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen);
    889         int[] spans = layout.rectToCell(appWidgetInfo.minWidth, appWidgetInfo.minHeight);
    890 
    891         // Try finding open space on Launcher screen
    892         final int[] xy = mCellCoordinates;
    893         if (!findSlot(cellInfo, xy, spans[0], spans[1])) {
    894             if (appWidgetId != -1) mAppWidgetHost.deleteAppWidgetId(appWidgetId);
    895             return;
    896         }
    897 
    898         // Build Launcher-specific widget info and save to database
    899         LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId);
    900         launcherInfo.spanX = spans[0];
    901         launcherInfo.spanY = spans[1];
    902 
    903         LauncherModel.addItemToDatabase(this, launcherInfo,
    904                 LauncherSettings.Favorites.CONTAINER_DESKTOP,
    905                 mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
    906 
    907         if (!mRestoring) {
    908             mDesktopItems.add(launcherInfo);
    909 
    910             // Perform actual inflation because we're live
    911             launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
    912 
    913             launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
    914             launcherInfo.hostView.setTag(launcherInfo);
    915 
    916             mWorkspace.addInCurrentScreen(launcherInfo.hostView, xy[0], xy[1],
    917                     launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
    918         }
    919     }
    920 
    921     public void removeAppWidget(LauncherAppWidgetInfo launcherInfo) {
    922         mDesktopItems.remove(launcherInfo);
    923         launcherInfo.hostView = null;
    924     }
    925 
    926     public LauncherAppWidgetHost getAppWidgetHost() {
    927         return mAppWidgetHost;
    928     }
    929 
    930     void closeSystemDialogs() {
    931         getWindow().closeAllPanels();
    932 
    933         try {
    934             dismissDialog(DIALOG_CREATE_SHORTCUT);
    935             // Unlock the workspace if the dialog was showing
    936         } catch (Exception e) {
    937             // An exception is thrown if the dialog is not visible, which is fine
    938         }
    939 
    940         try {
    941             dismissDialog(DIALOG_RENAME_FOLDER);
    942             // Unlock the workspace if the dialog was showing
    943         } catch (Exception e) {
    944             // An exception is thrown if the dialog is not visible, which is fine
    945         }
    946 
    947         // Whatever we were doing is hereby canceled.
    948         mWaitingForResult = false;
    949     }
    950 
    951     @Override
    952     protected void onNewIntent(Intent intent) {
    953         super.onNewIntent(intent);
    954 
    955         // Close the menu
    956         if (Intent.ACTION_MAIN.equals(intent.getAction())) {
    957             // also will cancel mWaitingForResult.
    958             closeSystemDialogs();
    959 
    960             boolean alreadyOnHome = ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
    961                         != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
    962             boolean allAppsVisible = isAllAppsVisible();
    963             if (!mWorkspace.isDefaultScreenShowing()) {
    964                 mWorkspace.moveToDefaultScreen(alreadyOnHome && !allAppsVisible);
    965             }
    966             closeAllApps(alreadyOnHome && allAppsVisible);
    967 
    968             final View v = getWindow().peekDecorView();
    969             if (v != null && v.getWindowToken() != null) {
    970                 InputMethodManager imm = (InputMethodManager)getSystemService(
    971                         INPUT_METHOD_SERVICE);
    972                 imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
    973             }
    974         }
    975     }
    976 
    977     @Override
    978     protected void onRestoreInstanceState(Bundle savedInstanceState) {
    979         // Do not call super here
    980         mSavedInstanceState = savedInstanceState;
    981     }
    982 
    983     @Override
    984     protected void onSaveInstanceState(Bundle outState) {
    985         outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getCurrentScreen());
    986 
    987         final ArrayList<Folder> folders = mWorkspace.getOpenFolders();
    988         if (folders.size() > 0) {
    989             final int count = folders.size();
    990             long[] ids = new long[count];
    991             for (int i = 0; i < count; i++) {
    992                 final FolderInfo info = folders.get(i).getInfo();
    993                 ids[i] = info.id;
    994             }
    995             outState.putLongArray(RUNTIME_STATE_USER_FOLDERS, ids);
    996         } else {
    997             super.onSaveInstanceState(outState);
    998         }
    999 
   1000         // TODO should not do this if the drawer is currently closing.
   1001         if (isAllAppsVisible()) {
   1002             outState.putBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, true);
   1003         }
   1004 
   1005         if (mAddItemCellInfo != null && mAddItemCellInfo.valid && mWaitingForResult) {
   1006             final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo;
   1007             final CellLayout layout = (CellLayout) mWorkspace.getChildAt(addItemCellInfo.screen);
   1008 
   1009             outState.putInt(RUNTIME_STATE_PENDING_ADD_SCREEN, addItemCellInfo.screen);
   1010             outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, addItemCellInfo.cellX);
   1011             outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, addItemCellInfo.cellY);
   1012             outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_X, addItemCellInfo.spanX);
   1013             outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y, addItemCellInfo.spanY);
   1014             outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_X, layout.getCountX());
   1015             outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y, layout.getCountY());
   1016             outState.putBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS,
   1017                    layout.getOccupiedCells());
   1018         }
   1019 
   1020         if (mFolderInfo != null && mWaitingForResult) {
   1021             outState.putBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, true);
   1022             outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id);
   1023         }
   1024     }
   1025 
   1026     @Override
   1027     public void onDestroy() {
   1028         super.onDestroy();
   1029 
   1030         try {
   1031             mAppWidgetHost.stopListening();
   1032         } catch (NullPointerException ex) {
   1033             Log.w(TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex);
   1034         }
   1035 
   1036         TextKeyListener.getInstance().release();
   1037 
   1038         mModel.stopLoader();
   1039 
   1040         unbindDesktopItems();
   1041 
   1042         getContentResolver().unregisterContentObserver(mWidgetObserver);
   1043 
   1044         dismissPreview(mPreviousView);
   1045         dismissPreview(mNextView);
   1046 
   1047         unregisterReceiver(mCloseSystemDialogsReceiver);
   1048     }
   1049 
   1050     @Override
   1051     public void startActivityForResult(Intent intent, int requestCode) {
   1052         if (requestCode >= 0) mWaitingForResult = true;
   1053         super.startActivityForResult(intent, requestCode);
   1054     }
   1055 
   1056     @Override
   1057     public void startSearch(String initialQuery, boolean selectInitialQuery,
   1058             Bundle appSearchData, boolean globalSearch) {
   1059 
   1060         closeAllApps(true);
   1061 
   1062         if (initialQuery == null) {
   1063             // Use any text typed in the launcher as the initial query
   1064             initialQuery = getTypedText();
   1065             clearTypedText();
   1066         }
   1067         if (appSearchData == null) {
   1068             appSearchData = new Bundle();
   1069             appSearchData.putString(Search.SOURCE, "launcher-search");
   1070         }
   1071 
   1072         final SearchManager searchManager =
   1073                 (SearchManager) getSystemService(Context.SEARCH_SERVICE);
   1074         searchManager.startSearch(initialQuery, selectInitialQuery, getComponentName(),
   1075             appSearchData, globalSearch);
   1076     }
   1077 
   1078     @Override
   1079     public boolean onCreateOptionsMenu(Menu menu) {
   1080         if (isWorkspaceLocked()) {
   1081             return false;
   1082         }
   1083 
   1084         super.onCreateOptionsMenu(menu);
   1085 
   1086         menu.add(MENU_GROUP_ADD, MENU_ADD, 0, R.string.menu_add)
   1087                 .setIcon(android.R.drawable.ic_menu_add)
   1088                 .setAlphabeticShortcut('A');
   1089         menu.add(MENU_GROUP_WALLPAPER, MENU_WALLPAPER_SETTINGS, 0, R.string.menu_wallpaper)
   1090                  .setIcon(android.R.drawable.ic_menu_gallery)
   1091                  .setAlphabeticShortcut('W');
   1092         menu.add(0, MENU_SEARCH, 0, R.string.menu_search)
   1093                 .setIcon(android.R.drawable.ic_search_category_default)
   1094                 .setAlphabeticShortcut(SearchManager.MENU_KEY);
   1095         menu.add(0, MENU_NOTIFICATIONS, 0, R.string.menu_notifications)
   1096                 .setIcon(com.android.internal.R.drawable.ic_menu_notifications)
   1097                 .setAlphabeticShortcut('N');
   1098 
   1099         final Intent settings = new Intent(android.provider.Settings.ACTION_SETTINGS);
   1100         settings.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
   1101                 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
   1102 
   1103         menu.add(0, MENU_SETTINGS, 0, R.string.menu_settings)
   1104                 .setIcon(android.R.drawable.ic_menu_preferences).setAlphabeticShortcut('P')
   1105                 .setIntent(settings);
   1106 
   1107         return true;
   1108     }
   1109 
   1110     @Override
   1111     public boolean onPrepareOptionsMenu(Menu menu) {
   1112         super.onPrepareOptionsMenu(menu);
   1113 
   1114         // If all apps is animating, don't show the menu, because we don't know
   1115         // which one to show.
   1116         if (mAllAppsGrid.isVisible() && !mAllAppsGrid.isOpaque()) {
   1117             return false;
   1118         }
   1119 
   1120         // Only show the add and wallpaper options when we're not in all apps.
   1121         boolean visible = !mAllAppsGrid.isOpaque();
   1122         menu.setGroupVisible(MENU_GROUP_ADD, visible);
   1123         menu.setGroupVisible(MENU_GROUP_WALLPAPER, visible);
   1124 
   1125         // Disable add if the workspace is full.
   1126         if (visible) {
   1127             mMenuAddInfo = mWorkspace.findAllVacantCells(null);
   1128             menu.setGroupEnabled(MENU_GROUP_ADD, mMenuAddInfo != null && mMenuAddInfo.valid);
   1129         }
   1130 
   1131         return true;
   1132     }
   1133 
   1134     @Override
   1135     public boolean onOptionsItemSelected(MenuItem item) {
   1136         switch (item.getItemId()) {
   1137             case MENU_ADD:
   1138                 addItems();
   1139                 return true;
   1140             case MENU_WALLPAPER_SETTINGS:
   1141                 startWallpaper();
   1142                 return true;
   1143             case MENU_SEARCH:
   1144                 onSearchRequested();
   1145                 return true;
   1146             case MENU_NOTIFICATIONS:
   1147                 showNotifications();
   1148                 return true;
   1149         }
   1150 
   1151         return super.onOptionsItemSelected(item);
   1152     }
   1153 
   1154     /**
   1155      * Indicates that we want global search for this activity by setting the globalSearch
   1156      * argument for {@link #startSearch} to true.
   1157      */
   1158 
   1159     @Override
   1160     public boolean onSearchRequested() {
   1161         startSearch(null, false, null, true);
   1162         return true;
   1163     }
   1164 
   1165     public boolean isWorkspaceLocked() {
   1166         return mWorkspaceLoading || mWaitingForResult;
   1167     }
   1168 
   1169     private void addItems() {
   1170         closeAllApps(true);
   1171         showAddDialog(mMenuAddInfo);
   1172     }
   1173 
   1174     void addAppWidget(Intent data) {
   1175         // TODO: catch bad widget exception when sent
   1176         int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
   1177         AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
   1178 
   1179         if (appWidget.configure != null) {
   1180             // Launch over to configure widget, if needed
   1181             Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
   1182             intent.setComponent(appWidget.configure);
   1183             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
   1184 
   1185             startActivityForResultSafely(intent, REQUEST_CREATE_APPWIDGET);
   1186         } else {
   1187             // Otherwise just add it
   1188             onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data);
   1189         }
   1190     }
   1191 
   1192     void processShortcut(Intent intent) {
   1193         // Handle case where user selected "Applications"
   1194         String applicationName = getResources().getString(R.string.group_applications);
   1195         String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
   1196 
   1197         if (applicationName != null && applicationName.equals(shortcutName)) {
   1198             Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
   1199             mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
   1200 
   1201             Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
   1202             pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent);
   1203             startActivityForResult(pickIntent, REQUEST_PICK_APPLICATION);
   1204         } else {
   1205             startActivityForResult(intent, REQUEST_CREATE_SHORTCUT);
   1206         }
   1207     }
   1208 
   1209     void addLiveFolder(Intent intent) {
   1210         // Handle case where user selected "Folder"
   1211         String folderName = getResources().getString(R.string.group_folder);
   1212         String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
   1213 
   1214         if (folderName != null && folderName.equals(shortcutName)) {
   1215             addFolder();
   1216         } else {
   1217             startActivityForResult(intent, REQUEST_CREATE_LIVE_FOLDER);
   1218         }
   1219     }
   1220 
   1221     void addFolder() {
   1222         UserFolderInfo folderInfo = new UserFolderInfo();
   1223         folderInfo.title = getText(R.string.folder_name);
   1224 
   1225         CellLayout.CellInfo cellInfo = mAddItemCellInfo;
   1226         cellInfo.screen = mWorkspace.getCurrentScreen();
   1227         if (!findSingleSlot(cellInfo)) return;
   1228 
   1229         // Update the model
   1230         LauncherModel.addItemToDatabase(this, folderInfo,
   1231                 LauncherSettings.Favorites.CONTAINER_DESKTOP,
   1232                 mWorkspace.getCurrentScreen(), cellInfo.cellX, cellInfo.cellY, false);
   1233         mFolders.put(folderInfo.id, folderInfo);
   1234 
   1235         // Create the view
   1236         FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
   1237                 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), folderInfo);
   1238         mWorkspace.addInCurrentScreen(newFolder,
   1239                 cellInfo.cellX, cellInfo.cellY, 1, 1, isWorkspaceLocked());
   1240     }
   1241 
   1242     void removeFolder(FolderInfo folder) {
   1243         mFolders.remove(folder.id);
   1244     }
   1245 
   1246     private void completeAddLiveFolder(Intent data, CellLayout.CellInfo cellInfo) {
   1247         cellInfo.screen = mWorkspace.getCurrentScreen();
   1248         if (!findSingleSlot(cellInfo)) return;
   1249 
   1250         final LiveFolderInfo info = addLiveFolder(this, data, cellInfo, false);
   1251 
   1252         if (!mRestoring) {
   1253             final View view = LiveFolderIcon.fromXml(R.layout.live_folder_icon, this,
   1254                 (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info);
   1255             mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1,
   1256                     isWorkspaceLocked());
   1257         }
   1258     }
   1259 
   1260     static LiveFolderInfo addLiveFolder(Context context, Intent data,
   1261             CellLayout.CellInfo cellInfo, boolean notify) {
   1262 
   1263         Intent baseIntent = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_BASE_INTENT);
   1264         String name = data.getStringExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME);
   1265 
   1266         Drawable icon = null;
   1267         Intent.ShortcutIconResource iconResource = null;
   1268 
   1269         Parcelable extra = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON);
   1270         if (extra != null && extra instanceof Intent.ShortcutIconResource) {
   1271             try {
   1272                 iconResource = (Intent.ShortcutIconResource) extra;
   1273                 final PackageManager packageManager = context.getPackageManager();
   1274                 Resources resources = packageManager.getResourcesForApplication(
   1275                         iconResource.packageName);
   1276                 final int id = resources.getIdentifier(iconResource.resourceName, null, null);
   1277                 icon = resources.getDrawable(id);
   1278             } catch (Exception e) {
   1279                 Log.w(TAG, "Could not load live folder icon: " + extra);
   1280             }
   1281         }
   1282 
   1283         if (icon == null) {
   1284             icon = context.getResources().getDrawable(R.drawable.ic_launcher_folder);
   1285         }
   1286 
   1287         final LiveFolderInfo info = new LiveFolderInfo();
   1288         info.icon = Utilities.createIconBitmap(icon, context);
   1289         info.title = name;
   1290         info.iconResource = iconResource;
   1291         info.uri = data.getData();
   1292         info.baseIntent = baseIntent;
   1293         info.displayMode = data.getIntExtra(LiveFolders.EXTRA_LIVE_FOLDER_DISPLAY_MODE,
   1294                 LiveFolders.DISPLAY_MODE_GRID);
   1295 
   1296         LauncherModel.addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
   1297                 cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
   1298         mFolders.put(info.id, info);
   1299 
   1300         return info;
   1301     }
   1302 
   1303     private boolean findSingleSlot(CellLayout.CellInfo cellInfo) {
   1304         final int[] xy = new int[2];
   1305         if (findSlot(cellInfo, xy, 1, 1)) {
   1306             cellInfo.cellX = xy[0];
   1307             cellInfo.cellY = xy[1];
   1308             return true;
   1309         }
   1310         return false;
   1311     }
   1312 
   1313     private boolean findSlot(CellLayout.CellInfo cellInfo, int[] xy, int spanX, int spanY) {
   1314         if (!cellInfo.findCellForSpan(xy, spanX, spanY)) {
   1315             boolean[] occupied = mSavedState != null ?
   1316                     mSavedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS) : null;
   1317             cellInfo = mWorkspace.findAllVacantCells(occupied);
   1318             if (!cellInfo.findCellForSpan(xy, spanX, spanY)) {
   1319                 Toast.makeText(this, getString(R.string.out_of_space), Toast.LENGTH_SHORT).show();
   1320                 return false;
   1321             }
   1322         }
   1323         return true;
   1324     }
   1325 
   1326     private void showNotifications() {
   1327         final StatusBarManager statusBar = (StatusBarManager) getSystemService(STATUS_BAR_SERVICE);
   1328         if (statusBar != null) {
   1329             statusBar.expand();
   1330         }
   1331     }
   1332 
   1333     private void startWallpaper() {
   1334         closeAllApps(true);
   1335         final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
   1336         Intent chooser = Intent.createChooser(pickWallpaper,
   1337                 getText(R.string.chooser_wallpaper));
   1338         // NOTE: Adds a configure option to the chooser if the wallpaper supports it
   1339         //       Removed in Eclair MR1
   1340 //        WallpaperManager wm = (WallpaperManager)
   1341 //                getSystemService(Context.WALLPAPER_SERVICE);
   1342 //        WallpaperInfo wi = wm.getWallpaperInfo();
   1343 //        if (wi != null && wi.getSettingsActivity() != null) {
   1344 //            LabeledIntent li = new LabeledIntent(getPackageName(),
   1345 //                    R.string.configure_wallpaper, 0);
   1346 //            li.setClassName(wi.getPackageName(), wi.getSettingsActivity());
   1347 //            chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { li });
   1348 //        }
   1349         startActivityForResult(chooser, REQUEST_PICK_WALLPAPER);
   1350     }
   1351 
   1352     /**
   1353      * Registers various content observers. The current implementation registers
   1354      * only a favorites observer to keep track of the favorites applications.
   1355      */
   1356     private void registerContentObservers() {
   1357         ContentResolver resolver = getContentResolver();
   1358         resolver.registerContentObserver(LauncherProvider.CONTENT_APPWIDGET_RESET_URI,
   1359                 true, mWidgetObserver);
   1360     }
   1361 
   1362     @Override
   1363     public boolean dispatchKeyEvent(KeyEvent event) {
   1364         if (event.getAction() == KeyEvent.ACTION_DOWN) {
   1365             switch (event.getKeyCode()) {
   1366                 case KeyEvent.KEYCODE_HOME:
   1367                     return true;
   1368                 case KeyEvent.KEYCODE_VOLUME_DOWN:
   1369                     if (SystemProperties.getInt("debug.launcher2.dumpstate", 0) != 0) {
   1370                         dumpState();
   1371                         return true;
   1372                     }
   1373                     break;
   1374             }
   1375         } else if (event.getAction() == KeyEvent.ACTION_UP) {
   1376             switch (event.getKeyCode()) {
   1377                 case KeyEvent.KEYCODE_HOME:
   1378                     return true;
   1379             }
   1380         }
   1381 
   1382         return super.dispatchKeyEvent(event);
   1383     }
   1384 
   1385     @Override
   1386     public void onBackPressed() {
   1387         if (isAllAppsVisible()) {
   1388             closeAllApps(true);
   1389         } else {
   1390             closeFolder();
   1391         }
   1392         dismissPreview(mPreviousView);
   1393         dismissPreview(mNextView);
   1394     }
   1395 
   1396     private void closeFolder() {
   1397         Folder folder = mWorkspace.getOpenFolder();
   1398         if (folder != null) {
   1399             closeFolder(folder);
   1400         }
   1401     }
   1402 
   1403     void closeFolder(Folder folder) {
   1404         folder.getInfo().opened = false;
   1405         ViewGroup parent = (ViewGroup) folder.getParent();
   1406         if (parent != null) {
   1407             parent.removeView(folder);
   1408             if (folder instanceof DropTarget) {
   1409                 // Live folders aren't DropTargets.
   1410                 mDragController.removeDropTarget((DropTarget)folder);
   1411             }
   1412         }
   1413         folder.onClose();
   1414     }
   1415 
   1416     /**
   1417      * Re-listen when widgets are reset.
   1418      */
   1419     private void onAppWidgetReset() {
   1420         mAppWidgetHost.startListening();
   1421     }
   1422 
   1423     /**
   1424      * Go through the and disconnect any of the callbacks in the drawables and the views or we
   1425      * leak the previous Home screen on orientation change.
   1426      */
   1427     private void unbindDesktopItems() {
   1428         for (ItemInfo item: mDesktopItems) {
   1429             item.unbind();
   1430         }
   1431     }
   1432 
   1433     /**
   1434      * Launches the intent referred by the clicked shortcut.
   1435      *
   1436      * @param v The view representing the clicked shortcut.
   1437      */
   1438     public void onClick(View v) {
   1439         Object tag = v.getTag();
   1440         if (tag instanceof ShortcutInfo) {
   1441             // Open shortcut
   1442             final Intent intent = ((ShortcutInfo) tag).intent;
   1443             int[] pos = new int[2];
   1444             v.getLocationOnScreen(pos);
   1445             intent.setSourceBounds(new Rect(pos[0], pos[1],
   1446                     pos[0] + v.getWidth(), pos[1] + v.getHeight()));
   1447             startActivitySafely(intent, tag);
   1448         } else if (tag instanceof FolderInfo) {
   1449             handleFolderClick((FolderInfo) tag);
   1450         } else if (v == mHandleView) {
   1451             if (isAllAppsVisible()) {
   1452                 closeAllApps(true);
   1453             } else {
   1454                 showAllApps(true);
   1455             }
   1456         }
   1457     }
   1458 
   1459     void startActivitySafely(Intent intent, Object tag) {
   1460         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   1461         try {
   1462             startActivity(intent);
   1463         } catch (ActivityNotFoundException e) {
   1464             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   1465             Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
   1466         } catch (SecurityException e) {
   1467             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   1468             Log.e(TAG, "Launcher does not have the permission to launch " + intent +
   1469                     ". Make sure to create a MAIN intent-filter for the corresponding activity " +
   1470                     "or use the exported attribute for this activity. "
   1471                     + "tag="+ tag + " intent=" + intent, e);
   1472         }
   1473     }
   1474 
   1475     void startActivityForResultSafely(Intent intent, int requestCode) {
   1476         try {
   1477             startActivityForResult(intent, requestCode);
   1478         } catch (ActivityNotFoundException e) {
   1479             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   1480         } catch (SecurityException e) {
   1481             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
   1482             Log.e(TAG, "Launcher does not have the permission to launch " + intent +
   1483                     ". Make sure to create a MAIN intent-filter for the corresponding activity " +
   1484                     "or use the exported attribute for this activity.", e);
   1485         }
   1486     }
   1487 
   1488     private void handleFolderClick(FolderInfo folderInfo) {
   1489         if (!folderInfo.opened) {
   1490             // Close any open folder
   1491             closeFolder();
   1492             // Open the requested folder
   1493             openFolder(folderInfo);
   1494         } else {
   1495             // Find the open folder...
   1496             Folder openFolder = mWorkspace.getFolderForTag(folderInfo);
   1497             int folderScreen;
   1498             if (openFolder != null) {
   1499                 folderScreen = mWorkspace.getScreenForView(openFolder);
   1500                 // .. and close it
   1501                 closeFolder(openFolder);
   1502                 if (folderScreen != mWorkspace.getCurrentScreen()) {
   1503                     // Close any folder open on the current screen
   1504                     closeFolder();
   1505                     // Pull the folder onto this screen
   1506                     openFolder(folderInfo);
   1507                 }
   1508             }
   1509         }
   1510     }
   1511 
   1512     /**
   1513      * Opens the user fodler described by the specified tag. The opening of the folder
   1514      * is animated relative to the specified View. If the View is null, no animation
   1515      * is played.
   1516      *
   1517      * @param folderInfo The FolderInfo describing the folder to open.
   1518      */
   1519     private void openFolder(FolderInfo folderInfo) {
   1520         Folder openFolder;
   1521 
   1522         if (folderInfo instanceof UserFolderInfo) {
   1523             openFolder = UserFolder.fromXml(this);
   1524         } else if (folderInfo instanceof LiveFolderInfo) {
   1525             openFolder = com.android.launcher2.LiveFolder.fromXml(this, folderInfo);
   1526         } else {
   1527             return;
   1528         }
   1529 
   1530         openFolder.setDragController(mDragController);
   1531         openFolder.setLauncher(this);
   1532 
   1533         openFolder.bind(folderInfo);
   1534         folderInfo.opened = true;
   1535 
   1536         mWorkspace.addInScreen(openFolder, folderInfo.screen, 0, 0, 4, 4);
   1537         openFolder.onOpen();
   1538     }
   1539 
   1540     public boolean onLongClick(View v) {
   1541         switch (v.getId()) {
   1542             case R.id.previous_screen:
   1543                 if (!isAllAppsVisible()) {
   1544                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
   1545                             HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
   1546                     showPreviews(v);
   1547                 }
   1548                 return true;
   1549             case R.id.next_screen:
   1550                 if (!isAllAppsVisible()) {
   1551                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
   1552                             HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
   1553                     showPreviews(v);
   1554                 }
   1555                 return true;
   1556             case R.id.all_apps_button:
   1557                 if (!isAllAppsVisible()) {
   1558                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
   1559                             HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
   1560                     showPreviews(v);
   1561                 }
   1562                 return true;
   1563         }
   1564 
   1565         if (isWorkspaceLocked()) {
   1566             return false;
   1567         }
   1568 
   1569         if (!(v instanceof CellLayout)) {
   1570             v = (View) v.getParent();
   1571         }
   1572 
   1573         CellLayout.CellInfo cellInfo = (CellLayout.CellInfo) v.getTag();
   1574 
   1575         // This happens when long clicking an item with the dpad/trackball
   1576         if (cellInfo == null) {
   1577             return true;
   1578         }
   1579 
   1580         if (mWorkspace.allowLongPress()) {
   1581             if (cellInfo.cell == null) {
   1582                 if (cellInfo.valid) {
   1583                     // User long pressed on empty space
   1584                     mWorkspace.setAllowLongPress(false);
   1585                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
   1586                             HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
   1587                     showAddDialog(cellInfo);
   1588                 }
   1589             } else {
   1590                 if (!(cellInfo.cell instanceof Folder)) {
   1591                     // User long pressed on an item
   1592                     mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
   1593                             HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
   1594                     mWorkspace.startDrag(cellInfo);
   1595                 }
   1596             }
   1597         }
   1598         return true;
   1599     }
   1600 
   1601     @SuppressWarnings({"unchecked"})
   1602     private void dismissPreview(final View v) {
   1603         final PopupWindow window = (PopupWindow) v.getTag();
   1604         if (window != null) {
   1605             window.setOnDismissListener(new PopupWindow.OnDismissListener() {
   1606                 public void onDismiss() {
   1607                     ViewGroup group = (ViewGroup) v.getTag(R.id.workspace);
   1608                     int count = group.getChildCount();
   1609                     for (int i = 0; i < count; i++) {
   1610                         ((ImageView) group.getChildAt(i)).setImageDrawable(null);
   1611                     }
   1612                     ArrayList<Bitmap> bitmaps = (ArrayList<Bitmap>) v.getTag(R.id.icon);
   1613                     for (Bitmap bitmap : bitmaps) bitmap.recycle();
   1614 
   1615                     v.setTag(R.id.workspace, null);
   1616                     v.setTag(R.id.icon, null);
   1617                     window.setOnDismissListener(null);
   1618                 }
   1619             });
   1620             window.dismiss();
   1621         }
   1622         v.setTag(null);
   1623     }
   1624 
   1625     private void showPreviews(View anchor) {
   1626         showPreviews(anchor, 0, mWorkspace.getChildCount());
   1627     }
   1628 
   1629     private void showPreviews(final View anchor, int start, int end) {
   1630         final Resources resources = getResources();
   1631         final Workspace workspace = mWorkspace;
   1632 
   1633         CellLayout cell = ((CellLayout) workspace.getChildAt(start));
   1634 
   1635         float max = workspace.getChildCount();
   1636 
   1637         final Rect r = new Rect();
   1638         resources.getDrawable(R.drawable.preview_background).getPadding(r);
   1639         int extraW = (int) ((r.left + r.right) * max);
   1640         int extraH = r.top + r.bottom;
   1641 
   1642         int aW = cell.getWidth() - extraW;
   1643         float w = aW / max;
   1644 
   1645         int width = cell.getWidth();
   1646         int height = cell.getHeight();
   1647         int x = cell.getLeftPadding();
   1648         int y = cell.getTopPadding();
   1649         width -= (x + cell.getRightPadding());
   1650         height -= (y + cell.getBottomPadding());
   1651 
   1652         float scale = w / width;
   1653 
   1654         int count = end - start;
   1655 
   1656         final float sWidth = width * scale;
   1657         float sHeight = height * scale;
   1658 
   1659         LinearLayout preview = new LinearLayout(this);
   1660 
   1661         PreviewTouchHandler handler = new PreviewTouchHandler(anchor);
   1662         ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>(count);
   1663 
   1664         for (int i = start; i < end; i++) {
   1665             ImageView image = new ImageView(this);
   1666             cell = (CellLayout) workspace.getChildAt(i);
   1667 
   1668             final Bitmap bitmap = Bitmap.createBitmap((int) sWidth, (int) sHeight,
   1669                     Bitmap.Config.ARGB_8888);
   1670 
   1671             final Canvas c = new Canvas(bitmap);
   1672             c.scale(scale, scale);
   1673             c.translate(-cell.getLeftPadding(), -cell.getTopPadding());
   1674             cell.dispatchDraw(c);
   1675 
   1676             image.setBackgroundDrawable(resources.getDrawable(R.drawable.preview_background));
   1677             image.setImageBitmap(bitmap);
   1678             image.setTag(i);
   1679             image.setOnClickListener(handler);
   1680             image.setOnFocusChangeListener(handler);
   1681             image.setFocusable(true);
   1682             if (i == mWorkspace.getCurrentScreen()) image.requestFocus();
   1683 
   1684             preview.addView(image,
   1685                     LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
   1686 
   1687             bitmaps.add(bitmap);
   1688         }
   1689 
   1690         final PopupWindow p = new PopupWindow(this);
   1691         p.setContentView(preview);
   1692         p.setWidth((int) (sWidth * count + extraW));
   1693         p.setHeight((int) (sHeight + extraH));
   1694         p.setAnimationStyle(R.style.AnimationPreview);
   1695         p.setOutsideTouchable(true);
   1696         p.setFocusable(true);
   1697         p.setBackgroundDrawable(new ColorDrawable(0));
   1698         p.showAsDropDown(anchor, 0, 0);
   1699 
   1700         p.setOnDismissListener(new PopupWindow.OnDismissListener() {
   1701             public void onDismiss() {
   1702                 dismissPreview(anchor);
   1703             }
   1704         });
   1705 
   1706         anchor.setTag(p);
   1707         anchor.setTag(R.id.workspace, preview);
   1708         anchor.setTag(R.id.icon, bitmaps);
   1709     }
   1710 
   1711     class PreviewTouchHandler implements View.OnClickListener, Runnable, View.OnFocusChangeListener {
   1712         private final View mAnchor;
   1713 
   1714         public PreviewTouchHandler(View anchor) {
   1715             mAnchor = anchor;
   1716         }
   1717 
   1718         public void onClick(View v) {
   1719             mWorkspace.snapToScreen((Integer) v.getTag());
   1720             v.post(this);
   1721         }
   1722 
   1723         public void run() {
   1724             dismissPreview(mAnchor);
   1725         }
   1726 
   1727         public void onFocusChange(View v, boolean hasFocus) {
   1728             if (hasFocus) {
   1729                 mWorkspace.snapToScreen((Integer) v.getTag());
   1730             }
   1731         }
   1732     }
   1733 
   1734     Workspace getWorkspace() {
   1735         return mWorkspace;
   1736     }
   1737 
   1738     @Override
   1739     protected Dialog onCreateDialog(int id) {
   1740         switch (id) {
   1741             case DIALOG_CREATE_SHORTCUT:
   1742                 return new CreateShortcut().createDialog();
   1743             case DIALOG_RENAME_FOLDER:
   1744                 return new RenameFolder().createDialog();
   1745         }
   1746 
   1747         return super.onCreateDialog(id);
   1748     }
   1749 
   1750     @Override
   1751     protected void onPrepareDialog(int id, Dialog dialog) {
   1752         switch (id) {
   1753             case DIALOG_CREATE_SHORTCUT:
   1754                 break;
   1755             case DIALOG_RENAME_FOLDER:
   1756                 if (mFolderInfo != null) {
   1757                     EditText input = (EditText) dialog.findViewById(R.id.folder_name);
   1758                     final CharSequence text = mFolderInfo.title;
   1759                     input.setText(text);
   1760                     input.setSelection(0, text.length());
   1761                 }
   1762                 break;
   1763         }
   1764     }
   1765 
   1766     void showRenameDialog(FolderInfo info) {
   1767         mFolderInfo = info;
   1768         mWaitingForResult = true;
   1769         showDialog(DIALOG_RENAME_FOLDER);
   1770     }
   1771 
   1772     private void showAddDialog(CellLayout.CellInfo cellInfo) {
   1773         mAddItemCellInfo = cellInfo;
   1774         mWaitingForResult = true;
   1775         showDialog(DIALOG_CREATE_SHORTCUT);
   1776     }
   1777 
   1778     private void pickShortcut() {
   1779         Bundle bundle = new Bundle();
   1780 
   1781         ArrayList<String> shortcutNames = new ArrayList<String>();
   1782         shortcutNames.add(getString(R.string.group_applications));
   1783         bundle.putStringArrayList(Intent.EXTRA_SHORTCUT_NAME, shortcutNames);
   1784 
   1785         ArrayList<ShortcutIconResource> shortcutIcons = new ArrayList<ShortcutIconResource>();
   1786         shortcutIcons.add(ShortcutIconResource.fromContext(Launcher.this,
   1787                         R.drawable.ic_launcher_application));
   1788         bundle.putParcelableArrayList(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, shortcutIcons);
   1789 
   1790         Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
   1791         pickIntent.putExtra(Intent.EXTRA_INTENT, new Intent(Intent.ACTION_CREATE_SHORTCUT));
   1792         pickIntent.putExtra(Intent.EXTRA_TITLE, getText(R.string.title_select_shortcut));
   1793         pickIntent.putExtras(bundle);
   1794 
   1795         startActivityForResult(pickIntent, REQUEST_PICK_SHORTCUT);
   1796     }
   1797 
   1798     private class RenameFolder {
   1799         private EditText mInput;
   1800 
   1801         Dialog createDialog() {
   1802             final View layout = View.inflate(Launcher.this, R.layout.rename_folder, null);
   1803             mInput = (EditText) layout.findViewById(R.id.folder_name);
   1804 
   1805             AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this);
   1806             builder.setIcon(0);
   1807             builder.setTitle(getString(R.string.rename_folder_title));
   1808             builder.setCancelable(true);
   1809             builder.setOnCancelListener(new Dialog.OnCancelListener() {
   1810                 public void onCancel(DialogInterface dialog) {
   1811                     cleanup();
   1812                 }
   1813             });
   1814             builder.setNegativeButton(getString(R.string.cancel_action),
   1815                 new Dialog.OnClickListener() {
   1816                     public void onClick(DialogInterface dialog, int which) {
   1817                         cleanup();
   1818                     }
   1819                 }
   1820             );
   1821             builder.setPositiveButton(getString(R.string.rename_action),
   1822                 new Dialog.OnClickListener() {
   1823                     public void onClick(DialogInterface dialog, int which) {
   1824                         changeFolderName();
   1825                     }
   1826                 }
   1827             );
   1828             builder.setView(layout);
   1829 
   1830             final AlertDialog dialog = builder.create();
   1831             dialog.setOnShowListener(new DialogInterface.OnShowListener() {
   1832                 public void onShow(DialogInterface dialog) {
   1833                     mWaitingForResult = true;
   1834                     mInput.requestFocus();
   1835                     InputMethodManager inputManager = (InputMethodManager)
   1836                             getSystemService(Context.INPUT_METHOD_SERVICE);
   1837                     inputManager.showSoftInput(mInput, 0);
   1838                 }
   1839             });
   1840 
   1841             return dialog;
   1842         }
   1843 
   1844         private void changeFolderName() {
   1845             final String name = mInput.getText().toString();
   1846             if (!TextUtils.isEmpty(name)) {
   1847                 // Make sure we have the right folder info
   1848                 mFolderInfo = mFolders.get(mFolderInfo.id);
   1849                 mFolderInfo.title = name;
   1850                 LauncherModel.updateItemInDatabase(Launcher.this, mFolderInfo);
   1851 
   1852                 if (mWorkspaceLoading) {
   1853                     lockAllApps();
   1854                     mModel.startLoader(Launcher.this, false);
   1855                 } else {
   1856                     final FolderIcon folderIcon = (FolderIcon)
   1857                             mWorkspace.getViewForTag(mFolderInfo);
   1858                     if (folderIcon != null) {
   1859                         folderIcon.setText(name);
   1860                         getWorkspace().requestLayout();
   1861                     } else {
   1862                         lockAllApps();
   1863                         mWorkspaceLoading = true;
   1864                         mModel.startLoader(Launcher.this, false);
   1865                     }
   1866                 }
   1867             }
   1868             cleanup();
   1869         }
   1870 
   1871         private void cleanup() {
   1872             dismissDialog(DIALOG_RENAME_FOLDER);
   1873             mWaitingForResult = false;
   1874             mFolderInfo = null;
   1875         }
   1876     }
   1877 
   1878     // Now a part of LauncherModel.Callbacks. Used to reorder loading steps.
   1879     public boolean isAllAppsVisible() {
   1880         return (mAllAppsGrid != null) ? mAllAppsGrid.isVisible() : false;
   1881     }
   1882 
   1883     // AllAppsView.Watcher
   1884     public void zoomed(float zoom) {
   1885         if (zoom == 1.0f) {
   1886             mWorkspace.setVisibility(View.GONE);
   1887         }
   1888     }
   1889 
   1890     void showAllApps(boolean animated) {
   1891         mAllAppsGrid.zoom(1.0f, animated);
   1892 
   1893         ((View) mAllAppsGrid).setFocusable(true);
   1894         ((View) mAllAppsGrid).requestFocus();
   1895 
   1896         // TODO: fade these two too
   1897         mDeleteZone.setVisibility(View.GONE);
   1898     }
   1899 
   1900     /**
   1901      * Things to test when changing this code.
   1902      *   - Home from workspace
   1903      *          - from center screen
   1904      *          - from other screens
   1905      *   - Home from all apps
   1906      *          - from center screen
   1907      *          - from other screens
   1908      *   - Back from all apps
   1909      *          - from center screen
   1910      *          - from other screens
   1911      *   - Launch app from workspace and quit
   1912      *          - with back
   1913      *          - with home
   1914      *   - Launch app from all apps and quit
   1915      *          - with back
   1916      *          - with home
   1917      *   - Go to a screen that's not the default, then all
   1918      *     apps, and launch and app, and go back
   1919      *          - with back
   1920      *          -with home
   1921      *   - On workspace, long press power and go back
   1922      *          - with back
   1923      *          - with home
   1924      *   - On all apps, long press power and go back
   1925      *          - with back
   1926      *          - with home
   1927      *   - On workspace, power off
   1928      *   - On all apps, power off
   1929      *   - Launch an app and turn off the screen while in that app
   1930      *          - Go back with home key
   1931      *          - Go back with back key  TODO: make this not go to workspace
   1932      *          - From all apps
   1933      *          - From workspace
   1934      *   - Enter and exit car mode (becuase it causes an extra configuration changed)
   1935      *          - From all apps
   1936      *          - From the center workspace
   1937      *          - From another workspace
   1938      */
   1939     void closeAllApps(boolean animated) {
   1940         if (mAllAppsGrid.isVisible()) {
   1941             mWorkspace.setVisibility(View.VISIBLE);
   1942             mAllAppsGrid.zoom(0.0f, animated);
   1943             ((View)mAllAppsGrid).setFocusable(false);
   1944             mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
   1945         }
   1946     }
   1947 
   1948     void lockAllApps() {
   1949         // TODO
   1950     }
   1951 
   1952     void unlockAllApps() {
   1953         // TODO
   1954     }
   1955 
   1956     /**
   1957      * Displays the shortcut creation dialog and launches, if necessary, the
   1958      * appropriate activity.
   1959      */
   1960     private class CreateShortcut implements DialogInterface.OnClickListener,
   1961             DialogInterface.OnCancelListener, DialogInterface.OnDismissListener,
   1962             DialogInterface.OnShowListener {
   1963 
   1964         private AddAdapter mAdapter;
   1965 
   1966         Dialog createDialog() {
   1967             mAdapter = new AddAdapter(Launcher.this);
   1968 
   1969             final AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this);
   1970             builder.setTitle(getString(R.string.menu_item_add_item));
   1971             builder.setAdapter(mAdapter, this);
   1972 
   1973             builder.setInverseBackgroundForced(true);
   1974 
   1975             AlertDialog dialog = builder.create();
   1976             dialog.setOnCancelListener(this);
   1977             dialog.setOnDismissListener(this);
   1978             dialog.setOnShowListener(this);
   1979 
   1980             return dialog;
   1981         }
   1982 
   1983         public void onCancel(DialogInterface dialog) {
   1984             mWaitingForResult = false;
   1985             cleanup();
   1986         }
   1987 
   1988         public void onDismiss(DialogInterface dialog) {
   1989         }
   1990 
   1991         private void cleanup() {
   1992             try {
   1993                 dismissDialog(DIALOG_CREATE_SHORTCUT);
   1994             } catch (Exception e) {
   1995                 // An exception is thrown if the dialog is not visible, which is fine
   1996             }
   1997         }
   1998 
   1999         /**
   2000          * Handle the action clicked in the "Add to home" dialog.
   2001          */
   2002         public void onClick(DialogInterface dialog, int which) {
   2003             Resources res = getResources();
   2004             cleanup();
   2005 
   2006             switch (which) {
   2007                 case AddAdapter.ITEM_SHORTCUT: {
   2008                     // Insert extra item to handle picking application
   2009                     pickShortcut();
   2010                     break;
   2011                 }
   2012 
   2013                 case AddAdapter.ITEM_APPWIDGET: {
   2014                     int appWidgetId = Launcher.this.mAppWidgetHost.allocateAppWidgetId();
   2015 
   2016                     Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
   2017                     pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
   2018                     // start the pick activity
   2019                     startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET);
   2020                     break;
   2021                 }
   2022 
   2023                 case AddAdapter.ITEM_LIVE_FOLDER: {
   2024                     // Insert extra item to handle inserting folder
   2025                     Bundle bundle = new Bundle();
   2026 
   2027                     ArrayList<String> shortcutNames = new ArrayList<String>();
   2028                     shortcutNames.add(res.getString(R.string.group_folder));
   2029                     bundle.putStringArrayList(Intent.EXTRA_SHORTCUT_NAME, shortcutNames);
   2030 
   2031                     ArrayList<ShortcutIconResource> shortcutIcons =
   2032                             new ArrayList<ShortcutIconResource>();
   2033                     shortcutIcons.add(ShortcutIconResource.fromContext(Launcher.this,
   2034                             R.drawable.ic_launcher_folder));
   2035                     bundle.putParcelableArrayList(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, shortcutIcons);
   2036 
   2037                     Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
   2038                     pickIntent.putExtra(Intent.EXTRA_INTENT,
   2039                             new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER));
   2040                     pickIntent.putExtra(Intent.EXTRA_TITLE,
   2041                             getText(R.string.title_select_live_folder));
   2042                     pickIntent.putExtras(bundle);
   2043 
   2044                     startActivityForResult(pickIntent, REQUEST_PICK_LIVE_FOLDER);
   2045                     break;
   2046                 }
   2047 
   2048                 case AddAdapter.ITEM_WALLPAPER: {
   2049                     startWallpaper();
   2050                     break;
   2051                 }
   2052             }
   2053         }
   2054 
   2055         public void onShow(DialogInterface dialog) {
   2056             mWaitingForResult = true;
   2057         }
   2058     }
   2059 
   2060     /**
   2061      * Receives notifications when applications are added/removed.
   2062      */
   2063     private class CloseSystemDialogsIntentReceiver extends BroadcastReceiver {
   2064         @Override
   2065         public void onReceive(Context context, Intent intent) {
   2066             closeSystemDialogs();
   2067             String reason = intent.getStringExtra("reason");
   2068             if (!"homekey".equals(reason)) {
   2069                 boolean animate = true;
   2070                 if (mPaused || "lock".equals(reason)) {
   2071                     animate = false;
   2072                 }
   2073                 closeAllApps(animate);
   2074             }
   2075         }
   2076     }
   2077 
   2078     /**
   2079      * Receives notifications whenever the appwidgets are reset.
   2080      */
   2081     private class AppWidgetResetObserver extends ContentObserver {
   2082         public AppWidgetResetObserver() {
   2083             super(new Handler());
   2084         }
   2085 
   2086         @Override
   2087         public void onChange(boolean selfChange) {
   2088             onAppWidgetReset();
   2089         }
   2090     }
   2091 
   2092     /**
   2093      * Implementation of the method from LauncherModel.Callbacks.
   2094      */
   2095     public int getCurrentWorkspaceScreen() {
   2096         if (mWorkspace != null) {
   2097             return mWorkspace.getCurrentScreen();
   2098         } else {
   2099             return SCREEN_COUNT / 2;
   2100         }
   2101     }
   2102 
   2103     /**
   2104      * Refreshes the shortcuts shown on the workspace.
   2105      *
   2106      * Implementation of the method from LauncherModel.Callbacks.
   2107      */
   2108     public void startBinding() {
   2109         final Workspace workspace = mWorkspace;
   2110         int count = workspace.getChildCount();
   2111         for (int i = 0; i < count; i++) {
   2112             // Use removeAllViewsInLayout() to avoid an extra requestLayout() and invalidate().
   2113             ((ViewGroup) workspace.getChildAt(i)).removeAllViewsInLayout();
   2114         }
   2115 
   2116         if (DEBUG_USER_INTERFACE) {
   2117             android.widget.Button finishButton = new android.widget.Button(this);
   2118             finishButton.setText("Finish");
   2119             workspace.addInScreen(finishButton, 1, 0, 0, 1, 1);
   2120 
   2121             finishButton.setOnClickListener(new android.widget.Button.OnClickListener() {
   2122                 public void onClick(View v) {
   2123                     finish();
   2124                 }
   2125             });
   2126         }
   2127     }
   2128 
   2129     /**
   2130      * Bind the items start-end from the list.
   2131      *
   2132      * Implementation of the method from LauncherModel.Callbacks.
   2133      */
   2134     public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end) {
   2135 
   2136         final Workspace workspace = mWorkspace;
   2137 
   2138         for (int i=start; i<end; i++) {
   2139             final ItemInfo item = shortcuts.get(i);
   2140             mDesktopItems.add(item);
   2141             switch (item.itemType) {
   2142                 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
   2143                 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
   2144                     final View shortcut = createShortcut((ShortcutInfo)item);
   2145                     workspace.addInScreen(shortcut, item.screen, item.cellX, item.cellY, 1, 1,
   2146                             false);
   2147                     break;
   2148                 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
   2149                     final FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
   2150                             (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),
   2151                             (UserFolderInfo) item);
   2152                     workspace.addInScreen(newFolder, item.screen, item.cellX, item.cellY, 1, 1,
   2153                             false);
   2154                     break;
   2155                 case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
   2156                     final FolderIcon newLiveFolder = LiveFolderIcon.fromXml(
   2157                             R.layout.live_folder_icon, this,
   2158                             (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),
   2159                             (LiveFolderInfo) item);
   2160                     workspace.addInScreen(newLiveFolder, item.screen, item.cellX, item.cellY, 1, 1,
   2161                             false);
   2162                     break;
   2163             }
   2164         }
   2165 
   2166         workspace.requestLayout();
   2167     }
   2168 
   2169     /**
   2170      * Implementation of the method from LauncherModel.Callbacks.
   2171      */
   2172     public void bindFolders(HashMap<Long, FolderInfo> folders) {
   2173         mFolders.clear();
   2174         mFolders.putAll(folders);
   2175     }
   2176 
   2177     /**
   2178      * Add the views for a widget to the workspace.
   2179      *
   2180      * Implementation of the method from LauncherModel.Callbacks.
   2181      */
   2182     public void bindAppWidget(LauncherAppWidgetInfo item) {
   2183         final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0;
   2184         if (DEBUG_WIDGETS) {
   2185             Log.d(TAG, "bindAppWidget: " + item);
   2186         }
   2187         final Workspace workspace = mWorkspace;
   2188 
   2189         final int appWidgetId = item.appWidgetId;
   2190         final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
   2191         if (DEBUG_WIDGETS) {
   2192             Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider);
   2193         }
   2194 
   2195         item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
   2196 
   2197         item.hostView.setAppWidget(appWidgetId, appWidgetInfo);
   2198         item.hostView.setTag(item);
   2199 
   2200         workspace.addInScreen(item.hostView, item.screen, item.cellX,
   2201                 item.cellY, item.spanX, item.spanY, false);
   2202 
   2203         workspace.requestLayout();
   2204 
   2205         mDesktopItems.add(item);
   2206 
   2207         if (DEBUG_WIDGETS) {
   2208             Log.d(TAG, "bound widget id="+item.appWidgetId+" in "
   2209                     + (SystemClock.uptimeMillis()-start) + "ms");
   2210         }
   2211     }
   2212 
   2213     /**
   2214      * Callback saying that there aren't any more items to bind.
   2215      *
   2216      * Implementation of the method from LauncherModel.Callbacks.
   2217      */
   2218     public void finishBindingItems() {
   2219         if (mSavedState != null) {
   2220             if (!mWorkspace.hasFocus()) {
   2221                 mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
   2222             }
   2223 
   2224             final long[] userFolders = mSavedState.getLongArray(RUNTIME_STATE_USER_FOLDERS);
   2225             if (userFolders != null) {
   2226                 for (long folderId : userFolders) {
   2227                     final FolderInfo info = mFolders.get(folderId);
   2228                     if (info != null) {
   2229                         openFolder(info);
   2230                     }
   2231                 }
   2232                 final Folder openFolder = mWorkspace.getOpenFolder();
   2233                 if (openFolder != null) {
   2234                     openFolder.requestFocus();
   2235                 }
   2236             }
   2237 
   2238             mSavedState = null;
   2239         }
   2240 
   2241         if (mSavedInstanceState != null) {
   2242             super.onRestoreInstanceState(mSavedInstanceState);
   2243             mSavedInstanceState = null;
   2244         }
   2245 
   2246         mWorkspaceLoading = false;
   2247     }
   2248 
   2249     /**
   2250      * Add the icons for all apps.
   2251      *
   2252      * Implementation of the method from LauncherModel.Callbacks.
   2253      */
   2254     public void bindAllApplications(ArrayList<ApplicationInfo> apps) {
   2255         mAllAppsGrid.setApps(apps);
   2256     }
   2257 
   2258     /**
   2259      * A package was installed.
   2260      *
   2261      * Implementation of the method from LauncherModel.Callbacks.
   2262      */
   2263     public void bindAppsAdded(ArrayList<ApplicationInfo> apps) {
   2264         removeDialog(DIALOG_CREATE_SHORTCUT);
   2265         mAllAppsGrid.addApps(apps);
   2266     }
   2267 
   2268     /**
   2269      * A package was updated.
   2270      *
   2271      * Implementation of the method from LauncherModel.Callbacks.
   2272      */
   2273     public void bindAppsUpdated(ArrayList<ApplicationInfo> apps) {
   2274         removeDialog(DIALOG_CREATE_SHORTCUT);
   2275         mWorkspace.updateShortcuts(apps);
   2276         mAllAppsGrid.updateApps(apps);
   2277     }
   2278 
   2279     /**
   2280      * A package was uninstalled.
   2281      *
   2282      * Implementation of the method from LauncherModel.Callbacks.
   2283      */
   2284     public void bindAppsRemoved(ArrayList<ApplicationInfo> apps) {
   2285         removeDialog(DIALOG_CREATE_SHORTCUT);
   2286         mWorkspace.removeItems(apps);
   2287         mAllAppsGrid.removeApps(apps);
   2288     }
   2289 
   2290     /**
   2291      * Prints out out state for debugging.
   2292      */
   2293     public void dumpState() {
   2294         Log.d(TAG, "BEGIN launcher2 dump state for launcher " + this);
   2295         Log.d(TAG, "mSavedState=" + mSavedState);
   2296         Log.d(TAG, "mWorkspaceLoading=" + mWorkspaceLoading);
   2297         Log.d(TAG, "mRestoring=" + mRestoring);
   2298         Log.d(TAG, "mWaitingForResult=" + mWaitingForResult);
   2299         Log.d(TAG, "mSavedInstanceState=" + mSavedInstanceState);
   2300         Log.d(TAG, "mDesktopItems.size=" + mDesktopItems.size());
   2301         Log.d(TAG, "mFolders.size=" + mFolders.size());
   2302         mModel.dumpState();
   2303         mAllAppsGrid.dumpState();
   2304         Log.d(TAG, "END launcher2 dump state");
   2305     }
   2306 }
   2307