blob: c0a1cfc1e147da9a69be10debcf52b2230973371 [file] [log] [blame]
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Daniel Sandler325dc232013-06-05 22:57:57 -040017package com.android.launcher3;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080018
Michael Jurka0280c3b2010-09-17 15:00:07 -070019import android.animation.Animator;
Adam Cohenf358a4b2013-07-23 16:47:31 -070020import android.animation.AnimatorListenerAdapter;
Adam Cohen22cba7f2013-07-19 16:14:00 -070021import android.animation.LayoutTransition;
Michael Jurka0280c3b2010-09-17 15:00:07 -070022import android.animation.ObjectAnimator;
Adam Cohenad4e15c2013-10-17 16:21:35 -070023import android.animation.PropertyValuesHolder;
Sunny Goyal70660032015-05-14 00:07:08 -070024import android.annotation.SuppressLint;
Adam Cohenc9735cf2015-01-23 16:11:55 -080025import android.annotation.TargetApi;
Dianne Hackborn8f573952009-08-10 23:21:09 -070026import android.app.WallpaperManager;
Michael Jurkabed61d22012-02-14 22:51:29 -080027import android.appwidget.AppWidgetHostView;
Romain Guy629de3e2010-01-13 12:20:59 -080028import android.appwidget.AppWidgetProviderInfo;
Adam Powell495f2892010-04-16 16:40:55 -070029import android.content.ComponentName;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080030import android.content.Context;
Michael Jurkaadc574c2013-09-12 00:05:02 +020031import android.content.SharedPreferences;
Patrick Dubroy7247f632010-08-04 16:02:59 -070032import android.content.res.Resources;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080033import android.content.res.TypedArray;
Joe Onorato4be866d2010-10-10 11:26:02 -070034import android.graphics.Bitmap;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080035import android.graphics.Canvas;
Michael Jurkaa63c4522010-08-19 13:52:27 -070036import android.graphics.Matrix;
Winson Chungbabb53e2014-04-14 17:12:49 -070037import android.graphics.Paint;
Winson Chungb8c69f32011-10-19 21:36:08 -070038import android.graphics.Point;
Winson Chung043f2af2012-03-01 16:09:54 -080039import android.graphics.PointF;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080040import android.graphics.Rect;
Joe Onorato4be866d2010-10-10 11:26:02 -070041import android.graphics.Region.Op;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070042import android.graphics.drawable.Drawable;
Michael Jurkaa6a05472013-11-13 17:59:46 +010043import android.os.AsyncTask;
Adam Cohenc9735cf2015-01-23 16:11:55 -080044import android.os.Build;
Sunny Goyal0fc1be12014-08-11 17:05:23 -070045import android.os.Handler;
Joe Onorato956091b2010-02-19 12:47:40 -080046import android.os.IBinder;
Adam Powell495f2892010-04-16 16:40:55 -070047import android.os.Parcelable;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080048import android.util.AttributeSet;
Daniel Sandler291ad122010-05-24 16:03:53 -040049import android.util.Log;
Adam Cohen1462de32012-07-24 22:34:36 -070050import android.util.SparseArray;
Michael Jurkacc07e7a2013-08-26 20:56:35 +020051import android.view.Choreographer;
Winson Chunga34abf82010-11-12 12:10:35 -080052import android.view.Display;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080053import android.view.MotionEvent;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080054import android.view.View;
Winson Chung6e314082011-01-27 16:46:51 -080055import android.view.ViewGroup;
Adam Cohen53805212013-10-01 10:39:23 -070056import android.view.accessibility.AccessibilityManager;
Patrick Dubroycd68ff52010-10-28 17:57:05 -070057import android.view.animation.DecelerateInterpolator;
Michael Jurkacc07e7a2013-08-26 20:56:35 +020058import android.view.animation.Interpolator;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070059import android.widget.TextView;
Sunny Goyal83a8f042015-05-19 12:52:12 -070060
Daniel Sandler325dc232013-06-05 22:57:57 -040061import com.android.launcher3.FolderIcon.FolderRingAnimator;
Adam Cohenbffe7452013-07-22 18:21:45 -070062import com.android.launcher3.Launcher.CustomContentCallbacks;
Adam Cohenc2d6e892014-10-16 09:49:24 -070063import com.android.launcher3.Launcher.LauncherOverlay;
Daniel Sandler325dc232013-06-05 22:57:57 -040064import com.android.launcher3.LauncherSettings.Favorites;
Sunny Goyalfa401a12015-04-10 13:45:42 -070065import com.android.launcher3.UninstallDropTarget.UninstallSource;
Sunny Goyal83a8f042015-05-19 12:52:12 -070066import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
Sunny Goyal1d08f702015-05-04 15:50:25 -070067import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate;
Sunny Goyal83a8f042015-05-19 12:52:12 -070068import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource;
Sunny Goyal651077b2014-06-30 14:15:31 -070069import com.android.launcher3.compat.UserHandleCompat;
Sunny Goyale2df0622015-04-24 11:27:00 -070070import com.android.launcher3.util.LongArrayMap;
Adam Cohen091440a2015-03-18 14:16:05 -070071import com.android.launcher3.util.Thunk;
Sunny Goyal6a1e95a2015-03-20 17:26:30 -070072import com.android.launcher3.util.WallpaperUtils;
Hyunyoung Song3f471442015-04-08 19:01:34 -070073import com.android.launcher3.widget.PendingAddShortcutInfo;
74import com.android.launcher3.widget.PendingAddWidgetInfo;
Sunny Goyal83a8f042015-05-19 12:52:12 -070075
Adam Cohen716b51e2011-06-30 12:09:54 -070076import java.util.ArrayList;
Adam Cohendcd297f2013-06-18 13:13:40 -070077import java.util.HashMap;
Adam Cohen716b51e2011-06-30 12:09:54 -070078import java.util.HashSet;
Sunny Goyal95abbb32014-08-04 10:53:22 -070079import java.util.concurrent.atomic.AtomicInteger;
Adam Cohen716b51e2011-06-30 12:09:54 -070080
The Android Open Source Project31dd5032009-03-03 19:32:27 -080081/**
Michael Jurka0142d492010-08-25 17:46:15 -070082 * The workspace is a wide area with a wallpaper and a finite number of pages.
83 * Each page contains a number of icons, folders or widgets the user can
Winson Chungaafa03c2010-06-11 17:34:16 -070084 * interact with. A workspace is meant to be used with a fixed width only.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080085 */
Sunny Goyal5a1f53b2015-05-27 10:24:24 -070086public class Workspace extends PagedView
Michael Jurkad74c9842011-07-10 12:44:21 -070087 implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
John Spurlock77e1f472013-09-11 10:09:51 -040088 DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener,
Sunny Goyale9b651e2015-04-24 11:44:51 -070089 Insettable, UninstallSource, AccessibilityDragSource {
Joe Onorato3a8820b2009-11-10 15:06:42 -080090 private static final String TAG = "Launcher.Workspace";
Michael Jurka0142d492010-08-25 17:46:15 -070091
Adam Cohenad4e15c2013-10-17 16:21:35 -070092 protected static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400;
93 protected static final int FADE_EMPTY_SCREEN_DURATION = 150;
94
Adam Cohened51cc92011-08-01 20:28:08 -070095 private static final int ADJACENT_SCREEN_DROP_DURATION = 300;
96
Chris Wren40c5ed32014-06-24 18:24:23 -040097 static final boolean MAP_NO_RECURSE = false;
98 static final boolean MAP_RECURSE = true;
Chris Wrenaeff7ea2014-02-14 16:59:24 -050099
Adam Cohen3d1b2b42013-08-14 15:57:58 -0700100 private static final long CUSTOM_CONTENT_GESTURE_DELAY = 200;
101 private long mTouchDownTime = -1;
102 private long mCustomContentShowTime = -1;
103
Adam Cohen22cba7f2013-07-19 16:14:00 -0700104 private LayoutTransition mLayoutTransition;
Adam Cohen091440a2015-03-18 14:16:05 -0700105 @Thunk final WallpaperManager mWallpaperManager;
106 @Thunk IBinder mWindowToken;
Winson Chungaafa03c2010-06-11 17:34:16 -0700107
Winson Chung9e6a0a22013-08-27 11:58:12 -0700108 private int mOriginalDefaultPage;
Michael Jurka0142d492010-08-25 17:46:15 -0700109 private int mDefaultPage;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800110
Adam Cohen5084cba2013-09-03 12:01:16 -0700111 private ShortcutAndWidgetContainer mDragSourceInternal;
112
Adam Cohendcd297f2013-06-18 13:13:40 -0700113 // The screen id used for the empty screen always present to the right.
Adrian Roos8f3f6832014-04-28 15:45:52 +0200114 final static long EXTRA_EMPTY_SCREEN_ID = -201;
Adam Cohendcd297f2013-06-18 13:13:40 -0700115 private final static long CUSTOM_CONTENT_SCREEN_ID = -301;
116
Sunny Goyale2df0622015-04-24 11:27:00 -0700117 @Thunk LongArrayMap<CellLayout> mWorkspaceScreens = new LongArrayMap<>();
Adam Cohen091440a2015-03-18 14:16:05 -0700118 @Thunk ArrayList<Long> mScreenOrder = new ArrayList<Long>();
Adam Cohendcd297f2013-06-18 13:13:40 -0700119
Adam Cohen091440a2015-03-18 14:16:05 -0700120 @Thunk Runnable mRemoveEmptyScreenRunnable;
121 @Thunk boolean mDeferRemoveExtraEmptyScreen = false;
Sunny Goyale9b651e2015-04-24 11:44:51 -0700122 @Thunk boolean mAddNewPageOnDrag = true;
Adam Cohenad4e15c2013-10-17 16:21:35 -0700123
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800124 /**
125 * CellInfo for the cell that is currently being dragged
126 */
127 private CellLayout.CellInfo mDragInfo;
Winson Chungaafa03c2010-06-11 17:34:16 -0700128
Jeff Sharkey70864282009-04-07 21:08:40 -0700129 /**
130 * Target drop area calculated during last acceptDrop call.
131 */
Adam Cohen091440a2015-03-18 14:16:05 -0700132 @Thunk int[] mTargetCell = new int[2];
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700133 private int mDragOverX = -1;
134 private int mDragOverY = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800135
Adam Cohena897f392012-04-27 18:12:05 -0700136 static Rect mLandscapeCellLayoutMetrics = null;
137 static Rect mPortraitCellLayoutMetrics = null;
138
Adam Cohenbffe7452013-07-22 18:21:45 -0700139 CustomContentCallbacks mCustomContentCallbacks;
140 boolean mCustomContentShowing;
Adam Cohenc36fa5c2013-08-29 11:54:42 -0700141 private float mLastCustomContentScrollProgress = -1f;
Adam Cohen53805212013-10-01 10:39:23 -0700142 private String mCustomContentDescription = "";
Adam Cohenbffe7452013-07-22 18:21:45 -0700143
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700144 /**
145 * The CellLayout that is currently being dragged over
146 */
Adam Cohen091440a2015-03-18 14:16:05 -0700147 @Thunk CellLayout mDragTargetLayout = null;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700148 /**
149 * The CellLayout that we will show as glowing
150 */
151 private CellLayout mDragOverlappingLayout = null;
152
153 /**
154 * The CellLayout which will be dropped to
155 */
156 private CellLayout mDropToLayout = null;
157
Adam Cohen091440a2015-03-18 14:16:05 -0700158 @Thunk Launcher mLauncher;
159 @Thunk IconCache mIconCache;
160 @Thunk DragController mDragController;
Winson Chungaafa03c2010-06-11 17:34:16 -0700161
Michael Jurka4516c112010-10-07 15:13:47 -0700162 // These are temporary variables to prevent having to allocate a new object just to
163 // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800164 private int[] mTempCell = new int[2];
Adam Cohen7d30a372013-07-01 17:03:59 -0700165 private int[] mTempPt = new int[2];
Jeff Sharkey70864282009-04-07 21:08:40 -0700166 private int[] mTempEstimate = new int[2];
Adam Cohen091440a2015-03-18 14:16:05 -0700167 @Thunk float[] mDragViewVisualCenter = new float[2];
Michael Jurka4516c112010-10-07 15:13:47 -0700168 private float[] mTempCellLayoutCenterCoordinates = new float[2];
Michael Jurka0280c3b2010-09-17 15:00:07 -0700169 private Matrix mTempInverseMatrix = new Matrix();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800170
Michael Jurkac2f7f472010-12-14 15:34:42 -0800171 private SpringLoadedDragController mSpringLoadedDragController;
Winson Chungb26f3d62011-06-02 10:49:29 -0700172 private float mSpringLoadedShrinkFactor;
Adam Cohenf358a4b2013-07-23 16:47:31 -0700173 private float mOverviewModeShrinkFactor;
Michael Jurkad3ef3062010-11-23 16:23:58 -0800174
Patrick Dubroy1262e362010-10-06 15:49:50 -0700175 // State variable that indicates whether the pages are small (ie when you're
Michael Jurkadee05892010-07-27 10:01:56 -0700176 // in all apps or customize mode)
Michael Jurkad74c9842011-07-10 12:44:21 -0700177
Adam Cohen6c5891a2014-07-09 23:53:15 -0700178 enum State { NORMAL, NORMAL_HIDDEN, SPRING_LOADED, OVERVIEW, OVERVIEW_HIDDEN};
Adam Cohen7777d962011-08-18 18:58:38 -0700179 private State mState = State.NORMAL;
Michael Jurkad74c9842011-07-10 12:44:21 -0700180 private boolean mIsSwitchingState = false;
Michael Jurkad74c9842011-07-10 12:44:21 -0700181
Michael Jurkad74c9842011-07-10 12:44:21 -0700182 boolean mAnimatingViewIntoPlace = false;
183 boolean mIsDragOccuring = false;
184 boolean mChildrenLayersEnabled = true;
Michael Jurkadee05892010-07-27 10:01:56 -0700185
Adam Cohenaccfd562013-07-12 14:40:40 -0700186 private boolean mStripScreensOnPageStopMoving = false;
187
Patrick Dubroy54fa3b92010-11-17 12:18:45 -0800188 /** Is the user is dragging an item near the edge of a page? */
Patrick Dubroy1262e362010-10-06 15:49:50 -0700189 private boolean mInScrollArea = false;
190
Daniel Sandlere4f98912013-06-25 15:13:26 -0400191 private HolographicOutlineHelper mOutlineHelper;
Adam Cohen091440a2015-03-18 14:16:05 -0700192 @Thunk Bitmap mDragOutline = null;
Sunny Goyal508da152014-08-14 10:53:27 -0700193 private static final Rect sTempRect = new Rect();
Patrick Dubroy8e58e912010-10-14 13:21:48 -0700194 private final int[] mTempXY = new int[2];
Michael Jurkad51f33a2012-06-28 15:35:26 -0700195 private int[] mTempVisiblePagesRange = new int[2];
Adam Cohen1e4359c2014-08-18 13:12:16 -0700196 private boolean mOverscrollEffectSet;
Michael Jurkaf8304f02012-04-26 13:33:26 -0700197 public static final int DRAG_BITMAP_PADDING = 2;
Michael Jurka869390b2012-05-06 15:55:19 -0700198 private boolean mWorkspaceFadeInAdjacentScreens;
Joe Onorato4be866d2010-10-10 11:26:02 -0700199
Michael Jurkaab1983f2011-01-18 15:50:17 -0800200 WallpaperOffsetInterpolator mWallpaperOffset;
Adam Cohen091440a2015-03-18 14:16:05 -0700201 @Thunk boolean mWallpaperIsLiveWallpaper;
202 @Thunk int mNumPagesForWallpaperParallax;
203 @Thunk float mLastSetWallpaperOffsetSteps = 0;
Michael Jurka2a4f4922014-01-29 16:32:39 +0100204
Adam Cohen091440a2015-03-18 14:16:05 -0700205 @Thunk Runnable mDelayedResizeRunnable;
Winson Chungf0c6ae02012-03-21 16:10:31 -0700206 private Runnable mDelayedSnapToPageRunnable;
Michael Jurka84f2ce72012-04-13 15:08:01 -0700207 private Point mDisplaySize = new Point();
Michael Jurkac5b262c2011-01-12 20:24:50 -0800208
Adam Cohen19072da2011-05-31 14:30:45 -0700209 // Variables relating to the creation of user folders by hovering shortcuts over shortcuts
Adam Cohen482ed822012-03-02 14:15:13 -0800210 private static final int FOLDER_CREATION_TIMEOUT = 0;
Adam Cohenfa3c58f2013-12-06 16:10:55 -0800211 public static final int REORDER_TIMEOUT = 350;
Adam Cohen19072da2011-05-31 14:30:45 -0700212 private final Alarm mFolderCreationAlarm = new Alarm();
Adam Cohen482ed822012-03-02 14:15:13 -0800213 private final Alarm mReorderAlarm = new Alarm();
Adam Cohen091440a2015-03-18 14:16:05 -0700214 @Thunk FolderRingAnimator mDragFolderRingAnimator = null;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700215 private FolderIcon mDragOverFolderIcon = null;
Adam Cohen19072da2011-05-31 14:30:45 -0700216 private boolean mCreateUserFolderOnDrop = false;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700217 private boolean mAddToExistingFolderOnDrop = false;
218 private DropTarget.DragEnforcer mDragEnforcer;
Adam Cohen3aff81c2012-05-16 21:01:01 -0700219 private float mMaxDistanceForFolderCreation;
Adam Cohen073a46f2011-05-17 16:28:09 -0700220
Sunny Goyal508da152014-08-14 10:53:27 -0700221 private final Canvas mCanvas = new Canvas();
222
Adam Cohenf8d28232011-02-01 21:47:00 -0800223 // Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget)
224 private float mXDown;
225 private float mYDown;
226 final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
227 final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
228 final static float TOUCH_SLOP_DAMPING_FACTOR = 4;
229
Adam Cohened66b2b2012-01-23 17:28:51 -0800230 // Relating to the animation of items being dropped externally
Adam Cohend41fbf52012-02-16 23:53:59 -0800231 public static final int ANIMATE_INTO_POSITION_AND_DISAPPEAR = 0;
232 public static final int ANIMATE_INTO_POSITION_AND_REMAIN = 1;
233 public static final int ANIMATE_INTO_POSITION_AND_RESIZE = 2;
234 public static final int COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION = 3;
235 public static final int CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION = 4;
Adam Cohened66b2b2012-01-23 17:28:51 -0800236
Adam Cohen482ed822012-03-02 14:15:13 -0800237 // Related to dragging, folder creation and reordering
238 private static final int DRAG_MODE_NONE = 0;
239 private static final int DRAG_MODE_CREATE_FOLDER = 1;
240 private static final int DRAG_MODE_ADD_TO_FOLDER = 2;
241 private static final int DRAG_MODE_REORDER = 3;
242 private int mDragMode = DRAG_MODE_NONE;
Adam Cohen091440a2015-03-18 14:16:05 -0700243 @Thunk int mLastReorderX = -1;
244 @Thunk int mLastReorderY = -1;
Adam Cohen482ed822012-03-02 14:15:13 -0800245
Adam Cohen1462de32012-07-24 22:34:36 -0700246 private SparseArray<Parcelable> mSavedStates;
247 private final ArrayList<Integer> mRestoredPages = new ArrayList<Integer>();
248
Adam Cohen4b285c52011-07-21 14:24:06 -0700249 // These variables are used for storing the initial and final values during workspace animations
Adam Cohened51cc92011-08-01 20:28:08 -0700250 private int mSavedScrollX;
251 private float mSavedRotationY;
252 private float mSavedTranslationX;
Adam Cohen7d30a372013-07-01 17:03:59 -0700253
254 private float mCurrentScale;
Winson Chung70442722012-02-10 15:43:22 -0800255 private float mTransitionProgress;
Adam Cohen4b285c52011-07-21 14:24:06 -0700256
Adam Cohen1e4359c2014-08-18 13:12:16 -0700257 float mOverScrollEffect = 0f;
258
Adam Cohen091440a2015-03-18 14:16:05 -0700259 @Thunk Runnable mDeferredAction;
Michael Jurka1e2f4652013-07-08 18:03:46 -0700260 private boolean mDeferDropAfterUninstall;
261 private boolean mUninstallSuccessful;
262
Adam Cohenc2d6e892014-10-16 09:49:24 -0700263 // State related to Launcher Overlay
264 LauncherOverlay mLauncherOverlay;
265 boolean mScrollInteractionBegan;
266 boolean mStartedSendingScrollEvents;
267 boolean mShouldSendPageSettled;
Adam Cohen8c4ca922014-10-24 17:40:34 -0700268 int mLastOverlaySroll = 0;
Adam Cohenc2d6e892014-10-16 09:49:24 -0700269
Winson Chungdc61c4d2015-04-20 18:26:57 -0700270 // Handles workspace state transitions
271 private WorkspaceStateTransitionAnimation mStateTransitionAnimation;
272
Sunny Goyal1d08f702015-05-04 15:50:25 -0700273 private AccessibilityDelegate mPagesAccessibilityDelegate;
274
Romain Guyeeacd562012-10-10 18:47:33 -0700275 private final Runnable mBindPages = new Runnable() {
276 @Override
277 public void run() {
278 mLauncher.getModel().bindRemainingSynchronousPages();
279 }
280 };
281
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800282 /**
283 * Used to inflate the Workspace from XML.
284 *
285 * @param context The application's context.
Michael Jurka0142d492010-08-25 17:46:15 -0700286 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800287 */
288 public Workspace(Context context, AttributeSet attrs) {
289 this(context, attrs, 0);
290 }
291
292 /**
293 * Used to inflate the Workspace from XML.
294 *
295 * @param context The application's context.
Michael Jurka0142d492010-08-25 17:46:15 -0700296 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800297 * @param defStyle Unused.
298 */
299 public Workspace(Context context, AttributeSet attrs, int defStyle) {
300 super(context, attrs, defStyle);
Michael Jurka5f1c5092010-09-03 14:15:02 -0700301
Daniel Sandlere4f98912013-06-25 15:13:26 -0400302 mOutlineHelper = HolographicOutlineHelper.obtain(context);
303
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700304 mDragEnforcer = new DropTarget.DragEnforcer(context);
Winson Chungf0ea4d32011-06-06 14:27:16 -0700305 // With workspace, data is available straight from the get-go
Winson Chungf0ea4d32011-06-06 14:27:16 -0700306
Michael Jurka8bc66c72012-06-21 08:36:45 -0700307 mLauncher = (Launcher) context;
Winson Chungdc61c4d2015-04-20 18:26:57 -0700308 mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this);
Winson Chung867ca622012-02-21 15:48:35 -0800309 final Resources res = getResources();
Adam Cohen2e6da152015-05-06 11:42:25 -0700310 DeviceProfile grid = mLauncher.getDeviceProfile();
311 mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
Michael Jurka869390b2012-05-06 15:55:19 -0700312 mFadeInAdjacentScreens = false;
Dianne Hackborn8f573952009-08-10 23:21:09 -0700313 mWallpaperManager = WallpaperManager.getInstance(context);
Winson Chungaafa03c2010-06-11 17:34:16 -0700314
315 TypedArray a = context.obtainStyledAttributes(attrs,
316 R.styleable.Workspace, defStyle, 0);
Winson Chungb26f3d62011-06-02 10:49:29 -0700317 mSpringLoadedShrinkFactor =
318 res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
Sunny Goyalc6205602015-05-21 20:46:33 -0700319 mOverviewModeShrinkFactor = grid.getOverviewModeScale(mIsRtl);
Winson Chung9e6a0a22013-08-27 11:58:12 -0700320 mOriginalDefaultPage = mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800321 a.recycle();
322
Michael Jurka8b805b12012-04-18 14:23:14 -0700323 setOnHierarchyChangeListener(this);
Joe Onorato0d44e942009-11-16 18:20:51 -0800324 setHapticFeedbackEnabled(false);
Michael Jurka0142d492010-08-25 17:46:15 -0700325
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800326 initWorkspace();
Winson Chungc35afb22011-02-23 13:01:49 -0800327
328 // Disable multitouch across the workspace/all apps/customize tray
329 setMotionEventSplittingEnabled(true);
Adam Cohen53805212013-10-01 10:39:23 -0700330 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800331 }
332
John Spurlock77e1f472013-09-11 10:09:51 -0400333 @Override
334 public void setInsets(Rect insets) {
335 mInsets.set(insets);
Adam Cohen6400b842013-11-26 15:45:38 -0800336
337 CellLayout customScreen = getScreenWithId(CUSTOM_CONTENT_SCREEN_ID);
338 if (customScreen != null) {
339 View customContent = customScreen.getShortcutsAndWidgets().getChildAt(0);
340 if (customContent instanceof Insettable) {
341 ((Insettable) customContent).setInsets(mInsets);
342 }
343 }
John Spurlock77e1f472013-09-11 10:09:51 -0400344 }
345
Michael Jurka038f9d82011-11-03 13:50:45 -0700346 // estimate the size of a widget with spans hSpan, vSpan. return MAX_VALUE for each
347 // dimension if unsuccessful
Sunny Goyal5b0e6692015-03-19 14:31:19 -0700348 public int[] estimateItemSize(ItemInfo itemInfo, boolean springLoaded) {
Michael Jurka038f9d82011-11-03 13:50:45 -0700349 int[] size = new int[2];
350 if (getChildCount() > 0) {
Winson Chungad7db6e2013-10-08 14:01:06 -0700351 // Use the first non-custom page to estimate the child position
352 CellLayout cl = (CellLayout) getChildAt(numCustomPages());
Sunny Goyal5b0e6692015-03-19 14:31:19 -0700353 Rect r = estimateItemPosition(cl, itemInfo, 0, 0, itemInfo.spanX, itemInfo.spanY);
Adam Cohend41fbf52012-02-16 23:53:59 -0800354 size[0] = r.width();
355 size[1] = r.height();
Michael Jurka038f9d82011-11-03 13:50:45 -0700356 if (springLoaded) {
357 size[0] *= mSpringLoadedShrinkFactor;
358 size[1] *= mSpringLoadedShrinkFactor;
359 }
360 return size;
361 } else {
362 size[0] = Integer.MAX_VALUE;
363 size[1] = Integer.MAX_VALUE;
364 return size;
365 }
366 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700367
Adam Cohend41fbf52012-02-16 23:53:59 -0800368 public Rect estimateItemPosition(CellLayout cl, ItemInfo pendingInfo,
Michael Jurka038f9d82011-11-03 13:50:45 -0700369 int hCell, int vCell, int hSpan, int vSpan) {
Adam Cohend41fbf52012-02-16 23:53:59 -0800370 Rect r = new Rect();
Michael Jurka038f9d82011-11-03 13:50:45 -0700371 cl.cellToRect(hCell, vCell, hSpan, vSpan, r);
Michael Jurka038f9d82011-11-03 13:50:45 -0700372 return r;
373 }
374
Adam Cohen5084cba2013-09-03 12:01:16 -0700375 public void onDragStart(final DragSource source, Object info, int dragAction) {
Michael Jurkad74c9842011-07-10 12:44:21 -0700376 mIsDragOccuring = true;
Michael Jurka3a0469d2012-06-21 09:38:41 -0700377 updateChildrenLayersEnabled(false);
Winson Chung641d71d2012-04-26 15:58:01 -0700378 mLauncher.lockScreenOrientation();
Sandeep Siddharthaab2d9d72013-09-17 13:18:24 -0700379 mLauncher.onInteractionBegin();
Winson Chungf561bdf2012-05-03 11:20:19 -0700380 // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging
381 InstallShortcutReceiver.enableInstallQueue();
Adam Cohen5084cba2013-09-03 12:01:16 -0700382 post(new Runnable() {
383 @Override
384 public void run() {
Sunny Goyale9b651e2015-04-24 11:44:51 -0700385 if (mIsDragOccuring && mAddNewPageOnDrag) {
Adam Cohen689ff162014-05-08 17:27:56 -0700386 mDeferRemoveExtraEmptyScreen = false;
Adam Cohen5084cba2013-09-03 12:01:16 -0700387 addExtraEmptyScreenOnDrag();
388 }
389 }
390 });
Michael Jurkad74c9842011-07-10 12:44:21 -0700391 }
392
Sunny Goyale9b651e2015-04-24 11:44:51 -0700393 public void setAddNewPageOnDrag(boolean addPage) {
394 mAddNewPageOnDrag = addPage;
395 }
Adam Cohen689ff162014-05-08 17:27:56 -0700396
397 public void deferRemoveExtraEmptyScreen() {
398 mDeferRemoveExtraEmptyScreen = true;
399 }
400
Michael Jurkad74c9842011-07-10 12:44:21 -0700401 public void onDragEnd() {
Adam Cohen689ff162014-05-08 17:27:56 -0700402 if (!mDeferRemoveExtraEmptyScreen) {
403 removeExtraEmptyScreen(true, mDragSourceInternal != null);
404 }
405
Michael Jurkad74c9842011-07-10 12:44:21 -0700406 mIsDragOccuring = false;
Michael Jurka3a0469d2012-06-21 09:38:41 -0700407 updateChildrenLayersEnabled(false);
Winson Chung4b919f82012-05-01 10:44:08 -0700408 mLauncher.unlockScreenOrientation(false);
Winson Chungf561bdf2012-05-03 11:20:19 -0700409
410 // Re-enable any Un/InstallShortcutReceiver and now process any queued items
411 InstallShortcutReceiver.disableAndFlushInstallQueue(getContext());
Adam Cohen5084cba2013-09-03 12:01:16 -0700412
Adam Cohen5084cba2013-09-03 12:01:16 -0700413 mDragSourceInternal = null;
Sandeep Siddharthaab2d9d72013-09-17 13:18:24 -0700414 mLauncher.onInteractionEnd();
Michael Jurkad74c9842011-07-10 12:44:21 -0700415 }
416
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800417 /**
418 * Initializes various states for this workspace.
419 */
Michael Jurka0142d492010-08-25 17:46:15 -0700420 protected void initWorkspace() {
Michael Jurka0142d492010-08-25 17:46:15 -0700421 mCurrentPage = mDefaultPage;
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400422 LauncherAppState app = LauncherAppState.getInstance();
Adam Cohen2e6da152015-05-06 11:42:25 -0700423 DeviceProfile grid = mLauncher.getDeviceProfile();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800424 mIconCache = app.getIconCache();
Patrick Dubroycd68ff52010-10-28 17:57:05 -0700425 setWillNotDraw(false);
Romain Guyce3cbd12013-02-25 15:00:36 -0800426 setClipChildren(false);
427 setClipToPadding(false);
Adam Cohen7777d962011-08-18 18:58:38 -0700428 setChildrenDrawnWithCacheEnabled(true);
Derek Prothrodadd9842014-01-17 13:43:50 -0500429
Winson Chungc82d2622013-11-06 13:23:29 -0800430 setMinScale(mOverviewModeShrinkFactor);
Adam Cohen22cba7f2013-07-19 16:14:00 -0700431 setupLayoutTransition();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800432
Michael Jurkaab1983f2011-01-18 15:50:17 -0800433 mWallpaperOffset = new WallpaperOffsetInterpolator();
Adam Cohencff6af82011-09-13 14:51:53 -0700434 Display display = mLauncher.getWindowManager().getDefaultDisplay();
Michael Jurka84f2ce72012-04-13 15:08:01 -0700435 display.getSize(mDisplaySize);
Adam Cohen265b9a62011-12-07 14:37:18 -0800436
Winson Chung5f8afe62013-08-12 16:19:28 -0700437 mMaxDistanceForFolderCreation = (0.55f * grid.iconSizePx);
Michael Jurkaa6a05472013-11-13 17:59:46 +0100438
439 // Set the wallpaper dimensions when Launcher starts up
Adam Cohen824fcb32014-05-21 23:01:25 +0000440 setWallpaperDimension();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800441 }
442
Adam Cohen22cba7f2013-07-19 16:14:00 -0700443 private void setupLayoutTransition() {
444 // We want to show layout transitions when pages are deleted, to close the gap.
445 mLayoutTransition = new LayoutTransition();
446 mLayoutTransition.enableTransitionType(LayoutTransition.DISAPPEARING);
447 mLayoutTransition.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
448 mLayoutTransition.disableTransitionType(LayoutTransition.APPEARING);
449 mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
450 setLayoutTransition(mLayoutTransition);
451 }
452
Winson Chung964df6b2013-10-11 15:55:37 -0700453 void enableLayoutTransitions() {
454 setLayoutTransition(mLayoutTransition);
455 }
456 void disableLayoutTransitions() {
457 setLayoutTransition(null);
458 }
459
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800460 @Override
Michael Jurka8b805b12012-04-18 14:23:14 -0700461 public void onChildViewAdded(View parent, View child) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800462 if (!(child instanceof CellLayout)) {
463 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
464 }
Adam Cohen2801caf2011-05-13 20:57:39 -0700465 CellLayout cl = ((CellLayout) child);
466 cl.setOnInterceptTouchListener(this);
Adam Cohen2801caf2011-05-13 20:57:39 -0700467 cl.setClickable(true);
Sunny Goyalc46bfef2015-01-05 12:40:08 -0800468 cl.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
Winson Chungf70696d2013-06-25 16:19:53 -0700469 super.onChildViewAdded(parent, child);
Michael Jurka8b805b12012-04-18 14:23:14 -0700470 }
471
Michael Jurka920d7f42012-05-14 16:29:55 -0700472 protected boolean shouldDrawChild(View child) {
473 final CellLayout cl = (CellLayout) child;
474 return super.shouldDrawChild(child) &&
Winson Chungf4bd2362013-10-07 17:11:27 -0700475 (mIsSwitchingState ||
476 cl.getShortcutsAndWidgets().getAlpha() > 0 ||
Michael Jurka920d7f42012-05-14 16:29:55 -0700477 cl.getBackgroundAlpha() > 0);
478 }
479
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800480 /**
481 * @return The open folder on the current screen, or null if there is none
482 */
Sunny Goyal83a8f042015-05-19 12:52:12 -0700483 public Folder getOpenFolder() {
Adam Cohen716b51e2011-06-30 12:09:54 -0700484 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohen8e776a62011-06-28 18:10:06 -0700485 int count = dragLayer.getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800486 for (int i = 0; i < count; i++) {
Adam Cohen8e776a62011-06-28 18:10:06 -0700487 View child = dragLayer.getChildAt(i);
Winson Chungaafa03c2010-06-11 17:34:16 -0700488 if (child instanceof Folder) {
489 Folder folder = (Folder) child;
490 if (folder.getInfo().opened)
491 return folder;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800492 }
493 }
494 return null;
495 }
496
Patrick Dubroya0aa0122011-02-24 11:42:23 -0800497 boolean isTouchActive() {
498 return mTouchState != TOUCH_STATE_REST;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800499 }
500
Winson Chung9e6a0a22013-08-27 11:58:12 -0700501 public void removeAllWorkspaceScreens() {
Winson Chung964df6b2013-10-11 15:55:37 -0700502 // Disable all layout transitions before removing all pages to ensure that we don't get the
503 // transition animations competing with us changing the scroll when we add pages or the
504 // custom content screen
505 disableLayoutTransitions();
506
507 // Since we increment the current page when we call addCustomContentPage via bindScreens
508 // (and other places), we need to adjust the current page back when we clear the pages
509 if (hasCustomContent()) {
510 removeCustomContentPage();
511 }
512
Winson Chung9e6a0a22013-08-27 11:58:12 -0700513 // Remove the pages and clear the screen models
514 removeAllViews();
515 mScreenOrder.clear();
516 mWorkspaceScreens.clear();
Winson Chung964df6b2013-10-11 15:55:37 -0700517
518 // Re-enable the layout transitions
519 enableLayoutTransitions();
Winson Chung9e6a0a22013-08-27 11:58:12 -0700520 }
521
Adam Cohen89bddfa2013-08-20 11:57:13 -0700522 public long insertNewWorkspaceScreenBeforeEmptyScreen(long screenId) {
Winson Chung64359a52013-07-08 17:17:08 -0700523 // Find the index to insert this view into. If the empty screen exists, then
524 // insert it before that.
525 int insertIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
526 if (insertIndex < 0) {
527 insertIndex = mScreenOrder.size();
528 }
Adam Cohen89bddfa2013-08-20 11:57:13 -0700529 return insertNewWorkspaceScreen(screenId, insertIndex);
Adam Cohendcd297f2013-06-18 13:13:40 -0700530 }
531
Adam Cohen89bddfa2013-08-20 11:57:13 -0700532 public long insertNewWorkspaceScreen(long screenId) {
533 return insertNewWorkspaceScreen(screenId, getChildCount());
Winson Chung64359a52013-07-08 17:17:08 -0700534 }
535
Adam Cohen89bddfa2013-08-20 11:57:13 -0700536 public long insertNewWorkspaceScreen(long screenId, int insertIndex) {
Adam Cohen5084cba2013-09-03 12:01:16 -0700537 if (mWorkspaceScreens.containsKey(screenId)) {
538 throw new RuntimeException("Screen id " + screenId + " already exists!");
539 }
540
Sunny Goyal70660032015-05-14 00:07:08 -0700541 // Inflate the cell layout, but do not add it automatically so that we can get the newly
542 // created CellLayout.
543 CellLayout newScreen = (CellLayout) mLauncher.getLayoutInflater().inflate(
544 R.layout.workspace_screen, this, false /* attachToRoot */);
Adam Cohendcd297f2013-06-18 13:13:40 -0700545
Adam Cohen7d30a372013-07-01 17:03:59 -0700546 newScreen.setOnLongClickListener(mLongClickListener);
Adam Cohenf358a4b2013-07-23 16:47:31 -0700547 newScreen.setOnClickListener(mLauncher);
Winson Chung837b2412013-09-09 14:43:51 -0700548 newScreen.setSoundEffectsEnabled(false);
Adam Cohendcd297f2013-06-18 13:13:40 -0700549 mWorkspaceScreens.put(screenId, newScreen);
Winson Chung64359a52013-07-08 17:17:08 -0700550 mScreenOrder.add(insertIndex, screenId);
551 addView(newScreen, insertIndex);
Adam Cohenc9735cf2015-01-23 16:11:55 -0800552
Winson Chung3d9490a2015-03-24 17:38:42 -0700553 LauncherAccessibilityDelegate delegate =
554 LauncherAppState.getInstance().getAccessibilityDelegate();
555 if (delegate != null && delegate.isInAccessibleDrag()) {
Sunny Goyale9b651e2015-04-24 11:44:51 -0700556 newScreen.enableAccessibleDrag(true, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
Adam Cohenc9735cf2015-01-23 16:11:55 -0800557 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700558 return screenId;
559 }
560
Derek Prothrodadd9842014-01-17 13:43:50 -0500561 public void createCustomContentContainer() {
Adam Cohendcd297f2013-06-18 13:13:40 -0700562 CellLayout customScreen = (CellLayout)
Sunny Goyal70660032015-05-14 00:07:08 -0700563 mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, this, false);
Adam Cohenc50438c2014-08-19 17:43:05 -0700564 customScreen.disableDragTarget();
Adam Cohendcd297f2013-06-18 13:13:40 -0700565
Adam Cohendcd297f2013-06-18 13:13:40 -0700566 mWorkspaceScreens.put(CUSTOM_CONTENT_SCREEN_ID, customScreen);
567 mScreenOrder.add(0, CUSTOM_CONTENT_SCREEN_ID);
Adam Cohenedb40762013-07-18 16:45:45 -0700568
Adam Cohen2702ea02013-08-15 16:17:42 -0700569 // We want no padding on the custom content
570 customScreen.setPadding(0, 0, 0, 0);
571
Adam Cohenedb40762013-07-18 16:45:45 -0700572 addFullScreenPage(customScreen);
Michael Jurkaee0ce2b2013-07-02 17:24:35 -0700573
Adam Cohendcd297f2013-06-18 13:13:40 -0700574 // Ensure that the current page and default page are maintained.
Winson Chung9e6a0a22013-08-27 11:58:12 -0700575 mDefaultPage = mOriginalDefaultPage + 1;
Winson Chung82e5c982013-10-09 12:04:50 -0700576
577 // Update the custom content hint
Adam Cohen21cd0022013-10-09 18:57:02 -0700578 if (mRestorePage != INVALID_RESTORE_PAGE) {
579 mRestorePage = mRestorePage + 1;
580 } else {
581 setCurrentPage(getCurrentPage() + 1);
582 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700583 }
584
Dave Hawkeya8881582013-09-17 15:55:33 -0600585 public void removeCustomContentPage() {
Dave Hawkeya8881582013-09-17 15:55:33 -0600586 CellLayout customScreen = getScreenWithId(CUSTOM_CONTENT_SCREEN_ID);
587 if (customScreen == null) {
588 throw new RuntimeException("Expected custom content screen to exist");
589 }
590
591 mWorkspaceScreens.remove(CUSTOM_CONTENT_SCREEN_ID);
592 mScreenOrder.remove(CUSTOM_CONTENT_SCREEN_ID);
593 removeView(customScreen);
Adam Cohenbb6fda62013-10-10 12:48:52 -0700594
595 if (mCustomContentCallbacks != null) {
596 mCustomContentCallbacks.onScrollProgressChanged(0);
597 mCustomContentCallbacks.onHide();
598 }
599
Dave Hawkeya8881582013-09-17 15:55:33 -0600600 mCustomContentCallbacks = null;
601
602 // Ensure that the current page and default page are maintained.
603 mDefaultPage = mOriginalDefaultPage - 1;
Winson Chung82e5c982013-10-09 12:04:50 -0700604
605 // Update the custom content hint
Adam Cohen21cd0022013-10-09 18:57:02 -0700606 if (mRestorePage != INVALID_RESTORE_PAGE) {
607 mRestorePage = mRestorePage - 1;
608 } else {
609 setCurrentPage(getCurrentPage() - 1);
610 }
Dave Hawkeya8881582013-09-17 15:55:33 -0600611 }
612
Adam Cohen53805212013-10-01 10:39:23 -0700613 public void addToCustomContentPage(View customContent, CustomContentCallbacks callbacks,
614 String description) {
Winson Chung98ca0f72013-07-29 12:58:51 -0700615 if (getPageIndexForScreenId(CUSTOM_CONTENT_SCREEN_ID) < 0) {
616 throw new RuntimeException("Expected custom content screen to exist");
617 }
618
619 // Add the custom content to the full screen custom page
620 CellLayout customScreen = getScreenWithId(CUSTOM_CONTENT_SCREEN_ID);
621 int spanX = customScreen.getCountX();
622 int spanY = customScreen.getCountY();
623 CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, spanX, spanY);
624 lp.canReorder = false;
625 lp.isFullscreen = true;
John Spurlock77e1f472013-09-11 10:09:51 -0400626 if (customContent instanceof Insettable) {
627 ((Insettable)customContent).setInsets(mInsets);
628 }
Adam Cohen166ebd42013-11-26 14:55:45 -0800629
630 // Verify that the child is removed from any existing parent.
631 if (customContent.getParent() instanceof ViewGroup) {
632 ViewGroup parent = (ViewGroup) customContent.getParent();
633 parent.removeView(customContent);
634 }
Adam Cohen225ad9b2013-10-07 13:03:00 -0700635 customScreen.removeAllViews();
Winson Chung98ca0f72013-07-29 12:58:51 -0700636 customScreen.addViewToCellLayout(customContent, 0, 0, lp, true);
Adam Cohen53805212013-10-01 10:39:23 -0700637 mCustomContentDescription = description;
Winson Chung98ca0f72013-07-29 12:58:51 -0700638
639 mCustomContentCallbacks = callbacks;
640 }
641
Adam Cohen5084cba2013-09-03 12:01:16 -0700642 public void addExtraEmptyScreenOnDrag() {
643 boolean lastChildOnScreen = false;
644 boolean childOnFinalScreen = false;
645
Adam Cohenad4e15c2013-10-17 16:21:35 -0700646 // Cancel any pending removal of empty screen
647 mRemoveEmptyScreenRunnable = null;
648
Adam Cohen5084cba2013-09-03 12:01:16 -0700649 if (mDragSourceInternal != null) {
650 if (mDragSourceInternal.getChildCount() == 1) {
651 lastChildOnScreen = true;
652 }
653 CellLayout cl = (CellLayout) mDragSourceInternal.getParent();
654 if (indexOfChild(cl) == getChildCount() - 1) {
655 childOnFinalScreen = true;
656 }
657 }
658
659 // If this is the last item on the final screen
660 if (lastChildOnScreen && childOnFinalScreen) {
661 return;
662 }
663 if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) {
664 insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID);
665 }
666 }
667
668 public boolean addExtraEmptyScreen() {
669 if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) {
670 insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID);
671 return true;
672 }
673 return false;
674 }
675
Adam Cohenad4e15c2013-10-17 16:21:35 -0700676 private void convertFinalScreenToEmptyScreenIfNecessary() {
Adam Cohendcb173d2014-04-01 13:33:58 -0700677 if (mLauncher.isWorkspaceLoading()) {
678 // Invalid and dangerous operation if workspace is loading
679 Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
680 return;
681 }
682
Adam Cohenad4e15c2013-10-17 16:21:35 -0700683 if (hasExtraEmptyScreen() || mScreenOrder.size() == 0) return;
684 long finalScreenId = mScreenOrder.get(mScreenOrder.size() - 1);
685
686 if (finalScreenId == CUSTOM_CONTENT_SCREEN_ID) return;
687 CellLayout finalScreen = mWorkspaceScreens.get(finalScreenId);
688
689 // If the final screen is empty, convert it to the extra empty screen
Adam Cohen917e3882013-10-31 15:03:35 -0700690 if (finalScreen.getShortcutsAndWidgets().getChildCount() == 0 &&
691 !finalScreen.isDropPending()) {
Adam Cohenad4e15c2013-10-17 16:21:35 -0700692 mWorkspaceScreens.remove(finalScreenId);
693 mScreenOrder.remove(finalScreenId);
694
695 // if this is the last non-custom content screen, convert it to the empty screen
696 mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, finalScreen);
697 mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
Winson Chunga90303b2013-11-15 13:05:06 -0800698
Winson Chungf0716b72013-11-18 16:09:54 -0800699 // Update the model if we have changed any screens
700 mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
Adam Cohen5084cba2013-09-03 12:01:16 -0700701 }
702 }
703
Adam Cohen689ff162014-05-08 17:27:56 -0700704 public void removeExtraEmptyScreen(final boolean animate, boolean stripEmptyScreens) {
705 removeExtraEmptyScreenDelayed(animate, null, 0, stripEmptyScreens);
Adam Cohenad4e15c2013-10-17 16:21:35 -0700706 }
707
Adam Cohen689ff162014-05-08 17:27:56 -0700708 public void removeExtraEmptyScreenDelayed(final boolean animate, final Runnable onComplete,
Adam Cohenad4e15c2013-10-17 16:21:35 -0700709 final int delay, final boolean stripEmptyScreens) {
Adam Cohendcb173d2014-04-01 13:33:58 -0700710 if (mLauncher.isWorkspaceLoading()) {
711 // Don't strip empty screens if the workspace is still loading
712 Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
713 return;
714 }
715
Adam Cohenad4e15c2013-10-17 16:21:35 -0700716 if (delay > 0) {
717 postDelayed(new Runnable() {
718 @Override
719 public void run() {
Adam Cohen689ff162014-05-08 17:27:56 -0700720 removeExtraEmptyScreenDelayed(animate, onComplete, 0, stripEmptyScreens);
Adam Cohenad4e15c2013-10-17 16:21:35 -0700721 }
Adam Cohenad4e15c2013-10-17 16:21:35 -0700722 }, delay);
723 return;
724 }
725
726 convertFinalScreenToEmptyScreenIfNecessary();
727 if (hasExtraEmptyScreen()) {
728 int emptyIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
729 if (getNextPage() == emptyIndex) {
730 snapToPage(getNextPage() - 1, SNAP_OFF_EMPTY_SCREEN_DURATION);
731 fadeAndRemoveEmptyScreen(SNAP_OFF_EMPTY_SCREEN_DURATION, FADE_EMPTY_SCREEN_DURATION,
732 onComplete, stripEmptyScreens);
733 } else {
734 fadeAndRemoveEmptyScreen(0, FADE_EMPTY_SCREEN_DURATION,
735 onComplete, stripEmptyScreens);
736 }
737 return;
Adam Cohen3a14eeb2013-11-15 16:51:22 +0000738 } else if (stripEmptyScreens) {
739 // If we're not going to strip the empty screens after removing
740 // the extra empty screen, do it right away.
741 stripEmptyScreens();
Adam Cohenad4e15c2013-10-17 16:21:35 -0700742 }
Adam Cohen3a14eeb2013-11-15 16:51:22 +0000743
Adam Cohenad4e15c2013-10-17 16:21:35 -0700744 if (onComplete != null) {
745 onComplete.run();
746 }
747 }
748
749 private void fadeAndRemoveEmptyScreen(int delay, int duration, final Runnable onComplete,
750 final boolean stripEmptyScreens) {
Winson Chunga90303b2013-11-15 13:05:06 -0800751 // XXX: Do we need to update LM workspace screens below?
Adam Cohenad4e15c2013-10-17 16:21:35 -0700752 PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0f);
753 PropertyValuesHolder bgAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", 0f);
754
755 final CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
756
757 mRemoveEmptyScreenRunnable = new Runnable() {
758 @Override
759 public void run() {
760 if (hasExtraEmptyScreen()) {
761 mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
762 mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
763 removeView(cl);
764 if (stripEmptyScreens) {
765 stripEmptyScreens();
766 }
767 }
768 }
769 };
770
771 ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(cl, alpha, bgAlpha);
772 oa.setDuration(duration);
773 oa.setStartDelay(delay);
774 oa.addListener(new AnimatorListenerAdapter() {
775 @Override
776 public void onAnimationEnd(Animator animation) {
777 if (mRemoveEmptyScreenRunnable != null) {
778 mRemoveEmptyScreenRunnable.run();
779 }
780 if (onComplete != null) {
781 onComplete.run();
782 }
783 }
784 });
785 oa.start();
786 }
787
Michael Jurka2f817312013-09-20 03:03:42 +0200788 public boolean hasExtraEmptyScreen() {
789 int nScreens = getChildCount();
Michael Jurkafe0ace32013-10-03 01:05:14 -0700790 nScreens = nScreens - numCustomPages();
Michael Jurka2f817312013-09-20 03:03:42 +0200791 return mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID) && nScreens > 1;
792 }
793
Adam Cohendcd297f2013-06-18 13:13:40 -0700794 public long commitExtraEmptyScreen() {
Adam Cohendcb173d2014-04-01 13:33:58 -0700795 if (mLauncher.isWorkspaceLoading()) {
796 // Invalid and dangerous operation if workspace is loading
797 Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
798 return -1;
799 }
Winson Chunga90303b2013-11-15 13:05:06 -0800800
Winson Chung89f97052013-09-20 11:32:26 -0700801 int index = getPageIndexForScreenId(EXTRA_EMPTY_SCREEN_ID);
Adam Cohendcd297f2013-06-18 13:13:40 -0700802 CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
Winson Chungc9168342013-06-26 14:54:55 -0700803 mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
Adam Cohendcd297f2013-06-18 13:13:40 -0700804 mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
805
Michael Jurka104c4562013-07-08 18:03:46 -0700806 long newId = LauncherAppState.getLauncherProvider().generateNewScreenId();
Adam Cohendcd297f2013-06-18 13:13:40 -0700807 mWorkspaceScreens.put(newId, cl);
808 mScreenOrder.add(newId);
809
Winson Chung89f97052013-09-20 11:32:26 -0700810 // Update the page indicator marker
811 if (getPageIndicator() != null) {
812 getPageIndicator().updateMarker(index, getPageIndicatorMarker(index));
813 }
814
Winson Chung64359a52013-07-08 17:17:08 -0700815 // Update the model for the new screen
816 mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
817
Adam Cohendcd297f2013-06-18 13:13:40 -0700818 return newId;
819 }
820
Adam Cohendcd297f2013-06-18 13:13:40 -0700821 public CellLayout getScreenWithId(long screenId) {
822 CellLayout layout = mWorkspaceScreens.get(screenId);
823 return layout;
824 }
825
826 public long getIdForScreen(CellLayout layout) {
Sunny Goyale2df0622015-04-24 11:27:00 -0700827 int index = mWorkspaceScreens.indexOfValue(layout);
828 if (index != -1) {
829 return mWorkspaceScreens.keyAt(index);
Adam Cohendcd297f2013-06-18 13:13:40 -0700830 }
831 return -1;
832 }
833
834 public int getPageIndexForScreenId(long screenId) {
835 return indexOfChild(mWorkspaceScreens.get(screenId));
836 }
837
838 public long getScreenIdForPageIndex(int index) {
Winson Chung5f8afe62013-08-12 16:19:28 -0700839 if (0 <= index && index < mScreenOrder.size()) {
840 return mScreenOrder.get(index);
841 }
842 return -1;
Adam Cohendcd297f2013-06-18 13:13:40 -0700843 }
844
Sunny Goyal83a8f042015-05-19 12:52:12 -0700845 public ArrayList<Long> getScreenOrder() {
Winson Chungc9168342013-06-26 14:54:55 -0700846 return mScreenOrder;
847 }
848
Adam Cohendcd297f2013-06-18 13:13:40 -0700849 public void stripEmptyScreens() {
Adam Cohen65e43032014-03-03 11:37:21 -0800850 if (mLauncher.isWorkspaceLoading()) {
Adam Cohendcb173d2014-04-01 13:33:58 -0700851 // Don't strip empty screens if the workspace is still loading.
852 // This is dangerous and can result in data loss.
Adam Cohen517a7f52014-03-01 12:12:59 -0800853 Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
854 return;
855 }
856
Adam Cohenaccfd562013-07-12 14:40:40 -0700857 if (isPageMoving()) {
858 mStripScreensOnPageStopMoving = true;
859 return;
860 }
861
862 int currentPage = getNextPage();
Adam Cohendcd297f2013-06-18 13:13:40 -0700863 ArrayList<Long> removeScreens = new ArrayList<Long>();
Sunny Goyale2df0622015-04-24 11:27:00 -0700864 int total = mWorkspaceScreens.size();
865 for (int i = 0; i < total; i++) {
866 long id = mWorkspaceScreens.keyAt(i);
867 CellLayout cl = mWorkspaceScreens.valueAt(i);
Winson Chungc9168342013-06-26 14:54:55 -0700868 if (id >= 0 && cl.getShortcutsAndWidgets().getChildCount() == 0) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700869 removeScreens.add(id);
870 }
871 }
872
Adam Cohen5084cba2013-09-03 12:01:16 -0700873 // We enforce at least one page to add new items to. In the case that we remove the last
874 // such screen, we convert the last screen to the empty screen
Michael Jurkafe0ace32013-10-03 01:05:14 -0700875 int minScreens = 1 + numCustomPages();
Adam Cohen5084cba2013-09-03 12:01:16 -0700876
Adam Cohendcd297f2013-06-18 13:13:40 -0700877 int pageShift = 0;
878 for (Long id: removeScreens) {
879 CellLayout cl = mWorkspaceScreens.get(id);
880 mWorkspaceScreens.remove(id);
881 mScreenOrder.remove(id);
Adam Cohen5084cba2013-09-03 12:01:16 -0700882
883 if (getChildCount() > minScreens) {
884 if (indexOfChild(cl) < currentPage) {
885 pageShift++;
886 }
887 removeView(cl);
888 } else {
889 // if this is the last non-custom content screen, convert it to the empty screen
Adam Cohenad4e15c2013-10-17 16:21:35 -0700890 mRemoveEmptyScreenRunnable = null;
Adam Cohen5084cba2013-09-03 12:01:16 -0700891 mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, cl);
892 mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
Adam Cohendcd297f2013-06-18 13:13:40 -0700893 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700894 }
Winson Chung64359a52013-07-08 17:17:08 -0700895
896 if (!removeScreens.isEmpty()) {
897 // Update the model if we have changed any screens
898 mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
899 }
Adam Cohenaccfd562013-07-12 14:40:40 -0700900
901 if (pageShift >= 0) {
902 setCurrentPage(currentPage - pageShift);
903 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700904 }
905
906 // See implementation for parameter definition.
907 void addInScreen(View child, long container, long screenId,
908 int x, int y, int spanX, int spanY) {
909 addInScreen(child, container, screenId, x, y, spanX, spanY, false, false);
910 }
911
912 // At bind time, we use the rank (screenId) to compute x and y for hotseat items.
913 // See implementation for parameter definition.
914 void addInScreenFromBind(View child, long container, long screenId, int x, int y,
915 int spanX, int spanY) {
916 addInScreen(child, container, screenId, x, y, spanX, spanY, false, true);
917 }
918
919 // See implementation for parameter definition.
920 void addInScreen(View child, long container, long screenId, int x, int y, int spanX, int spanY,
921 boolean insert) {
922 addInScreen(child, container, screenId, x, y, spanX, spanY, insert, false);
Winson Chungaafa03c2010-06-11 17:34:16 -0700923 }
924
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800925 /**
926 * Adds the specified child in the specified screen. The position and dimension of
927 * the child are defined by x, y, spanX and spanY.
928 *
929 * @param child The child to add in one of the workspace's screens.
Adam Cohendcd297f2013-06-18 13:13:40 -0700930 * @param screenId The screen in which to add the child.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800931 * @param x The X position of the child in the screen's grid.
932 * @param y The Y position of the child in the screen's grid.
933 * @param spanX The number of cells spanned horizontally by the child.
934 * @param spanY The number of cells spanned vertically by the child.
935 * @param insert When true, the child is inserted at the beginning of the children list.
Adam Cohendcd297f2013-06-18 13:13:40 -0700936 * @param computeXYFromRank When true, we use the rank (stored in screenId) to compute
937 * the x and y position in which to place hotseat items. Otherwise
938 * we use the x and y position to compute the rank.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800939 */
Adam Cohendcd297f2013-06-18 13:13:40 -0700940 void addInScreen(View child, long container, long screenId, int x, int y, int spanX, int spanY,
941 boolean insert, boolean computeXYFromRank) {
Winson Chung3d503fb2011-07-13 17:25:49 -0700942 if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700943 if (getScreenWithId(screenId) == null) {
944 Log.e(TAG, "Skipping child, screenId " + screenId + " not found");
Winson Chung8481e322013-08-09 16:06:38 -0700945 // DEBUGGING - Print out the stack trace to see where we are adding from
946 new Throwable().printStackTrace();
Winson Chung3d503fb2011-07-13 17:25:49 -0700947 return;
948 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800949 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700950 if (screenId == EXTRA_EMPTY_SCREEN_ID) {
Adam Cohendedbd962013-07-11 14:21:49 -0700951 // This should never happen
952 throw new RuntimeException("Screen id should not be EXTRA_EMPTY_SCREEN_ID");
Adam Cohendcd297f2013-06-18 13:13:40 -0700953 }
954
Winson Chung3d503fb2011-07-13 17:25:49 -0700955 final CellLayout layout;
956 if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
957 layout = mLauncher.getHotseat().getLayout();
Sunny Goyalb3726d92014-08-20 16:58:17 -0700958 child.setOnKeyListener(new HotseatIconKeyEventListener());
Winson Chung3d503fb2011-07-13 17:25:49 -0700959
Adam Cohen099f60d2011-08-23 21:07:26 -0700960 // Hide folder title in the hotseat
961 if (child instanceof FolderIcon) {
962 ((FolderIcon) child).setTextVisible(false);
963 }
964
Adam Cohendcd297f2013-06-18 13:13:40 -0700965 if (computeXYFromRank) {
966 x = mLauncher.getHotseat().getCellXFromOrder((int) screenId);
967 y = mLauncher.getHotseat().getCellYFromOrder((int) screenId);
Winson Chung3d503fb2011-07-13 17:25:49 -0700968 } else {
Adam Cohendcd297f2013-06-18 13:13:40 -0700969 screenId = mLauncher.getHotseat().getOrderInHotseat(x, y);
Winson Chung3d503fb2011-07-13 17:25:49 -0700970 }
971 } else {
Adam Cohen099f60d2011-08-23 21:07:26 -0700972 // Show folder title if not in the hotseat
973 if (child instanceof FolderIcon) {
974 ((FolderIcon) child).setTextVisible(true);
975 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700976 layout = getScreenWithId(screenId);
Adam Cohenac56cff2011-09-28 20:45:37 -0700977 child.setOnKeyListener(new IconKeyEventListener());
Winson Chung3d503fb2011-07-13 17:25:49 -0700978 }
979
Adam Cohen96d30a12013-07-16 18:13:21 -0700980 ViewGroup.LayoutParams genericLp = child.getLayoutParams();
Adam Cohened66b2b2012-01-23 17:28:51 -0800981 CellLayout.LayoutParams lp;
982 if (genericLp == null || !(genericLp instanceof CellLayout.LayoutParams)) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800983 lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
984 } else {
Adam Cohened66b2b2012-01-23 17:28:51 -0800985 lp = (CellLayout.LayoutParams) genericLp;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800986 lp.cellX = x;
987 lp.cellY = y;
988 lp.cellHSpan = spanX;
989 lp.cellVSpan = spanY;
990 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700991
Adam Cohen7f4eabe2011-04-21 16:19:16 -0700992 if (spanX < 0 && spanY < 0) {
993 lp.isLockedToGrid = false;
994 }
995
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700996 // Get the canonical child id to uniquely represent this view in this screen
Adam Cohenc76e1dd2013-11-14 11:09:14 +0000997 ItemInfo info = (ItemInfo) child.getTag();
998 int childId = mLauncher.getViewIdForItem(info);
999
Michael Jurkaf3ca3ab2010-10-20 17:08:24 -07001000 boolean markCellsAsOccupied = !(child instanceof Folder);
Winson Chung3d503fb2011-07-13 17:25:49 -07001001 if (!layout.addViewToCellLayout(child, insert ? 0 : -1, childId, lp, markCellsAsOccupied)) {
Winson Chungaafa03c2010-06-11 17:34:16 -07001002 // TODO: This branch occurs when the workspace is adding views
1003 // outside of the defined grid
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07001004 // maybe we should be deleting these items from the LauncherModel?
Adam Cohen4caf2982013-08-20 18:54:31 -07001005 Launcher.addDumpLog(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout", true);
Winson Chungaafa03c2010-06-11 17:34:16 -07001006 }
1007
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001008 if (!(child instanceof Folder)) {
Joe Onorato0d44e942009-11-16 18:20:51 -08001009 child.setHapticFeedbackEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001010 child.setOnLongClickListener(mLongClickListener);
1011 }
Joe Onorato00acb122009-08-04 16:04:30 -04001012 if (child instanceof DropTarget) {
Winson Chungaafa03c2010-06-11 17:34:16 -07001013 mDragController.addDropTarget((DropTarget) child);
Joe Onorato00acb122009-08-04 16:04:30 -04001014 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001015 }
1016
Patrick Dubroyd0ce1ec2011-01-19 18:47:27 -08001017 /**
Patrick Dubroye708c522011-03-01 16:03:43 -08001018 * Called directly from a CellLayout (not by the framework), after we've been added as a
1019 * listener via setOnInterceptTouchEventListener(). This allows us to tell the CellLayout
1020 * that it should intercept touch events, which is not something that is normally supported.
1021 */
Sunny Goyal70660032015-05-14 00:07:08 -07001022 @SuppressLint("ClickableViewAccessibility")
Patrick Dubroye708c522011-03-01 16:03:43 -08001023 @Override
Michael Jurkadee05892010-07-27 10:01:56 -07001024 public boolean onTouch(View v, MotionEvent event) {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001025 return (workspaceInModalState() || !isFinishedSwitchingState())
1026 || (!workspaceInModalState() && indexOfChild(v) != mCurrentPage);
Patrick Dubroye708c522011-03-01 16:03:43 -08001027 }
1028
Adam Cohenfc53cd22011-07-20 15:45:11 -07001029 public boolean isSwitchingState() {
1030 return mIsSwitchingState;
1031 }
1032
Winson Chung70442722012-02-10 15:43:22 -08001033 /** This differs from isSwitchingState in that we take into account how far the transition
1034 * has completed. */
Winson Chung9b0b2fe2012-02-24 13:03:34 -08001035 public boolean isFinishedSwitchingState() {
Winson Chung70442722012-02-10 15:43:22 -08001036 return !mIsSwitchingState || (mTransitionProgress > 0.5f);
1037 }
1038
Adam Cohended9f8d2010-11-03 13:25:16 -07001039 protected void onWindowVisibilityChanged (int visibility) {
1040 mLauncher.onWindowVisibilityChanged(visibility);
1041 }
1042
Michael Jurka5f1c5092010-09-03 14:15:02 -07001043 @Override
1044 public boolean dispatchUnhandledMove(View focused, int direction) {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001045 if (workspaceInModalState() || !isFinishedSwitchingState()) {
Michael Jurka5f1c5092010-09-03 14:15:02 -07001046 // when the home screens are shrunken, shouldn't allow side-scrolling
1047 return false;
1048 }
1049 return super.dispatchUnhandledMove(focused, direction);
1050 }
1051
1052 @Override
1053 public boolean onInterceptTouchEvent(MotionEvent ev) {
Michael Jurkad771c962011-08-09 15:00:48 -07001054 switch (ev.getAction() & MotionEvent.ACTION_MASK) {
1055 case MotionEvent.ACTION_DOWN:
Adam Cohenf8d28232011-02-01 21:47:00 -08001056 mXDown = ev.getX();
1057 mYDown = ev.getY();
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001058 mTouchDownTime = System.currentTimeMillis();
Michael Jurkad771c962011-08-09 15:00:48 -07001059 break;
1060 case MotionEvent.ACTION_POINTER_UP:
1061 case MotionEvent.ACTION_UP:
1062 if (mTouchState == TOUCH_STATE_REST) {
1063 final CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage);
Sunny Goyal2805e632015-05-20 15:35:32 -07001064 if (currentPage != null) {
Michael Jurkad771c962011-08-09 15:00:48 -07001065 onWallpaperTap(ev);
1066 }
1067 }
Adam Cohenf8d28232011-02-01 21:47:00 -08001068 }
Michael Jurka5f1c5092010-09-03 14:15:02 -07001069 return super.onInterceptTouchEvent(ev);
1070 }
1071
Jan-Willem Maarsed3fbe682014-08-19 15:27:48 -07001072 @Override
1073 public boolean onGenericMotionEvent(MotionEvent event) {
1074 // Ignore pointer scroll events if the custom content doesn't allow scrolling.
1075 if ((getScreenIdForPageIndex(getCurrentPage()) == CUSTOM_CONTENT_SCREEN_ID)
1076 && (mCustomContentCallbacks != null)
1077 && !mCustomContentCallbacks.isScrollingAllowed()) {
1078 return false;
1079 }
1080 return super.onGenericMotionEvent(event);
1081 }
1082
Adam Cohen3d509322012-06-06 14:10:04 -07001083 protected void reinflateWidgetsIfNecessary() {
1084 final int clCount = getChildCount();
1085 for (int i = 0; i < clCount; i++) {
1086 CellLayout cl = (CellLayout) getChildAt(i);
1087 ShortcutAndWidgetContainer swc = cl.getShortcutsAndWidgets();
1088 final int itemCount = swc.getChildCount();
1089 for (int j = 0; j < itemCount; j++) {
1090 View v = swc.getChildAt(j);
1091
Sunny Goyalff572272014-07-23 13:58:07 -07001092 if (v != null && v.getTag() instanceof LauncherAppWidgetInfo) {
Adam Cohen3d509322012-06-06 14:10:04 -07001093 LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
1094 LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) info.hostView;
Sunny Goyalff572272014-07-23 13:58:07 -07001095 if (lahv != null && lahv.isReinflateRequired()) {
Adam Cohen3d509322012-06-06 14:10:04 -07001096 mLauncher.removeAppWidget(info);
1097 // Remove the current widget which is inflated with the wrong orientation
1098 cl.removeView(lahv);
1099 mLauncher.bindAppWidget(info);
1100 }
1101 }
1102 }
1103 }
1104 }
1105
Michael Jurka1adf5392010-10-18 18:10:22 -07001106 @Override
1107 protected void determineScrollingStart(MotionEvent ev) {
Winson Chung70442722012-02-10 15:43:22 -08001108 if (!isFinishedSwitchingState()) return;
Adam Cohenf8d28232011-02-01 21:47:00 -08001109
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001110 float deltaX = ev.getX() - mXDown;
1111 float absDeltaX = Math.abs(deltaX);
1112 float absDeltaY = Math.abs(ev.getY() - mYDown);
Adam Cohenf8d28232011-02-01 21:47:00 -08001113
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001114 if (Float.compare(absDeltaX, 0f) == 0) return;
Adam Cohenf8d28232011-02-01 21:47:00 -08001115
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001116 float slope = absDeltaY / absDeltaX;
Winson Chung70442722012-02-10 15:43:22 -08001117 float theta = (float) Math.atan(slope);
Adam Cohenf8d28232011-02-01 21:47:00 -08001118
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001119 if (absDeltaX > mTouchSlop || absDeltaY > mTouchSlop) {
Winson Chung70442722012-02-10 15:43:22 -08001120 cancelCurrentPageLongPress();
1121 }
1122
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001123 boolean passRightSwipesToCustomContent =
1124 (mTouchDownTime - mCustomContentShowTime) > CUSTOM_CONTENT_GESTURE_DELAY;
1125
Sunny Goyal70660032015-05-14 00:07:08 -07001126 boolean swipeInIgnoreDirection = mIsRtl ? deltaX < 0 : deltaX > 0;
Jan-Willem Maarse2ff91c42014-07-10 15:58:12 -07001127 boolean onCustomContentScreen =
1128 getScreenIdForPageIndex(getCurrentPage()) == CUSTOM_CONTENT_SCREEN_ID;
1129 if (swipeInIgnoreDirection && onCustomContentScreen && passRightSwipesToCustomContent) {
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001130 // Pass swipes to the right to the custom content page.
1131 return;
1132 }
1133
Jan-Willem Maarse2ff91c42014-07-10 15:58:12 -07001134 if (onCustomContentScreen && (mCustomContentCallbacks != null)
1135 && !mCustomContentCallbacks.isScrollingAllowed()) {
1136 // Don't allow workspace scrolling if the current custom content screen doesn't allow
1137 // scrolling.
1138 return;
1139 }
1140
Winson Chung70442722012-02-10 15:43:22 -08001141 if (theta > MAX_SWIPE_ANGLE) {
1142 // Above MAX_SWIPE_ANGLE, we don't want to ever start scrolling the workspace
1143 return;
1144 } else if (theta > START_DAMPING_TOUCH_SLOP_ANGLE) {
1145 // Above START_DAMPING_TOUCH_SLOP_ANGLE and below MAX_SWIPE_ANGLE, we want to
1146 // increase the touch slop to make it harder to begin scrolling the workspace. This
1147 // results in vertically scrolling widgets to more easily. The higher the angle, the
1148 // more we increase touch slop.
1149 theta -= START_DAMPING_TOUCH_SLOP_ANGLE;
1150 float extraRatio = (float)
1151 Math.sqrt((theta / (MAX_SWIPE_ANGLE - START_DAMPING_TOUCH_SLOP_ANGLE)));
1152 super.determineScrollingStart(ev, 1 + TOUCH_SLOP_DAMPING_FACTOR * extraRatio);
1153 } else {
1154 // Below START_DAMPING_TOUCH_SLOP_ANGLE, we don't do anything special
1155 super.determineScrollingStart(ev);
Adam Cohenf8d28232011-02-01 21:47:00 -08001156 }
Michael Jurka1adf5392010-10-18 18:10:22 -07001157 }
1158
Patrick Dubroy1262e362010-10-06 15:49:50 -07001159 protected void onPageBeginMoving() {
Winson Chung007c6982011-06-14 13:27:53 -07001160 super.onPageBeginMoving();
1161
Michael Jurkad74c9842011-07-10 12:44:21 -07001162 if (isHardwareAccelerated()) {
Michael Jurka3a0469d2012-06-21 09:38:41 -07001163 updateChildrenLayersEnabled(false);
Michael Jurka0142d492010-08-25 17:46:15 -07001164 } else {
Michael Jurkad74c9842011-07-10 12:44:21 -07001165 if (mNextPage != INVALID_PAGE) {
1166 // we're snapping to a particular screen
1167 enableChildrenCache(mCurrentPage, mNextPage);
1168 } else {
1169 // this is when user is actively dragging a particular screen, they might
1170 // swipe it either left or right (but we won't advance by more than one screen)
1171 enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1);
1172 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001173 }
1174 }
1175
Patrick Dubroy1262e362010-10-06 15:49:50 -07001176 protected void onPageEndMoving() {
Winson Chung007c6982011-06-14 13:27:53 -07001177 super.onPageEndMoving();
1178
Michael Jurkad74c9842011-07-10 12:44:21 -07001179 if (isHardwareAccelerated()) {
Michael Jurka3a0469d2012-06-21 09:38:41 -07001180 updateChildrenLayersEnabled(false);
Michael Jurkad74c9842011-07-10 12:44:21 -07001181 } else {
1182 clearChildrenCache();
1183 }
1184
Winson Chung3bc21c32012-01-20 13:59:18 -08001185 if (mDragController.isDragging()) {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001186 if (workspaceInModalState()) {
Winson Chung3bc21c32012-01-20 13:59:18 -08001187 // If we are in springloaded mode, then force an event to check if the current touch
1188 // is under a new page (to scroll to)
Winson Chung25460a12013-04-01 18:21:28 -07001189 mDragController.forceTouchMove();
Winson Chung3bc21c32012-01-20 13:59:18 -08001190 }
Patrick Dubroy1262e362010-10-06 15:49:50 -07001191 }
Adam Cohen26976d92011-03-22 15:33:33 -07001192
1193 if (mDelayedResizeRunnable != null) {
1194 mDelayedResizeRunnable.run();
1195 mDelayedResizeRunnable = null;
1196 }
Winson Chungf0c6ae02012-03-21 16:10:31 -07001197
1198 if (mDelayedSnapToPageRunnable != null) {
1199 mDelayedSnapToPageRunnable.run();
1200 mDelayedSnapToPageRunnable = null;
1201 }
Adam Cohenaccfd562013-07-12 14:40:40 -07001202 if (mStripScreensOnPageStopMoving) {
1203 stripEmptyScreens();
1204 mStripScreensOnPageStopMoving = false;
1205 }
Adam Cohenc2d6e892014-10-16 09:49:24 -07001206
1207 if (mShouldSendPageSettled) {
1208 mLauncherOverlay.onScrollSettled();
1209 mShouldSendPageSettled = false;
1210 }
1211 }
1212
1213 protected void onScrollInteractionBegin() {
1214 super.onScrollInteractionEnd();
1215 mScrollInteractionBegan = true;
1216 }
1217
1218 protected void onScrollInteractionEnd() {
1219 super.onScrollInteractionEnd();
1220 mScrollInteractionBegan = false;
1221 if (mStartedSendingScrollEvents) {
1222 mStartedSendingScrollEvents = false;
1223 mLauncherOverlay.onScrollInteractionEnd();
1224 }
1225 }
1226
1227 public void setLauncherOverlay(LauncherOverlay overlay) {
1228 mLauncherOverlay = overlay;
1229 }
1230
1231 @Override
1232 protected void overScroll(float amount) {
Sunny Goyal70660032015-05-14 00:07:08 -07001233 boolean shouldOverScroll = (amount <= 0 && (!hasCustomContent() || mIsRtl)) ||
1234 (amount >= 0 && (!hasCustomContent() || !mIsRtl));
Adam Cohenc2d6e892014-10-16 09:49:24 -07001235
Adam Cohen8c4ca922014-10-24 17:40:34 -07001236 boolean shouldScrollOverlay = mLauncherOverlay != null &&
Sunny Goyal70660032015-05-14 00:07:08 -07001237 ((amount <= 0 && !mIsRtl) || (amount >= 0 && mIsRtl));
Adam Cohen8c4ca922014-10-24 17:40:34 -07001238
1239 boolean shouldZeroOverlay = mLauncherOverlay != null && mLastOverlaySroll != 0 &&
Sunny Goyal70660032015-05-14 00:07:08 -07001240 ((amount >= 0 && !mIsRtl) || (amount <= 0 && mIsRtl));
Adam Cohenc2d6e892014-10-16 09:49:24 -07001241
1242 if (shouldScrollOverlay) {
1243 if (!mStartedSendingScrollEvents && mScrollInteractionBegan) {
1244 mStartedSendingScrollEvents = true;
1245 mLauncherOverlay.onScrollInteractionBegin();
1246 mShouldSendPageSettled = true;
1247 }
1248 int screenSize = getViewportWidth();
1249 float f = (amount / screenSize);
1250
1251 int progress = (int) Math.abs((f * 100));
1252
Adam Cohen8c4ca922014-10-24 17:40:34 -07001253 mLastOverlaySroll = progress;
Sunny Goyal70660032015-05-14 00:07:08 -07001254 mLauncherOverlay.onScrollChange(progress, mIsRtl);
Adam Cohenc2d6e892014-10-16 09:49:24 -07001255 } else if (shouldOverScroll) {
1256 dampedOverScroll(amount);
1257 mOverScrollEffect = acceleratedOverFactor(amount);
1258 } else {
1259 mOverScrollEffect = 0;
1260 }
Adam Cohen8c4ca922014-10-24 17:40:34 -07001261
1262 if (shouldZeroOverlay) {
Sunny Goyal70660032015-05-14 00:07:08 -07001263 mLauncherOverlay.onScrollChange(0, mIsRtl);
Adam Cohen8c4ca922014-10-24 17:40:34 -07001264 }
Adam Cohen89cbbb92013-09-25 16:52:48 -07001265 }
1266
1267 @Override
1268 protected void notifyPageSwitchListener() {
1269 super.notifyPageSwitchListener();
Michael Jurka0142d492010-08-25 17:46:15 -07001270
Adam Cohen6ad0e7d2013-07-24 13:55:41 -07001271 if (hasCustomContent() && getNextPage() == 0 && !mCustomContentShowing) {
Adam Cohenbffe7452013-07-22 18:21:45 -07001272 mCustomContentShowing = true;
1273 if (mCustomContentCallbacks != null) {
Selim Cinek3a8a8f72014-01-16 10:38:38 -08001274 mCustomContentCallbacks.onShow(false);
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001275 mCustomContentShowTime = System.currentTimeMillis();
Adam Cohenbffe7452013-07-22 18:21:45 -07001276 }
Adam Cohen6ad0e7d2013-07-24 13:55:41 -07001277 } else if (hasCustomContent() && getNextPage() != 0 && mCustomContentShowing) {
Adam Cohenbffe7452013-07-22 18:21:45 -07001278 mCustomContentShowing = false;
1279 if (mCustomContentCallbacks != null) {
1280 mCustomContentCallbacks.onHide();
Adam Cohenbffe7452013-07-22 18:21:45 -07001281 }
1282 }
Chris Craik01f2d7f2013-10-01 14:41:56 -07001283 }
Michael Jurka0142d492010-08-25 17:46:15 -07001284
Adam Cohen6fecd412013-10-02 17:41:50 -07001285 protected CustomContentCallbacks getCustomContentCallbacks() {
1286 return mCustomContentCallbacks;
1287 }
1288
Adam Cohen824fcb32014-05-21 23:01:25 +00001289 protected void setWallpaperDimension() {
Michael Jurkaa6a05472013-11-13 17:59:46 +01001290 new AsyncTask<Void, Void, Void>() {
1291 public Void doInBackground(Void ... args) {
Sunny Goyal6a1e95a2015-03-20 17:26:30 -07001292 String spKey = LauncherFiles.WALLPAPER_CROP_PREFERENCES_KEY;
Michael Jurkaa6a05472013-11-13 17:59:46 +01001293 SharedPreferences sp =
1294 mLauncher.getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
Sunny Goyal6a1e95a2015-03-20 17:26:30 -07001295 WallpaperUtils.suggestWallpaperDimension(mLauncher.getResources(),
Adam Cohenea90f832014-05-21 15:03:34 -07001296 sp, mLauncher.getWindowManager(), mWallpaperManager,
1297 mLauncher.overrideWallpaperDimensions());
Michael Jurkaa6a05472013-11-13 17:59:46 +01001298 return null;
1299 }
1300 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
Michael Jurkac5b262c2011-01-12 20:24:50 -08001301 }
1302
Winson Chungf0c6ae02012-03-21 16:10:31 -07001303 protected void snapToPage(int whichPage, Runnable r) {
Adam Cohenad4e15c2013-10-17 16:21:35 -07001304 snapToPage(whichPage, SLOW_PAGE_SNAP_ANIMATION_DURATION, r);
1305 }
1306
1307 protected void snapToPage(int whichPage, int duration, Runnable r) {
Winson Chungf0c6ae02012-03-21 16:10:31 -07001308 if (mDelayedSnapToPageRunnable != null) {
1309 mDelayedSnapToPageRunnable.run();
1310 }
1311 mDelayedSnapToPageRunnable = r;
Adam Cohenad4e15c2013-10-17 16:21:35 -07001312 snapToPage(whichPage, duration);
Winson Chungf0c6ae02012-03-21 16:10:31 -07001313 }
1314
Adam Cohendb364c32014-05-20 14:23:40 -07001315 public void snapToScreenId(long screenId) {
1316 snapToScreenId(screenId, null);
1317 }
1318
Adam Cohendcd297f2013-06-18 13:13:40 -07001319 protected void snapToScreenId(long screenId, Runnable r) {
1320 snapToPage(getPageIndexForScreenId(screenId), r);
1321 }
1322
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001323 class WallpaperOffsetInterpolator implements Choreographer.FrameCallback {
1324 float mFinalOffset = 0.0f;
Michael Jurka046cf342013-09-14 16:40:34 +02001325 float mCurrentOffset = 0.5f; // to force an initial update
Michael Jurkafe09cb72013-08-27 15:48:58 +02001326 boolean mWaitingForUpdate;
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001327 Choreographer mChoreographer;
1328 Interpolator mInterpolator;
1329 boolean mAnimating;
1330 long mAnimationStartTime;
1331 float mAnimationStartOffset;
Michael Jurka2f817312013-09-20 03:03:42 +02001332 private final int ANIMATION_DURATION = 250;
1333 // Don't use all the wallpaper for parallax until you have at least this many pages
Michael Jurkafe0ace32013-10-03 01:05:14 -07001334 private final int MIN_PARALLAX_PAGE_SPAN = 3;
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001335 int mNumScreens;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001336
1337 public WallpaperOffsetInterpolator() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001338 mChoreographer = Choreographer.getInstance();
1339 mInterpolator = new DecelerateInterpolator(1.5f);
Michael Jurkaab1983f2011-01-18 15:50:17 -08001340 }
1341
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001342 @Override
1343 public void doFrame(long frameTimeNanos) {
Michael Jurkafe09cb72013-08-27 15:48:58 +02001344 updateOffset(false);
1345 }
1346
Michael Jurkafe09cb72013-08-27 15:48:58 +02001347 private void updateOffset(boolean force) {
1348 if (mWaitingForUpdate || force) {
1349 mWaitingForUpdate = false;
Michael Jurka1bd90b02013-09-05 23:10:14 +02001350 if (computeScrollOffset() && mWindowToken != null) {
1351 try {
1352 mWallpaperManager.setWallpaperOffsets(mWindowToken,
1353 mWallpaperOffset.getCurrX(), 0.5f);
1354 setWallpaperOffsetSteps();
1355 } catch (IllegalArgumentException e) {
1356 Log.e(TAG, "Error updating wallpaper offset: " + e);
1357 }
Michael Jurkafe09cb72013-08-27 15:48:58 +02001358 }
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001359 }
Michael Jurkaca5b8362011-01-27 13:23:26 -08001360 }
1361
Michael Jurkaab1983f2011-01-18 15:50:17 -08001362 public boolean computeScrollOffset() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001363 final float oldOffset = mCurrentOffset;
1364 if (mAnimating) {
1365 long durationSinceAnimation = System.currentTimeMillis() - mAnimationStartTime;
1366 float t0 = durationSinceAnimation / (float) ANIMATION_DURATION;
1367 float t1 = mInterpolator.getInterpolation(t0);
1368 mCurrentOffset = mAnimationStartOffset +
1369 (mFinalOffset - mAnimationStartOffset) * t1;
1370 mAnimating = durationSinceAnimation < ANIMATION_DURATION;
Michael Jurkaca5b8362011-01-27 13:23:26 -08001371 } else {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001372 mCurrentOffset = mFinalOffset;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001373 }
Michael Jurkaca5b8362011-01-27 13:23:26 -08001374
Michael Jurkafe09cb72013-08-27 15:48:58 +02001375 if (Math.abs(mCurrentOffset - mFinalOffset) > 0.0000001f) {
1376 scheduleUpdate();
1377 }
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001378 if (Math.abs(oldOffset - mCurrentOffset) > 0.0000001f) {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001379 return true;
1380 }
1381 return false;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001382 }
1383
Michael Jurkafe09cb72013-08-27 15:48:58 +02001384 private float wallpaperOffsetForCurrentScroll() {
Sunny Goyal3fa3c122015-04-15 13:15:06 -07001385 // TODO: do different behavior if it's a live wallpaper?
1386 // Don't use up all the wallpaper parallax until you have at least
1387 // MIN_PARALLAX_PAGE_SPAN pages
1388 int numScrollingPages = getNumScreensExcludingEmptyAndCustom();
1389 int parallaxPageSpan;
1390 if (mWallpaperIsLiveWallpaper) {
1391 parallaxPageSpan = numScrollingPages - 1;
1392 } else {
1393 parallaxPageSpan = Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages - 1);
1394 }
1395 mNumPagesForWallpaperParallax = parallaxPageSpan;
1396
Michael Jurkafe09cb72013-08-27 15:48:58 +02001397 if (getChildCount() <= 1) {
Sunny Goyal70660032015-05-14 00:07:08 -07001398 if (mIsRtl) {
Sunny Goyal3fa3c122015-04-15 13:15:06 -07001399 return 1 - 1.0f/mNumPagesForWallpaperParallax;
1400 }
Michael Jurkafe09cb72013-08-27 15:48:58 +02001401 return 0;
1402 }
Michael Jurka2f817312013-09-20 03:03:42 +02001403
1404 // Exclude the leftmost page
Michael Jurkafe0ace32013-10-03 01:05:14 -07001405 int emptyExtraPages = numEmptyScreensToIgnore();
1406 int firstIndex = numCustomPages();
Michael Jurka2f817312013-09-20 03:03:42 +02001407 // Exclude the last extra empty screen (if we have > MIN_PARALLAX_PAGE_SPAN pages)
Michael Jurkafe0ace32013-10-03 01:05:14 -07001408 int lastIndex = getChildCount() - 1 - emptyExtraPages;
Sunny Goyal70660032015-05-14 00:07:08 -07001409 if (mIsRtl) {
Michael Jurkafe0ace32013-10-03 01:05:14 -07001410 int temp = firstIndex;
1411 firstIndex = lastIndex;
1412 lastIndex = temp;
1413 }
Michael Jurka2f817312013-09-20 03:03:42 +02001414
Michael Jurkafe09cb72013-08-27 15:48:58 +02001415 int firstPageScrollX = getScrollForPage(firstIndex);
1416 int scrollRange = getScrollForPage(lastIndex) - firstPageScrollX;
1417 if (scrollRange == 0) {
1418 return 0;
1419 } else {
Michael Jurkafe0ace32013-10-03 01:05:14 -07001420 // Sometimes the left parameter of the pages is animated during a layout transition;
1421 // this parameter offsets it to keep the wallpaper from animating as well
Michael Jurkafe0ace32013-10-03 01:05:14 -07001422 int adjustedScroll =
Michael Jurka8fd3adc2013-10-16 13:50:24 -07001423 getScrollX() - firstPageScrollX - getLayoutTransitionOffsetForPage(0);
Michael Jurkafe0ace32013-10-03 01:05:14 -07001424 float offset = Math.min(1, adjustedScroll / (float) scrollRange);
Michael Jurka2f817312013-09-20 03:03:42 +02001425 offset = Math.max(0, offset);
Michael Jurka2a4f4922014-01-29 16:32:39 +01001426
Michael Jurkafe0ace32013-10-03 01:05:14 -07001427 // On RTL devices, push the wallpaper offset to the right if we don't have enough
1428 // pages (ie if numScrollingPages < MIN_PARALLAX_PAGE_SPAN)
Sunny Goyal3fa3c122015-04-15 13:15:06 -07001429 if (!mWallpaperIsLiveWallpaper && numScrollingPages < MIN_PARALLAX_PAGE_SPAN
Sunny Goyal70660032015-05-14 00:07:08 -07001430 && mIsRtl) {
Sunny Goyal3fa3c122015-04-15 13:15:06 -07001431 return offset * (parallaxPageSpan - numScrollingPages + 1) / parallaxPageSpan;
1432 }
1433 return offset * (numScrollingPages - 1) / parallaxPageSpan;
Michael Jurkafe09cb72013-08-27 15:48:58 +02001434 }
1435 }
1436
Michael Jurkafe0ace32013-10-03 01:05:14 -07001437 private int numEmptyScreensToIgnore() {
1438 int numScrollingPages = getChildCount() - numCustomPages();
1439 if (numScrollingPages >= MIN_PARALLAX_PAGE_SPAN && hasExtraEmptyScreen()) {
Michael Jurka2f817312013-09-20 03:03:42 +02001440 return 1;
1441 } else {
1442 return 0;
1443 }
1444 }
1445
Michael Jurkafe0ace32013-10-03 01:05:14 -07001446 private int getNumScreensExcludingEmptyAndCustom() {
1447 int numScrollingPages = getChildCount() - numEmptyScreensToIgnore() - numCustomPages();
Michael Jurka2f817312013-09-20 03:03:42 +02001448 return numScrollingPages;
1449 }
1450
Michael Jurkafe09cb72013-08-27 15:48:58 +02001451 public void syncWithScroll() {
1452 float offset = wallpaperOffsetForCurrentScroll();
1453 mWallpaperOffset.setFinalX(offset);
1454 updateOffset(true);
1455 }
1456
Michael Jurkaab1983f2011-01-18 15:50:17 -08001457 public float getCurrX() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001458 return mCurrentOffset;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001459 }
1460
1461 public float getFinalX() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001462 return mFinalOffset;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001463 }
1464
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001465 private void animateToFinal() {
1466 mAnimating = true;
1467 mAnimationStartOffset = mCurrentOffset;
1468 mAnimationStartTime = System.currentTimeMillis();
Michael Jurkaab1983f2011-01-18 15:50:17 -08001469 }
1470
Michael Jurkafe09cb72013-08-27 15:48:58 +02001471 private void setWallpaperOffsetSteps() {
1472 // Set wallpaper offset steps (1 / (number of screens - 1))
Michael Jurka2a4f4922014-01-29 16:32:39 +01001473 float xOffset = 1.0f / mNumPagesForWallpaperParallax;
1474 if (xOffset != mLastSetWallpaperOffsetSteps) {
1475 mWallpaperManager.setWallpaperOffsetSteps(xOffset, 1.0f);
1476 mLastSetWallpaperOffsetSteps = xOffset;
1477 }
Michael Jurkafe09cb72013-08-27 15:48:58 +02001478 }
1479
Michael Jurkaab1983f2011-01-18 15:50:17 -08001480 public void setFinalX(float x) {
Michael Jurkafe09cb72013-08-27 15:48:58 +02001481 scheduleUpdate();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001482 mFinalOffset = Math.max(0f, Math.min(x, 1.0f));
Michael Jurkafe0ace32013-10-03 01:05:14 -07001483 if (getNumScreensExcludingEmptyAndCustom() != mNumScreens) {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001484 if (mNumScreens > 0) {
1485 // Don't animate if we're going from 0 screens
1486 animateToFinal();
1487 }
Michael Jurkafe0ace32013-10-03 01:05:14 -07001488 mNumScreens = getNumScreensExcludingEmptyAndCustom();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001489 }
Michael Jurkaab1983f2011-01-18 15:50:17 -08001490 }
1491
Michael Jurkafe09cb72013-08-27 15:48:58 +02001492 private void scheduleUpdate() {
1493 if (!mWaitingForUpdate) {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001494 mChoreographer.postFrameCallback(this);
Michael Jurkafe09cb72013-08-27 15:48:58 +02001495 mWaitingForUpdate = true;
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001496 }
Michael Jurkaab1983f2011-01-18 15:50:17 -08001497 }
1498
1499 public void jumpToFinal() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001500 mCurrentOffset = mFinalOffset;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001501 }
Dianne Hackborn8f573952009-08-10 23:21:09 -07001502 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001503
Michael Jurka340c5f32010-10-21 16:49:19 -07001504 @Override
1505 public void computeScroll() {
1506 super.computeScroll();
Michael Jurkafe09cb72013-08-27 15:48:58 +02001507 mWallpaperOffset.syncWithScroll();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001508 }
1509
Jason Monked05f092014-04-24 10:13:05 -04001510 @Override
1511 public void announceForAccessibility(CharSequence text) {
1512 // Don't announce if apps is on top of us.
Winson Chungb745afb2015-03-02 11:51:23 -08001513 if (!mLauncher.isAppsViewVisible()) {
Jason Monked05f092014-04-24 10:13:05 -04001514 super.announceForAccessibility(text);
1515 }
1516 }
1517
Patrick Dubroy94f78a52011-02-28 17:39:16 -08001518 public void showOutlinesTemporarily() {
1519 if (!mIsPageMoving && !isTouchActive()) {
1520 snapToPage(mCurrentPage);
1521 }
1522 }
1523
Adam Cohen68d73932010-11-15 10:50:58 -08001524 float backgroundAlphaInterpolator(float r) {
1525 float pivotA = 0.1f;
1526 float pivotB = 0.4f;
1527 if (r < pivotA) {
1528 return 0;
1529 } else if (r > pivotB) {
1530 return 1.0f;
1531 } else {
1532 return (r - pivotA)/(pivotB - pivotA);
1533 }
1534 }
1535
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001536 private void updatePageAlphaValues(int screenCenter) {
Michael Jurka869390b2012-05-06 15:55:19 -07001537 boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
1538 if (mWorkspaceFadeInAdjacentScreens &&
Adam Cohenc50438c2014-08-19 17:43:05 -07001539 !workspaceInModalState() &&
Michael Jurka869390b2012-05-06 15:55:19 -07001540 !mIsSwitchingState &&
1541 !isInOverscroll) {
Adam Cohen84a465a2013-11-11 18:49:56 +00001542 for (int i = numCustomPages(); i < getChildCount(); i++) {
Michael Jurka869390b2012-05-06 15:55:19 -07001543 CellLayout child = (CellLayout) getChildAt(i);
1544 if (child != null) {
1545 float scrollProgress = getScrollProgress(screenCenter, child, i);
Adam Cohen73894962011-10-31 13:17:17 -07001546 float alpha = 1 - Math.abs(scrollProgress);
Michael Jurka869390b2012-05-06 15:55:19 -07001547 child.getShortcutsAndWidgets().setAlpha(alpha);
Adam Cohen73894962011-10-31 13:17:17 -07001548 }
Adam Cohenf34bab52010-09-30 14:11:56 -07001549 }
1550 }
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001551 }
1552
Adam Cohenc9735cf2015-01-23 16:11:55 -08001553 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
Sunny Goyale9b651e2015-04-24 11:44:51 -07001554 @Override
Adam Cohenc9735cf2015-01-23 16:11:55 -08001555 public void enableAccessibleDrag(boolean enable) {
1556 for (int i = 0; i < getChildCount(); i++) {
1557 CellLayout child = (CellLayout) getChildAt(i);
Sunny Goyale9b651e2015-04-24 11:44:51 -07001558 child.enableAccessibleDrag(enable, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
Adam Cohenc9735cf2015-01-23 16:11:55 -08001559 }
1560
1561 if (enable) {
1562 // We need to allow our individual children to become click handlers in this case
1563 setOnClickListener(null);
1564 } else {
1565 // Reset our click listener
1566 setOnClickListener(mLauncher);
1567 }
Sunny Goyal1a70cef2015-04-22 11:29:51 -07001568 mLauncher.getSearchBar().enableAccessibleDrag(enable);
Sunny Goyale9b651e2015-04-24 11:44:51 -07001569 mLauncher.getHotseat().getLayout()
1570 .enableAccessibleDrag(enable, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
Adam Cohenc9735cf2015-01-23 16:11:55 -08001571 }
1572
Winson Chung98ca0f72013-07-29 12:58:51 -07001573 public boolean hasCustomContent() {
Adam Cohenedb40762013-07-18 16:45:45 -07001574 return (mScreenOrder.size() > 0 && mScreenOrder.get(0) == CUSTOM_CONTENT_SCREEN_ID);
1575 }
1576
Michael Jurkafe0ace32013-10-03 01:05:14 -07001577 public int numCustomPages() {
1578 return hasCustomContent() ? 1 : 0;
1579 }
1580
Adam Cohenbffe7452013-07-22 18:21:45 -07001581 public boolean isOnOrMovingToCustomContent() {
1582 return hasCustomContent() && getNextPage() == 0;
1583 }
1584
Adam Cohenedb40762013-07-18 16:45:45 -07001585 private void updateStateForCustomContent(int screenCenter) {
Dave Hawkeya8881582013-09-17 15:55:33 -06001586 float translationX = 0;
1587 float progress = 0;
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01001588 if (hasCustomContent()) {
Adam Cohenedb40762013-07-18 16:45:45 -07001589 int index = mScreenOrder.indexOf(CUSTOM_CONTENT_SCREEN_ID);
Adam Cohen564a2e72013-10-09 14:47:32 -07001590
Adam Cohena45de072013-10-11 16:07:47 -07001591 int scrollDelta = getScrollX() - getScrollForPage(index) -
1592 getLayoutTransitionOffsetForPage(index);
1593 float scrollRange = getScrollForPage(index + 1) - getScrollForPage(index);
1594 translationX = scrollRange - scrollDelta;
1595 progress = (scrollRange - scrollDelta) / scrollRange;
Adam Cohenaf9b0e52013-09-23 19:27:38 -07001596
Sunny Goyal70660032015-05-14 00:07:08 -07001597 if (mIsRtl) {
Adam Cohenaf9b0e52013-09-23 19:27:38 -07001598 translationX = Math.min(0, translationX);
1599 } else {
1600 translationX = Math.max(0, translationX);
1601 }
Adam Cohen955806d2013-07-19 16:36:43 -07001602 progress = Math.max(0, progress);
Dave Hawkeya8881582013-09-17 15:55:33 -06001603 }
Adam Cohenedb40762013-07-18 16:45:45 -07001604
Dave Hawkeya8881582013-09-17 15:55:33 -06001605 if (Float.compare(progress, mLastCustomContentScrollProgress) == 0) return;
Adam Cohen84add1d2013-10-14 16:29:02 -07001606
1607 CellLayout cc = mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID);
Adam Cohen6c5891a2014-07-09 23:53:15 -07001608 if (progress > 0 && cc.getVisibility() != VISIBLE && !workspaceInModalState()) {
Adam Cohen84add1d2013-10-14 16:29:02 -07001609 cc.setVisibility(VISIBLE);
1610 }
1611
Dave Hawkeya8881582013-09-17 15:55:33 -06001612 mLastCustomContentScrollProgress = progress;
Adam Cohenc36fa5c2013-08-29 11:54:42 -07001613
Winson Chung0f785722015-04-08 10:27:49 -07001614 // We should only update the drag layer background alpha if we are not in all apps or the
1615 // widgets tray
1616 if (mState == State.NORMAL) {
1617 mLauncher.getDragLayer().setBackgroundAlpha(progress * 0.8f);
1618 }
Adam Cohen22cba7f2013-07-19 16:14:00 -07001619
Dave Hawkeya8881582013-09-17 15:55:33 -06001620 if (mLauncher.getHotseat() != null) {
1621 mLauncher.getHotseat().setTranslationX(translationX);
1622 }
Adam Cohenc36fa5c2013-08-29 11:54:42 -07001623
Dave Hawkeya8881582013-09-17 15:55:33 -06001624 if (getPageIndicator() != null) {
1625 getPageIndicator().setTranslationX(translationX);
1626 }
Adam Cohenc36fa5c2013-08-29 11:54:42 -07001627
Dave Hawkeya8881582013-09-17 15:55:33 -06001628 if (mCustomContentCallbacks != null) {
1629 mCustomContentCallbacks.onScrollProgressChanged(progress);
Adam Cohenedb40762013-07-18 16:45:45 -07001630 }
1631 }
1632
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001633 @Override
Adam Cohen53805212013-10-01 10:39:23 -07001634 protected OnClickListener getPageIndicatorClickListener() {
1635 AccessibilityManager am = (AccessibilityManager)
1636 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
1637 if (!am.isTouchExplorationEnabled()) {
1638 return null;
1639 }
1640 OnClickListener listener = new OnClickListener() {
1641 @Override
1642 public void onClick(View arg0) {
Winson Chungdc61c4d2015-04-20 18:26:57 -07001643 mLauncher.showOverviewMode(true);
Adam Cohen53805212013-10-01 10:39:23 -07001644 }
1645 };
1646 return listener;
1647 }
1648
1649 @Override
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001650 protected void screenScrolled(int screenCenter) {
1651 super.screenScrolled(screenCenter);
1652
1653 updatePageAlphaValues(screenCenter);
Adam Cohenedb40762013-07-18 16:45:45 -07001654 updateStateForCustomContent(screenCenter);
Michael Jurkad51f33a2012-06-28 15:35:26 -07001655 enableHwLayersOnVisiblePages();
Adam Cohenf34bab52010-09-30 14:11:56 -07001656
Adam Cohenc50438c2014-08-19 17:43:05 -07001657 boolean shouldOverScroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
Adam Cohenaf9b0e52013-09-23 19:27:38 -07001658
1659 if (shouldOverScroll) {
Winson Chung52aee602013-01-30 12:01:02 -08001660 int index = 0;
Winson Chung52aee602013-01-30 12:01:02 -08001661 final int lowerIndex = 0;
1662 final int upperIndex = getChildCount() - 1;
Adam Cohena29f5042013-09-26 14:29:35 -07001663
1664 final boolean isLeftPage = mOverScrollX < 0;
Sunny Goyal70660032015-05-14 00:07:08 -07001665 index = (!mIsRtl && isLeftPage) || (mIsRtl && !isLeftPage) ? lowerIndex : upperIndex;
Winson Chung52aee602013-01-30 12:01:02 -08001666
Adam Cohenb5ba0972011-09-07 18:02:31 -07001667 CellLayout cl = (CellLayout) getChildAt(index);
Adam Cohen1e4359c2014-08-18 13:12:16 -07001668 float effect = Math.abs(mOverScrollEffect);
1669 cl.setOverScrollAmount(Math.abs(effect), isLeftPage);
Adam Cohena29f5042013-09-26 14:29:35 -07001670
Adam Cohen1e4359c2014-08-18 13:12:16 -07001671 mOverscrollEffectSet = true;
Adam Cohenb5ba0972011-09-07 18:02:31 -07001672 } else {
Adam Cohen1e4359c2014-08-18 13:12:16 -07001673 if (mOverscrollEffectSet && getChildCount() > 0) {
1674 mOverscrollEffectSet = false;
1675 ((CellLayout) getChildAt(0)).setOverScrollAmount(0, false);
1676 ((CellLayout) getChildAt(getChildCount() - 1)).setOverScrollAmount(0, false);
Adam Cohen7842d7f2011-09-12 15:28:15 -07001677 }
Adam Cohenb5ba0972011-09-07 18:02:31 -07001678 }
1679 }
1680
Joe Onorato00acb122009-08-04 16:04:30 -04001681 protected void onAttachedToWindow() {
1682 super.onAttachedToWindow();
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001683 mWindowToken = getWindowToken();
Joe Onorato956091b2010-02-19 12:47:40 -08001684 computeScroll();
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001685 mDragController.setWindowToken(mWindowToken);
1686 }
1687
1688 protected void onDetachedFromWindow() {
Romain Guye6661952013-08-08 19:13:22 -07001689 super.onDetachedFromWindow();
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001690 mWindowToken = null;
Joe Onorato00acb122009-08-04 16:04:30 -04001691 }
1692
Adam Cohen53805212013-10-01 10:39:23 -07001693 protected void onResume() {
1694 if (getPageIndicator() != null) {
1695 // In case accessibility state has changed, we need to perform this on every
1696 // attach to window
Adam Cohend36d9472013-10-10 15:32:41 -07001697 OnClickListener listener = getPageIndicatorClickListener();
1698 if (listener != null) {
1699 getPageIndicator().setOnClickListener(listener);
1700 }
Adam Cohen53805212013-10-01 10:39:23 -07001701 }
Michael Jurkaa6a05472013-11-13 17:59:46 +01001702
1703 // Update wallpaper dimensions if they were changed since last onResume
1704 // (we also always set the wallpaper dimensions in the constructor)
1705 if (LauncherAppState.getInstance().hasWallpaperChangedSinceLastCheck()) {
Adam Cohen824fcb32014-05-21 23:01:25 +00001706 setWallpaperDimension();
Michael Jurkaa6a05472013-11-13 17:59:46 +01001707 }
Michael Jurka2a4f4922014-01-29 16:32:39 +01001708 mWallpaperIsLiveWallpaper = mWallpaperManager.getWallpaperInfo() != null;
1709 // Force the wallpaper offset steps to be set again, because another app might have changed
1710 // them
1711 mLastSetWallpaperOffsetSteps = 0f;
Adam Cohen53805212013-10-01 10:39:23 -07001712 }
1713
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001714 @Override
Michael Jurka28750fb2010-09-24 17:43:49 -07001715 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Michael Jurkac5b262c2011-01-12 20:24:50 -08001716 if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
Michael Jurkafe09cb72013-08-27 15:48:58 +02001717 mWallpaperOffset.syncWithScroll();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001718 mWallpaperOffset.jumpToFinal();
Michael Jurkac5b262c2011-01-12 20:24:50 -08001719 }
Michael Jurka0142d492010-08-25 17:46:15 -07001720 super.onLayout(changed, left, top, right, bottom);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001721 }
1722
1723 @Override
Winson Chung9171e6d2010-11-17 17:39:27 -08001724 protected void onDraw(Canvas canvas) {
Winson Chung9171e6d2010-11-17 17:39:27 -08001725 super.onDraw(canvas);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001726
1727 // Call back to LauncherModel to finish binding after the first draw
Romain Guyeeacd562012-10-10 18:47:33 -07001728 post(mBindPages);
Winson Chung9171e6d2010-11-17 17:39:27 -08001729 }
1730
Michael Jurkadfab7f02011-11-18 13:01:04 -08001731 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001732 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
Winson Chungb745afb2015-03-02 11:51:23 -08001733 if (!mLauncher.isAppsViewVisible()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001734 final Folder openFolder = getOpenFolder();
1735 if (openFolder != null) {
1736 return openFolder.requestFocus(direction, previouslyFocusedRect);
1737 } else {
Michael Jurka0142d492010-08-25 17:46:15 -07001738 return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001739 }
1740 }
1741 return false;
1742 }
1743
1744 @Override
Winson Chung97d85d22011-04-13 11:27:36 -07001745 public int getDescendantFocusability() {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001746 if (workspaceInModalState()) {
Winson Chung97d85d22011-04-13 11:27:36 -07001747 return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
1748 }
1749 return super.getDescendantFocusability();
1750 }
1751
1752 @Override
Romain Guyc2e24c02009-06-01 16:11:41 -07001753 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
Winson Chungb745afb2015-03-02 11:51:23 -08001754 if (!mLauncher.isAppsViewVisible()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001755 final Folder openFolder = getOpenFolder();
Michael Jurka0142d492010-08-25 17:46:15 -07001756 if (openFolder != null) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001757 openFolder.addFocusables(views, direction);
Michael Jurka0142d492010-08-25 17:46:15 -07001758 } else {
1759 super.addFocusables(views, direction, focusableMode);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001760 }
1761 }
1762 }
1763
Adam Cohen6c5891a2014-07-09 23:53:15 -07001764 public boolean workspaceInModalState() {
1765 return mState != State.NORMAL;
Michael Jurkad74c9842011-07-10 12:44:21 -07001766 }
1767
Michael Jurka0142d492010-08-25 17:46:15 -07001768 void enableChildrenCache(int fromPage, int toPage) {
1769 if (fromPage > toPage) {
1770 final int temp = fromPage;
1771 fromPage = toPage;
1772 toPage = temp;
Mike Cleron3a2b3f22009-11-05 17:17:42 -08001773 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001774
Michael Jurkadee05892010-07-27 10:01:56 -07001775 final int screenCount = getChildCount();
Adam Powellfea5d022010-04-29 11:42:45 -07001776
Michael Jurka0142d492010-08-25 17:46:15 -07001777 fromPage = Math.max(fromPage, 0);
1778 toPage = Math.min(toPage, screenCount - 1);
Adam Powellfea5d022010-04-29 11:42:45 -07001779
Michael Jurka0142d492010-08-25 17:46:15 -07001780 for (int i = fromPage; i <= toPage; i++) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001781 final CellLayout layout = (CellLayout) getChildAt(i);
1782 layout.setChildrenDrawnWithCacheEnabled(true);
1783 layout.setChildrenDrawingCacheEnabled(true);
1784 }
1785 }
1786
1787 void clearChildrenCache() {
Michael Jurkadee05892010-07-27 10:01:56 -07001788 final int screenCount = getChildCount();
1789 for (int i = 0; i < screenCount; i++) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001790 final CellLayout layout = (CellLayout) getChildAt(i);
1791 layout.setChildrenDrawnWithCacheEnabled(false);
Adam Cohen8182e5b2011-08-01 15:43:31 -07001792 // In software mode, we don't want the items to continue to be drawn into bitmaps
1793 if (!isHardwareAccelerated()) {
1794 layout.setChildrenDrawingCacheEnabled(false);
1795 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001796 }
1797 }
1798
Adam Cohen091440a2015-03-18 14:16:05 -07001799 @Thunk void updateChildrenLayersEnabled(boolean force) {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001800 boolean small = mState == State.OVERVIEW || mIsSwitchingState;
Michael Jurka3a0469d2012-06-21 09:38:41 -07001801 boolean enableChildrenLayers = force || small || mAnimatingViewIntoPlace || isPageMoving();
Michael Jurkad74c9842011-07-10 12:44:21 -07001802
1803 if (enableChildrenLayers != mChildrenLayersEnabled) {
1804 mChildrenLayersEnabled = enableChildrenLayers;
Michael Jurkad51f33a2012-06-28 15:35:26 -07001805 if (mChildrenLayersEnabled) {
1806 enableHwLayersOnVisiblePages();
1807 } else {
1808 for (int i = 0; i < getPageCount(); i++) {
1809 final CellLayout cl = (CellLayout) getChildAt(i);
Chris Craik01f2d7f2013-10-01 14:41:56 -07001810 cl.enableHardwareLayer(false);
Michael Jurkad51f33a2012-06-28 15:35:26 -07001811 }
1812 }
1813 }
1814 }
1815
1816 private void enableHwLayersOnVisiblePages() {
1817 if (mChildrenLayersEnabled) {
1818 final int screenCount = getChildCount();
1819 getVisiblePages(mTempVisiblePagesRange);
1820 int leftScreen = mTempVisiblePagesRange[0];
1821 int rightScreen = mTempVisiblePagesRange[1];
1822 if (leftScreen == rightScreen) {
1823 // make sure we're caching at least two pages always
1824 if (rightScreen < screenCount - 1) {
1825 rightScreen++;
1826 } else if (leftScreen > 0) {
1827 leftScreen--;
1828 }
1829 }
Chris Craik01f2d7f2013-10-01 14:41:56 -07001830
1831 final CellLayout customScreen = mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID);
Michael Jurkad51f33a2012-06-28 15:35:26 -07001832 for (int i = 0; i < screenCount; i++) {
Michael Jurka47639b92013-01-14 12:42:27 +01001833 final CellLayout layout = (CellLayout) getPageAt(i);
Chris Craik01f2d7f2013-10-01 14:41:56 -07001834
1835 // enable layers between left and right screen inclusive, except for the
1836 // customScreen, which may animate its content during transitions.
1837 boolean enableLayer = layout != customScreen &&
1838 leftScreen <= i && i <= rightScreen && shouldDrawChild(layout);
1839 layout.enableHardwareLayer(enableLayer);
Michael Jurkad74c9842011-07-10 12:44:21 -07001840 }
Michael Jurkace7e05f2011-02-01 22:02:35 -08001841 }
Michael Jurkace7e05f2011-02-01 22:02:35 -08001842 }
1843
Michael Jurka3a0469d2012-06-21 09:38:41 -07001844 public void buildPageHardwareLayers() {
1845 // force layers to be enabled just for the call to buildLayer
1846 updateChildrenLayersEnabled(true);
1847 if (getWindowToken() != null) {
1848 final int childCount = getChildCount();
1849 for (int i = 0; i < childCount; i++) {
1850 CellLayout cl = (CellLayout) getChildAt(i);
Michael Jurkad51f33a2012-06-28 15:35:26 -07001851 cl.buildHardwareLayer();
Michael Jurka3a0469d2012-06-21 09:38:41 -07001852 }
1853 }
1854 updateChildrenLayersEnabled(false);
1855 }
1856
Jeff Brown1d0867c2010-12-02 18:27:39 -08001857 protected void onWallpaperTap(MotionEvent ev) {
1858 final int[] position = mTempCell;
1859 getLocationOnScreen(position);
1860
1861 int pointerIndex = ev.getActionIndex();
1862 position[0] += (int) ev.getX(pointerIndex);
1863 position[1] += (int) ev.getY(pointerIndex);
1864
1865 mWallpaperManager.sendWallpaperCommand(getWindowToken(),
1866 ev.getAction() == MotionEvent.ACTION_UP
1867 ? WallpaperManager.COMMAND_TAP : WallpaperManager.COMMAND_SECONDARY_TAP,
1868 position[0], position[1], 0, null);
1869 }
1870
Adam Cohen61033d32010-11-15 18:29:44 -08001871 /*
Adam Cohen66396872011-04-15 17:50:36 -07001872 *
1873 * We call these methods (onDragStartedWithItemSpans/onDragStartedWithSize) whenever we
1874 * start a drag in Launcher, regardless of whether the drag has ever entered the Workspace
1875 *
1876 * These methods mark the appropriate pages as accepting drops (which alters their visual
1877 * appearance).
1878 *
1879 */
Sunny Goyal508da152014-08-14 10:53:27 -07001880 private static Rect getDrawableBounds(Drawable d) {
Mathew Inwood85900292014-04-16 14:17:39 +01001881 Rect bounds = new Rect();
1882 d.copyBounds(bounds);
1883 if (bounds.width() == 0 || bounds.height() == 0) {
1884 bounds.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
Sunny Goyal95abbb32014-08-04 10:53:22 -07001885 } else {
1886 bounds.offsetTo(0, 0);
1887 }
1888 if (d instanceof PreloadIconDrawable) {
1889 int inset = -((PreloadIconDrawable) d).getOutset();
1890 bounds.inset(inset, inset);
Mathew Inwood85900292014-04-16 14:17:39 +01001891 }
1892 return bounds;
1893 }
1894
Winson Chungbabb53e2014-04-14 17:12:49 -07001895 public void onExternalDragStartedWithItem(View v) {
Winson Chungbabb53e2014-04-14 17:12:49 -07001896 // Compose a drag bitmap with the view scaled to the icon size
Adam Cohen2e6da152015-05-06 11:42:25 -07001897 DeviceProfile grid = mLauncher.getDeviceProfile();
Winson Chungbabb53e2014-04-14 17:12:49 -07001898 int iconSize = grid.iconSizePx;
1899 int bmpWidth = v.getMeasuredWidth();
1900 int bmpHeight = v.getMeasuredHeight();
1901
1902 // If this is a text view, use its drawable instead
1903 if (v instanceof TextView) {
Winson Chungb745afb2015-03-02 11:51:23 -08001904 Drawable d = getTextViewIcon((TextView) v);
Mathew Inwood85900292014-04-16 14:17:39 +01001905 Rect bounds = getDrawableBounds(d);
1906 bmpWidth = bounds.width();
1907 bmpHeight = bounds.height();
Winson Chungbabb53e2014-04-14 17:12:49 -07001908 }
1909
1910 // Compose the bitmap to create the icon from
1911 Bitmap b = Bitmap.createBitmap(bmpWidth, bmpHeight,
1912 Bitmap.Config.ARGB_8888);
Sunny Goyal508da152014-08-14 10:53:27 -07001913 mCanvas.setBitmap(b);
1914 drawDragView(v, mCanvas, 0);
1915 mCanvas.setBitmap(null);
Winson Chungbabb53e2014-04-14 17:12:49 -07001916
1917 // The outline is used to visualize where the item will land if dropped
Sunny Goyal508da152014-08-14 10:53:27 -07001918 mDragOutline = createDragOutline(b, DRAG_BITMAP_PADDING, iconSize, iconSize, true);
Winson Chungbabb53e2014-04-14 17:12:49 -07001919 }
1920
Michael Jurka8c3339b2012-06-14 16:18:21 -07001921 public void onDragStartedWithItem(PendingAddItemInfo info, Bitmap b, boolean clipAlpha) {
Sunny Goyal5b0e6692015-03-19 14:31:19 -07001922 int[] size = estimateItemSize(info, false);
Adam Cohen66396872011-04-15 17:50:36 -07001923
Michael Jurkad3ef3062010-11-23 16:23:58 -08001924 // The outline is used to visualize where the item will land if dropped
Sunny Goyal508da152014-08-14 10:53:27 -07001925 mDragOutline = createDragOutline(b, DRAG_BITMAP_PADDING, size[0], size[1], clipAlpha);
Michael Jurka3e7c7632010-10-02 16:01:03 -07001926 }
1927
Patrick Dubroy758a9232011-03-03 19:54:56 -08001928 public void exitWidgetResizeMode() {
Adam Cohen716b51e2011-06-30 12:09:54 -07001929 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohen67882692011-03-11 15:29:03 -08001930 dragLayer.clearAllResizeFrames();
Patrick Dubroy758a9232011-03-03 19:54:56 -08001931 }
1932
Adam Cohenf358a4b2013-07-23 16:47:31 -07001933 @Override
Adam Cohen6f127a62014-06-12 14:54:41 -07001934 protected void getFreeScrollPageRange(int[] range) {
1935 getOverviewModePages(range);
1936 }
1937
1938 private void getOverviewModePages(int[] range) {
Michael Jurkafe0ace32013-10-03 01:05:14 -07001939 int start = numCustomPages();
Adam Cohen1003be92013-09-16 14:09:28 -07001940 int end = getChildCount() - 1;
1941
1942 range[0] = Math.max(0, Math.min(start, getChildCount() - 1));
1943 range[1] = Math.max(0, end);
Adam Cohen6f127a62014-06-12 14:54:41 -07001944 }
Adam Cohendedbd962013-07-11 14:21:49 -07001945
Sunny Goyal1d08f702015-05-04 15:50:25 -07001946 public void onStartReordering() {
Adam Cohendedbd962013-07-11 14:21:49 -07001947 super.onStartReordering();
Adam Cohen22cba7f2013-07-19 16:14:00 -07001948 // Reordering handles its own animations, disable the automatic ones.
Winson Chung964df6b2013-10-11 15:55:37 -07001949 disableLayoutTransitions();
Adam Cohendedbd962013-07-11 14:21:49 -07001950 }
1951
Sunny Goyal1d08f702015-05-04 15:50:25 -07001952 public void onEndReordering() {
Adam Cohendedbd962013-07-11 14:21:49 -07001953 super.onEndReordering();
Adam Cohendedbd962013-07-11 14:21:49 -07001954
Adam Cohendcb173d2014-04-01 13:33:58 -07001955 if (mLauncher.isWorkspaceLoading()) {
1956 // Invalid and dangerous operation if workspace is loading
1957 return;
1958 }
1959
Adam Cohendedbd962013-07-11 14:21:49 -07001960 mScreenOrder.clear();
Adam Cohen2bf63d52013-09-29 17:46:49 -07001961 int count = getChildCount();
Adam Cohendedbd962013-07-11 14:21:49 -07001962 for (int i = 0; i < count; i++) {
1963 CellLayout cl = ((CellLayout) getChildAt(i));
1964 mScreenOrder.add(getIdForScreen(cl));
1965 }
Winson Chungd64d1762013-08-20 14:37:16 -07001966
Adam Cohendedbd962013-07-11 14:21:49 -07001967 mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
Adam Cohen22cba7f2013-07-19 16:14:00 -07001968
1969 // Re-enable auto layout transitions for page deletion.
Winson Chung964df6b2013-10-11 15:55:37 -07001970 enableLayoutTransitions();
Adam Cohendedbd962013-07-11 14:21:49 -07001971 }
1972
Adam Cohenf358a4b2013-07-23 16:47:31 -07001973 public boolean isInOverviewMode() {
1974 return mState == State.OVERVIEW;
1975 }
1976
Adam Cohen410f3cd2013-09-22 12:09:32 -07001977 int getOverviewModeTranslationY() {
Adam Cohen2e6da152015-05-06 11:42:25 -07001978 DeviceProfile grid = mLauncher.getDeviceProfile();
Winson Chungc82d2622013-11-06 13:23:29 -08001979 Rect overviewBar = grid.getOverviewModeButtonBarRect();
Adam Cohen410f3cd2013-09-22 12:09:32 -07001980
Winson Chungc82d2622013-11-06 13:23:29 -08001981 int availableHeight = getViewportHeight();
1982 int scaledHeight = (int) (mOverviewModeShrinkFactor * getNormalChildHeight());
1983 int offsetFromTopEdge = (availableHeight - scaledHeight) / 2;
1984 int offsetToCenterInOverview = (availableHeight - mInsets.top - overviewBar.height()
1985 - scaledHeight) / 2;
Adam Cohen410f3cd2013-09-22 12:09:32 -07001986
Winson Chungc82d2622013-11-06 13:23:29 -08001987 return -offsetFromTopEdge + mInsets.top + offsetToCenterInOverview;
Adam Cohen410f3cd2013-09-22 12:09:32 -07001988 }
1989
Winson Chungdc61c4d2015-04-20 18:26:57 -07001990 /**
1991 * Sets the current workspace {@link State}, returning an animation transitioning the workspace
1992 * to that new state.
1993 */
1994 public Animator setStateWithAnimation(State toState, int toPage, boolean animated,
1995 HashMap<View, Integer> layerViews) {
1996 // Create the animation to the new state
1997 Animator workspaceAnim = mStateTransitionAnimation.getAnimationToState(getState(),
1998 toState, toPage, animated, layerViews);
1999
2000 // Update the current state
2001 mState = toState;
Adam Cohen53805212013-10-01 10:39:23 -07002002 updateAccessibilityFlags();
Winson Chungdc61c4d2015-04-20 18:26:57 -07002003
2004 return workspaceAnim;
Adam Cohen53805212013-10-01 10:39:23 -07002005 }
2006
Adam Cohene25c5d12014-06-18 10:34:58 -07002007 State getState() {
2008 return mState;
2009 }
2010
Sunny Goyal1d08f702015-05-04 15:50:25 -07002011 public void updateAccessibilityFlags() {
2012 if (Utilities.isLmpOrAbove()) {
2013 int total = getPageCount();
2014 for (int i = numCustomPages(); i < total; i++) {
2015 updateAccessibilityFlags((CellLayout) getPageAt(i), i);
2016 }
2017 if (mState == State.NORMAL) {
2018 setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
2019 } else {
2020 setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
2021 }
2022 } else {
2023 int accessible = mState == State.NORMAL ?
2024 IMPORTANT_FOR_ACCESSIBILITY_NO :
2025 IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
2026 setImportantForAccessibility(accessible);
2027 }
2028 }
2029
2030 private void updateAccessibilityFlags(CellLayout page, int pageNo) {
2031 if (mState == State.OVERVIEW) {
2032 page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
2033 page.getShortcutsAndWidgets().setImportantForAccessibility(
2034 IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
2035 page.setContentDescription(getPageDescription(pageNo));
2036
2037 if (mPagesAccessibilityDelegate == null) {
2038 mPagesAccessibilityDelegate = new OverviewScreenAccessibilityDelegate(this);
2039 }
2040 page.setAccessibilityDelegate(mPagesAccessibilityDelegate);
2041 } else {
2042 int accessible = mState == State.NORMAL ?
2043 IMPORTANT_FOR_ACCESSIBILITY_NO :
2044 IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
2045 page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
2046 page.getShortcutsAndWidgets().setImportantForAccessibility(accessible);
2047 page.setContentDescription(null);
2048 page.setAccessibilityDelegate(null);
2049 }
Adam Cohenedaaa302013-10-01 17:33:27 -07002050 }
2051
Michael Jurkabed61d22012-02-14 22:51:29 -08002052 @Override
Michael Jurkaa35e35a2012-04-26 15:04:28 -07002053 public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
Winson Chungdc61c4d2015-04-20 18:26:57 -07002054 mIsSwitchingState = true;
2055
2056 // Invalidate here to ensure that the pages are rendered during the state change transition.
2057 invalidate();
2058
2059 updateChildrenLayersEnabled(false);
2060 hideCustomContentIfNecessary();
Michael Jurkabed61d22012-02-14 22:51:29 -08002061 }
2062
2063 @Override
Michael Jurkaa35e35a2012-04-26 15:04:28 -07002064 public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
2065 }
2066
2067 @Override
Winson Chung70442722012-02-10 15:43:22 -08002068 public void onLauncherTransitionStep(Launcher l, float t) {
2069 mTransitionProgress = t;
2070 }
2071
2072 @Override
Michael Jurkabed61d22012-02-14 22:51:29 -08002073 public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
Winson Chungdc61c4d2015-04-20 18:26:57 -07002074 mIsSwitchingState = false;
Adam Cohenf3434992013-09-16 16:52:59 -07002075 updateChildrenLayersEnabled(false);
Winson Chungdc61c4d2015-04-20 18:26:57 -07002076 showCustomContentIfNecessary();
Adam Cohen3f452c82013-09-19 11:57:17 -07002077 }
2078
2079 void updateCustomContentVisibility() {
2080 int visibility = mState == Workspace.State.NORMAL ? VISIBLE : INVISIBLE;
2081 if (hasCustomContent()) {
2082 mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(visibility);
2083 }
2084 }
2085
2086 void showCustomContentIfNecessary() {
2087 boolean show = mState == Workspace.State.NORMAL;
2088 if (show && hasCustomContent()) {
2089 mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(VISIBLE);
2090 }
2091 }
2092
2093 void hideCustomContentIfNecessary() {
2094 boolean hide = mState != Workspace.State.NORMAL;
2095 if (hide && hasCustomContent()) {
Adam Cohen1774a8a2013-11-15 10:56:16 +00002096 disableLayoutTransitions();
Adam Cohen3f452c82013-09-19 11:57:17 -07002097 mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(INVISIBLE);
Adam Cohen1774a8a2013-11-15 10:56:16 +00002098 enableLayoutTransitions();
Adam Cohenf3434992013-09-16 16:52:59 -07002099 }
2100 }
2101
Michael Jurkabed61d22012-02-14 22:51:29 -08002102 @Override
2103 public View getContent() {
2104 return this;
2105 }
2106
Joe Onorato4be866d2010-10-10 11:26:02 -07002107 /**
Winson Chungb745afb2015-03-02 11:51:23 -08002108 * Returns the drawable for the given text view.
2109 */
2110 public static Drawable getTextViewIcon(TextView tv) {
2111 final Drawable[] drawables = tv.getCompoundDrawables();
2112 for (int i = 0; i < drawables.length; i++) {
2113 if (drawables[i] != null) {
2114 return drawables[i];
2115 }
2116 }
2117 return null;
2118 }
2119
2120 /**
Joe Onorato4be866d2010-10-10 11:26:02 -07002121 * Draw the View v into the given Canvas.
2122 *
2123 * @param v the view to draw
2124 * @param destCanvas the canvas to draw on
2125 * @param padding the horizontal and vertical padding to use when drawing
2126 */
Sunny Goyal508da152014-08-14 10:53:27 -07002127 private static void drawDragView(View v, Canvas destCanvas, int padding) {
2128 final Rect clipRect = sTempRect;
Joe Onorato4be866d2010-10-10 11:26:02 -07002129 v.getDrawingRect(clipRect);
2130
Adam Cohen099f60d2011-08-23 21:07:26 -07002131 boolean textVisible = false;
2132
Adam Cohenac8c8762011-07-13 11:15:27 -07002133 destCanvas.save();
Sunny Goyal95abbb32014-08-04 10:53:22 -07002134 if (v instanceof TextView) {
Winson Chungb745afb2015-03-02 11:51:23 -08002135 Drawable d = getTextViewIcon((TextView) v);
Mathew Inwood85900292014-04-16 14:17:39 +01002136 Rect bounds = getDrawableBounds(d);
2137 clipRect.set(0, 0, bounds.width() + padding, bounds.height() + padding);
Sunny Goyal95abbb32014-08-04 10:53:22 -07002138 destCanvas.translate(padding / 2 - bounds.left, padding / 2 - bounds.top);
Adam Cohenac8c8762011-07-13 11:15:27 -07002139 d.draw(destCanvas);
2140 } else {
2141 if (v instanceof FolderIcon) {
Adam Cohen099f60d2011-08-23 21:07:26 -07002142 // For FolderIcons the text can bleed into the icon area, and so we need to
2143 // hide the text completely (which can't be achieved by clipping).
2144 if (((FolderIcon) v).getTextVisible()) {
2145 ((FolderIcon) v).setTextVisible(false);
2146 textVisible = true;
2147 }
Adam Cohenac8c8762011-07-13 11:15:27 -07002148 }
2149 destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2);
2150 destCanvas.clipRect(clipRect, Op.REPLACE);
2151 v.draw(destCanvas);
Adam Cohen099f60d2011-08-23 21:07:26 -07002152
2153 // Restore text visibility of FolderIcon if necessary
2154 if (textVisible) {
2155 ((FolderIcon) v).setTextVisible(true);
2156 }
Adam Cohenac8c8762011-07-13 11:15:27 -07002157 }
2158 destCanvas.restore();
2159 }
2160
2161 /**
2162 * Returns a new bitmap to show when the given View is being dragged around.
2163 * Responsibility for the bitmap is transferred to the caller.
Sunny Goyal95abbb32014-08-04 10:53:22 -07002164 * @param expectedPadding padding to add to the drag view. If a different padding was used
2165 * its value will be changed
Adam Cohenac8c8762011-07-13 11:15:27 -07002166 */
Sunny Goyal508da152014-08-14 10:53:27 -07002167 public Bitmap createDragBitmap(View v, AtomicInteger expectedPadding) {
Adam Cohenac8c8762011-07-13 11:15:27 -07002168 Bitmap b;
2169
Sunny Goyal95abbb32014-08-04 10:53:22 -07002170 int padding = expectedPadding.get();
Adam Cohenac8c8762011-07-13 11:15:27 -07002171 if (v instanceof TextView) {
Winson Chungb745afb2015-03-02 11:51:23 -08002172 Drawable d = getTextViewIcon((TextView) v);
Mathew Inwood85900292014-04-16 14:17:39 +01002173 Rect bounds = getDrawableBounds(d);
2174 b = Bitmap.createBitmap(bounds.width() + padding,
2175 bounds.height() + padding, Bitmap.Config.ARGB_8888);
Sunny Goyal95abbb32014-08-04 10:53:22 -07002176 expectedPadding.set(padding - bounds.left - bounds.top);
Adam Cohenac8c8762011-07-13 11:15:27 -07002177 } else {
2178 b = Bitmap.createBitmap(
2179 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
Joe Onorato4be866d2010-10-10 11:26:02 -07002180 }
2181
Sunny Goyal508da152014-08-14 10:53:27 -07002182 mCanvas.setBitmap(b);
2183 drawDragView(v, mCanvas, padding);
2184 mCanvas.setBitmap(null);
Joe Onorato4be866d2010-10-10 11:26:02 -07002185
Adam Cohenac8c8762011-07-13 11:15:27 -07002186 return b;
Joe Onorato4be866d2010-10-10 11:26:02 -07002187 }
2188
2189 /**
2190 * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
2191 * Responsibility for the bitmap is transferred to the caller.
2192 */
Sunny Goyal508da152014-08-14 10:53:27 -07002193 private Bitmap createDragOutline(View v, int padding) {
Adam Cohen410f3cd2013-09-22 12:09:32 -07002194 final int outlineColor = getResources().getColor(R.color.outline_color);
Joe Onorato4be866d2010-10-10 11:26:02 -07002195 final Bitmap b = Bitmap.createBitmap(
2196 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
2197
Sunny Goyal508da152014-08-14 10:53:27 -07002198 mCanvas.setBitmap(b);
2199 drawDragView(v, mCanvas, padding);
2200 mOutlineHelper.applyExpensiveOutlineWithBlur(b, mCanvas, outlineColor, outlineColor);
2201 mCanvas.setBitmap(null);
Joe Onorato4be866d2010-10-10 11:26:02 -07002202 return b;
2203 }
2204
2205 /**
Michael Jurkad3ef3062010-11-23 16:23:58 -08002206 * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
2207 * Responsibility for the bitmap is transferred to the caller.
2208 */
Sunny Goyal508da152014-08-14 10:53:27 -07002209 private Bitmap createDragOutline(Bitmap orig, int padding, int w, int h,
Michael Jurka8c3339b2012-06-14 16:18:21 -07002210 boolean clipAlpha) {
Adam Cohen410f3cd2013-09-22 12:09:32 -07002211 final int outlineColor = getResources().getColor(R.color.outline_color);
Michael Jurkaf12c75c2011-01-25 22:41:40 -08002212 final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Sunny Goyal508da152014-08-14 10:53:27 -07002213 mCanvas.setBitmap(b);
Michael Jurkaf12c75c2011-01-25 22:41:40 -08002214
2215 Rect src = new Rect(0, 0, orig.getWidth(), orig.getHeight());
2216 float scaleFactor = Math.min((w - padding) / (float) orig.getWidth(),
2217 (h - padding) / (float) orig.getHeight());
2218 int scaledWidth = (int) (scaleFactor * orig.getWidth());
2219 int scaledHeight = (int) (scaleFactor * orig.getHeight());
2220 Rect dst = new Rect(0, 0, scaledWidth, scaledHeight);
2221
2222 // center the image
2223 dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2);
2224
Sunny Goyal508da152014-08-14 10:53:27 -07002225 mCanvas.drawBitmap(orig, src, dst, null);
2226 mOutlineHelper.applyExpensiveOutlineWithBlur(b, mCanvas, outlineColor, outlineColor,
Michael Jurka8c3339b2012-06-14 16:18:21 -07002227 clipAlpha);
Sunny Goyal508da152014-08-14 10:53:27 -07002228 mCanvas.setBitmap(null);
Michael Jurkad3ef3062010-11-23 16:23:58 -08002229
2230 return b;
2231 }
2232
Adam Cohenc9735cf2015-01-23 16:11:55 -08002233 public void startDrag(CellLayout.CellInfo cellInfo) {
2234 startDrag(cellInfo, false);
2235 }
2236
Sunny Goyale9b651e2015-04-24 11:44:51 -07002237 @Override
Adam Cohenc9735cf2015-01-23 16:11:55 -08002238 public void startDrag(CellLayout.CellInfo cellInfo, boolean accessible) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002239 View child = cellInfo.cell;
Winson Chungaafa03c2010-06-11 17:34:16 -07002240
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002241 // Make sure the drag was started by a long press as opposed to a long click.
Bjorn Bringert7984c942009-12-09 15:38:25 +00002242 if (!child.isInTouchMode()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002243 return;
2244 }
Winson Chungaafa03c2010-06-11 17:34:16 -07002245
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002246 mDragInfo = cellInfo;
Adam Cohend41fbf52012-02-16 23:53:59 -08002247 child.setVisibility(INVISIBLE);
Adam Cohen482ed822012-03-02 14:15:13 -08002248 CellLayout layout = (CellLayout) child.getParent().getParent();
2249 layout.prepareChildForDrag(child);
Joe Onorato4be866d2010-10-10 11:26:02 -07002250
Adam Cohenc9735cf2015-01-23 16:11:55 -08002251 beginDragShared(child, this, accessible);
Adam Cohenac8c8762011-07-13 11:15:27 -07002252 }
2253
Winson Chungb745afb2015-03-02 11:51:23 -08002254 public void beginDragShared(View child, DragSource source, boolean accessible) {
2255 beginDragShared(child, new Point(), source, accessible);
Adam Cohenc9735cf2015-01-23 16:11:55 -08002256 }
2257
Winson Chungb745afb2015-03-02 11:51:23 -08002258 public void beginDragShared(View child, Point relativeTouchPos, DragSource source,
2259 boolean accessible) {
Sunny Goyal508da152014-08-14 10:53:27 -07002260 child.clearFocus();
2261 child.setPressed(false);
2262
2263 // The outline is used to visualize where the item will land if dropped
2264 mDragOutline = createDragOutline(child, DRAG_BITMAP_PADDING);
2265
Anjali Koppal62d18ed2014-03-10 17:04:03 -07002266 mLauncher.onDragStarted(child);
Joe Onorato4be866d2010-10-10 11:26:02 -07002267 // The drag bitmap follows the touch point around on the screen
Sunny Goyal95abbb32014-08-04 10:53:22 -07002268 AtomicInteger padding = new AtomicInteger(DRAG_BITMAP_PADDING);
Sunny Goyal508da152014-08-14 10:53:27 -07002269 final Bitmap b = createDragBitmap(child, padding);
Joe Onorato4be866d2010-10-10 11:26:02 -07002270
2271 final int bmpWidth = b.getWidth();
Winson Chungeecf02d2012-03-02 17:14:58 -08002272 final int bmpHeight = b.getHeight();
Adam Cohene3e27a82011-04-15 12:07:39 -07002273
Adam Cohen307fe232012-08-16 17:55:58 -07002274 float scale = mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY);
Winson Chungbabb53e2014-04-14 17:12:49 -07002275 int dragLayerX = Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2);
2276 int dragLayerY = Math.round(mTempXY[1] - (bmpHeight - scale * bmpHeight) / 2
Sunny Goyal95abbb32014-08-04 10:53:22 -07002277 - padding.get() / 2);
Adam Cohene3e27a82011-04-15 12:07:39 -07002278
Adam Cohen2e6da152015-05-06 11:42:25 -07002279 DeviceProfile grid = mLauncher.getDeviceProfile();
Winson Chungb8c69f32011-10-19 21:36:08 -07002280 Point dragVisualizeOffset = null;
Adam Cohene3e27a82011-04-15 12:07:39 -07002281 Rect dragRect = null;
Sunny Goyal508da152014-08-14 10:53:27 -07002282 if (child instanceof BubbleTextView) {
Winson Chungb745afb2015-03-02 11:51:23 -08002283 BubbleTextView icon = (BubbleTextView) child;
Winson Chung5f8afe62013-08-12 16:19:28 -07002284 int iconSize = grid.iconSizePx;
Adam Cohene3e27a82011-04-15 12:07:39 -07002285 int top = child.getPaddingTop();
2286 int left = (bmpWidth - iconSize) / 2;
2287 int right = left + iconSize;
2288 int bottom = top + iconSize;
Winson Chungb745afb2015-03-02 11:51:23 -08002289 if (icon.isLayoutHorizontal()) {
2290 // If the layout is horizontal, then if we are just picking up the icon, then just
2291 // use the child position since the icon is top-left aligned. Otherwise, offset
2292 // the drag layer position horizontally so that the icon is under the current
2293 // touch position.
2294 if (icon.getIcon().getBounds().contains(relativeTouchPos.x, relativeTouchPos.y)) {
2295 dragLayerX = Math.round(mTempXY[0]);
2296 } else {
2297 dragLayerX = Math.round(mTempXY[0] + relativeTouchPos.x - (bmpWidth / 2));
2298 }
2299 }
Adam Cohenfc53cd22011-07-20 15:45:11 -07002300 dragLayerY += top;
Winson Chungb8c69f32011-10-19 21:36:08 -07002301 // Note: The drag region is used to calculate drag layer offsets, but the
2302 // dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
Sunny Goyal95abbb32014-08-04 10:53:22 -07002303 dragVisualizeOffset = new Point(-padding.get() / 2, padding.get() / 2);
Adam Cohene3e27a82011-04-15 12:07:39 -07002304 dragRect = new Rect(left, top, right, bottom);
Adam Cohen0e4857c2011-06-30 17:05:14 -07002305 } else if (child instanceof FolderIcon) {
Winson Chung5f8afe62013-08-12 16:19:28 -07002306 int previewSize = grid.folderIconSizePx;
2307 dragRect = new Rect(0, child.getPaddingTop(), child.getWidth(), previewSize);
Adam Cohene3e27a82011-04-15 12:07:39 -07002308 }
2309
Winson Chung1e9cbfe2011-09-30 16:52:26 -07002310 // Clear the pressed state if necessary
2311 if (child instanceof BubbleTextView) {
2312 BubbleTextView icon = (BubbleTextView) child;
Sunny Goyal508da152014-08-14 10:53:27 -07002313 icon.clearPressedBackground();
Winson Chung1e9cbfe2011-09-30 16:52:26 -07002314 }
2315
Adam Cohen2f32ad22013-11-13 11:29:49 +00002316 if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) {
2317 String msg = "Drag started with a view that has no tag set. This "
2318 + "will cause a crash (issue 11627249) down the line. "
2319 + "View: " + child + " tag: " + child.getTag();
2320 throw new IllegalStateException(msg);
2321 }
2322
Winson Chungeeb5bbc2013-11-13 15:47:05 -08002323 DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
Adam Cohenc9735cf2015-01-23 16:11:55 -08002324 DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale, accessible);
Winson Chungeeb5bbc2013-11-13 15:47:05 -08002325 dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
Adam Cohen5084cba2013-09-03 12:01:16 -07002326
2327 if (child.getParent() instanceof ShortcutAndWidgetContainer) {
2328 mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent();
2329 }
2330
Joe Onorato4be866d2010-10-10 11:26:02 -07002331 b.recycle();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002332 }
2333
Winson Chungbabb53e2014-04-14 17:12:49 -07002334 public void beginExternalDragShared(View child, DragSource source) {
Adam Cohen2e6da152015-05-06 11:42:25 -07002335 DeviceProfile grid = mLauncher.getDeviceProfile();
Winson Chungbabb53e2014-04-14 17:12:49 -07002336 int iconSize = grid.iconSizePx;
2337
2338 // Notify launcher of drag start
2339 mLauncher.onDragStarted(child);
2340
2341 // Compose a new drag bitmap that is of the icon size
Sunny Goyal95abbb32014-08-04 10:53:22 -07002342 AtomicInteger padding = new AtomicInteger(DRAG_BITMAP_PADDING);
Sunny Goyal508da152014-08-14 10:53:27 -07002343 final Bitmap tmpB = createDragBitmap(child, padding);
Winson Chungbabb53e2014-04-14 17:12:49 -07002344 Bitmap b = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
2345 Paint p = new Paint();
2346 p.setFilterBitmap(true);
Sunny Goyal508da152014-08-14 10:53:27 -07002347 mCanvas.setBitmap(b);
2348 mCanvas.drawBitmap(tmpB, new Rect(0, 0, tmpB.getWidth(), tmpB.getHeight()),
Winson Chungbabb53e2014-04-14 17:12:49 -07002349 new Rect(0, 0, iconSize, iconSize), p);
Sunny Goyal508da152014-08-14 10:53:27 -07002350 mCanvas.setBitmap(null);
Winson Chungbabb53e2014-04-14 17:12:49 -07002351
2352 // Find the child's location on the screen
2353 int bmpWidth = tmpB.getWidth();
2354 float iconScale = (float) bmpWidth / iconSize;
2355 float scale = mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY) * iconScale;
2356 int dragLayerX = Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2);
2357 int dragLayerY = Math.round(mTempXY[1]);
2358
2359 // Note: The drag region is used to calculate drag layer offsets, but the
2360 // dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
Sunny Goyal95abbb32014-08-04 10:53:22 -07002361 Point dragVisualizeOffset = new Point(-padding.get() / 2, padding.get() / 2);
Winson Chungbabb53e2014-04-14 17:12:49 -07002362 Rect dragRect = new Rect(0, 0, iconSize, iconSize);
2363
2364 if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) {
2365 String msg = "Drag started with a view that has no tag set. This "
2366 + "will cause a crash (issue 11627249) down the line. "
2367 + "View: " + child + " tag: " + child.getTag();
2368 throw new IllegalStateException(msg);
2369 }
2370
2371 // Start the drag
2372 DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
Adam Cohenc9735cf2015-01-23 16:11:55 -08002373 DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale, false);
Winson Chungbabb53e2014-04-14 17:12:49 -07002374 dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
2375
2376 // Recycle temporary bitmaps
2377 tmpB.recycle();
2378 }
2379
Adam Cohen4b285c52011-07-21 14:24:06 -07002380 public boolean transitionStateShouldAllowDrop() {
Adam Cohen6c5891a2014-07-09 23:53:15 -07002381 return ((!isSwitchingState() || mTransitionProgress > 0.5f) &&
2382 (mState == State.NORMAL || mState == State.SPRING_LOADED));
Adam Cohen4b285c52011-07-21 14:24:06 -07002383 }
2384
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002385 /**
2386 * {@inheritDoc}
2387 */
Adam Cohencb3382b2011-05-24 14:07:08 -07002388 public boolean acceptDrop(DragObject d) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002389 // If it's an external drop (e.g. from All Apps), check if it should be accepted
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002390 CellLayout dropTargetLayout = mDropToLayout;
Adam Cohencb3382b2011-05-24 14:07:08 -07002391 if (d.dragSource != this) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002392 // Don't accept the drop if we're not over a screen at time of drop
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002393 if (dropTargetLayout == null) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002394 return false;
2395 }
Adam Cohen4b285c52011-07-21 14:24:06 -07002396 if (!transitionStateShouldAllowDrop()) return false;
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002397
Sunny Goyal1587d532015-01-29 09:57:16 -08002398 mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);
Adam Cohena65beee2011-06-27 21:32:23 -07002399
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07002400 // We want the point to be mapped to the dragTarget.
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002401 if (mLauncher.isHotseatLayout(dropTargetLayout)) {
Adam Cohen307fe232012-08-16 17:55:58 -07002402 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07002403 } else {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002404 mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null);
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07002405 }
2406
Winson Chung557d6ed2011-07-08 15:34:52 -07002407 int spanX = 1;
2408 int spanY = 1;
Winson Chung557d6ed2011-07-08 15:34:52 -07002409 if (mDragInfo != null) {
2410 final CellLayout.CellInfo dragCellInfo = mDragInfo;
2411 spanX = dragCellInfo.spanX;
2412 spanY = dragCellInfo.spanY;
Winson Chung557d6ed2011-07-08 15:34:52 -07002413 } else {
2414 final ItemInfo dragInfo = (ItemInfo) d.dragInfo;
2415 spanX = dragInfo.spanX;
2416 spanY = dragInfo.spanY;
2417 }
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002418
Adam Cohend41fbf52012-02-16 23:53:59 -08002419 int minSpanX = spanX;
2420 int minSpanY = spanY;
2421 if (d.dragInfo instanceof PendingAddWidgetInfo) {
2422 minSpanX = ((PendingAddWidgetInfo) d.dragInfo).minSpanX;
2423 minSpanY = ((PendingAddWidgetInfo) d.dragInfo).minSpanY;
2424 }
Adam Cohen482ed822012-03-02 14:15:13 -08002425
Adam Cohena65beee2011-06-27 21:32:23 -07002426 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002427 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, dropTargetLayout,
Adam Cohenf0777b92012-02-28 14:02:45 -08002428 mTargetCell);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002429 float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08002430 mDragViewVisualCenter[1], mTargetCell);
Adam Cohenec0d61d2014-02-07 16:34:51 -08002431 if (mCreateUserFolderOnDrop && willCreateUserFolder((ItemInfo) d.dragInfo,
2432 dropTargetLayout, mTargetCell, distance, true)) {
Adam Cohena65beee2011-06-27 21:32:23 -07002433 return true;
2434 }
Adam Cohenec0d61d2014-02-07 16:34:51 -08002435
2436 if (mAddToExistingFolderOnDrop && willAddToExistingUserFolder((ItemInfo) d.dragInfo,
2437 dropTargetLayout, mTargetCell, distance)) {
Adam Cohena65beee2011-06-27 21:32:23 -07002438 return true;
2439 }
2440
Adam Cohen482ed822012-03-02 14:15:13 -08002441 int[] resultSpan = new int[2];
Adam Cohenfa3c58f2013-12-06 16:10:55 -08002442 mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08002443 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
2444 null, mTargetCell, resultSpan, CellLayout.MODE_ACCEPT_DROP);
2445 boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
2446
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002447 // Don't accept the drop if there's no room for the item
Adam Cohen482ed822012-03-02 14:15:13 -08002448 if (!foundCell) {
Winson Chung96ef4092011-11-22 12:25:14 -08002449 // Don't show the message if we are dropping on the AllApps button and the hotseat
2450 // is full
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002451 boolean isHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
Winson Chung93eef082012-03-23 15:59:27 -07002452 if (mTargetCell != null && isHotseat) {
Winson Chung96ef4092011-11-22 12:25:14 -08002453 Hotseat hotseat = mLauncher.getHotseat();
Andrew Flynn0dca1ec2012-02-29 13:33:22 -08002454 if (hotseat.isAllAppsButtonRank(
Winson Chung96ef4092011-11-22 12:25:14 -08002455 hotseat.getOrderInHotseat(mTargetCell[0], mTargetCell[1]))) {
2456 return false;
2457 }
2458 }
2459
Winson Chung93eef082012-03-23 15:59:27 -07002460 mLauncher.showOutOfSpaceMessage(isHotseat);
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002461 return false;
2462 }
2463 }
Adam Cohendedbd962013-07-11 14:21:49 -07002464
2465 long screenId = getIdForScreen(dropTargetLayout);
2466 if (screenId == EXTRA_EMPTY_SCREEN_ID) {
2467 commitExtraEmptyScreen();
2468 }
2469
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002470 return true;
2471 }
2472
Adam Cohen482ed822012-03-02 14:15:13 -08002473 boolean willCreateUserFolder(ItemInfo info, CellLayout target, int[] targetCell, float
2474 distance, boolean considerTimeout) {
2475 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohena65beee2011-06-27 21:32:23 -07002476 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
2477
Adam Cohen19f37922012-03-21 11:59:11 -07002478 if (dropOverView != null) {
2479 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
2480 if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) {
2481 return false;
2482 }
2483 }
2484
Winson Chung3d503fb2011-07-13 17:25:49 -07002485 boolean hasntMoved = false;
2486 if (mDragInfo != null) {
Adam Cohen482ed822012-03-02 14:15:13 -08002487 hasntMoved = dropOverView == mDragInfo.cell;
Winson Chung3d503fb2011-07-13 17:25:49 -07002488 }
Adam Cohene0310962011-04-18 16:15:31 -07002489
Adam Cohena65beee2011-06-27 21:32:23 -07002490 if (dropOverView == null || hasntMoved || (considerTimeout && !mCreateUserFolderOnDrop)) {
2491 return false;
2492 }
Adam Cohene0310962011-04-18 16:15:31 -07002493
Adam Cohena65beee2011-06-27 21:32:23 -07002494 boolean aboveShortcut = (dropOverView.getTag() instanceof ShortcutInfo);
Adam Cohene0310962011-04-18 16:15:31 -07002495 boolean willBecomeShortcut =
Adam Cohenc0dcf592011-06-01 15:30:43 -07002496 (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
2497 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT);
Adam Cohene0310962011-04-18 16:15:31 -07002498
2499 return (aboveShortcut && willBecomeShortcut);
2500 }
2501
Adam Cohen482ed822012-03-02 14:15:13 -08002502 boolean willAddToExistingUserFolder(Object dragInfo, CellLayout target, int[] targetCell,
2503 float distance) {
2504 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohena65beee2011-06-27 21:32:23 -07002505 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohen19f37922012-03-21 11:59:11 -07002506
2507 if (dropOverView != null) {
2508 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
2509 if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) {
2510 return false;
2511 }
2512 }
2513
Adam Cohena65beee2011-06-27 21:32:23 -07002514 if (dropOverView instanceof FolderIcon) {
2515 FolderIcon fi = (FolderIcon) dropOverView;
2516 if (fi.acceptDrop(dragInfo)) {
2517 return true;
2518 }
2519 }
2520 return false;
2521 }
2522
Winson Chung3d503fb2011-07-13 17:25:49 -07002523 boolean createUserFolderIfNecessary(View newView, long container, CellLayout target,
Adam Cohen482ed822012-03-02 14:15:13 -08002524 int[] targetCell, float distance, boolean external, DragView dragView,
2525 Runnable postAnimationRunnable) {
2526 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohenc0dcf592011-06-01 15:30:43 -07002527 View v = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohen19f37922012-03-21 11:59:11 -07002528
Winson Chungec9a0a42011-07-20 20:42:13 -07002529 boolean hasntMoved = false;
2530 if (mDragInfo != null) {
2531 CellLayout cellParent = getParentCellLayoutForView(mDragInfo.cell);
2532 hasntMoved = (mDragInfo.cellX == targetCell[0] &&
2533 mDragInfo.cellY == targetCell[1]) && (cellParent == target);
2534 }
Adam Cohen10b17372011-04-15 14:21:25 -07002535
Adam Cohen19072da2011-05-31 14:30:45 -07002536 if (v == null || hasntMoved || !mCreateUserFolderOnDrop) return false;
2537 mCreateUserFolderOnDrop = false;
Adam Cohendcd297f2013-06-18 13:13:40 -07002538 final long screenId = (targetCell == null) ? mDragInfo.screenId : getIdForScreen(target);
Adam Cohen10b17372011-04-15 14:21:25 -07002539
2540 boolean aboveShortcut = (v.getTag() instanceof ShortcutInfo);
2541 boolean willBecomeShortcut = (newView.getTag() instanceof ShortcutInfo);
2542
2543 if (aboveShortcut && willBecomeShortcut) {
2544 ShortcutInfo sourceInfo = (ShortcutInfo) newView.getTag();
2545 ShortcutInfo destInfo = (ShortcutInfo) v.getTag();
2546 // if the drag started here, we need to remove it from the workspace
2547 if (!external) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002548 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
Adam Cohen10b17372011-04-15 14:21:25 -07002549 }
2550
Adam Cohend0445262011-07-04 23:53:22 -07002551 Rect folderLocation = new Rect();
Adam Cohenac8c8762011-07-13 11:15:27 -07002552 float scale = mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation);
Adam Cohen10b17372011-04-15 14:21:25 -07002553 target.removeView(v);
Adam Cohend0445262011-07-04 23:53:22 -07002554
Winson Chung3d503fb2011-07-13 17:25:49 -07002555 FolderIcon fi =
Adam Cohendcd297f2013-06-18 13:13:40 -07002556 mLauncher.addFolder(target, container, screenId, targetCell[0], targetCell[1]);
Adam Cohena9cf38f2011-05-02 15:36:58 -07002557 destInfo.cellX = -1;
2558 destInfo.cellY = -1;
2559 sourceInfo.cellX = -1;
2560 sourceInfo.cellY = -1;
Adam Cohend0445262011-07-04 23:53:22 -07002561
Adam Cohen558baaf2011-08-15 15:22:57 -07002562 // If the dragView is null, we can't animate
2563 boolean animate = dragView != null;
2564 if (animate) {
2565 fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale,
2566 postAnimationRunnable);
2567 } else {
2568 fi.addItem(destInfo);
2569 fi.addItem(sourceInfo);
2570 }
Adam Cohen10b17372011-04-15 14:21:25 -07002571 return true;
2572 }
2573 return false;
2574 }
2575
Adam Cohena65beee2011-06-27 21:32:23 -07002576 boolean addToExistingFolderIfNecessary(View newView, CellLayout target, int[] targetCell,
Adam Cohen482ed822012-03-02 14:15:13 -08002577 float distance, DragObject d, boolean external) {
2578 if (distance > mMaxDistanceForFolderCreation) return false;
2579
Adam Cohena65beee2011-06-27 21:32:23 -07002580 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002581 if (!mAddToExistingFolderOnDrop) return false;
2582 mAddToExistingFolderOnDrop = false;
Adam Cohen19f37922012-03-21 11:59:11 -07002583
Adam Cohenc0dcf592011-06-01 15:30:43 -07002584 if (dropOverView instanceof FolderIcon) {
2585 FolderIcon fi = (FolderIcon) dropOverView;
Adam Cohen3e8f8112011-07-02 18:03:00 -07002586 if (fi.acceptDrop(d.dragInfo)) {
2587 fi.onDrop(d);
Adam Cohenc0dcf592011-06-01 15:30:43 -07002588
2589 // if the drag started here, we need to remove it from the workspace
2590 if (!external) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002591 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
Adam Cohenc0dcf592011-06-01 15:30:43 -07002592 }
2593 return true;
2594 }
2595 }
2596 return false;
2597 }
2598
Sunny Goyale9b651e2015-04-24 11:44:51 -07002599 @Override
2600 public void prepareAccessibilityDrop() { }
2601
Adam Cohend41fbf52012-02-16 23:53:59 -08002602 public void onDrop(final DragObject d) {
Sunny Goyal1587d532015-01-29 09:57:16 -08002603 mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002604 CellLayout dropTargetLayout = mDropToLayout;
2605
Adam Cohene3e27a82011-04-15 12:07:39 -07002606 // We want the point to be mapped to the dragTarget.
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002607 if (dropTargetLayout != null) {
2608 if (mLauncher.isHotseatLayout(dropTargetLayout)) {
Adam Cohenbb00ff22012-07-12 15:43:01 -07002609 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
Winson Chung3d503fb2011-07-13 17:25:49 -07002610 } else {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002611 mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null);
Winson Chung3d503fb2011-07-13 17:25:49 -07002612 }
Adam Cohenba781612011-05-09 14:37:39 -07002613 }
Michael Jurkac6ee42e2010-09-30 12:04:50 -07002614
Winson Chungdc61c4d2015-04-20 18:26:57 -07002615 int snapScreen = WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE;
Adam Cohend41fbf52012-02-16 23:53:59 -08002616 boolean resizeOnDrop = false;
Adam Cohencb3382b2011-05-24 14:07:08 -07002617 if (d.dragSource != this) {
Adam Cohene3e27a82011-04-15 12:07:39 -07002618 final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0],
2619 (int) mDragViewVisualCenter[1] };
Adam Cohen3e8f8112011-07-02 18:03:00 -07002620 onDropExternal(touchXY, d.dragInfo, dropTargetLayout, false, d);
Patrick Dubroyce34a972010-10-19 10:34:32 -07002621 } else if (mDragInfo != null) {
Patrick Dubroyce34a972010-10-19 10:34:32 -07002622 final View cell = mDragInfo.cell;
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08002623
Adam Cohend41fbf52012-02-16 23:53:59 -08002624 Runnable resizeRunnable = null;
Michael Jurka1e2f4652013-07-08 18:03:46 -07002625 if (dropTargetLayout != null && !d.cancelled) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002626 // Move internally
Winson Chung40e882b2011-07-21 19:01:11 -07002627 boolean hasMovedLayouts = (getParentCellLayoutForView(cell) != dropTargetLayout);
2628 boolean hasMovedIntoHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
2629 long container = hasMovedIntoHotseat ?
Winson Chung3d503fb2011-07-13 17:25:49 -07002630 LauncherSettings.Favorites.CONTAINER_HOTSEAT :
2631 LauncherSettings.Favorites.CONTAINER_DESKTOP;
Adam Cohendcd297f2013-06-18 13:13:40 -07002632 long screenId = (mTargetCell[0] < 0) ?
2633 mDragInfo.screenId : getIdForScreen(dropTargetLayout);
Adam Cohenc0dcf592011-06-01 15:30:43 -07002634 int spanX = mDragInfo != null ? mDragInfo.spanX : 1;
2635 int spanY = mDragInfo != null ? mDragInfo.spanY : 1;
2636 // First we find the cell nearest to point at which the item is
2637 // dropped, without any consideration to whether there is an item there.
Adam Cohen482ed822012-03-02 14:15:13 -08002638
Adam Cohenc0dcf592011-06-01 15:30:43 -07002639 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], (int)
2640 mDragViewVisualCenter[1], spanX, spanY, dropTargetLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08002641 float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
2642 mDragViewVisualCenter[1], mTargetCell);
2643
Adam Cohenc0dcf592011-06-01 15:30:43 -07002644 // If the item being dropped is a shortcut and the nearest drop
Adam Cohen76078c42011-06-09 15:06:52 -07002645 // cell also contains a shortcut, then create a folder with the two shortcuts.
Winson Chung1c4cf4a2011-07-29 14:49:10 -07002646 if (!mInScrollArea && createUserFolderIfNecessary(cell, container,
Adam Cohen482ed822012-03-02 14:15:13 -08002647 dropTargetLayout, mTargetCell, distance, false, d.dragView, null)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07002648 return;
2649 }
2650
Adam Cohen482ed822012-03-02 14:15:13 -08002651 if (addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell,
2652 distance, d, false)) {
Adam Cohendf035382011-04-11 17:22:04 -07002653 return;
2654 }
2655
2656 // Aside from the special case where we're dropping a shortcut onto a shortcut,
2657 // we need to find the nearest cell location that is vacant
Adam Cohend41fbf52012-02-16 23:53:59 -08002658 ItemInfo item = (ItemInfo) d.dragInfo;
2659 int minSpanX = item.spanX;
2660 int minSpanY = item.spanY;
2661 if (item.minSpanX > 0 && item.minSpanY > 0) {
2662 minSpanX = item.minSpanX;
2663 minSpanY = item.minSpanY;
2664 }
Adam Cohen482ed822012-03-02 14:15:13 -08002665
Adam Cohend41fbf52012-02-16 23:53:59 -08002666 int[] resultSpan = new int[2];
Adam Cohenfa3c58f2013-12-06 16:10:55 -08002667 mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08002668 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, cell,
2669 mTargetCell, resultSpan, CellLayout.MODE_ON_DROP);
2670
Adam Cohend41fbf52012-02-16 23:53:59 -08002671 boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
Adam Cohen9e05a5e2012-09-10 15:53:09 -07002672
Adam Cohenaaa5c212012-10-05 18:14:31 -07002673 // if the widget resizes on drop
Adam Cohen9e05a5e2012-09-10 15:53:09 -07002674 if (foundCell && (cell instanceof AppWidgetHostView) &&
Adam Cohenaaa5c212012-10-05 18:14:31 -07002675 (resultSpan[0] != item.spanX || resultSpan[1] != item.spanY)) {
Adam Cohend41fbf52012-02-16 23:53:59 -08002676 resizeOnDrop = true;
2677 item.spanX = resultSpan[0];
2678 item.spanY = resultSpan[1];
Adam Cohen7bdfc972012-05-22 16:50:35 -07002679 AppWidgetHostView awhv = (AppWidgetHostView) cell;
2680 AppWidgetResizeFrame.updateWidgetSizeRanges(awhv, mLauncher, resultSpan[0],
2681 resultSpan[1]);
Adam Cohend41fbf52012-02-16 23:53:59 -08002682 }
Adam Cohendf035382011-04-11 17:22:04 -07002683
Adam Cohendcd297f2013-06-18 13:13:40 -07002684 if (getScreenIdForPageIndex(mCurrentPage) != screenId && !hasMovedIntoHotseat) {
2685 snapScreen = getPageIndexForScreenId(screenId);
2686 snapToPage(snapScreen);
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08002687 }
2688
Adam Cohend41fbf52012-02-16 23:53:59 -08002689 if (foundCell) {
2690 final ItemInfo info = (ItemInfo) cell.getTag();
Winson Chung40e882b2011-07-21 19:01:11 -07002691 if (hasMovedLayouts) {
Patrick Dubroy94383362010-10-29 15:03:24 -07002692 // Reparent the view
Sunny Goyal25611b12014-07-22 09:52:37 -07002693 CellLayout parentCell = getParentCellLayoutForView(cell);
2694 if (parentCell != null) {
2695 parentCell.removeView(cell);
2696 } else if (LauncherAppState.isDogfoodBuild()) {
2697 throw new NullPointerException("mDragInfo.cell has null parent");
2698 }
Adam Cohendcd297f2013-06-18 13:13:40 -07002699 addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1],
Adam Cohend41fbf52012-02-16 23:53:59 -08002700 info.spanX, info.spanY);
Patrick Dubroy94383362010-10-29 15:03:24 -07002701 }
2702
2703 // update the item's position after drop
Patrick Dubroy94383362010-10-29 15:03:24 -07002704 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
Adam Cohen19f37922012-03-21 11:59:11 -07002705 lp.cellX = lp.tmpCellX = mTargetCell[0];
2706 lp.cellY = lp.tmpCellY = mTargetCell[1];
Adam Cohend41fbf52012-02-16 23:53:59 -08002707 lp.cellHSpan = item.spanX;
2708 lp.cellVSpan = item.spanY;
Adam Cohen19f37922012-03-21 11:59:11 -07002709 lp.isLockedToGrid = true;
Patrick Dubroy94383362010-10-29 15:03:24 -07002710
Winson Chung3d503fb2011-07-13 17:25:49 -07002711 if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
2712 cell instanceof LauncherAppWidgetHostView) {
Adam Cohend4844c32011-02-18 19:25:06 -08002713 final CellLayout cellLayout = dropTargetLayout;
2714 // We post this call so that the widget has a chance to be placed
2715 // in its final location
2716
2717 final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell;
Sunny Goyal2434d402015-02-17 11:44:15 -08002718 AppWidgetProviderInfo pInfo = hostView.getAppWidgetInfo();
Adam Cohenc9735cf2015-01-23 16:11:55 -08002719 if (pInfo != null && pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE
2720 && !d.accessibleDrag) {
Adam Cohend41fbf52012-02-16 23:53:59 -08002721 final Runnable addResizeFrame = new Runnable() {
Adam Cohend4844c32011-02-18 19:25:06 -08002722 public void run() {
Adam Cohen716b51e2011-06-30 12:09:54 -07002723 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohenc0dcf592011-06-01 15:30:43 -07002724 dragLayer.addResizeFrame(info, hostView, cellLayout);
Adam Cohend4844c32011-02-18 19:25:06 -08002725 }
Adam Cohen26976d92011-03-22 15:33:33 -07002726 };
Adam Cohend41fbf52012-02-16 23:53:59 -08002727 resizeRunnable = (new Runnable() {
Adam Cohen26976d92011-03-22 15:33:33 -07002728 public void run() {
2729 if (!isPageMoving()) {
Adam Cohend41fbf52012-02-16 23:53:59 -08002730 addResizeFrame.run();
Adam Cohen26976d92011-03-22 15:33:33 -07002731 } else {
Adam Cohend41fbf52012-02-16 23:53:59 -08002732 mDelayedResizeRunnable = addResizeFrame;
Adam Cohen26976d92011-03-22 15:33:33 -07002733 }
2734 }
Adam Cohend4844c32011-02-18 19:25:06 -08002735 });
2736 }
2737 }
2738
Adam Cohen949debe2013-09-29 14:22:48 -07002739 LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId, lp.cellX,
2740 lp.cellY, item.spanX, item.spanY);
Adam Cohend41fbf52012-02-16 23:53:59 -08002741 } else {
2742 // If we can't find a drop location, we return the item to its original position
2743 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
2744 mTargetCell[0] = lp.cellX;
2745 mTargetCell[1] = lp.cellY;
Adam Cohenf1dcdf62012-05-10 16:51:52 -07002746 CellLayout layout = (CellLayout) cell.getParent().getParent();
2747 layout.markCellsAsOccupiedForView(cell);
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002748 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002749 }
Patrick Dubroyce34a972010-10-19 10:34:32 -07002750
Michael Jurka8c920dd2011-01-20 14:16:56 -08002751 final CellLayout parent = (CellLayout) cell.getParent().getParent();
Adam Cohend41fbf52012-02-16 23:53:59 -08002752 final Runnable finalResizeRunnable = resizeRunnable;
Patrick Dubroyce34a972010-10-19 10:34:32 -07002753 // Prepare it to be animated into its new position
2754 // This must be called after the view has been re-parented
Adam Cohend41fbf52012-02-16 23:53:59 -08002755 final Runnable onCompleteRunnable = new Runnable() {
Michael Jurkad74c9842011-07-10 12:44:21 -07002756 @Override
2757 public void run() {
2758 mAnimatingViewIntoPlace = false;
Michael Jurka3a0469d2012-06-21 09:38:41 -07002759 updateChildrenLayersEnabled(false);
Adam Cohend41fbf52012-02-16 23:53:59 -08002760 if (finalResizeRunnable != null) {
2761 finalResizeRunnable.run();
2762 }
Michael Jurkad74c9842011-07-10 12:44:21 -07002763 }
2764 };
2765 mAnimatingViewIntoPlace = true;
Adam Cohenfc53cd22011-07-20 15:45:11 -07002766 if (d.dragView.hasDrawn()) {
Adam Cohend41fbf52012-02-16 23:53:59 -08002767 final ItemInfo info = (ItemInfo) cell.getTag();
Adam Cohen59400422014-03-05 18:07:04 -08002768 boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
2769 || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
2770 if (isWidget) {
Adam Cohend41fbf52012-02-16 23:53:59 -08002771 int animationType = resizeOnDrop ? ANIMATE_INTO_POSITION_AND_RESIZE :
2772 ANIMATE_INTO_POSITION_AND_DISAPPEAR;
2773 animateWidgetDrop(info, parent, d.dragView,
2774 onCompleteRunnable, animationType, cell, false);
2775 } else {
Winson Chungdc61c4d2015-04-20 18:26:57 -07002776 int duration = snapScreen < 0 ?
2777 WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE :
2778 ADJACENT_SCREEN_DROP_DURATION;
Adam Cohen85b467b2012-02-29 15:38:46 -08002779 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration,
2780 onCompleteRunnable, this);
Adam Cohend41fbf52012-02-16 23:53:59 -08002781 }
Adam Cohenfc53cd22011-07-20 15:45:11 -07002782 } else {
Winson Chung7bd1bbb2012-02-13 18:29:29 -08002783 d.deferDragViewCleanupPostAnimation = false;
Adam Cohenfc53cd22011-07-20 15:45:11 -07002784 cell.setVisibility(VISIBLE);
2785 }
Adam Cohen716b51e2011-06-30 12:09:54 -07002786 parent.onDropChild(cell);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002787 }
2788 }
2789
Adam Cohendcd297f2013-06-18 13:13:40 -07002790 public void setFinalScrollForPageChange(int pageIndex) {
2791 CellLayout cl = (CellLayout) getChildAt(pageIndex);
2792 if (cl != null) {
Adam Cohened51cc92011-08-01 20:28:08 -07002793 mSavedScrollX = getScrollX();
Adam Cohened51cc92011-08-01 20:28:08 -07002794 mSavedTranslationX = cl.getTranslationX();
2795 mSavedRotationY = cl.getRotationY();
Adam Cohenedb40762013-07-18 16:45:45 -07002796 final int newX = getScrollForPage(pageIndex);
Adam Cohened51cc92011-08-01 20:28:08 -07002797 setScrollX(newX);
2798 cl.setTranslationX(0f);
2799 cl.setRotationY(0f);
2800 }
2801 }
2802
Adam Cohendcd297f2013-06-18 13:13:40 -07002803 public void resetFinalScrollForPageChange(int pageIndex) {
2804 if (pageIndex >= 0) {
2805 CellLayout cl = (CellLayout) getChildAt(pageIndex);
Adam Cohened51cc92011-08-01 20:28:08 -07002806 setScrollX(mSavedScrollX);
2807 cl.setTranslationX(mSavedTranslationX);
2808 cl.setRotationY(mSavedRotationY);
2809 }
2810 }
2811
Adam Cohen76078c42011-06-09 15:06:52 -07002812 public void getViewLocationRelativeToSelf(View v, int[] location) {
Adam Cohen8dfcba42011-07-07 16:38:18 -07002813 getLocationInWindow(location);
Adam Cohene3e27a82011-04-15 12:07:39 -07002814 int x = location[0];
2815 int y = location[1];
2816
Adam Cohen8dfcba42011-07-07 16:38:18 -07002817 v.getLocationInWindow(location);
Adam Cohene3e27a82011-04-15 12:07:39 -07002818 int vX = location[0];
2819 int vY = location[1];
2820
2821 location[0] = vX - x;
2822 location[1] = vY - y;
2823 }
2824
Adam Cohencb3382b2011-05-24 14:07:08 -07002825 public void onDragEnter(DragObject d) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002826 mDragEnforcer.onDragEnter();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002827 mCreateUserFolderOnDrop = false;
2828 mAddToExistingFolderOnDrop = false;
2829
2830 mDropToLayout = null;
2831 CellLayout layout = getCurrentDropLayout();
2832 setCurrentDropLayout(layout);
2833 setCurrentDragOverlappingLayout(layout);
Winson Chungb26f3d62011-06-02 10:49:29 -07002834
Adam Cohenc50438c2014-08-19 17:43:05 -07002835 if (!workspaceInModalState()) {
2836 mLauncher.getDragLayer().showPageHints();
Michael Jurkad718d6a2010-10-14 15:35:17 -07002837 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002838 }
2839
Winson Chungfe411c82013-09-26 16:07:17 -07002840 /** Return a rect that has the cellWidth/cellHeight (left, top), and
2841 * widthGap/heightGap (right, bottom) */
Adam Cohena897f392012-04-27 18:12:05 -07002842 static Rect getCellLayoutMetrics(Launcher launcher, int orientation) {
Winson Chung5f8afe62013-08-12 16:19:28 -07002843 LauncherAppState app = LauncherAppState.getInstance();
Sunny Goyalc6205602015-05-21 20:46:33 -07002844 InvariantDeviceProfile inv = app.getInvariantDeviceProfile();
Winson Chung5f8afe62013-08-12 16:19:28 -07002845
Adam Cohena897f392012-04-27 18:12:05 -07002846 Display display = launcher.getWindowManager().getDefaultDisplay();
2847 Point smallestSize = new Point();
2848 Point largestSize = new Point();
2849 display.getCurrentSizeRange(smallestSize, largestSize);
Sunny Goyalc6205602015-05-21 20:46:33 -07002850 int countX = (int) inv.numColumns;
2851 int countY = (int) inv.numRows;
2852 boolean isLayoutRtl = Utilities.isRtl(launcher.getResources());
Adam Cohena897f392012-04-27 18:12:05 -07002853 if (orientation == CellLayout.LANDSCAPE) {
2854 if (mLandscapeCellLayoutMetrics == null) {
Sunny Goyalc6205602015-05-21 20:46:33 -07002855 Rect padding = inv.landscapeProfile.getWorkspacePadding(isLayoutRtl);
Winson Chungdcd27ba2013-12-11 15:28:15 -08002856 int width = largestSize.x - padding.left - padding.right;
2857 int height = smallestSize.y - padding.top - padding.bottom;
Adam Cohena897f392012-04-27 18:12:05 -07002858 mLandscapeCellLayoutMetrics = new Rect();
Winson Chungfe411c82013-09-26 16:07:17 -07002859 mLandscapeCellLayoutMetrics.set(
Sunny Goyalc6205602015-05-21 20:46:33 -07002860 DeviceProfile.calculateCellWidth(width, countX),
2861 DeviceProfile.calculateCellHeight(height, countY), 0, 0);
Adam Cohena897f392012-04-27 18:12:05 -07002862 }
2863 return mLandscapeCellLayoutMetrics;
2864 } else if (orientation == CellLayout.PORTRAIT) {
2865 if (mPortraitCellLayoutMetrics == null) {
Sunny Goyalc6205602015-05-21 20:46:33 -07002866 Rect padding = inv.portraitProfile.getWorkspacePadding(isLayoutRtl);
Winson Chungdcd27ba2013-12-11 15:28:15 -08002867 int width = smallestSize.x - padding.left - padding.right;
2868 int height = largestSize.y - padding.top - padding.bottom;
Adam Cohena897f392012-04-27 18:12:05 -07002869 mPortraitCellLayoutMetrics = new Rect();
Winson Chungfe411c82013-09-26 16:07:17 -07002870 mPortraitCellLayoutMetrics.set(
Sunny Goyalc6205602015-05-21 20:46:33 -07002871 DeviceProfile.calculateCellWidth(width, countX),
2872 DeviceProfile.calculateCellHeight(height, countY), 0, 0);
Adam Cohena897f392012-04-27 18:12:05 -07002873 }
2874 return mPortraitCellLayoutMetrics;
2875 }
2876 return null;
2877 }
2878
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002879 public void onDragExit(DragObject d) {
2880 mDragEnforcer.onDragExit();
Winson Chungc07918d2011-07-01 15:35:26 -07002881
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002882 // Here we store the final page that will be dropped to, if the workspace in fact
2883 // receives the drop
2884 if (mInScrollArea) {
Winson Chungcc1cfe42012-06-18 15:09:17 -07002885 if (isPageMoving()) {
2886 // If the user drops while the page is scrolling, we should use that page as the
2887 // destination instead of the page that is being hovered over.
2888 mDropToLayout = (CellLayout) getPageAt(getNextPage());
2889 } else {
2890 mDropToLayout = mDragOverlappingLayout;
2891 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002892 } else {
2893 mDropToLayout = mDragTargetLayout;
2894 }
2895
2896 if (mDragMode == DRAG_MODE_CREATE_FOLDER) {
2897 mCreateUserFolderOnDrop = true;
2898 } else if (mDragMode == DRAG_MODE_ADD_TO_FOLDER) {
2899 mAddToExistingFolderOnDrop = true;
Adam Cohen482ed822012-03-02 14:15:13 -08002900 }
2901
Winson Chungc07918d2011-07-01 15:35:26 -07002902 // Reset the scroll area and previous drag target
2903 onResetScrollArea();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002904 setCurrentDropLayout(null);
2905 setCurrentDragOverlappingLayout(null);
Winson Chungc07918d2011-07-01 15:35:26 -07002906
Adam Cohen74c28d12011-11-18 14:17:11 -08002907 mSpringLoadedDragController.cancel();
Winson Chungc07918d2011-07-01 15:35:26 -07002908
Adam Cohenc50438c2014-08-19 17:43:05 -07002909 mLauncher.getDragLayer().hidePageHints();
Winson Chungc07918d2011-07-01 15:35:26 -07002910 }
2911
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002912 void setCurrentDropLayout(CellLayout layout) {
2913 if (mDragTargetLayout != null) {
2914 mDragTargetLayout.revertTempState();
2915 mDragTargetLayout.onDragExit();
2916 }
2917 mDragTargetLayout = layout;
2918 if (mDragTargetLayout != null) {
2919 mDragTargetLayout.onDragEnter();
2920 }
2921 cleanupReorder(true);
2922 cleanupFolderCreation();
2923 setCurrentDropOverCell(-1, -1);
2924 }
2925
2926 void setCurrentDragOverlappingLayout(CellLayout layout) {
2927 if (mDragOverlappingLayout != null) {
2928 mDragOverlappingLayout.setIsDragOverlapping(false);
2929 }
2930 mDragOverlappingLayout = layout;
2931 if (mDragOverlappingLayout != null) {
2932 mDragOverlappingLayout.setIsDragOverlapping(true);
2933 }
2934 invalidate();
2935 }
2936
2937 void setCurrentDropOverCell(int x, int y) {
2938 if (x != mDragOverX || y != mDragOverY) {
2939 mDragOverX = x;
2940 mDragOverY = y;
2941 setDragMode(DRAG_MODE_NONE);
2942 }
2943 }
2944
2945 void setDragMode(int dragMode) {
2946 if (dragMode != mDragMode) {
2947 if (dragMode == DRAG_MODE_NONE) {
2948 cleanupAddToFolder();
2949 // We don't want to cancel the re-order alarm every time the target cell changes
2950 // as this feels to slow / unresponsive.
2951 cleanupReorder(false);
2952 cleanupFolderCreation();
2953 } else if (dragMode == DRAG_MODE_ADD_TO_FOLDER) {
2954 cleanupReorder(true);
2955 cleanupFolderCreation();
2956 } else if (dragMode == DRAG_MODE_CREATE_FOLDER) {
2957 cleanupAddToFolder();
2958 cleanupReorder(true);
2959 } else if (dragMode == DRAG_MODE_REORDER) {
2960 cleanupAddToFolder();
2961 cleanupFolderCreation();
2962 }
2963 mDragMode = dragMode;
2964 }
2965 }
2966
2967 private void cleanupFolderCreation() {
2968 if (mDragFolderRingAnimator != null) {
2969 mDragFolderRingAnimator.animateToNaturalState();
Adam Cohen8ec05f92013-10-13 12:29:03 -07002970 mDragFolderRingAnimator = null;
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002971 }
Adam Cohen8ec05f92013-10-13 12:29:03 -07002972 mFolderCreationAlarm.setOnAlarmListener(null);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002973 mFolderCreationAlarm.cancelAlarm();
2974 }
2975
2976 private void cleanupAddToFolder() {
2977 if (mDragOverFolderIcon != null) {
2978 mDragOverFolderIcon.onDragExit(null);
2979 mDragOverFolderIcon = null;
2980 }
2981 }
2982
2983 private void cleanupReorder(boolean cancelAlarm) {
2984 // Any pending reorders are canceled
2985 if (cancelAlarm) {
2986 mReorderAlarm.cancelAlarm();
2987 }
2988 mLastReorderX = -1;
2989 mLastReorderY = -1;
Winson Chungc07918d2011-07-01 15:35:26 -07002990 }
2991
Michael Jurka4516c112010-10-07 15:13:47 -07002992 /*
2993 *
2994 * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
2995 * coordinate space. The argument xy is modified with the return result.
2996 *
2997 * if cachedInverseMatrix is not null, this method will just use that matrix instead of
Michael Jurkad718d6a2010-10-14 15:35:17 -07002998 * computing it itself; we use this to avoid redundant matrix inversions in
Michael Jurka4516c112010-10-07 15:13:47 -07002999 * findMatchingPageForDragOver
3000 *
3001 */
3002 void mapPointFromSelfToChild(View v, float[] xy, Matrix cachedInverseMatrix) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003003 xy[0] = xy[0] - v.getLeft();
3004 xy[1] = xy[1] - v.getTop();
Michael Jurka4516c112010-10-07 15:13:47 -07003005 }
3006
Adam Cohen7d30a372013-07-01 17:03:59 -07003007 boolean isPointInSelfOverHotseat(int x, int y, Rect r) {
3008 if (r == null) {
3009 r = new Rect();
3010 }
3011 mTempPt[0] = x;
3012 mTempPt[1] = y;
3013 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempPt, true);
Winson Chungabedd9f2013-09-24 15:41:09 -07003014
Adam Cohen2e6da152015-05-06 11:42:25 -07003015 DeviceProfile grid = mLauncher.getDeviceProfile();
Winson Chungabedd9f2013-09-24 15:41:09 -07003016 r = grid.getHotseatRect();
Adam Cohen7d30a372013-07-01 17:03:59 -07003017 if (r.contains(mTempPt[0], mTempPt[1])) {
3018 return true;
3019 }
3020 return false;
3021 }
Winson Chung3d503fb2011-07-13 17:25:49 -07003022
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003023 void mapPointFromSelfToHotseatLayout(Hotseat hotseat, float[] xy) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003024 mTempPt[0] = (int) xy[0];
3025 mTempPt[1] = (int) xy[1];
3026 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempPt, true);
Winson Chung156ab5b2013-07-12 14:14:16 -07003027 mLauncher.getDragLayer().mapCoordInSelfToDescendent(hotseat.getLayout(), mTempPt);
Adam Cohen7d30a372013-07-01 17:03:59 -07003028
3029 xy[0] = mTempPt[0];
3030 xy[1] = mTempPt[1];
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003031 }
3032
Winson Chung3d503fb2011-07-13 17:25:49 -07003033 /*
Michael Jurka4516c112010-10-07 15:13:47 -07003034 *
3035 * Convert the 2D coordinate xy from this CellLayout's coordinate space to
3036 * the parent View's coordinate space. The argument xy is modified with the return result.
3037 *
3038 */
3039 void mapPointFromChildToSelf(View v, float[] xy) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003040 xy[0] += v.getLeft();
3041 xy[1] += v.getTop();
Michael Jurka4516c112010-10-07 15:13:47 -07003042 }
3043
Adam Cohene3e27a82011-04-15 12:07:39 -07003044 static private float squaredDistance(float[] point1, float[] point2) {
Michael Jurka4516c112010-10-07 15:13:47 -07003045 float distanceX = point1[0] - point2[0];
3046 float distanceY = point2[1] - point2[1];
3047 return distanceX * distanceX + distanceY * distanceY;
Adam Cohene3e27a82011-04-15 12:07:39 -07003048 }
Michael Jurka0280c3b2010-09-17 15:00:07 -07003049
Michael Jurka4516c112010-10-07 15:13:47 -07003050 /*
3051 *
Michael Jurka4516c112010-10-07 15:13:47 -07003052 * This method returns the CellLayout that is currently being dragged to. In order to drag
3053 * to a CellLayout, either the touch point must be directly over the CellLayout, or as a second
3054 * strategy, we see if the dragView is overlapping any CellLayout and choose the closest one
3055 *
3056 * Return null if no CellLayout is currently being dragged over
3057 *
3058 */
3059 private CellLayout findMatchingPageForDragOver(
Adam Cohen00618752011-07-20 12:06:04 -07003060 DragView dragView, float originX, float originY, boolean exact) {
Michael Jurka4516c112010-10-07 15:13:47 -07003061 // We loop through all the screens (ie CellLayouts) and see which ones overlap
3062 // with the item being dragged and then choose the one that's closest to the touch point
Michael Jurkaa63c4522010-08-19 13:52:27 -07003063 final int screenCount = getChildCount();
3064 CellLayout bestMatchingScreen = null;
Michael Jurka0280c3b2010-09-17 15:00:07 -07003065 float smallestDistSoFar = Float.MAX_VALUE;
Michael Jurka4516c112010-10-07 15:13:47 -07003066
Michael Jurkaa63c4522010-08-19 13:52:27 -07003067 for (int i = 0; i < screenCount; i++) {
Adam Cohendf041bc2013-09-22 12:32:22 -07003068 // The custom content screen is not a valid drag over option
3069 if (mScreenOrder.get(i) == CUSTOM_CONTENT_SCREEN_ID) {
3070 continue;
3071 }
3072
Winson Chung3d503fb2011-07-13 17:25:49 -07003073 CellLayout cl = (CellLayout) getChildAt(i);
Michael Jurkaa63c4522010-08-19 13:52:27 -07003074
Adam Cohen00618752011-07-20 12:06:04 -07003075 final float[] touchXy = {originX, originY};
Michael Jurka4516c112010-10-07 15:13:47 -07003076 // Transform the touch coordinates to the CellLayout's local coordinates
3077 // If the touch point is within the bounds of the cell layout, we can return immediately
Michael Jurka0280c3b2010-09-17 15:00:07 -07003078 cl.getMatrix().invert(mTempInverseMatrix);
Michael Jurka4516c112010-10-07 15:13:47 -07003079 mapPointFromSelfToChild(cl, touchXy, mTempInverseMatrix);
Michael Jurkaa63c4522010-08-19 13:52:27 -07003080
Michael Jurka4516c112010-10-07 15:13:47 -07003081 if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() &&
3082 touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) {
3083 return cl;
3084 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07003085
Winson Chung96ef4092011-11-22 12:25:14 -08003086 if (!exact) {
Michael Jurka4516c112010-10-07 15:13:47 -07003087 // Get the center of the cell layout in screen coordinates
3088 final float[] cellLayoutCenter = mTempCellLayoutCenterCoordinates;
3089 cellLayoutCenter[0] = cl.getWidth()/2;
3090 cellLayoutCenter[1] = cl.getHeight()/2;
3091 mapPointFromChildToSelf(cl, cellLayoutCenter);
Michael Jurka0280c3b2010-09-17 15:00:07 -07003092
Adam Cohen00618752011-07-20 12:06:04 -07003093 touchXy[0] = originX;
3094 touchXy[1] = originY;
Michael Jurka0280c3b2010-09-17 15:00:07 -07003095
Michael Jurka4516c112010-10-07 15:13:47 -07003096 // Calculate the distance between the center of the CellLayout
3097 // and the touch point
3098 float dist = squaredDistance(touchXy, cellLayoutCenter);
3099
3100 if (dist < smallestDistSoFar) {
Michael Jurka0280c3b2010-09-17 15:00:07 -07003101 smallestDistSoFar = dist;
Michael Jurkaa63c4522010-08-19 13:52:27 -07003102 bestMatchingScreen = cl;
Michael Jurkaa63c4522010-08-19 13:52:27 -07003103 }
Michael Jurka4516c112010-10-07 15:13:47 -07003104 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07003105 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07003106 return bestMatchingScreen;
3107 }
3108
Winson Chungea359c62011-08-03 17:06:35 -07003109 private boolean isDragWidget(DragObject d) {
3110 return (d.dragInfo instanceof LauncherAppWidgetInfo ||
3111 d.dragInfo instanceof PendingAddWidgetInfo);
3112 }
3113 private boolean isExternalDragWidget(DragObject d) {
3114 return d.dragSource != this && isDragWidget(d);
3115 }
3116
Adam Cohencb3382b2011-05-24 14:07:08 -07003117 public void onDragOver(DragObject d) {
Winson Chungc07918d2011-07-01 15:35:26 -07003118 // Skip drag over events while we are dragging over side pages
Adam Cohen6c5891a2014-07-09 23:53:15 -07003119 if (mInScrollArea || !transitionStateShouldAllowDrop()) return;
Winson Chungc07918d2011-07-01 15:35:26 -07003120
Winson Chung4afe9b32011-07-27 17:46:20 -07003121 Rect r = new Rect();
Winson Chung3d503fb2011-07-13 17:25:49 -07003122 CellLayout layout = null;
Winson Chungc07918d2011-07-01 15:35:26 -07003123 ItemInfo item = (ItemInfo) d.dragInfo;
Sunny Goyal25611b12014-07-22 09:52:37 -07003124 if (item == null) {
3125 if (LauncherAppState.isDogfoodBuild()) {
3126 throw new NullPointerException("DragObject has null info");
3127 }
3128 return;
3129 }
Winson Chungc07918d2011-07-01 15:35:26 -07003130
3131 // Ensure that we have proper spans for the item that we are dropping
3132 if (item.spanX < 0 || item.spanY < 0) throw new RuntimeException("Improper spans found");
Sunny Goyal1587d532015-01-29 09:57:16 -08003133 mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);
Adam Cohen00618752011-07-20 12:06:04 -07003134
Adam Cohen482ed822012-03-02 14:15:13 -08003135 final View child = (mDragInfo == null) ? null : mDragInfo.cell;
Winson Chungc07918d2011-07-01 15:35:26 -07003136 // Identify whether we have dragged over a side page
Adam Cohen6c5891a2014-07-09 23:53:15 -07003137 if (workspaceInModalState()) {
Winson Chungea359c62011-08-03 17:06:35 -07003138 if (mLauncher.getHotseat() != null && !isExternalDragWidget(d)) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003139 if (isPointInSelfOverHotseat(d.x, d.y, r)) {
Winson Chung4afe9b32011-07-27 17:46:20 -07003140 layout = mLauncher.getHotseat().getLayout();
3141 }
3142 }
3143 if (layout == null) {
Winson Chung96ef4092011-11-22 12:25:14 -08003144 layout = findMatchingPageForDragOver(d.dragView, d.x, d.y, false);
Winson Chung4afe9b32011-07-27 17:46:20 -07003145 }
Winson Chungc07918d2011-07-01 15:35:26 -07003146 if (layout != mDragTargetLayout) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003147 setCurrentDropLayout(layout);
3148 setCurrentDragOverlappingLayout(layout);
Winson Chungc07918d2011-07-01 15:35:26 -07003149
Michael Jurkad74c9842011-07-10 12:44:21 -07003150 boolean isInSpringLoadedMode = (mState == State.SPRING_LOADED);
Winson Chungc07918d2011-07-01 15:35:26 -07003151 if (isInSpringLoadedMode) {
Winson Chung4afe9b32011-07-27 17:46:20 -07003152 if (mLauncher.isHotseatLayout(layout)) {
3153 mSpringLoadedDragController.cancel();
3154 } else {
3155 mSpringLoadedDragController.setAlarm(mDragTargetLayout);
3156 }
Winson Chungc07918d2011-07-01 15:35:26 -07003157 }
3158 }
3159 } else {
Winson Chung3d503fb2011-07-13 17:25:49 -07003160 // Test to see if we are over the hotseat otherwise just use the current page
Winson Chungea359c62011-08-03 17:06:35 -07003161 if (mLauncher.getHotseat() != null && !isDragWidget(d)) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003162 if (isPointInSelfOverHotseat(d.x, d.y, r)) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003163 layout = mLauncher.getHotseat().getLayout();
3164 }
3165 }
3166 if (layout == null) {
3167 layout = getCurrentDropLayout();
3168 }
Winson Chungc07918d2011-07-01 15:35:26 -07003169 if (layout != mDragTargetLayout) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003170 setCurrentDropLayout(layout);
3171 setCurrentDragOverlappingLayout(layout);
Winson Chungc07918d2011-07-01 15:35:26 -07003172 }
3173 }
3174
3175 // Handle the drag over
3176 if (mDragTargetLayout != null) {
Winson Chungc07918d2011-07-01 15:35:26 -07003177 // We want the point to be mapped to the dragTarget.
Winson Chung3d503fb2011-07-13 17:25:49 -07003178 if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003179 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
Winson Chung3d503fb2011-07-13 17:25:49 -07003180 } else {
3181 mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
3182 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003183
Winson Chungc07918d2011-07-01 15:35:26 -07003184 ItemInfo info = (ItemInfo) d.dragInfo;
Patrick Dubroy1262e362010-10-06 15:49:50 -07003185
Adam Cohen74c54912013-09-29 14:48:04 -07003186 int minSpanX = item.spanX;
3187 int minSpanY = item.spanY;
3188 if (item.minSpanX > 0 && item.minSpanY > 0) {
3189 minSpanX = item.minSpanX;
3190 minSpanY = item.minSpanY;
3191 }
3192
Winson Chungc07918d2011-07-01 15:35:26 -07003193 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
Adam Cohen74c54912013-09-29 14:48:04 -07003194 (int) mDragViewVisualCenter[1], minSpanX, minSpanY,
Adam Cohend024f982012-05-23 18:26:45 -07003195 mDragTargetLayout, mTargetCell);
Adam Cohen74c54912013-09-29 14:48:04 -07003196 int reorderX = mTargetCell[0];
3197 int reorderY = mTargetCell[1];
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003198
3199 setCurrentDropOverCell(mTargetCell[0], mTargetCell[1]);
3200
Adam Cohen482ed822012-03-02 14:15:13 -08003201 float targetCellDistance = mDragTargetLayout.getDistanceFromCell(
3202 mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell);
3203
Winson Chungc07918d2011-07-01 15:35:26 -07003204 final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0],
3205 mTargetCell[1]);
Winson Chung785d2eb2011-04-14 16:08:02 -07003206
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003207 manageFolderFeedback(info, mDragTargetLayout, mTargetCell,
Adam Cohenc9735cf2015-01-23 16:11:55 -08003208 targetCellDistance, dragOverView, d.accessibleDrag);
Adam Cohen482ed822012-03-02 14:15:13 -08003209
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003210 boolean nearestDropOccupied = mDragTargetLayout.isNearestDropLocationOccupied((int)
3211 mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], item.spanX,
3212 item.spanY, child, mTargetCell);
3213
3214 if (!nearestDropOccupied) {
Adam Cohen19f37922012-03-21 11:59:11 -07003215 mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
3216 (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
3217 mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, false,
3218 d.dragView.getDragVisualizeOffset(), d.dragView.getDragRegion());
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003219 } else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
Adam Cohen74c54912013-09-29 14:48:04 -07003220 && !mReorderAlarm.alarmPending() && (mLastReorderX != reorderX ||
3221 mLastReorderY != reorderY)) {
Adam Cohend024f982012-05-23 18:26:45 -07003222
Adam Cohenfa3c58f2013-12-06 16:10:55 -08003223 int[] resultSpan = new int[2];
3224 mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0],
3225 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, item.spanX, item.spanY,
3226 child, mTargetCell, resultSpan, CellLayout.MODE_SHOW_REORDER_HINT);
3227
Adam Cohen19f37922012-03-21 11:59:11 -07003228 // Otherwise, if we aren't adding to or creating a folder and there's no pending
3229 // reorder, then we schedule a reorder
Adam Cohen482ed822012-03-02 14:15:13 -08003230 ReorderAlarmListener listener = new ReorderAlarmListener(mDragViewVisualCenter,
3231 minSpanX, minSpanY, item.spanX, item.spanY, d.dragView, child);
3232 mReorderAlarm.setOnAlarmListener(listener);
3233 mReorderAlarm.setAlarm(REORDER_TIMEOUT);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003234 }
3235
3236 if (mDragMode == DRAG_MODE_CREATE_FOLDER || mDragMode == DRAG_MODE_ADD_TO_FOLDER ||
3237 !nearestDropOccupied) {
3238 if (mDragTargetLayout != null) {
3239 mDragTargetLayout.revertTempState();
Michael Jurkad3ef3062010-11-23 16:23:58 -08003240 }
3241 }
Adam Cohen482ed822012-03-02 14:15:13 -08003242 }
3243 }
3244
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003245 private void manageFolderFeedback(ItemInfo info, CellLayout targetLayout,
Adam Cohenc9735cf2015-01-23 16:11:55 -08003246 int[] targetCell, float distance, View dragOverView, boolean accessibleDrag) {
Adam Cohen482ed822012-03-02 14:15:13 -08003247 boolean userFolderPending = willCreateUserFolder(info, targetLayout, targetCell, distance,
3248 false);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003249 if (mDragMode == DRAG_MODE_NONE && userFolderPending &&
3250 !mFolderCreationAlarm.alarmPending()) {
Adam Cohenc9735cf2015-01-23 16:11:55 -08003251
3252 FolderCreationAlarmListener listener = new
3253 FolderCreationAlarmListener(targetLayout, targetCell[0], targetCell[1]);
3254
3255 if (!accessibleDrag) {
3256 mFolderCreationAlarm.setOnAlarmListener(listener);
3257 mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT);
3258 } else {
3259 listener.onAlarm(mFolderCreationAlarm);
3260 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003261 return;
Adam Cohen482ed822012-03-02 14:15:13 -08003262 }
3263
3264 boolean willAddToFolder =
3265 willAddToExistingUserFolder(info, targetLayout, targetCell, distance);
3266
3267 if (willAddToFolder && mDragMode == DRAG_MODE_NONE) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003268 mDragOverFolderIcon = ((FolderIcon) dragOverView);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003269 mDragOverFolderIcon.onDragEnter(info);
Adam Cohen482ed822012-03-02 14:15:13 -08003270 if (targetLayout != null) {
3271 targetLayout.clearDragOutlines();
Winson Chungc07918d2011-07-01 15:35:26 -07003272 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003273 setDragMode(DRAG_MODE_ADD_TO_FOLDER);
3274 return;
Patrick Dubroy976ebec2010-08-04 20:03:37 -07003275 }
Adam Cohen482ed822012-03-02 14:15:13 -08003276
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003277 if (mDragMode == DRAG_MODE_ADD_TO_FOLDER && !willAddToFolder) {
3278 setDragMode(DRAG_MODE_NONE);
3279 }
3280 if (mDragMode == DRAG_MODE_CREATE_FOLDER && !userFolderPending) {
3281 setDragMode(DRAG_MODE_NONE);
Adam Cohen482ed822012-03-02 14:15:13 -08003282 }
3283
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003284 return;
Adam Cohenc0dcf592011-06-01 15:30:43 -07003285 }
3286
Adam Cohen19072da2011-05-31 14:30:45 -07003287 class FolderCreationAlarmListener implements OnAlarmListener {
Adam Cohen69ce2e52011-07-03 19:25:21 -07003288 CellLayout layout;
3289 int cellX;
3290 int cellY;
Adam Cohen19072da2011-05-31 14:30:45 -07003291
Adam Cohen69ce2e52011-07-03 19:25:21 -07003292 public FolderCreationAlarmListener(CellLayout layout, int cellX, int cellY) {
3293 this.layout = layout;
3294 this.cellX = cellX;
3295 this.cellY = cellY;
Adam Cohen19072da2011-05-31 14:30:45 -07003296 }
3297
3298 public void onAlarm(Alarm alarm) {
Adam Cohen8ec05f92013-10-13 12:29:03 -07003299 if (mDragFolderRingAnimator != null) {
3300 // This shouldn't happen ever, but just in case, make sure we clean up the mess.
3301 mDragFolderRingAnimator.animateToNaturalState();
Adam Cohen19072da2011-05-31 14:30:45 -07003302 }
Adam Cohen8ec05f92013-10-13 12:29:03 -07003303 mDragFolderRingAnimator = new FolderRingAnimator(mLauncher, null);
Adam Cohen69ce2e52011-07-03 19:25:21 -07003304 mDragFolderRingAnimator.setCell(cellX, cellY);
3305 mDragFolderRingAnimator.setCellLayout(layout);
Adam Cohen19072da2011-05-31 14:30:45 -07003306 mDragFolderRingAnimator.animateToAcceptState();
Adam Cohen69ce2e52011-07-03 19:25:21 -07003307 layout.showFolderAccept(mDragFolderRingAnimator);
3308 layout.clearDragOutlines();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003309 setDragMode(DRAG_MODE_CREATE_FOLDER);
Adam Cohen482ed822012-03-02 14:15:13 -08003310 }
3311 }
3312
3313 class ReorderAlarmListener implements OnAlarmListener {
3314 float[] dragViewCenter;
3315 int minSpanX, minSpanY, spanX, spanY;
3316 DragView dragView;
3317 View child;
3318
3319 public ReorderAlarmListener(float[] dragViewCenter, int minSpanX, int minSpanY, int spanX,
3320 int spanY, DragView dragView, View child) {
3321 this.dragViewCenter = dragViewCenter;
3322 this.minSpanX = minSpanX;
3323 this.minSpanY = minSpanY;
3324 this.spanX = spanX;
3325 this.spanY = spanY;
3326 this.child = child;
3327 this.dragView = dragView;
3328 }
3329
3330 public void onAlarm(Alarm alarm) {
3331 int[] resultSpan = new int[2];
Adam Cohen19f37922012-03-21 11:59:11 -07003332 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
Adam Cohen74c54912013-09-29 14:48:04 -07003333 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, mDragTargetLayout,
3334 mTargetCell);
Adam Cohen19f37922012-03-21 11:59:11 -07003335 mLastReorderX = mTargetCell[0];
3336 mLastReorderY = mTargetCell[1];
3337
Adam Cohenfa3c58f2013-12-06 16:10:55 -08003338 mTargetCell = mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08003339 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
3340 child, mTargetCell, resultSpan, CellLayout.MODE_DRAG_OVER);
3341
Adam Cohen19f37922012-03-21 11:59:11 -07003342 if (mTargetCell[0] < 0 || mTargetCell[1] < 0) {
3343 mDragTargetLayout.revertTempState();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003344 } else {
3345 setDragMode(DRAG_MODE_REORDER);
Adam Cohen19f37922012-03-21 11:59:11 -07003346 }
Adam Cohen482ed822012-03-02 14:15:13 -08003347
Adam Cohen482ed822012-03-02 14:15:13 -08003348 boolean resize = resultSpan[0] != spanX || resultSpan[1] != spanY;
3349 mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
3350 (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
3351 mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], resize,
3352 dragView.getDragVisualizeOffset(), dragView.getDragRegion());
Adam Cohen19072da2011-05-31 14:30:45 -07003353 }
3354 }
3355
Winson Chunga34abf82010-11-12 12:10:35 -08003356 @Override
Adam Cohen7d30a372013-07-01 17:03:59 -07003357 public void getHitRectRelativeToDragLayer(Rect outRect) {
Winson Chunga34abf82010-11-12 12:10:35 -08003358 // We want the workspace to have the whole area of the display (it will find the correct
3359 // cell layout to drop to in the existing drag/drop logic.
Adam Cohen7d30a372013-07-01 17:03:59 -07003360 mLauncher.getDragLayer().getDescendantRectRelativeToSelf(this, outRect);
Winson Chunga34abf82010-11-12 12:10:35 -08003361 }
3362
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07003363 /**
3364 * Add the item specified by dragInfo to the given layout.
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07003365 * @return true if successful
3366 */
Adam Cohen120980b2010-12-08 11:05:37 -08003367 public boolean addExternalItemToScreen(ItemInfo dragInfo, CellLayout layout) {
3368 if (layout.findCellForSpan(mTempEstimate, dragInfo.spanX, dragInfo.spanY)) {
Patrick Dubroybbaa75c2011-03-08 18:47:40 -08003369 onDropExternal(dragInfo.dropPos, (ItemInfo) dragInfo, (CellLayout) layout, false);
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07003370 return true;
3371 }
Winson Chung93eef082012-03-23 15:59:27 -07003372 mLauncher.showOutOfSpaceMessage(mLauncher.isHotseatLayout(layout));
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07003373 return false;
3374 }
3375
Adam Cohend5e42732011-03-28 17:33:39 -07003376 private void onDropExternal(int[] touchXY, Object dragInfo,
3377 CellLayout cellLayout, boolean insertAtFirst) {
Adam Cohene3e27a82011-04-15 12:07:39 -07003378 onDropExternal(touchXY, dragInfo, cellLayout, insertAtFirst, null);
Adam Cohend5e42732011-03-28 17:33:39 -07003379 }
3380
Adam Cohen120980b2010-12-08 11:05:37 -08003381 /**
3382 * Drop an item that didn't originate on one of the workspace screens.
3383 * It may have come from Launcher (e.g. from all apps or customize), or it may have
3384 * come from another app altogether.
3385 *
3386 * NOTE: This can also be called when we are outside of a drag event, when we want
3387 * to add an item to one of the workspace screens.
3388 */
Winson Chung557d6ed2011-07-08 15:34:52 -07003389 private void onDropExternal(final int[] touchXY, final Object dragInfo,
3390 final CellLayout cellLayout, boolean insertAtFirst, DragObject d) {
3391 final Runnable exitSpringLoadedRunnable = new Runnable() {
3392 @Override
3393 public void run() {
Adam Cohen689ff162014-05-08 17:27:56 -07003394 mLauncher.exitSpringLoadedDragModeDelayed(true,
3395 Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
Winson Chung557d6ed2011-07-08 15:34:52 -07003396 }
3397 };
Adam Cohenb7e16182011-07-15 17:55:02 -07003398
3399 ItemInfo info = (ItemInfo) dragInfo;
3400 int spanX = info.spanX;
3401 int spanY = info.spanY;
3402 if (mDragInfo != null) {
3403 spanX = mDragInfo.spanX;
3404 spanY = mDragInfo.spanY;
3405 }
3406
Winson Chung3d503fb2011-07-13 17:25:49 -07003407 final long container = mLauncher.isHotseatLayout(cellLayout) ?
3408 LauncherSettings.Favorites.CONTAINER_HOTSEAT :
3409 LauncherSettings.Favorites.CONTAINER_DESKTOP;
Adam Cohendcd297f2013-06-18 13:13:40 -07003410 final long screenId = getIdForScreen(cellLayout);
3411 if (!mLauncher.isHotseatLayout(cellLayout)
3412 && screenId != getScreenIdForPageIndex(mCurrentPage)
Winson Chung3d503fb2011-07-13 17:25:49 -07003413 && mState != State.SPRING_LOADED) {
Adam Cohendcd297f2013-06-18 13:13:40 -07003414 snapToScreenId(screenId, null);
Adam Cohen76078c42011-06-09 15:06:52 -07003415 }
Adam Cohenb7e16182011-07-15 17:55:02 -07003416
3417 if (info instanceof PendingAddItemInfo) {
3418 final PendingAddItemInfo pendingInfo = (PendingAddItemInfo) dragInfo;
3419
Adam Cohen558baaf2011-08-15 15:22:57 -07003420 boolean findNearestVacantCell = true;
3421 if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
3422 mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
3423 cellLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08003424 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
3425 mDragViewVisualCenter[1], mTargetCell);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003426 if (willCreateUserFolder((ItemInfo) d.dragInfo, cellLayout, mTargetCell,
Adam Cohen482ed822012-03-02 14:15:13 -08003427 distance, true) || willAddToExistingUserFolder((ItemInfo) d.dragInfo,
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003428 cellLayout, mTargetCell, distance)) {
Adam Cohen558baaf2011-08-15 15:22:57 -07003429 findNearestVacantCell = false;
3430 }
3431 }
Adam Cohen482ed822012-03-02 14:15:13 -08003432
Adam Cohend41fbf52012-02-16 23:53:59 -08003433 final ItemInfo item = (ItemInfo) d.dragInfo;
Adam Cohenaaa5c212012-10-05 18:14:31 -07003434 boolean updateWidgetSize = false;
Adam Cohen558baaf2011-08-15 15:22:57 -07003435 if (findNearestVacantCell) {
Adam Cohen482ed822012-03-02 14:15:13 -08003436 int minSpanX = item.spanX;
3437 int minSpanY = item.spanY;
3438 if (item.minSpanX > 0 && item.minSpanY > 0) {
3439 minSpanX = item.minSpanX;
3440 minSpanY = item.minSpanY;
3441 }
Adam Cohend41fbf52012-02-16 23:53:59 -08003442 int[] resultSpan = new int[2];
Adam Cohenfa3c58f2013-12-06 16:10:55 -08003443 mTargetCell = cellLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08003444 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, info.spanX, info.spanY,
3445 null, mTargetCell, resultSpan, CellLayout.MODE_ON_DROP_EXTERNAL);
Adam Cohen9e05a5e2012-09-10 15:53:09 -07003446
3447 if (resultSpan[0] != item.spanX || resultSpan[1] != item.spanY) {
3448 updateWidgetSize = true;
3449 }
Adam Cohend41fbf52012-02-16 23:53:59 -08003450 item.spanX = resultSpan[0];
3451 item.spanY = resultSpan[1];
Adam Cohen558baaf2011-08-15 15:22:57 -07003452 }
3453
Adam Cohenb7e16182011-07-15 17:55:02 -07003454 Runnable onAnimationCompleteRunnable = new Runnable() {
Winson Chung557d6ed2011-07-08 15:34:52 -07003455 @Override
3456 public void run() {
Adam Cohen689ff162014-05-08 17:27:56 -07003457 // Normally removeExtraEmptyScreen is called in Workspace#onDragEnd, but when
3458 // adding an item that may not be dropped right away (due to a config activity)
3459 // we defer the removal until the activity returns.
3460 deferRemoveExtraEmptyScreen();
3461
Winson Chung557d6ed2011-07-08 15:34:52 -07003462 // When dragging and dropping from customization tray, we deal with creating
3463 // widgets/shortcuts/folders in a slightly different way
Sunny Goyal71b5c0b2015-01-08 16:59:04 -08003464 mLauncher.addPendingItem(pendingInfo, container, screenId, mTargetCell,
3465 item.spanX, item.spanY);
Winson Chung557d6ed2011-07-08 15:34:52 -07003466 }
Adam Cohenb7e16182011-07-15 17:55:02 -07003467 };
Adam Cohen59400422014-03-05 18:07:04 -08003468 boolean isWidget = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
3469 || pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
3470
3471 View finalView = isWidget ? ((PendingAddWidgetInfo) pendingInfo).boundWidget : null;
Adam Cohen9e05a5e2012-09-10 15:53:09 -07003472
3473 if (finalView instanceof AppWidgetHostView && updateWidgetSize) {
3474 AppWidgetHostView awhv = (AppWidgetHostView) finalView;
3475 AppWidgetResizeFrame.updateWidgetSizeRanges(awhv, mLauncher, item.spanX,
3476 item.spanY);
3477 }
3478
Adam Cohend41fbf52012-02-16 23:53:59 -08003479 int animationStyle = ANIMATE_INTO_POSITION_AND_DISAPPEAR;
Adam Cohen59400422014-03-05 18:07:04 -08003480 if (isWidget && ((PendingAddWidgetInfo) pendingInfo).info != null &&
Adam Cohend41fbf52012-02-16 23:53:59 -08003481 ((PendingAddWidgetInfo) pendingInfo).info.configure != null) {
3482 animationStyle = ANIMATE_INTO_POSITION_AND_REMAIN;
3483 }
3484 animateWidgetDrop(info, cellLayout, d.dragView, onAnimationCompleteRunnable,
3485 animationStyle, finalView, true);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07003486 } else {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003487 // This is for other drag/drop cases, like dragging from All Apps
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003488 View view = null;
3489
3490 switch (info.itemType) {
3491 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
3492 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
Michael Jurkaeadbfc52013-09-04 00:45:37 +02003493 if (info.container == NO_ID && info instanceof AppInfo) {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003494 // Came from all apps -- make a copy
Sunny Goyal71b5c0b2015-01-08 16:59:04 -08003495 info = ((AppInfo) info).makeShortcut();
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003496 }
Sunny Goyaldfaccf62015-05-11 16:30:44 -07003497 view = mLauncher.createShortcut(cellLayout, (ShortcutInfo) info);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003498 break;
Adam Cohendf2cc412011-04-27 16:56:57 -07003499 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
Adam Cohenc0dcf592011-06-01 15:30:43 -07003500 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, cellLayout,
3501 (FolderInfo) info, mIconCache);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003502 break;
3503 default:
3504 throw new IllegalStateException("Unknown item type: " + info.itemType);
3505 }
3506
Adam Cohenc0dcf592011-06-01 15:30:43 -07003507 // First we find the cell nearest to point at which the item is
3508 // dropped, without any consideration to whether there is an item there.
3509 if (touchXY != null) {
3510 mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
3511 cellLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08003512 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
3513 mDragViewVisualCenter[1], mTargetCell);
Winson Chung557d6ed2011-07-08 15:34:52 -07003514 d.postAnimationRunnable = exitSpringLoadedRunnable;
Adam Cohen482ed822012-03-02 14:15:13 -08003515 if (createUserFolderIfNecessary(view, container, cellLayout, mTargetCell, distance,
3516 true, d.dragView, d.postAnimationRunnable)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07003517 return;
3518 }
Adam Cohen482ed822012-03-02 14:15:13 -08003519 if (addToExistingFolderIfNecessary(view, cellLayout, mTargetCell, distance, d,
3520 true)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07003521 return;
3522 }
Adam Cohen10b17372011-04-15 14:21:25 -07003523 }
3524
Michael Jurkac4e772e2011-02-10 13:32:01 -08003525 if (touchXY != null) {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003526 // when dragging and dropping, just find the closest free spot
Adam Cohenfa3c58f2013-12-06 16:10:55 -08003527 mTargetCell = cellLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08003528 (int) mDragViewVisualCenter[1], 1, 1, 1, 1,
Adam Cohenea889a22012-03-27 16:45:39 -07003529 null, mTargetCell, null, CellLayout.MODE_ON_DROP_EXTERNAL);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003530 } else {
3531 cellLayout.findCellForSpan(mTargetCell, 1, 1);
3532 }
Sunny Goyal95abbb32014-08-04 10:53:22 -07003533 // Add the item to DB before adding to screen ensures that the container and other
3534 // values of the info is properly updated.
3535 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId,
3536 mTargetCell[0], mTargetCell[1]);
3537
Adam Cohendcd297f2013-06-18 13:13:40 -07003538 addInScreen(view, container, screenId, mTargetCell[0], mTargetCell[1], info.spanX,
Winson Chung3d503fb2011-07-13 17:25:49 -07003539 info.spanY, insertAtFirst);
Adam Cohen716b51e2011-06-30 12:09:54 -07003540 cellLayout.onDropChild(view);
Michael Jurkaa52570f2012-03-20 03:18:20 -07003541 cellLayout.getShortcutsAndWidgets().measureChild(view);
Adam Cohend5e42732011-03-28 17:33:39 -07003542
Adam Cohen3e8f8112011-07-02 18:03:00 -07003543 if (d.dragView != null) {
Adam Cohen4b285c52011-07-21 14:24:06 -07003544 // We wrap the animation call in the temporary set and reset of the current
3545 // cellLayout to its final transform -- this means we animate the drag view to
3546 // the correct final location.
3547 setFinalTransitionTransform(cellLayout);
Winson Chung557d6ed2011-07-08 15:34:52 -07003548 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view,
Adam Cohen28f852a2013-10-15 14:34:05 -07003549 exitSpringLoadedRunnable, this);
Adam Cohen4b285c52011-07-21 14:24:06 -07003550 resetTransitionTransform(cellLayout);
Adam Cohen3e8f8112011-07-02 18:03:00 -07003551 }
Joe Onorato00acb122009-08-04 16:04:30 -04003552 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003553 }
Winson Chungaafa03c2010-06-11 17:34:16 -07003554
Adam Cohend41fbf52012-02-16 23:53:59 -08003555 public Bitmap createWidgetBitmap(ItemInfo widgetInfo, View layout) {
Sunny Goyal5b0e6692015-03-19 14:31:19 -07003556 int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(widgetInfo, false);
Adam Cohend41fbf52012-02-16 23:53:59 -08003557 int visibility = layout.getVisibility();
Adam Cohened66b2b2012-01-23 17:28:51 -08003558 layout.setVisibility(VISIBLE);
3559
3560 int width = MeasureSpec.makeMeasureSpec(unScaledSize[0], MeasureSpec.EXACTLY);
3561 int height = MeasureSpec.makeMeasureSpec(unScaledSize[1], MeasureSpec.EXACTLY);
3562 Bitmap b = Bitmap.createBitmap(unScaledSize[0], unScaledSize[1],
3563 Bitmap.Config.ARGB_8888);
Sunny Goyal508da152014-08-14 10:53:27 -07003564 mCanvas.setBitmap(b);
Adam Cohened66b2b2012-01-23 17:28:51 -08003565
3566 layout.measure(width, height);
3567 layout.layout(0, 0, unScaledSize[0], unScaledSize[1]);
Sunny Goyal508da152014-08-14 10:53:27 -07003568 layout.draw(mCanvas);
3569 mCanvas.setBitmap(null);
Adam Cohend41fbf52012-02-16 23:53:59 -08003570 layout.setVisibility(visibility);
Adam Cohened66b2b2012-01-23 17:28:51 -08003571 return b;
3572 }
3573
Adam Cohend41fbf52012-02-16 23:53:59 -08003574 private void getFinalPositionForDropAnimation(int[] loc, float[] scaleXY,
Adam Cohen9d5b7d82012-05-09 18:00:44 -07003575 DragView dragView, CellLayout layout, ItemInfo info, int[] targetCell,
Michael Jurkaa2fe7862012-05-29 05:58:17 -07003576 boolean external, boolean scale) {
Adam Cohened66b2b2012-01-23 17:28:51 -08003577 // Now we animate the dragView, (ie. the widget or shortcut preview) into its final
3578 // location and size on the home screen.
Adam Cohend41fbf52012-02-16 23:53:59 -08003579 int spanX = info.spanX;
3580 int spanY = info.spanY;
Adam Cohened66b2b2012-01-23 17:28:51 -08003581
Adam Cohend41fbf52012-02-16 23:53:59 -08003582 Rect r = estimateItemPosition(layout, info, targetCell[0], targetCell[1], spanX, spanY);
3583 loc[0] = r.left;
3584 loc[1] = r.top;
3585
3586 setFinalTransitionTransform(layout);
3587 float cellLayoutScale =
Adam Cohen7d30a372013-07-01 17:03:59 -07003588 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(layout, loc, true);
Adam Cohend41fbf52012-02-16 23:53:59 -08003589 resetTransitionTransform(layout);
Michael Jurkaa2fe7862012-05-29 05:58:17 -07003590
3591 float dragViewScaleX;
3592 float dragViewScaleY;
3593 if (scale) {
3594 dragViewScaleX = (1.0f * r.width()) / dragView.getMeasuredWidth();
3595 dragViewScaleY = (1.0f * r.height()) / dragView.getMeasuredHeight();
3596 } else {
3597 dragViewScaleX = 1f;
3598 dragViewScaleY = 1f;
3599 }
Adam Cohend41fbf52012-02-16 23:53:59 -08003600
Adam Cohened66b2b2012-01-23 17:28:51 -08003601 // The animation will scale the dragView about its center, so we need to center about
3602 // the final location.
3603 loc[0] -= (dragView.getMeasuredWidth() - cellLayoutScale * r.width()) / 2;
3604 loc[1] -= (dragView.getMeasuredHeight() - cellLayoutScale * r.height()) / 2;
3605
Adam Cohend41fbf52012-02-16 23:53:59 -08003606 scaleXY[0] = dragViewScaleX * cellLayoutScale;
3607 scaleXY[1] = dragViewScaleY * cellLayoutScale;
3608 }
3609
3610 public void animateWidgetDrop(ItemInfo info, CellLayout cellLayout, DragView dragView,
3611 final Runnable onCompleteRunnable, int animationType, final View finalView,
3612 boolean external) {
3613 Rect from = new Rect();
3614 mLauncher.getDragLayer().getViewRectRelativeToSelf(dragView, from);
3615
3616 int[] finalPos = new int[2];
3617 float scaleXY[] = new float[2];
Michael Jurkaa2fe7862012-05-29 05:58:17 -07003618 boolean scalePreview = !(info instanceof PendingAddShortcutInfo);
Adam Cohend41fbf52012-02-16 23:53:59 -08003619 getFinalPositionForDropAnimation(finalPos, scaleXY, dragView, cellLayout, info, mTargetCell,
Michael Jurkaa2fe7862012-05-29 05:58:17 -07003620 external, scalePreview);
Adam Cohened66b2b2012-01-23 17:28:51 -08003621
3622 Resources res = mLauncher.getResources();
Adam Cohenad4e15c2013-10-17 16:21:35 -07003623 final int duration = res.getInteger(R.integer.config_dropAnimMaxDuration) - 200;
Adam Cohened66b2b2012-01-23 17:28:51 -08003624
Adam Cohend41fbf52012-02-16 23:53:59 -08003625 // In the case where we've prebound the widget, we remove it from the DragLayer
3626 if (finalView instanceof AppWidgetHostView && external) {
3627 mLauncher.getDragLayer().removeView(finalView);
3628 }
Adam Cohen59400422014-03-05 18:07:04 -08003629
3630 boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET ||
3631 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
Adam Cohend41fbf52012-02-16 23:53:59 -08003632 if ((animationType == ANIMATE_INTO_POSITION_AND_RESIZE || external) && finalView != null) {
3633 Bitmap crossFadeBitmap = createWidgetBitmap(info, finalView);
Adam Cohened66b2b2012-01-23 17:28:51 -08003634 dragView.setCrossFadeBitmap(crossFadeBitmap);
3635 dragView.crossFade((int) (duration * 0.8f));
Adam Cohen59400422014-03-05 18:07:04 -08003636 } else if (isWidget && external) {
Adam Cohend41fbf52012-02-16 23:53:59 -08003637 scaleXY[0] = scaleXY[1] = Math.min(scaleXY[0], scaleXY[1]);
Adam Cohened66b2b2012-01-23 17:28:51 -08003638 }
3639
Adam Cohend41fbf52012-02-16 23:53:59 -08003640 DragLayer dragLayer = mLauncher.getDragLayer();
Winson Chung7bd1bbb2012-02-13 18:29:29 -08003641 if (animationType == CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION) {
Adam Cohend41fbf52012-02-16 23:53:59 -08003642 mLauncher.getDragLayer().animateViewIntoPosition(dragView, finalPos, 0f, 0.1f, 0.1f,
Adam Cohened66b2b2012-01-23 17:28:51 -08003643 DragLayer.ANIMATION_END_DISAPPEAR, onCompleteRunnable, duration);
3644 } else {
Adam Cohend41fbf52012-02-16 23:53:59 -08003645 int endStyle;
3646 if (animationType == ANIMATE_INTO_POSITION_AND_REMAIN) {
3647 endStyle = DragLayer.ANIMATION_END_REMAIN_VISIBLE;
3648 } else {
3649 endStyle = DragLayer.ANIMATION_END_DISAPPEAR;;
3650 }
3651
3652 Runnable onComplete = new Runnable() {
3653 @Override
3654 public void run() {
3655 if (finalView != null) {
3656 finalView.setVisibility(VISIBLE);
3657 }
3658 if (onCompleteRunnable != null) {
3659 onCompleteRunnable.run();
3660 }
3661 }
3662 };
3663 dragLayer.animateViewIntoPosition(dragView, from.left, from.top, finalPos[0],
3664 finalPos[1], 1, 1, 1, scaleXY[0], scaleXY[1], onComplete, endStyle,
3665 duration, this);
Adam Cohened66b2b2012-01-23 17:28:51 -08003666 }
3667 }
3668
Adam Cohen4b285c52011-07-21 14:24:06 -07003669 public void setFinalTransitionTransform(CellLayout layout) {
3670 if (isSwitchingState()) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003671 mCurrentScale = getScaleX();
Winson Chungdc61c4d2015-04-20 18:26:57 -07003672 setScaleX(mStateTransitionAnimation.getFinalScale());
3673 setScaleY(mStateTransitionAnimation.getFinalScale());
Adam Cohen4b285c52011-07-21 14:24:06 -07003674 }
3675 }
3676 public void resetTransitionTransform(CellLayout layout) {
3677 if (isSwitchingState()) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003678 setScaleX(mCurrentScale);
3679 setScaleY(mCurrentScale);
Adam Cohen4b285c52011-07-21 14:24:06 -07003680 }
3681 }
3682
Jeff Sharkey70864282009-04-07 21:08:40 -07003683 /**
3684 * Return the current {@link CellLayout}, correctly picking the destination
3685 * screen while a scroll is in progress.
3686 */
Patrick Dubroy5f445422011-02-18 14:35:21 -08003687 public CellLayout getCurrentDropLayout() {
Winson Chung360e63f2012-04-27 13:48:05 -07003688 return (CellLayout) getChildAt(getNextPage());
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003689 }
3690
Jeff Sharkey70864282009-04-07 21:08:40 -07003691 /**
Michael Jurka0280c3b2010-09-17 15:00:07 -07003692 * Return the current CellInfo describing our current drag; this method exists
3693 * so that Launcher can sync this object with the correct info when the activity is created/
3694 * destroyed
3695 *
3696 */
3697 public CellLayout.CellInfo getDragInfo() {
3698 return mDragInfo;
3699 }
3700
Winson Chung9b9fb962013-11-15 15:39:34 -08003701 public int getCurrentPageOffsetFromCustomContent() {
Adam Cohen21cd0022013-10-09 18:57:02 -07003702 return getNextPage() - numCustomPages();
3703 }
3704
Michael Jurka0280c3b2010-09-17 15:00:07 -07003705 /**
Jeff Sharkey70864282009-04-07 21:08:40 -07003706 * Calculate the nearest cell where the given object would be dropped.
Adam Cohene3e27a82011-04-15 12:07:39 -07003707 *
3708 * pixelX and pixelY should be in the coordinate system of layout
Jeff Sharkey70864282009-04-07 21:08:40 -07003709 */
Adam Cohen091440a2015-03-18 14:16:05 -07003710 @Thunk int[] findNearestArea(int pixelX, int pixelY,
Adam Cohene3e27a82011-04-15 12:07:39 -07003711 int spanX, int spanY, CellLayout layout, int[] recycle) {
Adam Cohendf035382011-04-11 17:22:04 -07003712 return layout.findNearestArea(
Adam Cohene3e27a82011-04-15 12:07:39 -07003713 pixelX, pixelY, spanX, spanY, recycle);
Adam Cohendf035382011-04-11 17:22:04 -07003714 }
3715
Adam Cohencff6af82011-09-13 14:51:53 -07003716 void setup(DragController dragController) {
Michael Jurkac2f7f472010-12-14 15:34:42 -08003717 mSpringLoadedDragController = new SpringLoadedDragController(mLauncher);
Joe Onorato00acb122009-08-04 16:04:30 -04003718 mDragController = dragController;
Michael Jurkad74c9842011-07-10 12:44:21 -07003719
Michael Jurkad74c9842011-07-10 12:44:21 -07003720 // hardware layers on children are enabled on startup, but should be disabled until
3721 // needed
Michael Jurka3a0469d2012-06-21 09:38:41 -07003722 updateChildrenLayersEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003723 }
3724
Patrick Dubroye3887cc2011-01-20 10:43:40 -08003725 /**
3726 * Called at the end of a drag which originated on the workspace.
3727 */
Michael Jurka1e2f4652013-07-08 18:03:46 -07003728 public void onDropCompleted(final View target, final DragObject d,
3729 final boolean isFlingToDelete, final boolean success) {
3730 if (mDeferDropAfterUninstall) {
3731 mDeferredAction = new Runnable() {
Adam Cohenad4e15c2013-10-17 16:21:35 -07003732 public void run() {
3733 onDropCompleted(target, d, isFlingToDelete, success);
3734 mDeferredAction = null;
3735 }
3736 };
Michael Jurka1e2f4652013-07-08 18:03:46 -07003737 return;
3738 }
3739
3740 boolean beingCalledAfterUninstall = mDeferredAction != null;
3741
3742 if (success && !(beingCalledAfterUninstall && !mUninstallSuccessful)) {
Sunny Goyal95deb3a2015-03-05 10:27:01 -08003743 if (target != this && mDragInfo != null) {
Sunny Goyal71b5c0b2015-01-08 16:59:04 -08003744 removeWorkspaceItem(mDragInfo.cell);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003745 }
Patrick Dubroyce34a972010-10-19 10:34:32 -07003746 } else if (mDragInfo != null) {
Sunny Goyal92820592015-03-02 11:31:35 -08003747 final CellLayout cellLayout = mLauncher.getCellLayout(
3748 mDragInfo.container, mDragInfo.screenId);
Jorim Jaggieedb00a2014-01-13 13:45:07 -08003749 if (cellLayout != null) {
3750 cellLayout.onDropChild(mDragInfo.cell);
Sunny Goyal92820592015-03-02 11:31:35 -08003751 } else if (LauncherAppState.isDogfoodBuild()) {
3752 throw new RuntimeException("Invalid state: cellLayout == null in "
3753 + "Workspace#onDropCompleted. Please file a bug. ");
3754 };
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003755 }
Michael Jurka1e2f4652013-07-08 18:03:46 -07003756 if ((d.cancelled || (beingCalledAfterUninstall && !mUninstallSuccessful))
3757 && mDragInfo.cell != null) {
3758 mDragInfo.cell.setVisibility(VISIBLE);
Adam Cohen36cc09b2011-09-29 17:33:15 -07003759 }
Joe Onorato4be866d2010-10-10 11:26:02 -07003760 mDragOutline = null;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003761 mDragInfo = null;
3762 }
3763
Hyunyoung Song31178b82015-02-24 14:12:51 -08003764 /**
3765 * For opposite operation. See {@link #addInScreen}.
3766 */
Sunny Goyal71b5c0b2015-01-08 16:59:04 -08003767 public void removeWorkspaceItem(View v) {
3768 CellLayout parentCell = getParentCellLayoutForView(v);
3769 if (parentCell != null) {
3770 parentCell.removeView(v);
3771 } else if (LauncherAppState.isDogfoodBuild()) {
3772 throw new NullPointerException("mDragInfo.cell has null parent");
3773 }
3774 if (v instanceof DropTarget) {
3775 mDragController.removeDropTarget((DropTarget) v);
3776 }
3777 }
3778
Sunny Goyalfa401a12015-04-10 13:45:42 -07003779 @Override
Michael Jurka1e2f4652013-07-08 18:03:46 -07003780 public void deferCompleteDropAfterUninstallActivity() {
3781 mDeferDropAfterUninstall = true;
3782 }
3783
3784 /// maybe move this into a smaller part
Sunny Goyalfa401a12015-04-10 13:45:42 -07003785 @Override
Michael Jurka1e2f4652013-07-08 18:03:46 -07003786 public void onUninstallActivityReturned(boolean success) {
3787 mDeferDropAfterUninstall = false;
3788 mUninstallSuccessful = success;
3789 if (mDeferredAction != null) {
3790 mDeferredAction.run();
3791 }
3792 }
3793
Adam Cohenea889a22012-03-27 16:45:39 -07003794 void updateItemLocationsInDatabase(CellLayout cl) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07003795 int count = cl.getShortcutsAndWidgets().getChildCount();
Adam Cohend3461712012-03-29 17:25:17 -07003796
Adam Cohendcd297f2013-06-18 13:13:40 -07003797 long screenId = getIdForScreen(cl);
Adam Cohend3461712012-03-29 17:25:17 -07003798 int container = Favorites.CONTAINER_DESKTOP;
3799
3800 if (mLauncher.isHotseatLayout(cl)) {
Adam Cohendcd297f2013-06-18 13:13:40 -07003801 screenId = -1;
Adam Cohend3461712012-03-29 17:25:17 -07003802 container = Favorites.CONTAINER_HOTSEAT;
3803 }
3804
Adam Cohen482ed822012-03-02 14:15:13 -08003805 for (int i = 0; i < count; i++) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07003806 View v = cl.getShortcutsAndWidgets().getChildAt(i);
Adam Cohen482ed822012-03-02 14:15:13 -08003807 ItemInfo info = (ItemInfo) v.getTag();
Adam Cohen2acce882012-03-28 19:03:19 -07003808 // Null check required as the AllApps button doesn't have an item info
Adam Cohen487f7dd2012-06-28 18:12:10 -07003809 if (info != null && info.requiresDbUpdate) {
3810 info.requiresDbUpdate = false;
Adam Cohendcd297f2013-06-18 13:13:40 -07003811 LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId, info.cellX,
Adam Cohenbebf0422012-04-11 18:06:28 -07003812 info.cellY, info.spanX, info.spanY);
Adam Cohen2acce882012-03-28 19:03:19 -07003813 }
Adam Cohen482ed822012-03-02 14:15:13 -08003814 }
3815 }
3816
Adam Cohene25af792013-06-06 23:08:25 -07003817 void saveWorkspaceToDb() {
3818 saveWorkspaceScreenToDb((CellLayout) mLauncher.getHotseat().getLayout());
3819 int count = getChildCount();
3820 for (int i = 0; i < count; i++) {
3821 CellLayout cl = (CellLayout) getChildAt(i);
3822 saveWorkspaceScreenToDb(cl);
3823 }
3824 }
3825
3826 void saveWorkspaceScreenToDb(CellLayout cl) {
3827 int count = cl.getShortcutsAndWidgets().getChildCount();
3828
Adam Cohendcd297f2013-06-18 13:13:40 -07003829 long screenId = getIdForScreen(cl);
Adam Cohene25af792013-06-06 23:08:25 -07003830 int container = Favorites.CONTAINER_DESKTOP;
3831
Winson Chungf70696d2013-06-25 16:19:53 -07003832 Hotseat hotseat = mLauncher.getHotseat();
Adam Cohene25af792013-06-06 23:08:25 -07003833 if (mLauncher.isHotseatLayout(cl)) {
Adam Cohendcd297f2013-06-18 13:13:40 -07003834 screenId = -1;
Adam Cohene25af792013-06-06 23:08:25 -07003835 container = Favorites.CONTAINER_HOTSEAT;
3836 }
3837
3838 for (int i = 0; i < count; i++) {
3839 View v = cl.getShortcutsAndWidgets().getChildAt(i);
3840 ItemInfo info = (ItemInfo) v.getTag();
3841 // Null check required as the AllApps button doesn't have an item info
3842 if (info != null) {
Winson Chungf70696d2013-06-25 16:19:53 -07003843 int cellX = info.cellX;
3844 int cellY = info.cellY;
3845 if (container == Favorites.CONTAINER_HOTSEAT) {
3846 cellX = hotseat.getCellXFromOrder((int) info.screenId);
3847 cellY = hotseat.getCellYFromOrder((int) info.screenId);
3848 }
Sunny Goyal1d4a2df2015-03-30 11:11:46 -07003849 LauncherModel.addItemToDatabase(mLauncher, info, container, screenId, cellX, cellY);
Adam Cohene25af792013-06-06 23:08:25 -07003850 }
3851 if (v instanceof FolderIcon) {
3852 FolderIcon fi = (FolderIcon) v;
3853 fi.getFolder().addItemLocationsInDatabase();
3854 }
3855 }
3856 }
3857
Winson Chunga48487a2012-03-20 16:19:37 -07003858 @Override
Winson Chungeeb5bbc2013-11-13 15:47:05 -08003859 public float getIntrinsicIconScaleFactor() {
3860 return 1f;
3861 }
3862
3863 @Override
Winson Chung043f2af2012-03-01 16:09:54 -08003864 public boolean supportsFlingToDelete() {
3865 return true;
3866 }
3867
Winson Chunga48487a2012-03-20 16:19:37 -07003868 @Override
Mathew Inwood1eeb3fc2013-11-25 17:01:34 +00003869 public boolean supportsAppInfoDropTarget() {
3870 return false;
3871 }
3872
3873 @Override
3874 public boolean supportsDeleteDropTarget() {
3875 return true;
3876 }
3877
3878 @Override
Sunny Goyalddec7342015-04-29 18:12:37 -07003879 public void onFlingToDelete(DragObject d, PointF vec) {
Winson Chunga48487a2012-03-20 16:19:37 -07003880 // Do nothing
3881 }
3882
3883 @Override
3884 public void onFlingToDeleteCompleted() {
3885 // Do nothing
3886 }
3887
Michael Jurka0280c3b2010-09-17 15:00:07 -07003888 public boolean isDropEnabled() {
3889 return true;
3890 }
3891
Michael Jurka0142d492010-08-25 17:46:15 -07003892 @Override
Adam Cohen1462de32012-07-24 22:34:36 -07003893 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
3894 // We don't dispatch restoreInstanceState to our children using this code path.
Dave Hawkeya8881582013-09-17 15:55:33 -06003895 // Some pages will be restored immediately as their items are bound immediately, and
Adam Cohen1462de32012-07-24 22:34:36 -07003896 // others we will need to wait until after their items are bound.
3897 mSavedStates = container;
3898 }
3899
3900 public void restoreInstanceStateForChild(int child) {
3901 if (mSavedStates != null) {
3902 mRestoredPages.add(child);
3903 CellLayout cl = (CellLayout) getChildAt(child);
Adam Cohenb0ee0812013-12-03 10:51:45 -08003904 if (cl != null) {
3905 cl.restoreInstanceState(mSavedStates);
3906 }
Adam Cohen1462de32012-07-24 22:34:36 -07003907 }
3908 }
3909
3910 public void restoreInstanceStateForRemainingPages() {
3911 int count = getChildCount();
3912 for (int i = 0; i < count; i++) {
3913 if (!mRestoredPages.contains(i)) {
3914 restoreInstanceStateForChild(i);
3915 }
3916 }
3917 mRestoredPages.clear();
Winson Chungd8e596d2013-10-21 17:14:12 -07003918 mSavedStates = null;
Adam Cohen1462de32012-07-24 22:34:36 -07003919 }
3920
3921 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003922 public void scrollLeft() {
Adam Cohen6c5891a2014-07-09 23:53:15 -07003923 if (!workspaceInModalState() && !mIsSwitchingState) {
Michael Jurka0142d492010-08-25 17:46:15 -07003924 super.scrollLeft();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003925 }
Adam Cohen95bb8002011-07-03 23:40:28 -07003926 Folder openFolder = getOpenFolder();
3927 if (openFolder != null) {
3928 openFolder.completeDragExit();
3929 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003930 }
3931
Michael Jurka0142d492010-08-25 17:46:15 -07003932 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003933 public void scrollRight() {
Adam Cohen6c5891a2014-07-09 23:53:15 -07003934 if (!workspaceInModalState() && !mIsSwitchingState) {
Michael Jurka0142d492010-08-25 17:46:15 -07003935 super.scrollRight();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003936 }
Adam Cohen95bb8002011-07-03 23:40:28 -07003937 Folder openFolder = getOpenFolder();
3938 if (openFolder != null) {
3939 openFolder.completeDragExit();
3940 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003941 }
3942
Patrick Dubroy1262e362010-10-06 15:49:50 -07003943 @Override
Winson Chung3e0839e2011-10-03 15:15:18 -07003944 public boolean onEnterScrollArea(int x, int y, int direction) {
Winson Chung1c4cf4a2011-07-29 14:49:10 -07003945 // Ignore the scroll area if we are dragging over the hot seat
Daniel Sandlercc8befa2013-06-11 14:45:48 -04003946 boolean isPortrait = !LauncherAppState.isScreenLandscape(getContext());
Winson Chung10bfc6e2012-03-28 15:41:26 -07003947 if (mLauncher.getHotseat() != null && isPortrait) {
Winson Chung1c4cf4a2011-07-29 14:49:10 -07003948 Rect r = new Rect();
3949 mLauncher.getHotseat().getHitRect(r);
3950 if (r.contains(x, y)) {
Winson Chung3e0839e2011-10-03 15:15:18 -07003951 return false;
Winson Chung1c4cf4a2011-07-29 14:49:10 -07003952 }
3953 }
3954
Winson Chung3e0839e2011-10-03 15:15:18 -07003955 boolean result = false;
Adam Cohen6c5891a2014-07-09 23:53:15 -07003956 if (!workspaceInModalState() && !mIsSwitchingState && getOpenFolder() == null) {
Michael Jurkad718d6a2010-10-14 15:35:17 -07003957 mInScrollArea = true;
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08003958
Winson Chung360e63f2012-04-27 13:48:05 -07003959 final int page = getNextPage() +
Winson Chungaa15ffe2012-01-18 15:45:28 -08003960 (direction == DragController.SCROLL_LEFT ? -1 : 1);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003961 // We always want to exit the current layout to ensure parity of enter / exit
3962 setCurrentDropLayout(null);
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08003963
Winson Chungaa15ffe2012-01-18 15:45:28 -08003964 if (0 <= page && page < getChildCount()) {
Winson Chung482a5b62013-07-31 17:19:51 -07003965 // Ensure that we are not dragging over to the custom content screen
3966 if (getScreenIdForPageIndex(page) == CUSTOM_CONTENT_SCREEN_ID) {
3967 return false;
3968 }
3969
Winson Chungaa15ffe2012-01-18 15:45:28 -08003970 CellLayout layout = (CellLayout) getChildAt(page);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003971 setCurrentDragOverlappingLayout(layout);
Winson Chungc07918d2011-07-01 15:35:26 -07003972
3973 // Workspace is responsible for drawing the edge glow on adjacent pages,
3974 // so we need to redraw the workspace when this may have changed.
3975 invalidate();
Winson Chung3e0839e2011-10-03 15:15:18 -07003976 result = true;
Michael Jurkad718d6a2010-10-14 15:35:17 -07003977 }
Patrick Dubroy1262e362010-10-06 15:49:50 -07003978 }
Winson Chung3e0839e2011-10-03 15:15:18 -07003979 return result;
Patrick Dubroy1262e362010-10-06 15:49:50 -07003980 }
3981
3982 @Override
Winson Chung3e0839e2011-10-03 15:15:18 -07003983 public boolean onExitScrollArea() {
3984 boolean result = false;
Michael Jurkad718d6a2010-10-14 15:35:17 -07003985 if (mInScrollArea) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003986 invalidate();
3987 CellLayout layout = getCurrentDropLayout();
3988 setCurrentDropLayout(layout);
3989 setCurrentDragOverlappingLayout(layout);
3990
Adam Cohen8a18afc2011-12-13 15:57:01 -08003991 result = true;
Winson Chungc07918d2011-07-01 15:35:26 -07003992 mInScrollArea = false;
Patrick Dubroy1262e362010-10-06 15:49:50 -07003993 }
Winson Chung3e0839e2011-10-03 15:15:18 -07003994 return result;
Patrick Dubroy1262e362010-10-06 15:49:50 -07003995 }
3996
Winson Chungc07918d2011-07-01 15:35:26 -07003997 private void onResetScrollArea() {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003998 setCurrentDragOverlappingLayout(null);
Winson Chungc07918d2011-07-01 15:35:26 -07003999 mInScrollArea = false;
4000 }
4001
Winson Chung3d503fb2011-07-13 17:25:49 -07004002 /**
4003 * Returns a specific CellLayout
4004 */
4005 CellLayout getParentCellLayoutForView(View v) {
4006 ArrayList<CellLayout> layouts = getWorkspaceAndHotseatCellLayouts();
4007 for (CellLayout layout : layouts) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004008 if (layout.getShortcutsAndWidgets().indexOfChild(v) > -1) {
Winson Chung3d503fb2011-07-13 17:25:49 -07004009 return layout;
4010 }
4011 }
4012 return null;
4013 }
4014
4015 /**
4016 * Returns a list of all the CellLayouts in the workspace.
4017 */
4018 ArrayList<CellLayout> getWorkspaceAndHotseatCellLayouts() {
4019 ArrayList<CellLayout> layouts = new ArrayList<CellLayout>();
4020 int screenCount = getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004021 for (int screen = 0; screen < screenCount; screen++) {
Winson Chung3d503fb2011-07-13 17:25:49 -07004022 layouts.add(((CellLayout) getChildAt(screen)));
4023 }
4024 if (mLauncher.getHotseat() != null) {
4025 layouts.add(mLauncher.getHotseat().getLayout());
4026 }
4027 return layouts;
4028 }
4029
4030 /**
4031 * We should only use this to search for specific children. Do not use this method to modify
Michael Jurkaa52570f2012-03-20 03:18:20 -07004032 * ShortcutsAndWidgetsContainer directly. Includes ShortcutAndWidgetContainers from
4033 * the hotseat and workspace pages
Winson Chung3d503fb2011-07-13 17:25:49 -07004034 */
Michael Jurkaa52570f2012-03-20 03:18:20 -07004035 ArrayList<ShortcutAndWidgetContainer> getAllShortcutAndWidgetContainers() {
4036 ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
4037 new ArrayList<ShortcutAndWidgetContainer>();
Winson Chung3d503fb2011-07-13 17:25:49 -07004038 int screenCount = getChildCount();
4039 for (int screen = 0; screen < screenCount; screen++) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004040 childrenLayouts.add(((CellLayout) getChildAt(screen)).getShortcutsAndWidgets());
Winson Chung3d503fb2011-07-13 17:25:49 -07004041 }
4042 if (mLauncher.getHotseat() != null) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004043 childrenLayouts.add(mLauncher.getHotseat().getLayout().getShortcutsAndWidgets());
Winson Chung3d503fb2011-07-13 17:25:49 -07004044 }
4045 return childrenLayouts;
4046 }
4047
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004048 public Folder getFolderForTag(final Object tag) {
Sunny Goyalff572272014-07-23 13:58:07 -07004049 return (Folder) getFirstMatch(new ItemOperator() {
4050
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004051 @Override
4052 public boolean evaluate(ItemInfo info, View v, View parent) {
Sunny Goyalff572272014-07-23 13:58:07 -07004053 return (v instanceof Folder) && (((Folder) v).getInfo() == tag)
4054 && ((Folder) v).getInfo().opened;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004055 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004056 });
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004057 }
4058
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004059 public View getViewForTag(final Object tag) {
Sunny Goyalff572272014-07-23 13:58:07 -07004060 return getFirstMatch(new ItemOperator() {
4061
4062 @Override
4063 public boolean evaluate(ItemInfo info, View v, View parent) {
4064 return info == tag;
4065 }
4066 });
4067 }
4068
4069 public LauncherAppWidgetHostView getWidgetForAppWidgetId(final int appWidgetId) {
4070 return (LauncherAppWidgetHostView) getFirstMatch(new ItemOperator() {
4071
4072 @Override
4073 public boolean evaluate(ItemInfo info, View v, View parent) {
4074 return (info instanceof LauncherAppWidgetInfo) &&
4075 ((LauncherAppWidgetInfo) info).appWidgetId == appWidgetId;
4076 }
4077 });
4078 }
4079
4080 private View getFirstMatch(final ItemOperator operator) {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004081 final View[] value = new View[1];
Sunny Goyal651077b2014-06-30 14:15:31 -07004082 mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004083 @Override
4084 public boolean evaluate(ItemInfo info, View v, View parent) {
Sunny Goyalff572272014-07-23 13:58:07 -07004085 if (operator.evaluate(info, v, parent)) {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004086 value[0] = v;
4087 return true;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004088 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004089 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004090 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004091 });
4092 return value[0];
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004093 }
4094
Adam Cohendf035382011-04-11 17:22:04 -07004095 void clearDropTargets() {
Sunny Goyal651077b2014-06-30 14:15:31 -07004096 mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004097 @Override
4098 public boolean evaluate(ItemInfo info, View v, View parent) {
Adam Cohendf035382011-04-11 17:22:04 -07004099 if (v instanceof DropTarget) {
4100 mDragController.removeDropTarget((DropTarget) v);
4101 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004102 // not done, process all the shortcuts
4103 return false;
Adam Cohendf035382011-04-11 17:22:04 -07004104 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004105 });
Adam Cohendf035382011-04-11 17:22:04 -07004106 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004107
Sunny Goyal1a745e82014-10-02 15:58:31 -07004108 public void disableShortcutsByPackageName(final ArrayList<String> packages,
4109 final UserHandleCompat user, final int reason) {
4110 final HashSet<String> packageNames = new HashSet<String>();
4111 packageNames.addAll(packages);
4112
4113 mapOverItems(MAP_RECURSE, new ItemOperator() {
4114 @Override
4115 public boolean evaluate(ItemInfo info, View v, View parent) {
4116 if (info instanceof ShortcutInfo && v instanceof BubbleTextView) {
4117 ShortcutInfo shortcutInfo = (ShortcutInfo) info;
4118 ComponentName cn = shortcutInfo.getTargetComponent();
4119 if (user.equals(shortcutInfo.user) && cn != null
4120 && packageNames.contains(cn.getPackageName())) {
4121 shortcutInfo.isDisabled |= reason;
4122 BubbleTextView shortcut = (BubbleTextView) v;
Sunny Goyaldfaccf62015-05-11 16:30:44 -07004123 shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache);
Sunny Goyal1a745e82014-10-02 15:58:31 -07004124
4125 if (parent != null) {
4126 parent.invalidate();
4127 }
4128 }
4129 }
4130 // process all the shortcuts
4131 return false;
4132 }
4133 });
4134 }
4135
Winson Chung83892cc2013-05-01 16:53:33 -07004136 // Removes ALL items that match a given package name, this is usually called when a package
4137 // has been removed and we want to remove all components (widgets, shortcuts, apps) that
4138 // belong to that package.
Kenny Guyed131872014-04-30 03:02:21 +01004139 void removeItemsByPackageName(final ArrayList<String> packages, final UserHandleCompat user) {
Winson Chung64359a52013-07-08 17:17:08 -07004140 final HashSet<String> packageNames = new HashSet<String>();
Winson Chungcd810732012-06-18 16:45:43 -07004141 packageNames.addAll(packages);
Joe Onorato64e6be72010-03-05 15:05:52 -05004142
Winson Chung64359a52013-07-08 17:17:08 -07004143 // Filter out all the ItemInfos that this is going to affect
4144 final HashSet<ItemInfo> infos = new HashSet<ItemInfo>();
4145 final HashSet<ComponentName> cns = new HashSet<ComponentName>();
Winson Chung83892cc2013-05-01 16:53:33 -07004146 ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
4147 for (CellLayout layoutParent : cellLayouts) {
4148 ViewGroup layout = layoutParent.getShortcutsAndWidgets();
4149 int childCount = layout.getChildCount();
4150 for (int i = 0; i < childCount; ++i) {
4151 View view = layout.getChildAt(i);
Winson Chung64359a52013-07-08 17:17:08 -07004152 infos.add((ItemInfo) view.getTag());
Winson Chung83892cc2013-05-01 16:53:33 -07004153 }
4154 }
Winson Chung64359a52013-07-08 17:17:08 -07004155 LauncherModel.ItemInfoFilter filter = new LauncherModel.ItemInfoFilter() {
4156 @Override
4157 public boolean filterItem(ItemInfo parent, ItemInfo info,
4158 ComponentName cn) {
Kenny Guyed131872014-04-30 03:02:21 +01004159 if (packageNames.contains(cn.getPackageName())
4160 && info.user.equals(user)) {
Winson Chung64359a52013-07-08 17:17:08 -07004161 cns.add(cn);
4162 return true;
4163 }
4164 return false;
4165 }
4166 };
4167 LauncherModel.filterItemInfos(infos, filter);
Winson Chung83892cc2013-05-01 16:53:33 -07004168
Winson Chung64359a52013-07-08 17:17:08 -07004169 // Remove the affected components
Kenny Guyed131872014-04-30 03:02:21 +01004170 removeItemsByComponentName(cns, user);
Winson Chung83892cc2013-05-01 16:53:33 -07004171 }
4172
Sunny Goyal4390ace2014-10-13 11:33:11 -07004173 /**
4174 * Removes items that match the item info specified. When applications are removed
4175 * as a part of an update, this is called to ensure that other widgets and application
4176 * shortcuts are not removed.
4177 */
Kenny Guyed131872014-04-30 03:02:21 +01004178 void removeItemsByComponentName(final HashSet<ComponentName> componentNames,
4179 final UserHandleCompat user) {
Winson Chung3d503fb2011-07-13 17:25:49 -07004180 ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
4181 for (final CellLayout layoutParent: cellLayouts) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004182 final ViewGroup layout = layoutParent.getShortcutsAndWidgets();
Romain Guy574d20e2009-06-01 15:34:04 -07004183
Winson Chung64359a52013-07-08 17:17:08 -07004184 final HashMap<ItemInfo, View> children = new HashMap<ItemInfo, View>();
4185 for (int j = 0; j < layout.getChildCount(); j++) {
4186 final View view = layout.getChildAt(j);
4187 children.put((ItemInfo) view.getTag(), view);
4188 }
Winson Chungaafa03c2010-06-11 17:34:16 -07004189
Winson Chung64359a52013-07-08 17:17:08 -07004190 final ArrayList<View> childrenToRemove = new ArrayList<View>();
4191 final HashMap<FolderInfo, ArrayList<ShortcutInfo>> folderAppsToRemove =
4192 new HashMap<FolderInfo, ArrayList<ShortcutInfo>>();
4193 LauncherModel.ItemInfoFilter filter = new LauncherModel.ItemInfoFilter() {
4194 @Override
4195 public boolean filterItem(ItemInfo parent, ItemInfo info,
4196 ComponentName cn) {
4197 if (parent instanceof FolderInfo) {
Kenny Guyed131872014-04-30 03:02:21 +01004198 if (componentNames.contains(cn) && info.user.equals(user)) {
Winson Chung64359a52013-07-08 17:17:08 -07004199 FolderInfo folder = (FolderInfo) parent;
4200 ArrayList<ShortcutInfo> appsToRemove;
4201 if (folderAppsToRemove.containsKey(folder)) {
4202 appsToRemove = folderAppsToRemove.get(folder);
4203 } else {
4204 appsToRemove = new ArrayList<ShortcutInfo>();
4205 folderAppsToRemove.put(folder, appsToRemove);
Romain Guy629de3e2010-01-13 12:20:59 -08004206 }
Winson Chung64359a52013-07-08 17:17:08 -07004207 appsToRemove.add((ShortcutInfo) info);
4208 return true;
4209 }
4210 } else {
Kenny Guyed131872014-04-30 03:02:21 +01004211 if (componentNames.contains(cn) && info.user.equals(user)) {
Winson Chung64359a52013-07-08 17:17:08 -07004212 childrenToRemove.add(children.get(info));
4213 return true;
Romain Guy574d20e2009-06-01 15:34:04 -07004214 }
4215 }
Winson Chung64359a52013-07-08 17:17:08 -07004216 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004217 }
Winson Chung64359a52013-07-08 17:17:08 -07004218 };
4219 LauncherModel.filterItemInfos(children.keySet(), filter);
4220
4221 // Remove all the apps from their folders
4222 for (FolderInfo folder : folderAppsToRemove.keySet()) {
4223 ArrayList<ShortcutInfo> appsToRemove = folderAppsToRemove.get(folder);
4224 for (ShortcutInfo info : appsToRemove) {
4225 folder.remove(info);
4226 }
4227 }
4228
4229 // Remove all the other children
4230 for (View child : childrenToRemove) {
4231 // Note: We can not remove the view directly from CellLayoutChildren as this
4232 // does not re-mark the spaces as unoccupied.
4233 layoutParent.removeViewInLayout(child);
4234 if (child instanceof DropTarget) {
4235 mDragController.removeDropTarget((DropTarget) child);
4236 }
4237 }
4238
4239 if (childrenToRemove.size() > 0) {
4240 layout.requestLayout();
4241 layout.invalidate();
4242 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004243 }
Winson Chung2efec4e2012-05-03 12:31:48 -07004244
Winson Chung64359a52013-07-08 17:17:08 -07004245 // Strip all the empty screens
4246 stripEmptyScreens();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004247 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07004248
Sunny Goyal651077b2014-06-30 14:15:31 -07004249 interface ItemOperator {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004250 /**
Sunny Goyal651077b2014-06-30 14:15:31 -07004251 * Process the next itemInfo, possibly with side-effect on {@link ItemOperator#value}.
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004252 *
4253 * @param info info for the shortcut
4254 * @param view view for the shortcut
4255 * @param parent containing folder, or null
4256 * @return true if done, false to continue the map
4257 */
4258 public boolean evaluate(ItemInfo info, View view, View parent);
4259 }
4260
4261 /**
Sunny Goyal651077b2014-06-30 14:15:31 -07004262 * Map the operator over the shortcuts and widgets, return the first-non-null value.
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004263 *
4264 * @param recurse true: iterate over folder children. false: op get the folders themselves.
4265 * @param op the operator to map over the shortcuts
4266 */
Sunny Goyal651077b2014-06-30 14:15:31 -07004267 void mapOverItems(boolean recurse, ItemOperator op) {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004268 ArrayList<ShortcutAndWidgetContainer> containers = getAllShortcutAndWidgetContainers();
4269 final int containerCount = containers.size();
4270 for (int containerIdx = 0; containerIdx < containerCount; containerIdx++) {
4271 ShortcutAndWidgetContainer container = containers.get(containerIdx);
4272 // map over all the shortcuts on the workspace
4273 final int itemCount = container.getChildCount();
4274 for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
4275 View item = container.getChildAt(itemIdx);
4276 ItemInfo info = (ItemInfo) item.getTag();
4277 if (recurse && info instanceof FolderInfo && item instanceof FolderIcon) {
4278 FolderIcon folder = (FolderIcon) item;
4279 ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
4280 // map over all the children in the folder
4281 final int childCount = folderChildren.size();
4282 for (int childIdx = 0; childIdx < childCount; childIdx++) {
4283 View child = folderChildren.get(childIdx);
4284 info = (ItemInfo) child.getTag();
4285 if (op.evaluate(info, child, folder)) {
4286 return;
4287 }
4288 }
4289 } else {
4290 if (op.evaluate(info, item, null)) {
4291 return;
4292 }
4293 }
4294 }
4295 }
4296 }
4297
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07004298 void updateShortcuts(ArrayList<ShortcutInfo> shortcuts) {
4299 final HashSet<ShortcutInfo> updates = new HashSet<ShortcutInfo>(shortcuts);
4300 mapOverItems(MAP_RECURSE, new ItemOperator() {
4301 @Override
4302 public boolean evaluate(ItemInfo info, View v, View parent) {
4303 if (info instanceof ShortcutInfo && v instanceof BubbleTextView &&
4304 updates.contains(info)) {
Sunny Goyal4390ace2014-10-13 11:33:11 -07004305 ShortcutInfo si = (ShortcutInfo) info;
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07004306 BubbleTextView shortcut = (BubbleTextView) v;
Winson Chungb745afb2015-03-02 11:51:23 -08004307 boolean oldPromiseState = getTextViewIcon(shortcut)
Sunny Goyal4390ace2014-10-13 11:33:11 -07004308 instanceof PreloadIconDrawable;
Sunny Goyaldfaccf62015-05-11 16:30:44 -07004309 shortcut.applyFromShortcutInfo(si, mIconCache,
Sunny Goyal4390ace2014-10-13 11:33:11 -07004310 si.isPromise() != oldPromiseState);
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07004311
4312 if (parent != null) {
4313 parent.invalidate();
4314 }
4315 }
4316 // process all the shortcuts
4317 return false;
4318 }
4319 });
4320 }
4321
Sunny Goyale7b8cd92014-08-27 14:04:33 -07004322 public void removeAbandonedPromise(String packageName, UserHandleCompat user) {
4323 ArrayList<String> packages = new ArrayList<String>(1);
4324 packages.add(packageName);
4325 LauncherModel.deletePackageFromDatabase(mLauncher, packageName, user);
4326 removeItemsByPackageName(packages, user);
Chris Wren40c5ed32014-06-24 18:24:23 -04004327 }
4328
Sunny Goyal756adbc2015-04-16 15:20:51 -07004329 public void updateRestoreItems(final HashSet<ItemInfo> updates) {
Sunny Goyala22666f2014-09-18 13:25:15 -07004330 mapOverItems(MAP_RECURSE, new ItemOperator() {
4331 @Override
4332 public boolean evaluate(ItemInfo info, View v, View parent) {
Sunny Goyal756adbc2015-04-16 15:20:51 -07004333 if (info instanceof ShortcutInfo && v instanceof BubbleTextView
4334 && updates.contains(info)) {
4335 ((BubbleTextView) v).applyState(false);
4336 } else if (v instanceof PendingAppWidgetHostView
4337 && info instanceof LauncherAppWidgetInfo
4338 && updates.contains(info)) {
4339 ((PendingAppWidgetHostView) v).applyState();
Sunny Goyala22666f2014-09-18 13:25:15 -07004340 }
4341 // process all the shortcuts
4342 return false;
4343 }
4344 });
4345 }
4346
Sunny Goyal4390ace2014-10-13 11:33:11 -07004347 void widgetsRestored(ArrayList<LauncherAppWidgetInfo> changedInfo) {
Sunny Goyal0fc1be12014-08-11 17:05:23 -07004348 if (!changedInfo.isEmpty()) {
4349 DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo,
4350 mLauncher.getAppWidgetHost());
Adam Cohen59400422014-03-05 18:07:04 -08004351 if (LauncherModel.getProviderInfo(getContext(),
Robin Lee26ace122015-03-16 19:41:43 +00004352 changedInfo.get(0).providerName,
4353 changedInfo.get(0).user) != null) {
Sunny Goyal0fc1be12014-08-11 17:05:23 -07004354 // Re-inflate the widgets which have changed status
4355 widgetRefresh.run();
4356 } else {
4357 // widgetRefresh will automatically run when the packages are updated.
Sunny Goyal4390ace2014-10-13 11:33:11 -07004358 // For now just update the progress bars
4359 for (LauncherAppWidgetInfo info : changedInfo) {
4360 if (info.hostView instanceof PendingAppWidgetHostView) {
4361 info.installProgress = 100;
4362 ((PendingAppWidgetHostView) info.hostView).applyState();
4363 }
4364 }
Sunny Goyal0fc1be12014-08-11 17:05:23 -07004365 }
Sunny Goyalff572272014-07-23 13:58:07 -07004366 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07004367 }
4368
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004369 private void moveToScreen(int page, boolean animate) {
Adam Cohen6c5891a2014-07-09 23:53:15 -07004370 if (!workspaceInModalState()) {
Winson Chungde1af762011-07-21 16:44:07 -07004371 if (animate) {
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004372 snapToPage(page);
Winson Chungde1af762011-07-21 16:44:07 -07004373 } else {
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004374 setCurrentPage(page);
Winson Chungde1af762011-07-21 16:44:07 -07004375 }
Joe Onoratoc45b1682010-01-11 18:48:40 -05004376 }
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004377 View child = getChildAt(page);
Winson Chung64359a52013-07-08 17:17:08 -07004378 if (child != null) {
4379 child.requestFocus();
4380 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004381 }
4382
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004383 void moveToDefaultScreen(boolean animate) {
4384 moveToScreen(mDefaultPage, animate);
4385 }
4386
4387 void moveToCustomContentScreen(boolean animate) {
4388 if (hasCustomContent()) {
4389 int ccIndex = getPageIndexForScreenId(CUSTOM_CONTENT_SCREEN_ID);
4390 if (animate) {
4391 snapToPage(ccIndex);
4392 } else {
4393 setCurrentPage(ccIndex);
4394 }
4395 View child = getChildAt(ccIndex);
4396 if (child != null) {
4397 child.requestFocus();
4398 }
4399 }
Adam Cohen76128b62013-11-14 16:43:59 +00004400 exitWidgetResizeMode();
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004401 }
4402
Michael Jurka0142d492010-08-25 17:46:15 -07004403 @Override
Winson Chung7819a562013-09-19 15:55:45 -07004404 protected PageIndicator.PageMarkerResources getPageIndicatorMarker(int pageIndex) {
4405 long screenId = getScreenIdForPageIndex(pageIndex);
4406 if (screenId == EXTRA_EMPTY_SCREEN_ID) {
Michael Jurkafe0ace32013-10-03 01:05:14 -07004407 int count = mScreenOrder.size() - numCustomPages();
Winson Chung7819a562013-09-19 15:55:45 -07004408 if (count > 1) {
Winson Chungba4e52f2013-10-01 17:22:13 -07004409 return new PageIndicator.PageMarkerResources(R.drawable.ic_pageindicator_current,
Winson Chung7819a562013-09-19 15:55:45 -07004410 R.drawable.ic_pageindicator_add);
4411 }
Winson Chung82dfe582013-07-26 15:07:49 -07004412 }
Winson Chung7819a562013-09-19 15:55:45 -07004413
Winson Chung82dfe582013-07-26 15:07:49 -07004414 return super.getPageIndicatorMarker(pageIndex);
4415 }
4416
Adam Cohen53805212013-10-01 10:39:23 -07004417 protected String getPageIndicatorDescription() {
4418 String settings = getResources().getString(R.string.settings_button_text);
4419 return getCurrentPageDescription() + ", " + settings;
4420 }
4421
Winson Chung6a0f57d2011-06-29 20:10:49 -07004422 protected String getCurrentPageDescription() {
Adam Cohen53805212013-10-01 10:39:23 -07004423 if (hasCustomContent() && getNextPage() == 0) {
4424 return mCustomContentDescription;
4425 }
Sunny Goyal1d08f702015-05-04 15:50:25 -07004426 int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
4427 return getPageDescription(page);
4428 }
4429
4430 private String getPageDescription(int page) {
4431 int delta = numCustomPages();
Michael Jurka8b805b12012-04-18 14:23:14 -07004432 return String.format(getContext().getString(R.string.workspace_scroll_format),
Adam Cohen53805212013-10-01 10:39:23 -07004433 page + 1 - delta, getChildCount() - delta);
Winson Chung6a0f57d2011-06-29 20:10:49 -07004434 }
Adam Cohen8dfcba42011-07-07 16:38:18 -07004435
4436 public void getLocationInDragLayer(int[] loc) {
4437 mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
4438 }
Sunny Goyal0fc1be12014-08-11 17:05:23 -07004439
4440 /**
4441 * Used as a workaround to ensure that the AppWidgetService receives the
4442 * PACKAGE_ADDED broadcast before updating widgets.
4443 */
4444 private class DeferredWidgetRefresh implements Runnable {
4445 private final ArrayList<LauncherAppWidgetInfo> mInfos;
4446 private final LauncherAppWidgetHost mHost;
4447 private final Handler mHandler;
4448
4449 private boolean mRefreshPending;
4450
4451 public DeferredWidgetRefresh(ArrayList<LauncherAppWidgetInfo> infos,
4452 LauncherAppWidgetHost host) {
4453 mInfos = infos;
4454 mHost = host;
4455 mHandler = new Handler();
4456 mRefreshPending = true;
4457
4458 mHost.addProviderChangeListener(this);
4459 // Force refresh after 10 seconds, if we don't get the provider changed event.
4460 // This could happen when the provider is no longer available in the app.
4461 mHandler.postDelayed(this, 10000);
4462 }
4463
4464 @Override
4465 public void run() {
4466 mHost.removeProviderChangeListener(this);
4467 mHandler.removeCallbacks(this);
4468
4469 if (!mRefreshPending) {
4470 return;
4471 }
4472
4473 mRefreshPending = false;
4474
4475 for (LauncherAppWidgetInfo info : mInfos) {
4476 if (info.hostView instanceof PendingAppWidgetHostView) {
4477 PendingAppWidgetHostView view = (PendingAppWidgetHostView) info.hostView;
4478 mLauncher.removeAppWidget(info);
4479
4480 CellLayout cl = (CellLayout) view.getParent().getParent();
4481 // Remove the current widget
4482 cl.removeView(view);
4483 mLauncher.bindAppWidget(info);
4484 }
4485 }
4486 }
4487 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004488}