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