blob: 720a692361137b061243313e823f2939f120ef63 [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;
Jon Miranda43348742019-03-06 10:40:13 -080022import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
23import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
24import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
Sunny Goyal3e3f44c2017-10-23 17:14:52 -070025import static com.android.launcher3.LauncherState.ALL_APPS;
26import static com.android.launcher3.LauncherState.NORMAL;
27import static com.android.launcher3.LauncherState.SPRING_LOADED;
Jon Miranda43348742019-03-06 10:40:13 -080028import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
Sunny Goyal6001ea22018-05-10 16:31:00 -070029import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_OVERLAY;
Sunny Goyalaeb16432017-10-16 11:46:41 -070030
Michael Jurka0280c3b2010-09-17 15:00:07 -070031import android.animation.Animator;
Adam Cohenf358a4b2013-07-23 16:47:31 -070032import android.animation.AnimatorListenerAdapter;
Adam Cohen22cba7f2013-07-19 16:14:00 -070033import android.animation.LayoutTransition;
Michael Jurka0280c3b2010-09-17 15:00:07 -070034import android.animation.ObjectAnimator;
Sunny Goyal6d64d882016-06-24 09:57:04 -070035import android.animation.ValueAnimator;
36import android.animation.ValueAnimator.AnimatorUpdateListener;
Sunny Goyal70660032015-05-14 00:07:08 -070037import android.annotation.SuppressLint;
Dianne Hackborn8f573952009-08-10 23:21:09 -070038import android.app.WallpaperManager;
Michael Jurkabed61d22012-02-14 22:51:29 -080039import android.appwidget.AppWidgetHostView;
Romain Guy629de3e2010-01-13 12:20:59 -080040import android.appwidget.AppWidgetProviderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080041import android.content.Context;
Patrick Dubroy7247f632010-08-04 16:02:59 -070042import android.content.res.Resources;
Joe Onorato4be866d2010-10-10 11:26:02 -070043import android.graphics.Bitmap;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080044import android.graphics.Canvas;
Winson Chungb8c69f32011-10-19 21:36:08 -070045import android.graphics.Point;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080046import android.graphics.Rect;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070047import android.graphics.drawable.Drawable;
Sunny Goyal0fc1be12014-08-11 17:05:23 -070048import android.os.Handler;
Joe Onorato956091b2010-02-19 12:47:40 -080049import android.os.IBinder;
Sunny Goyal2db53422019-03-01 16:06:12 -080050import android.os.Message;
Adam Powell495f2892010-04-16 16:40:55 -070051import android.os.Parcelable;
Sunny Goyal7c74e4a2016-12-15 15:53:17 -080052import android.os.UserHandle;
Jon Miranda43348742019-03-06 10:40:13 -080053import android.text.TextUtils;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080054import android.util.AttributeSet;
Daniel Sandler291ad122010-05-24 16:03:53 -040055import android.util.Log;
Adam Cohen1462de32012-07-24 22:34:36 -070056import android.util.SparseArray;
Sunny Goyal9b29ca52017-02-17 10:39:44 -080057import android.view.LayoutInflater;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080058import android.view.MotionEvent;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080059import android.view.View;
Winson Chung6e314082011-01-27 16:46:51 -080060import android.view.ViewGroup;
Tony Wickham618a5042018-02-12 15:59:15 -080061import android.view.ViewTreeObserver;
Vadim Tryshev7acde482018-05-31 14:33:09 -070062import android.view.accessibility.AccessibilityNodeInfo;
Jon Miranda9485e5f2016-10-18 11:47:42 -070063import android.widget.Toast;
Sunny Goyal64a75aa2017-07-03 13:50:52 -070064
Adam Cohenc2d6e892014-10-16 09:49:24 -070065import com.android.launcher3.Launcher.LauncherOverlay;
Sunny Goyal64a75aa2017-07-03 13:50:52 -070066import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
Sunny Goyal3e3f44c2017-10-23 17:14:52 -070067import com.android.launcher3.LauncherStateManager.AnimationConfig;
Hyunyoung Song0de01172016-10-05 16:27:48 -070068import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
Sunny Goyale78e3d72015-09-24 11:23:31 -070069import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
Sunny Goyale3c6d582017-12-07 12:45:49 -080070import com.android.launcher3.anim.AnimatorSetBuilder;
Sunny Goyal5bc6b6f2017-10-26 15:36:10 -070071import com.android.launcher3.anim.Interpolators;
Tony Wickhamf34bee82018-12-03 18:11:39 -080072import com.android.launcher3.dot.FolderDotInfo;
Sunny Goyal470f9002016-03-21 13:02:29 -070073import com.android.launcher3.compat.AppWidgetManagerCompat;
Tony Wickhame0c33232016-02-08 11:37:04 -080074import com.android.launcher3.config.FeatureFlags;
Vadim Tryshevfedca432015-08-19 17:55:02 -070075import com.android.launcher3.dragndrop.DragController;
76import com.android.launcher3.dragndrop.DragLayer;
Sunny Goyal94b510c2016-08-16 15:36:48 -070077import com.android.launcher3.dragndrop.DragOptions;
Vadim Tryshevfedca432015-08-19 17:55:02 -070078import com.android.launcher3.dragndrop.DragView;
79import com.android.launcher3.dragndrop.SpringLoadedDragController;
Sunny Goyal470f9002016-03-21 13:02:29 -070080import com.android.launcher3.folder.Folder;
81import com.android.launcher3.folder.FolderIcon;
Jon Mirandaa0233f72017-06-22 18:34:45 -070082import com.android.launcher3.folder.PreviewBackground;
Sunny Goyal06e21a22016-08-11 16:02:02 -070083import com.android.launcher3.graphics.DragPreviewProvider;
Sunny Goyal96ac68a2017-02-02 16:37:21 -080084import com.android.launcher3.graphics.PreloadIconDrawable;
Sunny Goyal0bd7f4f2018-02-01 09:53:35 -080085import com.android.launcher3.pageindicators.WorkspacePageIndicator;
Tony Wickham540913e2017-01-23 11:47:51 -080086import com.android.launcher3.popup.PopupContainerWithArrow;
Jon Mirandaaa6f66a2017-05-09 12:43:44 -070087import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
Sunny Goyal7c7be8c2018-03-07 19:58:07 -080088import com.android.launcher3.touch.WorkspaceTouchListener;
Hyunyoung Song3f5a11f2017-06-29 16:42:04 -070089import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
Sunny Goyal6c46a6d2016-11-23 02:24:32 +053090import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
Hyunyoung Songddec1c72016-04-12 18:32:04 -070091import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
Sunny Goyalefb7e842018-10-04 15:11:00 -070092import com.android.launcher3.util.IntArray;
Sunny Goyalefb7e842018-10-04 15:11:00 -070093import com.android.launcher3.util.IntSet;
Sunny Goyal876e4622018-11-02 13:50:40 -070094import com.android.launcher3.util.IntSparseArrayMap;
Sunny Goyald3b87ef2016-07-28 12:11:54 -070095import com.android.launcher3.util.ItemInfoMatcher;
Tony Wickham010d2552017-01-20 08:15:28 -080096import com.android.launcher3.util.PackageUserKey;
Adam Cohen091440a2015-03-18 14:16:05 -070097import com.android.launcher3.util.Thunk;
Sunny Goyal9eba1fd2015-10-16 08:58:57 -070098import com.android.launcher3.util.WallpaperOffsetInterpolator;
Sunny Goyal29947f02017-12-18 13:49:44 -080099import com.android.launcher3.widget.LauncherAppWidgetHostView;
Hyunyoung Song3f471442015-04-08 19:01:34 -0700100import com.android.launcher3.widget.PendingAddShortcutInfo;
101import com.android.launcher3.widget.PendingAddWidgetInfo;
Sunny Goyal29947f02017-12-18 13:49:44 -0800102import com.android.launcher3.widget.PendingAppWidgetHostView;
Sunny Goyal64a75aa2017-07-03 13:50:52 -0700103
Adam Cohen716b51e2011-06-30 12:09:54 -0700104import java.util.ArrayList;
105import java.util.HashSet;
Sunny Goyalaae6fbb2019-01-31 16:05:58 -0800106import java.util.function.Predicate;
Adam Cohen716b51e2011-06-30 12:09:54 -0700107
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800108/**
Michael Jurka0142d492010-08-25 17:46:15 -0700109 * The workspace is a wide area with a wallpaper and a finite number of pages.
110 * Each page contains a number of icons, folders or widgets the user can
Winson Chungaafa03c2010-06-11 17:34:16 -0700111 * interact with. A workspace is meant to be used with a fixed width only.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800112 */
Sunny Goyal0bd7f4f2018-02-01 09:53:35 -0800113public class Workspace extends PagedView<WorkspacePageIndicator>
Sunny Goyalf5440cb2016-12-14 15:13:00 -0800114 implements DropTarget, DragSource, View.OnTouchListener,
Sunny Goyalef92b822018-11-21 14:12:00 -0800115 DragController.DragListener, Insettable, LauncherStateManager.StateHandler,
116 WorkspaceLayoutManager {
Michael Jurka0142d492010-08-25 17:46:15 -0700117
Tony Wickham251ab6e2016-12-02 18:54:32 -0800118 /** The value that {@link #mTransitionProgress} must be greater than for
119 * {@link #transitionStateShouldAllowDrop()} to return true. */
120 private static final float ALLOW_DROP_TRANSITION_PROGRESS = 0.25f;
121
122 /** The value that {@link #mTransitionProgress} must be greater than for
123 * {@link #isFinishedSwitchingState()} ()} to return true. */
124 private static final float FINISHED_SWITCHING_STATE_TRANSITION_PROGRESS = 0.5f;
125
Rajeev Kumar93c05122017-06-12 12:59:47 -0700126 private static final boolean ENFORCE_DRAG_EVENT_ORDER = false;
Sunny Goyal58376922015-05-26 18:56:52 -0700127
Sunny Goyalcf25b522015-07-09 00:01:18 -0700128 private static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400;
129 private static final int FADE_EMPTY_SCREEN_DURATION = 150;
Adam Cohenad4e15c2013-10-17 16:21:35 -0700130
Adam Cohened51cc92011-08-01 20:28:08 -0700131 private static final int ADJACENT_SCREEN_DROP_DURATION = 300;
132
Sunny Goyal7ce471b2017-08-02 03:37:39 -0700133 private static final int DEFAULT_PAGE = 0;
134
Sunny Goyald0ae4922018-10-11 10:37:53 -0700135 public static final boolean MAP_NO_RECURSE = false;
136 public static final boolean MAP_RECURSE = true;
Sunny Goyalcf25b522015-07-09 00:01:18 -0700137
Adam Cohen22cba7f2013-07-19 16:14:00 -0700138 private LayoutTransition mLayoutTransition;
Adam Cohen091440a2015-03-18 14:16:05 -0700139 @Thunk final WallpaperManager mWallpaperManager;
Winson Chungaafa03c2010-06-11 17:34:16 -0700140
Adam Cohen5084cba2013-09-03 12:01:16 -0700141 private ShortcutAndWidgetContainer mDragSourceInternal;
142
Sunny Goyalefb7e842018-10-04 15:11:00 -0700143 @Thunk final IntSparseArrayMap<CellLayout> mWorkspaceScreens = new IntSparseArrayMap<>();
144 @Thunk final IntArray mScreenOrder = new IntArray();
Adam Cohendcd297f2013-06-18 13:13:40 -0700145
Adam Cohen091440a2015-03-18 14:16:05 -0700146 @Thunk Runnable mRemoveEmptyScreenRunnable;
147 @Thunk boolean mDeferRemoveExtraEmptyScreen = false;
Adam Cohenad4e15c2013-10-17 16:21:35 -0700148
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800149 /**
150 * CellInfo for the cell that is currently being dragged
151 */
152 private CellLayout.CellInfo mDragInfo;
Winson Chungaafa03c2010-06-11 17:34:16 -0700153
Jeff Sharkey70864282009-04-07 21:08:40 -0700154 /**
155 * Target drop area calculated during last acceptDrop call.
156 */
Adam Cohen091440a2015-03-18 14:16:05 -0700157 @Thunk int[] mTargetCell = new int[2];
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700158 private int mDragOverX = -1;
159 private int mDragOverY = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800160
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700161 /**
162 * The CellLayout that is currently being dragged over
163 */
Adam Cohen091440a2015-03-18 14:16:05 -0700164 @Thunk CellLayout mDragTargetLayout = null;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700165 /**
Tony Wickham95cdb3a2016-02-18 14:37:07 -0800166 * The CellLayout that we will show as highlighted
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700167 */
168 private CellLayout mDragOverlappingLayout = null;
169
170 /**
171 * The CellLayout which will be dropped to
172 */
173 private CellLayout mDropToLayout = null;
174
Rajeev Kumar93c05122017-06-12 12:59:47 -0700175 @Thunk final Launcher mLauncher;
Adam Cohen091440a2015-03-18 14:16:05 -0700176 @Thunk DragController mDragController;
Winson Chungaafa03c2010-06-11 17:34:16 -0700177
Sunny Goyalcf25b522015-07-09 00:01:18 -0700178 private final int[] mTempXY = new int[2];
Adam Cohen091440a2015-03-18 14:16:05 -0700179 @Thunk float[] mDragViewVisualCenter = new float[2];
Rajeev Kumar93c05122017-06-12 12:59:47 -0700180 private final float[] mTempTouchCoordinates = new float[2];
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800181
Michael Jurkac2f7f472010-12-14 15:34:42 -0800182 private SpringLoadedDragController mSpringLoadedDragController;
Michael Jurkad3ef3062010-11-23 16:23:58 -0800183
Michael Jurkad74c9842011-07-10 12:44:21 -0700184 private boolean mIsSwitchingState = false;
Michael Jurkad74c9842011-07-10 12:44:21 -0700185
Michael Jurkad74c9842011-07-10 12:44:21 -0700186 boolean mChildrenLayersEnabled = true;
Michael Jurkadee05892010-07-27 10:01:56 -0700187
Adam Cohenaccfd562013-07-12 14:40:40 -0700188 private boolean mStripScreensOnPageStopMoving = false;
189
Sunny Goyal06e21a22016-08-11 16:02:02 -0700190 private DragPreviewProvider mOutlineProvider = null;
Sunny Goyal12069e62018-01-16 14:17:20 -0800191 private boolean mWorkspaceFadeInAdjacentScreens;
Joe Onorato4be866d2010-10-10 11:26:02 -0700192
Sunny Goyal9eba1fd2015-10-16 08:58:57 -0700193 final WallpaperOffsetInterpolator mWallpaperOffset;
Winsonc7d2e832016-07-28 12:24:55 -0700194 private boolean mUnlockWallpaperFromDefaultPageOnLayout;
Michael Jurka2a4f4922014-01-29 16:32:39 +0100195
Adam Cohen19072da2011-05-31 14:30:45 -0700196 // Variables relating to the creation of user folders by hovering shortcuts over shortcuts
Adam Cohen482ed822012-03-02 14:15:13 -0800197 private static final int FOLDER_CREATION_TIMEOUT = 0;
Jon Miranda217f0ca2018-05-02 11:57:27 -0700198 public static final int REORDER_TIMEOUT = 650;
Adam Cohen19072da2011-05-31 14:30:45 -0700199 private final Alarm mFolderCreationAlarm = new Alarm();
Adam Cohen482ed822012-03-02 14:15:13 -0800200 private final Alarm mReorderAlarm = new Alarm();
Jon Mirandaa0233f72017-06-22 18:34:45 -0700201 private PreviewBackground mFolderCreateBg;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700202 private FolderIcon mDragOverFolderIcon = null;
Adam Cohen19072da2011-05-31 14:30:45 -0700203 private boolean mCreateUserFolderOnDrop = false;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700204 private boolean mAddToExistingFolderOnDrop = false;
Adam Cohen3aff81c2012-05-16 21:01:01 -0700205 private float mMaxDistanceForFolderCreation;
Adam Cohen073a46f2011-05-17 16:28:09 -0700206
Adam Cohenf8d28232011-02-01 21:47:00 -0800207 // Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget)
208 private float mXDown;
209 private float mYDown;
210 final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
211 final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
212 final static float TOUCH_SLOP_DAMPING_FACTOR = 4;
213
Adam Cohened66b2b2012-01-23 17:28:51 -0800214 // Relating to the animation of items being dropped externally
Adam Cohend41fbf52012-02-16 23:53:59 -0800215 public static final int ANIMATE_INTO_POSITION_AND_DISAPPEAR = 0;
216 public static final int ANIMATE_INTO_POSITION_AND_REMAIN = 1;
217 public static final int ANIMATE_INTO_POSITION_AND_RESIZE = 2;
218 public static final int COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION = 3;
219 public static final int CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION = 4;
Adam Cohened66b2b2012-01-23 17:28:51 -0800220
Adam Cohen482ed822012-03-02 14:15:13 -0800221 // Related to dragging, folder creation and reordering
222 private static final int DRAG_MODE_NONE = 0;
223 private static final int DRAG_MODE_CREATE_FOLDER = 1;
224 private static final int DRAG_MODE_ADD_TO_FOLDER = 2;
225 private static final int DRAG_MODE_REORDER = 3;
226 private int mDragMode = DRAG_MODE_NONE;
Adam Cohen091440a2015-03-18 14:16:05 -0700227 @Thunk int mLastReorderX = -1;
228 @Thunk int mLastReorderY = -1;
Adam Cohen482ed822012-03-02 14:15:13 -0800229
Adam Cohen1462de32012-07-24 22:34:36 -0700230 private SparseArray<Parcelable> mSavedStates;
Sunny Goyalefb7e842018-10-04 15:11:00 -0700231 private final IntArray mRestoredPages = new IntArray();
Adam Cohen1462de32012-07-24 22:34:36 -0700232
Adam Cohen7d30a372013-07-01 17:03:59 -0700233 private float mCurrentScale;
Winson Chung70442722012-02-10 15:43:22 -0800234 private float mTransitionProgress;
Adam Cohen4b285c52011-07-21 14:24:06 -0700235
Adam Cohenc2d6e892014-10-16 09:49:24 -0700236 // State related to Launcher Overlay
237 LauncherOverlay mLauncherOverlay;
238 boolean mScrollInteractionBegan;
239 boolean mStartedSendingScrollEvents;
Hyunyoung Song0de01172016-10-05 16:27:48 -0700240 float mLastOverlayScroll = 0;
Hyunyoung Song3f5a11f2017-06-29 16:42:04 -0700241 boolean mOverlayShown = false;
Tony Wickham618a5042018-02-12 15:59:15 -0800242 private Runnable mOnOverlayHiddenCallback;
Adam Cohen8d769d62017-06-23 17:27:38 -0700243
Tony Wickham29d853c2015-09-08 10:35:56 -0700244 private boolean mForceDrawAdjacentPages = false;
Sunny Goyalc99cb172017-10-19 16:15:09 -0700245
Sunny Goyalc86df472016-02-25 09:19:38 -0800246 // Total over scrollX in the overlay direction.
247 private float mOverlayTranslation;
Adam Cohenc2d6e892014-10-16 09:49:24 -0700248
Winson Chungdc61c4d2015-04-20 18:26:57 -0700249 // Handles workspace state transitions
Rajeev Kumar93c05122017-06-12 12:59:47 -0700250 private final WorkspaceStateTransitionAnimation mStateTransitionAnimation;
Winson Chungdc61c4d2015-04-20 18:26:57 -0700251
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800252 /**
253 * Used to inflate the Workspace from XML.
254 *
255 * @param context The application's context.
Michael Jurka0142d492010-08-25 17:46:15 -0700256 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800257 */
258 public Workspace(Context context, AttributeSet attrs) {
259 this(context, attrs, 0);
260 }
261
262 /**
263 * Used to inflate the Workspace from XML.
264 *
265 * @param context The application's context.
Michael Jurka0142d492010-08-25 17:46:15 -0700266 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800267 * @param defStyle Unused.
268 */
269 public Workspace(Context context, AttributeSet attrs, int defStyle) {
270 super(context, attrs, defStyle);
Michael Jurka5f1c5092010-09-03 14:15:02 -0700271
Tony2fd02082016-10-07 12:50:01 -0700272 mLauncher = Launcher.getLauncher(context);
Winson Chungdc61c4d2015-04-20 18:26:57 -0700273 mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this);
Dianne Hackborn8f573952009-08-10 23:21:09 -0700274 mWallpaperManager = WallpaperManager.getInstance(context);
Winson Chungaafa03c2010-06-11 17:34:16 -0700275
Sunny Goyal9eba1fd2015-10-16 08:58:57 -0700276 mWallpaperOffset = new WallpaperOffsetInterpolator(this);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800277
Joe Onorato0d44e942009-11-16 18:20:51 -0800278 setHapticFeedbackEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800279 initWorkspace();
Winson Chungc35afb22011-02-23 13:01:49 -0800280
281 // Disable multitouch across the workspace/all apps/customize tray
282 setMotionEventSplittingEnabled(true);
Sunny Goyal7c7be8c2018-03-07 19:58:07 -0800283 setOnTouchListener(new WorkspaceTouchListener(mLauncher, this));
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800284 }
285
John Spurlock77e1f472013-09-11 10:09:51 -0400286 @Override
287 public void setInsets(Rect insets) {
288 mInsets.set(insets);
Sunny Goyal07b69292018-01-08 14:19:34 -0800289
290 DeviceProfile grid = mLauncher.getDeviceProfile();
Jon Miranda5aab8702018-07-17 13:49:12 -0700291 mMaxDistanceForFolderCreation = grid.isTablet
292 ? 0.75f * grid.iconSizePx
293 : 0.55f * grid.iconSizePx;
Sunny Goyal12069e62018-01-16 14:17:20 -0800294 mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
295
Sunny Goyal07b69292018-01-08 14:19:34 -0800296 Rect padding = grid.workspacePadding;
297 setPadding(padding.left, padding.top, padding.right, padding.bottom);
298
299 if (grid.shouldFadeAdjacentWorkspaceScreens()) {
300 // In landscape mode the page spacing is set to the default.
301 setPageSpacing(grid.defaultPageSpacingPx);
302 } else {
303 // In portrait, we want the pages spaced such that there is no
304 // overhang of the previous / next page into the current page viewport.
305 // We assume symmetrical padding in portrait mode.
306 setPageSpacing(Math.max(grid.defaultPageSpacingPx, padding.left + 1));
307 }
Sunny Goyal12069e62018-01-16 14:17:20 -0800308
309 int paddingLeftRight = grid.cellLayoutPaddingLeftRightPx;
310 int paddingBottom = grid.cellLayoutBottomPaddingPx;
311 for (int i = mWorkspaceScreens.size() - 1; i >= 0; i--) {
312 mWorkspaceScreens.valueAt(i)
313 .setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
314 }
John Spurlock77e1f472013-09-11 10:09:51 -0400315 }
316
Jon Mirandae96798e2016-12-07 12:10:44 -0800317 /**
318 * Estimates the size of an item using spans: hSpan, vSpan.
319 *
Jon Mirandae96798e2016-12-07 12:10:44 -0800320 * @return MAX_VALUE for each dimension if unsuccessful.
321 */
Sunny Goyalaa3a2ba2017-09-26 12:43:16 -0700322 public int[] estimateItemSize(ItemInfo itemInfo) {
Michael Jurka038f9d82011-11-03 13:50:45 -0700323 int[] size = new int[2];
324 if (getChildCount() > 0) {
Sunny Goyal7ce471b2017-08-02 03:37:39 -0700325 // Use the first page to estimate the child position
326 CellLayout cl = (CellLayout) getChildAt(0);
Jon Mirandae96798e2016-12-07 12:10:44 -0800327 boolean isWidget = itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
328
Tony Wickhama501d492015-11-03 18:05:01 -0800329 Rect r = estimateItemPosition(cl, 0, 0, itemInfo.spanX, itemInfo.spanY);
Jon Mirandae96798e2016-12-07 12:10:44 -0800330
331 float scale = 1;
332 if (isWidget) {
333 DeviceProfile profile = mLauncher.getDeviceProfile();
Jon Miranda6f6a06a2016-12-15 11:24:18 -0800334 scale = Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y);
Jon Mirandae96798e2016-12-07 12:10:44 -0800335 }
Adam Cohend41fbf52012-02-16 23:53:59 -0800336 size[0] = r.width();
337 size[1] = r.height();
Jon Mirandae96798e2016-12-07 12:10:44 -0800338
Sunny Goyalaa3a2ba2017-09-26 12:43:16 -0700339 if (isWidget) {
Jon Mirandae96798e2016-12-07 12:10:44 -0800340 size[0] /= scale;
341 size[1] /= scale;
342 }
Michael Jurka038f9d82011-11-03 13:50:45 -0700343 return size;
344 } else {
345 size[0] = Integer.MAX_VALUE;
346 size[1] = Integer.MAX_VALUE;
347 return size;
348 }
349 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700350
Sunny Goyal2fd7a8b2018-03-30 17:10:13 -0700351 public float getWallpaperOffsetForCenterPage() {
352 int pageScroll = getScrollForPage(getPageNearestToCenterOfScreen());
353 return mWallpaperOffset.wallpaperOffsetForScroll(pageScroll);
354 }
355
Tony Wickhama501d492015-11-03 18:05:01 -0800356 public Rect estimateItemPosition(CellLayout cl, int hCell, int vCell, int hSpan, int vSpan) {
Adam Cohend41fbf52012-02-16 23:53:59 -0800357 Rect r = new Rect();
Michael Jurka038f9d82011-11-03 13:50:45 -0700358 cl.cellToRect(hCell, vCell, hSpan, vSpan, r);
Michael Jurka038f9d82011-11-03 13:50:45 -0700359 return r;
360 }
361
Sunny Goyal58376922015-05-26 18:56:52 -0700362 @Override
Sunny Goyal94b510c2016-08-16 15:36:48 -0700363 public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
Sunny Goyal58376922015-05-26 18:56:52 -0700364 if (ENFORCE_DRAG_EVENT_ORDER) {
Hyunyoung Song0de01172016-10-05 16:27:48 -0700365 enforceDragParity("onDragStart", 0, 0);
Sunny Goyal58376922015-05-26 18:56:52 -0700366 }
367
Tony Wickham4e47b5b2016-10-18 14:43:01 -0700368 if (mDragInfo != null && mDragInfo.cell != null) {
369 CellLayout layout = (CellLayout) mDragInfo.cell.getParent().getParent();
370 layout.markCellsAsUnoccupiedForView(mDragInfo.cell);
371 }
372
Sunny Goyal06e21a22016-08-11 16:02:02 -0700373 if (mOutlineProvider != null) {
Sunny Goyalaa3a2ba2017-09-26 12:43:16 -0700374 if (dragObject.dragView != null) {
375 Bitmap preview = dragObject.dragView.getPreviewBitmap();
376
377 // The outline is used to visualize where the item will land if dropped
378 mOutlineProvider.generateDragOutline(preview);
379 }
Sunny Goyal06e21a22016-08-11 16:02:02 -0700380 }
381
Sunny Goyala502aa32017-10-02 16:04:06 -0700382 updateChildrenLayersEnabled();
Sunny Goyal58376922015-05-26 18:56:52 -0700383
Sunny Goyal94b510c2016-08-16 15:36:48 -0700384 // Do not add a new page if it is a accessible drag which was not started by the workspace.
385 // We do not support accessibility drag from other sources and instead provide a direct
386 // action for move/add to homescreen.
387 // When a accessible drag is started by the folder, we only allow rearranging withing the
388 // folder.
389 boolean addNewPage = !(options.isAccessibleDrag && dragObject.dragSource != this);
390
391 if (addNewPage) {
Sunny Goyal58376922015-05-26 18:56:52 -0700392 mDeferRemoveExtraEmptyScreen = false;
393 addExtraEmptyScreenOnDrag();
Sunny Goyal06e21a22016-08-11 16:02:02 -0700394
Sunny Goyal94b510c2016-08-16 15:36:48 -0700395 if (dragObject.dragInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
396 && dragObject.dragSource != this) {
Sunny Goyal06e21a22016-08-11 16:02:02 -0700397 // When dragging a widget from different source, move to a page which has
398 // enough space to place this widget (after rearranging/resizing). We special case
399 // widgets as they cannot be placed inside a folder.
400 // Start at the current page and search right (on LTR) until finding a page with
401 // enough space. Since an empty screen is the furthest right, a page must be found.
402 int currentPage = getPageNearestToCenterOfScreen();
403 for (int pageIndex = currentPage; pageIndex < getPageCount(); pageIndex++) {
404 CellLayout page = (CellLayout) getPageAt(pageIndex);
Sunny Goyal94b510c2016-08-16 15:36:48 -0700405 if (page.hasReorderSolution(dragObject.dragInfo)) {
Sunny Goyal06e21a22016-08-11 16:02:02 -0700406 setCurrentPage(pageIndex);
407 break;
408 }
409 }
410 }
411 }
412
Sunny Goyalf5440cb2016-12-14 15:13:00 -0800413 // Always enter the spring loaded mode
Sunny Goyal3e3f44c2017-10-23 17:14:52 -0700414 mLauncher.getStateManager().goToState(SPRING_LOADED);
Michael Jurkad74c9842011-07-10 12:44:21 -0700415 }
416
Adam Cohen689ff162014-05-08 17:27:56 -0700417 public void deferRemoveExtraEmptyScreen() {
418 mDeferRemoveExtraEmptyScreen = true;
419 }
420
Sunny Goyal58376922015-05-26 18:56:52 -0700421 @Override
Michael Jurkad74c9842011-07-10 12:44:21 -0700422 public void onDragEnd() {
Sunny Goyal58376922015-05-26 18:56:52 -0700423 if (ENFORCE_DRAG_EVENT_ORDER) {
Hyunyoung Song0de01172016-10-05 16:27:48 -0700424 enforceDragParity("onDragEnd", 0, 0);
Sunny Goyal58376922015-05-26 18:56:52 -0700425 }
426
Adam Cohen689ff162014-05-08 17:27:56 -0700427 if (!mDeferRemoveExtraEmptyScreen) {
428 removeExtraEmptyScreen(true, mDragSourceInternal != null);
429 }
430
Sunny Goyala502aa32017-10-02 16:04:06 -0700431 updateChildrenLayersEnabled();
Jon Miranda23585182018-02-09 15:33:37 -0800432 mDragInfo = null;
Tony Wickham4e47b5b2016-10-18 14:43:01 -0700433 mOutlineProvider = null;
Adam Cohen5084cba2013-09-03 12:01:16 -0700434 mDragSourceInternal = null;
Michael Jurkad74c9842011-07-10 12:44:21 -0700435 }
436
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800437 /**
438 * Initializes various states for this workspace.
439 */
Michael Jurka0142d492010-08-25 17:46:15 -0700440 protected void initWorkspace() {
Sunny Goyal7ce471b2017-08-02 03:37:39 -0700441 mCurrentPage = DEFAULT_PAGE;
Romain Guyce3cbd12013-02-25 15:00:36 -0800442 setClipToPadding(false);
Derek Prothrodadd9842014-01-17 13:43:50 -0500443
Adam Cohen22cba7f2013-07-19 16:14:00 -0700444 setupLayoutTransition();
Michael Jurkaa6a05472013-11-13 17:59:46 +0100445
446 // Set the wallpaper dimensions when Launcher starts up
Adam Cohen824fcb32014-05-21 23:01:25 +0000447 setWallpaperDimension();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800448 }
449
Adam Cohen22cba7f2013-07-19 16:14:00 -0700450 private void setupLayoutTransition() {
451 // We want to show layout transitions when pages are deleted, to close the gap.
452 mLayoutTransition = new LayoutTransition();
453 mLayoutTransition.enableTransitionType(LayoutTransition.DISAPPEARING);
454 mLayoutTransition.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
455 mLayoutTransition.disableTransitionType(LayoutTransition.APPEARING);
456 mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
457 setLayoutTransition(mLayoutTransition);
458 }
459
Winson Chung964df6b2013-10-11 15:55:37 -0700460 void enableLayoutTransitions() {
461 setLayoutTransition(mLayoutTransition);
462 }
463 void disableLayoutTransitions() {
464 setLayoutTransition(null);
465 }
466
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800467 @Override
Sunny Goyal0bd7f4f2018-02-01 09:53:35 -0800468 public void onViewAdded(View child) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800469 if (!(child instanceof CellLayout)) {
470 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
471 }
Adam Cohen2801caf2011-05-13 20:57:39 -0700472 CellLayout cl = ((CellLayout) child);
473 cl.setOnInterceptTouchListener(this);
Sunny Goyalc46bfef2015-01-05 12:40:08 -0800474 cl.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
Sunny Goyal0bd7f4f2018-02-01 09:53:35 -0800475 super.onViewAdded(child);
Michael Jurka8b805b12012-04-18 14:23:14 -0700476 }
477
Sunny Goyalab770a12018-11-14 15:17:26 -0800478 protected boolean onInterceptHotseatTouch(View v, MotionEvent ev) {
479 // We don't want any clicks to go through to the hotseat unless the workspace is in
480 // the normal state or an accessible drag is in progress.
481 return !workspaceIconsCanBeDragged()
482 && !mLauncher.getAccessibilityDelegate().isInAccessibleDrag();
483 }
484
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700485 /**
486 * Initializes and binds the first page
Hyunyoung Song0de01172016-10-05 16:27:48 -0700487 * @param qsb an existing qsb to recycle or null.
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700488 */
489 public void bindAndInitFirstWorkspaceScreen(View qsb) {
Jon Miranda7143ba62019-03-15 09:00:05 -0700490 if (!FeatureFlags.QSB_ON_FIRST_SCREEN) {
Sunny Goyala9e2f5a2016-06-10 12:22:04 -0700491 return;
492 }
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700493 // Add the first page
494 CellLayout firstPage = insertNewWorkspaceScreen(Workspace.FIRST_SCREEN_ID, 0);
Sunny Goyal6178f132016-07-11 17:30:03 -0700495 // Always add a QSB on the first screen.
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700496 if (qsb == null) {
Sunny Goyal6178f132016-07-11 17:30:03 -0700497 // In transposed layout, we add the QSB in the Grid. As workspace does not touch the
498 // edges, we do not need a full width QSB.
Sunny Goyal966d9012017-06-06 16:43:59 -0700499 qsb = LayoutInflater.from(getContext())
Sunny Goyalc7b8df82017-06-27 11:11:03 -0700500 .inflate(R.layout.search_container_workspace,firstPage, false);
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700501 }
502
Sunny Goyal6178f132016-07-11 17:30:03 -0700503 CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, firstPage.getCountX(), 1);
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700504 lp.canReorder = false;
Sunny Goyalc7b8df82017-06-27 11:11:03 -0700505 if (!firstPage.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true)) {
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700506 Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout");
507 }
508 }
509
Winson Chung9e6a0a22013-08-27 11:58:12 -0700510 public void removeAllWorkspaceScreens() {
Winson Chung964df6b2013-10-11 15:55:37 -0700511 // Disable all layout transitions before removing all pages to ensure that we don't get the
Sunny Goyal7ce471b2017-08-02 03:37:39 -0700512 // transition animations competing with us changing the scroll when we add pages
Winson Chung964df6b2013-10-11 15:55:37 -0700513 disableLayoutTransitions();
514
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700515 // Recycle the QSB widget
Sunny Goyalc7b8df82017-06-27 11:11:03 -0700516 View qsb = findViewById(R.id.search_container_workspace);
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700517 if (qsb != null) {
518 ((ViewGroup) qsb.getParent()).removeView(qsb);
519 }
520
Winson Chung9e6a0a22013-08-27 11:58:12 -0700521 // Remove the pages and clear the screen models
Sunny Goyal39306af2018-05-07 09:49:34 -0700522 removeFolderListeners();
Winson Chung9e6a0a22013-08-27 11:58:12 -0700523 removeAllViews();
524 mScreenOrder.clear();
525 mWorkspaceScreens.clear();
Winson Chung964df6b2013-10-11 15:55:37 -0700526
Sunny Goyal2db53422019-03-01 16:06:12 -0800527 // Remove any deferred refresh callbacks
528 mLauncher.mHandler.removeCallbacksAndMessages(DeferredWidgetRefresh.class);
529
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700530 // Ensure that the first page is always present
531 bindAndInitFirstWorkspaceScreen(qsb);
532
Winson Chung964df6b2013-10-11 15:55:37 -0700533 // Re-enable the layout transitions
534 enableLayoutTransitions();
Winson Chung9e6a0a22013-08-27 11:58:12 -0700535 }
536
Sunny Goyalefb7e842018-10-04 15:11:00 -0700537 public void insertNewWorkspaceScreenBeforeEmptyScreen(int screenId) {
Winson Chung64359a52013-07-08 17:17:08 -0700538 // Find the index to insert this view into. If the empty screen exists, then
539 // insert it before that.
540 int insertIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
541 if (insertIndex < 0) {
542 insertIndex = mScreenOrder.size();
543 }
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700544 insertNewWorkspaceScreen(screenId, insertIndex);
Adam Cohendcd297f2013-06-18 13:13:40 -0700545 }
546
Sunny Goyalefb7e842018-10-04 15:11:00 -0700547 public void insertNewWorkspaceScreen(int screenId) {
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700548 insertNewWorkspaceScreen(screenId, getChildCount());
Winson Chung64359a52013-07-08 17:17:08 -0700549 }
550
Sunny Goyalefb7e842018-10-04 15:11:00 -0700551 public CellLayout insertNewWorkspaceScreen(int screenId, int insertIndex) {
Adam Cohen5084cba2013-09-03 12:01:16 -0700552 if (mWorkspaceScreens.containsKey(screenId)) {
553 throw new RuntimeException("Screen id " + screenId + " already exists!");
554 }
555
Sunny Goyal70660032015-05-14 00:07:08 -0700556 // Inflate the cell layout, but do not add it automatically so that we can get the newly
557 // created CellLayout.
Sunny Goyal9b29ca52017-02-17 10:39:44 -0800558 CellLayout newScreen = (CellLayout) LayoutInflater.from(getContext()).inflate(
Sunny Goyal70660032015-05-14 00:07:08 -0700559 R.layout.workspace_screen, this, false /* attachToRoot */);
Jon Miranda28032002017-07-13 16:18:56 -0700560 int paddingLeftRight = mLauncher.getDeviceProfile().cellLayoutPaddingLeftRightPx;
Jon Miranda9593f9e2017-08-09 14:57:30 -0700561 int paddingBottom = mLauncher.getDeviceProfile().cellLayoutBottomPaddingPx;
562 newScreen.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
Jon Miranda28032002017-07-13 16:18:56 -0700563
Adam Cohendcd297f2013-06-18 13:13:40 -0700564 mWorkspaceScreens.put(screenId, newScreen);
Winson Chung64359a52013-07-08 17:17:08 -0700565 mScreenOrder.add(insertIndex, screenId);
566 addView(newScreen, insertIndex);
Sunny Goyalea609262017-10-25 15:47:38 -0700567 mStateTransitionAnimation.applyChildState(
568 mLauncher.getStateManager().getState(), newScreen, insertIndex);
Adam Cohenc9735cf2015-01-23 16:11:55 -0800569
Sunny Goyalae502842016-06-17 08:43:56 -0700570 if (mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
Sunny Goyale9b651e2015-04-24 11:44:51 -0700571 newScreen.enableAccessibleDrag(true, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
Adam Cohenc9735cf2015-01-23 16:11:55 -0800572 }
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700573
574 return newScreen;
Adam Cohendcd297f2013-06-18 13:13:40 -0700575 }
576
Adam Cohen5084cba2013-09-03 12:01:16 -0700577 public void addExtraEmptyScreenOnDrag() {
578 boolean lastChildOnScreen = false;
579 boolean childOnFinalScreen = false;
580
Adam Cohenad4e15c2013-10-17 16:21:35 -0700581 // Cancel any pending removal of empty screen
582 mRemoveEmptyScreenRunnable = null;
583
Adam Cohen5084cba2013-09-03 12:01:16 -0700584 if (mDragSourceInternal != null) {
585 if (mDragSourceInternal.getChildCount() == 1) {
586 lastChildOnScreen = true;
587 }
588 CellLayout cl = (CellLayout) mDragSourceInternal.getParent();
589 if (indexOfChild(cl) == getChildCount() - 1) {
590 childOnFinalScreen = true;
591 }
592 }
593
594 // If this is the last item on the final screen
595 if (lastChildOnScreen && childOnFinalScreen) {
596 return;
597 }
598 if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) {
599 insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID);
600 }
601 }
602
603 public boolean addExtraEmptyScreen() {
604 if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) {
605 insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID);
606 return true;
607 }
608 return false;
609 }
610
Adam Cohenad4e15c2013-10-17 16:21:35 -0700611 private void convertFinalScreenToEmptyScreenIfNecessary() {
Adam Cohendcb173d2014-04-01 13:33:58 -0700612 if (mLauncher.isWorkspaceLoading()) {
613 // Invalid and dangerous operation if workspace is loading
Adam Cohendcb173d2014-04-01 13:33:58 -0700614 return;
615 }
616
Adam Cohenad4e15c2013-10-17 16:21:35 -0700617 if (hasExtraEmptyScreen() || mScreenOrder.size() == 0) return;
Sunny Goyalefb7e842018-10-04 15:11:00 -0700618 int finalScreenId = mScreenOrder.get(mScreenOrder.size() - 1);
Adam Cohenad4e15c2013-10-17 16:21:35 -0700619
Adam Cohenad4e15c2013-10-17 16:21:35 -0700620 CellLayout finalScreen = mWorkspaceScreens.get(finalScreenId);
621
622 // If the final screen is empty, convert it to the extra empty screen
Adam Cohen917e3882013-10-31 15:03:35 -0700623 if (finalScreen.getShortcutsAndWidgets().getChildCount() == 0 &&
624 !finalScreen.isDropPending()) {
Adam Cohenad4e15c2013-10-17 16:21:35 -0700625 mWorkspaceScreens.remove(finalScreenId);
Sunny Goyalefb7e842018-10-04 15:11:00 -0700626 mScreenOrder.removeValue(finalScreenId);
Adam Cohenad4e15c2013-10-17 16:21:35 -0700627
Sunny Goyal7ce471b2017-08-02 03:37:39 -0700628 // if this is the last screen, convert it to the empty screen
Adam Cohenad4e15c2013-10-17 16:21:35 -0700629 mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, finalScreen);
630 mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
Adam Cohen5084cba2013-09-03 12:01:16 -0700631 }
632 }
633
Adam Cohen689ff162014-05-08 17:27:56 -0700634 public void removeExtraEmptyScreen(final boolean animate, boolean stripEmptyScreens) {
635 removeExtraEmptyScreenDelayed(animate, null, 0, stripEmptyScreens);
Adam Cohenad4e15c2013-10-17 16:21:35 -0700636 }
637
Adam Cohen689ff162014-05-08 17:27:56 -0700638 public void removeExtraEmptyScreenDelayed(final boolean animate, final Runnable onComplete,
Adam Cohenad4e15c2013-10-17 16:21:35 -0700639 final int delay, final boolean stripEmptyScreens) {
Adam Cohendcb173d2014-04-01 13:33:58 -0700640 if (mLauncher.isWorkspaceLoading()) {
641 // Don't strip empty screens if the workspace is still loading
Adam Cohendcb173d2014-04-01 13:33:58 -0700642 return;
643 }
644
Adam Cohenad4e15c2013-10-17 16:21:35 -0700645 if (delay > 0) {
646 postDelayed(new Runnable() {
647 @Override
648 public void run() {
Adam Cohen689ff162014-05-08 17:27:56 -0700649 removeExtraEmptyScreenDelayed(animate, onComplete, 0, stripEmptyScreens);
Adam Cohenad4e15c2013-10-17 16:21:35 -0700650 }
Adam Cohenad4e15c2013-10-17 16:21:35 -0700651 }, delay);
652 return;
653 }
654
655 convertFinalScreenToEmptyScreenIfNecessary();
656 if (hasExtraEmptyScreen()) {
657 int emptyIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
658 if (getNextPage() == emptyIndex) {
659 snapToPage(getNextPage() - 1, SNAP_OFF_EMPTY_SCREEN_DURATION);
660 fadeAndRemoveEmptyScreen(SNAP_OFF_EMPTY_SCREEN_DURATION, FADE_EMPTY_SCREEN_DURATION,
661 onComplete, stripEmptyScreens);
662 } else {
Sunny Goyal2b7bf872015-05-28 10:06:43 -0700663 snapToPage(getNextPage(), 0);
Adam Cohenad4e15c2013-10-17 16:21:35 -0700664 fadeAndRemoveEmptyScreen(0, FADE_EMPTY_SCREEN_DURATION,
665 onComplete, stripEmptyScreens);
666 }
667 return;
Adam Cohen3a14eeb2013-11-15 16:51:22 +0000668 } else if (stripEmptyScreens) {
669 // If we're not going to strip the empty screens after removing
670 // the extra empty screen, do it right away.
671 stripEmptyScreens();
Adam Cohenad4e15c2013-10-17 16:21:35 -0700672 }
Adam Cohen3a14eeb2013-11-15 16:51:22 +0000673
Adam Cohenad4e15c2013-10-17 16:21:35 -0700674 if (onComplete != null) {
675 onComplete.run();
676 }
677 }
678
679 private void fadeAndRemoveEmptyScreen(int delay, int duration, final Runnable onComplete,
680 final boolean stripEmptyScreens) {
Winson Chunga90303b2013-11-15 13:05:06 -0800681 // XXX: Do we need to update LM workspace screens below?
Adam Cohenad4e15c2013-10-17 16:21:35 -0700682 final CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
683
684 mRemoveEmptyScreenRunnable = new Runnable() {
685 @Override
686 public void run() {
687 if (hasExtraEmptyScreen()) {
688 mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
Sunny Goyalefb7e842018-10-04 15:11:00 -0700689 mScreenOrder.removeValue(EXTRA_EMPTY_SCREEN_ID);
Adam Cohenad4e15c2013-10-17 16:21:35 -0700690 removeView(cl);
691 if (stripEmptyScreens) {
692 stripEmptyScreens();
693 }
Tony Wickhamd4b28d22016-05-26 13:00:02 -0700694 // Update the page indicator to reflect the removed page.
695 showPageIndicatorAtCurrentScroll();
Adam Cohenad4e15c2013-10-17 16:21:35 -0700696 }
697 }
698 };
699
Sunny Goyal55bdeed2018-09-18 16:17:22 -0700700 ObjectAnimator oa = ObjectAnimator.ofFloat(cl, ALPHA, 0f);
Adam Cohenad4e15c2013-10-17 16:21:35 -0700701 oa.setDuration(duration);
702 oa.setStartDelay(delay);
703 oa.addListener(new AnimatorListenerAdapter() {
704 @Override
705 public void onAnimationEnd(Animator animation) {
706 if (mRemoveEmptyScreenRunnable != null) {
707 mRemoveEmptyScreenRunnable.run();
708 }
709 if (onComplete != null) {
710 onComplete.run();
711 }
712 }
713 });
714 oa.start();
715 }
716
Michael Jurka2f817312013-09-20 03:03:42 +0200717 public boolean hasExtraEmptyScreen() {
Sunny Goyal7ce471b2017-08-02 03:37:39 -0700718 return mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID) && getChildCount() > 1;
Michael Jurka2f817312013-09-20 03:03:42 +0200719 }
720
Sunny Goyalefb7e842018-10-04 15:11:00 -0700721 public int commitExtraEmptyScreen() {
Adam Cohendcb173d2014-04-01 13:33:58 -0700722 if (mLauncher.isWorkspaceLoading()) {
723 // Invalid and dangerous operation if workspace is loading
Adam Cohendcb173d2014-04-01 13:33:58 -0700724 return -1;
725 }
Winson Chunga90303b2013-11-15 13:05:06 -0800726
Adam Cohendcd297f2013-06-18 13:13:40 -0700727 CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
Winson Chungc9168342013-06-26 14:54:55 -0700728 mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
Sunny Goyalefb7e842018-10-04 15:11:00 -0700729 mScreenOrder.removeValue(EXTRA_EMPTY_SCREEN_ID);
Adam Cohendcd297f2013-06-18 13:13:40 -0700730
Sunny Goyalefb7e842018-10-04 15:11:00 -0700731 int newId = LauncherSettings.Settings.call(getContext().getContentResolver(),
Sunny Goyald2497482015-09-22 18:24:19 -0700732 LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
Sunny Goyalefb7e842018-10-04 15:11:00 -0700733 .getInt(LauncherSettings.Settings.EXTRA_VALUE);
Adam Cohendcd297f2013-06-18 13:13:40 -0700734 mWorkspaceScreens.put(newId, cl);
735 mScreenOrder.add(newId);
736
Adam Cohendcd297f2013-06-18 13:13:40 -0700737 return newId;
738 }
739
Sunny Goyalef92b822018-11-21 14:12:00 -0800740 @Override
741 public Hotseat getHotseat() {
742 return mLauncher.getHotseat();
743 }
744
745 @Override
746 public void onAddDropTarget(DropTarget target) {
747 mDragController.addDropTarget(target);
748 }
749
750 @Override
Sunny Goyalefb7e842018-10-04 15:11:00 -0700751 public CellLayout getScreenWithId(int screenId) {
Sunny Goyal9eba1fd2015-10-16 08:58:57 -0700752 return mWorkspaceScreens.get(screenId);
Adam Cohendcd297f2013-06-18 13:13:40 -0700753 }
754
Sunny Goyalefb7e842018-10-04 15:11:00 -0700755 public int getIdForScreen(CellLayout layout) {
Sunny Goyale2df0622015-04-24 11:27:00 -0700756 int index = mWorkspaceScreens.indexOfValue(layout);
757 if (index != -1) {
758 return mWorkspaceScreens.keyAt(index);
Adam Cohendcd297f2013-06-18 13:13:40 -0700759 }
760 return -1;
761 }
762
Sunny Goyalefb7e842018-10-04 15:11:00 -0700763 public int getPageIndexForScreenId(int screenId) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700764 return indexOfChild(mWorkspaceScreens.get(screenId));
765 }
766
Sunny Goyalefb7e842018-10-04 15:11:00 -0700767 public int getScreenIdForPageIndex(int index) {
Winson Chung5f8afe62013-08-12 16:19:28 -0700768 if (0 <= index && index < mScreenOrder.size()) {
769 return mScreenOrder.get(index);
770 }
771 return -1;
Adam Cohendcd297f2013-06-18 13:13:40 -0700772 }
773
Sunny Goyalefb7e842018-10-04 15:11:00 -0700774 public IntArray getScreenOrder() {
Winson Chungc9168342013-06-26 14:54:55 -0700775 return mScreenOrder;
776 }
777
Adam Cohendcd297f2013-06-18 13:13:40 -0700778 public void stripEmptyScreens() {
Adam Cohen65e43032014-03-03 11:37:21 -0800779 if (mLauncher.isWorkspaceLoading()) {
Adam Cohendcb173d2014-04-01 13:33:58 -0700780 // Don't strip empty screens if the workspace is still loading.
781 // This is dangerous and can result in data loss.
Adam Cohen517a7f52014-03-01 12:12:59 -0800782 return;
783 }
784
Sunny Goyal9ccafbf2016-10-26 13:06:08 -0700785 if (isPageInTransition()) {
Adam Cohenaccfd562013-07-12 14:40:40 -0700786 mStripScreensOnPageStopMoving = true;
787 return;
788 }
789
790 int currentPage = getNextPage();
Sunny Goyalefb7e842018-10-04 15:11:00 -0700791 IntArray removeScreens = new IntArray();
Sunny Goyale2df0622015-04-24 11:27:00 -0700792 int total = mWorkspaceScreens.size();
793 for (int i = 0; i < total; i++) {
Sunny Goyalefb7e842018-10-04 15:11:00 -0700794 int id = mWorkspaceScreens.keyAt(i);
Sunny Goyale2df0622015-04-24 11:27:00 -0700795 CellLayout cl = mWorkspaceScreens.valueAt(i);
Sunny Goyalda4fe1a2016-05-26 16:05:17 -0700796 // FIRST_SCREEN_ID can never be removed.
Jon Miranda7143ba62019-03-15 09:00:05 -0700797 if ((!FeatureFlags.QSB_ON_FIRST_SCREEN || id > FIRST_SCREEN_ID)
Sunny Goyala9e2f5a2016-06-10 12:22:04 -0700798 && cl.getShortcutsAndWidgets().getChildCount() == 0) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700799 removeScreens.add(id);
800 }
801 }
802
Sunny Goyalae502842016-06-17 08:43:56 -0700803 boolean isInAccessibleDrag = mLauncher.getAccessibilityDelegate().isInAccessibleDrag();
Vadim Tryshevde1e67c2015-08-11 16:21:04 -0700804
Adam Cohen5084cba2013-09-03 12:01:16 -0700805 // We enforce at least one page to add new items to. In the case that we remove the last
806 // such screen, we convert the last screen to the empty screen
Sunny Goyal7ce471b2017-08-02 03:37:39 -0700807 int minScreens = 1;
Adam Cohen5084cba2013-09-03 12:01:16 -0700808
Adam Cohendcd297f2013-06-18 13:13:40 -0700809 int pageShift = 0;
Sunny Goyalefb7e842018-10-04 15:11:00 -0700810 for (int i = 0; i < removeScreens.size(); i++) {
811 int id = removeScreens.get(i);
Adam Cohendcd297f2013-06-18 13:13:40 -0700812 CellLayout cl = mWorkspaceScreens.get(id);
813 mWorkspaceScreens.remove(id);
Sunny Goyalefb7e842018-10-04 15:11:00 -0700814 mScreenOrder.removeValue(id);
Adam Cohen5084cba2013-09-03 12:01:16 -0700815
816 if (getChildCount() > minScreens) {
817 if (indexOfChild(cl) < currentPage) {
818 pageShift++;
819 }
Vadim Tryshevde1e67c2015-08-11 16:21:04 -0700820
Sunny Goyalae502842016-06-17 08:43:56 -0700821 if (isInAccessibleDrag) {
Vadim Tryshevde1e67c2015-08-11 16:21:04 -0700822 cl.enableAccessibleDrag(false, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
823 }
824
Adam Cohen5084cba2013-09-03 12:01:16 -0700825 removeView(cl);
826 } else {
Sunny Goyal7ce471b2017-08-02 03:37:39 -0700827 // if this is the last screen, convert it to the empty screen
Adam Cohenad4e15c2013-10-17 16:21:35 -0700828 mRemoveEmptyScreenRunnable = null;
Adam Cohen5084cba2013-09-03 12:01:16 -0700829 mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, cl);
830 mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
Adam Cohendcd297f2013-06-18 13:13:40 -0700831 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700832 }
Winson Chung64359a52013-07-08 17:17:08 -0700833
Adam Cohenaccfd562013-07-12 14:40:40 -0700834 if (pageShift >= 0) {
835 setCurrentPage(currentPage - pageShift);
836 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700837 }
838
Sunny Goyald5462aa2016-11-22 16:52:39 +0530839 /**
Patrick Dubroye708c522011-03-01 16:03:43 -0800840 * Called directly from a CellLayout (not by the framework), after we've been added as a
841 * listener via setOnInterceptTouchEventListener(). This allows us to tell the CellLayout
842 * that it should intercept touch events, which is not something that is normally supported.
843 */
Sunny Goyal70660032015-05-14 00:07:08 -0700844 @SuppressLint("ClickableViewAccessibility")
Patrick Dubroye708c522011-03-01 16:03:43 -0800845 @Override
Michael Jurkadee05892010-07-27 10:01:56 -0700846 public boolean onTouch(View v, MotionEvent event) {
Hyunyoung Song46133612016-09-15 10:48:25 -0700847 return shouldConsumeTouch(v);
848 }
849
850 private boolean shouldConsumeTouch(View v) {
Tony Wickham0c7852f2016-12-02 14:47:04 -0800851 return !workspaceIconsCanBeDragged()
Adam Cohen6c5891a2014-07-09 23:53:15 -0700852 || (!workspaceInModalState() && indexOfChild(v) != mCurrentPage);
Patrick Dubroye708c522011-03-01 16:03:43 -0800853 }
854
Adam Cohenfc53cd22011-07-20 15:45:11 -0700855 public boolean isSwitchingState() {
856 return mIsSwitchingState;
857 }
858
Winson Chung70442722012-02-10 15:43:22 -0800859 /** This differs from isSwitchingState in that we take into account how far the transition
860 * has completed. */
Winson Chung9b0b2fe2012-02-24 13:03:34 -0800861 public boolean isFinishedSwitchingState() {
Tony Wickham251ab6e2016-12-02 18:54:32 -0800862 return !mIsSwitchingState
863 || (mTransitionProgress > FINISHED_SWITCHING_STATE_TRANSITION_PROGRESS);
Winson Chung70442722012-02-10 15:43:22 -0800864 }
865
Michael Jurka5f1c5092010-09-03 14:15:02 -0700866 @Override
867 public boolean dispatchUnhandledMove(View focused, int direction) {
Adam Cohen6c5891a2014-07-09 23:53:15 -0700868 if (workspaceInModalState() || !isFinishedSwitchingState()) {
Michael Jurka5f1c5092010-09-03 14:15:02 -0700869 // when the home screens are shrunken, shouldn't allow side-scrolling
870 return false;
871 }
872 return super.dispatchUnhandledMove(focused, direction);
873 }
874
875 @Override
876 public boolean onInterceptTouchEvent(MotionEvent ev) {
Jon Mirandad2d39cb2018-04-24 15:24:21 -0700877 if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
Adam Cohenf8d28232011-02-01 21:47:00 -0800878 mXDown = ev.getX();
879 mYDown = ev.getY();
880 }
Michael Jurka5f1c5092010-09-03 14:15:02 -0700881 return super.onInterceptTouchEvent(ev);
882 }
883
Michael Jurka1adf5392010-10-18 18:10:22 -0700884 @Override
885 protected void determineScrollingStart(MotionEvent ev) {
Winson Chung70442722012-02-10 15:43:22 -0800886 if (!isFinishedSwitchingState()) return;
Adam Cohenf8d28232011-02-01 21:47:00 -0800887
Adam Cohen3d1b2b42013-08-14 15:57:58 -0700888 float deltaX = ev.getX() - mXDown;
889 float absDeltaX = Math.abs(deltaX);
890 float absDeltaY = Math.abs(ev.getY() - mYDown);
Adam Cohenf8d28232011-02-01 21:47:00 -0800891
Adam Cohen3d1b2b42013-08-14 15:57:58 -0700892 if (Float.compare(absDeltaX, 0f) == 0) return;
Adam Cohenf8d28232011-02-01 21:47:00 -0800893
Adam Cohen3d1b2b42013-08-14 15:57:58 -0700894 float slope = absDeltaY / absDeltaX;
Winson Chung70442722012-02-10 15:43:22 -0800895 float theta = (float) Math.atan(slope);
Adam Cohenf8d28232011-02-01 21:47:00 -0800896
Adam Cohen3d1b2b42013-08-14 15:57:58 -0700897 if (absDeltaX > mTouchSlop || absDeltaY > mTouchSlop) {
Winson Chung70442722012-02-10 15:43:22 -0800898 cancelCurrentPageLongPress();
899 }
900
901 if (theta > MAX_SWIPE_ANGLE) {
902 // Above MAX_SWIPE_ANGLE, we don't want to ever start scrolling the workspace
903 return;
904 } else if (theta > START_DAMPING_TOUCH_SLOP_ANGLE) {
905 // Above START_DAMPING_TOUCH_SLOP_ANGLE and below MAX_SWIPE_ANGLE, we want to
906 // increase the touch slop to make it harder to begin scrolling the workspace. This
907 // results in vertically scrolling widgets to more easily. The higher the angle, the
908 // more we increase touch slop.
909 theta -= START_DAMPING_TOUCH_SLOP_ANGLE;
910 float extraRatio = (float)
911 Math.sqrt((theta / (MAX_SWIPE_ANGLE - START_DAMPING_TOUCH_SLOP_ANGLE)));
912 super.determineScrollingStart(ev, 1 + TOUCH_SLOP_DAMPING_FACTOR * extraRatio);
913 } else {
914 // Below START_DAMPING_TOUCH_SLOP_ANGLE, we don't do anything special
915 super.determineScrollingStart(ev);
Adam Cohenf8d28232011-02-01 21:47:00 -0800916 }
Michael Jurka1adf5392010-10-18 18:10:22 -0700917 }
918
Sunny Goyal9ccafbf2016-10-26 13:06:08 -0700919 protected void onPageBeginTransition() {
920 super.onPageBeginTransition();
Sunny Goyala502aa32017-10-02 16:04:06 -0700921 updateChildrenLayersEnabled();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800922 }
923
Sunny Goyal9ccafbf2016-10-26 13:06:08 -0700924 protected void onPageEndTransition() {
925 super.onPageEndTransition();
Sunny Goyala502aa32017-10-02 16:04:06 -0700926 updateChildrenLayersEnabled();
Michael Jurkad74c9842011-07-10 12:44:21 -0700927
Winson Chung3bc21c32012-01-20 13:59:18 -0800928 if (mDragController.isDragging()) {
Adam Cohen6c5891a2014-07-09 23:53:15 -0700929 if (workspaceInModalState()) {
Winson Chung3bc21c32012-01-20 13:59:18 -0800930 // If we are in springloaded mode, then force an event to check if the current touch
931 // is under a new page (to scroll to)
Winson Chung25460a12013-04-01 18:21:28 -0700932 mDragController.forceTouchMove();
Winson Chung3bc21c32012-01-20 13:59:18 -0800933 }
Patrick Dubroy1262e362010-10-06 15:49:50 -0700934 }
Adam Cohen26976d92011-03-22 15:33:33 -0700935
Adam Cohenaccfd562013-07-12 14:40:40 -0700936 if (mStripScreensOnPageStopMoving) {
937 stripEmptyScreens();
938 mStripScreensOnPageStopMoving = false;
939 }
Adam Cohenc2d6e892014-10-16 09:49:24 -0700940 }
941
942 protected void onScrollInteractionBegin() {
Vadim Tryshev2ce6a132018-06-18 19:14:44 -0700943 super.onScrollInteractionBegin();
Adam Cohenc2d6e892014-10-16 09:49:24 -0700944 mScrollInteractionBegan = true;
945 }
946
947 protected void onScrollInteractionEnd() {
948 super.onScrollInteractionEnd();
949 mScrollInteractionBegan = false;
950 if (mStartedSendingScrollEvents) {
951 mStartedSendingScrollEvents = false;
952 mLauncherOverlay.onScrollInteractionEnd();
953 }
954 }
955
956 public void setLauncherOverlay(LauncherOverlay overlay) {
957 mLauncherOverlay = overlay;
Sunny Goyalc86df472016-02-25 09:19:38 -0800958 // A new overlay has been set. Reset event tracking
959 mStartedSendingScrollEvents = false;
960 onOverlayScrollChanged(0);
961 }
962
Sunny Goyalc86df472016-02-25 09:19:38 -0800963
Sunny Goyal061380a2016-02-29 15:15:03 -0800964 private boolean isScrollingOverlay() {
965 return mLauncherOverlay != null &&
Adam Cohen8d769d62017-06-23 17:27:38 -0700966 ((mIsRtl && getUnboundedScrollX() > mMaxScrollX) || (!mIsRtl && getUnboundedScrollX() < 0));
Sunny Goyal061380a2016-02-29 15:15:03 -0800967 }
968
969 @Override
970 protected void snapToDestination() {
971 // If we're overscrolling the overlay, we make sure to immediately reset the PagedView
972 // to it's baseline position instead of letting the overscroll settle. The overlay handles
973 // it's own settling, and every gesture to the overlay should be self-contained and start
974 // from 0, so we zero it out here.
975 if (isScrollingOverlay()) {
Sunny Goyal061380a2016-02-29 15:15:03 -0800976 // We reset mWasInOverscroll so that PagedView doesn't zero out the overscroll
Sunny Goyal76dbf6f2017-01-03 14:55:47 -0800977 // interaction when we call snapToPageImmediately.
Sunny Goyal061380a2016-02-29 15:15:03 -0800978 mWasInOverscroll = false;
Sunny Goyal76dbf6f2017-01-03 14:55:47 -0800979 snapToPageImmediately(0);
Sunny Goyal061380a2016-02-29 15:15:03 -0800980 } else {
981 super.snapToDestination();
982 }
983 }
984
Sunny Goyalc86df472016-02-25 09:19:38 -0800985 @Override
Tony Wickhamf549dab2016-05-16 09:54:06 -0700986 protected void onScrollChanged(int l, int t, int oldl, int oldt) {
987 super.onScrollChanged(l, t, oldl, oldt);
988
989 // Update the page indicator progress.
990 boolean isTransitioning = mIsSwitchingState
991 || (getLayoutTransition() != null && getLayoutTransition().isRunning());
Tony Wickhamddb77002016-05-19 12:45:40 -0700992 if (!isTransitioning) {
Tony Wickhamf549dab2016-05-16 09:54:06 -0700993 showPageIndicatorAtCurrentScroll();
994 }
Sunny Goyal9ccafbf2016-10-26 13:06:08 -0700995
996 updatePageAlphaValues();
Sunny Goyal9ccafbf2016-10-26 13:06:08 -0700997 enableHwLayersOnVisiblePages();
Tony Wickhamf549dab2016-05-16 09:54:06 -0700998 }
999
Sunny Goyalc99cb172017-10-19 16:15:09 -07001000 public void showPageIndicatorAtCurrentScroll() {
Tony Wickhamddb77002016-05-19 12:45:40 -07001001 if (mPageIndicator != null) {
1002 mPageIndicator.setScroll(getScrollX(), computeMaxScrollX());
1003 }
Tony Wickhamf549dab2016-05-16 09:54:06 -07001004 }
1005
1006 @Override
Sunny Goyalb7b01352018-08-09 12:31:20 -07001007 protected void overScroll(int amount) {
Jon Miranda71cb80c2019-01-24 21:25:13 -08001008 boolean shouldScrollOverlay = mLauncherOverlay != null && !mScroller.isSpringing() &&
Sunny Goyal70660032015-05-14 00:07:08 -07001009 ((amount <= 0 && !mIsRtl) || (amount >= 0 && mIsRtl));
Adam Cohen8c4ca922014-10-24 17:40:34 -07001010
Hyunyoung Song0de01172016-10-05 16:27:48 -07001011 boolean shouldZeroOverlay = mLauncherOverlay != null && mLastOverlayScroll != 0 &&
Sunny Goyal70660032015-05-14 00:07:08 -07001012 ((amount >= 0 && !mIsRtl) || (amount <= 0 && mIsRtl));
Adam Cohenc2d6e892014-10-16 09:49:24 -07001013
1014 if (shouldScrollOverlay) {
1015 if (!mStartedSendingScrollEvents && mScrollInteractionBegan) {
1016 mStartedSendingScrollEvents = true;
1017 mLauncherOverlay.onScrollInteractionBegin();
Adam Cohenc2d6e892014-10-16 09:49:24 -07001018 }
Adam Cohenc2d6e892014-10-16 09:49:24 -07001019
Sunny Goyalb7b01352018-08-09 12:31:20 -07001020 mLastOverlayScroll = Math.abs(((float) amount) / getMeasuredWidth());
Hyunyoung Song0de01172016-10-05 16:27:48 -07001021 mLauncherOverlay.onScrollChange(mLastOverlayScroll, mIsRtl);
Sunny Goyal7ce471b2017-08-02 03:37:39 -07001022 } else {
Adam Cohenc2d6e892014-10-16 09:49:24 -07001023 dampedOverScroll(amount);
Adam Cohenc2d6e892014-10-16 09:49:24 -07001024 }
Adam Cohen8c4ca922014-10-24 17:40:34 -07001025
1026 if (shouldZeroOverlay) {
Sunny Goyal70660032015-05-14 00:07:08 -07001027 mLauncherOverlay.onScrollChange(0, mIsRtl);
Adam Cohen8c4ca922014-10-24 17:40:34 -07001028 }
Adam Cohen89cbbb92013-09-25 16:52:48 -07001029 }
1030
Sunny Goyal65d9ceb2017-05-08 09:17:42 -07001031 @Override
1032 protected boolean shouldFlingForVelocity(int velocityX) {
1033 // When the overlay is moving, the fling or settle transition is controlled by the overlay.
Adam Cohen5360b6e2017-07-18 16:14:12 -07001034 return Float.compare(Math.abs(mOverlayTranslation), 0) == 0 &&
Sunny Goyal65d9ceb2017-05-08 09:17:42 -07001035 super.shouldFlingForVelocity(velocityX);
1036 }
1037
Sunny Goyalc86df472016-02-25 09:19:38 -08001038 /**
1039 * The overlay scroll is being controlled locally, just update our overlay effect
1040 */
1041 public void onOverlayScrollChanged(float scroll) {
Hyunyoung Song3f5a11f2017-06-29 16:42:04 -07001042 if (Float.compare(scroll, 1f) == 0) {
1043 if (!mOverlayShown) {
1044 mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
1045 Action.Direction.LEFT, ContainerType.WORKSPACE, 0);
1046 }
1047 mOverlayShown = true;
Vadim Tryshev2c430b32018-04-04 14:45:21 -07001048 // Not announcing the overlay page for accessibility since it announces itself.
Hyunyoung Song3f5a11f2017-06-29 16:42:04 -07001049 } else if (Float.compare(scroll, 0f) == 0) {
1050 if (mOverlayShown) {
1051 mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
1052 Action.Direction.RIGHT, ContainerType.WORKSPACE, -1);
Vadim Tryshev2c430b32018-04-04 14:45:21 -07001053 } else if (Float.compare(mOverlayTranslation, 0f) != 0) {
1054 // When arriving to 0 overscroll from non-zero overscroll, announce page for
1055 // accessibility since default announcements were disabled while in overscroll
1056 // state.
1057 // Not doing this if mOverlayShown because in that case the accessibility service
1058 // will announce the launcher window description upon regaining focus after
1059 // switching from the overlay screen.
1060 announcePageForAccessibility();
Hyunyoung Song3f5a11f2017-06-29 16:42:04 -07001061 }
1062 mOverlayShown = false;
Sunny Goyalbf6dfc52018-02-13 17:04:55 -08001063 tryRunOverlayCallback();
Hyunyoung Song3f5a11f2017-06-29 16:42:04 -07001064 }
Vadim Tryshev2c430b32018-04-04 14:45:21 -07001065
Sunny Goyalc86df472016-02-25 09:19:38 -08001066 float offset = 0f;
Sunny Goyalc86df472016-02-25 09:19:38 -08001067
1068 scroll = Math.max(scroll - offset, 0);
1069 scroll = Math.min(1, scroll / (1 - offset));
1070
Sunny Goyal5bc6b6f2017-10-26 15:36:10 -07001071 float alpha = 1 - Interpolators.DEACCEL_3.getInterpolation(scroll);
Sunny Goyalc86df472016-02-25 09:19:38 -08001072 float transX = mLauncher.getDragLayer().getMeasuredWidth() * scroll;
Sunny Goyalc86df472016-02-25 09:19:38 -08001073
1074 if (mIsRtl) {
1075 transX = -transX;
1076 }
Sunny Goyalee688162016-02-12 14:24:21 -08001077 mOverlayTranslation = transX;
Sunny Goyalc86df472016-02-25 09:19:38 -08001078
1079 // TODO(adamcohen): figure out a final effect here. We may need to recommend
1080 // different effects based on device performance. On at least one relatively high-end
1081 // device I've tried, translating the launcher causes things to get quite laggy.
Sunny Goyal19d32b72018-01-19 14:26:18 -08001082 mLauncher.getDragLayer().setTranslationX(transX);
Sunny Goyal6001ea22018-05-10 16:31:00 -07001083 mLauncher.getDragLayer().getAlphaProperty(ALPHA_INDEX_OVERLAY).setValue(alpha);
Sunny Goyala92e0df2016-06-09 12:08:22 -07001084 }
1085
Tony Wickham618a5042018-02-12 15:59:15 -08001086 /**
Sunny Goyalbf6dfc52018-02-13 17:04:55 -08001087 * @return false if the callback is still pending
1088 */
1089 private boolean tryRunOverlayCallback() {
1090 if (mOnOverlayHiddenCallback == null) {
1091 // Return true as no callback is pending. This is used by OnWindowFocusChangeListener
1092 // to remove itself if multiple focus handles were added.
1093 return true;
1094 }
1095 if (mOverlayShown || !hasWindowFocus()) {
1096 return false;
1097 }
1098
1099 mOnOverlayHiddenCallback.run();
1100 mOnOverlayHiddenCallback = null;
1101 return true;
1102 }
1103
1104 /**
Tony Wickham618a5042018-02-12 15:59:15 -08001105 * Runs the given callback when the minus one overlay is hidden. Specifically, it is run
1106 * when launcher's window has focus and the overlay is no longer being shown. If a callback
1107 * is already present, the new callback will chain off it so both are run.
1108 *
1109 * @return Whether the callback was deferred.
1110 */
1111 public boolean runOnOverlayHidden(Runnable callback) {
Tony Wickham618a5042018-02-12 15:59:15 -08001112 if (mOnOverlayHiddenCallback == null) {
1113 mOnOverlayHiddenCallback = callback;
1114 } else {
1115 // Chain the new callback onto the previous callback(s).
1116 Runnable oldCallback = mOnOverlayHiddenCallback;
1117 mOnOverlayHiddenCallback = () -> {
1118 oldCallback.run();
1119 callback.run();
1120 };
1121 }
Sunny Goyalbf6dfc52018-02-13 17:04:55 -08001122 if (!tryRunOverlayCallback()) {
1123 ViewTreeObserver observer = getViewTreeObserver();
1124 if (observer != null && observer.isAlive()) {
1125 observer.addOnWindowFocusChangeListener(
1126 new ViewTreeObserver.OnWindowFocusChangeListener() {
1127 @Override
1128 public void onWindowFocusChanged(boolean hasFocus) {
1129 if (tryRunOverlayCallback() && observer.isAlive()) {
1130 observer.removeOnWindowFocusChangeListener(this);
1131 }
1132 }});
1133 }
1134 return true;
1135 }
1136 return false;
Tony Wickham618a5042018-02-12 15:59:15 -08001137 }
1138
Sunny Goyalee688162016-02-12 14:24:21 -08001139 @Override
Hyunyoung Song3f5a11f2017-06-29 16:42:04 -07001140 protected void notifyPageSwitchListener(int prevPage) {
1141 super.notifyPageSwitchListener(prevPage);
1142 if (prevPage != mCurrentPage) {
1143 int swipeDirection = (prevPage < mCurrentPage) ? Action.Direction.RIGHT : Action.Direction.LEFT;
1144 mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
1145 swipeDirection, ContainerType.WORKSPACE, prevPage);
1146 }
Adam Cohen6fecd412013-10-02 17:41:50 -07001147 }
1148
Adam Cohen824fcb32014-05-21 23:01:25 +00001149 protected void setWallpaperDimension() {
Sunny Goyal6f866092016-03-17 17:04:15 -07001150 Utilities.THREAD_POOL_EXECUTOR.execute(new Runnable() {
1151 @Override
1152 public void run() {
Sunny Goyal87f784c2017-01-11 10:48:34 -08001153 final Point size = LauncherAppState.getIDP(getContext()).defaultWallpaperSize;
Sunny Goyal6f866092016-03-17 17:04:15 -07001154 if (size.x != mWallpaperManager.getDesiredMinimumWidth()
1155 || size.y != mWallpaperManager.getDesiredMinimumHeight()) {
1156 mWallpaperManager.suggestDesiredDimensions(size.x, size.y);
Sunny Goyal4b171472015-08-19 12:43:27 -07001157 }
Michael Jurkaa6a05472013-11-13 17:59:46 +01001158 }
Sunny Goyal6f866092016-03-17 17:04:15 -07001159 });
Michael Jurkac5b262c2011-01-12 20:24:50 -08001160 }
1161
Winsonc7d2e832016-07-28 12:24:55 -07001162 public void lockWallpaperToDefaultPage() {
1163 mWallpaperOffset.setLockToDefaultPage(true);
1164 }
1165
1166 public void unlockWallpaperFromDefaultPageOnNextLayout() {
1167 if (mWallpaperOffset.isLockedToDefaultPage()) {
1168 mUnlockWallpaperFromDefaultPageOnLayout = true;
1169 requestLayout();
1170 }
1171 }
1172
Michael Jurka340c5f32010-10-21 16:49:19 -07001173 @Override
1174 public void computeScroll() {
1175 super.computeScroll();
Michael Jurkafe09cb72013-08-27 15:48:58 +02001176 mWallpaperOffset.syncWithScroll();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001177 }
1178
Tony Wickham95cdb3a2016-02-18 14:37:07 -08001179 public void computeScrollWithoutInvalidation() {
1180 computeScrollHelper(false);
1181 }
1182
Jason Monked05f092014-04-24 10:13:05 -04001183 @Override
Tony Wickhama5209e32015-11-19 12:45:30 -08001184 protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
1185 if (!isSwitchingState()) {
1186 super.determineScrollingStart(ev, touchSlopScale);
1187 }
1188 }
1189
1190 @Override
Jason Monked05f092014-04-24 10:13:05 -04001191 public void announceForAccessibility(CharSequence text) {
1192 // Don't announce if apps is on top of us.
Sunny Goyal3e3f44c2017-10-23 17:14:52 -07001193 if (!mLauncher.isInState(ALL_APPS)) {
Jason Monked05f092014-04-24 10:13:05 -04001194 super.announceForAccessibility(text);
1195 }
1196 }
1197
Sunny Goyal9ccafbf2016-10-26 13:06:08 -07001198 private void updatePageAlphaValues() {
Jon Miranda8d994cd2018-05-16 11:31:22 -07001199 // We need to check the isDragging case because updatePageAlphaValues is called between
1200 // goToState(SPRING_LOADED) and onStartStateTransition.
1201 if (!workspaceInModalState() && !mIsSwitchingState && !mDragController.isDragging()) {
Sunny Goyal228153d2018-01-04 15:35:22 -08001202 int screenCenter = getScrollX() + getMeasuredWidth() / 2;
Sunny Goyal7ce471b2017-08-02 03:37:39 -07001203 for (int i = 0; i < getChildCount(); i++) {
Michael Jurka869390b2012-05-06 15:55:19 -07001204 CellLayout child = (CellLayout) getChildAt(i);
1205 if (child != null) {
1206 float scrollProgress = getScrollProgress(screenCenter, child, i);
Adam Cohen73894962011-10-31 13:17:17 -07001207 float alpha = 1 - Math.abs(scrollProgress);
Tony6dea7cd2017-07-06 11:32:53 -07001208 if (mWorkspaceFadeInAdjacentScreens) {
1209 child.getShortcutsAndWidgets().setAlpha(alpha);
1210 } else {
1211 // Pages that are off-screen aren't important for accessibility.
1212 child.getShortcutsAndWidgets().setImportantForAccessibility(
1213 alpha > 0 ? IMPORTANT_FOR_ACCESSIBILITY_AUTO
1214 : IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
1215 }
Adam Cohen73894962011-10-31 13:17:17 -07001216 }
Adam Cohenf34bab52010-09-30 14:11:56 -07001217 }
1218 }
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001219 }
1220
Joe Onorato00acb122009-08-04 16:04:30 -04001221 protected void onAttachedToWindow() {
1222 super.onAttachedToWindow();
Sunny Goyal9eba1fd2015-10-16 08:58:57 -07001223 IBinder windowToken = getWindowToken();
1224 mWallpaperOffset.setWindowToken(windowToken);
Joe Onorato956091b2010-02-19 12:47:40 -08001225 computeScroll();
Sunny Goyal9eba1fd2015-10-16 08:58:57 -07001226 mDragController.setWindowToken(windowToken);
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001227 }
1228
1229 protected void onDetachedFromWindow() {
Romain Guye6661952013-08-08 19:13:22 -07001230 super.onDetachedFromWindow();
Sunny Goyal9eba1fd2015-10-16 08:58:57 -07001231 mWallpaperOffset.setWindowToken(null);
Joe Onorato00acb122009-08-04 16:04:30 -04001232 }
1233
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001234 @Override
Michael Jurka28750fb2010-09-24 17:43:49 -07001235 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Winsonc7d2e832016-07-28 12:24:55 -07001236 if (mUnlockWallpaperFromDefaultPageOnLayout) {
1237 mWallpaperOffset.setLockToDefaultPage(false);
1238 mUnlockWallpaperFromDefaultPageOnLayout = false;
1239 }
Michael Jurkac5b262c2011-01-12 20:24:50 -08001240 if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
Michael Jurkafe09cb72013-08-27 15:48:58 +02001241 mWallpaperOffset.syncWithScroll();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001242 mWallpaperOffset.jumpToFinal();
Michael Jurkac5b262c2011-01-12 20:24:50 -08001243 }
Michael Jurka0142d492010-08-25 17:46:15 -07001244 super.onLayout(changed, left, top, right, bottom);
Sunny Goyalcce07802016-11-01 14:00:41 -07001245 updatePageAlphaValues();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001246 }
1247
1248 @Override
Winson Chung97d85d22011-04-13 11:27:36 -07001249 public int getDescendantFocusability() {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001250 if (workspaceInModalState()) {
Winson Chung97d85d22011-04-13 11:27:36 -07001251 return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
1252 }
1253 return super.getDescendantFocusability();
1254 }
1255
Sunny Goyalea609262017-10-25 15:47:38 -07001256 private boolean workspaceInModalState() {
1257 return !mLauncher.isInState(NORMAL);
Michael Jurkad74c9842011-07-10 12:44:21 -07001258 }
1259
Tony Wickham0c7852f2016-12-02 14:47:04 -08001260 /** Returns whether a drag should be allowed to be started from the current workspace state. */
1261 public boolean workspaceIconsCanBeDragged() {
Tony Wickham53370672017-12-05 10:10:55 -08001262 return mLauncher.getStateManager().getState().workspaceIconsCanBeDragged;
Tony Wickham0c7852f2016-12-02 14:47:04 -08001263 }
1264
Sunny Goyala502aa32017-10-02 16:04:06 -07001265 private void updateChildrenLayersEnabled() {
Sunny Goyal7c7be8c2018-03-07 19:58:07 -08001266 boolean enableChildrenLayers = mIsSwitchingState || isPageInTransition();
Michael Jurkad74c9842011-07-10 12:44:21 -07001267
1268 if (enableChildrenLayers != mChildrenLayersEnabled) {
1269 mChildrenLayersEnabled = enableChildrenLayers;
Michael Jurkad51f33a2012-06-28 15:35:26 -07001270 if (mChildrenLayersEnabled) {
1271 enableHwLayersOnVisiblePages();
1272 } else {
1273 for (int i = 0; i < getPageCount(); i++) {
1274 final CellLayout cl = (CellLayout) getChildAt(i);
Chris Craik01f2d7f2013-10-01 14:41:56 -07001275 cl.enableHardwareLayer(false);
Michael Jurkad51f33a2012-06-28 15:35:26 -07001276 }
1277 }
1278 }
1279 }
1280
1281 private void enableHwLayersOnVisiblePages() {
1282 if (mChildrenLayersEnabled) {
1283 final int screenCount = getChildCount();
Sunny Goyal9ccafbf2016-10-26 13:06:08 -07001284
Vadim Tryshev528b9e02018-05-24 18:22:03 -07001285 final int[] visibleScreens = getVisibleChildrenRange();
1286 int leftScreen = visibleScreens[0];
1287 int rightScreen = visibleScreens[1];
Sunny Goyal9ccafbf2016-10-26 13:06:08 -07001288 if (mForceDrawAdjacentPages) {
1289 // In overview mode, make sure that the two side pages are visible.
Sunny Goyal7ce471b2017-08-02 03:37:39 -07001290 leftScreen = Utilities.boundToRange(getCurrentPage() - 1, 0, rightScreen);
Sunny Goyal9ccafbf2016-10-26 13:06:08 -07001291 rightScreen = Utilities.boundToRange(getCurrentPage() + 1,
1292 leftScreen, getPageCount() - 1);
1293 }
1294
Michael Jurkad51f33a2012-06-28 15:35:26 -07001295 if (leftScreen == rightScreen) {
1296 // make sure we're caching at least two pages always
1297 if (rightScreen < screenCount - 1) {
1298 rightScreen++;
1299 } else if (leftScreen > 0) {
1300 leftScreen--;
1301 }
1302 }
Chris Craik01f2d7f2013-10-01 14:41:56 -07001303
Sunny Goyal7ce471b2017-08-02 03:37:39 -07001304 for (int i = 0; i < screenCount; i++) {
Michael Jurka47639b92013-01-14 12:42:27 +01001305 final CellLayout layout = (CellLayout) getPageAt(i);
Sunny Goyal9ccafbf2016-10-26 13:06:08 -07001306 // enable layers between left and right screen inclusive.
1307 boolean enableLayer = leftScreen <= i && i <= rightScreen;
Chris Craik01f2d7f2013-10-01 14:41:56 -07001308 layout.enableHardwareLayer(enableLayer);
Michael Jurkad74c9842011-07-10 12:44:21 -07001309 }
Michael Jurkace7e05f2011-02-01 22:02:35 -08001310 }
Michael Jurkace7e05f2011-02-01 22:02:35 -08001311 }
1312
Jon Mirandad2d39cb2018-04-24 15:24:21 -07001313 public void onWallpaperTap(MotionEvent ev) {
Sunny Goyalcf25b522015-07-09 00:01:18 -07001314 final int[] position = mTempXY;
Jeff Brown1d0867c2010-12-02 18:27:39 -08001315 getLocationOnScreen(position);
1316
1317 int pointerIndex = ev.getActionIndex();
1318 position[0] += (int) ev.getX(pointerIndex);
1319 position[1] += (int) ev.getY(pointerIndex);
1320
1321 mWallpaperManager.sendWallpaperCommand(getWindowToken(),
1322 ev.getAction() == MotionEvent.ACTION_UP
1323 ? WallpaperManager.COMMAND_TAP : WallpaperManager.COMMAND_SECONDARY_TAP,
1324 position[0], position[1], 0, null);
1325 }
1326
Sunny Goyal06e21a22016-08-11 16:02:02 -07001327 public void prepareDragWithProvider(DragPreviewProvider outlineProvider) {
1328 mOutlineProvider = outlineProvider;
Michael Jurka3e7c7632010-10-02 16:01:03 -07001329 }
1330
Sunny Goyalce5a7e52015-07-08 15:44:27 -07001331 public void snapToPageFromOverView(int whichPage) {
Sunny Goyal5bc6b6f2017-10-26 15:36:10 -07001332 snapToPage(whichPage, OVERVIEW_TRANSITION_MS, Interpolators.ZOOM_IN);
Sunny Goyalce5a7e52015-07-08 15:44:27 -07001333 }
1334
Sunny Goyalc99cb172017-10-19 16:15:09 -07001335 private void onStartStateTransition(LauncherState state) {
Sunny Goyalc99cb172017-10-19 16:15:09 -07001336 mIsSwitchingState = true;
1337 mTransitionProgress = 0;
1338
1339 updateChildrenLayersEnabled();
Adam Cohen410f3cd2013-09-22 12:09:32 -07001340 }
1341
Sunny Goyalc99cb172017-10-19 16:15:09 -07001342 private void onEndStateTransition() {
1343 mIsSwitchingState = false;
1344 mForceDrawAdjacentPages = false;
1345 mTransitionProgress = 1;
Sunny Goyal47328fd2016-05-25 18:56:41 -07001346
Sunny Goyalc99cb172017-10-19 16:15:09 -07001347 updateChildrenLayersEnabled();
1348 updateAccessibilityFlags();
Tony Wickham34d2c912015-09-11 09:27:58 -07001349 }
1350
Winson Chungdc61c4d2015-04-20 18:26:57 -07001351 /**
Sunny Goyal4c7f2152017-10-17 17:17:16 -07001352 * Sets the current workspace {@link LauncherState} and updates the UI without any animations
Winson Chungdc61c4d2015-04-20 18:26:57 -07001353 */
Sunny Goyalc4fa8c32017-11-07 12:23:58 -08001354 @Override
Sunny Goyal4c7f2152017-10-17 17:17:16 -07001355 public void setState(LauncherState toState) {
Sunny Goyalc99cb172017-10-19 16:15:09 -07001356 onStartStateTransition(toState);
Sunny Goyalea609262017-10-25 15:47:38 -07001357 mStateTransitionAnimation.setState(toState);
Sunny Goyalaeb16432017-10-16 11:46:41 -07001358 onEndStateTransition();
1359 }
1360
1361 /**
Sunny Goyal4c7f2152017-10-17 17:17:16 -07001362 * Sets the current workspace {@link LauncherState}, then animates the UI
Sunny Goyalaeb16432017-10-16 11:46:41 -07001363 */
Sunny Goyalc4fa8c32017-11-07 12:23:58 -08001364 @Override
Sunny Goyal3c585dd2017-12-07 11:42:33 -08001365 public void setStateWithAnimation(LauncherState toState,
Sunny Goyale3c6d582017-12-07 12:45:49 -08001366 AnimatorSetBuilder builder, AnimationConfig config) {
Sunny Goyalc99cb172017-10-19 16:15:09 -07001367 StateTransitionListener listener = new StateTransitionListener(toState);
Sunny Goyal3c585dd2017-12-07 11:42:33 -08001368 mStateTransitionAnimation.setStateWithAnimation(toState, builder, config);
Winson Chungdc61c4d2015-04-20 18:26:57 -07001369
Sunny Goyalc99cb172017-10-19 16:15:09 -07001370 // Invalidate the pages now, so that we have the visible pages before the
1371 // animation is started
1372 if (toState.hasMultipleVisiblePages) {
1373 mForceDrawAdjacentPages = true;
1374 }
1375 invalidate(); // This will call dispatchDraw(), which calls getVisiblePages().
Mario Bertschler5a2edd62017-01-30 17:47:30 -08001376
Sunny Goyalaeb16432017-10-16 11:46:41 -07001377 ValueAnimator stepAnimator = ValueAnimator.ofFloat(0, 1);
1378 stepAnimator.addUpdateListener(listener);
Sunny Goyal03102202017-10-27 11:05:26 -07001379 stepAnimator.setDuration(config.duration);
Sunny Goyale3c6d582017-12-07 12:45:49 -08001380 stepAnimator.addListener(listener);
1381 builder.play(stepAnimator);
Adam Cohen53805212013-10-01 10:39:23 -07001382 }
1383
Sunny Goyal1d08f702015-05-04 15:50:25 -07001384 public void updateAccessibilityFlags() {
Sunny Goyald8d19932016-06-22 16:29:24 -07001385 // TODO: Update the accessibility flags appropriately when dragging.
Sunny Goyalea609262017-10-25 15:47:38 -07001386 int accessibilityFlag = mLauncher.getStateManager().getState().workspaceAccessibilityFlag;
Sunny Goyald8d19932016-06-22 16:29:24 -07001387 if (!mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
Sunny Goyala52ecb02016-12-16 15:04:51 -08001388 int total = getPageCount();
Sunny Goyal7ce471b2017-08-02 03:37:39 -07001389 for (int i = 0; i < total; i++) {
Sunny Goyal7c7be8c2018-03-07 19:58:07 -08001390 updateAccessibilityFlags(accessibilityFlag, (CellLayout) getPageAt(i));
Sunny Goyald8d19932016-06-22 16:29:24 -07001391 }
Sunny Goyalea609262017-10-25 15:47:38 -07001392 setImportantForAccessibility(accessibilityFlag);
Sunny Goyal1d08f702015-05-04 15:50:25 -07001393 }
1394 }
1395
Vadim Tryshev7acde482018-05-31 14:33:09 -07001396 @Override
1397 public AccessibilityNodeInfo createAccessibilityNodeInfo() {
1398 if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS) {
1399 // TAPL tests verify that workspace is not present in Overview and AllApps states.
1400 // TAPL can work only if UIDevice is set up as setCompressedLayoutHeirarchy(false).
1401 // Hiding workspace from the tests when it's
1402 // IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS.
1403 return null;
1404 }
1405 return super.createAccessibilityNodeInfo();
1406 }
1407
Sunny Goyal7c7be8c2018-03-07 19:58:07 -08001408 private void updateAccessibilityFlags(int accessibilityFlag, CellLayout page) {
1409 page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
1410 page.getShortcutsAndWidgets().setImportantForAccessibility(accessibilityFlag);
1411 page.setContentDescription(null);
1412 page.setAccessibilityDelegate(null);
Adam Cohen3f452c82013-09-19 11:57:17 -07001413 }
1414
Sunny Goyal94b510c2016-08-16 15:36:48 -07001415 public void startDrag(CellLayout.CellInfo cellInfo, DragOptions options) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001416 View child = cellInfo.cell;
Winson Chungaafa03c2010-06-11 17:34:16 -07001417
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001418 mDragInfo = cellInfo;
Adam Cohend41fbf52012-02-16 23:53:59 -08001419 child.setVisibility(INVISIBLE);
Joe Onorato4be866d2010-10-10 11:26:02 -07001420
Sunny Goyal94b510c2016-08-16 15:36:48 -07001421 if (options.isAccessibleDrag) {
Hyunyoung Song0de01172016-10-05 16:27:48 -07001422 mDragController.addDragListener(new AccessibleDragListenerAdapter(
Sunny Goyal94b510c2016-08-16 15:36:48 -07001423 this, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG) {
1424 @Override
1425 protected void enableAccessibleDrag(boolean enable) {
1426 super.enableAccessibleDrag(enable);
Sunny Goyal876e4622018-11-02 13:50:40 -07001427 setEnableForLayout(mLauncher.getHotseat(),enable);
Sunny Goyal94b510c2016-08-16 15:36:48 -07001428 }
1429 });
1430 }
1431
1432 beginDragShared(child, this, options);
Adam Cohenac8c8762011-07-13 11:15:27 -07001433 }
1434
Sunny Goyal94b510c2016-08-16 15:36:48 -07001435 public void beginDragShared(View child, DragSource source, DragOptions options) {
Sunny Goyal10923b32016-07-20 15:42:44 -07001436 Object dragObject = child.getTag();
1437 if (!(dragObject instanceof ItemInfo)) {
1438 String msg = "Drag started with a view that has no tag set. This "
1439 + "will cause a crash (issue 11627249) down the line. "
1440 + "View: " + child + " tag: " + child.getTag();
1441 throw new IllegalStateException(msg);
1442 }
Sunny Goyal94b510c2016-08-16 15:36:48 -07001443 beginDragShared(child, source, (ItemInfo) dragObject,
1444 new DragPreviewProvider(child), options);
Adam Cohenc9735cf2015-01-23 16:11:55 -08001445 }
1446
Sunny Goyal94b510c2016-08-16 15:36:48 -07001447 public DragView beginDragShared(View child, DragSource source, ItemInfo dragObject,
1448 DragPreviewProvider previewProvider, DragOptions dragOptions) {
Jon Miranda31942e72018-05-24 14:25:27 -07001449 float iconScale = 1f;
1450 if (child instanceof BubbleTextView) {
1451 Drawable icon = ((BubbleTextView) child).getIcon();
1452 if (icon instanceof FastBitmapDrawable) {
1453 iconScale = ((FastBitmapDrawable) icon).getAnimatedScale();
1454 }
1455 }
1456
Sunny Goyal508da152014-08-14 10:53:27 -07001457 child.clearFocus();
1458 child.setPressed(false);
Sunny Goyal06e21a22016-08-11 16:02:02 -07001459 mOutlineProvider = previewProvider;
Sunny Goyal508da152014-08-14 10:53:27 -07001460
Joe Onorato4be866d2010-10-10 11:26:02 -07001461 // The drag bitmap follows the touch point around on the screen
Sunny Goyalaa3a2ba2017-09-26 12:43:16 -07001462 final Bitmap b = previewProvider.createDragBitmap();
Sunny Goyal157793d2016-07-20 14:55:26 -07001463 int halfPadding = previewProvider.previewPadding / 2;
Joe Onorato4be866d2010-10-10 11:26:02 -07001464
Sunny Goyala2454ad2016-07-22 10:50:11 -07001465 float scale = previewProvider.getScaleAndPosition(b, mTempXY);
1466 int dragLayerX = mTempXY[0];
1467 int dragLayerY = mTempXY[1];
Adam Cohene3e27a82011-04-15 12:07:39 -07001468
Adam Cohen2e6da152015-05-06 11:42:25 -07001469 DeviceProfile grid = mLauncher.getDeviceProfile();
Winson Chungb8c69f32011-10-19 21:36:08 -07001470 Point dragVisualizeOffset = null;
Adam Cohene3e27a82011-04-15 12:07:39 -07001471 Rect dragRect = null;
Sunny Goyal508da152014-08-14 10:53:27 -07001472 if (child instanceof BubbleTextView) {
Tony Wickham1237df02017-02-24 08:59:36 -08001473 dragRect = new Rect();
1474 ((BubbleTextView) child).getIconBounds(dragRect);
1475 dragLayerY += dragRect.top;
1476 // Note: The dragRect is used to calculate drag layer offsets, but the
Winson Chungb8c69f32011-10-19 21:36:08 -07001477 // dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
Sunny Goyal157793d2016-07-20 14:55:26 -07001478 dragVisualizeOffset = new Point(- halfPadding, halfPadding);
Adam Cohen0e4857c2011-06-30 17:05:14 -07001479 } else if (child instanceof FolderIcon) {
Winson Chung5f8afe62013-08-12 16:19:28 -07001480 int previewSize = grid.folderIconSizePx;
Sunny Goyal157793d2016-07-20 14:55:26 -07001481 dragVisualizeOffset = new Point(- halfPadding, halfPadding - child.getPaddingTop());
Winson Chung5f8afe62013-08-12 16:19:28 -07001482 dragRect = new Rect(0, child.getPaddingTop(), child.getWidth(), previewSize);
Jon Mirandaaa6f66a2017-05-09 12:43:44 -07001483 } else if (previewProvider instanceof ShortcutDragPreviewProvider) {
1484 dragVisualizeOffset = new Point(- halfPadding, halfPadding);
Adam Cohene3e27a82011-04-15 12:07:39 -07001485 }
1486
Winson Chung1e9cbfe2011-09-30 16:52:26 -07001487 // Clear the pressed state if necessary
1488 if (child instanceof BubbleTextView) {
1489 BubbleTextView icon = (BubbleTextView) child;
Sunny Goyal508da152014-08-14 10:53:27 -07001490 icon.clearPressedBackground();
Winson Chung1e9cbfe2011-09-30 16:52:26 -07001491 }
1492
Adam Cohen5084cba2013-09-03 12:01:16 -07001493 if (child.getParent() instanceof ShortcutAndWidgetContainer) {
1494 mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent();
1495 }
1496
Tony Wickham02700392017-05-11 15:20:28 -07001497 if (child instanceof BubbleTextView && !dragOptions.isAccessibleDrag) {
Tony Wickham540913e2017-01-23 11:47:51 -08001498 PopupContainerWithArrow popupContainer = PopupContainerWithArrow
1499 .showForIcon((BubbleTextView) child);
1500 if (popupContainer != null) {
1501 dragOptions.preDragCondition = popupContainer.createPreDragCondition();
Jon Mirandaf3e35d92016-10-05 14:08:11 -07001502
Hyunyoung Songb3fbc0b2018-02-14 13:40:25 -08001503 mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("dragging started");
Tony Wickham10236d62016-09-28 12:49:25 -07001504 }
1505 }
1506
Sunny Goyalaa8ef112015-06-12 20:04:41 -07001507 DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source,
Jon Miranda31942e72018-05-24 14:25:27 -07001508 dragObject, dragVisualizeOffset, dragRect, scale * iconScale, scale, dragOptions);
Sunny Goyal1ce9c472017-10-03 16:17:32 -07001509 dv.setIntrinsicIconScaleFactor(dragOptions.intrinsicIconScaleFactor);
Sunny Goyala2454ad2016-07-22 10:50:11 -07001510 return dv;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001511 }
1512
Tony Wickham251ab6e2016-12-02 18:54:32 -08001513 private boolean transitionStateShouldAllowDrop() {
Sunny Goyalc99cb172017-10-19 16:15:09 -07001514 return (!isSwitchingState() || mTransitionProgress > ALLOW_DROP_TRANSITION_PROGRESS) &&
1515 workspaceIconsCanBeDragged();
Adam Cohen4b285c52011-07-21 14:24:06 -07001516 }
1517
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001518 /**
1519 * {@inheritDoc}
1520 */
Sunny Goyal1ce9c472017-10-03 16:17:32 -07001521 @Override
Adam Cohencb3382b2011-05-24 14:07:08 -07001522 public boolean acceptDrop(DragObject d) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001523 // If it's an external drop (e.g. from All Apps), check if it should be accepted
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001524 CellLayout dropTargetLayout = mDropToLayout;
Adam Cohencb3382b2011-05-24 14:07:08 -07001525 if (d.dragSource != this) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001526 // Don't accept the drop if we're not over a screen at time of drop
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001527 if (dropTargetLayout == null) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001528 return false;
1529 }
Adam Cohen4b285c52011-07-21 14:24:06 -07001530 if (!transitionStateShouldAllowDrop()) return false;
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001531
Sunny Goyal1587d532015-01-29 09:57:16 -08001532 mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);
Adam Cohena65beee2011-06-27 21:32:23 -07001533
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07001534 // We want the point to be mapped to the dragTarget.
Sunny Goyal024659c2018-11-08 11:10:20 -08001535 mapPointFromDropLayout(dropTargetLayout, mDragViewVisualCenter);
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07001536
Rajeev Kumar93c05122017-06-12 12:59:47 -07001537 int spanX;
1538 int spanY;
Winson Chung557d6ed2011-07-08 15:34:52 -07001539 if (mDragInfo != null) {
1540 final CellLayout.CellInfo dragCellInfo = mDragInfo;
1541 spanX = dragCellInfo.spanX;
1542 spanY = dragCellInfo.spanY;
Winson Chung557d6ed2011-07-08 15:34:52 -07001543 } else {
Sunny Goyalaa8ef112015-06-12 20:04:41 -07001544 spanX = d.dragInfo.spanX;
1545 spanY = d.dragInfo.spanY;
Winson Chung557d6ed2011-07-08 15:34:52 -07001546 }
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001547
Adam Cohend41fbf52012-02-16 23:53:59 -08001548 int minSpanX = spanX;
1549 int minSpanY = spanY;
1550 if (d.dragInfo instanceof PendingAddWidgetInfo) {
1551 minSpanX = ((PendingAddWidgetInfo) d.dragInfo).minSpanX;
1552 minSpanY = ((PendingAddWidgetInfo) d.dragInfo).minSpanY;
1553 }
Adam Cohen482ed822012-03-02 14:15:13 -08001554
Adam Cohena65beee2011-06-27 21:32:23 -07001555 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001556 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, dropTargetLayout,
Adam Cohenf0777b92012-02-28 14:02:45 -08001557 mTargetCell);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001558 float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08001559 mDragViewVisualCenter[1], mTargetCell);
Sunny Goyalaa8ef112015-06-12 20:04:41 -07001560 if (mCreateUserFolderOnDrop && willCreateUserFolder(d.dragInfo,
Adam Cohenec0d61d2014-02-07 16:34:51 -08001561 dropTargetLayout, mTargetCell, distance, true)) {
Adam Cohena65beee2011-06-27 21:32:23 -07001562 return true;
1563 }
Adam Cohenec0d61d2014-02-07 16:34:51 -08001564
Sunny Goyalaa8ef112015-06-12 20:04:41 -07001565 if (mAddToExistingFolderOnDrop && willAddToExistingUserFolder(d.dragInfo,
Adam Cohenec0d61d2014-02-07 16:34:51 -08001566 dropTargetLayout, mTargetCell, distance)) {
Adam Cohena65beee2011-06-27 21:32:23 -07001567 return true;
1568 }
1569
Adam Cohen482ed822012-03-02 14:15:13 -08001570 int[] resultSpan = new int[2];
Adam Cohenfa3c58f2013-12-06 16:10:55 -08001571 mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08001572 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
1573 null, mTargetCell, resultSpan, CellLayout.MODE_ACCEPT_DROP);
1574 boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
1575
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001576 // Don't accept the drop if there's no room for the item
Adam Cohen482ed822012-03-02 14:15:13 -08001577 if (!foundCell) {
Jon Miranda9485e5f2016-10-18 11:47:42 -07001578 onNoCellFound(dropTargetLayout);
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001579 return false;
1580 }
1581 }
Adam Cohendedbd962013-07-11 14:21:49 -07001582
Sunny Goyalefb7e842018-10-04 15:11:00 -07001583 int screenId = getIdForScreen(dropTargetLayout);
Adam Cohendedbd962013-07-11 14:21:49 -07001584 if (screenId == EXTRA_EMPTY_SCREEN_ID) {
1585 commitExtraEmptyScreen();
1586 }
1587
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001588 return true;
1589 }
1590
Sunny Goyale78e3d72015-09-24 11:23:31 -07001591 boolean willCreateUserFolder(ItemInfo info, CellLayout target, int[] targetCell,
1592 float distance, boolean considerTimeout) {
Adam Cohen482ed822012-03-02 14:15:13 -08001593 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohena65beee2011-06-27 21:32:23 -07001594 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
Sunny Goyale78e3d72015-09-24 11:23:31 -07001595 return willCreateUserFolder(info, dropOverView, considerTimeout);
1596 }
Adam Cohena65beee2011-06-27 21:32:23 -07001597
Sunny Goyale78e3d72015-09-24 11:23:31 -07001598 boolean willCreateUserFolder(ItemInfo info, View dropOverView, boolean considerTimeout) {
Adam Cohen19f37922012-03-21 11:59:11 -07001599 if (dropOverView != null) {
1600 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
Sunny Goyalb740f592015-12-17 23:22:42 -08001601 if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY)) {
Adam Cohen19f37922012-03-21 11:59:11 -07001602 return false;
1603 }
1604 }
1605
Winson Chung3d503fb2011-07-13 17:25:49 -07001606 boolean hasntMoved = false;
1607 if (mDragInfo != null) {
Adam Cohen482ed822012-03-02 14:15:13 -08001608 hasntMoved = dropOverView == mDragInfo.cell;
Winson Chung3d503fb2011-07-13 17:25:49 -07001609 }
Adam Cohene0310962011-04-18 16:15:31 -07001610
Adam Cohena65beee2011-06-27 21:32:23 -07001611 if (dropOverView == null || hasntMoved || (considerTimeout && !mCreateUserFolderOnDrop)) {
1612 return false;
1613 }
Adam Cohene0310962011-04-18 16:15:31 -07001614
Adam Cohena65beee2011-06-27 21:32:23 -07001615 boolean aboveShortcut = (dropOverView.getTag() instanceof ShortcutInfo);
Adam Cohene0310962011-04-18 16:15:31 -07001616 boolean willBecomeShortcut =
Jon Miranda43348742019-03-06 10:40:13 -08001617 (info.itemType == ITEM_TYPE_APPLICATION ||
Tony Wickhambfbf7f92016-05-19 11:19:39 -07001618 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
1619 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT);
Adam Cohene0310962011-04-18 16:15:31 -07001620
1621 return (aboveShortcut && willBecomeShortcut);
1622 }
1623
Sunny Goyalaa8ef112015-06-12 20:04:41 -07001624 boolean willAddToExistingUserFolder(ItemInfo dragInfo, CellLayout target, int[] targetCell,
Adam Cohen482ed822012-03-02 14:15:13 -08001625 float distance) {
1626 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohena65beee2011-06-27 21:32:23 -07001627 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
Sunny Goyale78e3d72015-09-24 11:23:31 -07001628 return willAddToExistingUserFolder(dragInfo, dropOverView);
Adam Cohen19f37922012-03-21 11:59:11 -07001629
Sunny Goyale78e3d72015-09-24 11:23:31 -07001630 }
Sunny Goyal9d438082015-09-25 11:50:16 -07001631 boolean willAddToExistingUserFolder(ItemInfo dragInfo, View dropOverView) {
Adam Cohen19f37922012-03-21 11:59:11 -07001632 if (dropOverView != null) {
1633 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
Sunny Goyalb740f592015-12-17 23:22:42 -08001634 if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY)) {
Adam Cohen19f37922012-03-21 11:59:11 -07001635 return false;
1636 }
1637 }
1638
Adam Cohena65beee2011-06-27 21:32:23 -07001639 if (dropOverView instanceof FolderIcon) {
1640 FolderIcon fi = (FolderIcon) dropOverView;
1641 if (fi.acceptDrop(dragInfo)) {
1642 return true;
1643 }
1644 }
1645 return false;
1646 }
1647
Sunny Goyalefb7e842018-10-04 15:11:00 -07001648 boolean createUserFolderIfNecessary(View newView, int container, CellLayout target,
Sunny Goyal1797af42017-10-06 13:29:57 -07001649 int[] targetCell, float distance, boolean external, DragView dragView) {
Adam Cohen482ed822012-03-02 14:15:13 -08001650 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohenc0dcf592011-06-01 15:30:43 -07001651 View v = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohen19f37922012-03-21 11:59:11 -07001652
Winson Chungec9a0a42011-07-20 20:42:13 -07001653 boolean hasntMoved = false;
1654 if (mDragInfo != null) {
1655 CellLayout cellParent = getParentCellLayoutForView(mDragInfo.cell);
1656 hasntMoved = (mDragInfo.cellX == targetCell[0] &&
1657 mDragInfo.cellY == targetCell[1]) && (cellParent == target);
1658 }
Adam Cohen10b17372011-04-15 14:21:25 -07001659
Adam Cohen19072da2011-05-31 14:30:45 -07001660 if (v == null || hasntMoved || !mCreateUserFolderOnDrop) return false;
1661 mCreateUserFolderOnDrop = false;
Sunny Goyalefb7e842018-10-04 15:11:00 -07001662 final int screenId = getIdForScreen(target);
Adam Cohen10b17372011-04-15 14:21:25 -07001663
1664 boolean aboveShortcut = (v.getTag() instanceof ShortcutInfo);
1665 boolean willBecomeShortcut = (newView.getTag() instanceof ShortcutInfo);
1666
1667 if (aboveShortcut && willBecomeShortcut) {
1668 ShortcutInfo sourceInfo = (ShortcutInfo) newView.getTag();
1669 ShortcutInfo destInfo = (ShortcutInfo) v.getTag();
1670 // if the drag started here, we need to remove it from the workspace
1671 if (!external) {
Winson Chung3d503fb2011-07-13 17:25:49 -07001672 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
Adam Cohen10b17372011-04-15 14:21:25 -07001673 }
1674
Adam Cohend0445262011-07-04 23:53:22 -07001675 Rect folderLocation = new Rect();
Adam Cohenac8c8762011-07-13 11:15:27 -07001676 float scale = mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation);
Adam Cohen10b17372011-04-15 14:21:25 -07001677 target.removeView(v);
Adam Cohend0445262011-07-04 23:53:22 -07001678
Winson Chung3d503fb2011-07-13 17:25:49 -07001679 FolderIcon fi =
Adam Cohendcd297f2013-06-18 13:13:40 -07001680 mLauncher.addFolder(target, container, screenId, targetCell[0], targetCell[1]);
Adam Cohena9cf38f2011-05-02 15:36:58 -07001681 destInfo.cellX = -1;
1682 destInfo.cellY = -1;
1683 sourceInfo.cellX = -1;
1684 sourceInfo.cellY = -1;
Adam Cohend0445262011-07-04 23:53:22 -07001685
Adam Cohen558baaf2011-08-15 15:22:57 -07001686 // If the dragView is null, we can't animate
1687 boolean animate = dragView != null;
1688 if (animate) {
Adam Cohenefca0272016-02-24 19:19:06 -08001689 // In order to keep everything continuous, we hand off the currently rendered
1690 // folder background to the newly created icon. This preserves animation state.
1691 fi.setFolderBackground(mFolderCreateBg);
Jon Mirandaa0233f72017-06-22 18:34:45 -07001692 mFolderCreateBg = new PreviewBackground();
Sunny Goyal1797af42017-10-06 13:29:57 -07001693 fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale
1694 );
Adam Cohen558baaf2011-08-15 15:22:57 -07001695 } else {
Jon Miranda6c5d1022017-07-06 09:55:50 -07001696 fi.prepareCreateAnimation(v);
Adam Cohen558baaf2011-08-15 15:22:57 -07001697 fi.addItem(destInfo);
1698 fi.addItem(sourceInfo);
1699 }
Adam Cohen10b17372011-04-15 14:21:25 -07001700 return true;
1701 }
1702 return false;
1703 }
1704
Adam Cohena65beee2011-06-27 21:32:23 -07001705 boolean addToExistingFolderIfNecessary(View newView, CellLayout target, int[] targetCell,
Adam Cohen482ed822012-03-02 14:15:13 -08001706 float distance, DragObject d, boolean external) {
1707 if (distance > mMaxDistanceForFolderCreation) return false;
1708
Adam Cohena65beee2011-06-27 21:32:23 -07001709 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001710 if (!mAddToExistingFolderOnDrop) return false;
1711 mAddToExistingFolderOnDrop = false;
Adam Cohen19f37922012-03-21 11:59:11 -07001712
Adam Cohenc0dcf592011-06-01 15:30:43 -07001713 if (dropOverView instanceof FolderIcon) {
1714 FolderIcon fi = (FolderIcon) dropOverView;
Adam Cohen3e8f8112011-07-02 18:03:00 -07001715 if (fi.acceptDrop(d.dragInfo)) {
Jon Miranda6d3586c2017-09-08 12:39:10 -07001716 fi.onDrop(d, false /* itemReturnedOnFailedDrop */);
Adam Cohenc0dcf592011-06-01 15:30:43 -07001717
1718 // if the drag started here, we need to remove it from the workspace
1719 if (!external) {
Winson Chung3d503fb2011-07-13 17:25:49 -07001720 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
Adam Cohenc0dcf592011-06-01 15:30:43 -07001721 }
1722 return true;
1723 }
1724 }
1725 return false;
1726 }
1727
Sunny Goyale9b651e2015-04-24 11:44:51 -07001728 @Override
1729 public void prepareAccessibilityDrop() { }
1730
Sunny Goyal3dce5f32017-10-05 11:40:05 -07001731 public void onDrop(final DragObject d, DragOptions options) {
Sunny Goyal1587d532015-01-29 09:57:16 -08001732 mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001733 CellLayout dropTargetLayout = mDropToLayout;
1734
Adam Cohene3e27a82011-04-15 12:07:39 -07001735 // We want the point to be mapped to the dragTarget.
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001736 if (dropTargetLayout != null) {
Sunny Goyal024659c2018-11-08 11:10:20 -08001737 mapPointFromDropLayout(dropTargetLayout, mDragViewVisualCenter);
Adam Cohenba781612011-05-09 14:37:39 -07001738 }
Michael Jurkac6ee42e2010-09-30 12:04:50 -07001739
Tony Wickham02700392017-05-11 15:20:28 -07001740 boolean droppedOnOriginalCell = false;
1741
Sunny Goyalce5a7e52015-07-08 15:44:27 -07001742 int snapScreen = -1;
Adam Cohend41fbf52012-02-16 23:53:59 -08001743 boolean resizeOnDrop = false;
Sunny Goyal1797af42017-10-06 13:29:57 -07001744 if (d.dragSource != this || mDragInfo == null) {
Adam Cohene3e27a82011-04-15 12:07:39 -07001745 final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0],
1746 (int) mDragViewVisualCenter[1] };
Sunny Goyaldec3a902017-01-25 18:23:36 -08001747 onDropExternal(touchXY, dropTargetLayout, d);
Sunny Goyal1797af42017-10-06 13:29:57 -07001748 } else {
Patrick Dubroyce34a972010-10-19 10:34:32 -07001749 final View cell = mDragInfo.cell;
Tony Wickhamea62fe02016-10-05 09:05:42 -07001750 boolean droppedOnOriginalCellDuringTransition = false;
Sunny Goyal1797af42017-10-06 13:29:57 -07001751 Runnable onCompleteRunnable = null;
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08001752
Michael Jurka1e2f4652013-07-08 18:03:46 -07001753 if (dropTargetLayout != null && !d.cancelled) {
Winson Chung3d503fb2011-07-13 17:25:49 -07001754 // Move internally
Winson Chung40e882b2011-07-21 19:01:11 -07001755 boolean hasMovedLayouts = (getParentCellLayoutForView(cell) != dropTargetLayout);
1756 boolean hasMovedIntoHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
Sunny Goyalefb7e842018-10-04 15:11:00 -07001757 int container = hasMovedIntoHotseat ?
Winson Chung3d503fb2011-07-13 17:25:49 -07001758 LauncherSettings.Favorites.CONTAINER_HOTSEAT :
1759 LauncherSettings.Favorites.CONTAINER_DESKTOP;
Sunny Goyalefb7e842018-10-04 15:11:00 -07001760 int screenId = (mTargetCell[0] < 0) ?
Adam Cohendcd297f2013-06-18 13:13:40 -07001761 mDragInfo.screenId : getIdForScreen(dropTargetLayout);
Adam Cohenc0dcf592011-06-01 15:30:43 -07001762 int spanX = mDragInfo != null ? mDragInfo.spanX : 1;
1763 int spanY = mDragInfo != null ? mDragInfo.spanY : 1;
1764 // First we find the cell nearest to point at which the item is
1765 // dropped, without any consideration to whether there is an item there.
Adam Cohen482ed822012-03-02 14:15:13 -08001766
Adam Cohenc0dcf592011-06-01 15:30:43 -07001767 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], (int)
1768 mDragViewVisualCenter[1], spanX, spanY, dropTargetLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08001769 float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
1770 mDragViewVisualCenter[1], mTargetCell);
1771
Adam Cohenc0dcf592011-06-01 15:30:43 -07001772 // If the item being dropped is a shortcut and the nearest drop
Adam Cohen76078c42011-06-09 15:06:52 -07001773 // cell also contains a shortcut, then create a folder with the two shortcuts.
Sunny Goyalf5440cb2016-12-14 15:13:00 -08001774 if (createUserFolderIfNecessary(cell, container,
Sunny Goyal1797af42017-10-06 13:29:57 -07001775 dropTargetLayout, mTargetCell, distance, false, d.dragView) ||
1776 addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell,
1777 distance, d, false)) {
Sunny Goyal3e3f44c2017-10-23 17:14:52 -07001778 mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
Adam Cohendf035382011-04-11 17:22:04 -07001779 return;
1780 }
1781
1782 // Aside from the special case where we're dropping a shortcut onto a shortcut,
1783 // we need to find the nearest cell location that is vacant
Sunny Goyalaa8ef112015-06-12 20:04:41 -07001784 ItemInfo item = d.dragInfo;
Adam Cohend41fbf52012-02-16 23:53:59 -08001785 int minSpanX = item.spanX;
1786 int minSpanY = item.spanY;
1787 if (item.minSpanX > 0 && item.minSpanY > 0) {
1788 minSpanX = item.minSpanX;
1789 minSpanY = item.minSpanY;
1790 }
Adam Cohen482ed822012-03-02 14:15:13 -08001791
Tony Wickham02700392017-05-11 15:20:28 -07001792 droppedOnOriginalCell = item.screenId == screenId && item.container == container
Tony Wickhamea62fe02016-10-05 09:05:42 -07001793 && item.cellX == mTargetCell[0] && item.cellY == mTargetCell[1];
Tony Wickham02700392017-05-11 15:20:28 -07001794 droppedOnOriginalCellDuringTransition = droppedOnOriginalCell && mIsSwitchingState;
Tony Wickhamea62fe02016-10-05 09:05:42 -07001795
Tony Wickham5dc74352016-12-06 16:24:43 -08001796 // When quickly moving an item, a user may accidentally rearrange their
1797 // workspace. So instead we move the icon back safely to its original position.
1798 boolean returnToOriginalCellToPreventShuffling = !isFinishedSwitchingState()
1799 && !droppedOnOriginalCellDuringTransition && !dropTargetLayout
1800 .isRegionVacant(mTargetCell[0], mTargetCell[1], spanX, spanY);
Adam Cohend41fbf52012-02-16 23:53:59 -08001801 int[] resultSpan = new int[2];
Tony Wickham5dc74352016-12-06 16:24:43 -08001802 if (returnToOriginalCellToPreventShuffling) {
1803 mTargetCell[0] = mTargetCell[1] = -1;
1804 } else {
1805 mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0],
1806 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, cell,
1807 mTargetCell, resultSpan, CellLayout.MODE_ON_DROP);
1808 }
Adam Cohen482ed822012-03-02 14:15:13 -08001809
Adam Cohend41fbf52012-02-16 23:53:59 -08001810 boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
Adam Cohen9e05a5e2012-09-10 15:53:09 -07001811
Adam Cohenaaa5c212012-10-05 18:14:31 -07001812 // if the widget resizes on drop
Adam Cohen9e05a5e2012-09-10 15:53:09 -07001813 if (foundCell && (cell instanceof AppWidgetHostView) &&
Adam Cohenaaa5c212012-10-05 18:14:31 -07001814 (resultSpan[0] != item.spanX || resultSpan[1] != item.spanY)) {
Adam Cohend41fbf52012-02-16 23:53:59 -08001815 resizeOnDrop = true;
1816 item.spanX = resultSpan[0];
1817 item.spanY = resultSpan[1];
Adam Cohen7bdfc972012-05-22 16:50:35 -07001818 AppWidgetHostView awhv = (AppWidgetHostView) cell;
1819 AppWidgetResizeFrame.updateWidgetSizeRanges(awhv, mLauncher, resultSpan[0],
1820 resultSpan[1]);
Adam Cohend41fbf52012-02-16 23:53:59 -08001821 }
Adam Cohendf035382011-04-11 17:22:04 -07001822
Adam Cohend41fbf52012-02-16 23:53:59 -08001823 if (foundCell) {
Tony Wickhamd1f03472016-10-24 10:27:56 -07001824 if (getScreenIdForPageIndex(mCurrentPage) != screenId && !hasMovedIntoHotseat) {
1825 snapScreen = getPageIndexForScreenId(screenId);
1826 snapToPage(snapScreen);
1827 }
1828
Adam Cohend41fbf52012-02-16 23:53:59 -08001829 final ItemInfo info = (ItemInfo) cell.getTag();
Winson Chung40e882b2011-07-21 19:01:11 -07001830 if (hasMovedLayouts) {
Patrick Dubroy94383362010-10-29 15:03:24 -07001831 // Reparent the view
Sunny Goyal25611b12014-07-22 09:52:37 -07001832 CellLayout parentCell = getParentCellLayoutForView(cell);
1833 if (parentCell != null) {
1834 parentCell.removeView(cell);
Sunny Goyal3d706ad2017-03-06 16:56:39 -08001835 } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
Sunny Goyal25611b12014-07-22 09:52:37 -07001836 throw new NullPointerException("mDragInfo.cell has null parent");
1837 }
Adam Cohendcd297f2013-06-18 13:13:40 -07001838 addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1],
Adam Cohend41fbf52012-02-16 23:53:59 -08001839 info.spanX, info.spanY);
Patrick Dubroy94383362010-10-29 15:03:24 -07001840 }
1841
1842 // update the item's position after drop
Patrick Dubroy94383362010-10-29 15:03:24 -07001843 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
Adam Cohen19f37922012-03-21 11:59:11 -07001844 lp.cellX = lp.tmpCellX = mTargetCell[0];
1845 lp.cellY = lp.tmpCellY = mTargetCell[1];
Adam Cohend41fbf52012-02-16 23:53:59 -08001846 lp.cellHSpan = item.spanX;
1847 lp.cellVSpan = item.spanY;
Adam Cohen19f37922012-03-21 11:59:11 -07001848 lp.isLockedToGrid = true;
Patrick Dubroy94383362010-10-29 15:03:24 -07001849
Winson Chung3d503fb2011-07-13 17:25:49 -07001850 if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
1851 cell instanceof LauncherAppWidgetHostView) {
Adam Cohend4844c32011-02-18 19:25:06 -08001852 final CellLayout cellLayout = dropTargetLayout;
1853 // We post this call so that the widget has a chance to be placed
1854 // in its final location
1855
1856 final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell;
Sunny Goyal2434d402015-02-17 11:44:15 -08001857 AppWidgetProviderInfo pInfo = hostView.getAppWidgetInfo();
Adam Cohenc9735cf2015-01-23 16:11:55 -08001858 if (pInfo != null && pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE
1859 && !d.accessibleDrag) {
Sunny Goyal1797af42017-10-06 13:29:57 -07001860 onCompleteRunnable = new Runnable() {
Adam Cohend4844c32011-02-18 19:25:06 -08001861 public void run() {
Tony Wickhamea62fe02016-10-05 09:05:42 -07001862 if (!isPageInTransition()) {
Sunny Goyal37920962017-09-28 13:43:24 -07001863 AppWidgetResizeFrame.showForWidget(hostView, cellLayout);
Adam Cohen26976d92011-03-22 15:33:33 -07001864 }
1865 }
Tony Wickhame0385622015-09-17 09:24:07 -07001866 };
Adam Cohend4844c32011-02-18 19:25:06 -08001867 }
1868 }
1869
Sunny Goyal43bf11d2017-02-02 13:52:53 -08001870 mLauncher.getModelWriter().modifyItemInDatabase(info, container, screenId,
1871 lp.cellX, lp.cellY, item.spanX, item.spanY);
Adam Cohend41fbf52012-02-16 23:53:59 -08001872 } else {
Tony Wickham5dc74352016-12-06 16:24:43 -08001873 if (!returnToOriginalCellToPreventShuffling) {
1874 onNoCellFound(dropTargetLayout);
1875 }
Jon Miranda9485e5f2016-10-18 11:47:42 -07001876
Adam Cohend41fbf52012-02-16 23:53:59 -08001877 // If we can't find a drop location, we return the item to its original position
1878 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
1879 mTargetCell[0] = lp.cellX;
1880 mTargetCell[1] = lp.cellY;
Adam Cohenf1dcdf62012-05-10 16:51:52 -07001881 CellLayout layout = (CellLayout) cell.getParent().getParent();
1882 layout.markCellsAsOccupiedForView(cell);
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001883 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001884 }
Patrick Dubroyce34a972010-10-19 10:34:32 -07001885
Michael Jurka8c920dd2011-01-20 14:16:56 -08001886 final CellLayout parent = (CellLayout) cell.getParent().getParent();
Adam Cohenfc53cd22011-07-20 15:45:11 -07001887 if (d.dragView.hasDrawn()) {
Tony Wickhamea62fe02016-10-05 09:05:42 -07001888 if (droppedOnOriginalCellDuringTransition) {
1889 // Animate the item to its original position, while simultaneously exiting
1890 // spring-loaded mode so the page meets the icon where it was picked up.
1891 mLauncher.getDragController().animateDragViewToOriginalPosition(
Sunny Goyalaeb16432017-10-16 11:46:41 -07001892 onCompleteRunnable, cell, SPRING_LOADED_TRANSITION_MS);
Sunny Goyal3e3f44c2017-10-23 17:14:52 -07001893 mLauncher.getStateManager().goToState(NORMAL);
Tony Wickhamea62fe02016-10-05 09:05:42 -07001894 mLauncher.getDropTargetBar().onDragEnd();
1895 parent.onDropChild(cell);
1896 return;
1897 }
Adam Cohend41fbf52012-02-16 23:53:59 -08001898 final ItemInfo info = (ItemInfo) cell.getTag();
Adam Cohen59400422014-03-05 18:07:04 -08001899 boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
1900 || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
1901 if (isWidget) {
Adam Cohend41fbf52012-02-16 23:53:59 -08001902 int animationType = resizeOnDrop ? ANIMATE_INTO_POSITION_AND_RESIZE :
1903 ANIMATE_INTO_POSITION_AND_DISAPPEAR;
Sunny Goyala502aa32017-10-02 16:04:06 -07001904 animateWidgetDrop(info, parent, d.dragView, null, animationType, cell, false);
Adam Cohend41fbf52012-02-16 23:53:59 -08001905 } else {
Sunny Goyalce5a7e52015-07-08 15:44:27 -07001906 int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION;
Adam Cohen85b467b2012-02-29 15:38:46 -08001907 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration,
Sunny Goyal1797af42017-10-06 13:29:57 -07001908 this);
Adam Cohend41fbf52012-02-16 23:53:59 -08001909 }
Adam Cohenfc53cd22011-07-20 15:45:11 -07001910 } else {
Winson Chung7bd1bbb2012-02-13 18:29:29 -08001911 d.deferDragViewCleanupPostAnimation = false;
Adam Cohenfc53cd22011-07-20 15:45:11 -07001912 cell.setVisibility(VISIBLE);
1913 }
Adam Cohen716b51e2011-06-30 12:09:54 -07001914 parent.onDropChild(cell);
Sunny Goyal1797af42017-10-06 13:29:57 -07001915
Sunny Goyal3e3f44c2017-10-23 17:14:52 -07001916 mLauncher.getStateManager().goToState(
1917 NORMAL, SPRING_LOADED_EXIT_DELAY, onCompleteRunnable);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001918 }
Sunny Goyal1797af42017-10-06 13:29:57 -07001919
Tony Wickham02700392017-05-11 15:20:28 -07001920 if (d.stateAnnouncer != null && !droppedOnOriginalCell) {
Sunny Goyal3f886402016-10-11 10:54:39 -07001921 d.stateAnnouncer.completeAction(R.string.item_moved);
1922 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001923 }
1924
Jon Miranda9485e5f2016-10-18 11:47:42 -07001925 public void onNoCellFound(View dropTargetLayout) {
Sunny Goyal36b54222018-07-10 13:50:50 -07001926 int strId = mLauncher.isHotseatLayout(dropTargetLayout)
1927 ? R.string.hotseat_out_of_space : R.string.out_of_space;
Jon Miranda9485e5f2016-10-18 11:47:42 -07001928 Toast.makeText(mLauncher, mLauncher.getString(strId), Toast.LENGTH_SHORT).show();
1929 }
1930
Sunny Goyal41d84172015-07-08 23:28:51 -07001931 /**
1932 * Computes the area relative to dragLayer which is used to display a page.
1933 */
1934 public void getPageAreaRelativeToDragLayer(Rect outArea) {
1935 CellLayout child = (CellLayout) getChildAt(getNextPage());
1936 if (child == null) {
1937 return;
Adam Cohened51cc92011-08-01 20:28:08 -07001938 }
Sunny Goyal41d84172015-07-08 23:28:51 -07001939 ShortcutAndWidgetContainer boundingLayout = child.getShortcutsAndWidgets();
Adam Cohened51cc92011-08-01 20:28:08 -07001940
Sunny Goyal41d84172015-07-08 23:28:51 -07001941 // Use the absolute left instead of the child left, as we want the visible area
1942 // irrespective of the visible child. Since the view can only scroll horizontally, the
1943 // top position is not affected.
Sunny Goyal228153d2018-01-04 15:35:22 -08001944 mTempXY[0] = getPaddingLeft() + boundingLayout.getLeft();
Sunny Goyal41d84172015-07-08 23:28:51 -07001945 mTempXY[1] = child.getTop() + boundingLayout.getTop();
1946
1947 float scale = mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY);
1948 outArea.set(mTempXY[0], mTempXY[1],
1949 (int) (mTempXY[0] + scale * boundingLayout.getMeasuredWidth()),
1950 (int) (mTempXY[1] + scale * boundingLayout.getMeasuredHeight()));
Adam Cohened51cc92011-08-01 20:28:08 -07001951 }
1952
Sunny Goyal58376922015-05-26 18:56:52 -07001953 @Override
Adam Cohencb3382b2011-05-24 14:07:08 -07001954 public void onDragEnter(DragObject d) {
Sunny Goyal58376922015-05-26 18:56:52 -07001955 if (ENFORCE_DRAG_EVENT_ORDER) {
Hyunyoung Song0de01172016-10-05 16:27:48 -07001956 enforceDragParity("onDragEnter", 1, 1);
Sunny Goyal58376922015-05-26 18:56:52 -07001957 }
1958
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001959 mCreateUserFolderOnDrop = false;
1960 mAddToExistingFolderOnDrop = false;
1961
1962 mDropToLayout = null;
Sunny Goyalaecb7cf2016-12-15 11:02:21 -08001963 mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);
1964 setDropLayoutForDragObject(d, mDragViewVisualCenter[0], mDragViewVisualCenter[1]);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001965 }
1966
Sunny Goyal58376922015-05-26 18:56:52 -07001967 @Override
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001968 public void onDragExit(DragObject d) {
Sunny Goyal58376922015-05-26 18:56:52 -07001969 if (ENFORCE_DRAG_EVENT_ORDER) {
Hyunyoung Song0de01172016-10-05 16:27:48 -07001970 enforceDragParity("onDragExit", -1, 0);
Sunny Goyal58376922015-05-26 18:56:52 -07001971 }
Winson Chungc07918d2011-07-01 15:35:26 -07001972
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001973 // Here we store the final page that will be dropped to, if the workspace in fact
1974 // receives the drop
Sunny Goyalf5440cb2016-12-14 15:13:00 -08001975 mDropToLayout = mDragTargetLayout;
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001976 if (mDragMode == DRAG_MODE_CREATE_FOLDER) {
1977 mCreateUserFolderOnDrop = true;
1978 } else if (mDragMode == DRAG_MODE_ADD_TO_FOLDER) {
1979 mAddToExistingFolderOnDrop = true;
Adam Cohen482ed822012-03-02 14:15:13 -08001980 }
1981
Sunny Goyalf5440cb2016-12-14 15:13:00 -08001982 // Reset the previous drag target
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001983 setCurrentDropLayout(null);
1984 setCurrentDragOverlappingLayout(null);
Winson Chungc07918d2011-07-01 15:35:26 -07001985
Adam Cohen74c28d12011-11-18 14:17:11 -08001986 mSpringLoadedDragController.cancel();
Winson Chungc07918d2011-07-01 15:35:26 -07001987 }
1988
Hyunyoung Song0de01172016-10-05 16:27:48 -07001989 private void enforceDragParity(String event, int update, int expectedValue) {
1990 enforceDragParity(this, event, update, expectedValue);
Sunny Goyal58376922015-05-26 18:56:52 -07001991 for (int i = 0; i < getChildCount(); i++) {
Hyunyoung Song0de01172016-10-05 16:27:48 -07001992 enforceDragParity(getChildAt(i), event, update, expectedValue);
Sunny Goyal58376922015-05-26 18:56:52 -07001993 }
1994 }
1995
Hyunyoung Song0de01172016-10-05 16:27:48 -07001996 private void enforceDragParity(View v, String event, int update, int expectedValue) {
Sunny Goyal58376922015-05-26 18:56:52 -07001997 Object tag = v.getTag(R.id.drag_event_parity);
1998 int value = tag == null ? 0 : (Integer) tag;
1999 value += update;
2000 v.setTag(R.id.drag_event_parity, value);
2001
2002 if (value != expectedValue) {
2003 Log.e(TAG, event + ": Drag contract violated: " + value);
2004 }
2005 }
2006
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002007 void setCurrentDropLayout(CellLayout layout) {
2008 if (mDragTargetLayout != null) {
2009 mDragTargetLayout.revertTempState();
2010 mDragTargetLayout.onDragExit();
2011 }
2012 mDragTargetLayout = layout;
2013 if (mDragTargetLayout != null) {
2014 mDragTargetLayout.onDragEnter();
2015 }
2016 cleanupReorder(true);
2017 cleanupFolderCreation();
2018 setCurrentDropOverCell(-1, -1);
2019 }
2020
2021 void setCurrentDragOverlappingLayout(CellLayout layout) {
2022 if (mDragOverlappingLayout != null) {
2023 mDragOverlappingLayout.setIsDragOverlapping(false);
2024 }
2025 mDragOverlappingLayout = layout;
2026 if (mDragOverlappingLayout != null) {
2027 mDragOverlappingLayout.setIsDragOverlapping(true);
2028 }
Tony Wickham95cdb3a2016-02-18 14:37:07 -08002029 // Invalidating the scrim will also force this CellLayout
2030 // to be invalidated so that it is highlighted if necessary.
Sunny Goyal5d1873a2018-05-08 11:10:44 -07002031 mLauncher.getDragLayer().getScrim().invalidate();
Tony Wickham95cdb3a2016-02-18 14:37:07 -08002032 }
2033
2034 public CellLayout getCurrentDragOverlappingLayout() {
2035 return mDragOverlappingLayout;
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002036 }
2037
2038 void setCurrentDropOverCell(int x, int y) {
2039 if (x != mDragOverX || y != mDragOverY) {
2040 mDragOverX = x;
2041 mDragOverY = y;
2042 setDragMode(DRAG_MODE_NONE);
2043 }
2044 }
2045
2046 void setDragMode(int dragMode) {
2047 if (dragMode != mDragMode) {
2048 if (dragMode == DRAG_MODE_NONE) {
2049 cleanupAddToFolder();
2050 // We don't want to cancel the re-order alarm every time the target cell changes
2051 // as this feels to slow / unresponsive.
2052 cleanupReorder(false);
2053 cleanupFolderCreation();
2054 } else if (dragMode == DRAG_MODE_ADD_TO_FOLDER) {
2055 cleanupReorder(true);
2056 cleanupFolderCreation();
2057 } else if (dragMode == DRAG_MODE_CREATE_FOLDER) {
2058 cleanupAddToFolder();
2059 cleanupReorder(true);
2060 } else if (dragMode == DRAG_MODE_REORDER) {
2061 cleanupAddToFolder();
2062 cleanupFolderCreation();
2063 }
2064 mDragMode = dragMode;
2065 }
2066 }
2067
2068 private void cleanupFolderCreation() {
Adam Cohenf172b742016-03-30 19:28:34 -07002069 if (mFolderCreateBg != null) {
2070 mFolderCreateBg.animateToRest();
2071 }
Adam Cohen8ec05f92013-10-13 12:29:03 -07002072 mFolderCreationAlarm.setOnAlarmListener(null);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002073 mFolderCreationAlarm.cancelAlarm();
2074 }
2075
2076 private void cleanupAddToFolder() {
2077 if (mDragOverFolderIcon != null) {
Sunny Goyale393d3a2016-09-09 12:42:10 -07002078 mDragOverFolderIcon.onDragExit();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002079 mDragOverFolderIcon = null;
2080 }
2081 }
2082
2083 private void cleanupReorder(boolean cancelAlarm) {
2084 // Any pending reorders are canceled
2085 if (cancelAlarm) {
2086 mReorderAlarm.cancelAlarm();
2087 }
2088 mLastReorderX = -1;
2089 mLastReorderY = -1;
Winson Chungc07918d2011-07-01 15:35:26 -07002090 }
2091
Michael Jurka4516c112010-10-07 15:13:47 -07002092 /*
2093 *
2094 * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
2095 * coordinate space. The argument xy is modified with the return result.
Michael Jurka4516c112010-10-07 15:13:47 -07002096 */
Sunny Goyal876e4622018-11-02 13:50:40 -07002097 private void mapPointFromSelfToChild(View v, float[] xy) {
Adam Cohen7d30a372013-07-01 17:03:59 -07002098 xy[0] = xy[0] - v.getLeft();
2099 xy[1] = xy[1] - v.getTop();
Michael Jurka4516c112010-10-07 15:13:47 -07002100 }
2101
Sunny Goyalcf25b522015-07-09 00:01:18 -07002102 boolean isPointInSelfOverHotseat(int x, int y) {
2103 mTempXY[0] = x;
2104 mTempXY[1] = y;
2105 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY, true);
Sunny Goyal6c2975e2016-07-06 09:47:56 -07002106 View hotseat = mLauncher.getHotseat();
2107 return mTempXY[0] >= hotseat.getLeft() &&
2108 mTempXY[0] <= hotseat.getRight() &&
2109 mTempXY[1] >= hotseat.getTop() &&
2110 mTempXY[1] <= hotseat.getBottom();
Adam Cohen7d30a372013-07-01 17:03:59 -07002111 }
Winson Chung3d503fb2011-07-13 17:25:49 -07002112
Sunny Goyal876e4622018-11-02 13:50:40 -07002113 /**
2114 * Updates the point in {@param xy} to point to the co-ordinate space of {@param layout}
2115 * @param layout either hotseat of a page in workspace
2116 * @param xy the point location in workspace co-ordinate space
2117 */
2118 private void mapPointFromDropLayout(CellLayout layout, float[] xy) {
2119 if (mLauncher.isHotseatLayout(layout)) {
2120 mTempXY[0] = (int) xy[0];
2121 mTempXY[1] = (int) xy[1];
2122 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY, true);
2123 mLauncher.getDragLayer().mapCoordInSelfToDescendant(layout, mTempXY);
Adam Cohen7d30a372013-07-01 17:03:59 -07002124
Sunny Goyal876e4622018-11-02 13:50:40 -07002125 xy[0] = mTempXY[0];
2126 xy[1] = mTempXY[1];
2127 } else {
2128 mapPointFromSelfToChild(layout, xy);
2129 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002130 }
2131
Winson Chungea359c62011-08-03 17:06:35 -07002132 private boolean isDragWidget(DragObject d) {
2133 return (d.dragInfo instanceof LauncherAppWidgetInfo ||
2134 d.dragInfo instanceof PendingAddWidgetInfo);
2135 }
Winson Chungea359c62011-08-03 17:06:35 -07002136
Adam Cohencb3382b2011-05-24 14:07:08 -07002137 public void onDragOver(DragObject d) {
Winson Chungc07918d2011-07-01 15:35:26 -07002138 // Skip drag over events while we are dragging over side pages
Sunny Goyalf5440cb2016-12-14 15:13:00 -08002139 if (!transitionStateShouldAllowDrop()) return;
Winson Chungc07918d2011-07-01 15:35:26 -07002140
Sunny Goyalaa8ef112015-06-12 20:04:41 -07002141 ItemInfo item = d.dragInfo;
Sunny Goyal25611b12014-07-22 09:52:37 -07002142 if (item == null) {
Sunny Goyal3d706ad2017-03-06 16:56:39 -08002143 if (FeatureFlags.IS_DOGFOOD_BUILD) {
Sunny Goyal25611b12014-07-22 09:52:37 -07002144 throw new NullPointerException("DragObject has null info");
2145 }
2146 return;
2147 }
Winson Chungc07918d2011-07-01 15:35:26 -07002148
2149 // Ensure that we have proper spans for the item that we are dropping
2150 if (item.spanX < 0 || item.spanY < 0) throw new RuntimeException("Improper spans found");
Sunny Goyal1587d532015-01-29 09:57:16 -08002151 mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);
Adam Cohen00618752011-07-20 12:06:04 -07002152
Adam Cohen482ed822012-03-02 14:15:13 -08002153 final View child = (mDragInfo == null) ? null : mDragInfo.cell;
Sunny Goyalaecb7cf2016-12-15 11:02:21 -08002154 if (setDropLayoutForDragObject(d, mDragViewVisualCenter[0], mDragViewVisualCenter[1])) {
2155 if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
2156 mSpringLoadedDragController.cancel();
2157 } else {
2158 mSpringLoadedDragController.setAlarm(mDragTargetLayout);
Winson Chung4afe9b32011-07-27 17:46:20 -07002159 }
Winson Chungc07918d2011-07-01 15:35:26 -07002160 }
2161
2162 // Handle the drag over
2163 if (mDragTargetLayout != null) {
Winson Chungc07918d2011-07-01 15:35:26 -07002164 // We want the point to be mapped to the dragTarget.
Sunny Goyal024659c2018-11-08 11:10:20 -08002165 mapPointFromDropLayout(mDragTargetLayout, mDragViewVisualCenter);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002166
Adam Cohen74c54912013-09-29 14:48:04 -07002167 int minSpanX = item.spanX;
2168 int minSpanY = item.spanY;
2169 if (item.minSpanX > 0 && item.minSpanY > 0) {
2170 minSpanX = item.minSpanX;
2171 minSpanY = item.minSpanY;
2172 }
2173
Winson Chungc07918d2011-07-01 15:35:26 -07002174 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
Adam Cohen74c54912013-09-29 14:48:04 -07002175 (int) mDragViewVisualCenter[1], minSpanX, minSpanY,
Adam Cohend024f982012-05-23 18:26:45 -07002176 mDragTargetLayout, mTargetCell);
Adam Cohen74c54912013-09-29 14:48:04 -07002177 int reorderX = mTargetCell[0];
2178 int reorderY = mTargetCell[1];
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002179
2180 setCurrentDropOverCell(mTargetCell[0], mTargetCell[1]);
2181
Adam Cohen482ed822012-03-02 14:15:13 -08002182 float targetCellDistance = mDragTargetLayout.getDistanceFromCell(
2183 mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell);
2184
Sunny Goyale78e3d72015-09-24 11:23:31 -07002185 manageFolderFeedback(mDragTargetLayout, mTargetCell, targetCellDistance, d);
Adam Cohen482ed822012-03-02 14:15:13 -08002186
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002187 boolean nearestDropOccupied = mDragTargetLayout.isNearestDropLocationOccupied((int)
2188 mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], item.spanX,
2189 item.spanY, child, mTargetCell);
2190
2191 if (!nearestDropOccupied) {
Sunny Goyal06e21a22016-08-11 16:02:02 -07002192 mDragTargetLayout.visualizeDropLocation(child, mOutlineProvider,
Sunny Goyale78e3d72015-09-24 11:23:31 -07002193 mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, false, d);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002194 } else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
Adam Cohen74c54912013-09-29 14:48:04 -07002195 && !mReorderAlarm.alarmPending() && (mLastReorderX != reorderX ||
2196 mLastReorderY != reorderY)) {
Adam Cohend024f982012-05-23 18:26:45 -07002197
Adam Cohenfa3c58f2013-12-06 16:10:55 -08002198 int[] resultSpan = new int[2];
2199 mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0],
2200 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, item.spanX, item.spanY,
2201 child, mTargetCell, resultSpan, CellLayout.MODE_SHOW_REORDER_HINT);
2202
Adam Cohen19f37922012-03-21 11:59:11 -07002203 // Otherwise, if we aren't adding to or creating a folder and there's no pending
2204 // reorder, then we schedule a reorder
Adam Cohen482ed822012-03-02 14:15:13 -08002205 ReorderAlarmListener listener = new ReorderAlarmListener(mDragViewVisualCenter,
Sunny Goyale78e3d72015-09-24 11:23:31 -07002206 minSpanX, minSpanY, item.spanX, item.spanY, d, child);
Adam Cohen482ed822012-03-02 14:15:13 -08002207 mReorderAlarm.setOnAlarmListener(listener);
2208 mReorderAlarm.setAlarm(REORDER_TIMEOUT);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002209 }
2210
2211 if (mDragMode == DRAG_MODE_CREATE_FOLDER || mDragMode == DRAG_MODE_ADD_TO_FOLDER ||
2212 !nearestDropOccupied) {
2213 if (mDragTargetLayout != null) {
2214 mDragTargetLayout.revertTempState();
Michael Jurkad3ef3062010-11-23 16:23:58 -08002215 }
2216 }
Adam Cohen482ed822012-03-02 14:15:13 -08002217 }
2218 }
2219
Tony Wickhamdbf08b42016-09-12 18:09:33 -07002220 /**
2221 * Updates {@link #mDragTargetLayout} and {@link #mDragOverlappingLayout}
2222 * based on the DragObject's position.
2223 *
2224 * The layout will be:
2225 * - The Hotseat if the drag object is over it
2226 * - A side page if we are in spring-loaded mode and the drag object is over it
2227 * - The current page otherwise
2228 *
2229 * @return whether the layout is different from the current {@link #mDragTargetLayout}.
2230 */
Sunny Goyalaecb7cf2016-12-15 11:02:21 -08002231 private boolean setDropLayoutForDragObject(DragObject d, float centerX, float centerY) {
Tony Wickhamdbf08b42016-09-12 18:09:33 -07002232 CellLayout layout = null;
2233 // Test to see if we are over the hotseat first
2234 if (mLauncher.getHotseat() != null && !isDragWidget(d)) {
2235 if (isPointInSelfOverHotseat(d.x, d.y)) {
Sunny Goyal876e4622018-11-02 13:50:40 -07002236 layout = mLauncher.getHotseat();
Tony Wickhamdbf08b42016-09-12 18:09:33 -07002237 }
2238 }
Sunny Goyalaecb7cf2016-12-15 11:02:21 -08002239
2240 int nextPage = getNextPage();
2241 if (layout == null && !isPageInTransition()) {
2242 // Check if the item is dragged over left page
2243 mTempTouchCoordinates[0] = Math.min(centerX, d.x);
2244 mTempTouchCoordinates[1] = d.y;
2245 layout = verifyInsidePage(nextPage + (mIsRtl ? 1 : -1), mTempTouchCoordinates);
2246 }
2247
2248 if (layout == null && !isPageInTransition()) {
2249 // Check if the item is dragged over right page
2250 mTempTouchCoordinates[0] = Math.max(centerX, d.x);
2251 mTempTouchCoordinates[1] = d.y;
2252 layout = verifyInsidePage(nextPage + (mIsRtl ? -1 : 1), mTempTouchCoordinates);
2253 }
2254
2255 // Always pick the current page.
Sunny Goyal7ce471b2017-08-02 03:37:39 -07002256 if (layout == null && nextPage >= 0 && nextPage < getPageCount()) {
Sunny Goyalaecb7cf2016-12-15 11:02:21 -08002257 layout = (CellLayout) getChildAt(nextPage);
Tony Wickhamdbf08b42016-09-12 18:09:33 -07002258 }
2259 if (layout != mDragTargetLayout) {
2260 setCurrentDropLayout(layout);
2261 setCurrentDragOverlappingLayout(layout);
2262 return true;
2263 }
2264 return false;
2265 }
2266
Sunny Goyalaecb7cf2016-12-15 11:02:21 -08002267 /**
2268 * Returns the child CellLayout if the point is inside the page coordinates, null otherwise.
2269 */
2270 private CellLayout verifyInsidePage(int pageNo, float[] touchXy) {
Sunny Goyal7ce471b2017-08-02 03:37:39 -07002271 if (pageNo >= 0 && pageNo < getPageCount()) {
Sunny Goyalaecb7cf2016-12-15 11:02:21 -08002272 CellLayout cl = (CellLayout) getChildAt(pageNo);
2273 mapPointFromSelfToChild(cl, touchXy);
2274 if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() &&
2275 touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) {
2276 // This point is inside the cell layout
2277 return cl;
2278 }
2279 }
2280 return null;
2281 }
2282
Sunny Goyale78e3d72015-09-24 11:23:31 -07002283 private void manageFolderFeedback(CellLayout targetLayout,
2284 int[] targetCell, float distance, DragObject dragObject) {
Jon Miranda36f63592018-06-12 15:05:12 -07002285 if (distance > mMaxDistanceForFolderCreation) {
2286 if (mDragMode != DRAG_MODE_NONE) {
2287 setDragMode(DRAG_MODE_NONE);
2288 }
2289 return;
2290 }
Sunny Goyale78e3d72015-09-24 11:23:31 -07002291
2292 final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0], mTargetCell[1]);
Sunny Goyal9d438082015-09-25 11:50:16 -07002293 ItemInfo info = dragObject.dragInfo;
Sunny Goyale78e3d72015-09-24 11:23:31 -07002294 boolean userFolderPending = willCreateUserFolder(info, dragOverView, false);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002295 if (mDragMode == DRAG_MODE_NONE && userFolderPending &&
2296 !mFolderCreationAlarm.alarmPending()) {
Adam Cohenc9735cf2015-01-23 16:11:55 -08002297
2298 FolderCreationAlarmListener listener = new
2299 FolderCreationAlarmListener(targetLayout, targetCell[0], targetCell[1]);
2300
Sunny Goyale78e3d72015-09-24 11:23:31 -07002301 if (!dragObject.accessibleDrag) {
Adam Cohenc9735cf2015-01-23 16:11:55 -08002302 mFolderCreationAlarm.setOnAlarmListener(listener);
2303 mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT);
2304 } else {
2305 listener.onAlarm(mFolderCreationAlarm);
2306 }
Sunny Goyale78e3d72015-09-24 11:23:31 -07002307
2308 if (dragObject.stateAnnouncer != null) {
2309 dragObject.stateAnnouncer.announce(WorkspaceAccessibilityHelper
2310 .getDescriptionForDropOver(dragOverView, getContext()));
2311 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002312 return;
Adam Cohen482ed822012-03-02 14:15:13 -08002313 }
2314
Sunny Goyale78e3d72015-09-24 11:23:31 -07002315 boolean willAddToFolder = willAddToExistingUserFolder(info, dragOverView);
Adam Cohen482ed822012-03-02 14:15:13 -08002316 if (willAddToFolder && mDragMode == DRAG_MODE_NONE) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002317 mDragOverFolderIcon = ((FolderIcon) dragOverView);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002318 mDragOverFolderIcon.onDragEnter(info);
Adam Cohen482ed822012-03-02 14:15:13 -08002319 if (targetLayout != null) {
2320 targetLayout.clearDragOutlines();
Winson Chungc07918d2011-07-01 15:35:26 -07002321 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002322 setDragMode(DRAG_MODE_ADD_TO_FOLDER);
Sunny Goyale78e3d72015-09-24 11:23:31 -07002323
2324 if (dragObject.stateAnnouncer != null) {
2325 dragObject.stateAnnouncer.announce(WorkspaceAccessibilityHelper
2326 .getDescriptionForDropOver(dragOverView, getContext()));
2327 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002328 return;
Patrick Dubroy976ebec2010-08-04 20:03:37 -07002329 }
Adam Cohen482ed822012-03-02 14:15:13 -08002330
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002331 if (mDragMode == DRAG_MODE_ADD_TO_FOLDER && !willAddToFolder) {
2332 setDragMode(DRAG_MODE_NONE);
2333 }
2334 if (mDragMode == DRAG_MODE_CREATE_FOLDER && !userFolderPending) {
2335 setDragMode(DRAG_MODE_NONE);
Adam Cohen482ed822012-03-02 14:15:13 -08002336 }
Adam Cohenc0dcf592011-06-01 15:30:43 -07002337 }
2338
Adam Cohen19072da2011-05-31 14:30:45 -07002339 class FolderCreationAlarmListener implements OnAlarmListener {
Rajeev Kumar93c05122017-06-12 12:59:47 -07002340 final CellLayout layout;
2341 final int cellX;
2342 final int cellY;
Adam Cohen19072da2011-05-31 14:30:45 -07002343
Jon Mirandaa0233f72017-06-22 18:34:45 -07002344 final PreviewBackground bg = new PreviewBackground();
Adam Cohenf172b742016-03-30 19:28:34 -07002345
Adam Cohen69ce2e52011-07-03 19:25:21 -07002346 public FolderCreationAlarmListener(CellLayout layout, int cellX, int cellY) {
2347 this.layout = layout;
2348 this.cellX = cellX;
2349 this.cellY = cellY;
Adam Cohenefca0272016-02-24 19:19:06 -08002350
Adam Cohenefca0272016-02-24 19:19:06 -08002351 BubbleTextView cell = (BubbleTextView) layout.getChildAt(cellX, cellY);
Sunny Goyalab770a12018-11-14 15:17:26 -08002352 bg.setup(mLauncher, mLauncher, null, cell.getMeasuredWidth(), cell.getPaddingTop());
Adam Cohenf172b742016-03-30 19:28:34 -07002353
2354 // The full preview background should appear behind the icon
2355 bg.isClipping = false;
Adam Cohen19072da2011-05-31 14:30:45 -07002356 }
2357
2358 public void onAlarm(Alarm alarm) {
Adam Cohenf172b742016-03-30 19:28:34 -07002359 mFolderCreateBg = bg;
Adam Cohenefca0272016-02-24 19:19:06 -08002360 mFolderCreateBg.animateToAccept(layout, cellX, cellY);
Adam Cohen69ce2e52011-07-03 19:25:21 -07002361 layout.clearDragOutlines();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002362 setDragMode(DRAG_MODE_CREATE_FOLDER);
Adam Cohen482ed822012-03-02 14:15:13 -08002363 }
2364 }
2365
2366 class ReorderAlarmListener implements OnAlarmListener {
Rajeev Kumar93c05122017-06-12 12:59:47 -07002367 final float[] dragViewCenter;
2368 final int minSpanX, minSpanY, spanX, spanY;
2369 final DragObject dragObject;
2370 final View child;
Adam Cohen482ed822012-03-02 14:15:13 -08002371
2372 public ReorderAlarmListener(float[] dragViewCenter, int minSpanX, int minSpanY, int spanX,
Sunny Goyale78e3d72015-09-24 11:23:31 -07002373 int spanY, DragObject dragObject, View child) {
Adam Cohen482ed822012-03-02 14:15:13 -08002374 this.dragViewCenter = dragViewCenter;
2375 this.minSpanX = minSpanX;
2376 this.minSpanY = minSpanY;
2377 this.spanX = spanX;
2378 this.spanY = spanY;
2379 this.child = child;
Sunny Goyale78e3d72015-09-24 11:23:31 -07002380 this.dragObject = dragObject;
Adam Cohen482ed822012-03-02 14:15:13 -08002381 }
2382
2383 public void onAlarm(Alarm alarm) {
2384 int[] resultSpan = new int[2];
Adam Cohen19f37922012-03-21 11:59:11 -07002385 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
Adam Cohen74c54912013-09-29 14:48:04 -07002386 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, mDragTargetLayout,
2387 mTargetCell);
Adam Cohen19f37922012-03-21 11:59:11 -07002388 mLastReorderX = mTargetCell[0];
2389 mLastReorderY = mTargetCell[1];
2390
Adam Cohenfa3c58f2013-12-06 16:10:55 -08002391 mTargetCell = mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08002392 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
2393 child, mTargetCell, resultSpan, CellLayout.MODE_DRAG_OVER);
2394
Adam Cohen19f37922012-03-21 11:59:11 -07002395 if (mTargetCell[0] < 0 || mTargetCell[1] < 0) {
2396 mDragTargetLayout.revertTempState();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002397 } else {
2398 setDragMode(DRAG_MODE_REORDER);
Adam Cohen19f37922012-03-21 11:59:11 -07002399 }
Adam Cohen482ed822012-03-02 14:15:13 -08002400
Adam Cohen482ed822012-03-02 14:15:13 -08002401 boolean resize = resultSpan[0] != spanX || resultSpan[1] != spanY;
Sunny Goyal06e21a22016-08-11 16:02:02 -07002402 mDragTargetLayout.visualizeDropLocation(child, mOutlineProvider,
Sunny Goyale78e3d72015-09-24 11:23:31 -07002403 mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], resize, dragObject);
Adam Cohen19072da2011-05-31 14:30:45 -07002404 }
2405 }
2406
Winson Chunga34abf82010-11-12 12:10:35 -08002407 @Override
Adam Cohen7d30a372013-07-01 17:03:59 -07002408 public void getHitRectRelativeToDragLayer(Rect outRect) {
Winson Chunga34abf82010-11-12 12:10:35 -08002409 // We want the workspace to have the whole area of the display (it will find the correct
2410 // cell layout to drop to in the existing drag/drop logic.
Adam Cohen7d30a372013-07-01 17:03:59 -07002411 mLauncher.getDragLayer().getDescendantRectRelativeToSelf(this, outRect);
Winson Chunga34abf82010-11-12 12:10:35 -08002412 }
2413
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07002414 /**
Adam Cohen120980b2010-12-08 11:05:37 -08002415 * Drop an item that didn't originate on one of the workspace screens.
2416 * It may have come from Launcher (e.g. from all apps or customize), or it may have
2417 * come from another app altogether.
2418 *
2419 * NOTE: This can also be called when we are outside of a drag event, when we want
2420 * to add an item to one of the workspace screens.
2421 */
Sunny Goyaldec3a902017-01-25 18:23:36 -08002422 private void onDropExternal(final int[] touchXY, final CellLayout cellLayout, DragObject d) {
Sunny Goyaldec3a902017-01-25 18:23:36 -08002423 if (d.dragInfo instanceof PendingAddShortcutInfo) {
2424 ShortcutInfo si = ((PendingAddShortcutInfo) d.dragInfo)
2425 .activityInfo.createShortcutInfo();
2426 if (si != null) {
2427 d.dragInfo = si;
2428 }
2429 }
2430
2431 ItemInfo info = d.dragInfo;
Adam Cohenb7e16182011-07-15 17:55:02 -07002432 int spanX = info.spanX;
2433 int spanY = info.spanY;
2434 if (mDragInfo != null) {
2435 spanX = mDragInfo.spanX;
2436 spanY = mDragInfo.spanY;
2437 }
2438
Sunny Goyalefb7e842018-10-04 15:11:00 -07002439 final int container = mLauncher.isHotseatLayout(cellLayout) ?
Winson Chung3d503fb2011-07-13 17:25:49 -07002440 LauncherSettings.Favorites.CONTAINER_HOTSEAT :
2441 LauncherSettings.Favorites.CONTAINER_DESKTOP;
Sunny Goyalefb7e842018-10-04 15:11:00 -07002442 final int screenId = getIdForScreen(cellLayout);
Adam Cohendcd297f2013-06-18 13:13:40 -07002443 if (!mLauncher.isHotseatLayout(cellLayout)
2444 && screenId != getScreenIdForPageIndex(mCurrentPage)
Sunny Goyalea609262017-10-25 15:47:38 -07002445 && !mLauncher.isInState(SPRING_LOADED)) {
Sunny Goyal7ce471b2017-08-02 03:37:39 -07002446 snapToPage(getPageIndexForScreenId(screenId));
Adam Cohen76078c42011-06-09 15:06:52 -07002447 }
Adam Cohenb7e16182011-07-15 17:55:02 -07002448
2449 if (info instanceof PendingAddItemInfo) {
Sunny Goyaldec3a902017-01-25 18:23:36 -08002450 final PendingAddItemInfo pendingInfo = (PendingAddItemInfo) info;
Adam Cohenb7e16182011-07-15 17:55:02 -07002451
Adam Cohen558baaf2011-08-15 15:22:57 -07002452 boolean findNearestVacantCell = true;
2453 if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
Rajeev Kumar93c05122017-06-12 12:59:47 -07002454 mTargetCell = findNearestArea(touchXY[0], touchXY[1], spanX, spanY,
Adam Cohen558baaf2011-08-15 15:22:57 -07002455 cellLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08002456 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
2457 mDragViewVisualCenter[1], mTargetCell);
Sunny Goyalaa8ef112015-06-12 20:04:41 -07002458 if (willCreateUserFolder(d.dragInfo, cellLayout, mTargetCell, distance, true)
2459 || willAddToExistingUserFolder(
2460 d.dragInfo, cellLayout, mTargetCell, distance)) {
Adam Cohen558baaf2011-08-15 15:22:57 -07002461 findNearestVacantCell = false;
2462 }
2463 }
Adam Cohen482ed822012-03-02 14:15:13 -08002464
Sunny Goyalaa8ef112015-06-12 20:04:41 -07002465 final ItemInfo item = d.dragInfo;
Adam Cohenaaa5c212012-10-05 18:14:31 -07002466 boolean updateWidgetSize = false;
Adam Cohen558baaf2011-08-15 15:22:57 -07002467 if (findNearestVacantCell) {
Adam Cohen482ed822012-03-02 14:15:13 -08002468 int minSpanX = item.spanX;
2469 int minSpanY = item.spanY;
2470 if (item.minSpanX > 0 && item.minSpanY > 0) {
2471 minSpanX = item.minSpanX;
2472 minSpanY = item.minSpanY;
2473 }
Adam Cohend41fbf52012-02-16 23:53:59 -08002474 int[] resultSpan = new int[2];
Adam Cohenfa3c58f2013-12-06 16:10:55 -08002475 mTargetCell = cellLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08002476 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, info.spanX, info.spanY,
2477 null, mTargetCell, resultSpan, CellLayout.MODE_ON_DROP_EXTERNAL);
Adam Cohen9e05a5e2012-09-10 15:53:09 -07002478
2479 if (resultSpan[0] != item.spanX || resultSpan[1] != item.spanY) {
2480 updateWidgetSize = true;
2481 }
Adam Cohend41fbf52012-02-16 23:53:59 -08002482 item.spanX = resultSpan[0];
2483 item.spanY = resultSpan[1];
Adam Cohen558baaf2011-08-15 15:22:57 -07002484 }
2485
Adam Cohenb7e16182011-07-15 17:55:02 -07002486 Runnable onAnimationCompleteRunnable = new Runnable() {
Winson Chung557d6ed2011-07-08 15:34:52 -07002487 @Override
2488 public void run() {
Adam Cohen689ff162014-05-08 17:27:56 -07002489 // Normally removeExtraEmptyScreen is called in Workspace#onDragEnd, but when
2490 // adding an item that may not be dropped right away (due to a config activity)
2491 // we defer the removal until the activity returns.
2492 deferRemoveExtraEmptyScreen();
2493
Winson Chung557d6ed2011-07-08 15:34:52 -07002494 // When dragging and dropping from customization tray, we deal with creating
2495 // widgets/shortcuts/folders in a slightly different way
Sunny Goyal71b5c0b2015-01-08 16:59:04 -08002496 mLauncher.addPendingItem(pendingInfo, container, screenId, mTargetCell,
2497 item.spanX, item.spanY);
Winson Chung557d6ed2011-07-08 15:34:52 -07002498 }
Adam Cohenb7e16182011-07-15 17:55:02 -07002499 };
Adam Cohen59400422014-03-05 18:07:04 -08002500 boolean isWidget = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
2501 || pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
2502
Sunny Goyalb740f592015-12-17 23:22:42 -08002503 AppWidgetHostView finalView = isWidget ?
2504 ((PendingAddWidgetInfo) pendingInfo).boundWidget : null;
Adam Cohen9e05a5e2012-09-10 15:53:09 -07002505
Sunny Goyalb740f592015-12-17 23:22:42 -08002506 if (finalView != null && updateWidgetSize) {
2507 AppWidgetResizeFrame.updateWidgetSizeRanges(finalView, mLauncher, item.spanX,
Adam Cohen9e05a5e2012-09-10 15:53:09 -07002508 item.spanY);
2509 }
2510
Adam Cohend41fbf52012-02-16 23:53:59 -08002511 int animationStyle = ANIMATE_INTO_POSITION_AND_DISAPPEAR;
Adam Cohen59400422014-03-05 18:07:04 -08002512 if (isWidget && ((PendingAddWidgetInfo) pendingInfo).info != null &&
Sunny Goyaldec3a902017-01-25 18:23:36 -08002513 ((PendingAddWidgetInfo) pendingInfo).getHandler().needsConfigure()) {
Adam Cohend41fbf52012-02-16 23:53:59 -08002514 animationStyle = ANIMATE_INTO_POSITION_AND_REMAIN;
2515 }
2516 animateWidgetDrop(info, cellLayout, d.dragView, onAnimationCompleteRunnable,
2517 animationStyle, finalView, true);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07002518 } else {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08002519 // This is for other drag/drop cases, like dragging from All Apps
Sunny Goyal3e3f44c2017-10-23 17:14:52 -07002520 mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
Sunny Goyal1797af42017-10-06 13:29:57 -07002521
Rajeev Kumar93c05122017-06-12 12:59:47 -07002522 View view;
Michael Jurka9c6de3d2010-11-23 16:23:58 -08002523
2524 switch (info.itemType) {
Jon Miranda43348742019-03-06 10:40:13 -08002525 case ITEM_TYPE_APPLICATION:
Michael Jurka9c6de3d2010-11-23 16:23:58 -08002526 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
Tony Wickhambfbf7f92016-05-19 11:19:39 -07002527 case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
Sunny Goyalc487b362018-07-13 17:03:34 +00002528 if (info.container == NO_ID && info instanceof AppInfo) {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08002529 // Came from all apps -- make a copy
Sunny Goyalc487b362018-07-13 17:03:34 +00002530 info = ((AppInfo) info).makeShortcut();
2531 d.dragInfo = info;
Michael Jurka9c6de3d2010-11-23 16:23:58 -08002532 }
Sunny Goyaldfaccf62015-05-11 16:30:44 -07002533 view = mLauncher.createShortcut(cellLayout, (ShortcutInfo) info);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08002534 break;
Adam Cohendf2cc412011-04-27 16:56:57 -07002535 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
Adam Cohenc0dcf592011-06-01 15:30:43 -07002536 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, cellLayout,
Sunny Goyal1cd01b02016-11-09 10:43:58 -08002537 (FolderInfo) info);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08002538 break;
2539 default:
2540 throw new IllegalStateException("Unknown item type: " + info.itemType);
2541 }
2542
Adam Cohenc0dcf592011-06-01 15:30:43 -07002543 // First we find the cell nearest to point at which the item is
2544 // dropped, without any consideration to whether there is an item there.
2545 if (touchXY != null) {
Rajeev Kumar93c05122017-06-12 12:59:47 -07002546 mTargetCell = findNearestArea(touchXY[0], touchXY[1], spanX, spanY,
Adam Cohenc0dcf592011-06-01 15:30:43 -07002547 cellLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08002548 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
2549 mDragViewVisualCenter[1], mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08002550 if (createUserFolderIfNecessary(view, container, cellLayout, mTargetCell, distance,
Sunny Goyal1797af42017-10-06 13:29:57 -07002551 true, d.dragView)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07002552 return;
2553 }
Adam Cohen482ed822012-03-02 14:15:13 -08002554 if (addToExistingFolderIfNecessary(view, cellLayout, mTargetCell, distance, d,
2555 true)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07002556 return;
2557 }
Adam Cohen10b17372011-04-15 14:21:25 -07002558 }
2559
Michael Jurkac4e772e2011-02-10 13:32:01 -08002560 if (touchXY != null) {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08002561 // when dragging and dropping, just find the closest free spot
Adam Cohenfa3c58f2013-12-06 16:10:55 -08002562 mTargetCell = cellLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08002563 (int) mDragViewVisualCenter[1], 1, 1, 1, 1,
Adam Cohenea889a22012-03-27 16:45:39 -07002564 null, mTargetCell, null, CellLayout.MODE_ON_DROP_EXTERNAL);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08002565 } else {
2566 cellLayout.findCellForSpan(mTargetCell, 1, 1);
2567 }
Sunny Goyal95abbb32014-08-04 10:53:22 -07002568 // Add the item to DB before adding to screen ensures that the container and other
2569 // values of the info is properly updated.
Sunny Goyal43bf11d2017-02-02 13:52:53 -08002570 mLauncher.getModelWriter().addOrMoveItemInDatabase(info, container, screenId,
Sunny Goyal95abbb32014-08-04 10:53:22 -07002571 mTargetCell[0], mTargetCell[1]);
2572
Sunny Goyald5462aa2016-11-22 16:52:39 +05302573 addInScreen(view, container, screenId, mTargetCell[0], mTargetCell[1],
2574 info.spanX, info.spanY);
Adam Cohen716b51e2011-06-30 12:09:54 -07002575 cellLayout.onDropChild(view);
Michael Jurkaa52570f2012-03-20 03:18:20 -07002576 cellLayout.getShortcutsAndWidgets().measureChild(view);
Adam Cohend5e42732011-03-28 17:33:39 -07002577
Adam Cohen3e8f8112011-07-02 18:03:00 -07002578 if (d.dragView != null) {
Adam Cohen4b285c52011-07-21 14:24:06 -07002579 // We wrap the animation call in the temporary set and reset of the current
2580 // cellLayout to its final transform -- this means we animate the drag view to
2581 // the correct final location.
Sunny Goyalaeb16432017-10-16 11:46:41 -07002582 setFinalTransitionTransform();
2583 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view, this);
2584 resetTransitionTransform();
Adam Cohen3e8f8112011-07-02 18:03:00 -07002585 }
Joe Onorato00acb122009-08-04 16:04:30 -04002586 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002587 }
Winson Chungaafa03c2010-06-11 17:34:16 -07002588
Adam Cohend41fbf52012-02-16 23:53:59 -08002589 public Bitmap createWidgetBitmap(ItemInfo widgetInfo, View layout) {
Sunny Goyalaa3a2ba2017-09-26 12:43:16 -07002590 int[] unScaledSize = estimateItemSize(widgetInfo);
Adam Cohend41fbf52012-02-16 23:53:59 -08002591 int visibility = layout.getVisibility();
Adam Cohened66b2b2012-01-23 17:28:51 -08002592 layout.setVisibility(VISIBLE);
2593
2594 int width = MeasureSpec.makeMeasureSpec(unScaledSize[0], MeasureSpec.EXACTLY);
2595 int height = MeasureSpec.makeMeasureSpec(unScaledSize[1], MeasureSpec.EXACTLY);
2596 Bitmap b = Bitmap.createBitmap(unScaledSize[0], unScaledSize[1],
2597 Bitmap.Config.ARGB_8888);
Adam Cohened66b2b2012-01-23 17:28:51 -08002598 layout.measure(width, height);
2599 layout.layout(0, 0, unScaledSize[0], unScaledSize[1]);
Sunny Goyalaa3a2ba2017-09-26 12:43:16 -07002600 layout.draw(new Canvas(b));
Adam Cohend41fbf52012-02-16 23:53:59 -08002601 layout.setVisibility(visibility);
Adam Cohened66b2b2012-01-23 17:28:51 -08002602 return b;
2603 }
2604
Adam Cohend41fbf52012-02-16 23:53:59 -08002605 private void getFinalPositionForDropAnimation(int[] loc, float[] scaleXY,
Tony Wickhama501d492015-11-03 18:05:01 -08002606 DragView dragView, CellLayout layout, ItemInfo info, int[] targetCell, boolean scale) {
Adam Cohened66b2b2012-01-23 17:28:51 -08002607 // Now we animate the dragView, (ie. the widget or shortcut preview) into its final
2608 // location and size on the home screen.
Adam Cohend41fbf52012-02-16 23:53:59 -08002609 int spanX = info.spanX;
2610 int spanY = info.spanY;
Adam Cohened66b2b2012-01-23 17:28:51 -08002611
Tony Wickhama501d492015-11-03 18:05:01 -08002612 Rect r = estimateItemPosition(layout, targetCell[0], targetCell[1], spanX, spanY);
Jon Mirandae96798e2016-12-07 12:10:44 -08002613 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET) {
2614 DeviceProfile profile = mLauncher.getDeviceProfile();
Jon Miranda6f6a06a2016-12-15 11:24:18 -08002615 Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y);
Jon Mirandae96798e2016-12-07 12:10:44 -08002616 }
Adam Cohend41fbf52012-02-16 23:53:59 -08002617 loc[0] = r.left;
2618 loc[1] = r.top;
2619
Sunny Goyalaeb16432017-10-16 11:46:41 -07002620 setFinalTransitionTransform();
Adam Cohend41fbf52012-02-16 23:53:59 -08002621 float cellLayoutScale =
Adam Cohen7d30a372013-07-01 17:03:59 -07002622 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(layout, loc, true);
Sunny Goyalaeb16432017-10-16 11:46:41 -07002623 resetTransitionTransform();
Michael Jurkaa2fe7862012-05-29 05:58:17 -07002624
Michael Jurkaa2fe7862012-05-29 05:58:17 -07002625 if (scale) {
Sunny Goyaldec3a902017-01-25 18:23:36 -08002626 float dragViewScaleX = (1.0f * r.width()) / dragView.getMeasuredWidth();
2627 float dragViewScaleY = (1.0f * r.height()) / dragView.getMeasuredHeight();
2628
2629 // The animation will scale the dragView about its center, so we need to center about
2630 // the final location.
2631 loc[0] -= (dragView.getMeasuredWidth() - cellLayoutScale * r.width()) / 2
2632 - Math.ceil(layout.getUnusedHorizontalSpace() / 2f);
2633 loc[1] -= (dragView.getMeasuredHeight() - cellLayoutScale * r.height()) / 2;
2634 scaleXY[0] = dragViewScaleX * cellLayoutScale;
2635 scaleXY[1] = dragViewScaleY * cellLayoutScale;
2636 } else {
2637 // Since we are not cross-fading the dragView, align the drag view to the
2638 // final cell position.
2639 float dragScale = dragView.getInitialScale() * cellLayoutScale;
2640 loc[0] += (dragScale - 1) * dragView.getWidth() / 2;
2641 loc[1] += (dragScale - 1) * dragView.getHeight() / 2;
2642 scaleXY[0] = scaleXY[1] = dragScale;
2643
2644 // If a dragRegion was provided, offset the final position accordingly.
2645 Rect dragRegion = dragView.getDragRegion();
2646 if (dragRegion != null) {
2647 loc[0] += cellLayoutScale * dragRegion.left;
2648 loc[1] += cellLayoutScale * dragRegion.top;
2649 }
Michael Jurkaa2fe7862012-05-29 05:58:17 -07002650 }
Adam Cohend41fbf52012-02-16 23:53:59 -08002651 }
2652
Tony Wickhama501d492015-11-03 18:05:01 -08002653 public void animateWidgetDrop(ItemInfo info, CellLayout cellLayout, final DragView dragView,
Adam Cohend41fbf52012-02-16 23:53:59 -08002654 final Runnable onCompleteRunnable, int animationType, final View finalView,
2655 boolean external) {
2656 Rect from = new Rect();
2657 mLauncher.getDragLayer().getViewRectRelativeToSelf(dragView, from);
2658
2659 int[] finalPos = new int[2];
2660 float scaleXY[] = new float[2];
Michael Jurkaa2fe7862012-05-29 05:58:17 -07002661 boolean scalePreview = !(info instanceof PendingAddShortcutInfo);
Adam Cohend41fbf52012-02-16 23:53:59 -08002662 getFinalPositionForDropAnimation(finalPos, scaleXY, dragView, cellLayout, info, mTargetCell,
Tony Wickhama501d492015-11-03 18:05:01 -08002663 scalePreview);
Adam Cohened66b2b2012-01-23 17:28:51 -08002664
2665 Resources res = mLauncher.getResources();
Adam Cohenad4e15c2013-10-17 16:21:35 -07002666 final int duration = res.getInteger(R.integer.config_dropAnimMaxDuration) - 200;
Adam Cohened66b2b2012-01-23 17:28:51 -08002667
Adam Cohen59400422014-03-05 18:07:04 -08002668 boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET ||
2669 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
Adam Cohend41fbf52012-02-16 23:53:59 -08002670 if ((animationType == ANIMATE_INTO_POSITION_AND_RESIZE || external) && finalView != null) {
2671 Bitmap crossFadeBitmap = createWidgetBitmap(info, finalView);
Adam Cohened66b2b2012-01-23 17:28:51 -08002672 dragView.setCrossFadeBitmap(crossFadeBitmap);
2673 dragView.crossFade((int) (duration * 0.8f));
Adam Cohen59400422014-03-05 18:07:04 -08002674 } else if (isWidget && external) {
Adam Cohend41fbf52012-02-16 23:53:59 -08002675 scaleXY[0] = scaleXY[1] = Math.min(scaleXY[0], scaleXY[1]);
Adam Cohened66b2b2012-01-23 17:28:51 -08002676 }
2677
Adam Cohend41fbf52012-02-16 23:53:59 -08002678 DragLayer dragLayer = mLauncher.getDragLayer();
Winson Chung7bd1bbb2012-02-13 18:29:29 -08002679 if (animationType == CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION) {
Adam Cohend41fbf52012-02-16 23:53:59 -08002680 mLauncher.getDragLayer().animateViewIntoPosition(dragView, finalPos, 0f, 0.1f, 0.1f,
Adam Cohened66b2b2012-01-23 17:28:51 -08002681 DragLayer.ANIMATION_END_DISAPPEAR, onCompleteRunnable, duration);
2682 } else {
Adam Cohend41fbf52012-02-16 23:53:59 -08002683 int endStyle;
2684 if (animationType == ANIMATE_INTO_POSITION_AND_REMAIN) {
2685 endStyle = DragLayer.ANIMATION_END_REMAIN_VISIBLE;
2686 } else {
Tony Wickhama501d492015-11-03 18:05:01 -08002687 endStyle = DragLayer.ANIMATION_END_DISAPPEAR;
Adam Cohend41fbf52012-02-16 23:53:59 -08002688 }
2689
2690 Runnable onComplete = new Runnable() {
2691 @Override
2692 public void run() {
2693 if (finalView != null) {
2694 finalView.setVisibility(VISIBLE);
2695 }
2696 if (onCompleteRunnable != null) {
2697 onCompleteRunnable.run();
2698 }
2699 }
2700 };
2701 dragLayer.animateViewIntoPosition(dragView, from.left, from.top, finalPos[0],
2702 finalPos[1], 1, 1, 1, scaleXY[0], scaleXY[1], onComplete, endStyle,
2703 duration, this);
Adam Cohened66b2b2012-01-23 17:28:51 -08002704 }
2705 }
2706
Sunny Goyalaeb16432017-10-16 11:46:41 -07002707 public void setFinalTransitionTransform() {
Adam Cohen4b285c52011-07-21 14:24:06 -07002708 if (isSwitchingState()) {
Adam Cohen7d30a372013-07-01 17:03:59 -07002709 mCurrentScale = getScaleX();
Winson Chungdc61c4d2015-04-20 18:26:57 -07002710 setScaleX(mStateTransitionAnimation.getFinalScale());
2711 setScaleY(mStateTransitionAnimation.getFinalScale());
Adam Cohen4b285c52011-07-21 14:24:06 -07002712 }
2713 }
Sunny Goyalaeb16432017-10-16 11:46:41 -07002714 public void resetTransitionTransform() {
Adam Cohen4b285c52011-07-21 14:24:06 -07002715 if (isSwitchingState()) {
Adam Cohen7d30a372013-07-01 17:03:59 -07002716 setScaleX(mCurrentScale);
2717 setScaleY(mCurrentScale);
Adam Cohen4b285c52011-07-21 14:24:06 -07002718 }
2719 }
2720
Jeff Sharkey70864282009-04-07 21:08:40 -07002721 /**
Michael Jurka0280c3b2010-09-17 15:00:07 -07002722 * Return the current CellInfo describing our current drag; this method exists
2723 * so that Launcher can sync this object with the correct info when the activity is created/
2724 * destroyed
2725 *
2726 */
2727 public CellLayout.CellInfo getDragInfo() {
2728 return mDragInfo;
2729 }
2730
2731 /**
Jeff Sharkey70864282009-04-07 21:08:40 -07002732 * Calculate the nearest cell where the given object would be dropped.
Adam Cohene3e27a82011-04-15 12:07:39 -07002733 *
2734 * pixelX and pixelY should be in the coordinate system of layout
Jeff Sharkey70864282009-04-07 21:08:40 -07002735 */
Adam Cohen091440a2015-03-18 14:16:05 -07002736 @Thunk int[] findNearestArea(int pixelX, int pixelY,
Adam Cohene3e27a82011-04-15 12:07:39 -07002737 int spanX, int spanY, CellLayout layout, int[] recycle) {
Adam Cohendf035382011-04-11 17:22:04 -07002738 return layout.findNearestArea(
Adam Cohene3e27a82011-04-15 12:07:39 -07002739 pixelX, pixelY, spanX, spanY, recycle);
Adam Cohendf035382011-04-11 17:22:04 -07002740 }
2741
Adam Cohencff6af82011-09-13 14:51:53 -07002742 void setup(DragController dragController) {
Michael Jurkac2f7f472010-12-14 15:34:42 -08002743 mSpringLoadedDragController = new SpringLoadedDragController(mLauncher);
Joe Onorato00acb122009-08-04 16:04:30 -04002744 mDragController = dragController;
Michael Jurkad74c9842011-07-10 12:44:21 -07002745
Michael Jurkad74c9842011-07-10 12:44:21 -07002746 // hardware layers on children are enabled on startup, but should be disabled until
2747 // needed
Sunny Goyala502aa32017-10-02 16:04:06 -07002748 updateChildrenLayersEnabled();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002749 }
2750
Patrick Dubroye3887cc2011-01-20 10:43:40 -08002751 /**
2752 * Called at the end of a drag which originated on the workspace.
2753 */
Michael Jurka1e2f4652013-07-08 18:03:46 -07002754 public void onDropCompleted(final View target, final DragObject d,
Sunny Goyal1797af42017-10-06 13:29:57 -07002755 final boolean success) {
Sunny Goyal3dce5f32017-10-05 11:40:05 -07002756 if (success) {
Sunny Goyal95deb3a2015-03-05 10:27:01 -08002757 if (target != this && mDragInfo != null) {
Sunny Goyal71b5c0b2015-01-08 16:59:04 -08002758 removeWorkspaceItem(mDragInfo.cell);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002759 }
Patrick Dubroyce34a972010-10-19 10:34:32 -07002760 } else if (mDragInfo != null) {
Sunny Goyal92820592015-03-02 11:31:35 -08002761 final CellLayout cellLayout = mLauncher.getCellLayout(
2762 mDragInfo.container, mDragInfo.screenId);
Jorim Jaggieedb00a2014-01-13 13:45:07 -08002763 if (cellLayout != null) {
2764 cellLayout.onDropChild(mDragInfo.cell);
Sunny Goyal3d706ad2017-03-06 16:56:39 -08002765 } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
Sunny Goyal92820592015-03-02 11:31:35 -08002766 throw new RuntimeException("Invalid state: cellLayout == null in "
2767 + "Workspace#onDropCompleted. Please file a bug. ");
Rajeev Kumar93c05122017-06-12 12:59:47 -07002768 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002769 }
Tony4d910322018-03-08 13:43:48 +00002770 View cell = getHomescreenIconByItemId(d.originalDragInfo.id);
2771 if (d.cancelled && cell != null) {
2772 cell.setVisibility(VISIBLE);
Adam Cohen36cc09b2011-09-29 17:33:15 -07002773 }
Tony Wickham4feb27e2016-10-26 12:51:14 -07002774 mDragInfo = null;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002775 }
2776
Hyunyoung Song31178b82015-02-24 14:12:51 -08002777 /**
2778 * For opposite operation. See {@link #addInScreen}.
2779 */
Sunny Goyal71b5c0b2015-01-08 16:59:04 -08002780 public void removeWorkspaceItem(View v) {
2781 CellLayout parentCell = getParentCellLayoutForView(v);
2782 if (parentCell != null) {
2783 parentCell.removeView(v);
Sunny Goyal3d706ad2017-03-06 16:56:39 -08002784 } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
Sunny Goyal912bdfc2015-08-03 19:09:55 -07002785 // When an app is uninstalled using the drop target, we wait until resume to remove
2786 // the icon. We also remove all the corresponding items from the workspace at
2787 // {@link Launcher#bindComponentsRemoved}. That call can come before or after
2788 // {@link Launcher#mOnResumeCallbacks} depending on how busy the worker thread is.
2789 Log.e(TAG, "mDragInfo.cell has null parent");
Sunny Goyal71b5c0b2015-01-08 16:59:04 -08002790 }
2791 if (v instanceof DropTarget) {
2792 mDragController.removeDropTarget((DropTarget) v);
2793 }
2794 }
2795
Sunny Goyalaaf7d1d2016-05-17 13:38:54 -07002796 /**
2797 * Removes all folder listeners
2798 */
2799 public void removeFolderListeners() {
2800 mapOverItems(false, new ItemOperator() {
2801 @Override
2802 public boolean evaluate(ItemInfo info, View view) {
2803 if (view instanceof FolderIcon) {
2804 ((FolderIcon) view).removeListeners();
2805 }
2806 return false;
2807 }
2808 });
2809 }
2810
Michael Jurka0280c3b2010-09-17 15:00:07 -07002811 public boolean isDropEnabled() {
2812 return true;
2813 }
2814
Michael Jurka0142d492010-08-25 17:46:15 -07002815 @Override
Adam Cohen1462de32012-07-24 22:34:36 -07002816 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
2817 // We don't dispatch restoreInstanceState to our children using this code path.
Dave Hawkeya8881582013-09-17 15:55:33 -06002818 // Some pages will be restored immediately as their items are bound immediately, and
Adam Cohen1462de32012-07-24 22:34:36 -07002819 // others we will need to wait until after their items are bound.
2820 mSavedStates = container;
2821 }
2822
2823 public void restoreInstanceStateForChild(int child) {
2824 if (mSavedStates != null) {
2825 mRestoredPages.add(child);
2826 CellLayout cl = (CellLayout) getChildAt(child);
Adam Cohenb0ee0812013-12-03 10:51:45 -08002827 if (cl != null) {
2828 cl.restoreInstanceState(mSavedStates);
2829 }
Adam Cohen1462de32012-07-24 22:34:36 -07002830 }
2831 }
2832
2833 public void restoreInstanceStateForRemainingPages() {
2834 int count = getChildCount();
2835 for (int i = 0; i < count; i++) {
2836 if (!mRestoredPages.contains(i)) {
2837 restoreInstanceStateForChild(i);
2838 }
2839 }
2840 mRestoredPages.clear();
Winson Chungd8e596d2013-10-21 17:14:12 -07002841 mSavedStates = null;
Adam Cohen1462de32012-07-24 22:34:36 -07002842 }
2843
2844 @Override
Vadim Tryshev98913d02018-05-18 18:41:34 -07002845 public boolean scrollLeft() {
2846 boolean result = false;
Adam Cohen6c5891a2014-07-09 23:53:15 -07002847 if (!workspaceInModalState() && !mIsSwitchingState) {
Vadim Tryshev98913d02018-05-18 18:41:34 -07002848 result = super.scrollLeft();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002849 }
Sunny Goyal740ac7f2016-09-28 16:47:32 -07002850 Folder openFolder = Folder.getOpen(mLauncher);
Adam Cohen95bb8002011-07-03 23:40:28 -07002851 if (openFolder != null) {
2852 openFolder.completeDragExit();
2853 }
Vadim Tryshev98913d02018-05-18 18:41:34 -07002854 return result;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002855 }
2856
Michael Jurka0142d492010-08-25 17:46:15 -07002857 @Override
Vadim Tryshev98913d02018-05-18 18:41:34 -07002858 public boolean scrollRight() {
2859 boolean result = false;
Adam Cohen6c5891a2014-07-09 23:53:15 -07002860 if (!workspaceInModalState() && !mIsSwitchingState) {
Vadim Tryshev98913d02018-05-18 18:41:34 -07002861 result = super.scrollRight();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002862 }
Sunny Goyal740ac7f2016-09-28 16:47:32 -07002863 Folder openFolder = Folder.getOpen(mLauncher);
Adam Cohen95bb8002011-07-03 23:40:28 -07002864 if (openFolder != null) {
2865 openFolder.completeDragExit();
2866 }
Vadim Tryshev98913d02018-05-18 18:41:34 -07002867 return result;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002868 }
2869
Winson Chung3d503fb2011-07-13 17:25:49 -07002870 /**
2871 * Returns a specific CellLayout
2872 */
2873 CellLayout getParentCellLayoutForView(View v) {
Sunny Goyal876e4622018-11-02 13:50:40 -07002874 for (CellLayout layout : getWorkspaceAndHotseatCellLayouts()) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07002875 if (layout.getShortcutsAndWidgets().indexOfChild(v) > -1) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002876 return layout;
2877 }
2878 }
2879 return null;
2880 }
2881
2882 /**
Sunny Goyal876e4622018-11-02 13:50:40 -07002883 * Returns a list of all the CellLayouts on the Homescreen.
Winson Chung3d503fb2011-07-13 17:25:49 -07002884 */
Sunny Goyal876e4622018-11-02 13:50:40 -07002885 private CellLayout[] getWorkspaceAndHotseatCellLayouts() {
Winson Chung3d503fb2011-07-13 17:25:49 -07002886 int screenCount = getChildCount();
Sunny Goyal876e4622018-11-02 13:50:40 -07002887 final CellLayout[] layouts;
Winson Chung3d503fb2011-07-13 17:25:49 -07002888 if (mLauncher.getHotseat() != null) {
Sunny Goyal876e4622018-11-02 13:50:40 -07002889 layouts = new CellLayout[screenCount + 1];
2890 layouts[screenCount] = mLauncher.getHotseat();
2891 } else {
2892 layouts = new CellLayout[screenCount];
2893 }
2894 for (int screen = 0; screen < screenCount; screen++) {
2895 layouts[screen] = (CellLayout) getChildAt(screen);
Winson Chung3d503fb2011-07-13 17:25:49 -07002896 }
2897 return layouts;
2898 }
2899
Jon Miranda0128d3f2019-02-14 09:19:31 -08002900 /**
Jon Miranda0128d3f2019-02-14 09:19:31 -08002901 * Similar to {@link #getFirstMatch} but optimized to finding a suitable view for the app close
2902 * animation.
2903 *
Jon Miranda43348742019-03-06 10:40:13 -08002904 * @param packageName The package name of the app to match.
2905 * @param user The user of the app to match.
Jon Miranda0128d3f2019-02-14 09:19:31 -08002906 */
Jon Miranda43348742019-03-06 10:40:13 -08002907 public View getFirstMatchForAppClose(String packageName, UserHandle user) {
Jon Miranda0128d3f2019-02-14 09:19:31 -08002908 final int curPage = getCurrentPage();
2909 final CellLayout currentPage = (CellLayout) getPageAt(curPage);
Jon Miranda43348742019-03-06 10:40:13 -08002910 final Workspace.ItemOperator packageAndUser = (ItemInfo info, View view) -> info != null
2911 && info.getTargetComponent() != null
2912 && TextUtils.equals(info.getTargetComponent().getPackageName(), packageName)
2913 && info.user.equals(user);
2914 final Workspace.ItemOperator packageAndUserAndApp = (ItemInfo info, View view) ->
2915 packageAndUser.evaluate(info, view) && info.itemType == ITEM_TYPE_APPLICATION;
2916 final Workspace.ItemOperator packageAndUserAndShortcut = (ItemInfo info, View view) ->
2917 packageAndUser.evaluate(info, view) && (info.itemType == ITEM_TYPE_SHORTCUT
2918 || info.itemType == ITEM_TYPE_DEEP_SHORTCUT);
2919 final Workspace.ItemOperator packageAndUserInFolder = (info, view) -> {
Jon Miranda0128d3f2019-02-14 09:19:31 -08002920 if (info instanceof FolderInfo) {
2921 FolderInfo folderInfo = (FolderInfo) info;
2922 for (ShortcutInfo shortcutInfo : folderInfo.contents) {
Jon Miranda43348742019-03-06 10:40:13 -08002923 if (packageAndUser.evaluate(shortcutInfo, view)) {
Jon Miranda0128d3f2019-02-14 09:19:31 -08002924 return true;
2925 }
2926 }
2927 }
2928 return false;
2929 };
2930
Jon Miranda43348742019-03-06 10:40:13 -08002931 // Order: App icons, shortcuts, app/shortcut in folder. Items in hotseat get returned first.
2932 if (ADAPTIVE_ICON_WINDOW_ANIM.get()) {
2933 return getFirstMatch(new CellLayout[] { getHotseat(), currentPage },
2934 packageAndUserAndApp, packageAndUserAndShortcut, packageAndUserInFolder);
2935 } else {
2936 // Do not use Folder as a criteria, since it'll cause a crash when trying to draw
2937 // FolderAdaptiveIcon as the background.
2938 return getFirstMatch(new CellLayout[] { getHotseat(), currentPage },
2939 packageAndUserAndApp, packageAndUserAndShortcut);
Jon Miranda0128d3f2019-02-14 09:19:31 -08002940 }
Jon Miranda0128d3f2019-02-14 09:19:31 -08002941 }
2942
Sunny Goyalefb7e842018-10-04 15:11:00 -07002943 public View getHomescreenIconByItemId(final int id) {
Sunny Goyal876e4622018-11-02 13:50:40 -07002944 return getFirstMatch((info, v) -> info != null && info.id == id);
Sunny Goyal317698b2015-07-29 11:45:41 -07002945 }
2946
Sunny Goyalff572272014-07-23 13:58:07 -07002947 public LauncherAppWidgetHostView getWidgetForAppWidgetId(final int appWidgetId) {
Sunny Goyal876e4622018-11-02 13:50:40 -07002948 return (LauncherAppWidgetHostView) getFirstMatch((info, v) ->
2949 (info instanceof LauncherAppWidgetInfo) &&
2950 ((LauncherAppWidgetInfo) info).appWidgetId == appWidgetId);
Sunny Goyalff572272014-07-23 13:58:07 -07002951 }
2952
Sunny Goyal6d02c7a2016-05-19 12:15:39 -07002953 public View getFirstMatch(final ItemOperator operator) {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05002954 final View[] value = new View[1];
Sunny Goyal651077b2014-06-30 14:15:31 -07002955 mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05002956 @Override
Sunny Goyalc52ba712016-04-05 15:59:05 -07002957 public boolean evaluate(ItemInfo info, View v) {
2958 if (operator.evaluate(info, v)) {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05002959 value[0] = v;
2960 return true;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002961 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05002962 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002963 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05002964 });
2965 return value[0];
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002966 }
2967
Jon Miranda43348742019-03-06 10:40:13 -08002968 /**
2969 * @param cellLayouts List of CellLayouts to scan, in order of preference.
2970 * @param operators List of operators, in order starting from best matching operator.
2971 * @return
2972 */
2973 private View getFirstMatch(CellLayout[] cellLayouts, final ItemOperator... operators) {
2974 // This array is filled with the first match for each operator.
2975 final View[] matches = new View[operators.length];
2976 // For efficiency, the outer loop should be CellLayout.
Jon Miranda0128d3f2019-02-14 09:19:31 -08002977 for (CellLayout cellLayout : cellLayouts) {
2978 mapOverCellLayout(MAP_NO_RECURSE, cellLayout, (info, v) -> {
Jon Miranda43348742019-03-06 10:40:13 -08002979 for (int i = 0; i < operators.length; ++i) {
2980 if (matches[i] == null && operators[i].evaluate(info, v)) {
2981 matches[i] = v;
2982 if (i == 0) {
2983 // We can return since this is the best match possible.
2984 return true;
2985 }
2986 }
Jon Miranda0128d3f2019-02-14 09:19:31 -08002987 }
2988 return false;
2989 });
Jon Miranda43348742019-03-06 10:40:13 -08002990 if (matches[0] != null) {
Jon Miranda0128d3f2019-02-14 09:19:31 -08002991 break;
2992 }
2993 }
Jon Miranda43348742019-03-06 10:40:13 -08002994 for (View match : matches) {
2995 if (match != null) {
2996 return match;
2997 }
2998 }
2999 return null;
Jon Miranda0128d3f2019-02-14 09:19:31 -08003000 }
3001
Adam Cohendf035382011-04-11 17:22:04 -07003002 void clearDropTargets() {
Sunny Goyal651077b2014-06-30 14:15:31 -07003003 mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003004 @Override
Sunny Goyalc52ba712016-04-05 15:59:05 -07003005 public boolean evaluate(ItemInfo info, View v) {
Adam Cohendf035382011-04-11 17:22:04 -07003006 if (v instanceof DropTarget) {
3007 mDragController.removeDropTarget((DropTarget) v);
3008 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003009 // not done, process all the shortcuts
3010 return false;
Adam Cohendf035382011-04-11 17:22:04 -07003011 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003012 });
Adam Cohendf035382011-04-11 17:22:04 -07003013 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003014
Sunny Goyal4390ace2014-10-13 11:33:11 -07003015 /**
Sunny Goyald3b87ef2016-07-28 12:11:54 -07003016 * Removes items that match the {@param matcher}. When applications are removed
Sunny Goyal4390ace2014-10-13 11:33:11 -07003017 * as a part of an update, this is called to ensure that other widgets and application
3018 * shortcuts are not removed.
3019 */
Sunny Goyald3b87ef2016-07-28 12:11:54 -07003020 public void removeItemsByMatcher(final ItemInfoMatcher matcher) {
Sunny Goyal876e4622018-11-02 13:50:40 -07003021 for (final CellLayout layoutParent: getWorkspaceAndHotseatCellLayouts()) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07003022 final ViewGroup layout = layoutParent.getShortcutsAndWidgets();
Romain Guy574d20e2009-06-01 15:34:04 -07003023
Sunny Goyalefb7e842018-10-04 15:11:00 -07003024 IntSparseArrayMap<View> idToViewMap = new IntSparseArrayMap<>();
Sunny Goyal40452cf2016-09-01 15:17:46 -07003025 ArrayList<ItemInfo> items = new ArrayList<>();
Winson Chung64359a52013-07-08 17:17:08 -07003026 for (int j = 0; j < layout.getChildCount(); j++) {
3027 final View view = layout.getChildAt(j);
Sunny Goyal40452cf2016-09-01 15:17:46 -07003028 if (view.getTag() instanceof ItemInfo) {
3029 ItemInfo item = (ItemInfo) view.getTag();
3030 items.add(item);
3031 idToViewMap.put(item.id, view);
3032 }
Winson Chung64359a52013-07-08 17:17:08 -07003033 }
Winson Chungaafa03c2010-06-11 17:34:16 -07003034
Sunny Goyal40452cf2016-09-01 15:17:46 -07003035 for (ItemInfo itemToRemove : matcher.filterItemInfos(items)) {
3036 View child = idToViewMap.get(itemToRemove.id);
3037
3038 if (child != null) {
3039 // Note: We can not remove the view directly from CellLayoutChildren as this
3040 // does not re-mark the spaces as unoccupied.
3041 layoutParent.removeViewInLayout(child);
3042 if (child instanceof DropTarget) {
3043 mDragController.removeDropTarget((DropTarget) child);
Romain Guy574d20e2009-06-01 15:34:04 -07003044 }
Sunny Goyal40452cf2016-09-01 15:17:46 -07003045 } else if (itemToRemove.container >= 0) {
3046 // The item may belong to a folder.
3047 View parent = idToViewMap.get(itemToRemove.container);
3048 if (parent != null) {
Jon Miranda608d0db2017-02-28 13:15:28 -08003049 FolderInfo folderInfo = (FolderInfo) parent.getTag();
3050 folderInfo.prepareAutoUpdate();
3051 folderInfo.remove((ShortcutInfo) itemToRemove, false);
Sunny Goyal40452cf2016-09-01 15:17:46 -07003052 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003053 }
Winson Chung64359a52013-07-08 17:17:08 -07003054 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003055 }
Winson Chung2efec4e2012-05-03 12:31:48 -07003056
Winson Chung64359a52013-07-08 17:17:08 -07003057 // Strip all the empty screens
3058 stripEmptyScreens();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003059 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07003060
Adam Cohenf9c184a2016-01-15 16:47:43 -08003061 public interface ItemOperator {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003062 /**
Sunny Goyald0a6ae72016-06-16 12:29:03 -07003063 * Process the next itemInfo, possibly with side-effect on the next item.
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003064 *
3065 * @param info info for the shortcut
3066 * @param view view for the shortcut
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003067 * @return true if done, false to continue the map
3068 */
Rajeev Kumar93c05122017-06-12 12:59:47 -07003069 boolean evaluate(ItemInfo info, View view);
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003070 }
3071
3072 /**
Sunny Goyal651077b2014-06-30 14:15:31 -07003073 * Map the operator over the shortcuts and widgets, return the first-non-null value.
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003074 *
3075 * @param recurse true: iterate over folder children. false: op get the folders themselves.
3076 * @param op the operator to map over the shortcuts
3077 */
Sunny Goyald0ae4922018-10-11 10:37:53 -07003078 public void mapOverItems(boolean recurse, ItemOperator op) {
Sunny Goyal876e4622018-11-02 13:50:40 -07003079 for (CellLayout layout : getWorkspaceAndHotseatCellLayouts()) {
Jon Miranda0128d3f2019-02-14 09:19:31 -08003080 if (mapOverCellLayout(recurse, layout, op)) {
3081 return;
3082 }
3083 }
3084 }
3085
3086 private boolean mapOverCellLayout(boolean recurse, CellLayout layout, ItemOperator op) {
Jon Miranda5bc0dd02019-03-12 17:19:30 -07003087 // TODO(b/128460496) Potential race condition where layout is not yet loaded
3088 if (layout == null) {
3089 return false;
3090 }
Jon Miranda0128d3f2019-02-14 09:19:31 -08003091 ShortcutAndWidgetContainer container = layout.getShortcutsAndWidgets();
3092 // map over all the shortcuts on the workspace
3093 final int itemCount = container.getChildCount();
3094 for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
3095 View item = container.getChildAt(itemIdx);
3096 ItemInfo info = (ItemInfo) item.getTag();
3097 if (recurse && info instanceof FolderInfo && item instanceof FolderIcon) {
3098 FolderIcon folder = (FolderIcon) item;
3099 ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
3100 // map over all the children in the folder
3101 final int childCount = folderChildren.size();
3102 for (int childIdx = 0; childIdx < childCount; childIdx++) {
3103 View child = folderChildren.get(childIdx);
3104 info = (ItemInfo) child.getTag();
3105 if (op.evaluate(info, child)) {
3106 return true;
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003107 }
Jon Miranda0128d3f2019-02-14 09:19:31 -08003108 }
3109 } else {
3110 if (op.evaluate(info, item)) {
3111 return true;
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003112 }
3113 }
3114 }
Jon Miranda0128d3f2019-02-14 09:19:31 -08003115 return false;
Chris Wrenaeff7ea2014-02-14 16:59:24 -05003116 }
3117
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07003118 void updateShortcuts(ArrayList<ShortcutInfo> shortcuts) {
Sunny Goyalc52ba712016-04-05 15:59:05 -07003119 int total = shortcuts.size();
Rajeev Kumar93c05122017-06-12 12:59:47 -07003120 final HashSet<ShortcutInfo> updates = new HashSet<>(total);
Sunny Goyalefb7e842018-10-04 15:11:00 -07003121 final IntSet folderIds = new IntSet();
Sunny Goyalc52ba712016-04-05 15:59:05 -07003122
3123 for (int i = 0; i < total; i++) {
3124 ShortcutInfo s = shortcuts.get(i);
3125 updates.add(s);
3126 folderIds.add(s.container);
3127 }
3128
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07003129 mapOverItems(MAP_RECURSE, new ItemOperator() {
3130 @Override
Sunny Goyalc52ba712016-04-05 15:59:05 -07003131 public boolean evaluate(ItemInfo info, View v) {
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07003132 if (info instanceof ShortcutInfo && v instanceof BubbleTextView &&
3133 updates.contains(info)) {
Sunny Goyal4390ace2014-10-13 11:33:11 -07003134 ShortcutInfo si = (ShortcutInfo) info;
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07003135 BubbleTextView shortcut = (BubbleTextView) v;
Hyunyoung Song1322f9c2017-06-23 14:57:38 -07003136 Drawable oldIcon = shortcut.getIcon();
Sunny Goyal3fcab662015-08-11 15:17:30 -07003137 boolean oldPromiseState = (oldIcon instanceof PreloadIconDrawable)
3138 && ((PreloadIconDrawable) oldIcon).hasNotCompleted();
Sunny Goyal1cd01b02016-11-09 10:43:58 -08003139 shortcut.applyFromShortcutInfo(si, si.isPromise() != oldPromiseState);
Sunny Goyalc52ba712016-04-05 15:59:05 -07003140 }
3141 // process all the shortcuts
3142 return false;
3143 }
3144 });
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07003145
Sunny Goyalc52ba712016-04-05 15:59:05 -07003146 // Update folder icons
3147 mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
3148 @Override
3149 public boolean evaluate(ItemInfo info, View v) {
3150 if (info instanceof FolderInfo && folderIds.contains(info.id)) {
3151 ((FolderInfo) info).itemsChanged(false);
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07003152 }
3153 // process all the shortcuts
3154 return false;
3155 }
3156 });
3157 }
3158
Sunny Goyalaae6fbb2019-01-31 16:05:58 -08003159 public void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
Tony Wickham010d2552017-01-20 08:15:28 -08003160 final PackageUserKey packageUserKey = new PackageUserKey(null, null);
Sunny Goyalefb7e842018-10-04 15:11:00 -07003161 final IntSet folderIds = new IntSet();
Tony Wickham010d2552017-01-20 08:15:28 -08003162 mapOverItems(MAP_RECURSE, new ItemOperator() {
3163 @Override
3164 public boolean evaluate(ItemInfo info, View v) {
Tony Wickham1e58cd72018-12-04 14:03:32 -08003165 if (info instanceof ShortcutInfo && v instanceof BubbleTextView) {
3166 if (!packageUserKey.updateFromItemInfo(info)
Sunny Goyalaae6fbb2019-01-31 16:05:58 -08003167 || updatedDots.test(packageUserKey)) {
Tony Wickhamf34bee82018-12-03 18:11:39 -08003168 ((BubbleTextView) v).applyDotState(info, true /* animate */);
Tony Wickham11ba5072017-02-02 12:42:50 -08003169 folderIds.add(info.container);
Tony Wickham010d2552017-01-20 08:15:28 -08003170 }
3171 }
3172 // process all the shortcuts
3173 return false;
3174 }
3175 });
Tony Wickham11ba5072017-02-02 12:42:50 -08003176
3177 // Update folder icons
3178 mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
3179 @Override
3180 public boolean evaluate(ItemInfo info, View v) {
3181 if (info instanceof FolderInfo && folderIds.contains(info.id)
3182 && v instanceof FolderIcon) {
Tony Wickhamf34bee82018-12-03 18:11:39 -08003183 FolderDotInfo folderDotInfo = new FolderDotInfo();
Tony Wickham11ba5072017-02-02 12:42:50 -08003184 for (ShortcutInfo si : ((FolderInfo) info).contents) {
Tony Wickhamf34bee82018-12-03 18:11:39 -08003185 folderDotInfo.addDotInfo(mLauncher.getDotInfoForItem(si));
Tony Wickham11ba5072017-02-02 12:42:50 -08003186 }
Tony Wickhamf34bee82018-12-03 18:11:39 -08003187 ((FolderIcon) v).setDotInfo(folderDotInfo);
Tony Wickham11ba5072017-02-02 12:42:50 -08003188 }
3189 // process all the shortcuts
3190 return false;
3191 }
3192 });
Tony Wickham010d2552017-01-20 08:15:28 -08003193 }
3194
Sunny Goyal7c74e4a2016-12-15 15:53:17 -08003195 public void removeAbandonedPromise(String packageName, UserHandle user) {
Sunny Goyal3bbbabc2016-03-15 09:16:30 -07003196 HashSet<String> packages = new HashSet<>(1);
Sunny Goyale7b8cd92014-08-27 14:04:33 -07003197 packages.add(packageName);
Sunny Goyal40452cf2016-09-01 15:17:46 -07003198 ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packages, user);
Sunny Goyal43bf11d2017-02-02 13:52:53 -08003199 mLauncher.getModelWriter().deleteItemsFromDatabase(matcher);
Sunny Goyal40452cf2016-09-01 15:17:46 -07003200 removeItemsByMatcher(matcher);
Chris Wren40c5ed32014-06-24 18:24:23 -04003201 }
3202
Sunny Goyal756adbc2015-04-16 15:20:51 -07003203 public void updateRestoreItems(final HashSet<ItemInfo> updates) {
Sunny Goyala22666f2014-09-18 13:25:15 -07003204 mapOverItems(MAP_RECURSE, new ItemOperator() {
3205 @Override
Sunny Goyalc52ba712016-04-05 15:59:05 -07003206 public boolean evaluate(ItemInfo info, View v) {
Sunny Goyal756adbc2015-04-16 15:20:51 -07003207 if (info instanceof ShortcutInfo && v instanceof BubbleTextView
3208 && updates.contains(info)) {
Tony Wickham9a8d11f2017-01-11 09:53:12 -08003209 ((BubbleTextView) v).applyPromiseState(false /* promiseStateChanged */);
Sunny Goyal756adbc2015-04-16 15:20:51 -07003210 } else if (v instanceof PendingAppWidgetHostView
3211 && info instanceof LauncherAppWidgetInfo
3212 && updates.contains(info)) {
3213 ((PendingAppWidgetHostView) v).applyState();
Sunny Goyala22666f2014-09-18 13:25:15 -07003214 }
3215 // process all the shortcuts
3216 return false;
3217 }
3218 });
3219 }
3220
Sunny Goyal87af0fd2016-05-16 14:56:02 -07003221 public void widgetsRestored(final ArrayList<LauncherAppWidgetInfo> changedInfo) {
Sunny Goyal0fc1be12014-08-11 17:05:23 -07003222 if (!changedInfo.isEmpty()) {
3223 DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo,
3224 mLauncher.getAppWidgetHost());
Sunny Goyal2e1efb42016-03-03 16:58:55 -08003225
3226 LauncherAppWidgetInfo item = changedInfo.get(0);
3227 final AppWidgetProviderInfo widgetInfo;
3228 if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
3229 widgetInfo = AppWidgetManagerCompat
3230 .getInstance(mLauncher).findProvider(item.providerName, item.user);
3231 } else {
3232 widgetInfo = AppWidgetManagerCompat.getInstance(mLauncher)
Sunny Goyal952e63d2017-08-16 04:59:08 -07003233 .getLauncherAppWidgetInfo(item.appWidgetId);
Sunny Goyal2e1efb42016-03-03 16:58:55 -08003234 }
3235
3236 if (widgetInfo != null) {
Sunny Goyal0fc1be12014-08-11 17:05:23 -07003237 // Re-inflate the widgets which have changed status
3238 widgetRefresh.run();
3239 } else {
3240 // widgetRefresh will automatically run when the packages are updated.
Sunny Goyal4390ace2014-10-13 11:33:11 -07003241 // For now just update the progress bars
Sunny Goyal87af0fd2016-05-16 14:56:02 -07003242 mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
3243 @Override
3244 public boolean evaluate(ItemInfo info, View view) {
3245 if (view instanceof PendingAppWidgetHostView
3246 && changedInfo.contains(info)) {
3247 ((LauncherAppWidgetInfo) info).installProgress = 100;
3248 ((PendingAppWidgetHostView) view).applyState();
3249 }
3250 // process all the shortcuts
3251 return false;
Sunny Goyal4390ace2014-10-13 11:33:11 -07003252 }
Sunny Goyal87af0fd2016-05-16 14:56:02 -07003253 });
Sunny Goyal0fc1be12014-08-11 17:05:23 -07003254 }
Sunny Goyalff572272014-07-23 13:58:07 -07003255 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07003256 }
3257
Sunny Goyal7ce471b2017-08-02 03:37:39 -07003258 void moveToDefaultScreen() {
3259 int page = DEFAULT_PAGE;
Sunny Goyalc210f5b2017-07-10 20:46:53 -07003260 if (!workspaceInModalState() && getNextPage() != page) {
Sunny Goyal7ce471b2017-08-02 03:37:39 -07003261 snapToPage(page);
Joe Onoratoc45b1682010-01-11 18:48:40 -05003262 }
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01003263 View child = getChildAt(page);
Winson Chung64359a52013-07-08 17:17:08 -07003264 if (child != null) {
3265 child.requestFocus();
3266 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003267 }
3268
Sunny Goyald0a6ae72016-06-16 12:29:03 -07003269 @Override
Sunny Goyalce8809a2018-01-18 14:02:47 -08003270 public int getExpectedHeight() {
Sunny Goyal85a7fe02018-02-02 10:09:32 -08003271 return getMeasuredHeight() <= 0 || !mIsLayoutValid
Sunny Goyalce8809a2018-01-18 14:02:47 -08003272 ? mLauncher.getDeviceProfile().heightPx : getMeasuredHeight();
3273 }
3274
3275 @Override
3276 public int getExpectedWidth() {
Sunny Goyal85a7fe02018-02-02 10:09:32 -08003277 return getMeasuredWidth() <= 0 || !mIsLayoutValid
Sunny Goyalce8809a2018-01-18 14:02:47 -08003278 ? mLauncher.getDeviceProfile().widthPx : getMeasuredWidth();
3279 }
3280
3281 @Override
Vadim Tryshev2c430b32018-04-04 14:45:21 -07003282 protected boolean canAnnouncePageDescription() {
3283 // Disable announcements while overscrolling potentially to overlay screen because if we end
3284 // up on the overlay screen, it will take care of announcing itself.
3285 return Float.compare(mOverlayTranslation, 0f) == 0;
3286 }
3287
3288 @Override
Winson Chung6a0f57d2011-06-29 20:10:49 -07003289 protected String getCurrentPageDescription() {
Sunny Goyal1d08f702015-05-04 15:50:25 -07003290 int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
3291 return getPageDescription(page);
3292 }
3293
3294 private String getPageDescription(int page) {
Sunny Goyal7ce471b2017-08-02 03:37:39 -07003295 int nScreens = getChildCount();
Sunny Goyale78e3d72015-09-24 11:23:31 -07003296 int extraScreenId = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
3297 if (extraScreenId >= 0 && nScreens > 1) {
3298 if (page == extraScreenId) {
3299 return getContext().getString(R.string.workspace_new_page);
3300 }
3301 nScreens--;
3302 }
Sunny Goyal5be3e4c2016-04-12 16:09:52 -07003303 if (nScreens == 0) {
3304 // When the workspace is not loaded, we do not know how many screen will be bound.
3305 return getContext().getString(R.string.all_apps_home_button_label);
3306 }
Sunny Goyal7ce471b2017-08-02 03:37:39 -07003307 return getContext().getString(R.string.workspace_scroll_format, page + 1, nScreens);
Winson Chung6a0f57d2011-06-29 20:10:49 -07003308 }
Adam Cohen8dfcba42011-07-07 16:38:18 -07003309
Winson Chung8f1eff72015-05-28 17:33:40 -07003310 @Override
Hyunyoung Songdf7ef682016-10-06 17:52:22 -07003311 public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
Hyunyoung Songddec1c72016-04-12 18:32:04 -07003312 target.gridX = info.cellX;
3313 target.gridY = info.cellY;
3314 target.pageIndex = getCurrentPage();
Sunny Goyal6c46a6d2016-11-23 02:24:32 +05303315 targetParent.containerType = ContainerType.WORKSPACE;
Hyunyoung Song59a23802016-09-01 12:47:12 -07003316 if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
3317 target.rank = info.rank;
Sunny Goyal6c46a6d2016-11-23 02:24:32 +05303318 targetParent.containerType = ContainerType.HOTSEAT;
Hyunyoung Song59a23802016-09-01 12:47:12 -07003319 } else if (info.container >= 0) {
Sunny Goyal6c46a6d2016-11-23 02:24:32 +05303320 targetParent.containerType = ContainerType.FOLDER;
Hyunyoung Song59a23802016-09-01 12:47:12 -07003321 }
Winson Chung8f1eff72015-05-28 17:33:40 -07003322 }
3323
Sunny Goyal0fc1be12014-08-11 17:05:23 -07003324 /**
3325 * Used as a workaround to ensure that the AppWidgetService receives the
3326 * PACKAGE_ADDED broadcast before updating widgets.
3327 */
Sunny Goyal64a75aa2017-07-03 13:50:52 -07003328 private class DeferredWidgetRefresh implements Runnable, ProviderChangedListener {
Sunny Goyal0fc1be12014-08-11 17:05:23 -07003329 private final ArrayList<LauncherAppWidgetInfo> mInfos;
3330 private final LauncherAppWidgetHost mHost;
3331 private final Handler mHandler;
3332
3333 private boolean mRefreshPending;
3334
Rajeev Kumar11c7bc62017-06-15 18:18:17 -07003335 DeferredWidgetRefresh(ArrayList<LauncherAppWidgetInfo> infos,
3336 LauncherAppWidgetHost host) {
Sunny Goyal0fc1be12014-08-11 17:05:23 -07003337 mInfos = infos;
3338 mHost = host;
Sunny Goyal2db53422019-03-01 16:06:12 -08003339 mHandler = mLauncher.mHandler;
Sunny Goyal0fc1be12014-08-11 17:05:23 -07003340 mRefreshPending = true;
3341
3342 mHost.addProviderChangeListener(this);
3343 // Force refresh after 10 seconds, if we don't get the provider changed event.
3344 // This could happen when the provider is no longer available in the app.
Sunny Goyal2db53422019-03-01 16:06:12 -08003345 Message msg = Message.obtain(mHandler, this);
3346 msg.obj = DeferredWidgetRefresh.class;
3347 mHandler.sendMessageDelayed(msg, 10000);
Sunny Goyal0fc1be12014-08-11 17:05:23 -07003348 }
3349
3350 @Override
3351 public void run() {
3352 mHost.removeProviderChangeListener(this);
3353 mHandler.removeCallbacks(this);
3354
3355 if (!mRefreshPending) {
3356 return;
3357 }
3358
3359 mRefreshPending = false;
3360
Sunny Goyalc7d808a2017-12-04 14:50:41 -08003361 ArrayList<PendingAppWidgetHostView> views = new ArrayList<>(mInfos.size());
3362 mapOverItems(MAP_NO_RECURSE, (info, view) -> {
3363 if (view instanceof PendingAppWidgetHostView && mInfos.contains(info)) {
3364 views.add((PendingAppWidgetHostView) view);
Sunny Goyal0fc1be12014-08-11 17:05:23 -07003365 }
Sunny Goyalc7d808a2017-12-04 14:50:41 -08003366 // process all children
3367 return false;
Sunny Goyal87af0fd2016-05-16 14:56:02 -07003368 });
Sunny Goyalc7d808a2017-12-04 14:50:41 -08003369 for (PendingAppWidgetHostView view : views) {
Sunny Goyal29947f02017-12-18 13:49:44 -08003370 view.reInflate();
Sunny Goyalc7d808a2017-12-04 14:50:41 -08003371 }
Sunny Goyal0fc1be12014-08-11 17:05:23 -07003372 }
Sunny Goyal64a75aa2017-07-03 13:50:52 -07003373
3374 @Override
3375 public void notifyWidgetProvidersChanged() {
3376 run();
3377 }
Sunny Goyal0fc1be12014-08-11 17:05:23 -07003378 }
Sunny Goyal9443ef52016-06-07 16:21:03 -07003379
Sunny Goyaldb364372016-10-26 19:12:47 -07003380 private class StateTransitionListener extends AnimatorListenerAdapter
3381 implements AnimatorUpdateListener {
Sunny Goyalc99cb172017-10-19 16:15:09 -07003382
3383 private final LauncherState mToState;
3384
3385 StateTransitionListener(LauncherState toState) {
3386 mToState = toState;
3387 }
3388
Sunny Goyaldb364372016-10-26 19:12:47 -07003389 @Override
3390 public void onAnimationUpdate(ValueAnimator anim) {
3391 mTransitionProgress = anim.getAnimatedFraction();
3392 }
3393
3394 @Override
3395 public void onAnimationStart(Animator animation) {
Sunny Goyalc99cb172017-10-19 16:15:09 -07003396 onStartStateTransition(mToState);
Sunny Goyaldb364372016-10-26 19:12:47 -07003397 }
3398
3399 @Override
3400 public void onAnimationEnd(Animator animation) {
3401 onEndStateTransition();
3402 }
3403 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003404}