Workspace.java revision 472b281d5cb4f5660df981a6c912266b9f5703fe
1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.launcher2;
18
19import android.animation.AnimatorListenerAdapter;
20import com.android.launcher.R;
21
22import android.animation.Animator;
23import android.animation.Animator.AnimatorListener;
24import android.animation.AnimatorSet;
25import android.animation.ObjectAnimator;
26import android.animation.PropertyValuesHolder;
27import android.app.WallpaperManager;
28import android.appwidget.AppWidgetManager;
29import android.appwidget.AppWidgetProviderInfo;
30import android.content.ComponentName;
31import android.content.Context;
32import android.content.Intent;
33import android.content.pm.PackageManager;
34import android.content.pm.ProviderInfo;
35import android.content.res.Resources;
36import android.content.res.TypedArray;
37import android.graphics.Bitmap;
38import android.graphics.Canvas;
39import android.graphics.Color;
40import android.graphics.Matrix;
41import android.graphics.Rect;
42import android.graphics.Region.Op;
43import android.graphics.drawable.Drawable;
44import android.net.Uri;
45import android.os.IBinder;
46import android.os.Parcelable;
47import android.util.AttributeSet;
48import android.util.Log;
49import android.view.MotionEvent;
50import android.view.View;
51import android.widget.TextView;
52
53import java.util.ArrayList;
54import java.util.HashSet;
55
56/**
57 * The workspace is a wide area with a wallpaper and a finite number of pages.
58 * Each page contains a number of icons, folders or widgets the user can
59 * interact with. A workspace is meant to be used with a fixed width only.
60 */
61public class Workspace extends SmoothPagedView
62        implements DropTarget, DragSource, DragScroller, View.OnTouchListener {
63    @SuppressWarnings({"UnusedDeclaration"})
64    private static final String TAG = "Launcher.Workspace";
65
66    // This is how much the workspace shrinks when we enter all apps or
67    // customization mode
68    private static final float SHRINK_FACTOR = 0.16f;
69
70    // Y rotation to apply to the workspace screens
71    private static final float WORKSPACE_ROTATION = 12.5f;
72
73    // These are extra scale factors to apply to the mini home screens
74    // so as to achieve the desired transform
75    private static final float EXTRA_SCALE_FACTOR_0 = 0.97f;
76    private static final float EXTRA_SCALE_FACTOR_1 = 1.0f;
77    private static final float EXTRA_SCALE_FACTOR_2 = 1.08f;
78
79    private static final int BACKGROUND_FADE_OUT_DELAY = 300;
80    private static final int BACKGROUND_FADE_OUT_DURATION = 300;
81    private static final int BACKGROUND_FADE_IN_DURATION = 100;
82
83    // These animators are used to fade the background
84    private ObjectAnimator mBackgroundFadeInAnimation;
85    private ObjectAnimator mBackgroundFadeOutAnimation;
86    private float mBackgroundAlpha = 0;
87
88    private final WallpaperManager mWallpaperManager;
89
90    private int mDefaultPage;
91
92    private boolean mWaitingToShrinkToBottom = false;
93
94    private boolean mPageMoving = false;
95
96    /**
97     * CellInfo for the cell that is currently being dragged
98     */
99    private CellLayout.CellInfo mDragInfo;
100
101    /**
102     * Target drop area calculated during last acceptDrop call.
103     */
104    private int[] mTargetCell = null;
105
106    /**
107     * The CellLayout that is currently being dragged over
108     */
109    private CellLayout mDragTargetLayout = null;
110
111    private Launcher mLauncher;
112    private IconCache mIconCache;
113    private DragController mDragController;
114
115    // These are temporary variables to prevent having to allocate a new object just to
116    // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
117    private int[] mTempCell = new int[2];
118    private int[] mTempEstimate = new int[2];
119    private float[] mTempOriginXY = new float[2];
120    private float[] mTempDragCoordinates = new float[2];
121    private float[] mTempTouchCoordinates = new float[2];
122    private float[] mTempCellLayoutCenterCoordinates = new float[2];
123    private float[] mTempDragBottomRightCoordinates = new float[2];
124    private Matrix mTempInverseMatrix = new Matrix();
125
126    private static final int DEFAULT_CELL_COUNT_X = 4;
127    private static final int DEFAULT_CELL_COUNT_Y = 4;
128
129    private Drawable mPreviousIndicator;
130    private Drawable mNextIndicator;
131
132    // State variable that indicates whether the pages are small (ie when you're
133    // in all apps or customize mode)
134    private boolean mIsSmall = false;
135    private boolean mIsInUnshrinkAnimation = false;
136    private AnimatorListener mUnshrinkAnimationListener;
137    private enum ShrinkPosition {
138        SHRINK_TO_TOP, SHRINK_TO_MIDDLE, SHRINK_TO_BOTTOM_HIDDEN, SHRINK_TO_BOTTOM_VISIBLE };
139    private ShrinkPosition mShrunkenState;
140
141    private boolean mInScrollArea = false;
142
143    private HolographicOutlineHelper mOutlineHelper = new HolographicOutlineHelper();
144    private Bitmap mDragOutline = null;
145    private Rect mTempRect = new Rect();
146    private int[] mTempXY = new int[2];
147
148    /**
149     * Used to inflate the Workspace from XML.
150     *
151     * @param context The application's context.
152     * @param attrs The attributes set containing the Workspace's customization values.
153     */
154    public Workspace(Context context, AttributeSet attrs) {
155        this(context, attrs, 0);
156    }
157
158    /**
159     * Used to inflate the Workspace from XML.
160     *
161     * @param context The application's context.
162     * @param attrs The attributes set containing the Workspace's customization values.
163     * @param defStyle Unused.
164     */
165    public Workspace(Context context, AttributeSet attrs, int defStyle) {
166        super(context, attrs, defStyle);
167        mContentIsRefreshable = false;
168
169        if (!LauncherApplication.isScreenXLarge()) {
170            mFadeInAdjacentScreens = false;
171        }
172
173        mWallpaperManager = WallpaperManager.getInstance(context);
174
175        TypedArray a = context.obtainStyledAttributes(attrs,
176                R.styleable.Workspace, defStyle, 0);
177        int cellCountX = a.getInt(R.styleable.Workspace_cellCountX, DEFAULT_CELL_COUNT_X);
178        int cellCountY = a.getInt(R.styleable.Workspace_cellCountY, DEFAULT_CELL_COUNT_Y);
179        mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
180        a.recycle();
181
182        LauncherModel.updateWorkspaceLayoutCells(cellCountX, cellCountY);
183        setHapticFeedbackEnabled(false);
184
185        initWorkspace();
186    }
187
188    /**
189     * Initializes various states for this workspace.
190     */
191    protected void initWorkspace() {
192        Context context = getContext();
193        mCurrentPage = mDefaultPage;
194        Launcher.setScreen(mCurrentPage);
195        LauncherApplication app = (LauncherApplication)context.getApplicationContext();
196        mIconCache = app.getIconCache();
197
198        mUnshrinkAnimationListener = new AnimatorListenerAdapter() {
199            public void onAnimationStart(Animator animation) {
200                mIsInUnshrinkAnimation = true;
201            }
202            public void onAnimationEnd(Animator animation) {
203                mIsInUnshrinkAnimation = false;
204            }
205        };
206
207        mSnapVelocity = 600;
208    }
209
210    @Override
211    protected int getScrollMode() {
212        if (LauncherApplication.isScreenXLarge()) {
213            return SmoothPagedView.QUINTIC_MODE;
214        } else {
215            return SmoothPagedView.OVERSHOOT_MODE;
216        }
217    }
218
219    @Override
220    public void addView(View child, int index, LayoutParams params) {
221        if (!(child instanceof CellLayout)) {
222            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
223        }
224        ((CellLayout) child).setOnInterceptTouchListener(this);
225        super.addView(child, index, params);
226    }
227
228    @Override
229    public void addView(View child) {
230        if (!(child instanceof CellLayout)) {
231            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
232        }
233        ((CellLayout) child).setOnInterceptTouchListener(this);
234        super.addView(child);
235    }
236
237    @Override
238    public void addView(View child, int index) {
239        if (!(child instanceof CellLayout)) {
240            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
241        }
242        ((CellLayout) child).setOnInterceptTouchListener(this);
243        super.addView(child, index);
244    }
245
246    @Override
247    public void addView(View child, int width, int height) {
248        if (!(child instanceof CellLayout)) {
249            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
250        }
251        ((CellLayout) child).setOnInterceptTouchListener(this);
252        super.addView(child, width, height);
253    }
254
255    @Override
256    public void addView(View child, LayoutParams params) {
257        if (!(child instanceof CellLayout)) {
258            throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
259        }
260        ((CellLayout) child).setOnInterceptTouchListener(this);
261        super.addView(child, params);
262    }
263
264    /**
265     * @return The open folder on the current screen, or null if there is none
266     */
267    Folder getOpenFolder() {
268        CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage);
269        int count = currentPage.getChildCount();
270        for (int i = 0; i < count; i++) {
271            View child = currentPage.getChildAt(i);
272            if (child instanceof Folder) {
273                Folder folder = (Folder) child;
274                if (folder.getInfo().opened)
275                    return folder;
276            }
277        }
278        return null;
279    }
280
281    ArrayList<Folder> getOpenFolders() {
282        final int screenCount = getChildCount();
283        ArrayList<Folder> folders = new ArrayList<Folder>(screenCount);
284
285        for (int screen = 0; screen < screenCount; screen++) {
286            CellLayout currentPage = (CellLayout) getChildAt(screen);
287            int count = currentPage.getChildCount();
288            for (int i = 0; i < count; i++) {
289                View child = currentPage.getChildAt(i);
290                if (child instanceof Folder) {
291                    Folder folder = (Folder) child;
292                    if (folder.getInfo().opened)
293                        folders.add(folder);
294                    break;
295                }
296            }
297        }
298
299        return folders;
300    }
301
302    boolean isDefaultPageShowing() {
303        return mCurrentPage == mDefaultPage;
304    }
305
306    /**
307     * Sets the current screen.
308     *
309     * @param currentPage
310     */
311    @Override
312    void setCurrentPage(int currentPage) {
313        super.setCurrentPage(currentPage);
314        updateWallpaperOffset(mScrollX);
315    }
316
317    /**
318     * Adds the specified child in the specified screen. The position and dimension of
319     * the child are defined by x, y, spanX and spanY.
320     *
321     * @param child The child to add in one of the workspace's screens.
322     * @param screen The screen in which to add the child.
323     * @param x The X position of the child in the screen's grid.
324     * @param y The Y position of the child in the screen's grid.
325     * @param spanX The number of cells spanned horizontally by the child.
326     * @param spanY The number of cells spanned vertically by the child.
327     */
328    void addInScreen(View child, int screen, int x, int y, int spanX, int spanY) {
329        addInScreen(child, screen, x, y, spanX, spanY, false);
330    }
331
332    void addInFullScreen(View child, int screen) {
333        addInScreen(child, screen, 0, 0, -1, -1);
334    }
335
336    /**
337     * Adds the specified child in the specified screen. The position and dimension of
338     * the child are defined by x, y, spanX and spanY.
339     *
340     * @param child The child to add in one of the workspace's screens.
341     * @param screen The screen in which to add the child.
342     * @param x The X position of the child in the screen's grid.
343     * @param y The Y position of the child in the screen's grid.
344     * @param spanX The number of cells spanned horizontally by the child.
345     * @param spanY The number of cells spanned vertically by the child.
346     * @param insert When true, the child is inserted at the beginning of the children list.
347     */
348    void addInScreen(View child, int screen, int x, int y, int spanX, int spanY, boolean insert) {
349        if (screen < 0 || screen >= getChildCount()) {
350            Log.e(TAG, "The screen must be >= 0 and < " + getChildCount()
351                + " (was " + screen + "); skipping child");
352            return;
353        }
354
355        final CellLayout group = (CellLayout) getChildAt(screen);
356        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
357        if (lp == null) {
358            lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
359        } else {
360            lp.cellX = x;
361            lp.cellY = y;
362            lp.cellHSpan = spanX;
363            lp.cellVSpan = spanY;
364        }
365
366        // Get the canonical child id to uniquely represent this view in this screen
367        int childId = LauncherModel.getCellLayoutChildId(-1, screen, x, y, spanX, spanY);
368        if (!group.addViewToCellLayout(child, insert ? 0 : -1, childId, lp)) {
369            // TODO: This branch occurs when the workspace is adding views
370            // outside of the defined grid
371            // maybe we should be deleting these items from the LauncherModel?
372            Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
373        }
374
375        if (!(child instanceof Folder)) {
376            child.setHapticFeedbackEnabled(false);
377            child.setOnLongClickListener(mLongClickListener);
378        }
379        if (child instanceof DropTarget) {
380            mDragController.addDropTarget((DropTarget) child);
381        }
382    }
383
384    public boolean onTouch(View v, MotionEvent event) {
385        // this is an intercepted event being forwarded from a cell layout
386        if (mIsSmall || mIsInUnshrinkAnimation) {
387            mLauncher.onWorkspaceClick((CellLayout) v);
388            return true;
389        } else if (!mPageMoving) {
390            if (v == getChildAt(mCurrentPage - 1)) {
391                snapToPage(mCurrentPage - 1);
392                return true;
393            } else if (v == getChildAt(mCurrentPage + 1)) {
394                snapToPage(mCurrentPage + 1);
395                return true;
396            }
397        }
398        return false;
399    }
400
401    @Override
402    public boolean dispatchUnhandledMove(View focused, int direction) {
403        if (mIsSmall || mIsInUnshrinkAnimation) {
404            // when the home screens are shrunken, shouldn't allow side-scrolling
405            return false;
406        }
407        return super.dispatchUnhandledMove(focused, direction);
408    }
409
410    @Override
411    public boolean onInterceptTouchEvent(MotionEvent ev) {
412        if (mIsSmall || mIsInUnshrinkAnimation) {
413            // when the home screens are shrunken, shouldn't allow side-scrolling
414            return false;
415        }
416        return super.onInterceptTouchEvent(ev);
417    }
418
419    protected void onPageBeginMoving() {
420        if (mNextPage != INVALID_PAGE) {
421            // we're snapping to a particular screen
422            enableChildrenCache(mCurrentPage, mNextPage);
423        } else {
424            // this is when user is actively dragging a particular screen, they might
425            // swipe it either left or right (but we won't advance by more than one screen)
426            enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1);
427        }
428        showOutlines();
429        mPageMoving = true;
430    }
431
432    protected void onPageEndMoving() {
433        clearChildrenCache();
434        // Hide the outlines, as long as we're not dragging
435        if (!mDragController.dragging()) {
436            hideOutlines();
437        }
438        mPageMoving = false;
439    }
440
441    @Override
442    protected void notifyPageSwitchListener() {
443        super.notifyPageSwitchListener();
444
445        if (mPreviousIndicator != null) {
446            // if we know the next page, we show the indication for it right away; it looks
447            // weird if the indicators are lagging
448            int page = mNextPage;
449            if (page == INVALID_PAGE) {
450                page = mCurrentPage;
451            }
452            mPreviousIndicator.setLevel(page);
453            mNextIndicator.setLevel(page);
454        }
455        Launcher.setScreen(mCurrentPage);
456    };
457
458    private void updateWallpaperOffset() {
459        updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft));
460    }
461
462    private void updateWallpaperOffset(int scrollRange) {
463        final boolean isStaticWallpaper = (mWallpaperManager != null) &&
464                (mWallpaperManager.getWallpaperInfo() == null);
465        if (LauncherApplication.isScreenXLarge() && !isStaticWallpaper) {
466            IBinder token = getWindowToken();
467            if (token != null) {
468                mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 );
469                mWallpaperManager.setWallpaperOffsets(getWindowToken(),
470                        Math.max(0.f, Math.min(mScrollX/(float)scrollRange, 1.f)), 0);
471            }
472        }
473    }
474
475    public void showOutlines() {
476        if (!mIsSmall && !mIsInUnshrinkAnimation) {
477            if (mBackgroundFadeOutAnimation != null) mBackgroundFadeOutAnimation.cancel();
478            if (mBackgroundFadeInAnimation != null) mBackgroundFadeInAnimation.cancel();
479            mBackgroundFadeInAnimation = ObjectAnimator.ofFloat(this, "backgroundAlpha", 1.0f);
480            mBackgroundFadeInAnimation.setDuration(BACKGROUND_FADE_IN_DURATION);
481            mBackgroundFadeInAnimation.start();
482        }
483    }
484
485    public void hideOutlines() {
486        if (!mIsSmall && !mIsInUnshrinkAnimation) {
487            if (mBackgroundFadeInAnimation != null) mBackgroundFadeInAnimation.cancel();
488            if (mBackgroundFadeOutAnimation != null) mBackgroundFadeOutAnimation.cancel();
489            mBackgroundFadeOutAnimation = ObjectAnimator.ofFloat(this, "backgroundAlpha", 0.0f);
490            mBackgroundFadeOutAnimation.setDuration(BACKGROUND_FADE_OUT_DURATION);
491            mBackgroundFadeOutAnimation.setStartDelay(BACKGROUND_FADE_OUT_DELAY);
492            mBackgroundFadeOutAnimation.start();
493        }
494    }
495
496    public void setBackgroundAlpha(float alpha) {
497        mBackgroundAlpha = alpha;
498        for (int i = 0; i < getChildCount(); i++) {
499            CellLayout cl = (CellLayout) getChildAt(i);
500            cl.setBackgroundAlpha(alpha);
501        }
502    }
503
504    public float getBackgroundAlpha() {
505        return mBackgroundAlpha;
506    }
507
508    @Override
509    protected void screenScrolled(int screenCenter) {
510        final int halfScreenSize = getMeasuredWidth() / 2;
511        for (int i = 0; i < getChildCount(); i++) {
512            View v = getChildAt(i);
513            if (v != null) {
514                int totalDistance = v.getMeasuredWidth() + mPageSpacing;
515                int delta = screenCenter - (getChildOffset(i) -
516                        getRelativeChildOffset(i) + halfScreenSize);
517
518                float scrollProgress = delta/(totalDistance*1.0f);
519                scrollProgress = Math.min(scrollProgress, 1.0f);
520                scrollProgress = Math.max(scrollProgress, -1.0f);
521
522                float rotation = WORKSPACE_ROTATION * scrollProgress;
523                v.setRotationY(rotation);
524            }
525        }
526    }
527
528    protected void onAttachedToWindow() {
529        super.onAttachedToWindow();
530        computeScroll();
531        mDragController.setWindowToken(getWindowToken());
532    }
533
534    @Override
535    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
536        super.onLayout(changed, left, top, right, bottom);
537
538        // if shrinkToBottom() is called on initialization, it has to be deferred
539        // until after the first call to onLayout so that it has the correct width
540        if (mWaitingToShrinkToBottom) {
541            shrinkToBottom(false);
542            mWaitingToShrinkToBottom = false;
543        }
544
545        if (LauncherApplication.isInPlaceRotationEnabled()) {
546            // When the device is rotated, the scroll position of the current screen
547            // needs to be refreshed
548            setCurrentPage(getCurrentPage());
549        }
550    }
551
552    @Override
553    protected void dispatchDraw(Canvas canvas) {
554        if (mIsSmall || mIsInUnshrinkAnimation) {
555            // Draw all the workspaces if we're small
556            final int pageCount = getChildCount();
557            final long drawingTime = getDrawingTime();
558            for (int i = 0; i < pageCount; i++) {
559                final View page = (View) getChildAt(i);
560
561                drawChild(canvas, page, drawingTime);
562            }
563        } else {
564            super.dispatchDraw(canvas);
565        }
566    }
567
568    @Override
569    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
570        if (!mLauncher.isAllAppsVisible()) {
571            final Folder openFolder = getOpenFolder();
572            if (openFolder != null) {
573                return openFolder.requestFocus(direction, previouslyFocusedRect);
574            } else {
575                return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
576            }
577        }
578        return false;
579    }
580
581    @Override
582    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
583        if (!mLauncher.isAllAppsVisible()) {
584            final Folder openFolder = getOpenFolder();
585            if (openFolder != null) {
586                openFolder.addFocusables(views, direction);
587            } else {
588                super.addFocusables(views, direction, focusableMode);
589            }
590        }
591    }
592
593    @Override
594    public boolean dispatchTouchEvent(MotionEvent ev) {
595        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
596            // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps
597            // ie when you click on a mini-screen, it zooms back to that screen)
598            if (!LauncherApplication.isScreenXLarge() && mLauncher.isAllAppsVisible()) {
599                return false;
600            }
601        }
602        return super.dispatchTouchEvent(ev);
603    }
604
605    void enableChildrenCache(int fromPage, int toPage) {
606        if (fromPage > toPage) {
607            final int temp = fromPage;
608            fromPage = toPage;
609            toPage = temp;
610        }
611
612        final int screenCount = getChildCount();
613
614        fromPage = Math.max(fromPage, 0);
615        toPage = Math.min(toPage, screenCount - 1);
616
617        for (int i = fromPage; i <= toPage; i++) {
618            final CellLayout layout = (CellLayout) getChildAt(i);
619            layout.setChildrenDrawnWithCacheEnabled(true);
620            layout.setChildrenDrawingCacheEnabled(true);
621        }
622    }
623
624    void clearChildrenCache() {
625        final int screenCount = getChildCount();
626        for (int i = 0; i < screenCount; i++) {
627            final CellLayout layout = (CellLayout) getChildAt(i);
628            layout.setChildrenDrawnWithCacheEnabled(false);
629        }
630    }
631
632    @Override
633    public boolean onTouchEvent(MotionEvent ev) {
634        if (mLauncher.isAllAppsVisible()) {
635            // Cancel any scrolling that is in progress.
636            if (!mScroller.isFinished()) {
637                mScroller.abortAnimation();
638            }
639            setCurrentPage(mCurrentPage);
640            return false; // We don't want the events.  Let them fall through to the all apps view.
641        }
642
643        return super.onTouchEvent(ev);
644    }
645
646    public boolean isSmall() {
647        return mIsSmall;
648    }
649
650    void shrinkToTop(boolean animated) {
651        shrink(ShrinkPosition.SHRINK_TO_TOP, animated);
652    }
653
654    void shrinkToMiddle() {
655        shrink(ShrinkPosition.SHRINK_TO_MIDDLE, true);
656    }
657
658    void shrinkToBottom() {
659        shrinkToBottom(true);
660    }
661
662    void shrinkToBottom(boolean animated) {
663        if (mFirstLayout) {
664            // (mFirstLayout == "first layout has not happened yet")
665            // if we get a call to shrink() as part of our initialization (for example, if
666            // Launcher is started in All Apps mode) then we need to wait for a layout call
667            // to get our width so we can layout the mini-screen views correctly
668            mWaitingToShrinkToBottom = true;
669        } else {
670            shrink(ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN, animated);
671        }
672    }
673
674    private float getYScaleForScreen(int screen) {
675        int x = Math.abs(screen - 2);
676
677        // TODO: This should be generalized for use with arbitrary rotation angles.
678        switch(x) {
679            case 0: return EXTRA_SCALE_FACTOR_0;
680            case 1: return EXTRA_SCALE_FACTOR_1;
681            case 2: return EXTRA_SCALE_FACTOR_2;
682        }
683        return 1.0f;
684    }
685
686    // we use this to shrink the workspace for the all apps view and the customize view
687    private void shrink(ShrinkPosition shrinkPosition, boolean animated) {
688        mIsSmall = true;
689        mShrunkenState = shrinkPosition;
690
691        // Stop any scrolling, move to the current page right away
692        setCurrentPage(mCurrentPage);
693        updateWhichPagesAcceptDrops(mShrunkenState);
694
695        // we intercept and reject all touch events when we're small, so be sure to reset the state
696        mTouchState = TOUCH_STATE_REST;
697        mActivePointerId = INVALID_POINTER;
698
699        final Resources res = getResources();
700        final int screenWidth = getWidth();
701        final int screenHeight = getHeight();
702
703        // Making the assumption that all pages have the same width as the 0th
704        final int pageWidth = getChildAt(0).getMeasuredWidth();
705        final int pageHeight = getChildAt(0).getMeasuredHeight();
706
707        final int scaledPageWidth = (int) (SHRINK_FACTOR * pageWidth);
708        final int scaledPageHeight = (int) (SHRINK_FACTOR * pageHeight);
709        final float extraScaledSpacing = res.getDimension(R.dimen.smallScreenExtraSpacing);
710
711        final int screenCount = getChildCount();
712        float totalWidth = screenCount * scaledPageWidth + (screenCount - 1) * extraScaledSpacing;
713
714        float newY = getResources().getDimension(R.dimen.smallScreenVerticalMargin);
715        float finalAlpha = 1.0f;
716        float extraShrinkFactor = 1.0f;
717        if (shrinkPosition == ShrinkPosition.SHRINK_TO_BOTTOM_VISIBLE) {
718             newY = screenHeight - newY - scaledPageHeight;
719        } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN) {
720
721            // We shrink and disappear to nothing in the case of all apps
722            // (which is when we shrink to the bottom)
723            newY = screenHeight - newY - scaledPageHeight;
724            finalAlpha = 0.0f;
725            extraShrinkFactor = 0.1f;
726        } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_MIDDLE) {
727            newY = screenHeight / 2 - scaledPageHeight / 2;
728            finalAlpha = 1.0f;
729        } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_TOP) {
730            newY = screenHeight / 10;
731        }
732
733        // We animate all the screens to the centered position in workspace
734        // At the same time, the screens become greyed/dimmed
735
736        // newX is initialized to the left-most position of the centered screens
737        float newX = mScroller.getFinalX() + screenWidth / 2 - totalWidth / 2;
738
739        // We are going to scale about the center of the view, so we need to adjust the positions
740        // of the views accordingly
741        newX -= (pageWidth - scaledPageWidth) / 2.0f;
742        newY -= (pageHeight - scaledPageHeight) / 2.0f;
743        for (int i = 0; i < screenCount; i++) {
744            CellLayout cl = (CellLayout) getChildAt(i);
745
746            float rotation = (-i + 2) * WORKSPACE_ROTATION;
747            float rotationScaleX = (float) (1.0f / Math.cos(Math.PI * rotation / 180.0f));
748            float rotationScaleY = getYScaleForScreen(i);
749
750            if (animated) {
751                final int duration = res.getInteger(R.integer.config_workspaceShrinkTime);
752                ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(cl,
753                        PropertyValuesHolder.ofFloat("x", newX),
754                        PropertyValuesHolder.ofFloat("y", newY),
755                        PropertyValuesHolder.ofFloat("scaleX",
756                                SHRINK_FACTOR * rotationScaleX * extraShrinkFactor),
757                        PropertyValuesHolder.ofFloat("scaleY",
758                                SHRINK_FACTOR * rotationScaleY * extraShrinkFactor),
759                        PropertyValuesHolder.ofFloat("backgroundAlpha", finalAlpha),
760                        PropertyValuesHolder.ofFloat("alpha", finalAlpha),
761                        PropertyValuesHolder.ofFloat("rotationY", rotation));
762                anim.setDuration(duration);
763                anim.start();
764            } else {
765                cl.setX((int)newX);
766                cl.setY((int)newY);
767                cl.setScaleX(SHRINK_FACTOR * rotationScaleX);
768                cl.setScaleY(SHRINK_FACTOR * rotationScaleY);
769                cl.setBackgroundAlpha(1.0f);
770                cl.setAlpha(finalAlpha);
771                cl.setRotationY(rotation);
772            }
773            // increment newX for the next screen
774            newX += scaledPageWidth + extraScaledSpacing;
775        }
776        setChildrenDrawnWithCacheEnabled(true);
777    }
778
779
780    private void updateWhichPagesAcceptDrops(ShrinkPosition state) {
781        updateWhichPagesAcceptDropsHelper(state, false, 1, 1);
782    }
783
784
785    private void updateWhichPagesAcceptDropsDuringDrag(ShrinkPosition state, int spanX, int spanY) {
786        updateWhichPagesAcceptDropsHelper(state, true, spanX, spanY);
787    }
788
789    private void updateWhichPagesAcceptDropsHelper(
790            ShrinkPosition state, boolean isDragHappening, int spanX, int spanY) {
791        final int screenCount = getChildCount();
792        for (int i = 0; i < screenCount; i++) {
793            CellLayout cl = (CellLayout) getChildAt(i);
794
795            switch (state) {
796                case SHRINK_TO_TOP:
797                    if (!isDragHappening) {
798                        boolean showDropHighlight = i == mCurrentPage;
799                        cl.setAcceptsDrops(showDropHighlight);
800                        break;
801                    }
802                    // otherwise, fall through below and mark non-full screens as accepting drops
803                case SHRINK_TO_BOTTOM_HIDDEN:
804                case SHRINK_TO_BOTTOM_VISIBLE:
805                    if (!isDragHappening) {
806                        // even if a drag isn't happening, we don't want to show a screen as
807                        // accepting drops if it doesn't have at least one free cell
808                        spanX = 1;
809                        spanY = 1;
810                    }
811                    // the page accepts drops if we can find at least one empty spot
812                    cl.setAcceptsDrops(cl.findCellForSpan(null, spanX, spanY));
813                    break;
814                default:
815                     throw new RuntimeException(
816                             "updateWhichPagesAcceptDropsHelper passed an unhandled ShrinkPosition");
817            }
818        }
819    }
820
821    /*
822     *
823     * We call these methods (onDragStartedWithItemSpans/onDragStartedWithItemMinSize) whenever we
824     * start a drag in Launcher, regardless of whether the drag has ever entered the Workspace
825     *
826     * These methods mark the appropriate pages as accepting drops (which alters their visual
827     * appearance) and, if the pages are hidden, makes them visible.
828     *
829     */
830    public void onDragStartedWithItemSpans(int spanX, int spanY) {
831        updateWhichPagesAcceptDropsDuringDrag(mShrunkenState, spanX, spanY);
832        if (mShrunkenState == ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN) {
833            shrink(ShrinkPosition.SHRINK_TO_BOTTOM_VISIBLE, true);
834        }
835    }
836
837    public void onDragStartedWithItemMinSize(int minWidth, int minHeight) {
838        int[] spanXY = CellLayout.rectToCell(getResources(), minWidth, minHeight, null);
839        onDragStartedWithItemSpans(spanXY[0], spanXY[1]);
840    }
841
842    // we call this method whenever a drag and drop in Launcher finishes, even if Workspace was
843    // never dragged over
844    public void onDragStopped() {
845        updateWhichPagesAcceptDrops(mShrunkenState);
846        if (mShrunkenState == ShrinkPosition.SHRINK_TO_BOTTOM_VISIBLE) {
847            shrink(ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN, true);
848        }
849    }
850
851    // We call this when we trigger an unshrink by clicking on the CellLayout cl
852    public void unshrink(CellLayout clThatWasClicked) {
853        int newCurrentPage = mCurrentPage;
854        final int screenCount = getChildCount();
855        for (int i = 0; i < screenCount; i++) {
856            if (getChildAt(i) == clThatWasClicked) {
857                newCurrentPage = i;
858            }
859        }
860        unshrink(newCurrentPage);
861    }
862
863    @Override
864    protected boolean handlePagingClicks() {
865        return true;
866    }
867
868    private void unshrink(int newCurrentPage) {
869        if (mIsSmall) {
870            int newX = getChildOffset(newCurrentPage) - getRelativeChildOffset(newCurrentPage);
871            int delta = newX - mScrollX;
872
873            final int screenCount = getChildCount();
874            for (int i = 0; i < screenCount; i++) {
875                CellLayout cl = (CellLayout) getChildAt(i);
876                cl.setX(cl.getX() + delta);
877            }
878            setCurrentPage(newCurrentPage);
879            unshrink();
880        }
881    }
882
883    void unshrink() {
884        unshrink(true);
885    }
886
887    void unshrink(boolean animated) {
888        if (mIsSmall) {
889            mIsSmall = false;
890            AnimatorSet s = new AnimatorSet();
891            final int screenCount = getChildCount();
892
893            final int duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime);
894            for (int i = 0; i < screenCount; i++) {
895                final CellLayout cl = (CellLayout)getChildAt(i);
896                float finalAlphaValue = (i == mCurrentPage) ? 1.0f : 0.0f;
897                float rotation = 0.0f;
898
899                if (i < mCurrentPage) {
900                    rotation = WORKSPACE_ROTATION;
901                } else if (i > mCurrentPage) {
902                    rotation = -WORKSPACE_ROTATION;
903                }
904
905                if (animated) {
906
907                    s.playTogether(
908                            ObjectAnimator.ofFloat(cl, "translationX", 0.0f).setDuration(duration),
909                            ObjectAnimator.ofFloat(cl, "translationY", 0.0f).setDuration(duration),
910                            ObjectAnimator.ofFloat(cl, "scaleX", 1.0f).setDuration(duration),
911                            ObjectAnimator.ofFloat(cl, "scaleY", 1.0f).setDuration(duration),
912                            ObjectAnimator.ofFloat(cl, "backgroundAlpha", 0.0f).setDuration(duration),
913                            ObjectAnimator.ofFloat(cl, "alpha", finalAlphaValue).setDuration(duration),
914                            ObjectAnimator.ofFloat(cl, "rotationY", rotation).setDuration(duration));
915                } else {
916                    cl.setTranslationX(0.0f);
917                    cl.setTranslationY(0.0f);
918                    cl.setScaleX(1.0f);
919                    cl.setScaleY(1.0f);
920                    cl.setBackgroundAlpha(0.0f);
921                    cl.setAlpha(finalAlphaValue);
922                    cl.setRotationY(rotation);
923                }
924            }
925            if (animated) {
926                // If we call this when we're not animated, onAnimationEnd is never called on
927                // the listener; make sure we only use the listener when we're actually animating
928                s.addListener(mUnshrinkAnimationListener);
929                s.start();
930            }
931        }
932    }
933
934    /**
935     * Draw the View v into the given Canvas.
936     *
937     * @param v the view to draw
938     * @param destCanvas the canvas to draw on
939     * @param padding the horizontal and vertical padding to use when drawing
940     */
941    private void drawDragView(View v, Canvas destCanvas, int padding) {
942        final Rect clipRect = mTempRect;
943        v.getDrawingRect(clipRect);
944
945        // For a TextView, adjust the clip rect so that we don't include the text label
946        if (v instanceof TextView) {
947            final int iconHeight = ((TextView)v).getCompoundPaddingTop() - v.getPaddingTop();
948            clipRect.bottom = clipRect.top + iconHeight;
949        }
950
951        // Draw the View into the bitmap.
952        // The translate of scrollX and scrollY is necessary when drawing TextViews, because
953        // they set scrollX and scrollY to large values to achieve centered text
954
955        destCanvas.save();
956        destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2);
957        destCanvas.clipRect(clipRect, Op.REPLACE);
958        v.draw(destCanvas);
959        destCanvas.restore();
960    }
961
962    /**
963     * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
964     * Responsibility for the bitmap is transferred to the caller.
965     */
966    private Bitmap createDragOutline(View v, Canvas canvas, int padding) {
967        final int outlineColor = getResources().getColor(R.color.drag_outline_color);
968        final Bitmap b = Bitmap.createBitmap(
969                v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
970
971        canvas.setBitmap(b);
972        drawDragView(v, canvas, padding);
973        mOutlineHelper.applyExpensiveOuterOutline(b, canvas, outlineColor, true);
974
975        return b;
976    }
977
978    /**
979     * Returns a new bitmap to show when the given View is being dragged around.
980     * Responsibility for the bitmap is transferred to the caller.
981     */
982    private Bitmap createDragBitmap(View v, Canvas canvas, int padding) {
983        final int outlineColor = getResources().getColor(R.color.drag_outline_color);
984        final Bitmap b = Bitmap.createBitmap(
985                mDragOutline.getWidth(), mDragOutline.getHeight(), Bitmap.Config.ARGB_8888);
986
987        canvas.setBitmap(b);
988        canvas.drawBitmap(mDragOutline, 0, 0, null);
989        drawDragView(v, canvas, padding);
990        mOutlineHelper.applyGlow(b, canvas, outlineColor);
991
992        return b;
993    }
994
995    void startDrag(CellLayout.CellInfo cellInfo) {
996        View child = cellInfo.cell;
997        final int blurPadding = 40;
998
999        // Make sure the drag was started by a long press as opposed to a long click.
1000        if (!child.isInTouchMode()) {
1001            return;
1002        }
1003
1004        mDragInfo = cellInfo;
1005        mDragInfo.screen = mCurrentPage;
1006
1007        CellLayout current = ((CellLayout) getChildAt(mCurrentPage));
1008
1009        current.onDragChild(child);
1010
1011        child.clearFocus();
1012        child.setPressed(false);
1013
1014        final Canvas canvas = new Canvas();
1015
1016        // The outline is used to visualize where the item will land if dropped
1017        mDragOutline = createDragOutline(child, canvas, blurPadding);
1018
1019        // The drag bitmap follows the touch point around on the screen
1020        final Bitmap b = createDragBitmap(child, canvas, blurPadding);
1021
1022        final int bmpWidth = b.getWidth();
1023        final int bmpHeight = b.getHeight();
1024        child.getLocationOnScreen(mTempXY);
1025        final int screenX = (int) mTempXY[0] + (child.getWidth() - bmpWidth) / 2;
1026        final int screenY = (int) mTempXY[1] + (child.getHeight() - bmpHeight) / 2;
1027        mDragController.startDrag(b, screenX, screenY, 0, 0, bmpWidth, bmpHeight, this,
1028                child.getTag(), DragController.DRAG_ACTION_MOVE, null);
1029        b.recycle();
1030
1031        current.onDragEnter(child);
1032        child.setVisibility(View.GONE);
1033    }
1034
1035    void addApplicationShortcut(ShortcutInfo info, int screen, int cellX, int cellY,
1036            boolean insertAtFirst, int intersectX, int intersectY) {
1037        final CellLayout cellLayout = (CellLayout) getChildAt(screen);
1038        View view = mLauncher.createShortcut(R.layout.application, cellLayout, (ShortcutInfo) info);
1039
1040        final int[] cellXY = new int[2];
1041        cellLayout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY);
1042        addInScreen(view, screen, cellXY[0], cellXY[1], 1, 1, insertAtFirst);
1043        LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
1044                LauncherSettings.Favorites.CONTAINER_DESKTOP, screen,
1045                cellXY[0], cellXY[1]);
1046    }
1047
1048
1049    public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
1050            DragView dragView, Object dragInfo) {
1051        CellLayout cellLayout;
1052        int originX = x - xOffset;
1053        int originY = y - yOffset;
1054        if (mIsSmall || mIsInUnshrinkAnimation) {
1055            cellLayout = findMatchingPageForDragOver(dragView, originX, originY, xOffset, yOffset);
1056            if (cellLayout == null) {
1057                // cancel the drag if we're not over a mini-screen at time of drop
1058                // TODO: maybe add a nice fade here?
1059                return;
1060            }
1061            // get originX and originY in the local coordinate system of the screen
1062            mTempOriginXY[0] = originX;
1063            mTempOriginXY[1] = originY;
1064            mapPointFromSelfToChild(cellLayout, mTempOriginXY);
1065            originX = (int)mTempOriginXY[0];
1066            originY = (int)mTempOriginXY[1];
1067        } else {
1068            cellLayout = getCurrentDropLayout();
1069        }
1070
1071        if (source != this) {
1072            onDropExternal(originX, originY, dragInfo, cellLayout);
1073        } else {
1074            // Move internally
1075            if (mDragInfo != null) {
1076                final View cell = mDragInfo.cell;
1077
1078                mTargetCell = findNearestVacantArea(originX, originY,
1079                        mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout,
1080                        mTargetCell);
1081
1082                int screen = indexOfChild(cellLayout);
1083                if (screen != mDragInfo.screen) {
1084                    final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1085                    originalCellLayout.removeView(cell);
1086                    addInScreen(cell, screen, mTargetCell[0], mTargetCell[1],
1087                            mDragInfo.spanX, mDragInfo.spanY);
1088                }
1089                cellLayout.onDropChild(cell);
1090
1091                // update the item's position after drop
1092                final ItemInfo info = (ItemInfo) cell.getTag();
1093                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
1094                cellLayout.onMove(cell, mTargetCell[0], mTargetCell[1]);
1095                lp.cellX = mTargetCell[0];
1096                lp.cellY = mTargetCell[1];
1097                cell.setId(LauncherModel.getCellLayoutChildId(-1, mDragInfo.screen,
1098                        mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY));
1099
1100                LauncherModel.moveItemInDatabase(mLauncher, info,
1101                        LauncherSettings.Favorites.CONTAINER_DESKTOP, screen,
1102                        lp.cellX, lp.cellY);
1103            }
1104        }
1105    }
1106
1107    public void onDragEnter(DragSource source, int x, int y, int xOffset,
1108            int yOffset, DragView dragView, Object dragInfo) {
1109        getCurrentDropLayout().onDragEnter(dragView);
1110        showOutlines();
1111    }
1112
1113    public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
1114            DragView dragView, Object dragInfo) {
1115
1116        if (mIsSmall || mIsInUnshrinkAnimation) {
1117            // If we're shrunken, don't let anyone drag on folders/etc  that are on the mini-screens
1118            return null;
1119        }
1120        // We may need to delegate the drag to a child view. If a 1x1 item
1121        // would land in a cell occupied by a DragTarget (e.g. a Folder),
1122        // then drag events should be handled by that child.
1123
1124        ItemInfo item = (ItemInfo)dragInfo;
1125        CellLayout currentLayout = getCurrentDropLayout();
1126
1127        int dragPointX, dragPointY;
1128        if (item.spanX == 1 && item.spanY == 1) {
1129            // For a 1x1, calculate the drop cell exactly as in onDragOver
1130            dragPointX = x - xOffset;
1131            dragPointY = y - yOffset;
1132        } else {
1133            // Otherwise, use the exact drag coordinates
1134            dragPointX = x;
1135            dragPointY = y;
1136        }
1137        dragPointX += mScrollX - currentLayout.getLeft();
1138        dragPointY += mScrollY - currentLayout.getTop();
1139
1140        // If we are dragging over a cell that contains a DropTarget that will
1141        // accept the drop, delegate to that DropTarget.
1142        final int[] cellXY = mTempCell;
1143        currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY);
1144        View child = currentLayout.getChildAt(cellXY[0], cellXY[1]);
1145        if (child instanceof DropTarget) {
1146            DropTarget target = (DropTarget)child;
1147            if (target.acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) {
1148                return target;
1149            }
1150        }
1151        return null;
1152    }
1153
1154    /*
1155    *
1156    * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
1157    * coordinate space. The argument xy is modified with the return result.
1158    *
1159    */
1160   void mapPointFromSelfToChild(View v, float[] xy) {
1161       mapPointFromSelfToChild(v, xy, null);
1162   }
1163
1164   /*
1165    *
1166    * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
1167    * coordinate space. The argument xy is modified with the return result.
1168    *
1169    * if cachedInverseMatrix is not null, this method will just use that matrix instead of
1170    * computing it itself; we use this to avoid redudant matrix inversions in
1171    * findMatchingPageForDragOver
1172    *
1173    */
1174   void mapPointFromSelfToChild(View v, float[] xy, Matrix cachedInverseMatrix) {
1175       if (cachedInverseMatrix == null) {
1176           v.getMatrix().invert(mTempInverseMatrix);
1177           cachedInverseMatrix = mTempInverseMatrix;
1178       }
1179       xy[0] = xy[0] + mScrollX - v.getLeft();
1180       xy[1] = xy[1] + mScrollY - v.getTop();
1181       cachedInverseMatrix.mapPoints(xy);
1182   }
1183
1184   /*
1185    *
1186    * Convert the 2D coordinate xy from this CellLayout's coordinate space to
1187    * the parent View's coordinate space. The argument xy is modified with the return result.
1188    *
1189    */
1190   void mapPointFromChildToSelf(View v, float[] xy) {
1191       v.getMatrix().mapPoints(xy);
1192       xy[0] -= (mScrollX - v.getLeft());
1193       xy[1] -= (mScrollY - v.getTop());
1194   }
1195
1196    static private float squaredDistance(float[] point1, float[] point2) {
1197        float distanceX = point1[0] - point2[0];
1198        float distanceY = point2[1] - point2[1];
1199        return distanceX * distanceX + distanceY * distanceY;
1200    }
1201
1202    /*
1203     *
1204     * Returns true if the passed CellLayout cl overlaps with dragView
1205     *
1206     */
1207    boolean overlaps(CellLayout cl, DragView dragView,
1208            int dragViewX, int dragViewY, Matrix cachedInverseMatrix) {
1209        // Transform the coordinates of the item being dragged to the CellLayout's coordinates
1210        final float[] draggedItemTopLeft = mTempDragCoordinates;
1211        draggedItemTopLeft[0] = dragViewX + dragView.getScaledDragRegionXOffset();
1212        draggedItemTopLeft[1] = dragViewY + dragView.getScaledDragRegionYOffset();
1213        final float[] draggedItemBottomRight = mTempDragBottomRightCoordinates;
1214        draggedItemBottomRight[0] = draggedItemTopLeft[0] + dragView.getScaledDragRegionWidth();
1215        draggedItemBottomRight[1] = draggedItemTopLeft[1] + dragView.getScaledDragRegionHeight();
1216
1217        // Transform the dragged item's top left coordinates
1218        // to the CellLayout's local coordinates
1219        mapPointFromSelfToChild(cl, draggedItemTopLeft, cachedInverseMatrix);
1220        float overlapRegionLeft = Math.max(0f, draggedItemTopLeft[0]);
1221        float overlapRegionTop = Math.max(0f, draggedItemTopLeft[1]);
1222
1223        if (overlapRegionLeft <= cl.getWidth() && overlapRegionTop >= 0) {
1224            // Transform the dragged item's bottom right coordinates
1225            // to the CellLayout's local coordinates
1226            mapPointFromSelfToChild(cl, draggedItemBottomRight, cachedInverseMatrix);
1227            float overlapRegionRight = Math.min(cl.getWidth(), draggedItemBottomRight[0]);
1228            float overlapRegionBottom = Math.min(cl.getHeight(), draggedItemBottomRight[1]);
1229
1230            if (overlapRegionRight >= 0 && overlapRegionBottom <= cl.getHeight()) {
1231                float overlap = (overlapRegionRight - overlapRegionLeft) *
1232                         (overlapRegionBottom - overlapRegionTop);
1233                if (overlap > 0) {
1234                    return true;
1235                }
1236             }
1237        }
1238        return false;
1239    }
1240
1241    /*
1242     *
1243     * This method returns the CellLayout that is currently being dragged to. In order to drag
1244     * to a CellLayout, either the touch point must be directly over the CellLayout, or as a second
1245     * strategy, we see if the dragView is overlapping any CellLayout and choose the closest one
1246     *
1247     * Return null if no CellLayout is currently being dragged over
1248     *
1249     */
1250    private CellLayout findMatchingPageForDragOver(
1251            DragView dragView, int originX, int originY, int offsetX, int offsetY) {
1252        // We loop through all the screens (ie CellLayouts) and see which ones overlap
1253        // with the item being dragged and then choose the one that's closest to the touch point
1254        final int screenCount = getChildCount();
1255        CellLayout bestMatchingScreen = null;
1256        float smallestDistSoFar = Float.MAX_VALUE;
1257
1258        for (int i = 0; i < screenCount; i++) {
1259            CellLayout cl = (CellLayout)getChildAt(i);
1260
1261            final float[] touchXy = mTempTouchCoordinates;
1262            touchXy[0] = originX + offsetX;
1263            touchXy[1] = originY + offsetY;
1264
1265            // Transform the touch coordinates to the CellLayout's local coordinates
1266            // If the touch point is within the bounds of the cell layout, we can return immediately
1267            cl.getMatrix().invert(mTempInverseMatrix);
1268            mapPointFromSelfToChild(cl, touchXy, mTempInverseMatrix);
1269
1270            if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() &&
1271                    touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) {
1272                return cl;
1273            }
1274
1275            if (overlaps(cl, dragView, originX, originY, mTempInverseMatrix)) {
1276                // Get the center of the cell layout in screen coordinates
1277                final float[] cellLayoutCenter = mTempCellLayoutCenterCoordinates;
1278                cellLayoutCenter[0] = cl.getWidth()/2;
1279                cellLayoutCenter[1] = cl.getHeight()/2;
1280                mapPointFromChildToSelf(cl, cellLayoutCenter);
1281
1282                touchXy[0] = originX + offsetX;
1283                touchXy[1] = originY + offsetY;
1284
1285                // Calculate the distance between the center of the CellLayout
1286                // and the touch point
1287                float dist = squaredDistance(touchXy, cellLayoutCenter);
1288
1289                if (dist < smallestDistSoFar) {
1290                    smallestDistSoFar = dist;
1291                    bestMatchingScreen = cl;
1292                }
1293            }
1294        }
1295
1296        if (bestMatchingScreen != mDragTargetLayout) {
1297            if (mDragTargetLayout != null) {
1298                mDragTargetLayout.onDragExit();
1299            }
1300            mDragTargetLayout = bestMatchingScreen;
1301            // TODO: Should we be calling mDragTargetLayout.onDragEnter() here?
1302        }
1303        return bestMatchingScreen;
1304    }
1305
1306    public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
1307            DragView dragView, Object dragInfo) {
1308        CellLayout currentLayout;
1309        int originX = x - xOffset;
1310        int originY = y - yOffset;
1311        if (mIsSmall || mIsInUnshrinkAnimation) {
1312            currentLayout = findMatchingPageForDragOver(
1313                    dragView, originX, originY, xOffset, yOffset);
1314
1315            if (currentLayout == null) {
1316                return;
1317            }
1318
1319            currentLayout.setHover(true);
1320            // get originX and originY in the local coordinate system of the screen
1321            mTempOriginXY[0] = originX;
1322            mTempOriginXY[1] = originY;
1323            mapPointFromSelfToChild(currentLayout, mTempOriginXY);
1324            originX = (int)mTempOriginXY[0];
1325            originY = (int)mTempOriginXY[1];
1326        } else {
1327            currentLayout = getCurrentDropLayout();
1328        }
1329
1330        final ItemInfo item = (ItemInfo)dragInfo;
1331
1332        if (dragInfo instanceof LauncherAppWidgetInfo) {
1333            LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo;
1334
1335            if (widgetInfo.spanX == -1) {
1336                // Calculate the grid spans needed to fit this widget
1337                int[] spans = currentLayout.rectToCell(widgetInfo.minWidth, widgetInfo.minHeight, null);
1338                item.spanX = spans[0];
1339                item.spanY = spans[1];
1340            }
1341        }
1342
1343        if (source instanceof AllAppsPagedView) {
1344            // This is a hack to fix the point used to determine which cell an icon from the all
1345            // apps screen is over
1346            if (item != null && item.spanX == 1 && currentLayout != null) {
1347                int dragRegionLeft = (dragView.getWidth() - currentLayout.getCellWidth()) / 2;
1348
1349                originX += dragRegionLeft - dragView.getDragRegionLeft();
1350                if (dragView.getDragRegionWidth() != currentLayout.getCellWidth()) {
1351                    dragView.setDragRegion(dragView.getDragRegionLeft(), dragView.getDragRegionTop(),
1352                            currentLayout.getCellWidth(), dragView.getDragRegionHeight());
1353                }
1354            }
1355        }
1356
1357        // When touch is inside the scroll area, skip dragOver actions for the current screen
1358        if (!mInScrollArea) {
1359            if (currentLayout != mDragTargetLayout) {
1360                if (mDragTargetLayout != null) {
1361                    mDragTargetLayout.onDragExit();
1362                }
1363                currentLayout.onDragEnter(dragView);
1364                mDragTargetLayout = currentLayout;
1365            }
1366
1367            // only visualize the drop locations for moving icons within the home screen on tablet
1368            // on phone, we also visualize icons dragged in from All Apps
1369            if ((!LauncherApplication.isScreenXLarge() || source == this)
1370                    && mDragTargetLayout != null) {
1371                final View child = (mDragInfo == null) ? null : mDragInfo.cell;
1372                int localOriginX = originX - (mDragTargetLayout.getLeft() - mScrollX);
1373                int localOriginY = originY - (mDragTargetLayout.getTop() - mScrollY);
1374                mDragTargetLayout.visualizeDropLocation(
1375                        child, mDragOutline, localOriginX, localOriginY, item.spanX, item.spanY);
1376            }
1377        }
1378    }
1379
1380    public void onDragExit(DragSource source, int x, int y, int xOffset,
1381            int yOffset, DragView dragView, Object dragInfo) {
1382        if (mDragTargetLayout != null) {
1383            mDragTargetLayout.onDragExit();
1384            mDragTargetLayout = null;
1385        }
1386        if (!mIsPageMoving) {
1387            hideOutlines();
1388        }
1389    }
1390
1391    private void onDropExternal(int x, int y, Object dragInfo,
1392            CellLayout cellLayout) {
1393        onDropExternal(x, y, dragInfo, cellLayout, false);
1394    }
1395
1396    /**
1397     * Add the item specified by dragInfo to the given layout.
1398     * This is basically the equivalent of onDropExternal, except it's not initiated
1399     * by drag and drop.
1400     * @return true if successful
1401     */
1402    public boolean addExternalItemToScreen(Object dragInfo, View layout) {
1403        CellLayout cl = (CellLayout) layout;
1404        ItemInfo info = (ItemInfo) dragInfo;
1405
1406        if (cl.findCellForSpan(mTempEstimate, info.spanX, info.spanY)) {
1407            onDropExternal(0, 0, dragInfo, cl, false);
1408            return true;
1409        }
1410        mLauncher.showOutOfSpaceMessage();
1411        return false;
1412    }
1413
1414    // Drag from somewhere else
1415    // NOTE: This can also be called when we are outside of a drag event, when we want
1416    // to add an item to one of the workspace screens.
1417    private void onDropExternal(int x, int y, Object dragInfo,
1418            CellLayout cellLayout, boolean insertAtFirst) {
1419        int screen = indexOfChild(cellLayout);
1420        if (dragInfo instanceof PendingAddItemInfo) {
1421            PendingAddItemInfo info = (PendingAddItemInfo) dragInfo;
1422            // When dragging and dropping from customization tray, we deal with creating
1423            // widgets/shortcuts/folders in a slightly different way
1424            int[] touchXY = new int[2];
1425            touchXY[0] = x;
1426            touchXY[1] = y;
1427            switch (info.itemType) {
1428                case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
1429                    mLauncher.addAppWidgetFromDrop(info.componentName, screen, touchXY);
1430                    break;
1431                case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
1432                    mLauncher.addLiveFolderFromDrop(info.componentName, screen, touchXY);
1433                    break;
1434                case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1435                    mLauncher.processShortcutFromDrop(info.componentName, screen, touchXY);
1436                    break;
1437                default:
1438                    throw new IllegalStateException("Unknown item type: " + info.itemType);
1439            }
1440            cellLayout.onDragExit();
1441            return;
1442        }
1443
1444        // This is for other drag/drop cases, like dragging from All Apps
1445        ItemInfo info = (ItemInfo) dragInfo;
1446
1447        View view = null;
1448
1449        switch (info.itemType) {
1450        case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
1451        case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
1452            if (info.container == NO_ID && info instanceof ApplicationInfo) {
1453                // Came from all apps -- make a copy
1454                info = new ShortcutInfo((ApplicationInfo) info);
1455            }
1456            view = mLauncher.createShortcut(R.layout.application, cellLayout,
1457                    (ShortcutInfo) info);
1458            break;
1459        case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
1460            view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher,
1461                    cellLayout, ((UserFolderInfo) info));
1462            break;
1463        default:
1464            throw new IllegalStateException("Unknown item type: " + info.itemType);
1465        }
1466
1467        // If the view is null, it has already been added.
1468        if (view == null) {
1469            cellLayout.onDragExit();
1470        } else {
1471            mTargetCell = findNearestVacantArea(x, y, 1, 1, null, cellLayout, mTargetCell);
1472            addInScreen(view, indexOfChild(cellLayout), mTargetCell[0],
1473                    mTargetCell[1], info.spanX, info.spanY, insertAtFirst);
1474            cellLayout.onDropChild(view);
1475            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
1476
1477            LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
1478                    LauncherSettings.Favorites.CONTAINER_DESKTOP, screen,
1479                    lp.cellX, lp.cellY);
1480        }
1481    }
1482
1483    /**
1484     * Return the current {@link CellLayout}, correctly picking the destination
1485     * screen while a scroll is in progress.
1486     */
1487    private CellLayout getCurrentDropLayout() {
1488        int index = mScroller.isFinished() ? mCurrentPage : mNextPage;
1489        return (CellLayout) getChildAt(index);
1490    }
1491
1492    /**
1493     * Return the current CellInfo describing our current drag; this method exists
1494     * so that Launcher can sync this object with the correct info when the activity is created/
1495     * destroyed
1496     *
1497     */
1498    public CellLayout.CellInfo getDragInfo() {
1499        return mDragInfo;
1500    }
1501
1502    /**
1503     * {@inheritDoc}
1504     */
1505    public boolean acceptDrop(DragSource source, int x, int y,
1506            int xOffset, int yOffset, DragView dragView, Object dragInfo) {
1507        CellLayout layout;
1508        if (mIsSmall || mIsInUnshrinkAnimation) {
1509            layout = findMatchingPageForDragOver(
1510                    dragView, x - xOffset, y - yOffset, xOffset, yOffset);
1511            if (layout == null) {
1512                // cancel the drag if we're not over a mini-screen at time of drop
1513                return false;
1514            }
1515        } else {
1516            layout = getCurrentDropLayout();
1517        }
1518        final CellLayout.CellInfo dragCellInfo = mDragInfo;
1519        final int spanX = dragCellInfo == null ? 1 : dragCellInfo.spanX;
1520        final int spanY = dragCellInfo == null ? 1 : dragCellInfo.spanY;
1521
1522        final View ignoreView = dragCellInfo == null ? null : dragCellInfo.cell;
1523
1524        if (layout.findCellForSpanIgnoring(null, spanX, spanY, ignoreView)) {
1525            return true;
1526        } else {
1527            mLauncher.showOutOfSpaceMessage();
1528            return false;
1529        }
1530    }
1531
1532    /**
1533     * Calculate the nearest cell where the given object would be dropped.
1534     */
1535    private int[] findNearestVacantArea(int pixelX, int pixelY,
1536            int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
1537
1538        int localPixelX = pixelX - (layout.getLeft() - mScrollX);
1539        int localPixelY = pixelY - (layout.getTop() - mScrollY);
1540
1541        // Find the best target drop location
1542        return layout.findNearestVacantArea(
1543                localPixelX, localPixelY, spanX, spanY, ignoreView, recycle);
1544    }
1545
1546    /**
1547     * Estimate the size that a child with the given dimensions will take in the current screen.
1548     */
1549    void estimateChildSize(int minWidth, int minHeight, int[] result) {
1550        ((CellLayout)getChildAt(mCurrentPage)).estimateChildSize(minWidth, minHeight, result);
1551    }
1552
1553    void setLauncher(Launcher launcher) {
1554        mLauncher = launcher;
1555    }
1556
1557    public void setDragController(DragController dragController) {
1558        mDragController = dragController;
1559    }
1560
1561    public void onDropCompleted(View target, boolean success) {
1562        if (success) {
1563            if (target != this && mDragInfo != null) {
1564                final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1565                cellLayout.removeView(mDragInfo.cell);
1566                if (mDragInfo.cell instanceof DropTarget) {
1567                    mDragController.removeDropTarget((DropTarget)mDragInfo.cell);
1568                }
1569                // final Object tag = mDragInfo.cell.getTag();
1570            }
1571        } else {
1572            if (mDragInfo != null) {
1573                final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1574                cellLayout.onDropAborted(mDragInfo.cell);
1575            }
1576        }
1577
1578        if (mDragInfo != null) {
1579            mDragInfo.cell.setVisibility(View.VISIBLE);
1580        }
1581        mDragOutline = null;
1582        mDragInfo = null;
1583    }
1584
1585    public boolean isDropEnabled() {
1586        return true;
1587    }
1588
1589    @Override
1590    protected void onRestoreInstanceState(Parcelable state) {
1591        super.onRestoreInstanceState(state);
1592        Launcher.setScreen(mCurrentPage);
1593    }
1594
1595    @Override
1596    public void scrollLeft() {
1597        if (!mIsSmall && !mIsInUnshrinkAnimation) {
1598            super.scrollLeft();
1599        }
1600    }
1601
1602    @Override
1603    public void scrollRight() {
1604        if (!mIsSmall && !mIsInUnshrinkAnimation) {
1605            super.scrollRight();
1606        }
1607    }
1608
1609    @Override
1610    public void onEnterScrollArea(int direction) {
1611        mInScrollArea = true;
1612        final int screen = getCurrentPage() + ((direction == DragController.SCROLL_LEFT) ? -1 : 1);
1613        if (0 <= screen && screen < getChildCount()) {
1614            ((CellLayout) getChildAt(screen)).setHover(true);
1615        }
1616
1617        if (mDragTargetLayout != null) {
1618            mDragTargetLayout.onDragExit();
1619            mDragTargetLayout = null;
1620        }
1621    }
1622
1623    @Override
1624    public void onExitScrollArea() {
1625        mInScrollArea = false;
1626        final int childCount = getChildCount();
1627        for (int i = 0; i < childCount; i++) {
1628            ((CellLayout) getChildAt(i)).setHover(false);
1629        }
1630    }
1631
1632    public Folder getFolderForTag(Object tag) {
1633        final int screenCount = getChildCount();
1634        for (int screen = 0; screen < screenCount; screen++) {
1635            CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1636            int count = currentScreen.getChildCount();
1637            for (int i = 0; i < count; i++) {
1638                View child = currentScreen.getChildAt(i);
1639                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
1640                if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
1641                    Folder f = (Folder) child;
1642                    if (f.getInfo() == tag && f.getInfo().opened) {
1643                        return f;
1644                    }
1645                }
1646            }
1647        }
1648        return null;
1649    }
1650
1651    public View getViewForTag(Object tag) {
1652        int screenCount = getChildCount();
1653        for (int screen = 0; screen < screenCount; screen++) {
1654            CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1655            int count = currentScreen.getChildCount();
1656            for (int i = 0; i < count; i++) {
1657                View child = currentScreen.getChildAt(i);
1658                if (child.getTag() == tag) {
1659                    return child;
1660                }
1661            }
1662        }
1663        return null;
1664    }
1665
1666
1667    void removeItems(final ArrayList<ApplicationInfo> apps) {
1668        final int screenCount = getChildCount();
1669        final PackageManager manager = getContext().getPackageManager();
1670        final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext());
1671
1672        final HashSet<String> packageNames = new HashSet<String>();
1673        final int appCount = apps.size();
1674        for (int i = 0; i < appCount; i++) {
1675            packageNames.add(apps.get(i).componentName.getPackageName());
1676        }
1677
1678        for (int i = 0; i < screenCount; i++) {
1679            final CellLayout layout = (CellLayout) getChildAt(i);
1680
1681            // Avoid ANRs by treating each screen separately
1682            post(new Runnable() {
1683                public void run() {
1684                    final ArrayList<View> childrenToRemove = new ArrayList<View>();
1685                    childrenToRemove.clear();
1686
1687                    int childCount = layout.getChildCount();
1688                    for (int j = 0; j < childCount; j++) {
1689                        final View view = layout.getChildAt(j);
1690                        Object tag = view.getTag();
1691
1692                        if (tag instanceof ShortcutInfo) {
1693                            final ShortcutInfo info = (ShortcutInfo) tag;
1694                            final Intent intent = info.intent;
1695                            final ComponentName name = intent.getComponent();
1696
1697                            if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1698                                for (String packageName: packageNames) {
1699                                    if (packageName.equals(name.getPackageName())) {
1700                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1701                                        childrenToRemove.add(view);
1702                                    }
1703                                }
1704                            }
1705                        } else if (tag instanceof UserFolderInfo) {
1706                            final UserFolderInfo info = (UserFolderInfo) tag;
1707                            final ArrayList<ShortcutInfo> contents = info.contents;
1708                            final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1);
1709                            final int contentsCount = contents.size();
1710                            boolean removedFromFolder = false;
1711
1712                            for (int k = 0; k < contentsCount; k++) {
1713                                final ShortcutInfo appInfo = contents.get(k);
1714                                final Intent intent = appInfo.intent;
1715                                final ComponentName name = intent.getComponent();
1716
1717                                if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1718                                    for (String packageName: packageNames) {
1719                                        if (packageName.equals(name.getPackageName())) {
1720                                            toRemove.add(appInfo);
1721                                            LauncherModel.deleteItemFromDatabase(mLauncher, appInfo);
1722                                            removedFromFolder = true;
1723                                        }
1724                                    }
1725                                }
1726                            }
1727
1728                            contents.removeAll(toRemove);
1729                            if (removedFromFolder) {
1730                                final Folder folder = getOpenFolder();
1731                                if (folder != null)
1732                                    folder.notifyDataSetChanged();
1733                            }
1734                        } else if (tag instanceof LiveFolderInfo) {
1735                            final LiveFolderInfo info = (LiveFolderInfo) tag;
1736                            final Uri uri = info.uri;
1737                            final ProviderInfo providerInfo = manager.resolveContentProvider(
1738                                    uri.getAuthority(), 0);
1739
1740                            if (providerInfo != null) {
1741                                for (String packageName: packageNames) {
1742                                    if (packageName.equals(providerInfo.packageName)) {
1743                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1744                                        childrenToRemove.add(view);
1745                                    }
1746                                }
1747                            }
1748                        } else if (tag instanceof LauncherAppWidgetInfo) {
1749                            final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag;
1750                            final AppWidgetProviderInfo provider =
1751                                    widgets.getAppWidgetInfo(info.appWidgetId);
1752                            if (provider != null) {
1753                                for (String packageName: packageNames) {
1754                                    if (packageName.equals(provider.provider.getPackageName())) {
1755                                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
1756                                        childrenToRemove.add(view);
1757                                    }
1758                                }
1759                            }
1760                        }
1761                    }
1762
1763                    childCount = childrenToRemove.size();
1764                    for (int j = 0; j < childCount; j++) {
1765                        View child = childrenToRemove.get(j);
1766                        layout.removeViewInLayout(child);
1767                        if (child instanceof DropTarget) {
1768                            mDragController.removeDropTarget((DropTarget)child);
1769                        }
1770                    }
1771
1772                    if (childCount > 0) {
1773                        layout.requestLayout();
1774                        layout.invalidate();
1775                    }
1776                }
1777            });
1778        }
1779    }
1780
1781    void updateShortcuts(ArrayList<ApplicationInfo> apps) {
1782        final int screenCount = getChildCount();
1783        for (int i = 0; i < screenCount; i++) {
1784            final CellLayout layout = (CellLayout) getChildAt(i);
1785            int childCount = layout.getChildCount();
1786            for (int j = 0; j < childCount; j++) {
1787                final View view = layout.getChildAt(j);
1788                Object tag = view.getTag();
1789                if (tag instanceof ShortcutInfo) {
1790                    ShortcutInfo info = (ShortcutInfo)tag;
1791                    // We need to check for ACTION_MAIN otherwise getComponent() might
1792                    // return null for some shortcuts (for instance, for shortcuts to
1793                    // web pages.)
1794                    final Intent intent = info.intent;
1795                    final ComponentName name = intent.getComponent();
1796                    if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
1797                            Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
1798                        final int appCount = apps.size();
1799                        for (int k = 0; k < appCount; k++) {
1800                            ApplicationInfo app = apps.get(k);
1801                            if (app.componentName.equals(name)) {
1802                                info.setIcon(mIconCache.getIcon(info.intent));
1803                                ((TextView)view).setCompoundDrawablesWithIntrinsicBounds(null,
1804                                        new FastBitmapDrawable(info.getIcon(mIconCache)),
1805                                        null, null);
1806                                }
1807                        }
1808                    }
1809                }
1810            }
1811        }
1812    }
1813
1814    void moveToDefaultScreen(boolean animate) {
1815        if (mIsSmall || mIsInUnshrinkAnimation) {
1816            mLauncher.showWorkspace(animate, (CellLayout)getChildAt(mDefaultPage));
1817        } else if (animate) {
1818            snapToPage(mDefaultPage);
1819        } else {
1820            setCurrentPage(mDefaultPage);
1821        }
1822        getChildAt(mDefaultPage).requestFocus();
1823    }
1824
1825    void setIndicators(Drawable previous, Drawable next) {
1826        mPreviousIndicator = previous;
1827        mNextIndicator = next;
1828        previous.setLevel(mCurrentPage);
1829        next.setLevel(mCurrentPage);
1830    }
1831
1832    @Override
1833    public void syncPages() {
1834    }
1835
1836    @Override
1837    public void syncPageItems(int page) {
1838    }
1839
1840}
1841