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