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