Home | History | Annotate | Download | only in launcher3
      1 /*
      2  * Copyright (C) 2011 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.launcher3;
     18 
     19 import android.animation.AnimatorSet;
     20 import android.animation.ValueAnimator;
     21 import android.appwidget.AppWidgetHostView;
     22 import android.appwidget.AppWidgetManager;
     23 import android.appwidget.AppWidgetProviderInfo;
     24 import android.content.ComponentName;
     25 import android.content.Context;
     26 import android.content.pm.PackageManager;
     27 import android.content.pm.ResolveInfo;
     28 import android.content.res.Resources;
     29 import android.content.res.TypedArray;
     30 import android.graphics.Bitmap;
     31 import android.graphics.Point;
     32 import android.graphics.Rect;
     33 import android.graphics.drawable.Drawable;
     34 import android.os.AsyncTask;
     35 import android.os.Build;
     36 import android.os.Bundle;
     37 import android.os.Process;
     38 import android.util.AttributeSet;
     39 import android.util.Log;
     40 import android.view.Gravity;
     41 import android.view.KeyEvent;
     42 import android.view.LayoutInflater;
     43 import android.view.View;
     44 import android.view.ViewGroup;
     45 import android.view.animation.AccelerateInterpolator;
     46 import android.widget.GridLayout;
     47 import android.widget.ImageView;
     48 import android.widget.Toast;
     49 
     50 import com.android.launcher3.DropTarget.DragObject;
     51 import com.android.launcher3.compat.AppWidgetManagerCompat;
     52 
     53 import java.util.ArrayList;
     54 import java.util.Collections;
     55 import java.util.Iterator;
     56 import java.util.List;
     57 
     58 /**
     59  * A simple callback interface which also provides the results of the task.
     60  */
     61 interface AsyncTaskCallback {
     62     void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data);
     63 }
     64 
     65 /**
     66  * The data needed to perform either of the custom AsyncTasks.
     67  */
     68 class AsyncTaskPageData {
     69     enum Type {
     70         LoadWidgetPreviewData
     71     }
     72 
     73     AsyncTaskPageData(int p, ArrayList<Object> l, int cw, int ch, AsyncTaskCallback bgR,
     74             AsyncTaskCallback postR, WidgetPreviewLoader w) {
     75         page = p;
     76         items = l;
     77         generatedImages = new ArrayList<Bitmap>();
     78         maxImageWidth = cw;
     79         maxImageHeight = ch;
     80         doInBackgroundCallback = bgR;
     81         postExecuteCallback = postR;
     82         widgetPreviewLoader = w;
     83     }
     84     void cleanup(boolean cancelled) {
     85         // Clean up any references to source/generated bitmaps
     86         if (generatedImages != null) {
     87             if (cancelled) {
     88                 for (int i = 0; i < generatedImages.size(); i++) {
     89                     widgetPreviewLoader.recycleBitmap(items.get(i), generatedImages.get(i));
     90                 }
     91             }
     92             generatedImages.clear();
     93         }
     94     }
     95     int page;
     96     ArrayList<Object> items;
     97     ArrayList<Bitmap> sourceImages;
     98     ArrayList<Bitmap> generatedImages;
     99     int maxImageWidth;
    100     int maxImageHeight;
    101     AsyncTaskCallback doInBackgroundCallback;
    102     AsyncTaskCallback postExecuteCallback;
    103     WidgetPreviewLoader widgetPreviewLoader;
    104 }
    105 
    106 /**
    107  * A generic template for an async task used in AppsCustomize.
    108  */
    109 class AppsCustomizeAsyncTask extends AsyncTask<AsyncTaskPageData, Void, AsyncTaskPageData> {
    110     AppsCustomizeAsyncTask(int p, AsyncTaskPageData.Type ty) {
    111         page = p;
    112         threadPriority = Process.THREAD_PRIORITY_DEFAULT;
    113         dataType = ty;
    114     }
    115     @Override
    116     protected AsyncTaskPageData doInBackground(AsyncTaskPageData... params) {
    117         if (params.length != 1) return null;
    118         // Load each of the widget previews in the background
    119         params[0].doInBackgroundCallback.run(this, params[0]);
    120         return params[0];
    121     }
    122     @Override
    123     protected void onPostExecute(AsyncTaskPageData result) {
    124         // All the widget previews are loaded, so we can just callback to inflate the page
    125         result.postExecuteCallback.run(this, result);
    126     }
    127 
    128     void setThreadPriority(int p) {
    129         threadPriority = p;
    130     }
    131     void syncThreadPriority() {
    132         Process.setThreadPriority(threadPriority);
    133     }
    134 
    135     // The page that this async task is associated with
    136     AsyncTaskPageData.Type dataType;
    137     int page;
    138     int threadPriority;
    139 }
    140 
    141 /**
    142  * The Apps/Customize page that displays all the applications, widgets, and shortcuts.
    143  */
    144 public class AppsCustomizePagedView extends PagedViewWithDraggableItems implements
    145         View.OnClickListener, View.OnKeyListener, DragSource,
    146         PagedViewWidget.ShortPressListener, LauncherTransitionable {
    147     static final String TAG = "AppsCustomizePagedView";
    148 
    149     private static Rect sTmpRect = new Rect();
    150 
    151     /**
    152      * The different content types that this paged view can show.
    153      */
    154     public enum ContentType {
    155         Applications,
    156         Widgets
    157     }
    158     private ContentType mContentType = ContentType.Applications;
    159 
    160     // Refs
    161     private Launcher mLauncher;
    162     private DragController mDragController;
    163     private final LayoutInflater mLayoutInflater;
    164     private final PackageManager mPackageManager;
    165 
    166     // Save and Restore
    167     private int mSaveInstanceStateItemIndex = -1;
    168 
    169     // Content
    170     private ArrayList<AppInfo> mApps;
    171     private ArrayList<Object> mWidgets;
    172 
    173     // Caching
    174     private IconCache mIconCache;
    175 
    176     // Dimens
    177     private int mContentWidth, mContentHeight;
    178     private int mWidgetCountX, mWidgetCountY;
    179     private PagedViewCellLayout mWidgetSpacingLayout;
    180     private int mNumAppsPages;
    181     private int mNumWidgetPages;
    182     private Rect mAllAppsPadding = new Rect();
    183 
    184     // Previews & outlines
    185     ArrayList<AppsCustomizeAsyncTask> mRunningTasks;
    186     private static final int sPageSleepDelay = 200;
    187 
    188     private Runnable mInflateWidgetRunnable = null;
    189     private Runnable mBindWidgetRunnable = null;
    190     static final int WIDGET_NO_CLEANUP_REQUIRED = -1;
    191     static final int WIDGET_PRELOAD_PENDING = 0;
    192     static final int WIDGET_BOUND = 1;
    193     static final int WIDGET_INFLATED = 2;
    194     int mWidgetCleanupState = WIDGET_NO_CLEANUP_REQUIRED;
    195     int mWidgetLoadingId = -1;
    196     PendingAddWidgetInfo mCreateWidgetInfo = null;
    197     private boolean mDraggingWidget = false;
    198     boolean mPageBackgroundsVisible = true;
    199 
    200     private Toast mWidgetInstructionToast;
    201 
    202     // Deferral of loading widget previews during launcher transitions
    203     private boolean mInTransition;
    204     private ArrayList<AsyncTaskPageData> mDeferredSyncWidgetPageItems =
    205         new ArrayList<AsyncTaskPageData>();
    206     private ArrayList<Runnable> mDeferredPrepareLoadWidgetPreviewsTasks =
    207         new ArrayList<Runnable>();
    208 
    209     WidgetPreviewLoader mWidgetPreviewLoader;
    210 
    211     private boolean mInBulkBind;
    212     private boolean mNeedToUpdatePageCountsAndInvalidateData;
    213 
    214     public AppsCustomizePagedView(Context context, AttributeSet attrs) {
    215         super(context, attrs);
    216         mLayoutInflater = LayoutInflater.from(context);
    217         mPackageManager = context.getPackageManager();
    218         mApps = new ArrayList<AppInfo>();
    219         mWidgets = new ArrayList<Object>();
    220         mIconCache = (LauncherAppState.getInstance()).getIconCache();
    221         mRunningTasks = new ArrayList<AppsCustomizeAsyncTask>();
    222 
    223         // Save the default widget preview background
    224         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppsCustomizePagedView, 0, 0);
    225         mWidgetCountX = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountX, 2);
    226         mWidgetCountY = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountY, 2);
    227         a.recycle();
    228         mWidgetSpacingLayout = new PagedViewCellLayout(getContext());
    229 
    230         // The padding on the non-matched dimension for the default widget preview icons
    231         // (top + bottom)
    232         mFadeInAdjacentScreens = false;
    233 
    234         // Unless otherwise specified this view is important for accessibility.
    235         if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
    236             setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
    237         }
    238         setSinglePageInViewport();
    239     }
    240 
    241     @Override
    242     protected void init() {
    243         super.init();
    244         mCenterPagesVertically = false;
    245 
    246         Context context = getContext();
    247         Resources r = context.getResources();
    248         setDragSlopeThreshold(r.getInteger(R.integer.config_appsCustomizeDragSlopeThreshold)/100f);
    249     }
    250 
    251     public void onFinishInflate() {
    252         super.onFinishInflate();
    253 
    254         LauncherAppState app = LauncherAppState.getInstance();
    255         DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
    256         setPadding(grid.edgeMarginPx, 2 * grid.edgeMarginPx,
    257                 grid.edgeMarginPx, 2 * grid.edgeMarginPx);
    258     }
    259 
    260     void setAllAppsPadding(Rect r) {
    261         mAllAppsPadding.set(r);
    262     }
    263 
    264     void setWidgetsPageIndicatorPadding(int pageIndicatorHeight) {
    265         setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), pageIndicatorHeight);
    266     }
    267 
    268     WidgetPreviewLoader getWidgetPreviewLoader() {
    269         if (mWidgetPreviewLoader == null) {
    270             mWidgetPreviewLoader = new WidgetPreviewLoader(mLauncher);
    271         }
    272         return mWidgetPreviewLoader;
    273     }
    274 
    275     /** Returns the item index of the center item on this page so that we can restore to this
    276      *  item index when we rotate. */
    277     private int getMiddleComponentIndexOnCurrentPage() {
    278         int i = -1;
    279         if (getPageCount() > 0) {
    280             int currentPage = getCurrentPage();
    281             if (mContentType == ContentType.Applications) {
    282                 AppsCustomizeCellLayout layout = (AppsCustomizeCellLayout) getPageAt(currentPage);
    283                 ShortcutAndWidgetContainer childrenLayout = layout.getShortcutsAndWidgets();
    284                 int numItemsPerPage = mCellCountX * mCellCountY;
    285                 int childCount = childrenLayout.getChildCount();
    286                 if (childCount > 0) {
    287                     i = (currentPage * numItemsPerPage) + (childCount / 2);
    288                 }
    289             } else if (mContentType == ContentType.Widgets) {
    290                 int numApps = mApps.size();
    291                 PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(currentPage);
    292                 int numItemsPerPage = mWidgetCountX * mWidgetCountY;
    293                 int childCount = layout.getChildCount();
    294                 if (childCount > 0) {
    295                     i = numApps +
    296                         (currentPage * numItemsPerPage) + (childCount / 2);
    297                 }
    298             } else {
    299                 throw new RuntimeException("Invalid ContentType");
    300             }
    301         }
    302         return i;
    303     }
    304 
    305     /** Get the index of the item to restore to if we need to restore the current page. */
    306     int getSaveInstanceStateIndex() {
    307         if (mSaveInstanceStateItemIndex == -1) {
    308             mSaveInstanceStateItemIndex = getMiddleComponentIndexOnCurrentPage();
    309         }
    310         return mSaveInstanceStateItemIndex;
    311     }
    312 
    313     /** Returns the page in the current orientation which is expected to contain the specified
    314      *  item index. */
    315     int getPageForComponent(int index) {
    316         if (index < 0) return 0;
    317 
    318         if (index < mApps.size()) {
    319             int numItemsPerPage = mCellCountX * mCellCountY;
    320             return (index / numItemsPerPage);
    321         } else {
    322             int numItemsPerPage = mWidgetCountX * mWidgetCountY;
    323             return (index - mApps.size()) / numItemsPerPage;
    324         }
    325     }
    326 
    327     /** Restores the page for an item at the specified index */
    328     void restorePageForIndex(int index) {
    329         if (index < 0) return;
    330         mSaveInstanceStateItemIndex = index;
    331     }
    332 
    333     private void updatePageCounts() {
    334         mNumWidgetPages = (int) Math.ceil(mWidgets.size() /
    335                 (float) (mWidgetCountX * mWidgetCountY));
    336         mNumAppsPages = (int) Math.ceil((float) mApps.size() / (mCellCountX * mCellCountY));
    337     }
    338 
    339     protected void onDataReady(int width, int height) {
    340         // Now that the data is ready, we can calculate the content width, the number of cells to
    341         // use for each page
    342         LauncherAppState app = LauncherAppState.getInstance();
    343         DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
    344         mCellCountX = (int) grid.allAppsNumCols;
    345         mCellCountY = (int) grid.allAppsNumRows;
    346         updatePageCounts();
    347 
    348         // Force a measure to update recalculate the gaps
    349         mContentWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
    350         mContentHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
    351         int widthSpec = MeasureSpec.makeMeasureSpec(mContentWidth, MeasureSpec.AT_MOST);
    352         int heightSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.AT_MOST);
    353         mWidgetSpacingLayout.measure(widthSpec, heightSpec);
    354 
    355         final boolean hostIsTransitioning = getTabHost().isInTransition();
    356         int page = getPageForComponent(mSaveInstanceStateItemIndex);
    357         invalidatePageData(Math.max(0, page), hostIsTransitioning);
    358     }
    359 
    360     protected void onLayout(boolean changed, int l, int t, int r, int b) {
    361         super.onLayout(changed, l, t, r, b);
    362 
    363         if (!isDataReady()) {
    364             if ((LauncherAppState.isDisableAllApps() || !mApps.isEmpty()) && !mWidgets.isEmpty()) {
    365                 post(new Runnable() {
    366                     // This code triggers requestLayout so must be posted outside of the
    367                     // layout pass.
    368                     public void run() {
    369                         if (Utilities.isViewAttachedToWindow(AppsCustomizePagedView.this)) {
    370                             setDataIsReady();
    371                             onDataReady(getMeasuredWidth(), getMeasuredHeight());
    372                         }
    373                     }
    374                 });
    375             }
    376         }
    377     }
    378 
    379     public void onPackagesUpdated(ArrayList<Object> widgetsAndShortcuts) {
    380         LauncherAppState app = LauncherAppState.getInstance();
    381         DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
    382 
    383         // Get the list of widgets and shortcuts
    384         mWidgets.clear();
    385         for (Object o : widgetsAndShortcuts) {
    386             if (o instanceof AppWidgetProviderInfo) {
    387                 AppWidgetProviderInfo widget = (AppWidgetProviderInfo) o;
    388                 if (!app.shouldShowAppOrWidgetProvider(widget.provider)) {
    389                     continue;
    390                 }
    391                 if (widget.minWidth > 0 && widget.minHeight > 0) {
    392                     // Ensure that all widgets we show can be added on a workspace of this size
    393                     int[] spanXY = Launcher.getSpanForWidget(mLauncher, widget);
    394                     int[] minSpanXY = Launcher.getMinSpanForWidget(mLauncher, widget);
    395                     int minSpanX = Math.min(spanXY[0], minSpanXY[0]);
    396                     int minSpanY = Math.min(spanXY[1], minSpanXY[1]);
    397                     if (minSpanX <= (int) grid.numColumns &&
    398                         minSpanY <= (int) grid.numRows) {
    399                         mWidgets.add(widget);
    400                     } else {
    401                         Log.e(TAG, "Widget " + widget.provider + " can not fit on this device (" +
    402                               widget.minWidth + ", " + widget.minHeight + ")");
    403                     }
    404                 } else {
    405                     Log.e(TAG, "Widget " + widget.provider + " has invalid dimensions (" +
    406                           widget.minWidth + ", " + widget.minHeight + ")");
    407                 }
    408             } else {
    409                 // just add shortcuts
    410                 mWidgets.add(o);
    411             }
    412         }
    413         updatePageCountsAndInvalidateData();
    414     }
    415 
    416     public void setBulkBind(boolean bulkBind) {
    417         if (bulkBind) {
    418             mInBulkBind = true;
    419         } else {
    420             mInBulkBind = false;
    421             if (mNeedToUpdatePageCountsAndInvalidateData) {
    422                 updatePageCountsAndInvalidateData();
    423             }
    424         }
    425     }
    426 
    427     private void updatePageCountsAndInvalidateData() {
    428         if (mInBulkBind) {
    429             mNeedToUpdatePageCountsAndInvalidateData = true;
    430         } else {
    431             updatePageCounts();
    432             invalidateOnDataChange();
    433             mNeedToUpdatePageCountsAndInvalidateData = false;
    434         }
    435     }
    436 
    437     @Override
    438     public void onClick(View v) {
    439         // When we have exited all apps or are in transition, disregard clicks
    440         if (!mLauncher.isAllAppsVisible()
    441                 || mLauncher.getWorkspace().isSwitchingState()
    442                 || !(v instanceof PagedViewWidget)) return;
    443 
    444         // Let the user know that they have to long press to add a widget
    445         if (mWidgetInstructionToast != null) {
    446             mWidgetInstructionToast.cancel();
    447         }
    448         mWidgetInstructionToast = Toast.makeText(getContext(),R.string.long_press_widget_to_add,
    449             Toast.LENGTH_SHORT);
    450         mWidgetInstructionToast.show();
    451 
    452         // Create a little animation to show that the widget can move
    453         float offsetY = getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY);
    454         final ImageView p = (ImageView) v.findViewById(R.id.widget_preview);
    455         AnimatorSet bounce = LauncherAnimUtils.createAnimatorSet();
    456         ValueAnimator tyuAnim = LauncherAnimUtils.ofFloat(p, "translationY", offsetY);
    457         tyuAnim.setDuration(125);
    458         ValueAnimator tydAnim = LauncherAnimUtils.ofFloat(p, "translationY", 0f);
    459         tydAnim.setDuration(100);
    460         bounce.play(tyuAnim).before(tydAnim);
    461         bounce.setInterpolator(new AccelerateInterpolator());
    462         bounce.start();
    463     }
    464 
    465     public boolean onKey(View v, int keyCode, KeyEvent event) {
    466         return FocusHelper.handleAppsCustomizeKeyEvent(v,  keyCode, event);
    467     }
    468 
    469     /*
    470      * PagedViewWithDraggableItems implementation
    471      */
    472     @Override
    473     protected void determineDraggingStart(android.view.MotionEvent ev) {
    474         // Disable dragging by pulling an app down for now.
    475     }
    476 
    477     private void beginDraggingApplication(View v) {
    478         mLauncher.getWorkspace().beginDragShared(v, this);
    479     }
    480 
    481     static Bundle getDefaultOptionsForWidget(Launcher launcher, PendingAddWidgetInfo info) {
    482         Bundle options = null;
    483         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
    484             AppWidgetResizeFrame.getWidgetSizeRanges(launcher, info.spanX, info.spanY, sTmpRect);
    485             Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(launcher,
    486                     info.componentName, null);
    487 
    488             float density = launcher.getResources().getDisplayMetrics().density;
    489             int xPaddingDips = (int) ((padding.left + padding.right) / density);
    490             int yPaddingDips = (int) ((padding.top + padding.bottom) / density);
    491 
    492             options = new Bundle();
    493             options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
    494                     sTmpRect.left - xPaddingDips);
    495             options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
    496                     sTmpRect.top - yPaddingDips);
    497             options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
    498                     sTmpRect.right - xPaddingDips);
    499             options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
    500                     sTmpRect.bottom - yPaddingDips);
    501         }
    502         return options;
    503     }
    504 
    505     private void preloadWidget(final PendingAddWidgetInfo info) {
    506         final AppWidgetProviderInfo pInfo = info.info;
    507         final Bundle options = getDefaultOptionsForWidget(mLauncher, info);
    508 
    509         if (pInfo.configure != null) {
    510             info.bindOptions = options;
    511             return;
    512         }
    513 
    514         mWidgetCleanupState = WIDGET_PRELOAD_PENDING;
    515         mBindWidgetRunnable = new Runnable() {
    516             @Override
    517             public void run() {
    518                 mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId();
    519                 if(AppWidgetManagerCompat.getInstance(mLauncher).bindAppWidgetIdIfAllowed(
    520                         mWidgetLoadingId, pInfo, options)) {
    521                     mWidgetCleanupState = WIDGET_BOUND;
    522                 }
    523             }
    524         };
    525         post(mBindWidgetRunnable);
    526 
    527         mInflateWidgetRunnable = new Runnable() {
    528             @Override
    529             public void run() {
    530                 if (mWidgetCleanupState != WIDGET_BOUND) {
    531                     return;
    532                 }
    533                 AppWidgetHostView hostView = mLauncher.
    534                         getAppWidgetHost().createView(getContext(), mWidgetLoadingId, pInfo);
    535                 info.boundWidget = hostView;
    536                 mWidgetCleanupState = WIDGET_INFLATED;
    537                 hostView.setVisibility(INVISIBLE);
    538                 int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(info.spanX,
    539                         info.spanY, info, false);
    540 
    541                 // We want the first widget layout to be the correct size. This will be important
    542                 // for width size reporting to the AppWidgetManager.
    543                 DragLayer.LayoutParams lp = new DragLayer.LayoutParams(unScaledSize[0],
    544                         unScaledSize[1]);
    545                 lp.x = lp.y = 0;
    546                 lp.customPosition = true;
    547                 hostView.setLayoutParams(lp);
    548                 mLauncher.getDragLayer().addView(hostView);
    549             }
    550         };
    551         post(mInflateWidgetRunnable);
    552     }
    553 
    554     @Override
    555     public void onShortPress(View v) {
    556         // We are anticipating a long press, and we use this time to load bind and instantiate
    557         // the widget. This will need to be cleaned up if it turns out no long press occurs.
    558         if (mCreateWidgetInfo != null) {
    559             // Just in case the cleanup process wasn't properly executed. This shouldn't happen.
    560             cleanupWidgetPreloading(false);
    561         }
    562         mCreateWidgetInfo = new PendingAddWidgetInfo((PendingAddWidgetInfo) v.getTag());
    563         preloadWidget(mCreateWidgetInfo);
    564     }
    565 
    566     private void cleanupWidgetPreloading(boolean widgetWasAdded) {
    567         if (!widgetWasAdded) {
    568             // If the widget was not added, we may need to do further cleanup.
    569             PendingAddWidgetInfo info = mCreateWidgetInfo;
    570             mCreateWidgetInfo = null;
    571 
    572             if (mWidgetCleanupState == WIDGET_PRELOAD_PENDING) {
    573                 // We never did any preloading, so just remove pending callbacks to do so
    574                 removeCallbacks(mBindWidgetRunnable);
    575                 removeCallbacks(mInflateWidgetRunnable);
    576             } else if (mWidgetCleanupState == WIDGET_BOUND) {
    577                  // Delete the widget id which was allocated
    578                 if (mWidgetLoadingId != -1) {
    579                     mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
    580                 }
    581 
    582                 // We never got around to inflating the widget, so remove the callback to do so.
    583                 removeCallbacks(mInflateWidgetRunnable);
    584             } else if (mWidgetCleanupState == WIDGET_INFLATED) {
    585                 // Delete the widget id which was allocated
    586                 if (mWidgetLoadingId != -1) {
    587                     mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
    588                 }
    589 
    590                 // The widget was inflated and added to the DragLayer -- remove it.
    591                 AppWidgetHostView widget = info.boundWidget;
    592                 mLauncher.getDragLayer().removeView(widget);
    593             }
    594         }
    595         mWidgetCleanupState = WIDGET_NO_CLEANUP_REQUIRED;
    596         mWidgetLoadingId = -1;
    597         mCreateWidgetInfo = null;
    598         PagedViewWidget.resetShortPressTarget();
    599     }
    600 
    601     @Override
    602     public void cleanUpShortPress(View v) {
    603         if (!mDraggingWidget) {
    604             cleanupWidgetPreloading(false);
    605         }
    606     }
    607 
    608     private boolean beginDraggingWidget(View v) {
    609         mDraggingWidget = true;
    610         // Get the widget preview as the drag representation
    611         ImageView image = (ImageView) v.findViewById(R.id.widget_preview);
    612         PendingAddItemInfo createItemInfo = (PendingAddItemInfo) v.getTag();
    613 
    614         // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
    615         // we abort the drag.
    616         if (image.getDrawable() == null) {
    617             mDraggingWidget = false;
    618             return false;
    619         }
    620 
    621         // Compose the drag image
    622         Bitmap preview;
    623         Bitmap outline;
    624         float scale = 1f;
    625         Point previewPadding = null;
    626 
    627         if (createItemInfo instanceof PendingAddWidgetInfo) {
    628             // This can happen in some weird cases involving multi-touch. We can't start dragging
    629             // the widget if this is null, so we break out.
    630             if (mCreateWidgetInfo == null) {
    631                 return false;
    632             }
    633 
    634             PendingAddWidgetInfo createWidgetInfo = mCreateWidgetInfo;
    635             createItemInfo = createWidgetInfo;
    636             int spanX = createItemInfo.spanX;
    637             int spanY = createItemInfo.spanY;
    638             int[] size = mLauncher.getWorkspace().estimateItemSize(spanX, spanY,
    639                     createWidgetInfo, true);
    640 
    641             FastBitmapDrawable previewDrawable = (FastBitmapDrawable) image.getDrawable();
    642             float minScale = 1.25f;
    643             int maxWidth, maxHeight;
    644             maxWidth = Math.min((int) (previewDrawable.getIntrinsicWidth() * minScale), size[0]);
    645             maxHeight = Math.min((int) (previewDrawable.getIntrinsicHeight() * minScale), size[1]);
    646 
    647             int[] previewSizeBeforeScale = new int[1];
    648 
    649             preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.info,
    650                     spanX, spanY, maxWidth, maxHeight, null, previewSizeBeforeScale);
    651 
    652             // Compare the size of the drag preview to the preview in the AppsCustomize tray
    653             int previewWidthInAppsCustomize = Math.min(previewSizeBeforeScale[0],
    654                     getWidgetPreviewLoader().maxWidthForWidgetPreview(spanX));
    655             scale = previewWidthInAppsCustomize / (float) preview.getWidth();
    656 
    657             // The bitmap in the AppsCustomize tray is always the the same size, so there
    658             // might be extra pixels around the preview itself - this accounts for that
    659             if (previewWidthInAppsCustomize < previewDrawable.getIntrinsicWidth()) {
    660                 int padding =
    661                         (previewDrawable.getIntrinsicWidth() - previewWidthInAppsCustomize) / 2;
    662                 previewPadding = new Point(padding, 0);
    663             }
    664         } else {
    665             PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) v.getTag();
    666             Drawable icon = mIconCache.getFullResIcon(createShortcutInfo.shortcutActivityInfo);
    667             preview = Utilities.createIconBitmap(icon, mLauncher);
    668             createItemInfo.spanX = createItemInfo.spanY = 1;
    669         }
    670 
    671         // Don't clip alpha values for the drag outline if we're using the default widget preview
    672         boolean clipAlpha = !(createItemInfo instanceof PendingAddWidgetInfo &&
    673                 (((PendingAddWidgetInfo) createItemInfo).previewImage == 0));
    674 
    675         // Save the preview for the outline generation, then dim the preview
    676         outline = Bitmap.createScaledBitmap(preview, preview.getWidth(), preview.getHeight(),
    677                 false);
    678 
    679         // Start the drag
    680         mLauncher.lockScreenOrientation();
    681         mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, outline, clipAlpha);
    682         mDragController.startDrag(image, preview, this, createItemInfo,
    683                 DragController.DRAG_ACTION_COPY, previewPadding, scale);
    684         outline.recycle();
    685         preview.recycle();
    686         return true;
    687     }
    688 
    689     @Override
    690     protected boolean beginDragging(final View v) {
    691         if (!super.beginDragging(v)) return false;
    692 
    693         if (v instanceof BubbleTextView) {
    694             beginDraggingApplication(v);
    695         } else if (v instanceof PagedViewWidget) {
    696             if (!beginDraggingWidget(v)) {
    697                 return false;
    698             }
    699         }
    700 
    701         // We delay entering spring-loaded mode slightly to make sure the UI
    702         // thready is free of any work.
    703         postDelayed(new Runnable() {
    704             @Override
    705             public void run() {
    706                 // We don't enter spring-loaded mode if the drag has been cancelled
    707                 if (mLauncher.getDragController().isDragging()) {
    708                     // Go into spring loaded mode (must happen before we startDrag())
    709                     mLauncher.enterSpringLoadedDragMode();
    710                 }
    711             }
    712         }, 150);
    713 
    714         return true;
    715     }
    716 
    717     /**
    718      * Clean up after dragging.
    719      *
    720      * @param target where the item was dragged to (can be null if the item was flung)
    721      */
    722     private void endDragging(View target, boolean isFlingToDelete, boolean success) {
    723         if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
    724                 !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
    725             // Exit spring loaded mode if we have not successfully dropped or have not handled the
    726             // drop in Workspace
    727             mLauncher.exitSpringLoadedDragModeDelayed(true,
    728                     Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
    729             mLauncher.unlockScreenOrientation(false);
    730         } else {
    731             mLauncher.unlockScreenOrientation(false);
    732         }
    733     }
    734 
    735     @Override
    736     public View getContent() {
    737         if (getChildCount() > 0) {
    738             return getChildAt(0);
    739         }
    740         return null;
    741     }
    742 
    743     @Override
    744     public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
    745         mInTransition = true;
    746         if (toWorkspace) {
    747             cancelAllTasks();
    748         }
    749     }
    750 
    751     @Override
    752     public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
    753     }
    754 
    755     @Override
    756     public void onLauncherTransitionStep(Launcher l, float t) {
    757     }
    758 
    759     @Override
    760     public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
    761         mInTransition = false;
    762         for (AsyncTaskPageData d : mDeferredSyncWidgetPageItems) {
    763             onSyncWidgetPageItems(d, false);
    764         }
    765         mDeferredSyncWidgetPageItems.clear();
    766         for (Runnable r : mDeferredPrepareLoadWidgetPreviewsTasks) {
    767             r.run();
    768         }
    769         mDeferredPrepareLoadWidgetPreviewsTasks.clear();
    770         mForceDrawAllChildrenNextFrame = !toWorkspace;
    771     }
    772 
    773     @Override
    774     public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete,
    775             boolean success) {
    776         // Return early and wait for onFlingToDeleteCompleted if this was the result of a fling
    777         if (isFlingToDelete) return;
    778 
    779         endDragging(target, false, success);
    780 
    781         // Display an error message if the drag failed due to there not being enough space on the
    782         // target layout we were dropping on.
    783         if (!success) {
    784             boolean showOutOfSpaceMessage = false;
    785             if (target instanceof Workspace) {
    786                 int currentScreen = mLauncher.getCurrentWorkspaceScreen();
    787                 Workspace workspace = (Workspace) target;
    788                 CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen);
    789                 ItemInfo itemInfo = (ItemInfo) d.dragInfo;
    790                 if (layout != null) {
    791                     layout.calculateSpans(itemInfo);
    792                     showOutOfSpaceMessage =
    793                             !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY);
    794                 }
    795             }
    796             if (showOutOfSpaceMessage) {
    797                 mLauncher.showOutOfSpaceMessage(false);
    798             }
    799 
    800             d.deferDragViewCleanupPostAnimation = false;
    801         }
    802         cleanupWidgetPreloading(success);
    803         mDraggingWidget = false;
    804     }
    805 
    806     @Override
    807     public void onFlingToDeleteCompleted() {
    808         // We just dismiss the drag when we fling, so cleanup here
    809         endDragging(null, true, true);
    810         cleanupWidgetPreloading(false);
    811         mDraggingWidget = false;
    812     }
    813 
    814     @Override
    815     public boolean supportsFlingToDelete() {
    816         return true;
    817     }
    818 
    819     @Override
    820     public boolean supportsAppInfoDropTarget() {
    821         return true;
    822     }
    823 
    824     @Override
    825     public boolean supportsDeleteDropTarget() {
    826         return false;
    827     }
    828 
    829     @Override
    830     public float getIntrinsicIconScaleFactor() {
    831         LauncherAppState app = LauncherAppState.getInstance();
    832         DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
    833         return (float) grid.allAppsIconSizePx / grid.iconSizePx;
    834     }
    835 
    836     @Override
    837     protected void onDetachedFromWindow() {
    838         super.onDetachedFromWindow();
    839         cancelAllTasks();
    840     }
    841 
    842     @Override
    843     public void trimMemory() {
    844         super.trimMemory();
    845         clearAllWidgetPages();
    846     }
    847 
    848     public void clearAllWidgetPages() {
    849         cancelAllTasks();
    850         int count = getChildCount();
    851         for (int i = 0; i < count; i++) {
    852             View v = getPageAt(i);
    853             if (v instanceof PagedViewGridLayout) {
    854                 ((PagedViewGridLayout) v).removeAllViewsOnPage();
    855                 mDirtyPageContent.set(i, true);
    856             }
    857         }
    858     }
    859 
    860     private void cancelAllTasks() {
    861         // Clean up all the async tasks
    862         Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator();
    863         while (iter.hasNext()) {
    864             AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next();
    865             task.cancel(false);
    866             iter.remove();
    867             mDirtyPageContent.set(task.page, true);
    868 
    869             // We've already preallocated the views for the data to load into, so clear them as well
    870             View v = getPageAt(task.page);
    871             if (v instanceof PagedViewGridLayout) {
    872                 ((PagedViewGridLayout) v).removeAllViewsOnPage();
    873             }
    874         }
    875         mDeferredSyncWidgetPageItems.clear();
    876         mDeferredPrepareLoadWidgetPreviewsTasks.clear();
    877     }
    878 
    879     public void setContentType(ContentType type) {
    880         // Widgets appear to be cleared every time you leave, always force invalidate for them
    881         if (mContentType != type || type == ContentType.Widgets) {
    882             int page = (mContentType != type) ? 0 : getCurrentPage();
    883             mContentType = type;
    884             invalidatePageData(page, true);
    885         }
    886     }
    887 
    888     public ContentType getContentType() {
    889         return mContentType;
    890     }
    891 
    892     protected void snapToPage(int whichPage, int delta, int duration) {
    893         super.snapToPage(whichPage, delta, duration);
    894 
    895         // Update the thread priorities given the direction lookahead
    896         Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator();
    897         while (iter.hasNext()) {
    898             AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next();
    899             int pageIndex = task.page;
    900             if ((mNextPage > mCurrentPage && pageIndex >= mCurrentPage) ||
    901                 (mNextPage < mCurrentPage && pageIndex <= mCurrentPage)) {
    902                 task.setThreadPriority(getThreadPriorityForPage(pageIndex));
    903             } else {
    904                 task.setThreadPriority(Process.THREAD_PRIORITY_LOWEST);
    905             }
    906         }
    907     }
    908 
    909     /*
    910      * Apps PagedView implementation
    911      */
    912     private void setVisibilityOnChildren(ViewGroup layout, int visibility) {
    913         int childCount = layout.getChildCount();
    914         for (int i = 0; i < childCount; ++i) {
    915             layout.getChildAt(i).setVisibility(visibility);
    916         }
    917     }
    918     private void setupPage(AppsCustomizeCellLayout layout) {
    919         layout.setGridSize(mCellCountX, mCellCountY);
    920 
    921         // Note: We force a measure here to get around the fact that when we do layout calculations
    922         // immediately after syncing, we don't have a proper width.  That said, we already know the
    923         // expected page width, so we can actually optimize by hiding all the TextView-based
    924         // children that are expensive to measure, and let that happen naturally later.
    925         setVisibilityOnChildren(layout, View.GONE);
    926         int widthSpec = MeasureSpec.makeMeasureSpec(mContentWidth, MeasureSpec.AT_MOST);
    927         int heightSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.AT_MOST);
    928         layout.measure(widthSpec, heightSpec);
    929 
    930         Drawable bg = getContext().getResources().getDrawable(R.drawable.quantum_panel);
    931         if (bg != null) {
    932             bg.setAlpha(mPageBackgroundsVisible ? 255: 0);
    933             layout.setBackground(bg);
    934         }
    935 
    936         setVisibilityOnChildren(layout, View.VISIBLE);
    937     }
    938 
    939     public void setPageBackgroundsVisible(boolean visible) {
    940         mPageBackgroundsVisible = visible;
    941         int childCount = getChildCount();
    942         for (int i = 0; i < childCount; ++i) {
    943             Drawable bg = getChildAt(i).getBackground();
    944             if (bg != null) {
    945                 bg.setAlpha(visible ? 255 : 0);
    946             }
    947         }
    948     }
    949 
    950     public void syncAppsPageItems(int page, boolean immediate) {
    951         // ensure that we have the right number of items on the pages
    952         final boolean isRtl = isLayoutRtl();
    953         int numCells = mCellCountX * mCellCountY;
    954         int startIndex = page * numCells;
    955         int endIndex = Math.min(startIndex + numCells, mApps.size());
    956         AppsCustomizeCellLayout layout = (AppsCustomizeCellLayout) getPageAt(page);
    957 
    958         layout.removeAllViewsOnPage();
    959         ArrayList<Object> items = new ArrayList<Object>();
    960         ArrayList<Bitmap> images = new ArrayList<Bitmap>();
    961         for (int i = startIndex; i < endIndex; ++i) {
    962             AppInfo info = mApps.get(i);
    963             BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
    964                     R.layout.apps_customize_application, layout, false);
    965             icon.applyFromApplicationInfo(info);
    966             icon.setOnClickListener(mLauncher);
    967             icon.setOnLongClickListener(this);
    968             icon.setOnTouchListener(this);
    969             icon.setOnKeyListener(this);
    970             icon.setOnFocusChangeListener(layout.mFocusHandlerView);
    971 
    972             int index = i - startIndex;
    973             int x = index % mCellCountX;
    974             int y = index / mCellCountX;
    975             if (isRtl) {
    976                 x = mCellCountX - x - 1;
    977             }
    978             layout.addViewToCellLayout(icon, -1, i, new CellLayout.LayoutParams(x,y, 1,1), false);
    979 
    980             items.add(info);
    981             images.add(info.iconBitmap);
    982         }
    983 
    984         enableHwLayersOnVisiblePages();
    985     }
    986 
    987     /**
    988      * A helper to return the priority for loading of the specified widget page.
    989      */
    990     private int getWidgetPageLoadPriority(int page) {
    991         // If we are snapping to another page, use that index as the target page index
    992         int toPage = mCurrentPage;
    993         if (mNextPage > -1) {
    994             toPage = mNextPage;
    995         }
    996 
    997         // We use the distance from the target page as an initial guess of priority, but if there
    998         // are no pages of higher priority than the page specified, then bump up the priority of
    999         // the specified page.
   1000         Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator();
   1001         int minPageDiff = Integer.MAX_VALUE;
   1002         while (iter.hasNext()) {
   1003             AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next();
   1004             minPageDiff = Math.abs(task.page - toPage);
   1005         }
   1006 
   1007         int rawPageDiff = Math.abs(page - toPage);
   1008         return rawPageDiff - Math.min(rawPageDiff, minPageDiff);
   1009     }
   1010     /**
   1011      * Return the appropriate thread priority for loading for a given page (we give the current
   1012      * page much higher priority)
   1013      */
   1014     private int getThreadPriorityForPage(int page) {
   1015         // TODO-APPS_CUSTOMIZE: detect number of cores and set thread priorities accordingly below
   1016         int pageDiff = getWidgetPageLoadPriority(page);
   1017         if (pageDiff <= 0) {
   1018             return Process.THREAD_PRIORITY_LESS_FAVORABLE;
   1019         } else if (pageDiff <= 1) {
   1020             return Process.THREAD_PRIORITY_LOWEST;
   1021         } else {
   1022             return Process.THREAD_PRIORITY_LOWEST;
   1023         }
   1024     }
   1025     private int getSleepForPage(int page) {
   1026         int pageDiff = getWidgetPageLoadPriority(page);
   1027         return Math.max(0, pageDiff * sPageSleepDelay);
   1028     }
   1029     /**
   1030      * Creates and executes a new AsyncTask to load a page of widget previews.
   1031      */
   1032     private void prepareLoadWidgetPreviewsTask(int page, ArrayList<Object> widgets,
   1033             int cellWidth, int cellHeight, int cellCountX) {
   1034 
   1035         // Prune all tasks that are no longer needed
   1036         Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator();
   1037         while (iter.hasNext()) {
   1038             AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next();
   1039             int taskPage = task.page;
   1040             if (taskPage < getAssociatedLowerPageBound(mCurrentPage) ||
   1041                     taskPage > getAssociatedUpperPageBound(mCurrentPage)) {
   1042                 task.cancel(false);
   1043                 iter.remove();
   1044             } else {
   1045                 task.setThreadPriority(getThreadPriorityForPage(taskPage));
   1046             }
   1047         }
   1048 
   1049         // We introduce a slight delay to order the loading of side pages so that we don't thrash
   1050         final int sleepMs = getSleepForPage(page);
   1051         AsyncTaskPageData pageData = new AsyncTaskPageData(page, widgets, cellWidth, cellHeight,
   1052             new AsyncTaskCallback() {
   1053                 @Override
   1054                 public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) {
   1055                     try {
   1056                         try {
   1057                             Thread.sleep(sleepMs);
   1058                         } catch (Exception e) {}
   1059                         loadWidgetPreviewsInBackground(task, data);
   1060                     } finally {
   1061                         if (task.isCancelled()) {
   1062                             data.cleanup(true);
   1063                         }
   1064                     }
   1065                 }
   1066             },
   1067             new AsyncTaskCallback() {
   1068                 @Override
   1069                 public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) {
   1070                     mRunningTasks.remove(task);
   1071                     if (task.isCancelled()) return;
   1072                     // do cleanup inside onSyncWidgetPageItems
   1073                     onSyncWidgetPageItems(data, false);
   1074                 }
   1075             }, getWidgetPreviewLoader());
   1076 
   1077         // Ensure that the task is appropriately prioritized and runs in parallel
   1078         AppsCustomizeAsyncTask t = new AppsCustomizeAsyncTask(page,
   1079                 AsyncTaskPageData.Type.LoadWidgetPreviewData);
   1080         t.setThreadPriority(getThreadPriorityForPage(page));
   1081         t.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, pageData);
   1082         mRunningTasks.add(t);
   1083     }
   1084 
   1085     /*
   1086      * Widgets PagedView implementation
   1087      */
   1088     private void setupPage(PagedViewGridLayout layout) {
   1089         // Note: We force a measure here to get around the fact that when we do layout calculations
   1090         // immediately after syncing, we don't have a proper width.
   1091         int widthSpec = MeasureSpec.makeMeasureSpec(mContentWidth, MeasureSpec.AT_MOST);
   1092         int heightSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.AT_MOST);
   1093 
   1094         Drawable bg = getContext().getResources().getDrawable(R.drawable.quantum_panel_dark);
   1095         if (bg != null) {
   1096             bg.setAlpha(mPageBackgroundsVisible ? 255 : 0);
   1097             layout.setBackground(bg);
   1098         }
   1099         layout.measure(widthSpec, heightSpec);
   1100     }
   1101 
   1102     public void syncWidgetPageItems(final int page, final boolean immediate) {
   1103         int numItemsPerPage = mWidgetCountX * mWidgetCountY;
   1104 
   1105         final PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(page);
   1106 
   1107         // Calculate the dimensions of each cell we are giving to each widget
   1108         final ArrayList<Object> items = new ArrayList<Object>();
   1109         int contentWidth = mContentWidth - layout.getPaddingLeft() - layout.getPaddingRight();
   1110         final int cellWidth = contentWidth / mWidgetCountX;
   1111         int contentHeight = mContentHeight - layout.getPaddingTop() - layout.getPaddingBottom();
   1112 
   1113         final int cellHeight = contentHeight / mWidgetCountY;
   1114 
   1115         // Prepare the set of widgets to load previews for in the background
   1116         int offset = page * numItemsPerPage;
   1117         for (int i = offset; i < Math.min(offset + numItemsPerPage, mWidgets.size()); ++i) {
   1118             items.add(mWidgets.get(i));
   1119         }
   1120 
   1121         // Prepopulate the pages with the other widget info, and fill in the previews later
   1122         layout.setColumnCount(layout.getCellCountX());
   1123         for (int i = 0; i < items.size(); ++i) {
   1124             Object rawInfo = items.get(i);
   1125             PendingAddItemInfo createItemInfo = null;
   1126             PagedViewWidget widget = (PagedViewWidget) mLayoutInflater.inflate(
   1127                     R.layout.apps_customize_widget, layout, false);
   1128             if (rawInfo instanceof AppWidgetProviderInfo) {
   1129                 // Fill in the widget information
   1130                 AppWidgetProviderInfo info = (AppWidgetProviderInfo) rawInfo;
   1131                 createItemInfo = new PendingAddWidgetInfo(info, null, null);
   1132 
   1133                 // Determine the widget spans and min resize spans.
   1134                 int[] spanXY = Launcher.getSpanForWidget(mLauncher, info);
   1135                 createItemInfo.spanX = spanXY[0];
   1136                 createItemInfo.spanY = spanXY[1];
   1137                 int[] minSpanXY = Launcher.getMinSpanForWidget(mLauncher, info);
   1138                 createItemInfo.minSpanX = minSpanXY[0];
   1139                 createItemInfo.minSpanY = minSpanXY[1];
   1140 
   1141                 widget.applyFromAppWidgetProviderInfo(info, -1, spanXY, getWidgetPreviewLoader());
   1142                 widget.setTag(createItemInfo);
   1143                 widget.setShortPressListener(this);
   1144             } else if (rawInfo instanceof ResolveInfo) {
   1145                 // Fill in the shortcuts information
   1146                 ResolveInfo info = (ResolveInfo) rawInfo;
   1147                 createItemInfo = new PendingAddShortcutInfo(info.activityInfo);
   1148                 createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
   1149                 createItemInfo.componentName = new ComponentName(info.activityInfo.packageName,
   1150                         info.activityInfo.name);
   1151                 widget.applyFromResolveInfo(mPackageManager, info, getWidgetPreviewLoader());
   1152                 widget.setTag(createItemInfo);
   1153             }
   1154             widget.setOnClickListener(this);
   1155             widget.setOnLongClickListener(this);
   1156             widget.setOnTouchListener(this);
   1157             widget.setOnKeyListener(this);
   1158 
   1159             // Layout each widget
   1160             int ix = i % mWidgetCountX;
   1161             int iy = i / mWidgetCountX;
   1162 
   1163             if (ix > 0) {
   1164                 View border = widget.findViewById(R.id.left_border);
   1165                 border.setVisibility(View.VISIBLE);
   1166             }
   1167             if (ix < mWidgetCountX - 1) {
   1168                 View border = widget.findViewById(R.id.right_border);
   1169                 border.setVisibility(View.VISIBLE);
   1170             }
   1171 
   1172             GridLayout.LayoutParams lp = new GridLayout.LayoutParams(
   1173                     GridLayout.spec(iy, GridLayout.START),
   1174                     GridLayout.spec(ix, GridLayout.TOP));
   1175             lp.width = cellWidth;
   1176             lp.height = cellHeight;
   1177             lp.setGravity(Gravity.TOP | Gravity.START);
   1178             layout.addView(widget, lp);
   1179         }
   1180 
   1181         // wait until a call on onLayout to start loading, because
   1182         // PagedViewWidget.getPreviewSize() will return 0 if it hasn't been laid out
   1183         // TODO: can we do a measure/layout immediately?
   1184         layout.setOnLayoutListener(new Runnable() {
   1185             public void run() {
   1186                 // Load the widget previews
   1187                 int maxPreviewWidth = cellWidth;
   1188                 int maxPreviewHeight = cellHeight;
   1189                 if (layout.getChildCount() > 0) {
   1190                     PagedViewWidget w = (PagedViewWidget) layout.getChildAt(0);
   1191                     int[] maxSize = w.getPreviewSize();
   1192                     maxPreviewWidth = maxSize[0];
   1193                     maxPreviewHeight = maxSize[1];
   1194                 }
   1195 
   1196                 getWidgetPreviewLoader().setPreviewSize(
   1197                         maxPreviewWidth, maxPreviewHeight, mWidgetSpacingLayout);
   1198                 if (immediate) {
   1199                     AsyncTaskPageData data = new AsyncTaskPageData(page, items,
   1200                             maxPreviewWidth, maxPreviewHeight, null, null, getWidgetPreviewLoader());
   1201                     loadWidgetPreviewsInBackground(null, data);
   1202                     onSyncWidgetPageItems(data, immediate);
   1203                 } else {
   1204                     if (mInTransition) {
   1205                         mDeferredPrepareLoadWidgetPreviewsTasks.add(this);
   1206                     } else {
   1207                         prepareLoadWidgetPreviewsTask(page, items,
   1208                                 maxPreviewWidth, maxPreviewHeight, mWidgetCountX);
   1209                     }
   1210                 }
   1211                 layout.setOnLayoutListener(null);
   1212             }
   1213         });
   1214     }
   1215     private void loadWidgetPreviewsInBackground(AppsCustomizeAsyncTask task,
   1216             AsyncTaskPageData data) {
   1217         // loadWidgetPreviewsInBackground can be called without a task to load a set of widget
   1218         // previews synchronously
   1219         if (task != null) {
   1220             // Ensure that this task starts running at the correct priority
   1221             task.syncThreadPriority();
   1222         }
   1223 
   1224         // Load each of the widget/shortcut previews
   1225         ArrayList<Object> items = data.items;
   1226         ArrayList<Bitmap> images = data.generatedImages;
   1227         int count = items.size();
   1228         for (int i = 0; i < count; ++i) {
   1229             if (task != null) {
   1230                 // Ensure we haven't been cancelled yet
   1231                 if (task.isCancelled()) break;
   1232                 // Before work on each item, ensure that this task is running at the correct
   1233                 // priority
   1234                 task.syncThreadPriority();
   1235             }
   1236 
   1237             images.add(getWidgetPreviewLoader().getPreview(items.get(i)));
   1238         }
   1239     }
   1240 
   1241     private void onSyncWidgetPageItems(AsyncTaskPageData data, boolean immediatelySyncItems) {
   1242         if (!immediatelySyncItems && mInTransition) {
   1243             mDeferredSyncWidgetPageItems.add(data);
   1244             return;
   1245         }
   1246         try {
   1247             int page = data.page;
   1248             PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(page);
   1249 
   1250             ArrayList<Object> items = data.items;
   1251             int count = items.size();
   1252             for (int i = 0; i < count; ++i) {
   1253                 PagedViewWidget widget = (PagedViewWidget) layout.getChildAt(i);
   1254                 if (widget != null) {
   1255                     Bitmap preview = data.generatedImages.get(i);
   1256                     widget.applyPreview(new FastBitmapDrawable(preview), i);
   1257                 }
   1258             }
   1259 
   1260             enableHwLayersOnVisiblePages();
   1261 
   1262             // Update all thread priorities
   1263             Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator();
   1264             while (iter.hasNext()) {
   1265                 AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next();
   1266                 int pageIndex = task.page;
   1267                 task.setThreadPriority(getThreadPriorityForPage(pageIndex));
   1268             }
   1269         } finally {
   1270             data.cleanup(false);
   1271         }
   1272     }
   1273 
   1274     @Override
   1275     public void syncPages() {
   1276         disablePagedViewAnimations();
   1277 
   1278         removeAllViews();
   1279         cancelAllTasks();
   1280 
   1281         Context context = getContext();
   1282         if (mContentType == ContentType.Applications) {
   1283             for (int i = 0; i < mNumAppsPages; ++i) {
   1284                 AppsCustomizeCellLayout layout = new AppsCustomizeCellLayout(context);
   1285                 setupPage(layout);
   1286                 addView(layout, new PagedView.LayoutParams(LayoutParams.MATCH_PARENT,
   1287                         LayoutParams.MATCH_PARENT));
   1288             }
   1289         } else if (mContentType == ContentType.Widgets) {
   1290             for (int j = 0; j < mNumWidgetPages; ++j) {
   1291                 PagedViewGridLayout layout = new PagedViewGridLayout(context, mWidgetCountX,
   1292                         mWidgetCountY);
   1293                 setupPage(layout);
   1294                 addView(layout, new PagedView.LayoutParams(LayoutParams.MATCH_PARENT,
   1295                         LayoutParams.MATCH_PARENT));
   1296             }
   1297         } else {
   1298             throw new RuntimeException("Invalid ContentType");
   1299         }
   1300 
   1301         enablePagedViewAnimations();
   1302     }
   1303 
   1304     @Override
   1305     public void syncPageItems(int page, boolean immediate) {
   1306         if (mContentType == ContentType.Widgets) {
   1307             syncWidgetPageItems(page, immediate);
   1308         } else {
   1309             syncAppsPageItems(page, immediate);
   1310         }
   1311     }
   1312 
   1313     // We want our pages to be z-ordered such that the further a page is to the left, the higher
   1314     // it is in the z-order. This is important to insure touch events are handled correctly.
   1315     View getPageAt(int index) {
   1316         return getChildAt(indexToPage(index));
   1317     }
   1318 
   1319     @Override
   1320     protected int indexToPage(int index) {
   1321         return getChildCount() - index - 1;
   1322     }
   1323 
   1324     // In apps customize, we have a scrolling effect which emulates pulling cards off of a stack.
   1325     @Override
   1326     protected void screenScrolled(int screenCenter) {
   1327         super.screenScrolled(screenCenter);
   1328         enableHwLayersOnVisiblePages();
   1329     }
   1330 
   1331     private void enableHwLayersOnVisiblePages() {
   1332         final int screenCount = getChildCount();
   1333 
   1334         getVisiblePages(mTempVisiblePagesRange);
   1335         int leftScreen = mTempVisiblePagesRange[0];
   1336         int rightScreen = mTempVisiblePagesRange[1];
   1337         int forceDrawScreen = -1;
   1338         if (leftScreen == rightScreen) {
   1339             // make sure we're caching at least two pages always
   1340             if (rightScreen < screenCount - 1) {
   1341                 rightScreen++;
   1342                 forceDrawScreen = rightScreen;
   1343             } else if (leftScreen > 0) {
   1344                 leftScreen--;
   1345                 forceDrawScreen = leftScreen;
   1346             }
   1347         } else {
   1348             forceDrawScreen = leftScreen + 1;
   1349         }
   1350 
   1351         for (int i = 0; i < screenCount; i++) {
   1352             final View layout = (View) getPageAt(i);
   1353             if (!(leftScreen <= i && i <= rightScreen &&
   1354                     (i == forceDrawScreen || shouldDrawChild(layout)))) {
   1355                 layout.setLayerType(LAYER_TYPE_NONE, null);
   1356             }
   1357         }
   1358 
   1359         for (int i = 0; i < screenCount; i++) {
   1360             final View layout = (View) getPageAt(i);
   1361 
   1362             if (leftScreen <= i && i <= rightScreen &&
   1363                     (i == forceDrawScreen || shouldDrawChild(layout))) {
   1364                 if (layout.getLayerType() != LAYER_TYPE_HARDWARE) {
   1365                     layout.setLayerType(LAYER_TYPE_HARDWARE, null);
   1366                 }
   1367             }
   1368         }
   1369     }
   1370 
   1371     protected void overScroll(float amount) {
   1372         dampedOverScroll(amount);
   1373     }
   1374 
   1375     /**
   1376      * Used by the parent to get the content width to set the tab bar to
   1377      * @return
   1378      */
   1379     public int getPageContentWidth() {
   1380         return mContentWidth;
   1381     }
   1382 
   1383     @Override
   1384     protected void onPageEndMoving() {
   1385         super.onPageEndMoving();
   1386         mForceDrawAllChildrenNextFrame = true;
   1387         // We reset the save index when we change pages so that it will be recalculated on next
   1388         // rotation
   1389         mSaveInstanceStateItemIndex = -1;
   1390     }
   1391 
   1392     /*
   1393      * AllAppsView implementation
   1394      */
   1395     public void setup(Launcher launcher, DragController dragController) {
   1396         mLauncher = launcher;
   1397         mDragController = dragController;
   1398     }
   1399 
   1400     /**
   1401      * We should call thise method whenever the core data changes (mApps, mWidgets) so that we can
   1402      * appropriately determine when to invalidate the PagedView page data.  In cases where the data
   1403      * has yet to be set, we can requestLayout() and wait for onDataReady() to be called in the
   1404      * next onMeasure() pass, which will trigger an invalidatePageData() itself.
   1405      */
   1406     private void invalidateOnDataChange() {
   1407         if (!isDataReady()) {
   1408             // The next layout pass will trigger data-ready if both widgets and apps are set, so
   1409             // request a layout to trigger the page data when ready.
   1410             requestLayout();
   1411         } else {
   1412             cancelAllTasks();
   1413             invalidatePageData();
   1414         }
   1415     }
   1416 
   1417     public void setApps(ArrayList<AppInfo> list) {
   1418         if (!LauncherAppState.isDisableAllApps()) {
   1419             mApps = list;
   1420             Collections.sort(mApps, LauncherModel.getAppNameComparator());
   1421             updatePageCountsAndInvalidateData();
   1422         }
   1423     }
   1424     private void addAppsWithoutInvalidate(ArrayList<AppInfo> list) {
   1425         // We add it in place, in alphabetical order
   1426         int count = list.size();
   1427         for (int i = 0; i < count; ++i) {
   1428             AppInfo info = list.get(i);
   1429             int index = Collections.binarySearch(mApps, info, LauncherModel.getAppNameComparator());
   1430             if (index < 0) {
   1431                 mApps.add(-(index + 1), info);
   1432             }
   1433         }
   1434     }
   1435     public void addApps(ArrayList<AppInfo> list) {
   1436         if (!LauncherAppState.isDisableAllApps()) {
   1437             addAppsWithoutInvalidate(list);
   1438             updatePageCountsAndInvalidateData();
   1439         }
   1440     }
   1441     private int findAppByComponent(List<AppInfo> list, AppInfo item) {
   1442         ComponentName removeComponent = item.intent.getComponent();
   1443         int length = list.size();
   1444         for (int i = 0; i < length; ++i) {
   1445             AppInfo info = list.get(i);
   1446             if (info.user.equals(item.user)
   1447                     && info.intent.getComponent().equals(removeComponent)) {
   1448                 return i;
   1449             }
   1450         }
   1451         return -1;
   1452     }
   1453     private void removeAppsWithoutInvalidate(ArrayList<AppInfo> list) {
   1454         // loop through all the apps and remove apps that have the same component
   1455         int length = list.size();
   1456         for (int i = 0; i < length; ++i) {
   1457             AppInfo info = list.get(i);
   1458             int removeIndex = findAppByComponent(mApps, info);
   1459             if (removeIndex > -1) {
   1460                 mApps.remove(removeIndex);
   1461             }
   1462         }
   1463     }
   1464     public void removeApps(ArrayList<AppInfo> appInfos) {
   1465         if (!LauncherAppState.isDisableAllApps()) {
   1466             removeAppsWithoutInvalidate(appInfos);
   1467             updatePageCountsAndInvalidateData();
   1468         }
   1469     }
   1470     public void updateApps(ArrayList<AppInfo> list) {
   1471         // We remove and re-add the updated applications list because it's properties may have
   1472         // changed (ie. the title), and this will ensure that the items will be in their proper
   1473         // place in the list.
   1474         if (!LauncherAppState.isDisableAllApps()) {
   1475             removeAppsWithoutInvalidate(list);
   1476             addAppsWithoutInvalidate(list);
   1477             updatePageCountsAndInvalidateData();
   1478         }
   1479     }
   1480 
   1481     public void reset() {
   1482         // If we have reset, then we should not continue to restore the previous state
   1483         mSaveInstanceStateItemIndex = -1;
   1484 
   1485         if (mContentType != ContentType.Applications) {
   1486             setContentType(ContentType.Applications);
   1487         }
   1488 
   1489         if (mCurrentPage != 0) {
   1490             invalidatePageData(0);
   1491         }
   1492     }
   1493 
   1494     private AppsCustomizeTabHost getTabHost() {
   1495         return (AppsCustomizeTabHost) mLauncher.findViewById(R.id.apps_customize_pane);
   1496     }
   1497 
   1498     public void dumpState() {
   1499         // TODO: Dump information related to current list of Applications, Widgets, etc.
   1500         AppInfo.dumpApplicationInfoList(TAG, "mApps", mApps);
   1501         dumpAppWidgetProviderInfoList(TAG, "mWidgets", mWidgets);
   1502     }
   1503 
   1504     private void dumpAppWidgetProviderInfoList(String tag, String label,
   1505             ArrayList<Object> list) {
   1506         Log.d(tag, label + " size=" + list.size());
   1507         for (Object i: list) {
   1508             if (i instanceof AppWidgetProviderInfo) {
   1509                 AppWidgetProviderInfo info = (AppWidgetProviderInfo) i;
   1510                 Log.d(tag, "   label=\"" + info.label + "\" previewImage=" + info.previewImage
   1511                         + " resizeMode=" + info.resizeMode + " configure=" + info.configure
   1512                         + " initialLayout=" + info.initialLayout
   1513                         + " minWidth=" + info.minWidth + " minHeight=" + info.minHeight);
   1514             } else if (i instanceof ResolveInfo) {
   1515                 ResolveInfo info = (ResolveInfo) i;
   1516                 Log.d(tag, "   label=\"" + info.loadLabel(mPackageManager) + "\" icon="
   1517                         + info.icon);
   1518             }
   1519         }
   1520     }
   1521 
   1522     public void surrender() {
   1523         // TODO: If we are in the middle of any process (ie. for holographic outlines, etc) we
   1524         // should stop this now.
   1525 
   1526         // Stop all background tasks
   1527         cancelAllTasks();
   1528     }
   1529 
   1530     /*
   1531      * We load an extra page on each side to prevent flashes from scrolling and loading of the
   1532      * widget previews in the background with the AsyncTasks.
   1533      */
   1534     final static int sLookBehindPageCount = 2;
   1535     final static int sLookAheadPageCount = 2;
   1536     protected int getAssociatedLowerPageBound(int page) {
   1537         final int count = getChildCount();
   1538         int windowSize = Math.min(count, sLookBehindPageCount + sLookAheadPageCount + 1);
   1539         int windowMinIndex = Math.max(Math.min(page - sLookBehindPageCount, count - windowSize), 0);
   1540         return windowMinIndex;
   1541     }
   1542     protected int getAssociatedUpperPageBound(int page) {
   1543         final int count = getChildCount();
   1544         int windowSize = Math.min(count, sLookBehindPageCount + sLookAheadPageCount + 1);
   1545         int windowMaxIndex = Math.min(Math.max(page + sLookAheadPageCount, windowSize - 1),
   1546                 count - 1);
   1547         return windowMaxIndex;
   1548     }
   1549 
   1550     protected String getCurrentPageDescription() {
   1551         int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
   1552         int stringId = R.string.default_scroll_format;
   1553         int count = 0;
   1554 
   1555         if (mContentType == ContentType.Applications) {
   1556             stringId = R.string.apps_customize_apps_scroll_format;
   1557             count = mNumAppsPages;
   1558         } else if (mContentType == ContentType.Widgets) {
   1559             stringId = R.string.apps_customize_widgets_scroll_format;
   1560             count = mNumWidgetPages;
   1561         } else {
   1562             throw new RuntimeException("Invalid ContentType");
   1563         }
   1564 
   1565         return String.format(getContext().getString(stringId), page + 1, count);
   1566     }
   1567 }
   1568