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