blob: 685dcee4f29eace6284ad1cb9d46e6f8ca1a054f [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
Sunny Goyalaeb16432017-10-16 11:46:41 -070019import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
Sunny Goyal5bc6b6f2017-10-26 15:36:10 -070020import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
Sunny Goyalaeb16432017-10-16 11:46:41 -070021import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS;
Sunny Goyal3e3f44c2017-10-23 17:14:52 -070022import static com.android.launcher3.LauncherState.ALL_APPS;
23import static com.android.launcher3.LauncherState.NORMAL;
24import static com.android.launcher3.LauncherState.SPRING_LOADED;
Sunny Goyalc99cb172017-10-19 16:15:09 -070025import static com.android.launcher3.Utilities.isAccessibilityEnabled;
Sunny Goyalaeb16432017-10-16 11:46:41 -070026
Michael Jurka0280c3b2010-09-17 15:00:07 -070027import android.animation.Animator;
Adam Cohenf358a4b2013-07-23 16:47:31 -070028import android.animation.AnimatorListenerAdapter;
Sunny Goyal9443ef52016-06-07 16:21:03 -070029import android.animation.AnimatorSet;
Adam Cohen22cba7f2013-07-19 16:14:00 -070030import android.animation.LayoutTransition;
Michael Jurka0280c3b2010-09-17 15:00:07 -070031import android.animation.ObjectAnimator;
Adam Cohenad4e15c2013-10-17 16:21:35 -070032import android.animation.PropertyValuesHolder;
Sunny Goyal6d64d882016-06-24 09:57:04 -070033import android.animation.ValueAnimator;
34import android.animation.ValueAnimator.AnimatorUpdateListener;
Sunny Goyal70660032015-05-14 00:07:08 -070035import android.annotation.SuppressLint;
Dianne Hackborn8f573952009-08-10 23:21:09 -070036import android.app.WallpaperManager;
Michael Jurkabed61d22012-02-14 22:51:29 -080037import android.appwidget.AppWidgetHostView;
Romain Guy629de3e2010-01-13 12:20:59 -080038import android.appwidget.AppWidgetProviderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080039import android.content.Context;
Patrick Dubroy7247f632010-08-04 16:02:59 -070040import android.content.res.Resources;
Joe Onorato4be866d2010-10-10 11:26:02 -070041import android.graphics.Bitmap;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080042import android.graphics.Canvas;
Winson Chungb8c69f32011-10-19 21:36:08 -070043import android.graphics.Point;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080044import android.graphics.Rect;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070045import android.graphics.drawable.Drawable;
Sunny Goyal0fc1be12014-08-11 17:05:23 -070046import android.os.Handler;
Joe Onorato956091b2010-02-19 12:47:40 -080047import android.os.IBinder;
Adam Powell495f2892010-04-16 16:40:55 -070048import android.os.Parcelable;
Sunny Goyal7c74e4a2016-12-15 15:53:17 -080049import android.os.UserHandle;
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;
Sunny Goyala92e0df2016-06-09 12:08:22 -070052import android.util.Property;
Adam Cohen1462de32012-07-24 22:34:36 -070053import android.util.SparseArray;
Sunny Goyal9b29ca52017-02-17 10:39:44 -080054import android.view.LayoutInflater;
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;
Jon Miranda9485e5f2016-10-18 11:47:42 -070058import android.widget.Toast;
Sunny Goyal64a75aa2017-07-03 13:50:52 -070059
Adam Cohenc2d6e892014-10-16 09:49:24 -070060import com.android.launcher3.Launcher.LauncherOverlay;
Sunny Goyal64a75aa2017-07-03 13:50:52 -070061import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
Sunny Goyal3e3f44c2017-10-23 17:14:52 -070062import com.android.launcher3.LauncherStateManager.AnimationConfig;
Hyunyoung Song0de01172016-10-05 16:27:48 -070063import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
Sunny Goyal58376922015-05-26 18:56:52 -070064import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate;
Sunny Goyale78e3d72015-09-24 11:23:31 -070065import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
Sunny Goyalb5e65c82016-10-26 18:32:38 -070066import com.android.launcher3.anim.AnimationLayerSet;
Sunny Goyal5bc6b6f2017-10-26 15:36:10 -070067import com.android.launcher3.anim.Interpolators;
Tony Wickham11ba5072017-02-02 12:42:50 -080068import com.android.launcher3.badge.FolderBadgeInfo;
Sunny Goyal470f9002016-03-21 13:02:29 -070069import com.android.launcher3.compat.AppWidgetManagerCompat;
Tony Wickhame0c33232016-02-08 11:37:04 -080070import com.android.launcher3.config.FeatureFlags;
Vadim Tryshevfedca432015-08-19 17:55:02 -070071import com.android.launcher3.dragndrop.DragController;
72import com.android.launcher3.dragndrop.DragLayer;
Sunny Goyal94b510c2016-08-16 15:36:48 -070073import com.android.launcher3.dragndrop.DragOptions;
Vadim Tryshevfedca432015-08-19 17:55:02 -070074import com.android.launcher3.dragndrop.DragView;
75import com.android.launcher3.dragndrop.SpringLoadedDragController;
Sunny Goyal470f9002016-03-21 13:02:29 -070076import com.android.launcher3.folder.Folder;
77import com.android.launcher3.folder.FolderIcon;
Jon Mirandaa0233f72017-06-22 18:34:45 -070078import com.android.launcher3.folder.PreviewBackground;
Sunny Goyal06e21a22016-08-11 16:02:02 -070079import com.android.launcher3.graphics.DragPreviewProvider;
Sunny Goyal96ac68a2017-02-02 16:37:21 -080080import com.android.launcher3.graphics.PreloadIconDrawable;
Tony Wickham540913e2017-01-23 11:47:51 -080081import com.android.launcher3.popup.PopupContainerWithArrow;
Jon Mirandaaa6f66a2017-05-09 12:43:44 -070082import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
Sunny Goyal85525172017-11-06 13:00:42 -080083import com.android.launcher3.uioverrides.OverviewState;
Sunny Goyal16764582017-11-06 16:00:34 -080084import com.android.launcher3.uioverrides.UiFactory;
Hyunyoung Song3f5a11f2017-06-29 16:42:04 -070085import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
Sunny Goyal6c46a6d2016-11-23 02:24:32 +053086import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
Hyunyoung Songddec1c72016-04-12 18:32:04 -070087import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
Sunny Goyald3b87ef2016-07-28 12:11:54 -070088import com.android.launcher3.util.ItemInfoMatcher;
Sunny Goyale2df0622015-04-24 11:27:00 -070089import com.android.launcher3.util.LongArrayMap;
Tony Wickham010d2552017-01-20 08:15:28 -080090import com.android.launcher3.util.PackageUserKey;
Adam Cohen091440a2015-03-18 14:16:05 -070091import com.android.launcher3.util.Thunk;
Sunny Goyal9eba1fd2015-10-16 08:58:57 -070092import com.android.launcher3.util.WallpaperOffsetInterpolator;
Hyunyoung Song3f471442015-04-08 19:01:34 -070093import com.android.launcher3.widget.PendingAddShortcutInfo;
94import com.android.launcher3.widget.PendingAddWidgetInfo;
Sunny Goyal64a75aa2017-07-03 13:50:52 -070095
Adam Cohen716b51e2011-06-30 12:09:54 -070096import java.util.ArrayList;
97import java.util.HashSet;
Tony Wickham010d2552017-01-20 08:15:28 -080098import java.util.Set;
Adam Cohen716b51e2011-06-30 12:09:54 -070099
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800100/**
Michael Jurka0142d492010-08-25 17:46:15 -0700101 * The workspace is a wide area with a wallpaper and a finite number of pages.
102 * Each page contains a number of icons, folders or widgets the user can
Winson Chungaafa03c2010-06-11 17:34:16 -0700103 * interact with. A workspace is meant to be used with a fixed width only.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800104 */
Sunny Goyal5a1f53b2015-05-27 10:24:24 -0700105public class Workspace extends PagedView
Sunny Goyalf5440cb2016-12-14 15:13:00 -0800106 implements DropTarget, DragSource, View.OnTouchListener,
Sunny Goyaldb364372016-10-26 19:12:47 -0700107 DragController.DragListener, ViewGroup.OnHierarchyChangeListener,
Sunny Goyalc4fa8c32017-11-07 12:23:58 -0800108 Insettable, LauncherStateManager.StateHandler {
Joe Onorato3a8820b2009-11-10 15:06:42 -0800109 private static final String TAG = "Launcher.Workspace";
Michael Jurka0142d492010-08-25 17:46:15 -0700110
Tony Wickham251ab6e2016-12-02 18:54:32 -0800111 /** The value that {@link #mTransitionProgress} must be greater than for
112 * {@link #transitionStateShouldAllowDrop()} to return true. */
113 private static final float ALLOW_DROP_TRANSITION_PROGRESS = 0.25f;
114
115 /** The value that {@link #mTransitionProgress} must be greater than for
116 * {@link #isFinishedSwitchingState()} ()} to return true. */
117 private static final float FINISHED_SWITCHING_STATE_TRANSITION_PROGRESS = 0.5f;
118
Rajeev Kumar93c05122017-06-12 12:59:47 -0700119 private static final boolean ENFORCE_DRAG_EVENT_ORDER = false;
Sunny Goyal58376922015-05-26 18:56:52 -0700120
Sunny Goyalcf25b522015-07-09 00:01:18 -0700121 private static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400;
122 private static final int FADE_EMPTY_SCREEN_DURATION = 150;
Adam Cohenad4e15c2013-10-17 16:21:35 -0700123
Adam Cohened51cc92011-08-01 20:28:08 -0700124 private static final int ADJACENT_SCREEN_DROP_DURATION = 300;
125
Sunny Goyal7ce471b2017-08-02 03:37:39 -0700126 private static final int DEFAULT_PAGE = 0;
127
Sunny Goyalcf25b522015-07-09 00:01:18 -0700128 private static final boolean MAP_NO_RECURSE = false;
129 private static final boolean MAP_RECURSE = true;
130
131 // The screen id used for the empty screen always present to the right.
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700132 public static final long EXTRA_EMPTY_SCREEN_ID = -201;
133 // The is the first screen. It is always present, even if its empty.
134 public static final long FIRST_SCREEN_ID = 0;
135
Adam Cohen22cba7f2013-07-19 16:14:00 -0700136 private LayoutTransition mLayoutTransition;
Adam Cohen091440a2015-03-18 14:16:05 -0700137 @Thunk final WallpaperManager mWallpaperManager;
Winson Chungaafa03c2010-06-11 17:34:16 -0700138
Adam Cohen5084cba2013-09-03 12:01:16 -0700139 private ShortcutAndWidgetContainer mDragSourceInternal;
140
Rajeev Kumar93c05122017-06-12 12:59:47 -0700141 @Thunk final LongArrayMap<CellLayout> mWorkspaceScreens = new LongArrayMap<>();
142 @Thunk final ArrayList<Long> mScreenOrder = new ArrayList<>();
Adam Cohendcd297f2013-06-18 13:13:40 -0700143
Adam Cohen091440a2015-03-18 14:16:05 -0700144 @Thunk Runnable mRemoveEmptyScreenRunnable;
145 @Thunk boolean mDeferRemoveExtraEmptyScreen = false;
Adam Cohenad4e15c2013-10-17 16:21:35 -0700146
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800147 /**
148 * CellInfo for the cell that is currently being dragged
149 */
150 private CellLayout.CellInfo mDragInfo;
Winson Chungaafa03c2010-06-11 17:34:16 -0700151
Jeff Sharkey70864282009-04-07 21:08:40 -0700152 /**
153 * Target drop area calculated during last acceptDrop call.
154 */
Adam Cohen091440a2015-03-18 14:16:05 -0700155 @Thunk int[] mTargetCell = new int[2];
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700156 private int mDragOverX = -1;
157 private int mDragOverY = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800158
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700159 /**
160 * The CellLayout that is currently being dragged over
161 */
Adam Cohen091440a2015-03-18 14:16:05 -0700162 @Thunk CellLayout mDragTargetLayout = null;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700163 /**
Tony Wickham95cdb3a2016-02-18 14:37:07 -0800164 * The CellLayout that we will show as highlighted
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700165 */
166 private CellLayout mDragOverlappingLayout = null;
167
168 /**
169 * The CellLayout which will be dropped to
170 */
171 private CellLayout mDropToLayout = null;
172
Rajeev Kumar93c05122017-06-12 12:59:47 -0700173 @Thunk final Launcher mLauncher;
Adam Cohen091440a2015-03-18 14:16:05 -0700174 @Thunk DragController mDragController;
Winson Chungaafa03c2010-06-11 17:34:16 -0700175
Sunny Goyalcf25b522015-07-09 00:01:18 -0700176 private final int[] mTempXY = new int[2];
Adam Cohen091440a2015-03-18 14:16:05 -0700177 @Thunk float[] mDragViewVisualCenter = new float[2];
Rajeev Kumar93c05122017-06-12 12:59:47 -0700178 private final float[] mTempTouchCoordinates = new float[2];
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800179
Michael Jurkac2f7f472010-12-14 15:34:42 -0800180 private SpringLoadedDragController mSpringLoadedDragController;
Michael Jurkad3ef3062010-11-23 16:23:58 -0800181
Sunny Goyalbfa8fbd2016-06-20 10:53:52 -0700182 // Direction used for moving the workspace and hotseat UI
183 public enum Direction {
184 X (TRANSLATION_X),
185 Y (TRANSLATION_Y);
186
187 private final Property<View, Float> viewProperty;
188
189 Direction(Property<View, Float> viewProperty) {
190 this.viewProperty = viewProperty;
191 }
192 }
193
Sunny Goyal6d64d882016-06-24 09:57:04 -0700194 private static final int HOTSEAT_STATE_ALPHA_INDEX = 2;
195
196 /**
197 * These values correspond to {@link Direction#X} & {@link Direction#Y}
198 */
Rajeev Kumar93c05122017-06-12 12:59:47 -0700199 private final float[] mPageAlpha = new float[] {1, 1};
Sunny Goyal6d64d882016-06-24 09:57:04 -0700200 /**
201 * Hotseat alpha can be changed when moving horizontally, vertically, changing states.
202 * The values correspond to {@link Direction#X}, {@link Direction#Y} &
203 * {@link #HOTSEAT_STATE_ALPHA_INDEX} respectively.
204 */
Rajeev Kumar93c05122017-06-12 12:59:47 -0700205 private final float[] mHotseatAlpha = new float[] {1, 1, 1};
Sunny Goyalbfa8fbd2016-06-20 10:53:52 -0700206
Michael Jurkad74c9842011-07-10 12:44:21 -0700207 private boolean mIsSwitchingState = false;
Michael Jurkad74c9842011-07-10 12:44:21 -0700208
Michael Jurkad74c9842011-07-10 12:44:21 -0700209 boolean mChildrenLayersEnabled = true;
Michael Jurkadee05892010-07-27 10:01:56 -0700210
Adam Cohenaccfd562013-07-12 14:40:40 -0700211 private boolean mStripScreensOnPageStopMoving = false;
212
Sunny Goyal06e21a22016-08-11 16:02:02 -0700213 private DragPreviewProvider mOutlineProvider = null;
Rajeev Kumar93c05122017-06-12 12:59:47 -0700214 private final boolean mWorkspaceFadeInAdjacentScreens;
Joe Onorato4be866d2010-10-10 11:26:02 -0700215
Sunny Goyal9eba1fd2015-10-16 08:58:57 -0700216 final WallpaperOffsetInterpolator mWallpaperOffset;
Winsonc7d2e832016-07-28 12:24:55 -0700217 private boolean mUnlockWallpaperFromDefaultPageOnLayout;
Michael Jurka2a4f4922014-01-29 16:32:39 +0100218
Adam Cohen19072da2011-05-31 14:30:45 -0700219 // Variables relating to the creation of user folders by hovering shortcuts over shortcuts
Adam Cohen482ed822012-03-02 14:15:13 -0800220 private static final int FOLDER_CREATION_TIMEOUT = 0;
Adam Cohenfa3c58f2013-12-06 16:10:55 -0800221 public static final int REORDER_TIMEOUT = 350;
Adam Cohen19072da2011-05-31 14:30:45 -0700222 private final Alarm mFolderCreationAlarm = new Alarm();
Adam Cohen482ed822012-03-02 14:15:13 -0800223 private final Alarm mReorderAlarm = new Alarm();
Jon Mirandaa0233f72017-06-22 18:34:45 -0700224 private PreviewBackground mFolderCreateBg;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700225 private FolderIcon mDragOverFolderIcon = null;
Adam Cohen19072da2011-05-31 14:30:45 -0700226 private boolean mCreateUserFolderOnDrop = false;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700227 private boolean mAddToExistingFolderOnDrop = false;
Adam Cohen3aff81c2012-05-16 21:01:01 -0700228 private float mMaxDistanceForFolderCreation;
Adam Cohen073a46f2011-05-17 16:28:09 -0700229
Adam Cohenf8d28232011-02-01 21:47:00 -0800230 // Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget)
231 private float mXDown;
232 private float mYDown;
233 final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
234 final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
235 final static float TOUCH_SLOP_DAMPING_FACTOR = 4;
236
Adam Cohened66b2b2012-01-23 17:28:51 -0800237 // Relating to the animation of items being dropped externally
Adam Cohend41fbf52012-02-16 23:53:59 -0800238 public static final int ANIMATE_INTO_POSITION_AND_DISAPPEAR = 0;
239 public static final int ANIMATE_INTO_POSITION_AND_REMAIN = 1;
240 public static final int ANIMATE_INTO_POSITION_AND_RESIZE = 2;
241 public static final int COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION = 3;
242 public static final int CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION = 4;
Adam Cohened66b2b2012-01-23 17:28:51 -0800243
Adam Cohen482ed822012-03-02 14:15:13 -0800244 // Related to dragging, folder creation and reordering
245 private static final int DRAG_MODE_NONE = 0;
246 private static final int DRAG_MODE_CREATE_FOLDER = 1;
247 private static final int DRAG_MODE_ADD_TO_FOLDER = 2;
248 private static final int DRAG_MODE_REORDER = 3;
249 private int mDragMode = DRAG_MODE_NONE;
Adam Cohen091440a2015-03-18 14:16:05 -0700250 @Thunk int mLastReorderX = -1;
251 @Thunk int mLastReorderY = -1;
Adam Cohen482ed822012-03-02 14:15:13 -0800252
Adam Cohen1462de32012-07-24 22:34:36 -0700253 private SparseArray<Parcelable> mSavedStates;
Rajeev Kumar93c05122017-06-12 12:59:47 -0700254 private final ArrayList<Integer> mRestoredPages = new ArrayList<>();
Adam Cohen1462de32012-07-24 22:34:36 -0700255
Adam Cohen7d30a372013-07-01 17:03:59 -0700256 private float mCurrentScale;
Winson Chung70442722012-02-10 15:43:22 -0800257 private float mTransitionProgress;
Adam Cohen4b285c52011-07-21 14:24:06 -0700258
Adam Cohenc2d6e892014-10-16 09:49:24 -0700259 // State related to Launcher Overlay
260 LauncherOverlay mLauncherOverlay;
261 boolean mScrollInteractionBegan;
262 boolean mStartedSendingScrollEvents;
Hyunyoung Song0de01172016-10-05 16:27:48 -0700263 float mLastOverlayScroll = 0;
Hyunyoung Song3f5a11f2017-06-29 16:42:04 -0700264 boolean mOverlayShown = false;
Adam Cohen8d769d62017-06-23 17:27:38 -0700265
Tony Wickham29d853c2015-09-08 10:35:56 -0700266 private boolean mForceDrawAdjacentPages = false;
Sunny Goyalc99cb172017-10-19 16:15:09 -0700267 private boolean mPageRearrangeEnabled = false;
268
Sunny Goyalc86df472016-02-25 09:19:38 -0800269 // Total over scrollX in the overlay direction.
270 private float mOverlayTranslation;
Adam Cohenc2d6e892014-10-16 09:49:24 -0700271
Winson Chungdc61c4d2015-04-20 18:26:57 -0700272 // Handles workspace state transitions
Rajeev Kumar93c05122017-06-12 12:59:47 -0700273 private final WorkspaceStateTransitionAnimation mStateTransitionAnimation;
Winson Chungdc61c4d2015-04-20 18:26:57 -0700274
Sunny Goyal1d08f702015-05-04 15:50:25 -0700275 private AccessibilityDelegate mPagesAccessibilityDelegate;
276
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800277 /**
278 * Used to inflate the Workspace from XML.
279 *
280 * @param context The application's context.
Michael Jurka0142d492010-08-25 17:46:15 -0700281 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800282 */
283 public Workspace(Context context, AttributeSet attrs) {
284 this(context, attrs, 0);
285 }
286
287 /**
288 * Used to inflate the Workspace from XML.
289 *
290 * @param context The application's context.
Michael Jurka0142d492010-08-25 17:46:15 -0700291 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800292 * @param defStyle Unused.
293 */
294 public Workspace(Context context, AttributeSet attrs, int defStyle) {
295 super(context, attrs, defStyle);
Michael Jurka5f1c5092010-09-03 14:15:02 -0700296
Tony2fd02082016-10-07 12:50:01 -0700297 mLauncher = Launcher.getLauncher(context);
Winson Chungdc61c4d2015-04-20 18:26:57 -0700298 mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this);
Winson Chung867ca622012-02-21 15:48:35 -0800299 final Resources res = getResources();
Adam Cohen2e6da152015-05-06 11:42:25 -0700300 DeviceProfile grid = mLauncher.getDeviceProfile();
301 mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
Dianne Hackborn8f573952009-08-10 23:21:09 -0700302 mWallpaperManager = WallpaperManager.getInstance(context);
Winson Chungaafa03c2010-06-11 17:34:16 -0700303
Sunny Goyal9eba1fd2015-10-16 08:58:57 -0700304 mWallpaperOffset = new WallpaperOffsetInterpolator(this);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800305
Michael Jurka8b805b12012-04-18 14:23:14 -0700306 setOnHierarchyChangeListener(this);
Joe Onorato0d44e942009-11-16 18:20:51 -0800307 setHapticFeedbackEnabled(false);
Michael Jurka0142d492010-08-25 17:46:15 -0700308
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800309 initWorkspace();
Winson Chungc35afb22011-02-23 13:01:49 -0800310
311 // Disable multitouch across the workspace/all apps/customize tray
312 setMotionEventSplittingEnabled(true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800313 }
314
John Spurlock77e1f472013-09-11 10:09:51 -0400315 @Override
316 public void setInsets(Rect insets) {
317 mInsets.set(insets);
318 }
319
Jon Mirandae96798e2016-12-07 12:10:44 -0800320 /**
321 * Estimates the size of an item using spans: hSpan, vSpan.
322 *
Jon Mirandae96798e2016-12-07 12:10:44 -0800323 * @return MAX_VALUE for each dimension if unsuccessful.
324 */
Sunny Goyalaa3a2ba2017-09-26 12:43:16 -0700325 public int[] estimateItemSize(ItemInfo itemInfo) {
Michael Jurka038f9d82011-11-03 13:50:45 -0700326 int[] size = new int[2];
327 if (getChildCount() > 0) {
Sunny Goyal7ce471b2017-08-02 03:37:39 -0700328 // Use the first page to estimate the child position
329 CellLayout cl = (CellLayout) getChildAt(0);
Jon Mirandae96798e2016-12-07 12:10:44 -0800330 boolean isWidget = itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
331
Tony Wickhama501d492015-11-03 18:05:01 -0800332 Rect r = estimateItemPosition(cl, 0, 0, itemInfo.spanX, itemInfo.spanY);
Jon Mirandae96798e2016-12-07 12:10:44 -0800333
334 float scale = 1;
335 if (isWidget) {
336 DeviceProfile profile = mLauncher.getDeviceProfile();
Jon Miranda6f6a06a2016-12-15 11:24:18 -0800337 scale = Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y);
Jon Mirandae96798e2016-12-07 12:10:44 -0800338 }
Adam Cohend41fbf52012-02-16 23:53:59 -0800339 size[0] = r.width();
340 size[1] = r.height();
Jon Mirandae96798e2016-12-07 12:10:44 -0800341
Sunny Goyalaa3a2ba2017-09-26 12:43:16 -0700342 if (isWidget) {
Jon Mirandae96798e2016-12-07 12:10:44 -0800343 size[0] /= scale;
344 size[1] /= scale;
345 }
Michael Jurka038f9d82011-11-03 13:50:45 -0700346 return size;
347 } else {
348 size[0] = Integer.MAX_VALUE;
349 size[1] = Integer.MAX_VALUE;
350 return size;
351 }
352 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700353
Tony Wickhama501d492015-11-03 18:05:01 -0800354 public Rect estimateItemPosition(CellLayout cl, int hCell, int vCell, int hSpan, int vSpan) {
Adam Cohend41fbf52012-02-16 23:53:59 -0800355 Rect r = new Rect();
Michael Jurka038f9d82011-11-03 13:50:45 -0700356 cl.cellToRect(hCell, vCell, hSpan, vSpan, r);
Michael Jurka038f9d82011-11-03 13:50:45 -0700357 return r;
358 }
359
Sunny Goyal58376922015-05-26 18:56:52 -0700360 @Override
Sunny Goyal94b510c2016-08-16 15:36:48 -0700361 public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
Sunny Goyal58376922015-05-26 18:56:52 -0700362 if (ENFORCE_DRAG_EVENT_ORDER) {
Hyunyoung Song0de01172016-10-05 16:27:48 -0700363 enforceDragParity("onDragStart", 0, 0);
Sunny Goyal58376922015-05-26 18:56:52 -0700364 }
365
Tony Wickham4e47b5b2016-10-18 14:43:01 -0700366 if (mDragInfo != null && mDragInfo.cell != null) {
367 CellLayout layout = (CellLayout) mDragInfo.cell.getParent().getParent();
368 layout.markCellsAsUnoccupiedForView(mDragInfo.cell);
369 }
370
Sunny Goyal06e21a22016-08-11 16:02:02 -0700371 if (mOutlineProvider != null) {
Sunny Goyalaa3a2ba2017-09-26 12:43:16 -0700372 if (dragObject.dragView != null) {
373 Bitmap preview = dragObject.dragView.getPreviewBitmap();
374
375 // The outline is used to visualize where the item will land if dropped
376 mOutlineProvider.generateDragOutline(preview);
377 }
Sunny Goyal06e21a22016-08-11 16:02:02 -0700378 }
379
Sunny Goyala502aa32017-10-02 16:04:06 -0700380 updateChildrenLayersEnabled();
Sunny Goyal58376922015-05-26 18:56:52 -0700381
Sunny Goyal94b510c2016-08-16 15:36:48 -0700382 // Do not add a new page if it is a accessible drag which was not started by the workspace.
383 // We do not support accessibility drag from other sources and instead provide a direct
384 // action for move/add to homescreen.
385 // When a accessible drag is started by the folder, we only allow rearranging withing the
386 // folder.
387 boolean addNewPage = !(options.isAccessibleDrag && dragObject.dragSource != this);
388
389 if (addNewPage) {
Sunny Goyal58376922015-05-26 18:56:52 -0700390 mDeferRemoveExtraEmptyScreen = false;
391 addExtraEmptyScreenOnDrag();
Sunny Goyal06e21a22016-08-11 16:02:02 -0700392
Sunny Goyal94b510c2016-08-16 15:36:48 -0700393 if (dragObject.dragInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
394 && dragObject.dragSource != this) {
Sunny Goyal06e21a22016-08-11 16:02:02 -0700395 // When dragging a widget from different source, move to a page which has
396 // enough space to place this widget (after rearranging/resizing). We special case
397 // widgets as they cannot be placed inside a folder.
398 // Start at the current page and search right (on LTR) until finding a page with
399 // enough space. Since an empty screen is the furthest right, a page must be found.
400 int currentPage = getPageNearestToCenterOfScreen();
401 for (int pageIndex = currentPage; pageIndex < getPageCount(); pageIndex++) {
402 CellLayout page = (CellLayout) getPageAt(pageIndex);
Sunny Goyal94b510c2016-08-16 15:36:48 -0700403 if (page.hasReorderSolution(dragObject.dragInfo)) {
Sunny Goyal06e21a22016-08-11 16:02:02 -0700404 setCurrentPage(pageIndex);
405 break;
406 }
407 }
408 }
409 }
410
Sunny Goyalf5440cb2016-12-14 15:13:00 -0800411 // Always enter the spring loaded mode
Sunny Goyal3e3f44c2017-10-23 17:14:52 -0700412 mLauncher.getStateManager().goToState(SPRING_LOADED);
Michael Jurkad74c9842011-07-10 12:44:21 -0700413 }
414
Adam Cohen689ff162014-05-08 17:27:56 -0700415 public void deferRemoveExtraEmptyScreen() {
416 mDeferRemoveExtraEmptyScreen = true;
417 }
418
Sunny Goyal58376922015-05-26 18:56:52 -0700419 @Override
Michael Jurkad74c9842011-07-10 12:44:21 -0700420 public void onDragEnd() {
Sunny Goyal58376922015-05-26 18:56:52 -0700421 if (ENFORCE_DRAG_EVENT_ORDER) {
Hyunyoung Song0de01172016-10-05 16:27:48 -0700422 enforceDragParity("onDragEnd", 0, 0);
Sunny Goyal58376922015-05-26 18:56:52 -0700423 }
424
Adam Cohen689ff162014-05-08 17:27:56 -0700425 if (!mDeferRemoveExtraEmptyScreen) {
426 removeExtraEmptyScreen(true, mDragSourceInternal != null);
427 }
428
Sunny Goyala502aa32017-10-02 16:04:06 -0700429 updateChildrenLayersEnabled();
Tony Wickham4e47b5b2016-10-18 14:43:01 -0700430 mOutlineProvider = null;
Adam Cohen5084cba2013-09-03 12:01:16 -0700431 mDragSourceInternal = null;
Michael Jurkad74c9842011-07-10 12:44:21 -0700432 }
433
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800434 /**
435 * Initializes various states for this workspace.
436 */
Michael Jurka0142d492010-08-25 17:46:15 -0700437 protected void initWorkspace() {
Sunny Goyal7ce471b2017-08-02 03:37:39 -0700438 mCurrentPage = DEFAULT_PAGE;
Adam Cohen2e6da152015-05-06 11:42:25 -0700439 DeviceProfile grid = mLauncher.getDeviceProfile();
Patrick Dubroycd68ff52010-10-28 17:57:05 -0700440 setWillNotDraw(false);
Romain Guyce3cbd12013-02-25 15:00:36 -0800441 setClipChildren(false);
442 setClipToPadding(false);
Derek Prothrodadd9842014-01-17 13:43:50 -0500443
Sunny Goyalc99cb172017-10-19 16:15:09 -0700444 // TODO: Remove this
445 setMinScale(OverviewState.SCALE_FACTOR);
Adam Cohen22cba7f2013-07-19 16:14:00 -0700446 setupLayoutTransition();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800447
Winson Chung5f8afe62013-08-12 16:19:28 -0700448 mMaxDistanceForFolderCreation = (0.55f * grid.iconSizePx);
Michael Jurkaa6a05472013-11-13 17:59:46 +0100449
450 // Set the wallpaper dimensions when Launcher starts up
Adam Cohen824fcb32014-05-21 23:01:25 +0000451 setWallpaperDimension();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800452 }
453
Sunny Goyald0a6ae72016-06-16 12:29:03 -0700454 @Override
455 public void initParentViews(View parent) {
456 super.initParentViews(parent);
Sunny Goyal16764582017-11-06 16:00:34 -0800457 mPageIndicator.setAccessibilityDelegate(UiFactory.newPageIndicatorAccessibilityDelegate());
Sunny Goyald0a6ae72016-06-16 12:29:03 -0700458 }
459
Adam Cohen22cba7f2013-07-19 16:14:00 -0700460 private void setupLayoutTransition() {
461 // We want to show layout transitions when pages are deleted, to close the gap.
462 mLayoutTransition = new LayoutTransition();
463 mLayoutTransition.enableTransitionType(LayoutTransition.DISAPPEARING);
464 mLayoutTransition.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
465 mLayoutTransition.disableTransitionType(LayoutTransition.APPEARING);
466 mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
467 setLayoutTransition(mLayoutTransition);
468 }
469
Winson Chung964df6b2013-10-11 15:55:37 -0700470 void enableLayoutTransitions() {
471 setLayoutTransition(mLayoutTransition);
472 }
473 void disableLayoutTransitions() {
474 setLayoutTransition(null);
475 }
476
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800477 @Override
Michael Jurka8b805b12012-04-18 14:23:14 -0700478 public void onChildViewAdded(View parent, View child) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800479 if (!(child instanceof CellLayout)) {
480 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
481 }
Adam Cohen2801caf2011-05-13 20:57:39 -0700482 CellLayout cl = ((CellLayout) child);
483 cl.setOnInterceptTouchListener(this);
Adam Cohen2801caf2011-05-13 20:57:39 -0700484 cl.setClickable(true);
Sunny Goyalc46bfef2015-01-05 12:40:08 -0800485 cl.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
Winson Chungf70696d2013-06-25 16:19:53 -0700486 super.onChildViewAdded(parent, child);
Michael Jurka8b805b12012-04-18 14:23:14 -0700487 }
488
Patrick Dubroya0aa0122011-02-24 11:42:23 -0800489 boolean isTouchActive() {
490 return mTouchState != TOUCH_STATE_REST;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800491 }
492
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700493 /**
494 * Initializes and binds the first page
Hyunyoung Song0de01172016-10-05 16:27:48 -0700495 * @param qsb an existing qsb to recycle or null.
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700496 */
497 public void bindAndInitFirstWorkspaceScreen(View qsb) {
Sunny Goyala9e2f5a2016-06-10 12:22:04 -0700498 if (!FeatureFlags.QSB_ON_FIRST_SCREEN) {
499 return;
500 }
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700501 // Add the first page
502 CellLayout firstPage = insertNewWorkspaceScreen(Workspace.FIRST_SCREEN_ID, 0);
Sunny Goyal6178f132016-07-11 17:30:03 -0700503 // Always add a QSB on the first screen.
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700504 if (qsb == null) {
Sunny Goyal6178f132016-07-11 17:30:03 -0700505 // In transposed layout, we add the QSB in the Grid. As workspace does not touch the
506 // edges, we do not need a full width QSB.
Sunny Goyal966d9012017-06-06 16:43:59 -0700507 qsb = LayoutInflater.from(getContext())
Sunny Goyalc7b8df82017-06-27 11:11:03 -0700508 .inflate(R.layout.search_container_workspace,firstPage, false);
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700509 }
510
Sunny Goyal6178f132016-07-11 17:30:03 -0700511 CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, firstPage.getCountX(), 1);
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700512 lp.canReorder = false;
Sunny Goyalc7b8df82017-06-27 11:11:03 -0700513 if (!firstPage.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true)) {
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700514 Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout");
515 }
516 }
517
Winson Chung9e6a0a22013-08-27 11:58:12 -0700518 public void removeAllWorkspaceScreens() {
Winson Chung964df6b2013-10-11 15:55:37 -0700519 // Disable all layout transitions before removing all pages to ensure that we don't get the
Sunny Goyal7ce471b2017-08-02 03:37:39 -0700520 // transition animations competing with us changing the scroll when we add pages
Winson Chung964df6b2013-10-11 15:55:37 -0700521 disableLayoutTransitions();
522
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700523 // Recycle the QSB widget
Sunny Goyalc7b8df82017-06-27 11:11:03 -0700524 View qsb = findViewById(R.id.search_container_workspace);
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700525 if (qsb != null) {
526 ((ViewGroup) qsb.getParent()).removeView(qsb);
527 }
528
Winson Chung9e6a0a22013-08-27 11:58:12 -0700529 // Remove the pages and clear the screen models
530 removeAllViews();
531 mScreenOrder.clear();
532 mWorkspaceScreens.clear();
Winson Chung964df6b2013-10-11 15:55:37 -0700533
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700534 // Ensure that the first page is always present
535 bindAndInitFirstWorkspaceScreen(qsb);
536
Winson Chung964df6b2013-10-11 15:55:37 -0700537 // Re-enable the layout transitions
538 enableLayoutTransitions();
Winson Chung9e6a0a22013-08-27 11:58:12 -0700539 }
540
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700541 public void insertNewWorkspaceScreenBeforeEmptyScreen(long screenId) {
Winson Chung64359a52013-07-08 17:17:08 -0700542 // Find the index to insert this view into. If the empty screen exists, then
543 // insert it before that.
544 int insertIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
545 if (insertIndex < 0) {
546 insertIndex = mScreenOrder.size();
547 }
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700548 insertNewWorkspaceScreen(screenId, insertIndex);
Adam Cohendcd297f2013-06-18 13:13:40 -0700549 }
550
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700551 public void insertNewWorkspaceScreen(long screenId) {
552 insertNewWorkspaceScreen(screenId, getChildCount());
Winson Chung64359a52013-07-08 17:17:08 -0700553 }
554
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700555 public CellLayout insertNewWorkspaceScreen(long screenId, int insertIndex) {
Adam Cohen5084cba2013-09-03 12:01:16 -0700556 if (mWorkspaceScreens.containsKey(screenId)) {
557 throw new RuntimeException("Screen id " + screenId + " already exists!");
558 }
559
Sunny Goyal70660032015-05-14 00:07:08 -0700560 // Inflate the cell layout, but do not add it automatically so that we can get the newly
561 // created CellLayout.
Sunny Goyal9b29ca52017-02-17 10:39:44 -0800562 CellLayout newScreen = (CellLayout) LayoutInflater.from(getContext()).inflate(
Sunny Goyal70660032015-05-14 00:07:08 -0700563 R.layout.workspace_screen, this, false /* attachToRoot */);
Adam Cohen7d30a372013-07-01 17:03:59 -0700564 newScreen.setOnLongClickListener(mLongClickListener);
Adam Cohenf358a4b2013-07-23 16:47:31 -0700565 newScreen.setOnClickListener(mLauncher);
Winson Chung837b2412013-09-09 14:43:51 -0700566 newScreen.setSoundEffectsEnabled(false);
Jon Miranda28032002017-07-13 16:18:56 -0700567
568 int paddingLeftRight = mLauncher.getDeviceProfile().cellLayoutPaddingLeftRightPx;
Jon Miranda9593f9e2017-08-09 14:57:30 -0700569 int paddingBottom = mLauncher.getDeviceProfile().cellLayoutBottomPaddingPx;
570 newScreen.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
Jon Miranda28032002017-07-13 16:18:56 -0700571
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);
Sunny Goyalea609262017-10-25 15:47:38 -0700575 mStateTransitionAnimation.applyChildState(
576 mLauncher.getStateManager().getState(), newScreen, insertIndex);
Adam Cohenc9735cf2015-01-23 16:11:55 -0800577
Sunny Goyalae502842016-06-17 08:43:56 -0700578 if (mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
Sunny Goyale9b651e2015-04-24 11:44:51 -0700579 newScreen.enableAccessibleDrag(true, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
Adam Cohenc9735cf2015-01-23 16:11:55 -0800580 }
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700581
582 return newScreen;
Adam Cohendcd297f2013-06-18 13:13:40 -0700583 }
584
Adam Cohen5084cba2013-09-03 12:01:16 -0700585 public void addExtraEmptyScreenOnDrag() {
586 boolean lastChildOnScreen = false;
587 boolean childOnFinalScreen = false;
588
Adam Cohenad4e15c2013-10-17 16:21:35 -0700589 // Cancel any pending removal of empty screen
590 mRemoveEmptyScreenRunnable = null;
591
Adam Cohen5084cba2013-09-03 12:01:16 -0700592 if (mDragSourceInternal != null) {
593 if (mDragSourceInternal.getChildCount() == 1) {
594 lastChildOnScreen = true;
595 }
596 CellLayout cl = (CellLayout) mDragSourceInternal.getParent();
597 if (indexOfChild(cl) == getChildCount() - 1) {
598 childOnFinalScreen = true;
599 }
600 }
601
602 // If this is the last item on the final screen
603 if (lastChildOnScreen && childOnFinalScreen) {
604 return;
605 }
606 if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) {
607 insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID);
608 }
609 }
610
611 public boolean addExtraEmptyScreen() {
612 if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) {
613 insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID);
614 return true;
615 }
616 return false;
617 }
618
Adam Cohenad4e15c2013-10-17 16:21:35 -0700619 private void convertFinalScreenToEmptyScreenIfNecessary() {
Adam Cohendcb173d2014-04-01 13:33:58 -0700620 if (mLauncher.isWorkspaceLoading()) {
621 // Invalid and dangerous operation if workspace is loading
Adam Cohendcb173d2014-04-01 13:33:58 -0700622 return;
623 }
624
Adam Cohenad4e15c2013-10-17 16:21:35 -0700625 if (hasExtraEmptyScreen() || mScreenOrder.size() == 0) return;
626 long finalScreenId = mScreenOrder.get(mScreenOrder.size() - 1);
627
Adam Cohenad4e15c2013-10-17 16:21:35 -0700628 CellLayout finalScreen = mWorkspaceScreens.get(finalScreenId);
629
630 // If the final screen is empty, convert it to the extra empty screen
Adam Cohen917e3882013-10-31 15:03:35 -0700631 if (finalScreen.getShortcutsAndWidgets().getChildCount() == 0 &&
632 !finalScreen.isDropPending()) {
Adam Cohenad4e15c2013-10-17 16:21:35 -0700633 mWorkspaceScreens.remove(finalScreenId);
634 mScreenOrder.remove(finalScreenId);
635
Sunny Goyal7ce471b2017-08-02 03:37:39 -0700636 // if this is the last screen, convert it to the empty screen
Adam Cohenad4e15c2013-10-17 16:21:35 -0700637 mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, finalScreen);
638 mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
Winson Chunga90303b2013-11-15 13:05:06 -0800639
Winson Chungf0716b72013-11-18 16:09:54 -0800640 // Update the model if we have changed any screens
Rajeev Kumar93c05122017-06-12 12:59:47 -0700641 LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
Adam Cohen5084cba2013-09-03 12:01:16 -0700642 }
643 }
644
Adam Cohen689ff162014-05-08 17:27:56 -0700645 public void removeExtraEmptyScreen(final boolean animate, boolean stripEmptyScreens) {
646 removeExtraEmptyScreenDelayed(animate, null, 0, stripEmptyScreens);
Adam Cohenad4e15c2013-10-17 16:21:35 -0700647 }
648
Adam Cohen689ff162014-05-08 17:27:56 -0700649 public void removeExtraEmptyScreenDelayed(final boolean animate, final Runnable onComplete,
Adam Cohenad4e15c2013-10-17 16:21:35 -0700650 final int delay, final boolean stripEmptyScreens) {
Adam Cohendcb173d2014-04-01 13:33:58 -0700651 if (mLauncher.isWorkspaceLoading()) {
652 // Don't strip empty screens if the workspace is still loading
Adam Cohendcb173d2014-04-01 13:33:58 -0700653 return;
654 }
655
Adam Cohenad4e15c2013-10-17 16:21:35 -0700656 if (delay > 0) {
657 postDelayed(new Runnable() {
658 @Override
659 public void run() {
Adam Cohen689ff162014-05-08 17:27:56 -0700660 removeExtraEmptyScreenDelayed(animate, onComplete, 0, stripEmptyScreens);
Adam Cohenad4e15c2013-10-17 16:21:35 -0700661 }
Adam Cohenad4e15c2013-10-17 16:21:35 -0700662 }, delay);
663 return;
664 }
665
666 convertFinalScreenToEmptyScreenIfNecessary();
667 if (hasExtraEmptyScreen()) {
668 int emptyIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
669 if (getNextPage() == emptyIndex) {
670 snapToPage(getNextPage() - 1, SNAP_OFF_EMPTY_SCREEN_DURATION);
671 fadeAndRemoveEmptyScreen(SNAP_OFF_EMPTY_SCREEN_DURATION, FADE_EMPTY_SCREEN_DURATION,
672 onComplete, stripEmptyScreens);
673 } else {
Sunny Goyal2b7bf872015-05-28 10:06:43 -0700674 snapToPage(getNextPage(), 0);
Adam Cohenad4e15c2013-10-17 16:21:35 -0700675 fadeAndRemoveEmptyScreen(0, FADE_EMPTY_SCREEN_DURATION,
676 onComplete, stripEmptyScreens);
677 }
678 return;
Adam Cohen3a14eeb2013-11-15 16:51:22 +0000679 } else if (stripEmptyScreens) {
680 // If we're not going to strip the empty screens after removing
681 // the extra empty screen, do it right away.
682 stripEmptyScreens();
Adam Cohenad4e15c2013-10-17 16:21:35 -0700683 }
Adam Cohen3a14eeb2013-11-15 16:51:22 +0000684
Adam Cohenad4e15c2013-10-17 16:21:35 -0700685 if (onComplete != null) {
686 onComplete.run();
687 }
688 }
689
690 private void fadeAndRemoveEmptyScreen(int delay, int duration, final Runnable onComplete,
691 final boolean stripEmptyScreens) {
Winson Chunga90303b2013-11-15 13:05:06 -0800692 // XXX: Do we need to update LM workspace screens below?
Adam Cohenad4e15c2013-10-17 16:21:35 -0700693 PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0f);
694 PropertyValuesHolder bgAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", 0f);
695
696 final CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
697
698 mRemoveEmptyScreenRunnable = new Runnable() {
699 @Override
700 public void run() {
701 if (hasExtraEmptyScreen()) {
702 mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
703 mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
704 removeView(cl);
705 if (stripEmptyScreens) {
706 stripEmptyScreens();
707 }
Tony Wickhamd4b28d22016-05-26 13:00:02 -0700708 // Update the page indicator to reflect the removed page.
709 showPageIndicatorAtCurrentScroll();
Adam Cohenad4e15c2013-10-17 16:21:35 -0700710 }
711 }
712 };
713
714 ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(cl, alpha, bgAlpha);
715 oa.setDuration(duration);
716 oa.setStartDelay(delay);
717 oa.addListener(new AnimatorListenerAdapter() {
718 @Override
719 public void onAnimationEnd(Animator animation) {
720 if (mRemoveEmptyScreenRunnable != null) {
721 mRemoveEmptyScreenRunnable.run();
722 }
723 if (onComplete != null) {
724 onComplete.run();
725 }
726 }
727 });
728 oa.start();
729 }
730
Michael Jurka2f817312013-09-20 03:03:42 +0200731 public boolean hasExtraEmptyScreen() {
Sunny Goyal7ce471b2017-08-02 03:37:39 -0700732 return mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID) && getChildCount() > 1;
Michael Jurka2f817312013-09-20 03:03:42 +0200733 }
734
Adam Cohendcd297f2013-06-18 13:13:40 -0700735 public long commitExtraEmptyScreen() {
Adam Cohendcb173d2014-04-01 13:33:58 -0700736 if (mLauncher.isWorkspaceLoading()) {
737 // Invalid and dangerous operation if workspace is loading
Adam Cohendcb173d2014-04-01 13:33:58 -0700738 return -1;
739 }
Winson Chunga90303b2013-11-15 13:05:06 -0800740
Adam Cohendcd297f2013-06-18 13:13:40 -0700741 CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
Winson Chungc9168342013-06-26 14:54:55 -0700742 mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
Adam Cohendcd297f2013-06-18 13:13:40 -0700743 mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
744
Sunny Goyald2497482015-09-22 18:24:19 -0700745 long newId = LauncherSettings.Settings.call(getContext().getContentResolver(),
746 LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
747 .getLong(LauncherSettings.Settings.EXTRA_VALUE);
Adam Cohendcd297f2013-06-18 13:13:40 -0700748 mWorkspaceScreens.put(newId, cl);
749 mScreenOrder.add(newId);
750
Winson Chung64359a52013-07-08 17:17:08 -0700751 // Update the model for the new screen
Rajeev Kumar93c05122017-06-12 12:59:47 -0700752 LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
Winson Chung64359a52013-07-08 17:17:08 -0700753
Adam Cohendcd297f2013-06-18 13:13:40 -0700754 return newId;
755 }
756
Adam Cohendcd297f2013-06-18 13:13:40 -0700757 public CellLayout getScreenWithId(long screenId) {
Sunny Goyal9eba1fd2015-10-16 08:58:57 -0700758 return mWorkspaceScreens.get(screenId);
Adam Cohendcd297f2013-06-18 13:13:40 -0700759 }
760
761 public long getIdForScreen(CellLayout layout) {
Sunny Goyale2df0622015-04-24 11:27:00 -0700762 int index = mWorkspaceScreens.indexOfValue(layout);
763 if (index != -1) {
764 return mWorkspaceScreens.keyAt(index);
Adam Cohendcd297f2013-06-18 13:13:40 -0700765 }
766 return -1;
767 }
768
769 public int getPageIndexForScreenId(long screenId) {
770 return indexOfChild(mWorkspaceScreens.get(screenId));
771 }
772
773 public long getScreenIdForPageIndex(int index) {
Winson Chung5f8afe62013-08-12 16:19:28 -0700774 if (0 <= index && index < mScreenOrder.size()) {
775 return mScreenOrder.get(index);
776 }
777 return -1;
Adam Cohendcd297f2013-06-18 13:13:40 -0700778 }
779
Sunny Goyal83a8f042015-05-19 12:52:12 -0700780 public ArrayList<Long> getScreenOrder() {
Winson Chungc9168342013-06-26 14:54:55 -0700781 return mScreenOrder;
782 }
783
Adam Cohendcd297f2013-06-18 13:13:40 -0700784 public void stripEmptyScreens() {
Adam Cohen65e43032014-03-03 11:37:21 -0800785 if (mLauncher.isWorkspaceLoading()) {
Adam Cohendcb173d2014-04-01 13:33:58 -0700786 // Don't strip empty screens if the workspace is still loading.
787 // This is dangerous and can result in data loss.
Adam Cohen517a7f52014-03-01 12:12:59 -0800788 return;
789 }
790
Sunny Goyal9ccafbf2016-10-26 13:06:08 -0700791 if (isPageInTransition()) {
Adam Cohenaccfd562013-07-12 14:40:40 -0700792 mStripScreensOnPageStopMoving = true;
793 return;
794 }
795
796 int currentPage = getNextPage();
Rajeev Kumar93c05122017-06-12 12:59:47 -0700797 ArrayList<Long> removeScreens = new ArrayList<>();
Sunny Goyale2df0622015-04-24 11:27:00 -0700798 int total = mWorkspaceScreens.size();
799 for (int i = 0; i < total; i++) {
800 long id = mWorkspaceScreens.keyAt(i);
801 CellLayout cl = mWorkspaceScreens.valueAt(i);
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700802 // FIRST_SCREEN_ID can never be removed.
Sunny Goyala9e2f5a2016-06-10 12:22:04 -0700803 if ((!FeatureFlags.QSB_ON_FIRST_SCREEN || id > FIRST_SCREEN_ID)
804 && cl.getShortcutsAndWidgets().getChildCount() == 0) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700805 removeScreens.add(id);
806 }
807 }
808
Sunny Goyalae502842016-06-17 08:43:56 -0700809 boolean isInAccessibleDrag = mLauncher.getAccessibilityDelegate().isInAccessibleDrag();
Vadim Tryshevde1e67c2015-08-11 16:21:04 -0700810
Adam Cohen5084cba2013-09-03 12:01:16 -0700811 // We enforce at least one page to add new items to. In the case that we remove the last
812 // such screen, we convert the last screen to the empty screen
Sunny Goyal7ce471b2017-08-02 03:37:39 -0700813 int minScreens = 1;
Adam Cohen5084cba2013-09-03 12:01:16 -0700814
Adam Cohendcd297f2013-06-18 13:13:40 -0700815 int pageShift = 0;
816 for (Long id: removeScreens) {
817 CellLayout cl = mWorkspaceScreens.get(id);
818 mWorkspaceScreens.remove(id);
819 mScreenOrder.remove(id);
Adam Cohen5084cba2013-09-03 12:01:16 -0700820
821 if (getChildCount() > minScreens) {
822 if (indexOfChild(cl) < currentPage) {
823 pageShift++;
824 }
Vadim Tryshevde1e67c2015-08-11 16:21:04 -0700825
Sunny Goyalae502842016-06-17 08:43:56 -0700826 if (isInAccessibleDrag) {
Vadim Tryshevde1e67c2015-08-11 16:21:04 -0700827 cl.enableAccessibleDrag(false, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
828 }
829
Adam Cohen5084cba2013-09-03 12:01:16 -0700830 removeView(cl);
831 } else {
Sunny Goyal7ce471b2017-08-02 03:37:39 -0700832 // if this is the last screen, convert it to the empty screen
Adam Cohenad4e15c2013-10-17 16:21:35 -0700833 mRemoveEmptyScreenRunnable = null;
Adam Cohen5084cba2013-09-03 12:01:16 -0700834 mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, cl);
835 mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
Adam Cohendcd297f2013-06-18 13:13:40 -0700836 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700837 }
Winson Chung64359a52013-07-08 17:17:08 -0700838
839 if (!removeScreens.isEmpty()) {
840 // Update the model if we have changed any screens
Rajeev Kumar93c05122017-06-12 12:59:47 -0700841 LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
Winson Chung64359a52013-07-08 17:17:08 -0700842 }
Adam Cohenaccfd562013-07-12 14:40:40 -0700843
844 if (pageShift >= 0) {
845 setCurrentPage(currentPage - pageShift);
846 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700847 }
848
Sunny Goyald5462aa2016-11-22 16:52:39 +0530849 /**
850 * At bind time, we use the rank (screenId) to compute x and y for hotseat items.
851 * See {@link #addInScreen}.
852 */
853 public void addInScreenFromBind(View child, ItemInfo info) {
854 int x = info.cellX;
855 int y = info.cellY;
856 if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
857 int screenId = (int) info.screenId;
858 x = mLauncher.getHotseat().getCellXFromOrder(screenId);
859 y = mLauncher.getHotseat().getCellYFromOrder(screenId);
860 }
861 addInScreen(child, info.container, info.screenId, x, y, info.spanX, info.spanY);
Adam Cohendcd297f2013-06-18 13:13:40 -0700862 }
863
Sunny Goyald5462aa2016-11-22 16:52:39 +0530864 /**
865 * Adds the specified child in the specified screen based on the {@param info}
Rajeev Kumar93c05122017-06-12 12:59:47 -0700866 * See {@link #addInScreen(View, long, long, int, int, int, int)}.
Sunny Goyald5462aa2016-11-22 16:52:39 +0530867 */
868 public void addInScreen(View child, ItemInfo info) {
869 addInScreen(child, info.container, info.screenId, info.cellX, info.cellY,
870 info.spanX, info.spanY);
Winson Chungaafa03c2010-06-11 17:34:16 -0700871 }
872
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800873 /**
874 * Adds the specified child in the specified screen. The position and dimension of
875 * the child are defined by x, y, spanX and spanY.
876 *
877 * @param child The child to add in one of the workspace's screens.
Adam Cohendcd297f2013-06-18 13:13:40 -0700878 * @param screenId The screen in which to add the child.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800879 * @param x The X position of the child in the screen's grid.
880 * @param y The Y position of the child in the screen's grid.
881 * @param spanX The number of cells spanned horizontally by the child.
882 * @param spanY The number of cells spanned vertically by the child.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800883 */
Sunny Goyald5462aa2016-11-22 16:52:39 +0530884 private void addInScreen(View child, long container, long screenId, int x, int y,
885 int spanX, int spanY) {
Winson Chung3d503fb2011-07-13 17:25:49 -0700886 if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700887 if (getScreenWithId(screenId) == null) {
888 Log.e(TAG, "Skipping child, screenId " + screenId + " not found");
Winson Chung8481e322013-08-09 16:06:38 -0700889 // DEBUGGING - Print out the stack trace to see where we are adding from
890 new Throwable().printStackTrace();
Winson Chung3d503fb2011-07-13 17:25:49 -0700891 return;
892 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800893 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700894 if (screenId == EXTRA_EMPTY_SCREEN_ID) {
Adam Cohendedbd962013-07-11 14:21:49 -0700895 // This should never happen
896 throw new RuntimeException("Screen id should not be EXTRA_EMPTY_SCREEN_ID");
Adam Cohendcd297f2013-06-18 13:13:40 -0700897 }
898
Winson Chung3d503fb2011-07-13 17:25:49 -0700899 final CellLayout layout;
900 if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
901 layout = mLauncher.getHotseat().getLayout();
Sunny Goyalb3726d92014-08-20 16:58:17 -0700902 child.setOnKeyListener(new HotseatIconKeyEventListener());
Winson Chung3d503fb2011-07-13 17:25:49 -0700903
Adam Cohen099f60d2011-08-23 21:07:26 -0700904 // Hide folder title in the hotseat
905 if (child instanceof FolderIcon) {
906 ((FolderIcon) child).setTextVisible(false);
907 }
Winson Chung3d503fb2011-07-13 17:25:49 -0700908 } else {
Adam Cohen099f60d2011-08-23 21:07:26 -0700909 // Show folder title if not in the hotseat
910 if (child instanceof FolderIcon) {
911 ((FolderIcon) child).setTextVisible(true);
912 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700913 layout = getScreenWithId(screenId);
Adam Cohenac56cff2011-09-28 20:45:37 -0700914 child.setOnKeyListener(new IconKeyEventListener());
Winson Chung3d503fb2011-07-13 17:25:49 -0700915 }
916
Adam Cohen96d30a12013-07-16 18:13:21 -0700917 ViewGroup.LayoutParams genericLp = child.getLayoutParams();
Adam Cohened66b2b2012-01-23 17:28:51 -0800918 CellLayout.LayoutParams lp;
919 if (genericLp == null || !(genericLp instanceof CellLayout.LayoutParams)) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800920 lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
921 } else {
Adam Cohened66b2b2012-01-23 17:28:51 -0800922 lp = (CellLayout.LayoutParams) genericLp;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800923 lp.cellX = x;
924 lp.cellY = y;
925 lp.cellHSpan = spanX;
926 lp.cellVSpan = spanY;
927 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700928
Adam Cohen7f4eabe2011-04-21 16:19:16 -0700929 if (spanX < 0 && spanY < 0) {
930 lp.isLockedToGrid = false;
931 }
932
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700933 // Get the canonical child id to uniquely represent this view in this screen
Adam Cohenc76e1dd2013-11-14 11:09:14 +0000934 ItemInfo info = (ItemInfo) child.getTag();
935 int childId = mLauncher.getViewIdForItem(info);
936
Michael Jurkaf3ca3ab2010-10-20 17:08:24 -0700937 boolean markCellsAsOccupied = !(child instanceof Folder);
Sunny Goyald5462aa2016-11-22 16:52:39 +0530938 if (!layout.addViewToCellLayout(child, -1, childId, lp, markCellsAsOccupied)) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700939 // TODO: This branch occurs when the workspace is adding views
940 // outside of the defined grid
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700941 // maybe we should be deleting these items from the LauncherModel?
Sunny Goyala1365452015-10-01 15:46:24 -0700942 Log.e(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
Winson Chungaafa03c2010-06-11 17:34:16 -0700943 }
944
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800945 if (!(child instanceof Folder)) {
Joe Onorato0d44e942009-11-16 18:20:51 -0800946 child.setHapticFeedbackEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800947 child.setOnLongClickListener(mLongClickListener);
948 }
Joe Onorato00acb122009-08-04 16:04:30 -0400949 if (child instanceof DropTarget) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700950 mDragController.addDropTarget((DropTarget) child);
Joe Onorato00acb122009-08-04 16:04:30 -0400951 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800952 }
953
Patrick Dubroyd0ce1ec2011-01-19 18:47:27 -0800954 /**
Patrick Dubroye708c522011-03-01 16:03:43 -0800955 * Called directly from a CellLayout (not by the framework), after we've been added as a
956 * listener via setOnInterceptTouchEventListener(). This allows us to tell the CellLayout
957 * that it should intercept touch events, which is not something that is normally supported.
958 */
Sunny Goyal70660032015-05-14 00:07:08 -0700959 @SuppressLint("ClickableViewAccessibility")
Patrick Dubroye708c522011-03-01 16:03:43 -0800960 @Override
Michael Jurkadee05892010-07-27 10:01:56 -0700961 public boolean onTouch(View v, MotionEvent event) {
Hyunyoung Song46133612016-09-15 10:48:25 -0700962 return shouldConsumeTouch(v);
963 }
964
965 private boolean shouldConsumeTouch(View v) {
Tony Wickham0c7852f2016-12-02 14:47:04 -0800966 return !workspaceIconsCanBeDragged()
Adam Cohen6c5891a2014-07-09 23:53:15 -0700967 || (!workspaceInModalState() && indexOfChild(v) != mCurrentPage);
Patrick Dubroye708c522011-03-01 16:03:43 -0800968 }
969
Adam Cohenfc53cd22011-07-20 15:45:11 -0700970 public boolean isSwitchingState() {
971 return mIsSwitchingState;
972 }
973
Winson Chung70442722012-02-10 15:43:22 -0800974 /** This differs from isSwitchingState in that we take into account how far the transition
975 * has completed. */
Winson Chung9b0b2fe2012-02-24 13:03:34 -0800976 public boolean isFinishedSwitchingState() {
Tony Wickham251ab6e2016-12-02 18:54:32 -0800977 return !mIsSwitchingState
978 || (mTransitionProgress > FINISHED_SWITCHING_STATE_TRANSITION_PROGRESS);
Winson Chung70442722012-02-10 15:43:22 -0800979 }
980
Michael Jurka5f1c5092010-09-03 14:15:02 -0700981 @Override
982 public boolean dispatchUnhandledMove(View focused, int direction) {
Adam Cohen6c5891a2014-07-09 23:53:15 -0700983 if (workspaceInModalState() || !isFinishedSwitchingState()) {
Michael Jurka5f1c5092010-09-03 14:15:02 -0700984 // when the home screens are shrunken, shouldn't allow side-scrolling
985 return false;
986 }
987 return super.dispatchUnhandledMove(focused, direction);
988 }
989
990 @Override
991 public boolean onInterceptTouchEvent(MotionEvent ev) {
Michael Jurkad771c962011-08-09 15:00:48 -0700992 switch (ev.getAction() & MotionEvent.ACTION_MASK) {
993 case MotionEvent.ACTION_DOWN:
Adam Cohenf8d28232011-02-01 21:47:00 -0800994 mXDown = ev.getX();
995 mYDown = ev.getY();
Michael Jurkad771c962011-08-09 15:00:48 -0700996 break;
997 case MotionEvent.ACTION_POINTER_UP:
998 case MotionEvent.ACTION_UP:
999 if (mTouchState == TOUCH_STATE_REST) {
1000 final CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage);
Sunny Goyal2805e632015-05-20 15:35:32 -07001001 if (currentPage != null) {
Michael Jurkad771c962011-08-09 15:00:48 -07001002 onWallpaperTap(ev);
1003 }
1004 }
Adam Cohenf8d28232011-02-01 21:47:00 -08001005 }
Michael Jurka5f1c5092010-09-03 14:15:02 -07001006 return super.onInterceptTouchEvent(ev);
1007 }
1008
Michael Jurka1adf5392010-10-18 18:10:22 -07001009 @Override
1010 protected void determineScrollingStart(MotionEvent ev) {
Winson Chung70442722012-02-10 15:43:22 -08001011 if (!isFinishedSwitchingState()) return;
Adam Cohenf8d28232011-02-01 21:47:00 -08001012
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001013 float deltaX = ev.getX() - mXDown;
1014 float absDeltaX = Math.abs(deltaX);
1015 float absDeltaY = Math.abs(ev.getY() - mYDown);
Adam Cohenf8d28232011-02-01 21:47:00 -08001016
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001017 if (Float.compare(absDeltaX, 0f) == 0) return;
Adam Cohenf8d28232011-02-01 21:47:00 -08001018
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001019 float slope = absDeltaY / absDeltaX;
Winson Chung70442722012-02-10 15:43:22 -08001020 float theta = (float) Math.atan(slope);
Adam Cohenf8d28232011-02-01 21:47:00 -08001021
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001022 if (absDeltaX > mTouchSlop || absDeltaY > mTouchSlop) {
Winson Chung70442722012-02-10 15:43:22 -08001023 cancelCurrentPageLongPress();
1024 }
1025
1026 if (theta > MAX_SWIPE_ANGLE) {
1027 // Above MAX_SWIPE_ANGLE, we don't want to ever start scrolling the workspace
1028 return;
1029 } else if (theta > START_DAMPING_TOUCH_SLOP_ANGLE) {
1030 // Above START_DAMPING_TOUCH_SLOP_ANGLE and below MAX_SWIPE_ANGLE, we want to
1031 // increase the touch slop to make it harder to begin scrolling the workspace. This
1032 // results in vertically scrolling widgets to more easily. The higher the angle, the
1033 // more we increase touch slop.
1034 theta -= START_DAMPING_TOUCH_SLOP_ANGLE;
1035 float extraRatio = (float)
1036 Math.sqrt((theta / (MAX_SWIPE_ANGLE - START_DAMPING_TOUCH_SLOP_ANGLE)));
1037 super.determineScrollingStart(ev, 1 + TOUCH_SLOP_DAMPING_FACTOR * extraRatio);
1038 } else {
1039 // Below START_DAMPING_TOUCH_SLOP_ANGLE, we don't do anything special
1040 super.determineScrollingStart(ev);
Adam Cohenf8d28232011-02-01 21:47:00 -08001041 }
Michael Jurka1adf5392010-10-18 18:10:22 -07001042 }
1043
Sunny Goyal9ccafbf2016-10-26 13:06:08 -07001044 protected void onPageBeginTransition() {
1045 super.onPageBeginTransition();
Sunny Goyala502aa32017-10-02 16:04:06 -07001046 updateChildrenLayersEnabled();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001047 }
1048
Sunny Goyal9ccafbf2016-10-26 13:06:08 -07001049 protected void onPageEndTransition() {
1050 super.onPageEndTransition();
Sunny Goyala502aa32017-10-02 16:04:06 -07001051 updateChildrenLayersEnabled();
Michael Jurkad74c9842011-07-10 12:44:21 -07001052
Winson Chung3bc21c32012-01-20 13:59:18 -08001053 if (mDragController.isDragging()) {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001054 if (workspaceInModalState()) {
Winson Chung3bc21c32012-01-20 13:59:18 -08001055 // If we are in springloaded mode, then force an event to check if the current touch
1056 // is under a new page (to scroll to)
Winson Chung25460a12013-04-01 18:21:28 -07001057 mDragController.forceTouchMove();
Winson Chung3bc21c32012-01-20 13:59:18 -08001058 }
Patrick Dubroy1262e362010-10-06 15:49:50 -07001059 }
Adam Cohen26976d92011-03-22 15:33:33 -07001060
Adam Cohenaccfd562013-07-12 14:40:40 -07001061 if (mStripScreensOnPageStopMoving) {
1062 stripEmptyScreens();
1063 mStripScreensOnPageStopMoving = false;
1064 }
Adam Cohenc2d6e892014-10-16 09:49:24 -07001065 }
1066
1067 protected void onScrollInteractionBegin() {
1068 super.onScrollInteractionEnd();
1069 mScrollInteractionBegan = true;
1070 }
1071
1072 protected void onScrollInteractionEnd() {
1073 super.onScrollInteractionEnd();
1074 mScrollInteractionBegan = false;
1075 if (mStartedSendingScrollEvents) {
1076 mStartedSendingScrollEvents = false;
1077 mLauncherOverlay.onScrollInteractionEnd();
1078 }
1079 }
1080
1081 public void setLauncherOverlay(LauncherOverlay overlay) {
1082 mLauncherOverlay = overlay;
Sunny Goyalc86df472016-02-25 09:19:38 -08001083 // A new overlay has been set. Reset event tracking
1084 mStartedSendingScrollEvents = false;
1085 onOverlayScrollChanged(0);
1086 }
1087
Sunny Goyalc86df472016-02-25 09:19:38 -08001088
Sunny Goyal061380a2016-02-29 15:15:03 -08001089 private boolean isScrollingOverlay() {
1090 return mLauncherOverlay != null &&
Adam Cohen8d769d62017-06-23 17:27:38 -07001091 ((mIsRtl && getUnboundedScrollX() > mMaxScrollX) || (!mIsRtl && getUnboundedScrollX() < 0));
Sunny Goyal061380a2016-02-29 15:15:03 -08001092 }
1093
1094 @Override
1095 protected void snapToDestination() {
1096 // If we're overscrolling the overlay, we make sure to immediately reset the PagedView
1097 // to it's baseline position instead of letting the overscroll settle. The overlay handles
1098 // it's own settling, and every gesture to the overlay should be self-contained and start
1099 // from 0, so we zero it out here.
1100 if (isScrollingOverlay()) {
Sunny Goyal061380a2016-02-29 15:15:03 -08001101 // We reset mWasInOverscroll so that PagedView doesn't zero out the overscroll
Sunny Goyal76dbf6f2017-01-03 14:55:47 -08001102 // interaction when we call snapToPageImmediately.
Sunny Goyal061380a2016-02-29 15:15:03 -08001103 mWasInOverscroll = false;
Sunny Goyal76dbf6f2017-01-03 14:55:47 -08001104 snapToPageImmediately(0);
Sunny Goyal061380a2016-02-29 15:15:03 -08001105 } else {
1106 super.snapToDestination();
1107 }
1108 }
1109
Sunny Goyalc86df472016-02-25 09:19:38 -08001110 @Override
Tony Wickhamf549dab2016-05-16 09:54:06 -07001111 protected void onScrollChanged(int l, int t, int oldl, int oldt) {
1112 super.onScrollChanged(l, t, oldl, oldt);
1113
1114 // Update the page indicator progress.
1115 boolean isTransitioning = mIsSwitchingState
1116 || (getLayoutTransition() != null && getLayoutTransition().isRunning());
Tony Wickhamddb77002016-05-19 12:45:40 -07001117 if (!isTransitioning) {
Tony Wickhamf549dab2016-05-16 09:54:06 -07001118 showPageIndicatorAtCurrentScroll();
1119 }
Sunny Goyal9ccafbf2016-10-26 13:06:08 -07001120
1121 updatePageAlphaValues();
Sunny Goyal9ccafbf2016-10-26 13:06:08 -07001122 enableHwLayersOnVisiblePages();
Tony Wickhamf549dab2016-05-16 09:54:06 -07001123 }
1124
Sunny Goyalc99cb172017-10-19 16:15:09 -07001125 public void showPageIndicatorAtCurrentScroll() {
Tony Wickhamddb77002016-05-19 12:45:40 -07001126 if (mPageIndicator != null) {
1127 mPageIndicator.setScroll(getScrollX(), computeMaxScrollX());
1128 }
Tony Wickhamf549dab2016-05-16 09:54:06 -07001129 }
1130
1131 @Override
Adam Cohenc2d6e892014-10-16 09:49:24 -07001132 protected void overScroll(float amount) {
Adam Cohen8c4ca922014-10-24 17:40:34 -07001133 boolean shouldScrollOverlay = mLauncherOverlay != null &&
Sunny Goyal70660032015-05-14 00:07:08 -07001134 ((amount <= 0 && !mIsRtl) || (amount >= 0 && mIsRtl));
Adam Cohen8c4ca922014-10-24 17:40:34 -07001135
Hyunyoung Song0de01172016-10-05 16:27:48 -07001136 boolean shouldZeroOverlay = mLauncherOverlay != null && mLastOverlayScroll != 0 &&
Sunny Goyal70660032015-05-14 00:07:08 -07001137 ((amount >= 0 && !mIsRtl) || (amount <= 0 && mIsRtl));
Adam Cohenc2d6e892014-10-16 09:49:24 -07001138
1139 if (shouldScrollOverlay) {
1140 if (!mStartedSendingScrollEvents && mScrollInteractionBegan) {
1141 mStartedSendingScrollEvents = true;
1142 mLauncherOverlay.onScrollInteractionBegin();
Adam Cohenc2d6e892014-10-16 09:49:24 -07001143 }
Adam Cohenc2d6e892014-10-16 09:49:24 -07001144
Hyunyoung Song0de01172016-10-05 16:27:48 -07001145 mLastOverlayScroll = Math.abs(amount / getViewportWidth());
1146 mLauncherOverlay.onScrollChange(mLastOverlayScroll, mIsRtl);
Sunny Goyal7ce471b2017-08-02 03:37:39 -07001147 } else {
Adam Cohenc2d6e892014-10-16 09:49:24 -07001148 dampedOverScroll(amount);
Adam Cohenc2d6e892014-10-16 09:49:24 -07001149 }
Adam Cohen8c4ca922014-10-24 17:40:34 -07001150
1151 if (shouldZeroOverlay) {
Sunny Goyal70660032015-05-14 00:07:08 -07001152 mLauncherOverlay.onScrollChange(0, mIsRtl);
Adam Cohen8c4ca922014-10-24 17:40:34 -07001153 }
Adam Cohen89cbbb92013-09-25 16:52:48 -07001154 }
1155
Sunny Goyal65d9ceb2017-05-08 09:17:42 -07001156 @Override
1157 protected boolean shouldFlingForVelocity(int velocityX) {
1158 // When the overlay is moving, the fling or settle transition is controlled by the overlay.
Adam Cohen5360b6e2017-07-18 16:14:12 -07001159 return Float.compare(Math.abs(mOverlayTranslation), 0) == 0 &&
Sunny Goyal65d9ceb2017-05-08 09:17:42 -07001160 super.shouldFlingForVelocity(velocityX);
1161 }
1162
Sunny Goyalc86df472016-02-25 09:19:38 -08001163 /**
1164 * The overlay scroll is being controlled locally, just update our overlay effect
1165 */
1166 public void onOverlayScrollChanged(float scroll) {
Hyunyoung Song3f5a11f2017-06-29 16:42:04 -07001167
1168 if (Float.compare(scroll, 1f) == 0) {
1169 if (!mOverlayShown) {
1170 mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
1171 Action.Direction.LEFT, ContainerType.WORKSPACE, 0);
1172 }
1173 mOverlayShown = true;
1174 } else if (Float.compare(scroll, 0f) == 0) {
1175 if (mOverlayShown) {
1176 mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
1177 Action.Direction.RIGHT, ContainerType.WORKSPACE, -1);
1178 }
1179 mOverlayShown = false;
1180 }
Sunny Goyalc86df472016-02-25 09:19:38 -08001181 float offset = 0f;
1182 float slip = 0f;
1183
1184 scroll = Math.max(scroll - offset, 0);
1185 scroll = Math.min(1, scroll / (1 - offset));
1186
Sunny Goyal5bc6b6f2017-10-26 15:36:10 -07001187 float alpha = 1 - Interpolators.DEACCEL_3.getInterpolation(scroll);
Sunny Goyalc86df472016-02-25 09:19:38 -08001188 float transX = mLauncher.getDragLayer().getMeasuredWidth() * scroll;
1189 transX *= 1 - slip;
1190
1191 if (mIsRtl) {
1192 transX = -transX;
1193 }
Sunny Goyalee688162016-02-12 14:24:21 -08001194 mOverlayTranslation = transX;
Sunny Goyalc86df472016-02-25 09:19:38 -08001195
1196 // TODO(adamcohen): figure out a final effect here. We may need to recommend
1197 // different effects based on device performance. On at least one relatively high-end
1198 // device I've tried, translating the launcher causes things to get quite laggy.
Hyunyoung Song230b2b72016-07-06 15:03:59 -07001199 setWorkspaceTranslationAndAlpha(Direction.X, transX, alpha);
1200 setHotseatTranslationAndAlpha(Direction.X, transX, alpha);
Sunny Goyal6178f132016-07-11 17:30:03 -07001201 }
1202
1203 /**
1204 * Moves the workspace UI in the Y direction.
1205 * @param translation the amount of shift.
1206 * @param alpha the alpha for the workspace page
1207 */
1208 public void setWorkspaceYTranslationAndAlpha(float translation, float alpha) {
1209 setWorkspaceTranslationAndAlpha(Direction.Y, translation, alpha);
Sunny Goyala92e0df2016-06-09 12:08:22 -07001210 }
1211
1212 /**
1213 * Moves the workspace UI in the provided direction.
Sunny Goyalbfa8fbd2016-06-20 10:53:52 -07001214 * @param direction the direction to move the workspace
1215 * @param translation the amount of shift.
Sunny Goyala92e0df2016-06-09 12:08:22 -07001216 * @param alpha the alpha for the workspace page
1217 */
Sunny Goyal6178f132016-07-11 17:30:03 -07001218 private void setWorkspaceTranslationAndAlpha(Direction direction, float translation, float alpha) {
Sunny Goyalbfa8fbd2016-06-20 10:53:52 -07001219 Property<View, Float> property = direction.viewProperty;
1220 mPageAlpha[direction.ordinal()] = alpha;
1221 float finalAlpha = mPageAlpha[0] * mPageAlpha[1];
1222
Sunny Goyala92e0df2016-06-09 12:08:22 -07001223 View currentChild = getChildAt(getCurrentPage());
1224 if (currentChild != null) {
Sunny Goyalbfa8fbd2016-06-20 10:53:52 -07001225 property.set(currentChild, translation);
1226 currentChild.setAlpha(finalAlpha);
Sunny Goyala92e0df2016-06-09 12:08:22 -07001227 }
Sunny Goyalc86df472016-02-25 09:19:38 -08001228
1229 // When the animation finishes, reset all pages, just in case we missed a page.
Sunny Goyala92e0df2016-06-09 12:08:22 -07001230 if (Float.compare(translation, 0) == 0) {
Sunny Goyalc86df472016-02-25 09:19:38 -08001231 for (int i = getChildCount() - 1; i >= 0; i--) {
Sunny Goyala92e0df2016-06-09 12:08:22 -07001232 View child = getChildAt(i);
Sunny Goyalbfa8fbd2016-06-20 10:53:52 -07001233 property.set(child, translation);
1234 child.setAlpha(finalAlpha);
Sunny Goyalc86df472016-02-25 09:19:38 -08001235 }
1236 }
1237 }
1238
Sunny Goyala92e0df2016-06-09 12:08:22 -07001239 /**
1240 * Moves the Hotseat UI in the provided direction.
Sunny Goyalbfa8fbd2016-06-20 10:53:52 -07001241 * @param direction the direction to move the workspace
Winson1f064272016-07-18 17:18:02 -07001242 * @param translation the amount of shift.
Sunny Goyala92e0df2016-06-09 12:08:22 -07001243 * @param alpha the alpha for the hotseat page
1244 */
Hyunyoung Song230b2b72016-07-06 15:03:59 -07001245 public void setHotseatTranslationAndAlpha(Direction direction, float translation, float alpha) {
Sunny Goyalbfa8fbd2016-06-20 10:53:52 -07001246 Property<View, Float> property = direction.viewProperty;
Winson1f064272016-07-18 17:18:02 -07001247 // Skip the page indicator movement in the vertical bar layout
1248 if (direction != Direction.Y || !mLauncher.getDeviceProfile().isVerticalBarLayout()) {
1249 property.set(mPageIndicator, translation);
1250 }
Sunny Goyalbfa8fbd2016-06-20 10:53:52 -07001251 property.set(mLauncher.getHotseat(), translation);
Sunny Goyal6d64d882016-06-24 09:57:04 -07001252 setHotseatAlphaAtIndex(alpha, direction.ordinal());
1253 }
1254
1255 private void setHotseatAlphaAtIndex(float alpha, int index) {
1256 mHotseatAlpha[index] = alpha;
Peter Schillerbbff2922016-07-15 11:57:54 -07001257 final float hotseatAlpha = mHotseatAlpha[0] * mHotseatAlpha[1] * mHotseatAlpha[2];
1258 final float pageIndicatorAlpha = mHotseatAlpha[0] * mHotseatAlpha[2];
Sunny Goyal6d64d882016-06-24 09:57:04 -07001259
Peter Schillerbbff2922016-07-15 11:57:54 -07001260 mLauncher.getHotseat().setAlpha(hotseatAlpha);
1261 mPageIndicator.setAlpha(pageIndicatorAlpha);
Sunny Goyal6d64d882016-06-24 09:57:04 -07001262 }
1263
1264 public ValueAnimator createHotseatAlphaAnimator(float finalValue) {
1265 if (Float.compare(finalValue, mHotseatAlpha[HOTSEAT_STATE_ALPHA_INDEX]) == 0) {
1266 // Return a dummy animator to avoid null checks.
1267 return ValueAnimator.ofFloat(0, 0);
1268 } else {
1269 ValueAnimator animator = ValueAnimator
1270 .ofFloat(mHotseatAlpha[HOTSEAT_STATE_ALPHA_INDEX], finalValue);
1271 animator.addUpdateListener(new AnimatorUpdateListener() {
1272 @Override
1273 public void onAnimationUpdate(ValueAnimator valueAnimator) {
1274 float value = (Float) valueAnimator.getAnimatedValue();
1275 setHotseatAlphaAtIndex(value, HOTSEAT_STATE_ALPHA_INDEX);
1276 }
1277 });
1278
Sunny Goyalc99cb172017-10-19 16:15:09 -07001279 final boolean accessibilityEnabled = isAccessibilityEnabled(mLauncher);
Sunny Goyal6d64d882016-06-24 09:57:04 -07001280 animator.addUpdateListener(
1281 new AlphaUpdateListener(mLauncher.getHotseat(), accessibilityEnabled));
1282 animator.addUpdateListener(
1283 new AlphaUpdateListener(mPageIndicator, accessibilityEnabled));
1284 return animator;
1285 }
Sunny Goyala92e0df2016-06-09 12:08:22 -07001286 }
1287
Sunny Goyalee688162016-02-12 14:24:21 -08001288 @Override
Hyunyoung Song3f5a11f2017-06-29 16:42:04 -07001289 protected void notifyPageSwitchListener(int prevPage) {
1290 super.notifyPageSwitchListener(prevPage);
1291 if (prevPage != mCurrentPage) {
1292 int swipeDirection = (prevPage < mCurrentPage) ? Action.Direction.RIGHT : Action.Direction.LEFT;
1293 mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
1294 swipeDirection, ContainerType.WORKSPACE, prevPage);
1295 }
Adam Cohen6fecd412013-10-02 17:41:50 -07001296 }
1297
Adam Cohen824fcb32014-05-21 23:01:25 +00001298 protected void setWallpaperDimension() {
Sunny Goyal6f866092016-03-17 17:04:15 -07001299 Utilities.THREAD_POOL_EXECUTOR.execute(new Runnable() {
1300 @Override
1301 public void run() {
Sunny Goyal87f784c2017-01-11 10:48:34 -08001302 final Point size = LauncherAppState.getIDP(getContext()).defaultWallpaperSize;
Sunny Goyal6f866092016-03-17 17:04:15 -07001303 if (size.x != mWallpaperManager.getDesiredMinimumWidth()
1304 || size.y != mWallpaperManager.getDesiredMinimumHeight()) {
1305 mWallpaperManager.suggestDesiredDimensions(size.x, size.y);
Sunny Goyal4b171472015-08-19 12:43:27 -07001306 }
Michael Jurkaa6a05472013-11-13 17:59:46 +01001307 }
Sunny Goyal6f866092016-03-17 17:04:15 -07001308 });
Michael Jurkac5b262c2011-01-12 20:24:50 -08001309 }
1310
Winsonc7d2e832016-07-28 12:24:55 -07001311 public void lockWallpaperToDefaultPage() {
1312 mWallpaperOffset.setLockToDefaultPage(true);
1313 }
1314
1315 public void unlockWallpaperFromDefaultPageOnNextLayout() {
1316 if (mWallpaperOffset.isLockedToDefaultPage()) {
1317 mUnlockWallpaperFromDefaultPageOnLayout = true;
1318 requestLayout();
1319 }
1320 }
1321
Michael Jurka340c5f32010-10-21 16:49:19 -07001322 @Override
1323 public void computeScroll() {
1324 super.computeScroll();
Michael Jurkafe09cb72013-08-27 15:48:58 +02001325 mWallpaperOffset.syncWithScroll();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001326 }
1327
Tony Wickham95cdb3a2016-02-18 14:37:07 -08001328 public void computeScrollWithoutInvalidation() {
1329 computeScrollHelper(false);
1330 }
1331
Jason Monked05f092014-04-24 10:13:05 -04001332 @Override
Tony Wickhama5209e32015-11-19 12:45:30 -08001333 protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
1334 if (!isSwitchingState()) {
1335 super.determineScrollingStart(ev, touchSlopScale);
1336 }
1337 }
1338
1339 @Override
Jason Monked05f092014-04-24 10:13:05 -04001340 public void announceForAccessibility(CharSequence text) {
1341 // Don't announce if apps is on top of us.
Sunny Goyal3e3f44c2017-10-23 17:14:52 -07001342 if (!mLauncher.isInState(ALL_APPS)) {
Jason Monked05f092014-04-24 10:13:05 -04001343 super.announceForAccessibility(text);
1344 }
1345 }
1346
Patrick Dubroy94f78a52011-02-28 17:39:16 -08001347 public void showOutlinesTemporarily() {
Sunny Goyal9ccafbf2016-10-26 13:06:08 -07001348 if (!mIsPageInTransition && !isTouchActive()) {
Patrick Dubroy94f78a52011-02-28 17:39:16 -08001349 snapToPage(mCurrentPage);
1350 }
1351 }
1352
Sunny Goyal9ccafbf2016-10-26 13:06:08 -07001353 private void updatePageAlphaValues() {
Tony6dea7cd2017-07-06 11:32:53 -07001354 if (!workspaceInModalState() && !mIsSwitchingState) {
Sunny Goyalcce07802016-11-01 14:00:41 -07001355 int screenCenter = getScrollX() + getViewportWidth() / 2;
Sunny Goyal7ce471b2017-08-02 03:37:39 -07001356 for (int i = 0; i < getChildCount(); i++) {
Michael Jurka869390b2012-05-06 15:55:19 -07001357 CellLayout child = (CellLayout) getChildAt(i);
1358 if (child != null) {
1359 float scrollProgress = getScrollProgress(screenCenter, child, i);
Adam Cohen73894962011-10-31 13:17:17 -07001360 float alpha = 1 - Math.abs(scrollProgress);
Tony6dea7cd2017-07-06 11:32:53 -07001361 if (mWorkspaceFadeInAdjacentScreens) {
1362 child.getShortcutsAndWidgets().setAlpha(alpha);
1363 } else {
1364 // Pages that are off-screen aren't important for accessibility.
1365 child.getShortcutsAndWidgets().setImportantForAccessibility(
1366 alpha > 0 ? IMPORTANT_FOR_ACCESSIBILITY_AUTO
1367 : IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
1368 }
Adam Cohen73894962011-10-31 13:17:17 -07001369 }
Adam Cohenf34bab52010-09-30 14:11:56 -07001370 }
1371 }
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001372 }
1373
Joe Onorato00acb122009-08-04 16:04:30 -04001374 protected void onAttachedToWindow() {
1375 super.onAttachedToWindow();
Sunny Goyal9eba1fd2015-10-16 08:58:57 -07001376 IBinder windowToken = getWindowToken();
1377 mWallpaperOffset.setWindowToken(windowToken);
Joe Onorato956091b2010-02-19 12:47:40 -08001378 computeScroll();
Sunny Goyal9eba1fd2015-10-16 08:58:57 -07001379 mDragController.setWindowToken(windowToken);
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001380 }
1381
1382 protected void onDetachedFromWindow() {
Romain Guye6661952013-08-08 19:13:22 -07001383 super.onDetachedFromWindow();
Sunny Goyal9eba1fd2015-10-16 08:58:57 -07001384 mWallpaperOffset.setWindowToken(null);
Joe Onorato00acb122009-08-04 16:04:30 -04001385 }
1386
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001387 @Override
Michael Jurka28750fb2010-09-24 17:43:49 -07001388 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Winsonc7d2e832016-07-28 12:24:55 -07001389 if (mUnlockWallpaperFromDefaultPageOnLayout) {
1390 mWallpaperOffset.setLockToDefaultPage(false);
1391 mUnlockWallpaperFromDefaultPageOnLayout = false;
1392 }
Michael Jurkac5b262c2011-01-12 20:24:50 -08001393 if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
Michael Jurkafe09cb72013-08-27 15:48:58 +02001394 mWallpaperOffset.syncWithScroll();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001395 mWallpaperOffset.jumpToFinal();
Michael Jurkac5b262c2011-01-12 20:24:50 -08001396 }
Michael Jurka0142d492010-08-25 17:46:15 -07001397 super.onLayout(changed, left, top, right, bottom);
Sunny Goyalcce07802016-11-01 14:00:41 -07001398 updatePageAlphaValues();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001399 }
1400
1401 @Override
Winson Chung97d85d22011-04-13 11:27:36 -07001402 public int getDescendantFocusability() {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001403 if (workspaceInModalState()) {
Winson Chung97d85d22011-04-13 11:27:36 -07001404 return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
1405 }
1406 return super.getDescendantFocusability();
1407 }
1408
Sunny Goyalea609262017-10-25 15:47:38 -07001409 private boolean workspaceInModalState() {
1410 return !mLauncher.isInState(NORMAL);
Michael Jurkad74c9842011-07-10 12:44:21 -07001411 }
1412
Tony Wickham0c7852f2016-12-02 14:47:04 -08001413 /** Returns whether a drag should be allowed to be started from the current workspace state. */
1414 public boolean workspaceIconsCanBeDragged() {
Sunny Goyalea609262017-10-25 15:47:38 -07001415 return mLauncher.isInState(NORMAL) || mLauncher.isInState(SPRING_LOADED);
Tony Wickham0c7852f2016-12-02 14:47:04 -08001416 }
1417
Sunny Goyala502aa32017-10-02 16:04:06 -07001418 private void updateChildrenLayersEnabled() {
Sunny Goyalc99cb172017-10-19 16:15:09 -07001419 boolean enableChildrenLayers =
1420 isPageRearrangeEnabled() || mIsSwitchingState || isPageInTransition();
Michael Jurkad74c9842011-07-10 12:44:21 -07001421
1422 if (enableChildrenLayers != mChildrenLayersEnabled) {
1423 mChildrenLayersEnabled = enableChildrenLayers;
Michael Jurkad51f33a2012-06-28 15:35:26 -07001424 if (mChildrenLayersEnabled) {
1425 enableHwLayersOnVisiblePages();
1426 } else {
1427 for (int i = 0; i < getPageCount(); i++) {
1428 final CellLayout cl = (CellLayout) getChildAt(i);
Chris Craik01f2d7f2013-10-01 14:41:56 -07001429 cl.enableHardwareLayer(false);
Michael Jurkad51f33a2012-06-28 15:35:26 -07001430 }
1431 }
1432 }
1433 }
1434
1435 private void enableHwLayersOnVisiblePages() {
1436 if (mChildrenLayersEnabled) {
1437 final int screenCount = getChildCount();
Sunny Goyal9ccafbf2016-10-26 13:06:08 -07001438
1439 float visibleLeft = getViewportOffsetX();
1440 float visibleRight = visibleLeft + getViewportWidth();
1441 float scaleX = getScaleX();
1442 if (scaleX < 1 && scaleX > 0) {
1443 float mid = getMeasuredWidth() / 2;
1444 visibleLeft = mid - ((mid - visibleLeft) / scaleX);
1445 visibleRight = mid + ((visibleRight - mid) / scaleX);
1446 }
1447
1448 int leftScreen = -1;
1449 int rightScreen = -1;
Sunny Goyal7ce471b2017-08-02 03:37:39 -07001450 for (int i = 0; i < screenCount; i++) {
Sunny Goyal9ccafbf2016-10-26 13:06:08 -07001451 final View child = getPageAt(i);
1452
1453 float left = child.getLeft() + child.getTranslationX() - getScrollX();
1454 if (left <= visibleRight && (left + child.getMeasuredWidth()) >= visibleLeft) {
1455 if (leftScreen == -1) {
1456 leftScreen = i;
1457 }
1458 rightScreen = i;
1459 }
1460 }
1461 if (mForceDrawAdjacentPages) {
1462 // In overview mode, make sure that the two side pages are visible.
Sunny Goyal7ce471b2017-08-02 03:37:39 -07001463 leftScreen = Utilities.boundToRange(getCurrentPage() - 1, 0, rightScreen);
Sunny Goyal9ccafbf2016-10-26 13:06:08 -07001464 rightScreen = Utilities.boundToRange(getCurrentPage() + 1,
1465 leftScreen, getPageCount() - 1);
1466 }
1467
Michael Jurkad51f33a2012-06-28 15:35:26 -07001468 if (leftScreen == rightScreen) {
1469 // make sure we're caching at least two pages always
1470 if (rightScreen < screenCount - 1) {
1471 rightScreen++;
1472 } else if (leftScreen > 0) {
1473 leftScreen--;
1474 }
1475 }
Chris Craik01f2d7f2013-10-01 14:41:56 -07001476
Sunny Goyal7ce471b2017-08-02 03:37:39 -07001477 for (int i = 0; i < screenCount; i++) {
Michael Jurka47639b92013-01-14 12:42:27 +01001478 final CellLayout layout = (CellLayout) getPageAt(i);
Sunny Goyal9ccafbf2016-10-26 13:06:08 -07001479 // enable layers between left and right screen inclusive.
1480 boolean enableLayer = leftScreen <= i && i <= rightScreen;
Chris Craik01f2d7f2013-10-01 14:41:56 -07001481 layout.enableHardwareLayer(enableLayer);
Michael Jurkad74c9842011-07-10 12:44:21 -07001482 }
Michael Jurkace7e05f2011-02-01 22:02:35 -08001483 }
Michael Jurkace7e05f2011-02-01 22:02:35 -08001484 }
1485
Jeff Brown1d0867c2010-12-02 18:27:39 -08001486 protected void onWallpaperTap(MotionEvent ev) {
Sunny Goyalcf25b522015-07-09 00:01:18 -07001487 final int[] position = mTempXY;
Jeff Brown1d0867c2010-12-02 18:27:39 -08001488 getLocationOnScreen(position);
1489
1490 int pointerIndex = ev.getActionIndex();
1491 position[0] += (int) ev.getX(pointerIndex);
1492 position[1] += (int) ev.getY(pointerIndex);
1493
1494 mWallpaperManager.sendWallpaperCommand(getWindowToken(),
1495 ev.getAction() == MotionEvent.ACTION_UP
1496 ? WallpaperManager.COMMAND_TAP : WallpaperManager.COMMAND_SECONDARY_TAP,
1497 position[0], position[1], 0, null);
1498 }
1499
Sunny Goyal06e21a22016-08-11 16:02:02 -07001500 public void prepareDragWithProvider(DragPreviewProvider outlineProvider) {
1501 mOutlineProvider = outlineProvider;
Michael Jurka3e7c7632010-10-02 16:01:03 -07001502 }
1503
Sunny Goyal1d08f702015-05-04 15:50:25 -07001504 public void onStartReordering() {
Adam Cohendedbd962013-07-11 14:21:49 -07001505 super.onStartReordering();
Adam Cohen22cba7f2013-07-19 16:14:00 -07001506 // Reordering handles its own animations, disable the automatic ones.
Winson Chung964df6b2013-10-11 15:55:37 -07001507 disableLayoutTransitions();
Adam Cohendedbd962013-07-11 14:21:49 -07001508 }
1509
Sunny Goyal1d08f702015-05-04 15:50:25 -07001510 public void onEndReordering() {
Adam Cohendedbd962013-07-11 14:21:49 -07001511 super.onEndReordering();
Adam Cohendedbd962013-07-11 14:21:49 -07001512
Adam Cohendcb173d2014-04-01 13:33:58 -07001513 if (mLauncher.isWorkspaceLoading()) {
1514 // Invalid and dangerous operation if workspace is loading
1515 return;
1516 }
1517
Hyunyoung Song3f5a11f2017-06-29 16:42:04 -07001518 ArrayList<Long> prevScreenOrder = (ArrayList<Long>) mScreenOrder.clone();
Adam Cohendedbd962013-07-11 14:21:49 -07001519 mScreenOrder.clear();
Adam Cohen2bf63d52013-09-29 17:46:49 -07001520 int count = getChildCount();
Adam Cohendedbd962013-07-11 14:21:49 -07001521 for (int i = 0; i < count; i++) {
1522 CellLayout cl = ((CellLayout) getChildAt(i));
1523 mScreenOrder.add(getIdForScreen(cl));
1524 }
Hyunyoung Song3f5a11f2017-06-29 16:42:04 -07001525
1526 for (int i = 0; i < prevScreenOrder.size(); i++) {
1527 if (mScreenOrder.get(i) != prevScreenOrder.get(i)) {
1528 mLauncher.getUserEventDispatcher().logOverviewReorder();
1529 break;
1530 }
1531 }
Rajeev Kumar93c05122017-06-12 12:59:47 -07001532 LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
Adam Cohen22cba7f2013-07-19 16:14:00 -07001533
1534 // Re-enable auto layout transitions for page deletion.
Winson Chung964df6b2013-10-11 15:55:37 -07001535 enableLayoutTransitions();
Adam Cohendedbd962013-07-11 14:21:49 -07001536 }
1537
Sunny Goyalce5a7e52015-07-08 15:44:27 -07001538 public void snapToPageFromOverView(int whichPage) {
Sunny Goyal5bc6b6f2017-10-26 15:36:10 -07001539 snapToPage(whichPage, OVERVIEW_TRANSITION_MS, Interpolators.ZOOM_IN);
Sunny Goyalce5a7e52015-07-08 15:44:27 -07001540 }
1541
Sunny Goyalc99cb172017-10-19 16:15:09 -07001542 private void onStartStateTransition(LauncherState state) {
Sunny Goyalc99cb172017-10-19 16:15:09 -07001543 mIsSwitchingState = true;
1544 mTransitionProgress = 0;
1545
1546 updateChildrenLayersEnabled();
Adam Cohen410f3cd2013-09-22 12:09:32 -07001547 }
1548
Sunny Goyalc99cb172017-10-19 16:15:09 -07001549 private void onEndStateTransition() {
1550 mIsSwitchingState = false;
1551 mForceDrawAdjacentPages = false;
1552 mTransitionProgress = 1;
Sunny Goyal47328fd2016-05-25 18:56:41 -07001553
Sunny Goyalc99cb172017-10-19 16:15:09 -07001554 updateChildrenLayersEnabled();
1555 updateAccessibilityFlags();
Tony Wickham34d2c912015-09-11 09:27:58 -07001556 }
1557
Winson Chungdc61c4d2015-04-20 18:26:57 -07001558 /**
Sunny Goyal4c7f2152017-10-17 17:17:16 -07001559 * Sets the current workspace {@link LauncherState} and updates the UI without any animations
Winson Chungdc61c4d2015-04-20 18:26:57 -07001560 */
Sunny Goyalc4fa8c32017-11-07 12:23:58 -08001561 @Override
Sunny Goyal4c7f2152017-10-17 17:17:16 -07001562 public void setState(LauncherState toState) {
Sunny Goyalc99cb172017-10-19 16:15:09 -07001563 onStartStateTransition(toState);
Sunny Goyalea609262017-10-25 15:47:38 -07001564 mStateTransitionAnimation.setState(toState);
Sunny Goyalaeb16432017-10-16 11:46:41 -07001565 onEndStateTransition();
1566 }
1567
1568 /**
Sunny Goyal4c7f2152017-10-17 17:17:16 -07001569 * Sets the current workspace {@link LauncherState}, then animates the UI
Sunny Goyalaeb16432017-10-16 11:46:41 -07001570 */
Sunny Goyalc4fa8c32017-11-07 12:23:58 -08001571 @Override
Sunny Goyal4c7f2152017-10-17 17:17:16 -07001572 public void setStateWithAnimation(LauncherState toState, AnimationLayerSet layerViews,
1573 AnimatorSet anim, AnimationConfig config) {
Sunny Goyalc99cb172017-10-19 16:15:09 -07001574 StateTransitionListener listener = new StateTransitionListener(toState);
Sunny Goyalea609262017-10-25 15:47:38 -07001575 mStateTransitionAnimation.setStateWithAnimation(toState, anim, layerViews, config);
Winson Chungdc61c4d2015-04-20 18:26:57 -07001576
Sunny Goyalc99cb172017-10-19 16:15:09 -07001577 // Invalidate the pages now, so that we have the visible pages before the
1578 // animation is started
1579 if (toState.hasMultipleVisiblePages) {
1580 mForceDrawAdjacentPages = true;
1581 }
1582 invalidate(); // This will call dispatchDraw(), which calls getVisiblePages().
Mario Bertschler5a2edd62017-01-30 17:47:30 -08001583
Sunny Goyalaeb16432017-10-16 11:46:41 -07001584 ValueAnimator stepAnimator = ValueAnimator.ofFloat(0, 1);
1585 stepAnimator.addUpdateListener(listener);
Sunny Goyal03102202017-10-27 11:05:26 -07001586 stepAnimator.setDuration(config.duration);
Sunny Goyalaeb16432017-10-16 11:46:41 -07001587 anim.play(stepAnimator);
1588 anim.addListener(listener);
Adam Cohen53805212013-10-01 10:39:23 -07001589 }
1590
Sunny Goyal1d08f702015-05-04 15:50:25 -07001591 public void updateAccessibilityFlags() {
Sunny Goyald8d19932016-06-22 16:29:24 -07001592 // TODO: Update the accessibility flags appropriately when dragging.
Sunny Goyalea609262017-10-25 15:47:38 -07001593 int accessibilityFlag = mLauncher.getStateManager().getState().workspaceAccessibilityFlag;
Sunny Goyald8d19932016-06-22 16:29:24 -07001594 if (!mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
Sunny Goyala52ecb02016-12-16 15:04:51 -08001595 int total = getPageCount();
Sunny Goyal7ce471b2017-08-02 03:37:39 -07001596 for (int i = 0; i < total; i++) {
Sunny Goyalea609262017-10-25 15:47:38 -07001597 updateAccessibilityFlags(accessibilityFlag, (CellLayout) getPageAt(i), i);
Sunny Goyald8d19932016-06-22 16:29:24 -07001598 }
Sunny Goyalea609262017-10-25 15:47:38 -07001599 setImportantForAccessibility(accessibilityFlag);
Sunny Goyal1d08f702015-05-04 15:50:25 -07001600 }
1601 }
1602
Sunny Goyalea609262017-10-25 15:47:38 -07001603 private void updateAccessibilityFlags(int accessibilityFlag, CellLayout page, int pageNo) {
Sunny Goyalc99cb172017-10-19 16:15:09 -07001604 if (isPageRearrangeEnabled()) {
Sunny Goyal1d08f702015-05-04 15:50:25 -07001605 page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
1606 page.getShortcutsAndWidgets().setImportantForAccessibility(
1607 IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
1608 page.setContentDescription(getPageDescription(pageNo));
1609
Sunny Goyald0a6ae72016-06-16 12:29:03 -07001610 // No custom action for the first page.
1611 if (!FeatureFlags.QSB_ON_FIRST_SCREEN || pageNo > 0) {
1612 if (mPagesAccessibilityDelegate == null) {
1613 mPagesAccessibilityDelegate = new OverviewScreenAccessibilityDelegate(this);
1614 }
1615 page.setAccessibilityDelegate(mPagesAccessibilityDelegate);
Sunny Goyal1d08f702015-05-04 15:50:25 -07001616 }
Sunny Goyal1d08f702015-05-04 15:50:25 -07001617 } else {
Sunny Goyal1d08f702015-05-04 15:50:25 -07001618 page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
Sunny Goyalea609262017-10-25 15:47:38 -07001619 page.getShortcutsAndWidgets().setImportantForAccessibility(accessibilityFlag);
Sunny Goyal1d08f702015-05-04 15:50:25 -07001620 page.setContentDescription(null);
1621 page.setAccessibilityDelegate(null);
1622 }
Adam Cohenedaaa302013-10-01 17:33:27 -07001623 }
1624
Sunny Goyalc99cb172017-10-19 16:15:09 -07001625 public void setPageRearrangeEnabled(boolean isEnabled) {
1626 if (mPageRearrangeEnabled != isEnabled) {
1627 mPageRearrangeEnabled = isEnabled;
1628 if (isEnabled) {
1629 enableFreeScroll();
1630 } else {
1631 disableFreeScroll();
1632 }
Tony Wickhamf898b972016-04-05 18:36:36 -07001633 }
Michael Jurkabed61d22012-02-14 22:51:29 -08001634 }
1635
Sunny Goyalc99cb172017-10-19 16:15:09 -07001636 public boolean isPageRearrangeEnabled() {
1637 return mPageRearrangeEnabled;
Adam Cohen3f452c82013-09-19 11:57:17 -07001638 }
1639
Sunny Goyal94b510c2016-08-16 15:36:48 -07001640 public void startDrag(CellLayout.CellInfo cellInfo, DragOptions options) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001641 View child = cellInfo.cell;
Winson Chungaafa03c2010-06-11 17:34:16 -07001642
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001643 mDragInfo = cellInfo;
Adam Cohend41fbf52012-02-16 23:53:59 -08001644 child.setVisibility(INVISIBLE);
Joe Onorato4be866d2010-10-10 11:26:02 -07001645
Sunny Goyal94b510c2016-08-16 15:36:48 -07001646 if (options.isAccessibleDrag) {
Hyunyoung Song0de01172016-10-05 16:27:48 -07001647 mDragController.addDragListener(new AccessibleDragListenerAdapter(
Sunny Goyal94b510c2016-08-16 15:36:48 -07001648 this, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG) {
1649 @Override
1650 protected void enableAccessibleDrag(boolean enable) {
1651 super.enableAccessibleDrag(enable);
1652 setEnableForLayout(mLauncher.getHotseat().getLayout(),enable);
1653
1654 // We need to allow our individual children to become click handlers in this
1655 // case, so temporarily unset the click handlers.
1656 setOnClickListener(enable ? null : mLauncher);
1657 }
1658 });
1659 }
1660
1661 beginDragShared(child, this, options);
Adam Cohenac8c8762011-07-13 11:15:27 -07001662 }
1663
Sunny Goyal94b510c2016-08-16 15:36:48 -07001664 public void beginDragShared(View child, DragSource source, DragOptions options) {
Sunny Goyal10923b32016-07-20 15:42:44 -07001665 Object dragObject = child.getTag();
1666 if (!(dragObject instanceof ItemInfo)) {
1667 String msg = "Drag started with a view that has no tag set. This "
1668 + "will cause a crash (issue 11627249) down the line. "
1669 + "View: " + child + " tag: " + child.getTag();
1670 throw new IllegalStateException(msg);
1671 }
Sunny Goyal94b510c2016-08-16 15:36:48 -07001672 beginDragShared(child, source, (ItemInfo) dragObject,
1673 new DragPreviewProvider(child), options);
Adam Cohenc9735cf2015-01-23 16:11:55 -08001674 }
1675
Sunny Goyala2454ad2016-07-22 10:50:11 -07001676
Sunny Goyal94b510c2016-08-16 15:36:48 -07001677 public DragView beginDragShared(View child, DragSource source, ItemInfo dragObject,
1678 DragPreviewProvider previewProvider, DragOptions dragOptions) {
Sunny Goyal508da152014-08-14 10:53:27 -07001679 child.clearFocus();
1680 child.setPressed(false);
Sunny Goyal06e21a22016-08-11 16:02:02 -07001681 mOutlineProvider = previewProvider;
Sunny Goyal508da152014-08-14 10:53:27 -07001682
Joe Onorato4be866d2010-10-10 11:26:02 -07001683 // The drag bitmap follows the touch point around on the screen
Sunny Goyalaa3a2ba2017-09-26 12:43:16 -07001684 final Bitmap b = previewProvider.createDragBitmap();
Sunny Goyal157793d2016-07-20 14:55:26 -07001685 int halfPadding = previewProvider.previewPadding / 2;
Joe Onorato4be866d2010-10-10 11:26:02 -07001686
Sunny Goyala2454ad2016-07-22 10:50:11 -07001687 float scale = previewProvider.getScaleAndPosition(b, mTempXY);
1688 int dragLayerX = mTempXY[0];
1689 int dragLayerY = mTempXY[1];
Adam Cohene3e27a82011-04-15 12:07:39 -07001690
Adam Cohen2e6da152015-05-06 11:42:25 -07001691 DeviceProfile grid = mLauncher.getDeviceProfile();
Winson Chungb8c69f32011-10-19 21:36:08 -07001692 Point dragVisualizeOffset = null;
Adam Cohene3e27a82011-04-15 12:07:39 -07001693 Rect dragRect = null;
Sunny Goyal508da152014-08-14 10:53:27 -07001694 if (child instanceof BubbleTextView) {
Tony Wickham1237df02017-02-24 08:59:36 -08001695 dragRect = new Rect();
1696 ((BubbleTextView) child).getIconBounds(dragRect);
1697 dragLayerY += dragRect.top;
1698 // Note: The dragRect is used to calculate drag layer offsets, but the
Winson Chungb8c69f32011-10-19 21:36:08 -07001699 // dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
Sunny Goyal157793d2016-07-20 14:55:26 -07001700 dragVisualizeOffset = new Point(- halfPadding, halfPadding);
Adam Cohen0e4857c2011-06-30 17:05:14 -07001701 } else if (child instanceof FolderIcon) {
Winson Chung5f8afe62013-08-12 16:19:28 -07001702 int previewSize = grid.folderIconSizePx;
Sunny Goyal157793d2016-07-20 14:55:26 -07001703 dragVisualizeOffset = new Point(- halfPadding, halfPadding - child.getPaddingTop());
Winson Chung5f8afe62013-08-12 16:19:28 -07001704 dragRect = new Rect(0, child.getPaddingTop(), child.getWidth(), previewSize);
Jon Mirandaaa6f66a2017-05-09 12:43:44 -07001705 } else if (previewProvider instanceof ShortcutDragPreviewProvider) {
1706 dragVisualizeOffset = new Point(- halfPadding, halfPadding);
Adam Cohene3e27a82011-04-15 12:07:39 -07001707 }
1708
Winson Chung1e9cbfe2011-09-30 16:52:26 -07001709 // Clear the pressed state if necessary
1710 if (child instanceof BubbleTextView) {
1711 BubbleTextView icon = (BubbleTextView) child;
Sunny Goyal508da152014-08-14 10:53:27 -07001712 icon.clearPressedBackground();
Winson Chung1e9cbfe2011-09-30 16:52:26 -07001713 }
1714
Adam Cohen5084cba2013-09-03 12:01:16 -07001715 if (child.getParent() instanceof ShortcutAndWidgetContainer) {
1716 mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent();
1717 }
1718
Tony Wickham02700392017-05-11 15:20:28 -07001719 if (child instanceof BubbleTextView && !dragOptions.isAccessibleDrag) {
Tony Wickham540913e2017-01-23 11:47:51 -08001720 PopupContainerWithArrow popupContainer = PopupContainerWithArrow
1721 .showForIcon((BubbleTextView) child);
1722 if (popupContainer != null) {
1723 dragOptions.preDragCondition = popupContainer.createPreDragCondition();
Jon Mirandaf3e35d92016-10-05 14:08:11 -07001724
1725 mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
Tony Wickham10236d62016-09-28 12:49:25 -07001726 }
1727 }
1728
Sunny Goyalaa8ef112015-06-12 20:04:41 -07001729 DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source,
Sunny Goyal94b510c2016-08-16 15:36:48 -07001730 dragObject, dragVisualizeOffset, dragRect, scale, dragOptions);
Sunny Goyal1ce9c472017-10-03 16:17:32 -07001731 dv.setIntrinsicIconScaleFactor(dragOptions.intrinsicIconScaleFactor);
Sunny Goyala2454ad2016-07-22 10:50:11 -07001732 return dv;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001733 }
1734
Tony Wickham251ab6e2016-12-02 18:54:32 -08001735 private boolean transitionStateShouldAllowDrop() {
Sunny Goyalc99cb172017-10-19 16:15:09 -07001736 return (!isSwitchingState() || mTransitionProgress > ALLOW_DROP_TRANSITION_PROGRESS) &&
1737 workspaceIconsCanBeDragged();
Adam Cohen4b285c52011-07-21 14:24:06 -07001738 }
1739
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001740 /**
1741 * {@inheritDoc}
1742 */
Sunny Goyal1ce9c472017-10-03 16:17:32 -07001743 @Override
Adam Cohencb3382b2011-05-24 14:07:08 -07001744 public boolean acceptDrop(DragObject d) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001745 // If it's an external drop (e.g. from All Apps), check if it should be accepted
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001746 CellLayout dropTargetLayout = mDropToLayout;
Adam Cohencb3382b2011-05-24 14:07:08 -07001747 if (d.dragSource != this) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001748 // Don't accept the drop if we're not over a screen at time of drop
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001749 if (dropTargetLayout == null) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001750 return false;
1751 }
Adam Cohen4b285c52011-07-21 14:24:06 -07001752 if (!transitionStateShouldAllowDrop()) return false;
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001753
Sunny Goyal1587d532015-01-29 09:57:16 -08001754 mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);
Adam Cohena65beee2011-06-27 21:32:23 -07001755
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07001756 // We want the point to be mapped to the dragTarget.
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001757 if (mLauncher.isHotseatLayout(dropTargetLayout)) {
Adam Cohen307fe232012-08-16 17:55:58 -07001758 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07001759 } else {
Sunny Goyal106bf642015-07-16 12:18:06 -07001760 mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter);
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07001761 }
1762
Rajeev Kumar93c05122017-06-12 12:59:47 -07001763 int spanX;
1764 int spanY;
Winson Chung557d6ed2011-07-08 15:34:52 -07001765 if (mDragInfo != null) {
1766 final CellLayout.CellInfo dragCellInfo = mDragInfo;
1767 spanX = dragCellInfo.spanX;
1768 spanY = dragCellInfo.spanY;
Winson Chung557d6ed2011-07-08 15:34:52 -07001769 } else {
Sunny Goyalaa8ef112015-06-12 20:04:41 -07001770 spanX = d.dragInfo.spanX;
1771 spanY = d.dragInfo.spanY;
Winson Chung557d6ed2011-07-08 15:34:52 -07001772 }
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001773
Adam Cohend41fbf52012-02-16 23:53:59 -08001774 int minSpanX = spanX;
1775 int minSpanY = spanY;
1776 if (d.dragInfo instanceof PendingAddWidgetInfo) {
1777 minSpanX = ((PendingAddWidgetInfo) d.dragInfo).minSpanX;
1778 minSpanY = ((PendingAddWidgetInfo) d.dragInfo).minSpanY;
1779 }
Adam Cohen482ed822012-03-02 14:15:13 -08001780
Adam Cohena65beee2011-06-27 21:32:23 -07001781 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001782 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, dropTargetLayout,
Adam Cohenf0777b92012-02-28 14:02:45 -08001783 mTargetCell);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001784 float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08001785 mDragViewVisualCenter[1], mTargetCell);
Sunny Goyalaa8ef112015-06-12 20:04:41 -07001786 if (mCreateUserFolderOnDrop && willCreateUserFolder(d.dragInfo,
Adam Cohenec0d61d2014-02-07 16:34:51 -08001787 dropTargetLayout, mTargetCell, distance, true)) {
Adam Cohena65beee2011-06-27 21:32:23 -07001788 return true;
1789 }
Adam Cohenec0d61d2014-02-07 16:34:51 -08001790
Sunny Goyalaa8ef112015-06-12 20:04:41 -07001791 if (mAddToExistingFolderOnDrop && willAddToExistingUserFolder(d.dragInfo,
Adam Cohenec0d61d2014-02-07 16:34:51 -08001792 dropTargetLayout, mTargetCell, distance)) {
Adam Cohena65beee2011-06-27 21:32:23 -07001793 return true;
1794 }
1795
Adam Cohen482ed822012-03-02 14:15:13 -08001796 int[] resultSpan = new int[2];
Adam Cohenfa3c58f2013-12-06 16:10:55 -08001797 mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08001798 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
1799 null, mTargetCell, resultSpan, CellLayout.MODE_ACCEPT_DROP);
1800 boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
1801
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001802 // Don't accept the drop if there's no room for the item
Adam Cohen482ed822012-03-02 14:15:13 -08001803 if (!foundCell) {
Jon Miranda9485e5f2016-10-18 11:47:42 -07001804 onNoCellFound(dropTargetLayout);
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001805 return false;
1806 }
1807 }
Adam Cohendedbd962013-07-11 14:21:49 -07001808
1809 long screenId = getIdForScreen(dropTargetLayout);
1810 if (screenId == EXTRA_EMPTY_SCREEN_ID) {
1811 commitExtraEmptyScreen();
1812 }
1813
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001814 return true;
1815 }
1816
Sunny Goyale78e3d72015-09-24 11:23:31 -07001817 boolean willCreateUserFolder(ItemInfo info, CellLayout target, int[] targetCell,
1818 float distance, boolean considerTimeout) {
Adam Cohen482ed822012-03-02 14:15:13 -08001819 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohena65beee2011-06-27 21:32:23 -07001820 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
Sunny Goyale78e3d72015-09-24 11:23:31 -07001821 return willCreateUserFolder(info, dropOverView, considerTimeout);
1822 }
Adam Cohena65beee2011-06-27 21:32:23 -07001823
Sunny Goyale78e3d72015-09-24 11:23:31 -07001824 boolean willCreateUserFolder(ItemInfo info, View dropOverView, boolean considerTimeout) {
Adam Cohen19f37922012-03-21 11:59:11 -07001825 if (dropOverView != null) {
1826 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
Sunny Goyalb740f592015-12-17 23:22:42 -08001827 if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY)) {
Adam Cohen19f37922012-03-21 11:59:11 -07001828 return false;
1829 }
1830 }
1831
Winson Chung3d503fb2011-07-13 17:25:49 -07001832 boolean hasntMoved = false;
1833 if (mDragInfo != null) {
Adam Cohen482ed822012-03-02 14:15:13 -08001834 hasntMoved = dropOverView == mDragInfo.cell;
Winson Chung3d503fb2011-07-13 17:25:49 -07001835 }
Adam Cohene0310962011-04-18 16:15:31 -07001836
Adam Cohena65beee2011-06-27 21:32:23 -07001837 if (dropOverView == null || hasntMoved || (considerTimeout && !mCreateUserFolderOnDrop)) {
1838 return false;
1839 }
Adam Cohene0310962011-04-18 16:15:31 -07001840
Adam Cohena65beee2011-06-27 21:32:23 -07001841 boolean aboveShortcut = (dropOverView.getTag() instanceof ShortcutInfo);
Adam Cohene0310962011-04-18 16:15:31 -07001842 boolean willBecomeShortcut =
Adam Cohenc0dcf592011-06-01 15:30:43 -07001843 (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
Tony Wickhambfbf7f92016-05-19 11:19:39 -07001844 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
1845 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT);
Adam Cohene0310962011-04-18 16:15:31 -07001846
1847 return (aboveShortcut && willBecomeShortcut);
1848 }
1849
Sunny Goyalaa8ef112015-06-12 20:04:41 -07001850 boolean willAddToExistingUserFolder(ItemInfo dragInfo, CellLayout target, int[] targetCell,
Adam Cohen482ed822012-03-02 14:15:13 -08001851 float distance) {
1852 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohena65beee2011-06-27 21:32:23 -07001853 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
Sunny Goyale78e3d72015-09-24 11:23:31 -07001854 return willAddToExistingUserFolder(dragInfo, dropOverView);
Adam Cohen19f37922012-03-21 11:59:11 -07001855
Sunny Goyale78e3d72015-09-24 11:23:31 -07001856 }
Sunny Goyal9d438082015-09-25 11:50:16 -07001857 boolean willAddToExistingUserFolder(ItemInfo dragInfo, View dropOverView) {
Adam Cohen19f37922012-03-21 11:59:11 -07001858 if (dropOverView != null) {
1859 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
Sunny Goyalb740f592015-12-17 23:22:42 -08001860 if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY)) {
Adam Cohen19f37922012-03-21 11:59:11 -07001861 return false;
1862 }
1863 }
1864
Adam Cohena65beee2011-06-27 21:32:23 -07001865 if (dropOverView instanceof FolderIcon) {
1866 FolderIcon fi = (FolderIcon) dropOverView;
1867 if (fi.acceptDrop(dragInfo)) {
1868 return true;
1869 }
1870 }
1871 return false;
1872 }
1873
Winson Chung3d503fb2011-07-13 17:25:49 -07001874 boolean createUserFolderIfNecessary(View newView, long container, CellLayout target,
Sunny Goyal1797af42017-10-06 13:29:57 -07001875 int[] targetCell, float distance, boolean external, DragView dragView) {
Adam Cohen482ed822012-03-02 14:15:13 -08001876 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohenc0dcf592011-06-01 15:30:43 -07001877 View v = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohen19f37922012-03-21 11:59:11 -07001878
Winson Chungec9a0a42011-07-20 20:42:13 -07001879 boolean hasntMoved = false;
1880 if (mDragInfo != null) {
1881 CellLayout cellParent = getParentCellLayoutForView(mDragInfo.cell);
1882 hasntMoved = (mDragInfo.cellX == targetCell[0] &&
1883 mDragInfo.cellY == targetCell[1]) && (cellParent == target);
1884 }
Adam Cohen10b17372011-04-15 14:21:25 -07001885
Adam Cohen19072da2011-05-31 14:30:45 -07001886 if (v == null || hasntMoved || !mCreateUserFolderOnDrop) return false;
1887 mCreateUserFolderOnDrop = false;
Sunny Goyalb740f592015-12-17 23:22:42 -08001888 final long screenId = getIdForScreen(target);
Adam Cohen10b17372011-04-15 14:21:25 -07001889
1890 boolean aboveShortcut = (v.getTag() instanceof ShortcutInfo);
1891 boolean willBecomeShortcut = (newView.getTag() instanceof ShortcutInfo);
1892
1893 if (aboveShortcut && willBecomeShortcut) {
1894 ShortcutInfo sourceInfo = (ShortcutInfo) newView.getTag();
1895 ShortcutInfo destInfo = (ShortcutInfo) v.getTag();
1896 // if the drag started here, we need to remove it from the workspace
1897 if (!external) {
Winson Chung3d503fb2011-07-13 17:25:49 -07001898 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
Adam Cohen10b17372011-04-15 14:21:25 -07001899 }
1900
Adam Cohend0445262011-07-04 23:53:22 -07001901 Rect folderLocation = new Rect();
Adam Cohenac8c8762011-07-13 11:15:27 -07001902 float scale = mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation);
Adam Cohen10b17372011-04-15 14:21:25 -07001903 target.removeView(v);
Adam Cohend0445262011-07-04 23:53:22 -07001904
Winson Chung3d503fb2011-07-13 17:25:49 -07001905 FolderIcon fi =
Adam Cohendcd297f2013-06-18 13:13:40 -07001906 mLauncher.addFolder(target, container, screenId, targetCell[0], targetCell[1]);
Adam Cohena9cf38f2011-05-02 15:36:58 -07001907 destInfo.cellX = -1;
1908 destInfo.cellY = -1;
1909 sourceInfo.cellX = -1;
1910 sourceInfo.cellY = -1;
Adam Cohend0445262011-07-04 23:53:22 -07001911
Adam Cohen558baaf2011-08-15 15:22:57 -07001912 // If the dragView is null, we can't animate
1913 boolean animate = dragView != null;
1914 if (animate) {
Adam Cohenefca0272016-02-24 19:19:06 -08001915 // In order to keep everything continuous, we hand off the currently rendered
1916 // folder background to the newly created icon. This preserves animation state.
1917 fi.setFolderBackground(mFolderCreateBg);
Jon Mirandaa0233f72017-06-22 18:34:45 -07001918 mFolderCreateBg = new PreviewBackground();
Sunny Goyal1797af42017-10-06 13:29:57 -07001919 fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale
1920 );
Adam Cohen558baaf2011-08-15 15:22:57 -07001921 } else {
Jon Miranda6c5d1022017-07-06 09:55:50 -07001922 fi.prepareCreateAnimation(v);
Adam Cohen558baaf2011-08-15 15:22:57 -07001923 fi.addItem(destInfo);
1924 fi.addItem(sourceInfo);
1925 }
Adam Cohen10b17372011-04-15 14:21:25 -07001926 return true;
1927 }
1928 return false;
1929 }
1930
Adam Cohena65beee2011-06-27 21:32:23 -07001931 boolean addToExistingFolderIfNecessary(View newView, CellLayout target, int[] targetCell,
Adam Cohen482ed822012-03-02 14:15:13 -08001932 float distance, DragObject d, boolean external) {
1933 if (distance > mMaxDistanceForFolderCreation) return false;
1934
Adam Cohena65beee2011-06-27 21:32:23 -07001935 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001936 if (!mAddToExistingFolderOnDrop) return false;
1937 mAddToExistingFolderOnDrop = false;
Adam Cohen19f37922012-03-21 11:59:11 -07001938
Adam Cohenc0dcf592011-06-01 15:30:43 -07001939 if (dropOverView instanceof FolderIcon) {
1940 FolderIcon fi = (FolderIcon) dropOverView;
Adam Cohen3e8f8112011-07-02 18:03:00 -07001941 if (fi.acceptDrop(d.dragInfo)) {
Jon Miranda6d3586c2017-09-08 12:39:10 -07001942 fi.onDrop(d, false /* itemReturnedOnFailedDrop */);
Adam Cohenc0dcf592011-06-01 15:30:43 -07001943
1944 // if the drag started here, we need to remove it from the workspace
1945 if (!external) {
Winson Chung3d503fb2011-07-13 17:25:49 -07001946 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
Adam Cohenc0dcf592011-06-01 15:30:43 -07001947 }
1948 return true;
1949 }
1950 }
1951 return false;
1952 }
1953
Sunny Goyale9b651e2015-04-24 11:44:51 -07001954 @Override
1955 public void prepareAccessibilityDrop() { }
1956
Sunny Goyal3dce5f32017-10-05 11:40:05 -07001957 public void onDrop(final DragObject d, DragOptions options) {
Sunny Goyal1587d532015-01-29 09:57:16 -08001958 mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001959 CellLayout dropTargetLayout = mDropToLayout;
1960
Adam Cohene3e27a82011-04-15 12:07:39 -07001961 // We want the point to be mapped to the dragTarget.
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001962 if (dropTargetLayout != null) {
1963 if (mLauncher.isHotseatLayout(dropTargetLayout)) {
Adam Cohenbb00ff22012-07-12 15:43:01 -07001964 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
Winson Chung3d503fb2011-07-13 17:25:49 -07001965 } else {
Sunny Goyal106bf642015-07-16 12:18:06 -07001966 mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter);
Winson Chung3d503fb2011-07-13 17:25:49 -07001967 }
Adam Cohenba781612011-05-09 14:37:39 -07001968 }
Michael Jurkac6ee42e2010-09-30 12:04:50 -07001969
Tony Wickham02700392017-05-11 15:20:28 -07001970 boolean droppedOnOriginalCell = false;
1971
Sunny Goyalce5a7e52015-07-08 15:44:27 -07001972 int snapScreen = -1;
Adam Cohend41fbf52012-02-16 23:53:59 -08001973 boolean resizeOnDrop = false;
Sunny Goyal1797af42017-10-06 13:29:57 -07001974 if (d.dragSource != this || mDragInfo == null) {
Adam Cohene3e27a82011-04-15 12:07:39 -07001975 final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0],
1976 (int) mDragViewVisualCenter[1] };
Sunny Goyaldec3a902017-01-25 18:23:36 -08001977 onDropExternal(touchXY, dropTargetLayout, d);
Sunny Goyal1797af42017-10-06 13:29:57 -07001978 } else {
Patrick Dubroyce34a972010-10-19 10:34:32 -07001979 final View cell = mDragInfo.cell;
Tony Wickhamea62fe02016-10-05 09:05:42 -07001980 boolean droppedOnOriginalCellDuringTransition = false;
Sunny Goyal1797af42017-10-06 13:29:57 -07001981 Runnable onCompleteRunnable = null;
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08001982
Michael Jurka1e2f4652013-07-08 18:03:46 -07001983 if (dropTargetLayout != null && !d.cancelled) {
Winson Chung3d503fb2011-07-13 17:25:49 -07001984 // Move internally
Winson Chung40e882b2011-07-21 19:01:11 -07001985 boolean hasMovedLayouts = (getParentCellLayoutForView(cell) != dropTargetLayout);
1986 boolean hasMovedIntoHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
1987 long container = hasMovedIntoHotseat ?
Winson Chung3d503fb2011-07-13 17:25:49 -07001988 LauncherSettings.Favorites.CONTAINER_HOTSEAT :
1989 LauncherSettings.Favorites.CONTAINER_DESKTOP;
Adam Cohendcd297f2013-06-18 13:13:40 -07001990 long screenId = (mTargetCell[0] < 0) ?
1991 mDragInfo.screenId : getIdForScreen(dropTargetLayout);
Adam Cohenc0dcf592011-06-01 15:30:43 -07001992 int spanX = mDragInfo != null ? mDragInfo.spanX : 1;
1993 int spanY = mDragInfo != null ? mDragInfo.spanY : 1;
1994 // First we find the cell nearest to point at which the item is
1995 // dropped, without any consideration to whether there is an item there.
Adam Cohen482ed822012-03-02 14:15:13 -08001996
Adam Cohenc0dcf592011-06-01 15:30:43 -07001997 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], (int)
1998 mDragViewVisualCenter[1], spanX, spanY, dropTargetLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08001999 float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
2000 mDragViewVisualCenter[1], mTargetCell);
2001
Adam Cohenc0dcf592011-06-01 15:30:43 -07002002 // If the item being dropped is a shortcut and the nearest drop
Adam Cohen76078c42011-06-09 15:06:52 -07002003 // cell also contains a shortcut, then create a folder with the two shortcuts.
Sunny Goyalf5440cb2016-12-14 15:13:00 -08002004 if (createUserFolderIfNecessary(cell, container,
Sunny Goyal1797af42017-10-06 13:29:57 -07002005 dropTargetLayout, mTargetCell, distance, false, d.dragView) ||
2006 addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell,
2007 distance, d, false)) {
Sunny Goyal3e3f44c2017-10-23 17:14:52 -07002008 mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
Adam Cohendf035382011-04-11 17:22:04 -07002009 return;
2010 }
2011
2012 // Aside from the special case where we're dropping a shortcut onto a shortcut,
2013 // we need to find the nearest cell location that is vacant
Sunny Goyalaa8ef112015-06-12 20:04:41 -07002014 ItemInfo item = d.dragInfo;
Adam Cohend41fbf52012-02-16 23:53:59 -08002015 int minSpanX = item.spanX;
2016 int minSpanY = item.spanY;
2017 if (item.minSpanX > 0 && item.minSpanY > 0) {
2018 minSpanX = item.minSpanX;
2019 minSpanY = item.minSpanY;
2020 }
Adam Cohen482ed822012-03-02 14:15:13 -08002021
Tony Wickham02700392017-05-11 15:20:28 -07002022 droppedOnOriginalCell = item.screenId == screenId && item.container == container
Tony Wickhamea62fe02016-10-05 09:05:42 -07002023 && item.cellX == mTargetCell[0] && item.cellY == mTargetCell[1];
Tony Wickham02700392017-05-11 15:20:28 -07002024 droppedOnOriginalCellDuringTransition = droppedOnOriginalCell && mIsSwitchingState;
Tony Wickhamea62fe02016-10-05 09:05:42 -07002025
Tony Wickham5dc74352016-12-06 16:24:43 -08002026 // When quickly moving an item, a user may accidentally rearrange their
2027 // workspace. So instead we move the icon back safely to its original position.
2028 boolean returnToOriginalCellToPreventShuffling = !isFinishedSwitchingState()
2029 && !droppedOnOriginalCellDuringTransition && !dropTargetLayout
2030 .isRegionVacant(mTargetCell[0], mTargetCell[1], spanX, spanY);
Adam Cohend41fbf52012-02-16 23:53:59 -08002031 int[] resultSpan = new int[2];
Tony Wickham5dc74352016-12-06 16:24:43 -08002032 if (returnToOriginalCellToPreventShuffling) {
2033 mTargetCell[0] = mTargetCell[1] = -1;
2034 } else {
2035 mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0],
2036 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, cell,
2037 mTargetCell, resultSpan, CellLayout.MODE_ON_DROP);
2038 }
Adam Cohen482ed822012-03-02 14:15:13 -08002039
Adam Cohend41fbf52012-02-16 23:53:59 -08002040 boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
Adam Cohen9e05a5e2012-09-10 15:53:09 -07002041
Adam Cohenaaa5c212012-10-05 18:14:31 -07002042 // if the widget resizes on drop
Adam Cohen9e05a5e2012-09-10 15:53:09 -07002043 if (foundCell && (cell instanceof AppWidgetHostView) &&
Adam Cohenaaa5c212012-10-05 18:14:31 -07002044 (resultSpan[0] != item.spanX || resultSpan[1] != item.spanY)) {
Adam Cohend41fbf52012-02-16 23:53:59 -08002045 resizeOnDrop = true;
2046 item.spanX = resultSpan[0];
2047 item.spanY = resultSpan[1];
Adam Cohen7bdfc972012-05-22 16:50:35 -07002048 AppWidgetHostView awhv = (AppWidgetHostView) cell;
2049 AppWidgetResizeFrame.updateWidgetSizeRanges(awhv, mLauncher, resultSpan[0],
2050 resultSpan[1]);
Adam Cohend41fbf52012-02-16 23:53:59 -08002051 }
Adam Cohendf035382011-04-11 17:22:04 -07002052
Adam Cohend41fbf52012-02-16 23:53:59 -08002053 if (foundCell) {
Tony Wickhamd1f03472016-10-24 10:27:56 -07002054 if (getScreenIdForPageIndex(mCurrentPage) != screenId && !hasMovedIntoHotseat) {
2055 snapScreen = getPageIndexForScreenId(screenId);
2056 snapToPage(snapScreen);
2057 }
2058
Adam Cohend41fbf52012-02-16 23:53:59 -08002059 final ItemInfo info = (ItemInfo) cell.getTag();
Winson Chung40e882b2011-07-21 19:01:11 -07002060 if (hasMovedLayouts) {
Patrick Dubroy94383362010-10-29 15:03:24 -07002061 // Reparent the view
Sunny Goyal25611b12014-07-22 09:52:37 -07002062 CellLayout parentCell = getParentCellLayoutForView(cell);
2063 if (parentCell != null) {
2064 parentCell.removeView(cell);
Sunny Goyal3d706ad2017-03-06 16:56:39 -08002065 } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
Sunny Goyal25611b12014-07-22 09:52:37 -07002066 throw new NullPointerException("mDragInfo.cell has null parent");
2067 }
Adam Cohendcd297f2013-06-18 13:13:40 -07002068 addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1],
Adam Cohend41fbf52012-02-16 23:53:59 -08002069 info.spanX, info.spanY);
Patrick Dubroy94383362010-10-29 15:03:24 -07002070 }
2071
2072 // update the item's position after drop
Patrick Dubroy94383362010-10-29 15:03:24 -07002073 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
Adam Cohen19f37922012-03-21 11:59:11 -07002074 lp.cellX = lp.tmpCellX = mTargetCell[0];
2075 lp.cellY = lp.tmpCellY = mTargetCell[1];
Adam Cohend41fbf52012-02-16 23:53:59 -08002076 lp.cellHSpan = item.spanX;
2077 lp.cellVSpan = item.spanY;
Adam Cohen19f37922012-03-21 11:59:11 -07002078 lp.isLockedToGrid = true;
Patrick Dubroy94383362010-10-29 15:03:24 -07002079
Winson Chung3d503fb2011-07-13 17:25:49 -07002080 if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
2081 cell instanceof LauncherAppWidgetHostView) {
Adam Cohend4844c32011-02-18 19:25:06 -08002082 final CellLayout cellLayout = dropTargetLayout;
2083 // We post this call so that the widget has a chance to be placed
2084 // in its final location
2085
2086 final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell;
Sunny Goyal2434d402015-02-17 11:44:15 -08002087 AppWidgetProviderInfo pInfo = hostView.getAppWidgetInfo();
Adam Cohenc9735cf2015-01-23 16:11:55 -08002088 if (pInfo != null && pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE
2089 && !d.accessibleDrag) {
Sunny Goyal1797af42017-10-06 13:29:57 -07002090 onCompleteRunnable = new Runnable() {
Adam Cohend4844c32011-02-18 19:25:06 -08002091 public void run() {
Tony Wickhamea62fe02016-10-05 09:05:42 -07002092 if (!isPageInTransition()) {
Sunny Goyal37920962017-09-28 13:43:24 -07002093 AppWidgetResizeFrame.showForWidget(hostView, cellLayout);
Adam Cohen26976d92011-03-22 15:33:33 -07002094 }
2095 }
Tony Wickhame0385622015-09-17 09:24:07 -07002096 };
Adam Cohend4844c32011-02-18 19:25:06 -08002097 }
2098 }
2099
Sunny Goyal43bf11d2017-02-02 13:52:53 -08002100 mLauncher.getModelWriter().modifyItemInDatabase(info, container, screenId,
2101 lp.cellX, lp.cellY, item.spanX, item.spanY);
Adam Cohend41fbf52012-02-16 23:53:59 -08002102 } else {
Tony Wickham5dc74352016-12-06 16:24:43 -08002103 if (!returnToOriginalCellToPreventShuffling) {
2104 onNoCellFound(dropTargetLayout);
2105 }
Jon Miranda9485e5f2016-10-18 11:47:42 -07002106
Adam Cohend41fbf52012-02-16 23:53:59 -08002107 // If we can't find a drop location, we return the item to its original position
2108 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
2109 mTargetCell[0] = lp.cellX;
2110 mTargetCell[1] = lp.cellY;
Adam Cohenf1dcdf62012-05-10 16:51:52 -07002111 CellLayout layout = (CellLayout) cell.getParent().getParent();
2112 layout.markCellsAsOccupiedForView(cell);
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002113 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002114 }
Patrick Dubroyce34a972010-10-19 10:34:32 -07002115
Michael Jurka8c920dd2011-01-20 14:16:56 -08002116 final CellLayout parent = (CellLayout) cell.getParent().getParent();
Adam Cohenfc53cd22011-07-20 15:45:11 -07002117 if (d.dragView.hasDrawn()) {
Tony Wickhamea62fe02016-10-05 09:05:42 -07002118 if (droppedOnOriginalCellDuringTransition) {
2119 // Animate the item to its original position, while simultaneously exiting
2120 // spring-loaded mode so the page meets the icon where it was picked up.
2121 mLauncher.getDragController().animateDragViewToOriginalPosition(
Sunny Goyalaeb16432017-10-16 11:46:41 -07002122 onCompleteRunnable, cell, SPRING_LOADED_TRANSITION_MS);
Sunny Goyal3e3f44c2017-10-23 17:14:52 -07002123 mLauncher.getStateManager().goToState(NORMAL);
Tony Wickhamea62fe02016-10-05 09:05:42 -07002124 mLauncher.getDropTargetBar().onDragEnd();
2125 parent.onDropChild(cell);
2126 return;
2127 }
Adam Cohend41fbf52012-02-16 23:53:59 -08002128 final ItemInfo info = (ItemInfo) cell.getTag();
Adam Cohen59400422014-03-05 18:07:04 -08002129 boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
2130 || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
2131 if (isWidget) {
Adam Cohend41fbf52012-02-16 23:53:59 -08002132 int animationType = resizeOnDrop ? ANIMATE_INTO_POSITION_AND_RESIZE :
2133 ANIMATE_INTO_POSITION_AND_DISAPPEAR;
Sunny Goyala502aa32017-10-02 16:04:06 -07002134 animateWidgetDrop(info, parent, d.dragView, null, animationType, cell, false);
Adam Cohend41fbf52012-02-16 23:53:59 -08002135 } else {
Sunny Goyalce5a7e52015-07-08 15:44:27 -07002136 int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION;
Adam Cohen85b467b2012-02-29 15:38:46 -08002137 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration,
Sunny Goyal1797af42017-10-06 13:29:57 -07002138 this);
Adam Cohend41fbf52012-02-16 23:53:59 -08002139 }
Adam Cohenfc53cd22011-07-20 15:45:11 -07002140 } else {
Winson Chung7bd1bbb2012-02-13 18:29:29 -08002141 d.deferDragViewCleanupPostAnimation = false;
Adam Cohenfc53cd22011-07-20 15:45:11 -07002142 cell.setVisibility(VISIBLE);
2143 }
Adam Cohen716b51e2011-06-30 12:09:54 -07002144 parent.onDropChild(cell);
Sunny Goyal1797af42017-10-06 13:29:57 -07002145
Sunny Goyal3e3f44c2017-10-23 17:14:52 -07002146 mLauncher.getStateManager().goToState(
2147 NORMAL, SPRING_LOADED_EXIT_DELAY, onCompleteRunnable);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002148 }
Sunny Goyal1797af42017-10-06 13:29:57 -07002149
Tony Wickham02700392017-05-11 15:20:28 -07002150 if (d.stateAnnouncer != null && !droppedOnOriginalCell) {
Sunny Goyal3f886402016-10-11 10:54:39 -07002151 d.stateAnnouncer.completeAction(R.string.item_moved);
2152 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002153 }
2154
Jon Miranda9485e5f2016-10-18 11:47:42 -07002155 public void onNoCellFound(View dropTargetLayout) {
2156 if (mLauncher.isHotseatLayout(dropTargetLayout)) {
2157 Hotseat hotseat = mLauncher.getHotseat();
2158 boolean droppedOnAllAppsIcon = !FeatureFlags.NO_ALL_APPS_ICON
2159 && mTargetCell != null && !mLauncher.getDeviceProfile().inv.isAllAppsButtonRank(
2160 hotseat.getOrderInHotseat(mTargetCell[0], mTargetCell[1]));
2161 if (!droppedOnAllAppsIcon) {
2162 // Only show message when hotseat is full and drop target was not AllApps button
2163 showOutOfSpaceMessage(true);
2164 }
2165 } else {
2166 showOutOfSpaceMessage(false);
2167 }
2168 }
2169
2170 private void showOutOfSpaceMessage(boolean isHotseatLayout) {
2171 int strId = (isHotseatLayout ? R.string.hotseat_out_of_space : R.string.out_of_space);
2172 Toast.makeText(mLauncher, mLauncher.getString(strId), Toast.LENGTH_SHORT).show();
2173 }
2174
Sunny Goyal41d84172015-07-08 23:28:51 -07002175 /**
2176 * Computes the area relative to dragLayer which is used to display a page.
2177 */
2178 public void getPageAreaRelativeToDragLayer(Rect outArea) {
2179 CellLayout child = (CellLayout) getChildAt(getNextPage());
2180 if (child == null) {
2181 return;
Adam Cohened51cc92011-08-01 20:28:08 -07002182 }
Sunny Goyal41d84172015-07-08 23:28:51 -07002183 ShortcutAndWidgetContainer boundingLayout = child.getShortcutsAndWidgets();
Adam Cohened51cc92011-08-01 20:28:08 -07002184
Sunny Goyal41d84172015-07-08 23:28:51 -07002185 // Use the absolute left instead of the child left, as we want the visible area
2186 // irrespective of the visible child. Since the view can only scroll horizontally, the
2187 // top position is not affected.
2188 mTempXY[0] = getViewportOffsetX() + getPaddingLeft() + boundingLayout.getLeft();
2189 mTempXY[1] = child.getTop() + boundingLayout.getTop();
2190
2191 float scale = mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY);
2192 outArea.set(mTempXY[0], mTempXY[1],
2193 (int) (mTempXY[0] + scale * boundingLayout.getMeasuredWidth()),
2194 (int) (mTempXY[1] + scale * boundingLayout.getMeasuredHeight()));
Adam Cohened51cc92011-08-01 20:28:08 -07002195 }
2196
Sunny Goyal58376922015-05-26 18:56:52 -07002197 @Override
Adam Cohencb3382b2011-05-24 14:07:08 -07002198 public void onDragEnter(DragObject d) {
Sunny Goyal58376922015-05-26 18:56:52 -07002199 if (ENFORCE_DRAG_EVENT_ORDER) {
Hyunyoung Song0de01172016-10-05 16:27:48 -07002200 enforceDragParity("onDragEnter", 1, 1);
Sunny Goyal58376922015-05-26 18:56:52 -07002201 }
2202
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002203 mCreateUserFolderOnDrop = false;
2204 mAddToExistingFolderOnDrop = false;
2205
2206 mDropToLayout = null;
Sunny Goyalaecb7cf2016-12-15 11:02:21 -08002207 mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);
2208 setDropLayoutForDragObject(d, mDragViewVisualCenter[0], mDragViewVisualCenter[1]);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002209 }
2210
Sunny Goyal58376922015-05-26 18:56:52 -07002211 @Override
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002212 public void onDragExit(DragObject d) {
Sunny Goyal58376922015-05-26 18:56:52 -07002213 if (ENFORCE_DRAG_EVENT_ORDER) {
Hyunyoung Song0de01172016-10-05 16:27:48 -07002214 enforceDragParity("onDragExit", -1, 0);
Sunny Goyal58376922015-05-26 18:56:52 -07002215 }
Winson Chungc07918d2011-07-01 15:35:26 -07002216
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002217 // Here we store the final page that will be dropped to, if the workspace in fact
2218 // receives the drop
Sunny Goyalf5440cb2016-12-14 15:13:00 -08002219 mDropToLayout = mDragTargetLayout;
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002220 if (mDragMode == DRAG_MODE_CREATE_FOLDER) {
2221 mCreateUserFolderOnDrop = true;
2222 } else if (mDragMode == DRAG_MODE_ADD_TO_FOLDER) {
2223 mAddToExistingFolderOnDrop = true;
Adam Cohen482ed822012-03-02 14:15:13 -08002224 }
2225
Sunny Goyalf5440cb2016-12-14 15:13:00 -08002226 // Reset the previous drag target
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002227 setCurrentDropLayout(null);
2228 setCurrentDragOverlappingLayout(null);
Winson Chungc07918d2011-07-01 15:35:26 -07002229
Adam Cohen74c28d12011-11-18 14:17:11 -08002230 mSpringLoadedDragController.cancel();
Winson Chungc07918d2011-07-01 15:35:26 -07002231 }
2232
Hyunyoung Song0de01172016-10-05 16:27:48 -07002233 private void enforceDragParity(String event, int update, int expectedValue) {
2234 enforceDragParity(this, event, update, expectedValue);
Sunny Goyal58376922015-05-26 18:56:52 -07002235 for (int i = 0; i < getChildCount(); i++) {
Hyunyoung Song0de01172016-10-05 16:27:48 -07002236 enforceDragParity(getChildAt(i), event, update, expectedValue);
Sunny Goyal58376922015-05-26 18:56:52 -07002237 }
2238 }
2239
Hyunyoung Song0de01172016-10-05 16:27:48 -07002240 private void enforceDragParity(View v, String event, int update, int expectedValue) {
Sunny Goyal58376922015-05-26 18:56:52 -07002241 Object tag = v.getTag(R.id.drag_event_parity);
2242 int value = tag == null ? 0 : (Integer) tag;
2243 value += update;
2244 v.setTag(R.id.drag_event_parity, value);
2245
2246 if (value != expectedValue) {
2247 Log.e(TAG, event + ": Drag contract violated: " + value);
2248 }
2249 }
2250
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002251 void setCurrentDropLayout(CellLayout layout) {
2252 if (mDragTargetLayout != null) {
2253 mDragTargetLayout.revertTempState();
2254 mDragTargetLayout.onDragExit();
2255 }
2256 mDragTargetLayout = layout;
2257 if (mDragTargetLayout != null) {
2258 mDragTargetLayout.onDragEnter();
2259 }
2260 cleanupReorder(true);
2261 cleanupFolderCreation();
2262 setCurrentDropOverCell(-1, -1);
2263 }
2264
2265 void setCurrentDragOverlappingLayout(CellLayout layout) {
2266 if (mDragOverlappingLayout != null) {
2267 mDragOverlappingLayout.setIsDragOverlapping(false);
2268 }
2269 mDragOverlappingLayout = layout;
2270 if (mDragOverlappingLayout != null) {
2271 mDragOverlappingLayout.setIsDragOverlapping(true);
2272 }
Tony Wickham95cdb3a2016-02-18 14:37:07 -08002273 // Invalidating the scrim will also force this CellLayout
2274 // to be invalidated so that it is highlighted if necessary.
2275 mLauncher.getDragLayer().invalidateScrim();
2276 }
2277
2278 public CellLayout getCurrentDragOverlappingLayout() {
2279 return mDragOverlappingLayout;
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002280 }
2281
2282 void setCurrentDropOverCell(int x, int y) {
2283 if (x != mDragOverX || y != mDragOverY) {
2284 mDragOverX = x;
2285 mDragOverY = y;
2286 setDragMode(DRAG_MODE_NONE);
2287 }
2288 }
2289
2290 void setDragMode(int dragMode) {
2291 if (dragMode != mDragMode) {
2292 if (dragMode == DRAG_MODE_NONE) {
2293 cleanupAddToFolder();
2294 // We don't want to cancel the re-order alarm every time the target cell changes
2295 // as this feels to slow / unresponsive.
2296 cleanupReorder(false);
2297 cleanupFolderCreation();
2298 } else if (dragMode == DRAG_MODE_ADD_TO_FOLDER) {
2299 cleanupReorder(true);
2300 cleanupFolderCreation();
2301 } else if (dragMode == DRAG_MODE_CREATE_FOLDER) {
2302 cleanupAddToFolder();
2303 cleanupReorder(true);
2304 } else if (dragMode == DRAG_MODE_REORDER) {
2305 cleanupAddToFolder();
2306 cleanupFolderCreation();
2307 }
2308 mDragMode = dragMode;
2309 }
2310 }
2311
2312 private void cleanupFolderCreation() {
Adam Cohenf172b742016-03-30 19:28:34 -07002313 if (mFolderCreateBg != null) {
2314 mFolderCreateBg.animateToRest();
2315 }
Adam Cohen8ec05f92013-10-13 12:29:03 -07002316 mFolderCreationAlarm.setOnAlarmListener(null);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002317 mFolderCreationAlarm.cancelAlarm();
2318 }
2319
2320 private void cleanupAddToFolder() {
2321 if (mDragOverFolderIcon != null) {
Sunny Goyale393d3a2016-09-09 12:42:10 -07002322 mDragOverFolderIcon.onDragExit();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002323 mDragOverFolderIcon = null;
2324 }
2325 }
2326
2327 private void cleanupReorder(boolean cancelAlarm) {
2328 // Any pending reorders are canceled
2329 if (cancelAlarm) {
2330 mReorderAlarm.cancelAlarm();
2331 }
2332 mLastReorderX = -1;
2333 mLastReorderY = -1;
Winson Chungc07918d2011-07-01 15:35:26 -07002334 }
2335
Michael Jurka4516c112010-10-07 15:13:47 -07002336 /*
2337 *
2338 * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
2339 * coordinate space. The argument xy is modified with the return result.
Michael Jurka4516c112010-10-07 15:13:47 -07002340 */
Sunny Goyal106bf642015-07-16 12:18:06 -07002341 void mapPointFromSelfToChild(View v, float[] xy) {
Adam Cohen7d30a372013-07-01 17:03:59 -07002342 xy[0] = xy[0] - v.getLeft();
2343 xy[1] = xy[1] - v.getTop();
Michael Jurka4516c112010-10-07 15:13:47 -07002344 }
2345
Sunny Goyalcf25b522015-07-09 00:01:18 -07002346 boolean isPointInSelfOverHotseat(int x, int y) {
2347 mTempXY[0] = x;
2348 mTempXY[1] = y;
2349 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY, true);
Sunny Goyal6c2975e2016-07-06 09:47:56 -07002350 View hotseat = mLauncher.getHotseat();
2351 return mTempXY[0] >= hotseat.getLeft() &&
2352 mTempXY[0] <= hotseat.getRight() &&
2353 mTempXY[1] >= hotseat.getTop() &&
2354 mTempXY[1] <= hotseat.getBottom();
Adam Cohen7d30a372013-07-01 17:03:59 -07002355 }
Winson Chung3d503fb2011-07-13 17:25:49 -07002356
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002357 void mapPointFromSelfToHotseatLayout(Hotseat hotseat, float[] xy) {
Sunny Goyalcf25b522015-07-09 00:01:18 -07002358 mTempXY[0] = (int) xy[0];
2359 mTempXY[1] = (int) xy[1];
2360 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY, true);
Hyunyoung Song0de01172016-10-05 16:27:48 -07002361 mLauncher.getDragLayer().mapCoordInSelfToDescendant(hotseat.getLayout(), mTempXY);
Adam Cohen7d30a372013-07-01 17:03:59 -07002362
Sunny Goyalcf25b522015-07-09 00:01:18 -07002363 xy[0] = mTempXY[0];
2364 xy[1] = mTempXY[1];
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002365 }
2366
Winson Chungea359c62011-08-03 17:06:35 -07002367 private boolean isDragWidget(DragObject d) {
2368 return (d.dragInfo instanceof LauncherAppWidgetInfo ||
2369 d.dragInfo instanceof PendingAddWidgetInfo);
2370 }
Winson Chungea359c62011-08-03 17:06:35 -07002371
Adam Cohencb3382b2011-05-24 14:07:08 -07002372 public void onDragOver(DragObject d) {
Winson Chungc07918d2011-07-01 15:35:26 -07002373 // Skip drag over events while we are dragging over side pages
Sunny Goyalf5440cb2016-12-14 15:13:00 -08002374 if (!transitionStateShouldAllowDrop()) return;
Winson Chungc07918d2011-07-01 15:35:26 -07002375
Sunny Goyalaa8ef112015-06-12 20:04:41 -07002376 ItemInfo item = d.dragInfo;
Sunny Goyal25611b12014-07-22 09:52:37 -07002377 if (item == null) {
Sunny Goyal3d706ad2017-03-06 16:56:39 -08002378 if (FeatureFlags.IS_DOGFOOD_BUILD) {
Sunny Goyal25611b12014-07-22 09:52:37 -07002379 throw new NullPointerException("DragObject has null info");
2380 }
2381 return;
2382 }
Winson Chungc07918d2011-07-01 15:35:26 -07002383
2384 // Ensure that we have proper spans for the item that we are dropping
2385 if (item.spanX < 0 || item.spanY < 0) throw new RuntimeException("Improper spans found");
Sunny Goyal1587d532015-01-29 09:57:16 -08002386 mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);
Adam Cohen00618752011-07-20 12:06:04 -07002387
Adam Cohen482ed822012-03-02 14:15:13 -08002388 final View child = (mDragInfo == null) ? null : mDragInfo.cell;
Sunny Goyalaecb7cf2016-12-15 11:02:21 -08002389 if (setDropLayoutForDragObject(d, mDragViewVisualCenter[0], mDragViewVisualCenter[1])) {
2390 if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
2391 mSpringLoadedDragController.cancel();
2392 } else {
2393 mSpringLoadedDragController.setAlarm(mDragTargetLayout);
Winson Chung4afe9b32011-07-27 17:46:20 -07002394 }
Winson Chungc07918d2011-07-01 15:35:26 -07002395 }
2396
2397 // Handle the drag over
2398 if (mDragTargetLayout != null) {
Winson Chungc07918d2011-07-01 15:35:26 -07002399 // We want the point to be mapped to the dragTarget.
Winson Chung3d503fb2011-07-13 17:25:49 -07002400 if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002401 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
Winson Chung3d503fb2011-07-13 17:25:49 -07002402 } else {
Sunny Goyal106bf642015-07-16 12:18:06 -07002403 mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter);
Winson Chung3d503fb2011-07-13 17:25:49 -07002404 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002405
Adam Cohen74c54912013-09-29 14:48:04 -07002406 int minSpanX = item.spanX;
2407 int minSpanY = item.spanY;
2408 if (item.minSpanX > 0 && item.minSpanY > 0) {
2409 minSpanX = item.minSpanX;
2410 minSpanY = item.minSpanY;
2411 }
2412
Winson Chungc07918d2011-07-01 15:35:26 -07002413 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
Adam Cohen74c54912013-09-29 14:48:04 -07002414 (int) mDragViewVisualCenter[1], minSpanX, minSpanY,
Adam Cohend024f982012-05-23 18:26:45 -07002415 mDragTargetLayout, mTargetCell);
Adam Cohen74c54912013-09-29 14:48:04 -07002416 int reorderX = mTargetCell[0];
2417 int reorderY = mTargetCell[1];
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002418
2419 setCurrentDropOverCell(mTargetCell[0], mTargetCell[1]);
2420
Adam Cohen482ed822012-03-02 14:15:13 -08002421 float targetCellDistance = mDragTargetLayout.getDistanceFromCell(
2422 mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell);
2423
Sunny Goyale78e3d72015-09-24 11:23:31 -07002424 manageFolderFeedback(mDragTargetLayout, mTargetCell, targetCellDistance, d);
Adam Cohen482ed822012-03-02 14:15:13 -08002425
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002426 boolean nearestDropOccupied = mDragTargetLayout.isNearestDropLocationOccupied((int)
2427 mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], item.spanX,
2428 item.spanY, child, mTargetCell);
2429
2430 if (!nearestDropOccupied) {
Sunny Goyal06e21a22016-08-11 16:02:02 -07002431 mDragTargetLayout.visualizeDropLocation(child, mOutlineProvider,
Sunny Goyale78e3d72015-09-24 11:23:31 -07002432 mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, false, d);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002433 } else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
Adam Cohen74c54912013-09-29 14:48:04 -07002434 && !mReorderAlarm.alarmPending() && (mLastReorderX != reorderX ||
2435 mLastReorderY != reorderY)) {
Adam Cohend024f982012-05-23 18:26:45 -07002436
Adam Cohenfa3c58f2013-12-06 16:10:55 -08002437 int[] resultSpan = new int[2];
2438 mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0],
2439 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, item.spanX, item.spanY,
2440 child, mTargetCell, resultSpan, CellLayout.MODE_SHOW_REORDER_HINT);
2441
Adam Cohen19f37922012-03-21 11:59:11 -07002442 // Otherwise, if we aren't adding to or creating a folder and there's no pending
2443 // reorder, then we schedule a reorder
Adam Cohen482ed822012-03-02 14:15:13 -08002444 ReorderAlarmListener listener = new ReorderAlarmListener(mDragViewVisualCenter,
Sunny Goyale78e3d72015-09-24 11:23:31 -07002445 minSpanX, minSpanY, item.spanX, item.spanY, d, child);
Adam Cohen482ed822012-03-02 14:15:13 -08002446 mReorderAlarm.setOnAlarmListener(listener);
2447 mReorderAlarm.setAlarm(REORDER_TIMEOUT);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002448 }
2449
2450 if (mDragMode == DRAG_MODE_CREATE_FOLDER || mDragMode == DRAG_MODE_ADD_TO_FOLDER ||
2451 !nearestDropOccupied) {
2452 if (mDragTargetLayout != null) {
2453 mDragTargetLayout.revertTempState();
Michael Jurkad3ef3062010-11-23 16:23:58 -08002454 }
2455 }
Adam Cohen482ed822012-03-02 14:15:13 -08002456 }
2457 }
2458
Tony Wickhamdbf08b42016-09-12 18:09:33 -07002459 /**
2460 * Updates {@link #mDragTargetLayout} and {@link #mDragOverlappingLayout}
2461 * based on the DragObject's position.
2462 *
2463 * The layout will be:
2464 * - The Hotseat if the drag object is over it
2465 * - A side page if we are in spring-loaded mode and the drag object is over it
2466 * - The current page otherwise
2467 *
2468 * @return whether the layout is different from the current {@link #mDragTargetLayout}.
2469 */
Sunny Goyalaecb7cf2016-12-15 11:02:21 -08002470 private boolean setDropLayoutForDragObject(DragObject d, float centerX, float centerY) {
Tony Wickhamdbf08b42016-09-12 18:09:33 -07002471 CellLayout layout = null;
2472 // Test to see if we are over the hotseat first
2473 if (mLauncher.getHotseat() != null && !isDragWidget(d)) {
2474 if (isPointInSelfOverHotseat(d.x, d.y)) {
2475 layout = mLauncher.getHotseat().getLayout();
2476 }
2477 }
Sunny Goyalaecb7cf2016-12-15 11:02:21 -08002478
2479 int nextPage = getNextPage();
2480 if (layout == null && !isPageInTransition()) {
2481 // Check if the item is dragged over left page
2482 mTempTouchCoordinates[0] = Math.min(centerX, d.x);
2483 mTempTouchCoordinates[1] = d.y;
2484 layout = verifyInsidePage(nextPage + (mIsRtl ? 1 : -1), mTempTouchCoordinates);
2485 }
2486
2487 if (layout == null && !isPageInTransition()) {
2488 // Check if the item is dragged over right page
2489 mTempTouchCoordinates[0] = Math.max(centerX, d.x);
2490 mTempTouchCoordinates[1] = d.y;
2491 layout = verifyInsidePage(nextPage + (mIsRtl ? -1 : 1), mTempTouchCoordinates);
2492 }
2493
2494 // Always pick the current page.
Sunny Goyal7ce471b2017-08-02 03:37:39 -07002495 if (layout == null && nextPage >= 0 && nextPage < getPageCount()) {
Sunny Goyalaecb7cf2016-12-15 11:02:21 -08002496 layout = (CellLayout) getChildAt(nextPage);
Tony Wickhamdbf08b42016-09-12 18:09:33 -07002497 }
2498 if (layout != mDragTargetLayout) {
2499 setCurrentDropLayout(layout);
2500 setCurrentDragOverlappingLayout(layout);
2501 return true;
2502 }
2503 return false;
2504 }
2505
Sunny Goyalaecb7cf2016-12-15 11:02:21 -08002506 /**
2507 * Returns the child CellLayout if the point is inside the page coordinates, null otherwise.
2508 */
2509 private CellLayout verifyInsidePage(int pageNo, float[] touchXy) {
Sunny Goyal7ce471b2017-08-02 03:37:39 -07002510 if (pageNo >= 0 && pageNo < getPageCount()) {
Sunny Goyalaecb7cf2016-12-15 11:02:21 -08002511 CellLayout cl = (CellLayout) getChildAt(pageNo);
2512 mapPointFromSelfToChild(cl, touchXy);
2513 if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() &&
2514 touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) {
2515 // This point is inside the cell layout
2516 return cl;
2517 }
2518 }
2519 return null;
2520 }
2521
Sunny Goyale78e3d72015-09-24 11:23:31 -07002522 private void manageFolderFeedback(CellLayout targetLayout,
2523 int[] targetCell, float distance, DragObject dragObject) {
2524 if (distance > mMaxDistanceForFolderCreation) return;
2525
2526 final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0], mTargetCell[1]);
Sunny Goyal9d438082015-09-25 11:50:16 -07002527 ItemInfo info = dragObject.dragInfo;
Sunny Goyale78e3d72015-09-24 11:23:31 -07002528 boolean userFolderPending = willCreateUserFolder(info, dragOverView, false);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002529 if (mDragMode == DRAG_MODE_NONE && userFolderPending &&
2530 !mFolderCreationAlarm.alarmPending()) {
Adam Cohenc9735cf2015-01-23 16:11:55 -08002531
2532 FolderCreationAlarmListener listener = new
2533 FolderCreationAlarmListener(targetLayout, targetCell[0], targetCell[1]);
2534
Sunny Goyale78e3d72015-09-24 11:23:31 -07002535 if (!dragObject.accessibleDrag) {
Adam Cohenc9735cf2015-01-23 16:11:55 -08002536 mFolderCreationAlarm.setOnAlarmListener(listener);
2537 mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT);
2538 } else {
2539 listener.onAlarm(mFolderCreationAlarm);
2540 }
Sunny Goyale78e3d72015-09-24 11:23:31 -07002541
2542 if (dragObject.stateAnnouncer != null) {
2543 dragObject.stateAnnouncer.announce(WorkspaceAccessibilityHelper
2544 .getDescriptionForDropOver(dragOverView, getContext()));
2545 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002546 return;
Adam Cohen482ed822012-03-02 14:15:13 -08002547 }
2548
Sunny Goyale78e3d72015-09-24 11:23:31 -07002549 boolean willAddToFolder = willAddToExistingUserFolder(info, dragOverView);
Adam Cohen482ed822012-03-02 14:15:13 -08002550 if (willAddToFolder && mDragMode == DRAG_MODE_NONE) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002551 mDragOverFolderIcon = ((FolderIcon) dragOverView);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002552 mDragOverFolderIcon.onDragEnter(info);
Adam Cohen482ed822012-03-02 14:15:13 -08002553 if (targetLayout != null) {
2554 targetLayout.clearDragOutlines();
Winson Chungc07918d2011-07-01 15:35:26 -07002555 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002556 setDragMode(DRAG_MODE_ADD_TO_FOLDER);
Sunny Goyale78e3d72015-09-24 11:23:31 -07002557
2558 if (dragObject.stateAnnouncer != null) {
2559 dragObject.stateAnnouncer.announce(WorkspaceAccessibilityHelper
2560 .getDescriptionForDropOver(dragOverView, getContext()));
2561 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002562 return;
Patrick Dubroy976ebec2010-08-04 20:03:37 -07002563 }
Adam Cohen482ed822012-03-02 14:15:13 -08002564
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002565 if (mDragMode == DRAG_MODE_ADD_TO_FOLDER && !willAddToFolder) {
2566 setDragMode(DRAG_MODE_NONE);
2567 }
2568 if (mDragMode == DRAG_MODE_CREATE_FOLDER && !userFolderPending) {
2569 setDragMode(DRAG_MODE_NONE);
Adam Cohen482ed822012-03-02 14:15:13 -08002570 }
Adam Cohenc0dcf592011-06-01 15:30:43 -07002571 }
2572
Adam Cohen19072da2011-05-31 14:30:45 -07002573 class FolderCreationAlarmListener implements OnAlarmListener {
Rajeev Kumar93c05122017-06-12 12:59:47 -07002574 final CellLayout layout;
2575 final int cellX;
2576 final int cellY;
Adam Cohen19072da2011-05-31 14:30:45 -07002577
Jon Mirandaa0233f72017-06-22 18:34:45 -07002578 final PreviewBackground bg = new PreviewBackground();
Adam Cohenf172b742016-03-30 19:28:34 -07002579
Adam Cohen69ce2e52011-07-03 19:25:21 -07002580 public FolderCreationAlarmListener(CellLayout layout, int cellX, int cellY) {
2581 this.layout = layout;
2582 this.cellX = cellX;
2583 this.cellY = cellY;
Adam Cohenefca0272016-02-24 19:19:06 -08002584
Adam Cohenefca0272016-02-24 19:19:06 -08002585 BubbleTextView cell = (BubbleTextView) layout.getChildAt(cellX, cellY);
Sunny Goyal368ae772017-05-24 13:19:15 -07002586 bg.setup(mLauncher, null, cell.getMeasuredWidth(), cell.getPaddingTop());
Adam Cohenf172b742016-03-30 19:28:34 -07002587
2588 // The full preview background should appear behind the icon
2589 bg.isClipping = false;
Adam Cohen19072da2011-05-31 14:30:45 -07002590 }
2591
2592 public void onAlarm(Alarm alarm) {
Adam Cohenf172b742016-03-30 19:28:34 -07002593 mFolderCreateBg = bg;
Adam Cohenefca0272016-02-24 19:19:06 -08002594 mFolderCreateBg.animateToAccept(layout, cellX, cellY);
Adam Cohen69ce2e52011-07-03 19:25:21 -07002595 layout.clearDragOutlines();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002596 setDragMode(DRAG_MODE_CREATE_FOLDER);
Adam Cohen482ed822012-03-02 14:15:13 -08002597 }
2598 }
2599
2600 class ReorderAlarmListener implements OnAlarmListener {
Rajeev Kumar93c05122017-06-12 12:59:47 -07002601 final float[] dragViewCenter;
2602 final int minSpanX, minSpanY, spanX, spanY;
2603 final DragObject dragObject;
2604 final View child;
Adam Cohen482ed822012-03-02 14:15:13 -08002605
2606 public ReorderAlarmListener(float[] dragViewCenter, int minSpanX, int minSpanY, int spanX,
Sunny Goyale78e3d72015-09-24 11:23:31 -07002607 int spanY, DragObject dragObject, View child) {
Adam Cohen482ed822012-03-02 14:15:13 -08002608 this.dragViewCenter = dragViewCenter;
2609 this.minSpanX = minSpanX;
2610 this.minSpanY = minSpanY;
2611 this.spanX = spanX;
2612 this.spanY = spanY;
2613 this.child = child;
Sunny Goyale78e3d72015-09-24 11:23:31 -07002614 this.dragObject = dragObject;
Adam Cohen482ed822012-03-02 14:15:13 -08002615 }
2616
2617 public void onAlarm(Alarm alarm) {
2618 int[] resultSpan = new int[2];
Adam Cohen19f37922012-03-21 11:59:11 -07002619 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
Adam Cohen74c54912013-09-29 14:48:04 -07002620 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, mDragTargetLayout,
2621 mTargetCell);
Adam Cohen19f37922012-03-21 11:59:11 -07002622 mLastReorderX = mTargetCell[0];
2623 mLastReorderY = mTargetCell[1];
2624
Adam Cohenfa3c58f2013-12-06 16:10:55 -08002625 mTargetCell = mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08002626 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
2627 child, mTargetCell, resultSpan, CellLayout.MODE_DRAG_OVER);
2628
Adam Cohen19f37922012-03-21 11:59:11 -07002629 if (mTargetCell[0] < 0 || mTargetCell[1] < 0) {
2630 mDragTargetLayout.revertTempState();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002631 } else {
2632 setDragMode(DRAG_MODE_REORDER);
Adam Cohen19f37922012-03-21 11:59:11 -07002633 }
Adam Cohen482ed822012-03-02 14:15:13 -08002634
Adam Cohen482ed822012-03-02 14:15:13 -08002635 boolean resize = resultSpan[0] != spanX || resultSpan[1] != spanY;
Sunny Goyal06e21a22016-08-11 16:02:02 -07002636 mDragTargetLayout.visualizeDropLocation(child, mOutlineProvider,
Sunny Goyale78e3d72015-09-24 11:23:31 -07002637 mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], resize, dragObject);
Adam Cohen19072da2011-05-31 14:30:45 -07002638 }
2639 }
2640
Winson Chunga34abf82010-11-12 12:10:35 -08002641 @Override
Adam Cohen7d30a372013-07-01 17:03:59 -07002642 public void getHitRectRelativeToDragLayer(Rect outRect) {
Winson Chunga34abf82010-11-12 12:10:35 -08002643 // We want the workspace to have the whole area of the display (it will find the correct
2644 // cell layout to drop to in the existing drag/drop logic.
Adam Cohen7d30a372013-07-01 17:03:59 -07002645 mLauncher.getDragLayer().getDescendantRectRelativeToSelf(this, outRect);
Winson Chunga34abf82010-11-12 12:10:35 -08002646 }
2647
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07002648 /**
Adam Cohen120980b2010-12-08 11:05:37 -08002649 * Drop an item that didn't originate on one of the workspace screens.
2650 * It may have come from Launcher (e.g. from all apps or customize), or it may have
2651 * come from another app altogether.
2652 *
2653 * NOTE: This can also be called when we are outside of a drag event, when we want
2654 * to add an item to one of the workspace screens.
2655 */
Sunny Goyaldec3a902017-01-25 18:23:36 -08002656 private void onDropExternal(final int[] touchXY, final CellLayout cellLayout, DragObject d) {
Sunny Goyaldec3a902017-01-25 18:23:36 -08002657 if (d.dragInfo instanceof PendingAddShortcutInfo) {
2658 ShortcutInfo si = ((PendingAddShortcutInfo) d.dragInfo)
2659 .activityInfo.createShortcutInfo();
2660 if (si != null) {
2661 d.dragInfo = si;
2662 }
2663 }
2664
2665 ItemInfo info = d.dragInfo;
Adam Cohenb7e16182011-07-15 17:55:02 -07002666 int spanX = info.spanX;
2667 int spanY = info.spanY;
2668 if (mDragInfo != null) {
2669 spanX = mDragInfo.spanX;
2670 spanY = mDragInfo.spanY;
2671 }
2672
Winson Chung3d503fb2011-07-13 17:25:49 -07002673 final long container = mLauncher.isHotseatLayout(cellLayout) ?
2674 LauncherSettings.Favorites.CONTAINER_HOTSEAT :
2675 LauncherSettings.Favorites.CONTAINER_DESKTOP;
Adam Cohendcd297f2013-06-18 13:13:40 -07002676 final long screenId = getIdForScreen(cellLayout);
2677 if (!mLauncher.isHotseatLayout(cellLayout)
2678 && screenId != getScreenIdForPageIndex(mCurrentPage)
Sunny Goyalea609262017-10-25 15:47:38 -07002679 && !mLauncher.isInState(SPRING_LOADED)) {
Sunny Goyal7ce471b2017-08-02 03:37:39 -07002680 snapToPage(getPageIndexForScreenId(screenId));
Adam Cohen76078c42011-06-09 15:06:52 -07002681 }
Adam Cohenb7e16182011-07-15 17:55:02 -07002682
2683 if (info instanceof PendingAddItemInfo) {
Sunny Goyaldec3a902017-01-25 18:23:36 -08002684 final PendingAddItemInfo pendingInfo = (PendingAddItemInfo) info;
Adam Cohenb7e16182011-07-15 17:55:02 -07002685
Adam Cohen558baaf2011-08-15 15:22:57 -07002686 boolean findNearestVacantCell = true;
2687 if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
Rajeev Kumar93c05122017-06-12 12:59:47 -07002688 mTargetCell = findNearestArea(touchXY[0], touchXY[1], spanX, spanY,
Adam Cohen558baaf2011-08-15 15:22:57 -07002689 cellLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08002690 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
2691 mDragViewVisualCenter[1], mTargetCell);
Sunny Goyalaa8ef112015-06-12 20:04:41 -07002692 if (willCreateUserFolder(d.dragInfo, cellLayout, mTargetCell, distance, true)
2693 || willAddToExistingUserFolder(
2694 d.dragInfo, cellLayout, mTargetCell, distance)) {
Adam Cohen558baaf2011-08-15 15:22:57 -07002695 findNearestVacantCell = false;
2696 }
2697 }
Adam Cohen482ed822012-03-02 14:15:13 -08002698
Sunny Goyalaa8ef112015-06-12 20:04:41 -07002699 final ItemInfo item = d.dragInfo;
Adam Cohenaaa5c212012-10-05 18:14:31 -07002700 boolean updateWidgetSize = false;
Adam Cohen558baaf2011-08-15 15:22:57 -07002701 if (findNearestVacantCell) {
Adam Cohen482ed822012-03-02 14:15:13 -08002702 int minSpanX = item.spanX;
2703 int minSpanY = item.spanY;
2704 if (item.minSpanX > 0 && item.minSpanY > 0) {
2705 minSpanX = item.minSpanX;
2706 minSpanY = item.minSpanY;
2707 }
Adam Cohend41fbf52012-02-16 23:53:59 -08002708 int[] resultSpan = new int[2];
Adam Cohenfa3c58f2013-12-06 16:10:55 -08002709 mTargetCell = cellLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08002710 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, info.spanX, info.spanY,
2711 null, mTargetCell, resultSpan, CellLayout.MODE_ON_DROP_EXTERNAL);
Adam Cohen9e05a5e2012-09-10 15:53:09 -07002712
2713 if (resultSpan[0] != item.spanX || resultSpan[1] != item.spanY) {
2714 updateWidgetSize = true;
2715 }
Adam Cohend41fbf52012-02-16 23:53:59 -08002716 item.spanX = resultSpan[0];
2717 item.spanY = resultSpan[1];
Adam Cohen558baaf2011-08-15 15:22:57 -07002718 }
2719
Adam Cohenb7e16182011-07-15 17:55:02 -07002720 Runnable onAnimationCompleteRunnable = new Runnable() {
Winson Chung557d6ed2011-07-08 15:34:52 -07002721 @Override
2722 public void run() {
Adam Cohen689ff162014-05-08 17:27:56 -07002723 // Normally removeExtraEmptyScreen is called in Workspace#onDragEnd, but when
2724 // adding an item that may not be dropped right away (due to a config activity)
2725 // we defer the removal until the activity returns.
2726 deferRemoveExtraEmptyScreen();
2727
Winson Chung557d6ed2011-07-08 15:34:52 -07002728 // When dragging and dropping from customization tray, we deal with creating
2729 // widgets/shortcuts/folders in a slightly different way
Sunny Goyal71b5c0b2015-01-08 16:59:04 -08002730 mLauncher.addPendingItem(pendingInfo, container, screenId, mTargetCell,
2731 item.spanX, item.spanY);
Winson Chung557d6ed2011-07-08 15:34:52 -07002732 }
Adam Cohenb7e16182011-07-15 17:55:02 -07002733 };
Adam Cohen59400422014-03-05 18:07:04 -08002734 boolean isWidget = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
2735 || pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
2736
Sunny Goyalb740f592015-12-17 23:22:42 -08002737 AppWidgetHostView finalView = isWidget ?
2738 ((PendingAddWidgetInfo) pendingInfo).boundWidget : null;
Adam Cohen9e05a5e2012-09-10 15:53:09 -07002739
Sunny Goyalb740f592015-12-17 23:22:42 -08002740 if (finalView != null && updateWidgetSize) {
2741 AppWidgetResizeFrame.updateWidgetSizeRanges(finalView, mLauncher, item.spanX,
Adam Cohen9e05a5e2012-09-10 15:53:09 -07002742 item.spanY);
2743 }
2744
Adam Cohend41fbf52012-02-16 23:53:59 -08002745 int animationStyle = ANIMATE_INTO_POSITION_AND_DISAPPEAR;
Adam Cohen59400422014-03-05 18:07:04 -08002746 if (isWidget && ((PendingAddWidgetInfo) pendingInfo).info != null &&
Sunny Goyaldec3a902017-01-25 18:23:36 -08002747 ((PendingAddWidgetInfo) pendingInfo).getHandler().needsConfigure()) {
Adam Cohend41fbf52012-02-16 23:53:59 -08002748 animationStyle = ANIMATE_INTO_POSITION_AND_REMAIN;
2749 }
2750 animateWidgetDrop(info, cellLayout, d.dragView, onAnimationCompleteRunnable,
2751 animationStyle, finalView, true);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07002752 } else {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08002753 // This is for other drag/drop cases, like dragging from All Apps
Sunny Goyal3e3f44c2017-10-23 17:14:52 -07002754 mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
Sunny Goyal1797af42017-10-06 13:29:57 -07002755
Rajeev Kumar93c05122017-06-12 12:59:47 -07002756 View view;
Michael Jurka9c6de3d2010-11-23 16:23:58 -08002757
2758 switch (info.itemType) {
2759 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
2760 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
Tony Wickhambfbf7f92016-05-19 11:19:39 -07002761 case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
Michael Jurkaeadbfc52013-09-04 00:45:37 +02002762 if (info.container == NO_ID && info instanceof AppInfo) {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08002763 // Came from all apps -- make a copy
Sunny Goyal71b5c0b2015-01-08 16:59:04 -08002764 info = ((AppInfo) info).makeShortcut();
Hyunyoung Song59a23802016-09-01 12:47:12 -07002765 d.dragInfo = info;
Michael Jurka9c6de3d2010-11-23 16:23:58 -08002766 }
Sunny Goyaldfaccf62015-05-11 16:30:44 -07002767 view = mLauncher.createShortcut(cellLayout, (ShortcutInfo) info);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08002768 break;
Adam Cohendf2cc412011-04-27 16:56:57 -07002769 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
Adam Cohenc0dcf592011-06-01 15:30:43 -07002770 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, cellLayout,
Sunny Goyal1cd01b02016-11-09 10:43:58 -08002771 (FolderInfo) info);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08002772 break;
2773 default:
2774 throw new IllegalStateException("Unknown item type: " + info.itemType);
2775 }
2776
Adam Cohenc0dcf592011-06-01 15:30:43 -07002777 // First we find the cell nearest to point at which the item is
2778 // dropped, without any consideration to whether there is an item there.
2779 if (touchXY != null) {
Rajeev Kumar93c05122017-06-12 12:59:47 -07002780 mTargetCell = findNearestArea(touchXY[0], touchXY[1], spanX, spanY,
Adam Cohenc0dcf592011-06-01 15:30:43 -07002781 cellLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08002782 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
2783 mDragViewVisualCenter[1], mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08002784 if (createUserFolderIfNecessary(view, container, cellLayout, mTargetCell, distance,
Sunny Goyal1797af42017-10-06 13:29:57 -07002785 true, d.dragView)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07002786 return;
2787 }
Adam Cohen482ed822012-03-02 14:15:13 -08002788 if (addToExistingFolderIfNecessary(view, cellLayout, mTargetCell, distance, d,
2789 true)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07002790 return;
2791 }
Adam Cohen10b17372011-04-15 14:21:25 -07002792 }
2793
Michael Jurkac4e772e2011-02-10 13:32:01 -08002794 if (touchXY != null) {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08002795 // when dragging and dropping, just find the closest free spot
Adam Cohenfa3c58f2013-12-06 16:10:55 -08002796 mTargetCell = cellLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08002797 (int) mDragViewVisualCenter[1], 1, 1, 1, 1,
Adam Cohenea889a22012-03-27 16:45:39 -07002798 null, mTargetCell, null, CellLayout.MODE_ON_DROP_EXTERNAL);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08002799 } else {
2800 cellLayout.findCellForSpan(mTargetCell, 1, 1);
2801 }
Sunny Goyal95abbb32014-08-04 10:53:22 -07002802 // Add the item to DB before adding to screen ensures that the container and other
2803 // values of the info is properly updated.
Sunny Goyal43bf11d2017-02-02 13:52:53 -08002804 mLauncher.getModelWriter().addOrMoveItemInDatabase(info, container, screenId,
Sunny Goyal95abbb32014-08-04 10:53:22 -07002805 mTargetCell[0], mTargetCell[1]);
2806
Sunny Goyald5462aa2016-11-22 16:52:39 +05302807 addInScreen(view, container, screenId, mTargetCell[0], mTargetCell[1],
2808 info.spanX, info.spanY);
Adam Cohen716b51e2011-06-30 12:09:54 -07002809 cellLayout.onDropChild(view);
Michael Jurkaa52570f2012-03-20 03:18:20 -07002810 cellLayout.getShortcutsAndWidgets().measureChild(view);
Adam Cohend5e42732011-03-28 17:33:39 -07002811
Adam Cohen3e8f8112011-07-02 18:03:00 -07002812 if (d.dragView != null) {
Adam Cohen4b285c52011-07-21 14:24:06 -07002813 // We wrap the animation call in the temporary set and reset of the current
2814 // cellLayout to its final transform -- this means we animate the drag view to
2815 // the correct final location.
Sunny Goyalaeb16432017-10-16 11:46:41 -07002816 setFinalTransitionTransform();
2817 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view, this);
2818 resetTransitionTransform();
Adam Cohen3e8f8112011-07-02 18:03:00 -07002819 }
Joe Onorato00acb122009-08-04 16:04:30 -04002820 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002821 }
Winson Chungaafa03c2010-06-11 17:34:16 -07002822
Adam Cohend41fbf52012-02-16 23:53:59 -08002823 public Bitmap createWidgetBitmap(ItemInfo widgetInfo, View layout) {
Sunny Goyalaa3a2ba2017-09-26 12:43:16 -07002824 int[] unScaledSize = estimateItemSize(widgetInfo);
Adam Cohend41fbf52012-02-16 23:53:59 -08002825 int visibility = layout.getVisibility();
Adam Cohened66b2b2012-01-23 17:28:51 -08002826 layout.setVisibility(VISIBLE);
2827
2828 int width = MeasureSpec.makeMeasureSpec(unScaledSize[0], MeasureSpec.EXACTLY);
2829 int height = MeasureSpec.makeMeasureSpec(unScaledSize[1], MeasureSpec.EXACTLY);
2830 Bitmap b = Bitmap.createBitmap(unScaledSize[0], unScaledSize[1],
2831 Bitmap.Config.ARGB_8888);
Adam Cohened66b2b2012-01-23 17:28:51 -08002832 layout.measure(width, height);
2833 layout.layout(0, 0, unScaledSize[0], unScaledSize[1]);
Sunny Goyalaa3a2ba2017-09-26 12:43:16 -07002834 layout.draw(new Canvas(b));
Adam Cohend41fbf52012-02-16 23:53:59 -08002835 layout.setVisibility(visibility);
Adam Cohened66b2b2012-01-23 17:28:51 -08002836 return b;
2837 }
2838
Adam Cohend41fbf52012-02-16 23:53:59 -08002839 private void getFinalPositionForDropAnimation(int[] loc, float[] scaleXY,
Tony Wickhama501d492015-11-03 18:05:01 -08002840 DragView dragView, CellLayout layout, ItemInfo info, int[] targetCell, boolean scale) {
Adam Cohened66b2b2012-01-23 17:28:51 -08002841 // Now we animate the dragView, (ie. the widget or shortcut preview) into its final
2842 // location and size on the home screen.
Adam Cohend41fbf52012-02-16 23:53:59 -08002843 int spanX = info.spanX;
2844 int spanY = info.spanY;
Adam Cohened66b2b2012-01-23 17:28:51 -08002845
Tony Wickhama501d492015-11-03 18:05:01 -08002846 Rect r = estimateItemPosition(layout, targetCell[0], targetCell[1], spanX, spanY);
Jon Mirandae96798e2016-12-07 12:10:44 -08002847 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET) {
2848 DeviceProfile profile = mLauncher.getDeviceProfile();
Jon Miranda6f6a06a2016-12-15 11:24:18 -08002849 Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y);
Jon Mirandae96798e2016-12-07 12:10:44 -08002850 }
Adam Cohend41fbf52012-02-16 23:53:59 -08002851 loc[0] = r.left;
2852 loc[1] = r.top;
2853
Sunny Goyalaeb16432017-10-16 11:46:41 -07002854 setFinalTransitionTransform();
Adam Cohend41fbf52012-02-16 23:53:59 -08002855 float cellLayoutScale =
Adam Cohen7d30a372013-07-01 17:03:59 -07002856 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(layout, loc, true);
Sunny Goyalaeb16432017-10-16 11:46:41 -07002857 resetTransitionTransform();
Michael Jurkaa2fe7862012-05-29 05:58:17 -07002858
Michael Jurkaa2fe7862012-05-29 05:58:17 -07002859 if (scale) {
Sunny Goyaldec3a902017-01-25 18:23:36 -08002860 float dragViewScaleX = (1.0f * r.width()) / dragView.getMeasuredWidth();
2861 float dragViewScaleY = (1.0f * r.height()) / dragView.getMeasuredHeight();
2862
2863 // The animation will scale the dragView about its center, so we need to center about
2864 // the final location.
2865 loc[0] -= (dragView.getMeasuredWidth() - cellLayoutScale * r.width()) / 2
2866 - Math.ceil(layout.getUnusedHorizontalSpace() / 2f);
2867 loc[1] -= (dragView.getMeasuredHeight() - cellLayoutScale * r.height()) / 2;
2868 scaleXY[0] = dragViewScaleX * cellLayoutScale;
2869 scaleXY[1] = dragViewScaleY * cellLayoutScale;
2870 } else {
2871 // Since we are not cross-fading the dragView, align the drag view to the
2872 // final cell position.
2873 float dragScale = dragView.getInitialScale() * cellLayoutScale;
2874 loc[0] += (dragScale - 1) * dragView.getWidth() / 2;
2875 loc[1] += (dragScale - 1) * dragView.getHeight() / 2;
2876 scaleXY[0] = scaleXY[1] = dragScale;
2877
2878 // If a dragRegion was provided, offset the final position accordingly.
2879 Rect dragRegion = dragView.getDragRegion();
2880 if (dragRegion != null) {
2881 loc[0] += cellLayoutScale * dragRegion.left;
2882 loc[1] += cellLayoutScale * dragRegion.top;
2883 }
Michael Jurkaa2fe7862012-05-29 05:58:17 -07002884 }
Adam Cohend41fbf52012-02-16 23:53:59 -08002885 }
2886
Tony Wickhama501d492015-11-03 18:05:01 -08002887 public void animateWidgetDrop(ItemInfo info, CellLayout cellLayout, final DragView dragView,
Adam Cohend41fbf52012-02-16 23:53:59 -08002888 final Runnable onCompleteRunnable, int animationType, final View finalView,
2889 boolean external) {
2890 Rect from = new Rect();
2891 mLauncher.getDragLayer().getViewRectRelativeToSelf(dragView, from);
2892
2893 int[] finalPos = new int[2];
2894 float scaleXY[] = new float[2];
Michael Jurkaa2fe7862012-05-29 05:58:17 -07002895 boolean scalePreview = !(info instanceof PendingAddShortcutInfo);
Adam Cohend41fbf52012-02-16 23:53:59 -08002896 getFinalPositionForDropAnimation(finalPos, scaleXY, dragView, cellLayout, info, mTargetCell,
Tony Wickhama501d492015-11-03 18:05:01 -08002897 scalePreview);
Adam Cohened66b2b2012-01-23 17:28:51 -08002898
2899 Resources res = mLauncher.getResources();
Adam Cohenad4e15c2013-10-17 16:21:35 -07002900 final int duration = res.getInteger(R.integer.config_dropAnimMaxDuration) - 200;
Adam Cohened66b2b2012-01-23 17:28:51 -08002901
Adam Cohen59400422014-03-05 18:07:04 -08002902 boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET ||
2903 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
Adam Cohend41fbf52012-02-16 23:53:59 -08002904 if ((animationType == ANIMATE_INTO_POSITION_AND_RESIZE || external) && finalView != null) {
2905 Bitmap crossFadeBitmap = createWidgetBitmap(info, finalView);
Adam Cohened66b2b2012-01-23 17:28:51 -08002906 dragView.setCrossFadeBitmap(crossFadeBitmap);
2907 dragView.crossFade((int) (duration * 0.8f));
Adam Cohen59400422014-03-05 18:07:04 -08002908 } else if (isWidget && external) {
Adam Cohend41fbf52012-02-16 23:53:59 -08002909 scaleXY[0] = scaleXY[1] = Math.min(scaleXY[0], scaleXY[1]);
Adam Cohened66b2b2012-01-23 17:28:51 -08002910 }
2911
Adam Cohend41fbf52012-02-16 23:53:59 -08002912 DragLayer dragLayer = mLauncher.getDragLayer();
Winson Chung7bd1bbb2012-02-13 18:29:29 -08002913 if (animationType == CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION) {
Adam Cohend41fbf52012-02-16 23:53:59 -08002914 mLauncher.getDragLayer().animateViewIntoPosition(dragView, finalPos, 0f, 0.1f, 0.1f,
Adam Cohened66b2b2012-01-23 17:28:51 -08002915 DragLayer.ANIMATION_END_DISAPPEAR, onCompleteRunnable, duration);
2916 } else {
Adam Cohend41fbf52012-02-16 23:53:59 -08002917 int endStyle;
2918 if (animationType == ANIMATE_INTO_POSITION_AND_REMAIN) {
2919 endStyle = DragLayer.ANIMATION_END_REMAIN_VISIBLE;
2920 } else {
Tony Wickhama501d492015-11-03 18:05:01 -08002921 endStyle = DragLayer.ANIMATION_END_DISAPPEAR;
Adam Cohend41fbf52012-02-16 23:53:59 -08002922 }
2923
2924 Runnable onComplete = new Runnable() {
2925 @Override
2926 public void run() {
2927 if (finalView != null) {
2928 finalView.setVisibility(VISIBLE);
2929 }
2930 if (onCompleteRunnable != null) {
2931 onCompleteRunnable.run();
2932 }
2933 }
2934 };
2935 dragLayer.animateViewIntoPosition(dragView, from.left, from.top, finalPos[0],
2936 finalPos[1], 1, 1, 1, scaleXY[0], scaleXY[1], onComplete, endStyle,
2937 duration, this);
Adam Cohened66b2b2012-01-23 17:28:51 -08002938 }
2939 }
2940
Sunny Goyalaeb16432017-10-16 11:46:41 -07002941 public void setFinalTransitionTransform() {
Adam Cohen4b285c52011-07-21 14:24:06 -07002942 if (isSwitchingState()) {
Adam Cohen7d30a372013-07-01 17:03:59 -07002943 mCurrentScale = getScaleX();
Winson Chungdc61c4d2015-04-20 18:26:57 -07002944 setScaleX(mStateTransitionAnimation.getFinalScale());
2945 setScaleY(mStateTransitionAnimation.getFinalScale());
Adam Cohen4b285c52011-07-21 14:24:06 -07002946 }
2947 }
Sunny Goyalaeb16432017-10-16 11:46:41 -07002948 public void resetTransitionTransform() {
Adam Cohen4b285c52011-07-21 14:24:06 -07002949 if (isSwitchingState()) {
Adam Cohen7d30a372013-07-01 17:03:59 -07002950 setScaleX(mCurrentScale);
2951 setScaleY(mCurrentScale);
Adam Cohen4b285c52011-07-21 14:24:06 -07002952 }
2953 }
2954
Jeff Sharkey70864282009-04-07 21:08:40 -07002955 /**
Michael Jurka0280c3b2010-09-17 15:00:07 -07002956 * Return the current CellInfo describing our current drag; this method exists
2957 * so that Launcher can sync this object with the correct info when the activity is created/
2958 * destroyed
2959 *
2960 */
2961 public CellLayout.CellInfo getDragInfo() {
2962 return mDragInfo;
2963 }
2964
2965 /**
Jeff Sharkey70864282009-04-07 21:08:40 -07002966 * Calculate the nearest cell where the given object would be dropped.
Adam Cohene3e27a82011-04-15 12:07:39 -07002967 *
2968 * pixelX and pixelY should be in the coordinate system of layout
Jeff Sharkey70864282009-04-07 21:08:40 -07002969 */
Adam Cohen091440a2015-03-18 14:16:05 -07002970 @Thunk int[] findNearestArea(int pixelX, int pixelY,
Adam Cohene3e27a82011-04-15 12:07:39 -07002971 int spanX, int spanY, CellLayout layout, int[] recycle) {
Adam Cohendf035382011-04-11 17:22:04 -07002972 return layout.findNearestArea(
Adam Cohene3e27a82011-04-15 12:07:39 -07002973 pixelX, pixelY, spanX, spanY, recycle);
Adam Cohendf035382011-04-11 17:22:04 -07002974 }
2975
Adam Cohencff6af82011-09-13 14:51:53 -07002976 void setup(DragController dragController) {
Michael Jurkac2f7f472010-12-14 15:34:42 -08002977 mSpringLoadedDragController = new SpringLoadedDragController(mLauncher);
Joe Onorato00acb122009-08-04 16:04:30 -04002978 mDragController = dragController;
Michael Jurkad74c9842011-07-10 12:44:21 -07002979
Michael Jurkad74c9842011-07-10 12:44:21 -07002980 // hardware layers on children are enabled on startup, but should be disabled until
2981 // needed
Sunny Goyala502aa32017-10-02 16:04:06 -07002982 updateChildrenLayersEnabled();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002983 }
2984
Patrick Dubroye3887cc2011-01-20 10:43:40 -08002985 /**
2986 * Called at the end of a drag which originated on the workspace.
2987 */
Michael Jurka1e2f4652013-07-08 18:03:46 -07002988 public void onDropCompleted(final View target, final DragObject d,
Sunny Goyal1797af42017-10-06 13:29:57 -07002989 final boolean success) {
Michael Jurka1e2f4652013-07-08 18:03:46 -07002990
Sunny Goyal3dce5f32017-10-05 11:40:05 -07002991 if (success) {
Sunny Goyal95deb3a2015-03-05 10:27:01 -08002992 if (target != this && mDragInfo != null) {
Sunny Goyal71b5c0b2015-01-08 16:59:04 -08002993 removeWorkspaceItem(mDragInfo.cell);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002994 }
Patrick Dubroyce34a972010-10-19 10:34:32 -07002995 } else if (mDragInfo != null) {
Sunny Goyal92820592015-03-02 11:31:35 -08002996 final CellLayout cellLayout = mLauncher.getCellLayout(
2997 mDragInfo.container, mDragInfo.screenId);
Jorim Jaggieedb00a2014-01-13 13:45:07 -08002998 if (cellLayout != null) {
2999 cellLayout.onDropChild(mDragInfo.cell);
Sunny Goyal3d706ad2017-03-06 16:56:39 -08003000 } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
Sunny Goyal92820592015-03-02 11:31:35 -08003001 throw new RuntimeException("Invalid state: cellLayout == null in "
3002 + "Workspace#onDropCompleted. Please file a bug. ");
Rajeev Kumar93c05122017-06-12 12:59:47 -07003003 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003004 }
Sunny Goyal3dce5f32017-10-05 11:40:05 -07003005 if (d.cancelled && mDragInfo != null && mDragInfo.cell != null) {
Michael Jurka1e2f4652013-07-08 18:03:46 -07003006 mDragInfo.cell.setVisibility(VISIBLE);
Adam Cohen36cc09b2011-09-29 17:33:15 -07003007 }
Tony Wickham4feb27e2016-10-26 12:51:14 -07003008 mDragInfo = null;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003009 }
3010
Hyunyoung Song31178b82015-02-24 14:12:51 -08003011 /**
3012 * For opposite operation. See {@link #addInScreen}.
3013 */
Sunny Goyal71b5c0b2015-01-08 16:59:04 -08003014 public void removeWorkspaceItem(View v) {
3015 CellLayout parentCell = getParentCellLayoutForView(v);
3016 if (parentCell != null) {
3017 parentCell.removeView(v);
Sunny Goyal3d706ad2017-03-06 16:56:39 -08003018 } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
Sunny Goyal912bdfc2015-08-03 19:09:55 -07003019 // When an app is uninstalled using the drop target, we wait until resume to remove
3020 // the icon. We also remove all the corresponding items from the workspace at
3021 // {@link Launcher#bindComponentsRemoved}. That call can come before or after
3022 // {@link Launcher#mOnResumeCallbacks} depending on how busy the worker thread is.
3023 Log.e(TAG, "mDragInfo.cell has null parent");
Sunny Goyal71b5c0b2015-01-08 16:59:04 -08003024 }
3025 if (v instanceof DropTarget) {
3026 mDragController.removeDropTarget((DropTarget) v);
3027 }
3028 }
3029
Sunny Goyalaaf7d1d2016-05-17 13:38:54 -07003030 /**
3031 * Removes all folder listeners
3032 */
3033 public void removeFolderListeners() {
3034 mapOverItems(false, new ItemOperator() {
3035 @Override
3036 public boolean evaluate(ItemInfo info, View view) {
3037 if (view instanceof FolderIcon) {
3038 ((FolderIcon) view).removeListeners();
3039 }
3040 return false;
3041 }
3042 });
3043 }
3044
Michael Jurka0280c3b2010-09-17 15:00:07 -07003045 public boolean isDropEnabled() {
3046 return true;
3047 }
3048
Michael Jurka0142d492010-08-25 17:46:15 -07003049 @Override
Adam Cohen1462de32012-07-24 22:34:36 -07003050 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
3051 // We don't dispatch restoreInstanceState to our children using this code path.
Dave Hawkeya8881582013-09-17 15:55:33 -06003052 // Some pages will be restored immediately as their items are bound immediately, and
Adam Cohen1462de32012-07-24 22:34:36 -07003053 // others we will need to wait until after their items are bound.
3054 mSavedStates = container;
3055 }
3056
3057 public void restoreInstanceStateForChild(int child) {
3058 if (mSavedStates != null) {
3059 mRestoredPages.add(child);
3060 CellLayout cl = (CellLayout) getChildAt(child);
Adam Cohenb0ee0812013-12-03 10:51:45 -08003061 if (cl != null) {
3062 cl.restoreInstanceState(mSavedStates);
3063 }
Adam Cohen1462de32012-07-24 22:34:36 -07003064 }
3065 }
3066
3067 public void restoreInstanceStateForRemainingPages() {
3068 int count = getChildCount();
3069 for (int i = 0; i < count; i++) {
3070 if (!mRestoredPages.contains(i)) {
3071 restoreInstanceStateForChild(i);
3072 }
3073 }
3074 mRestoredPages.clear();
Winson Chungd8e596d2013-10-21 17:14:12 -07003075 mSavedStates = null;
Adam Cohen1462de32012-07-24 22:34:36 -07003076 }
3077
3078 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003079 public void scrollLeft() {
Adam Cohen6c5891a2014-07-09 23:53:15 -07003080 if (!workspaceInModalState() && !mIsSwitchingState) {
Michael Jurka0142d492010-08-25 17:46:15 -07003081 super.scrollLeft();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003082 }
Sunny Goyal740ac7f2016-09-28 16:47:32 -07003083 Folder openFolder = Folder.getOpen(mLauncher);
Adam Cohen95bb8002011-07-03 23:40:28 -07003084 if (openFolder != null) {
3085 openFolder.completeDragExit();
3086 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003087 }
3088
Michael Jurka0142d492010-08-25 17:46:15 -07003089 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003090 public void scrollRight() {
Adam Cohen6c5891a2014-07-09 23:53:15 -07003091 if (!workspaceInModalState() && !mIsSwitchingState) {
Michael Jurka0142d492010-08-25 17:46:15 -07003092 super.scrollRight();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003093 }
Sunny Goyal740ac7f2016-09-28 16:47:32 -07003094 Folder openFolder = Folder.getOpen(mLauncher);
Adam Cohen95bb8002011-07-03 23:40:28 -07003095 if (openFolder != null) {
3096 openFolder.completeDragExit();
3097 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003098 }
3099
Winson Chung3d503fb2011-07-13 17:25:49 -07003100 /**
3101 * Returns a specific CellLayout
3102 */
3103 CellLayout getParentCellLayoutForView(View v) {
3104 ArrayList<CellLayout> layouts = getWorkspaceAndHotseatCellLayouts();
3105 for (CellLayout layout : layouts) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07003106 if (layout.getShortcutsAndWidgets().indexOfChild(v) > -1) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003107 return layout;
3108 }
3109 }
3110 return null;
3111 }
3112
3113 /**
3114 * Returns a list of all the CellLayouts in the workspace.
3115 */
3116 ArrayList<CellLayout> getWorkspaceAndHotseatCellLayouts() {
Rajeev Kumar93c05122017-06-12 12:59:47 -07003117 ArrayList<CellLayout> layouts = new ArrayList<>();
Winson Chung3d503fb2011-07-13 17:25:49 -07003118 int screenCount = getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003119 for (int screen = 0; screen < screenCount; screen++) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003120 layouts.add(((CellLayout) getChildAt(screen)));
3121 }
3122 if (mLauncher.getHotseat() != null) {
3123 layouts.add(mLauncher.getHotseat().getLayout());
3124 }
3125 return layouts;
3126 }
3127
3128 /**
3129 * We should only use this to search for specific children. Do not use this method to modify
Michael Jurkaa52570f2012-03-20 03:18:20 -07003130 * ShortcutsAndWidgetsContainer directly. Includes ShortcutAndWidgetContainers from
3131 * the hotseat and workspace pages
Winson Chung3d503fb2011-07-13 17:25:49 -07003132 */
Michael Jurkaa52570f2012-03-20 03:18:20 -07003133 ArrayList<ShortcutAndWidgetContainer> getAllShortcutAndWidgetContainers() {
Sunny Goyal08442b82015-10-21 17:15:08 -07003134 ArrayList<ShortcutAndWidgetContainer> childrenLayouts = new ArrayList<>();
Winson Chung3d503fb2011-07-13 17:25:49 -07003135 int screenCount = getChildCount();
3136 for (int screen = 0; screen < screenCount; screen++) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07003137 childrenLayouts.add(((CellLayout) getChildAt(screen)).getShortcutsAndWidgets());
Winson Chung3d503fb2011-07-13 17:25:49 -07003138 }
3139 if (mLauncher.getHotseat() != null) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07003140 childrenLayouts.add(mLauncher.getHotseat().getLayout().getShortcutsAndWidgets());
Winson Chung3d503fb2011-07-13 17:25:49 -07003141 }
3142 return childrenLayouts;
3143 }
3144
Sunny Goyal317698b2015-07-29 11:45:41 -07003145 public View getHomescreenIconByItemId(final long id) {
3146 return getFirstMatch(new ItemOperator() {
3147
3148 @Override
Sunny Goyalc52ba712016-04-05 15:59:05 -07003149 public boolean evaluate(ItemInfo info, View v) {
Sunny Goyala75053f2015-09-03 09:48:50 -07003150 return info != null && info.id == id;
Sunny Goyal317698b2015-07-29 11:45:41 -07003151 }
3152 });
3153 }
3154
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003155 public View getViewForTag(final Object tag) {
Sunny Goyalff572272014-07-23 13:58:07 -07003156 return getFirstMatch(new ItemOperator() {
3157
3158 @Override
Sunny Goyalc52ba712016-04-05 15:59:05 -07003159 public boolean evaluate(ItemInfo info, View v) {
Sunny Goyalff572272014-07-23 13:58:07 -07003160 return info == tag;
3161 }
3162 });
3163 }
3164
3165 public LauncherAppWidgetHostView getWidgetForAppWidgetId(final int appWidgetId) {
3166 return (LauncherAppWidgetHostView) getFirstMatch(new ItemOperator() {
3167
3168 @Override
Sunny Goyalc52ba712016-04-05 15:59:05 -07003169 public boolean evaluate(ItemInfo info, View v) {
Sunny Goyalff572272014-07-23 13:58:07 -07003170 return (info instanceof LauncherAppWidgetInfo) &&
3171 ((LauncherAppWidgetInfo) info).appWidgetId == appWidgetId;
3172 }
3173 });
3174 }
3175
Sunny Goyal6d02c7a2016-05-19 12:15:39 -07003176 public View getFirstMatch(final ItemOperator operator) {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003177 final View[] value = new View[1];
Sunny Goyal651077b2014-06-30 14:15:31 -07003178 mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003179 @Override
Sunny Goyalc52ba712016-04-05 15:59:05 -07003180 public boolean evaluate(ItemInfo info, View v) {
3181 if (operator.evaluate(info, v)) {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003182 value[0] = v;
3183 return true;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003184 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003185 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003186 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003187 });
3188 return value[0];
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003189 }
3190
Adam Cohendf035382011-04-11 17:22:04 -07003191 void clearDropTargets() {
Sunny Goyal651077b2014-06-30 14:15:31 -07003192 mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003193 @Override
Sunny Goyalc52ba712016-04-05 15:59:05 -07003194 public boolean evaluate(ItemInfo info, View v) {
Adam Cohendf035382011-04-11 17:22:04 -07003195 if (v instanceof DropTarget) {
3196 mDragController.removeDropTarget((DropTarget) v);
3197 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003198 // not done, process all the shortcuts
3199 return false;
Adam Cohendf035382011-04-11 17:22:04 -07003200 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003201 });
Adam Cohendf035382011-04-11 17:22:04 -07003202 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003203
Sunny Goyal4390ace2014-10-13 11:33:11 -07003204 /**
Sunny Goyald3b87ef2016-07-28 12:11:54 -07003205 * Removes items that match the {@param matcher}. When applications are removed
Sunny Goyal4390ace2014-10-13 11:33:11 -07003206 * as a part of an update, this is called to ensure that other widgets and application
3207 * shortcuts are not removed.
3208 */
Sunny Goyald3b87ef2016-07-28 12:11:54 -07003209 public void removeItemsByMatcher(final ItemInfoMatcher matcher) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003210 ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
3211 for (final CellLayout layoutParent: cellLayouts) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07003212 final ViewGroup layout = layoutParent.getShortcutsAndWidgets();
Romain Guy574d20e2009-06-01 15:34:04 -07003213
Sunny Goyal40452cf2016-09-01 15:17:46 -07003214 LongArrayMap<View> idToViewMap = new LongArrayMap<>();
3215 ArrayList<ItemInfo> items = new ArrayList<>();
Winson Chung64359a52013-07-08 17:17:08 -07003216 for (int j = 0; j < layout.getChildCount(); j++) {
3217 final View view = layout.getChildAt(j);
Sunny Goyal40452cf2016-09-01 15:17:46 -07003218 if (view.getTag() instanceof ItemInfo) {
3219 ItemInfo item = (ItemInfo) view.getTag();
3220 items.add(item);
3221 idToViewMap.put(item.id, view);
3222 }
Winson Chung64359a52013-07-08 17:17:08 -07003223 }
Winson Chungaafa03c2010-06-11 17:34:16 -07003224
Sunny Goyal40452cf2016-09-01 15:17:46 -07003225 for (ItemInfo itemToRemove : matcher.filterItemInfos(items)) {
3226 View child = idToViewMap.get(itemToRemove.id);
3227
3228 if (child != null) {
3229 // Note: We can not remove the view directly from CellLayoutChildren as this
3230 // does not re-mark the spaces as unoccupied.
3231 layoutParent.removeViewInLayout(child);
3232 if (child instanceof DropTarget) {
3233 mDragController.removeDropTarget((DropTarget) child);
Romain Guy574d20e2009-06-01 15:34:04 -07003234 }
Sunny Goyal40452cf2016-09-01 15:17:46 -07003235 } else if (itemToRemove.container >= 0) {
3236 // The item may belong to a folder.
3237 View parent = idToViewMap.get(itemToRemove.container);
3238 if (parent != null) {
Jon Miranda608d0db2017-02-28 13:15:28 -08003239 FolderInfo folderInfo = (FolderInfo) parent.getTag();
3240 folderInfo.prepareAutoUpdate();
3241 folderInfo.remove((ShortcutInfo) itemToRemove, false);
Sunny Goyal40452cf2016-09-01 15:17:46 -07003242 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003243 }
Winson Chung64359a52013-07-08 17:17:08 -07003244 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003245 }
Winson Chung2efec4e2012-05-03 12:31:48 -07003246
Winson Chung64359a52013-07-08 17:17:08 -07003247 // Strip all the empty screens
3248 stripEmptyScreens();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003249 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07003250
Adam Cohenf9c184a2016-01-15 16:47:43 -08003251 public interface ItemOperator {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003252 /**
Sunny Goyald0a6ae72016-06-16 12:29:03 -07003253 * Process the next itemInfo, possibly with side-effect on the next item.
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003254 *
3255 * @param info info for the shortcut
3256 * @param view view for the shortcut
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003257 * @return true if done, false to continue the map
3258 */
Rajeev Kumar93c05122017-06-12 12:59:47 -07003259 boolean evaluate(ItemInfo info, View view);
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003260 }
3261
3262 /**
Sunny Goyal651077b2014-06-30 14:15:31 -07003263 * Map the operator over the shortcuts and widgets, return the first-non-null value.
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003264 *
3265 * @param recurse true: iterate over folder children. false: op get the folders themselves.
3266 * @param op the operator to map over the shortcuts
3267 */
Sunny Goyal651077b2014-06-30 14:15:31 -07003268 void mapOverItems(boolean recurse, ItemOperator op) {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003269 ArrayList<ShortcutAndWidgetContainer> containers = getAllShortcutAndWidgetContainers();
3270 final int containerCount = containers.size();
3271 for (int containerIdx = 0; containerIdx < containerCount; containerIdx++) {
3272 ShortcutAndWidgetContainer container = containers.get(containerIdx);
3273 // map over all the shortcuts on the workspace
3274 final int itemCount = container.getChildCount();
3275 for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
3276 View item = container.getChildAt(itemIdx);
3277 ItemInfo info = (ItemInfo) item.getTag();
3278 if (recurse && info instanceof FolderInfo && item instanceof FolderIcon) {
3279 FolderIcon folder = (FolderIcon) item;
Jonathan Miranda8701cd52017-07-13 19:26:46 +00003280 ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003281 // map over all the children in the folder
3282 final int childCount = folderChildren.size();
3283 for (int childIdx = 0; childIdx < childCount; childIdx++) {
3284 View child = folderChildren.get(childIdx);
3285 info = (ItemInfo) child.getTag();
Sunny Goyalc52ba712016-04-05 15:59:05 -07003286 if (op.evaluate(info, child)) {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003287 return;
3288 }
3289 }
3290 } else {
Sunny Goyalc52ba712016-04-05 15:59:05 -07003291 if (op.evaluate(info, item)) {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003292 return;
3293 }
3294 }
3295 }
3296 }
3297 }
3298
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07003299 void updateShortcuts(ArrayList<ShortcutInfo> shortcuts) {
Sunny Goyalc52ba712016-04-05 15:59:05 -07003300 int total = shortcuts.size();
Rajeev Kumar93c05122017-06-12 12:59:47 -07003301 final HashSet<ShortcutInfo> updates = new HashSet<>(total);
Sunny Goyalc52ba712016-04-05 15:59:05 -07003302 final HashSet<Long> folderIds = new HashSet<>();
3303
3304 for (int i = 0; i < total; i++) {
3305 ShortcutInfo s = shortcuts.get(i);
3306 updates.add(s);
3307 folderIds.add(s.container);
3308 }
3309
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07003310 mapOverItems(MAP_RECURSE, new ItemOperator() {
3311 @Override
Sunny Goyalc52ba712016-04-05 15:59:05 -07003312 public boolean evaluate(ItemInfo info, View v) {
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07003313 if (info instanceof ShortcutInfo && v instanceof BubbleTextView &&
3314 updates.contains(info)) {
Sunny Goyal4390ace2014-10-13 11:33:11 -07003315 ShortcutInfo si = (ShortcutInfo) info;
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07003316 BubbleTextView shortcut = (BubbleTextView) v;
Hyunyoung Song1322f9c2017-06-23 14:57:38 -07003317 Drawable oldIcon = shortcut.getIcon();
Sunny Goyal3fcab662015-08-11 15:17:30 -07003318 boolean oldPromiseState = (oldIcon instanceof PreloadIconDrawable)
3319 && ((PreloadIconDrawable) oldIcon).hasNotCompleted();
Sunny Goyal1cd01b02016-11-09 10:43:58 -08003320 shortcut.applyFromShortcutInfo(si, si.isPromise() != oldPromiseState);
Sunny Goyalc52ba712016-04-05 15:59:05 -07003321 }
3322 // process all the shortcuts
3323 return false;
3324 }
3325 });
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07003326
Sunny Goyalc52ba712016-04-05 15:59:05 -07003327 // Update folder icons
3328 mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
3329 @Override
3330 public boolean evaluate(ItemInfo info, View v) {
3331 if (info instanceof FolderInfo && folderIds.contains(info.id)) {
3332 ((FolderInfo) info).itemsChanged(false);
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07003333 }
3334 // process all the shortcuts
3335 return false;
3336 }
3337 });
3338 }
3339
Tony Wickham010d2552017-01-20 08:15:28 -08003340 public void updateIconBadges(final Set<PackageUserKey> updatedBadges) {
3341 final PackageUserKey packageUserKey = new PackageUserKey(null, null);
Tony Wickham11ba5072017-02-02 12:42:50 -08003342 final HashSet<Long> folderIds = new HashSet<>();
Tony Wickham010d2552017-01-20 08:15:28 -08003343 mapOverItems(MAP_RECURSE, new ItemOperator() {
3344 @Override
3345 public boolean evaluate(ItemInfo info, View v) {
Tony Wickham18a1b5d2017-02-02 13:58:33 -08003346 if (info instanceof ShortcutInfo && v instanceof BubbleTextView
3347 && packageUserKey.updateFromItemInfo(info)) {
Tony Wickham010d2552017-01-20 08:15:28 -08003348 if (updatedBadges.contains(packageUserKey)) {
Tony Wickham1e618492017-02-02 12:57:18 -08003349 ((BubbleTextView) v).applyBadgeState(info, true /* animate */);
Tony Wickham11ba5072017-02-02 12:42:50 -08003350 folderIds.add(info.container);
Tony Wickham010d2552017-01-20 08:15:28 -08003351 }
3352 }
3353 // process all the shortcuts
3354 return false;
3355 }
3356 });
Tony Wickham11ba5072017-02-02 12:42:50 -08003357
3358 // Update folder icons
3359 mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
3360 @Override
3361 public boolean evaluate(ItemInfo info, View v) {
3362 if (info instanceof FolderInfo && folderIds.contains(info.id)
3363 && v instanceof FolderIcon) {
3364 FolderBadgeInfo folderBadgeInfo = new FolderBadgeInfo();
3365 for (ShortcutInfo si : ((FolderInfo) info).contents) {
3366 folderBadgeInfo.addBadgeInfo(mLauncher.getPopupDataProvider()
3367 .getBadgeInfoForItem(si));
3368 }
3369 ((FolderIcon) v).setBadgeInfo(folderBadgeInfo);
3370 }
3371 // process all the shortcuts
3372 return false;
3373 }
3374 });
Tony Wickham010d2552017-01-20 08:15:28 -08003375 }
3376
Sunny Goyal7c74e4a2016-12-15 15:53:17 -08003377 public void removeAbandonedPromise(String packageName, UserHandle user) {
Sunny Goyal3bbbabc2016-03-15 09:16:30 -07003378 HashSet<String> packages = new HashSet<>(1);
Sunny Goyale7b8cd92014-08-27 14:04:33 -07003379 packages.add(packageName);
Sunny Goyal40452cf2016-09-01 15:17:46 -07003380 ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packages, user);
Sunny Goyal43bf11d2017-02-02 13:52:53 -08003381 mLauncher.getModelWriter().deleteItemsFromDatabase(matcher);
Sunny Goyal40452cf2016-09-01 15:17:46 -07003382 removeItemsByMatcher(matcher);
Chris Wren40c5ed32014-06-24 18:24:23 -04003383 }
3384
Sunny Goyal756adbc2015-04-16 15:20:51 -07003385 public void updateRestoreItems(final HashSet<ItemInfo> updates) {
Sunny Goyala22666f2014-09-18 13:25:15 -07003386 mapOverItems(MAP_RECURSE, new ItemOperator() {
3387 @Override
Sunny Goyalc52ba712016-04-05 15:59:05 -07003388 public boolean evaluate(ItemInfo info, View v) {
Sunny Goyal756adbc2015-04-16 15:20:51 -07003389 if (info instanceof ShortcutInfo && v instanceof BubbleTextView
3390 && updates.contains(info)) {
Tony Wickham9a8d11f2017-01-11 09:53:12 -08003391 ((BubbleTextView) v).applyPromiseState(false /* promiseStateChanged */);
Sunny Goyal756adbc2015-04-16 15:20:51 -07003392 } else if (v instanceof PendingAppWidgetHostView
3393 && info instanceof LauncherAppWidgetInfo
3394 && updates.contains(info)) {
3395 ((PendingAppWidgetHostView) v).applyState();
Sunny Goyala22666f2014-09-18 13:25:15 -07003396 }
3397 // process all the shortcuts
3398 return false;
3399 }
3400 });
3401 }
3402
Sunny Goyal87af0fd2016-05-16 14:56:02 -07003403 public void widgetsRestored(final ArrayList<LauncherAppWidgetInfo> changedInfo) {
Sunny Goyal0fc1be12014-08-11 17:05:23 -07003404 if (!changedInfo.isEmpty()) {
3405 DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo,
3406 mLauncher.getAppWidgetHost());
Sunny Goyal2e1efb42016-03-03 16:58:55 -08003407
3408 LauncherAppWidgetInfo item = changedInfo.get(0);
3409 final AppWidgetProviderInfo widgetInfo;
3410 if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
3411 widgetInfo = AppWidgetManagerCompat
3412 .getInstance(mLauncher).findProvider(item.providerName, item.user);
3413 } else {
3414 widgetInfo = AppWidgetManagerCompat.getInstance(mLauncher)
Sunny Goyal952e63d2017-08-16 04:59:08 -07003415 .getLauncherAppWidgetInfo(item.appWidgetId);
Sunny Goyal2e1efb42016-03-03 16:58:55 -08003416 }
3417
3418 if (widgetInfo != null) {
Sunny Goyal0fc1be12014-08-11 17:05:23 -07003419 // Re-inflate the widgets which have changed status
3420 widgetRefresh.run();
3421 } else {
3422 // widgetRefresh will automatically run when the packages are updated.
Sunny Goyal4390ace2014-10-13 11:33:11 -07003423 // For now just update the progress bars
Sunny Goyal87af0fd2016-05-16 14:56:02 -07003424 mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
3425 @Override
3426 public boolean evaluate(ItemInfo info, View view) {
3427 if (view instanceof PendingAppWidgetHostView
3428 && changedInfo.contains(info)) {
3429 ((LauncherAppWidgetInfo) info).installProgress = 100;
3430 ((PendingAppWidgetHostView) view).applyState();
3431 }
3432 // process all the shortcuts
3433 return false;
Sunny Goyal4390ace2014-10-13 11:33:11 -07003434 }
Sunny Goyal87af0fd2016-05-16 14:56:02 -07003435 });
Sunny Goyal0fc1be12014-08-11 17:05:23 -07003436 }
Sunny Goyalff572272014-07-23 13:58:07 -07003437 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07003438 }
3439
Sunny Goyal7ce471b2017-08-02 03:37:39 -07003440 void moveToDefaultScreen() {
3441 int page = DEFAULT_PAGE;
Sunny Goyalc210f5b2017-07-10 20:46:53 -07003442 if (!workspaceInModalState() && getNextPage() != page) {
Sunny Goyal7ce471b2017-08-02 03:37:39 -07003443 snapToPage(page);
Joe Onoratoc45b1682010-01-11 18:48:40 -05003444 }
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01003445 View child = getChildAt(page);
Winson Chung64359a52013-07-08 17:17:08 -07003446 if (child != null) {
3447 child.requestFocus();
3448 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003449 }
3450
Sunny Goyald0a6ae72016-06-16 12:29:03 -07003451 @Override
Sunny Goyal53fe1f22016-07-08 08:47:07 -07003452 protected String getPageIndicatorDescription() {
3453 return getResources().getString(R.string.all_apps_button_label);
3454 }
3455
3456 @Override
Winson Chung6a0f57d2011-06-29 20:10:49 -07003457 protected String getCurrentPageDescription() {
Sunny Goyal1d08f702015-05-04 15:50:25 -07003458 int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
3459 return getPageDescription(page);
3460 }
3461
3462 private String getPageDescription(int page) {
Sunny Goyal7ce471b2017-08-02 03:37:39 -07003463 int nScreens = getChildCount();
Sunny Goyale78e3d72015-09-24 11:23:31 -07003464 int extraScreenId = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
3465 if (extraScreenId >= 0 && nScreens > 1) {
3466 if (page == extraScreenId) {
3467 return getContext().getString(R.string.workspace_new_page);
3468 }
3469 nScreens--;
3470 }
Sunny Goyal5be3e4c2016-04-12 16:09:52 -07003471 if (nScreens == 0) {
3472 // When the workspace is not loaded, we do not know how many screen will be bound.
3473 return getContext().getString(R.string.all_apps_home_button_label);
3474 }
Sunny Goyal7ce471b2017-08-02 03:37:39 -07003475 return getContext().getString(R.string.workspace_scroll_format, page + 1, nScreens);
Winson Chung6a0f57d2011-06-29 20:10:49 -07003476 }
Adam Cohen8dfcba42011-07-07 16:38:18 -07003477
Winson Chung8f1eff72015-05-28 17:33:40 -07003478 @Override
Hyunyoung Songdf7ef682016-10-06 17:52:22 -07003479 public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
Hyunyoung Songddec1c72016-04-12 18:32:04 -07003480 target.gridX = info.cellX;
3481 target.gridY = info.cellY;
3482 target.pageIndex = getCurrentPage();
Sunny Goyal6c46a6d2016-11-23 02:24:32 +05303483 targetParent.containerType = ContainerType.WORKSPACE;
Hyunyoung Song59a23802016-09-01 12:47:12 -07003484 if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
3485 target.rank = info.rank;
Sunny Goyal6c46a6d2016-11-23 02:24:32 +05303486 targetParent.containerType = ContainerType.HOTSEAT;
Hyunyoung Song59a23802016-09-01 12:47:12 -07003487 } else if (info.container >= 0) {
Sunny Goyal6c46a6d2016-11-23 02:24:32 +05303488 targetParent.containerType = ContainerType.FOLDER;
Hyunyoung Song59a23802016-09-01 12:47:12 -07003489 }
Winson Chung8f1eff72015-05-28 17:33:40 -07003490 }
3491
Sunny Goyal0fc1be12014-08-11 17:05:23 -07003492 /**
3493 * Used as a workaround to ensure that the AppWidgetService receives the
3494 * PACKAGE_ADDED broadcast before updating widgets.
3495 */
Sunny Goyal64a75aa2017-07-03 13:50:52 -07003496 private class DeferredWidgetRefresh implements Runnable, ProviderChangedListener {
Sunny Goyal0fc1be12014-08-11 17:05:23 -07003497 private final ArrayList<LauncherAppWidgetInfo> mInfos;
3498 private final LauncherAppWidgetHost mHost;
3499 private final Handler mHandler;
3500
3501 private boolean mRefreshPending;
3502
Rajeev Kumar11c7bc62017-06-15 18:18:17 -07003503 DeferredWidgetRefresh(ArrayList<LauncherAppWidgetInfo> infos,
3504 LauncherAppWidgetHost host) {
Sunny Goyal0fc1be12014-08-11 17:05:23 -07003505 mInfos = infos;
3506 mHost = host;
3507 mHandler = new Handler();
3508 mRefreshPending = true;
3509
3510 mHost.addProviderChangeListener(this);
3511 // Force refresh after 10 seconds, if we don't get the provider changed event.
3512 // This could happen when the provider is no longer available in the app.
3513 mHandler.postDelayed(this, 10000);
3514 }
3515
3516 @Override
3517 public void run() {
3518 mHost.removeProviderChangeListener(this);
3519 mHandler.removeCallbacks(this);
3520
3521 if (!mRefreshPending) {
3522 return;
3523 }
3524
3525 mRefreshPending = false;
3526
Sunny Goyal87af0fd2016-05-16 14:56:02 -07003527 mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
3528 @Override
3529 public boolean evaluate(ItemInfo info, View view) {
3530 if (view instanceof PendingAppWidgetHostView && mInfos.contains(info)) {
Sunny Goyal87af0fd2016-05-16 14:56:02 -07003531 mLauncher.removeItem(view, info, false /* deleteFromDb */);
3532 mLauncher.bindAppWidget((LauncherAppWidgetInfo) info);
3533 }
3534 // process all the shortcuts
3535 return false;
Sunny Goyal0fc1be12014-08-11 17:05:23 -07003536 }
Sunny Goyal87af0fd2016-05-16 14:56:02 -07003537 });
Sunny Goyal0fc1be12014-08-11 17:05:23 -07003538 }
Sunny Goyal64a75aa2017-07-03 13:50:52 -07003539
3540 @Override
3541 public void notifyWidgetProvidersChanged() {
3542 run();
3543 }
Sunny Goyal0fc1be12014-08-11 17:05:23 -07003544 }
Sunny Goyal9443ef52016-06-07 16:21:03 -07003545
Sunny Goyaldb364372016-10-26 19:12:47 -07003546 private class StateTransitionListener extends AnimatorListenerAdapter
3547 implements AnimatorUpdateListener {
Sunny Goyalc99cb172017-10-19 16:15:09 -07003548
3549 private final LauncherState mToState;
3550
3551 StateTransitionListener(LauncherState toState) {
3552 mToState = toState;
3553 }
3554
Sunny Goyaldb364372016-10-26 19:12:47 -07003555 @Override
3556 public void onAnimationUpdate(ValueAnimator anim) {
3557 mTransitionProgress = anim.getAnimatedFraction();
3558 }
3559
3560 @Override
3561 public void onAnimationStart(Animator animation) {
Sunny Goyalc99cb172017-10-19 16:15:09 -07003562 onStartStateTransition(mToState);
Sunny Goyaldb364372016-10-26 19:12:47 -07003563 }
3564
3565 @Override
3566 public void onAnimationEnd(Animator animation) {
3567 onEndStateTransition();
3568 }
3569 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003570}