19258b6bc66e09368ada54001f619d53b4fc976d5ager@chromium.org/*
23484964a86451e86dcf04be9bd8c0d76ee04f081rossberg@chromium.org * Copyright (C) 2011 The Android Open Source Project
33484964a86451e86dcf04be9bd8c0d76ee04f081rossberg@chromium.org *
443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen * Licensed under the Apache License, Version 2.0 (the "License");
543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen * you may not use this file except in compliance with the License.
643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen * You may obtain a copy of the License at
743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen *
843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen *      http://www.apache.org/licenses/LICENSE-2.0
943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen *
1043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen * Unless required by applicable law or agreed to in writing, software
1143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen * distributed under the License is distributed on an "AS IS" BASIS,
1243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen * See the License for the specific language governing permissions and
1443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen * limitations under the License.
1543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen */
1643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
1743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansenpackage com.android.launcher2;
1843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
19f7a588466d1e61e14fec951e5f10c7cec501b3b4jkummerow@chromium.orgimport android.animation.AnimatorSet;
20f7a588466d1e61e14fec951e5f10c7cec501b3b4jkummerow@chromium.orgimport android.animation.ValueAnimator;
21f7a588466d1e61e14fec951e5f10c7cec501b3b4jkummerow@chromium.orgimport android.appwidget.AppWidgetHostView;
22f7a588466d1e61e14fec951e5f10c7cec501b3b4jkummerow@chromium.orgimport android.appwidget.AppWidgetManager;
23f7a588466d1e61e14fec951e5f10c7cec501b3b4jkummerow@chromium.orgimport android.appwidget.AppWidgetProviderInfo;
24f7a588466d1e61e14fec951e5f10c7cec501b3b4jkummerow@chromium.orgimport android.content.ComponentName;
25f7a588466d1e61e14fec951e5f10c7cec501b3b4jkummerow@chromium.orgimport android.content.Context;
26f7a588466d1e61e14fec951e5f10c7cec501b3b4jkummerow@chromium.orgimport android.content.Intent;
2788d326b0935b802e71e3a75e02f31fae8c2af96cmstarzinger@chromium.orgimport android.content.pm.LauncherActivityInfo;
2843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansenimport android.content.pm.LauncherApps;
29fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.orgimport android.content.pm.PackageManager;
3043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansenimport android.content.pm.ResolveInfo;
317c537e2abe09729ed6cb827b4dd206470d8c4a42ager@chromium.orgimport android.content.res.Configuration;
3243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansenimport android.content.res.Resources;
3343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansenimport android.content.res.TypedArray;
3443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansenimport android.graphics.Bitmap;
3543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansenimport android.graphics.Canvas;
36fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.orgimport android.graphics.Point;
37fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.orgimport android.graphics.Rect;
38fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.orgimport android.graphics.drawable.Drawable;
3931c0e32e19ad3df48525fa9e7b2d1c0c07496d00machenbach@chromium.orgimport android.os.AsyncTask;
40fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.orgimport android.os.Build;
41fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.orgimport android.os.Bundle;
42fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.orgimport android.os.Process;
43fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.orgimport android.util.AttributeSet;
44fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.orgimport android.util.Log;
45fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.orgimport android.util.SparseArray;
4643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansenimport android.view.Gravity;
47fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.orgimport android.view.KeyEvent;
48fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.orgimport android.view.LayoutInflater;
49f705b5034dc5bc422ac1019b591469a7d0534772mstarzinger@chromium.orgimport android.view.View;
50fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.orgimport android.view.ViewGroup;
51fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.orgimport android.view.animation.AccelerateInterpolator;
52fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.orgimport android.view.animation.DecelerateInterpolator;
53fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.orgimport android.widget.GridLayout;
54fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.orgimport android.widget.ImageView;
55f705b5034dc5bc422ac1019b591469a7d0534772mstarzinger@chromium.orgimport android.widget.Toast;
56f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
57f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.orgimport com.android.launcher.R;
58fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.orgimport com.android.launcher2.DropTarget.DragObject;
59fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.org
60fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.orgimport java.util.ArrayList;
6143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansenimport java.util.Collections;
62fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.orgimport java.util.Iterator;
63f705b5034dc5bc422ac1019b591469a7d0534772mstarzinger@chromium.orgimport java.util.List;
64fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.org
65fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.org/**
6643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen * A simple callback interface which also provides the results of the task.
67fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.org */
68fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.orginterface AsyncTaskCallback {
6943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data);
70fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.org}
714980dff4208f9b77bc5320af43d7cc4b2a3d9688ricow@chromium.org
729258b6bc66e09368ada54001f619d53b4fc976d5ager@chromium.org/**
739258b6bc66e09368ada54001f619d53b4fc976d5ager@chromium.org * The data needed to perform either of the custom AsyncTasks.
74fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.org */
75f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.orgclass AsyncTaskPageData {
76fb144a0716afe7ab8bf245f2391a9e53b3db3c89fschneider@chromium.org    enum Type {
7743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        LoadWidgetPreviewData
7843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    }
7943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
8041044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org    AsyncTaskPageData(int p, ArrayList<Object> l, int cw, int ch, AsyncTaskCallback bgR,
8143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            AsyncTaskCallback postR, WidgetPreviewLoader w) {
8243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        page = p;
8343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        items = l;
8443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        generatedImages = new ArrayList<Bitmap>();
8543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        maxImageWidth = cw;
8643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        maxImageHeight = ch;
87eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org        doInBackgroundCallback = bgR;
889258b6bc66e09368ada54001f619d53b4fc976d5ager@chromium.org        postExecuteCallback = postR;
897c537e2abe09729ed6cb827b4dd206470d8c4a42ager@chromium.org        widgetPreviewLoader = w;
907c537e2abe09729ed6cb827b4dd206470d8c4a42ager@chromium.org    }
917c537e2abe09729ed6cb827b4dd206470d8c4a42ager@chromium.org    void cleanup(boolean cancelled) {
92eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org        // Clean up any references to source/generated bitmaps
939258b6bc66e09368ada54001f619d53b4fc976d5ager@chromium.org        if (generatedImages != null) {
948ccb0be15fd0cd7e7804e90fa1ebf302cb7fa44bkasperl@chromium.org            if (cancelled) {
958ccb0be15fd0cd7e7804e90fa1ebf302cb7fa44bkasperl@chromium.org                for (int i = 0; i < generatedImages.size(); i++) {
968ccb0be15fd0cd7e7804e90fa1ebf302cb7fa44bkasperl@chromium.org                    widgetPreviewLoader.recycleBitmap(items.get(i), generatedImages.get(i));
979258b6bc66e09368ada54001f619d53b4fc976d5ager@chromium.org                }
9841044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org            }
9943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            generatedImages.clear();
10043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        }
10143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    }
10243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    int page;
10343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    ArrayList<Object> items;
1040c20e676f8a0209982ff89e5a9c707771748a585fschneider@chromium.org    ArrayList<Bitmap> sourceImages;
10532d961d4454609ab4251a760fc46b19f661da90clrn@chromium.org    ArrayList<Bitmap> generatedImages;
1060c20e676f8a0209982ff89e5a9c707771748a585fschneider@chromium.org    int maxImageWidth;
1070c20e676f8a0209982ff89e5a9c707771748a585fschneider@chromium.org    int maxImageHeight;
1080c20e676f8a0209982ff89e5a9c707771748a585fschneider@chromium.org    AsyncTaskCallback doInBackgroundCallback;
1090c20e676f8a0209982ff89e5a9c707771748a585fschneider@chromium.org    AsyncTaskCallback postExecuteCallback;
1100c20e676f8a0209982ff89e5a9c707771748a585fschneider@chromium.org    WidgetPreviewLoader widgetPreviewLoader;
1110c20e676f8a0209982ff89e5a9c707771748a585fschneider@chromium.org}
1120c20e676f8a0209982ff89e5a9c707771748a585fschneider@chromium.org
1130c20e676f8a0209982ff89e5a9c707771748a585fschneider@chromium.org/**
1140c20e676f8a0209982ff89e5a9c707771748a585fschneider@chromium.org * A generic template for an async task used in AppsCustomize.
1150c20e676f8a0209982ff89e5a9c707771748a585fschneider@chromium.org */
11632d961d4454609ab4251a760fc46b19f661da90clrn@chromium.orgclass AppsCustomizeAsyncTask extends AsyncTask<AsyncTaskPageData, Void, AsyncTaskPageData> {
11732d961d4454609ab4251a760fc46b19f661da90clrn@chromium.org    AppsCustomizeAsyncTask(int p, AsyncTaskPageData.Type ty) {
11832d961d4454609ab4251a760fc46b19f661da90clrn@chromium.org        page = p;
11932d961d4454609ab4251a760fc46b19f661da90clrn@chromium.org        threadPriority = Process.THREAD_PRIORITY_DEFAULT;
12032d961d4454609ab4251a760fc46b19f661da90clrn@chromium.org        dataType = ty;
12132d961d4454609ab4251a760fc46b19f661da90clrn@chromium.org    }
12213bd294acf56c7f824d92d4941a2aeb3cec58e0ckmillikin@chromium.org    @Override
1230c20e676f8a0209982ff89e5a9c707771748a585fschneider@chromium.org    protected AsyncTaskPageData doInBackground(AsyncTaskPageData... params) {
1240c20e676f8a0209982ff89e5a9c707771748a585fschneider@chromium.org        if (params.length != 1) return null;
12513bd294acf56c7f824d92d4941a2aeb3cec58e0ckmillikin@chromium.org        // Load each of the widget previews in the background
12613bd294acf56c7f824d92d4941a2aeb3cec58e0ckmillikin@chromium.org        params[0].doInBackgroundCallback.run(this, params[0]);
12732d961d4454609ab4251a760fc46b19f661da90clrn@chromium.org        return params[0];
1280c20e676f8a0209982ff89e5a9c707771748a585fschneider@chromium.org    }
1290c20e676f8a0209982ff89e5a9c707771748a585fschneider@chromium.org    @Override
13043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    protected void onPostExecute(AsyncTaskPageData result) {
1310c20e676f8a0209982ff89e5a9c707771748a585fschneider@chromium.org        // All the widget previews are loaded, so we can just callback to inflate the page
1320c20e676f8a0209982ff89e5a9c707771748a585fschneider@chromium.org        result.postExecuteCallback.run(this, result);
1330c20e676f8a0209982ff89e5a9c707771748a585fschneider@chromium.org    }
1340c20e676f8a0209982ff89e5a9c707771748a585fschneider@chromium.org
13543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    void setThreadPriority(int p) {
13641044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org        threadPriority = p;
13743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    }
13843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    void syncThreadPriority() {
13943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        Process.setThreadPriority(threadPriority);
14043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    }
14143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
14243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    // The page that this async task is associated with
14343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    AsyncTaskPageData.Type dataType;
14443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    int page;
14543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    int threadPriority;
14643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen}
14743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
1487c537e2abe09729ed6cb827b4dd206470d8c4a42ager@chromium.org/**
149ac6aa175ab59d65cfb7a88dbb621e1d7f1a80b8fsgjesse@chromium.org * The Apps/Customize page that displays all the applications, widgets, and shortcuts.
15041044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org */
1517c537e2abe09729ed6cb827b4dd206470d8c4a42ager@chromium.orgpublic class AppsCustomizePagedView extends PagedViewWithDraggableItems implements
15243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        View.OnClickListener, View.OnKeyListener, DragSource,
15343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        PagedViewIcon.PressedCallback, PagedViewWidget.ShortPressListener,
154eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org        LauncherTransitionable {
15543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    static final String TAG = "AppsCustomizePagedView";
156ac6aa175ab59d65cfb7a88dbb621e1d7f1a80b8fsgjesse@chromium.org
15743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    /**
158d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org     * The different content types that this paged view can show.
15943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen     */
16043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    public enum ContentType {
16143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        Applications,
16241044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org        Widgets
16343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    }
16443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
1658ccb0be15fd0cd7e7804e90fa1ebf302cb7fa44bkasperl@chromium.org    // Refs
166eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org    private Launcher mLauncher;
16786f77b7fe492ed2bdfbf4e1147dab2f09c7d7003kasperl@chromium.org    private DragController mDragController;
168ea4f62e1df22417fc8dc2c2425485dca98b13d07ager@chromium.org    private final LayoutInflater mLayoutInflater;
16986f77b7fe492ed2bdfbf4e1147dab2f09c7d7003kasperl@chromium.org    private final PackageManager mPackageManager;
17086f77b7fe492ed2bdfbf4e1147dab2f09c7d7003kasperl@chromium.org
17186f77b7fe492ed2bdfbf4e1147dab2f09c7d7003kasperl@chromium.org    // Save and Restore
1725c838251403b0be9a882540f1922577abba4c872ager@chromium.org    private int mSaveInstanceStateItemIndex = -1;
17386f77b7fe492ed2bdfbf4e1147dab2f09c7d7003kasperl@chromium.org    private PagedViewIcon mPressedIcon;
17486f77b7fe492ed2bdfbf4e1147dab2f09c7d7003kasperl@chromium.org
17586f77b7fe492ed2bdfbf4e1147dab2f09c7d7003kasperl@chromium.org    // Content
176ac6aa175ab59d65cfb7a88dbb621e1d7f1a80b8fsgjesse@chromium.org    private ArrayList<ApplicationInfo> mApps;
1778ccb0be15fd0cd7e7804e90fa1ebf302cb7fa44bkasperl@chromium.org    private ArrayList<Object> mWidgets;
1788ccb0be15fd0cd7e7804e90fa1ebf302cb7fa44bkasperl@chromium.org
1798ccb0be15fd0cd7e7804e90fa1ebf302cb7fa44bkasperl@chromium.org    // Cling
180eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org    private boolean mHasShownAllAppsCling;
181eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org    private int mClingFocusedX;
18286f77b7fe492ed2bdfbf4e1147dab2f09c7d7003kasperl@chromium.org    private int mClingFocusedY;
18386f77b7fe492ed2bdfbf4e1147dab2f09c7d7003kasperl@chromium.org
184ea4f62e1df22417fc8dc2c2425485dca98b13d07ager@chromium.org    // Caching
18586f77b7fe492ed2bdfbf4e1147dab2f09c7d7003kasperl@chromium.org    private Canvas mCanvas;
18686f77b7fe492ed2bdfbf4e1147dab2f09c7d7003kasperl@chromium.org    private IconCache mIconCache;
18786f77b7fe492ed2bdfbf4e1147dab2f09c7d7003kasperl@chromium.org
1885c838251403b0be9a882540f1922577abba4c872ager@chromium.org    // Dimens
18986f77b7fe492ed2bdfbf4e1147dab2f09c7d7003kasperl@chromium.org    private int mContentWidth;
19086f77b7fe492ed2bdfbf4e1147dab2f09c7d7003kasperl@chromium.org    private int mMaxAppCellCountX, mMaxAppCellCountY;
19186f77b7fe492ed2bdfbf4e1147dab2f09c7d7003kasperl@chromium.org    private int mWidgetCountX, mWidgetCountY;
192ac6aa175ab59d65cfb7a88dbb621e1d7f1a80b8fsgjesse@chromium.org    private int mWidgetWidthGap, mWidgetHeightGap;
1938ccb0be15fd0cd7e7804e90fa1ebf302cb7fa44bkasperl@chromium.org    private PagedViewCellLayout mWidgetSpacingLayout;
1948ccb0be15fd0cd7e7804e90fa1ebf302cb7fa44bkasperl@chromium.org    private int mNumAppsPages;
1958ccb0be15fd0cd7e7804e90fa1ebf302cb7fa44bkasperl@chromium.org    private int mNumWidgetPages;
19643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
197eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org    // Relating to the scroll and overscroll effects
198d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org    Workspace.ZInterpolator mZInterpolator = new Workspace.ZInterpolator(0.5f);
199d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org    private static float CAMERA_DISTANCE = 6500;
200eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org    private static float TRANSITION_SCALE_FACTOR = 0.74f;
20141044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org    private static float TRANSITION_PIVOT = 0.65f;
20243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    private static float TRANSITION_MAX_ROTATION = 22;
20343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    private static final boolean PERFORM_OVERSCROLL_ROTATION = true;
20443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    private AccelerateInterpolator mAlphaInterpolator = new AccelerateInterpolator(0.9f);
205eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org    private DecelerateInterpolator mLeftScreenAlphaInterpolator = new DecelerateInterpolator(4);
206d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org
207d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org    // Previews & outlines
208eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org    ArrayList<AppsCustomizeAsyncTask> mRunningTasks;
20941044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org    private static final int sPageSleepDelay = 200;
21043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
21143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    private Runnable mInflateWidgetRunnable = null;
21243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    private Runnable mBindWidgetRunnable = null;
213eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org    static final int WIDGET_NO_CLEANUP_REQUIRED = -1;
214d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org    static final int WIDGET_PRELOAD_PENDING = 0;
215d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org    static final int WIDGET_BOUND = 1;
216eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org    static final int WIDGET_INFLATED = 2;
21741044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org    int mWidgetCleanupState = WIDGET_NO_CLEANUP_REQUIRED;
21843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    int mWidgetLoadingId = -1;
21943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    PendingAddWidgetInfo mCreateWidgetInfo = null;
22043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    private boolean mDraggingWidget = false;
221eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org
222d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org    private Toast mWidgetInstructionToast;
223d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org
224eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org    // Deferral of loading widget previews during launcher transitions
22541044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org    private boolean mInTransition;
22643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    private ArrayList<AsyncTaskPageData> mDeferredSyncWidgetPageItems =
22743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        new ArrayList<AsyncTaskPageData>();
22843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    private ArrayList<Runnable> mDeferredPrepareLoadWidgetPreviewsTasks =
22943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        new ArrayList<Runnable>();
23043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
23143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    private Rect mTmpRect = new Rect();
23243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
23343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    // Used for drawing shortcut previews
23443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    BitmapCache mCachedShortcutPreviewBitmap = new BitmapCache();
235eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org    PaintCache mCachedShortcutPreviewPaint = new PaintCache();
236d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org    CanvasCache mCachedShortcutPreviewCanvas = new CanvasCache();
237d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org
238eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org    // Used for drawing widget previews
23941044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org    CanvasCache mCachedAppWidgetPreviewCanvas = new CanvasCache();
24043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    RectCache mCachedAppWidgetPreviewSrcRect = new RectCache();
24143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    RectCache mCachedAppWidgetPreviewDestRect = new RectCache();
24243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    PaintCache mCachedAppWidgetPreviewPaint = new PaintCache();
243eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org
244eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org    WidgetPreviewLoader mWidgetPreviewLoader;
245eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org
246eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org    private boolean mInBulkBind;
247d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org    private boolean mNeedToUpdatePageCountsAndInvalidateData;
248eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org
249d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org    public AppsCustomizePagedView(Context context, AttributeSet attrs) {
250eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org        super(context, attrs);
251eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org        mLayoutInflater = LayoutInflater.from(context);
252eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org        mPackageManager = context.getPackageManager();
253eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org        mApps = new ArrayList<ApplicationInfo>();
254d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org        mWidgets = new ArrayList<Object>();
255eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org        mIconCache = ((LauncherApplication) context.getApplicationContext()).getIconCache();
256eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org        mCanvas = new Canvas();
257eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org        mRunningTasks = new ArrayList<AppsCustomizeAsyncTask>();
258eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org
259eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org        // Save the default widget preview background
260eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppsCustomizePagedView, 0, 0);
26141044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org        mMaxAppCellCountX = a.getInt(R.styleable.AppsCustomizePagedView_maxAppCellCountX, -1);
26243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        mMaxAppCellCountY = a.getInt(R.styleable.AppsCustomizePagedView_maxAppCellCountY, -1);
26343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        mWidgetWidthGap =
26443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            a.getDimensionPixelSize(R.styleable.AppsCustomizePagedView_widgetCellWidthGap, 0);
265eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org        mWidgetHeightGap =
266d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org            a.getDimensionPixelSize(R.styleable.AppsCustomizePagedView_widgetCellHeightGap, 0);
267d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org        mWidgetCountX = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountX, 2);
268eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org        mWidgetCountY = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountY, 2);
26941044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org        mClingFocusedX = a.getInt(R.styleable.AppsCustomizePagedView_clingFocusedX, 0);
27043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        mClingFocusedY = a.getInt(R.styleable.AppsCustomizePagedView_clingFocusedY, 0);
27143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        a.recycle();
27243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        mWidgetSpacingLayout = new PagedViewCellLayout(getContext());
273eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org
274d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org        // The padding on the non-matched dimension for the default widget preview icons
275d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org        // (top + bottom)
276eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org        mFadeInAdjacentScreens = false;
27741044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org
27843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        // Unless otherwise specified this view is important for accessibility.
27943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
28043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
281eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org        }
282eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org    }
283eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org
284eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org    @Override
285d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org    protected void init() {
286eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org        super.init();
287d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org        mCenterPagesVertically = false;
288eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org
289eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org        Context context = getContext();
290eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org        Resources r = context.getResources();
291eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org        setDragSlopeThreshold(r.getInteger(R.integer.config_appsCustomizeDragSlopeThreshold)/100f);
292d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org    }
293eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org
294eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org    /** Returns the item index of the center item on this page so that we can restore to this
295eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org     *  item index when we rotate. */
296eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org    private int getMiddleComponentIndexOnCurrentPage() {
297eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org        int i = -1;
298eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org        if (getPageCount() > 0) {
29941044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org            int currentPage = getCurrentPage();
30043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            if (currentPage < mNumAppsPages) {
30143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                PagedViewCellLayout layout = (PagedViewCellLayout) getPageAt(currentPage);
30243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                PagedViewCellLayoutChildren childrenLayout = layout.getChildrenLayout();
303eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org                int numItemsPerPage = mCellCountX * mCellCountY;
304d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org                int childCount = childrenLayout.getChildCount();
305d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org                if (childCount > 0) {
306eadaf2282ee421d7a63a21d71369b029105341ccager@chromium.org                    i = (currentPage * numItemsPerPage) + (childCount / 2);
30741044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org                }
30843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            } else {
30943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                int numApps = mApps.size();
31043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(currentPage);
31143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                int numItemsPerPage = mWidgetCountX * mWidgetCountY;
31243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                int childCount = layout.getChildCount();
31343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                if (childCount > 0) {
31443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                    i = numApps +
31543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                        ((currentPage - mNumAppsPages) * numItemsPerPage) + (childCount / 2);
31643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                }
31749edbdf52640c88918f8e6638ab4965819eb1dfekmillikin@chromium.org            }
318750145ab1b720c97adf2b548cc8fbd28c8b8e06dulan@chromium.org        }
31941044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org        return i;
32043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    }
32143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
32243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    /** Get the index of the item to restore to if we need to restore the current page. */
32343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    int getSaveInstanceStateIndex() {
3244980dff4208f9b77bc5320af43d7cc4b2a3d9688ricow@chromium.org        if (mSaveInstanceStateItemIndex == -1) {
32543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            mSaveInstanceStateItemIndex = getMiddleComponentIndexOnCurrentPage();
32643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        }
327c3b670ff19220959730d7886892bc4beb95d2ebaerik.corry@gmail.com        return mSaveInstanceStateItemIndex;
328750145ab1b720c97adf2b548cc8fbd28c8b8e06dulan@chromium.org    }
32941044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org
33043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    /** Returns the page in the current orientation which is expected to contain the specified
33143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen     *  item index. */
3327c537e2abe09729ed6cb827b4dd206470d8c4a42ager@chromium.org    int getPageForComponent(int index) {
3338ccb0be15fd0cd7e7804e90fa1ebf302cb7fa44bkasperl@chromium.org        if (index < 0) return 0;
3347c537e2abe09729ed6cb827b4dd206470d8c4a42ager@chromium.org
3357c537e2abe09729ed6cb827b4dd206470d8c4a42ager@chromium.org        if (index < mApps.size()) {
33643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            int numItemsPerPage = mCellCountX * mCellCountY;
33743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            return (index / numItemsPerPage);
33834e60787ea1e76f3ee49e859f71f036170c21f0elrn@chromium.org        } else {
339935a7790c70d49e252069bc2d34eaa72f8c6677fmachenbach@chromium.org            int numItemsPerPage = mWidgetCountX * mWidgetCountY;
34043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            return mNumAppsPages + ((index - mApps.size()) / numItemsPerPage);
34143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        }
34243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    }
3434980dff4208f9b77bc5320af43d7cc4b2a3d9688ricow@chromium.org
3447c537e2abe09729ed6cb827b4dd206470d8c4a42ager@chromium.org    /** Restores the page for an item at the specified index */
34543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    void restorePageForIndex(int index) {
34643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        if (index < 0) return;
347394dbcf9009cf5203b6d85e8b515fcff072040f3erik.corry@gmail.com        mSaveInstanceStateItemIndex = index;
348394dbcf9009cf5203b6d85e8b515fcff072040f3erik.corry@gmail.com    }
349394dbcf9009cf5203b6d85e8b515fcff072040f3erik.corry@gmail.com
350394dbcf9009cf5203b6d85e8b515fcff072040f3erik.corry@gmail.com    private void updatePageCounts() {
351394dbcf9009cf5203b6d85e8b515fcff072040f3erik.corry@gmail.com        mNumWidgetPages = (int) Math.ceil(mWidgets.size() /
352394dbcf9009cf5203b6d85e8b515fcff072040f3erik.corry@gmail.com                (float) (mWidgetCountX * mWidgetCountY));
35343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        mNumAppsPages = (int) Math.ceil((float) mApps.size() / (mCellCountX * mCellCountY));
35443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    }
3554980dff4208f9b77bc5320af43d7cc4b2a3d9688ricow@chromium.org
35643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    protected void onDataReady(int width, int height) {
35743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        if (mWidgetPreviewLoader == null) {
35843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            mWidgetPreviewLoader = new WidgetPreviewLoader(mLauncher);
35943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        }
3607c537e2abe09729ed6cb827b4dd206470d8c4a42ager@chromium.org
36141044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org        // Note that we transpose the counts in portrait so that we get a similar layout
36243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        boolean isLandscape = getResources().getConfiguration().orientation ==
36343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            Configuration.ORIENTATION_LANDSCAPE;
36443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        int maxCellCountX = Integer.MAX_VALUE;
36543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        int maxCellCountY = Integer.MAX_VALUE;
366ea4f62e1df22417fc8dc2c2425485dca98b13d07ager@chromium.org        if (LauncherApplication.isScreenLarge()) {
36743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            maxCellCountX = (isLandscape ? LauncherModel.getCellCountX() :
368750145ab1b720c97adf2b548cc8fbd28c8b8e06dulan@chromium.org                LauncherModel.getCellCountY());
36943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            maxCellCountY = (isLandscape ? LauncherModel.getCellCountY() :
370ea4f62e1df22417fc8dc2c2425485dca98b13d07ager@chromium.org                LauncherModel.getCellCountX());
37141044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org        }
37243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        if (mMaxAppCellCountX > -1) {
37343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            maxCellCountX = Math.min(maxCellCountX, mMaxAppCellCountX);
37443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        }
3755c838251403b0be9a882540f1922577abba4c872ager@chromium.org        // Temp hack for now: only use the max cell count Y for widget layout
37643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        int maxWidgetCellCountY = maxCellCountY;
3775c838251403b0be9a882540f1922577abba4c872ager@chromium.org        if (mMaxAppCellCountY > -1) {
37843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            maxWidgetCellCountY = Math.min(maxWidgetCellCountY, mMaxAppCellCountY);
37934e60787ea1e76f3ee49e859f71f036170c21f0elrn@chromium.org        }
38005521fcc4bd495d3dc2b3ae7a6bc7e52ccb7937bsgjesse@chromium.org
38105521fcc4bd495d3dc2b3ae7a6bc7e52ccb7937bsgjesse@chromium.org        // Now that the data is ready, we can calculate the content width, the number of cells to
38205521fcc4bd495d3dc2b3ae7a6bc7e52ccb7937bsgjesse@chromium.org        // use for each page
38305521fcc4bd495d3dc2b3ae7a6bc7e52ccb7937bsgjesse@chromium.org        mWidgetSpacingLayout.setGap(mPageLayoutWidthGap, mPageLayoutHeightGap);
3845c838251403b0be9a882540f1922577abba4c872ager@chromium.org        mWidgetSpacingLayout.setPadding(mPageLayoutPaddingLeft, mPageLayoutPaddingTop,
38505521fcc4bd495d3dc2b3ae7a6bc7e52ccb7937bsgjesse@chromium.org                mPageLayoutPaddingRight, mPageLayoutPaddingBottom);
3865c838251403b0be9a882540f1922577abba4c872ager@chromium.org        mWidgetSpacingLayout.calculateCellCount(width, height, maxCellCountX, maxCellCountY);
38705521fcc4bd495d3dc2b3ae7a6bc7e52ccb7937bsgjesse@chromium.org        mCellCountX = mWidgetSpacingLayout.getCellCountX();
38834e60787ea1e76f3ee49e859f71f036170c21f0elrn@chromium.org        mCellCountY = mWidgetSpacingLayout.getCellCountY();
38934e60787ea1e76f3ee49e859f71f036170c21f0elrn@chromium.org        updatePageCounts();
39034e60787ea1e76f3ee49e859f71f036170c21f0elrn@chromium.org
39134e60787ea1e76f3ee49e859f71f036170c21f0elrn@chromium.org        // Force a measure to update recalculate the gaps
39234e60787ea1e76f3ee49e859f71f036170c21f0elrn@chromium.org        int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.AT_MOST);
39334e60787ea1e76f3ee49e859f71f036170c21f0elrn@chromium.org        int heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST);
39434e60787ea1e76f3ee49e859f71f036170c21f0elrn@chromium.org        mWidgetSpacingLayout.calculateCellCount(width, height, maxCellCountX, maxWidgetCellCountY);
39534e60787ea1e76f3ee49e859f71f036170c21f0elrn@chromium.org        mWidgetSpacingLayout.measure(widthSpec, heightSpec);
39634e60787ea1e76f3ee49e859f71f036170c21f0elrn@chromium.org        mContentWidth = mWidgetSpacingLayout.getContentWidth();
39734e60787ea1e76f3ee49e859f71f036170c21f0elrn@chromium.org
39834e60787ea1e76f3ee49e859f71f036170c21f0elrn@chromium.org        AppsCustomizeTabHost host = (AppsCustomizeTabHost) getTabHost();
39934e60787ea1e76f3ee49e859f71f036170c21f0elrn@chromium.org        final boolean hostIsTransitioning = host.isTransitioning();
400c3b670ff19220959730d7886892bc4beb95d2ebaerik.corry@gmail.com
401c3b670ff19220959730d7886892bc4beb95d2ebaerik.corry@gmail.com        // Restore the page
40234e60787ea1e76f3ee49e859f71f036170c21f0elrn@chromium.org        int page = getPageForComponent(mSaveInstanceStateItemIndex);
403c3b670ff19220959730d7886892bc4beb95d2ebaerik.corry@gmail.com        invalidatePageData(Math.max(0, page), hostIsTransitioning);
40441044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org
40543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        // Show All Apps cling if we are finished transitioning, otherwise, we will try again when
40643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        // the transition completes in AppsCustomizeTabHost (otherwise the wrong offsets will be
40743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        // returned while animating)
40843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        if (!hostIsTransitioning) {
4093e87580939cb78c5802369f723680d4a16cc2902ager@chromium.org            post(new Runnable() {
4103e87580939cb78c5802369f723680d4a16cc2902ager@chromium.org                @Override
4113e87580939cb78c5802369f723680d4a16cc2902ager@chromium.org                public void run() {
4122abc450936e88b5c98a5e9d43ee6230ccc748272kasperl@chromium.org                    showAllAppsCling();
41343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                }
41434e60787ea1e76f3ee49e859f71f036170c21f0elrn@chromium.org            });
41534e60787ea1e76f3ee49e859f71f036170c21f0elrn@chromium.org        }
41643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    }
41743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
41843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    void showAllAppsCling() {
41943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        if (!mHasShownAllAppsCling && isDataReady()) {
42043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            mHasShownAllAppsCling = true;
42143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            // Calculate the position for the cling punch through
42243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            int[] offset = new int[2];
42343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            int[] pos = mWidgetSpacingLayout.estimateCellPosition(mClingFocusedX, mClingFocusedY);
42443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            mLauncher.getDragLayer().getLocationInDragLayer(this, offset);
42543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            // PagedViews are centered horizontally but top aligned
4266a2b0aa331a1ae1829a9b9637ad18cfc7ec9d840ager@chromium.org            // Note we have to shift the items up now that Launcher sits under the status bar
42743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            pos[0] += (getMeasuredWidth() - mWidgetSpacingLayout.getMeasuredWidth()) / 2 +
42843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                    offset[0];
42934e60787ea1e76f3ee49e859f71f036170c21f0elrn@chromium.org            pos[1] += offset[1] - mLauncher.getDragLayer().getPaddingTop();
4301b3afd1cab9087ca3c4e585d3da77d374d65c082mstarzinger@chromium.org            mLauncher.showFirstRunAllAppsCling(pos);
4311b3afd1cab9087ca3c4e585d3da77d374d65c082mstarzinger@chromium.org        }
43243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    }
43343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
43443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    @Override
435394dbcf9009cf5203b6d85e8b515fcff072040f3erik.corry@gmail.com    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
43643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        int width = MeasureSpec.getSize(widthMeasureSpec);
43743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        int height = MeasureSpec.getSize(heightMeasureSpec);
4389a4089a092cad9ff23b6416b92cd5d818dc101d1mads.s.ager@gmail.com        if (!isDataReady()) {
43943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            if (mApps.size() > 0 && !mWidgets.isEmpty()) {
44043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                setDataIsReady();
44143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                setMeasuredDimension(width, height);
44241044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org                onDataReady(width, height);
44343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            }
44443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        }
445e9fd6580f52407c94d77bfcb4be04207f2ebb2f1machenbach@chromium.org
4466a2b0aa331a1ae1829a9b9637ad18cfc7ec9d840ager@chromium.org        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
44741044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org    }
44843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
44943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    public void onPackagesUpdated(ArrayList<Object> widgetsAndShortcuts) {
45043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        // Get the list of widgets and shortcuts
45143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        mWidgets.clear();
45243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        for (Object o : widgetsAndShortcuts) {
45341044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org            if (o instanceof AppWidgetProviderInfo) {
45443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                AppWidgetProviderInfo widget = (AppWidgetProviderInfo) o;
45543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                if (widget.minWidth > 0 && widget.minHeight > 0) {
45643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                    // Ensure that all widgets we show can be added on a workspace of this size
45743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                    int[] spanXY = Launcher.getSpanForWidget(mLauncher, widget);
45843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                    int[] minSpanXY = Launcher.getMinSpanForWidget(mLauncher, widget);
45941044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org                    int minSpanX = Math.min(spanXY[0], minSpanXY[0]);
46043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                    int minSpanY = Math.min(spanXY[1], minSpanXY[1]);
46143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                    if (minSpanX <= LauncherModel.getCellCountX() &&
46243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                        minSpanY <= LauncherModel.getCellCountY()) {
46343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                        mWidgets.add(widget);
46443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                    } else {
46541044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org                        Log.e(TAG, "Widget " + widget.provider + " can not fit on this device (" +
46643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                              widget.minWidth + ", " + widget.minHeight + ")");
46743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                    }
46843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                } else {
46943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                    Log.e(TAG, "Widget " + widget.provider + " has invalid dimensions (" +
47043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                          widget.minWidth + ", " + widget.minHeight + ")");
47143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                }
47243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            } else {
47343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                // just add shortcuts
47443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                mWidgets.add(o);
47543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            }
4767c537e2abe09729ed6cb827b4dd206470d8c4a42ager@chromium.org        }
4777c537e2abe09729ed6cb827b4dd206470d8c4a42ager@chromium.org        updatePageCountsAndInvalidateData();
4787c537e2abe09729ed6cb827b4dd206470d8c4a42ager@chromium.org    }
4794980dff4208f9b77bc5320af43d7cc4b2a3d9688ricow@chromium.org
480f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    public void setBulkBind(boolean bulkBind) {
48143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        if (bulkBind) {
48243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            mInBulkBind = true;
48341044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org        } else {
48443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            mInBulkBind = false;
48543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            if (mNeedToUpdatePageCountsAndInvalidateData) {
4865c838251403b0be9a882540f1922577abba4c872ager@chromium.org                updatePageCountsAndInvalidateData();
4875c838251403b0be9a882540f1922577abba4c872ager@chromium.org            }
4885c838251403b0be9a882540f1922577abba4c872ager@chromium.org        }
4895c838251403b0be9a882540f1922577abba4c872ager@chromium.org    }
4905c838251403b0be9a882540f1922577abba4c872ager@chromium.org
4915c838251403b0be9a882540f1922577abba4c872ager@chromium.org    private void updatePageCountsAndInvalidateData() {
4925c838251403b0be9a882540f1922577abba4c872ager@chromium.org        if (mInBulkBind) {
4935c838251403b0be9a882540f1922577abba4c872ager@chromium.org            mNeedToUpdatePageCountsAndInvalidateData = true;
4945c838251403b0be9a882540f1922577abba4c872ager@chromium.org        } else {
4955c838251403b0be9a882540f1922577abba4c872ager@chromium.org            updatePageCounts();
49643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            invalidateOnDataChange();
49743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            mNeedToUpdatePageCountsAndInvalidateData = false;
49843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        }
499d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org    }
500d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org
501d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org    @Override
502d236f4d96b5dd21acc5ec57f4e50fa97cdd97bb6ricow@chromium.org    public void onClick(View v) {
50343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        // When we have exited all apps or are in transition, disregard clicks
504690083842e7c67a362017dae50909d4bb0b2a9c2mstarzinger@chromium.org        if (!mLauncher.isAllAppsVisible() ||
50531c0e32e19ad3df48525fa9e7b2d1c0c07496d00machenbach@chromium.org                mLauncher.getWorkspace().isSwitchingState()) return;
50643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
50741044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org        if (v instanceof PagedViewIcon) {
50843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            // Animate some feedback to the click
509d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org            final ApplicationInfo appInfo = (ApplicationInfo) v.getTag();
510d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org
511d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org            // Lock the drawable state to pressed until we return to Launcher
512d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org            if (mPressedIcon != null) {
513d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org                mPressedIcon.lockDrawableState();
514d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org            }
515690083842e7c67a362017dae50909d4bb0b2a9c2mstarzinger@chromium.org
51631c0e32e19ad3df48525fa9e7b2d1c0c07496d00machenbach@chromium.org            // NOTE: We want all transitions from launcher to act as if the wallpaper were enabled
517d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org            // to be consistent.  So re-enable the flag here, and we will re-disable it as necessary
518d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org            // when Launcher resumes and we are still in AllApps.
519d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org            mLauncher.updateWallpaperVisibility(true);
52043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            mLauncher.startActivitySafely(v, appInfo.intent, appInfo);
52143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
52243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        } else if (v instanceof PagedViewWidget) {
52343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            // Let the user know that they have to long press to add a widget
5245c838251403b0be9a882540f1922577abba4c872ager@chromium.org            if (mWidgetInstructionToast != null) {
52543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                mWidgetInstructionToast.cancel();
52643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            }
527f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org            mWidgetInstructionToast = Toast.makeText(getContext(),R.string.long_press_widget_to_add,
52843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                Toast.LENGTH_SHORT);
52941044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org            mWidgetInstructionToast.show();
53043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
531b95b98b0c30bcd40b657aa45f6cd75a46a4772adfschneider@chromium.org            // Create a little animation to show that the widget can move
532357bf65ed5309ac3a2c4bf20b6ce7770488787c2ager@chromium.org            float offsetY = getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY);
533b95b98b0c30bcd40b657aa45f6cd75a46a4772adfschneider@chromium.org            final ImageView p = (ImageView) v.findViewById(R.id.widget_preview);
534b95b98b0c30bcd40b657aa45f6cd75a46a4772adfschneider@chromium.org            AnimatorSet bounce = LauncherAnimUtils.createAnimatorSet();
535f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org            ValueAnimator tyuAnim = LauncherAnimUtils.ofFloat(p, "translationY", offsetY);
536b95b98b0c30bcd40b657aa45f6cd75a46a4772adfschneider@chromium.org            tyuAnim.setDuration(125);
537b95b98b0c30bcd40b657aa45f6cd75a46a4772adfschneider@chromium.org            ValueAnimator tydAnim = LauncherAnimUtils.ofFloat(p, "translationY", 0f);
538b95b98b0c30bcd40b657aa45f6cd75a46a4772adfschneider@chromium.org            tydAnim.setDuration(100);
53943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            bounce.play(tyuAnim).before(tydAnim);
540750145ab1b720c97adf2b548cc8fbd28c8b8e06dulan@chromium.org            bounce.setInterpolator(new AccelerateInterpolator());
541750145ab1b720c97adf2b548cc8fbd28c8b8e06dulan@chromium.org            bounce.start();
542750145ab1b720c97adf2b548cc8fbd28c8b8e06dulan@chromium.org        }
543750145ab1b720c97adf2b548cc8fbd28c8b8e06dulan@chromium.org    }
544750145ab1b720c97adf2b548cc8fbd28c8b8e06dulan@chromium.org
545750145ab1b720c97adf2b548cc8fbd28c8b8e06dulan@chromium.org    public boolean onKey(View v, int keyCode, KeyEvent event) {
54643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        return FocusHelper.handleAppsCustomizeKeyEvent(v,  keyCode, event);
54743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    }
54843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
54943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    /*
55043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen     * PagedViewWithDraggableItems implementation
551f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org     */
552b302e56e5b70c4504faa2adf4ec3efb64a9d3e38sgjesse@chromium.org    @Override
553e0e1b0d3e70c933d36ed381d511e9fda39f2a751mstarzinger@chromium.org    protected void determineDraggingStart(android.view.MotionEvent ev) {
554b302e56e5b70c4504faa2adf4ec3efb64a9d3e38sgjesse@chromium.org        // Disable dragging by pulling an app down for now.
55543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    }
55641044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org
55743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    private void beginDraggingApplication(View v) {
55843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        mLauncher.getWorkspace().onDragStartedWithItem(v);
55943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        mLauncher.getWorkspace().beginDragShared(v, this);
56043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    }
56143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
56243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    Bundle getDefaultOptionsForWidget(Launcher launcher, PendingAddWidgetInfo info) {
56341044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org        Bundle options = null;
56443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
56543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            AppWidgetResizeFrame.getWidgetSizeRanges(mLauncher, info.spanX, info.spanY, mTmpRect);
566a2c0c1516848536a514b3178d2c040b7df0ceb5bmachenbach@chromium.org            Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(mLauncher,
567a2c0c1516848536a514b3178d2c040b7df0ceb5bmachenbach@chromium.org                    info.componentName, null);
568a2c0c1516848536a514b3178d2c040b7df0ceb5bmachenbach@chromium.org
569a2c0c1516848536a514b3178d2c040b7df0ceb5bmachenbach@chromium.org            float density = getResources().getDisplayMetrics().density;
570a2c0c1516848536a514b3178d2c040b7df0ceb5bmachenbach@chromium.org            int xPaddingDips = (int) ((padding.left + padding.right) / density);
571a2c0c1516848536a514b3178d2c040b7df0ceb5bmachenbach@chromium.org            int yPaddingDips = (int) ((padding.top + padding.bottom) / density);
572a2c0c1516848536a514b3178d2c040b7df0ceb5bmachenbach@chromium.org
573a2c0c1516848536a514b3178d2c040b7df0ceb5bmachenbach@chromium.org            options = new Bundle();
57443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
57543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                    mTmpRect.left - xPaddingDips);
576769cc962a043dd8d92cc010dd2c50bc26f652c94mads.s.ager@gmail.com            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
57743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                    mTmpRect.top - yPaddingDips);
57841044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
57943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                    mTmpRect.right - xPaddingDips);
58043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
58143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                    mTmpRect.bottom - yPaddingDips);
58243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        }
58343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        return options;
58443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    }
58541044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org
58643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    private void preloadWidget(final PendingAddWidgetInfo info) {
58743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        final AppWidgetProviderInfo pInfo = info.info;
5885c838251403b0be9a882540f1922577abba4c872ager@chromium.org        final Bundle options = getDefaultOptionsForWidget(mLauncher, info);
5895c838251403b0be9a882540f1922577abba4c872ager@chromium.org
5905c838251403b0be9a882540f1922577abba4c872ager@chromium.org        if (pInfo.configure != null) {
5915c838251403b0be9a882540f1922577abba4c872ager@chromium.org            info.bindOptions = options;
5925c838251403b0be9a882540f1922577abba4c872ager@chromium.org            return;
5939155e252524a2bf92aecd27493feafed86702312kmillikin@chromium.org        }
5940cc095007a3ccded63f6577751c6a04300eb7ae9machenbach@chromium.org
5950cc095007a3ccded63f6577751c6a04300eb7ae9machenbach@chromium.org        mWidgetCleanupState = WIDGET_PRELOAD_PENDING;
5960cc095007a3ccded63f6577751c6a04300eb7ae9machenbach@chromium.org        mBindWidgetRunnable = new Runnable() {
5975c838251403b0be9a882540f1922577abba4c872ager@chromium.org            @Override
598d2c22f0121ebc55ee26a9e742f0fd7c0b8397730kmillikin@chromium.org            public void run() {
5995c838251403b0be9a882540f1922577abba4c872ager@chromium.org                mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId();
6005c838251403b0be9a882540f1922577abba4c872ager@chromium.org                if (AppWidgetManager.getInstance(mLauncher).bindAppWidgetIdIfAllowed(
60143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                        mWidgetLoadingId, info.info.getProfile(), info.componentName, options)) {
60243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                    mWidgetCleanupState = WIDGET_BOUND;
60343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                }
60443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            }
60543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        };
60643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        post(mBindWidgetRunnable);
60743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
60843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        mInflateWidgetRunnable = new Runnable() {
60943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            @Override
6104980dff4208f9b77bc5320af43d7cc4b2a3d9688ricow@chromium.org            public void run() {
6114980dff4208f9b77bc5320af43d7cc4b2a3d9688ricow@chromium.org                if (mWidgetCleanupState != WIDGET_BOUND) {
6124980dff4208f9b77bc5320af43d7cc4b2a3d9688ricow@chromium.org                    return;
6134980dff4208f9b77bc5320af43d7cc4b2a3d9688ricow@chromium.org                }
61441044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org                AppWidgetHostView hostView = mLauncher.
61543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                        getAppWidgetHost().createView(getContext(), mWidgetLoadingId, pInfo);
61643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                info.boundWidget = hostView;
61743d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                mWidgetCleanupState = WIDGET_INFLATED;
61843d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                hostView.setVisibility(INVISIBLE);
6194c3ce7c3fd2802da8f91c6516a9c9aea3cd93f1emachenbach@chromium.org                int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(info.spanX,
6204c3ce7c3fd2802da8f91c6516a9c9aea3cd93f1emachenbach@chromium.org                        info.spanY, info, false);
6214c3ce7c3fd2802da8f91c6516a9c9aea3cd93f1emachenbach@chromium.org
6224c3ce7c3fd2802da8f91c6516a9c9aea3cd93f1emachenbach@chromium.org                // We want the first widget layout to be the correct size. This will be important
6234c3ce7c3fd2802da8f91c6516a9c9aea3cd93f1emachenbach@chromium.org                // for width size reporting to the AppWidgetManager.
6244c3ce7c3fd2802da8f91c6516a9c9aea3cd93f1emachenbach@chromium.org                DragLayer.LayoutParams lp = new DragLayer.LayoutParams(unScaledSize[0],
62543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                        unScaledSize[1]);
6264c3ce7c3fd2802da8f91c6516a9c9aea3cd93f1emachenbach@chromium.org                lp.x = lp.y = 0;
6274c3ce7c3fd2802da8f91c6516a9c9aea3cd93f1emachenbach@chromium.org                lp.customPosition = true;
6284c3ce7c3fd2802da8f91c6516a9c9aea3cd93f1emachenbach@chromium.org                hostView.setLayoutParams(lp);
6294c3ce7c3fd2802da8f91c6516a9c9aea3cd93f1emachenbach@chromium.org                mLauncher.getDragLayer().addView(hostView);
6304c3ce7c3fd2802da8f91c6516a9c9aea3cd93f1emachenbach@chromium.org            }
63143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        };
63243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        post(mInflateWidgetRunnable);
63341044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org    }
63443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
63543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    @Override
63643d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen    public void onShortPress(View v) {
6374c3ce7c3fd2802da8f91c6516a9c9aea3cd93f1emachenbach@chromium.org        // We are anticipating a long press, and we use this time to load bind and instantiate
6384c3ce7c3fd2802da8f91c6516a9c9aea3cd93f1emachenbach@chromium.org        // the widget. This will need to be cleaned up if it turns out no long press occurs.
6394c3ce7c3fd2802da8f91c6516a9c9aea3cd93f1emachenbach@chromium.org        if (mCreateWidgetInfo != null) {
6404c3ce7c3fd2802da8f91c6516a9c9aea3cd93f1emachenbach@chromium.org            // Just in case the cleanup process wasn't properly executed. This shouldn't happen.
6414c3ce7c3fd2802da8f91c6516a9c9aea3cd93f1emachenbach@chromium.org            cleanupWidgetPreloading(false);
6424c3ce7c3fd2802da8f91c6516a9c9aea3cd93f1emachenbach@chromium.org        }
64343d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen        mCreateWidgetInfo = new PendingAddWidgetInfo((PendingAddWidgetInfo) v.getTag());
6444c3ce7c3fd2802da8f91c6516a9c9aea3cd93f1emachenbach@chromium.org        preloadWidget(mCreateWidgetInfo);
6454c3ce7c3fd2802da8f91c6516a9c9aea3cd93f1emachenbach@chromium.org    }
6464c3ce7c3fd2802da8f91c6516a9c9aea3cd93f1emachenbach@chromium.org
6474c3ce7c3fd2802da8f91c6516a9c9aea3cd93f1emachenbach@chromium.org    private void cleanupWidgetPreloading(boolean widgetWasAdded) {
6484c3ce7c3fd2802da8f91c6516a9c9aea3cd93f1emachenbach@chromium.org        if (!widgetWasAdded) {
64943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            // If the widget was not added, we may need to do further cleanup.
65043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            PendingAddWidgetInfo info = mCreateWidgetInfo;
65141044eb0969b0d7d5c041a077519a36efa6aff27kasperl@chromium.org            mCreateWidgetInfo = null;
65243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
6531510d58cbcf57c82a10e7d390bfe21a7ae68ba43mstarzinger@chromium.org            if (mWidgetCleanupState == WIDGET_PRELOAD_PENDING) {
6541510d58cbcf57c82a10e7d390bfe21a7ae68ba43mstarzinger@chromium.org                // We never did any preloading, so just remove pending callbacks to do so
655af9cfcbed5daf6e636e189bce451c6fafdbb127dmachenbach@chromium.org                removeCallbacks(mBindWidgetRunnable);
6561510d58cbcf57c82a10e7d390bfe21a7ae68ba43mstarzinger@chromium.org                removeCallbacks(mInflateWidgetRunnable);
6571510d58cbcf57c82a10e7d390bfe21a7ae68ba43mstarzinger@chromium.org            } else if (mWidgetCleanupState == WIDGET_BOUND) {
6581510d58cbcf57c82a10e7d390bfe21a7ae68ba43mstarzinger@chromium.org                 // Delete the widget id which was allocated
65943d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                if (mWidgetLoadingId != -1) {
66043d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                    mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
66143d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                }
66243d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen
6637304bcac06a6a63b9f3dcebac2eeceada87ca146vegorov@chromium.org                // We never got around to inflating the widget, so remove the callback to do so.
66443d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen                removeCallbacks(mInflateWidgetRunnable);
66543d26ecc3563a46f62a0224030667c8f8f3f6cebchristian.plesner.hansen            } else if (mWidgetCleanupState == WIDGET_INFLATED) {
666                // Delete the widget id which was allocated
667                if (mWidgetLoadingId != -1) {
668                    mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
669                }
670
671                // The widget was inflated and added to the DragLayer -- remove it.
672                AppWidgetHostView widget = info.boundWidget;
673                mLauncher.getDragLayer().removeView(widget);
674            }
675        }
676        mWidgetCleanupState = WIDGET_NO_CLEANUP_REQUIRED;
677        mWidgetLoadingId = -1;
678        mCreateWidgetInfo = null;
679        PagedViewWidget.resetShortPressTarget();
680    }
681
682    @Override
683    public void cleanUpShortPress(View v) {
684        if (!mDraggingWidget) {
685            cleanupWidgetPreloading(false);
686        }
687    }
688
689    private boolean beginDraggingWidget(View v) {
690        mDraggingWidget = true;
691        // Get the widget preview as the drag representation
692        ImageView image = (ImageView) v.findViewById(R.id.widget_preview);
693        PendingAddItemInfo createItemInfo = (PendingAddItemInfo) v.getTag();
694
695        // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
696        // we abort the drag.
697        if (image.getDrawable() == null) {
698            mDraggingWidget = false;
699            return false;
700        }
701
702        // Compose the drag image
703        Bitmap preview;
704        Bitmap outline;
705        float scale = 1f;
706        Point previewPadding = null;
707
708        if (createItemInfo instanceof PendingAddWidgetInfo) {
709            // This can happen in some weird cases involving multi-touch. We can't start dragging
710            // the widget if this is null, so we break out.
711            if (mCreateWidgetInfo == null) {
712                return false;
713            }
714
715            PendingAddWidgetInfo createWidgetInfo = mCreateWidgetInfo;
716            createItemInfo = createWidgetInfo;
717            int spanX = createItemInfo.spanX;
718            int spanY = createItemInfo.spanY;
719            int[] size = mLauncher.getWorkspace().estimateItemSize(spanX, spanY,
720                    createWidgetInfo, true);
721
722            FastBitmapDrawable previewDrawable = (FastBitmapDrawable) image.getDrawable();
723            float minScale = 1.25f;
724            int maxWidth, maxHeight;
725            maxWidth = Math.min((int) (previewDrawable.getIntrinsicWidth() * minScale), size[0]);
726            maxHeight = Math.min((int) (previewDrawable.getIntrinsicHeight() * minScale), size[1]);
727
728            int[] previewSizeBeforeScale = new int[1];
729
730            preview = mWidgetPreviewLoader.generateWidgetPreview(createWidgetInfo.info, spanX,
731                    spanY, maxWidth, maxHeight, null, previewSizeBeforeScale);
732
733            // Compare the size of the drag preview to the preview in the AppsCustomize tray
734            int previewWidthInAppsCustomize = Math.min(previewSizeBeforeScale[0],
735                    mWidgetPreviewLoader.maxWidthForWidgetPreview(spanX));
736            scale = previewWidthInAppsCustomize / (float) preview.getWidth();
737
738            // The bitmap in the AppsCustomize tray is always the the same size, so there
739            // might be extra pixels around the preview itself - this accounts for that
740            if (previewWidthInAppsCustomize < previewDrawable.getIntrinsicWidth()) {
741                int padding =
742                        (previewDrawable.getIntrinsicWidth() - previewWidthInAppsCustomize) / 2;
743                previewPadding = new Point(padding, 0);
744            }
745        } else {
746            PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) v.getTag();
747            // Widgets are only supported for current user, not for other profiles.
748            // Hence use myUserHandle().
749            Drawable icon = mIconCache.getFullResIcon(createShortcutInfo.shortcutActivityInfo,
750                    android.os.Process.myUserHandle());
751            preview = Bitmap.createBitmap(icon.getIntrinsicWidth(),
752                    icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
753
754            mCanvas.setBitmap(preview);
755            mCanvas.save();
756            WidgetPreviewLoader.renderDrawableToBitmap(icon, preview, 0, 0,
757                    icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
758            mCanvas.restore();
759            mCanvas.setBitmap(null);
760            createItemInfo.spanX = createItemInfo.spanY = 1;
761        }
762
763        // Don't clip alpha values for the drag outline if we're using the default widget preview
764        boolean clipAlpha = !(createItemInfo instanceof PendingAddWidgetInfo &&
765                (((PendingAddWidgetInfo) createItemInfo).info.previewImage == 0));
766
767        // Save the preview for the outline generation, then dim the preview
768        outline = Bitmap.createScaledBitmap(preview, preview.getWidth(), preview.getHeight(),
769                false);
770
771        // Start the drag
772        mLauncher.lockScreenOrientation();
773        mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, outline, clipAlpha);
774        mDragController.startDrag(image, preview, this, createItemInfo,
775                DragController.DRAG_ACTION_COPY, previewPadding, scale);
776        outline.recycle();
777        preview.recycle();
778        return true;
779    }
780
781    @Override
782    protected boolean beginDragging(final View v) {
783        if (!super.beginDragging(v)) return false;
784
785        if (v instanceof PagedViewIcon) {
786            beginDraggingApplication(v);
787        } else if (v instanceof PagedViewWidget) {
788            if (!beginDraggingWidget(v)) {
789                return false;
790            }
791        }
792
793        // We delay entering spring-loaded mode slightly to make sure the UI
794        // thready is free of any work.
795        postDelayed(new Runnable() {
796            @Override
797            public void run() {
798                // We don't enter spring-loaded mode if the drag has been cancelled
799                if (mLauncher.getDragController().isDragging()) {
800                    // Dismiss the cling
801                    mLauncher.dismissAllAppsCling(null);
802
803                    // Reset the alpha on the dragged icon before we drag
804                    resetDrawableState();
805
806                    // Go into spring loaded mode (must happen before we startDrag())
807                    mLauncher.enterSpringLoadedDragMode();
808                }
809            }
810        }, 150);
811
812        return true;
813    }
814
815    /**
816     * Clean up after dragging.
817     *
818     * @param target where the item was dragged to (can be null if the item was flung)
819     */
820    private void endDragging(View target, boolean isFlingToDelete, boolean success) {
821        if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
822                !(target instanceof DeleteDropTarget))) {
823            // Exit spring loaded mode if we have not successfully dropped or have not handled the
824            // drop in Workspace
825            mLauncher.exitSpringLoadedDragMode();
826        }
827        mLauncher.unlockScreenOrientation(false);
828    }
829
830    @Override
831    public View getContent() {
832        return null;
833    }
834
835    @Override
836    public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
837        mInTransition = true;
838        if (toWorkspace) {
839            cancelAllTasks();
840        }
841    }
842
843    @Override
844    public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
845    }
846
847    @Override
848    public void onLauncherTransitionStep(Launcher l, float t) {
849    }
850
851    @Override
852    public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
853        mInTransition = false;
854        for (AsyncTaskPageData d : mDeferredSyncWidgetPageItems) {
855            onSyncWidgetPageItems(d);
856        }
857        mDeferredSyncWidgetPageItems.clear();
858        for (Runnable r : mDeferredPrepareLoadWidgetPreviewsTasks) {
859            r.run();
860        }
861        mDeferredPrepareLoadWidgetPreviewsTasks.clear();
862        mForceDrawAllChildrenNextFrame = !toWorkspace;
863    }
864
865    @Override
866    public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete,
867            boolean success) {
868        // Return early and wait for onFlingToDeleteCompleted if this was the result of a fling
869        if (isFlingToDelete) return;
870
871        endDragging(target, false, success);
872
873        // Display an error message if the drag failed due to there not being enough space on the
874        // target layout we were dropping on.
875        if (!success) {
876            boolean showOutOfSpaceMessage = false;
877            if (target instanceof Workspace) {
878                int currentScreen = mLauncher.getCurrentWorkspaceScreen();
879                Workspace workspace = (Workspace) target;
880                CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen);
881                ItemInfo itemInfo = (ItemInfo) d.dragInfo;
882                if (layout != null) {
883                    layout.calculateSpans(itemInfo);
884                    showOutOfSpaceMessage =
885                            !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY);
886                }
887            }
888            if (showOutOfSpaceMessage) {
889                mLauncher.showOutOfSpaceMessage(false);
890            }
891
892            d.deferDragViewCleanupPostAnimation = false;
893        }
894        cleanupWidgetPreloading(success);
895        mDraggingWidget = false;
896    }
897
898    @Override
899    public void onFlingToDeleteCompleted() {
900        // We just dismiss the drag when we fling, so cleanup here
901        endDragging(null, true, true);
902        cleanupWidgetPreloading(false);
903        mDraggingWidget = false;
904    }
905
906    @Override
907    public boolean supportsFlingToDelete() {
908        return true;
909    }
910
911    @Override
912    protected void onDetachedFromWindow() {
913        super.onDetachedFromWindow();
914        cancelAllTasks();
915    }
916
917    public void clearAllWidgetPages() {
918        cancelAllTasks();
919        int count = getChildCount();
920        for (int i = 0; i < count; i++) {
921            View v = getPageAt(i);
922            if (v instanceof PagedViewGridLayout) {
923                ((PagedViewGridLayout) v).removeAllViewsOnPage();
924                mDirtyPageContent.set(i, true);
925            }
926        }
927    }
928
929    private void cancelAllTasks() {
930        // Clean up all the async tasks
931        Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator();
932        while (iter.hasNext()) {
933            AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next();
934            task.cancel(false);
935            iter.remove();
936            mDirtyPageContent.set(task.page, true);
937
938            // We've already preallocated the views for the data to load into, so clear them as well
939            View v = getPageAt(task.page);
940            if (v instanceof PagedViewGridLayout) {
941                ((PagedViewGridLayout) v).removeAllViewsOnPage();
942            }
943        }
944        mDeferredSyncWidgetPageItems.clear();
945        mDeferredPrepareLoadWidgetPreviewsTasks.clear();
946    }
947
948    public void setContentType(ContentType type) {
949        if (type == ContentType.Widgets) {
950            invalidatePageData(mNumAppsPages, true);
951        } else if (type == ContentType.Applications) {
952            invalidatePageData(0, true);
953        }
954    }
955
956    protected void snapToPage(int whichPage, int delta, int duration) {
957        super.snapToPage(whichPage, delta, duration);
958        updateCurrentTab(whichPage);
959
960        // Update the thread priorities given the direction lookahead
961        Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator();
962        while (iter.hasNext()) {
963            AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next();
964            int pageIndex = task.page;
965            if ((mNextPage > mCurrentPage && pageIndex >= mCurrentPage) ||
966                (mNextPage < mCurrentPage && pageIndex <= mCurrentPage)) {
967                task.setThreadPriority(getThreadPriorityForPage(pageIndex));
968            } else {
969                task.setThreadPriority(Process.THREAD_PRIORITY_LOWEST);
970            }
971        }
972    }
973
974    private void updateCurrentTab(int currentPage) {
975        AppsCustomizeTabHost tabHost = getTabHost();
976        if (tabHost != null) {
977            String tag = tabHost.getCurrentTabTag();
978            if (tag != null) {
979                if (currentPage >= mNumAppsPages &&
980                        !tag.equals(tabHost.getTabTagForContentType(ContentType.Widgets))) {
981                    tabHost.setCurrentTabFromContent(ContentType.Widgets);
982                } else if (currentPage < mNumAppsPages &&
983                        !tag.equals(tabHost.getTabTagForContentType(ContentType.Applications))) {
984                    tabHost.setCurrentTabFromContent(ContentType.Applications);
985                }
986            }
987        }
988    }
989
990    /*
991     * Apps PagedView implementation
992     */
993    private void setVisibilityOnChildren(ViewGroup layout, int visibility) {
994        int childCount = layout.getChildCount();
995        for (int i = 0; i < childCount; ++i) {
996            layout.getChildAt(i).setVisibility(visibility);
997        }
998    }
999    private void setupPage(PagedViewCellLayout layout) {
1000        layout.setCellCount(mCellCountX, mCellCountY);
1001        layout.setGap(mPageLayoutWidthGap, mPageLayoutHeightGap);
1002        layout.setPadding(mPageLayoutPaddingLeft, mPageLayoutPaddingTop,
1003                mPageLayoutPaddingRight, mPageLayoutPaddingBottom);
1004
1005        // Note: We force a measure here to get around the fact that when we do layout calculations
1006        // immediately after syncing, we don't have a proper width.  That said, we already know the
1007        // expected page width, so we can actually optimize by hiding all the TextView-based
1008        // children that are expensive to measure, and let that happen naturally later.
1009        setVisibilityOnChildren(layout, View.GONE);
1010        int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.AT_MOST);
1011        int heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST);
1012        layout.setMinimumWidth(getPageContentWidth());
1013        layout.measure(widthSpec, heightSpec);
1014        setVisibilityOnChildren(layout, View.VISIBLE);
1015    }
1016
1017    public void syncAppsPageItems(int page, boolean immediate) {
1018        // ensure that we have the right number of items on the pages
1019        final boolean isRtl = isLayoutRtl();
1020        int numCells = mCellCountX * mCellCountY;
1021        int startIndex = page * numCells;
1022        int endIndex = Math.min(startIndex + numCells, mApps.size());
1023        PagedViewCellLayout layout = (PagedViewCellLayout) getPageAt(page);
1024
1025        layout.removeAllViewsOnPage();
1026        ArrayList<Object> items = new ArrayList<Object>();
1027        ArrayList<Bitmap> images = new ArrayList<Bitmap>();
1028        for (int i = startIndex; i < endIndex; ++i) {
1029            ApplicationInfo info = mApps.get(i);
1030            PagedViewIcon icon = (PagedViewIcon) mLayoutInflater.inflate(
1031                    R.layout.apps_customize_application, layout, false);
1032            icon.applyFromApplicationInfo(info, true, this);
1033            icon.setOnClickListener(this);
1034            icon.setOnLongClickListener(this);
1035            icon.setOnTouchListener(this);
1036            icon.setOnKeyListener(this);
1037
1038            int index = i - startIndex;
1039            int x = index % mCellCountX;
1040            int y = index / mCellCountX;
1041            if (isRtl) {
1042                x = mCellCountX - x - 1;
1043            }
1044            layout.addViewToCellLayout(icon, -1, i, new PagedViewCellLayout.LayoutParams(x,y, 1,1));
1045
1046            items.add(info);
1047            images.add(info.iconBitmap);
1048        }
1049
1050        enableHwLayersOnVisiblePages();
1051    }
1052
1053    /**
1054     * A helper to return the priority for loading of the specified widget page.
1055     */
1056    private int getWidgetPageLoadPriority(int page) {
1057        // If we are snapping to another page, use that index as the target page index
1058        int toPage = mCurrentPage;
1059        if (mNextPage > -1) {
1060            toPage = mNextPage;
1061        }
1062
1063        // We use the distance from the target page as an initial guess of priority, but if there
1064        // are no pages of higher priority than the page specified, then bump up the priority of
1065        // the specified page.
1066        Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator();
1067        int minPageDiff = Integer.MAX_VALUE;
1068        while (iter.hasNext()) {
1069            AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next();
1070            minPageDiff = Math.abs(task.page - toPage);
1071        }
1072
1073        int rawPageDiff = Math.abs(page - toPage);
1074        return rawPageDiff - Math.min(rawPageDiff, minPageDiff);
1075    }
1076    /**
1077     * Return the appropriate thread priority for loading for a given page (we give the current
1078     * page much higher priority)
1079     */
1080    private int getThreadPriorityForPage(int page) {
1081        // TODO-APPS_CUSTOMIZE: detect number of cores and set thread priorities accordingly below
1082        int pageDiff = getWidgetPageLoadPriority(page);
1083        if (pageDiff <= 0) {
1084            return Process.THREAD_PRIORITY_LESS_FAVORABLE;
1085        } else if (pageDiff <= 1) {
1086            return Process.THREAD_PRIORITY_LOWEST;
1087        } else {
1088            return Process.THREAD_PRIORITY_LOWEST;
1089        }
1090    }
1091    private int getSleepForPage(int page) {
1092        int pageDiff = getWidgetPageLoadPriority(page);
1093        return Math.max(0, pageDiff * sPageSleepDelay);
1094    }
1095    /**
1096     * Creates and executes a new AsyncTask to load a page of widget previews.
1097     */
1098    private void prepareLoadWidgetPreviewsTask(int page, ArrayList<Object> widgets,
1099            int cellWidth, int cellHeight, int cellCountX) {
1100
1101        // Prune all tasks that are no longer needed
1102        Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator();
1103        while (iter.hasNext()) {
1104            AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next();
1105            int taskPage = task.page;
1106            if (taskPage < getAssociatedLowerPageBound(mCurrentPage) ||
1107                    taskPage > getAssociatedUpperPageBound(mCurrentPage)) {
1108                task.cancel(false);
1109                iter.remove();
1110            } else {
1111                task.setThreadPriority(getThreadPriorityForPage(taskPage));
1112            }
1113        }
1114
1115        // We introduce a slight delay to order the loading of side pages so that we don't thrash
1116        final int sleepMs = getSleepForPage(page);
1117        AsyncTaskPageData pageData = new AsyncTaskPageData(page, widgets, cellWidth, cellHeight,
1118            new AsyncTaskCallback() {
1119                @Override
1120                public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) {
1121                    try {
1122                        try {
1123                            Thread.sleep(sleepMs);
1124                        } catch (Exception e) {}
1125                        loadWidgetPreviewsInBackground(task, data);
1126                    } finally {
1127                        if (task.isCancelled()) {
1128                            data.cleanup(true);
1129                        }
1130                    }
1131                }
1132            },
1133            new AsyncTaskCallback() {
1134                @Override
1135                public void run(AppsCustomizeAsyncTask task, AsyncTaskPageData data) {
1136                    mRunningTasks.remove(task);
1137                    if (task.isCancelled()) return;
1138                    // do cleanup inside onSyncWidgetPageItems
1139                    onSyncWidgetPageItems(data);
1140                }
1141            }, mWidgetPreviewLoader);
1142
1143        // Ensure that the task is appropriately prioritized and runs in parallel
1144        AppsCustomizeAsyncTask t = new AppsCustomizeAsyncTask(page,
1145                AsyncTaskPageData.Type.LoadWidgetPreviewData);
1146        t.setThreadPriority(getThreadPriorityForPage(page));
1147        t.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, pageData);
1148        mRunningTasks.add(t);
1149    }
1150
1151    /*
1152     * Widgets PagedView implementation
1153     */
1154    private void setupPage(PagedViewGridLayout layout) {
1155        layout.setPadding(mPageLayoutPaddingLeft, mPageLayoutPaddingTop,
1156                mPageLayoutPaddingRight, mPageLayoutPaddingBottom);
1157
1158        // Note: We force a measure here to get around the fact that when we do layout calculations
1159        // immediately after syncing, we don't have a proper width.
1160        int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.AT_MOST);
1161        int heightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST);
1162        layout.setMinimumWidth(getPageContentWidth());
1163        layout.measure(widthSpec, heightSpec);
1164    }
1165
1166    public void syncWidgetPageItems(final int page, final boolean immediate) {
1167        int numItemsPerPage = mWidgetCountX * mWidgetCountY;
1168
1169        // Calculate the dimensions of each cell we are giving to each widget
1170        final ArrayList<Object> items = new ArrayList<Object>();
1171        int contentWidth = mWidgetSpacingLayout.getContentWidth();
1172        final int cellWidth = ((contentWidth - mPageLayoutPaddingLeft - mPageLayoutPaddingRight
1173                - ((mWidgetCountX - 1) * mWidgetWidthGap)) / mWidgetCountX);
1174        int contentHeight = mWidgetSpacingLayout.getContentHeight();
1175        final int cellHeight = ((contentHeight - mPageLayoutPaddingTop - mPageLayoutPaddingBottom
1176                - ((mWidgetCountY - 1) * mWidgetHeightGap)) / mWidgetCountY);
1177
1178        // Prepare the set of widgets to load previews for in the background
1179        int offset = (page - mNumAppsPages) * numItemsPerPage;
1180        for (int i = offset; i < Math.min(offset + numItemsPerPage, mWidgets.size()); ++i) {
1181            items.add(mWidgets.get(i));
1182        }
1183
1184        // Prepopulate the pages with the other widget info, and fill in the previews later
1185        final PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(page);
1186        layout.setColumnCount(layout.getCellCountX());
1187        for (int i = 0; i < items.size(); ++i) {
1188            Object rawInfo = items.get(i);
1189            PendingAddItemInfo createItemInfo = null;
1190            PagedViewWidget widget = (PagedViewWidget) mLayoutInflater.inflate(
1191                    R.layout.apps_customize_widget, layout, false);
1192            if (rawInfo instanceof AppWidgetProviderInfo) {
1193                // Fill in the widget information
1194                AppWidgetProviderInfo info = (AppWidgetProviderInfo) rawInfo;
1195                createItemInfo = new PendingAddWidgetInfo(info, null, null);
1196
1197                // Determine the widget spans and min resize spans.
1198                int[] spanXY = Launcher.getSpanForWidget(mLauncher, info);
1199                createItemInfo.spanX = spanXY[0];
1200                createItemInfo.spanY = spanXY[1];
1201                int[] minSpanXY = Launcher.getMinSpanForWidget(mLauncher, info);
1202                createItemInfo.minSpanX = minSpanXY[0];
1203                createItemInfo.minSpanY = minSpanXY[1];
1204
1205                widget.applyFromAppWidgetProviderInfo(info, -1, spanXY, mWidgetPreviewLoader);
1206                widget.setTag(createItemInfo);
1207                widget.setShortPressListener(this);
1208            } else if (rawInfo instanceof ResolveInfo) {
1209                // Fill in the shortcuts information
1210                ResolveInfo info = (ResolveInfo) rawInfo;
1211                createItemInfo = new PendingAddShortcutInfo(info.activityInfo);
1212                createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
1213                createItemInfo.componentName = new ComponentName(info.activityInfo.packageName,
1214                        info.activityInfo.name);
1215                widget.applyFromResolveInfo(mPackageManager, info, mWidgetPreviewLoader);
1216                widget.setTag(createItemInfo);
1217            }
1218            widget.setOnClickListener(this);
1219            widget.setOnLongClickListener(this);
1220            widget.setOnTouchListener(this);
1221            widget.setOnKeyListener(this);
1222
1223            // Layout each widget
1224            int ix = i % mWidgetCountX;
1225            int iy = i / mWidgetCountX;
1226            GridLayout.LayoutParams lp = new GridLayout.LayoutParams(
1227                    GridLayout.spec(iy, GridLayout.START),
1228                    GridLayout.spec(ix, GridLayout.TOP));
1229            lp.width = cellWidth;
1230            lp.height = cellHeight;
1231            lp.setGravity(Gravity.TOP | Gravity.START);
1232            if (ix > 0) lp.leftMargin = mWidgetWidthGap;
1233            if (iy > 0) lp.topMargin = mWidgetHeightGap;
1234            layout.addView(widget, lp);
1235        }
1236
1237        // wait until a call on onLayout to start loading, because
1238        // PagedViewWidget.getPreviewSize() will return 0 if it hasn't been laid out
1239        // TODO: can we do a measure/layout immediately?
1240        layout.setOnLayoutListener(new Runnable() {
1241            public void run() {
1242                // Load the widget previews
1243                int maxPreviewWidth = cellWidth;
1244                int maxPreviewHeight = cellHeight;
1245                if (layout.getChildCount() > 0) {
1246                    PagedViewWidget w = (PagedViewWidget) layout.getChildAt(0);
1247                    int[] maxSize = w.getPreviewSize();
1248                    maxPreviewWidth = maxSize[0];
1249                    maxPreviewHeight = maxSize[1];
1250                }
1251
1252                mWidgetPreviewLoader.setPreviewSize(
1253                        maxPreviewWidth, maxPreviewHeight, mWidgetSpacingLayout);
1254                if (immediate) {
1255                    AsyncTaskPageData data = new AsyncTaskPageData(page, items,
1256                            maxPreviewWidth, maxPreviewHeight, null, null, mWidgetPreviewLoader);
1257                    loadWidgetPreviewsInBackground(null, data);
1258                    onSyncWidgetPageItems(data);
1259                } else {
1260                    if (mInTransition) {
1261                        mDeferredPrepareLoadWidgetPreviewsTasks.add(this);
1262                    } else {
1263                        prepareLoadWidgetPreviewsTask(page, items,
1264                                maxPreviewWidth, maxPreviewHeight, mWidgetCountX);
1265                    }
1266                }
1267                layout.setOnLayoutListener(null);
1268            }
1269        });
1270    }
1271    private void loadWidgetPreviewsInBackground(AppsCustomizeAsyncTask task,
1272            AsyncTaskPageData data) {
1273        // loadWidgetPreviewsInBackground can be called without a task to load a set of widget
1274        // previews synchronously
1275        if (task != null) {
1276            // Ensure that this task starts running at the correct priority
1277            task.syncThreadPriority();
1278        }
1279
1280        // Load each of the widget/shortcut previews
1281        ArrayList<Object> items = data.items;
1282        ArrayList<Bitmap> images = data.generatedImages;
1283        int count = items.size();
1284        for (int i = 0; i < count; ++i) {
1285            if (task != null) {
1286                // Ensure we haven't been cancelled yet
1287                if (task.isCancelled()) break;
1288                // Before work on each item, ensure that this task is running at the correct
1289                // priority
1290                task.syncThreadPriority();
1291            }
1292
1293            images.add(mWidgetPreviewLoader.getPreview(items.get(i)));
1294        }
1295    }
1296
1297    private void onSyncWidgetPageItems(AsyncTaskPageData data) {
1298        if (mInTransition) {
1299            mDeferredSyncWidgetPageItems.add(data);
1300            return;
1301        }
1302        try {
1303            int page = data.page;
1304            PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(page);
1305
1306            ArrayList<Object> items = data.items;
1307            int count = items.size();
1308            for (int i = 0; i < count; ++i) {
1309                PagedViewWidget widget = (PagedViewWidget) layout.getChildAt(i);
1310                if (widget != null) {
1311                    Bitmap preview = data.generatedImages.get(i);
1312                    widget.applyPreview(new FastBitmapDrawable(preview), i);
1313                }
1314            }
1315
1316            enableHwLayersOnVisiblePages();
1317
1318            // Update all thread priorities
1319            Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator();
1320            while (iter.hasNext()) {
1321                AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next();
1322                int pageIndex = task.page;
1323                task.setThreadPriority(getThreadPriorityForPage(pageIndex));
1324            }
1325        } finally {
1326            data.cleanup(false);
1327        }
1328    }
1329
1330    @Override
1331    public void syncPages() {
1332        removeAllViews();
1333        cancelAllTasks();
1334
1335        Context context = getContext();
1336        for (int j = 0; j < mNumWidgetPages; ++j) {
1337            PagedViewGridLayout layout = new PagedViewGridLayout(context, mWidgetCountX,
1338                    mWidgetCountY);
1339            setupPage(layout);
1340            addView(layout, new PagedView.LayoutParams(LayoutParams.MATCH_PARENT,
1341                    LayoutParams.MATCH_PARENT));
1342        }
1343
1344        for (int i = 0; i < mNumAppsPages; ++i) {
1345            PagedViewCellLayout layout = new PagedViewCellLayout(context);
1346            setupPage(layout);
1347            addView(layout);
1348        }
1349    }
1350
1351    @Override
1352    public void syncPageItems(int page, boolean immediate) {
1353        if (page < mNumAppsPages) {
1354            syncAppsPageItems(page, immediate);
1355        } else {
1356            syncWidgetPageItems(page, immediate);
1357        }
1358    }
1359
1360    // We want our pages to be z-ordered such that the further a page is to the left, the higher
1361    // it is in the z-order. This is important to insure touch events are handled correctly.
1362    View getPageAt(int index) {
1363        return getChildAt(indexToPage(index));
1364    }
1365
1366    @Override
1367    protected int indexToPage(int index) {
1368        return getChildCount() - index - 1;
1369    }
1370
1371    // In apps customize, we have a scrolling effect which emulates pulling cards off of a stack.
1372    @Override
1373    protected void screenScrolled(int screenCenter) {
1374        final boolean isRtl = isLayoutRtl();
1375        super.screenScrolled(screenCenter);
1376
1377        for (int i = 0; i < getChildCount(); i++) {
1378            View v = getPageAt(i);
1379            if (v != null) {
1380                float scrollProgress = getScrollProgress(screenCenter, v, i);
1381
1382                float interpolatedProgress;
1383                float translationX;
1384                float maxScrollProgress = Math.max(0, scrollProgress);
1385                float minScrollProgress = Math.min(0, scrollProgress);
1386
1387                if (isRtl) {
1388                    translationX = maxScrollProgress * v.getMeasuredWidth();
1389                    interpolatedProgress = mZInterpolator.getInterpolation(Math.abs(maxScrollProgress));
1390                } else {
1391                    translationX = minScrollProgress * v.getMeasuredWidth();
1392                    interpolatedProgress = mZInterpolator.getInterpolation(Math.abs(minScrollProgress));
1393                }
1394                float scale = (1 - interpolatedProgress) +
1395                        interpolatedProgress * TRANSITION_SCALE_FACTOR;
1396
1397                float alpha;
1398                if (isRtl && (scrollProgress > 0)) {
1399                    alpha = mAlphaInterpolator.getInterpolation(1 - Math.abs(maxScrollProgress));
1400                } else if (!isRtl && (scrollProgress < 0)) {
1401                    alpha = mAlphaInterpolator.getInterpolation(1 - Math.abs(scrollProgress));
1402                } else {
1403                    //  On large screens we need to fade the page as it nears its leftmost position
1404                    alpha = mLeftScreenAlphaInterpolator.getInterpolation(1 - scrollProgress);
1405                }
1406
1407                v.setCameraDistance(mDensity * CAMERA_DISTANCE);
1408                int pageWidth = v.getMeasuredWidth();
1409                int pageHeight = v.getMeasuredHeight();
1410
1411                if (PERFORM_OVERSCROLL_ROTATION) {
1412                    float xPivot = isRtl ? 1f - TRANSITION_PIVOT : TRANSITION_PIVOT;
1413                    boolean isOverscrollingFirstPage = isRtl ? scrollProgress > 0 : scrollProgress < 0;
1414                    boolean isOverscrollingLastPage = isRtl ? scrollProgress < 0 : scrollProgress > 0;
1415
1416                    if (i == 0 && isOverscrollingFirstPage) {
1417                        // Overscroll to the left
1418                        v.setPivotX(xPivot * pageWidth);
1419                        v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
1420                        scale = 1.0f;
1421                        alpha = 1.0f;
1422                        // On the first page, we don't want the page to have any lateral motion
1423                        translationX = 0;
1424                    } else if (i == getChildCount() - 1 && isOverscrollingLastPage) {
1425                        // Overscroll to the right
1426                        v.setPivotX((1 - xPivot) * pageWidth);
1427                        v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
1428                        scale = 1.0f;
1429                        alpha = 1.0f;
1430                        // On the last page, we don't want the page to have any lateral motion.
1431                        translationX = 0;
1432                    } else {
1433                        v.setPivotY(pageHeight / 2.0f);
1434                        v.setPivotX(pageWidth / 2.0f);
1435                        v.setRotationY(0f);
1436                    }
1437                }
1438
1439                v.setTranslationX(translationX);
1440                v.setScaleX(scale);
1441                v.setScaleY(scale);
1442                v.setAlpha(alpha);
1443
1444                // If the view has 0 alpha, we set it to be invisible so as to prevent
1445                // it from accepting touches
1446                if (alpha == 0) {
1447                    v.setVisibility(INVISIBLE);
1448                } else if (v.getVisibility() != VISIBLE) {
1449                    v.setVisibility(VISIBLE);
1450                }
1451            }
1452        }
1453
1454        enableHwLayersOnVisiblePages();
1455    }
1456
1457    private void enableHwLayersOnVisiblePages() {
1458        final int screenCount = getChildCount();
1459
1460        getVisiblePages(mTempVisiblePagesRange);
1461        int leftScreen = mTempVisiblePagesRange[0];
1462        int rightScreen = mTempVisiblePagesRange[1];
1463        int forceDrawScreen = -1;
1464        if (leftScreen == rightScreen) {
1465            // make sure we're caching at least two pages always
1466            if (rightScreen < screenCount - 1) {
1467                rightScreen++;
1468                forceDrawScreen = rightScreen;
1469            } else if (leftScreen > 0) {
1470                leftScreen--;
1471                forceDrawScreen = leftScreen;
1472            }
1473        } else {
1474            forceDrawScreen = leftScreen + 1;
1475        }
1476
1477        for (int i = 0; i < screenCount; i++) {
1478            final View layout = (View) getPageAt(i);
1479            if (!(leftScreen <= i && i <= rightScreen &&
1480                    (i == forceDrawScreen || shouldDrawChild(layout)))) {
1481                layout.setLayerType(LAYER_TYPE_NONE, null);
1482            }
1483        }
1484
1485        for (int i = 0; i < screenCount; i++) {
1486            final View layout = (View) getPageAt(i);
1487
1488            if (leftScreen <= i && i <= rightScreen &&
1489                    (i == forceDrawScreen || shouldDrawChild(layout))) {
1490                if (layout.getLayerType() != LAYER_TYPE_HARDWARE) {
1491                    layout.setLayerType(LAYER_TYPE_HARDWARE, null);
1492                }
1493            }
1494        }
1495    }
1496
1497    protected void overScroll(float amount) {
1498        acceleratedOverScroll(amount);
1499    }
1500
1501    /**
1502     * Used by the parent to get the content width to set the tab bar to
1503     * @return
1504     */
1505    public int getPageContentWidth() {
1506        return mContentWidth;
1507    }
1508
1509    @Override
1510    protected void onPageEndMoving() {
1511        super.onPageEndMoving();
1512        mForceDrawAllChildrenNextFrame = true;
1513        // We reset the save index when we change pages so that it will be recalculated on next
1514        // rotation
1515        mSaveInstanceStateItemIndex = -1;
1516    }
1517
1518    /*
1519     * AllAppsView implementation
1520     */
1521    public void setup(Launcher launcher, DragController dragController) {
1522        mLauncher = launcher;
1523        mDragController = dragController;
1524    }
1525
1526    /**
1527     * We should call thise method whenever the core data changes (mApps, mWidgets) so that we can
1528     * appropriately determine when to invalidate the PagedView page data.  In cases where the data
1529     * has yet to be set, we can requestLayout() and wait for onDataReady() to be called in the
1530     * next onMeasure() pass, which will trigger an invalidatePageData() itself.
1531     */
1532    private void invalidateOnDataChange() {
1533        if (!isDataReady()) {
1534            // The next layout pass will trigger data-ready if both widgets and apps are set, so
1535            // request a layout to trigger the page data when ready.
1536            requestLayout();
1537        } else {
1538            cancelAllTasks();
1539            invalidatePageData();
1540        }
1541    }
1542
1543    public void setApps(ArrayList<ApplicationInfo> list) {
1544        mApps = list;
1545        Collections.sort(mApps, LauncherModel.getAppNameComparator());
1546        updatePageCountsAndInvalidateData();
1547    }
1548
1549    private void addAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) {
1550        // We add it in place, in alphabetical order
1551        int count = list.size();
1552        for (int i = 0; i < count; ++i) {
1553            ApplicationInfo info = list.get(i);
1554            int index = Collections.binarySearch(mApps, info, LauncherModel.getAppNameComparator());
1555            if (index < 0) {
1556                mApps.add(-(index + 1), info);
1557            }
1558        }
1559    }
1560    public void addApps(ArrayList<ApplicationInfo> list) {
1561        addAppsWithoutInvalidate(list);
1562        updatePageCountsAndInvalidateData();
1563    }
1564    private int findAppByComponent(List<ApplicationInfo> list, ApplicationInfo item) {
1565        ComponentName removeComponent = item.intent.getComponent();
1566        int length = list.size();
1567        for (int i = 0; i < length; ++i) {
1568            ApplicationInfo info = list.get(i);
1569            if (info.user.equals(item.user)
1570                    && info.intent.getComponent().equals(removeComponent)) {
1571                return i;
1572            }
1573        }
1574        return -1;
1575    }
1576    private void removeAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) {
1577        // loop through all the apps and remove apps that have the same component
1578        int length = list.size();
1579        for (int i = 0; i < length; ++i) {
1580            ApplicationInfo info = list.get(i);
1581            int removeIndex = findAppByComponent(mApps, info);
1582            if (removeIndex > -1) {
1583                mApps.remove(removeIndex);
1584            }
1585        }
1586    }
1587    public void removeApps(ArrayList<ApplicationInfo> appInfos) {
1588        removeAppsWithoutInvalidate(appInfos);
1589        updatePageCountsAndInvalidateData();
1590    }
1591    public void updateApps(ArrayList<ApplicationInfo> list) {
1592        // We remove and re-add the updated applications list because it's properties may have
1593        // changed (ie. the title), and this will ensure that the items will be in their proper
1594        // place in the list.
1595        removeAppsWithoutInvalidate(list);
1596        addAppsWithoutInvalidate(list);
1597        updatePageCountsAndInvalidateData();
1598    }
1599
1600    public void reset() {
1601        // If we have reset, then we should not continue to restore the previous state
1602        mSaveInstanceStateItemIndex = -1;
1603
1604        AppsCustomizeTabHost tabHost = getTabHost();
1605        String tag = tabHost.getCurrentTabTag();
1606        if (tag != null) {
1607            if (!tag.equals(tabHost.getTabTagForContentType(ContentType.Applications))) {
1608                tabHost.setCurrentTabFromContent(ContentType.Applications);
1609            }
1610        }
1611
1612        if (mCurrentPage != 0) {
1613            invalidatePageData(0);
1614        }
1615    }
1616
1617    private AppsCustomizeTabHost getTabHost() {
1618        return (AppsCustomizeTabHost) mLauncher.findViewById(R.id.apps_customize_pane);
1619    }
1620
1621    public void dumpState() {
1622        // TODO: Dump information related to current list of Applications, Widgets, etc.
1623        ApplicationInfo.dumpApplicationInfoList(TAG, "mApps", mApps);
1624        dumpAppWidgetProviderInfoList(TAG, "mWidgets", mWidgets);
1625    }
1626
1627    private void dumpAppWidgetProviderInfoList(String tag, String label,
1628            ArrayList<Object> list) {
1629        Log.d(tag, label + " size=" + list.size());
1630        for (Object i: list) {
1631            if (i instanceof AppWidgetProviderInfo) {
1632                AppWidgetProviderInfo info = (AppWidgetProviderInfo) i;
1633                Log.d(tag, "   label=\"" + info.label + "\" previewImage=" + info.previewImage
1634                        + " resizeMode=" + info.resizeMode + " configure=" + info.configure
1635                        + " initialLayout=" + info.initialLayout
1636                        + " minWidth=" + info.minWidth + " minHeight=" + info.minHeight);
1637            } else if (i instanceof ResolveInfo) {
1638                ResolveInfo info = (ResolveInfo) i;
1639                Log.d(tag, "   label=\"" + info.loadLabel(mPackageManager) + "\" icon="
1640                        + info.icon);
1641            }
1642        }
1643    }
1644
1645    public void surrender() {
1646        // TODO: If we are in the middle of any process (ie. for holographic outlines, etc) we
1647        // should stop this now.
1648
1649        // Stop all background tasks
1650        cancelAllTasks();
1651    }
1652
1653    @Override
1654    public void iconPressed(PagedViewIcon icon) {
1655        // Reset the previously pressed icon and store a reference to the pressed icon so that
1656        // we can reset it on return to Launcher (in Launcher.onResume())
1657        if (mPressedIcon != null) {
1658            mPressedIcon.resetDrawableState();
1659        }
1660        mPressedIcon = icon;
1661    }
1662
1663    public void resetDrawableState() {
1664        if (mPressedIcon != null) {
1665            mPressedIcon.resetDrawableState();
1666            mPressedIcon = null;
1667        }
1668    }
1669
1670    /*
1671     * We load an extra page on each side to prevent flashes from scrolling and loading of the
1672     * widget previews in the background with the AsyncTasks.
1673     */
1674    final static int sLookBehindPageCount = 2;
1675    final static int sLookAheadPageCount = 2;
1676    protected int getAssociatedLowerPageBound(int page) {
1677        final int count = getChildCount();
1678        int windowSize = Math.min(count, sLookBehindPageCount + sLookAheadPageCount + 1);
1679        int windowMinIndex = Math.max(Math.min(page - sLookBehindPageCount, count - windowSize), 0);
1680        return windowMinIndex;
1681    }
1682    protected int getAssociatedUpperPageBound(int page) {
1683        final int count = getChildCount();
1684        int windowSize = Math.min(count, sLookBehindPageCount + sLookAheadPageCount + 1);
1685        int windowMaxIndex = Math.min(Math.max(page + sLookAheadPageCount, windowSize - 1),
1686                count - 1);
1687        return windowMaxIndex;
1688    }
1689
1690    @Override
1691    protected String getCurrentPageDescription() {
1692        int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
1693        int stringId = R.string.default_scroll_format;
1694        int count = 0;
1695
1696        if (page < mNumAppsPages) {
1697            stringId = R.string.apps_customize_apps_scroll_format;
1698            count = mNumAppsPages;
1699        } else {
1700            page -= mNumAppsPages;
1701            stringId = R.string.apps_customize_widgets_scroll_format;
1702            count = mNumWidgetPages;
1703        }
1704
1705        return String.format(getContext().getString(stringId), page + 1, count);
1706    }
1707}
1708