blob: 4e16a451a2c7ed9022d652544de8722ab41c8a05 [file] [log] [blame]
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001/*
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
Daniel Sandler325dc232013-06-05 22:57:57 -040017package com.android.launcher3;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080018
Michael Jurka0280c3b2010-09-17 15:00:07 -070019import android.animation.Animator;
Adam Cohena5f4e482013-10-11 12:10:28 -070020import android.animation.Animator.AnimatorListener;
Adam Cohenf358a4b2013-07-23 16:47:31 -070021import android.animation.AnimatorListenerAdapter;
Chet Haaseb1254a62010-09-07 13:35:00 -070022import android.animation.AnimatorSet;
Adam Cohen22cba7f2013-07-19 16:14:00 -070023import android.animation.LayoutTransition;
Michael Jurka0280c3b2010-09-17 15:00:07 -070024import android.animation.ObjectAnimator;
Adam Cohenad4e15c2013-10-17 16:21:35 -070025import android.animation.PropertyValuesHolder;
Patrick Dubroycd68ff52010-10-28 17:57:05 -070026import android.animation.TimeInterpolator;
27import android.animation.ValueAnimator;
28import android.animation.ValueAnimator.AnimatorUpdateListener;
Dianne Hackborn8f573952009-08-10 23:21:09 -070029import android.app.WallpaperManager;
Michael Jurkabed61d22012-02-14 22:51:29 -080030import android.appwidget.AppWidgetHostView;
Romain Guy629de3e2010-01-13 12:20:59 -080031import android.appwidget.AppWidgetProviderInfo;
Adam Powell495f2892010-04-16 16:40:55 -070032import android.content.ComponentName;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080033import android.content.Context;
Michael Jurkaadc574c2013-09-12 00:05:02 +020034import android.content.SharedPreferences;
Patrick Dubroy7247f632010-08-04 16:02:59 -070035import android.content.res.Resources;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080036import android.content.res.TypedArray;
Joe Onorato4be866d2010-10-10 11:26:02 -070037import android.graphics.Bitmap;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080038import android.graphics.Canvas;
Michael Jurkaa63c4522010-08-19 13:52:27 -070039import android.graphics.Matrix;
Winson Chungbabb53e2014-04-14 17:12:49 -070040import android.graphics.Paint;
Winson Chungb8c69f32011-10-19 21:36:08 -070041import android.graphics.Point;
Winson Chung043f2af2012-03-01 16:09:54 -080042import android.graphics.PointF;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080043import android.graphics.Rect;
Joe Onorato4be866d2010-10-10 11:26:02 -070044import android.graphics.Region.Op;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070045import android.graphics.drawable.Drawable;
Michael Jurkaa6a05472013-11-13 17:59:46 +010046import android.os.AsyncTask;
Sunny Goyal0fc1be12014-08-11 17:05:23 -070047import android.os.Handler;
Joe Onorato956091b2010-02-19 12:47:40 -080048import android.os.IBinder;
Adam Powell495f2892010-04-16 16:40:55 -070049import android.os.Parcelable;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080050import android.util.AttributeSet;
Daniel Sandler291ad122010-05-24 16:03:53 -040051import android.util.Log;
Adam Cohen1462de32012-07-24 22:34:36 -070052import android.util.SparseArray;
Michael Jurkacc07e7a2013-08-26 20:56:35 +020053import android.view.Choreographer;
Winson Chunga34abf82010-11-12 12:10:35 -080054import android.view.Display;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080055import android.view.MotionEvent;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080056import android.view.View;
Winson Chung6e314082011-01-27 16:46:51 -080057import android.view.ViewGroup;
Adam Cohen53805212013-10-01 10:39:23 -070058import android.view.accessibility.AccessibilityManager;
Patrick Dubroycd68ff52010-10-28 17:57:05 -070059import android.view.animation.DecelerateInterpolator;
Michael Jurkacc07e7a2013-08-26 20:56:35 +020060import android.view.animation.Interpolator;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070061import android.widget.TextView;
Kenny Guyed131872014-04-30 03:02:21 +010062
Daniel Sandler325dc232013-06-05 22:57:57 -040063import com.android.launcher3.FolderIcon.FolderRingAnimator;
Adam Cohenbffe7452013-07-22 18:21:45 -070064import com.android.launcher3.Launcher.CustomContentCallbacks;
Adam Cohenc2d6e892014-10-16 09:49:24 -070065import com.android.launcher3.Launcher.LauncherOverlay;
Daniel Sandler325dc232013-06-05 22:57:57 -040066import com.android.launcher3.LauncherSettings.Favorites;
Sunny Goyal34942622014-08-29 17:20:55 -070067import com.android.launcher3.compat.PackageInstallerCompat;
Sunny Goyale755d462014-07-22 13:48:29 -070068import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
Sunny Goyal651077b2014-06-30 14:15:31 -070069import com.android.launcher3.compat.UserHandleCompat;
Romain Guyedcce092010-03-04 13:03:17 -080070
Adam Cohen716b51e2011-06-30 12:09:54 -070071import java.util.ArrayList;
Adam Cohendcd297f2013-06-18 13:13:40 -070072import java.util.HashMap;
Adam Cohen716b51e2011-06-30 12:09:54 -070073import java.util.HashSet;
Michael Jurka8bd65f52012-06-25 14:56:22 -070074import java.util.Iterator;
Sunny Goyal95abbb32014-08-04 10:53:22 -070075import java.util.concurrent.atomic.AtomicInteger;
Adam Cohen716b51e2011-06-30 12:09:54 -070076
The Android Open Source Project31dd5032009-03-03 19:32:27 -080077/**
Michael Jurka0142d492010-08-25 17:46:15 -070078 * The workspace is a wide area with a wallpaper and a finite number of pages.
79 * Each page contains a number of icons, folders or widgets the user can
Winson Chungaafa03c2010-06-11 17:34:16 -070080 * interact with. A workspace is meant to be used with a fixed width only.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080081 */
Michael Jurka0142d492010-08-25 17:46:15 -070082public class Workspace extends SmoothPagedView
Michael Jurkad74c9842011-07-10 12:44:21 -070083 implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
John Spurlock77e1f472013-09-11 10:09:51 -040084 DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener,
85 Insettable {
Joe Onorato3a8820b2009-11-10 15:06:42 -080086 private static final String TAG = "Launcher.Workspace";
Michael Jurka0142d492010-08-25 17:46:15 -070087
Adam Cohen68d73932010-11-15 10:50:58 -080088 private static final int CHILDREN_OUTLINE_FADE_OUT_DELAY = 0;
89 private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375;
Winson Chung9171e6d2010-11-17 17:39:27 -080090 private static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100;
Adam Cohenf34bab52010-09-30 14:11:56 -070091
Adam Cohenad4e15c2013-10-17 16:21:35 -070092 protected static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400;
93 protected static final int FADE_EMPTY_SCREEN_DURATION = 150;
94
Winson Chungf135c6c2010-11-18 16:32:08 -080095 private static final int BACKGROUND_FADE_OUT_DURATION = 350;
Adam Cohened51cc92011-08-01 20:28:08 -070096 private static final int ADJACENT_SCREEN_DROP_DURATION = 300;
Adam Cohen265b9a62011-12-07 14:37:18 -080097 private static final int FLING_THRESHOLD_VELOCITY = 500;
Adam Cohened51cc92011-08-01 20:28:08 -070098
Adam Cohenf358a4b2013-07-23 16:47:31 -070099 private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f;
100
Chris Wren40c5ed32014-06-24 18:24:23 -0400101 static final boolean MAP_NO_RECURSE = false;
102 static final boolean MAP_RECURSE = true;
Chris Wrenaeff7ea2014-02-14 16:59:24 -0500103
Winson Chung9171e6d2010-11-17 17:39:27 -0800104 // These animators are used to fade the children's outlines
105 private ObjectAnimator mChildrenOutlineFadeInAnimation;
106 private ObjectAnimator mChildrenOutlineFadeOutAnimation;
107 private float mChildrenOutlineAlpha = 0;
108
109 // These properties refer to the background protection gradient used for AllApps and Customize
Michael Jurkae0f5a612011-02-07 16:45:41 -0800110 private ValueAnimator mBackgroundFadeInAnimation;
111 private ValueAnimator mBackgroundFadeOutAnimation;
Adam Cohenf34bab52010-09-30 14:11:56 -0700112
Adam Cohen3d1b2b42013-08-14 15:57:58 -0700113 private static final long CUSTOM_CONTENT_GESTURE_DELAY = 200;
114 private long mTouchDownTime = -1;
115 private long mCustomContentShowTime = -1;
116
Adam Cohen22cba7f2013-07-19 16:14:00 -0700117 private LayoutTransition mLayoutTransition;
Dianne Hackborn8f573952009-08-10 23:21:09 -0700118 private final WallpaperManager mWallpaperManager;
Michael Jurka9c6fbed2011-03-02 17:41:34 -0800119 private IBinder mWindowToken;
Winson Chungaafa03c2010-06-11 17:34:16 -0700120
Winson Chung9e6a0a22013-08-27 11:58:12 -0700121 private int mOriginalDefaultPage;
Michael Jurka0142d492010-08-25 17:46:15 -0700122 private int mDefaultPage;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800123
Adam Cohen5084cba2013-09-03 12:01:16 -0700124 private ShortcutAndWidgetContainer mDragSourceInternal;
Adam Cohen53805212013-10-01 10:39:23 -0700125 private static boolean sAccessibilityEnabled;
Adam Cohen5084cba2013-09-03 12:01:16 -0700126
Adam Cohendcd297f2013-06-18 13:13:40 -0700127 // The screen id used for the empty screen always present to the right.
Adrian Roos8f3f6832014-04-28 15:45:52 +0200128 final static long EXTRA_EMPTY_SCREEN_ID = -201;
Adam Cohendcd297f2013-06-18 13:13:40 -0700129 private final static long CUSTOM_CONTENT_SCREEN_ID = -301;
130
131 private HashMap<Long, CellLayout> mWorkspaceScreens = new HashMap<Long, CellLayout>();
132 private ArrayList<Long> mScreenOrder = new ArrayList<Long>();
133
Adam Cohenad4e15c2013-10-17 16:21:35 -0700134 private Runnable mRemoveEmptyScreenRunnable;
Adam Cohen689ff162014-05-08 17:27:56 -0700135 private boolean mDeferRemoveExtraEmptyScreen = false;
Adam Cohenad4e15c2013-10-17 16:21:35 -0700136
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800137 /**
138 * CellInfo for the cell that is currently being dragged
139 */
140 private CellLayout.CellInfo mDragInfo;
Winson Chungaafa03c2010-06-11 17:34:16 -0700141
Jeff Sharkey70864282009-04-07 21:08:40 -0700142 /**
143 * Target drop area calculated during last acceptDrop call.
144 */
Adam Cohenc0dcf592011-06-01 15:30:43 -0700145 private int[] mTargetCell = new int[2];
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700146 private int mDragOverX = -1;
147 private int mDragOverY = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800148
Adam Cohena897f392012-04-27 18:12:05 -0700149 static Rect mLandscapeCellLayoutMetrics = null;
150 static Rect mPortraitCellLayoutMetrics = null;
151
Adam Cohenbffe7452013-07-22 18:21:45 -0700152 CustomContentCallbacks mCustomContentCallbacks;
153 boolean mCustomContentShowing;
Adam Cohenc36fa5c2013-08-29 11:54:42 -0700154 private float mLastCustomContentScrollProgress = -1f;
Adam Cohen53805212013-10-01 10:39:23 -0700155 private String mCustomContentDescription = "";
Adam Cohenbffe7452013-07-22 18:21:45 -0700156
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700157 /**
158 * The CellLayout that is currently being dragged over
159 */
160 private CellLayout mDragTargetLayout = null;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700161 /**
162 * The CellLayout that we will show as glowing
163 */
164 private CellLayout mDragOverlappingLayout = null;
165
166 /**
167 * The CellLayout which will be dropped to
168 */
169 private CellLayout mDropToLayout = null;
170
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800171 private Launcher mLauncher;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800172 private IconCache mIconCache;
Joe Onorato00acb122009-08-04 16:04:30 -0400173 private DragController mDragController;
Winson Chungaafa03c2010-06-11 17:34:16 -0700174
Michael Jurka4516c112010-10-07 15:13:47 -0700175 // These are temporary variables to prevent having to allocate a new object just to
176 // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800177 private int[] mTempCell = new int[2];
Adam Cohen7d30a372013-07-01 17:03:59 -0700178 private int[] mTempPt = new int[2];
Jeff Sharkey70864282009-04-07 21:08:40 -0700179 private int[] mTempEstimate = new int[2];
Adam Cohene3e27a82011-04-15 12:07:39 -0700180 private float[] mDragViewVisualCenter = new float[2];
Michael Jurka4516c112010-10-07 15:13:47 -0700181 private float[] mTempCellLayoutCenterCoordinates = new float[2];
Michael Jurka0280c3b2010-09-17 15:00:07 -0700182 private Matrix mTempInverseMatrix = new Matrix();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800183
Michael Jurkac2f7f472010-12-14 15:34:42 -0800184 private SpringLoadedDragController mSpringLoadedDragController;
Winson Chungb26f3d62011-06-02 10:49:29 -0700185 private float mSpringLoadedShrinkFactor;
Adam Cohenf358a4b2013-07-23 16:47:31 -0700186 private float mOverviewModeShrinkFactor;
Michael Jurkad3ef3062010-11-23 16:23:58 -0800187
Patrick Dubroy1262e362010-10-06 15:49:50 -0700188 // State variable that indicates whether the pages are small (ie when you're
Michael Jurkadee05892010-07-27 10:01:56 -0700189 // in all apps or customize mode)
Michael Jurkad74c9842011-07-10 12:44:21 -0700190
Adam Cohen6c5891a2014-07-09 23:53:15 -0700191 enum State { NORMAL, NORMAL_HIDDEN, SPRING_LOADED, OVERVIEW, OVERVIEW_HIDDEN};
Adam Cohen7777d962011-08-18 18:58:38 -0700192 private State mState = State.NORMAL;
Michael Jurkad74c9842011-07-10 12:44:21 -0700193 private boolean mIsSwitchingState = false;
Michael Jurkad74c9842011-07-10 12:44:21 -0700194
Michael Jurkad74c9842011-07-10 12:44:21 -0700195 boolean mAnimatingViewIntoPlace = false;
196 boolean mIsDragOccuring = false;
197 boolean mChildrenLayersEnabled = true;
Michael Jurkadee05892010-07-27 10:01:56 -0700198
Adam Cohenaccfd562013-07-12 14:40:40 -0700199 private boolean mStripScreensOnPageStopMoving = false;
200
Patrick Dubroy54fa3b92010-11-17 12:18:45 -0800201 /** Is the user is dragging an item near the edge of a page? */
Patrick Dubroy1262e362010-10-06 15:49:50 -0700202 private boolean mInScrollArea = false;
203
Daniel Sandlere4f98912013-06-25 15:13:26 -0400204 private HolographicOutlineHelper mOutlineHelper;
Joe Onorato4be866d2010-10-10 11:26:02 -0700205 private Bitmap mDragOutline = null;
Sunny Goyal508da152014-08-14 10:53:27 -0700206 private static final Rect sTempRect = new Rect();
Patrick Dubroy8e58e912010-10-14 13:21:48 -0700207 private final int[] mTempXY = new int[2];
Michael Jurkad51f33a2012-06-28 15:35:26 -0700208 private int[] mTempVisiblePagesRange = new int[2];
Adam Cohen1e4359c2014-08-18 13:12:16 -0700209 private boolean mOverscrollEffectSet;
Michael Jurkaf8304f02012-04-26 13:33:26 -0700210 public static final int DRAG_BITMAP_PADDING = 2;
Michael Jurka869390b2012-05-06 15:55:19 -0700211 private boolean mWorkspaceFadeInAdjacentScreens;
Joe Onorato4be866d2010-10-10 11:26:02 -0700212
Michael Jurkaab1983f2011-01-18 15:50:17 -0800213 WallpaperOffsetInterpolator mWallpaperOffset;
Michael Jurka2a4f4922014-01-29 16:32:39 +0100214 private boolean mWallpaperIsLiveWallpaper;
215 private int mNumPagesForWallpaperParallax;
216 private float mLastSetWallpaperOffsetSteps = 0;
217
Adam Cohen26976d92011-03-22 15:33:33 -0700218 private Runnable mDelayedResizeRunnable;
Winson Chungf0c6ae02012-03-21 16:10:31 -0700219 private Runnable mDelayedSnapToPageRunnable;
Michael Jurka84f2ce72012-04-13 15:08:01 -0700220 private Point mDisplaySize = new Point();
Michael Jurkac5b262c2011-01-12 20:24:50 -0800221
Adam Cohen19072da2011-05-31 14:30:45 -0700222 // Variables relating to the creation of user folders by hovering shortcuts over shortcuts
Adam Cohen482ed822012-03-02 14:15:13 -0800223 private static final int FOLDER_CREATION_TIMEOUT = 0;
Adam Cohenfa3c58f2013-12-06 16:10:55 -0800224 public static final int REORDER_TIMEOUT = 350;
Adam Cohen19072da2011-05-31 14:30:45 -0700225 private final Alarm mFolderCreationAlarm = new Alarm();
Adam Cohen482ed822012-03-02 14:15:13 -0800226 private final Alarm mReorderAlarm = new Alarm();
Adam Cohen19072da2011-05-31 14:30:45 -0700227 private FolderRingAnimator mDragFolderRingAnimator = null;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700228 private FolderIcon mDragOverFolderIcon = null;
Adam Cohen19072da2011-05-31 14:30:45 -0700229 private boolean mCreateUserFolderOnDrop = false;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700230 private boolean mAddToExistingFolderOnDrop = false;
231 private DropTarget.DragEnforcer mDragEnforcer;
Adam Cohen3aff81c2012-05-16 21:01:01 -0700232 private float mMaxDistanceForFolderCreation;
Adam Cohen073a46f2011-05-17 16:28:09 -0700233
Sunny Goyal508da152014-08-14 10:53:27 -0700234 private final Canvas mCanvas = new Canvas();
235
Adam Cohenf8d28232011-02-01 21:47:00 -0800236 // Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget)
237 private float mXDown;
238 private float mYDown;
239 final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
240 final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
241 final static float TOUCH_SLOP_DAMPING_FACTOR = 4;
242
Adam Cohened66b2b2012-01-23 17:28:51 -0800243 // Relating to the animation of items being dropped externally
Adam Cohend41fbf52012-02-16 23:53:59 -0800244 public static final int ANIMATE_INTO_POSITION_AND_DISAPPEAR = 0;
245 public static final int ANIMATE_INTO_POSITION_AND_REMAIN = 1;
246 public static final int ANIMATE_INTO_POSITION_AND_RESIZE = 2;
247 public static final int COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION = 3;
248 public static final int CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION = 4;
Adam Cohened66b2b2012-01-23 17:28:51 -0800249
Adam Cohen482ed822012-03-02 14:15:13 -0800250 // Related to dragging, folder creation and reordering
251 private static final int DRAG_MODE_NONE = 0;
252 private static final int DRAG_MODE_CREATE_FOLDER = 1;
253 private static final int DRAG_MODE_ADD_TO_FOLDER = 2;
254 private static final int DRAG_MODE_REORDER = 3;
255 private int mDragMode = DRAG_MODE_NONE;
256 private int mLastReorderX = -1;
257 private int mLastReorderY = -1;
258
Adam Cohen1462de32012-07-24 22:34:36 -0700259 private SparseArray<Parcelable> mSavedStates;
260 private final ArrayList<Integer> mRestoredPages = new ArrayList<Integer>();
261
Adam Cohen4b285c52011-07-21 14:24:06 -0700262 // These variables are used for storing the initial and final values during workspace animations
Adam Cohened51cc92011-08-01 20:28:08 -0700263 private int mSavedScrollX;
264 private float mSavedRotationY;
265 private float mSavedTranslationX;
Adam Cohen7d30a372013-07-01 17:03:59 -0700266
267 private float mCurrentScale;
268 private float mNewScale;
Adam Cohen4b285c52011-07-21 14:24:06 -0700269 private float[] mOldBackgroundAlphas;
Adam Cohen4b285c52011-07-21 14:24:06 -0700270 private float[] mOldAlphas;
Adam Cohen4b285c52011-07-21 14:24:06 -0700271 private float[] mNewBackgroundAlphas;
Adam Cohen4b285c52011-07-21 14:24:06 -0700272 private float[] mNewAlphas;
Adam Cohendcd297f2013-06-18 13:13:40 -0700273 private int mLastChildCount = -1;
Winson Chung70442722012-02-10 15:43:22 -0800274 private float mTransitionProgress;
Adam Cohen08072c02014-11-18 17:53:44 -0800275 private Animator mStateAnimator = null;
Adam Cohen4b285c52011-07-21 14:24:06 -0700276
Adam Cohen1e4359c2014-08-18 13:12:16 -0700277 float mOverScrollEffect = 0f;
278
Michael Jurka1e2f4652013-07-08 18:03:46 -0700279 private Runnable mDeferredAction;
280 private boolean mDeferDropAfterUninstall;
281 private boolean mUninstallSuccessful;
282
Adam Cohenc2d6e892014-10-16 09:49:24 -0700283 // State related to Launcher Overlay
284 LauncherOverlay mLauncherOverlay;
285 boolean mScrollInteractionBegan;
286 boolean mStartedSendingScrollEvents;
287 boolean mShouldSendPageSettled;
Adam Cohen8c4ca922014-10-24 17:40:34 -0700288 int mLastOverlaySroll = 0;
Adam Cohenc2d6e892014-10-16 09:49:24 -0700289
Romain Guyeeacd562012-10-10 18:47:33 -0700290 private final Runnable mBindPages = new Runnable() {
291 @Override
292 public void run() {
293 mLauncher.getModel().bindRemainingSynchronousPages();
294 }
295 };
296
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800297 /**
298 * Used to inflate the Workspace from XML.
299 *
300 * @param context The application's context.
Michael Jurka0142d492010-08-25 17:46:15 -0700301 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800302 */
303 public Workspace(Context context, AttributeSet attrs) {
304 this(context, attrs, 0);
305 }
306
307 /**
308 * Used to inflate the Workspace from XML.
309 *
310 * @param context The application's context.
Michael Jurka0142d492010-08-25 17:46:15 -0700311 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800312 * @param defStyle Unused.
313 */
314 public Workspace(Context context, AttributeSet attrs, int defStyle) {
315 super(context, attrs, defStyle);
Michael Jurka0142d492010-08-25 17:46:15 -0700316 mContentIsRefreshable = false;
Michael Jurka5f1c5092010-09-03 14:15:02 -0700317
Daniel Sandlere4f98912013-06-25 15:13:26 -0400318 mOutlineHelper = HolographicOutlineHelper.obtain(context);
319
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700320 mDragEnforcer = new DropTarget.DragEnforcer(context);
Winson Chungf0ea4d32011-06-06 14:27:16 -0700321 // With workspace, data is available straight from the get-go
322 setDataIsReady();
323
Michael Jurka8bc66c72012-06-21 08:36:45 -0700324 mLauncher = (Launcher) context;
Winson Chung867ca622012-02-21 15:48:35 -0800325 final Resources res = getResources();
Adam Cohen3b185e22013-10-29 14:45:58 -0700326 mWorkspaceFadeInAdjacentScreens = LauncherAppState.getInstance().getDynamicGrid().
327 getDeviceProfile().shouldFadeAdjacentWorkspaceScreens();
Michael Jurka869390b2012-05-06 15:55:19 -0700328 mFadeInAdjacentScreens = false;
Dianne Hackborn8f573952009-08-10 23:21:09 -0700329 mWallpaperManager = WallpaperManager.getInstance(context);
Winson Chungaafa03c2010-06-11 17:34:16 -0700330
Winson Chungc82d2622013-11-06 13:23:29 -0800331 LauncherAppState app = LauncherAppState.getInstance();
332 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
Winson Chungaafa03c2010-06-11 17:34:16 -0700333 TypedArray a = context.obtainStyledAttributes(attrs,
334 R.styleable.Workspace, defStyle, 0);
Winson Chungb26f3d62011-06-02 10:49:29 -0700335 mSpringLoadedShrinkFactor =
336 res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
Winson Chungc82d2622013-11-06 13:23:29 -0800337 mOverviewModeShrinkFactor = grid.getOverviewModeScale();
Winson Chung9e6a0a22013-08-27 11:58:12 -0700338 mOriginalDefaultPage = mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800339 a.recycle();
340
Michael Jurka8b805b12012-04-18 14:23:14 -0700341 setOnHierarchyChangeListener(this);
Joe Onorato0d44e942009-11-16 18:20:51 -0800342 setHapticFeedbackEnabled(false);
Michael Jurka0142d492010-08-25 17:46:15 -0700343
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800344 initWorkspace();
Winson Chungc35afb22011-02-23 13:01:49 -0800345
346 // Disable multitouch across the workspace/all apps/customize tray
347 setMotionEventSplittingEnabled(true);
Adam Cohen53805212013-10-01 10:39:23 -0700348 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800349 }
350
John Spurlock77e1f472013-09-11 10:09:51 -0400351 @Override
352 public void setInsets(Rect insets) {
353 mInsets.set(insets);
Adam Cohen6400b842013-11-26 15:45:38 -0800354
355 CellLayout customScreen = getScreenWithId(CUSTOM_CONTENT_SCREEN_ID);
356 if (customScreen != null) {
357 View customContent = customScreen.getShortcutsAndWidgets().getChildAt(0);
358 if (customContent instanceof Insettable) {
359 ((Insettable) customContent).setInsets(mInsets);
360 }
361 }
John Spurlock77e1f472013-09-11 10:09:51 -0400362 }
363
Michael Jurka038f9d82011-11-03 13:50:45 -0700364 // estimate the size of a widget with spans hSpan, vSpan. return MAX_VALUE for each
365 // dimension if unsuccessful
366 public int[] estimateItemSize(int hSpan, int vSpan,
Adam Cohend41fbf52012-02-16 23:53:59 -0800367 ItemInfo itemInfo, boolean springLoaded) {
Michael Jurka038f9d82011-11-03 13:50:45 -0700368 int[] size = new int[2];
369 if (getChildCount() > 0) {
Winson Chungad7db6e2013-10-08 14:01:06 -0700370 // Use the first non-custom page to estimate the child position
371 CellLayout cl = (CellLayout) getChildAt(numCustomPages());
Adam Cohend41fbf52012-02-16 23:53:59 -0800372 Rect r = estimateItemPosition(cl, itemInfo, 0, 0, hSpan, vSpan);
373 size[0] = r.width();
374 size[1] = r.height();
Michael Jurka038f9d82011-11-03 13:50:45 -0700375 if (springLoaded) {
376 size[0] *= mSpringLoadedShrinkFactor;
377 size[1] *= mSpringLoadedShrinkFactor;
378 }
379 return size;
380 } else {
381 size[0] = Integer.MAX_VALUE;
382 size[1] = Integer.MAX_VALUE;
383 return size;
384 }
385 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700386
Adam Cohend41fbf52012-02-16 23:53:59 -0800387 public Rect estimateItemPosition(CellLayout cl, ItemInfo pendingInfo,
Michael Jurka038f9d82011-11-03 13:50:45 -0700388 int hCell, int vCell, int hSpan, int vSpan) {
Adam Cohend41fbf52012-02-16 23:53:59 -0800389 Rect r = new Rect();
Michael Jurka038f9d82011-11-03 13:50:45 -0700390 cl.cellToRect(hCell, vCell, hSpan, vSpan, r);
Michael Jurka038f9d82011-11-03 13:50:45 -0700391 return r;
392 }
393
Adam Cohen5084cba2013-09-03 12:01:16 -0700394 public void onDragStart(final DragSource source, Object info, int dragAction) {
Michael Jurkad74c9842011-07-10 12:44:21 -0700395 mIsDragOccuring = true;
Michael Jurka3a0469d2012-06-21 09:38:41 -0700396 updateChildrenLayersEnabled(false);
Winson Chung641d71d2012-04-26 15:58:01 -0700397 mLauncher.lockScreenOrientation();
Sandeep Siddharthaab2d9d72013-09-17 13:18:24 -0700398 mLauncher.onInteractionBegin();
Michael Jurkaa3d30ad2012-05-08 13:43:43 -0700399 setChildrenBackgroundAlphaMultipliers(1f);
Winson Chungf561bdf2012-05-03 11:20:19 -0700400 // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging
401 InstallShortcutReceiver.enableInstallQueue();
402 UninstallShortcutReceiver.enableUninstallQueue();
Adam Cohen5084cba2013-09-03 12:01:16 -0700403 post(new Runnable() {
404 @Override
405 public void run() {
406 if (mIsDragOccuring) {
Adam Cohen689ff162014-05-08 17:27:56 -0700407 mDeferRemoveExtraEmptyScreen = false;
Adam Cohen5084cba2013-09-03 12:01:16 -0700408 addExtraEmptyScreenOnDrag();
409 }
410 }
411 });
Michael Jurkad74c9842011-07-10 12:44:21 -0700412 }
413
Adam Cohen689ff162014-05-08 17:27:56 -0700414
415 public void deferRemoveExtraEmptyScreen() {
416 mDeferRemoveExtraEmptyScreen = true;
417 }
418
Michael Jurkad74c9842011-07-10 12:44:21 -0700419 public void onDragEnd() {
Adam Cohen689ff162014-05-08 17:27:56 -0700420 if (!mDeferRemoveExtraEmptyScreen) {
421 removeExtraEmptyScreen(true, mDragSourceInternal != null);
422 }
423
Michael Jurkad74c9842011-07-10 12:44:21 -0700424 mIsDragOccuring = false;
Michael Jurka3a0469d2012-06-21 09:38:41 -0700425 updateChildrenLayersEnabled(false);
Winson Chung4b919f82012-05-01 10:44:08 -0700426 mLauncher.unlockScreenOrientation(false);
Winson Chungf561bdf2012-05-03 11:20:19 -0700427
428 // Re-enable any Un/InstallShortcutReceiver and now process any queued items
429 InstallShortcutReceiver.disableAndFlushInstallQueue(getContext());
430 UninstallShortcutReceiver.disableAndFlushUninstallQueue(getContext());
Adam Cohen5084cba2013-09-03 12:01:16 -0700431
Adam Cohen5084cba2013-09-03 12:01:16 -0700432 mDragSourceInternal = null;
Sandeep Siddharthaab2d9d72013-09-17 13:18:24 -0700433 mLauncher.onInteractionEnd();
Michael Jurkad74c9842011-07-10 12:44:21 -0700434 }
435
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800436 /**
437 * Initializes various states for this workspace.
438 */
Michael Jurka0142d492010-08-25 17:46:15 -0700439 protected void initWorkspace() {
Michael Jurka0142d492010-08-25 17:46:15 -0700440 mCurrentPage = mDefaultPage;
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400441 LauncherAppState app = LauncherAppState.getInstance();
Winson Chung5f8afe62013-08-12 16:19:28 -0700442 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800443 mIconCache = app.getIconCache();
Patrick Dubroycd68ff52010-10-28 17:57:05 -0700444 setWillNotDraw(false);
Romain Guyce3cbd12013-02-25 15:00:36 -0800445 setClipChildren(false);
446 setClipToPadding(false);
Adam Cohen7777d962011-08-18 18:58:38 -0700447 setChildrenDrawnWithCacheEnabled(true);
Derek Prothrodadd9842014-01-17 13:43:50 -0500448
Winson Chungc82d2622013-11-06 13:23:29 -0800449 setMinScale(mOverviewModeShrinkFactor);
Adam Cohen22cba7f2013-07-19 16:14:00 -0700450 setupLayoutTransition();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800451
Michael Jurkaab1983f2011-01-18 15:50:17 -0800452 mWallpaperOffset = new WallpaperOffsetInterpolator();
Adam Cohencff6af82011-09-13 14:51:53 -0700453 Display display = mLauncher.getWindowManager().getDefaultDisplay();
Michael Jurka84f2ce72012-04-13 15:08:01 -0700454 display.getSize(mDisplaySize);
Adam Cohen265b9a62011-12-07 14:37:18 -0800455
Winson Chung5f8afe62013-08-12 16:19:28 -0700456 mMaxDistanceForFolderCreation = (0.55f * grid.iconSizePx);
Adam Cohen265b9a62011-12-07 14:37:18 -0800457 mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity);
Michael Jurkaa6a05472013-11-13 17:59:46 +0100458
459 // Set the wallpaper dimensions when Launcher starts up
Adam Cohen824fcb32014-05-21 23:01:25 +0000460 setWallpaperDimension();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800461 }
462
Adam Cohen22cba7f2013-07-19 16:14:00 -0700463 private void setupLayoutTransition() {
464 // We want to show layout transitions when pages are deleted, to close the gap.
465 mLayoutTransition = new LayoutTransition();
466 mLayoutTransition.enableTransitionType(LayoutTransition.DISAPPEARING);
467 mLayoutTransition.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
468 mLayoutTransition.disableTransitionType(LayoutTransition.APPEARING);
469 mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
470 setLayoutTransition(mLayoutTransition);
471 }
472
Winson Chung964df6b2013-10-11 15:55:37 -0700473 void enableLayoutTransitions() {
474 setLayoutTransition(mLayoutTransition);
475 }
476 void disableLayoutTransitions() {
477 setLayoutTransition(null);
478 }
479
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800480 @Override
Adam Cohenf34bab52010-09-30 14:11:56 -0700481 protected int getScrollMode() {
Winson Chungb26f3d62011-06-02 10:49:29 -0700482 return SmoothPagedView.X_LARGE_MODE;
Adam Cohenf34bab52010-09-30 14:11:56 -0700483 }
484
Michael Jurka08ee7702011-08-11 16:53:35 -0700485 @Override
Michael Jurka8b805b12012-04-18 14:23:14 -0700486 public void onChildViewAdded(View parent, View child) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800487 if (!(child instanceof CellLayout)) {
488 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
489 }
Adam Cohen2801caf2011-05-13 20:57:39 -0700490 CellLayout cl = ((CellLayout) child);
491 cl.setOnInterceptTouchListener(this);
Adam Cohen2801caf2011-05-13 20:57:39 -0700492 cl.setClickable(true);
Sunny Goyalc46bfef2015-01-05 12:40:08 -0800493 cl.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
Winson Chungf70696d2013-06-25 16:19:53 -0700494 super.onChildViewAdded(parent, child);
Michael Jurka8b805b12012-04-18 14:23:14 -0700495 }
496
Michael Jurka920d7f42012-05-14 16:29:55 -0700497 protected boolean shouldDrawChild(View child) {
498 final CellLayout cl = (CellLayout) child;
499 return super.shouldDrawChild(child) &&
Winson Chungf4bd2362013-10-07 17:11:27 -0700500 (mIsSwitchingState ||
501 cl.getShortcutsAndWidgets().getAlpha() > 0 ||
Michael Jurka920d7f42012-05-14 16:29:55 -0700502 cl.getBackgroundAlpha() > 0);
503 }
504
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800505 /**
506 * @return The open folder on the current screen, or null if there is none
507 */
508 Folder getOpenFolder() {
Adam Cohen716b51e2011-06-30 12:09:54 -0700509 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohen8e776a62011-06-28 18:10:06 -0700510 int count = dragLayer.getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800511 for (int i = 0; i < count; i++) {
Adam Cohen8e776a62011-06-28 18:10:06 -0700512 View child = dragLayer.getChildAt(i);
Winson Chungaafa03c2010-06-11 17:34:16 -0700513 if (child instanceof Folder) {
514 Folder folder = (Folder) child;
515 if (folder.getInfo().opened)
516 return folder;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800517 }
518 }
519 return null;
520 }
521
Patrick Dubroya0aa0122011-02-24 11:42:23 -0800522 boolean isTouchActive() {
523 return mTouchState != TOUCH_STATE_REST;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800524 }
525
Winson Chung9e6a0a22013-08-27 11:58:12 -0700526 public void removeAllWorkspaceScreens() {
Winson Chung964df6b2013-10-11 15:55:37 -0700527 // Disable all layout transitions before removing all pages to ensure that we don't get the
528 // transition animations competing with us changing the scroll when we add pages or the
529 // custom content screen
530 disableLayoutTransitions();
531
532 // Since we increment the current page when we call addCustomContentPage via bindScreens
533 // (and other places), we need to adjust the current page back when we clear the pages
534 if (hasCustomContent()) {
535 removeCustomContentPage();
536 }
537
Winson Chung9e6a0a22013-08-27 11:58:12 -0700538 // Remove the pages and clear the screen models
539 removeAllViews();
540 mScreenOrder.clear();
541 mWorkspaceScreens.clear();
Winson Chung964df6b2013-10-11 15:55:37 -0700542
543 // Re-enable the layout transitions
544 enableLayoutTransitions();
Winson Chung9e6a0a22013-08-27 11:58:12 -0700545 }
546
Adam Cohen89bddfa2013-08-20 11:57:13 -0700547 public long insertNewWorkspaceScreenBeforeEmptyScreen(long screenId) {
Winson Chung64359a52013-07-08 17:17:08 -0700548 // Find the index to insert this view into. If the empty screen exists, then
549 // insert it before that.
550 int insertIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
551 if (insertIndex < 0) {
552 insertIndex = mScreenOrder.size();
553 }
Adam Cohen89bddfa2013-08-20 11:57:13 -0700554 return insertNewWorkspaceScreen(screenId, insertIndex);
Adam Cohendcd297f2013-06-18 13:13:40 -0700555 }
556
Adam Cohen89bddfa2013-08-20 11:57:13 -0700557 public long insertNewWorkspaceScreen(long screenId) {
558 return insertNewWorkspaceScreen(screenId, getChildCount());
Winson Chung64359a52013-07-08 17:17:08 -0700559 }
560
Adam Cohen89bddfa2013-08-20 11:57:13 -0700561 public long insertNewWorkspaceScreen(long screenId, int insertIndex) {
Adam Cohen5084cba2013-09-03 12:01:16 -0700562 if (mWorkspaceScreens.containsKey(screenId)) {
563 throw new RuntimeException("Screen id " + screenId + " already exists!");
564 }
565
Adam Cohendcd297f2013-06-18 13:13:40 -0700566 CellLayout newScreen = (CellLayout)
567 mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null);
568
Adam Cohen7d30a372013-07-01 17:03:59 -0700569 newScreen.setOnLongClickListener(mLongClickListener);
Adam Cohenf358a4b2013-07-23 16:47:31 -0700570 newScreen.setOnClickListener(mLauncher);
Winson Chung837b2412013-09-09 14:43:51 -0700571 newScreen.setSoundEffectsEnabled(false);
Adam Cohendcd297f2013-06-18 13:13:40 -0700572 mWorkspaceScreens.put(screenId, newScreen);
Winson Chung64359a52013-07-08 17:17:08 -0700573 mScreenOrder.add(insertIndex, screenId);
574 addView(newScreen, insertIndex);
Adam Cohendcd297f2013-06-18 13:13:40 -0700575 return screenId;
576 }
577
Derek Prothrodadd9842014-01-17 13:43:50 -0500578 public void createCustomContentContainer() {
Adam Cohendcd297f2013-06-18 13:13:40 -0700579 CellLayout customScreen = (CellLayout)
Adam Cohen41eb4702013-06-27 15:08:59 -0700580 mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null);
Winson Chung59a488a2013-12-10 12:32:14 -0800581 customScreen.disableBackground();
Adam Cohenc50438c2014-08-19 17:43:05 -0700582 customScreen.disableDragTarget();
Adam Cohendcd297f2013-06-18 13:13:40 -0700583
Adam Cohendcd297f2013-06-18 13:13:40 -0700584 mWorkspaceScreens.put(CUSTOM_CONTENT_SCREEN_ID, customScreen);
585 mScreenOrder.add(0, CUSTOM_CONTENT_SCREEN_ID);
Adam Cohenedb40762013-07-18 16:45:45 -0700586
Adam Cohen2702ea02013-08-15 16:17:42 -0700587 // We want no padding on the custom content
588 customScreen.setPadding(0, 0, 0, 0);
589
Adam Cohenedb40762013-07-18 16:45:45 -0700590 addFullScreenPage(customScreen);
Michael Jurkaee0ce2b2013-07-02 17:24:35 -0700591
Adam Cohendcd297f2013-06-18 13:13:40 -0700592 // Ensure that the current page and default page are maintained.
Winson Chung9e6a0a22013-08-27 11:58:12 -0700593 mDefaultPage = mOriginalDefaultPage + 1;
Winson Chung82e5c982013-10-09 12:04:50 -0700594
595 // Update the custom content hint
Adam Cohen21cd0022013-10-09 18:57:02 -0700596 if (mRestorePage != INVALID_RESTORE_PAGE) {
597 mRestorePage = mRestorePage + 1;
598 } else {
599 setCurrentPage(getCurrentPage() + 1);
600 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700601 }
602
Dave Hawkeya8881582013-09-17 15:55:33 -0600603 public void removeCustomContentPage() {
Dave Hawkeya8881582013-09-17 15:55:33 -0600604 CellLayout customScreen = getScreenWithId(CUSTOM_CONTENT_SCREEN_ID);
605 if (customScreen == null) {
606 throw new RuntimeException("Expected custom content screen to exist");
607 }
608
609 mWorkspaceScreens.remove(CUSTOM_CONTENT_SCREEN_ID);
610 mScreenOrder.remove(CUSTOM_CONTENT_SCREEN_ID);
611 removeView(customScreen);
Adam Cohenbb6fda62013-10-10 12:48:52 -0700612
613 if (mCustomContentCallbacks != null) {
614 mCustomContentCallbacks.onScrollProgressChanged(0);
615 mCustomContentCallbacks.onHide();
616 }
617
Dave Hawkeya8881582013-09-17 15:55:33 -0600618 mCustomContentCallbacks = null;
619
620 // Ensure that the current page and default page are maintained.
621 mDefaultPage = mOriginalDefaultPage - 1;
Winson Chung82e5c982013-10-09 12:04:50 -0700622
623 // Update the custom content hint
Adam Cohen21cd0022013-10-09 18:57:02 -0700624 if (mRestorePage != INVALID_RESTORE_PAGE) {
625 mRestorePage = mRestorePage - 1;
626 } else {
627 setCurrentPage(getCurrentPage() - 1);
628 }
Dave Hawkeya8881582013-09-17 15:55:33 -0600629 }
630
Adam Cohen53805212013-10-01 10:39:23 -0700631 public void addToCustomContentPage(View customContent, CustomContentCallbacks callbacks,
632 String description) {
Winson Chung98ca0f72013-07-29 12:58:51 -0700633 if (getPageIndexForScreenId(CUSTOM_CONTENT_SCREEN_ID) < 0) {
634 throw new RuntimeException("Expected custom content screen to exist");
635 }
636
637 // Add the custom content to the full screen custom page
638 CellLayout customScreen = getScreenWithId(CUSTOM_CONTENT_SCREEN_ID);
639 int spanX = customScreen.getCountX();
640 int spanY = customScreen.getCountY();
641 CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, spanX, spanY);
642 lp.canReorder = false;
643 lp.isFullscreen = true;
John Spurlock77e1f472013-09-11 10:09:51 -0400644 if (customContent instanceof Insettable) {
645 ((Insettable)customContent).setInsets(mInsets);
646 }
Adam Cohen166ebd42013-11-26 14:55:45 -0800647
648 // Verify that the child is removed from any existing parent.
649 if (customContent.getParent() instanceof ViewGroup) {
650 ViewGroup parent = (ViewGroup) customContent.getParent();
651 parent.removeView(customContent);
652 }
Adam Cohen225ad9b2013-10-07 13:03:00 -0700653 customScreen.removeAllViews();
Winson Chung98ca0f72013-07-29 12:58:51 -0700654 customScreen.addViewToCellLayout(customContent, 0, 0, lp, true);
Adam Cohen53805212013-10-01 10:39:23 -0700655 mCustomContentDescription = description;
Winson Chung98ca0f72013-07-29 12:58:51 -0700656
657 mCustomContentCallbacks = callbacks;
658 }
659
Adam Cohen5084cba2013-09-03 12:01:16 -0700660 public void addExtraEmptyScreenOnDrag() {
661 boolean lastChildOnScreen = false;
662 boolean childOnFinalScreen = false;
663
Adam Cohenad4e15c2013-10-17 16:21:35 -0700664 // Cancel any pending removal of empty screen
665 mRemoveEmptyScreenRunnable = null;
666
Adam Cohen5084cba2013-09-03 12:01:16 -0700667 if (mDragSourceInternal != null) {
668 if (mDragSourceInternal.getChildCount() == 1) {
669 lastChildOnScreen = true;
670 }
671 CellLayout cl = (CellLayout) mDragSourceInternal.getParent();
672 if (indexOfChild(cl) == getChildCount() - 1) {
673 childOnFinalScreen = true;
674 }
675 }
676
677 // If this is the last item on the final screen
678 if (lastChildOnScreen && childOnFinalScreen) {
679 return;
680 }
681 if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) {
682 insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID);
683 }
684 }
685
686 public boolean addExtraEmptyScreen() {
687 if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) {
688 insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID);
689 return true;
690 }
691 return false;
692 }
693
Adam Cohenad4e15c2013-10-17 16:21:35 -0700694 private void convertFinalScreenToEmptyScreenIfNecessary() {
Adam Cohendcb173d2014-04-01 13:33:58 -0700695 if (mLauncher.isWorkspaceLoading()) {
696 // Invalid and dangerous operation if workspace is loading
697 Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
698 return;
699 }
700
Adam Cohenad4e15c2013-10-17 16:21:35 -0700701 if (hasExtraEmptyScreen() || mScreenOrder.size() == 0) return;
702 long finalScreenId = mScreenOrder.get(mScreenOrder.size() - 1);
703
704 if (finalScreenId == CUSTOM_CONTENT_SCREEN_ID) return;
705 CellLayout finalScreen = mWorkspaceScreens.get(finalScreenId);
706
707 // If the final screen is empty, convert it to the extra empty screen
Adam Cohen917e3882013-10-31 15:03:35 -0700708 if (finalScreen.getShortcutsAndWidgets().getChildCount() == 0 &&
709 !finalScreen.isDropPending()) {
Adam Cohenad4e15c2013-10-17 16:21:35 -0700710 mWorkspaceScreens.remove(finalScreenId);
711 mScreenOrder.remove(finalScreenId);
712
713 // if this is the last non-custom content screen, convert it to the empty screen
714 mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, finalScreen);
715 mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
Winson Chunga90303b2013-11-15 13:05:06 -0800716
Winson Chungf0716b72013-11-18 16:09:54 -0800717 // Update the model if we have changed any screens
718 mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
Adam Cohen5084cba2013-09-03 12:01:16 -0700719 }
720 }
721
Adam Cohen689ff162014-05-08 17:27:56 -0700722 public void removeExtraEmptyScreen(final boolean animate, boolean stripEmptyScreens) {
723 removeExtraEmptyScreenDelayed(animate, null, 0, stripEmptyScreens);
Adam Cohenad4e15c2013-10-17 16:21:35 -0700724 }
725
Adam Cohen689ff162014-05-08 17:27:56 -0700726 public void removeExtraEmptyScreenDelayed(final boolean animate, final Runnable onComplete,
Adam Cohenad4e15c2013-10-17 16:21:35 -0700727 final int delay, final boolean stripEmptyScreens) {
Adam Cohendcb173d2014-04-01 13:33:58 -0700728 if (mLauncher.isWorkspaceLoading()) {
729 // Don't strip empty screens if the workspace is still loading
730 Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
731 return;
732 }
733
Adam Cohenad4e15c2013-10-17 16:21:35 -0700734 if (delay > 0) {
735 postDelayed(new Runnable() {
736 @Override
737 public void run() {
Adam Cohen689ff162014-05-08 17:27:56 -0700738 removeExtraEmptyScreenDelayed(animate, onComplete, 0, stripEmptyScreens);
Adam Cohenad4e15c2013-10-17 16:21:35 -0700739 }
Adam Cohenad4e15c2013-10-17 16:21:35 -0700740 }, delay);
741 return;
742 }
743
744 convertFinalScreenToEmptyScreenIfNecessary();
745 if (hasExtraEmptyScreen()) {
746 int emptyIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
747 if (getNextPage() == emptyIndex) {
748 snapToPage(getNextPage() - 1, SNAP_OFF_EMPTY_SCREEN_DURATION);
749 fadeAndRemoveEmptyScreen(SNAP_OFF_EMPTY_SCREEN_DURATION, FADE_EMPTY_SCREEN_DURATION,
750 onComplete, stripEmptyScreens);
751 } else {
752 fadeAndRemoveEmptyScreen(0, FADE_EMPTY_SCREEN_DURATION,
753 onComplete, stripEmptyScreens);
754 }
755 return;
Adam Cohen3a14eeb2013-11-15 16:51:22 +0000756 } else if (stripEmptyScreens) {
757 // If we're not going to strip the empty screens after removing
758 // the extra empty screen, do it right away.
759 stripEmptyScreens();
Adam Cohenad4e15c2013-10-17 16:21:35 -0700760 }
Adam Cohen3a14eeb2013-11-15 16:51:22 +0000761
Adam Cohenad4e15c2013-10-17 16:21:35 -0700762 if (onComplete != null) {
763 onComplete.run();
764 }
765 }
766
767 private void fadeAndRemoveEmptyScreen(int delay, int duration, final Runnable onComplete,
768 final boolean stripEmptyScreens) {
Winson Chunga90303b2013-11-15 13:05:06 -0800769 // XXX: Do we need to update LM workspace screens below?
Adam Cohenad4e15c2013-10-17 16:21:35 -0700770 PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0f);
771 PropertyValuesHolder bgAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", 0f);
772
773 final CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
774
775 mRemoveEmptyScreenRunnable = new Runnable() {
776 @Override
777 public void run() {
778 if (hasExtraEmptyScreen()) {
779 mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
780 mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
781 removeView(cl);
782 if (stripEmptyScreens) {
783 stripEmptyScreens();
784 }
785 }
786 }
787 };
788
789 ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(cl, alpha, bgAlpha);
790 oa.setDuration(duration);
791 oa.setStartDelay(delay);
792 oa.addListener(new AnimatorListenerAdapter() {
793 @Override
794 public void onAnimationEnd(Animator animation) {
795 if (mRemoveEmptyScreenRunnable != null) {
796 mRemoveEmptyScreenRunnable.run();
797 }
798 if (onComplete != null) {
799 onComplete.run();
800 }
801 }
802 });
803 oa.start();
804 }
805
Michael Jurka2f817312013-09-20 03:03:42 +0200806 public boolean hasExtraEmptyScreen() {
807 int nScreens = getChildCount();
Michael Jurkafe0ace32013-10-03 01:05:14 -0700808 nScreens = nScreens - numCustomPages();
Michael Jurka2f817312013-09-20 03:03:42 +0200809 return mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID) && nScreens > 1;
810 }
811
Adam Cohendcd297f2013-06-18 13:13:40 -0700812 public long commitExtraEmptyScreen() {
Adam Cohendcb173d2014-04-01 13:33:58 -0700813 if (mLauncher.isWorkspaceLoading()) {
814 // Invalid and dangerous operation if workspace is loading
815 Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
816 return -1;
817 }
Winson Chunga90303b2013-11-15 13:05:06 -0800818
Winson Chung89f97052013-09-20 11:32:26 -0700819 int index = getPageIndexForScreenId(EXTRA_EMPTY_SCREEN_ID);
Adam Cohendcd297f2013-06-18 13:13:40 -0700820 CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
Winson Chungc9168342013-06-26 14:54:55 -0700821 mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
Adam Cohendcd297f2013-06-18 13:13:40 -0700822 mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
823
Michael Jurka104c4562013-07-08 18:03:46 -0700824 long newId = LauncherAppState.getLauncherProvider().generateNewScreenId();
Adam Cohendcd297f2013-06-18 13:13:40 -0700825 mWorkspaceScreens.put(newId, cl);
826 mScreenOrder.add(newId);
827
Winson Chung89f97052013-09-20 11:32:26 -0700828 // Update the page indicator marker
829 if (getPageIndicator() != null) {
830 getPageIndicator().updateMarker(index, getPageIndicatorMarker(index));
831 }
832
Winson Chung64359a52013-07-08 17:17:08 -0700833 // Update the model for the new screen
834 mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
835
Adam Cohendcd297f2013-06-18 13:13:40 -0700836 return newId;
837 }
838
Adam Cohendcd297f2013-06-18 13:13:40 -0700839 public CellLayout getScreenWithId(long screenId) {
840 CellLayout layout = mWorkspaceScreens.get(screenId);
841 return layout;
842 }
843
844 public long getIdForScreen(CellLayout layout) {
845 Iterator<Long> iter = mWorkspaceScreens.keySet().iterator();
846 while (iter.hasNext()) {
847 long id = iter.next();
848 if (mWorkspaceScreens.get(id) == layout) {
849 return id;
850 }
851 }
852 return -1;
853 }
854
855 public int getPageIndexForScreenId(long screenId) {
856 return indexOfChild(mWorkspaceScreens.get(screenId));
857 }
858
859 public long getScreenIdForPageIndex(int index) {
Winson Chung5f8afe62013-08-12 16:19:28 -0700860 if (0 <= index && index < mScreenOrder.size()) {
861 return mScreenOrder.get(index);
862 }
863 return -1;
Adam Cohendcd297f2013-06-18 13:13:40 -0700864 }
865
Winson Chungc9168342013-06-26 14:54:55 -0700866 ArrayList<Long> getScreenOrder() {
867 return mScreenOrder;
868 }
869
Adam Cohendcd297f2013-06-18 13:13:40 -0700870 public void stripEmptyScreens() {
Adam Cohen65e43032014-03-03 11:37:21 -0800871 if (mLauncher.isWorkspaceLoading()) {
Adam Cohendcb173d2014-04-01 13:33:58 -0700872 // Don't strip empty screens if the workspace is still loading.
873 // This is dangerous and can result in data loss.
Adam Cohen517a7f52014-03-01 12:12:59 -0800874 Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
875 return;
876 }
877
Adam Cohenaccfd562013-07-12 14:40:40 -0700878 if (isPageMoving()) {
879 mStripScreensOnPageStopMoving = true;
880 return;
881 }
882
883 int currentPage = getNextPage();
Adam Cohendcd297f2013-06-18 13:13:40 -0700884 ArrayList<Long> removeScreens = new ArrayList<Long>();
885 for (Long id: mWorkspaceScreens.keySet()) {
886 CellLayout cl = mWorkspaceScreens.get(id);
Winson Chungc9168342013-06-26 14:54:55 -0700887 if (id >= 0 && cl.getShortcutsAndWidgets().getChildCount() == 0) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700888 removeScreens.add(id);
889 }
890 }
891
Adam Cohen5084cba2013-09-03 12:01:16 -0700892 // We enforce at least one page to add new items to. In the case that we remove the last
893 // such screen, we convert the last screen to the empty screen
Michael Jurkafe0ace32013-10-03 01:05:14 -0700894 int minScreens = 1 + numCustomPages();
Adam Cohen5084cba2013-09-03 12:01:16 -0700895
Adam Cohendcd297f2013-06-18 13:13:40 -0700896 int pageShift = 0;
897 for (Long id: removeScreens) {
898 CellLayout cl = mWorkspaceScreens.get(id);
899 mWorkspaceScreens.remove(id);
900 mScreenOrder.remove(id);
Adam Cohen5084cba2013-09-03 12:01:16 -0700901
902 if (getChildCount() > minScreens) {
903 if (indexOfChild(cl) < currentPage) {
904 pageShift++;
905 }
906 removeView(cl);
907 } else {
908 // if this is the last non-custom content screen, convert it to the empty screen
Adam Cohenad4e15c2013-10-17 16:21:35 -0700909 mRemoveEmptyScreenRunnable = null;
Adam Cohen5084cba2013-09-03 12:01:16 -0700910 mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, cl);
911 mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
Adam Cohendcd297f2013-06-18 13:13:40 -0700912 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700913 }
Winson Chung64359a52013-07-08 17:17:08 -0700914
915 if (!removeScreens.isEmpty()) {
916 // Update the model if we have changed any screens
917 mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
918 }
Adam Cohenaccfd562013-07-12 14:40:40 -0700919
920 if (pageShift >= 0) {
921 setCurrentPage(currentPage - pageShift);
922 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700923 }
924
925 // See implementation for parameter definition.
926 void addInScreen(View child, long container, long screenId,
927 int x, int y, int spanX, int spanY) {
928 addInScreen(child, container, screenId, x, y, spanX, spanY, false, false);
929 }
930
931 // At bind time, we use the rank (screenId) to compute x and y for hotseat items.
932 // See implementation for parameter definition.
933 void addInScreenFromBind(View child, long container, long screenId, int x, int y,
934 int spanX, int spanY) {
935 addInScreen(child, container, screenId, x, y, spanX, spanY, false, true);
936 }
937
938 // See implementation for parameter definition.
939 void addInScreen(View child, long container, long screenId, int x, int y, int spanX, int spanY,
940 boolean insert) {
941 addInScreen(child, container, screenId, x, y, spanX, spanY, insert, false);
Winson Chungaafa03c2010-06-11 17:34:16 -0700942 }
943
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800944 /**
945 * Adds the specified child in the specified screen. The position and dimension of
946 * the child are defined by x, y, spanX and spanY.
947 *
948 * @param child The child to add in one of the workspace's screens.
Adam Cohendcd297f2013-06-18 13:13:40 -0700949 * @param screenId The screen in which to add the child.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800950 * @param x The X position of the child in the screen's grid.
951 * @param y The Y position of the child in the screen's grid.
952 * @param spanX The number of cells spanned horizontally by the child.
953 * @param spanY The number of cells spanned vertically by the child.
954 * @param insert When true, the child is inserted at the beginning of the children list.
Adam Cohendcd297f2013-06-18 13:13:40 -0700955 * @param computeXYFromRank When true, we use the rank (stored in screenId) to compute
956 * the x and y position in which to place hotseat items. Otherwise
957 * we use the x and y position to compute the rank.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800958 */
Adam Cohendcd297f2013-06-18 13:13:40 -0700959 void addInScreen(View child, long container, long screenId, int x, int y, int spanX, int spanY,
960 boolean insert, boolean computeXYFromRank) {
Winson Chung3d503fb2011-07-13 17:25:49 -0700961 if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700962 if (getScreenWithId(screenId) == null) {
963 Log.e(TAG, "Skipping child, screenId " + screenId + " not found");
Winson Chung8481e322013-08-09 16:06:38 -0700964 // DEBUGGING - Print out the stack trace to see where we are adding from
965 new Throwable().printStackTrace();
Winson Chung3d503fb2011-07-13 17:25:49 -0700966 return;
967 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800968 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700969 if (screenId == EXTRA_EMPTY_SCREEN_ID) {
Adam Cohendedbd962013-07-11 14:21:49 -0700970 // This should never happen
971 throw new RuntimeException("Screen id should not be EXTRA_EMPTY_SCREEN_ID");
Adam Cohendcd297f2013-06-18 13:13:40 -0700972 }
973
Winson Chung3d503fb2011-07-13 17:25:49 -0700974 final CellLayout layout;
975 if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
976 layout = mLauncher.getHotseat().getLayout();
Sunny Goyalb3726d92014-08-20 16:58:17 -0700977 child.setOnKeyListener(new HotseatIconKeyEventListener());
Winson Chung3d503fb2011-07-13 17:25:49 -0700978
Adam Cohen099f60d2011-08-23 21:07:26 -0700979 // Hide folder title in the hotseat
980 if (child instanceof FolderIcon) {
981 ((FolderIcon) child).setTextVisible(false);
982 }
983
Adam Cohendcd297f2013-06-18 13:13:40 -0700984 if (computeXYFromRank) {
985 x = mLauncher.getHotseat().getCellXFromOrder((int) screenId);
986 y = mLauncher.getHotseat().getCellYFromOrder((int) screenId);
Winson Chung3d503fb2011-07-13 17:25:49 -0700987 } else {
Adam Cohendcd297f2013-06-18 13:13:40 -0700988 screenId = mLauncher.getHotseat().getOrderInHotseat(x, y);
Winson Chung3d503fb2011-07-13 17:25:49 -0700989 }
990 } else {
Adam Cohen099f60d2011-08-23 21:07:26 -0700991 // Show folder title if not in the hotseat
992 if (child instanceof FolderIcon) {
993 ((FolderIcon) child).setTextVisible(true);
994 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700995 layout = getScreenWithId(screenId);
Adam Cohenac56cff2011-09-28 20:45:37 -0700996 child.setOnKeyListener(new IconKeyEventListener());
Winson Chung3d503fb2011-07-13 17:25:49 -0700997 }
998
Adam Cohen96d30a12013-07-16 18:13:21 -0700999 ViewGroup.LayoutParams genericLp = child.getLayoutParams();
Adam Cohened66b2b2012-01-23 17:28:51 -08001000 CellLayout.LayoutParams lp;
1001 if (genericLp == null || !(genericLp instanceof CellLayout.LayoutParams)) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001002 lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
1003 } else {
Adam Cohened66b2b2012-01-23 17:28:51 -08001004 lp = (CellLayout.LayoutParams) genericLp;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001005 lp.cellX = x;
1006 lp.cellY = y;
1007 lp.cellHSpan = spanX;
1008 lp.cellVSpan = spanY;
1009 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001010
Adam Cohen7f4eabe2011-04-21 16:19:16 -07001011 if (spanX < 0 && spanY < 0) {
1012 lp.isLockedToGrid = false;
1013 }
1014
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07001015 // Get the canonical child id to uniquely represent this view in this screen
Adam Cohenc76e1dd2013-11-14 11:09:14 +00001016 ItemInfo info = (ItemInfo) child.getTag();
1017 int childId = mLauncher.getViewIdForItem(info);
1018
Michael Jurkaf3ca3ab2010-10-20 17:08:24 -07001019 boolean markCellsAsOccupied = !(child instanceof Folder);
Winson Chung3d503fb2011-07-13 17:25:49 -07001020 if (!layout.addViewToCellLayout(child, insert ? 0 : -1, childId, lp, markCellsAsOccupied)) {
Winson Chungaafa03c2010-06-11 17:34:16 -07001021 // TODO: This branch occurs when the workspace is adding views
1022 // outside of the defined grid
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07001023 // maybe we should be deleting these items from the LauncherModel?
Adam Cohen4caf2982013-08-20 18:54:31 -07001024 Launcher.addDumpLog(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout", true);
Winson Chungaafa03c2010-06-11 17:34:16 -07001025 }
1026
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001027 if (!(child instanceof Folder)) {
Joe Onorato0d44e942009-11-16 18:20:51 -08001028 child.setHapticFeedbackEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001029 child.setOnLongClickListener(mLongClickListener);
1030 }
Joe Onorato00acb122009-08-04 16:04:30 -04001031 if (child instanceof DropTarget) {
Winson Chungaafa03c2010-06-11 17:34:16 -07001032 mDragController.addDropTarget((DropTarget) child);
Joe Onorato00acb122009-08-04 16:04:30 -04001033 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001034 }
1035
Patrick Dubroyd0ce1ec2011-01-19 18:47:27 -08001036 /**
Patrick Dubroye708c522011-03-01 16:03:43 -08001037 * Called directly from a CellLayout (not by the framework), after we've been added as a
1038 * listener via setOnInterceptTouchEventListener(). This allows us to tell the CellLayout
1039 * that it should intercept touch events, which is not something that is normally supported.
1040 */
1041 @Override
Michael Jurkadee05892010-07-27 10:01:56 -07001042 public boolean onTouch(View v, MotionEvent event) {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001043 return (workspaceInModalState() || !isFinishedSwitchingState())
1044 || (!workspaceInModalState() && indexOfChild(v) != mCurrentPage);
Patrick Dubroye708c522011-03-01 16:03:43 -08001045 }
1046
Adam Cohenfc53cd22011-07-20 15:45:11 -07001047 public boolean isSwitchingState() {
1048 return mIsSwitchingState;
1049 }
1050
Winson Chung70442722012-02-10 15:43:22 -08001051 /** This differs from isSwitchingState in that we take into account how far the transition
1052 * has completed. */
Winson Chung9b0b2fe2012-02-24 13:03:34 -08001053 public boolean isFinishedSwitchingState() {
Winson Chung70442722012-02-10 15:43:22 -08001054 return !mIsSwitchingState || (mTransitionProgress > 0.5f);
1055 }
1056
Adam Cohended9f8d2010-11-03 13:25:16 -07001057 protected void onWindowVisibilityChanged (int visibility) {
1058 mLauncher.onWindowVisibilityChanged(visibility);
1059 }
1060
Michael Jurka5f1c5092010-09-03 14:15:02 -07001061 @Override
1062 public boolean dispatchUnhandledMove(View focused, int direction) {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001063 if (workspaceInModalState() || !isFinishedSwitchingState()) {
Michael Jurka5f1c5092010-09-03 14:15:02 -07001064 // when the home screens are shrunken, shouldn't allow side-scrolling
1065 return false;
1066 }
1067 return super.dispatchUnhandledMove(focused, direction);
1068 }
1069
1070 @Override
1071 public boolean onInterceptTouchEvent(MotionEvent ev) {
Michael Jurkad771c962011-08-09 15:00:48 -07001072 switch (ev.getAction() & MotionEvent.ACTION_MASK) {
1073 case MotionEvent.ACTION_DOWN:
Adam Cohenf8d28232011-02-01 21:47:00 -08001074 mXDown = ev.getX();
1075 mYDown = ev.getY();
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001076 mTouchDownTime = System.currentTimeMillis();
Michael Jurkad771c962011-08-09 15:00:48 -07001077 break;
1078 case MotionEvent.ACTION_POINTER_UP:
1079 case MotionEvent.ACTION_UP:
1080 if (mTouchState == TOUCH_STATE_REST) {
1081 final CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage);
Adam Cohen83e59412014-09-17 16:46:55 -07001082 if (currentPage != null && !currentPage.lastDownOnOccupiedCell()) {
Michael Jurkad771c962011-08-09 15:00:48 -07001083 onWallpaperTap(ev);
1084 }
1085 }
Adam Cohenf8d28232011-02-01 21:47:00 -08001086 }
Michael Jurka5f1c5092010-09-03 14:15:02 -07001087 return super.onInterceptTouchEvent(ev);
1088 }
1089
Jan-Willem Maarsed3fbe682014-08-19 15:27:48 -07001090 @Override
1091 public boolean onGenericMotionEvent(MotionEvent event) {
1092 // Ignore pointer scroll events if the custom content doesn't allow scrolling.
1093 if ((getScreenIdForPageIndex(getCurrentPage()) == CUSTOM_CONTENT_SCREEN_ID)
1094 && (mCustomContentCallbacks != null)
1095 && !mCustomContentCallbacks.isScrollingAllowed()) {
1096 return false;
1097 }
1098 return super.onGenericMotionEvent(event);
1099 }
1100
Adam Cohen3d509322012-06-06 14:10:04 -07001101 protected void reinflateWidgetsIfNecessary() {
1102 final int clCount = getChildCount();
1103 for (int i = 0; i < clCount; i++) {
1104 CellLayout cl = (CellLayout) getChildAt(i);
1105 ShortcutAndWidgetContainer swc = cl.getShortcutsAndWidgets();
1106 final int itemCount = swc.getChildCount();
1107 for (int j = 0; j < itemCount; j++) {
1108 View v = swc.getChildAt(j);
1109
Sunny Goyalff572272014-07-23 13:58:07 -07001110 if (v != null && v.getTag() instanceof LauncherAppWidgetInfo) {
Adam Cohen3d509322012-06-06 14:10:04 -07001111 LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
1112 LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) info.hostView;
Sunny Goyalff572272014-07-23 13:58:07 -07001113 if (lahv != null && lahv.isReinflateRequired()) {
Adam Cohen3d509322012-06-06 14:10:04 -07001114 mLauncher.removeAppWidget(info);
1115 // Remove the current widget which is inflated with the wrong orientation
1116 cl.removeView(lahv);
1117 mLauncher.bindAppWidget(info);
1118 }
1119 }
1120 }
1121 }
1122 }
1123
Michael Jurka1adf5392010-10-18 18:10:22 -07001124 @Override
1125 protected void determineScrollingStart(MotionEvent ev) {
Winson Chung70442722012-02-10 15:43:22 -08001126 if (!isFinishedSwitchingState()) return;
Adam Cohenf8d28232011-02-01 21:47:00 -08001127
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001128 float deltaX = ev.getX() - mXDown;
1129 float absDeltaX = Math.abs(deltaX);
1130 float absDeltaY = Math.abs(ev.getY() - mYDown);
Adam Cohenf8d28232011-02-01 21:47:00 -08001131
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001132 if (Float.compare(absDeltaX, 0f) == 0) return;
Adam Cohenf8d28232011-02-01 21:47:00 -08001133
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001134 float slope = absDeltaY / absDeltaX;
Winson Chung70442722012-02-10 15:43:22 -08001135 float theta = (float) Math.atan(slope);
Adam Cohenf8d28232011-02-01 21:47:00 -08001136
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001137 if (absDeltaX > mTouchSlop || absDeltaY > mTouchSlop) {
Winson Chung70442722012-02-10 15:43:22 -08001138 cancelCurrentPageLongPress();
1139 }
1140
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001141 boolean passRightSwipesToCustomContent =
1142 (mTouchDownTime - mCustomContentShowTime) > CUSTOM_CONTENT_GESTURE_DELAY;
1143
Adam Cohenaf9b0e52013-09-23 19:27:38 -07001144 boolean swipeInIgnoreDirection = isLayoutRtl() ? deltaX < 0 : deltaX > 0;
Jan-Willem Maarse2ff91c42014-07-10 15:58:12 -07001145 boolean onCustomContentScreen =
1146 getScreenIdForPageIndex(getCurrentPage()) == CUSTOM_CONTENT_SCREEN_ID;
1147 if (swipeInIgnoreDirection && onCustomContentScreen && passRightSwipesToCustomContent) {
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001148 // Pass swipes to the right to the custom content page.
1149 return;
1150 }
1151
Jan-Willem Maarse2ff91c42014-07-10 15:58:12 -07001152 if (onCustomContentScreen && (mCustomContentCallbacks != null)
1153 && !mCustomContentCallbacks.isScrollingAllowed()) {
1154 // Don't allow workspace scrolling if the current custom content screen doesn't allow
1155 // scrolling.
1156 return;
1157 }
1158
Winson Chung70442722012-02-10 15:43:22 -08001159 if (theta > MAX_SWIPE_ANGLE) {
1160 // Above MAX_SWIPE_ANGLE, we don't want to ever start scrolling the workspace
1161 return;
1162 } else if (theta > START_DAMPING_TOUCH_SLOP_ANGLE) {
1163 // Above START_DAMPING_TOUCH_SLOP_ANGLE and below MAX_SWIPE_ANGLE, we want to
1164 // increase the touch slop to make it harder to begin scrolling the workspace. This
1165 // results in vertically scrolling widgets to more easily. The higher the angle, the
1166 // more we increase touch slop.
1167 theta -= START_DAMPING_TOUCH_SLOP_ANGLE;
1168 float extraRatio = (float)
1169 Math.sqrt((theta / (MAX_SWIPE_ANGLE - START_DAMPING_TOUCH_SLOP_ANGLE)));
1170 super.determineScrollingStart(ev, 1 + TOUCH_SLOP_DAMPING_FACTOR * extraRatio);
1171 } else {
1172 // Below START_DAMPING_TOUCH_SLOP_ANGLE, we don't do anything special
1173 super.determineScrollingStart(ev);
Adam Cohenf8d28232011-02-01 21:47:00 -08001174 }
Michael Jurka1adf5392010-10-18 18:10:22 -07001175 }
1176
Patrick Dubroy1262e362010-10-06 15:49:50 -07001177 protected void onPageBeginMoving() {
Winson Chung007c6982011-06-14 13:27:53 -07001178 super.onPageBeginMoving();
1179
Michael Jurkad74c9842011-07-10 12:44:21 -07001180 if (isHardwareAccelerated()) {
Michael Jurka3a0469d2012-06-21 09:38:41 -07001181 updateChildrenLayersEnabled(false);
Michael Jurka0142d492010-08-25 17:46:15 -07001182 } else {
Michael Jurkad74c9842011-07-10 12:44:21 -07001183 if (mNextPage != INVALID_PAGE) {
1184 // we're snapping to a particular screen
1185 enableChildrenCache(mCurrentPage, mNextPage);
1186 } else {
1187 // this is when user is actively dragging a particular screen, they might
1188 // swipe it either left or right (but we won't advance by more than one screen)
1189 enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1);
1190 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001191 }
1192 }
1193
Patrick Dubroy1262e362010-10-06 15:49:50 -07001194 protected void onPageEndMoving() {
Winson Chung007c6982011-06-14 13:27:53 -07001195 super.onPageEndMoving();
1196
Michael Jurkad74c9842011-07-10 12:44:21 -07001197 if (isHardwareAccelerated()) {
Michael Jurka3a0469d2012-06-21 09:38:41 -07001198 updateChildrenLayersEnabled(false);
Michael Jurkad74c9842011-07-10 12:44:21 -07001199 } else {
1200 clearChildrenCache();
1201 }
1202
Winson Chung3bc21c32012-01-20 13:59:18 -08001203 if (mDragController.isDragging()) {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001204 if (workspaceInModalState()) {
Winson Chung3bc21c32012-01-20 13:59:18 -08001205 // If we are in springloaded mode, then force an event to check if the current touch
1206 // is under a new page (to scroll to)
Winson Chung25460a12013-04-01 18:21:28 -07001207 mDragController.forceTouchMove();
Winson Chung3bc21c32012-01-20 13:59:18 -08001208 }
Patrick Dubroy1262e362010-10-06 15:49:50 -07001209 }
Adam Cohen26976d92011-03-22 15:33:33 -07001210
1211 if (mDelayedResizeRunnable != null) {
1212 mDelayedResizeRunnable.run();
1213 mDelayedResizeRunnable = null;
1214 }
Winson Chungf0c6ae02012-03-21 16:10:31 -07001215
1216 if (mDelayedSnapToPageRunnable != null) {
1217 mDelayedSnapToPageRunnable.run();
1218 mDelayedSnapToPageRunnable = null;
1219 }
Adam Cohenaccfd562013-07-12 14:40:40 -07001220 if (mStripScreensOnPageStopMoving) {
1221 stripEmptyScreens();
1222 mStripScreensOnPageStopMoving = false;
1223 }
Adam Cohenc2d6e892014-10-16 09:49:24 -07001224
1225 if (mShouldSendPageSettled) {
1226 mLauncherOverlay.onScrollSettled();
1227 mShouldSendPageSettled = false;
1228 }
1229 }
1230
1231 protected void onScrollInteractionBegin() {
1232 super.onScrollInteractionEnd();
1233 mScrollInteractionBegan = true;
1234 }
1235
1236 protected void onScrollInteractionEnd() {
1237 super.onScrollInteractionEnd();
1238 mScrollInteractionBegan = false;
1239 if (mStartedSendingScrollEvents) {
1240 mStartedSendingScrollEvents = false;
1241 mLauncherOverlay.onScrollInteractionEnd();
1242 }
1243 }
1244
1245 public void setLauncherOverlay(LauncherOverlay overlay) {
1246 mLauncherOverlay = overlay;
1247 }
1248
1249 @Override
1250 protected void overScroll(float amount) {
1251 boolean isRtl = isLayoutRtl();
1252 boolean shouldOverScroll = (amount <= 0 && (!hasCustomContent() || isRtl)) ||
1253 (amount >= 0 && (!hasCustomContent() || !isRtl));
1254
Adam Cohen8c4ca922014-10-24 17:40:34 -07001255 boolean shouldScrollOverlay = mLauncherOverlay != null &&
1256 ((amount <= 0 && !isRtl) || (amount >= 0 && isRtl));
1257
1258 boolean shouldZeroOverlay = mLauncherOverlay != null && mLastOverlaySroll != 0 &&
1259 ((amount >= 0 && !isRtl) || (amount <= 0 && isRtl));
Adam Cohenc2d6e892014-10-16 09:49:24 -07001260
1261 if (shouldScrollOverlay) {
1262 if (!mStartedSendingScrollEvents && mScrollInteractionBegan) {
1263 mStartedSendingScrollEvents = true;
1264 mLauncherOverlay.onScrollInteractionBegin();
1265 mShouldSendPageSettled = true;
1266 }
1267 int screenSize = getViewportWidth();
1268 float f = (amount / screenSize);
1269
1270 int progress = (int) Math.abs((f * 100));
1271
Adam Cohen8c4ca922014-10-24 17:40:34 -07001272 mLastOverlaySroll = progress;
Adam Cohenc2d6e892014-10-16 09:49:24 -07001273 mLauncherOverlay.onScrollChange(progress, isRtl);
1274 } else if (shouldOverScroll) {
1275 dampedOverScroll(amount);
1276 mOverScrollEffect = acceleratedOverFactor(amount);
1277 } else {
1278 mOverScrollEffect = 0;
1279 }
Adam Cohen8c4ca922014-10-24 17:40:34 -07001280
1281 if (shouldZeroOverlay) {
1282 mLauncherOverlay.onScrollChange(0, isRtl);
1283 }
Adam Cohen89cbbb92013-09-25 16:52:48 -07001284 }
1285
1286 @Override
1287 protected void notifyPageSwitchListener() {
1288 super.notifyPageSwitchListener();
Michael Jurka0142d492010-08-25 17:46:15 -07001289
Adam Cohen6ad0e7d2013-07-24 13:55:41 -07001290 if (hasCustomContent() && getNextPage() == 0 && !mCustomContentShowing) {
Adam Cohenbffe7452013-07-22 18:21:45 -07001291 mCustomContentShowing = true;
1292 if (mCustomContentCallbacks != null) {
Selim Cinek3a8a8f72014-01-16 10:38:38 -08001293 mCustomContentCallbacks.onShow(false);
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001294 mCustomContentShowTime = System.currentTimeMillis();
Adam Cohenbffe7452013-07-22 18:21:45 -07001295 }
Adam Cohen6ad0e7d2013-07-24 13:55:41 -07001296 } else if (hasCustomContent() && getNextPage() != 0 && mCustomContentShowing) {
Adam Cohenbffe7452013-07-22 18:21:45 -07001297 mCustomContentShowing = false;
1298 if (mCustomContentCallbacks != null) {
1299 mCustomContentCallbacks.onHide();
Adam Cohenbffe7452013-07-22 18:21:45 -07001300 }
1301 }
Chris Craik01f2d7f2013-10-01 14:41:56 -07001302 }
Michael Jurka0142d492010-08-25 17:46:15 -07001303
Adam Cohen6fecd412013-10-02 17:41:50 -07001304 protected CustomContentCallbacks getCustomContentCallbacks() {
1305 return mCustomContentCallbacks;
1306 }
1307
Adam Cohen824fcb32014-05-21 23:01:25 +00001308 protected void setWallpaperDimension() {
Michael Jurkaa6a05472013-11-13 17:59:46 +01001309 new AsyncTask<Void, Void, Void>() {
1310 public Void doInBackground(Void ... args) {
1311 String spKey = WallpaperCropActivity.getSharedPreferencesKey();
1312 SharedPreferences sp =
1313 mLauncher.getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
Adam Cohenc29217b2014-05-18 03:17:15 +00001314 LauncherWallpaperPickerActivity.suggestWallpaperDimension(mLauncher.getResources(),
Adam Cohenea90f832014-05-21 15:03:34 -07001315 sp, mLauncher.getWindowManager(), mWallpaperManager,
1316 mLauncher.overrideWallpaperDimensions());
Michael Jurkaa6a05472013-11-13 17:59:46 +01001317 return null;
1318 }
1319 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
Michael Jurkac5b262c2011-01-12 20:24:50 -08001320 }
1321
Winson Chungf0c6ae02012-03-21 16:10:31 -07001322 protected void snapToPage(int whichPage, Runnable r) {
Adam Cohenad4e15c2013-10-17 16:21:35 -07001323 snapToPage(whichPage, SLOW_PAGE_SNAP_ANIMATION_DURATION, r);
1324 }
1325
1326 protected void snapToPage(int whichPage, int duration, Runnable r) {
Winson Chungf0c6ae02012-03-21 16:10:31 -07001327 if (mDelayedSnapToPageRunnable != null) {
1328 mDelayedSnapToPageRunnable.run();
1329 }
1330 mDelayedSnapToPageRunnable = r;
Adam Cohenad4e15c2013-10-17 16:21:35 -07001331 snapToPage(whichPage, duration);
Winson Chungf0c6ae02012-03-21 16:10:31 -07001332 }
1333
Adam Cohendb364c32014-05-20 14:23:40 -07001334 public void snapToScreenId(long screenId) {
1335 snapToScreenId(screenId, null);
1336 }
1337
Adam Cohendcd297f2013-06-18 13:13:40 -07001338 protected void snapToScreenId(long screenId, Runnable r) {
1339 snapToPage(getPageIndexForScreenId(screenId), r);
1340 }
1341
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001342 class WallpaperOffsetInterpolator implements Choreographer.FrameCallback {
1343 float mFinalOffset = 0.0f;
Michael Jurka046cf342013-09-14 16:40:34 +02001344 float mCurrentOffset = 0.5f; // to force an initial update
Michael Jurkafe09cb72013-08-27 15:48:58 +02001345 boolean mWaitingForUpdate;
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001346 Choreographer mChoreographer;
1347 Interpolator mInterpolator;
1348 boolean mAnimating;
1349 long mAnimationStartTime;
1350 float mAnimationStartOffset;
Michael Jurka2f817312013-09-20 03:03:42 +02001351 private final int ANIMATION_DURATION = 250;
1352 // Don't use all the wallpaper for parallax until you have at least this many pages
Michael Jurkafe0ace32013-10-03 01:05:14 -07001353 private final int MIN_PARALLAX_PAGE_SPAN = 3;
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001354 int mNumScreens;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001355
1356 public WallpaperOffsetInterpolator() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001357 mChoreographer = Choreographer.getInstance();
1358 mInterpolator = new DecelerateInterpolator(1.5f);
Michael Jurkaab1983f2011-01-18 15:50:17 -08001359 }
1360
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001361 @Override
1362 public void doFrame(long frameTimeNanos) {
Michael Jurkafe09cb72013-08-27 15:48:58 +02001363 updateOffset(false);
1364 }
1365
Michael Jurkafe09cb72013-08-27 15:48:58 +02001366 private void updateOffset(boolean force) {
1367 if (mWaitingForUpdate || force) {
1368 mWaitingForUpdate = false;
Michael Jurka1bd90b02013-09-05 23:10:14 +02001369 if (computeScrollOffset() && mWindowToken != null) {
1370 try {
1371 mWallpaperManager.setWallpaperOffsets(mWindowToken,
1372 mWallpaperOffset.getCurrX(), 0.5f);
1373 setWallpaperOffsetSteps();
1374 } catch (IllegalArgumentException e) {
1375 Log.e(TAG, "Error updating wallpaper offset: " + e);
1376 }
Michael Jurkafe09cb72013-08-27 15:48:58 +02001377 }
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001378 }
Michael Jurkaca5b8362011-01-27 13:23:26 -08001379 }
1380
Michael Jurkaab1983f2011-01-18 15:50:17 -08001381 public boolean computeScrollOffset() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001382 final float oldOffset = mCurrentOffset;
1383 if (mAnimating) {
1384 long durationSinceAnimation = System.currentTimeMillis() - mAnimationStartTime;
1385 float t0 = durationSinceAnimation / (float) ANIMATION_DURATION;
1386 float t1 = mInterpolator.getInterpolation(t0);
1387 mCurrentOffset = mAnimationStartOffset +
1388 (mFinalOffset - mAnimationStartOffset) * t1;
1389 mAnimating = durationSinceAnimation < ANIMATION_DURATION;
Michael Jurkaca5b8362011-01-27 13:23:26 -08001390 } else {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001391 mCurrentOffset = mFinalOffset;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001392 }
Michael Jurkaca5b8362011-01-27 13:23:26 -08001393
Michael Jurkafe09cb72013-08-27 15:48:58 +02001394 if (Math.abs(mCurrentOffset - mFinalOffset) > 0.0000001f) {
1395 scheduleUpdate();
1396 }
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001397 if (Math.abs(oldOffset - mCurrentOffset) > 0.0000001f) {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001398 return true;
1399 }
1400 return false;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001401 }
1402
Michael Jurkafe09cb72013-08-27 15:48:58 +02001403 private float wallpaperOffsetForCurrentScroll() {
1404 if (getChildCount() <= 1) {
1405 return 0;
1406 }
Michael Jurka2f817312013-09-20 03:03:42 +02001407
1408 // Exclude the leftmost page
Michael Jurkafe0ace32013-10-03 01:05:14 -07001409 int emptyExtraPages = numEmptyScreensToIgnore();
1410 int firstIndex = numCustomPages();
Michael Jurka2f817312013-09-20 03:03:42 +02001411 // Exclude the last extra empty screen (if we have > MIN_PARALLAX_PAGE_SPAN pages)
Michael Jurkafe0ace32013-10-03 01:05:14 -07001412 int lastIndex = getChildCount() - 1 - emptyExtraPages;
1413 if (isLayoutRtl()) {
1414 int temp = firstIndex;
1415 firstIndex = lastIndex;
1416 lastIndex = temp;
1417 }
Michael Jurka2f817312013-09-20 03:03:42 +02001418
Michael Jurkafe09cb72013-08-27 15:48:58 +02001419 int firstPageScrollX = getScrollForPage(firstIndex);
1420 int scrollRange = getScrollForPage(lastIndex) - firstPageScrollX;
1421 if (scrollRange == 0) {
1422 return 0;
1423 } else {
Michael Jurka2f817312013-09-20 03:03:42 +02001424 // TODO: do different behavior if it's a live wallpaper?
Michael Jurkafe0ace32013-10-03 01:05:14 -07001425 // Sometimes the left parameter of the pages is animated during a layout transition;
1426 // this parameter offsets it to keep the wallpaper from animating as well
Michael Jurkafe0ace32013-10-03 01:05:14 -07001427 int adjustedScroll =
Michael Jurka8fd3adc2013-10-16 13:50:24 -07001428 getScrollX() - firstPageScrollX - getLayoutTransitionOffsetForPage(0);
Michael Jurkafe0ace32013-10-03 01:05:14 -07001429 float offset = Math.min(1, adjustedScroll / (float) scrollRange);
Michael Jurka2f817312013-09-20 03:03:42 +02001430 offset = Math.max(0, offset);
1431 // Don't use up all the wallpaper parallax until you have at least
1432 // MIN_PARALLAX_PAGE_SPAN pages
Michael Jurkafe0ace32013-10-03 01:05:14 -07001433 int numScrollingPages = getNumScreensExcludingEmptyAndCustom();
Michael Jurka2a4f4922014-01-29 16:32:39 +01001434 int parallaxPageSpan;
1435 if (mWallpaperIsLiveWallpaper) {
1436 parallaxPageSpan = numScrollingPages - 1;
1437 } else {
1438 parallaxPageSpan = Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages - 1);
1439 }
1440 mNumPagesForWallpaperParallax = parallaxPageSpan;
1441
Michael Jurkafe0ace32013-10-03 01:05:14 -07001442 // On RTL devices, push the wallpaper offset to the right if we don't have enough
1443 // pages (ie if numScrollingPages < MIN_PARALLAX_PAGE_SPAN)
1444 int padding = isLayoutRtl() ? parallaxPageSpan - numScrollingPages + 1 : 0;
1445 return offset * (padding + numScrollingPages - 1) / parallaxPageSpan;
Michael Jurkafe09cb72013-08-27 15:48:58 +02001446 }
1447 }
1448
Michael Jurkafe0ace32013-10-03 01:05:14 -07001449 private int numEmptyScreensToIgnore() {
1450 int numScrollingPages = getChildCount() - numCustomPages();
1451 if (numScrollingPages >= MIN_PARALLAX_PAGE_SPAN && hasExtraEmptyScreen()) {
Michael Jurka2f817312013-09-20 03:03:42 +02001452 return 1;
1453 } else {
1454 return 0;
1455 }
1456 }
1457
Michael Jurkafe0ace32013-10-03 01:05:14 -07001458 private int getNumScreensExcludingEmptyAndCustom() {
1459 int numScrollingPages = getChildCount() - numEmptyScreensToIgnore() - numCustomPages();
Michael Jurka2f817312013-09-20 03:03:42 +02001460 return numScrollingPages;
1461 }
1462
Michael Jurkafe09cb72013-08-27 15:48:58 +02001463 public void syncWithScroll() {
1464 float offset = wallpaperOffsetForCurrentScroll();
1465 mWallpaperOffset.setFinalX(offset);
1466 updateOffset(true);
1467 }
1468
Michael Jurkaab1983f2011-01-18 15:50:17 -08001469 public float getCurrX() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001470 return mCurrentOffset;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001471 }
1472
1473 public float getFinalX() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001474 return mFinalOffset;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001475 }
1476
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001477 private void animateToFinal() {
1478 mAnimating = true;
1479 mAnimationStartOffset = mCurrentOffset;
1480 mAnimationStartTime = System.currentTimeMillis();
Michael Jurkaab1983f2011-01-18 15:50:17 -08001481 }
1482
Michael Jurkafe09cb72013-08-27 15:48:58 +02001483 private void setWallpaperOffsetSteps() {
1484 // Set wallpaper offset steps (1 / (number of screens - 1))
Michael Jurka2a4f4922014-01-29 16:32:39 +01001485 float xOffset = 1.0f / mNumPagesForWallpaperParallax;
1486 if (xOffset != mLastSetWallpaperOffsetSteps) {
1487 mWallpaperManager.setWallpaperOffsetSteps(xOffset, 1.0f);
1488 mLastSetWallpaperOffsetSteps = xOffset;
1489 }
Michael Jurkafe09cb72013-08-27 15:48:58 +02001490 }
1491
Michael Jurkaab1983f2011-01-18 15:50:17 -08001492 public void setFinalX(float x) {
Michael Jurkafe09cb72013-08-27 15:48:58 +02001493 scheduleUpdate();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001494 mFinalOffset = Math.max(0f, Math.min(x, 1.0f));
Michael Jurkafe0ace32013-10-03 01:05:14 -07001495 if (getNumScreensExcludingEmptyAndCustom() != mNumScreens) {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001496 if (mNumScreens > 0) {
1497 // Don't animate if we're going from 0 screens
1498 animateToFinal();
1499 }
Michael Jurkafe0ace32013-10-03 01:05:14 -07001500 mNumScreens = getNumScreensExcludingEmptyAndCustom();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001501 }
Michael Jurkaab1983f2011-01-18 15:50:17 -08001502 }
1503
Michael Jurkafe09cb72013-08-27 15:48:58 +02001504 private void scheduleUpdate() {
1505 if (!mWaitingForUpdate) {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001506 mChoreographer.postFrameCallback(this);
Michael Jurkafe09cb72013-08-27 15:48:58 +02001507 mWaitingForUpdate = true;
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001508 }
Michael Jurkaab1983f2011-01-18 15:50:17 -08001509 }
1510
1511 public void jumpToFinal() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001512 mCurrentOffset = mFinalOffset;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001513 }
Dianne Hackborn8f573952009-08-10 23:21:09 -07001514 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001515
Michael Jurka340c5f32010-10-21 16:49:19 -07001516 @Override
1517 public void computeScroll() {
1518 super.computeScroll();
Michael Jurkafe09cb72013-08-27 15:48:58 +02001519 mWallpaperOffset.syncWithScroll();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001520 }
1521
Jason Monked05f092014-04-24 10:13:05 -04001522 @Override
1523 public void announceForAccessibility(CharSequence text) {
1524 // Don't announce if apps is on top of us.
1525 if (!mLauncher.isAllAppsVisible()) {
1526 super.announceForAccessibility(text);
1527 }
1528 }
1529
Patrick Dubroy94f78a52011-02-28 17:39:16 -08001530 void showOutlines() {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001531 if (!workspaceInModalState() && !mIsSwitchingState) {
Winson Chung9171e6d2010-11-17 17:39:27 -08001532 if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
1533 if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
Michael Jurka2ecf9952012-06-18 12:52:28 -07001534 mChildrenOutlineFadeInAnimation = LauncherAnimUtils.ofFloat(this, "childrenOutlineAlpha", 1.0f);
Winson Chung9171e6d2010-11-17 17:39:27 -08001535 mChildrenOutlineFadeInAnimation.setDuration(CHILDREN_OUTLINE_FADE_IN_DURATION);
1536 mChildrenOutlineFadeInAnimation.start();
Michael Jurka3e7c7632010-10-02 16:01:03 -07001537 }
Adam Cohenf34bab52010-09-30 14:11:56 -07001538 }
1539
Patrick Dubroy94f78a52011-02-28 17:39:16 -08001540 void hideOutlines() {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001541 if (!workspaceInModalState() && !mIsSwitchingState) {
Winson Chung9171e6d2010-11-17 17:39:27 -08001542 if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
1543 if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
Michael Jurka2ecf9952012-06-18 12:52:28 -07001544 mChildrenOutlineFadeOutAnimation = LauncherAnimUtils.ofFloat(this, "childrenOutlineAlpha", 0.0f);
Winson Chung9171e6d2010-11-17 17:39:27 -08001545 mChildrenOutlineFadeOutAnimation.setDuration(CHILDREN_OUTLINE_FADE_OUT_DURATION);
1546 mChildrenOutlineFadeOutAnimation.setStartDelay(CHILDREN_OUTLINE_FADE_OUT_DELAY);
1547 mChildrenOutlineFadeOutAnimation.start();
Michael Jurka3e7c7632010-10-02 16:01:03 -07001548 }
Adam Cohenf34bab52010-09-30 14:11:56 -07001549 }
1550
Patrick Dubroy94f78a52011-02-28 17:39:16 -08001551 public void showOutlinesTemporarily() {
1552 if (!mIsPageMoving && !isTouchActive()) {
1553 snapToPage(mCurrentPage);
1554 }
1555 }
1556
Winson Chung9171e6d2010-11-17 17:39:27 -08001557 public void setChildrenOutlineAlpha(float alpha) {
1558 mChildrenOutlineAlpha = alpha;
Adam Cohenf34bab52010-09-30 14:11:56 -07001559 for (int i = 0; i < getChildCount(); i++) {
1560 CellLayout cl = (CellLayout) getChildAt(i);
1561 cl.setBackgroundAlpha(alpha);
1562 }
1563 }
1564
Winson Chung9171e6d2010-11-17 17:39:27 -08001565 public float getChildrenOutlineAlpha() {
1566 return mChildrenOutlineAlpha;
1567 }
1568
Michael Jurka7bdb25a2011-08-03 15:16:44 -07001569 private void animateBackgroundGradient(float finalAlpha, boolean animated) {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001570 final DragLayer dragLayer = mLauncher.getDragLayer();
1571
Michael Jurkab9e14972011-07-25 17:57:40 -07001572 if (mBackgroundFadeInAnimation != null) {
1573 mBackgroundFadeInAnimation.cancel();
1574 mBackgroundFadeInAnimation = null;
1575 }
1576 if (mBackgroundFadeOutAnimation != null) {
1577 mBackgroundFadeOutAnimation.cancel();
1578 mBackgroundFadeOutAnimation = null;
1579 }
Adam Cohen6c5891a2014-07-09 23:53:15 -07001580 float startAlpha = dragLayer.getBackgroundAlpha();
Michael Jurka7bdb25a2011-08-03 15:16:44 -07001581 if (finalAlpha != startAlpha) {
1582 if (animated) {
Michael Jurkaf1ad6082013-03-13 12:55:46 +01001583 mBackgroundFadeOutAnimation =
1584 LauncherAnimUtils.ofFloat(this, startAlpha, finalAlpha);
Michael Jurka7bdb25a2011-08-03 15:16:44 -07001585 mBackgroundFadeOutAnimation.addUpdateListener(new AnimatorUpdateListener() {
1586 public void onAnimationUpdate(ValueAnimator animation) {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001587 dragLayer.setBackgroundAlpha(
1588 ((Float)animation.getAnimatedValue()).floatValue());
Michael Jurka7bdb25a2011-08-03 15:16:44 -07001589 }
1590 });
1591 mBackgroundFadeOutAnimation.setInterpolator(new DecelerateInterpolator(1.5f));
1592 mBackgroundFadeOutAnimation.setDuration(BACKGROUND_FADE_OUT_DURATION);
1593 mBackgroundFadeOutAnimation.start();
1594 } else {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001595 dragLayer.setBackgroundAlpha(finalAlpha);
Michael Jurka7bdb25a2011-08-03 15:16:44 -07001596 }
Michael Jurkab9e14972011-07-25 17:57:40 -07001597 }
Winson Chung9171e6d2010-11-17 17:39:27 -08001598 }
1599
Adam Cohen68d73932010-11-15 10:50:58 -08001600 float backgroundAlphaInterpolator(float r) {
1601 float pivotA = 0.1f;
1602 float pivotB = 0.4f;
1603 if (r < pivotA) {
1604 return 0;
1605 } else if (r > pivotB) {
1606 return 1.0f;
1607 } else {
1608 return (r - pivotA)/(pivotB - pivotA);
1609 }
1610 }
1611
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001612 private void updatePageAlphaValues(int screenCenter) {
Michael Jurka869390b2012-05-06 15:55:19 -07001613 boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
1614 if (mWorkspaceFadeInAdjacentScreens &&
Adam Cohenc50438c2014-08-19 17:43:05 -07001615 !workspaceInModalState() &&
Michael Jurka869390b2012-05-06 15:55:19 -07001616 !mIsSwitchingState &&
1617 !isInOverscroll) {
Adam Cohen84a465a2013-11-11 18:49:56 +00001618 for (int i = numCustomPages(); i < getChildCount(); i++) {
Michael Jurka869390b2012-05-06 15:55:19 -07001619 CellLayout child = (CellLayout) getChildAt(i);
1620 if (child != null) {
1621 float scrollProgress = getScrollProgress(screenCenter, child, i);
Adam Cohen73894962011-10-31 13:17:17 -07001622 float alpha = 1 - Math.abs(scrollProgress);
Michael Jurka869390b2012-05-06 15:55:19 -07001623 child.getShortcutsAndWidgets().setAlpha(alpha);
Adam Cohenc50438c2014-08-19 17:43:05 -07001624 //child.setBackgroundAlphaMultiplier(1 - alpha);
Adam Cohen73894962011-10-31 13:17:17 -07001625 }
Adam Cohenf34bab52010-09-30 14:11:56 -07001626 }
1627 }
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001628 }
1629
1630 private void setChildrenBackgroundAlphaMultipliers(float a) {
1631 for (int i = 0; i < getChildCount(); i++) {
1632 CellLayout child = (CellLayout) getChildAt(i);
1633 child.setBackgroundAlphaMultiplier(a);
1634 }
1635 }
1636
Winson Chung98ca0f72013-07-29 12:58:51 -07001637 public boolean hasCustomContent() {
Adam Cohenedb40762013-07-18 16:45:45 -07001638 return (mScreenOrder.size() > 0 && mScreenOrder.get(0) == CUSTOM_CONTENT_SCREEN_ID);
1639 }
1640
Michael Jurkafe0ace32013-10-03 01:05:14 -07001641 public int numCustomPages() {
1642 return hasCustomContent() ? 1 : 0;
1643 }
1644
Adam Cohenbffe7452013-07-22 18:21:45 -07001645 public boolean isOnOrMovingToCustomContent() {
1646 return hasCustomContent() && getNextPage() == 0;
1647 }
1648
Adam Cohenedb40762013-07-18 16:45:45 -07001649 private void updateStateForCustomContent(int screenCenter) {
Dave Hawkeya8881582013-09-17 15:55:33 -06001650 float translationX = 0;
1651 float progress = 0;
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01001652 if (hasCustomContent()) {
Adam Cohenedb40762013-07-18 16:45:45 -07001653 int index = mScreenOrder.indexOf(CUSTOM_CONTENT_SCREEN_ID);
Adam Cohen564a2e72013-10-09 14:47:32 -07001654
Adam Cohena45de072013-10-11 16:07:47 -07001655 int scrollDelta = getScrollX() - getScrollForPage(index) -
1656 getLayoutTransitionOffsetForPage(index);
1657 float scrollRange = getScrollForPage(index + 1) - getScrollForPage(index);
1658 translationX = scrollRange - scrollDelta;
1659 progress = (scrollRange - scrollDelta) / scrollRange;
Adam Cohenaf9b0e52013-09-23 19:27:38 -07001660
1661 if (isLayoutRtl()) {
1662 translationX = Math.min(0, translationX);
1663 } else {
1664 translationX = Math.max(0, translationX);
1665 }
Adam Cohen955806d2013-07-19 16:36:43 -07001666 progress = Math.max(0, progress);
Dave Hawkeya8881582013-09-17 15:55:33 -06001667 }
Adam Cohenedb40762013-07-18 16:45:45 -07001668
Dave Hawkeya8881582013-09-17 15:55:33 -06001669 if (Float.compare(progress, mLastCustomContentScrollProgress) == 0) return;
Adam Cohen84add1d2013-10-14 16:29:02 -07001670
1671 CellLayout cc = mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID);
Adam Cohen6c5891a2014-07-09 23:53:15 -07001672 if (progress > 0 && cc.getVisibility() != VISIBLE && !workspaceInModalState()) {
Adam Cohen84add1d2013-10-14 16:29:02 -07001673 cc.setVisibility(VISIBLE);
1674 }
1675
Dave Hawkeya8881582013-09-17 15:55:33 -06001676 mLastCustomContentScrollProgress = progress;
Adam Cohenc36fa5c2013-08-29 11:54:42 -07001677
Adam Cohen6c5891a2014-07-09 23:53:15 -07001678 mLauncher.getDragLayer().setBackgroundAlpha(progress * 0.8f);
Adam Cohen22cba7f2013-07-19 16:14:00 -07001679
Dave Hawkeya8881582013-09-17 15:55:33 -06001680 if (mLauncher.getHotseat() != null) {
1681 mLauncher.getHotseat().setTranslationX(translationX);
1682 }
Adam Cohenc36fa5c2013-08-29 11:54:42 -07001683
Dave Hawkeya8881582013-09-17 15:55:33 -06001684 if (getPageIndicator() != null) {
1685 getPageIndicator().setTranslationX(translationX);
1686 }
Adam Cohenc36fa5c2013-08-29 11:54:42 -07001687
Dave Hawkeya8881582013-09-17 15:55:33 -06001688 if (mCustomContentCallbacks != null) {
1689 mCustomContentCallbacks.onScrollProgressChanged(progress);
Adam Cohenedb40762013-07-18 16:45:45 -07001690 }
1691 }
1692
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001693 @Override
Adam Cohen53805212013-10-01 10:39:23 -07001694 protected OnClickListener getPageIndicatorClickListener() {
1695 AccessibilityManager am = (AccessibilityManager)
1696 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
1697 if (!am.isTouchExplorationEnabled()) {
1698 return null;
1699 }
1700 OnClickListener listener = new OnClickListener() {
1701 @Override
1702 public void onClick(View arg0) {
1703 enterOverviewMode();
1704 }
1705 };
1706 return listener;
1707 }
1708
1709 @Override
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001710 protected void screenScrolled(int screenCenter) {
Winson Chung52aee602013-01-30 12:01:02 -08001711 final boolean isRtl = isLayoutRtl();
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001712 super.screenScrolled(screenCenter);
1713
1714 updatePageAlphaValues(screenCenter);
Adam Cohenedb40762013-07-18 16:45:45 -07001715 updateStateForCustomContent(screenCenter);
Michael Jurkad51f33a2012-06-28 15:35:26 -07001716 enableHwLayersOnVisiblePages();
Adam Cohenf34bab52010-09-30 14:11:56 -07001717
Adam Cohenc50438c2014-08-19 17:43:05 -07001718 boolean shouldOverScroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
Adam Cohenaf9b0e52013-09-23 19:27:38 -07001719
1720 if (shouldOverScroll) {
Winson Chung52aee602013-01-30 12:01:02 -08001721 int index = 0;
Winson Chung52aee602013-01-30 12:01:02 -08001722 final int lowerIndex = 0;
1723 final int upperIndex = getChildCount() - 1;
Adam Cohena29f5042013-09-26 14:29:35 -07001724
1725 final boolean isLeftPage = mOverScrollX < 0;
1726 index = (!isRtl && isLeftPage) || (isRtl && !isLeftPage) ? lowerIndex : upperIndex;
Winson Chung52aee602013-01-30 12:01:02 -08001727
Adam Cohenb5ba0972011-09-07 18:02:31 -07001728 CellLayout cl = (CellLayout) getChildAt(index);
Adam Cohen1e4359c2014-08-18 13:12:16 -07001729 float effect = Math.abs(mOverScrollEffect);
1730 cl.setOverScrollAmount(Math.abs(effect), isLeftPage);
Adam Cohena29f5042013-09-26 14:29:35 -07001731
Adam Cohen1e4359c2014-08-18 13:12:16 -07001732 mOverscrollEffectSet = true;
Adam Cohenb5ba0972011-09-07 18:02:31 -07001733 } else {
Adam Cohen1e4359c2014-08-18 13:12:16 -07001734 if (mOverscrollEffectSet && getChildCount() > 0) {
1735 mOverscrollEffectSet = false;
1736 ((CellLayout) getChildAt(0)).setOverScrollAmount(0, false);
1737 ((CellLayout) getChildAt(getChildCount() - 1)).setOverScrollAmount(0, false);
Adam Cohen7842d7f2011-09-12 15:28:15 -07001738 }
Adam Cohenb5ba0972011-09-07 18:02:31 -07001739 }
1740 }
1741
Joe Onorato00acb122009-08-04 16:04:30 -04001742 protected void onAttachedToWindow() {
1743 super.onAttachedToWindow();
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001744 mWindowToken = getWindowToken();
Joe Onorato956091b2010-02-19 12:47:40 -08001745 computeScroll();
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001746 mDragController.setWindowToken(mWindowToken);
1747 }
1748
1749 protected void onDetachedFromWindow() {
Romain Guye6661952013-08-08 19:13:22 -07001750 super.onDetachedFromWindow();
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001751 mWindowToken = null;
Joe Onorato00acb122009-08-04 16:04:30 -04001752 }
1753
Adam Cohen53805212013-10-01 10:39:23 -07001754 protected void onResume() {
1755 if (getPageIndicator() != null) {
1756 // In case accessibility state has changed, we need to perform this on every
1757 // attach to window
Adam Cohend36d9472013-10-10 15:32:41 -07001758 OnClickListener listener = getPageIndicatorClickListener();
1759 if (listener != null) {
1760 getPageIndicator().setOnClickListener(listener);
1761 }
Adam Cohen53805212013-10-01 10:39:23 -07001762 }
1763 AccessibilityManager am = (AccessibilityManager)
1764 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
1765 sAccessibilityEnabled = am.isEnabled();
Michael Jurkaa6a05472013-11-13 17:59:46 +01001766
1767 // Update wallpaper dimensions if they were changed since last onResume
1768 // (we also always set the wallpaper dimensions in the constructor)
1769 if (LauncherAppState.getInstance().hasWallpaperChangedSinceLastCheck()) {
Adam Cohen824fcb32014-05-21 23:01:25 +00001770 setWallpaperDimension();
Michael Jurkaa6a05472013-11-13 17:59:46 +01001771 }
Michael Jurka2a4f4922014-01-29 16:32:39 +01001772 mWallpaperIsLiveWallpaper = mWallpaperManager.getWallpaperInfo() != null;
1773 // Force the wallpaper offset steps to be set again, because another app might have changed
1774 // them
1775 mLastSetWallpaperOffsetSteps = 0f;
Adam Cohen53805212013-10-01 10:39:23 -07001776 }
1777
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001778 @Override
Michael Jurka28750fb2010-09-24 17:43:49 -07001779 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Michael Jurkac5b262c2011-01-12 20:24:50 -08001780 if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
Michael Jurkafe09cb72013-08-27 15:48:58 +02001781 mWallpaperOffset.syncWithScroll();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001782 mWallpaperOffset.jumpToFinal();
Michael Jurkac5b262c2011-01-12 20:24:50 -08001783 }
Michael Jurka0142d492010-08-25 17:46:15 -07001784 super.onLayout(changed, left, top, right, bottom);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001785 }
1786
1787 @Override
Winson Chung9171e6d2010-11-17 17:39:27 -08001788 protected void onDraw(Canvas canvas) {
Winson Chung9171e6d2010-11-17 17:39:27 -08001789 super.onDraw(canvas);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001790
1791 // Call back to LauncherModel to finish binding after the first draw
Romain Guyeeacd562012-10-10 18:47:33 -07001792 post(mBindPages);
Winson Chung9171e6d2010-11-17 17:39:27 -08001793 }
1794
Michael Jurkadfab7f02011-11-18 13:01:04 -08001795 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001796 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
Joe Onorato67886212009-09-14 19:05:05 -04001797 if (!mLauncher.isAllAppsVisible()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001798 final Folder openFolder = getOpenFolder();
1799 if (openFolder != null) {
1800 return openFolder.requestFocus(direction, previouslyFocusedRect);
1801 } else {
Michael Jurka0142d492010-08-25 17:46:15 -07001802 return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001803 }
1804 }
1805 return false;
1806 }
1807
1808 @Override
Winson Chung97d85d22011-04-13 11:27:36 -07001809 public int getDescendantFocusability() {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001810 if (workspaceInModalState()) {
Winson Chung97d85d22011-04-13 11:27:36 -07001811 return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
1812 }
1813 return super.getDescendantFocusability();
1814 }
1815
1816 @Override
Romain Guyc2e24c02009-06-01 16:11:41 -07001817 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
Joe Onorato67886212009-09-14 19:05:05 -04001818 if (!mLauncher.isAllAppsVisible()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001819 final Folder openFolder = getOpenFolder();
Michael Jurka0142d492010-08-25 17:46:15 -07001820 if (openFolder != null) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001821 openFolder.addFocusables(views, direction);
Michael Jurka0142d492010-08-25 17:46:15 -07001822 } else {
1823 super.addFocusables(views, direction, focusableMode);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001824 }
1825 }
1826 }
1827
Adam Cohen6c5891a2014-07-09 23:53:15 -07001828 public boolean workspaceInModalState() {
1829 return mState != State.NORMAL;
Michael Jurkad74c9842011-07-10 12:44:21 -07001830 }
1831
Michael Jurka0142d492010-08-25 17:46:15 -07001832 void enableChildrenCache(int fromPage, int toPage) {
1833 if (fromPage > toPage) {
1834 final int temp = fromPage;
1835 fromPage = toPage;
1836 toPage = temp;
Mike Cleron3a2b3f22009-11-05 17:17:42 -08001837 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001838
Michael Jurkadee05892010-07-27 10:01:56 -07001839 final int screenCount = getChildCount();
Adam Powellfea5d022010-04-29 11:42:45 -07001840
Michael Jurka0142d492010-08-25 17:46:15 -07001841 fromPage = Math.max(fromPage, 0);
1842 toPage = Math.min(toPage, screenCount - 1);
Adam Powellfea5d022010-04-29 11:42:45 -07001843
Michael Jurka0142d492010-08-25 17:46:15 -07001844 for (int i = fromPage; i <= toPage; i++) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001845 final CellLayout layout = (CellLayout) getChildAt(i);
1846 layout.setChildrenDrawnWithCacheEnabled(true);
1847 layout.setChildrenDrawingCacheEnabled(true);
1848 }
1849 }
1850
1851 void clearChildrenCache() {
Michael Jurkadee05892010-07-27 10:01:56 -07001852 final int screenCount = getChildCount();
1853 for (int i = 0; i < screenCount; i++) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001854 final CellLayout layout = (CellLayout) getChildAt(i);
1855 layout.setChildrenDrawnWithCacheEnabled(false);
Adam Cohen8182e5b2011-08-01 15:43:31 -07001856 // In software mode, we don't want the items to continue to be drawn into bitmaps
1857 if (!isHardwareAccelerated()) {
1858 layout.setChildrenDrawingCacheEnabled(false);
1859 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001860 }
1861 }
1862
Michael Jurka3a0469d2012-06-21 09:38:41 -07001863 private void updateChildrenLayersEnabled(boolean force) {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001864 boolean small = mState == State.OVERVIEW || mIsSwitchingState;
Michael Jurka3a0469d2012-06-21 09:38:41 -07001865 boolean enableChildrenLayers = force || small || mAnimatingViewIntoPlace || isPageMoving();
Michael Jurkad74c9842011-07-10 12:44:21 -07001866
1867 if (enableChildrenLayers != mChildrenLayersEnabled) {
1868 mChildrenLayersEnabled = enableChildrenLayers;
Michael Jurkad51f33a2012-06-28 15:35:26 -07001869 if (mChildrenLayersEnabled) {
1870 enableHwLayersOnVisiblePages();
1871 } else {
1872 for (int i = 0; i < getPageCount(); i++) {
1873 final CellLayout cl = (CellLayout) getChildAt(i);
Chris Craik01f2d7f2013-10-01 14:41:56 -07001874 cl.enableHardwareLayer(false);
Michael Jurkad51f33a2012-06-28 15:35:26 -07001875 }
1876 }
1877 }
1878 }
1879
1880 private void enableHwLayersOnVisiblePages() {
1881 if (mChildrenLayersEnabled) {
1882 final int screenCount = getChildCount();
1883 getVisiblePages(mTempVisiblePagesRange);
1884 int leftScreen = mTempVisiblePagesRange[0];
1885 int rightScreen = mTempVisiblePagesRange[1];
1886 if (leftScreen == rightScreen) {
1887 // make sure we're caching at least two pages always
1888 if (rightScreen < screenCount - 1) {
1889 rightScreen++;
1890 } else if (leftScreen > 0) {
1891 leftScreen--;
1892 }
1893 }
Chris Craik01f2d7f2013-10-01 14:41:56 -07001894
1895 final CellLayout customScreen = mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID);
Michael Jurkad51f33a2012-06-28 15:35:26 -07001896 for (int i = 0; i < screenCount; i++) {
Michael Jurka47639b92013-01-14 12:42:27 +01001897 final CellLayout layout = (CellLayout) getPageAt(i);
Chris Craik01f2d7f2013-10-01 14:41:56 -07001898
1899 // enable layers between left and right screen inclusive, except for the
1900 // customScreen, which may animate its content during transitions.
1901 boolean enableLayer = layout != customScreen &&
1902 leftScreen <= i && i <= rightScreen && shouldDrawChild(layout);
1903 layout.enableHardwareLayer(enableLayer);
Michael Jurkad74c9842011-07-10 12:44:21 -07001904 }
Michael Jurkace7e05f2011-02-01 22:02:35 -08001905 }
Michael Jurkace7e05f2011-02-01 22:02:35 -08001906 }
1907
Michael Jurka3a0469d2012-06-21 09:38:41 -07001908 public void buildPageHardwareLayers() {
1909 // force layers to be enabled just for the call to buildLayer
1910 updateChildrenLayersEnabled(true);
1911 if (getWindowToken() != null) {
1912 final int childCount = getChildCount();
1913 for (int i = 0; i < childCount; i++) {
1914 CellLayout cl = (CellLayout) getChildAt(i);
Michael Jurkad51f33a2012-06-28 15:35:26 -07001915 cl.buildHardwareLayer();
Michael Jurka3a0469d2012-06-21 09:38:41 -07001916 }
1917 }
1918 updateChildrenLayersEnabled(false);
1919 }
1920
Jeff Brown1d0867c2010-12-02 18:27:39 -08001921 protected void onWallpaperTap(MotionEvent ev) {
1922 final int[] position = mTempCell;
1923 getLocationOnScreen(position);
1924
1925 int pointerIndex = ev.getActionIndex();
1926 position[0] += (int) ev.getX(pointerIndex);
1927 position[1] += (int) ev.getY(pointerIndex);
1928
1929 mWallpaperManager.sendWallpaperCommand(getWindowToken(),
1930 ev.getAction() == MotionEvent.ACTION_UP
1931 ? WallpaperManager.COMMAND_TAP : WallpaperManager.COMMAND_SECONDARY_TAP,
1932 position[0], position[1], 0, null);
1933 }
1934
Adam Cohen61033d32010-11-15 18:29:44 -08001935 /*
1936 * This interpolator emulates the rate at which the perceived scale of an object changes
1937 * as its distance from a camera increases. When this interpolator is applied to a scale
1938 * animation on a view, it evokes the sense that the object is shrinking due to moving away
Andrew Flynn0dca1ec2012-02-29 13:33:22 -08001939 * from the camera.
Adam Cohen61033d32010-11-15 18:29:44 -08001940 */
1941 static class ZInterpolator implements TimeInterpolator {
1942 private float focalLength;
1943
1944 public ZInterpolator(float foc) {
1945 focalLength = foc;
1946 }
1947
Adam Coheneed565d2010-11-15 11:30:05 -08001948 public float getInterpolation(float input) {
1949 return (1.0f - focalLength / (focalLength + input)) /
Adam Cohen61033d32010-11-15 18:29:44 -08001950 (1.0f - focalLength / (focalLength + 1.0f));
Adam Cohencbbaf982010-11-12 14:50:33 -08001951 }
1952 }
1953
Adam Cohen61033d32010-11-15 18:29:44 -08001954 /*
1955 * The exact reverse of ZInterpolator.
1956 */
1957 static class InverseZInterpolator implements TimeInterpolator {
1958 private ZInterpolator zInterpolator;
1959 public InverseZInterpolator(float foc) {
1960 zInterpolator = new ZInterpolator(foc);
1961 }
Adam Cohencbbaf982010-11-12 14:50:33 -08001962 public float getInterpolation(float input) {
Adam Cohen61033d32010-11-15 18:29:44 -08001963 return 1 - zInterpolator.getInterpolation(1 - input);
Adam Cohencbbaf982010-11-12 14:50:33 -08001964 }
1965 }
1966
Adam Cohen61033d32010-11-15 18:29:44 -08001967 /*
1968 * ZInterpolator compounded with an ease-out.
1969 */
1970 static class ZoomOutInterpolator implements TimeInterpolator {
Adam Cohenb64d36e2011-10-17 21:48:02 -07001971 private final DecelerateInterpolator decelerate = new DecelerateInterpolator(0.75f);
1972 private final ZInterpolator zInterpolator = new ZInterpolator(0.13f);
Adam Cohen61033d32010-11-15 18:29:44 -08001973
1974 public float getInterpolation(float input) {
1975 return decelerate.getInterpolation(zInterpolator.getInterpolation(input));
1976 }
1977 }
1978
1979 /*
1980 * InvereZInterpolator compounded with an ease-out.
1981 */
1982 static class ZoomInInterpolator implements TimeInterpolator {
1983 private final InverseZInterpolator inverseZInterpolator = new InverseZInterpolator(0.35f);
1984 private final DecelerateInterpolator decelerate = new DecelerateInterpolator(3.0f);
1985
1986 public float getInterpolation(float input) {
1987 return decelerate.getInterpolation(inverseZInterpolator.getInterpolation(input));
1988 }
1989 }
1990
Adam Cohen61033d32010-11-15 18:29:44 -08001991 private final ZoomInInterpolator mZoomInInterpolator = new ZoomInInterpolator();
Michael Jurka3e7c7632010-10-02 16:01:03 -07001992
Michael Jurka3e7c7632010-10-02 16:01:03 -07001993 /*
Adam Cohen66396872011-04-15 17:50:36 -07001994 *
1995 * We call these methods (onDragStartedWithItemSpans/onDragStartedWithSize) whenever we
1996 * start a drag in Launcher, regardless of whether the drag has ever entered the Workspace
1997 *
1998 * These methods mark the appropriate pages as accepting drops (which alters their visual
1999 * appearance).
2000 *
2001 */
Sunny Goyal508da152014-08-14 10:53:27 -07002002 private static Rect getDrawableBounds(Drawable d) {
Mathew Inwood85900292014-04-16 14:17:39 +01002003 Rect bounds = new Rect();
2004 d.copyBounds(bounds);
2005 if (bounds.width() == 0 || bounds.height() == 0) {
2006 bounds.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
Sunny Goyal95abbb32014-08-04 10:53:22 -07002007 } else {
2008 bounds.offsetTo(0, 0);
2009 }
2010 if (d instanceof PreloadIconDrawable) {
2011 int inset = -((PreloadIconDrawable) d).getOutset();
2012 bounds.inset(inset, inset);
Mathew Inwood85900292014-04-16 14:17:39 +01002013 }
2014 return bounds;
2015 }
2016
Winson Chungbabb53e2014-04-14 17:12:49 -07002017 public void onExternalDragStartedWithItem(View v) {
Winson Chungbabb53e2014-04-14 17:12:49 -07002018 // Compose a drag bitmap with the view scaled to the icon size
2019 LauncherAppState app = LauncherAppState.getInstance();
2020 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
2021 int iconSize = grid.iconSizePx;
2022 int bmpWidth = v.getMeasuredWidth();
2023 int bmpHeight = v.getMeasuredHeight();
2024
2025 // If this is a text view, use its drawable instead
2026 if (v instanceof TextView) {
2027 TextView tv = (TextView) v;
2028 Drawable d = tv.getCompoundDrawables()[1];
Mathew Inwood85900292014-04-16 14:17:39 +01002029 Rect bounds = getDrawableBounds(d);
2030 bmpWidth = bounds.width();
2031 bmpHeight = bounds.height();
Winson Chungbabb53e2014-04-14 17:12:49 -07002032 }
2033
2034 // Compose the bitmap to create the icon from
2035 Bitmap b = Bitmap.createBitmap(bmpWidth, bmpHeight,
2036 Bitmap.Config.ARGB_8888);
Sunny Goyal508da152014-08-14 10:53:27 -07002037 mCanvas.setBitmap(b);
2038 drawDragView(v, mCanvas, 0);
2039 mCanvas.setBitmap(null);
Winson Chungbabb53e2014-04-14 17:12:49 -07002040
2041 // The outline is used to visualize where the item will land if dropped
Sunny Goyal508da152014-08-14 10:53:27 -07002042 mDragOutline = createDragOutline(b, DRAG_BITMAP_PADDING, iconSize, iconSize, true);
Winson Chungbabb53e2014-04-14 17:12:49 -07002043 }
2044
Michael Jurka8c3339b2012-06-14 16:18:21 -07002045 public void onDragStartedWithItem(PendingAddItemInfo info, Bitmap b, boolean clipAlpha) {
Michael Jurka038f9d82011-11-03 13:50:45 -07002046 int[] size = estimateItemSize(info.spanX, info.spanY, info, false);
Adam Cohen66396872011-04-15 17:50:36 -07002047
Michael Jurkad3ef3062010-11-23 16:23:58 -08002048 // The outline is used to visualize where the item will land if dropped
Sunny Goyal508da152014-08-14 10:53:27 -07002049 mDragOutline = createDragOutline(b, DRAG_BITMAP_PADDING, size[0], size[1], clipAlpha);
Michael Jurka3e7c7632010-10-02 16:01:03 -07002050 }
2051
Patrick Dubroy758a9232011-03-03 19:54:56 -08002052 public void exitWidgetResizeMode() {
Adam Cohen716b51e2011-06-30 12:09:54 -07002053 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohen67882692011-03-11 15:29:03 -08002054 dragLayer.clearAllResizeFrames();
Patrick Dubroy758a9232011-03-03 19:54:56 -08002055 }
2056
Adam Cohen4b285c52011-07-21 14:24:06 -07002057 private void initAnimationArrays() {
2058 final int childCount = getChildCount();
Adam Cohendcd297f2013-06-18 13:13:40 -07002059 if (mLastChildCount == childCount) return;
Adam Cohen7d30a372013-07-01 17:03:59 -07002060
Adam Cohen4b285c52011-07-21 14:24:06 -07002061 mOldBackgroundAlphas = new float[childCount];
Adam Cohen4b285c52011-07-21 14:24:06 -07002062 mOldAlphas = new float[childCount];
Adam Cohen4b285c52011-07-21 14:24:06 -07002063 mNewBackgroundAlphas = new float[childCount];
Adam Cohen4b285c52011-07-21 14:24:06 -07002064 mNewAlphas = new float[childCount];
Adam Cohen4b285c52011-07-21 14:24:06 -07002065 }
2066
Adam Cohen2854d252014-08-27 16:04:07 -07002067 Animator getChangeStateAnimation(final State state, boolean animated,
Adam Cohen80e6beb2015-02-13 16:14:33 -08002068 HashMap<View, Integer> layerViews) {
Adam Cohen2854d252014-08-27 16:04:07 -07002069 return getChangeStateAnimation(state, animated, 0, -1, layerViews);
Adam Cohen7777d962011-08-18 18:58:38 -07002070 }
2071
Adam Cohenf358a4b2013-07-23 16:47:31 -07002072 @Override
Adam Cohen6f127a62014-06-12 14:54:41 -07002073 protected void getFreeScrollPageRange(int[] range) {
2074 getOverviewModePages(range);
2075 }
2076
2077 private void getOverviewModePages(int[] range) {
Michael Jurkafe0ace32013-10-03 01:05:14 -07002078 int start = numCustomPages();
Adam Cohen1003be92013-09-16 14:09:28 -07002079 int end = getChildCount() - 1;
2080
2081 range[0] = Math.max(0, Math.min(start, getChildCount() - 1));
2082 range[1] = Math.max(0, end);
Adam Cohen6f127a62014-06-12 14:54:41 -07002083 }
Adam Cohendedbd962013-07-11 14:21:49 -07002084
2085 protected void onStartReordering() {
2086 super.onStartReordering();
Adam Cohendedbd962013-07-11 14:21:49 -07002087 showOutlines();
Adam Cohen22cba7f2013-07-19 16:14:00 -07002088 // Reordering handles its own animations, disable the automatic ones.
Winson Chung964df6b2013-10-11 15:55:37 -07002089 disableLayoutTransitions();
Adam Cohendedbd962013-07-11 14:21:49 -07002090 }
2091
2092 protected void onEndReordering() {
2093 super.onEndReordering();
Adam Cohendedbd962013-07-11 14:21:49 -07002094
Adam Cohendcb173d2014-04-01 13:33:58 -07002095 if (mLauncher.isWorkspaceLoading()) {
2096 // Invalid and dangerous operation if workspace is loading
2097 return;
2098 }
2099
Adam Cohen2bf63d52013-09-29 17:46:49 -07002100 hideOutlines();
Adam Cohendedbd962013-07-11 14:21:49 -07002101 mScreenOrder.clear();
Adam Cohen2bf63d52013-09-29 17:46:49 -07002102 int count = getChildCount();
Adam Cohendedbd962013-07-11 14:21:49 -07002103 for (int i = 0; i < count; i++) {
2104 CellLayout cl = ((CellLayout) getChildAt(i));
2105 mScreenOrder.add(getIdForScreen(cl));
2106 }
Winson Chungd64d1762013-08-20 14:37:16 -07002107
Adam Cohendedbd962013-07-11 14:21:49 -07002108 mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
Adam Cohen22cba7f2013-07-19 16:14:00 -07002109
2110 // Re-enable auto layout transitions for page deletion.
Winson Chung964df6b2013-10-11 15:55:37 -07002111 enableLayoutTransitions();
Adam Cohendedbd962013-07-11 14:21:49 -07002112 }
2113
Adam Cohenf358a4b2013-07-23 16:47:31 -07002114 public boolean isInOverviewMode() {
2115 return mState == State.OVERVIEW;
2116 }
2117
Adam Cohen93c97562013-09-26 13:48:01 -07002118 public boolean enterOverviewMode() {
2119 if (mTouchState != TOUCH_STATE_REST) {
2120 return false;
2121 }
Michael Jurka336fd4f2013-09-12 00:05:02 +02002122 enableOverviewMode(true, -1, true);
Adam Cohen93c97562013-09-26 13:48:01 -07002123 return true;
Adam Cohenf358a4b2013-07-23 16:47:31 -07002124 }
2125
Michael Jurka336fd4f2013-09-12 00:05:02 +02002126 public void exitOverviewMode(boolean animated) {
2127 exitOverviewMode(-1, animated);
Adam Cohenf358a4b2013-07-23 16:47:31 -07002128 }
2129
Michael Jurka336fd4f2013-09-12 00:05:02 +02002130 public void exitOverviewMode(int snapPage, boolean animated) {
2131 enableOverviewMode(false, snapPage, animated);
Adam Cohenf358a4b2013-07-23 16:47:31 -07002132 }
2133
Michael Jurka336fd4f2013-09-12 00:05:02 +02002134 private void enableOverviewMode(boolean enable, int snapPage, boolean animated) {
Adam Cohenf358a4b2013-07-23 16:47:31 -07002135 State finalState = Workspace.State.OVERVIEW;
2136 if (!enable) {
2137 finalState = Workspace.State.NORMAL;
2138 }
2139
Michael Jurka336fd4f2013-09-12 00:05:02 +02002140 Animator workspaceAnim = getChangeStateAnimation(finalState, animated, 0, snapPage);
2141 if (workspaceAnim != null) {
Adam Cohenf3434992013-09-16 16:52:59 -07002142 onTransitionPrepare();
Michael Jurka336fd4f2013-09-12 00:05:02 +02002143 workspaceAnim.addListener(new AnimatorListenerAdapter() {
2144 @Override
2145 public void onAnimationEnd(Animator arg0) {
Adam Cohenf3434992013-09-16 16:52:59 -07002146 onTransitionEnd();
Michael Jurka336fd4f2013-09-12 00:05:02 +02002147 }
2148 });
2149 workspaceAnim.start();
2150 }
Adam Cohenf358a4b2013-07-23 16:47:31 -07002151 }
2152
Adam Cohen410f3cd2013-09-22 12:09:32 -07002153 int getOverviewModeTranslationY() {
Winson Chungc82d2622013-11-06 13:23:29 -08002154 LauncherAppState app = LauncherAppState.getInstance();
2155 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
2156 Rect overviewBar = grid.getOverviewModeButtonBarRect();
Adam Cohen410f3cd2013-09-22 12:09:32 -07002157
Winson Chungc82d2622013-11-06 13:23:29 -08002158 int availableHeight = getViewportHeight();
2159 int scaledHeight = (int) (mOverviewModeShrinkFactor * getNormalChildHeight());
2160 int offsetFromTopEdge = (availableHeight - scaledHeight) / 2;
2161 int offsetToCenterInOverview = (availableHeight - mInsets.top - overviewBar.height()
2162 - scaledHeight) / 2;
Adam Cohen410f3cd2013-09-22 12:09:32 -07002163
Winson Chungc82d2622013-11-06 13:23:29 -08002164 return -offsetFromTopEdge + mInsets.top + offsetToCenterInOverview;
Adam Cohen410f3cd2013-09-22 12:09:32 -07002165 }
2166
Adam Cohenedaaa302013-10-01 17:33:27 -07002167 public void updateInteractionForState() {
2168 if (mState != State.NORMAL) {
2169 mLauncher.onInteractionBegin();
2170 } else {
2171 mLauncher.onInteractionEnd();
2172 }
2173 }
2174
2175 private void setState(State state) {
2176 mState = state;
2177 updateInteractionForState();
Adam Cohen53805212013-10-01 10:39:23 -07002178 updateAccessibilityFlags();
2179 }
2180
Adam Cohene25c5d12014-06-18 10:34:58 -07002181 State getState() {
2182 return mState;
2183 }
2184
Adam Cohen53805212013-10-01 10:39:23 -07002185 private void updateAccessibilityFlags() {
2186 int accessible = mState == State.NORMAL ?
Sunny Goyalc46bfef2015-01-05 12:40:08 -08002187 IMPORTANT_FOR_ACCESSIBILITY_YES :
2188 IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
Adam Cohen53805212013-10-01 10:39:23 -07002189 setImportantForAccessibility(accessible);
Adam Cohenedaaa302013-10-01 17:33:27 -07002190 }
2191
Adam Cohen6c5891a2014-07-09 23:53:15 -07002192 private static final int HIDE_WORKSPACE_DURATION = 100;
2193
Adam Cohenf358a4b2013-07-23 16:47:31 -07002194 Animator getChangeStateAnimation(final State state, boolean animated, int delay, int snapPage) {
Adam Cohen2854d252014-08-27 16:04:07 -07002195 return getChangeStateAnimation(state, animated, delay, snapPage, null);
2196 }
2197
2198 Animator getChangeStateAnimation(final State state, boolean animated, int delay, int snapPage,
Adam Cohen80e6beb2015-02-13 16:14:33 -08002199 HashMap<View, Integer> layerViews) {
Michael Jurkabdf78552011-10-31 14:34:25 -07002200 if (mState == state) {
Michael Jurka2a4b1a82011-12-07 14:00:02 -08002201 return null;
Michael Jurka99633da2011-07-27 22:40:17 -07002202 }
2203
Winson Chung1b7c1d32011-10-25 12:09:38 -07002204 // Initialize animation arrays for the first time if necessary
2205 initAnimationArrays();
2206
Michael Jurka2ecf9952012-06-18 12:52:28 -07002207 AnimatorSet anim = animated ? LauncherAnimUtils.createAnimatorSet() : null;
Adam Lesinski6b879f02010-11-04 16:15:23 -07002208
Adam Cohen08072c02014-11-18 17:53:44 -08002209 // We only want a single instance of a workspace animation to be running at once, so
2210 // we cancel any incomplete transition.
2211 if (mStateAnimator != null) {
2212 mStateAnimator.cancel();
2213 }
2214 mStateAnimator = anim;
2215
Winson Chung1b7c1d32011-10-25 12:09:38 -07002216 final State oldState = mState;
2217 final boolean oldStateIsNormal = (oldState == State.NORMAL);
Michael Jurka4ff7d792012-04-02 03:46:50 -07002218 final boolean oldStateIsSpringLoaded = (oldState == State.SPRING_LOADED);
Adam Cohen6c5891a2014-07-09 23:53:15 -07002219 final boolean oldStateIsNormalHidden = (oldState == State.NORMAL_HIDDEN);
2220 final boolean oldStateIsOverviewHidden = (oldState == State.OVERVIEW_HIDDEN);
Adam Cohenf358a4b2013-07-23 16:47:31 -07002221 final boolean oldStateIsOverview = (oldState == State.OVERVIEW);
Adam Cohenedaaa302013-10-01 17:33:27 -07002222 setState(state);
Winson Chung1b7c1d32011-10-25 12:09:38 -07002223 final boolean stateIsNormal = (state == State.NORMAL);
2224 final boolean stateIsSpringLoaded = (state == State.SPRING_LOADED);
Adam Cohen6c5891a2014-07-09 23:53:15 -07002225 final boolean stateIsNormalHidden = (state == State.NORMAL_HIDDEN);
2226 final boolean stateIsOverviewHidden = (state == State.OVERVIEW_HIDDEN);
Adam Cohenf358a4b2013-07-23 16:47:31 -07002227 final boolean stateIsOverview = (state == State.OVERVIEW);
Winson Chung2d75f122013-09-23 16:53:31 -07002228 float finalBackgroundAlpha = (stateIsSpringLoaded || stateIsOverview) ? 1.0f : 0f;
Adam Cohen6c5891a2014-07-09 23:53:15 -07002229 float finalHotseatAndPageIndicatorAlpha = (stateIsNormal || stateIsSpringLoaded) ? 1f : 0f;
Adam Cohenf358a4b2013-07-23 16:47:31 -07002230 float finalOverviewPanelAlpha = stateIsOverview ? 1f : 0f;
Winson Chung2d75f122013-09-23 16:53:31 -07002231 float finalSearchBarAlpha = !stateIsNormal ? 0f : 1f;
Adam Cohen6c5891a2014-07-09 23:53:15 -07002232 float finalWorkspaceTranslationY = stateIsOverview || stateIsOverviewHidden ?
2233 getOverviewModeTranslationY() : 0;
Adam Cohenf358a4b2013-07-23 16:47:31 -07002234
Adam Cohen6c5891a2014-07-09 23:53:15 -07002235 boolean workspaceToAllApps = (oldStateIsNormal && stateIsNormalHidden);
2236 boolean overviewToAllApps = (oldStateIsOverview && stateIsOverviewHidden);
2237 boolean allAppsToWorkspace = (stateIsNormalHidden && stateIsNormal);
Adam Cohen58993ad2013-10-11 17:57:38 -07002238 boolean workspaceToOverview = (oldStateIsNormal && stateIsOverview);
2239 boolean overviewToWorkspace = (oldStateIsOverview && stateIsNormal);
2240
Adam Cohen7d30a372013-07-01 17:03:59 -07002241 mNewScale = 1.0f;
Adam Cohenbeff8c62011-08-31 17:46:01 -07002242
Adam Cohenf358a4b2013-07-23 16:47:31 -07002243 if (oldStateIsOverview) {
Adam Cohenf9618852013-11-08 06:45:03 -08002244 disableFreeScroll();
Adam Cohenf358a4b2013-07-23 16:47:31 -07002245 } else if (stateIsOverview) {
2246 enableFreeScroll();
2247 }
2248
Adam Cohen7777d962011-08-18 18:58:38 -07002249 if (state != State.NORMAL) {
Adam Cohenf358a4b2013-07-23 16:47:31 -07002250 if (stateIsSpringLoaded) {
2251 mNewScale = mSpringLoadedShrinkFactor;
Adam Cohen6c5891a2014-07-09 23:53:15 -07002252 } else if (stateIsOverview || stateIsOverviewHidden) {
Adam Cohenf358a4b2013-07-23 16:47:31 -07002253 mNewScale = mOverviewModeShrinkFactor;
Adam Cohenf358a4b2013-07-23 16:47:31 -07002254 }
Adam Cohen7777d962011-08-18 18:58:38 -07002255 }
Winson Chungf4bd2362013-10-07 17:11:27 -07002256
Adam Cohenfa2450a2013-10-11 18:20:27 -07002257 final int duration;
Adam Cohen6c5891a2014-07-09 23:53:15 -07002258 if (workspaceToAllApps || overviewToAllApps) {
2259 duration = HIDE_WORKSPACE_DURATION; //getResources().getInteger(R.integer.config_workspaceUnshrinkTime);
Adam Cohenfa2450a2013-10-11 18:20:27 -07002260 } else if (workspaceToOverview || overviewToWorkspace) {
2261 duration = getResources().getInteger(R.integer.config_overviewTransitionTime);
2262 } else {
2263 duration = getResources().getInteger(R.integer.config_appsCustomizeWorkspaceShrinkTime);
2264 }
2265
Adam Cohenf9618852013-11-08 06:45:03 -08002266 if (snapPage == -1) {
2267 snapPage = getPageNearestToCenterOfScreen();
2268 }
2269 snapToPage(snapPage, duration, mZoomInInterpolator);
2270
Winson Chung1b7c1d32011-10-25 12:09:38 -07002271 for (int i = 0; i < getChildCount(); i++) {
2272 final CellLayout cl = (CellLayout) getChildAt(i);
Adam Cohenf9618852013-11-08 06:45:03 -08002273 boolean isCurrentPage = (i == snapPage);
Winson Chung2d75f122013-09-23 16:53:31 -07002274 float initialAlpha = cl.getShortcutsAndWidgets().getAlpha();
Adam Cohen3b185e22013-10-29 14:45:58 -07002275 float finalAlpha;
Adam Cohen6c5891a2014-07-09 23:53:15 -07002276 if (stateIsNormalHidden || stateIsOverviewHidden) {
Adam Cohen3b185e22013-10-29 14:45:58 -07002277 finalAlpha = 0f;
2278 } else if (stateIsNormal && mWorkspaceFadeInAdjacentScreens) {
Adam Cohenf9618852013-11-08 06:45:03 -08002279 finalAlpha = (i == snapPage || i < numCustomPages()) ? 1f : 0f;
Adam Cohen3b185e22013-10-29 14:45:58 -07002280 } else {
2281 finalAlpha = 1f;
2282 }
Winson Chungf4bd2362013-10-07 17:11:27 -07002283
2284 // If we are animating to/from the small state, then hide the side pages and fade the
2285 // current page in
2286 if (!mIsSwitchingState) {
2287 if (workspaceToAllApps || allAppsToWorkspace) {
2288 if (allAppsToWorkspace && isCurrentPage) {
2289 initialAlpha = 0f;
2290 } else if (!isCurrentPage) {
2291 initialAlpha = finalAlpha = 0f;
2292 }
2293 cl.setShortcutAndWidgetAlpha(initialAlpha);
2294 }
2295 }
Adam Cohen7777d962011-08-18 18:58:38 -07002296
Winson Chung1b7c1d32011-10-25 12:09:38 -07002297 mOldAlphas[i] = initialAlpha;
2298 mNewAlphas[i] = finalAlpha;
Adam Cohen7777d962011-08-18 18:58:38 -07002299 if (animated) {
Adam Cohen7777d962011-08-18 18:58:38 -07002300 mOldBackgroundAlphas[i] = cl.getBackgroundAlpha();
Adam Cohen7777d962011-08-18 18:58:38 -07002301 mNewBackgroundAlphas[i] = finalBackgroundAlpha;
Adam Cohen7777d962011-08-18 18:58:38 -07002302 } else {
Winson Chung1b7c1d32011-10-25 12:09:38 -07002303 cl.setBackgroundAlpha(finalBackgroundAlpha);
Michael Jurkaa52570f2012-03-20 03:18:20 -07002304 cl.setShortcutAndWidgetAlpha(finalAlpha);
Adam Cohen7777d962011-08-18 18:58:38 -07002305 }
Michael Jurkadee05892010-07-27 10:01:56 -07002306 }
Winson Chung8d006d52010-11-29 15:55:29 -08002307
Adam Cohen3d411982013-09-24 17:02:06 -07002308 final View searchBar = mLauncher.getQsbBar();
2309 final View overviewPanel = mLauncher.getOverviewPanel();
2310 final View hotseat = mLauncher.getHotseat();
Michael Jurka853f5a42013-11-25 14:32:21 +01002311 final View pageIndicator = getPageIndicator();
Adam Cohen7777d962011-08-18 18:58:38 -07002312 if (animated) {
Adam Cohen7d30a372013-07-01 17:03:59 -07002313 LauncherViewPropertyAnimator scale = new LauncherViewPropertyAnimator(this);
2314 scale.scaleX(mNewScale)
2315 .scaleY(mNewScale)
Adam Cohen410f3cd2013-09-22 12:09:32 -07002316 .translationY(finalWorkspaceTranslationY)
Adam Cohen6c5891a2014-07-09 23:53:15 -07002317 .setDuration(duration)
Adam Cohen7d30a372013-07-01 17:03:59 -07002318 .setInterpolator(mZoomInInterpolator);
2319 anim.play(scale);
Michael Jurka7407d2a2011-12-12 21:48:38 -08002320 for (int index = 0; index < getChildCount(); index++) {
2321 final int i = index;
2322 final CellLayout cl = (CellLayout) getChildAt(i);
Michael Jurka869390b2012-05-06 15:55:19 -07002323 float currentAlpha = cl.getShortcutsAndWidgets().getAlpha();
Michael Jurka2a4b1a82011-12-07 14:00:02 -08002324 if (mOldAlphas[i] == 0 && mNewAlphas[i] == 0) {
Michael Jurka7372c592012-01-16 04:21:35 -08002325 cl.setBackgroundAlpha(mNewBackgroundAlphas[i]);
Michael Jurkaa52570f2012-03-20 03:18:20 -07002326 cl.setShortcutAndWidgetAlpha(mNewAlphas[i]);
Michael Jurka7407d2a2011-12-12 21:48:38 -08002327 } else {
Adam Cohen2854d252014-08-27 16:04:07 -07002328 if (layerViews != null) {
Adam Cohen80e6beb2015-02-13 16:14:33 -08002329 layerViews.put(cl, Launcher.BUILD_LAYER);
Adam Cohen2854d252014-08-27 16:04:07 -07002330 }
Michael Jurka869390b2012-05-06 15:55:19 -07002331 if (mOldAlphas[i] != mNewAlphas[i] || currentAlpha != mNewAlphas[i]) {
Michael Jurka6c8a5792012-03-22 05:24:37 -07002332 LauncherViewPropertyAnimator alphaAnim =
2333 new LauncherViewPropertyAnimator(cl.getShortcutsAndWidgets());
Michael Jurkaa52570f2012-03-20 03:18:20 -07002334 alphaAnim.alpha(mNewAlphas[i])
Adam Cohen6c5891a2014-07-09 23:53:15 -07002335 .setDuration(duration)
Michael Jurkaa52570f2012-03-20 03:18:20 -07002336 .setInterpolator(mZoomInInterpolator);
2337 anim.play(alphaAnim);
2338 }
Michael Jurka7407d2a2011-12-12 21:48:38 -08002339 if (mOldBackgroundAlphas[i] != 0 ||
Michael Jurka869390b2012-05-06 15:55:19 -07002340 mNewBackgroundAlphas[i] != 0) {
Michael Jurkaf1ad6082013-03-13 12:55:46 +01002341 ValueAnimator bgAnim =
Adam Cohenfa2450a2013-10-11 18:20:27 -07002342 LauncherAnimUtils.ofFloat(cl, 0f, 1f);
Michael Jurka7407d2a2011-12-12 21:48:38 -08002343 bgAnim.setInterpolator(mZoomInInterpolator);
Adam Cohen6c5891a2014-07-09 23:53:15 -07002344 bgAnim.setDuration(duration);
Michael Jurka7407d2a2011-12-12 21:48:38 -08002345 bgAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
2346 public void onAnimationUpdate(float a, float b) {
Michael Jurka7372c592012-01-16 04:21:35 -08002347 cl.setBackgroundAlpha(
Michael Jurka7407d2a2011-12-12 21:48:38 -08002348 a * mOldBackgroundAlphas[i] +
2349 b * mNewBackgroundAlphas[i]);
Michael Jurka7407d2a2011-12-12 21:48:38 -08002350 }
2351 });
2352 anim.play(bgAnim);
2353 }
Michael Jurka2a4b1a82011-12-07 14:00:02 -08002354 }
2355 }
Michael Jurka853f5a42013-11-25 14:32:21 +01002356 Animator pageIndicatorAlpha = null;
2357 if (pageIndicator != null) {
2358 pageIndicatorAlpha = new LauncherViewPropertyAnimator(pageIndicator)
2359 .alpha(finalHotseatAndPageIndicatorAlpha).withLayer();
Michael Jurka1c00fd02013-12-02 16:11:22 -08002360 pageIndicatorAlpha.addListener(new AlphaUpdateListener(pageIndicator));
Michael Jurka853f5a42013-11-25 14:32:21 +01002361 } else {
2362 // create a dummy animation so we don't need to do null checks later
2363 pageIndicatorAlpha = ValueAnimator.ofFloat(0, 0);
Winson Chung66700732013-08-20 16:56:15 -07002364 }
Michael Jurka1c00fd02013-12-02 16:11:22 -08002365
Adam Cohen80e6beb2015-02-13 16:14:33 -08002366 LauncherViewPropertyAnimator hotseatAlpha = new LauncherViewPropertyAnimator(hotseat)
2367 .alpha(finalHotseatAndPageIndicatorAlpha);
Michael Jurka1c00fd02013-12-02 16:11:22 -08002368 hotseatAlpha.addListener(new AlphaUpdateListener(hotseat));
2369
Adam Cohen80e6beb2015-02-13 16:14:33 -08002370 LauncherViewPropertyAnimator overviewPanelAlpha =
2371 new LauncherViewPropertyAnimator(overviewPanel).alpha(finalOverviewPanelAlpha);
Michael Jurka853f5a42013-11-25 14:32:21 +01002372 overviewPanelAlpha.addListener(new AlphaUpdateListener(overviewPanel));
Adam Cohen3d411982013-09-24 17:02:06 -07002373
Adam Cohen2854d252014-08-27 16:04:07 -07002374 // For animation optimations, we may need to provide the Launcher transition
2375 // with a set of views on which to force build layers in certain scenarios.
2376 hotseat.setLayerType(View.LAYER_TYPE_HARDWARE, null);
Adam Cohen2854d252014-08-27 16:04:07 -07002377 overviewPanel.setLayerType(View.LAYER_TYPE_HARDWARE, null);
2378 if (layerViews != null) {
Adam Cohen80e6beb2015-02-13 16:14:33 -08002379 // If layerViews is not null, we add these views, and indicate that
2380 // the caller can manage layer state.
2381 layerViews.put(hotseat, Launcher.BUILD_AND_SET_LAYER);
2382 layerViews.put(overviewPanel, Launcher.BUILD_AND_SET_LAYER);
2383 } else {
2384 // Otherwise let the animator handle layer management.
2385 hotseatAlpha.withLayer();
2386 overviewPanelAlpha.withLayer();
Adam Cohen2854d252014-08-27 16:04:07 -07002387 }
2388
Adam Cohen58993ad2013-10-11 17:57:38 -07002389 if (workspaceToOverview) {
Michael Jurka853f5a42013-11-25 14:32:21 +01002390 pageIndicatorAlpha.setInterpolator(new DecelerateInterpolator(2));
Adam Cohen58993ad2013-10-11 17:57:38 -07002391 hotseatAlpha.setInterpolator(new DecelerateInterpolator(2));
Michael Jurka853f5a42013-11-25 14:32:21 +01002392 overviewPanelAlpha.setInterpolator(null);
Adam Cohen58993ad2013-10-11 17:57:38 -07002393 } else if (overviewToWorkspace) {
Michael Jurka853f5a42013-11-25 14:32:21 +01002394 pageIndicatorAlpha.setInterpolator(null);
2395 hotseatAlpha.setInterpolator(null);
Adam Cohen58993ad2013-10-11 17:57:38 -07002396 overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2));
2397 }
Adam Cohen6c5891a2014-07-09 23:53:15 -07002398
2399 overviewPanelAlpha.setDuration(duration);
2400 pageIndicatorAlpha.setDuration(duration);
2401 hotseatAlpha.setDuration(duration);
Adam Cohenc2d6e892014-10-16 09:49:24 -07002402
2403 if (searchBar != null) {
Adam Cohen80e6beb2015-02-13 16:14:33 -08002404 LauncherViewPropertyAnimator searchBarAlpha = new LauncherViewPropertyAnimator(searchBar)
2405 .alpha(finalSearchBarAlpha);
Adam Cohenc2d6e892014-10-16 09:49:24 -07002406 searchBarAlpha.addListener(new AlphaUpdateListener(searchBar));
2407 searchBar.setLayerType(View.LAYER_TYPE_HARDWARE, null);
2408 if (layerViews != null) {
Adam Cohen80e6beb2015-02-13 16:14:33 -08002409 // If layerViews is not null, we add these views, and indicate that
2410 // the caller can manage layer state.
2411 layerViews.put(searchBar, Launcher.BUILD_AND_SET_LAYER);
2412 } else {
2413 // Otherwise let the animator handle layer management.
2414 searchBarAlpha.withLayer();
Adam Cohenc2d6e892014-10-16 09:49:24 -07002415 }
2416 searchBarAlpha.setDuration(duration);
2417 anim.play(searchBarAlpha);
2418 }
Adam Cohen3d411982013-09-24 17:02:06 -07002419
Adam Cohenf358a4b2013-07-23 16:47:31 -07002420 anim.play(overviewPanelAlpha);
2421 anim.play(hotseatAlpha);
2422 anim.play(pageIndicatorAlpha);
Michael Jurka2a4b1a82011-12-07 14:00:02 -08002423 anim.setStartDelay(delay);
Adam Cohen08072c02014-11-18 17:53:44 -08002424 anim.addListener(new AnimatorListenerAdapter() {
2425 @Override
2426 public void onAnimationEnd(Animator animation) {
2427 mStateAnimator = null;
2428 }
2429 });
Adam Cohenf358a4b2013-07-23 16:47:31 -07002430 } else {
Adam Cohen3d411982013-09-24 17:02:06 -07002431 overviewPanel.setAlpha(finalOverviewPanelAlpha);
2432 AlphaUpdateListener.updateVisibility(overviewPanel);
2433 hotseat.setAlpha(finalHotseatAndPageIndicatorAlpha);
2434 AlphaUpdateListener.updateVisibility(hotseat);
Michael Jurka853f5a42013-11-25 14:32:21 +01002435 if (pageIndicator != null) {
2436 pageIndicator.setAlpha(finalHotseatAndPageIndicatorAlpha);
2437 AlphaUpdateListener.updateVisibility(pageIndicator);
Winson Chung66700732013-08-20 16:56:15 -07002438 }
Sunny Goyal416541c2014-11-14 11:59:57 -08002439 if (searchBar != null) {
2440 searchBar.setAlpha(finalSearchBarAlpha);
2441 AlphaUpdateListener.updateVisibility(searchBar);
2442 }
Adam Cohen3f452c82013-09-19 11:57:17 -07002443 updateCustomContentVisibility();
Adam Cohen410f3cd2013-09-22 12:09:32 -07002444 setScaleX(mNewScale);
2445 setScaleY(mNewScale);
2446 setTranslationY(finalWorkspaceTranslationY);
Adam Cohendbdff6b2013-09-18 19:09:15 -07002447 }
Adam Cohen7777d962011-08-18 18:58:38 -07002448
Adam Cohen6c5891a2014-07-09 23:53:15 -07002449 if (stateIsNormal) {
Adam Cohened307df2013-10-02 09:37:31 -07002450 animateBackgroundGradient(0f, animated);
Adam Cohen6c5891a2014-07-09 23:53:15 -07002451 } else {
2452 animateBackgroundGradient(getResources().getInteger(
2453 R.integer.config_workspaceScrimAlpha) / 100f, animated);
Michael Jurka7bdb25a2011-08-03 15:16:44 -07002454 }
Michael Jurka2a4b1a82011-12-07 14:00:02 -08002455 return anim;
Michael Jurkadee05892010-07-27 10:01:56 -07002456 }
2457
Adam Cohena5f4e482013-10-11 12:10:28 -07002458 static class AlphaUpdateListener implements AnimatorUpdateListener, AnimatorListener {
Adam Cohenf358a4b2013-07-23 16:47:31 -07002459 View view;
2460 public AlphaUpdateListener(View v) {
2461 view = v;
2462 }
2463
2464 @Override
2465 public void onAnimationUpdate(ValueAnimator arg0) {
Michael Jurka336fd4f2013-09-12 00:05:02 +02002466 updateVisibility(view);
2467 }
2468
2469 public static void updateVisibility(View view) {
Adam Cohen53805212013-10-01 10:39:23 -07002470 // We want to avoid the extra layout pass by setting the views to GONE unless
2471 // accessibility is on, in which case not setting them to GONE causes a glitch.
2472 int invisibleState = sAccessibilityEnabled ? GONE : INVISIBLE;
2473 if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != invisibleState) {
2474 view.setVisibility(invisibleState);
Adam Cohenf358a4b2013-07-23 16:47:31 -07002475 } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD
2476 && view.getVisibility() != VISIBLE) {
2477 view.setVisibility(VISIBLE);
2478 }
2479 }
Adam Cohena5f4e482013-10-11 12:10:28 -07002480
2481 @Override
2482 public void onAnimationCancel(Animator arg0) {
2483 }
2484
2485 @Override
2486 public void onAnimationEnd(Animator arg0) {
2487 updateVisibility(view);
2488 }
2489
2490 @Override
2491 public void onAnimationRepeat(Animator arg0) {
2492 }
2493
2494 @Override
2495 public void onAnimationStart(Animator arg0) {
2496 // We want the views to be visible for animation, so fade-in/out is visible
2497 view.setVisibility(VISIBLE);
2498 }
Adam Cohenf358a4b2013-07-23 16:47:31 -07002499 }
2500
Michael Jurkabed61d22012-02-14 22:51:29 -08002501 @Override
Michael Jurkaa35e35a2012-04-26 15:04:28 -07002502 public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
Adam Cohenf3434992013-09-16 16:52:59 -07002503 onTransitionPrepare();
Michael Jurkabed61d22012-02-14 22:51:29 -08002504 }
2505
2506 @Override
Michael Jurkaa35e35a2012-04-26 15:04:28 -07002507 public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
2508 }
2509
2510 @Override
Winson Chung70442722012-02-10 15:43:22 -08002511 public void onLauncherTransitionStep(Launcher l, float t) {
2512 mTransitionProgress = t;
2513 }
2514
2515 @Override
Michael Jurkabed61d22012-02-14 22:51:29 -08002516 public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
Adam Cohenf3434992013-09-16 16:52:59 -07002517 onTransitionEnd();
2518 }
2519
2520 private void onTransitionPrepare() {
2521 mIsSwitchingState = true;
Winson Chungf4bd2362013-10-07 17:11:27 -07002522
2523 // Invalidate here to ensure that the pages are rendered during the state change transition.
2524 invalidate();
2525
Adam Cohenf3434992013-09-16 16:52:59 -07002526 updateChildrenLayersEnabled(false);
Adam Cohen3f452c82013-09-19 11:57:17 -07002527 hideCustomContentIfNecessary();
2528 }
2529
2530 void updateCustomContentVisibility() {
2531 int visibility = mState == Workspace.State.NORMAL ? VISIBLE : INVISIBLE;
2532 if (hasCustomContent()) {
2533 mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(visibility);
2534 }
2535 }
2536
2537 void showCustomContentIfNecessary() {
2538 boolean show = mState == Workspace.State.NORMAL;
2539 if (show && hasCustomContent()) {
2540 mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(VISIBLE);
2541 }
2542 }
2543
2544 void hideCustomContentIfNecessary() {
2545 boolean hide = mState != Workspace.State.NORMAL;
2546 if (hide && hasCustomContent()) {
Adam Cohen1774a8a2013-11-15 10:56:16 +00002547 disableLayoutTransitions();
Adam Cohen3f452c82013-09-19 11:57:17 -07002548 mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(INVISIBLE);
Adam Cohen1774a8a2013-11-15 10:56:16 +00002549 enableLayoutTransitions();
Adam Cohenf3434992013-09-16 16:52:59 -07002550 }
2551 }
2552
2553 private void onTransitionEnd() {
Michael Jurkabed61d22012-02-14 22:51:29 -08002554 mIsSwitchingState = false;
Michael Jurka3a0469d2012-06-21 09:38:41 -07002555 updateChildrenLayersEnabled(false);
Adam Cohen3f452c82013-09-19 11:57:17 -07002556 showCustomContentIfNecessary();
Michael Jurkabed61d22012-02-14 22:51:29 -08002557 }
2558
2559 @Override
2560 public View getContent() {
2561 return this;
2562 }
2563
Joe Onorato4be866d2010-10-10 11:26:02 -07002564 /**
2565 * Draw the View v into the given Canvas.
2566 *
2567 * @param v the view to draw
2568 * @param destCanvas the canvas to draw on
2569 * @param padding the horizontal and vertical padding to use when drawing
2570 */
Sunny Goyal508da152014-08-14 10:53:27 -07002571 private static void drawDragView(View v, Canvas destCanvas, int padding) {
2572 final Rect clipRect = sTempRect;
Joe Onorato4be866d2010-10-10 11:26:02 -07002573 v.getDrawingRect(clipRect);
2574
Adam Cohen099f60d2011-08-23 21:07:26 -07002575 boolean textVisible = false;
2576
Adam Cohenac8c8762011-07-13 11:15:27 -07002577 destCanvas.save();
Sunny Goyal95abbb32014-08-04 10:53:22 -07002578 if (v instanceof TextView) {
Adam Cohenac8c8762011-07-13 11:15:27 -07002579 Drawable d = ((TextView) v).getCompoundDrawables()[1];
Mathew Inwood85900292014-04-16 14:17:39 +01002580 Rect bounds = getDrawableBounds(d);
2581 clipRect.set(0, 0, bounds.width() + padding, bounds.height() + padding);
Sunny Goyal95abbb32014-08-04 10:53:22 -07002582 destCanvas.translate(padding / 2 - bounds.left, padding / 2 - bounds.top);
Adam Cohenac8c8762011-07-13 11:15:27 -07002583 d.draw(destCanvas);
2584 } else {
2585 if (v instanceof FolderIcon) {
Adam Cohen099f60d2011-08-23 21:07:26 -07002586 // For FolderIcons the text can bleed into the icon area, and so we need to
2587 // hide the text completely (which can't be achieved by clipping).
2588 if (((FolderIcon) v).getTextVisible()) {
2589 ((FolderIcon) v).setTextVisible(false);
2590 textVisible = true;
2591 }
Adam Cohenac8c8762011-07-13 11:15:27 -07002592 }
2593 destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2);
2594 destCanvas.clipRect(clipRect, Op.REPLACE);
2595 v.draw(destCanvas);
Adam Cohen099f60d2011-08-23 21:07:26 -07002596
2597 // Restore text visibility of FolderIcon if necessary
2598 if (textVisible) {
2599 ((FolderIcon) v).setTextVisible(true);
2600 }
Adam Cohenac8c8762011-07-13 11:15:27 -07002601 }
2602 destCanvas.restore();
2603 }
2604
2605 /**
2606 * Returns a new bitmap to show when the given View is being dragged around.
2607 * Responsibility for the bitmap is transferred to the caller.
Sunny Goyal95abbb32014-08-04 10:53:22 -07002608 * @param expectedPadding padding to add to the drag view. If a different padding was used
2609 * its value will be changed
Adam Cohenac8c8762011-07-13 11:15:27 -07002610 */
Sunny Goyal508da152014-08-14 10:53:27 -07002611 public Bitmap createDragBitmap(View v, AtomicInteger expectedPadding) {
Adam Cohenac8c8762011-07-13 11:15:27 -07002612 Bitmap b;
2613
Sunny Goyal95abbb32014-08-04 10:53:22 -07002614 int padding = expectedPadding.get();
Adam Cohenac8c8762011-07-13 11:15:27 -07002615 if (v instanceof TextView) {
2616 Drawable d = ((TextView) v).getCompoundDrawables()[1];
Mathew Inwood85900292014-04-16 14:17:39 +01002617 Rect bounds = getDrawableBounds(d);
2618 b = Bitmap.createBitmap(bounds.width() + padding,
2619 bounds.height() + padding, Bitmap.Config.ARGB_8888);
Sunny Goyal95abbb32014-08-04 10:53:22 -07002620 expectedPadding.set(padding - bounds.left - bounds.top);
Adam Cohenac8c8762011-07-13 11:15:27 -07002621 } else {
2622 b = Bitmap.createBitmap(
2623 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
Joe Onorato4be866d2010-10-10 11:26:02 -07002624 }
2625
Sunny Goyal508da152014-08-14 10:53:27 -07002626 mCanvas.setBitmap(b);
2627 drawDragView(v, mCanvas, padding);
2628 mCanvas.setBitmap(null);
Joe Onorato4be866d2010-10-10 11:26:02 -07002629
Adam Cohenac8c8762011-07-13 11:15:27 -07002630 return b;
Joe Onorato4be866d2010-10-10 11:26:02 -07002631 }
2632
2633 /**
2634 * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
2635 * Responsibility for the bitmap is transferred to the caller.
2636 */
Sunny Goyal508da152014-08-14 10:53:27 -07002637 private Bitmap createDragOutline(View v, int padding) {
Adam Cohen410f3cd2013-09-22 12:09:32 -07002638 final int outlineColor = getResources().getColor(R.color.outline_color);
Joe Onorato4be866d2010-10-10 11:26:02 -07002639 final Bitmap b = Bitmap.createBitmap(
2640 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
2641
Sunny Goyal508da152014-08-14 10:53:27 -07002642 mCanvas.setBitmap(b);
2643 drawDragView(v, mCanvas, padding);
2644 mOutlineHelper.applyExpensiveOutlineWithBlur(b, mCanvas, outlineColor, outlineColor);
2645 mCanvas.setBitmap(null);
Joe Onorato4be866d2010-10-10 11:26:02 -07002646 return b;
2647 }
2648
2649 /**
Michael Jurkad3ef3062010-11-23 16:23:58 -08002650 * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
2651 * Responsibility for the bitmap is transferred to the caller.
2652 */
Sunny Goyal508da152014-08-14 10:53:27 -07002653 private Bitmap createDragOutline(Bitmap orig, int padding, int w, int h,
Michael Jurka8c3339b2012-06-14 16:18:21 -07002654 boolean clipAlpha) {
Adam Cohen410f3cd2013-09-22 12:09:32 -07002655 final int outlineColor = getResources().getColor(R.color.outline_color);
Michael Jurkaf12c75c2011-01-25 22:41:40 -08002656 final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Sunny Goyal508da152014-08-14 10:53:27 -07002657 mCanvas.setBitmap(b);
Michael Jurkaf12c75c2011-01-25 22:41:40 -08002658
2659 Rect src = new Rect(0, 0, orig.getWidth(), orig.getHeight());
2660 float scaleFactor = Math.min((w - padding) / (float) orig.getWidth(),
2661 (h - padding) / (float) orig.getHeight());
2662 int scaledWidth = (int) (scaleFactor * orig.getWidth());
2663 int scaledHeight = (int) (scaleFactor * orig.getHeight());
2664 Rect dst = new Rect(0, 0, scaledWidth, scaledHeight);
2665
2666 // center the image
2667 dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2);
2668
Sunny Goyal508da152014-08-14 10:53:27 -07002669 mCanvas.drawBitmap(orig, src, dst, null);
2670 mOutlineHelper.applyExpensiveOutlineWithBlur(b, mCanvas, outlineColor, outlineColor,
Michael Jurka8c3339b2012-06-14 16:18:21 -07002671 clipAlpha);
Sunny Goyal508da152014-08-14 10:53:27 -07002672 mCanvas.setBitmap(null);
Michael Jurkad3ef3062010-11-23 16:23:58 -08002673
2674 return b;
2675 }
2676
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002677 void startDrag(CellLayout.CellInfo cellInfo) {
2678 View child = cellInfo.cell;
Winson Chungaafa03c2010-06-11 17:34:16 -07002679
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002680 // Make sure the drag was started by a long press as opposed to a long click.
Bjorn Bringert7984c942009-12-09 15:38:25 +00002681 if (!child.isInTouchMode()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002682 return;
2683 }
Winson Chungaafa03c2010-06-11 17:34:16 -07002684
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002685 mDragInfo = cellInfo;
Adam Cohend41fbf52012-02-16 23:53:59 -08002686 child.setVisibility(INVISIBLE);
Adam Cohen482ed822012-03-02 14:15:13 -08002687 CellLayout layout = (CellLayout) child.getParent().getParent();
2688 layout.prepareChildForDrag(child);
Joe Onorato4be866d2010-10-10 11:26:02 -07002689
Adam Cohenac8c8762011-07-13 11:15:27 -07002690 beginDragShared(child, this);
2691 }
2692
2693 public void beginDragShared(View child, DragSource source) {
Sunny Goyal508da152014-08-14 10:53:27 -07002694 child.clearFocus();
2695 child.setPressed(false);
2696
2697 // The outline is used to visualize where the item will land if dropped
2698 mDragOutline = createDragOutline(child, DRAG_BITMAP_PADDING);
2699
Anjali Koppal62d18ed2014-03-10 17:04:03 -07002700 mLauncher.onDragStarted(child);
Joe Onorato4be866d2010-10-10 11:26:02 -07002701 // The drag bitmap follows the touch point around on the screen
Sunny Goyal95abbb32014-08-04 10:53:22 -07002702 AtomicInteger padding = new AtomicInteger(DRAG_BITMAP_PADDING);
Sunny Goyal508da152014-08-14 10:53:27 -07002703 final Bitmap b = createDragBitmap(child, padding);
Joe Onorato4be866d2010-10-10 11:26:02 -07002704
2705 final int bmpWidth = b.getWidth();
Winson Chungeecf02d2012-03-02 17:14:58 -08002706 final int bmpHeight = b.getHeight();
Adam Cohene3e27a82011-04-15 12:07:39 -07002707
Adam Cohen307fe232012-08-16 17:55:58 -07002708 float scale = mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY);
Winson Chungbabb53e2014-04-14 17:12:49 -07002709 int dragLayerX = Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2);
2710 int dragLayerY = Math.round(mTempXY[1] - (bmpHeight - scale * bmpHeight) / 2
Sunny Goyal95abbb32014-08-04 10:53:22 -07002711 - padding.get() / 2);
Adam Cohene3e27a82011-04-15 12:07:39 -07002712
Winson Chung5f8afe62013-08-12 16:19:28 -07002713 LauncherAppState app = LauncherAppState.getInstance();
2714 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
Winson Chungb8c69f32011-10-19 21:36:08 -07002715 Point dragVisualizeOffset = null;
Adam Cohene3e27a82011-04-15 12:07:39 -07002716 Rect dragRect = null;
Sunny Goyal508da152014-08-14 10:53:27 -07002717 if (child instanceof BubbleTextView) {
Winson Chung5f8afe62013-08-12 16:19:28 -07002718 int iconSize = grid.iconSizePx;
Adam Cohene3e27a82011-04-15 12:07:39 -07002719 int top = child.getPaddingTop();
2720 int left = (bmpWidth - iconSize) / 2;
2721 int right = left + iconSize;
2722 int bottom = top + iconSize;
Adam Cohenfc53cd22011-07-20 15:45:11 -07002723 dragLayerY += top;
Winson Chungb8c69f32011-10-19 21:36:08 -07002724 // Note: The drag region is used to calculate drag layer offsets, but the
2725 // dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
Sunny Goyal95abbb32014-08-04 10:53:22 -07002726 dragVisualizeOffset = new Point(-padding.get() / 2, padding.get() / 2);
Adam Cohene3e27a82011-04-15 12:07:39 -07002727 dragRect = new Rect(left, top, right, bottom);
Adam Cohen0e4857c2011-06-30 17:05:14 -07002728 } else if (child instanceof FolderIcon) {
Winson Chung5f8afe62013-08-12 16:19:28 -07002729 int previewSize = grid.folderIconSizePx;
2730 dragRect = new Rect(0, child.getPaddingTop(), child.getWidth(), previewSize);
Adam Cohene3e27a82011-04-15 12:07:39 -07002731 }
2732
Winson Chung1e9cbfe2011-09-30 16:52:26 -07002733 // Clear the pressed state if necessary
2734 if (child instanceof BubbleTextView) {
2735 BubbleTextView icon = (BubbleTextView) child;
Sunny Goyal508da152014-08-14 10:53:27 -07002736 icon.clearPressedBackground();
Winson Chung1e9cbfe2011-09-30 16:52:26 -07002737 }
2738
Adam Cohen2f32ad22013-11-13 11:29:49 +00002739 if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) {
2740 String msg = "Drag started with a view that has no tag set. This "
2741 + "will cause a crash (issue 11627249) down the line. "
2742 + "View: " + child + " tag: " + child.getTag();
2743 throw new IllegalStateException(msg);
2744 }
2745
Winson Chungeeb5bbc2013-11-13 15:47:05 -08002746 DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
Adam Cohen307fe232012-08-16 17:55:58 -07002747 DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale);
Winson Chungeeb5bbc2013-11-13 15:47:05 -08002748 dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
Adam Cohen5084cba2013-09-03 12:01:16 -07002749
2750 if (child.getParent() instanceof ShortcutAndWidgetContainer) {
2751 mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent();
2752 }
2753
Joe Onorato4be866d2010-10-10 11:26:02 -07002754 b.recycle();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002755 }
2756
Winson Chungbabb53e2014-04-14 17:12:49 -07002757 public void beginExternalDragShared(View child, DragSource source) {
2758 LauncherAppState app = LauncherAppState.getInstance();
2759 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
2760 int iconSize = grid.iconSizePx;
2761
2762 // Notify launcher of drag start
2763 mLauncher.onDragStarted(child);
2764
2765 // Compose a new drag bitmap that is of the icon size
Sunny Goyal95abbb32014-08-04 10:53:22 -07002766 AtomicInteger padding = new AtomicInteger(DRAG_BITMAP_PADDING);
Sunny Goyal508da152014-08-14 10:53:27 -07002767 final Bitmap tmpB = createDragBitmap(child, padding);
Winson Chungbabb53e2014-04-14 17:12:49 -07002768 Bitmap b = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
2769 Paint p = new Paint();
2770 p.setFilterBitmap(true);
Sunny Goyal508da152014-08-14 10:53:27 -07002771 mCanvas.setBitmap(b);
2772 mCanvas.drawBitmap(tmpB, new Rect(0, 0, tmpB.getWidth(), tmpB.getHeight()),
Winson Chungbabb53e2014-04-14 17:12:49 -07002773 new Rect(0, 0, iconSize, iconSize), p);
Sunny Goyal508da152014-08-14 10:53:27 -07002774 mCanvas.setBitmap(null);
Winson Chungbabb53e2014-04-14 17:12:49 -07002775
2776 // Find the child's location on the screen
2777 int bmpWidth = tmpB.getWidth();
2778 float iconScale = (float) bmpWidth / iconSize;
2779 float scale = mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY) * iconScale;
2780 int dragLayerX = Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2);
2781 int dragLayerY = Math.round(mTempXY[1]);
2782
2783 // Note: The drag region is used to calculate drag layer offsets, but the
2784 // dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
Sunny Goyal95abbb32014-08-04 10:53:22 -07002785 Point dragVisualizeOffset = new Point(-padding.get() / 2, padding.get() / 2);
Winson Chungbabb53e2014-04-14 17:12:49 -07002786 Rect dragRect = new Rect(0, 0, iconSize, iconSize);
2787
2788 if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) {
2789 String msg = "Drag started with a view that has no tag set. This "
2790 + "will cause a crash (issue 11627249) down the line. "
2791 + "View: " + child + " tag: " + child.getTag();
2792 throw new IllegalStateException(msg);
2793 }
2794
2795 // Start the drag
2796 DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
2797 DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale);
2798 dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
2799
2800 // Recycle temporary bitmaps
2801 tmpB.recycle();
2802 }
2803
Adam Cohendcd297f2013-06-18 13:13:40 -07002804 void addApplicationShortcut(ShortcutInfo info, CellLayout target, long container, long screenId,
Winson Chung3d503fb2011-07-13 17:25:49 -07002805 int cellX, int cellY, boolean insertAtFirst, int intersectX, int intersectY) {
2806 View view = mLauncher.createShortcut(R.layout.application, target, (ShortcutInfo) info);
Michael Jurka0280c3b2010-09-17 15:00:07 -07002807
2808 final int[] cellXY = new int[2];
Winson Chung3d503fb2011-07-13 17:25:49 -07002809 target.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY);
Adam Cohendcd297f2013-06-18 13:13:40 -07002810 addInScreen(view, container, screenId, cellXY[0], cellXY[1], 1, 1, insertAtFirst);
Adam Cohen7c4c5102013-06-14 17:42:35 -07002811
Adam Cohendcd297f2013-06-18 13:13:40 -07002812 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId, cellXY[0],
Winson Chung3d503fb2011-07-13 17:25:49 -07002813 cellXY[1]);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002814 }
2815
Adam Cohen4b285c52011-07-21 14:24:06 -07002816 public boolean transitionStateShouldAllowDrop() {
Adam Cohen6c5891a2014-07-09 23:53:15 -07002817 return ((!isSwitchingState() || mTransitionProgress > 0.5f) &&
2818 (mState == State.NORMAL || mState == State.SPRING_LOADED));
Adam Cohen4b285c52011-07-21 14:24:06 -07002819 }
2820
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002821 /**
2822 * {@inheritDoc}
2823 */
Adam Cohencb3382b2011-05-24 14:07:08 -07002824 public boolean acceptDrop(DragObject d) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002825 // If it's an external drop (e.g. from All Apps), check if it should be accepted
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002826 CellLayout dropTargetLayout = mDropToLayout;
Adam Cohencb3382b2011-05-24 14:07:08 -07002827 if (d.dragSource != this) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002828 // Don't accept the drop if we're not over a screen at time of drop
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002829 if (dropTargetLayout == null) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002830 return false;
2831 }
Adam Cohen4b285c52011-07-21 14:24:06 -07002832 if (!transitionStateShouldAllowDrop()) return false;
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002833
Adam Cohena65beee2011-06-27 21:32:23 -07002834 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset,
2835 d.dragView, mDragViewVisualCenter);
2836
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07002837 // We want the point to be mapped to the dragTarget.
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002838 if (mLauncher.isHotseatLayout(dropTargetLayout)) {
Adam Cohen307fe232012-08-16 17:55:58 -07002839 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07002840 } else {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002841 mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null);
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07002842 }
2843
Winson Chung557d6ed2011-07-08 15:34:52 -07002844 int spanX = 1;
2845 int spanY = 1;
Winson Chung557d6ed2011-07-08 15:34:52 -07002846 if (mDragInfo != null) {
2847 final CellLayout.CellInfo dragCellInfo = mDragInfo;
2848 spanX = dragCellInfo.spanX;
2849 spanY = dragCellInfo.spanY;
Winson Chung557d6ed2011-07-08 15:34:52 -07002850 } else {
2851 final ItemInfo dragInfo = (ItemInfo) d.dragInfo;
2852 spanX = dragInfo.spanX;
2853 spanY = dragInfo.spanY;
2854 }
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002855
Adam Cohend41fbf52012-02-16 23:53:59 -08002856 int minSpanX = spanX;
2857 int minSpanY = spanY;
2858 if (d.dragInfo instanceof PendingAddWidgetInfo) {
2859 minSpanX = ((PendingAddWidgetInfo) d.dragInfo).minSpanX;
2860 minSpanY = ((PendingAddWidgetInfo) d.dragInfo).minSpanY;
2861 }
Adam Cohen482ed822012-03-02 14:15:13 -08002862
Adam Cohena65beee2011-06-27 21:32:23 -07002863 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002864 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, dropTargetLayout,
Adam Cohenf0777b92012-02-28 14:02:45 -08002865 mTargetCell);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002866 float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08002867 mDragViewVisualCenter[1], mTargetCell);
Adam Cohenec0d61d2014-02-07 16:34:51 -08002868 if (mCreateUserFolderOnDrop && willCreateUserFolder((ItemInfo) d.dragInfo,
2869 dropTargetLayout, mTargetCell, distance, true)) {
Adam Cohena65beee2011-06-27 21:32:23 -07002870 return true;
2871 }
Adam Cohenec0d61d2014-02-07 16:34:51 -08002872
2873 if (mAddToExistingFolderOnDrop && willAddToExistingUserFolder((ItemInfo) d.dragInfo,
2874 dropTargetLayout, mTargetCell, distance)) {
Adam Cohena65beee2011-06-27 21:32:23 -07002875 return true;
2876 }
2877
Adam Cohen482ed822012-03-02 14:15:13 -08002878 int[] resultSpan = new int[2];
Adam Cohenfa3c58f2013-12-06 16:10:55 -08002879 mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08002880 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
2881 null, mTargetCell, resultSpan, CellLayout.MODE_ACCEPT_DROP);
2882 boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
2883
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002884 // Don't accept the drop if there's no room for the item
Adam Cohen482ed822012-03-02 14:15:13 -08002885 if (!foundCell) {
Winson Chung96ef4092011-11-22 12:25:14 -08002886 // Don't show the message if we are dropping on the AllApps button and the hotseat
2887 // is full
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002888 boolean isHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
Winson Chung93eef082012-03-23 15:59:27 -07002889 if (mTargetCell != null && isHotseat) {
Winson Chung96ef4092011-11-22 12:25:14 -08002890 Hotseat hotseat = mLauncher.getHotseat();
Andrew Flynn0dca1ec2012-02-29 13:33:22 -08002891 if (hotseat.isAllAppsButtonRank(
Winson Chung96ef4092011-11-22 12:25:14 -08002892 hotseat.getOrderInHotseat(mTargetCell[0], mTargetCell[1]))) {
2893 return false;
2894 }
2895 }
2896
Winson Chung93eef082012-03-23 15:59:27 -07002897 mLauncher.showOutOfSpaceMessage(isHotseat);
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002898 return false;
2899 }
2900 }
Adam Cohendedbd962013-07-11 14:21:49 -07002901
2902 long screenId = getIdForScreen(dropTargetLayout);
2903 if (screenId == EXTRA_EMPTY_SCREEN_ID) {
2904 commitExtraEmptyScreen();
2905 }
2906
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002907 return true;
2908 }
2909
Adam Cohen482ed822012-03-02 14:15:13 -08002910 boolean willCreateUserFolder(ItemInfo info, CellLayout target, int[] targetCell, float
2911 distance, boolean considerTimeout) {
2912 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohena65beee2011-06-27 21:32:23 -07002913 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
2914
Adam Cohen19f37922012-03-21 11:59:11 -07002915 if (dropOverView != null) {
2916 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
2917 if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) {
2918 return false;
2919 }
2920 }
2921
Winson Chung3d503fb2011-07-13 17:25:49 -07002922 boolean hasntMoved = false;
2923 if (mDragInfo != null) {
Adam Cohen482ed822012-03-02 14:15:13 -08002924 hasntMoved = dropOverView == mDragInfo.cell;
Winson Chung3d503fb2011-07-13 17:25:49 -07002925 }
Adam Cohene0310962011-04-18 16:15:31 -07002926
Adam Cohena65beee2011-06-27 21:32:23 -07002927 if (dropOverView == null || hasntMoved || (considerTimeout && !mCreateUserFolderOnDrop)) {
2928 return false;
2929 }
Adam Cohene0310962011-04-18 16:15:31 -07002930
Adam Cohena65beee2011-06-27 21:32:23 -07002931 boolean aboveShortcut = (dropOverView.getTag() instanceof ShortcutInfo);
Adam Cohene0310962011-04-18 16:15:31 -07002932 boolean willBecomeShortcut =
Adam Cohenc0dcf592011-06-01 15:30:43 -07002933 (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
2934 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT);
Adam Cohene0310962011-04-18 16:15:31 -07002935
2936 return (aboveShortcut && willBecomeShortcut);
2937 }
2938
Adam Cohen482ed822012-03-02 14:15:13 -08002939 boolean willAddToExistingUserFolder(Object dragInfo, CellLayout target, int[] targetCell,
2940 float distance) {
2941 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohena65beee2011-06-27 21:32:23 -07002942 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohen19f37922012-03-21 11:59:11 -07002943
2944 if (dropOverView != null) {
2945 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
2946 if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) {
2947 return false;
2948 }
2949 }
2950
Adam Cohena65beee2011-06-27 21:32:23 -07002951 if (dropOverView instanceof FolderIcon) {
2952 FolderIcon fi = (FolderIcon) dropOverView;
2953 if (fi.acceptDrop(dragInfo)) {
2954 return true;
2955 }
2956 }
2957 return false;
2958 }
2959
Winson Chung3d503fb2011-07-13 17:25:49 -07002960 boolean createUserFolderIfNecessary(View newView, long container, CellLayout target,
Adam Cohen482ed822012-03-02 14:15:13 -08002961 int[] targetCell, float distance, boolean external, DragView dragView,
2962 Runnable postAnimationRunnable) {
2963 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohenc0dcf592011-06-01 15:30:43 -07002964 View v = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohen19f37922012-03-21 11:59:11 -07002965
Winson Chungec9a0a42011-07-20 20:42:13 -07002966 boolean hasntMoved = false;
2967 if (mDragInfo != null) {
2968 CellLayout cellParent = getParentCellLayoutForView(mDragInfo.cell);
2969 hasntMoved = (mDragInfo.cellX == targetCell[0] &&
2970 mDragInfo.cellY == targetCell[1]) && (cellParent == target);
2971 }
Adam Cohen10b17372011-04-15 14:21:25 -07002972
Adam Cohen19072da2011-05-31 14:30:45 -07002973 if (v == null || hasntMoved || !mCreateUserFolderOnDrop) return false;
2974 mCreateUserFolderOnDrop = false;
Adam Cohendcd297f2013-06-18 13:13:40 -07002975 final long screenId = (targetCell == null) ? mDragInfo.screenId : getIdForScreen(target);
Adam Cohen10b17372011-04-15 14:21:25 -07002976
2977 boolean aboveShortcut = (v.getTag() instanceof ShortcutInfo);
2978 boolean willBecomeShortcut = (newView.getTag() instanceof ShortcutInfo);
2979
2980 if (aboveShortcut && willBecomeShortcut) {
2981 ShortcutInfo sourceInfo = (ShortcutInfo) newView.getTag();
2982 ShortcutInfo destInfo = (ShortcutInfo) v.getTag();
2983 // if the drag started here, we need to remove it from the workspace
2984 if (!external) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002985 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
Adam Cohen10b17372011-04-15 14:21:25 -07002986 }
2987
Adam Cohend0445262011-07-04 23:53:22 -07002988 Rect folderLocation = new Rect();
Adam Cohenac8c8762011-07-13 11:15:27 -07002989 float scale = mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation);
Adam Cohen10b17372011-04-15 14:21:25 -07002990 target.removeView(v);
Adam Cohend0445262011-07-04 23:53:22 -07002991
Winson Chung3d503fb2011-07-13 17:25:49 -07002992 FolderIcon fi =
Adam Cohendcd297f2013-06-18 13:13:40 -07002993 mLauncher.addFolder(target, container, screenId, targetCell[0], targetCell[1]);
Adam Cohena9cf38f2011-05-02 15:36:58 -07002994 destInfo.cellX = -1;
2995 destInfo.cellY = -1;
2996 sourceInfo.cellX = -1;
2997 sourceInfo.cellY = -1;
Adam Cohend0445262011-07-04 23:53:22 -07002998
Adam Cohen558baaf2011-08-15 15:22:57 -07002999 // If the dragView is null, we can't animate
3000 boolean animate = dragView != null;
3001 if (animate) {
3002 fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale,
3003 postAnimationRunnable);
3004 } else {
3005 fi.addItem(destInfo);
3006 fi.addItem(sourceInfo);
3007 }
Adam Cohen10b17372011-04-15 14:21:25 -07003008 return true;
3009 }
3010 return false;
3011 }
3012
Adam Cohena65beee2011-06-27 21:32:23 -07003013 boolean addToExistingFolderIfNecessary(View newView, CellLayout target, int[] targetCell,
Adam Cohen482ed822012-03-02 14:15:13 -08003014 float distance, DragObject d, boolean external) {
3015 if (distance > mMaxDistanceForFolderCreation) return false;
3016
Adam Cohena65beee2011-06-27 21:32:23 -07003017 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003018 if (!mAddToExistingFolderOnDrop) return false;
3019 mAddToExistingFolderOnDrop = false;
Adam Cohen19f37922012-03-21 11:59:11 -07003020
Adam Cohenc0dcf592011-06-01 15:30:43 -07003021 if (dropOverView instanceof FolderIcon) {
3022 FolderIcon fi = (FolderIcon) dropOverView;
Adam Cohen3e8f8112011-07-02 18:03:00 -07003023 if (fi.acceptDrop(d.dragInfo)) {
3024 fi.onDrop(d);
Adam Cohenc0dcf592011-06-01 15:30:43 -07003025
3026 // if the drag started here, we need to remove it from the workspace
3027 if (!external) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003028 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
Adam Cohenc0dcf592011-06-01 15:30:43 -07003029 }
3030 return true;
3031 }
3032 }
3033 return false;
3034 }
3035
Adam Cohend41fbf52012-02-16 23:53:59 -08003036 public void onDrop(final DragObject d) {
Adam Cohencb3382b2011-05-24 14:07:08 -07003037 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, d.dragView,
Adam Cohene3e27a82011-04-15 12:07:39 -07003038 mDragViewVisualCenter);
3039
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003040 CellLayout dropTargetLayout = mDropToLayout;
3041
Adam Cohene3e27a82011-04-15 12:07:39 -07003042 // We want the point to be mapped to the dragTarget.
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003043 if (dropTargetLayout != null) {
3044 if (mLauncher.isHotseatLayout(dropTargetLayout)) {
Adam Cohenbb00ff22012-07-12 15:43:01 -07003045 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
Winson Chung3d503fb2011-07-13 17:25:49 -07003046 } else {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003047 mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null);
Winson Chung3d503fb2011-07-13 17:25:49 -07003048 }
Adam Cohenba781612011-05-09 14:37:39 -07003049 }
Michael Jurkac6ee42e2010-09-30 12:04:50 -07003050
Adam Cohened51cc92011-08-01 20:28:08 -07003051 int snapScreen = -1;
Adam Cohend41fbf52012-02-16 23:53:59 -08003052 boolean resizeOnDrop = false;
Adam Cohencb3382b2011-05-24 14:07:08 -07003053 if (d.dragSource != this) {
Adam Cohene3e27a82011-04-15 12:07:39 -07003054 final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0],
3055 (int) mDragViewVisualCenter[1] };
Adam Cohen3e8f8112011-07-02 18:03:00 -07003056 onDropExternal(touchXY, d.dragInfo, dropTargetLayout, false, d);
Patrick Dubroyce34a972010-10-19 10:34:32 -07003057 } else if (mDragInfo != null) {
Patrick Dubroyce34a972010-10-19 10:34:32 -07003058 final View cell = mDragInfo.cell;
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08003059
Adam Cohend41fbf52012-02-16 23:53:59 -08003060 Runnable resizeRunnable = null;
Michael Jurka1e2f4652013-07-08 18:03:46 -07003061 if (dropTargetLayout != null && !d.cancelled) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003062 // Move internally
Winson Chung40e882b2011-07-21 19:01:11 -07003063 boolean hasMovedLayouts = (getParentCellLayoutForView(cell) != dropTargetLayout);
3064 boolean hasMovedIntoHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
3065 long container = hasMovedIntoHotseat ?
Winson Chung3d503fb2011-07-13 17:25:49 -07003066 LauncherSettings.Favorites.CONTAINER_HOTSEAT :
3067 LauncherSettings.Favorites.CONTAINER_DESKTOP;
Adam Cohendcd297f2013-06-18 13:13:40 -07003068 long screenId = (mTargetCell[0] < 0) ?
3069 mDragInfo.screenId : getIdForScreen(dropTargetLayout);
Adam Cohenc0dcf592011-06-01 15:30:43 -07003070 int spanX = mDragInfo != null ? mDragInfo.spanX : 1;
3071 int spanY = mDragInfo != null ? mDragInfo.spanY : 1;
3072 // First we find the cell nearest to point at which the item is
3073 // dropped, without any consideration to whether there is an item there.
Adam Cohen482ed822012-03-02 14:15:13 -08003074
Adam Cohenc0dcf592011-06-01 15:30:43 -07003075 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], (int)
3076 mDragViewVisualCenter[1], spanX, spanY, dropTargetLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08003077 float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
3078 mDragViewVisualCenter[1], mTargetCell);
3079
Adam Cohenc0dcf592011-06-01 15:30:43 -07003080 // If the item being dropped is a shortcut and the nearest drop
Adam Cohen76078c42011-06-09 15:06:52 -07003081 // cell also contains a shortcut, then create a folder with the two shortcuts.
Winson Chung1c4cf4a2011-07-29 14:49:10 -07003082 if (!mInScrollArea && createUserFolderIfNecessary(cell, container,
Adam Cohen482ed822012-03-02 14:15:13 -08003083 dropTargetLayout, mTargetCell, distance, false, d.dragView, null)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07003084 return;
3085 }
3086
Adam Cohen482ed822012-03-02 14:15:13 -08003087 if (addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell,
3088 distance, d, false)) {
Adam Cohendf035382011-04-11 17:22:04 -07003089 return;
3090 }
3091
3092 // Aside from the special case where we're dropping a shortcut onto a shortcut,
3093 // we need to find the nearest cell location that is vacant
Adam Cohend41fbf52012-02-16 23:53:59 -08003094 ItemInfo item = (ItemInfo) d.dragInfo;
3095 int minSpanX = item.spanX;
3096 int minSpanY = item.spanY;
3097 if (item.minSpanX > 0 && item.minSpanY > 0) {
3098 minSpanX = item.minSpanX;
3099 minSpanY = item.minSpanY;
3100 }
Adam Cohen482ed822012-03-02 14:15:13 -08003101
Adam Cohend41fbf52012-02-16 23:53:59 -08003102 int[] resultSpan = new int[2];
Adam Cohenfa3c58f2013-12-06 16:10:55 -08003103 mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08003104 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, cell,
3105 mTargetCell, resultSpan, CellLayout.MODE_ON_DROP);
3106
Adam Cohend41fbf52012-02-16 23:53:59 -08003107 boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
Adam Cohen9e05a5e2012-09-10 15:53:09 -07003108
Adam Cohenaaa5c212012-10-05 18:14:31 -07003109 // if the widget resizes on drop
Adam Cohen9e05a5e2012-09-10 15:53:09 -07003110 if (foundCell && (cell instanceof AppWidgetHostView) &&
Adam Cohenaaa5c212012-10-05 18:14:31 -07003111 (resultSpan[0] != item.spanX || resultSpan[1] != item.spanY)) {
Adam Cohend41fbf52012-02-16 23:53:59 -08003112 resizeOnDrop = true;
3113 item.spanX = resultSpan[0];
3114 item.spanY = resultSpan[1];
Adam Cohen7bdfc972012-05-22 16:50:35 -07003115 AppWidgetHostView awhv = (AppWidgetHostView) cell;
3116 AppWidgetResizeFrame.updateWidgetSizeRanges(awhv, mLauncher, resultSpan[0],
3117 resultSpan[1]);
Adam Cohend41fbf52012-02-16 23:53:59 -08003118 }
Adam Cohendf035382011-04-11 17:22:04 -07003119
Adam Cohendcd297f2013-06-18 13:13:40 -07003120 if (getScreenIdForPageIndex(mCurrentPage) != screenId && !hasMovedIntoHotseat) {
3121 snapScreen = getPageIndexForScreenId(screenId);
3122 snapToPage(snapScreen);
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08003123 }
3124
Adam Cohend41fbf52012-02-16 23:53:59 -08003125 if (foundCell) {
3126 final ItemInfo info = (ItemInfo) cell.getTag();
Winson Chung40e882b2011-07-21 19:01:11 -07003127 if (hasMovedLayouts) {
Patrick Dubroy94383362010-10-29 15:03:24 -07003128 // Reparent the view
Sunny Goyal25611b12014-07-22 09:52:37 -07003129 CellLayout parentCell = getParentCellLayoutForView(cell);
3130 if (parentCell != null) {
3131 parentCell.removeView(cell);
3132 } else if (LauncherAppState.isDogfoodBuild()) {
3133 throw new NullPointerException("mDragInfo.cell has null parent");
3134 }
Adam Cohendcd297f2013-06-18 13:13:40 -07003135 addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1],
Adam Cohend41fbf52012-02-16 23:53:59 -08003136 info.spanX, info.spanY);
Patrick Dubroy94383362010-10-29 15:03:24 -07003137 }
3138
3139 // update the item's position after drop
Patrick Dubroy94383362010-10-29 15:03:24 -07003140 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
Adam Cohen19f37922012-03-21 11:59:11 -07003141 lp.cellX = lp.tmpCellX = mTargetCell[0];
3142 lp.cellY = lp.tmpCellY = mTargetCell[1];
Adam Cohend41fbf52012-02-16 23:53:59 -08003143 lp.cellHSpan = item.spanX;
3144 lp.cellVSpan = item.spanY;
Adam Cohen19f37922012-03-21 11:59:11 -07003145 lp.isLockedToGrid = true;
Patrick Dubroy94383362010-10-29 15:03:24 -07003146
Winson Chung3d503fb2011-07-13 17:25:49 -07003147 if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
3148 cell instanceof LauncherAppWidgetHostView) {
Adam Cohend4844c32011-02-18 19:25:06 -08003149 final CellLayout cellLayout = dropTargetLayout;
3150 // We post this call so that the widget has a chance to be placed
3151 // in its final location
3152
3153 final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell;
Sunny Goyal2434d402015-02-17 11:44:15 -08003154 AppWidgetProviderInfo pInfo = hostView.getAppWidgetInfo();
3155 if (pInfo != null && pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) {
Adam Cohend41fbf52012-02-16 23:53:59 -08003156 final Runnable addResizeFrame = new Runnable() {
Adam Cohend4844c32011-02-18 19:25:06 -08003157 public void run() {
Adam Cohen716b51e2011-06-30 12:09:54 -07003158 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohenc0dcf592011-06-01 15:30:43 -07003159 dragLayer.addResizeFrame(info, hostView, cellLayout);
Adam Cohend4844c32011-02-18 19:25:06 -08003160 }
Adam Cohen26976d92011-03-22 15:33:33 -07003161 };
Adam Cohend41fbf52012-02-16 23:53:59 -08003162 resizeRunnable = (new Runnable() {
Adam Cohen26976d92011-03-22 15:33:33 -07003163 public void run() {
3164 if (!isPageMoving()) {
Adam Cohend41fbf52012-02-16 23:53:59 -08003165 addResizeFrame.run();
Adam Cohen26976d92011-03-22 15:33:33 -07003166 } else {
Adam Cohend41fbf52012-02-16 23:53:59 -08003167 mDelayedResizeRunnable = addResizeFrame;
Adam Cohen26976d92011-03-22 15:33:33 -07003168 }
3169 }
Adam Cohend4844c32011-02-18 19:25:06 -08003170 });
3171 }
3172 }
3173
Adam Cohen949debe2013-09-29 14:22:48 -07003174 LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId, lp.cellX,
3175 lp.cellY, item.spanX, item.spanY);
Adam Cohend41fbf52012-02-16 23:53:59 -08003176 } else {
3177 // If we can't find a drop location, we return the item to its original position
3178 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
3179 mTargetCell[0] = lp.cellX;
3180 mTargetCell[1] = lp.cellY;
Adam Cohenf1dcdf62012-05-10 16:51:52 -07003181 CellLayout layout = (CellLayout) cell.getParent().getParent();
3182 layout.markCellsAsOccupiedForView(cell);
Patrick Dubroycd68ff52010-10-28 17:57:05 -07003183 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003184 }
Patrick Dubroyce34a972010-10-19 10:34:32 -07003185
Michael Jurka8c920dd2011-01-20 14:16:56 -08003186 final CellLayout parent = (CellLayout) cell.getParent().getParent();
Adam Cohend41fbf52012-02-16 23:53:59 -08003187 final Runnable finalResizeRunnable = resizeRunnable;
Patrick Dubroyce34a972010-10-19 10:34:32 -07003188 // Prepare it to be animated into its new position
3189 // This must be called after the view has been re-parented
Adam Cohend41fbf52012-02-16 23:53:59 -08003190 final Runnable onCompleteRunnable = new Runnable() {
Michael Jurkad74c9842011-07-10 12:44:21 -07003191 @Override
3192 public void run() {
3193 mAnimatingViewIntoPlace = false;
Michael Jurka3a0469d2012-06-21 09:38:41 -07003194 updateChildrenLayersEnabled(false);
Adam Cohend41fbf52012-02-16 23:53:59 -08003195 if (finalResizeRunnable != null) {
3196 finalResizeRunnable.run();
3197 }
Michael Jurkad74c9842011-07-10 12:44:21 -07003198 }
3199 };
3200 mAnimatingViewIntoPlace = true;
Adam Cohenfc53cd22011-07-20 15:45:11 -07003201 if (d.dragView.hasDrawn()) {
Adam Cohend41fbf52012-02-16 23:53:59 -08003202 final ItemInfo info = (ItemInfo) cell.getTag();
Adam Cohen59400422014-03-05 18:07:04 -08003203 boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
3204 || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
3205 if (isWidget) {
Adam Cohend41fbf52012-02-16 23:53:59 -08003206 int animationType = resizeOnDrop ? ANIMATE_INTO_POSITION_AND_RESIZE :
3207 ANIMATE_INTO_POSITION_AND_DISAPPEAR;
3208 animateWidgetDrop(info, parent, d.dragView,
3209 onCompleteRunnable, animationType, cell, false);
3210 } else {
Adam Cohen85b467b2012-02-29 15:38:46 -08003211 int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION;
3212 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration,
3213 onCompleteRunnable, this);
Adam Cohend41fbf52012-02-16 23:53:59 -08003214 }
Adam Cohenfc53cd22011-07-20 15:45:11 -07003215 } else {
Winson Chung7bd1bbb2012-02-13 18:29:29 -08003216 d.deferDragViewCleanupPostAnimation = false;
Adam Cohenfc53cd22011-07-20 15:45:11 -07003217 cell.setVisibility(VISIBLE);
3218 }
Adam Cohen716b51e2011-06-30 12:09:54 -07003219 parent.onDropChild(cell);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003220 }
3221 }
3222
Adam Cohendcd297f2013-06-18 13:13:40 -07003223 public void setFinalScrollForPageChange(int pageIndex) {
3224 CellLayout cl = (CellLayout) getChildAt(pageIndex);
3225 if (cl != null) {
Adam Cohened51cc92011-08-01 20:28:08 -07003226 mSavedScrollX = getScrollX();
Adam Cohened51cc92011-08-01 20:28:08 -07003227 mSavedTranslationX = cl.getTranslationX();
3228 mSavedRotationY = cl.getRotationY();
Adam Cohenedb40762013-07-18 16:45:45 -07003229 final int newX = getScrollForPage(pageIndex);
Adam Cohened51cc92011-08-01 20:28:08 -07003230 setScrollX(newX);
3231 cl.setTranslationX(0f);
3232 cl.setRotationY(0f);
3233 }
3234 }
3235
Adam Cohendcd297f2013-06-18 13:13:40 -07003236 public void resetFinalScrollForPageChange(int pageIndex) {
3237 if (pageIndex >= 0) {
3238 CellLayout cl = (CellLayout) getChildAt(pageIndex);
Adam Cohened51cc92011-08-01 20:28:08 -07003239 setScrollX(mSavedScrollX);
3240 cl.setTranslationX(mSavedTranslationX);
3241 cl.setRotationY(mSavedRotationY);
3242 }
3243 }
3244
Adam Cohen76078c42011-06-09 15:06:52 -07003245 public void getViewLocationRelativeToSelf(View v, int[] location) {
Adam Cohen8dfcba42011-07-07 16:38:18 -07003246 getLocationInWindow(location);
Adam Cohene3e27a82011-04-15 12:07:39 -07003247 int x = location[0];
3248 int y = location[1];
3249
Adam Cohen8dfcba42011-07-07 16:38:18 -07003250 v.getLocationInWindow(location);
Adam Cohene3e27a82011-04-15 12:07:39 -07003251 int vX = location[0];
3252 int vY = location[1];
3253
3254 location[0] = vX - x;
3255 location[1] = vY - y;
3256 }
3257
Adam Cohencb3382b2011-05-24 14:07:08 -07003258 public void onDragEnter(DragObject d) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003259 mDragEnforcer.onDragEnter();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003260 mCreateUserFolderOnDrop = false;
3261 mAddToExistingFolderOnDrop = false;
3262
3263 mDropToLayout = null;
3264 CellLayout layout = getCurrentDropLayout();
3265 setCurrentDropLayout(layout);
3266 setCurrentDragOverlappingLayout(layout);
Winson Chungb26f3d62011-06-02 10:49:29 -07003267
Adam Cohenc50438c2014-08-19 17:43:05 -07003268 if (!workspaceInModalState()) {
3269 mLauncher.getDragLayer().showPageHints();
Michael Jurkad718d6a2010-10-14 15:35:17 -07003270 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003271 }
3272
Winson Chungfe411c82013-09-26 16:07:17 -07003273 /** Return a rect that has the cellWidth/cellHeight (left, top), and
3274 * widthGap/heightGap (right, bottom) */
Adam Cohena897f392012-04-27 18:12:05 -07003275 static Rect getCellLayoutMetrics(Launcher launcher, int orientation) {
Winson Chung5f8afe62013-08-12 16:19:28 -07003276 LauncherAppState app = LauncherAppState.getInstance();
3277 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
3278
Adam Cohena897f392012-04-27 18:12:05 -07003279 Display display = launcher.getWindowManager().getDefaultDisplay();
3280 Point smallestSize = new Point();
3281 Point largestSize = new Point();
3282 display.getCurrentSizeRange(smallestSize, largestSize);
Winson Chung892c74d2013-08-22 16:15:50 -07003283 int countX = (int) grid.numColumns;
3284 int countY = (int) grid.numRows;
Adam Cohena897f392012-04-27 18:12:05 -07003285 if (orientation == CellLayout.LANDSCAPE) {
3286 if (mLandscapeCellLayoutMetrics == null) {
Winson Chung5f8afe62013-08-12 16:19:28 -07003287 Rect padding = grid.getWorkspacePadding(CellLayout.LANDSCAPE);
Winson Chungdcd27ba2013-12-11 15:28:15 -08003288 int width = largestSize.x - padding.left - padding.right;
3289 int height = smallestSize.y - padding.top - padding.bottom;
Adam Cohena897f392012-04-27 18:12:05 -07003290 mLandscapeCellLayoutMetrics = new Rect();
Winson Chungfe411c82013-09-26 16:07:17 -07003291 mLandscapeCellLayoutMetrics.set(
3292 grid.calculateCellWidth(width, countX),
3293 grid.calculateCellHeight(height, countY), 0, 0);
Adam Cohena897f392012-04-27 18:12:05 -07003294 }
3295 return mLandscapeCellLayoutMetrics;
3296 } else if (orientation == CellLayout.PORTRAIT) {
3297 if (mPortraitCellLayoutMetrics == null) {
Winson Chung5f8afe62013-08-12 16:19:28 -07003298 Rect padding = grid.getWorkspacePadding(CellLayout.PORTRAIT);
Winson Chungdcd27ba2013-12-11 15:28:15 -08003299 int width = smallestSize.x - padding.left - padding.right;
3300 int height = largestSize.y - padding.top - padding.bottom;
Adam Cohena897f392012-04-27 18:12:05 -07003301 mPortraitCellLayoutMetrics = new Rect();
Winson Chungfe411c82013-09-26 16:07:17 -07003302 mPortraitCellLayoutMetrics.set(
3303 grid.calculateCellWidth(width, countX),
3304 grid.calculateCellHeight(height, countY), 0, 0);
Adam Cohena897f392012-04-27 18:12:05 -07003305 }
3306 return mPortraitCellLayoutMetrics;
3307 }
3308 return null;
3309 }
3310
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003311 public void onDragExit(DragObject d) {
3312 mDragEnforcer.onDragExit();
Winson Chungc07918d2011-07-01 15:35:26 -07003313
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003314 // Here we store the final page that will be dropped to, if the workspace in fact
3315 // receives the drop
3316 if (mInScrollArea) {
Winson Chungcc1cfe42012-06-18 15:09:17 -07003317 if (isPageMoving()) {
3318 // If the user drops while the page is scrolling, we should use that page as the
3319 // destination instead of the page that is being hovered over.
3320 mDropToLayout = (CellLayout) getPageAt(getNextPage());
3321 } else {
3322 mDropToLayout = mDragOverlappingLayout;
3323 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003324 } else {
3325 mDropToLayout = mDragTargetLayout;
3326 }
3327
3328 if (mDragMode == DRAG_MODE_CREATE_FOLDER) {
3329 mCreateUserFolderOnDrop = true;
3330 } else if (mDragMode == DRAG_MODE_ADD_TO_FOLDER) {
3331 mAddToExistingFolderOnDrop = true;
Adam Cohen482ed822012-03-02 14:15:13 -08003332 }
3333
Winson Chungc07918d2011-07-01 15:35:26 -07003334 // Reset the scroll area and previous drag target
3335 onResetScrollArea();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003336 setCurrentDropLayout(null);
3337 setCurrentDragOverlappingLayout(null);
Winson Chungc07918d2011-07-01 15:35:26 -07003338
Adam Cohen74c28d12011-11-18 14:17:11 -08003339 mSpringLoadedDragController.cancel();
Winson Chungc07918d2011-07-01 15:35:26 -07003340
3341 if (!mIsPageMoving) {
3342 hideOutlines();
3343 }
Adam Cohenc50438c2014-08-19 17:43:05 -07003344 mLauncher.getDragLayer().hidePageHints();
Winson Chungc07918d2011-07-01 15:35:26 -07003345 }
3346
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003347 void setCurrentDropLayout(CellLayout layout) {
3348 if (mDragTargetLayout != null) {
3349 mDragTargetLayout.revertTempState();
3350 mDragTargetLayout.onDragExit();
3351 }
3352 mDragTargetLayout = layout;
3353 if (mDragTargetLayout != null) {
3354 mDragTargetLayout.onDragEnter();
3355 }
3356 cleanupReorder(true);
3357 cleanupFolderCreation();
3358 setCurrentDropOverCell(-1, -1);
3359 }
3360
3361 void setCurrentDragOverlappingLayout(CellLayout layout) {
3362 if (mDragOverlappingLayout != null) {
3363 mDragOverlappingLayout.setIsDragOverlapping(false);
3364 }
3365 mDragOverlappingLayout = layout;
3366 if (mDragOverlappingLayout != null) {
3367 mDragOverlappingLayout.setIsDragOverlapping(true);
3368 }
3369 invalidate();
3370 }
3371
3372 void setCurrentDropOverCell(int x, int y) {
3373 if (x != mDragOverX || y != mDragOverY) {
3374 mDragOverX = x;
3375 mDragOverY = y;
3376 setDragMode(DRAG_MODE_NONE);
3377 }
3378 }
3379
3380 void setDragMode(int dragMode) {
3381 if (dragMode != mDragMode) {
3382 if (dragMode == DRAG_MODE_NONE) {
3383 cleanupAddToFolder();
3384 // We don't want to cancel the re-order alarm every time the target cell changes
3385 // as this feels to slow / unresponsive.
3386 cleanupReorder(false);
3387 cleanupFolderCreation();
3388 } else if (dragMode == DRAG_MODE_ADD_TO_FOLDER) {
3389 cleanupReorder(true);
3390 cleanupFolderCreation();
3391 } else if (dragMode == DRAG_MODE_CREATE_FOLDER) {
3392 cleanupAddToFolder();
3393 cleanupReorder(true);
3394 } else if (dragMode == DRAG_MODE_REORDER) {
3395 cleanupAddToFolder();
3396 cleanupFolderCreation();
3397 }
3398 mDragMode = dragMode;
3399 }
3400 }
3401
3402 private void cleanupFolderCreation() {
3403 if (mDragFolderRingAnimator != null) {
3404 mDragFolderRingAnimator.animateToNaturalState();
Adam Cohen8ec05f92013-10-13 12:29:03 -07003405 mDragFolderRingAnimator = null;
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003406 }
Adam Cohen8ec05f92013-10-13 12:29:03 -07003407 mFolderCreationAlarm.setOnAlarmListener(null);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003408 mFolderCreationAlarm.cancelAlarm();
3409 }
3410
3411 private void cleanupAddToFolder() {
3412 if (mDragOverFolderIcon != null) {
3413 mDragOverFolderIcon.onDragExit(null);
3414 mDragOverFolderIcon = null;
3415 }
3416 }
3417
3418 private void cleanupReorder(boolean cancelAlarm) {
3419 // Any pending reorders are canceled
3420 if (cancelAlarm) {
3421 mReorderAlarm.cancelAlarm();
3422 }
3423 mLastReorderX = -1;
3424 mLastReorderY = -1;
Winson Chungc07918d2011-07-01 15:35:26 -07003425 }
3426
Michael Jurka4516c112010-10-07 15:13:47 -07003427 /*
3428 *
3429 * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
3430 * coordinate space. The argument xy is modified with the return result.
3431 *
3432 * if cachedInverseMatrix is not null, this method will just use that matrix instead of
Michael Jurkad718d6a2010-10-14 15:35:17 -07003433 * computing it itself; we use this to avoid redundant matrix inversions in
Michael Jurka4516c112010-10-07 15:13:47 -07003434 * findMatchingPageForDragOver
3435 *
3436 */
3437 void mapPointFromSelfToChild(View v, float[] xy, Matrix cachedInverseMatrix) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003438 xy[0] = xy[0] - v.getLeft();
3439 xy[1] = xy[1] - v.getTop();
Michael Jurka4516c112010-10-07 15:13:47 -07003440 }
3441
Adam Cohen7d30a372013-07-01 17:03:59 -07003442 boolean isPointInSelfOverHotseat(int x, int y, Rect r) {
3443 if (r == null) {
3444 r = new Rect();
3445 }
3446 mTempPt[0] = x;
3447 mTempPt[1] = y;
3448 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempPt, true);
Winson Chungabedd9f2013-09-24 15:41:09 -07003449
3450 LauncherAppState app = LauncherAppState.getInstance();
3451 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
3452 r = grid.getHotseatRect();
Adam Cohen7d30a372013-07-01 17:03:59 -07003453 if (r.contains(mTempPt[0], mTempPt[1])) {
3454 return true;
3455 }
3456 return false;
3457 }
Winson Chung3d503fb2011-07-13 17:25:49 -07003458
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003459 void mapPointFromSelfToHotseatLayout(Hotseat hotseat, float[] xy) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003460 mTempPt[0] = (int) xy[0];
3461 mTempPt[1] = (int) xy[1];
3462 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempPt, true);
Winson Chung156ab5b2013-07-12 14:14:16 -07003463 mLauncher.getDragLayer().mapCoordInSelfToDescendent(hotseat.getLayout(), mTempPt);
Adam Cohen7d30a372013-07-01 17:03:59 -07003464
3465 xy[0] = mTempPt[0];
3466 xy[1] = mTempPt[1];
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003467 }
3468
Winson Chung3d503fb2011-07-13 17:25:49 -07003469 /*
Michael Jurka4516c112010-10-07 15:13:47 -07003470 *
3471 * Convert the 2D coordinate xy from this CellLayout's coordinate space to
3472 * the parent View's coordinate space. The argument xy is modified with the return result.
3473 *
3474 */
3475 void mapPointFromChildToSelf(View v, float[] xy) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003476 xy[0] += v.getLeft();
3477 xy[1] += v.getTop();
Michael Jurka4516c112010-10-07 15:13:47 -07003478 }
3479
Adam Cohene3e27a82011-04-15 12:07:39 -07003480 static private float squaredDistance(float[] point1, float[] point2) {
Michael Jurka4516c112010-10-07 15:13:47 -07003481 float distanceX = point1[0] - point2[0];
3482 float distanceY = point2[1] - point2[1];
3483 return distanceX * distanceX + distanceY * distanceY;
Adam Cohene3e27a82011-04-15 12:07:39 -07003484 }
Michael Jurka0280c3b2010-09-17 15:00:07 -07003485
Michael Jurka4516c112010-10-07 15:13:47 -07003486 /*
3487 *
Michael Jurka4516c112010-10-07 15:13:47 -07003488 * This method returns the CellLayout that is currently being dragged to. In order to drag
3489 * to a CellLayout, either the touch point must be directly over the CellLayout, or as a second
3490 * strategy, we see if the dragView is overlapping any CellLayout and choose the closest one
3491 *
3492 * Return null if no CellLayout is currently being dragged over
3493 *
3494 */
3495 private CellLayout findMatchingPageForDragOver(
Adam Cohen00618752011-07-20 12:06:04 -07003496 DragView dragView, float originX, float originY, boolean exact) {
Michael Jurka4516c112010-10-07 15:13:47 -07003497 // We loop through all the screens (ie CellLayouts) and see which ones overlap
3498 // with the item being dragged and then choose the one that's closest to the touch point
Michael Jurkaa63c4522010-08-19 13:52:27 -07003499 final int screenCount = getChildCount();
3500 CellLayout bestMatchingScreen = null;
Michael Jurka0280c3b2010-09-17 15:00:07 -07003501 float smallestDistSoFar = Float.MAX_VALUE;
Michael Jurka4516c112010-10-07 15:13:47 -07003502
Michael Jurkaa63c4522010-08-19 13:52:27 -07003503 for (int i = 0; i < screenCount; i++) {
Adam Cohendf041bc2013-09-22 12:32:22 -07003504 // The custom content screen is not a valid drag over option
3505 if (mScreenOrder.get(i) == CUSTOM_CONTENT_SCREEN_ID) {
3506 continue;
3507 }
3508
Winson Chung3d503fb2011-07-13 17:25:49 -07003509 CellLayout cl = (CellLayout) getChildAt(i);
Michael Jurkaa63c4522010-08-19 13:52:27 -07003510
Adam Cohen00618752011-07-20 12:06:04 -07003511 final float[] touchXy = {originX, originY};
Michael Jurka4516c112010-10-07 15:13:47 -07003512 // Transform the touch coordinates to the CellLayout's local coordinates
3513 // If the touch point is within the bounds of the cell layout, we can return immediately
Michael Jurka0280c3b2010-09-17 15:00:07 -07003514 cl.getMatrix().invert(mTempInverseMatrix);
Michael Jurka4516c112010-10-07 15:13:47 -07003515 mapPointFromSelfToChild(cl, touchXy, mTempInverseMatrix);
Michael Jurkaa63c4522010-08-19 13:52:27 -07003516
Michael Jurka4516c112010-10-07 15:13:47 -07003517 if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() &&
3518 touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) {
3519 return cl;
3520 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07003521
Winson Chung96ef4092011-11-22 12:25:14 -08003522 if (!exact) {
Michael Jurka4516c112010-10-07 15:13:47 -07003523 // Get the center of the cell layout in screen coordinates
3524 final float[] cellLayoutCenter = mTempCellLayoutCenterCoordinates;
3525 cellLayoutCenter[0] = cl.getWidth()/2;
3526 cellLayoutCenter[1] = cl.getHeight()/2;
3527 mapPointFromChildToSelf(cl, cellLayoutCenter);
Michael Jurka0280c3b2010-09-17 15:00:07 -07003528
Adam Cohen00618752011-07-20 12:06:04 -07003529 touchXy[0] = originX;
3530 touchXy[1] = originY;
Michael Jurka0280c3b2010-09-17 15:00:07 -07003531
Michael Jurka4516c112010-10-07 15:13:47 -07003532 // Calculate the distance between the center of the CellLayout
3533 // and the touch point
3534 float dist = squaredDistance(touchXy, cellLayoutCenter);
3535
3536 if (dist < smallestDistSoFar) {
Michael Jurka0280c3b2010-09-17 15:00:07 -07003537 smallestDistSoFar = dist;
Michael Jurkaa63c4522010-08-19 13:52:27 -07003538 bestMatchingScreen = cl;
Michael Jurkaa63c4522010-08-19 13:52:27 -07003539 }
Michael Jurka4516c112010-10-07 15:13:47 -07003540 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07003541 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07003542 return bestMatchingScreen;
3543 }
3544
Adam Cohene3e27a82011-04-15 12:07:39 -07003545 // This is used to compute the visual center of the dragView. This point is then
3546 // used to visualize drop locations and determine where to drop an item. The idea is that
3547 // the visual center represents the user's interpretation of where the item is, and hence
3548 // is the appropriate point to use when determining drop location.
3549 private float[] getDragViewVisualCenter(int x, int y, int xOffset, int yOffset,
3550 DragView dragView, float[] recycle) {
3551 float res[];
3552 if (recycle == null) {
3553 res = new float[2];
3554 } else {
3555 res = recycle;
3556 }
3557
3558 // First off, the drag view has been shifted in a way that is not represented in the
3559 // x and y values or the x/yOffsets. Here we account for that shift.
3560 x += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetX);
3561 y += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY);
3562
3563 // These represent the visual top and left of drag view if a dragRect was provided.
3564 // If a dragRect was not provided, then they correspond to the actual view left and
3565 // top, as the dragRect is in that case taken to be the entire dragView.
3566 // R.dimen.dragViewOffsetY.
3567 int left = x - xOffset;
3568 int top = y - yOffset;
3569
3570 // In order to find the visual center, we shift by half the dragRect
3571 res[0] = left + dragView.getDragRegion().width() / 2;
3572 res[1] = top + dragView.getDragRegion().height() / 2;
3573
3574 return res;
3575 }
3576
Winson Chungea359c62011-08-03 17:06:35 -07003577 private boolean isDragWidget(DragObject d) {
3578 return (d.dragInfo instanceof LauncherAppWidgetInfo ||
3579 d.dragInfo instanceof PendingAddWidgetInfo);
3580 }
3581 private boolean isExternalDragWidget(DragObject d) {
3582 return d.dragSource != this && isDragWidget(d);
3583 }
3584
Adam Cohencb3382b2011-05-24 14:07:08 -07003585 public void onDragOver(DragObject d) {
Winson Chungc07918d2011-07-01 15:35:26 -07003586 // Skip drag over events while we are dragging over side pages
Adam Cohen6c5891a2014-07-09 23:53:15 -07003587 if (mInScrollArea || !transitionStateShouldAllowDrop()) return;
Winson Chungc07918d2011-07-01 15:35:26 -07003588
Winson Chung4afe9b32011-07-27 17:46:20 -07003589 Rect r = new Rect();
Winson Chung3d503fb2011-07-13 17:25:49 -07003590 CellLayout layout = null;
Winson Chungc07918d2011-07-01 15:35:26 -07003591 ItemInfo item = (ItemInfo) d.dragInfo;
Sunny Goyal25611b12014-07-22 09:52:37 -07003592 if (item == null) {
3593 if (LauncherAppState.isDogfoodBuild()) {
3594 throw new NullPointerException("DragObject has null info");
3595 }
3596 return;
3597 }
Winson Chungc07918d2011-07-01 15:35:26 -07003598
3599 // Ensure that we have proper spans for the item that we are dropping
3600 if (item.spanX < 0 || item.spanY < 0) throw new RuntimeException("Improper spans found");
Adam Cohen00618752011-07-20 12:06:04 -07003601 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset,
Winson Chung4afe9b32011-07-27 17:46:20 -07003602 d.dragView, mDragViewVisualCenter);
Adam Cohen00618752011-07-20 12:06:04 -07003603
Adam Cohen482ed822012-03-02 14:15:13 -08003604 final View child = (mDragInfo == null) ? null : mDragInfo.cell;
Winson Chungc07918d2011-07-01 15:35:26 -07003605 // Identify whether we have dragged over a side page
Adam Cohen6c5891a2014-07-09 23:53:15 -07003606 if (workspaceInModalState()) {
Winson Chungea359c62011-08-03 17:06:35 -07003607 if (mLauncher.getHotseat() != null && !isExternalDragWidget(d)) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003608 if (isPointInSelfOverHotseat(d.x, d.y, r)) {
Winson Chung4afe9b32011-07-27 17:46:20 -07003609 layout = mLauncher.getHotseat().getLayout();
3610 }
3611 }
3612 if (layout == null) {
Winson Chung96ef4092011-11-22 12:25:14 -08003613 layout = findMatchingPageForDragOver(d.dragView, d.x, d.y, false);
Winson Chung4afe9b32011-07-27 17:46:20 -07003614 }
Winson Chungc07918d2011-07-01 15:35:26 -07003615 if (layout != mDragTargetLayout) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003616 setCurrentDropLayout(layout);
3617 setCurrentDragOverlappingLayout(layout);
Winson Chungc07918d2011-07-01 15:35:26 -07003618
Michael Jurkad74c9842011-07-10 12:44:21 -07003619 boolean isInSpringLoadedMode = (mState == State.SPRING_LOADED);
Winson Chungc07918d2011-07-01 15:35:26 -07003620 if (isInSpringLoadedMode) {
Winson Chung4afe9b32011-07-27 17:46:20 -07003621 if (mLauncher.isHotseatLayout(layout)) {
3622 mSpringLoadedDragController.cancel();
3623 } else {
3624 mSpringLoadedDragController.setAlarm(mDragTargetLayout);
3625 }
Winson Chungc07918d2011-07-01 15:35:26 -07003626 }
3627 }
3628 } else {
Winson Chung3d503fb2011-07-13 17:25:49 -07003629 // Test to see if we are over the hotseat otherwise just use the current page
Winson Chungea359c62011-08-03 17:06:35 -07003630 if (mLauncher.getHotseat() != null && !isDragWidget(d)) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003631 if (isPointInSelfOverHotseat(d.x, d.y, r)) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003632 layout = mLauncher.getHotseat().getLayout();
3633 }
3634 }
3635 if (layout == null) {
3636 layout = getCurrentDropLayout();
3637 }
Winson Chungc07918d2011-07-01 15:35:26 -07003638 if (layout != mDragTargetLayout) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003639 setCurrentDropLayout(layout);
3640 setCurrentDragOverlappingLayout(layout);
Winson Chungc07918d2011-07-01 15:35:26 -07003641 }
3642 }
3643
3644 // Handle the drag over
3645 if (mDragTargetLayout != null) {
Winson Chungc07918d2011-07-01 15:35:26 -07003646 // We want the point to be mapped to the dragTarget.
Winson Chung3d503fb2011-07-13 17:25:49 -07003647 if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003648 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
Winson Chung3d503fb2011-07-13 17:25:49 -07003649 } else {
3650 mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
3651 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003652
Winson Chungc07918d2011-07-01 15:35:26 -07003653 ItemInfo info = (ItemInfo) d.dragInfo;
Patrick Dubroy1262e362010-10-06 15:49:50 -07003654
Adam Cohen74c54912013-09-29 14:48:04 -07003655 int minSpanX = item.spanX;
3656 int minSpanY = item.spanY;
3657 if (item.minSpanX > 0 && item.minSpanY > 0) {
3658 minSpanX = item.minSpanX;
3659 minSpanY = item.minSpanY;
3660 }
3661
Winson Chungc07918d2011-07-01 15:35:26 -07003662 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
Adam Cohen74c54912013-09-29 14:48:04 -07003663 (int) mDragViewVisualCenter[1], minSpanX, minSpanY,
Adam Cohend024f982012-05-23 18:26:45 -07003664 mDragTargetLayout, mTargetCell);
Adam Cohen74c54912013-09-29 14:48:04 -07003665 int reorderX = mTargetCell[0];
3666 int reorderY = mTargetCell[1];
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003667
3668 setCurrentDropOverCell(mTargetCell[0], mTargetCell[1]);
3669
Adam Cohen482ed822012-03-02 14:15:13 -08003670 float targetCellDistance = mDragTargetLayout.getDistanceFromCell(
3671 mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell);
3672
Winson Chungc07918d2011-07-01 15:35:26 -07003673 final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0],
3674 mTargetCell[1]);
Winson Chung785d2eb2011-04-14 16:08:02 -07003675
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003676 manageFolderFeedback(info, mDragTargetLayout, mTargetCell,
3677 targetCellDistance, dragOverView);
Adam Cohen482ed822012-03-02 14:15:13 -08003678
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003679 boolean nearestDropOccupied = mDragTargetLayout.isNearestDropLocationOccupied((int)
3680 mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], item.spanX,
3681 item.spanY, child, mTargetCell);
3682
3683 if (!nearestDropOccupied) {
Adam Cohen19f37922012-03-21 11:59:11 -07003684 mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
3685 (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
3686 mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, false,
3687 d.dragView.getDragVisualizeOffset(), d.dragView.getDragRegion());
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003688 } else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
Adam Cohen74c54912013-09-29 14:48:04 -07003689 && !mReorderAlarm.alarmPending() && (mLastReorderX != reorderX ||
3690 mLastReorderY != reorderY)) {
Adam Cohend024f982012-05-23 18:26:45 -07003691
Adam Cohenfa3c58f2013-12-06 16:10:55 -08003692 int[] resultSpan = new int[2];
3693 mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0],
3694 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, item.spanX, item.spanY,
3695 child, mTargetCell, resultSpan, CellLayout.MODE_SHOW_REORDER_HINT);
3696
Adam Cohen19f37922012-03-21 11:59:11 -07003697 // Otherwise, if we aren't adding to or creating a folder and there's no pending
3698 // reorder, then we schedule a reorder
Adam Cohen482ed822012-03-02 14:15:13 -08003699 ReorderAlarmListener listener = new ReorderAlarmListener(mDragViewVisualCenter,
3700 minSpanX, minSpanY, item.spanX, item.spanY, d.dragView, child);
3701 mReorderAlarm.setOnAlarmListener(listener);
3702 mReorderAlarm.setAlarm(REORDER_TIMEOUT);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003703 }
3704
3705 if (mDragMode == DRAG_MODE_CREATE_FOLDER || mDragMode == DRAG_MODE_ADD_TO_FOLDER ||
3706 !nearestDropOccupied) {
3707 if (mDragTargetLayout != null) {
3708 mDragTargetLayout.revertTempState();
Michael Jurkad3ef3062010-11-23 16:23:58 -08003709 }
3710 }
Adam Cohen482ed822012-03-02 14:15:13 -08003711 }
3712 }
3713
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003714 private void manageFolderFeedback(ItemInfo info, CellLayout targetLayout,
3715 int[] targetCell, float distance, View dragOverView) {
Adam Cohen482ed822012-03-02 14:15:13 -08003716 boolean userFolderPending = willCreateUserFolder(info, targetLayout, targetCell, distance,
3717 false);
3718
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003719 if (mDragMode == DRAG_MODE_NONE && userFolderPending &&
3720 !mFolderCreationAlarm.alarmPending()) {
Adam Cohen482ed822012-03-02 14:15:13 -08003721 mFolderCreationAlarm.setOnAlarmListener(new
3722 FolderCreationAlarmListener(targetLayout, targetCell[0], targetCell[1]));
3723 mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003724 return;
Adam Cohen482ed822012-03-02 14:15:13 -08003725 }
3726
3727 boolean willAddToFolder =
3728 willAddToExistingUserFolder(info, targetLayout, targetCell, distance);
3729
3730 if (willAddToFolder && mDragMode == DRAG_MODE_NONE) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003731 mDragOverFolderIcon = ((FolderIcon) dragOverView);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003732 mDragOverFolderIcon.onDragEnter(info);
Adam Cohen482ed822012-03-02 14:15:13 -08003733 if (targetLayout != null) {
3734 targetLayout.clearDragOutlines();
Winson Chungc07918d2011-07-01 15:35:26 -07003735 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003736 setDragMode(DRAG_MODE_ADD_TO_FOLDER);
3737 return;
Patrick Dubroy976ebec2010-08-04 20:03:37 -07003738 }
Adam Cohen482ed822012-03-02 14:15:13 -08003739
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003740 if (mDragMode == DRAG_MODE_ADD_TO_FOLDER && !willAddToFolder) {
3741 setDragMode(DRAG_MODE_NONE);
3742 }
3743 if (mDragMode == DRAG_MODE_CREATE_FOLDER && !userFolderPending) {
3744 setDragMode(DRAG_MODE_NONE);
Adam Cohen482ed822012-03-02 14:15:13 -08003745 }
3746
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003747 return;
Adam Cohenc0dcf592011-06-01 15:30:43 -07003748 }
3749
Adam Cohen19072da2011-05-31 14:30:45 -07003750 class FolderCreationAlarmListener implements OnAlarmListener {
Adam Cohen69ce2e52011-07-03 19:25:21 -07003751 CellLayout layout;
3752 int cellX;
3753 int cellY;
Adam Cohen19072da2011-05-31 14:30:45 -07003754
Adam Cohen69ce2e52011-07-03 19:25:21 -07003755 public FolderCreationAlarmListener(CellLayout layout, int cellX, int cellY) {
3756 this.layout = layout;
3757 this.cellX = cellX;
3758 this.cellY = cellY;
Adam Cohen19072da2011-05-31 14:30:45 -07003759 }
3760
3761 public void onAlarm(Alarm alarm) {
Adam Cohen8ec05f92013-10-13 12:29:03 -07003762 if (mDragFolderRingAnimator != null) {
3763 // This shouldn't happen ever, but just in case, make sure we clean up the mess.
3764 mDragFolderRingAnimator.animateToNaturalState();
Adam Cohen19072da2011-05-31 14:30:45 -07003765 }
Adam Cohen8ec05f92013-10-13 12:29:03 -07003766 mDragFolderRingAnimator = new FolderRingAnimator(mLauncher, null);
Adam Cohen69ce2e52011-07-03 19:25:21 -07003767 mDragFolderRingAnimator.setCell(cellX, cellY);
3768 mDragFolderRingAnimator.setCellLayout(layout);
Adam Cohen19072da2011-05-31 14:30:45 -07003769 mDragFolderRingAnimator.animateToAcceptState();
Adam Cohen69ce2e52011-07-03 19:25:21 -07003770 layout.showFolderAccept(mDragFolderRingAnimator);
3771 layout.clearDragOutlines();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003772 setDragMode(DRAG_MODE_CREATE_FOLDER);
Adam Cohen482ed822012-03-02 14:15:13 -08003773 }
3774 }
3775
3776 class ReorderAlarmListener implements OnAlarmListener {
3777 float[] dragViewCenter;
3778 int minSpanX, minSpanY, spanX, spanY;
3779 DragView dragView;
3780 View child;
3781
3782 public ReorderAlarmListener(float[] dragViewCenter, int minSpanX, int minSpanY, int spanX,
3783 int spanY, DragView dragView, View child) {
3784 this.dragViewCenter = dragViewCenter;
3785 this.minSpanX = minSpanX;
3786 this.minSpanY = minSpanY;
3787 this.spanX = spanX;
3788 this.spanY = spanY;
3789 this.child = child;
3790 this.dragView = dragView;
3791 }
3792
3793 public void onAlarm(Alarm alarm) {
3794 int[] resultSpan = new int[2];
Adam Cohen19f37922012-03-21 11:59:11 -07003795 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
Adam Cohen74c54912013-09-29 14:48:04 -07003796 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, mDragTargetLayout,
3797 mTargetCell);
Adam Cohen19f37922012-03-21 11:59:11 -07003798 mLastReorderX = mTargetCell[0];
3799 mLastReorderY = mTargetCell[1];
3800
Adam Cohenfa3c58f2013-12-06 16:10:55 -08003801 mTargetCell = mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08003802 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
3803 child, mTargetCell, resultSpan, CellLayout.MODE_DRAG_OVER);
3804
Adam Cohen19f37922012-03-21 11:59:11 -07003805 if (mTargetCell[0] < 0 || mTargetCell[1] < 0) {
3806 mDragTargetLayout.revertTempState();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003807 } else {
3808 setDragMode(DRAG_MODE_REORDER);
Adam Cohen19f37922012-03-21 11:59:11 -07003809 }
Adam Cohen482ed822012-03-02 14:15:13 -08003810
Adam Cohen482ed822012-03-02 14:15:13 -08003811 boolean resize = resultSpan[0] != spanX || resultSpan[1] != spanY;
3812 mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
3813 (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
3814 mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], resize,
3815 dragView.getDragVisualizeOffset(), dragView.getDragRegion());
Adam Cohen19072da2011-05-31 14:30:45 -07003816 }
3817 }
3818
Winson Chunga34abf82010-11-12 12:10:35 -08003819 @Override
Adam Cohen7d30a372013-07-01 17:03:59 -07003820 public void getHitRectRelativeToDragLayer(Rect outRect) {
Winson Chunga34abf82010-11-12 12:10:35 -08003821 // We want the workspace to have the whole area of the display (it will find the correct
3822 // cell layout to drop to in the existing drag/drop logic.
Adam Cohen7d30a372013-07-01 17:03:59 -07003823 mLauncher.getDragLayer().getDescendantRectRelativeToSelf(this, outRect);
Winson Chunga34abf82010-11-12 12:10:35 -08003824 }
3825
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07003826 /**
3827 * Add the item specified by dragInfo to the given layout.
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07003828 * @return true if successful
3829 */
Adam Cohen120980b2010-12-08 11:05:37 -08003830 public boolean addExternalItemToScreen(ItemInfo dragInfo, CellLayout layout) {
3831 if (layout.findCellForSpan(mTempEstimate, dragInfo.spanX, dragInfo.spanY)) {
Patrick Dubroybbaa75c2011-03-08 18:47:40 -08003832 onDropExternal(dragInfo.dropPos, (ItemInfo) dragInfo, (CellLayout) layout, false);
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07003833 return true;
3834 }
Winson Chung93eef082012-03-23 15:59:27 -07003835 mLauncher.showOutOfSpaceMessage(mLauncher.isHotseatLayout(layout));
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07003836 return false;
3837 }
3838
Adam Cohend5e42732011-03-28 17:33:39 -07003839 private void onDropExternal(int[] touchXY, Object dragInfo,
3840 CellLayout cellLayout, boolean insertAtFirst) {
Adam Cohene3e27a82011-04-15 12:07:39 -07003841 onDropExternal(touchXY, dragInfo, cellLayout, insertAtFirst, null);
Adam Cohend5e42732011-03-28 17:33:39 -07003842 }
3843
Adam Cohen120980b2010-12-08 11:05:37 -08003844 /**
3845 * Drop an item that didn't originate on one of the workspace screens.
3846 * It may have come from Launcher (e.g. from all apps or customize), or it may have
3847 * come from another app altogether.
3848 *
3849 * NOTE: This can also be called when we are outside of a drag event, when we want
3850 * to add an item to one of the workspace screens.
3851 */
Winson Chung557d6ed2011-07-08 15:34:52 -07003852 private void onDropExternal(final int[] touchXY, final Object dragInfo,
3853 final CellLayout cellLayout, boolean insertAtFirst, DragObject d) {
3854 final Runnable exitSpringLoadedRunnable = new Runnable() {
3855 @Override
3856 public void run() {
Adam Cohen689ff162014-05-08 17:27:56 -07003857 mLauncher.exitSpringLoadedDragModeDelayed(true,
3858 Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
Winson Chung557d6ed2011-07-08 15:34:52 -07003859 }
3860 };
Adam Cohenb7e16182011-07-15 17:55:02 -07003861
3862 ItemInfo info = (ItemInfo) dragInfo;
3863 int spanX = info.spanX;
3864 int spanY = info.spanY;
3865 if (mDragInfo != null) {
3866 spanX = mDragInfo.spanX;
3867 spanY = mDragInfo.spanY;
3868 }
3869
Winson Chung3d503fb2011-07-13 17:25:49 -07003870 final long container = mLauncher.isHotseatLayout(cellLayout) ?
3871 LauncherSettings.Favorites.CONTAINER_HOTSEAT :
3872 LauncherSettings.Favorites.CONTAINER_DESKTOP;
Adam Cohendcd297f2013-06-18 13:13:40 -07003873 final long screenId = getIdForScreen(cellLayout);
3874 if (!mLauncher.isHotseatLayout(cellLayout)
3875 && screenId != getScreenIdForPageIndex(mCurrentPage)
Winson Chung3d503fb2011-07-13 17:25:49 -07003876 && mState != State.SPRING_LOADED) {
Adam Cohendcd297f2013-06-18 13:13:40 -07003877 snapToScreenId(screenId, null);
Adam Cohen76078c42011-06-09 15:06:52 -07003878 }
Adam Cohenb7e16182011-07-15 17:55:02 -07003879
3880 if (info instanceof PendingAddItemInfo) {
3881 final PendingAddItemInfo pendingInfo = (PendingAddItemInfo) dragInfo;
3882
Adam Cohen558baaf2011-08-15 15:22:57 -07003883 boolean findNearestVacantCell = true;
3884 if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
3885 mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
3886 cellLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08003887 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
3888 mDragViewVisualCenter[1], mTargetCell);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003889 if (willCreateUserFolder((ItemInfo) d.dragInfo, cellLayout, mTargetCell,
Adam Cohen482ed822012-03-02 14:15:13 -08003890 distance, true) || willAddToExistingUserFolder((ItemInfo) d.dragInfo,
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003891 cellLayout, mTargetCell, distance)) {
Adam Cohen558baaf2011-08-15 15:22:57 -07003892 findNearestVacantCell = false;
3893 }
3894 }
Adam Cohen482ed822012-03-02 14:15:13 -08003895
Adam Cohend41fbf52012-02-16 23:53:59 -08003896 final ItemInfo item = (ItemInfo) d.dragInfo;
Adam Cohenaaa5c212012-10-05 18:14:31 -07003897 boolean updateWidgetSize = false;
Adam Cohen558baaf2011-08-15 15:22:57 -07003898 if (findNearestVacantCell) {
Adam Cohen482ed822012-03-02 14:15:13 -08003899 int minSpanX = item.spanX;
3900 int minSpanY = item.spanY;
3901 if (item.minSpanX > 0 && item.minSpanY > 0) {
3902 minSpanX = item.minSpanX;
3903 minSpanY = item.minSpanY;
3904 }
Adam Cohend41fbf52012-02-16 23:53:59 -08003905 int[] resultSpan = new int[2];
Adam Cohenfa3c58f2013-12-06 16:10:55 -08003906 mTargetCell = cellLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08003907 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, info.spanX, info.spanY,
3908 null, mTargetCell, resultSpan, CellLayout.MODE_ON_DROP_EXTERNAL);
Adam Cohen9e05a5e2012-09-10 15:53:09 -07003909
3910 if (resultSpan[0] != item.spanX || resultSpan[1] != item.spanY) {
3911 updateWidgetSize = true;
3912 }
Adam Cohend41fbf52012-02-16 23:53:59 -08003913 item.spanX = resultSpan[0];
3914 item.spanY = resultSpan[1];
Adam Cohen558baaf2011-08-15 15:22:57 -07003915 }
3916
Adam Cohenb7e16182011-07-15 17:55:02 -07003917 Runnable onAnimationCompleteRunnable = new Runnable() {
Winson Chung557d6ed2011-07-08 15:34:52 -07003918 @Override
3919 public void run() {
Adam Cohen689ff162014-05-08 17:27:56 -07003920 // Normally removeExtraEmptyScreen is called in Workspace#onDragEnd, but when
3921 // adding an item that may not be dropped right away (due to a config activity)
3922 // we defer the removal until the activity returns.
3923 deferRemoveExtraEmptyScreen();
3924
Winson Chung557d6ed2011-07-08 15:34:52 -07003925 // When dragging and dropping from customization tray, we deal with creating
3926 // widgets/shortcuts/folders in a slightly different way
Sunny Goyal71b5c0b2015-01-08 16:59:04 -08003927 mLauncher.addPendingItem(pendingInfo, container, screenId, mTargetCell,
3928 item.spanX, item.spanY);
Winson Chung557d6ed2011-07-08 15:34:52 -07003929 }
Adam Cohenb7e16182011-07-15 17:55:02 -07003930 };
Adam Cohen59400422014-03-05 18:07:04 -08003931 boolean isWidget = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
3932 || pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
3933
3934 View finalView = isWidget ? ((PendingAddWidgetInfo) pendingInfo).boundWidget : null;
Adam Cohen9e05a5e2012-09-10 15:53:09 -07003935
3936 if (finalView instanceof AppWidgetHostView && updateWidgetSize) {
3937 AppWidgetHostView awhv = (AppWidgetHostView) finalView;
3938 AppWidgetResizeFrame.updateWidgetSizeRanges(awhv, mLauncher, item.spanX,
3939 item.spanY);
3940 }
3941
Adam Cohend41fbf52012-02-16 23:53:59 -08003942 int animationStyle = ANIMATE_INTO_POSITION_AND_DISAPPEAR;
Adam Cohen59400422014-03-05 18:07:04 -08003943 if (isWidget && ((PendingAddWidgetInfo) pendingInfo).info != null &&
Adam Cohend41fbf52012-02-16 23:53:59 -08003944 ((PendingAddWidgetInfo) pendingInfo).info.configure != null) {
3945 animationStyle = ANIMATE_INTO_POSITION_AND_REMAIN;
3946 }
3947 animateWidgetDrop(info, cellLayout, d.dragView, onAnimationCompleteRunnable,
3948 animationStyle, finalView, true);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07003949 } else {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003950 // This is for other drag/drop cases, like dragging from All Apps
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003951 View view = null;
3952
3953 switch (info.itemType) {
3954 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
3955 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
Michael Jurkaeadbfc52013-09-04 00:45:37 +02003956 if (info.container == NO_ID && info instanceof AppInfo) {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003957 // Came from all apps -- make a copy
Sunny Goyal71b5c0b2015-01-08 16:59:04 -08003958 info = ((AppInfo) info).makeShortcut();
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003959 }
3960 view = mLauncher.createShortcut(R.layout.application, cellLayout,
3961 (ShortcutInfo) info);
3962 break;
Adam Cohendf2cc412011-04-27 16:56:57 -07003963 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
Adam Cohenc0dcf592011-06-01 15:30:43 -07003964 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, cellLayout,
3965 (FolderInfo) info, mIconCache);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003966 break;
3967 default:
3968 throw new IllegalStateException("Unknown item type: " + info.itemType);
3969 }
3970
Adam Cohenc0dcf592011-06-01 15:30:43 -07003971 // First we find the cell nearest to point at which the item is
3972 // dropped, without any consideration to whether there is an item there.
3973 if (touchXY != null) {
3974 mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
3975 cellLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08003976 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
3977 mDragViewVisualCenter[1], mTargetCell);
Winson Chung557d6ed2011-07-08 15:34:52 -07003978 d.postAnimationRunnable = exitSpringLoadedRunnable;
Adam Cohen482ed822012-03-02 14:15:13 -08003979 if (createUserFolderIfNecessary(view, container, cellLayout, mTargetCell, distance,
3980 true, d.dragView, d.postAnimationRunnable)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07003981 return;
3982 }
Adam Cohen482ed822012-03-02 14:15:13 -08003983 if (addToExistingFolderIfNecessary(view, cellLayout, mTargetCell, distance, d,
3984 true)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07003985 return;
3986 }
Adam Cohen10b17372011-04-15 14:21:25 -07003987 }
3988
Michael Jurkac4e772e2011-02-10 13:32:01 -08003989 if (touchXY != null) {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003990 // when dragging and dropping, just find the closest free spot
Adam Cohenfa3c58f2013-12-06 16:10:55 -08003991 mTargetCell = cellLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08003992 (int) mDragViewVisualCenter[1], 1, 1, 1, 1,
Adam Cohenea889a22012-03-27 16:45:39 -07003993 null, mTargetCell, null, CellLayout.MODE_ON_DROP_EXTERNAL);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003994 } else {
3995 cellLayout.findCellForSpan(mTargetCell, 1, 1);
3996 }
Sunny Goyal95abbb32014-08-04 10:53:22 -07003997 // Add the item to DB before adding to screen ensures that the container and other
3998 // values of the info is properly updated.
3999 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId,
4000 mTargetCell[0], mTargetCell[1]);
4001
Adam Cohendcd297f2013-06-18 13:13:40 -07004002 addInScreen(view, container, screenId, mTargetCell[0], mTargetCell[1], info.spanX,
Winson Chung3d503fb2011-07-13 17:25:49 -07004003 info.spanY, insertAtFirst);
Adam Cohen716b51e2011-06-30 12:09:54 -07004004 cellLayout.onDropChild(view);
Michael Jurkaa52570f2012-03-20 03:18:20 -07004005 cellLayout.getShortcutsAndWidgets().measureChild(view);
Adam Cohend5e42732011-03-28 17:33:39 -07004006
Adam Cohen3e8f8112011-07-02 18:03:00 -07004007 if (d.dragView != null) {
Adam Cohen4b285c52011-07-21 14:24:06 -07004008 // We wrap the animation call in the temporary set and reset of the current
4009 // cellLayout to its final transform -- this means we animate the drag view to
4010 // the correct final location.
4011 setFinalTransitionTransform(cellLayout);
Winson Chung557d6ed2011-07-08 15:34:52 -07004012 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view,
Adam Cohen28f852a2013-10-15 14:34:05 -07004013 exitSpringLoadedRunnable, this);
Adam Cohen4b285c52011-07-21 14:24:06 -07004014 resetTransitionTransform(cellLayout);
Adam Cohen3e8f8112011-07-02 18:03:00 -07004015 }
Joe Onorato00acb122009-08-04 16:04:30 -04004016 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004017 }
Winson Chungaafa03c2010-06-11 17:34:16 -07004018
Adam Cohend41fbf52012-02-16 23:53:59 -08004019 public Bitmap createWidgetBitmap(ItemInfo widgetInfo, View layout) {
Adam Cohened66b2b2012-01-23 17:28:51 -08004020 int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(widgetInfo.spanX,
4021 widgetInfo.spanY, widgetInfo, false);
Adam Cohend41fbf52012-02-16 23:53:59 -08004022 int visibility = layout.getVisibility();
Adam Cohened66b2b2012-01-23 17:28:51 -08004023 layout.setVisibility(VISIBLE);
4024
4025 int width = MeasureSpec.makeMeasureSpec(unScaledSize[0], MeasureSpec.EXACTLY);
4026 int height = MeasureSpec.makeMeasureSpec(unScaledSize[1], MeasureSpec.EXACTLY);
4027 Bitmap b = Bitmap.createBitmap(unScaledSize[0], unScaledSize[1],
4028 Bitmap.Config.ARGB_8888);
Sunny Goyal508da152014-08-14 10:53:27 -07004029 mCanvas.setBitmap(b);
Adam Cohened66b2b2012-01-23 17:28:51 -08004030
4031 layout.measure(width, height);
4032 layout.layout(0, 0, unScaledSize[0], unScaledSize[1]);
Sunny Goyal508da152014-08-14 10:53:27 -07004033 layout.draw(mCanvas);
4034 mCanvas.setBitmap(null);
Adam Cohend41fbf52012-02-16 23:53:59 -08004035 layout.setVisibility(visibility);
Adam Cohened66b2b2012-01-23 17:28:51 -08004036 return b;
4037 }
4038
Adam Cohend41fbf52012-02-16 23:53:59 -08004039 private void getFinalPositionForDropAnimation(int[] loc, float[] scaleXY,
Adam Cohen9d5b7d82012-05-09 18:00:44 -07004040 DragView dragView, CellLayout layout, ItemInfo info, int[] targetCell,
Michael Jurkaa2fe7862012-05-29 05:58:17 -07004041 boolean external, boolean scale) {
Adam Cohened66b2b2012-01-23 17:28:51 -08004042 // Now we animate the dragView, (ie. the widget or shortcut preview) into its final
4043 // location and size on the home screen.
Adam Cohend41fbf52012-02-16 23:53:59 -08004044 int spanX = info.spanX;
4045 int spanY = info.spanY;
Adam Cohened66b2b2012-01-23 17:28:51 -08004046
Adam Cohend41fbf52012-02-16 23:53:59 -08004047 Rect r = estimateItemPosition(layout, info, targetCell[0], targetCell[1], spanX, spanY);
4048 loc[0] = r.left;
4049 loc[1] = r.top;
4050
4051 setFinalTransitionTransform(layout);
4052 float cellLayoutScale =
Adam Cohen7d30a372013-07-01 17:03:59 -07004053 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(layout, loc, true);
Adam Cohend41fbf52012-02-16 23:53:59 -08004054 resetTransitionTransform(layout);
Michael Jurkaa2fe7862012-05-29 05:58:17 -07004055
4056 float dragViewScaleX;
4057 float dragViewScaleY;
4058 if (scale) {
4059 dragViewScaleX = (1.0f * r.width()) / dragView.getMeasuredWidth();
4060 dragViewScaleY = (1.0f * r.height()) / dragView.getMeasuredHeight();
4061 } else {
4062 dragViewScaleX = 1f;
4063 dragViewScaleY = 1f;
4064 }
Adam Cohend41fbf52012-02-16 23:53:59 -08004065
Adam Cohened66b2b2012-01-23 17:28:51 -08004066 // The animation will scale the dragView about its center, so we need to center about
4067 // the final location.
4068 loc[0] -= (dragView.getMeasuredWidth() - cellLayoutScale * r.width()) / 2;
4069 loc[1] -= (dragView.getMeasuredHeight() - cellLayoutScale * r.height()) / 2;
4070
Adam Cohend41fbf52012-02-16 23:53:59 -08004071 scaleXY[0] = dragViewScaleX * cellLayoutScale;
4072 scaleXY[1] = dragViewScaleY * cellLayoutScale;
4073 }
4074
4075 public void animateWidgetDrop(ItemInfo info, CellLayout cellLayout, DragView dragView,
4076 final Runnable onCompleteRunnable, int animationType, final View finalView,
4077 boolean external) {
4078 Rect from = new Rect();
4079 mLauncher.getDragLayer().getViewRectRelativeToSelf(dragView, from);
4080
4081 int[] finalPos = new int[2];
4082 float scaleXY[] = new float[2];
Michael Jurkaa2fe7862012-05-29 05:58:17 -07004083 boolean scalePreview = !(info instanceof PendingAddShortcutInfo);
Adam Cohend41fbf52012-02-16 23:53:59 -08004084 getFinalPositionForDropAnimation(finalPos, scaleXY, dragView, cellLayout, info, mTargetCell,
Michael Jurkaa2fe7862012-05-29 05:58:17 -07004085 external, scalePreview);
Adam Cohened66b2b2012-01-23 17:28:51 -08004086
4087 Resources res = mLauncher.getResources();
Adam Cohenad4e15c2013-10-17 16:21:35 -07004088 final int duration = res.getInteger(R.integer.config_dropAnimMaxDuration) - 200;
Adam Cohened66b2b2012-01-23 17:28:51 -08004089
Adam Cohend41fbf52012-02-16 23:53:59 -08004090 // In the case where we've prebound the widget, we remove it from the DragLayer
4091 if (finalView instanceof AppWidgetHostView && external) {
4092 mLauncher.getDragLayer().removeView(finalView);
4093 }
Adam Cohen59400422014-03-05 18:07:04 -08004094
4095 boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET ||
4096 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
Adam Cohend41fbf52012-02-16 23:53:59 -08004097 if ((animationType == ANIMATE_INTO_POSITION_AND_RESIZE || external) && finalView != null) {
4098 Bitmap crossFadeBitmap = createWidgetBitmap(info, finalView);
Adam Cohened66b2b2012-01-23 17:28:51 -08004099 dragView.setCrossFadeBitmap(crossFadeBitmap);
4100 dragView.crossFade((int) (duration * 0.8f));
Adam Cohen59400422014-03-05 18:07:04 -08004101 } else if (isWidget && external) {
Adam Cohend41fbf52012-02-16 23:53:59 -08004102 scaleXY[0] = scaleXY[1] = Math.min(scaleXY[0], scaleXY[1]);
Adam Cohened66b2b2012-01-23 17:28:51 -08004103 }
4104
Adam Cohend41fbf52012-02-16 23:53:59 -08004105 DragLayer dragLayer = mLauncher.getDragLayer();
Winson Chung7bd1bbb2012-02-13 18:29:29 -08004106 if (animationType == CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION) {
Adam Cohend41fbf52012-02-16 23:53:59 -08004107 mLauncher.getDragLayer().animateViewIntoPosition(dragView, finalPos, 0f, 0.1f, 0.1f,
Adam Cohened66b2b2012-01-23 17:28:51 -08004108 DragLayer.ANIMATION_END_DISAPPEAR, onCompleteRunnable, duration);
4109 } else {
Adam Cohend41fbf52012-02-16 23:53:59 -08004110 int endStyle;
4111 if (animationType == ANIMATE_INTO_POSITION_AND_REMAIN) {
4112 endStyle = DragLayer.ANIMATION_END_REMAIN_VISIBLE;
4113 } else {
4114 endStyle = DragLayer.ANIMATION_END_DISAPPEAR;;
4115 }
4116
4117 Runnable onComplete = new Runnable() {
4118 @Override
4119 public void run() {
4120 if (finalView != null) {
4121 finalView.setVisibility(VISIBLE);
4122 }
4123 if (onCompleteRunnable != null) {
4124 onCompleteRunnable.run();
4125 }
4126 }
4127 };
4128 dragLayer.animateViewIntoPosition(dragView, from.left, from.top, finalPos[0],
4129 finalPos[1], 1, 1, 1, scaleXY[0], scaleXY[1], onComplete, endStyle,
4130 duration, this);
Adam Cohened66b2b2012-01-23 17:28:51 -08004131 }
4132 }
4133
Adam Cohen4b285c52011-07-21 14:24:06 -07004134 public void setFinalTransitionTransform(CellLayout layout) {
4135 if (isSwitchingState()) {
Adam Cohen7d30a372013-07-01 17:03:59 -07004136 mCurrentScale = getScaleX();
4137 setScaleX(mNewScale);
4138 setScaleY(mNewScale);
Adam Cohen4b285c52011-07-21 14:24:06 -07004139 }
4140 }
4141 public void resetTransitionTransform(CellLayout layout) {
4142 if (isSwitchingState()) {
Adam Cohen7d30a372013-07-01 17:03:59 -07004143 setScaleX(mCurrentScale);
4144 setScaleY(mCurrentScale);
Adam Cohen4b285c52011-07-21 14:24:06 -07004145 }
4146 }
4147
Jeff Sharkey70864282009-04-07 21:08:40 -07004148 /**
4149 * Return the current {@link CellLayout}, correctly picking the destination
4150 * screen while a scroll is in progress.
4151 */
Patrick Dubroy5f445422011-02-18 14:35:21 -08004152 public CellLayout getCurrentDropLayout() {
Winson Chung360e63f2012-04-27 13:48:05 -07004153 return (CellLayout) getChildAt(getNextPage());
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004154 }
4155
Jeff Sharkey70864282009-04-07 21:08:40 -07004156 /**
Michael Jurka0280c3b2010-09-17 15:00:07 -07004157 * Return the current CellInfo describing our current drag; this method exists
4158 * so that Launcher can sync this object with the correct info when the activity is created/
4159 * destroyed
4160 *
4161 */
4162 public CellLayout.CellInfo getDragInfo() {
4163 return mDragInfo;
4164 }
4165
Winson Chung9b9fb962013-11-15 15:39:34 -08004166 public int getCurrentPageOffsetFromCustomContent() {
Adam Cohen21cd0022013-10-09 18:57:02 -07004167 return getNextPage() - numCustomPages();
4168 }
4169
Michael Jurka0280c3b2010-09-17 15:00:07 -07004170 /**
Jeff Sharkey70864282009-04-07 21:08:40 -07004171 * Calculate the nearest cell where the given object would be dropped.
Adam Cohene3e27a82011-04-15 12:07:39 -07004172 *
4173 * pixelX and pixelY should be in the coordinate system of layout
Jeff Sharkey70864282009-04-07 21:08:40 -07004174 */
Adam Cohendf035382011-04-11 17:22:04 -07004175 private int[] findNearestArea(int pixelX, int pixelY,
Adam Cohene3e27a82011-04-15 12:07:39 -07004176 int spanX, int spanY, CellLayout layout, int[] recycle) {
Adam Cohendf035382011-04-11 17:22:04 -07004177 return layout.findNearestArea(
Adam Cohene3e27a82011-04-15 12:07:39 -07004178 pixelX, pixelY, spanX, spanY, recycle);
Adam Cohendf035382011-04-11 17:22:04 -07004179 }
4180
Adam Cohencff6af82011-09-13 14:51:53 -07004181 void setup(DragController dragController) {
Michael Jurkac2f7f472010-12-14 15:34:42 -08004182 mSpringLoadedDragController = new SpringLoadedDragController(mLauncher);
Joe Onorato00acb122009-08-04 16:04:30 -04004183 mDragController = dragController;
Michael Jurkad74c9842011-07-10 12:44:21 -07004184
Michael Jurkad74c9842011-07-10 12:44:21 -07004185 // hardware layers on children are enabled on startup, but should be disabled until
4186 // needed
Michael Jurka3a0469d2012-06-21 09:38:41 -07004187 updateChildrenLayersEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004188 }
4189
Patrick Dubroye3887cc2011-01-20 10:43:40 -08004190 /**
4191 * Called at the end of a drag which originated on the workspace.
4192 */
Michael Jurka1e2f4652013-07-08 18:03:46 -07004193 public void onDropCompleted(final View target, final DragObject d,
4194 final boolean isFlingToDelete, final boolean success) {
4195 if (mDeferDropAfterUninstall) {
4196 mDeferredAction = new Runnable() {
Adam Cohenad4e15c2013-10-17 16:21:35 -07004197 public void run() {
4198 onDropCompleted(target, d, isFlingToDelete, success);
4199 mDeferredAction = null;
4200 }
4201 };
Michael Jurka1e2f4652013-07-08 18:03:46 -07004202 return;
4203 }
4204
4205 boolean beingCalledAfterUninstall = mDeferredAction != null;
4206
4207 if (success && !(beingCalledAfterUninstall && !mUninstallSuccessful)) {
4208 if (target != this && mDragInfo != null) {
Sunny Goyal71b5c0b2015-01-08 16:59:04 -08004209 removeWorkspaceItem(mDragInfo.cell);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004210 }
Patrick Dubroyce34a972010-10-19 10:34:32 -07004211 } else if (mDragInfo != null) {
Winson Chung3d503fb2011-07-13 17:25:49 -07004212 CellLayout cellLayout;
4213 if (mLauncher.isHotseatLayout(target)) {
4214 cellLayout = mLauncher.getHotseat().getLayout();
4215 } else {
Adam Cohendcd297f2013-06-18 13:13:40 -07004216 cellLayout = getScreenWithId(mDragInfo.screenId);
Winson Chung3d503fb2011-07-13 17:25:49 -07004217 }
Jorim Jaggieedb00a2014-01-13 13:45:07 -08004218 if (cellLayout == null && LauncherAppState.isDogfoodBuild()) {
4219 throw new RuntimeException("Invalid state: cellLayout == null in "
4220 + "Workspace#onDropCompleted. Please file a bug. ");
4221 }
4222 if (cellLayout != null) {
4223 cellLayout.onDropChild(mDragInfo.cell);
4224 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004225 }
Michael Jurka1e2f4652013-07-08 18:03:46 -07004226 if ((d.cancelled || (beingCalledAfterUninstall && !mUninstallSuccessful))
4227 && mDragInfo.cell != null) {
4228 mDragInfo.cell.setVisibility(VISIBLE);
Adam Cohen36cc09b2011-09-29 17:33:15 -07004229 }
Joe Onorato4be866d2010-10-10 11:26:02 -07004230 mDragOutline = null;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004231 mDragInfo = null;
4232 }
4233
Hyunyoung Song31178b82015-02-24 14:12:51 -08004234 /**
4235 * For opposite operation. See {@link #addInScreen}.
4236 */
Sunny Goyal71b5c0b2015-01-08 16:59:04 -08004237 public void removeWorkspaceItem(View v) {
4238 CellLayout parentCell = getParentCellLayoutForView(v);
4239 if (parentCell != null) {
4240 parentCell.removeView(v);
4241 } else if (LauncherAppState.isDogfoodBuild()) {
4242 throw new NullPointerException("mDragInfo.cell has null parent");
4243 }
4244 if (v instanceof DropTarget) {
4245 mDragController.removeDropTarget((DropTarget) v);
4246 }
4247 }
4248
Michael Jurka1e2f4652013-07-08 18:03:46 -07004249 public void deferCompleteDropAfterUninstallActivity() {
4250 mDeferDropAfterUninstall = true;
4251 }
4252
4253 /// maybe move this into a smaller part
4254 public void onUninstallActivityReturned(boolean success) {
4255 mDeferDropAfterUninstall = false;
4256 mUninstallSuccessful = success;
4257 if (mDeferredAction != null) {
4258 mDeferredAction.run();
4259 }
4260 }
4261
Adam Cohenea889a22012-03-27 16:45:39 -07004262 void updateItemLocationsInDatabase(CellLayout cl) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004263 int count = cl.getShortcutsAndWidgets().getChildCount();
Adam Cohend3461712012-03-29 17:25:17 -07004264
Adam Cohendcd297f2013-06-18 13:13:40 -07004265 long screenId = getIdForScreen(cl);
Adam Cohend3461712012-03-29 17:25:17 -07004266 int container = Favorites.CONTAINER_DESKTOP;
4267
4268 if (mLauncher.isHotseatLayout(cl)) {
Adam Cohendcd297f2013-06-18 13:13:40 -07004269 screenId = -1;
Adam Cohend3461712012-03-29 17:25:17 -07004270 container = Favorites.CONTAINER_HOTSEAT;
4271 }
4272
Adam Cohen482ed822012-03-02 14:15:13 -08004273 for (int i = 0; i < count; i++) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004274 View v = cl.getShortcutsAndWidgets().getChildAt(i);
Adam Cohen482ed822012-03-02 14:15:13 -08004275 ItemInfo info = (ItemInfo) v.getTag();
Adam Cohen2acce882012-03-28 19:03:19 -07004276 // Null check required as the AllApps button doesn't have an item info
Adam Cohen487f7dd2012-06-28 18:12:10 -07004277 if (info != null && info.requiresDbUpdate) {
4278 info.requiresDbUpdate = false;
Adam Cohendcd297f2013-06-18 13:13:40 -07004279 LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId, info.cellX,
Adam Cohenbebf0422012-04-11 18:06:28 -07004280 info.cellY, info.spanX, info.spanY);
Adam Cohen2acce882012-03-28 19:03:19 -07004281 }
Adam Cohen482ed822012-03-02 14:15:13 -08004282 }
4283 }
4284
Adam Cohene25af792013-06-06 23:08:25 -07004285 void saveWorkspaceToDb() {
4286 saveWorkspaceScreenToDb((CellLayout) mLauncher.getHotseat().getLayout());
4287 int count = getChildCount();
4288 for (int i = 0; i < count; i++) {
4289 CellLayout cl = (CellLayout) getChildAt(i);
4290 saveWorkspaceScreenToDb(cl);
4291 }
4292 }
4293
4294 void saveWorkspaceScreenToDb(CellLayout cl) {
4295 int count = cl.getShortcutsAndWidgets().getChildCount();
4296
Adam Cohendcd297f2013-06-18 13:13:40 -07004297 long screenId = getIdForScreen(cl);
Adam Cohene25af792013-06-06 23:08:25 -07004298 int container = Favorites.CONTAINER_DESKTOP;
4299
Winson Chungf70696d2013-06-25 16:19:53 -07004300 Hotseat hotseat = mLauncher.getHotseat();
Adam Cohene25af792013-06-06 23:08:25 -07004301 if (mLauncher.isHotseatLayout(cl)) {
Adam Cohendcd297f2013-06-18 13:13:40 -07004302 screenId = -1;
Adam Cohene25af792013-06-06 23:08:25 -07004303 container = Favorites.CONTAINER_HOTSEAT;
4304 }
4305
4306 for (int i = 0; i < count; i++) {
4307 View v = cl.getShortcutsAndWidgets().getChildAt(i);
4308 ItemInfo info = (ItemInfo) v.getTag();
4309 // Null check required as the AllApps button doesn't have an item info
4310 if (info != null) {
Winson Chungf70696d2013-06-25 16:19:53 -07004311 int cellX = info.cellX;
4312 int cellY = info.cellY;
4313 if (container == Favorites.CONTAINER_HOTSEAT) {
4314 cellX = hotseat.getCellXFromOrder((int) info.screenId);
4315 cellY = hotseat.getCellYFromOrder((int) info.screenId);
4316 }
4317 LauncherModel.addItemToDatabase(mLauncher, info, container, screenId, cellX,
4318 cellY, false);
Adam Cohene25af792013-06-06 23:08:25 -07004319 }
4320 if (v instanceof FolderIcon) {
4321 FolderIcon fi = (FolderIcon) v;
4322 fi.getFolder().addItemLocationsInDatabase();
4323 }
4324 }
4325 }
4326
Winson Chunga48487a2012-03-20 16:19:37 -07004327 @Override
Winson Chungeeb5bbc2013-11-13 15:47:05 -08004328 public float getIntrinsicIconScaleFactor() {
4329 return 1f;
4330 }
4331
4332 @Override
Winson Chung043f2af2012-03-01 16:09:54 -08004333 public boolean supportsFlingToDelete() {
4334 return true;
4335 }
4336
Winson Chunga48487a2012-03-20 16:19:37 -07004337 @Override
Mathew Inwood1eeb3fc2013-11-25 17:01:34 +00004338 public boolean supportsAppInfoDropTarget() {
4339 return false;
4340 }
4341
4342 @Override
4343 public boolean supportsDeleteDropTarget() {
4344 return true;
4345 }
4346
4347 @Override
Winson Chunga48487a2012-03-20 16:19:37 -07004348 public void onFlingToDelete(DragObject d, int x, int y, PointF vec) {
4349 // Do nothing
4350 }
4351
4352 @Override
4353 public void onFlingToDeleteCompleted() {
4354 // Do nothing
4355 }
4356
Michael Jurka0280c3b2010-09-17 15:00:07 -07004357 public boolean isDropEnabled() {
4358 return true;
4359 }
4360
Michael Jurka0142d492010-08-25 17:46:15 -07004361 @Override
Adam Cohen1462de32012-07-24 22:34:36 -07004362 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
4363 // We don't dispatch restoreInstanceState to our children using this code path.
Dave Hawkeya8881582013-09-17 15:55:33 -06004364 // Some pages will be restored immediately as their items are bound immediately, and
Adam Cohen1462de32012-07-24 22:34:36 -07004365 // others we will need to wait until after their items are bound.
4366 mSavedStates = container;
4367 }
4368
4369 public void restoreInstanceStateForChild(int child) {
4370 if (mSavedStates != null) {
4371 mRestoredPages.add(child);
4372 CellLayout cl = (CellLayout) getChildAt(child);
Adam Cohenb0ee0812013-12-03 10:51:45 -08004373 if (cl != null) {
4374 cl.restoreInstanceState(mSavedStates);
4375 }
Adam Cohen1462de32012-07-24 22:34:36 -07004376 }
4377 }
4378
4379 public void restoreInstanceStateForRemainingPages() {
4380 int count = getChildCount();
4381 for (int i = 0; i < count; i++) {
4382 if (!mRestoredPages.contains(i)) {
4383 restoreInstanceStateForChild(i);
4384 }
4385 }
4386 mRestoredPages.clear();
Winson Chungd8e596d2013-10-21 17:14:12 -07004387 mSavedStates = null;
Adam Cohen1462de32012-07-24 22:34:36 -07004388 }
4389
4390 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004391 public void scrollLeft() {
Adam Cohen6c5891a2014-07-09 23:53:15 -07004392 if (!workspaceInModalState() && !mIsSwitchingState) {
Michael Jurka0142d492010-08-25 17:46:15 -07004393 super.scrollLeft();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004394 }
Adam Cohen95bb8002011-07-03 23:40:28 -07004395 Folder openFolder = getOpenFolder();
4396 if (openFolder != null) {
4397 openFolder.completeDragExit();
4398 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004399 }
4400
Michael Jurka0142d492010-08-25 17:46:15 -07004401 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004402 public void scrollRight() {
Adam Cohen6c5891a2014-07-09 23:53:15 -07004403 if (!workspaceInModalState() && !mIsSwitchingState) {
Michael Jurka0142d492010-08-25 17:46:15 -07004404 super.scrollRight();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004405 }
Adam Cohen95bb8002011-07-03 23:40:28 -07004406 Folder openFolder = getOpenFolder();
4407 if (openFolder != null) {
4408 openFolder.completeDragExit();
4409 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004410 }
4411
Patrick Dubroy1262e362010-10-06 15:49:50 -07004412 @Override
Winson Chung3e0839e2011-10-03 15:15:18 -07004413 public boolean onEnterScrollArea(int x, int y, int direction) {
Winson Chung1c4cf4a2011-07-29 14:49:10 -07004414 // Ignore the scroll area if we are dragging over the hot seat
Daniel Sandlercc8befa2013-06-11 14:45:48 -04004415 boolean isPortrait = !LauncherAppState.isScreenLandscape(getContext());
Winson Chung10bfc6e2012-03-28 15:41:26 -07004416 if (mLauncher.getHotseat() != null && isPortrait) {
Winson Chung1c4cf4a2011-07-29 14:49:10 -07004417 Rect r = new Rect();
4418 mLauncher.getHotseat().getHitRect(r);
4419 if (r.contains(x, y)) {
Winson Chung3e0839e2011-10-03 15:15:18 -07004420 return false;
Winson Chung1c4cf4a2011-07-29 14:49:10 -07004421 }
4422 }
4423
Winson Chung3e0839e2011-10-03 15:15:18 -07004424 boolean result = false;
Adam Cohen6c5891a2014-07-09 23:53:15 -07004425 if (!workspaceInModalState() && !mIsSwitchingState && getOpenFolder() == null) {
Michael Jurkad718d6a2010-10-14 15:35:17 -07004426 mInScrollArea = true;
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08004427
Winson Chung360e63f2012-04-27 13:48:05 -07004428 final int page = getNextPage() +
Winson Chungaa15ffe2012-01-18 15:45:28 -08004429 (direction == DragController.SCROLL_LEFT ? -1 : 1);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07004430 // We always want to exit the current layout to ensure parity of enter / exit
4431 setCurrentDropLayout(null);
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08004432
Winson Chungaa15ffe2012-01-18 15:45:28 -08004433 if (0 <= page && page < getChildCount()) {
Winson Chung482a5b62013-07-31 17:19:51 -07004434 // Ensure that we are not dragging over to the custom content screen
4435 if (getScreenIdForPageIndex(page) == CUSTOM_CONTENT_SCREEN_ID) {
4436 return false;
4437 }
4438
Winson Chungaa15ffe2012-01-18 15:45:28 -08004439 CellLayout layout = (CellLayout) getChildAt(page);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07004440 setCurrentDragOverlappingLayout(layout);
Winson Chungc07918d2011-07-01 15:35:26 -07004441
4442 // Workspace is responsible for drawing the edge glow on adjacent pages,
4443 // so we need to redraw the workspace when this may have changed.
4444 invalidate();
Winson Chung3e0839e2011-10-03 15:15:18 -07004445 result = true;
Michael Jurkad718d6a2010-10-14 15:35:17 -07004446 }
Patrick Dubroy1262e362010-10-06 15:49:50 -07004447 }
Winson Chung3e0839e2011-10-03 15:15:18 -07004448 return result;
Patrick Dubroy1262e362010-10-06 15:49:50 -07004449 }
4450
4451 @Override
Winson Chung3e0839e2011-10-03 15:15:18 -07004452 public boolean onExitScrollArea() {
4453 boolean result = false;
Michael Jurkad718d6a2010-10-14 15:35:17 -07004454 if (mInScrollArea) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07004455 invalidate();
4456 CellLayout layout = getCurrentDropLayout();
4457 setCurrentDropLayout(layout);
4458 setCurrentDragOverlappingLayout(layout);
4459
Adam Cohen8a18afc2011-12-13 15:57:01 -08004460 result = true;
Winson Chungc07918d2011-07-01 15:35:26 -07004461 mInScrollArea = false;
Patrick Dubroy1262e362010-10-06 15:49:50 -07004462 }
Winson Chung3e0839e2011-10-03 15:15:18 -07004463 return result;
Patrick Dubroy1262e362010-10-06 15:49:50 -07004464 }
4465
Winson Chungc07918d2011-07-01 15:35:26 -07004466 private void onResetScrollArea() {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07004467 setCurrentDragOverlappingLayout(null);
Winson Chungc07918d2011-07-01 15:35:26 -07004468 mInScrollArea = false;
4469 }
4470
Winson Chung3d503fb2011-07-13 17:25:49 -07004471 /**
4472 * Returns a specific CellLayout
4473 */
4474 CellLayout getParentCellLayoutForView(View v) {
4475 ArrayList<CellLayout> layouts = getWorkspaceAndHotseatCellLayouts();
4476 for (CellLayout layout : layouts) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004477 if (layout.getShortcutsAndWidgets().indexOfChild(v) > -1) {
Winson Chung3d503fb2011-07-13 17:25:49 -07004478 return layout;
4479 }
4480 }
4481 return null;
4482 }
4483
4484 /**
4485 * Returns a list of all the CellLayouts in the workspace.
4486 */
4487 ArrayList<CellLayout> getWorkspaceAndHotseatCellLayouts() {
4488 ArrayList<CellLayout> layouts = new ArrayList<CellLayout>();
4489 int screenCount = getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004490 for (int screen = 0; screen < screenCount; screen++) {
Winson Chung3d503fb2011-07-13 17:25:49 -07004491 layouts.add(((CellLayout) getChildAt(screen)));
4492 }
4493 if (mLauncher.getHotseat() != null) {
4494 layouts.add(mLauncher.getHotseat().getLayout());
4495 }
4496 return layouts;
4497 }
4498
4499 /**
4500 * We should only use this to search for specific children. Do not use this method to modify
Michael Jurkaa52570f2012-03-20 03:18:20 -07004501 * ShortcutsAndWidgetsContainer directly. Includes ShortcutAndWidgetContainers from
4502 * the hotseat and workspace pages
Winson Chung3d503fb2011-07-13 17:25:49 -07004503 */
Michael Jurkaa52570f2012-03-20 03:18:20 -07004504 ArrayList<ShortcutAndWidgetContainer> getAllShortcutAndWidgetContainers() {
4505 ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
4506 new ArrayList<ShortcutAndWidgetContainer>();
Winson Chung3d503fb2011-07-13 17:25:49 -07004507 int screenCount = getChildCount();
4508 for (int screen = 0; screen < screenCount; screen++) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004509 childrenLayouts.add(((CellLayout) getChildAt(screen)).getShortcutsAndWidgets());
Winson Chung3d503fb2011-07-13 17:25:49 -07004510 }
4511 if (mLauncher.getHotseat() != null) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004512 childrenLayouts.add(mLauncher.getHotseat().getLayout().getShortcutsAndWidgets());
Winson Chung3d503fb2011-07-13 17:25:49 -07004513 }
4514 return childrenLayouts;
4515 }
4516
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004517 public Folder getFolderForTag(final Object tag) {
Sunny Goyalff572272014-07-23 13:58:07 -07004518 return (Folder) getFirstMatch(new ItemOperator() {
4519
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004520 @Override
4521 public boolean evaluate(ItemInfo info, View v, View parent) {
Sunny Goyalff572272014-07-23 13:58:07 -07004522 return (v instanceof Folder) && (((Folder) v).getInfo() == tag)
4523 && ((Folder) v).getInfo().opened;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004524 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004525 });
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004526 }
4527
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004528 public View getViewForTag(final Object tag) {
Sunny Goyalff572272014-07-23 13:58:07 -07004529 return getFirstMatch(new ItemOperator() {
4530
4531 @Override
4532 public boolean evaluate(ItemInfo info, View v, View parent) {
4533 return info == tag;
4534 }
4535 });
4536 }
4537
4538 public LauncherAppWidgetHostView getWidgetForAppWidgetId(final int appWidgetId) {
4539 return (LauncherAppWidgetHostView) getFirstMatch(new ItemOperator() {
4540
4541 @Override
4542 public boolean evaluate(ItemInfo info, View v, View parent) {
4543 return (info instanceof LauncherAppWidgetInfo) &&
4544 ((LauncherAppWidgetInfo) info).appWidgetId == appWidgetId;
4545 }
4546 });
4547 }
4548
4549 private View getFirstMatch(final ItemOperator operator) {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004550 final View[] value = new View[1];
Sunny Goyal651077b2014-06-30 14:15:31 -07004551 mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004552 @Override
4553 public boolean evaluate(ItemInfo info, View v, View parent) {
Sunny Goyalff572272014-07-23 13:58:07 -07004554 if (operator.evaluate(info, v, parent)) {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004555 value[0] = v;
4556 return true;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004557 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004558 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004559 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004560 });
4561 return value[0];
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004562 }
4563
Adam Cohendf035382011-04-11 17:22:04 -07004564 void clearDropTargets() {
Sunny Goyal651077b2014-06-30 14:15:31 -07004565 mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004566 @Override
4567 public boolean evaluate(ItemInfo info, View v, View parent) {
Adam Cohendf035382011-04-11 17:22:04 -07004568 if (v instanceof DropTarget) {
4569 mDragController.removeDropTarget((DropTarget) v);
4570 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004571 // not done, process all the shortcuts
4572 return false;
Adam Cohendf035382011-04-11 17:22:04 -07004573 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004574 });
Adam Cohendf035382011-04-11 17:22:04 -07004575 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004576
Sunny Goyal1a745e82014-10-02 15:58:31 -07004577 public void disableShortcutsByPackageName(final ArrayList<String> packages,
4578 final UserHandleCompat user, final int reason) {
4579 final HashSet<String> packageNames = new HashSet<String>();
4580 packageNames.addAll(packages);
4581
4582 mapOverItems(MAP_RECURSE, new ItemOperator() {
4583 @Override
4584 public boolean evaluate(ItemInfo info, View v, View parent) {
4585 if (info instanceof ShortcutInfo && v instanceof BubbleTextView) {
4586 ShortcutInfo shortcutInfo = (ShortcutInfo) info;
4587 ComponentName cn = shortcutInfo.getTargetComponent();
4588 if (user.equals(shortcutInfo.user) && cn != null
4589 && packageNames.contains(cn.getPackageName())) {
4590 shortcutInfo.isDisabled |= reason;
4591 BubbleTextView shortcut = (BubbleTextView) v;
4592 shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache, true, false);
4593
4594 if (parent != null) {
4595 parent.invalidate();
4596 }
4597 }
4598 }
4599 // process all the shortcuts
4600 return false;
4601 }
4602 });
4603 }
4604
Winson Chung83892cc2013-05-01 16:53:33 -07004605 // Removes ALL items that match a given package name, this is usually called when a package
4606 // has been removed and we want to remove all components (widgets, shortcuts, apps) that
4607 // belong to that package.
Kenny Guyed131872014-04-30 03:02:21 +01004608 void removeItemsByPackageName(final ArrayList<String> packages, final UserHandleCompat user) {
Winson Chung64359a52013-07-08 17:17:08 -07004609 final HashSet<String> packageNames = new HashSet<String>();
Winson Chungcd810732012-06-18 16:45:43 -07004610 packageNames.addAll(packages);
Joe Onorato64e6be72010-03-05 15:05:52 -05004611
Winson Chung64359a52013-07-08 17:17:08 -07004612 // Filter out all the ItemInfos that this is going to affect
4613 final HashSet<ItemInfo> infos = new HashSet<ItemInfo>();
4614 final HashSet<ComponentName> cns = new HashSet<ComponentName>();
Winson Chung83892cc2013-05-01 16:53:33 -07004615 ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
4616 for (CellLayout layoutParent : cellLayouts) {
4617 ViewGroup layout = layoutParent.getShortcutsAndWidgets();
4618 int childCount = layout.getChildCount();
4619 for (int i = 0; i < childCount; ++i) {
4620 View view = layout.getChildAt(i);
Winson Chung64359a52013-07-08 17:17:08 -07004621 infos.add((ItemInfo) view.getTag());
Winson Chung83892cc2013-05-01 16:53:33 -07004622 }
4623 }
Winson Chung64359a52013-07-08 17:17:08 -07004624 LauncherModel.ItemInfoFilter filter = new LauncherModel.ItemInfoFilter() {
4625 @Override
4626 public boolean filterItem(ItemInfo parent, ItemInfo info,
4627 ComponentName cn) {
Kenny Guyed131872014-04-30 03:02:21 +01004628 if (packageNames.contains(cn.getPackageName())
4629 && info.user.equals(user)) {
Winson Chung64359a52013-07-08 17:17:08 -07004630 cns.add(cn);
4631 return true;
4632 }
4633 return false;
4634 }
4635 };
4636 LauncherModel.filterItemInfos(infos, filter);
Winson Chung83892cc2013-05-01 16:53:33 -07004637
Winson Chung64359a52013-07-08 17:17:08 -07004638 // Remove the affected components
Kenny Guyed131872014-04-30 03:02:21 +01004639 removeItemsByComponentName(cns, user);
Winson Chung83892cc2013-05-01 16:53:33 -07004640 }
4641
Sunny Goyal4390ace2014-10-13 11:33:11 -07004642 /**
4643 * Removes items that match the item info specified. When applications are removed
4644 * as a part of an update, this is called to ensure that other widgets and application
4645 * shortcuts are not removed.
4646 */
Kenny Guyed131872014-04-30 03:02:21 +01004647 void removeItemsByComponentName(final HashSet<ComponentName> componentNames,
4648 final UserHandleCompat user) {
Winson Chung3d503fb2011-07-13 17:25:49 -07004649 ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
4650 for (final CellLayout layoutParent: cellLayouts) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004651 final ViewGroup layout = layoutParent.getShortcutsAndWidgets();
Romain Guy574d20e2009-06-01 15:34:04 -07004652
Winson Chung64359a52013-07-08 17:17:08 -07004653 final HashMap<ItemInfo, View> children = new HashMap<ItemInfo, View>();
4654 for (int j = 0; j < layout.getChildCount(); j++) {
4655 final View view = layout.getChildAt(j);
4656 children.put((ItemInfo) view.getTag(), view);
4657 }
Winson Chungaafa03c2010-06-11 17:34:16 -07004658
Winson Chung64359a52013-07-08 17:17:08 -07004659 final ArrayList<View> childrenToRemove = new ArrayList<View>();
4660 final HashMap<FolderInfo, ArrayList<ShortcutInfo>> folderAppsToRemove =
4661 new HashMap<FolderInfo, ArrayList<ShortcutInfo>>();
4662 LauncherModel.ItemInfoFilter filter = new LauncherModel.ItemInfoFilter() {
4663 @Override
4664 public boolean filterItem(ItemInfo parent, ItemInfo info,
4665 ComponentName cn) {
4666 if (parent instanceof FolderInfo) {
Kenny Guyed131872014-04-30 03:02:21 +01004667 if (componentNames.contains(cn) && info.user.equals(user)) {
Winson Chung64359a52013-07-08 17:17:08 -07004668 FolderInfo folder = (FolderInfo) parent;
4669 ArrayList<ShortcutInfo> appsToRemove;
4670 if (folderAppsToRemove.containsKey(folder)) {
4671 appsToRemove = folderAppsToRemove.get(folder);
4672 } else {
4673 appsToRemove = new ArrayList<ShortcutInfo>();
4674 folderAppsToRemove.put(folder, appsToRemove);
Romain Guy629de3e2010-01-13 12:20:59 -08004675 }
Winson Chung64359a52013-07-08 17:17:08 -07004676 appsToRemove.add((ShortcutInfo) info);
4677 return true;
4678 }
4679 } else {
Kenny Guyed131872014-04-30 03:02:21 +01004680 if (componentNames.contains(cn) && info.user.equals(user)) {
Winson Chung64359a52013-07-08 17:17:08 -07004681 childrenToRemove.add(children.get(info));
4682 return true;
Romain Guy574d20e2009-06-01 15:34:04 -07004683 }
4684 }
Winson Chung64359a52013-07-08 17:17:08 -07004685 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004686 }
Winson Chung64359a52013-07-08 17:17:08 -07004687 };
4688 LauncherModel.filterItemInfos(children.keySet(), filter);
4689
4690 // Remove all the apps from their folders
4691 for (FolderInfo folder : folderAppsToRemove.keySet()) {
4692 ArrayList<ShortcutInfo> appsToRemove = folderAppsToRemove.get(folder);
4693 for (ShortcutInfo info : appsToRemove) {
4694 folder.remove(info);
4695 }
4696 }
4697
4698 // Remove all the other children
4699 for (View child : childrenToRemove) {
4700 // Note: We can not remove the view directly from CellLayoutChildren as this
4701 // does not re-mark the spaces as unoccupied.
4702 layoutParent.removeViewInLayout(child);
4703 if (child instanceof DropTarget) {
4704 mDragController.removeDropTarget((DropTarget) child);
4705 }
4706 }
4707
4708 if (childrenToRemove.size() > 0) {
4709 layout.requestLayout();
4710 layout.invalidate();
4711 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004712 }
Winson Chung2efec4e2012-05-03 12:31:48 -07004713
Winson Chung64359a52013-07-08 17:17:08 -07004714 // Strip all the empty screens
4715 stripEmptyScreens();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004716 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07004717
Sunny Goyal651077b2014-06-30 14:15:31 -07004718 interface ItemOperator {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004719 /**
Sunny Goyal651077b2014-06-30 14:15:31 -07004720 * Process the next itemInfo, possibly with side-effect on {@link ItemOperator#value}.
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004721 *
4722 * @param info info for the shortcut
4723 * @param view view for the shortcut
4724 * @param parent containing folder, or null
4725 * @return true if done, false to continue the map
4726 */
4727 public boolean evaluate(ItemInfo info, View view, View parent);
4728 }
4729
4730 /**
Sunny Goyal651077b2014-06-30 14:15:31 -07004731 * Map the operator over the shortcuts and widgets, return the first-non-null value.
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004732 *
4733 * @param recurse true: iterate over folder children. false: op get the folders themselves.
4734 * @param op the operator to map over the shortcuts
4735 */
Sunny Goyal651077b2014-06-30 14:15:31 -07004736 void mapOverItems(boolean recurse, ItemOperator op) {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004737 ArrayList<ShortcutAndWidgetContainer> containers = getAllShortcutAndWidgetContainers();
4738 final int containerCount = containers.size();
4739 for (int containerIdx = 0; containerIdx < containerCount; containerIdx++) {
4740 ShortcutAndWidgetContainer container = containers.get(containerIdx);
4741 // map over all the shortcuts on the workspace
4742 final int itemCount = container.getChildCount();
4743 for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
4744 View item = container.getChildAt(itemIdx);
4745 ItemInfo info = (ItemInfo) item.getTag();
4746 if (recurse && info instanceof FolderInfo && item instanceof FolderIcon) {
4747 FolderIcon folder = (FolderIcon) item;
4748 ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
4749 // map over all the children in the folder
4750 final int childCount = folderChildren.size();
4751 for (int childIdx = 0; childIdx < childCount; childIdx++) {
4752 View child = folderChildren.get(childIdx);
4753 info = (ItemInfo) child.getTag();
4754 if (op.evaluate(info, child, folder)) {
4755 return;
4756 }
4757 }
4758 } else {
4759 if (op.evaluate(info, item, null)) {
4760 return;
4761 }
4762 }
4763 }
4764 }
4765 }
4766
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07004767 void updateShortcuts(ArrayList<ShortcutInfo> shortcuts) {
4768 final HashSet<ShortcutInfo> updates = new HashSet<ShortcutInfo>(shortcuts);
4769 mapOverItems(MAP_RECURSE, new ItemOperator() {
4770 @Override
4771 public boolean evaluate(ItemInfo info, View v, View parent) {
4772 if (info instanceof ShortcutInfo && v instanceof BubbleTextView &&
4773 updates.contains(info)) {
Sunny Goyal4390ace2014-10-13 11:33:11 -07004774 ShortcutInfo si = (ShortcutInfo) info;
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07004775 BubbleTextView shortcut = (BubbleTextView) v;
Sunny Goyal4390ace2014-10-13 11:33:11 -07004776 boolean oldPromiseState = shortcut.getCompoundDrawables()[1]
4777 instanceof PreloadIconDrawable;
4778 shortcut.applyFromShortcutInfo(si, mIconCache, true,
4779 si.isPromise() != oldPromiseState);
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07004780
4781 if (parent != null) {
4782 parent.invalidate();
4783 }
4784 }
4785 // process all the shortcuts
4786 return false;
4787 }
4788 });
4789 }
4790
Sunny Goyale7b8cd92014-08-27 14:04:33 -07004791 public void removeAbandonedPromise(String packageName, UserHandleCompat user) {
4792 ArrayList<String> packages = new ArrayList<String>(1);
4793 packages.add(packageName);
4794 LauncherModel.deletePackageFromDatabase(mLauncher, packageName, user);
4795 removeItemsByPackageName(packages, user);
Chris Wren40c5ed32014-06-24 18:24:23 -04004796 }
4797
Sunny Goyala22666f2014-09-18 13:25:15 -07004798 public void updatePackageBadge(final String packageName, final UserHandleCompat user) {
4799 mapOverItems(MAP_RECURSE, new ItemOperator() {
4800 @Override
4801 public boolean evaluate(ItemInfo info, View v, View parent) {
4802 if (info instanceof ShortcutInfo && v instanceof BubbleTextView) {
4803 ShortcutInfo shortcutInfo = (ShortcutInfo) info;
4804 ComponentName cn = shortcutInfo.getTargetComponent();
4805 if (user.equals(shortcutInfo.user) && cn != null
4806 && shortcutInfo.isPromise()
4807 && packageName.equals(cn.getPackageName())) {
4808 if (shortcutInfo.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
4809 // For auto install apps update the icon as well as label.
4810 mIconCache.getTitleAndIcon(shortcutInfo,
4811 shortcutInfo.promisedIntent, user, true);
4812 } else {
4813 // Only update the icon for restored apps.
4814 shortcutInfo.updateIcon(mIconCache);
4815 }
4816 BubbleTextView shortcut = (BubbleTextView) v;
4817 shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache, true, false);
4818
4819 if (parent != null) {
4820 parent.invalidate();
4821 }
4822 }
4823 }
4824 // process all the shortcuts
4825 return false;
4826 }
4827 });
4828 }
4829
Sunny Goyale755d462014-07-22 13:48:29 -07004830 public void updatePackageState(ArrayList<PackageInstallInfo> installInfos) {
Sunny Goyale755d462014-07-22 13:48:29 -07004831 for (final PackageInstallInfo installInfo : installInfos) {
Sunny Goyal4390ace2014-10-13 11:33:11 -07004832 if (installInfo.state == PackageInstallerCompat.STATUS_INSTALLED) {
4833 continue;
4834 }
4835
Sunny Goyale755d462014-07-22 13:48:29 -07004836 mapOverItems(MAP_RECURSE, new ItemOperator() {
4837 @Override
4838 public boolean evaluate(ItemInfo info, View v, View parent) {
Sunny Goyal34942622014-08-29 17:20:55 -07004839 if (info instanceof ShortcutInfo && v instanceof BubbleTextView) {
4840 ShortcutInfo si = (ShortcutInfo) info;
4841 ComponentName cn = si.getTargetComponent();
4842 if (si.isPromise() && (cn != null)
4843 && installInfo.packageName.equals(cn.getPackageName())) {
4844 si.setInstallProgress(installInfo.progress);
4845 if (installInfo.state == PackageInstallerCompat.STATUS_FAILED) {
4846 // Mark this info as broken.
4847 si.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
4848 }
4849 ((BubbleTextView)v).applyState(false);
4850 }
Sunny Goyal0fc1be12014-08-11 17:05:23 -07004851 } else if (v instanceof PendingAppWidgetHostView
4852 && info instanceof LauncherAppWidgetInfo
4853 && ((LauncherAppWidgetInfo) info).providerName.getPackageName()
4854 .equals(installInfo.packageName)) {
4855 ((LauncherAppWidgetInfo) info).installProgress = installInfo.progress;
4856 ((PendingAppWidgetHostView) v).applyState();
Sunny Goyale755d462014-07-22 13:48:29 -07004857 }
Sunny Goyal0fc1be12014-08-11 17:05:23 -07004858
Sunny Goyale755d462014-07-22 13:48:29 -07004859 // process all the shortcuts
4860 return false;
4861 }
4862 });
Sunny Goyal651077b2014-06-30 14:15:31 -07004863 }
4864 }
4865
Sunny Goyal4390ace2014-10-13 11:33:11 -07004866 void widgetsRestored(ArrayList<LauncherAppWidgetInfo> changedInfo) {
Sunny Goyal0fc1be12014-08-11 17:05:23 -07004867 if (!changedInfo.isEmpty()) {
4868 DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo,
4869 mLauncher.getAppWidgetHost());
Adam Cohen59400422014-03-05 18:07:04 -08004870 if (LauncherModel.getProviderInfo(getContext(),
Sunny Goyal0fc1be12014-08-11 17:05:23 -07004871 changedInfo.get(0).providerName) != null) {
4872 // Re-inflate the widgets which have changed status
4873 widgetRefresh.run();
4874 } else {
4875 // widgetRefresh will automatically run when the packages are updated.
Sunny Goyal4390ace2014-10-13 11:33:11 -07004876 // For now just update the progress bars
4877 for (LauncherAppWidgetInfo info : changedInfo) {
4878 if (info.hostView instanceof PendingAppWidgetHostView) {
4879 info.installProgress = 100;
4880 ((PendingAppWidgetHostView) info.hostView).applyState();
4881 }
4882 }
Sunny Goyal0fc1be12014-08-11 17:05:23 -07004883 }
Sunny Goyalff572272014-07-23 13:58:07 -07004884 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07004885 }
4886
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004887 private void moveToScreen(int page, boolean animate) {
Adam Cohen6c5891a2014-07-09 23:53:15 -07004888 if (!workspaceInModalState()) {
Winson Chungde1af762011-07-21 16:44:07 -07004889 if (animate) {
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004890 snapToPage(page);
Winson Chungde1af762011-07-21 16:44:07 -07004891 } else {
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004892 setCurrentPage(page);
Winson Chungde1af762011-07-21 16:44:07 -07004893 }
Joe Onoratoc45b1682010-01-11 18:48:40 -05004894 }
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004895 View child = getChildAt(page);
Winson Chung64359a52013-07-08 17:17:08 -07004896 if (child != null) {
4897 child.requestFocus();
4898 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004899 }
4900
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004901 void moveToDefaultScreen(boolean animate) {
4902 moveToScreen(mDefaultPage, animate);
4903 }
4904
4905 void moveToCustomContentScreen(boolean animate) {
4906 if (hasCustomContent()) {
4907 int ccIndex = getPageIndexForScreenId(CUSTOM_CONTENT_SCREEN_ID);
4908 if (animate) {
4909 snapToPage(ccIndex);
4910 } else {
4911 setCurrentPage(ccIndex);
4912 }
4913 View child = getChildAt(ccIndex);
4914 if (child != null) {
4915 child.requestFocus();
4916 }
4917 }
Adam Cohen76128b62013-11-14 16:43:59 +00004918 exitWidgetResizeMode();
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004919 }
4920
Michael Jurka0142d492010-08-25 17:46:15 -07004921 @Override
Winson Chung7819a562013-09-19 15:55:45 -07004922 protected PageIndicator.PageMarkerResources getPageIndicatorMarker(int pageIndex) {
4923 long screenId = getScreenIdForPageIndex(pageIndex);
4924 if (screenId == EXTRA_EMPTY_SCREEN_ID) {
Michael Jurkafe0ace32013-10-03 01:05:14 -07004925 int count = mScreenOrder.size() - numCustomPages();
Winson Chung7819a562013-09-19 15:55:45 -07004926 if (count > 1) {
Winson Chungba4e52f2013-10-01 17:22:13 -07004927 return new PageIndicator.PageMarkerResources(R.drawable.ic_pageindicator_current,
Winson Chung7819a562013-09-19 15:55:45 -07004928 R.drawable.ic_pageindicator_add);
4929 }
Winson Chung82dfe582013-07-26 15:07:49 -07004930 }
Winson Chung7819a562013-09-19 15:55:45 -07004931
Winson Chung82dfe582013-07-26 15:07:49 -07004932 return super.getPageIndicatorMarker(pageIndex);
4933 }
4934
4935 @Override
Michael Jurka0142d492010-08-25 17:46:15 -07004936 public void syncPages() {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004937 }
Michael Jurka0142d492010-08-25 17:46:15 -07004938
4939 @Override
Winson Chungf314b0e2011-08-16 11:54:27 -07004940 public void syncPageItems(int page, boolean immediate) {
Michael Jurka0142d492010-08-25 17:46:15 -07004941 }
Winson Chung6a0f57d2011-06-29 20:10:49 -07004942
Adam Cohen53805212013-10-01 10:39:23 -07004943 protected String getPageIndicatorDescription() {
4944 String settings = getResources().getString(R.string.settings_button_text);
4945 return getCurrentPageDescription() + ", " + settings;
4946 }
4947
Winson Chung6a0f57d2011-06-29 20:10:49 -07004948 protected String getCurrentPageDescription() {
4949 int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
Adam Cohen53805212013-10-01 10:39:23 -07004950 int delta = numCustomPages();
4951 if (hasCustomContent() && getNextPage() == 0) {
4952 return mCustomContentDescription;
4953 }
Michael Jurka8b805b12012-04-18 14:23:14 -07004954 return String.format(getContext().getString(R.string.workspace_scroll_format),
Adam Cohen53805212013-10-01 10:39:23 -07004955 page + 1 - delta, getChildCount() - delta);
Winson Chung6a0f57d2011-06-29 20:10:49 -07004956 }
Adam Cohen8dfcba42011-07-07 16:38:18 -07004957
4958 public void getLocationInDragLayer(int[] loc) {
4959 mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
4960 }
Sunny Goyal0fc1be12014-08-11 17:05:23 -07004961
4962 /**
4963 * Used as a workaround to ensure that the AppWidgetService receives the
4964 * PACKAGE_ADDED broadcast before updating widgets.
4965 */
4966 private class DeferredWidgetRefresh implements Runnable {
4967 private final ArrayList<LauncherAppWidgetInfo> mInfos;
4968 private final LauncherAppWidgetHost mHost;
4969 private final Handler mHandler;
4970
4971 private boolean mRefreshPending;
4972
4973 public DeferredWidgetRefresh(ArrayList<LauncherAppWidgetInfo> infos,
4974 LauncherAppWidgetHost host) {
4975 mInfos = infos;
4976 mHost = host;
4977 mHandler = new Handler();
4978 mRefreshPending = true;
4979
4980 mHost.addProviderChangeListener(this);
4981 // Force refresh after 10 seconds, if we don't get the provider changed event.
4982 // This could happen when the provider is no longer available in the app.
4983 mHandler.postDelayed(this, 10000);
4984 }
4985
4986 @Override
4987 public void run() {
4988 mHost.removeProviderChangeListener(this);
4989 mHandler.removeCallbacks(this);
4990
4991 if (!mRefreshPending) {
4992 return;
4993 }
4994
4995 mRefreshPending = false;
4996
4997 for (LauncherAppWidgetInfo info : mInfos) {
4998 if (info.hostView instanceof PendingAppWidgetHostView) {
4999 PendingAppWidgetHostView view = (PendingAppWidgetHostView) info.hostView;
5000 mLauncher.removeAppWidget(info);
5001
5002 CellLayout cl = (CellLayout) view.getParent().getParent();
5003 // Remove the current widget
5004 cl.removeView(view);
5005 mLauncher.bindAppWidget(info);
5006 }
5007 }
5008 }
5009 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08005010}