blob: 56803d960f8046547d4c491ed20ca4dcc0c1751b [file] [log] [blame]
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Daniel Sandler325dc232013-06-05 22:57:57 -040017package com.android.launcher3;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080018
Michael Jurka0280c3b2010-09-17 15:00:07 -070019import android.animation.Animator;
Adam Cohena5f4e482013-10-11 12:10:28 -070020import android.animation.Animator.AnimatorListener;
Adam Cohenf358a4b2013-07-23 16:47:31 -070021import android.animation.AnimatorListenerAdapter;
Chet Haaseb1254a62010-09-07 13:35:00 -070022import android.animation.AnimatorSet;
Adam Cohen22cba7f2013-07-19 16:14:00 -070023import android.animation.LayoutTransition;
Michael Jurka0280c3b2010-09-17 15:00:07 -070024import android.animation.ObjectAnimator;
Adam Cohenad4e15c2013-10-17 16:21:35 -070025import android.animation.PropertyValuesHolder;
Patrick Dubroycd68ff52010-10-28 17:57:05 -070026import android.animation.TimeInterpolator;
27import android.animation.ValueAnimator;
28import android.animation.ValueAnimator.AnimatorUpdateListener;
Dianne Hackborn8f573952009-08-10 23:21:09 -070029import android.app.WallpaperManager;
Michael Jurkabed61d22012-02-14 22:51:29 -080030import android.appwidget.AppWidgetHostView;
Romain Guy629de3e2010-01-13 12:20:59 -080031import android.appwidget.AppWidgetProviderInfo;
Adam Powell495f2892010-04-16 16:40:55 -070032import android.content.ComponentName;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080033import android.content.Context;
Michael Jurkaadc574c2013-09-12 00:05:02 +020034import android.content.SharedPreferences;
Patrick Dubroy7247f632010-08-04 16:02:59 -070035import android.content.res.Resources;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080036import android.content.res.TypedArray;
Joe Onorato4be866d2010-10-10 11:26:02 -070037import android.graphics.Bitmap;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080038import android.graphics.Canvas;
Michael Jurkaa63c4522010-08-19 13:52:27 -070039import android.graphics.Matrix;
Winson Chungbabb53e2014-04-14 17:12:49 -070040import android.graphics.Paint;
Winson Chungb8c69f32011-10-19 21:36:08 -070041import android.graphics.Point;
Winson Chung043f2af2012-03-01 16:09:54 -080042import android.graphics.PointF;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080043import android.graphics.Rect;
Joe Onorato4be866d2010-10-10 11:26:02 -070044import android.graphics.Region.Op;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070045import android.graphics.drawable.Drawable;
Michael Jurkaa6a05472013-11-13 17:59:46 +010046import android.os.AsyncTask;
Sunny Goyal0fc1be12014-08-11 17:05:23 -070047import android.os.Handler;
Joe Onorato956091b2010-02-19 12:47:40 -080048import android.os.IBinder;
Adam Powell495f2892010-04-16 16:40:55 -070049import android.os.Parcelable;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080050import android.util.AttributeSet;
Daniel Sandler291ad122010-05-24 16:03:53 -040051import android.util.Log;
Adam Cohen1462de32012-07-24 22:34:36 -070052import android.util.SparseArray;
Michael Jurkacc07e7a2013-08-26 20:56:35 +020053import android.view.Choreographer;
Winson Chunga34abf82010-11-12 12:10:35 -080054import android.view.Display;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080055import android.view.MotionEvent;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080056import android.view.View;
Winson Chung6e314082011-01-27 16:46:51 -080057import android.view.ViewGroup;
Adam Cohen53805212013-10-01 10:39:23 -070058import android.view.accessibility.AccessibilityManager;
Patrick Dubroycd68ff52010-10-28 17:57:05 -070059import android.view.animation.DecelerateInterpolator;
Michael Jurkacc07e7a2013-08-26 20:56:35 +020060import android.view.animation.Interpolator;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070061import android.widget.TextView;
Kenny Guyed131872014-04-30 03:02:21 +010062
Daniel Sandler325dc232013-06-05 22:57:57 -040063import com.android.launcher3.FolderIcon.FolderRingAnimator;
Adam Cohenbffe7452013-07-22 18:21:45 -070064import com.android.launcher3.Launcher.CustomContentCallbacks;
Adam Cohenc2d6e892014-10-16 09:49:24 -070065import com.android.launcher3.Launcher.LauncherOverlay;
Daniel Sandler325dc232013-06-05 22:57:57 -040066import com.android.launcher3.LauncherSettings.Favorites;
Sunny Goyal34942622014-08-29 17:20:55 -070067import com.android.launcher3.compat.PackageInstallerCompat;
Sunny Goyale755d462014-07-22 13:48:29 -070068import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
Sunny Goyal651077b2014-06-30 14:15:31 -070069import com.android.launcher3.compat.UserHandleCompat;
Romain Guyedcce092010-03-04 13:03:17 -080070
Adam Cohen716b51e2011-06-30 12:09:54 -070071import java.util.ArrayList;
Adam Cohendcd297f2013-06-18 13:13:40 -070072import java.util.HashMap;
Adam Cohen716b51e2011-06-30 12:09:54 -070073import java.util.HashSet;
Michael Jurka8bd65f52012-06-25 14:56:22 -070074import java.util.Iterator;
Sunny Goyal95abbb32014-08-04 10:53:22 -070075import java.util.concurrent.atomic.AtomicInteger;
Adam Cohen716b51e2011-06-30 12:09:54 -070076
The Android Open Source Project31dd5032009-03-03 19:32:27 -080077/**
Michael Jurka0142d492010-08-25 17:46:15 -070078 * The workspace is a wide area with a wallpaper and a finite number of pages.
79 * Each page contains a number of icons, folders or widgets the user can
Winson Chungaafa03c2010-06-11 17:34:16 -070080 * interact with. A workspace is meant to be used with a fixed width only.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080081 */
Michael Jurka0142d492010-08-25 17:46:15 -070082public class Workspace extends SmoothPagedView
Michael Jurkad74c9842011-07-10 12:44:21 -070083 implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
John Spurlock77e1f472013-09-11 10:09:51 -040084 DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener,
85 Insettable {
Joe Onorato3a8820b2009-11-10 15:06:42 -080086 private static final String TAG = "Launcher.Workspace";
Michael Jurka0142d492010-08-25 17:46:15 -070087
Adam Cohen68d73932010-11-15 10:50:58 -080088 private static final int CHILDREN_OUTLINE_FADE_OUT_DELAY = 0;
89 private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375;
Winson Chung9171e6d2010-11-17 17:39:27 -080090 private static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100;
Adam Cohenf34bab52010-09-30 14:11:56 -070091
Adam Cohenad4e15c2013-10-17 16:21:35 -070092 protected static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400;
93 protected static final int FADE_EMPTY_SCREEN_DURATION = 150;
94
Winson Chungf135c6c2010-11-18 16:32:08 -080095 private static final int BACKGROUND_FADE_OUT_DURATION = 350;
Adam Cohened51cc92011-08-01 20:28:08 -070096 private static final int ADJACENT_SCREEN_DROP_DURATION = 300;
Adam Cohen265b9a62011-12-07 14:37:18 -080097 private static final int FLING_THRESHOLD_VELOCITY = 500;
Adam Cohened51cc92011-08-01 20:28:08 -070098
Adam Cohenf358a4b2013-07-23 16:47:31 -070099 private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f;
100
Chris Wren40c5ed32014-06-24 18:24:23 -0400101 static final boolean MAP_NO_RECURSE = false;
102 static final boolean MAP_RECURSE = true;
Chris Wrenaeff7ea2014-02-14 16:59:24 -0500103
Winson Chung9171e6d2010-11-17 17:39:27 -0800104 // These animators are used to fade the children's outlines
105 private ObjectAnimator mChildrenOutlineFadeInAnimation;
106 private ObjectAnimator mChildrenOutlineFadeOutAnimation;
107 private float mChildrenOutlineAlpha = 0;
108
109 // These properties refer to the background protection gradient used for AllApps and Customize
Michael Jurkae0f5a612011-02-07 16:45:41 -0800110 private ValueAnimator mBackgroundFadeInAnimation;
111 private ValueAnimator mBackgroundFadeOutAnimation;
Adam Cohenf34bab52010-09-30 14:11:56 -0700112
Adam Cohen3d1b2b42013-08-14 15:57:58 -0700113 private static final long CUSTOM_CONTENT_GESTURE_DELAY = 200;
114 private long mTouchDownTime = -1;
115 private long mCustomContentShowTime = -1;
116
Adam Cohen22cba7f2013-07-19 16:14:00 -0700117 private LayoutTransition mLayoutTransition;
Dianne Hackborn8f573952009-08-10 23:21:09 -0700118 private final WallpaperManager mWallpaperManager;
Michael Jurka9c6fbed2011-03-02 17:41:34 -0800119 private IBinder mWindowToken;
Winson Chungaafa03c2010-06-11 17:34:16 -0700120
Winson Chung9e6a0a22013-08-27 11:58:12 -0700121 private int mOriginalDefaultPage;
Michael Jurka0142d492010-08-25 17:46:15 -0700122 private int mDefaultPage;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800123
Adam Cohen5084cba2013-09-03 12:01:16 -0700124 private ShortcutAndWidgetContainer mDragSourceInternal;
Adam Cohen53805212013-10-01 10:39:23 -0700125 private static boolean sAccessibilityEnabled;
Adam Cohen5084cba2013-09-03 12:01:16 -0700126
Adam Cohendcd297f2013-06-18 13:13:40 -0700127 // The screen id used for the empty screen always present to the right.
Adrian Roos8f3f6832014-04-28 15:45:52 +0200128 final static long EXTRA_EMPTY_SCREEN_ID = -201;
Adam Cohendcd297f2013-06-18 13:13:40 -0700129 private final static long CUSTOM_CONTENT_SCREEN_ID = -301;
130
131 private HashMap<Long, CellLayout> mWorkspaceScreens = new HashMap<Long, CellLayout>();
132 private ArrayList<Long> mScreenOrder = new ArrayList<Long>();
133
Adam Cohenad4e15c2013-10-17 16:21:35 -0700134 private Runnable mRemoveEmptyScreenRunnable;
Adam Cohen689ff162014-05-08 17:27:56 -0700135 private boolean mDeferRemoveExtraEmptyScreen = false;
Adam Cohenad4e15c2013-10-17 16:21:35 -0700136
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800137 /**
138 * CellInfo for the cell that is currently being dragged
139 */
140 private CellLayout.CellInfo mDragInfo;
Winson Chungaafa03c2010-06-11 17:34:16 -0700141
Jeff Sharkey70864282009-04-07 21:08:40 -0700142 /**
143 * Target drop area calculated during last acceptDrop call.
144 */
Adam Cohenc0dcf592011-06-01 15:30:43 -0700145 private int[] mTargetCell = new int[2];
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700146 private int mDragOverX = -1;
147 private int mDragOverY = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800148
Adam Cohena897f392012-04-27 18:12:05 -0700149 static Rect mLandscapeCellLayoutMetrics = null;
150 static Rect mPortraitCellLayoutMetrics = null;
151
Adam Cohenbffe7452013-07-22 18:21:45 -0700152 CustomContentCallbacks mCustomContentCallbacks;
153 boolean mCustomContentShowing;
Adam Cohenc36fa5c2013-08-29 11:54:42 -0700154 private float mLastCustomContentScrollProgress = -1f;
Adam Cohen53805212013-10-01 10:39:23 -0700155 private String mCustomContentDescription = "";
Adam Cohenbffe7452013-07-22 18:21:45 -0700156
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700157 /**
158 * The CellLayout that is currently being dragged over
159 */
160 private CellLayout mDragTargetLayout = null;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700161 /**
162 * The CellLayout that we will show as glowing
163 */
164 private CellLayout mDragOverlappingLayout = null;
165
166 /**
167 * The CellLayout which will be dropped to
168 */
169 private CellLayout mDropToLayout = null;
170
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800171 private Launcher mLauncher;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800172 private IconCache mIconCache;
Joe Onorato00acb122009-08-04 16:04:30 -0400173 private DragController mDragController;
Winson Chungaafa03c2010-06-11 17:34:16 -0700174
Michael Jurka4516c112010-10-07 15:13:47 -0700175 // These are temporary variables to prevent having to allocate a new object just to
176 // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800177 private int[] mTempCell = new int[2];
Adam Cohen7d30a372013-07-01 17:03:59 -0700178 private int[] mTempPt = new int[2];
Jeff Sharkey70864282009-04-07 21:08:40 -0700179 private int[] mTempEstimate = new int[2];
Adam Cohene3e27a82011-04-15 12:07:39 -0700180 private float[] mDragViewVisualCenter = new float[2];
Michael Jurka4516c112010-10-07 15:13:47 -0700181 private float[] mTempCellLayoutCenterCoordinates = new float[2];
Michael Jurka0280c3b2010-09-17 15:00:07 -0700182 private Matrix mTempInverseMatrix = new Matrix();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800183
Michael Jurkac2f7f472010-12-14 15:34:42 -0800184 private SpringLoadedDragController mSpringLoadedDragController;
Winson Chungb26f3d62011-06-02 10:49:29 -0700185 private float mSpringLoadedShrinkFactor;
Adam Cohenf358a4b2013-07-23 16:47:31 -0700186 private float mOverviewModeShrinkFactor;
Michael Jurkad3ef3062010-11-23 16:23:58 -0800187
Patrick Dubroy1262e362010-10-06 15:49:50 -0700188 // State variable that indicates whether the pages are small (ie when you're
Michael Jurkadee05892010-07-27 10:01:56 -0700189 // in all apps or customize mode)
Michael Jurkad74c9842011-07-10 12:44:21 -0700190
Adam Cohen6c5891a2014-07-09 23:53:15 -0700191 enum State { NORMAL, NORMAL_HIDDEN, SPRING_LOADED, OVERVIEW, OVERVIEW_HIDDEN};
Adam Cohen7777d962011-08-18 18:58:38 -0700192 private State mState = State.NORMAL;
Michael Jurkad74c9842011-07-10 12:44:21 -0700193 private boolean mIsSwitchingState = false;
Michael Jurkad74c9842011-07-10 12:44:21 -0700194
Michael Jurkad74c9842011-07-10 12:44:21 -0700195 boolean mAnimatingViewIntoPlace = false;
196 boolean mIsDragOccuring = false;
197 boolean mChildrenLayersEnabled = true;
Michael Jurkadee05892010-07-27 10:01:56 -0700198
Adam Cohenaccfd562013-07-12 14:40:40 -0700199 private boolean mStripScreensOnPageStopMoving = false;
200
Patrick Dubroy54fa3b92010-11-17 12:18:45 -0800201 /** Is the user is dragging an item near the edge of a page? */
Patrick Dubroy1262e362010-10-06 15:49:50 -0700202 private boolean mInScrollArea = false;
203
Daniel Sandlere4f98912013-06-25 15:13:26 -0400204 private HolographicOutlineHelper mOutlineHelper;
Joe Onorato4be866d2010-10-10 11:26:02 -0700205 private Bitmap mDragOutline = null;
Sunny Goyal508da152014-08-14 10:53:27 -0700206 private static final Rect sTempRect = new Rect();
Patrick Dubroy8e58e912010-10-14 13:21:48 -0700207 private final int[] mTempXY = new int[2];
Michael Jurkad51f33a2012-06-28 15:35:26 -0700208 private int[] mTempVisiblePagesRange = new int[2];
Adam Cohen1e4359c2014-08-18 13:12:16 -0700209 private boolean mOverscrollEffectSet;
Michael Jurkaf8304f02012-04-26 13:33:26 -0700210 public static final int DRAG_BITMAP_PADDING = 2;
Michael Jurka869390b2012-05-06 15:55:19 -0700211 private boolean mWorkspaceFadeInAdjacentScreens;
Joe Onorato4be866d2010-10-10 11:26:02 -0700212
Michael Jurkaab1983f2011-01-18 15:50:17 -0800213 WallpaperOffsetInterpolator mWallpaperOffset;
Michael Jurka2a4f4922014-01-29 16:32:39 +0100214 private boolean mWallpaperIsLiveWallpaper;
215 private int mNumPagesForWallpaperParallax;
216 private float mLastSetWallpaperOffsetSteps = 0;
217
Adam Cohen26976d92011-03-22 15:33:33 -0700218 private Runnable mDelayedResizeRunnable;
Winson Chungf0c6ae02012-03-21 16:10:31 -0700219 private Runnable mDelayedSnapToPageRunnable;
Michael Jurka84f2ce72012-04-13 15:08:01 -0700220 private Point mDisplaySize = new Point();
Michael Jurkac5b262c2011-01-12 20:24:50 -0800221
Adam Cohen19072da2011-05-31 14:30:45 -0700222 // Variables relating to the creation of user folders by hovering shortcuts over shortcuts
Adam Cohen482ed822012-03-02 14:15:13 -0800223 private static final int FOLDER_CREATION_TIMEOUT = 0;
Adam Cohenfa3c58f2013-12-06 16:10:55 -0800224 public static final int REORDER_TIMEOUT = 350;
Adam Cohen19072da2011-05-31 14:30:45 -0700225 private final Alarm mFolderCreationAlarm = new Alarm();
Adam Cohen482ed822012-03-02 14:15:13 -0800226 private final Alarm mReorderAlarm = new Alarm();
Adam Cohen19072da2011-05-31 14:30:45 -0700227 private FolderRingAnimator mDragFolderRingAnimator = null;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700228 private FolderIcon mDragOverFolderIcon = null;
Adam Cohen19072da2011-05-31 14:30:45 -0700229 private boolean mCreateUserFolderOnDrop = false;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700230 private boolean mAddToExistingFolderOnDrop = false;
231 private DropTarget.DragEnforcer mDragEnforcer;
Adam Cohen3aff81c2012-05-16 21:01:01 -0700232 private float mMaxDistanceForFolderCreation;
Adam Cohen073a46f2011-05-17 16:28:09 -0700233
Sunny Goyal508da152014-08-14 10:53:27 -0700234 private final Canvas mCanvas = new Canvas();
235
Adam Cohenf8d28232011-02-01 21:47:00 -0800236 // Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget)
237 private float mXDown;
238 private float mYDown;
239 final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
240 final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
241 final static float TOUCH_SLOP_DAMPING_FACTOR = 4;
242
Adam Cohened66b2b2012-01-23 17:28:51 -0800243 // Relating to the animation of items being dropped externally
Adam Cohend41fbf52012-02-16 23:53:59 -0800244 public static final int ANIMATE_INTO_POSITION_AND_DISAPPEAR = 0;
245 public static final int ANIMATE_INTO_POSITION_AND_REMAIN = 1;
246 public static final int ANIMATE_INTO_POSITION_AND_RESIZE = 2;
247 public static final int COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION = 3;
248 public static final int CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION = 4;
Adam Cohened66b2b2012-01-23 17:28:51 -0800249
Adam Cohen482ed822012-03-02 14:15:13 -0800250 // Related to dragging, folder creation and reordering
251 private static final int DRAG_MODE_NONE = 0;
252 private static final int DRAG_MODE_CREATE_FOLDER = 1;
253 private static final int DRAG_MODE_ADD_TO_FOLDER = 2;
254 private static final int DRAG_MODE_REORDER = 3;
255 private int mDragMode = DRAG_MODE_NONE;
256 private int mLastReorderX = -1;
257 private int mLastReorderY = -1;
258
Adam Cohen1462de32012-07-24 22:34:36 -0700259 private SparseArray<Parcelable> mSavedStates;
260 private final ArrayList<Integer> mRestoredPages = new ArrayList<Integer>();
261
Adam Cohen4b285c52011-07-21 14:24:06 -0700262 // These variables are used for storing the initial and final values during workspace animations
Adam Cohened51cc92011-08-01 20:28:08 -0700263 private int mSavedScrollX;
264 private float mSavedRotationY;
265 private float mSavedTranslationX;
Adam Cohen7d30a372013-07-01 17:03:59 -0700266
267 private float mCurrentScale;
268 private float mNewScale;
Adam Cohen4b285c52011-07-21 14:24:06 -0700269 private float[] mOldBackgroundAlphas;
Adam Cohen4b285c52011-07-21 14:24:06 -0700270 private float[] mOldAlphas;
Adam Cohen4b285c52011-07-21 14:24:06 -0700271 private float[] mNewBackgroundAlphas;
Adam Cohen4b285c52011-07-21 14:24:06 -0700272 private float[] mNewAlphas;
Adam Cohendcd297f2013-06-18 13:13:40 -0700273 private int mLastChildCount = -1;
Winson Chung70442722012-02-10 15:43:22 -0800274 private float mTransitionProgress;
Adam Cohen08072c02014-11-18 17:53:44 -0800275 private Animator mStateAnimator = null;
Adam Cohen4b285c52011-07-21 14:24:06 -0700276
Adam Cohen1e4359c2014-08-18 13:12:16 -0700277 float mOverScrollEffect = 0f;
278
Michael Jurka1e2f4652013-07-08 18:03:46 -0700279 private Runnable mDeferredAction;
280 private boolean mDeferDropAfterUninstall;
281 private boolean mUninstallSuccessful;
282
Adam Cohenc2d6e892014-10-16 09:49:24 -0700283 // State related to Launcher Overlay
284 LauncherOverlay mLauncherOverlay;
285 boolean mScrollInteractionBegan;
286 boolean mStartedSendingScrollEvents;
287 boolean mShouldSendPageSettled;
Adam Cohen8c4ca922014-10-24 17:40:34 -0700288 int mLastOverlaySroll = 0;
Adam Cohenc2d6e892014-10-16 09:49:24 -0700289
Romain Guyeeacd562012-10-10 18:47:33 -0700290 private final Runnable mBindPages = new Runnable() {
291 @Override
292 public void run() {
293 mLauncher.getModel().bindRemainingSynchronousPages();
294 }
295 };
296
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800297 /**
298 * Used to inflate the Workspace from XML.
299 *
300 * @param context The application's context.
Michael Jurka0142d492010-08-25 17:46:15 -0700301 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800302 */
303 public Workspace(Context context, AttributeSet attrs) {
304 this(context, attrs, 0);
305 }
306
307 /**
308 * Used to inflate the Workspace from XML.
309 *
310 * @param context The application's context.
Michael Jurka0142d492010-08-25 17:46:15 -0700311 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800312 * @param defStyle Unused.
313 */
314 public Workspace(Context context, AttributeSet attrs, int defStyle) {
315 super(context, attrs, defStyle);
Michael Jurka0142d492010-08-25 17:46:15 -0700316 mContentIsRefreshable = false;
Michael Jurka5f1c5092010-09-03 14:15:02 -0700317
Daniel Sandlere4f98912013-06-25 15:13:26 -0400318 mOutlineHelper = HolographicOutlineHelper.obtain(context);
319
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700320 mDragEnforcer = new DropTarget.DragEnforcer(context);
Winson Chungf0ea4d32011-06-06 14:27:16 -0700321 // With workspace, data is available straight from the get-go
322 setDataIsReady();
323
Michael Jurka8bc66c72012-06-21 08:36:45 -0700324 mLauncher = (Launcher) context;
Winson Chung867ca622012-02-21 15:48:35 -0800325 final Resources res = getResources();
Adam Cohen3b185e22013-10-29 14:45:58 -0700326 mWorkspaceFadeInAdjacentScreens = LauncherAppState.getInstance().getDynamicGrid().
327 getDeviceProfile().shouldFadeAdjacentWorkspaceScreens();
Michael Jurka869390b2012-05-06 15:55:19 -0700328 mFadeInAdjacentScreens = false;
Dianne Hackborn8f573952009-08-10 23:21:09 -0700329 mWallpaperManager = WallpaperManager.getInstance(context);
Winson Chungaafa03c2010-06-11 17:34:16 -0700330
Winson Chungc82d2622013-11-06 13:23:29 -0800331 LauncherAppState app = LauncherAppState.getInstance();
332 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
Winson Chungaafa03c2010-06-11 17:34:16 -0700333 TypedArray a = context.obtainStyledAttributes(attrs,
334 R.styleable.Workspace, defStyle, 0);
Winson Chungb26f3d62011-06-02 10:49:29 -0700335 mSpringLoadedShrinkFactor =
336 res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
Winson Chungc82d2622013-11-06 13:23:29 -0800337 mOverviewModeShrinkFactor = grid.getOverviewModeScale();
Winson Chung9e6a0a22013-08-27 11:58:12 -0700338 mOriginalDefaultPage = mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800339 a.recycle();
340
Michael Jurka8b805b12012-04-18 14:23:14 -0700341 setOnHierarchyChangeListener(this);
Joe Onorato0d44e942009-11-16 18:20:51 -0800342 setHapticFeedbackEnabled(false);
Michael Jurka0142d492010-08-25 17:46:15 -0700343
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800344 initWorkspace();
Winson Chungc35afb22011-02-23 13:01:49 -0800345
346 // Disable multitouch across the workspace/all apps/customize tray
347 setMotionEventSplittingEnabled(true);
Adam Cohen53805212013-10-01 10:39:23 -0700348 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800349 }
350
John Spurlock77e1f472013-09-11 10:09:51 -0400351 @Override
352 public void setInsets(Rect insets) {
353 mInsets.set(insets);
Adam Cohen6400b842013-11-26 15:45:38 -0800354
355 CellLayout customScreen = getScreenWithId(CUSTOM_CONTENT_SCREEN_ID);
356 if (customScreen != null) {
357 View customContent = customScreen.getShortcutsAndWidgets().getChildAt(0);
358 if (customContent instanceof Insettable) {
359 ((Insettable) customContent).setInsets(mInsets);
360 }
361 }
John Spurlock77e1f472013-09-11 10:09:51 -0400362 }
363
Michael Jurka038f9d82011-11-03 13:50:45 -0700364 // estimate the size of a widget with spans hSpan, vSpan. return MAX_VALUE for each
365 // dimension if unsuccessful
366 public int[] estimateItemSize(int hSpan, int vSpan,
Adam Cohend41fbf52012-02-16 23:53:59 -0800367 ItemInfo itemInfo, boolean springLoaded) {
Michael Jurka038f9d82011-11-03 13:50:45 -0700368 int[] size = new int[2];
369 if (getChildCount() > 0) {
Winson Chungad7db6e2013-10-08 14:01:06 -0700370 // Use the first non-custom page to estimate the child position
371 CellLayout cl = (CellLayout) getChildAt(numCustomPages());
Adam Cohend41fbf52012-02-16 23:53:59 -0800372 Rect r = estimateItemPosition(cl, itemInfo, 0, 0, hSpan, vSpan);
373 size[0] = r.width();
374 size[1] = r.height();
Michael Jurka038f9d82011-11-03 13:50:45 -0700375 if (springLoaded) {
376 size[0] *= mSpringLoadedShrinkFactor;
377 size[1] *= mSpringLoadedShrinkFactor;
378 }
379 return size;
380 } else {
381 size[0] = Integer.MAX_VALUE;
382 size[1] = Integer.MAX_VALUE;
383 return size;
384 }
385 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700386
Adam Cohend41fbf52012-02-16 23:53:59 -0800387 public Rect estimateItemPosition(CellLayout cl, ItemInfo pendingInfo,
Michael Jurka038f9d82011-11-03 13:50:45 -0700388 int hCell, int vCell, int hSpan, int vSpan) {
Adam Cohend41fbf52012-02-16 23:53:59 -0800389 Rect r = new Rect();
Michael Jurka038f9d82011-11-03 13:50:45 -0700390 cl.cellToRect(hCell, vCell, hSpan, vSpan, r);
Michael Jurka038f9d82011-11-03 13:50:45 -0700391 return r;
392 }
393
Adam Cohen5084cba2013-09-03 12:01:16 -0700394 public void onDragStart(final DragSource source, Object info, int dragAction) {
Michael Jurkad74c9842011-07-10 12:44:21 -0700395 mIsDragOccuring = true;
Michael Jurka3a0469d2012-06-21 09:38:41 -0700396 updateChildrenLayersEnabled(false);
Winson Chung641d71d2012-04-26 15:58:01 -0700397 mLauncher.lockScreenOrientation();
Sandeep Siddharthaab2d9d72013-09-17 13:18:24 -0700398 mLauncher.onInteractionBegin();
Michael Jurkaa3d30ad2012-05-08 13:43:43 -0700399 setChildrenBackgroundAlphaMultipliers(1f);
Winson Chungf561bdf2012-05-03 11:20:19 -0700400 // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging
401 InstallShortcutReceiver.enableInstallQueue();
402 UninstallShortcutReceiver.enableUninstallQueue();
Adam Cohen5084cba2013-09-03 12:01:16 -0700403 post(new Runnable() {
404 @Override
405 public void run() {
406 if (mIsDragOccuring) {
Adam Cohen689ff162014-05-08 17:27:56 -0700407 mDeferRemoveExtraEmptyScreen = false;
Adam Cohen5084cba2013-09-03 12:01:16 -0700408 addExtraEmptyScreenOnDrag();
409 }
410 }
411 });
Michael Jurkad74c9842011-07-10 12:44:21 -0700412 }
413
Adam Cohen689ff162014-05-08 17:27:56 -0700414
415 public void deferRemoveExtraEmptyScreen() {
416 mDeferRemoveExtraEmptyScreen = true;
417 }
418
Michael Jurkad74c9842011-07-10 12:44:21 -0700419 public void onDragEnd() {
Adam Cohen689ff162014-05-08 17:27:56 -0700420 if (!mDeferRemoveExtraEmptyScreen) {
421 removeExtraEmptyScreen(true, mDragSourceInternal != null);
422 }
423
Michael Jurkad74c9842011-07-10 12:44:21 -0700424 mIsDragOccuring = false;
Michael Jurka3a0469d2012-06-21 09:38:41 -0700425 updateChildrenLayersEnabled(false);
Winson Chung4b919f82012-05-01 10:44:08 -0700426 mLauncher.unlockScreenOrientation(false);
Winson Chungf561bdf2012-05-03 11:20:19 -0700427
428 // Re-enable any Un/InstallShortcutReceiver and now process any queued items
429 InstallShortcutReceiver.disableAndFlushInstallQueue(getContext());
430 UninstallShortcutReceiver.disableAndFlushUninstallQueue(getContext());
Adam Cohen5084cba2013-09-03 12:01:16 -0700431
Adam Cohen5084cba2013-09-03 12:01:16 -0700432 mDragSourceInternal = null;
Sandeep Siddharthaab2d9d72013-09-17 13:18:24 -0700433 mLauncher.onInteractionEnd();
Michael Jurkad74c9842011-07-10 12:44:21 -0700434 }
435
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800436 /**
437 * Initializes various states for this workspace.
438 */
Michael Jurka0142d492010-08-25 17:46:15 -0700439 protected void initWorkspace() {
Michael Jurka0142d492010-08-25 17:46:15 -0700440 mCurrentPage = mDefaultPage;
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400441 LauncherAppState app = LauncherAppState.getInstance();
Winson Chung5f8afe62013-08-12 16:19:28 -0700442 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800443 mIconCache = app.getIconCache();
Patrick Dubroycd68ff52010-10-28 17:57:05 -0700444 setWillNotDraw(false);
Romain Guyce3cbd12013-02-25 15:00:36 -0800445 setClipChildren(false);
446 setClipToPadding(false);
Adam Cohen7777d962011-08-18 18:58:38 -0700447 setChildrenDrawnWithCacheEnabled(true);
Derek Prothrodadd9842014-01-17 13:43:50 -0500448
Winson Chungc82d2622013-11-06 13:23:29 -0800449 setMinScale(mOverviewModeShrinkFactor);
Adam Cohen22cba7f2013-07-19 16:14:00 -0700450 setupLayoutTransition();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800451
Michael Jurkaab1983f2011-01-18 15:50:17 -0800452 mWallpaperOffset = new WallpaperOffsetInterpolator();
Adam Cohencff6af82011-09-13 14:51:53 -0700453 Display display = mLauncher.getWindowManager().getDefaultDisplay();
Michael Jurka84f2ce72012-04-13 15:08:01 -0700454 display.getSize(mDisplaySize);
Adam Cohen265b9a62011-12-07 14:37:18 -0800455
Winson Chung5f8afe62013-08-12 16:19:28 -0700456 mMaxDistanceForFolderCreation = (0.55f * grid.iconSizePx);
Adam Cohen265b9a62011-12-07 14:37:18 -0800457 mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity);
Michael Jurkaa6a05472013-11-13 17:59:46 +0100458
459 // Set the wallpaper dimensions when Launcher starts up
Adam Cohen824fcb32014-05-21 23:01:25 +0000460 setWallpaperDimension();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800461 }
462
Adam Cohen22cba7f2013-07-19 16:14:00 -0700463 private void setupLayoutTransition() {
464 // We want to show layout transitions when pages are deleted, to close the gap.
465 mLayoutTransition = new LayoutTransition();
466 mLayoutTransition.enableTransitionType(LayoutTransition.DISAPPEARING);
467 mLayoutTransition.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
468 mLayoutTransition.disableTransitionType(LayoutTransition.APPEARING);
469 mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
470 setLayoutTransition(mLayoutTransition);
471 }
472
Winson Chung964df6b2013-10-11 15:55:37 -0700473 void enableLayoutTransitions() {
474 setLayoutTransition(mLayoutTransition);
475 }
476 void disableLayoutTransitions() {
477 setLayoutTransition(null);
478 }
479
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800480 @Override
Adam Cohenf34bab52010-09-30 14:11:56 -0700481 protected int getScrollMode() {
Winson Chungb26f3d62011-06-02 10:49:29 -0700482 return SmoothPagedView.X_LARGE_MODE;
Adam Cohenf34bab52010-09-30 14:11:56 -0700483 }
484
Michael Jurka08ee7702011-08-11 16:53:35 -0700485 @Override
Michael Jurka8b805b12012-04-18 14:23:14 -0700486 public void onChildViewAdded(View parent, View child) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800487 if (!(child instanceof CellLayout)) {
488 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
489 }
Adam Cohen2801caf2011-05-13 20:57:39 -0700490 CellLayout cl = ((CellLayout) child);
491 cl.setOnInterceptTouchListener(this);
Adam Cohen2801caf2011-05-13 20:57:39 -0700492 cl.setClickable(true);
Sunny Goyalc46bfef2015-01-05 12:40:08 -0800493 cl.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
Winson Chungf70696d2013-06-25 16:19:53 -0700494 super.onChildViewAdded(parent, child);
Michael Jurka8b805b12012-04-18 14:23:14 -0700495 }
496
Michael Jurka920d7f42012-05-14 16:29:55 -0700497 protected boolean shouldDrawChild(View child) {
498 final CellLayout cl = (CellLayout) child;
499 return super.shouldDrawChild(child) &&
Winson Chungf4bd2362013-10-07 17:11:27 -0700500 (mIsSwitchingState ||
501 cl.getShortcutsAndWidgets().getAlpha() > 0 ||
Michael Jurka920d7f42012-05-14 16:29:55 -0700502 cl.getBackgroundAlpha() > 0);
503 }
504
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800505 /**
506 * @return The open folder on the current screen, or null if there is none
507 */
508 Folder getOpenFolder() {
Adam Cohen716b51e2011-06-30 12:09:54 -0700509 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohen8e776a62011-06-28 18:10:06 -0700510 int count = dragLayer.getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800511 for (int i = 0; i < count; i++) {
Adam Cohen8e776a62011-06-28 18:10:06 -0700512 View child = dragLayer.getChildAt(i);
Winson Chungaafa03c2010-06-11 17:34:16 -0700513 if (child instanceof Folder) {
514 Folder folder = (Folder) child;
515 if (folder.getInfo().opened)
516 return folder;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800517 }
518 }
519 return null;
520 }
521
Patrick Dubroya0aa0122011-02-24 11:42:23 -0800522 boolean isTouchActive() {
523 return mTouchState != TOUCH_STATE_REST;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800524 }
525
Winson Chung9e6a0a22013-08-27 11:58:12 -0700526 public void removeAllWorkspaceScreens() {
Winson Chung964df6b2013-10-11 15:55:37 -0700527 // Disable all layout transitions before removing all pages to ensure that we don't get the
528 // transition animations competing with us changing the scroll when we add pages or the
529 // custom content screen
530 disableLayoutTransitions();
531
532 // Since we increment the current page when we call addCustomContentPage via bindScreens
533 // (and other places), we need to adjust the current page back when we clear the pages
534 if (hasCustomContent()) {
535 removeCustomContentPage();
536 }
537
Winson Chung9e6a0a22013-08-27 11:58:12 -0700538 // Remove the pages and clear the screen models
539 removeAllViews();
540 mScreenOrder.clear();
541 mWorkspaceScreens.clear();
Winson Chung964df6b2013-10-11 15:55:37 -0700542
543 // Re-enable the layout transitions
544 enableLayoutTransitions();
Winson Chung9e6a0a22013-08-27 11:58:12 -0700545 }
546
Adam Cohen89bddfa2013-08-20 11:57:13 -0700547 public long insertNewWorkspaceScreenBeforeEmptyScreen(long screenId) {
Winson Chung64359a52013-07-08 17:17:08 -0700548 // Find the index to insert this view into. If the empty screen exists, then
549 // insert it before that.
550 int insertIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
551 if (insertIndex < 0) {
552 insertIndex = mScreenOrder.size();
553 }
Adam Cohen89bddfa2013-08-20 11:57:13 -0700554 return insertNewWorkspaceScreen(screenId, insertIndex);
Adam Cohendcd297f2013-06-18 13:13:40 -0700555 }
556
Adam Cohen89bddfa2013-08-20 11:57:13 -0700557 public long insertNewWorkspaceScreen(long screenId) {
558 return insertNewWorkspaceScreen(screenId, getChildCount());
Winson Chung64359a52013-07-08 17:17:08 -0700559 }
560
Adam Cohen89bddfa2013-08-20 11:57:13 -0700561 public long insertNewWorkspaceScreen(long screenId, int insertIndex) {
Winson Chunga90303b2013-11-15 13:05:06 -0800562 // Log to disk
563 Launcher.addDumpLog(TAG, "11683562 - insertNewWorkspaceScreen(): " + screenId +
564 " at index: " + insertIndex, true);
565
Adam Cohen5084cba2013-09-03 12:01:16 -0700566 if (mWorkspaceScreens.containsKey(screenId)) {
567 throw new RuntimeException("Screen id " + screenId + " already exists!");
568 }
569
Adam Cohendcd297f2013-06-18 13:13:40 -0700570 CellLayout newScreen = (CellLayout)
571 mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null);
572
Adam Cohen7d30a372013-07-01 17:03:59 -0700573 newScreen.setOnLongClickListener(mLongClickListener);
Adam Cohenf358a4b2013-07-23 16:47:31 -0700574 newScreen.setOnClickListener(mLauncher);
Winson Chung837b2412013-09-09 14:43:51 -0700575 newScreen.setSoundEffectsEnabled(false);
Adam Cohendcd297f2013-06-18 13:13:40 -0700576 mWorkspaceScreens.put(screenId, newScreen);
Winson Chung64359a52013-07-08 17:17:08 -0700577 mScreenOrder.add(insertIndex, screenId);
578 addView(newScreen, insertIndex);
Adam Cohendcd297f2013-06-18 13:13:40 -0700579 return screenId;
580 }
581
Derek Prothrodadd9842014-01-17 13:43:50 -0500582 public void createCustomContentContainer() {
Adam Cohendcd297f2013-06-18 13:13:40 -0700583 CellLayout customScreen = (CellLayout)
Adam Cohen41eb4702013-06-27 15:08:59 -0700584 mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null);
Winson Chung59a488a2013-12-10 12:32:14 -0800585 customScreen.disableBackground();
Adam Cohenc50438c2014-08-19 17:43:05 -0700586 customScreen.disableDragTarget();
Adam Cohendcd297f2013-06-18 13:13:40 -0700587
Adam Cohendcd297f2013-06-18 13:13:40 -0700588 mWorkspaceScreens.put(CUSTOM_CONTENT_SCREEN_ID, customScreen);
589 mScreenOrder.add(0, CUSTOM_CONTENT_SCREEN_ID);
Adam Cohenedb40762013-07-18 16:45:45 -0700590
Adam Cohen2702ea02013-08-15 16:17:42 -0700591 // We want no padding on the custom content
592 customScreen.setPadding(0, 0, 0, 0);
593
Adam Cohenedb40762013-07-18 16:45:45 -0700594 addFullScreenPage(customScreen);
Michael Jurkaee0ce2b2013-07-02 17:24:35 -0700595
Adam Cohendcd297f2013-06-18 13:13:40 -0700596 // Ensure that the current page and default page are maintained.
Winson Chung9e6a0a22013-08-27 11:58:12 -0700597 mDefaultPage = mOriginalDefaultPage + 1;
Winson Chung82e5c982013-10-09 12:04:50 -0700598
599 // Update the custom content hint
Adam Cohen21cd0022013-10-09 18:57:02 -0700600 if (mRestorePage != INVALID_RESTORE_PAGE) {
601 mRestorePage = mRestorePage + 1;
602 } else {
603 setCurrentPage(getCurrentPage() + 1);
604 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700605 }
606
Dave Hawkeya8881582013-09-17 15:55:33 -0600607 public void removeCustomContentPage() {
Dave Hawkeya8881582013-09-17 15:55:33 -0600608 CellLayout customScreen = getScreenWithId(CUSTOM_CONTENT_SCREEN_ID);
609 if (customScreen == null) {
610 throw new RuntimeException("Expected custom content screen to exist");
611 }
612
613 mWorkspaceScreens.remove(CUSTOM_CONTENT_SCREEN_ID);
614 mScreenOrder.remove(CUSTOM_CONTENT_SCREEN_ID);
615 removeView(customScreen);
Adam Cohenbb6fda62013-10-10 12:48:52 -0700616
617 if (mCustomContentCallbacks != null) {
618 mCustomContentCallbacks.onScrollProgressChanged(0);
619 mCustomContentCallbacks.onHide();
620 }
621
Dave Hawkeya8881582013-09-17 15:55:33 -0600622 mCustomContentCallbacks = null;
623
624 // Ensure that the current page and default page are maintained.
625 mDefaultPage = mOriginalDefaultPage - 1;
Winson Chung82e5c982013-10-09 12:04:50 -0700626
627 // Update the custom content hint
Adam Cohen21cd0022013-10-09 18:57:02 -0700628 if (mRestorePage != INVALID_RESTORE_PAGE) {
629 mRestorePage = mRestorePage - 1;
630 } else {
631 setCurrentPage(getCurrentPage() - 1);
632 }
Dave Hawkeya8881582013-09-17 15:55:33 -0600633 }
634
Adam Cohen53805212013-10-01 10:39:23 -0700635 public void addToCustomContentPage(View customContent, CustomContentCallbacks callbacks,
636 String description) {
Winson Chung98ca0f72013-07-29 12:58:51 -0700637 if (getPageIndexForScreenId(CUSTOM_CONTENT_SCREEN_ID) < 0) {
638 throw new RuntimeException("Expected custom content screen to exist");
639 }
640
641 // Add the custom content to the full screen custom page
642 CellLayout customScreen = getScreenWithId(CUSTOM_CONTENT_SCREEN_ID);
643 int spanX = customScreen.getCountX();
644 int spanY = customScreen.getCountY();
645 CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, spanX, spanY);
646 lp.canReorder = false;
647 lp.isFullscreen = true;
John Spurlock77e1f472013-09-11 10:09:51 -0400648 if (customContent instanceof Insettable) {
649 ((Insettable)customContent).setInsets(mInsets);
650 }
Adam Cohen166ebd42013-11-26 14:55:45 -0800651
652 // Verify that the child is removed from any existing parent.
653 if (customContent.getParent() instanceof ViewGroup) {
654 ViewGroup parent = (ViewGroup) customContent.getParent();
655 parent.removeView(customContent);
656 }
Adam Cohen225ad9b2013-10-07 13:03:00 -0700657 customScreen.removeAllViews();
Winson Chung98ca0f72013-07-29 12:58:51 -0700658 customScreen.addViewToCellLayout(customContent, 0, 0, lp, true);
Adam Cohen53805212013-10-01 10:39:23 -0700659 mCustomContentDescription = description;
Winson Chung98ca0f72013-07-29 12:58:51 -0700660
661 mCustomContentCallbacks = callbacks;
662 }
663
Adam Cohen5084cba2013-09-03 12:01:16 -0700664 public void addExtraEmptyScreenOnDrag() {
Winson Chunga90303b2013-11-15 13:05:06 -0800665 // Log to disk
666 Launcher.addDumpLog(TAG, "11683562 - addExtraEmptyScreenOnDrag()", true);
667
Adam Cohen5084cba2013-09-03 12:01:16 -0700668 boolean lastChildOnScreen = false;
669 boolean childOnFinalScreen = false;
670
Adam Cohenad4e15c2013-10-17 16:21:35 -0700671 // Cancel any pending removal of empty screen
672 mRemoveEmptyScreenRunnable = null;
673
Adam Cohen5084cba2013-09-03 12:01:16 -0700674 if (mDragSourceInternal != null) {
675 if (mDragSourceInternal.getChildCount() == 1) {
676 lastChildOnScreen = true;
677 }
678 CellLayout cl = (CellLayout) mDragSourceInternal.getParent();
679 if (indexOfChild(cl) == getChildCount() - 1) {
680 childOnFinalScreen = true;
681 }
682 }
683
684 // If this is the last item on the final screen
685 if (lastChildOnScreen && childOnFinalScreen) {
686 return;
687 }
688 if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) {
689 insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID);
690 }
691 }
692
693 public boolean addExtraEmptyScreen() {
Winson Chunga90303b2013-11-15 13:05:06 -0800694 // Log to disk
695 Launcher.addDumpLog(TAG, "11683562 - addExtraEmptyScreen()", true);
696
Adam Cohen5084cba2013-09-03 12:01:16 -0700697 if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) {
698 insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID);
699 return true;
700 }
701 return false;
702 }
703
Adam Cohenad4e15c2013-10-17 16:21:35 -0700704 private void convertFinalScreenToEmptyScreenIfNecessary() {
Winson Chunga90303b2013-11-15 13:05:06 -0800705 // Log to disk
706 Launcher.addDumpLog(TAG, "11683562 - convertFinalScreenToEmptyScreenIfNecessary()", true);
707
Adam Cohendcb173d2014-04-01 13:33:58 -0700708 if (mLauncher.isWorkspaceLoading()) {
709 // Invalid and dangerous operation if workspace is loading
710 Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
711 return;
712 }
713
Adam Cohenad4e15c2013-10-17 16:21:35 -0700714 if (hasExtraEmptyScreen() || mScreenOrder.size() == 0) return;
715 long finalScreenId = mScreenOrder.get(mScreenOrder.size() - 1);
716
717 if (finalScreenId == CUSTOM_CONTENT_SCREEN_ID) return;
718 CellLayout finalScreen = mWorkspaceScreens.get(finalScreenId);
719
720 // If the final screen is empty, convert it to the extra empty screen
Adam Cohen917e3882013-10-31 15:03:35 -0700721 if (finalScreen.getShortcutsAndWidgets().getChildCount() == 0 &&
722 !finalScreen.isDropPending()) {
Adam Cohenad4e15c2013-10-17 16:21:35 -0700723 mWorkspaceScreens.remove(finalScreenId);
724 mScreenOrder.remove(finalScreenId);
725
726 // if this is the last non-custom content screen, convert it to the empty screen
727 mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, finalScreen);
728 mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
Winson Chunga90303b2013-11-15 13:05:06 -0800729
Winson Chungf0716b72013-11-18 16:09:54 -0800730 // Update the model if we have changed any screens
731 mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
Winson Chunga90303b2013-11-15 13:05:06 -0800732 Launcher.addDumpLog(TAG, "11683562 - extra empty screen: " + finalScreenId, true);
Adam Cohen5084cba2013-09-03 12:01:16 -0700733 }
734 }
735
Adam Cohen689ff162014-05-08 17:27:56 -0700736 public void removeExtraEmptyScreen(final boolean animate, boolean stripEmptyScreens) {
737 removeExtraEmptyScreenDelayed(animate, null, 0, stripEmptyScreens);
Adam Cohenad4e15c2013-10-17 16:21:35 -0700738 }
739
Adam Cohen689ff162014-05-08 17:27:56 -0700740 public void removeExtraEmptyScreenDelayed(final boolean animate, final Runnable onComplete,
Adam Cohenad4e15c2013-10-17 16:21:35 -0700741 final int delay, final boolean stripEmptyScreens) {
Winson Chunga90303b2013-11-15 13:05:06 -0800742 // Log to disk
743 Launcher.addDumpLog(TAG, "11683562 - removeExtraEmptyScreen()", true);
Adam Cohendcb173d2014-04-01 13:33:58 -0700744 if (mLauncher.isWorkspaceLoading()) {
745 // Don't strip empty screens if the workspace is still loading
746 Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
747 return;
748 }
749
Adam Cohenad4e15c2013-10-17 16:21:35 -0700750 if (delay > 0) {
751 postDelayed(new Runnable() {
752 @Override
753 public void run() {
Adam Cohen689ff162014-05-08 17:27:56 -0700754 removeExtraEmptyScreenDelayed(animate, onComplete, 0, stripEmptyScreens);
Adam Cohenad4e15c2013-10-17 16:21:35 -0700755 }
Adam Cohenad4e15c2013-10-17 16:21:35 -0700756 }, delay);
757 return;
758 }
759
760 convertFinalScreenToEmptyScreenIfNecessary();
761 if (hasExtraEmptyScreen()) {
762 int emptyIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
763 if (getNextPage() == emptyIndex) {
764 snapToPage(getNextPage() - 1, SNAP_OFF_EMPTY_SCREEN_DURATION);
765 fadeAndRemoveEmptyScreen(SNAP_OFF_EMPTY_SCREEN_DURATION, FADE_EMPTY_SCREEN_DURATION,
766 onComplete, stripEmptyScreens);
767 } else {
768 fadeAndRemoveEmptyScreen(0, FADE_EMPTY_SCREEN_DURATION,
769 onComplete, stripEmptyScreens);
770 }
771 return;
Adam Cohen3a14eeb2013-11-15 16:51:22 +0000772 } else if (stripEmptyScreens) {
773 // If we're not going to strip the empty screens after removing
774 // the extra empty screen, do it right away.
775 stripEmptyScreens();
Adam Cohenad4e15c2013-10-17 16:21:35 -0700776 }
Adam Cohen3a14eeb2013-11-15 16:51:22 +0000777
Adam Cohenad4e15c2013-10-17 16:21:35 -0700778 if (onComplete != null) {
779 onComplete.run();
780 }
781 }
782
783 private void fadeAndRemoveEmptyScreen(int delay, int duration, final Runnable onComplete,
784 final boolean stripEmptyScreens) {
Winson Chunga90303b2013-11-15 13:05:06 -0800785 // Log to disk
786 // XXX: Do we need to update LM workspace screens below?
787 Launcher.addDumpLog(TAG, "11683562 - fadeAndRemoveEmptyScreen()", true);
Adam Cohenad4e15c2013-10-17 16:21:35 -0700788 PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0f);
789 PropertyValuesHolder bgAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", 0f);
790
791 final CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
792
793 mRemoveEmptyScreenRunnable = new Runnable() {
794 @Override
795 public void run() {
796 if (hasExtraEmptyScreen()) {
797 mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
798 mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
799 removeView(cl);
800 if (stripEmptyScreens) {
801 stripEmptyScreens();
802 }
803 }
804 }
805 };
806
807 ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(cl, alpha, bgAlpha);
808 oa.setDuration(duration);
809 oa.setStartDelay(delay);
810 oa.addListener(new AnimatorListenerAdapter() {
811 @Override
812 public void onAnimationEnd(Animator animation) {
813 if (mRemoveEmptyScreenRunnable != null) {
814 mRemoveEmptyScreenRunnable.run();
815 }
816 if (onComplete != null) {
817 onComplete.run();
818 }
819 }
820 });
821 oa.start();
822 }
823
Michael Jurka2f817312013-09-20 03:03:42 +0200824 public boolean hasExtraEmptyScreen() {
825 int nScreens = getChildCount();
Michael Jurkafe0ace32013-10-03 01:05:14 -0700826 nScreens = nScreens - numCustomPages();
Michael Jurka2f817312013-09-20 03:03:42 +0200827 return mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID) && nScreens > 1;
828 }
829
Adam Cohendcd297f2013-06-18 13:13:40 -0700830 public long commitExtraEmptyScreen() {
Winson Chunga90303b2013-11-15 13:05:06 -0800831 // Log to disk
832 Launcher.addDumpLog(TAG, "11683562 - commitExtraEmptyScreen()", true);
Adam Cohendcb173d2014-04-01 13:33:58 -0700833 if (mLauncher.isWorkspaceLoading()) {
834 // Invalid and dangerous operation if workspace is loading
835 Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
836 return -1;
837 }
Winson Chunga90303b2013-11-15 13:05:06 -0800838
Winson Chung89f97052013-09-20 11:32:26 -0700839 int index = getPageIndexForScreenId(EXTRA_EMPTY_SCREEN_ID);
Adam Cohendcd297f2013-06-18 13:13:40 -0700840 CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
Winson Chungc9168342013-06-26 14:54:55 -0700841 mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
Adam Cohendcd297f2013-06-18 13:13:40 -0700842 mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
843
Michael Jurka104c4562013-07-08 18:03:46 -0700844 long newId = LauncherAppState.getLauncherProvider().generateNewScreenId();
Adam Cohendcd297f2013-06-18 13:13:40 -0700845 mWorkspaceScreens.put(newId, cl);
846 mScreenOrder.add(newId);
847
Winson Chung89f97052013-09-20 11:32:26 -0700848 // Update the page indicator marker
849 if (getPageIndicator() != null) {
850 getPageIndicator().updateMarker(index, getPageIndicatorMarker(index));
851 }
852
Winson Chung64359a52013-07-08 17:17:08 -0700853 // Update the model for the new screen
854 mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
855
Adam Cohendcd297f2013-06-18 13:13:40 -0700856 return newId;
857 }
858
Adam Cohendcd297f2013-06-18 13:13:40 -0700859 public CellLayout getScreenWithId(long screenId) {
860 CellLayout layout = mWorkspaceScreens.get(screenId);
861 return layout;
862 }
863
864 public long getIdForScreen(CellLayout layout) {
865 Iterator<Long> iter = mWorkspaceScreens.keySet().iterator();
866 while (iter.hasNext()) {
867 long id = iter.next();
868 if (mWorkspaceScreens.get(id) == layout) {
869 return id;
870 }
871 }
872 return -1;
873 }
874
875 public int getPageIndexForScreenId(long screenId) {
876 return indexOfChild(mWorkspaceScreens.get(screenId));
877 }
878
879 public long getScreenIdForPageIndex(int index) {
Winson Chung5f8afe62013-08-12 16:19:28 -0700880 if (0 <= index && index < mScreenOrder.size()) {
881 return mScreenOrder.get(index);
882 }
883 return -1;
Adam Cohendcd297f2013-06-18 13:13:40 -0700884 }
885
Winson Chungc9168342013-06-26 14:54:55 -0700886 ArrayList<Long> getScreenOrder() {
887 return mScreenOrder;
888 }
889
Adam Cohendcd297f2013-06-18 13:13:40 -0700890 public void stripEmptyScreens() {
Winson Chunga90303b2013-11-15 13:05:06 -0800891 // Log to disk
892 Launcher.addDumpLog(TAG, "11683562 - stripEmptyScreens()", true);
893
Adam Cohen65e43032014-03-03 11:37:21 -0800894 if (mLauncher.isWorkspaceLoading()) {
Adam Cohendcb173d2014-04-01 13:33:58 -0700895 // Don't strip empty screens if the workspace is still loading.
896 // This is dangerous and can result in data loss.
Adam Cohen517a7f52014-03-01 12:12:59 -0800897 Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
898 return;
899 }
900
Adam Cohenaccfd562013-07-12 14:40:40 -0700901 if (isPageMoving()) {
902 mStripScreensOnPageStopMoving = true;
903 return;
904 }
905
906 int currentPage = getNextPage();
Adam Cohendcd297f2013-06-18 13:13:40 -0700907 ArrayList<Long> removeScreens = new ArrayList<Long>();
908 for (Long id: mWorkspaceScreens.keySet()) {
909 CellLayout cl = mWorkspaceScreens.get(id);
Winson Chungc9168342013-06-26 14:54:55 -0700910 if (id >= 0 && cl.getShortcutsAndWidgets().getChildCount() == 0) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700911 removeScreens.add(id);
912 }
913 }
914
Adam Cohen5084cba2013-09-03 12:01:16 -0700915 // We enforce at least one page to add new items to. In the case that we remove the last
916 // such screen, we convert the last screen to the empty screen
Michael Jurkafe0ace32013-10-03 01:05:14 -0700917 int minScreens = 1 + numCustomPages();
Adam Cohen5084cba2013-09-03 12:01:16 -0700918
Adam Cohendcd297f2013-06-18 13:13:40 -0700919 int pageShift = 0;
920 for (Long id: removeScreens) {
Winson Chunga90303b2013-11-15 13:05:06 -0800921 Launcher.addDumpLog(TAG, "11683562 - removing id: " + id, true);
Adam Cohendcd297f2013-06-18 13:13:40 -0700922 CellLayout cl = mWorkspaceScreens.get(id);
923 mWorkspaceScreens.remove(id);
924 mScreenOrder.remove(id);
Adam Cohen5084cba2013-09-03 12:01:16 -0700925
926 if (getChildCount() > minScreens) {
927 if (indexOfChild(cl) < currentPage) {
928 pageShift++;
929 }
930 removeView(cl);
931 } else {
932 // if this is the last non-custom content screen, convert it to the empty screen
Adam Cohenad4e15c2013-10-17 16:21:35 -0700933 mRemoveEmptyScreenRunnable = null;
Adam Cohen5084cba2013-09-03 12:01:16 -0700934 mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, cl);
935 mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
Adam Cohendcd297f2013-06-18 13:13:40 -0700936 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700937 }
Winson Chung64359a52013-07-08 17:17:08 -0700938
939 if (!removeScreens.isEmpty()) {
940 // Update the model if we have changed any screens
941 mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
942 }
Adam Cohenaccfd562013-07-12 14:40:40 -0700943
944 if (pageShift >= 0) {
945 setCurrentPage(currentPage - pageShift);
946 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700947 }
948
949 // See implementation for parameter definition.
950 void addInScreen(View child, long container, long screenId,
951 int x, int y, int spanX, int spanY) {
952 addInScreen(child, container, screenId, x, y, spanX, spanY, false, false);
953 }
954
955 // At bind time, we use the rank (screenId) to compute x and y for hotseat items.
956 // See implementation for parameter definition.
957 void addInScreenFromBind(View child, long container, long screenId, int x, int y,
958 int spanX, int spanY) {
959 addInScreen(child, container, screenId, x, y, spanX, spanY, false, true);
960 }
961
962 // See implementation for parameter definition.
963 void addInScreen(View child, long container, long screenId, int x, int y, int spanX, int spanY,
964 boolean insert) {
965 addInScreen(child, container, screenId, x, y, spanX, spanY, insert, false);
Winson Chungaafa03c2010-06-11 17:34:16 -0700966 }
967
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800968 /**
969 * Adds the specified child in the specified screen. The position and dimension of
970 * the child are defined by x, y, spanX and spanY.
971 *
972 * @param child The child to add in one of the workspace's screens.
Adam Cohendcd297f2013-06-18 13:13:40 -0700973 * @param screenId The screen in which to add the child.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800974 * @param x The X position of the child in the screen's grid.
975 * @param y The Y position of the child in the screen's grid.
976 * @param spanX The number of cells spanned horizontally by the child.
977 * @param spanY The number of cells spanned vertically by the child.
978 * @param insert When true, the child is inserted at the beginning of the children list.
Adam Cohendcd297f2013-06-18 13:13:40 -0700979 * @param computeXYFromRank When true, we use the rank (stored in screenId) to compute
980 * the x and y position in which to place hotseat items. Otherwise
981 * we use the x and y position to compute the rank.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800982 */
Adam Cohendcd297f2013-06-18 13:13:40 -0700983 void addInScreen(View child, long container, long screenId, int x, int y, int spanX, int spanY,
984 boolean insert, boolean computeXYFromRank) {
Winson Chung3d503fb2011-07-13 17:25:49 -0700985 if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700986 if (getScreenWithId(screenId) == null) {
987 Log.e(TAG, "Skipping child, screenId " + screenId + " not found");
Winson Chung8481e322013-08-09 16:06:38 -0700988 // DEBUGGING - Print out the stack trace to see where we are adding from
989 new Throwable().printStackTrace();
Winson Chung3d503fb2011-07-13 17:25:49 -0700990 return;
991 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800992 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700993 if (screenId == EXTRA_EMPTY_SCREEN_ID) {
Adam Cohendedbd962013-07-11 14:21:49 -0700994 // This should never happen
995 throw new RuntimeException("Screen id should not be EXTRA_EMPTY_SCREEN_ID");
Adam Cohendcd297f2013-06-18 13:13:40 -0700996 }
997
Winson Chung3d503fb2011-07-13 17:25:49 -0700998 final CellLayout layout;
999 if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1000 layout = mLauncher.getHotseat().getLayout();
Sunny Goyalb3726d92014-08-20 16:58:17 -07001001 child.setOnKeyListener(new HotseatIconKeyEventListener());
Winson Chung3d503fb2011-07-13 17:25:49 -07001002
Adam Cohen099f60d2011-08-23 21:07:26 -07001003 // Hide folder title in the hotseat
1004 if (child instanceof FolderIcon) {
1005 ((FolderIcon) child).setTextVisible(false);
1006 }
1007
Adam Cohendcd297f2013-06-18 13:13:40 -07001008 if (computeXYFromRank) {
1009 x = mLauncher.getHotseat().getCellXFromOrder((int) screenId);
1010 y = mLauncher.getHotseat().getCellYFromOrder((int) screenId);
Winson Chung3d503fb2011-07-13 17:25:49 -07001011 } else {
Adam Cohendcd297f2013-06-18 13:13:40 -07001012 screenId = mLauncher.getHotseat().getOrderInHotseat(x, y);
Winson Chung3d503fb2011-07-13 17:25:49 -07001013 }
1014 } else {
Adam Cohen099f60d2011-08-23 21:07:26 -07001015 // Show folder title if not in the hotseat
1016 if (child instanceof FolderIcon) {
1017 ((FolderIcon) child).setTextVisible(true);
1018 }
Adam Cohendcd297f2013-06-18 13:13:40 -07001019 layout = getScreenWithId(screenId);
Adam Cohenac56cff2011-09-28 20:45:37 -07001020 child.setOnKeyListener(new IconKeyEventListener());
Winson Chung3d503fb2011-07-13 17:25:49 -07001021 }
1022
Adam Cohen96d30a12013-07-16 18:13:21 -07001023 ViewGroup.LayoutParams genericLp = child.getLayoutParams();
Adam Cohened66b2b2012-01-23 17:28:51 -08001024 CellLayout.LayoutParams lp;
1025 if (genericLp == null || !(genericLp instanceof CellLayout.LayoutParams)) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001026 lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
1027 } else {
Adam Cohened66b2b2012-01-23 17:28:51 -08001028 lp = (CellLayout.LayoutParams) genericLp;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001029 lp.cellX = x;
1030 lp.cellY = y;
1031 lp.cellHSpan = spanX;
1032 lp.cellVSpan = spanY;
1033 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001034
Adam Cohen7f4eabe2011-04-21 16:19:16 -07001035 if (spanX < 0 && spanY < 0) {
1036 lp.isLockedToGrid = false;
1037 }
1038
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07001039 // Get the canonical child id to uniquely represent this view in this screen
Adam Cohenc76e1dd2013-11-14 11:09:14 +00001040 ItemInfo info = (ItemInfo) child.getTag();
1041 int childId = mLauncher.getViewIdForItem(info);
1042
Michael Jurkaf3ca3ab2010-10-20 17:08:24 -07001043 boolean markCellsAsOccupied = !(child instanceof Folder);
Winson Chung3d503fb2011-07-13 17:25:49 -07001044 if (!layout.addViewToCellLayout(child, insert ? 0 : -1, childId, lp, markCellsAsOccupied)) {
Winson Chungaafa03c2010-06-11 17:34:16 -07001045 // TODO: This branch occurs when the workspace is adding views
1046 // outside of the defined grid
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07001047 // maybe we should be deleting these items from the LauncherModel?
Adam Cohen4caf2982013-08-20 18:54:31 -07001048 Launcher.addDumpLog(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout", true);
Winson Chungaafa03c2010-06-11 17:34:16 -07001049 }
1050
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001051 if (!(child instanceof Folder)) {
Joe Onorato0d44e942009-11-16 18:20:51 -08001052 child.setHapticFeedbackEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001053 child.setOnLongClickListener(mLongClickListener);
1054 }
Joe Onorato00acb122009-08-04 16:04:30 -04001055 if (child instanceof DropTarget) {
Winson Chungaafa03c2010-06-11 17:34:16 -07001056 mDragController.addDropTarget((DropTarget) child);
Joe Onorato00acb122009-08-04 16:04:30 -04001057 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001058 }
1059
Patrick Dubroyd0ce1ec2011-01-19 18:47:27 -08001060 /**
Patrick Dubroye708c522011-03-01 16:03:43 -08001061 * Called directly from a CellLayout (not by the framework), after we've been added as a
1062 * listener via setOnInterceptTouchEventListener(). This allows us to tell the CellLayout
1063 * that it should intercept touch events, which is not something that is normally supported.
1064 */
1065 @Override
Michael Jurkadee05892010-07-27 10:01:56 -07001066 public boolean onTouch(View v, MotionEvent event) {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001067 return (workspaceInModalState() || !isFinishedSwitchingState())
1068 || (!workspaceInModalState() && indexOfChild(v) != mCurrentPage);
Patrick Dubroye708c522011-03-01 16:03:43 -08001069 }
1070
Adam Cohenfc53cd22011-07-20 15:45:11 -07001071 public boolean isSwitchingState() {
1072 return mIsSwitchingState;
1073 }
1074
Winson Chung70442722012-02-10 15:43:22 -08001075 /** This differs from isSwitchingState in that we take into account how far the transition
1076 * has completed. */
Winson Chung9b0b2fe2012-02-24 13:03:34 -08001077 public boolean isFinishedSwitchingState() {
Winson Chung70442722012-02-10 15:43:22 -08001078 return !mIsSwitchingState || (mTransitionProgress > 0.5f);
1079 }
1080
Adam Cohended9f8d2010-11-03 13:25:16 -07001081 protected void onWindowVisibilityChanged (int visibility) {
1082 mLauncher.onWindowVisibilityChanged(visibility);
1083 }
1084
Michael Jurka5f1c5092010-09-03 14:15:02 -07001085 @Override
1086 public boolean dispatchUnhandledMove(View focused, int direction) {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001087 if (workspaceInModalState() || !isFinishedSwitchingState()) {
Michael Jurka5f1c5092010-09-03 14:15:02 -07001088 // when the home screens are shrunken, shouldn't allow side-scrolling
1089 return false;
1090 }
1091 return super.dispatchUnhandledMove(focused, direction);
1092 }
1093
1094 @Override
1095 public boolean onInterceptTouchEvent(MotionEvent ev) {
Michael Jurkad771c962011-08-09 15:00:48 -07001096 switch (ev.getAction() & MotionEvent.ACTION_MASK) {
1097 case MotionEvent.ACTION_DOWN:
Adam Cohenf8d28232011-02-01 21:47:00 -08001098 mXDown = ev.getX();
1099 mYDown = ev.getY();
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001100 mTouchDownTime = System.currentTimeMillis();
Michael Jurkad771c962011-08-09 15:00:48 -07001101 break;
1102 case MotionEvent.ACTION_POINTER_UP:
1103 case MotionEvent.ACTION_UP:
1104 if (mTouchState == TOUCH_STATE_REST) {
1105 final CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage);
Adam Cohen83e59412014-09-17 16:46:55 -07001106 if (currentPage != null && !currentPage.lastDownOnOccupiedCell()) {
Michael Jurkad771c962011-08-09 15:00:48 -07001107 onWallpaperTap(ev);
1108 }
1109 }
Adam Cohenf8d28232011-02-01 21:47:00 -08001110 }
Michael Jurka5f1c5092010-09-03 14:15:02 -07001111 return super.onInterceptTouchEvent(ev);
1112 }
1113
Jan-Willem Maarsed3fbe682014-08-19 15:27:48 -07001114 @Override
1115 public boolean onGenericMotionEvent(MotionEvent event) {
1116 // Ignore pointer scroll events if the custom content doesn't allow scrolling.
1117 if ((getScreenIdForPageIndex(getCurrentPage()) == CUSTOM_CONTENT_SCREEN_ID)
1118 && (mCustomContentCallbacks != null)
1119 && !mCustomContentCallbacks.isScrollingAllowed()) {
1120 return false;
1121 }
1122 return super.onGenericMotionEvent(event);
1123 }
1124
Adam Cohen3d509322012-06-06 14:10:04 -07001125 protected void reinflateWidgetsIfNecessary() {
1126 final int clCount = getChildCount();
1127 for (int i = 0; i < clCount; i++) {
1128 CellLayout cl = (CellLayout) getChildAt(i);
1129 ShortcutAndWidgetContainer swc = cl.getShortcutsAndWidgets();
1130 final int itemCount = swc.getChildCount();
1131 for (int j = 0; j < itemCount; j++) {
1132 View v = swc.getChildAt(j);
1133
Sunny Goyalff572272014-07-23 13:58:07 -07001134 if (v != null && v.getTag() instanceof LauncherAppWidgetInfo) {
Adam Cohen3d509322012-06-06 14:10:04 -07001135 LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
1136 LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) info.hostView;
Sunny Goyalff572272014-07-23 13:58:07 -07001137 if (lahv != null && lahv.isReinflateRequired()) {
Adam Cohen3d509322012-06-06 14:10:04 -07001138 mLauncher.removeAppWidget(info);
1139 // Remove the current widget which is inflated with the wrong orientation
1140 cl.removeView(lahv);
1141 mLauncher.bindAppWidget(info);
1142 }
1143 }
1144 }
1145 }
1146 }
1147
Michael Jurka1adf5392010-10-18 18:10:22 -07001148 @Override
1149 protected void determineScrollingStart(MotionEvent ev) {
Winson Chung70442722012-02-10 15:43:22 -08001150 if (!isFinishedSwitchingState()) return;
Adam Cohenf8d28232011-02-01 21:47:00 -08001151
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001152 float deltaX = ev.getX() - mXDown;
1153 float absDeltaX = Math.abs(deltaX);
1154 float absDeltaY = Math.abs(ev.getY() - mYDown);
Adam Cohenf8d28232011-02-01 21:47:00 -08001155
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001156 if (Float.compare(absDeltaX, 0f) == 0) return;
Adam Cohenf8d28232011-02-01 21:47:00 -08001157
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001158 float slope = absDeltaY / absDeltaX;
Winson Chung70442722012-02-10 15:43:22 -08001159 float theta = (float) Math.atan(slope);
Adam Cohenf8d28232011-02-01 21:47:00 -08001160
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001161 if (absDeltaX > mTouchSlop || absDeltaY > mTouchSlop) {
Winson Chung70442722012-02-10 15:43:22 -08001162 cancelCurrentPageLongPress();
1163 }
1164
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001165 boolean passRightSwipesToCustomContent =
1166 (mTouchDownTime - mCustomContentShowTime) > CUSTOM_CONTENT_GESTURE_DELAY;
1167
Adam Cohenaf9b0e52013-09-23 19:27:38 -07001168 boolean swipeInIgnoreDirection = isLayoutRtl() ? deltaX < 0 : deltaX > 0;
Jan-Willem Maarse2ff91c42014-07-10 15:58:12 -07001169 boolean onCustomContentScreen =
1170 getScreenIdForPageIndex(getCurrentPage()) == CUSTOM_CONTENT_SCREEN_ID;
1171 if (swipeInIgnoreDirection && onCustomContentScreen && passRightSwipesToCustomContent) {
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001172 // Pass swipes to the right to the custom content page.
1173 return;
1174 }
1175
Jan-Willem Maarse2ff91c42014-07-10 15:58:12 -07001176 if (onCustomContentScreen && (mCustomContentCallbacks != null)
1177 && !mCustomContentCallbacks.isScrollingAllowed()) {
1178 // Don't allow workspace scrolling if the current custom content screen doesn't allow
1179 // scrolling.
1180 return;
1181 }
1182
Winson Chung70442722012-02-10 15:43:22 -08001183 if (theta > MAX_SWIPE_ANGLE) {
1184 // Above MAX_SWIPE_ANGLE, we don't want to ever start scrolling the workspace
1185 return;
1186 } else if (theta > START_DAMPING_TOUCH_SLOP_ANGLE) {
1187 // Above START_DAMPING_TOUCH_SLOP_ANGLE and below MAX_SWIPE_ANGLE, we want to
1188 // increase the touch slop to make it harder to begin scrolling the workspace. This
1189 // results in vertically scrolling widgets to more easily. The higher the angle, the
1190 // more we increase touch slop.
1191 theta -= START_DAMPING_TOUCH_SLOP_ANGLE;
1192 float extraRatio = (float)
1193 Math.sqrt((theta / (MAX_SWIPE_ANGLE - START_DAMPING_TOUCH_SLOP_ANGLE)));
1194 super.determineScrollingStart(ev, 1 + TOUCH_SLOP_DAMPING_FACTOR * extraRatio);
1195 } else {
1196 // Below START_DAMPING_TOUCH_SLOP_ANGLE, we don't do anything special
1197 super.determineScrollingStart(ev);
Adam Cohenf8d28232011-02-01 21:47:00 -08001198 }
Michael Jurka1adf5392010-10-18 18:10:22 -07001199 }
1200
Patrick Dubroy1262e362010-10-06 15:49:50 -07001201 protected void onPageBeginMoving() {
Winson Chung007c6982011-06-14 13:27:53 -07001202 super.onPageBeginMoving();
1203
Michael Jurkad74c9842011-07-10 12:44:21 -07001204 if (isHardwareAccelerated()) {
Michael Jurka3a0469d2012-06-21 09:38:41 -07001205 updateChildrenLayersEnabled(false);
Michael Jurka0142d492010-08-25 17:46:15 -07001206 } else {
Michael Jurkad74c9842011-07-10 12:44:21 -07001207 if (mNextPage != INVALID_PAGE) {
1208 // we're snapping to a particular screen
1209 enableChildrenCache(mCurrentPage, mNextPage);
1210 } else {
1211 // this is when user is actively dragging a particular screen, they might
1212 // swipe it either left or right (but we won't advance by more than one screen)
1213 enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1);
1214 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001215 }
1216 }
1217
Patrick Dubroy1262e362010-10-06 15:49:50 -07001218 protected void onPageEndMoving() {
Winson Chung007c6982011-06-14 13:27:53 -07001219 super.onPageEndMoving();
1220
Michael Jurkad74c9842011-07-10 12:44:21 -07001221 if (isHardwareAccelerated()) {
Michael Jurka3a0469d2012-06-21 09:38:41 -07001222 updateChildrenLayersEnabled(false);
Michael Jurkad74c9842011-07-10 12:44:21 -07001223 } else {
1224 clearChildrenCache();
1225 }
1226
Winson Chung3bc21c32012-01-20 13:59:18 -08001227 if (mDragController.isDragging()) {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001228 if (workspaceInModalState()) {
Winson Chung3bc21c32012-01-20 13:59:18 -08001229 // If we are in springloaded mode, then force an event to check if the current touch
1230 // is under a new page (to scroll to)
Winson Chung25460a12013-04-01 18:21:28 -07001231 mDragController.forceTouchMove();
Winson Chung3bc21c32012-01-20 13:59:18 -08001232 }
Patrick Dubroy1262e362010-10-06 15:49:50 -07001233 }
Adam Cohen26976d92011-03-22 15:33:33 -07001234
1235 if (mDelayedResizeRunnable != null) {
1236 mDelayedResizeRunnable.run();
1237 mDelayedResizeRunnable = null;
1238 }
Winson Chungf0c6ae02012-03-21 16:10:31 -07001239
1240 if (mDelayedSnapToPageRunnable != null) {
1241 mDelayedSnapToPageRunnable.run();
1242 mDelayedSnapToPageRunnable = null;
1243 }
Adam Cohenaccfd562013-07-12 14:40:40 -07001244 if (mStripScreensOnPageStopMoving) {
1245 stripEmptyScreens();
1246 mStripScreensOnPageStopMoving = false;
1247 }
Adam Cohenc2d6e892014-10-16 09:49:24 -07001248
1249 if (mShouldSendPageSettled) {
1250 mLauncherOverlay.onScrollSettled();
1251 mShouldSendPageSettled = false;
1252 }
1253 }
1254
1255 protected void onScrollInteractionBegin() {
1256 super.onScrollInteractionEnd();
1257 mScrollInteractionBegan = true;
1258 }
1259
1260 protected void onScrollInteractionEnd() {
1261 super.onScrollInteractionEnd();
1262 mScrollInteractionBegan = false;
1263 if (mStartedSendingScrollEvents) {
1264 mStartedSendingScrollEvents = false;
1265 mLauncherOverlay.onScrollInteractionEnd();
1266 }
1267 }
1268
1269 public void setLauncherOverlay(LauncherOverlay overlay) {
1270 mLauncherOverlay = overlay;
1271 }
1272
1273 @Override
1274 protected void overScroll(float amount) {
1275 boolean isRtl = isLayoutRtl();
1276 boolean shouldOverScroll = (amount <= 0 && (!hasCustomContent() || isRtl)) ||
1277 (amount >= 0 && (!hasCustomContent() || !isRtl));
1278
Adam Cohen8c4ca922014-10-24 17:40:34 -07001279 boolean shouldScrollOverlay = mLauncherOverlay != null &&
1280 ((amount <= 0 && !isRtl) || (amount >= 0 && isRtl));
1281
1282 boolean shouldZeroOverlay = mLauncherOverlay != null && mLastOverlaySroll != 0 &&
1283 ((amount >= 0 && !isRtl) || (amount <= 0 && isRtl));
Adam Cohenc2d6e892014-10-16 09:49:24 -07001284
1285 if (shouldScrollOverlay) {
1286 if (!mStartedSendingScrollEvents && mScrollInteractionBegan) {
1287 mStartedSendingScrollEvents = true;
1288 mLauncherOverlay.onScrollInteractionBegin();
1289 mShouldSendPageSettled = true;
1290 }
1291 int screenSize = getViewportWidth();
1292 float f = (amount / screenSize);
1293
1294 int progress = (int) Math.abs((f * 100));
1295
Adam Cohen8c4ca922014-10-24 17:40:34 -07001296 mLastOverlaySroll = progress;
Adam Cohenc2d6e892014-10-16 09:49:24 -07001297 mLauncherOverlay.onScrollChange(progress, isRtl);
1298 } else if (shouldOverScroll) {
1299 dampedOverScroll(amount);
1300 mOverScrollEffect = acceleratedOverFactor(amount);
1301 } else {
1302 mOverScrollEffect = 0;
1303 }
Adam Cohen8c4ca922014-10-24 17:40:34 -07001304
1305 if (shouldZeroOverlay) {
1306 mLauncherOverlay.onScrollChange(0, isRtl);
1307 }
Adam Cohen89cbbb92013-09-25 16:52:48 -07001308 }
1309
1310 @Override
1311 protected void notifyPageSwitchListener() {
1312 super.notifyPageSwitchListener();
Michael Jurka0142d492010-08-25 17:46:15 -07001313
Adam Cohen6ad0e7d2013-07-24 13:55:41 -07001314 if (hasCustomContent() && getNextPage() == 0 && !mCustomContentShowing) {
Adam Cohenbffe7452013-07-22 18:21:45 -07001315 mCustomContentShowing = true;
1316 if (mCustomContentCallbacks != null) {
Selim Cinek3a8a8f72014-01-16 10:38:38 -08001317 mCustomContentCallbacks.onShow(false);
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001318 mCustomContentShowTime = System.currentTimeMillis();
Adam Cohenbffe7452013-07-22 18:21:45 -07001319 }
Adam Cohen6ad0e7d2013-07-24 13:55:41 -07001320 } else if (hasCustomContent() && getNextPage() != 0 && mCustomContentShowing) {
Adam Cohenbffe7452013-07-22 18:21:45 -07001321 mCustomContentShowing = false;
1322 if (mCustomContentCallbacks != null) {
1323 mCustomContentCallbacks.onHide();
Adam Cohenbffe7452013-07-22 18:21:45 -07001324 }
1325 }
Chris Craik01f2d7f2013-10-01 14:41:56 -07001326 }
Michael Jurka0142d492010-08-25 17:46:15 -07001327
Adam Cohen6fecd412013-10-02 17:41:50 -07001328 protected CustomContentCallbacks getCustomContentCallbacks() {
1329 return mCustomContentCallbacks;
1330 }
1331
Adam Cohen824fcb32014-05-21 23:01:25 +00001332 protected void setWallpaperDimension() {
Michael Jurkaa6a05472013-11-13 17:59:46 +01001333 new AsyncTask<Void, Void, Void>() {
1334 public Void doInBackground(Void ... args) {
1335 String spKey = WallpaperCropActivity.getSharedPreferencesKey();
1336 SharedPreferences sp =
1337 mLauncher.getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
Adam Cohenc29217b2014-05-18 03:17:15 +00001338 LauncherWallpaperPickerActivity.suggestWallpaperDimension(mLauncher.getResources(),
Adam Cohenea90f832014-05-21 15:03:34 -07001339 sp, mLauncher.getWindowManager(), mWallpaperManager,
1340 mLauncher.overrideWallpaperDimensions());
Michael Jurkaa6a05472013-11-13 17:59:46 +01001341 return null;
1342 }
1343 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
Michael Jurkac5b262c2011-01-12 20:24:50 -08001344 }
1345
Winson Chungf0c6ae02012-03-21 16:10:31 -07001346 protected void snapToPage(int whichPage, Runnable r) {
Adam Cohenad4e15c2013-10-17 16:21:35 -07001347 snapToPage(whichPage, SLOW_PAGE_SNAP_ANIMATION_DURATION, r);
1348 }
1349
1350 protected void snapToPage(int whichPage, int duration, Runnable r) {
Winson Chungf0c6ae02012-03-21 16:10:31 -07001351 if (mDelayedSnapToPageRunnable != null) {
1352 mDelayedSnapToPageRunnable.run();
1353 }
1354 mDelayedSnapToPageRunnable = r;
Adam Cohenad4e15c2013-10-17 16:21:35 -07001355 snapToPage(whichPage, duration);
Winson Chungf0c6ae02012-03-21 16:10:31 -07001356 }
1357
Adam Cohendb364c32014-05-20 14:23:40 -07001358 public void snapToScreenId(long screenId) {
1359 snapToScreenId(screenId, null);
1360 }
1361
Adam Cohendcd297f2013-06-18 13:13:40 -07001362 protected void snapToScreenId(long screenId, Runnable r) {
1363 snapToPage(getPageIndexForScreenId(screenId), r);
1364 }
1365
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001366 class WallpaperOffsetInterpolator implements Choreographer.FrameCallback {
1367 float mFinalOffset = 0.0f;
Michael Jurka046cf342013-09-14 16:40:34 +02001368 float mCurrentOffset = 0.5f; // to force an initial update
Michael Jurkafe09cb72013-08-27 15:48:58 +02001369 boolean mWaitingForUpdate;
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001370 Choreographer mChoreographer;
1371 Interpolator mInterpolator;
1372 boolean mAnimating;
1373 long mAnimationStartTime;
1374 float mAnimationStartOffset;
Michael Jurka2f817312013-09-20 03:03:42 +02001375 private final int ANIMATION_DURATION = 250;
1376 // Don't use all the wallpaper for parallax until you have at least this many pages
Michael Jurkafe0ace32013-10-03 01:05:14 -07001377 private final int MIN_PARALLAX_PAGE_SPAN = 3;
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001378 int mNumScreens;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001379
1380 public WallpaperOffsetInterpolator() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001381 mChoreographer = Choreographer.getInstance();
1382 mInterpolator = new DecelerateInterpolator(1.5f);
Michael Jurkaab1983f2011-01-18 15:50:17 -08001383 }
1384
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001385 @Override
1386 public void doFrame(long frameTimeNanos) {
Michael Jurkafe09cb72013-08-27 15:48:58 +02001387 updateOffset(false);
1388 }
1389
Michael Jurkafe09cb72013-08-27 15:48:58 +02001390 private void updateOffset(boolean force) {
1391 if (mWaitingForUpdate || force) {
1392 mWaitingForUpdate = false;
Michael Jurka1bd90b02013-09-05 23:10:14 +02001393 if (computeScrollOffset() && mWindowToken != null) {
1394 try {
1395 mWallpaperManager.setWallpaperOffsets(mWindowToken,
1396 mWallpaperOffset.getCurrX(), 0.5f);
1397 setWallpaperOffsetSteps();
1398 } catch (IllegalArgumentException e) {
1399 Log.e(TAG, "Error updating wallpaper offset: " + e);
1400 }
Michael Jurkafe09cb72013-08-27 15:48:58 +02001401 }
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001402 }
Michael Jurkaca5b8362011-01-27 13:23:26 -08001403 }
1404
Michael Jurkaab1983f2011-01-18 15:50:17 -08001405 public boolean computeScrollOffset() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001406 final float oldOffset = mCurrentOffset;
1407 if (mAnimating) {
1408 long durationSinceAnimation = System.currentTimeMillis() - mAnimationStartTime;
1409 float t0 = durationSinceAnimation / (float) ANIMATION_DURATION;
1410 float t1 = mInterpolator.getInterpolation(t0);
1411 mCurrentOffset = mAnimationStartOffset +
1412 (mFinalOffset - mAnimationStartOffset) * t1;
1413 mAnimating = durationSinceAnimation < ANIMATION_DURATION;
Michael Jurkaca5b8362011-01-27 13:23:26 -08001414 } else {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001415 mCurrentOffset = mFinalOffset;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001416 }
Michael Jurkaca5b8362011-01-27 13:23:26 -08001417
Michael Jurkafe09cb72013-08-27 15:48:58 +02001418 if (Math.abs(mCurrentOffset - mFinalOffset) > 0.0000001f) {
1419 scheduleUpdate();
1420 }
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001421 if (Math.abs(oldOffset - mCurrentOffset) > 0.0000001f) {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001422 return true;
1423 }
1424 return false;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001425 }
1426
Michael Jurkafe09cb72013-08-27 15:48:58 +02001427 private float wallpaperOffsetForCurrentScroll() {
1428 if (getChildCount() <= 1) {
1429 return 0;
1430 }
Michael Jurka2f817312013-09-20 03:03:42 +02001431
1432 // Exclude the leftmost page
Michael Jurkafe0ace32013-10-03 01:05:14 -07001433 int emptyExtraPages = numEmptyScreensToIgnore();
1434 int firstIndex = numCustomPages();
Michael Jurka2f817312013-09-20 03:03:42 +02001435 // Exclude the last extra empty screen (if we have > MIN_PARALLAX_PAGE_SPAN pages)
Michael Jurkafe0ace32013-10-03 01:05:14 -07001436 int lastIndex = getChildCount() - 1 - emptyExtraPages;
1437 if (isLayoutRtl()) {
1438 int temp = firstIndex;
1439 firstIndex = lastIndex;
1440 lastIndex = temp;
1441 }
Michael Jurka2f817312013-09-20 03:03:42 +02001442
Michael Jurkafe09cb72013-08-27 15:48:58 +02001443 int firstPageScrollX = getScrollForPage(firstIndex);
1444 int scrollRange = getScrollForPage(lastIndex) - firstPageScrollX;
1445 if (scrollRange == 0) {
1446 return 0;
1447 } else {
Michael Jurka2f817312013-09-20 03:03:42 +02001448 // TODO: do different behavior if it's a live wallpaper?
Michael Jurkafe0ace32013-10-03 01:05:14 -07001449 // Sometimes the left parameter of the pages is animated during a layout transition;
1450 // this parameter offsets it to keep the wallpaper from animating as well
Michael Jurkafe0ace32013-10-03 01:05:14 -07001451 int adjustedScroll =
Michael Jurka8fd3adc2013-10-16 13:50:24 -07001452 getScrollX() - firstPageScrollX - getLayoutTransitionOffsetForPage(0);
Michael Jurkafe0ace32013-10-03 01:05:14 -07001453 float offset = Math.min(1, adjustedScroll / (float) scrollRange);
Michael Jurka2f817312013-09-20 03:03:42 +02001454 offset = Math.max(0, offset);
1455 // Don't use up all the wallpaper parallax until you have at least
1456 // MIN_PARALLAX_PAGE_SPAN pages
Michael Jurkafe0ace32013-10-03 01:05:14 -07001457 int numScrollingPages = getNumScreensExcludingEmptyAndCustom();
Michael Jurka2a4f4922014-01-29 16:32:39 +01001458 int parallaxPageSpan;
1459 if (mWallpaperIsLiveWallpaper) {
1460 parallaxPageSpan = numScrollingPages - 1;
1461 } else {
1462 parallaxPageSpan = Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages - 1);
1463 }
1464 mNumPagesForWallpaperParallax = parallaxPageSpan;
1465
Michael Jurkafe0ace32013-10-03 01:05:14 -07001466 // On RTL devices, push the wallpaper offset to the right if we don't have enough
1467 // pages (ie if numScrollingPages < MIN_PARALLAX_PAGE_SPAN)
1468 int padding = isLayoutRtl() ? parallaxPageSpan - numScrollingPages + 1 : 0;
1469 return offset * (padding + numScrollingPages - 1) / parallaxPageSpan;
Michael Jurkafe09cb72013-08-27 15:48:58 +02001470 }
1471 }
1472
Michael Jurkafe0ace32013-10-03 01:05:14 -07001473 private int numEmptyScreensToIgnore() {
1474 int numScrollingPages = getChildCount() - numCustomPages();
1475 if (numScrollingPages >= MIN_PARALLAX_PAGE_SPAN && hasExtraEmptyScreen()) {
Michael Jurka2f817312013-09-20 03:03:42 +02001476 return 1;
1477 } else {
1478 return 0;
1479 }
1480 }
1481
Michael Jurkafe0ace32013-10-03 01:05:14 -07001482 private int getNumScreensExcludingEmptyAndCustom() {
1483 int numScrollingPages = getChildCount() - numEmptyScreensToIgnore() - numCustomPages();
Michael Jurka2f817312013-09-20 03:03:42 +02001484 return numScrollingPages;
1485 }
1486
Michael Jurkafe09cb72013-08-27 15:48:58 +02001487 public void syncWithScroll() {
1488 float offset = wallpaperOffsetForCurrentScroll();
1489 mWallpaperOffset.setFinalX(offset);
1490 updateOffset(true);
1491 }
1492
Michael Jurkaab1983f2011-01-18 15:50:17 -08001493 public float getCurrX() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001494 return mCurrentOffset;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001495 }
1496
1497 public float getFinalX() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001498 return mFinalOffset;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001499 }
1500
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001501 private void animateToFinal() {
1502 mAnimating = true;
1503 mAnimationStartOffset = mCurrentOffset;
1504 mAnimationStartTime = System.currentTimeMillis();
Michael Jurkaab1983f2011-01-18 15:50:17 -08001505 }
1506
Michael Jurkafe09cb72013-08-27 15:48:58 +02001507 private void setWallpaperOffsetSteps() {
1508 // Set wallpaper offset steps (1 / (number of screens - 1))
Michael Jurka2a4f4922014-01-29 16:32:39 +01001509 float xOffset = 1.0f / mNumPagesForWallpaperParallax;
1510 if (xOffset != mLastSetWallpaperOffsetSteps) {
1511 mWallpaperManager.setWallpaperOffsetSteps(xOffset, 1.0f);
1512 mLastSetWallpaperOffsetSteps = xOffset;
1513 }
Michael Jurkafe09cb72013-08-27 15:48:58 +02001514 }
1515
Michael Jurkaab1983f2011-01-18 15:50:17 -08001516 public void setFinalX(float x) {
Michael Jurkafe09cb72013-08-27 15:48:58 +02001517 scheduleUpdate();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001518 mFinalOffset = Math.max(0f, Math.min(x, 1.0f));
Michael Jurkafe0ace32013-10-03 01:05:14 -07001519 if (getNumScreensExcludingEmptyAndCustom() != mNumScreens) {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001520 if (mNumScreens > 0) {
1521 // Don't animate if we're going from 0 screens
1522 animateToFinal();
1523 }
Michael Jurkafe0ace32013-10-03 01:05:14 -07001524 mNumScreens = getNumScreensExcludingEmptyAndCustom();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001525 }
Michael Jurkaab1983f2011-01-18 15:50:17 -08001526 }
1527
Michael Jurkafe09cb72013-08-27 15:48:58 +02001528 private void scheduleUpdate() {
1529 if (!mWaitingForUpdate) {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001530 mChoreographer.postFrameCallback(this);
Michael Jurkafe09cb72013-08-27 15:48:58 +02001531 mWaitingForUpdate = true;
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001532 }
Michael Jurkaab1983f2011-01-18 15:50:17 -08001533 }
1534
1535 public void jumpToFinal() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001536 mCurrentOffset = mFinalOffset;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001537 }
Dianne Hackborn8f573952009-08-10 23:21:09 -07001538 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001539
Michael Jurka340c5f32010-10-21 16:49:19 -07001540 @Override
1541 public void computeScroll() {
1542 super.computeScroll();
Michael Jurkafe09cb72013-08-27 15:48:58 +02001543 mWallpaperOffset.syncWithScroll();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001544 }
1545
Jason Monked05f092014-04-24 10:13:05 -04001546 @Override
1547 public void announceForAccessibility(CharSequence text) {
1548 // Don't announce if apps is on top of us.
1549 if (!mLauncher.isAllAppsVisible()) {
1550 super.announceForAccessibility(text);
1551 }
1552 }
1553
Patrick Dubroy94f78a52011-02-28 17:39:16 -08001554 void showOutlines() {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001555 if (!workspaceInModalState() && !mIsSwitchingState) {
Winson Chung9171e6d2010-11-17 17:39:27 -08001556 if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
1557 if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
Michael Jurka2ecf9952012-06-18 12:52:28 -07001558 mChildrenOutlineFadeInAnimation = LauncherAnimUtils.ofFloat(this, "childrenOutlineAlpha", 1.0f);
Winson Chung9171e6d2010-11-17 17:39:27 -08001559 mChildrenOutlineFadeInAnimation.setDuration(CHILDREN_OUTLINE_FADE_IN_DURATION);
1560 mChildrenOutlineFadeInAnimation.start();
Michael Jurka3e7c7632010-10-02 16:01:03 -07001561 }
Adam Cohenf34bab52010-09-30 14:11:56 -07001562 }
1563
Patrick Dubroy94f78a52011-02-28 17:39:16 -08001564 void hideOutlines() {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001565 if (!workspaceInModalState() && !mIsSwitchingState) {
Winson Chung9171e6d2010-11-17 17:39:27 -08001566 if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
1567 if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
Michael Jurka2ecf9952012-06-18 12:52:28 -07001568 mChildrenOutlineFadeOutAnimation = LauncherAnimUtils.ofFloat(this, "childrenOutlineAlpha", 0.0f);
Winson Chung9171e6d2010-11-17 17:39:27 -08001569 mChildrenOutlineFadeOutAnimation.setDuration(CHILDREN_OUTLINE_FADE_OUT_DURATION);
1570 mChildrenOutlineFadeOutAnimation.setStartDelay(CHILDREN_OUTLINE_FADE_OUT_DELAY);
1571 mChildrenOutlineFadeOutAnimation.start();
Michael Jurka3e7c7632010-10-02 16:01:03 -07001572 }
Adam Cohenf34bab52010-09-30 14:11:56 -07001573 }
1574
Patrick Dubroy94f78a52011-02-28 17:39:16 -08001575 public void showOutlinesTemporarily() {
1576 if (!mIsPageMoving && !isTouchActive()) {
1577 snapToPage(mCurrentPage);
1578 }
1579 }
1580
Winson Chung9171e6d2010-11-17 17:39:27 -08001581 public void setChildrenOutlineAlpha(float alpha) {
1582 mChildrenOutlineAlpha = alpha;
Adam Cohenf34bab52010-09-30 14:11:56 -07001583 for (int i = 0; i < getChildCount(); i++) {
1584 CellLayout cl = (CellLayout) getChildAt(i);
1585 cl.setBackgroundAlpha(alpha);
1586 }
1587 }
1588
Winson Chung9171e6d2010-11-17 17:39:27 -08001589 public float getChildrenOutlineAlpha() {
1590 return mChildrenOutlineAlpha;
1591 }
1592
Michael Jurka7bdb25a2011-08-03 15:16:44 -07001593 private void animateBackgroundGradient(float finalAlpha, boolean animated) {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001594 final DragLayer dragLayer = mLauncher.getDragLayer();
1595
Michael Jurkab9e14972011-07-25 17:57:40 -07001596 if (mBackgroundFadeInAnimation != null) {
1597 mBackgroundFadeInAnimation.cancel();
1598 mBackgroundFadeInAnimation = null;
1599 }
1600 if (mBackgroundFadeOutAnimation != null) {
1601 mBackgroundFadeOutAnimation.cancel();
1602 mBackgroundFadeOutAnimation = null;
1603 }
Adam Cohen6c5891a2014-07-09 23:53:15 -07001604 float startAlpha = dragLayer.getBackgroundAlpha();
Michael Jurka7bdb25a2011-08-03 15:16:44 -07001605 if (finalAlpha != startAlpha) {
1606 if (animated) {
Michael Jurkaf1ad6082013-03-13 12:55:46 +01001607 mBackgroundFadeOutAnimation =
1608 LauncherAnimUtils.ofFloat(this, startAlpha, finalAlpha);
Michael Jurka7bdb25a2011-08-03 15:16:44 -07001609 mBackgroundFadeOutAnimation.addUpdateListener(new AnimatorUpdateListener() {
1610 public void onAnimationUpdate(ValueAnimator animation) {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001611 dragLayer.setBackgroundAlpha(
1612 ((Float)animation.getAnimatedValue()).floatValue());
Michael Jurka7bdb25a2011-08-03 15:16:44 -07001613 }
1614 });
1615 mBackgroundFadeOutAnimation.setInterpolator(new DecelerateInterpolator(1.5f));
1616 mBackgroundFadeOutAnimation.setDuration(BACKGROUND_FADE_OUT_DURATION);
1617 mBackgroundFadeOutAnimation.start();
1618 } else {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001619 dragLayer.setBackgroundAlpha(finalAlpha);
Michael Jurka7bdb25a2011-08-03 15:16:44 -07001620 }
Michael Jurkab9e14972011-07-25 17:57:40 -07001621 }
Winson Chung9171e6d2010-11-17 17:39:27 -08001622 }
1623
Adam Cohen68d73932010-11-15 10:50:58 -08001624 float backgroundAlphaInterpolator(float r) {
1625 float pivotA = 0.1f;
1626 float pivotB = 0.4f;
1627 if (r < pivotA) {
1628 return 0;
1629 } else if (r > pivotB) {
1630 return 1.0f;
1631 } else {
1632 return (r - pivotA)/(pivotB - pivotA);
1633 }
1634 }
1635
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001636 private void updatePageAlphaValues(int screenCenter) {
Michael Jurka869390b2012-05-06 15:55:19 -07001637 boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
1638 if (mWorkspaceFadeInAdjacentScreens &&
Adam Cohenc50438c2014-08-19 17:43:05 -07001639 !workspaceInModalState() &&
Michael Jurka869390b2012-05-06 15:55:19 -07001640 !mIsSwitchingState &&
1641 !isInOverscroll) {
Adam Cohen84a465a2013-11-11 18:49:56 +00001642 for (int i = numCustomPages(); i < getChildCount(); i++) {
Michael Jurka869390b2012-05-06 15:55:19 -07001643 CellLayout child = (CellLayout) getChildAt(i);
1644 if (child != null) {
1645 float scrollProgress = getScrollProgress(screenCenter, child, i);
Adam Cohen73894962011-10-31 13:17:17 -07001646 float alpha = 1 - Math.abs(scrollProgress);
Michael Jurka869390b2012-05-06 15:55:19 -07001647 child.getShortcutsAndWidgets().setAlpha(alpha);
Adam Cohenc50438c2014-08-19 17:43:05 -07001648 //child.setBackgroundAlphaMultiplier(1 - alpha);
Adam Cohen73894962011-10-31 13:17:17 -07001649 }
Adam Cohenf34bab52010-09-30 14:11:56 -07001650 }
1651 }
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001652 }
1653
1654 private void setChildrenBackgroundAlphaMultipliers(float a) {
1655 for (int i = 0; i < getChildCount(); i++) {
1656 CellLayout child = (CellLayout) getChildAt(i);
1657 child.setBackgroundAlphaMultiplier(a);
1658 }
1659 }
1660
Winson Chung98ca0f72013-07-29 12:58:51 -07001661 public boolean hasCustomContent() {
Adam Cohenedb40762013-07-18 16:45:45 -07001662 return (mScreenOrder.size() > 0 && mScreenOrder.get(0) == CUSTOM_CONTENT_SCREEN_ID);
1663 }
1664
Michael Jurkafe0ace32013-10-03 01:05:14 -07001665 public int numCustomPages() {
1666 return hasCustomContent() ? 1 : 0;
1667 }
1668
Adam Cohenbffe7452013-07-22 18:21:45 -07001669 public boolean isOnOrMovingToCustomContent() {
1670 return hasCustomContent() && getNextPage() == 0;
1671 }
1672
Adam Cohenedb40762013-07-18 16:45:45 -07001673 private void updateStateForCustomContent(int screenCenter) {
Dave Hawkeya8881582013-09-17 15:55:33 -06001674 float translationX = 0;
1675 float progress = 0;
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01001676 if (hasCustomContent()) {
Adam Cohenedb40762013-07-18 16:45:45 -07001677 int index = mScreenOrder.indexOf(CUSTOM_CONTENT_SCREEN_ID);
Adam Cohen564a2e72013-10-09 14:47:32 -07001678
Adam Cohena45de072013-10-11 16:07:47 -07001679 int scrollDelta = getScrollX() - getScrollForPage(index) -
1680 getLayoutTransitionOffsetForPage(index);
1681 float scrollRange = getScrollForPage(index + 1) - getScrollForPage(index);
1682 translationX = scrollRange - scrollDelta;
1683 progress = (scrollRange - scrollDelta) / scrollRange;
Adam Cohenaf9b0e52013-09-23 19:27:38 -07001684
1685 if (isLayoutRtl()) {
1686 translationX = Math.min(0, translationX);
1687 } else {
1688 translationX = Math.max(0, translationX);
1689 }
Adam Cohen955806d2013-07-19 16:36:43 -07001690 progress = Math.max(0, progress);
Dave Hawkeya8881582013-09-17 15:55:33 -06001691 }
Adam Cohenedb40762013-07-18 16:45:45 -07001692
Dave Hawkeya8881582013-09-17 15:55:33 -06001693 if (Float.compare(progress, mLastCustomContentScrollProgress) == 0) return;
Adam Cohen84add1d2013-10-14 16:29:02 -07001694
1695 CellLayout cc = mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID);
Adam Cohen6c5891a2014-07-09 23:53:15 -07001696 if (progress > 0 && cc.getVisibility() != VISIBLE && !workspaceInModalState()) {
Adam Cohen84add1d2013-10-14 16:29:02 -07001697 cc.setVisibility(VISIBLE);
1698 }
1699
Dave Hawkeya8881582013-09-17 15:55:33 -06001700 mLastCustomContentScrollProgress = progress;
Adam Cohenc36fa5c2013-08-29 11:54:42 -07001701
Adam Cohen6c5891a2014-07-09 23:53:15 -07001702 mLauncher.getDragLayer().setBackgroundAlpha(progress * 0.8f);
Adam Cohen22cba7f2013-07-19 16:14:00 -07001703
Dave Hawkeya8881582013-09-17 15:55:33 -06001704 if (mLauncher.getHotseat() != null) {
1705 mLauncher.getHotseat().setTranslationX(translationX);
1706 }
Adam Cohenc36fa5c2013-08-29 11:54:42 -07001707
Dave Hawkeya8881582013-09-17 15:55:33 -06001708 if (getPageIndicator() != null) {
1709 getPageIndicator().setTranslationX(translationX);
1710 }
Adam Cohenc36fa5c2013-08-29 11:54:42 -07001711
Dave Hawkeya8881582013-09-17 15:55:33 -06001712 if (mCustomContentCallbacks != null) {
1713 mCustomContentCallbacks.onScrollProgressChanged(progress);
Adam Cohenedb40762013-07-18 16:45:45 -07001714 }
1715 }
1716
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001717 @Override
Adam Cohen53805212013-10-01 10:39:23 -07001718 protected OnClickListener getPageIndicatorClickListener() {
1719 AccessibilityManager am = (AccessibilityManager)
1720 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
1721 if (!am.isTouchExplorationEnabled()) {
1722 return null;
1723 }
1724 OnClickListener listener = new OnClickListener() {
1725 @Override
1726 public void onClick(View arg0) {
1727 enterOverviewMode();
1728 }
1729 };
1730 return listener;
1731 }
1732
1733 @Override
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001734 protected void screenScrolled(int screenCenter) {
Winson Chung52aee602013-01-30 12:01:02 -08001735 final boolean isRtl = isLayoutRtl();
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001736 super.screenScrolled(screenCenter);
1737
1738 updatePageAlphaValues(screenCenter);
Adam Cohenedb40762013-07-18 16:45:45 -07001739 updateStateForCustomContent(screenCenter);
Michael Jurkad51f33a2012-06-28 15:35:26 -07001740 enableHwLayersOnVisiblePages();
Adam Cohenf34bab52010-09-30 14:11:56 -07001741
Adam Cohenc50438c2014-08-19 17:43:05 -07001742 boolean shouldOverScroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
Adam Cohenaf9b0e52013-09-23 19:27:38 -07001743
1744 if (shouldOverScroll) {
Winson Chung52aee602013-01-30 12:01:02 -08001745 int index = 0;
Winson Chung52aee602013-01-30 12:01:02 -08001746 final int lowerIndex = 0;
1747 final int upperIndex = getChildCount() - 1;
Adam Cohena29f5042013-09-26 14:29:35 -07001748
1749 final boolean isLeftPage = mOverScrollX < 0;
1750 index = (!isRtl && isLeftPage) || (isRtl && !isLeftPage) ? lowerIndex : upperIndex;
Winson Chung52aee602013-01-30 12:01:02 -08001751
Adam Cohenb5ba0972011-09-07 18:02:31 -07001752 CellLayout cl = (CellLayout) getChildAt(index);
Adam Cohen1e4359c2014-08-18 13:12:16 -07001753 float effect = Math.abs(mOverScrollEffect);
1754 cl.setOverScrollAmount(Math.abs(effect), isLeftPage);
Adam Cohena29f5042013-09-26 14:29:35 -07001755
Adam Cohen1e4359c2014-08-18 13:12:16 -07001756 mOverscrollEffectSet = true;
Adam Cohenb5ba0972011-09-07 18:02:31 -07001757 } else {
Adam Cohen1e4359c2014-08-18 13:12:16 -07001758 if (mOverscrollEffectSet && getChildCount() > 0) {
1759 mOverscrollEffectSet = false;
1760 ((CellLayout) getChildAt(0)).setOverScrollAmount(0, false);
1761 ((CellLayout) getChildAt(getChildCount() - 1)).setOverScrollAmount(0, false);
Adam Cohen7842d7f2011-09-12 15:28:15 -07001762 }
Adam Cohenb5ba0972011-09-07 18:02:31 -07001763 }
1764 }
1765
Joe Onorato00acb122009-08-04 16:04:30 -04001766 protected void onAttachedToWindow() {
1767 super.onAttachedToWindow();
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001768 mWindowToken = getWindowToken();
Joe Onorato956091b2010-02-19 12:47:40 -08001769 computeScroll();
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001770 mDragController.setWindowToken(mWindowToken);
1771 }
1772
1773 protected void onDetachedFromWindow() {
Romain Guye6661952013-08-08 19:13:22 -07001774 super.onDetachedFromWindow();
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001775 mWindowToken = null;
Joe Onorato00acb122009-08-04 16:04:30 -04001776 }
1777
Adam Cohen53805212013-10-01 10:39:23 -07001778 protected void onResume() {
1779 if (getPageIndicator() != null) {
1780 // In case accessibility state has changed, we need to perform this on every
1781 // attach to window
Adam Cohend36d9472013-10-10 15:32:41 -07001782 OnClickListener listener = getPageIndicatorClickListener();
1783 if (listener != null) {
1784 getPageIndicator().setOnClickListener(listener);
1785 }
Adam Cohen53805212013-10-01 10:39:23 -07001786 }
1787 AccessibilityManager am = (AccessibilityManager)
1788 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
1789 sAccessibilityEnabled = am.isEnabled();
Michael Jurkaa6a05472013-11-13 17:59:46 +01001790
1791 // Update wallpaper dimensions if they were changed since last onResume
1792 // (we also always set the wallpaper dimensions in the constructor)
1793 if (LauncherAppState.getInstance().hasWallpaperChangedSinceLastCheck()) {
Adam Cohen824fcb32014-05-21 23:01:25 +00001794 setWallpaperDimension();
Michael Jurkaa6a05472013-11-13 17:59:46 +01001795 }
Michael Jurka2a4f4922014-01-29 16:32:39 +01001796 mWallpaperIsLiveWallpaper = mWallpaperManager.getWallpaperInfo() != null;
1797 // Force the wallpaper offset steps to be set again, because another app might have changed
1798 // them
1799 mLastSetWallpaperOffsetSteps = 0f;
Adam Cohen53805212013-10-01 10:39:23 -07001800 }
1801
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001802 @Override
Michael Jurka28750fb2010-09-24 17:43:49 -07001803 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Michael Jurkac5b262c2011-01-12 20:24:50 -08001804 if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
Michael Jurkafe09cb72013-08-27 15:48:58 +02001805 mWallpaperOffset.syncWithScroll();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001806 mWallpaperOffset.jumpToFinal();
Michael Jurkac5b262c2011-01-12 20:24:50 -08001807 }
Michael Jurka0142d492010-08-25 17:46:15 -07001808 super.onLayout(changed, left, top, right, bottom);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001809 }
1810
1811 @Override
Winson Chung9171e6d2010-11-17 17:39:27 -08001812 protected void onDraw(Canvas canvas) {
Winson Chung9171e6d2010-11-17 17:39:27 -08001813 super.onDraw(canvas);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001814
1815 // Call back to LauncherModel to finish binding after the first draw
Romain Guyeeacd562012-10-10 18:47:33 -07001816 post(mBindPages);
Winson Chung9171e6d2010-11-17 17:39:27 -08001817 }
1818
Michael Jurkadfab7f02011-11-18 13:01:04 -08001819 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001820 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
Joe Onorato67886212009-09-14 19:05:05 -04001821 if (!mLauncher.isAllAppsVisible()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001822 final Folder openFolder = getOpenFolder();
1823 if (openFolder != null) {
1824 return openFolder.requestFocus(direction, previouslyFocusedRect);
1825 } else {
Michael Jurka0142d492010-08-25 17:46:15 -07001826 return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001827 }
1828 }
1829 return false;
1830 }
1831
1832 @Override
Winson Chung97d85d22011-04-13 11:27:36 -07001833 public int getDescendantFocusability() {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001834 if (workspaceInModalState()) {
Winson Chung97d85d22011-04-13 11:27:36 -07001835 return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
1836 }
1837 return super.getDescendantFocusability();
1838 }
1839
1840 @Override
Romain Guyc2e24c02009-06-01 16:11:41 -07001841 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
Joe Onorato67886212009-09-14 19:05:05 -04001842 if (!mLauncher.isAllAppsVisible()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001843 final Folder openFolder = getOpenFolder();
Michael Jurka0142d492010-08-25 17:46:15 -07001844 if (openFolder != null) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001845 openFolder.addFocusables(views, direction);
Michael Jurka0142d492010-08-25 17:46:15 -07001846 } else {
1847 super.addFocusables(views, direction, focusableMode);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001848 }
1849 }
1850 }
1851
Adam Cohen6c5891a2014-07-09 23:53:15 -07001852 public boolean workspaceInModalState() {
1853 return mState != State.NORMAL;
Michael Jurkad74c9842011-07-10 12:44:21 -07001854 }
1855
Michael Jurka0142d492010-08-25 17:46:15 -07001856 void enableChildrenCache(int fromPage, int toPage) {
1857 if (fromPage > toPage) {
1858 final int temp = fromPage;
1859 fromPage = toPage;
1860 toPage = temp;
Mike Cleron3a2b3f22009-11-05 17:17:42 -08001861 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001862
Michael Jurkadee05892010-07-27 10:01:56 -07001863 final int screenCount = getChildCount();
Adam Powellfea5d022010-04-29 11:42:45 -07001864
Michael Jurka0142d492010-08-25 17:46:15 -07001865 fromPage = Math.max(fromPage, 0);
1866 toPage = Math.min(toPage, screenCount - 1);
Adam Powellfea5d022010-04-29 11:42:45 -07001867
Michael Jurka0142d492010-08-25 17:46:15 -07001868 for (int i = fromPage; i <= toPage; i++) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001869 final CellLayout layout = (CellLayout) getChildAt(i);
1870 layout.setChildrenDrawnWithCacheEnabled(true);
1871 layout.setChildrenDrawingCacheEnabled(true);
1872 }
1873 }
1874
1875 void clearChildrenCache() {
Michael Jurkadee05892010-07-27 10:01:56 -07001876 final int screenCount = getChildCount();
1877 for (int i = 0; i < screenCount; i++) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001878 final CellLayout layout = (CellLayout) getChildAt(i);
1879 layout.setChildrenDrawnWithCacheEnabled(false);
Adam Cohen8182e5b2011-08-01 15:43:31 -07001880 // In software mode, we don't want the items to continue to be drawn into bitmaps
1881 if (!isHardwareAccelerated()) {
1882 layout.setChildrenDrawingCacheEnabled(false);
1883 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001884 }
1885 }
1886
Michael Jurka3a0469d2012-06-21 09:38:41 -07001887 private void updateChildrenLayersEnabled(boolean force) {
Adam Cohen6c5891a2014-07-09 23:53:15 -07001888 boolean small = mState == State.OVERVIEW || mIsSwitchingState;
Michael Jurka3a0469d2012-06-21 09:38:41 -07001889 boolean enableChildrenLayers = force || small || mAnimatingViewIntoPlace || isPageMoving();
Michael Jurkad74c9842011-07-10 12:44:21 -07001890
1891 if (enableChildrenLayers != mChildrenLayersEnabled) {
1892 mChildrenLayersEnabled = enableChildrenLayers;
Michael Jurkad51f33a2012-06-28 15:35:26 -07001893 if (mChildrenLayersEnabled) {
1894 enableHwLayersOnVisiblePages();
1895 } else {
1896 for (int i = 0; i < getPageCount(); i++) {
1897 final CellLayout cl = (CellLayout) getChildAt(i);
Chris Craik01f2d7f2013-10-01 14:41:56 -07001898 cl.enableHardwareLayer(false);
Michael Jurkad51f33a2012-06-28 15:35:26 -07001899 }
1900 }
1901 }
1902 }
1903
1904 private void enableHwLayersOnVisiblePages() {
1905 if (mChildrenLayersEnabled) {
1906 final int screenCount = getChildCount();
1907 getVisiblePages(mTempVisiblePagesRange);
1908 int leftScreen = mTempVisiblePagesRange[0];
1909 int rightScreen = mTempVisiblePagesRange[1];
1910 if (leftScreen == rightScreen) {
1911 // make sure we're caching at least two pages always
1912 if (rightScreen < screenCount - 1) {
1913 rightScreen++;
1914 } else if (leftScreen > 0) {
1915 leftScreen--;
1916 }
1917 }
Chris Craik01f2d7f2013-10-01 14:41:56 -07001918
1919 final CellLayout customScreen = mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID);
Michael Jurkad51f33a2012-06-28 15:35:26 -07001920 for (int i = 0; i < screenCount; i++) {
Michael Jurka47639b92013-01-14 12:42:27 +01001921 final CellLayout layout = (CellLayout) getPageAt(i);
Chris Craik01f2d7f2013-10-01 14:41:56 -07001922
1923 // enable layers between left and right screen inclusive, except for the
1924 // customScreen, which may animate its content during transitions.
1925 boolean enableLayer = layout != customScreen &&
1926 leftScreen <= i && i <= rightScreen && shouldDrawChild(layout);
1927 layout.enableHardwareLayer(enableLayer);
Michael Jurkad74c9842011-07-10 12:44:21 -07001928 }
Michael Jurkace7e05f2011-02-01 22:02:35 -08001929 }
Michael Jurkace7e05f2011-02-01 22:02:35 -08001930 }
1931
Michael Jurka3a0469d2012-06-21 09:38:41 -07001932 public void buildPageHardwareLayers() {
1933 // force layers to be enabled just for the call to buildLayer
1934 updateChildrenLayersEnabled(true);
1935 if (getWindowToken() != null) {
1936 final int childCount = getChildCount();
1937 for (int i = 0; i < childCount; i++) {
1938 CellLayout cl = (CellLayout) getChildAt(i);
Michael Jurkad51f33a2012-06-28 15:35:26 -07001939 cl.buildHardwareLayer();
Michael Jurka3a0469d2012-06-21 09:38:41 -07001940 }
1941 }
1942 updateChildrenLayersEnabled(false);
1943 }
1944
Jeff Brown1d0867c2010-12-02 18:27:39 -08001945 protected void onWallpaperTap(MotionEvent ev) {
1946 final int[] position = mTempCell;
1947 getLocationOnScreen(position);
1948
1949 int pointerIndex = ev.getActionIndex();
1950 position[0] += (int) ev.getX(pointerIndex);
1951 position[1] += (int) ev.getY(pointerIndex);
1952
1953 mWallpaperManager.sendWallpaperCommand(getWindowToken(),
1954 ev.getAction() == MotionEvent.ACTION_UP
1955 ? WallpaperManager.COMMAND_TAP : WallpaperManager.COMMAND_SECONDARY_TAP,
1956 position[0], position[1], 0, null);
1957 }
1958
Adam Cohen61033d32010-11-15 18:29:44 -08001959 /*
1960 * This interpolator emulates the rate at which the perceived scale of an object changes
1961 * as its distance from a camera increases. When this interpolator is applied to a scale
1962 * animation on a view, it evokes the sense that the object is shrinking due to moving away
Andrew Flynn0dca1ec2012-02-29 13:33:22 -08001963 * from the camera.
Adam Cohen61033d32010-11-15 18:29:44 -08001964 */
1965 static class ZInterpolator implements TimeInterpolator {
1966 private float focalLength;
1967
1968 public ZInterpolator(float foc) {
1969 focalLength = foc;
1970 }
1971
Adam Coheneed565d2010-11-15 11:30:05 -08001972 public float getInterpolation(float input) {
1973 return (1.0f - focalLength / (focalLength + input)) /
Adam Cohen61033d32010-11-15 18:29:44 -08001974 (1.0f - focalLength / (focalLength + 1.0f));
Adam Cohencbbaf982010-11-12 14:50:33 -08001975 }
1976 }
1977
Adam Cohen61033d32010-11-15 18:29:44 -08001978 /*
1979 * The exact reverse of ZInterpolator.
1980 */
1981 static class InverseZInterpolator implements TimeInterpolator {
1982 private ZInterpolator zInterpolator;
1983 public InverseZInterpolator(float foc) {
1984 zInterpolator = new ZInterpolator(foc);
1985 }
Adam Cohencbbaf982010-11-12 14:50:33 -08001986 public float getInterpolation(float input) {
Adam Cohen61033d32010-11-15 18:29:44 -08001987 return 1 - zInterpolator.getInterpolation(1 - input);
Adam Cohencbbaf982010-11-12 14:50:33 -08001988 }
1989 }
1990
Adam Cohen61033d32010-11-15 18:29:44 -08001991 /*
1992 * ZInterpolator compounded with an ease-out.
1993 */
1994 static class ZoomOutInterpolator implements TimeInterpolator {
Adam Cohenb64d36e2011-10-17 21:48:02 -07001995 private final DecelerateInterpolator decelerate = new DecelerateInterpolator(0.75f);
1996 private final ZInterpolator zInterpolator = new ZInterpolator(0.13f);
Adam Cohen61033d32010-11-15 18:29:44 -08001997
1998 public float getInterpolation(float input) {
1999 return decelerate.getInterpolation(zInterpolator.getInterpolation(input));
2000 }
2001 }
2002
2003 /*
2004 * InvereZInterpolator compounded with an ease-out.
2005 */
2006 static class ZoomInInterpolator implements TimeInterpolator {
2007 private final InverseZInterpolator inverseZInterpolator = new InverseZInterpolator(0.35f);
2008 private final DecelerateInterpolator decelerate = new DecelerateInterpolator(3.0f);
2009
2010 public float getInterpolation(float input) {
2011 return decelerate.getInterpolation(inverseZInterpolator.getInterpolation(input));
2012 }
2013 }
2014
Adam Cohen61033d32010-11-15 18:29:44 -08002015 private final ZoomInInterpolator mZoomInInterpolator = new ZoomInInterpolator();
Michael Jurka3e7c7632010-10-02 16:01:03 -07002016
Michael Jurka3e7c7632010-10-02 16:01:03 -07002017 /*
Adam Cohen66396872011-04-15 17:50:36 -07002018 *
2019 * We call these methods (onDragStartedWithItemSpans/onDragStartedWithSize) whenever we
2020 * start a drag in Launcher, regardless of whether the drag has ever entered the Workspace
2021 *
2022 * These methods mark the appropriate pages as accepting drops (which alters their visual
2023 * appearance).
2024 *
2025 */
Sunny Goyal508da152014-08-14 10:53:27 -07002026 private static Rect getDrawableBounds(Drawable d) {
Mathew Inwood85900292014-04-16 14:17:39 +01002027 Rect bounds = new Rect();
2028 d.copyBounds(bounds);
2029 if (bounds.width() == 0 || bounds.height() == 0) {
2030 bounds.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
Sunny Goyal95abbb32014-08-04 10:53:22 -07002031 } else {
2032 bounds.offsetTo(0, 0);
2033 }
2034 if (d instanceof PreloadIconDrawable) {
2035 int inset = -((PreloadIconDrawable) d).getOutset();
2036 bounds.inset(inset, inset);
Mathew Inwood85900292014-04-16 14:17:39 +01002037 }
2038 return bounds;
2039 }
2040
Winson Chungbabb53e2014-04-14 17:12:49 -07002041 public void onExternalDragStartedWithItem(View v) {
Winson Chungbabb53e2014-04-14 17:12:49 -07002042 // Compose a drag bitmap with the view scaled to the icon size
2043 LauncherAppState app = LauncherAppState.getInstance();
2044 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
2045 int iconSize = grid.iconSizePx;
2046 int bmpWidth = v.getMeasuredWidth();
2047 int bmpHeight = v.getMeasuredHeight();
2048
2049 // If this is a text view, use its drawable instead
2050 if (v instanceof TextView) {
2051 TextView tv = (TextView) v;
2052 Drawable d = tv.getCompoundDrawables()[1];
Mathew Inwood85900292014-04-16 14:17:39 +01002053 Rect bounds = getDrawableBounds(d);
2054 bmpWidth = bounds.width();
2055 bmpHeight = bounds.height();
Winson Chungbabb53e2014-04-14 17:12:49 -07002056 }
2057
2058 // Compose the bitmap to create the icon from
2059 Bitmap b = Bitmap.createBitmap(bmpWidth, bmpHeight,
2060 Bitmap.Config.ARGB_8888);
Sunny Goyal508da152014-08-14 10:53:27 -07002061 mCanvas.setBitmap(b);
2062 drawDragView(v, mCanvas, 0);
2063 mCanvas.setBitmap(null);
Winson Chungbabb53e2014-04-14 17:12:49 -07002064
2065 // The outline is used to visualize where the item will land if dropped
Sunny Goyal508da152014-08-14 10:53:27 -07002066 mDragOutline = createDragOutline(b, DRAG_BITMAP_PADDING, iconSize, iconSize, true);
Winson Chungbabb53e2014-04-14 17:12:49 -07002067 }
2068
Michael Jurka8c3339b2012-06-14 16:18:21 -07002069 public void onDragStartedWithItem(PendingAddItemInfo info, Bitmap b, boolean clipAlpha) {
Michael Jurka038f9d82011-11-03 13:50:45 -07002070 int[] size = estimateItemSize(info.spanX, info.spanY, info, false);
Adam Cohen66396872011-04-15 17:50:36 -07002071
Michael Jurkad3ef3062010-11-23 16:23:58 -08002072 // The outline is used to visualize where the item will land if dropped
Sunny Goyal508da152014-08-14 10:53:27 -07002073 mDragOutline = createDragOutline(b, DRAG_BITMAP_PADDING, size[0], size[1], clipAlpha);
Michael Jurka3e7c7632010-10-02 16:01:03 -07002074 }
2075
Patrick Dubroy758a9232011-03-03 19:54:56 -08002076 public void exitWidgetResizeMode() {
Adam Cohen716b51e2011-06-30 12:09:54 -07002077 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohen67882692011-03-11 15:29:03 -08002078 dragLayer.clearAllResizeFrames();
Patrick Dubroy758a9232011-03-03 19:54:56 -08002079 }
2080
Adam Cohen4b285c52011-07-21 14:24:06 -07002081 private void initAnimationArrays() {
2082 final int childCount = getChildCount();
Adam Cohendcd297f2013-06-18 13:13:40 -07002083 if (mLastChildCount == childCount) return;
Adam Cohen7d30a372013-07-01 17:03:59 -07002084
Adam Cohen4b285c52011-07-21 14:24:06 -07002085 mOldBackgroundAlphas = new float[childCount];
Adam Cohen4b285c52011-07-21 14:24:06 -07002086 mOldAlphas = new float[childCount];
Adam Cohen4b285c52011-07-21 14:24:06 -07002087 mNewBackgroundAlphas = new float[childCount];
Adam Cohen4b285c52011-07-21 14:24:06 -07002088 mNewAlphas = new float[childCount];
Adam Cohen4b285c52011-07-21 14:24:06 -07002089 }
2090
Adam Cohen2854d252014-08-27 16:04:07 -07002091 Animator getChangeStateAnimation(final State state, boolean animated,
2092 ArrayList<View> layerViews) {
2093 return getChangeStateAnimation(state, animated, 0, -1, layerViews);
Adam Cohen7777d962011-08-18 18:58:38 -07002094 }
2095
Adam Cohenf358a4b2013-07-23 16:47:31 -07002096 @Override
Adam Cohen6f127a62014-06-12 14:54:41 -07002097 protected void getFreeScrollPageRange(int[] range) {
2098 getOverviewModePages(range);
2099 }
2100
2101 private void getOverviewModePages(int[] range) {
Michael Jurkafe0ace32013-10-03 01:05:14 -07002102 int start = numCustomPages();
Adam Cohen1003be92013-09-16 14:09:28 -07002103 int end = getChildCount() - 1;
2104
2105 range[0] = Math.max(0, Math.min(start, getChildCount() - 1));
2106 range[1] = Math.max(0, end);
Adam Cohen6f127a62014-06-12 14:54:41 -07002107 }
Adam Cohendedbd962013-07-11 14:21:49 -07002108
2109 protected void onStartReordering() {
2110 super.onStartReordering();
Adam Cohendedbd962013-07-11 14:21:49 -07002111 showOutlines();
Adam Cohen22cba7f2013-07-19 16:14:00 -07002112 // Reordering handles its own animations, disable the automatic ones.
Winson Chung964df6b2013-10-11 15:55:37 -07002113 disableLayoutTransitions();
Adam Cohendedbd962013-07-11 14:21:49 -07002114 }
2115
2116 protected void onEndReordering() {
2117 super.onEndReordering();
Adam Cohendedbd962013-07-11 14:21:49 -07002118
Adam Cohendcb173d2014-04-01 13:33:58 -07002119 if (mLauncher.isWorkspaceLoading()) {
2120 // Invalid and dangerous operation if workspace is loading
2121 return;
2122 }
2123
Adam Cohen2bf63d52013-09-29 17:46:49 -07002124 hideOutlines();
Adam Cohendedbd962013-07-11 14:21:49 -07002125 mScreenOrder.clear();
Adam Cohen2bf63d52013-09-29 17:46:49 -07002126 int count = getChildCount();
Adam Cohendedbd962013-07-11 14:21:49 -07002127 for (int i = 0; i < count; i++) {
2128 CellLayout cl = ((CellLayout) getChildAt(i));
2129 mScreenOrder.add(getIdForScreen(cl));
2130 }
Winson Chungd64d1762013-08-20 14:37:16 -07002131
Adam Cohendedbd962013-07-11 14:21:49 -07002132 mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
Adam Cohen22cba7f2013-07-19 16:14:00 -07002133
2134 // Re-enable auto layout transitions for page deletion.
Winson Chung964df6b2013-10-11 15:55:37 -07002135 enableLayoutTransitions();
Adam Cohendedbd962013-07-11 14:21:49 -07002136 }
2137
Adam Cohenf358a4b2013-07-23 16:47:31 -07002138 public boolean isInOverviewMode() {
2139 return mState == State.OVERVIEW;
2140 }
2141
Adam Cohen93c97562013-09-26 13:48:01 -07002142 public boolean enterOverviewMode() {
2143 if (mTouchState != TOUCH_STATE_REST) {
2144 return false;
2145 }
Michael Jurka336fd4f2013-09-12 00:05:02 +02002146 enableOverviewMode(true, -1, true);
Adam Cohen93c97562013-09-26 13:48:01 -07002147 return true;
Adam Cohenf358a4b2013-07-23 16:47:31 -07002148 }
2149
Michael Jurka336fd4f2013-09-12 00:05:02 +02002150 public void exitOverviewMode(boolean animated) {
2151 exitOverviewMode(-1, animated);
Adam Cohenf358a4b2013-07-23 16:47:31 -07002152 }
2153
Michael Jurka336fd4f2013-09-12 00:05:02 +02002154 public void exitOverviewMode(int snapPage, boolean animated) {
2155 enableOverviewMode(false, snapPage, animated);
Adam Cohenf358a4b2013-07-23 16:47:31 -07002156 }
2157
Michael Jurka336fd4f2013-09-12 00:05:02 +02002158 private void enableOverviewMode(boolean enable, int snapPage, boolean animated) {
Adam Cohenf358a4b2013-07-23 16:47:31 -07002159 State finalState = Workspace.State.OVERVIEW;
2160 if (!enable) {
2161 finalState = Workspace.State.NORMAL;
2162 }
2163
Michael Jurka336fd4f2013-09-12 00:05:02 +02002164 Animator workspaceAnim = getChangeStateAnimation(finalState, animated, 0, snapPage);
2165 if (workspaceAnim != null) {
Adam Cohenf3434992013-09-16 16:52:59 -07002166 onTransitionPrepare();
Michael Jurka336fd4f2013-09-12 00:05:02 +02002167 workspaceAnim.addListener(new AnimatorListenerAdapter() {
2168 @Override
2169 public void onAnimationEnd(Animator arg0) {
Adam Cohenf3434992013-09-16 16:52:59 -07002170 onTransitionEnd();
Michael Jurka336fd4f2013-09-12 00:05:02 +02002171 }
2172 });
2173 workspaceAnim.start();
2174 }
Adam Cohenf358a4b2013-07-23 16:47:31 -07002175 }
2176
Adam Cohen410f3cd2013-09-22 12:09:32 -07002177 int getOverviewModeTranslationY() {
Winson Chungc82d2622013-11-06 13:23:29 -08002178 LauncherAppState app = LauncherAppState.getInstance();
2179 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
2180 Rect overviewBar = grid.getOverviewModeButtonBarRect();
Adam Cohen410f3cd2013-09-22 12:09:32 -07002181
Winson Chungc82d2622013-11-06 13:23:29 -08002182 int availableHeight = getViewportHeight();
2183 int scaledHeight = (int) (mOverviewModeShrinkFactor * getNormalChildHeight());
2184 int offsetFromTopEdge = (availableHeight - scaledHeight) / 2;
2185 int offsetToCenterInOverview = (availableHeight - mInsets.top - overviewBar.height()
2186 - scaledHeight) / 2;
Adam Cohen410f3cd2013-09-22 12:09:32 -07002187
Winson Chungc82d2622013-11-06 13:23:29 -08002188 return -offsetFromTopEdge + mInsets.top + offsetToCenterInOverview;
Adam Cohen410f3cd2013-09-22 12:09:32 -07002189 }
2190
Adam Cohenedaaa302013-10-01 17:33:27 -07002191 public void updateInteractionForState() {
2192 if (mState != State.NORMAL) {
2193 mLauncher.onInteractionBegin();
2194 } else {
2195 mLauncher.onInteractionEnd();
2196 }
2197 }
2198
2199 private void setState(State state) {
2200 mState = state;
2201 updateInteractionForState();
Adam Cohen53805212013-10-01 10:39:23 -07002202 updateAccessibilityFlags();
2203 }
2204
Adam Cohene25c5d12014-06-18 10:34:58 -07002205 State getState() {
2206 return mState;
2207 }
2208
Adam Cohen53805212013-10-01 10:39:23 -07002209 private void updateAccessibilityFlags() {
2210 int accessible = mState == State.NORMAL ?
Sunny Goyalc46bfef2015-01-05 12:40:08 -08002211 IMPORTANT_FOR_ACCESSIBILITY_YES :
2212 IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
Adam Cohen53805212013-10-01 10:39:23 -07002213 setImportantForAccessibility(accessible);
Adam Cohenedaaa302013-10-01 17:33:27 -07002214 }
2215
Adam Cohen6c5891a2014-07-09 23:53:15 -07002216 private static final int HIDE_WORKSPACE_DURATION = 100;
2217
Adam Cohenf358a4b2013-07-23 16:47:31 -07002218 Animator getChangeStateAnimation(final State state, boolean animated, int delay, int snapPage) {
Adam Cohen2854d252014-08-27 16:04:07 -07002219 return getChangeStateAnimation(state, animated, delay, snapPage, null);
2220 }
2221
2222 Animator getChangeStateAnimation(final State state, boolean animated, int delay, int snapPage,
2223 ArrayList<View> layerViews) {
Michael Jurkabdf78552011-10-31 14:34:25 -07002224 if (mState == state) {
Michael Jurka2a4b1a82011-12-07 14:00:02 -08002225 return null;
Michael Jurka99633da2011-07-27 22:40:17 -07002226 }
2227
Winson Chung1b7c1d32011-10-25 12:09:38 -07002228 // Initialize animation arrays for the first time if necessary
2229 initAnimationArrays();
2230
Michael Jurka2ecf9952012-06-18 12:52:28 -07002231 AnimatorSet anim = animated ? LauncherAnimUtils.createAnimatorSet() : null;
Adam Lesinski6b879f02010-11-04 16:15:23 -07002232
Adam Cohen08072c02014-11-18 17:53:44 -08002233 // We only want a single instance of a workspace animation to be running at once, so
2234 // we cancel any incomplete transition.
2235 if (mStateAnimator != null) {
2236 mStateAnimator.cancel();
2237 }
2238 mStateAnimator = anim;
2239
Winson Chung1b7c1d32011-10-25 12:09:38 -07002240 final State oldState = mState;
2241 final boolean oldStateIsNormal = (oldState == State.NORMAL);
Michael Jurka4ff7d792012-04-02 03:46:50 -07002242 final boolean oldStateIsSpringLoaded = (oldState == State.SPRING_LOADED);
Adam Cohen6c5891a2014-07-09 23:53:15 -07002243 final boolean oldStateIsNormalHidden = (oldState == State.NORMAL_HIDDEN);
2244 final boolean oldStateIsOverviewHidden = (oldState == State.OVERVIEW_HIDDEN);
Adam Cohenf358a4b2013-07-23 16:47:31 -07002245 final boolean oldStateIsOverview = (oldState == State.OVERVIEW);
Adam Cohenedaaa302013-10-01 17:33:27 -07002246 setState(state);
Winson Chung1b7c1d32011-10-25 12:09:38 -07002247 final boolean stateIsNormal = (state == State.NORMAL);
2248 final boolean stateIsSpringLoaded = (state == State.SPRING_LOADED);
Adam Cohen6c5891a2014-07-09 23:53:15 -07002249 final boolean stateIsNormalHidden = (state == State.NORMAL_HIDDEN);
2250 final boolean stateIsOverviewHidden = (state == State.OVERVIEW_HIDDEN);
Adam Cohenf358a4b2013-07-23 16:47:31 -07002251 final boolean stateIsOverview = (state == State.OVERVIEW);
Winson Chung2d75f122013-09-23 16:53:31 -07002252 float finalBackgroundAlpha = (stateIsSpringLoaded || stateIsOverview) ? 1.0f : 0f;
Adam Cohen6c5891a2014-07-09 23:53:15 -07002253 float finalHotseatAndPageIndicatorAlpha = (stateIsNormal || stateIsSpringLoaded) ? 1f : 0f;
Adam Cohenf358a4b2013-07-23 16:47:31 -07002254 float finalOverviewPanelAlpha = stateIsOverview ? 1f : 0f;
Winson Chung2d75f122013-09-23 16:53:31 -07002255 float finalSearchBarAlpha = !stateIsNormal ? 0f : 1f;
Adam Cohen6c5891a2014-07-09 23:53:15 -07002256 float finalWorkspaceTranslationY = stateIsOverview || stateIsOverviewHidden ?
2257 getOverviewModeTranslationY() : 0;
Adam Cohenf358a4b2013-07-23 16:47:31 -07002258
Adam Cohen6c5891a2014-07-09 23:53:15 -07002259 boolean workspaceToAllApps = (oldStateIsNormal && stateIsNormalHidden);
2260 boolean overviewToAllApps = (oldStateIsOverview && stateIsOverviewHidden);
2261 boolean allAppsToWorkspace = (stateIsNormalHidden && stateIsNormal);
Adam Cohen58993ad2013-10-11 17:57:38 -07002262 boolean workspaceToOverview = (oldStateIsNormal && stateIsOverview);
2263 boolean overviewToWorkspace = (oldStateIsOverview && stateIsNormal);
2264
Adam Cohen7d30a372013-07-01 17:03:59 -07002265 mNewScale = 1.0f;
Adam Cohenbeff8c62011-08-31 17:46:01 -07002266
Adam Cohenf358a4b2013-07-23 16:47:31 -07002267 if (oldStateIsOverview) {
Adam Cohenf9618852013-11-08 06:45:03 -08002268 disableFreeScroll();
Adam Cohenf358a4b2013-07-23 16:47:31 -07002269 } else if (stateIsOverview) {
2270 enableFreeScroll();
2271 }
2272
Adam Cohen7777d962011-08-18 18:58:38 -07002273 if (state != State.NORMAL) {
Adam Cohenf358a4b2013-07-23 16:47:31 -07002274 if (stateIsSpringLoaded) {
2275 mNewScale = mSpringLoadedShrinkFactor;
Adam Cohen6c5891a2014-07-09 23:53:15 -07002276 } else if (stateIsOverview || stateIsOverviewHidden) {
Adam Cohenf358a4b2013-07-23 16:47:31 -07002277 mNewScale = mOverviewModeShrinkFactor;
Adam Cohenf358a4b2013-07-23 16:47:31 -07002278 }
Adam Cohen7777d962011-08-18 18:58:38 -07002279 }
Winson Chungf4bd2362013-10-07 17:11:27 -07002280
Adam Cohenfa2450a2013-10-11 18:20:27 -07002281 final int duration;
Adam Cohen6c5891a2014-07-09 23:53:15 -07002282 if (workspaceToAllApps || overviewToAllApps) {
2283 duration = HIDE_WORKSPACE_DURATION; //getResources().getInteger(R.integer.config_workspaceUnshrinkTime);
Adam Cohenfa2450a2013-10-11 18:20:27 -07002284 } else if (workspaceToOverview || overviewToWorkspace) {
2285 duration = getResources().getInteger(R.integer.config_overviewTransitionTime);
2286 } else {
2287 duration = getResources().getInteger(R.integer.config_appsCustomizeWorkspaceShrinkTime);
2288 }
2289
Adam Cohenf9618852013-11-08 06:45:03 -08002290 if (snapPage == -1) {
2291 snapPage = getPageNearestToCenterOfScreen();
2292 }
2293 snapToPage(snapPage, duration, mZoomInInterpolator);
2294
Winson Chung1b7c1d32011-10-25 12:09:38 -07002295 for (int i = 0; i < getChildCount(); i++) {
2296 final CellLayout cl = (CellLayout) getChildAt(i);
Adam Cohenf9618852013-11-08 06:45:03 -08002297 boolean isCurrentPage = (i == snapPage);
Winson Chung2d75f122013-09-23 16:53:31 -07002298 float initialAlpha = cl.getShortcutsAndWidgets().getAlpha();
Adam Cohen3b185e22013-10-29 14:45:58 -07002299 float finalAlpha;
Adam Cohen6c5891a2014-07-09 23:53:15 -07002300 if (stateIsNormalHidden || stateIsOverviewHidden) {
Adam Cohen3b185e22013-10-29 14:45:58 -07002301 finalAlpha = 0f;
2302 } else if (stateIsNormal && mWorkspaceFadeInAdjacentScreens) {
Adam Cohenf9618852013-11-08 06:45:03 -08002303 finalAlpha = (i == snapPage || i < numCustomPages()) ? 1f : 0f;
Adam Cohen3b185e22013-10-29 14:45:58 -07002304 } else {
2305 finalAlpha = 1f;
2306 }
Winson Chungf4bd2362013-10-07 17:11:27 -07002307
2308 // If we are animating to/from the small state, then hide the side pages and fade the
2309 // current page in
2310 if (!mIsSwitchingState) {
2311 if (workspaceToAllApps || allAppsToWorkspace) {
2312 if (allAppsToWorkspace && isCurrentPage) {
2313 initialAlpha = 0f;
2314 } else if (!isCurrentPage) {
2315 initialAlpha = finalAlpha = 0f;
2316 }
2317 cl.setShortcutAndWidgetAlpha(initialAlpha);
2318 }
2319 }
Adam Cohen7777d962011-08-18 18:58:38 -07002320
Winson Chung1b7c1d32011-10-25 12:09:38 -07002321 mOldAlphas[i] = initialAlpha;
2322 mNewAlphas[i] = finalAlpha;
Adam Cohen7777d962011-08-18 18:58:38 -07002323 if (animated) {
Adam Cohen7777d962011-08-18 18:58:38 -07002324 mOldBackgroundAlphas[i] = cl.getBackgroundAlpha();
Adam Cohen7777d962011-08-18 18:58:38 -07002325 mNewBackgroundAlphas[i] = finalBackgroundAlpha;
Adam Cohen7777d962011-08-18 18:58:38 -07002326 } else {
Winson Chung1b7c1d32011-10-25 12:09:38 -07002327 cl.setBackgroundAlpha(finalBackgroundAlpha);
Michael Jurkaa52570f2012-03-20 03:18:20 -07002328 cl.setShortcutAndWidgetAlpha(finalAlpha);
Adam Cohen7777d962011-08-18 18:58:38 -07002329 }
Michael Jurkadee05892010-07-27 10:01:56 -07002330 }
Winson Chung8d006d52010-11-29 15:55:29 -08002331
Adam Cohen3d411982013-09-24 17:02:06 -07002332 final View searchBar = mLauncher.getQsbBar();
2333 final View overviewPanel = mLauncher.getOverviewPanel();
2334 final View hotseat = mLauncher.getHotseat();
Michael Jurka853f5a42013-11-25 14:32:21 +01002335 final View pageIndicator = getPageIndicator();
Adam Cohen7777d962011-08-18 18:58:38 -07002336 if (animated) {
Adam Cohen7d30a372013-07-01 17:03:59 -07002337 LauncherViewPropertyAnimator scale = new LauncherViewPropertyAnimator(this);
2338 scale.scaleX(mNewScale)
2339 .scaleY(mNewScale)
Adam Cohen410f3cd2013-09-22 12:09:32 -07002340 .translationY(finalWorkspaceTranslationY)
Adam Cohen6c5891a2014-07-09 23:53:15 -07002341 .setDuration(duration)
Adam Cohen7d30a372013-07-01 17:03:59 -07002342 .setInterpolator(mZoomInInterpolator);
2343 anim.play(scale);
Michael Jurka7407d2a2011-12-12 21:48:38 -08002344 for (int index = 0; index < getChildCount(); index++) {
2345 final int i = index;
2346 final CellLayout cl = (CellLayout) getChildAt(i);
Michael Jurka869390b2012-05-06 15:55:19 -07002347 float currentAlpha = cl.getShortcutsAndWidgets().getAlpha();
Michael Jurka2a4b1a82011-12-07 14:00:02 -08002348 if (mOldAlphas[i] == 0 && mNewAlphas[i] == 0) {
Michael Jurka7372c592012-01-16 04:21:35 -08002349 cl.setBackgroundAlpha(mNewBackgroundAlphas[i]);
Michael Jurkaa52570f2012-03-20 03:18:20 -07002350 cl.setShortcutAndWidgetAlpha(mNewAlphas[i]);
Michael Jurka7407d2a2011-12-12 21:48:38 -08002351 } else {
Adam Cohen2854d252014-08-27 16:04:07 -07002352 if (layerViews != null) {
2353 layerViews.add(cl);
2354 }
Michael Jurka869390b2012-05-06 15:55:19 -07002355 if (mOldAlphas[i] != mNewAlphas[i] || currentAlpha != mNewAlphas[i]) {
Michael Jurka6c8a5792012-03-22 05:24:37 -07002356 LauncherViewPropertyAnimator alphaAnim =
2357 new LauncherViewPropertyAnimator(cl.getShortcutsAndWidgets());
Michael Jurkaa52570f2012-03-20 03:18:20 -07002358 alphaAnim.alpha(mNewAlphas[i])
Adam Cohen6c5891a2014-07-09 23:53:15 -07002359 .setDuration(duration)
Michael Jurkaa52570f2012-03-20 03:18:20 -07002360 .setInterpolator(mZoomInInterpolator);
2361 anim.play(alphaAnim);
2362 }
Michael Jurka7407d2a2011-12-12 21:48:38 -08002363 if (mOldBackgroundAlphas[i] != 0 ||
Michael Jurka869390b2012-05-06 15:55:19 -07002364 mNewBackgroundAlphas[i] != 0) {
Michael Jurkaf1ad6082013-03-13 12:55:46 +01002365 ValueAnimator bgAnim =
Adam Cohenfa2450a2013-10-11 18:20:27 -07002366 LauncherAnimUtils.ofFloat(cl, 0f, 1f);
Michael Jurka7407d2a2011-12-12 21:48:38 -08002367 bgAnim.setInterpolator(mZoomInInterpolator);
Adam Cohen6c5891a2014-07-09 23:53:15 -07002368 bgAnim.setDuration(duration);
Michael Jurka7407d2a2011-12-12 21:48:38 -08002369 bgAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
2370 public void onAnimationUpdate(float a, float b) {
Michael Jurka7372c592012-01-16 04:21:35 -08002371 cl.setBackgroundAlpha(
Michael Jurka7407d2a2011-12-12 21:48:38 -08002372 a * mOldBackgroundAlphas[i] +
2373 b * mNewBackgroundAlphas[i]);
Michael Jurka7407d2a2011-12-12 21:48:38 -08002374 }
2375 });
2376 anim.play(bgAnim);
2377 }
Michael Jurka2a4b1a82011-12-07 14:00:02 -08002378 }
2379 }
Michael Jurka853f5a42013-11-25 14:32:21 +01002380 Animator pageIndicatorAlpha = null;
2381 if (pageIndicator != null) {
2382 pageIndicatorAlpha = new LauncherViewPropertyAnimator(pageIndicator)
2383 .alpha(finalHotseatAndPageIndicatorAlpha).withLayer();
Michael Jurka1c00fd02013-12-02 16:11:22 -08002384 pageIndicatorAlpha.addListener(new AlphaUpdateListener(pageIndicator));
Michael Jurka853f5a42013-11-25 14:32:21 +01002385 } else {
2386 // create a dummy animation so we don't need to do null checks later
2387 pageIndicatorAlpha = ValueAnimator.ofFloat(0, 0);
Winson Chung66700732013-08-20 16:56:15 -07002388 }
Michael Jurka1c00fd02013-12-02 16:11:22 -08002389
Michael Jurka853f5a42013-11-25 14:32:21 +01002390 Animator hotseatAlpha = new LauncherViewPropertyAnimator(hotseat)
2391 .alpha(finalHotseatAndPageIndicatorAlpha).withLayer();
Michael Jurka1c00fd02013-12-02 16:11:22 -08002392 hotseatAlpha.addListener(new AlphaUpdateListener(hotseat));
2393
Michael Jurka853f5a42013-11-25 14:32:21 +01002394 Animator overviewPanelAlpha = new LauncherViewPropertyAnimator(overviewPanel)
2395 .alpha(finalOverviewPanelAlpha).withLayer();
Michael Jurka853f5a42013-11-25 14:32:21 +01002396 overviewPanelAlpha.addListener(new AlphaUpdateListener(overviewPanel));
Adam Cohen3d411982013-09-24 17:02:06 -07002397
Adam Cohen2854d252014-08-27 16:04:07 -07002398 // For animation optimations, we may need to provide the Launcher transition
2399 // with a set of views on which to force build layers in certain scenarios.
2400 hotseat.setLayerType(View.LAYER_TYPE_HARDWARE, null);
Adam Cohen2854d252014-08-27 16:04:07 -07002401 overviewPanel.setLayerType(View.LAYER_TYPE_HARDWARE, null);
2402 if (layerViews != null) {
2403 layerViews.add(hotseat);
Adam Cohen2854d252014-08-27 16:04:07 -07002404 layerViews.add(overviewPanel);
2405 }
2406
Adam Cohen58993ad2013-10-11 17:57:38 -07002407 if (workspaceToOverview) {
Michael Jurka853f5a42013-11-25 14:32:21 +01002408 pageIndicatorAlpha.setInterpolator(new DecelerateInterpolator(2));
Adam Cohen58993ad2013-10-11 17:57:38 -07002409 hotseatAlpha.setInterpolator(new DecelerateInterpolator(2));
Michael Jurka853f5a42013-11-25 14:32:21 +01002410 overviewPanelAlpha.setInterpolator(null);
Adam Cohen58993ad2013-10-11 17:57:38 -07002411 } else if (overviewToWorkspace) {
Michael Jurka853f5a42013-11-25 14:32:21 +01002412 pageIndicatorAlpha.setInterpolator(null);
2413 hotseatAlpha.setInterpolator(null);
Adam Cohen58993ad2013-10-11 17:57:38 -07002414 overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2));
2415 }
Adam Cohen6c5891a2014-07-09 23:53:15 -07002416
2417 overviewPanelAlpha.setDuration(duration);
2418 pageIndicatorAlpha.setDuration(duration);
2419 hotseatAlpha.setDuration(duration);
Adam Cohenc2d6e892014-10-16 09:49:24 -07002420
2421 if (searchBar != null) {
2422 Animator searchBarAlpha = new LauncherViewPropertyAnimator(searchBar)
2423 .alpha(finalSearchBarAlpha).withLayer();
2424 searchBarAlpha.addListener(new AlphaUpdateListener(searchBar));
2425 searchBar.setLayerType(View.LAYER_TYPE_HARDWARE, null);
2426 if (layerViews != null) {
2427 layerViews.add(searchBar);
2428 }
2429 searchBarAlpha.setDuration(duration);
2430 anim.play(searchBarAlpha);
2431 }
Adam Cohen3d411982013-09-24 17:02:06 -07002432
Adam Cohenf358a4b2013-07-23 16:47:31 -07002433 anim.play(overviewPanelAlpha);
2434 anim.play(hotseatAlpha);
2435 anim.play(pageIndicatorAlpha);
Michael Jurka2a4b1a82011-12-07 14:00:02 -08002436 anim.setStartDelay(delay);
Adam Cohen08072c02014-11-18 17:53:44 -08002437 anim.addListener(new AnimatorListenerAdapter() {
2438 @Override
2439 public void onAnimationEnd(Animator animation) {
2440 mStateAnimator = null;
2441 }
2442 });
Adam Cohenf358a4b2013-07-23 16:47:31 -07002443 } else {
Adam Cohen3d411982013-09-24 17:02:06 -07002444 overviewPanel.setAlpha(finalOverviewPanelAlpha);
2445 AlphaUpdateListener.updateVisibility(overviewPanel);
2446 hotseat.setAlpha(finalHotseatAndPageIndicatorAlpha);
2447 AlphaUpdateListener.updateVisibility(hotseat);
Michael Jurka853f5a42013-11-25 14:32:21 +01002448 if (pageIndicator != null) {
2449 pageIndicator.setAlpha(finalHotseatAndPageIndicatorAlpha);
2450 AlphaUpdateListener.updateVisibility(pageIndicator);
Winson Chung66700732013-08-20 16:56:15 -07002451 }
Sunny Goyal416541c2014-11-14 11:59:57 -08002452 if (searchBar != null) {
2453 searchBar.setAlpha(finalSearchBarAlpha);
2454 AlphaUpdateListener.updateVisibility(searchBar);
2455 }
Adam Cohen3f452c82013-09-19 11:57:17 -07002456 updateCustomContentVisibility();
Adam Cohen410f3cd2013-09-22 12:09:32 -07002457 setScaleX(mNewScale);
2458 setScaleY(mNewScale);
2459 setTranslationY(finalWorkspaceTranslationY);
Adam Cohendbdff6b2013-09-18 19:09:15 -07002460 }
Adam Cohen7777d962011-08-18 18:58:38 -07002461
Adam Cohen6c5891a2014-07-09 23:53:15 -07002462 if (stateIsNormal) {
Adam Cohened307df2013-10-02 09:37:31 -07002463 animateBackgroundGradient(0f, animated);
Adam Cohen6c5891a2014-07-09 23:53:15 -07002464 } else {
2465 animateBackgroundGradient(getResources().getInteger(
2466 R.integer.config_workspaceScrimAlpha) / 100f, animated);
Michael Jurka7bdb25a2011-08-03 15:16:44 -07002467 }
Michael Jurka2a4b1a82011-12-07 14:00:02 -08002468 return anim;
Michael Jurkadee05892010-07-27 10:01:56 -07002469 }
2470
Adam Cohena5f4e482013-10-11 12:10:28 -07002471 static class AlphaUpdateListener implements AnimatorUpdateListener, AnimatorListener {
Adam Cohenf358a4b2013-07-23 16:47:31 -07002472 View view;
2473 public AlphaUpdateListener(View v) {
2474 view = v;
2475 }
2476
2477 @Override
2478 public void onAnimationUpdate(ValueAnimator arg0) {
Michael Jurka336fd4f2013-09-12 00:05:02 +02002479 updateVisibility(view);
2480 }
2481
2482 public static void updateVisibility(View view) {
Adam Cohen53805212013-10-01 10:39:23 -07002483 // We want to avoid the extra layout pass by setting the views to GONE unless
2484 // accessibility is on, in which case not setting them to GONE causes a glitch.
2485 int invisibleState = sAccessibilityEnabled ? GONE : INVISIBLE;
2486 if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != invisibleState) {
2487 view.setVisibility(invisibleState);
Adam Cohenf358a4b2013-07-23 16:47:31 -07002488 } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD
2489 && view.getVisibility() != VISIBLE) {
2490 view.setVisibility(VISIBLE);
2491 }
2492 }
Adam Cohena5f4e482013-10-11 12:10:28 -07002493
2494 @Override
2495 public void onAnimationCancel(Animator arg0) {
2496 }
2497
2498 @Override
2499 public void onAnimationEnd(Animator arg0) {
2500 updateVisibility(view);
2501 }
2502
2503 @Override
2504 public void onAnimationRepeat(Animator arg0) {
2505 }
2506
2507 @Override
2508 public void onAnimationStart(Animator arg0) {
2509 // We want the views to be visible for animation, so fade-in/out is visible
2510 view.setVisibility(VISIBLE);
2511 }
Adam Cohenf358a4b2013-07-23 16:47:31 -07002512 }
2513
Michael Jurkabed61d22012-02-14 22:51:29 -08002514 @Override
Michael Jurkaa35e35a2012-04-26 15:04:28 -07002515 public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
Adam Cohenf3434992013-09-16 16:52:59 -07002516 onTransitionPrepare();
Michael Jurkabed61d22012-02-14 22:51:29 -08002517 }
2518
2519 @Override
Michael Jurkaa35e35a2012-04-26 15:04:28 -07002520 public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
2521 }
2522
2523 @Override
Winson Chung70442722012-02-10 15:43:22 -08002524 public void onLauncherTransitionStep(Launcher l, float t) {
2525 mTransitionProgress = t;
2526 }
2527
2528 @Override
Michael Jurkabed61d22012-02-14 22:51:29 -08002529 public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
Adam Cohenf3434992013-09-16 16:52:59 -07002530 onTransitionEnd();
2531 }
2532
2533 private void onTransitionPrepare() {
2534 mIsSwitchingState = true;
Winson Chungf4bd2362013-10-07 17:11:27 -07002535
2536 // Invalidate here to ensure that the pages are rendered during the state change transition.
2537 invalidate();
2538
Adam Cohenf3434992013-09-16 16:52:59 -07002539 updateChildrenLayersEnabled(false);
Adam Cohen3f452c82013-09-19 11:57:17 -07002540 hideCustomContentIfNecessary();
2541 }
2542
2543 void updateCustomContentVisibility() {
2544 int visibility = mState == Workspace.State.NORMAL ? VISIBLE : INVISIBLE;
2545 if (hasCustomContent()) {
2546 mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(visibility);
2547 }
2548 }
2549
2550 void showCustomContentIfNecessary() {
2551 boolean show = mState == Workspace.State.NORMAL;
2552 if (show && hasCustomContent()) {
2553 mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(VISIBLE);
2554 }
2555 }
2556
2557 void hideCustomContentIfNecessary() {
2558 boolean hide = mState != Workspace.State.NORMAL;
2559 if (hide && hasCustomContent()) {
Adam Cohen1774a8a2013-11-15 10:56:16 +00002560 disableLayoutTransitions();
Adam Cohen3f452c82013-09-19 11:57:17 -07002561 mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(INVISIBLE);
Adam Cohen1774a8a2013-11-15 10:56:16 +00002562 enableLayoutTransitions();
Adam Cohenf3434992013-09-16 16:52:59 -07002563 }
2564 }
2565
2566 private void onTransitionEnd() {
Michael Jurkabed61d22012-02-14 22:51:29 -08002567 mIsSwitchingState = false;
Michael Jurka3a0469d2012-06-21 09:38:41 -07002568 updateChildrenLayersEnabled(false);
Adam Cohen3f452c82013-09-19 11:57:17 -07002569 showCustomContentIfNecessary();
Michael Jurkabed61d22012-02-14 22:51:29 -08002570 }
2571
2572 @Override
2573 public View getContent() {
2574 return this;
2575 }
2576
Joe Onorato4be866d2010-10-10 11:26:02 -07002577 /**
2578 * Draw the View v into the given Canvas.
2579 *
2580 * @param v the view to draw
2581 * @param destCanvas the canvas to draw on
2582 * @param padding the horizontal and vertical padding to use when drawing
2583 */
Sunny Goyal508da152014-08-14 10:53:27 -07002584 private static void drawDragView(View v, Canvas destCanvas, int padding) {
2585 final Rect clipRect = sTempRect;
Joe Onorato4be866d2010-10-10 11:26:02 -07002586 v.getDrawingRect(clipRect);
2587
Adam Cohen099f60d2011-08-23 21:07:26 -07002588 boolean textVisible = false;
2589
Adam Cohenac8c8762011-07-13 11:15:27 -07002590 destCanvas.save();
Sunny Goyal95abbb32014-08-04 10:53:22 -07002591 if (v instanceof TextView) {
Adam Cohenac8c8762011-07-13 11:15:27 -07002592 Drawable d = ((TextView) v).getCompoundDrawables()[1];
Mathew Inwood85900292014-04-16 14:17:39 +01002593 Rect bounds = getDrawableBounds(d);
2594 clipRect.set(0, 0, bounds.width() + padding, bounds.height() + padding);
Sunny Goyal95abbb32014-08-04 10:53:22 -07002595 destCanvas.translate(padding / 2 - bounds.left, padding / 2 - bounds.top);
Adam Cohenac8c8762011-07-13 11:15:27 -07002596 d.draw(destCanvas);
2597 } else {
2598 if (v instanceof FolderIcon) {
Adam Cohen099f60d2011-08-23 21:07:26 -07002599 // For FolderIcons the text can bleed into the icon area, and so we need to
2600 // hide the text completely (which can't be achieved by clipping).
2601 if (((FolderIcon) v).getTextVisible()) {
2602 ((FolderIcon) v).setTextVisible(false);
2603 textVisible = true;
2604 }
Adam Cohenac8c8762011-07-13 11:15:27 -07002605 }
2606 destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2);
2607 destCanvas.clipRect(clipRect, Op.REPLACE);
2608 v.draw(destCanvas);
Adam Cohen099f60d2011-08-23 21:07:26 -07002609
2610 // Restore text visibility of FolderIcon if necessary
2611 if (textVisible) {
2612 ((FolderIcon) v).setTextVisible(true);
2613 }
Adam Cohenac8c8762011-07-13 11:15:27 -07002614 }
2615 destCanvas.restore();
2616 }
2617
2618 /**
2619 * Returns a new bitmap to show when the given View is being dragged around.
2620 * Responsibility for the bitmap is transferred to the caller.
Sunny Goyal95abbb32014-08-04 10:53:22 -07002621 * @param expectedPadding padding to add to the drag view. If a different padding was used
2622 * its value will be changed
Adam Cohenac8c8762011-07-13 11:15:27 -07002623 */
Sunny Goyal508da152014-08-14 10:53:27 -07002624 public Bitmap createDragBitmap(View v, AtomicInteger expectedPadding) {
Adam Cohenac8c8762011-07-13 11:15:27 -07002625 Bitmap b;
2626
Sunny Goyal95abbb32014-08-04 10:53:22 -07002627 int padding = expectedPadding.get();
Adam Cohenac8c8762011-07-13 11:15:27 -07002628 if (v instanceof TextView) {
2629 Drawable d = ((TextView) v).getCompoundDrawables()[1];
Mathew Inwood85900292014-04-16 14:17:39 +01002630 Rect bounds = getDrawableBounds(d);
2631 b = Bitmap.createBitmap(bounds.width() + padding,
2632 bounds.height() + padding, Bitmap.Config.ARGB_8888);
Sunny Goyal95abbb32014-08-04 10:53:22 -07002633 expectedPadding.set(padding - bounds.left - bounds.top);
Adam Cohenac8c8762011-07-13 11:15:27 -07002634 } else {
2635 b = Bitmap.createBitmap(
2636 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
Joe Onorato4be866d2010-10-10 11:26:02 -07002637 }
2638
Sunny Goyal508da152014-08-14 10:53:27 -07002639 mCanvas.setBitmap(b);
2640 drawDragView(v, mCanvas, padding);
2641 mCanvas.setBitmap(null);
Joe Onorato4be866d2010-10-10 11:26:02 -07002642
Adam Cohenac8c8762011-07-13 11:15:27 -07002643 return b;
Joe Onorato4be866d2010-10-10 11:26:02 -07002644 }
2645
2646 /**
2647 * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
2648 * Responsibility for the bitmap is transferred to the caller.
2649 */
Sunny Goyal508da152014-08-14 10:53:27 -07002650 private Bitmap createDragOutline(View v, int padding) {
Adam Cohen410f3cd2013-09-22 12:09:32 -07002651 final int outlineColor = getResources().getColor(R.color.outline_color);
Joe Onorato4be866d2010-10-10 11:26:02 -07002652 final Bitmap b = Bitmap.createBitmap(
2653 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
2654
Sunny Goyal508da152014-08-14 10:53:27 -07002655 mCanvas.setBitmap(b);
2656 drawDragView(v, mCanvas, padding);
2657 mOutlineHelper.applyExpensiveOutlineWithBlur(b, mCanvas, outlineColor, outlineColor);
2658 mCanvas.setBitmap(null);
Joe Onorato4be866d2010-10-10 11:26:02 -07002659 return b;
2660 }
2661
2662 /**
Michael Jurkad3ef3062010-11-23 16:23:58 -08002663 * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
2664 * Responsibility for the bitmap is transferred to the caller.
2665 */
Sunny Goyal508da152014-08-14 10:53:27 -07002666 private Bitmap createDragOutline(Bitmap orig, int padding, int w, int h,
Michael Jurka8c3339b2012-06-14 16:18:21 -07002667 boolean clipAlpha) {
Adam Cohen410f3cd2013-09-22 12:09:32 -07002668 final int outlineColor = getResources().getColor(R.color.outline_color);
Michael Jurkaf12c75c2011-01-25 22:41:40 -08002669 final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Sunny Goyal508da152014-08-14 10:53:27 -07002670 mCanvas.setBitmap(b);
Michael Jurkaf12c75c2011-01-25 22:41:40 -08002671
2672 Rect src = new Rect(0, 0, orig.getWidth(), orig.getHeight());
2673 float scaleFactor = Math.min((w - padding) / (float) orig.getWidth(),
2674 (h - padding) / (float) orig.getHeight());
2675 int scaledWidth = (int) (scaleFactor * orig.getWidth());
2676 int scaledHeight = (int) (scaleFactor * orig.getHeight());
2677 Rect dst = new Rect(0, 0, scaledWidth, scaledHeight);
2678
2679 // center the image
2680 dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2);
2681
Sunny Goyal508da152014-08-14 10:53:27 -07002682 mCanvas.drawBitmap(orig, src, dst, null);
2683 mOutlineHelper.applyExpensiveOutlineWithBlur(b, mCanvas, outlineColor, outlineColor,
Michael Jurka8c3339b2012-06-14 16:18:21 -07002684 clipAlpha);
Sunny Goyal508da152014-08-14 10:53:27 -07002685 mCanvas.setBitmap(null);
Michael Jurkad3ef3062010-11-23 16:23:58 -08002686
2687 return b;
2688 }
2689
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002690 void startDrag(CellLayout.CellInfo cellInfo) {
2691 View child = cellInfo.cell;
Winson Chungaafa03c2010-06-11 17:34:16 -07002692
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002693 // Make sure the drag was started by a long press as opposed to a long click.
Bjorn Bringert7984c942009-12-09 15:38:25 +00002694 if (!child.isInTouchMode()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002695 return;
2696 }
Winson Chungaafa03c2010-06-11 17:34:16 -07002697
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002698 mDragInfo = cellInfo;
Adam Cohend41fbf52012-02-16 23:53:59 -08002699 child.setVisibility(INVISIBLE);
Adam Cohen482ed822012-03-02 14:15:13 -08002700 CellLayout layout = (CellLayout) child.getParent().getParent();
2701 layout.prepareChildForDrag(child);
Joe Onorato4be866d2010-10-10 11:26:02 -07002702
Adam Cohenac8c8762011-07-13 11:15:27 -07002703 beginDragShared(child, this);
2704 }
2705
2706 public void beginDragShared(View child, DragSource source) {
Sunny Goyal508da152014-08-14 10:53:27 -07002707 child.clearFocus();
2708 child.setPressed(false);
2709
2710 // The outline is used to visualize where the item will land if dropped
2711 mDragOutline = createDragOutline(child, DRAG_BITMAP_PADDING);
2712
Anjali Koppal62d18ed2014-03-10 17:04:03 -07002713 mLauncher.onDragStarted(child);
Joe Onorato4be866d2010-10-10 11:26:02 -07002714 // The drag bitmap follows the touch point around on the screen
Sunny Goyal95abbb32014-08-04 10:53:22 -07002715 AtomicInteger padding = new AtomicInteger(DRAG_BITMAP_PADDING);
Sunny Goyal508da152014-08-14 10:53:27 -07002716 final Bitmap b = createDragBitmap(child, padding);
Joe Onorato4be866d2010-10-10 11:26:02 -07002717
2718 final int bmpWidth = b.getWidth();
Winson Chungeecf02d2012-03-02 17:14:58 -08002719 final int bmpHeight = b.getHeight();
Adam Cohene3e27a82011-04-15 12:07:39 -07002720
Adam Cohen307fe232012-08-16 17:55:58 -07002721 float scale = mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY);
Winson Chungbabb53e2014-04-14 17:12:49 -07002722 int dragLayerX = Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2);
2723 int dragLayerY = Math.round(mTempXY[1] - (bmpHeight - scale * bmpHeight) / 2
Sunny Goyal95abbb32014-08-04 10:53:22 -07002724 - padding.get() / 2);
Adam Cohene3e27a82011-04-15 12:07:39 -07002725
Winson Chung5f8afe62013-08-12 16:19:28 -07002726 LauncherAppState app = LauncherAppState.getInstance();
2727 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
Winson Chungb8c69f32011-10-19 21:36:08 -07002728 Point dragVisualizeOffset = null;
Adam Cohene3e27a82011-04-15 12:07:39 -07002729 Rect dragRect = null;
Sunny Goyal508da152014-08-14 10:53:27 -07002730 if (child instanceof BubbleTextView) {
Winson Chung5f8afe62013-08-12 16:19:28 -07002731 int iconSize = grid.iconSizePx;
Adam Cohene3e27a82011-04-15 12:07:39 -07002732 int top = child.getPaddingTop();
2733 int left = (bmpWidth - iconSize) / 2;
2734 int right = left + iconSize;
2735 int bottom = top + iconSize;
Adam Cohenfc53cd22011-07-20 15:45:11 -07002736 dragLayerY += top;
Winson Chungb8c69f32011-10-19 21:36:08 -07002737 // Note: The drag region is used to calculate drag layer offsets, but the
2738 // dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
Sunny Goyal95abbb32014-08-04 10:53:22 -07002739 dragVisualizeOffset = new Point(-padding.get() / 2, padding.get() / 2);
Adam Cohene3e27a82011-04-15 12:07:39 -07002740 dragRect = new Rect(left, top, right, bottom);
Adam Cohen0e4857c2011-06-30 17:05:14 -07002741 } else if (child instanceof FolderIcon) {
Winson Chung5f8afe62013-08-12 16:19:28 -07002742 int previewSize = grid.folderIconSizePx;
2743 dragRect = new Rect(0, child.getPaddingTop(), child.getWidth(), previewSize);
Adam Cohene3e27a82011-04-15 12:07:39 -07002744 }
2745
Winson Chung1e9cbfe2011-09-30 16:52:26 -07002746 // Clear the pressed state if necessary
2747 if (child instanceof BubbleTextView) {
2748 BubbleTextView icon = (BubbleTextView) child;
Sunny Goyal508da152014-08-14 10:53:27 -07002749 icon.clearPressedBackground();
Winson Chung1e9cbfe2011-09-30 16:52:26 -07002750 }
2751
Adam Cohen2f32ad22013-11-13 11:29:49 +00002752 if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) {
2753 String msg = "Drag started with a view that has no tag set. This "
2754 + "will cause a crash (issue 11627249) down the line. "
2755 + "View: " + child + " tag: " + child.getTag();
2756 throw new IllegalStateException(msg);
2757 }
2758
Winson Chungeeb5bbc2013-11-13 15:47:05 -08002759 DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
Adam Cohen307fe232012-08-16 17:55:58 -07002760 DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale);
Winson Chungeeb5bbc2013-11-13 15:47:05 -08002761 dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
Adam Cohen5084cba2013-09-03 12:01:16 -07002762
2763 if (child.getParent() instanceof ShortcutAndWidgetContainer) {
2764 mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent();
2765 }
2766
Joe Onorato4be866d2010-10-10 11:26:02 -07002767 b.recycle();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002768 }
2769
Winson Chungbabb53e2014-04-14 17:12:49 -07002770 public void beginExternalDragShared(View child, DragSource source) {
2771 LauncherAppState app = LauncherAppState.getInstance();
2772 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
2773 int iconSize = grid.iconSizePx;
2774
2775 // Notify launcher of drag start
2776 mLauncher.onDragStarted(child);
2777
2778 // Compose a new drag bitmap that is of the icon size
Sunny Goyal95abbb32014-08-04 10:53:22 -07002779 AtomicInteger padding = new AtomicInteger(DRAG_BITMAP_PADDING);
Sunny Goyal508da152014-08-14 10:53:27 -07002780 final Bitmap tmpB = createDragBitmap(child, padding);
Winson Chungbabb53e2014-04-14 17:12:49 -07002781 Bitmap b = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
2782 Paint p = new Paint();
2783 p.setFilterBitmap(true);
Sunny Goyal508da152014-08-14 10:53:27 -07002784 mCanvas.setBitmap(b);
2785 mCanvas.drawBitmap(tmpB, new Rect(0, 0, tmpB.getWidth(), tmpB.getHeight()),
Winson Chungbabb53e2014-04-14 17:12:49 -07002786 new Rect(0, 0, iconSize, iconSize), p);
Sunny Goyal508da152014-08-14 10:53:27 -07002787 mCanvas.setBitmap(null);
Winson Chungbabb53e2014-04-14 17:12:49 -07002788
2789 // Find the child's location on the screen
2790 int bmpWidth = tmpB.getWidth();
2791 float iconScale = (float) bmpWidth / iconSize;
2792 float scale = mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY) * iconScale;
2793 int dragLayerX = Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2);
2794 int dragLayerY = Math.round(mTempXY[1]);
2795
2796 // Note: The drag region is used to calculate drag layer offsets, but the
2797 // dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
Sunny Goyal95abbb32014-08-04 10:53:22 -07002798 Point dragVisualizeOffset = new Point(-padding.get() / 2, padding.get() / 2);
Winson Chungbabb53e2014-04-14 17:12:49 -07002799 Rect dragRect = new Rect(0, 0, iconSize, iconSize);
2800
2801 if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) {
2802 String msg = "Drag started with a view that has no tag set. This "
2803 + "will cause a crash (issue 11627249) down the line. "
2804 + "View: " + child + " tag: " + child.getTag();
2805 throw new IllegalStateException(msg);
2806 }
2807
2808 // Start the drag
2809 DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
2810 DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale);
2811 dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
2812
2813 // Recycle temporary bitmaps
2814 tmpB.recycle();
2815 }
2816
Adam Cohendcd297f2013-06-18 13:13:40 -07002817 void addApplicationShortcut(ShortcutInfo info, CellLayout target, long container, long screenId,
Winson Chung3d503fb2011-07-13 17:25:49 -07002818 int cellX, int cellY, boolean insertAtFirst, int intersectX, int intersectY) {
2819 View view = mLauncher.createShortcut(R.layout.application, target, (ShortcutInfo) info);
Michael Jurka0280c3b2010-09-17 15:00:07 -07002820
2821 final int[] cellXY = new int[2];
Winson Chung3d503fb2011-07-13 17:25:49 -07002822 target.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY);
Adam Cohendcd297f2013-06-18 13:13:40 -07002823 addInScreen(view, container, screenId, cellXY[0], cellXY[1], 1, 1, insertAtFirst);
Adam Cohen7c4c5102013-06-14 17:42:35 -07002824
Adam Cohendcd297f2013-06-18 13:13:40 -07002825 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId, cellXY[0],
Winson Chung3d503fb2011-07-13 17:25:49 -07002826 cellXY[1]);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002827 }
2828
Adam Cohen4b285c52011-07-21 14:24:06 -07002829 public boolean transitionStateShouldAllowDrop() {
Adam Cohen6c5891a2014-07-09 23:53:15 -07002830 return ((!isSwitchingState() || mTransitionProgress > 0.5f) &&
2831 (mState == State.NORMAL || mState == State.SPRING_LOADED));
Adam Cohen4b285c52011-07-21 14:24:06 -07002832 }
2833
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002834 /**
2835 * {@inheritDoc}
2836 */
Adam Cohencb3382b2011-05-24 14:07:08 -07002837 public boolean acceptDrop(DragObject d) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002838 // If it's an external drop (e.g. from All Apps), check if it should be accepted
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002839 CellLayout dropTargetLayout = mDropToLayout;
Adam Cohencb3382b2011-05-24 14:07:08 -07002840 if (d.dragSource != this) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002841 // Don't accept the drop if we're not over a screen at time of drop
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002842 if (dropTargetLayout == null) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002843 return false;
2844 }
Adam Cohen4b285c52011-07-21 14:24:06 -07002845 if (!transitionStateShouldAllowDrop()) return false;
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002846
Adam Cohena65beee2011-06-27 21:32:23 -07002847 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset,
2848 d.dragView, mDragViewVisualCenter);
2849
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07002850 // We want the point to be mapped to the dragTarget.
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002851 if (mLauncher.isHotseatLayout(dropTargetLayout)) {
Adam Cohen307fe232012-08-16 17:55:58 -07002852 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07002853 } else {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002854 mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null);
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07002855 }
2856
Winson Chung557d6ed2011-07-08 15:34:52 -07002857 int spanX = 1;
2858 int spanY = 1;
Winson Chung557d6ed2011-07-08 15:34:52 -07002859 if (mDragInfo != null) {
2860 final CellLayout.CellInfo dragCellInfo = mDragInfo;
2861 spanX = dragCellInfo.spanX;
2862 spanY = dragCellInfo.spanY;
Winson Chung557d6ed2011-07-08 15:34:52 -07002863 } else {
2864 final ItemInfo dragInfo = (ItemInfo) d.dragInfo;
2865 spanX = dragInfo.spanX;
2866 spanY = dragInfo.spanY;
2867 }
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002868
Adam Cohend41fbf52012-02-16 23:53:59 -08002869 int minSpanX = spanX;
2870 int minSpanY = spanY;
2871 if (d.dragInfo instanceof PendingAddWidgetInfo) {
2872 minSpanX = ((PendingAddWidgetInfo) d.dragInfo).minSpanX;
2873 minSpanY = ((PendingAddWidgetInfo) d.dragInfo).minSpanY;
2874 }
Adam Cohen482ed822012-03-02 14:15:13 -08002875
Adam Cohena65beee2011-06-27 21:32:23 -07002876 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002877 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, dropTargetLayout,
Adam Cohenf0777b92012-02-28 14:02:45 -08002878 mTargetCell);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002879 float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08002880 mDragViewVisualCenter[1], mTargetCell);
Adam Cohenec0d61d2014-02-07 16:34:51 -08002881 if (mCreateUserFolderOnDrop && willCreateUserFolder((ItemInfo) d.dragInfo,
2882 dropTargetLayout, mTargetCell, distance, true)) {
Adam Cohena65beee2011-06-27 21:32:23 -07002883 return true;
2884 }
Adam Cohenec0d61d2014-02-07 16:34:51 -08002885
2886 if (mAddToExistingFolderOnDrop && willAddToExistingUserFolder((ItemInfo) d.dragInfo,
2887 dropTargetLayout, mTargetCell, distance)) {
Adam Cohena65beee2011-06-27 21:32:23 -07002888 return true;
2889 }
2890
Adam Cohen482ed822012-03-02 14:15:13 -08002891 int[] resultSpan = new int[2];
Adam Cohenfa3c58f2013-12-06 16:10:55 -08002892 mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08002893 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
2894 null, mTargetCell, resultSpan, CellLayout.MODE_ACCEPT_DROP);
2895 boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
2896
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002897 // Don't accept the drop if there's no room for the item
Adam Cohen482ed822012-03-02 14:15:13 -08002898 if (!foundCell) {
Winson Chung96ef4092011-11-22 12:25:14 -08002899 // Don't show the message if we are dropping on the AllApps button and the hotseat
2900 // is full
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002901 boolean isHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
Winson Chung93eef082012-03-23 15:59:27 -07002902 if (mTargetCell != null && isHotseat) {
Winson Chung96ef4092011-11-22 12:25:14 -08002903 Hotseat hotseat = mLauncher.getHotseat();
Andrew Flynn0dca1ec2012-02-29 13:33:22 -08002904 if (hotseat.isAllAppsButtonRank(
Winson Chung96ef4092011-11-22 12:25:14 -08002905 hotseat.getOrderInHotseat(mTargetCell[0], mTargetCell[1]))) {
2906 return false;
2907 }
2908 }
2909
Winson Chung93eef082012-03-23 15:59:27 -07002910 mLauncher.showOutOfSpaceMessage(isHotseat);
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002911 return false;
2912 }
2913 }
Adam Cohendedbd962013-07-11 14:21:49 -07002914
2915 long screenId = getIdForScreen(dropTargetLayout);
2916 if (screenId == EXTRA_EMPTY_SCREEN_ID) {
2917 commitExtraEmptyScreen();
2918 }
2919
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002920 return true;
2921 }
2922
Adam Cohen482ed822012-03-02 14:15:13 -08002923 boolean willCreateUserFolder(ItemInfo info, CellLayout target, int[] targetCell, float
2924 distance, boolean considerTimeout) {
2925 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohena65beee2011-06-27 21:32:23 -07002926 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
2927
Adam Cohen19f37922012-03-21 11:59:11 -07002928 if (dropOverView != null) {
2929 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
2930 if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) {
2931 return false;
2932 }
2933 }
2934
Winson Chung3d503fb2011-07-13 17:25:49 -07002935 boolean hasntMoved = false;
2936 if (mDragInfo != null) {
Adam Cohen482ed822012-03-02 14:15:13 -08002937 hasntMoved = dropOverView == mDragInfo.cell;
Winson Chung3d503fb2011-07-13 17:25:49 -07002938 }
Adam Cohene0310962011-04-18 16:15:31 -07002939
Adam Cohena65beee2011-06-27 21:32:23 -07002940 if (dropOverView == null || hasntMoved || (considerTimeout && !mCreateUserFolderOnDrop)) {
2941 return false;
2942 }
Adam Cohene0310962011-04-18 16:15:31 -07002943
Adam Cohena65beee2011-06-27 21:32:23 -07002944 boolean aboveShortcut = (dropOverView.getTag() instanceof ShortcutInfo);
Adam Cohene0310962011-04-18 16:15:31 -07002945 boolean willBecomeShortcut =
Adam Cohenc0dcf592011-06-01 15:30:43 -07002946 (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
2947 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT);
Adam Cohene0310962011-04-18 16:15:31 -07002948
2949 return (aboveShortcut && willBecomeShortcut);
2950 }
2951
Adam Cohen482ed822012-03-02 14:15:13 -08002952 boolean willAddToExistingUserFolder(Object dragInfo, CellLayout target, int[] targetCell,
2953 float distance) {
2954 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohena65beee2011-06-27 21:32:23 -07002955 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohen19f37922012-03-21 11:59:11 -07002956
2957 if (dropOverView != null) {
2958 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
2959 if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) {
2960 return false;
2961 }
2962 }
2963
Adam Cohena65beee2011-06-27 21:32:23 -07002964 if (dropOverView instanceof FolderIcon) {
2965 FolderIcon fi = (FolderIcon) dropOverView;
2966 if (fi.acceptDrop(dragInfo)) {
2967 return true;
2968 }
2969 }
2970 return false;
2971 }
2972
Winson Chung3d503fb2011-07-13 17:25:49 -07002973 boolean createUserFolderIfNecessary(View newView, long container, CellLayout target,
Adam Cohen482ed822012-03-02 14:15:13 -08002974 int[] targetCell, float distance, boolean external, DragView dragView,
2975 Runnable postAnimationRunnable) {
2976 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohenc0dcf592011-06-01 15:30:43 -07002977 View v = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohen19f37922012-03-21 11:59:11 -07002978
Winson Chungec9a0a42011-07-20 20:42:13 -07002979 boolean hasntMoved = false;
2980 if (mDragInfo != null) {
2981 CellLayout cellParent = getParentCellLayoutForView(mDragInfo.cell);
2982 hasntMoved = (mDragInfo.cellX == targetCell[0] &&
2983 mDragInfo.cellY == targetCell[1]) && (cellParent == target);
2984 }
Adam Cohen10b17372011-04-15 14:21:25 -07002985
Adam Cohen19072da2011-05-31 14:30:45 -07002986 if (v == null || hasntMoved || !mCreateUserFolderOnDrop) return false;
2987 mCreateUserFolderOnDrop = false;
Adam Cohendcd297f2013-06-18 13:13:40 -07002988 final long screenId = (targetCell == null) ? mDragInfo.screenId : getIdForScreen(target);
Adam Cohen10b17372011-04-15 14:21:25 -07002989
2990 boolean aboveShortcut = (v.getTag() instanceof ShortcutInfo);
2991 boolean willBecomeShortcut = (newView.getTag() instanceof ShortcutInfo);
2992
2993 if (aboveShortcut && willBecomeShortcut) {
2994 ShortcutInfo sourceInfo = (ShortcutInfo) newView.getTag();
2995 ShortcutInfo destInfo = (ShortcutInfo) v.getTag();
2996 // if the drag started here, we need to remove it from the workspace
2997 if (!external) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002998 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
Adam Cohen10b17372011-04-15 14:21:25 -07002999 }
3000
Adam Cohend0445262011-07-04 23:53:22 -07003001 Rect folderLocation = new Rect();
Adam Cohenac8c8762011-07-13 11:15:27 -07003002 float scale = mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation);
Adam Cohen10b17372011-04-15 14:21:25 -07003003 target.removeView(v);
Adam Cohend0445262011-07-04 23:53:22 -07003004
Winson Chung3d503fb2011-07-13 17:25:49 -07003005 FolderIcon fi =
Adam Cohendcd297f2013-06-18 13:13:40 -07003006 mLauncher.addFolder(target, container, screenId, targetCell[0], targetCell[1]);
Adam Cohena9cf38f2011-05-02 15:36:58 -07003007 destInfo.cellX = -1;
3008 destInfo.cellY = -1;
3009 sourceInfo.cellX = -1;
3010 sourceInfo.cellY = -1;
Adam Cohend0445262011-07-04 23:53:22 -07003011
Adam Cohen558baaf2011-08-15 15:22:57 -07003012 // If the dragView is null, we can't animate
3013 boolean animate = dragView != null;
3014 if (animate) {
3015 fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale,
3016 postAnimationRunnable);
3017 } else {
3018 fi.addItem(destInfo);
3019 fi.addItem(sourceInfo);
3020 }
Adam Cohen10b17372011-04-15 14:21:25 -07003021 return true;
3022 }
3023 return false;
3024 }
3025
Adam Cohena65beee2011-06-27 21:32:23 -07003026 boolean addToExistingFolderIfNecessary(View newView, CellLayout target, int[] targetCell,
Adam Cohen482ed822012-03-02 14:15:13 -08003027 float distance, DragObject d, boolean external) {
3028 if (distance > mMaxDistanceForFolderCreation) return false;
3029
Adam Cohena65beee2011-06-27 21:32:23 -07003030 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003031 if (!mAddToExistingFolderOnDrop) return false;
3032 mAddToExistingFolderOnDrop = false;
Adam Cohen19f37922012-03-21 11:59:11 -07003033
Adam Cohenc0dcf592011-06-01 15:30:43 -07003034 if (dropOverView instanceof FolderIcon) {
3035 FolderIcon fi = (FolderIcon) dropOverView;
Adam Cohen3e8f8112011-07-02 18:03:00 -07003036 if (fi.acceptDrop(d.dragInfo)) {
3037 fi.onDrop(d);
Adam Cohenc0dcf592011-06-01 15:30:43 -07003038
3039 // if the drag started here, we need to remove it from the workspace
3040 if (!external) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003041 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
Adam Cohenc0dcf592011-06-01 15:30:43 -07003042 }
3043 return true;
3044 }
3045 }
3046 return false;
3047 }
3048
Adam Cohend41fbf52012-02-16 23:53:59 -08003049 public void onDrop(final DragObject d) {
Adam Cohencb3382b2011-05-24 14:07:08 -07003050 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, d.dragView,
Adam Cohene3e27a82011-04-15 12:07:39 -07003051 mDragViewVisualCenter);
3052
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003053 CellLayout dropTargetLayout = mDropToLayout;
3054
Adam Cohene3e27a82011-04-15 12:07:39 -07003055 // We want the point to be mapped to the dragTarget.
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003056 if (dropTargetLayout != null) {
3057 if (mLauncher.isHotseatLayout(dropTargetLayout)) {
Adam Cohenbb00ff22012-07-12 15:43:01 -07003058 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
Winson Chung3d503fb2011-07-13 17:25:49 -07003059 } else {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003060 mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null);
Winson Chung3d503fb2011-07-13 17:25:49 -07003061 }
Adam Cohenba781612011-05-09 14:37:39 -07003062 }
Michael Jurkac6ee42e2010-09-30 12:04:50 -07003063
Adam Cohened51cc92011-08-01 20:28:08 -07003064 int snapScreen = -1;
Adam Cohend41fbf52012-02-16 23:53:59 -08003065 boolean resizeOnDrop = false;
Adam Cohencb3382b2011-05-24 14:07:08 -07003066 if (d.dragSource != this) {
Adam Cohene3e27a82011-04-15 12:07:39 -07003067 final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0],
3068 (int) mDragViewVisualCenter[1] };
Adam Cohen3e8f8112011-07-02 18:03:00 -07003069 onDropExternal(touchXY, d.dragInfo, dropTargetLayout, false, d);
Patrick Dubroyce34a972010-10-19 10:34:32 -07003070 } else if (mDragInfo != null) {
Patrick Dubroyce34a972010-10-19 10:34:32 -07003071 final View cell = mDragInfo.cell;
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08003072
Adam Cohend41fbf52012-02-16 23:53:59 -08003073 Runnable resizeRunnable = null;
Michael Jurka1e2f4652013-07-08 18:03:46 -07003074 if (dropTargetLayout != null && !d.cancelled) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003075 // Move internally
Winson Chung40e882b2011-07-21 19:01:11 -07003076 boolean hasMovedLayouts = (getParentCellLayoutForView(cell) != dropTargetLayout);
3077 boolean hasMovedIntoHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
3078 long container = hasMovedIntoHotseat ?
Winson Chung3d503fb2011-07-13 17:25:49 -07003079 LauncherSettings.Favorites.CONTAINER_HOTSEAT :
3080 LauncherSettings.Favorites.CONTAINER_DESKTOP;
Adam Cohendcd297f2013-06-18 13:13:40 -07003081 long screenId = (mTargetCell[0] < 0) ?
3082 mDragInfo.screenId : getIdForScreen(dropTargetLayout);
Adam Cohenc0dcf592011-06-01 15:30:43 -07003083 int spanX = mDragInfo != null ? mDragInfo.spanX : 1;
3084 int spanY = mDragInfo != null ? mDragInfo.spanY : 1;
3085 // First we find the cell nearest to point at which the item is
3086 // dropped, without any consideration to whether there is an item there.
Adam Cohen482ed822012-03-02 14:15:13 -08003087
Adam Cohenc0dcf592011-06-01 15:30:43 -07003088 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], (int)
3089 mDragViewVisualCenter[1], spanX, spanY, dropTargetLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08003090 float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
3091 mDragViewVisualCenter[1], mTargetCell);
3092
Adam Cohenc0dcf592011-06-01 15:30:43 -07003093 // If the item being dropped is a shortcut and the nearest drop
Adam Cohen76078c42011-06-09 15:06:52 -07003094 // cell also contains a shortcut, then create a folder with the two shortcuts.
Winson Chung1c4cf4a2011-07-29 14:49:10 -07003095 if (!mInScrollArea && createUserFolderIfNecessary(cell, container,
Adam Cohen482ed822012-03-02 14:15:13 -08003096 dropTargetLayout, mTargetCell, distance, false, d.dragView, null)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07003097 return;
3098 }
3099
Adam Cohen482ed822012-03-02 14:15:13 -08003100 if (addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell,
3101 distance, d, false)) {
Adam Cohendf035382011-04-11 17:22:04 -07003102 return;
3103 }
3104
3105 // Aside from the special case where we're dropping a shortcut onto a shortcut,
3106 // we need to find the nearest cell location that is vacant
Adam Cohend41fbf52012-02-16 23:53:59 -08003107 ItemInfo item = (ItemInfo) d.dragInfo;
3108 int minSpanX = item.spanX;
3109 int minSpanY = item.spanY;
3110 if (item.minSpanX > 0 && item.minSpanY > 0) {
3111 minSpanX = item.minSpanX;
3112 minSpanY = item.minSpanY;
3113 }
Adam Cohen482ed822012-03-02 14:15:13 -08003114
Adam Cohend41fbf52012-02-16 23:53:59 -08003115 int[] resultSpan = new int[2];
Adam Cohenfa3c58f2013-12-06 16:10:55 -08003116 mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08003117 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, cell,
3118 mTargetCell, resultSpan, CellLayout.MODE_ON_DROP);
3119
Adam Cohend41fbf52012-02-16 23:53:59 -08003120 boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
Adam Cohen9e05a5e2012-09-10 15:53:09 -07003121
Adam Cohenaaa5c212012-10-05 18:14:31 -07003122 // if the widget resizes on drop
Adam Cohen9e05a5e2012-09-10 15:53:09 -07003123 if (foundCell && (cell instanceof AppWidgetHostView) &&
Adam Cohenaaa5c212012-10-05 18:14:31 -07003124 (resultSpan[0] != item.spanX || resultSpan[1] != item.spanY)) {
Adam Cohend41fbf52012-02-16 23:53:59 -08003125 resizeOnDrop = true;
3126 item.spanX = resultSpan[0];
3127 item.spanY = resultSpan[1];
Adam Cohen7bdfc972012-05-22 16:50:35 -07003128 AppWidgetHostView awhv = (AppWidgetHostView) cell;
3129 AppWidgetResizeFrame.updateWidgetSizeRanges(awhv, mLauncher, resultSpan[0],
3130 resultSpan[1]);
Adam Cohend41fbf52012-02-16 23:53:59 -08003131 }
Adam Cohendf035382011-04-11 17:22:04 -07003132
Adam Cohendcd297f2013-06-18 13:13:40 -07003133 if (getScreenIdForPageIndex(mCurrentPage) != screenId && !hasMovedIntoHotseat) {
3134 snapScreen = getPageIndexForScreenId(screenId);
3135 snapToPage(snapScreen);
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08003136 }
3137
Adam Cohend41fbf52012-02-16 23:53:59 -08003138 if (foundCell) {
3139 final ItemInfo info = (ItemInfo) cell.getTag();
Winson Chung40e882b2011-07-21 19:01:11 -07003140 if (hasMovedLayouts) {
Patrick Dubroy94383362010-10-29 15:03:24 -07003141 // Reparent the view
Sunny Goyal25611b12014-07-22 09:52:37 -07003142 CellLayout parentCell = getParentCellLayoutForView(cell);
3143 if (parentCell != null) {
3144 parentCell.removeView(cell);
3145 } else if (LauncherAppState.isDogfoodBuild()) {
3146 throw new NullPointerException("mDragInfo.cell has null parent");
3147 }
Adam Cohendcd297f2013-06-18 13:13:40 -07003148 addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1],
Adam Cohend41fbf52012-02-16 23:53:59 -08003149 info.spanX, info.spanY);
Patrick Dubroy94383362010-10-29 15:03:24 -07003150 }
3151
3152 // update the item's position after drop
Patrick Dubroy94383362010-10-29 15:03:24 -07003153 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
Adam Cohen19f37922012-03-21 11:59:11 -07003154 lp.cellX = lp.tmpCellX = mTargetCell[0];
3155 lp.cellY = lp.tmpCellY = mTargetCell[1];
Adam Cohend41fbf52012-02-16 23:53:59 -08003156 lp.cellHSpan = item.spanX;
3157 lp.cellVSpan = item.spanY;
Adam Cohen19f37922012-03-21 11:59:11 -07003158 lp.isLockedToGrid = true;
Patrick Dubroy94383362010-10-29 15:03:24 -07003159
Winson Chung3d503fb2011-07-13 17:25:49 -07003160 if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
3161 cell instanceof LauncherAppWidgetHostView) {
Adam Cohend4844c32011-02-18 19:25:06 -08003162 final CellLayout cellLayout = dropTargetLayout;
3163 // We post this call so that the widget has a chance to be placed
3164 // in its final location
3165
3166 final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell;
Adam Cohen59400422014-03-05 18:07:04 -08003167 LauncherAppWidgetProviderInfo pInfo = (LauncherAppWidgetProviderInfo)
3168 hostView.getAppWidgetInfo();
3169 if (pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) {
Adam Cohend41fbf52012-02-16 23:53:59 -08003170 final Runnable addResizeFrame = new Runnable() {
Adam Cohend4844c32011-02-18 19:25:06 -08003171 public void run() {
Adam Cohen716b51e2011-06-30 12:09:54 -07003172 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohenc0dcf592011-06-01 15:30:43 -07003173 dragLayer.addResizeFrame(info, hostView, cellLayout);
Adam Cohend4844c32011-02-18 19:25:06 -08003174 }
Adam Cohen26976d92011-03-22 15:33:33 -07003175 };
Adam Cohend41fbf52012-02-16 23:53:59 -08003176 resizeRunnable = (new Runnable() {
Adam Cohen26976d92011-03-22 15:33:33 -07003177 public void run() {
3178 if (!isPageMoving()) {
Adam Cohend41fbf52012-02-16 23:53:59 -08003179 addResizeFrame.run();
Adam Cohen26976d92011-03-22 15:33:33 -07003180 } else {
Adam Cohend41fbf52012-02-16 23:53:59 -08003181 mDelayedResizeRunnable = addResizeFrame;
Adam Cohen26976d92011-03-22 15:33:33 -07003182 }
3183 }
Adam Cohend4844c32011-02-18 19:25:06 -08003184 });
3185 }
3186 }
3187
Adam Cohen949debe2013-09-29 14:22:48 -07003188 LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId, lp.cellX,
3189 lp.cellY, item.spanX, item.spanY);
Adam Cohend41fbf52012-02-16 23:53:59 -08003190 } else {
3191 // If we can't find a drop location, we return the item to its original position
3192 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
3193 mTargetCell[0] = lp.cellX;
3194 mTargetCell[1] = lp.cellY;
Adam Cohenf1dcdf62012-05-10 16:51:52 -07003195 CellLayout layout = (CellLayout) cell.getParent().getParent();
3196 layout.markCellsAsOccupiedForView(cell);
Patrick Dubroycd68ff52010-10-28 17:57:05 -07003197 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003198 }
Patrick Dubroyce34a972010-10-19 10:34:32 -07003199
Michael Jurka8c920dd2011-01-20 14:16:56 -08003200 final CellLayout parent = (CellLayout) cell.getParent().getParent();
Adam Cohend41fbf52012-02-16 23:53:59 -08003201 final Runnable finalResizeRunnable = resizeRunnable;
Patrick Dubroyce34a972010-10-19 10:34:32 -07003202 // Prepare it to be animated into its new position
3203 // This must be called after the view has been re-parented
Adam Cohend41fbf52012-02-16 23:53:59 -08003204 final Runnable onCompleteRunnable = new Runnable() {
Michael Jurkad74c9842011-07-10 12:44:21 -07003205 @Override
3206 public void run() {
3207 mAnimatingViewIntoPlace = false;
Michael Jurka3a0469d2012-06-21 09:38:41 -07003208 updateChildrenLayersEnabled(false);
Adam Cohend41fbf52012-02-16 23:53:59 -08003209 if (finalResizeRunnable != null) {
3210 finalResizeRunnable.run();
3211 }
Michael Jurkad74c9842011-07-10 12:44:21 -07003212 }
3213 };
3214 mAnimatingViewIntoPlace = true;
Adam Cohenfc53cd22011-07-20 15:45:11 -07003215 if (d.dragView.hasDrawn()) {
Adam Cohend41fbf52012-02-16 23:53:59 -08003216 final ItemInfo info = (ItemInfo) cell.getTag();
Adam Cohen59400422014-03-05 18:07:04 -08003217 boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
3218 || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
3219 if (isWidget) {
Adam Cohend41fbf52012-02-16 23:53:59 -08003220 int animationType = resizeOnDrop ? ANIMATE_INTO_POSITION_AND_RESIZE :
3221 ANIMATE_INTO_POSITION_AND_DISAPPEAR;
3222 animateWidgetDrop(info, parent, d.dragView,
3223 onCompleteRunnable, animationType, cell, false);
3224 } else {
Adam Cohen85b467b2012-02-29 15:38:46 -08003225 int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION;
3226 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration,
3227 onCompleteRunnable, this);
Adam Cohend41fbf52012-02-16 23:53:59 -08003228 }
Adam Cohenfc53cd22011-07-20 15:45:11 -07003229 } else {
Winson Chung7bd1bbb2012-02-13 18:29:29 -08003230 d.deferDragViewCleanupPostAnimation = false;
Adam Cohenfc53cd22011-07-20 15:45:11 -07003231 cell.setVisibility(VISIBLE);
3232 }
Adam Cohen716b51e2011-06-30 12:09:54 -07003233 parent.onDropChild(cell);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003234 }
3235 }
3236
Adam Cohendcd297f2013-06-18 13:13:40 -07003237 public void setFinalScrollForPageChange(int pageIndex) {
3238 CellLayout cl = (CellLayout) getChildAt(pageIndex);
3239 if (cl != null) {
Adam Cohened51cc92011-08-01 20:28:08 -07003240 mSavedScrollX = getScrollX();
Adam Cohened51cc92011-08-01 20:28:08 -07003241 mSavedTranslationX = cl.getTranslationX();
3242 mSavedRotationY = cl.getRotationY();
Adam Cohenedb40762013-07-18 16:45:45 -07003243 final int newX = getScrollForPage(pageIndex);
Adam Cohened51cc92011-08-01 20:28:08 -07003244 setScrollX(newX);
3245 cl.setTranslationX(0f);
3246 cl.setRotationY(0f);
3247 }
3248 }
3249
Adam Cohendcd297f2013-06-18 13:13:40 -07003250 public void resetFinalScrollForPageChange(int pageIndex) {
3251 if (pageIndex >= 0) {
3252 CellLayout cl = (CellLayout) getChildAt(pageIndex);
Adam Cohened51cc92011-08-01 20:28:08 -07003253 setScrollX(mSavedScrollX);
3254 cl.setTranslationX(mSavedTranslationX);
3255 cl.setRotationY(mSavedRotationY);
3256 }
3257 }
3258
Adam Cohen76078c42011-06-09 15:06:52 -07003259 public void getViewLocationRelativeToSelf(View v, int[] location) {
Adam Cohen8dfcba42011-07-07 16:38:18 -07003260 getLocationInWindow(location);
Adam Cohene3e27a82011-04-15 12:07:39 -07003261 int x = location[0];
3262 int y = location[1];
3263
Adam Cohen8dfcba42011-07-07 16:38:18 -07003264 v.getLocationInWindow(location);
Adam Cohene3e27a82011-04-15 12:07:39 -07003265 int vX = location[0];
3266 int vY = location[1];
3267
3268 location[0] = vX - x;
3269 location[1] = vY - y;
3270 }
3271
Adam Cohencb3382b2011-05-24 14:07:08 -07003272 public void onDragEnter(DragObject d) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003273 mDragEnforcer.onDragEnter();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003274 mCreateUserFolderOnDrop = false;
3275 mAddToExistingFolderOnDrop = false;
3276
3277 mDropToLayout = null;
3278 CellLayout layout = getCurrentDropLayout();
3279 setCurrentDropLayout(layout);
3280 setCurrentDragOverlappingLayout(layout);
Winson Chungb26f3d62011-06-02 10:49:29 -07003281
Adam Cohenc50438c2014-08-19 17:43:05 -07003282 if (!workspaceInModalState()) {
3283 mLauncher.getDragLayer().showPageHints();
Michael Jurkad718d6a2010-10-14 15:35:17 -07003284 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003285 }
3286
Winson Chungfe411c82013-09-26 16:07:17 -07003287 /** Return a rect that has the cellWidth/cellHeight (left, top), and
3288 * widthGap/heightGap (right, bottom) */
Adam Cohena897f392012-04-27 18:12:05 -07003289 static Rect getCellLayoutMetrics(Launcher launcher, int orientation) {
Winson Chung5f8afe62013-08-12 16:19:28 -07003290 LauncherAppState app = LauncherAppState.getInstance();
3291 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
3292
Adam Cohena897f392012-04-27 18:12:05 -07003293 Display display = launcher.getWindowManager().getDefaultDisplay();
3294 Point smallestSize = new Point();
3295 Point largestSize = new Point();
3296 display.getCurrentSizeRange(smallestSize, largestSize);
Winson Chung892c74d2013-08-22 16:15:50 -07003297 int countX = (int) grid.numColumns;
3298 int countY = (int) grid.numRows;
Adam Cohena897f392012-04-27 18:12:05 -07003299 if (orientation == CellLayout.LANDSCAPE) {
3300 if (mLandscapeCellLayoutMetrics == null) {
Winson Chung5f8afe62013-08-12 16:19:28 -07003301 Rect padding = grid.getWorkspacePadding(CellLayout.LANDSCAPE);
Winson Chungdcd27ba2013-12-11 15:28:15 -08003302 int width = largestSize.x - padding.left - padding.right;
3303 int height = smallestSize.y - padding.top - padding.bottom;
Adam Cohena897f392012-04-27 18:12:05 -07003304 mLandscapeCellLayoutMetrics = new Rect();
Winson Chungfe411c82013-09-26 16:07:17 -07003305 mLandscapeCellLayoutMetrics.set(
3306 grid.calculateCellWidth(width, countX),
3307 grid.calculateCellHeight(height, countY), 0, 0);
Adam Cohena897f392012-04-27 18:12:05 -07003308 }
3309 return mLandscapeCellLayoutMetrics;
3310 } else if (orientation == CellLayout.PORTRAIT) {
3311 if (mPortraitCellLayoutMetrics == null) {
Winson Chung5f8afe62013-08-12 16:19:28 -07003312 Rect padding = grid.getWorkspacePadding(CellLayout.PORTRAIT);
Winson Chungdcd27ba2013-12-11 15:28:15 -08003313 int width = smallestSize.x - padding.left - padding.right;
3314 int height = largestSize.y - padding.top - padding.bottom;
Adam Cohena897f392012-04-27 18:12:05 -07003315 mPortraitCellLayoutMetrics = new Rect();
Winson Chungfe411c82013-09-26 16:07:17 -07003316 mPortraitCellLayoutMetrics.set(
3317 grid.calculateCellWidth(width, countX),
3318 grid.calculateCellHeight(height, countY), 0, 0);
Adam Cohena897f392012-04-27 18:12:05 -07003319 }
3320 return mPortraitCellLayoutMetrics;
3321 }
3322 return null;
3323 }
3324
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003325 public void onDragExit(DragObject d) {
3326 mDragEnforcer.onDragExit();
Winson Chungc07918d2011-07-01 15:35:26 -07003327
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003328 // Here we store the final page that will be dropped to, if the workspace in fact
3329 // receives the drop
3330 if (mInScrollArea) {
Winson Chungcc1cfe42012-06-18 15:09:17 -07003331 if (isPageMoving()) {
3332 // If the user drops while the page is scrolling, we should use that page as the
3333 // destination instead of the page that is being hovered over.
3334 mDropToLayout = (CellLayout) getPageAt(getNextPage());
3335 } else {
3336 mDropToLayout = mDragOverlappingLayout;
3337 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003338 } else {
3339 mDropToLayout = mDragTargetLayout;
3340 }
3341
3342 if (mDragMode == DRAG_MODE_CREATE_FOLDER) {
3343 mCreateUserFolderOnDrop = true;
3344 } else if (mDragMode == DRAG_MODE_ADD_TO_FOLDER) {
3345 mAddToExistingFolderOnDrop = true;
Adam Cohen482ed822012-03-02 14:15:13 -08003346 }
3347
Winson Chungc07918d2011-07-01 15:35:26 -07003348 // Reset the scroll area and previous drag target
3349 onResetScrollArea();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003350 setCurrentDropLayout(null);
3351 setCurrentDragOverlappingLayout(null);
Winson Chungc07918d2011-07-01 15:35:26 -07003352
Adam Cohen74c28d12011-11-18 14:17:11 -08003353 mSpringLoadedDragController.cancel();
Winson Chungc07918d2011-07-01 15:35:26 -07003354
3355 if (!mIsPageMoving) {
3356 hideOutlines();
3357 }
Adam Cohenc50438c2014-08-19 17:43:05 -07003358 mLauncher.getDragLayer().hidePageHints();
Winson Chungc07918d2011-07-01 15:35:26 -07003359 }
3360
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003361 void setCurrentDropLayout(CellLayout layout) {
3362 if (mDragTargetLayout != null) {
3363 mDragTargetLayout.revertTempState();
3364 mDragTargetLayout.onDragExit();
3365 }
3366 mDragTargetLayout = layout;
3367 if (mDragTargetLayout != null) {
3368 mDragTargetLayout.onDragEnter();
3369 }
3370 cleanupReorder(true);
3371 cleanupFolderCreation();
3372 setCurrentDropOverCell(-1, -1);
3373 }
3374
3375 void setCurrentDragOverlappingLayout(CellLayout layout) {
3376 if (mDragOverlappingLayout != null) {
3377 mDragOverlappingLayout.setIsDragOverlapping(false);
3378 }
3379 mDragOverlappingLayout = layout;
3380 if (mDragOverlappingLayout != null) {
3381 mDragOverlappingLayout.setIsDragOverlapping(true);
3382 }
3383 invalidate();
3384 }
3385
3386 void setCurrentDropOverCell(int x, int y) {
3387 if (x != mDragOverX || y != mDragOverY) {
3388 mDragOverX = x;
3389 mDragOverY = y;
3390 setDragMode(DRAG_MODE_NONE);
3391 }
3392 }
3393
3394 void setDragMode(int dragMode) {
3395 if (dragMode != mDragMode) {
3396 if (dragMode == DRAG_MODE_NONE) {
3397 cleanupAddToFolder();
3398 // We don't want to cancel the re-order alarm every time the target cell changes
3399 // as this feels to slow / unresponsive.
3400 cleanupReorder(false);
3401 cleanupFolderCreation();
3402 } else if (dragMode == DRAG_MODE_ADD_TO_FOLDER) {
3403 cleanupReorder(true);
3404 cleanupFolderCreation();
3405 } else if (dragMode == DRAG_MODE_CREATE_FOLDER) {
3406 cleanupAddToFolder();
3407 cleanupReorder(true);
3408 } else if (dragMode == DRAG_MODE_REORDER) {
3409 cleanupAddToFolder();
3410 cleanupFolderCreation();
3411 }
3412 mDragMode = dragMode;
3413 }
3414 }
3415
3416 private void cleanupFolderCreation() {
3417 if (mDragFolderRingAnimator != null) {
3418 mDragFolderRingAnimator.animateToNaturalState();
Adam Cohen8ec05f92013-10-13 12:29:03 -07003419 mDragFolderRingAnimator = null;
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003420 }
Adam Cohen8ec05f92013-10-13 12:29:03 -07003421 mFolderCreationAlarm.setOnAlarmListener(null);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003422 mFolderCreationAlarm.cancelAlarm();
3423 }
3424
3425 private void cleanupAddToFolder() {
3426 if (mDragOverFolderIcon != null) {
3427 mDragOverFolderIcon.onDragExit(null);
3428 mDragOverFolderIcon = null;
3429 }
3430 }
3431
3432 private void cleanupReorder(boolean cancelAlarm) {
3433 // Any pending reorders are canceled
3434 if (cancelAlarm) {
3435 mReorderAlarm.cancelAlarm();
3436 }
3437 mLastReorderX = -1;
3438 mLastReorderY = -1;
Winson Chungc07918d2011-07-01 15:35:26 -07003439 }
3440
Michael Jurka4516c112010-10-07 15:13:47 -07003441 /*
3442 *
3443 * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
3444 * coordinate space. The argument xy is modified with the return result.
3445 *
3446 * if cachedInverseMatrix is not null, this method will just use that matrix instead of
Michael Jurkad718d6a2010-10-14 15:35:17 -07003447 * computing it itself; we use this to avoid redundant matrix inversions in
Michael Jurka4516c112010-10-07 15:13:47 -07003448 * findMatchingPageForDragOver
3449 *
3450 */
3451 void mapPointFromSelfToChild(View v, float[] xy, Matrix cachedInverseMatrix) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003452 xy[0] = xy[0] - v.getLeft();
3453 xy[1] = xy[1] - v.getTop();
Michael Jurka4516c112010-10-07 15:13:47 -07003454 }
3455
Adam Cohen7d30a372013-07-01 17:03:59 -07003456 boolean isPointInSelfOverHotseat(int x, int y, Rect r) {
3457 if (r == null) {
3458 r = new Rect();
3459 }
3460 mTempPt[0] = x;
3461 mTempPt[1] = y;
3462 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempPt, true);
Winson Chungabedd9f2013-09-24 15:41:09 -07003463
3464 LauncherAppState app = LauncherAppState.getInstance();
3465 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
3466 r = grid.getHotseatRect();
Adam Cohen7d30a372013-07-01 17:03:59 -07003467 if (r.contains(mTempPt[0], mTempPt[1])) {
3468 return true;
3469 }
3470 return false;
3471 }
Winson Chung3d503fb2011-07-13 17:25:49 -07003472
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003473 void mapPointFromSelfToHotseatLayout(Hotseat hotseat, float[] xy) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003474 mTempPt[0] = (int) xy[0];
3475 mTempPt[1] = (int) xy[1];
3476 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempPt, true);
Winson Chung156ab5b2013-07-12 14:14:16 -07003477 mLauncher.getDragLayer().mapCoordInSelfToDescendent(hotseat.getLayout(), mTempPt);
Adam Cohen7d30a372013-07-01 17:03:59 -07003478
3479 xy[0] = mTempPt[0];
3480 xy[1] = mTempPt[1];
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003481 }
3482
Winson Chung3d503fb2011-07-13 17:25:49 -07003483 /*
Michael Jurka4516c112010-10-07 15:13:47 -07003484 *
3485 * Convert the 2D coordinate xy from this CellLayout's coordinate space to
3486 * the parent View's coordinate space. The argument xy is modified with the return result.
3487 *
3488 */
3489 void mapPointFromChildToSelf(View v, float[] xy) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003490 xy[0] += v.getLeft();
3491 xy[1] += v.getTop();
Michael Jurka4516c112010-10-07 15:13:47 -07003492 }
3493
Adam Cohene3e27a82011-04-15 12:07:39 -07003494 static private float squaredDistance(float[] point1, float[] point2) {
Michael Jurka4516c112010-10-07 15:13:47 -07003495 float distanceX = point1[0] - point2[0];
3496 float distanceY = point2[1] - point2[1];
3497 return distanceX * distanceX + distanceY * distanceY;
Adam Cohene3e27a82011-04-15 12:07:39 -07003498 }
Michael Jurka0280c3b2010-09-17 15:00:07 -07003499
Michael Jurka4516c112010-10-07 15:13:47 -07003500 /*
3501 *
Michael Jurka4516c112010-10-07 15:13:47 -07003502 * This method returns the CellLayout that is currently being dragged to. In order to drag
3503 * to a CellLayout, either the touch point must be directly over the CellLayout, or as a second
3504 * strategy, we see if the dragView is overlapping any CellLayout and choose the closest one
3505 *
3506 * Return null if no CellLayout is currently being dragged over
3507 *
3508 */
3509 private CellLayout findMatchingPageForDragOver(
Adam Cohen00618752011-07-20 12:06:04 -07003510 DragView dragView, float originX, float originY, boolean exact) {
Michael Jurka4516c112010-10-07 15:13:47 -07003511 // We loop through all the screens (ie CellLayouts) and see which ones overlap
3512 // with the item being dragged and then choose the one that's closest to the touch point
Michael Jurkaa63c4522010-08-19 13:52:27 -07003513 final int screenCount = getChildCount();
3514 CellLayout bestMatchingScreen = null;
Michael Jurka0280c3b2010-09-17 15:00:07 -07003515 float smallestDistSoFar = Float.MAX_VALUE;
Michael Jurka4516c112010-10-07 15:13:47 -07003516
Michael Jurkaa63c4522010-08-19 13:52:27 -07003517 for (int i = 0; i < screenCount; i++) {
Adam Cohendf041bc2013-09-22 12:32:22 -07003518 // The custom content screen is not a valid drag over option
3519 if (mScreenOrder.get(i) == CUSTOM_CONTENT_SCREEN_ID) {
3520 continue;
3521 }
3522
Winson Chung3d503fb2011-07-13 17:25:49 -07003523 CellLayout cl = (CellLayout) getChildAt(i);
Michael Jurkaa63c4522010-08-19 13:52:27 -07003524
Adam Cohen00618752011-07-20 12:06:04 -07003525 final float[] touchXy = {originX, originY};
Michael Jurka4516c112010-10-07 15:13:47 -07003526 // Transform the touch coordinates to the CellLayout's local coordinates
3527 // If the touch point is within the bounds of the cell layout, we can return immediately
Michael Jurka0280c3b2010-09-17 15:00:07 -07003528 cl.getMatrix().invert(mTempInverseMatrix);
Michael Jurka4516c112010-10-07 15:13:47 -07003529 mapPointFromSelfToChild(cl, touchXy, mTempInverseMatrix);
Michael Jurkaa63c4522010-08-19 13:52:27 -07003530
Michael Jurka4516c112010-10-07 15:13:47 -07003531 if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() &&
3532 touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) {
3533 return cl;
3534 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07003535
Winson Chung96ef4092011-11-22 12:25:14 -08003536 if (!exact) {
Michael Jurka4516c112010-10-07 15:13:47 -07003537 // Get the center of the cell layout in screen coordinates
3538 final float[] cellLayoutCenter = mTempCellLayoutCenterCoordinates;
3539 cellLayoutCenter[0] = cl.getWidth()/2;
3540 cellLayoutCenter[1] = cl.getHeight()/2;
3541 mapPointFromChildToSelf(cl, cellLayoutCenter);
Michael Jurka0280c3b2010-09-17 15:00:07 -07003542
Adam Cohen00618752011-07-20 12:06:04 -07003543 touchXy[0] = originX;
3544 touchXy[1] = originY;
Michael Jurka0280c3b2010-09-17 15:00:07 -07003545
Michael Jurka4516c112010-10-07 15:13:47 -07003546 // Calculate the distance between the center of the CellLayout
3547 // and the touch point
3548 float dist = squaredDistance(touchXy, cellLayoutCenter);
3549
3550 if (dist < smallestDistSoFar) {
Michael Jurka0280c3b2010-09-17 15:00:07 -07003551 smallestDistSoFar = dist;
Michael Jurkaa63c4522010-08-19 13:52:27 -07003552 bestMatchingScreen = cl;
Michael Jurkaa63c4522010-08-19 13:52:27 -07003553 }
Michael Jurka4516c112010-10-07 15:13:47 -07003554 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07003555 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07003556 return bestMatchingScreen;
3557 }
3558
Adam Cohene3e27a82011-04-15 12:07:39 -07003559 // This is used to compute the visual center of the dragView. This point is then
3560 // used to visualize drop locations and determine where to drop an item. The idea is that
3561 // the visual center represents the user's interpretation of where the item is, and hence
3562 // is the appropriate point to use when determining drop location.
3563 private float[] getDragViewVisualCenter(int x, int y, int xOffset, int yOffset,
3564 DragView dragView, float[] recycle) {
3565 float res[];
3566 if (recycle == null) {
3567 res = new float[2];
3568 } else {
3569 res = recycle;
3570 }
3571
3572 // First off, the drag view has been shifted in a way that is not represented in the
3573 // x and y values or the x/yOffsets. Here we account for that shift.
3574 x += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetX);
3575 y += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY);
3576
3577 // These represent the visual top and left of drag view if a dragRect was provided.
3578 // If a dragRect was not provided, then they correspond to the actual view left and
3579 // top, as the dragRect is in that case taken to be the entire dragView.
3580 // R.dimen.dragViewOffsetY.
3581 int left = x - xOffset;
3582 int top = y - yOffset;
3583
3584 // In order to find the visual center, we shift by half the dragRect
3585 res[0] = left + dragView.getDragRegion().width() / 2;
3586 res[1] = top + dragView.getDragRegion().height() / 2;
3587
3588 return res;
3589 }
3590
Winson Chungea359c62011-08-03 17:06:35 -07003591 private boolean isDragWidget(DragObject d) {
3592 return (d.dragInfo instanceof LauncherAppWidgetInfo ||
3593 d.dragInfo instanceof PendingAddWidgetInfo);
3594 }
3595 private boolean isExternalDragWidget(DragObject d) {
3596 return d.dragSource != this && isDragWidget(d);
3597 }
3598
Adam Cohencb3382b2011-05-24 14:07:08 -07003599 public void onDragOver(DragObject d) {
Winson Chungc07918d2011-07-01 15:35:26 -07003600 // Skip drag over events while we are dragging over side pages
Adam Cohen6c5891a2014-07-09 23:53:15 -07003601 if (mInScrollArea || !transitionStateShouldAllowDrop()) return;
Winson Chungc07918d2011-07-01 15:35:26 -07003602
Winson Chung4afe9b32011-07-27 17:46:20 -07003603 Rect r = new Rect();
Winson Chung3d503fb2011-07-13 17:25:49 -07003604 CellLayout layout = null;
Winson Chungc07918d2011-07-01 15:35:26 -07003605 ItemInfo item = (ItemInfo) d.dragInfo;
Sunny Goyal25611b12014-07-22 09:52:37 -07003606 if (item == null) {
3607 if (LauncherAppState.isDogfoodBuild()) {
3608 throw new NullPointerException("DragObject has null info");
3609 }
3610 return;
3611 }
Winson Chungc07918d2011-07-01 15:35:26 -07003612
3613 // Ensure that we have proper spans for the item that we are dropping
3614 if (item.spanX < 0 || item.spanY < 0) throw new RuntimeException("Improper spans found");
Adam Cohen00618752011-07-20 12:06:04 -07003615 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset,
Winson Chung4afe9b32011-07-27 17:46:20 -07003616 d.dragView, mDragViewVisualCenter);
Adam Cohen00618752011-07-20 12:06:04 -07003617
Adam Cohen482ed822012-03-02 14:15:13 -08003618 final View child = (mDragInfo == null) ? null : mDragInfo.cell;
Winson Chungc07918d2011-07-01 15:35:26 -07003619 // Identify whether we have dragged over a side page
Adam Cohen6c5891a2014-07-09 23:53:15 -07003620 if (workspaceInModalState()) {
Winson Chungea359c62011-08-03 17:06:35 -07003621 if (mLauncher.getHotseat() != null && !isExternalDragWidget(d)) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003622 if (isPointInSelfOverHotseat(d.x, d.y, r)) {
Winson Chung4afe9b32011-07-27 17:46:20 -07003623 layout = mLauncher.getHotseat().getLayout();
3624 }
3625 }
3626 if (layout == null) {
Winson Chung96ef4092011-11-22 12:25:14 -08003627 layout = findMatchingPageForDragOver(d.dragView, d.x, d.y, false);
Winson Chung4afe9b32011-07-27 17:46:20 -07003628 }
Winson Chungc07918d2011-07-01 15:35:26 -07003629 if (layout != mDragTargetLayout) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003630 setCurrentDropLayout(layout);
3631 setCurrentDragOverlappingLayout(layout);
Winson Chungc07918d2011-07-01 15:35:26 -07003632
Michael Jurkad74c9842011-07-10 12:44:21 -07003633 boolean isInSpringLoadedMode = (mState == State.SPRING_LOADED);
Winson Chungc07918d2011-07-01 15:35:26 -07003634 if (isInSpringLoadedMode) {
Winson Chung4afe9b32011-07-27 17:46:20 -07003635 if (mLauncher.isHotseatLayout(layout)) {
3636 mSpringLoadedDragController.cancel();
3637 } else {
3638 mSpringLoadedDragController.setAlarm(mDragTargetLayout);
3639 }
Winson Chungc07918d2011-07-01 15:35:26 -07003640 }
3641 }
3642 } else {
Winson Chung3d503fb2011-07-13 17:25:49 -07003643 // Test to see if we are over the hotseat otherwise just use the current page
Winson Chungea359c62011-08-03 17:06:35 -07003644 if (mLauncher.getHotseat() != null && !isDragWidget(d)) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003645 if (isPointInSelfOverHotseat(d.x, d.y, r)) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003646 layout = mLauncher.getHotseat().getLayout();
3647 }
3648 }
3649 if (layout == null) {
3650 layout = getCurrentDropLayout();
3651 }
Winson Chungc07918d2011-07-01 15:35:26 -07003652 if (layout != mDragTargetLayout) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003653 setCurrentDropLayout(layout);
3654 setCurrentDragOverlappingLayout(layout);
Winson Chungc07918d2011-07-01 15:35:26 -07003655 }
3656 }
3657
3658 // Handle the drag over
3659 if (mDragTargetLayout != null) {
Winson Chungc07918d2011-07-01 15:35:26 -07003660 // We want the point to be mapped to the dragTarget.
Winson Chung3d503fb2011-07-13 17:25:49 -07003661 if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003662 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
Winson Chung3d503fb2011-07-13 17:25:49 -07003663 } else {
3664 mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
3665 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003666
Winson Chungc07918d2011-07-01 15:35:26 -07003667 ItemInfo info = (ItemInfo) d.dragInfo;
Patrick Dubroy1262e362010-10-06 15:49:50 -07003668
Adam Cohen74c54912013-09-29 14:48:04 -07003669 int minSpanX = item.spanX;
3670 int minSpanY = item.spanY;
3671 if (item.minSpanX > 0 && item.minSpanY > 0) {
3672 minSpanX = item.minSpanX;
3673 minSpanY = item.minSpanY;
3674 }
3675
Winson Chungc07918d2011-07-01 15:35:26 -07003676 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
Adam Cohen74c54912013-09-29 14:48:04 -07003677 (int) mDragViewVisualCenter[1], minSpanX, minSpanY,
Adam Cohend024f982012-05-23 18:26:45 -07003678 mDragTargetLayout, mTargetCell);
Adam Cohen74c54912013-09-29 14:48:04 -07003679 int reorderX = mTargetCell[0];
3680 int reorderY = mTargetCell[1];
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003681
3682 setCurrentDropOverCell(mTargetCell[0], mTargetCell[1]);
3683
Adam Cohen482ed822012-03-02 14:15:13 -08003684 float targetCellDistance = mDragTargetLayout.getDistanceFromCell(
3685 mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell);
3686
Winson Chungc07918d2011-07-01 15:35:26 -07003687 final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0],
3688 mTargetCell[1]);
Winson Chung785d2eb2011-04-14 16:08:02 -07003689
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003690 manageFolderFeedback(info, mDragTargetLayout, mTargetCell,
3691 targetCellDistance, dragOverView);
Adam Cohen482ed822012-03-02 14:15:13 -08003692
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003693 boolean nearestDropOccupied = mDragTargetLayout.isNearestDropLocationOccupied((int)
3694 mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], item.spanX,
3695 item.spanY, child, mTargetCell);
3696
3697 if (!nearestDropOccupied) {
Adam Cohen19f37922012-03-21 11:59:11 -07003698 mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
3699 (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
3700 mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, false,
3701 d.dragView.getDragVisualizeOffset(), d.dragView.getDragRegion());
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003702 } else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
Adam Cohen74c54912013-09-29 14:48:04 -07003703 && !mReorderAlarm.alarmPending() && (mLastReorderX != reorderX ||
3704 mLastReorderY != reorderY)) {
Adam Cohend024f982012-05-23 18:26:45 -07003705
Adam Cohenfa3c58f2013-12-06 16:10:55 -08003706 int[] resultSpan = new int[2];
3707 mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0],
3708 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, item.spanX, item.spanY,
3709 child, mTargetCell, resultSpan, CellLayout.MODE_SHOW_REORDER_HINT);
3710
Adam Cohen19f37922012-03-21 11:59:11 -07003711 // Otherwise, if we aren't adding to or creating a folder and there's no pending
3712 // reorder, then we schedule a reorder
Adam Cohen482ed822012-03-02 14:15:13 -08003713 ReorderAlarmListener listener = new ReorderAlarmListener(mDragViewVisualCenter,
3714 minSpanX, minSpanY, item.spanX, item.spanY, d.dragView, child);
3715 mReorderAlarm.setOnAlarmListener(listener);
3716 mReorderAlarm.setAlarm(REORDER_TIMEOUT);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003717 }
3718
3719 if (mDragMode == DRAG_MODE_CREATE_FOLDER || mDragMode == DRAG_MODE_ADD_TO_FOLDER ||
3720 !nearestDropOccupied) {
3721 if (mDragTargetLayout != null) {
3722 mDragTargetLayout.revertTempState();
Michael Jurkad3ef3062010-11-23 16:23:58 -08003723 }
3724 }
Adam Cohen482ed822012-03-02 14:15:13 -08003725 }
3726 }
3727
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003728 private void manageFolderFeedback(ItemInfo info, CellLayout targetLayout,
3729 int[] targetCell, float distance, View dragOverView) {
Adam Cohen482ed822012-03-02 14:15:13 -08003730 boolean userFolderPending = willCreateUserFolder(info, targetLayout, targetCell, distance,
3731 false);
3732
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003733 if (mDragMode == DRAG_MODE_NONE && userFolderPending &&
3734 !mFolderCreationAlarm.alarmPending()) {
Adam Cohen482ed822012-03-02 14:15:13 -08003735 mFolderCreationAlarm.setOnAlarmListener(new
3736 FolderCreationAlarmListener(targetLayout, targetCell[0], targetCell[1]));
3737 mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003738 return;
Adam Cohen482ed822012-03-02 14:15:13 -08003739 }
3740
3741 boolean willAddToFolder =
3742 willAddToExistingUserFolder(info, targetLayout, targetCell, distance);
3743
3744 if (willAddToFolder && mDragMode == DRAG_MODE_NONE) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003745 mDragOverFolderIcon = ((FolderIcon) dragOverView);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003746 mDragOverFolderIcon.onDragEnter(info);
Adam Cohen482ed822012-03-02 14:15:13 -08003747 if (targetLayout != null) {
3748 targetLayout.clearDragOutlines();
Winson Chungc07918d2011-07-01 15:35:26 -07003749 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003750 setDragMode(DRAG_MODE_ADD_TO_FOLDER);
3751 return;
Patrick Dubroy976ebec2010-08-04 20:03:37 -07003752 }
Adam Cohen482ed822012-03-02 14:15:13 -08003753
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003754 if (mDragMode == DRAG_MODE_ADD_TO_FOLDER && !willAddToFolder) {
3755 setDragMode(DRAG_MODE_NONE);
3756 }
3757 if (mDragMode == DRAG_MODE_CREATE_FOLDER && !userFolderPending) {
3758 setDragMode(DRAG_MODE_NONE);
Adam Cohen482ed822012-03-02 14:15:13 -08003759 }
3760
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003761 return;
Adam Cohenc0dcf592011-06-01 15:30:43 -07003762 }
3763
Adam Cohen19072da2011-05-31 14:30:45 -07003764 class FolderCreationAlarmListener implements OnAlarmListener {
Adam Cohen69ce2e52011-07-03 19:25:21 -07003765 CellLayout layout;
3766 int cellX;
3767 int cellY;
Adam Cohen19072da2011-05-31 14:30:45 -07003768
Adam Cohen69ce2e52011-07-03 19:25:21 -07003769 public FolderCreationAlarmListener(CellLayout layout, int cellX, int cellY) {
3770 this.layout = layout;
3771 this.cellX = cellX;
3772 this.cellY = cellY;
Adam Cohen19072da2011-05-31 14:30:45 -07003773 }
3774
3775 public void onAlarm(Alarm alarm) {
Adam Cohen8ec05f92013-10-13 12:29:03 -07003776 if (mDragFolderRingAnimator != null) {
3777 // This shouldn't happen ever, but just in case, make sure we clean up the mess.
3778 mDragFolderRingAnimator.animateToNaturalState();
Adam Cohen19072da2011-05-31 14:30:45 -07003779 }
Adam Cohen8ec05f92013-10-13 12:29:03 -07003780 mDragFolderRingAnimator = new FolderRingAnimator(mLauncher, null);
Adam Cohen69ce2e52011-07-03 19:25:21 -07003781 mDragFolderRingAnimator.setCell(cellX, cellY);
3782 mDragFolderRingAnimator.setCellLayout(layout);
Adam Cohen19072da2011-05-31 14:30:45 -07003783 mDragFolderRingAnimator.animateToAcceptState();
Adam Cohen69ce2e52011-07-03 19:25:21 -07003784 layout.showFolderAccept(mDragFolderRingAnimator);
3785 layout.clearDragOutlines();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003786 setDragMode(DRAG_MODE_CREATE_FOLDER);
Adam Cohen482ed822012-03-02 14:15:13 -08003787 }
3788 }
3789
3790 class ReorderAlarmListener implements OnAlarmListener {
3791 float[] dragViewCenter;
3792 int minSpanX, minSpanY, spanX, spanY;
3793 DragView dragView;
3794 View child;
3795
3796 public ReorderAlarmListener(float[] dragViewCenter, int minSpanX, int minSpanY, int spanX,
3797 int spanY, DragView dragView, View child) {
3798 this.dragViewCenter = dragViewCenter;
3799 this.minSpanX = minSpanX;
3800 this.minSpanY = minSpanY;
3801 this.spanX = spanX;
3802 this.spanY = spanY;
3803 this.child = child;
3804 this.dragView = dragView;
3805 }
3806
3807 public void onAlarm(Alarm alarm) {
3808 int[] resultSpan = new int[2];
Adam Cohen19f37922012-03-21 11:59:11 -07003809 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
Adam Cohen74c54912013-09-29 14:48:04 -07003810 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, mDragTargetLayout,
3811 mTargetCell);
Adam Cohen19f37922012-03-21 11:59:11 -07003812 mLastReorderX = mTargetCell[0];
3813 mLastReorderY = mTargetCell[1];
3814
Adam Cohenfa3c58f2013-12-06 16:10:55 -08003815 mTargetCell = mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08003816 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
3817 child, mTargetCell, resultSpan, CellLayout.MODE_DRAG_OVER);
3818
Adam Cohen19f37922012-03-21 11:59:11 -07003819 if (mTargetCell[0] < 0 || mTargetCell[1] < 0) {
3820 mDragTargetLayout.revertTempState();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003821 } else {
3822 setDragMode(DRAG_MODE_REORDER);
Adam Cohen19f37922012-03-21 11:59:11 -07003823 }
Adam Cohen482ed822012-03-02 14:15:13 -08003824
Adam Cohen482ed822012-03-02 14:15:13 -08003825 boolean resize = resultSpan[0] != spanX || resultSpan[1] != spanY;
3826 mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
3827 (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
3828 mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], resize,
3829 dragView.getDragVisualizeOffset(), dragView.getDragRegion());
Adam Cohen19072da2011-05-31 14:30:45 -07003830 }
3831 }
3832
Winson Chunga34abf82010-11-12 12:10:35 -08003833 @Override
Adam Cohen7d30a372013-07-01 17:03:59 -07003834 public void getHitRectRelativeToDragLayer(Rect outRect) {
Winson Chunga34abf82010-11-12 12:10:35 -08003835 // We want the workspace to have the whole area of the display (it will find the correct
3836 // cell layout to drop to in the existing drag/drop logic.
Adam Cohen7d30a372013-07-01 17:03:59 -07003837 mLauncher.getDragLayer().getDescendantRectRelativeToSelf(this, outRect);
Winson Chunga34abf82010-11-12 12:10:35 -08003838 }
3839
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07003840 /**
3841 * Add the item specified by dragInfo to the given layout.
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07003842 * @return true if successful
3843 */
Adam Cohen120980b2010-12-08 11:05:37 -08003844 public boolean addExternalItemToScreen(ItemInfo dragInfo, CellLayout layout) {
3845 if (layout.findCellForSpan(mTempEstimate, dragInfo.spanX, dragInfo.spanY)) {
Patrick Dubroybbaa75c2011-03-08 18:47:40 -08003846 onDropExternal(dragInfo.dropPos, (ItemInfo) dragInfo, (CellLayout) layout, false);
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07003847 return true;
3848 }
Winson Chung93eef082012-03-23 15:59:27 -07003849 mLauncher.showOutOfSpaceMessage(mLauncher.isHotseatLayout(layout));
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07003850 return false;
3851 }
3852
Adam Cohend5e42732011-03-28 17:33:39 -07003853 private void onDropExternal(int[] touchXY, Object dragInfo,
3854 CellLayout cellLayout, boolean insertAtFirst) {
Adam Cohene3e27a82011-04-15 12:07:39 -07003855 onDropExternal(touchXY, dragInfo, cellLayout, insertAtFirst, null);
Adam Cohend5e42732011-03-28 17:33:39 -07003856 }
3857
Adam Cohen120980b2010-12-08 11:05:37 -08003858 /**
3859 * Drop an item that didn't originate on one of the workspace screens.
3860 * It may have come from Launcher (e.g. from all apps or customize), or it may have
3861 * come from another app altogether.
3862 *
3863 * NOTE: This can also be called when we are outside of a drag event, when we want
3864 * to add an item to one of the workspace screens.
3865 */
Winson Chung557d6ed2011-07-08 15:34:52 -07003866 private void onDropExternal(final int[] touchXY, final Object dragInfo,
3867 final CellLayout cellLayout, boolean insertAtFirst, DragObject d) {
3868 final Runnable exitSpringLoadedRunnable = new Runnable() {
3869 @Override
3870 public void run() {
Adam Cohen689ff162014-05-08 17:27:56 -07003871 mLauncher.exitSpringLoadedDragModeDelayed(true,
3872 Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
Winson Chung557d6ed2011-07-08 15:34:52 -07003873 }
3874 };
Adam Cohenb7e16182011-07-15 17:55:02 -07003875
3876 ItemInfo info = (ItemInfo) dragInfo;
3877 int spanX = info.spanX;
3878 int spanY = info.spanY;
3879 if (mDragInfo != null) {
3880 spanX = mDragInfo.spanX;
3881 spanY = mDragInfo.spanY;
3882 }
3883
Winson Chung3d503fb2011-07-13 17:25:49 -07003884 final long container = mLauncher.isHotseatLayout(cellLayout) ?
3885 LauncherSettings.Favorites.CONTAINER_HOTSEAT :
3886 LauncherSettings.Favorites.CONTAINER_DESKTOP;
Adam Cohendcd297f2013-06-18 13:13:40 -07003887 final long screenId = getIdForScreen(cellLayout);
3888 if (!mLauncher.isHotseatLayout(cellLayout)
3889 && screenId != getScreenIdForPageIndex(mCurrentPage)
Winson Chung3d503fb2011-07-13 17:25:49 -07003890 && mState != State.SPRING_LOADED) {
Adam Cohendcd297f2013-06-18 13:13:40 -07003891 snapToScreenId(screenId, null);
Adam Cohen76078c42011-06-09 15:06:52 -07003892 }
Adam Cohenb7e16182011-07-15 17:55:02 -07003893
3894 if (info instanceof PendingAddItemInfo) {
3895 final PendingAddItemInfo pendingInfo = (PendingAddItemInfo) dragInfo;
3896
Adam Cohen558baaf2011-08-15 15:22:57 -07003897 boolean findNearestVacantCell = true;
3898 if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
3899 mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
3900 cellLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08003901 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
3902 mDragViewVisualCenter[1], mTargetCell);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003903 if (willCreateUserFolder((ItemInfo) d.dragInfo, cellLayout, mTargetCell,
Adam Cohen482ed822012-03-02 14:15:13 -08003904 distance, true) || willAddToExistingUserFolder((ItemInfo) d.dragInfo,
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003905 cellLayout, mTargetCell, distance)) {
Adam Cohen558baaf2011-08-15 15:22:57 -07003906 findNearestVacantCell = false;
3907 }
3908 }
Adam Cohen482ed822012-03-02 14:15:13 -08003909
Adam Cohend41fbf52012-02-16 23:53:59 -08003910 final ItemInfo item = (ItemInfo) d.dragInfo;
Adam Cohenaaa5c212012-10-05 18:14:31 -07003911 boolean updateWidgetSize = false;
Adam Cohen558baaf2011-08-15 15:22:57 -07003912 if (findNearestVacantCell) {
Adam Cohen482ed822012-03-02 14:15:13 -08003913 int minSpanX = item.spanX;
3914 int minSpanY = item.spanY;
3915 if (item.minSpanX > 0 && item.minSpanY > 0) {
3916 minSpanX = item.minSpanX;
3917 minSpanY = item.minSpanY;
3918 }
Adam Cohend41fbf52012-02-16 23:53:59 -08003919 int[] resultSpan = new int[2];
Adam Cohenfa3c58f2013-12-06 16:10:55 -08003920 mTargetCell = cellLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08003921 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, info.spanX, info.spanY,
3922 null, mTargetCell, resultSpan, CellLayout.MODE_ON_DROP_EXTERNAL);
Adam Cohen9e05a5e2012-09-10 15:53:09 -07003923
3924 if (resultSpan[0] != item.spanX || resultSpan[1] != item.spanY) {
3925 updateWidgetSize = true;
3926 }
Adam Cohend41fbf52012-02-16 23:53:59 -08003927 item.spanX = resultSpan[0];
3928 item.spanY = resultSpan[1];
Adam Cohen558baaf2011-08-15 15:22:57 -07003929 }
3930
Adam Cohenb7e16182011-07-15 17:55:02 -07003931 Runnable onAnimationCompleteRunnable = new Runnable() {
Winson Chung557d6ed2011-07-08 15:34:52 -07003932 @Override
3933 public void run() {
Adam Cohen689ff162014-05-08 17:27:56 -07003934 // Normally removeExtraEmptyScreen is called in Workspace#onDragEnd, but when
3935 // adding an item that may not be dropped right away (due to a config activity)
3936 // we defer the removal until the activity returns.
3937 deferRemoveExtraEmptyScreen();
3938
Winson Chung557d6ed2011-07-08 15:34:52 -07003939 // When dragging and dropping from customization tray, we deal with creating
3940 // widgets/shortcuts/folders in a slightly different way
Sunny Goyal71b5c0b2015-01-08 16:59:04 -08003941 mLauncher.addPendingItem(pendingInfo, container, screenId, mTargetCell,
3942 item.spanX, item.spanY);
Winson Chung557d6ed2011-07-08 15:34:52 -07003943 }
Adam Cohenb7e16182011-07-15 17:55:02 -07003944 };
Adam Cohen59400422014-03-05 18:07:04 -08003945 boolean isWidget = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
3946 || pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
3947
3948 View finalView = isWidget ? ((PendingAddWidgetInfo) pendingInfo).boundWidget : null;
Adam Cohen9e05a5e2012-09-10 15:53:09 -07003949
3950 if (finalView instanceof AppWidgetHostView && updateWidgetSize) {
3951 AppWidgetHostView awhv = (AppWidgetHostView) finalView;
3952 AppWidgetResizeFrame.updateWidgetSizeRanges(awhv, mLauncher, item.spanX,
3953 item.spanY);
3954 }
3955
Adam Cohend41fbf52012-02-16 23:53:59 -08003956 int animationStyle = ANIMATE_INTO_POSITION_AND_DISAPPEAR;
Adam Cohen59400422014-03-05 18:07:04 -08003957 if (isWidget && ((PendingAddWidgetInfo) pendingInfo).info != null &&
Adam Cohend41fbf52012-02-16 23:53:59 -08003958 ((PendingAddWidgetInfo) pendingInfo).info.configure != null) {
3959 animationStyle = ANIMATE_INTO_POSITION_AND_REMAIN;
3960 }
3961 animateWidgetDrop(info, cellLayout, d.dragView, onAnimationCompleteRunnable,
3962 animationStyle, finalView, true);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07003963 } else {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003964 // This is for other drag/drop cases, like dragging from All Apps
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003965 View view = null;
3966
3967 switch (info.itemType) {
3968 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
3969 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
Michael Jurkaeadbfc52013-09-04 00:45:37 +02003970 if (info.container == NO_ID && info instanceof AppInfo) {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003971 // Came from all apps -- make a copy
Sunny Goyal71b5c0b2015-01-08 16:59:04 -08003972 info = ((AppInfo) info).makeShortcut();
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003973 }
3974 view = mLauncher.createShortcut(R.layout.application, cellLayout,
3975 (ShortcutInfo) info);
3976 break;
Adam Cohendf2cc412011-04-27 16:56:57 -07003977 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
Adam Cohenc0dcf592011-06-01 15:30:43 -07003978 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, cellLayout,
3979 (FolderInfo) info, mIconCache);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003980 break;
3981 default:
3982 throw new IllegalStateException("Unknown item type: " + info.itemType);
3983 }
3984
Adam Cohenc0dcf592011-06-01 15:30:43 -07003985 // First we find the cell nearest to point at which the item is
3986 // dropped, without any consideration to whether there is an item there.
3987 if (touchXY != null) {
3988 mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
3989 cellLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08003990 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
3991 mDragViewVisualCenter[1], mTargetCell);
Winson Chung557d6ed2011-07-08 15:34:52 -07003992 d.postAnimationRunnable = exitSpringLoadedRunnable;
Adam Cohen482ed822012-03-02 14:15:13 -08003993 if (createUserFolderIfNecessary(view, container, cellLayout, mTargetCell, distance,
3994 true, d.dragView, d.postAnimationRunnable)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07003995 return;
3996 }
Adam Cohen482ed822012-03-02 14:15:13 -08003997 if (addToExistingFolderIfNecessary(view, cellLayout, mTargetCell, distance, d,
3998 true)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07003999 return;
4000 }
Adam Cohen10b17372011-04-15 14:21:25 -07004001 }
4002
Michael Jurkac4e772e2011-02-10 13:32:01 -08004003 if (touchXY != null) {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08004004 // when dragging and dropping, just find the closest free spot
Adam Cohenfa3c58f2013-12-06 16:10:55 -08004005 mTargetCell = cellLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08004006 (int) mDragViewVisualCenter[1], 1, 1, 1, 1,
Adam Cohenea889a22012-03-27 16:45:39 -07004007 null, mTargetCell, null, CellLayout.MODE_ON_DROP_EXTERNAL);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08004008 } else {
4009 cellLayout.findCellForSpan(mTargetCell, 1, 1);
4010 }
Sunny Goyal95abbb32014-08-04 10:53:22 -07004011 // Add the item to DB before adding to screen ensures that the container and other
4012 // values of the info is properly updated.
4013 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId,
4014 mTargetCell[0], mTargetCell[1]);
4015
Adam Cohendcd297f2013-06-18 13:13:40 -07004016 addInScreen(view, container, screenId, mTargetCell[0], mTargetCell[1], info.spanX,
Winson Chung3d503fb2011-07-13 17:25:49 -07004017 info.spanY, insertAtFirst);
Adam Cohen716b51e2011-06-30 12:09:54 -07004018 cellLayout.onDropChild(view);
Michael Jurkaa52570f2012-03-20 03:18:20 -07004019 cellLayout.getShortcutsAndWidgets().measureChild(view);
Adam Cohend5e42732011-03-28 17:33:39 -07004020
Adam Cohen3e8f8112011-07-02 18:03:00 -07004021 if (d.dragView != null) {
Adam Cohen4b285c52011-07-21 14:24:06 -07004022 // We wrap the animation call in the temporary set and reset of the current
4023 // cellLayout to its final transform -- this means we animate the drag view to
4024 // the correct final location.
4025 setFinalTransitionTransform(cellLayout);
Winson Chung557d6ed2011-07-08 15:34:52 -07004026 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view,
Adam Cohen28f852a2013-10-15 14:34:05 -07004027 exitSpringLoadedRunnable, this);
Adam Cohen4b285c52011-07-21 14:24:06 -07004028 resetTransitionTransform(cellLayout);
Adam Cohen3e8f8112011-07-02 18:03:00 -07004029 }
Joe Onorato00acb122009-08-04 16:04:30 -04004030 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004031 }
Winson Chungaafa03c2010-06-11 17:34:16 -07004032
Adam Cohend41fbf52012-02-16 23:53:59 -08004033 public Bitmap createWidgetBitmap(ItemInfo widgetInfo, View layout) {
Adam Cohened66b2b2012-01-23 17:28:51 -08004034 int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(widgetInfo.spanX,
4035 widgetInfo.spanY, widgetInfo, false);
Adam Cohend41fbf52012-02-16 23:53:59 -08004036 int visibility = layout.getVisibility();
Adam Cohened66b2b2012-01-23 17:28:51 -08004037 layout.setVisibility(VISIBLE);
4038
4039 int width = MeasureSpec.makeMeasureSpec(unScaledSize[0], MeasureSpec.EXACTLY);
4040 int height = MeasureSpec.makeMeasureSpec(unScaledSize[1], MeasureSpec.EXACTLY);
4041 Bitmap b = Bitmap.createBitmap(unScaledSize[0], unScaledSize[1],
4042 Bitmap.Config.ARGB_8888);
Sunny Goyal508da152014-08-14 10:53:27 -07004043 mCanvas.setBitmap(b);
Adam Cohened66b2b2012-01-23 17:28:51 -08004044
4045 layout.measure(width, height);
4046 layout.layout(0, 0, unScaledSize[0], unScaledSize[1]);
Sunny Goyal508da152014-08-14 10:53:27 -07004047 layout.draw(mCanvas);
4048 mCanvas.setBitmap(null);
Adam Cohend41fbf52012-02-16 23:53:59 -08004049 layout.setVisibility(visibility);
Adam Cohened66b2b2012-01-23 17:28:51 -08004050 return b;
4051 }
4052
Adam Cohend41fbf52012-02-16 23:53:59 -08004053 private void getFinalPositionForDropAnimation(int[] loc, float[] scaleXY,
Adam Cohen9d5b7d82012-05-09 18:00:44 -07004054 DragView dragView, CellLayout layout, ItemInfo info, int[] targetCell,
Michael Jurkaa2fe7862012-05-29 05:58:17 -07004055 boolean external, boolean scale) {
Adam Cohened66b2b2012-01-23 17:28:51 -08004056 // Now we animate the dragView, (ie. the widget or shortcut preview) into its final
4057 // location and size on the home screen.
Adam Cohend41fbf52012-02-16 23:53:59 -08004058 int spanX = info.spanX;
4059 int spanY = info.spanY;
Adam Cohened66b2b2012-01-23 17:28:51 -08004060
Adam Cohend41fbf52012-02-16 23:53:59 -08004061 Rect r = estimateItemPosition(layout, info, targetCell[0], targetCell[1], spanX, spanY);
4062 loc[0] = r.left;
4063 loc[1] = r.top;
4064
4065 setFinalTransitionTransform(layout);
4066 float cellLayoutScale =
Adam Cohen7d30a372013-07-01 17:03:59 -07004067 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(layout, loc, true);
Adam Cohend41fbf52012-02-16 23:53:59 -08004068 resetTransitionTransform(layout);
Michael Jurkaa2fe7862012-05-29 05:58:17 -07004069
4070 float dragViewScaleX;
4071 float dragViewScaleY;
4072 if (scale) {
4073 dragViewScaleX = (1.0f * r.width()) / dragView.getMeasuredWidth();
4074 dragViewScaleY = (1.0f * r.height()) / dragView.getMeasuredHeight();
4075 } else {
4076 dragViewScaleX = 1f;
4077 dragViewScaleY = 1f;
4078 }
Adam Cohend41fbf52012-02-16 23:53:59 -08004079
Adam Cohened66b2b2012-01-23 17:28:51 -08004080 // The animation will scale the dragView about its center, so we need to center about
4081 // the final location.
4082 loc[0] -= (dragView.getMeasuredWidth() - cellLayoutScale * r.width()) / 2;
4083 loc[1] -= (dragView.getMeasuredHeight() - cellLayoutScale * r.height()) / 2;
4084
Adam Cohend41fbf52012-02-16 23:53:59 -08004085 scaleXY[0] = dragViewScaleX * cellLayoutScale;
4086 scaleXY[1] = dragViewScaleY * cellLayoutScale;
4087 }
4088
4089 public void animateWidgetDrop(ItemInfo info, CellLayout cellLayout, DragView dragView,
4090 final Runnable onCompleteRunnable, int animationType, final View finalView,
4091 boolean external) {
4092 Rect from = new Rect();
4093 mLauncher.getDragLayer().getViewRectRelativeToSelf(dragView, from);
4094
4095 int[] finalPos = new int[2];
4096 float scaleXY[] = new float[2];
Michael Jurkaa2fe7862012-05-29 05:58:17 -07004097 boolean scalePreview = !(info instanceof PendingAddShortcutInfo);
Adam Cohend41fbf52012-02-16 23:53:59 -08004098 getFinalPositionForDropAnimation(finalPos, scaleXY, dragView, cellLayout, info, mTargetCell,
Michael Jurkaa2fe7862012-05-29 05:58:17 -07004099 external, scalePreview);
Adam Cohened66b2b2012-01-23 17:28:51 -08004100
4101 Resources res = mLauncher.getResources();
Adam Cohenad4e15c2013-10-17 16:21:35 -07004102 final int duration = res.getInteger(R.integer.config_dropAnimMaxDuration) - 200;
Adam Cohened66b2b2012-01-23 17:28:51 -08004103
Adam Cohend41fbf52012-02-16 23:53:59 -08004104 // In the case where we've prebound the widget, we remove it from the DragLayer
4105 if (finalView instanceof AppWidgetHostView && external) {
Adam Cohen68f681b2012-05-28 15:01:16 -07004106 Log.d(TAG, "6557954 Animate widget drop, final view is appWidgetHostView");
Adam Cohend41fbf52012-02-16 23:53:59 -08004107 mLauncher.getDragLayer().removeView(finalView);
4108 }
Adam Cohen59400422014-03-05 18:07:04 -08004109
4110 boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET ||
4111 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
Adam Cohend41fbf52012-02-16 23:53:59 -08004112 if ((animationType == ANIMATE_INTO_POSITION_AND_RESIZE || external) && finalView != null) {
4113 Bitmap crossFadeBitmap = createWidgetBitmap(info, finalView);
Adam Cohened66b2b2012-01-23 17:28:51 -08004114 dragView.setCrossFadeBitmap(crossFadeBitmap);
4115 dragView.crossFade((int) (duration * 0.8f));
Adam Cohen59400422014-03-05 18:07:04 -08004116 } else if (isWidget && external) {
Adam Cohend41fbf52012-02-16 23:53:59 -08004117 scaleXY[0] = scaleXY[1] = Math.min(scaleXY[0], scaleXY[1]);
Adam Cohened66b2b2012-01-23 17:28:51 -08004118 }
4119
Adam Cohend41fbf52012-02-16 23:53:59 -08004120 DragLayer dragLayer = mLauncher.getDragLayer();
Winson Chung7bd1bbb2012-02-13 18:29:29 -08004121 if (animationType == CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION) {
Adam Cohend41fbf52012-02-16 23:53:59 -08004122 mLauncher.getDragLayer().animateViewIntoPosition(dragView, finalPos, 0f, 0.1f, 0.1f,
Adam Cohened66b2b2012-01-23 17:28:51 -08004123 DragLayer.ANIMATION_END_DISAPPEAR, onCompleteRunnable, duration);
4124 } else {
Adam Cohend41fbf52012-02-16 23:53:59 -08004125 int endStyle;
4126 if (animationType == ANIMATE_INTO_POSITION_AND_REMAIN) {
4127 endStyle = DragLayer.ANIMATION_END_REMAIN_VISIBLE;
4128 } else {
4129 endStyle = DragLayer.ANIMATION_END_DISAPPEAR;;
4130 }
4131
4132 Runnable onComplete = new Runnable() {
4133 @Override
4134 public void run() {
4135 if (finalView != null) {
4136 finalView.setVisibility(VISIBLE);
4137 }
4138 if (onCompleteRunnable != null) {
4139 onCompleteRunnable.run();
4140 }
4141 }
4142 };
4143 dragLayer.animateViewIntoPosition(dragView, from.left, from.top, finalPos[0],
4144 finalPos[1], 1, 1, 1, scaleXY[0], scaleXY[1], onComplete, endStyle,
4145 duration, this);
Adam Cohened66b2b2012-01-23 17:28:51 -08004146 }
4147 }
4148
Adam Cohen4b285c52011-07-21 14:24:06 -07004149 public void setFinalTransitionTransform(CellLayout layout) {
4150 if (isSwitchingState()) {
Adam Cohen7d30a372013-07-01 17:03:59 -07004151 mCurrentScale = getScaleX();
4152 setScaleX(mNewScale);
4153 setScaleY(mNewScale);
Adam Cohen4b285c52011-07-21 14:24:06 -07004154 }
4155 }
4156 public void resetTransitionTransform(CellLayout layout) {
4157 if (isSwitchingState()) {
Adam Cohen7d30a372013-07-01 17:03:59 -07004158 setScaleX(mCurrentScale);
4159 setScaleY(mCurrentScale);
Adam Cohen4b285c52011-07-21 14:24:06 -07004160 }
4161 }
4162
Jeff Sharkey70864282009-04-07 21:08:40 -07004163 /**
4164 * Return the current {@link CellLayout}, correctly picking the destination
4165 * screen while a scroll is in progress.
4166 */
Patrick Dubroy5f445422011-02-18 14:35:21 -08004167 public CellLayout getCurrentDropLayout() {
Winson Chung360e63f2012-04-27 13:48:05 -07004168 return (CellLayout) getChildAt(getNextPage());
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004169 }
4170
Jeff Sharkey70864282009-04-07 21:08:40 -07004171 /**
Michael Jurka0280c3b2010-09-17 15:00:07 -07004172 * Return the current CellInfo describing our current drag; this method exists
4173 * so that Launcher can sync this object with the correct info when the activity is created/
4174 * destroyed
4175 *
4176 */
4177 public CellLayout.CellInfo getDragInfo() {
4178 return mDragInfo;
4179 }
4180
Winson Chung9b9fb962013-11-15 15:39:34 -08004181 public int getCurrentPageOffsetFromCustomContent() {
Adam Cohen21cd0022013-10-09 18:57:02 -07004182 return getNextPage() - numCustomPages();
4183 }
4184
Michael Jurka0280c3b2010-09-17 15:00:07 -07004185 /**
Jeff Sharkey70864282009-04-07 21:08:40 -07004186 * Calculate the nearest cell where the given object would be dropped.
Adam Cohene3e27a82011-04-15 12:07:39 -07004187 *
4188 * pixelX and pixelY should be in the coordinate system of layout
Jeff Sharkey70864282009-04-07 21:08:40 -07004189 */
Adam Cohendf035382011-04-11 17:22:04 -07004190 private int[] findNearestArea(int pixelX, int pixelY,
Adam Cohene3e27a82011-04-15 12:07:39 -07004191 int spanX, int spanY, CellLayout layout, int[] recycle) {
Adam Cohendf035382011-04-11 17:22:04 -07004192 return layout.findNearestArea(
Adam Cohene3e27a82011-04-15 12:07:39 -07004193 pixelX, pixelY, spanX, spanY, recycle);
Adam Cohendf035382011-04-11 17:22:04 -07004194 }
4195
Adam Cohencff6af82011-09-13 14:51:53 -07004196 void setup(DragController dragController) {
Michael Jurkac2f7f472010-12-14 15:34:42 -08004197 mSpringLoadedDragController = new SpringLoadedDragController(mLauncher);
Joe Onorato00acb122009-08-04 16:04:30 -04004198 mDragController = dragController;
Michael Jurkad74c9842011-07-10 12:44:21 -07004199
Michael Jurkad74c9842011-07-10 12:44:21 -07004200 // hardware layers on children are enabled on startup, but should be disabled until
4201 // needed
Michael Jurka3a0469d2012-06-21 09:38:41 -07004202 updateChildrenLayersEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004203 }
4204
Patrick Dubroye3887cc2011-01-20 10:43:40 -08004205 /**
4206 * Called at the end of a drag which originated on the workspace.
4207 */
Michael Jurka1e2f4652013-07-08 18:03:46 -07004208 public void onDropCompleted(final View target, final DragObject d,
4209 final boolean isFlingToDelete, final boolean success) {
4210 if (mDeferDropAfterUninstall) {
4211 mDeferredAction = new Runnable() {
Adam Cohenad4e15c2013-10-17 16:21:35 -07004212 public void run() {
4213 onDropCompleted(target, d, isFlingToDelete, success);
4214 mDeferredAction = null;
4215 }
4216 };
Michael Jurka1e2f4652013-07-08 18:03:46 -07004217 return;
4218 }
4219
4220 boolean beingCalledAfterUninstall = mDeferredAction != null;
4221
4222 if (success && !(beingCalledAfterUninstall && !mUninstallSuccessful)) {
4223 if (target != this && mDragInfo != null) {
Sunny Goyal71b5c0b2015-01-08 16:59:04 -08004224 removeWorkspaceItem(mDragInfo.cell);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004225 }
Patrick Dubroyce34a972010-10-19 10:34:32 -07004226 } else if (mDragInfo != null) {
Winson Chung3d503fb2011-07-13 17:25:49 -07004227 CellLayout cellLayout;
4228 if (mLauncher.isHotseatLayout(target)) {
4229 cellLayout = mLauncher.getHotseat().getLayout();
4230 } else {
Adam Cohendcd297f2013-06-18 13:13:40 -07004231 cellLayout = getScreenWithId(mDragInfo.screenId);
Winson Chung3d503fb2011-07-13 17:25:49 -07004232 }
Jorim Jaggieedb00a2014-01-13 13:45:07 -08004233 if (cellLayout == null && LauncherAppState.isDogfoodBuild()) {
4234 throw new RuntimeException("Invalid state: cellLayout == null in "
4235 + "Workspace#onDropCompleted. Please file a bug. ");
4236 }
4237 if (cellLayout != null) {
4238 cellLayout.onDropChild(mDragInfo.cell);
4239 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004240 }
Michael Jurka1e2f4652013-07-08 18:03:46 -07004241 if ((d.cancelled || (beingCalledAfterUninstall && !mUninstallSuccessful))
4242 && mDragInfo.cell != null) {
4243 mDragInfo.cell.setVisibility(VISIBLE);
Adam Cohen36cc09b2011-09-29 17:33:15 -07004244 }
Joe Onorato4be866d2010-10-10 11:26:02 -07004245 mDragOutline = null;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004246 mDragInfo = null;
4247 }
4248
Sunny Goyal71b5c0b2015-01-08 16:59:04 -08004249 public void removeWorkspaceItem(View v) {
4250 CellLayout parentCell = getParentCellLayoutForView(v);
4251 if (parentCell != null) {
4252 parentCell.removeView(v);
4253 } else if (LauncherAppState.isDogfoodBuild()) {
4254 throw new NullPointerException("mDragInfo.cell has null parent");
4255 }
4256 if (v instanceof DropTarget) {
4257 mDragController.removeDropTarget((DropTarget) v);
4258 }
4259 }
4260
Michael Jurka1e2f4652013-07-08 18:03:46 -07004261 public void deferCompleteDropAfterUninstallActivity() {
4262 mDeferDropAfterUninstall = true;
4263 }
4264
4265 /// maybe move this into a smaller part
4266 public void onUninstallActivityReturned(boolean success) {
4267 mDeferDropAfterUninstall = false;
4268 mUninstallSuccessful = success;
4269 if (mDeferredAction != null) {
4270 mDeferredAction.run();
4271 }
4272 }
4273
Adam Cohenea889a22012-03-27 16:45:39 -07004274 void updateItemLocationsInDatabase(CellLayout cl) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004275 int count = cl.getShortcutsAndWidgets().getChildCount();
Adam Cohend3461712012-03-29 17:25:17 -07004276
Adam Cohendcd297f2013-06-18 13:13:40 -07004277 long screenId = getIdForScreen(cl);
Adam Cohend3461712012-03-29 17:25:17 -07004278 int container = Favorites.CONTAINER_DESKTOP;
4279
4280 if (mLauncher.isHotseatLayout(cl)) {
Adam Cohendcd297f2013-06-18 13:13:40 -07004281 screenId = -1;
Adam Cohend3461712012-03-29 17:25:17 -07004282 container = Favorites.CONTAINER_HOTSEAT;
4283 }
4284
Adam Cohen482ed822012-03-02 14:15:13 -08004285 for (int i = 0; i < count; i++) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004286 View v = cl.getShortcutsAndWidgets().getChildAt(i);
Adam Cohen482ed822012-03-02 14:15:13 -08004287 ItemInfo info = (ItemInfo) v.getTag();
Adam Cohen2acce882012-03-28 19:03:19 -07004288 // Null check required as the AllApps button doesn't have an item info
Adam Cohen487f7dd2012-06-28 18:12:10 -07004289 if (info != null && info.requiresDbUpdate) {
4290 info.requiresDbUpdate = false;
Adam Cohendcd297f2013-06-18 13:13:40 -07004291 LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId, info.cellX,
Adam Cohenbebf0422012-04-11 18:06:28 -07004292 info.cellY, info.spanX, info.spanY);
Adam Cohen2acce882012-03-28 19:03:19 -07004293 }
Adam Cohen482ed822012-03-02 14:15:13 -08004294 }
4295 }
4296
Adam Cohene25af792013-06-06 23:08:25 -07004297 void saveWorkspaceToDb() {
4298 saveWorkspaceScreenToDb((CellLayout) mLauncher.getHotseat().getLayout());
4299 int count = getChildCount();
4300 for (int i = 0; i < count; i++) {
4301 CellLayout cl = (CellLayout) getChildAt(i);
4302 saveWorkspaceScreenToDb(cl);
4303 }
4304 }
4305
4306 void saveWorkspaceScreenToDb(CellLayout cl) {
4307 int count = cl.getShortcutsAndWidgets().getChildCount();
4308
Adam Cohendcd297f2013-06-18 13:13:40 -07004309 long screenId = getIdForScreen(cl);
Adam Cohene25af792013-06-06 23:08:25 -07004310 int container = Favorites.CONTAINER_DESKTOP;
4311
Winson Chungf70696d2013-06-25 16:19:53 -07004312 Hotseat hotseat = mLauncher.getHotseat();
Adam Cohene25af792013-06-06 23:08:25 -07004313 if (mLauncher.isHotseatLayout(cl)) {
Adam Cohendcd297f2013-06-18 13:13:40 -07004314 screenId = -1;
Adam Cohene25af792013-06-06 23:08:25 -07004315 container = Favorites.CONTAINER_HOTSEAT;
4316 }
4317
4318 for (int i = 0; i < count; i++) {
4319 View v = cl.getShortcutsAndWidgets().getChildAt(i);
4320 ItemInfo info = (ItemInfo) v.getTag();
4321 // Null check required as the AllApps button doesn't have an item info
4322 if (info != null) {
Winson Chungf70696d2013-06-25 16:19:53 -07004323 int cellX = info.cellX;
4324 int cellY = info.cellY;
4325 if (container == Favorites.CONTAINER_HOTSEAT) {
4326 cellX = hotseat.getCellXFromOrder((int) info.screenId);
4327 cellY = hotseat.getCellYFromOrder((int) info.screenId);
4328 }
4329 LauncherModel.addItemToDatabase(mLauncher, info, container, screenId, cellX,
4330 cellY, false);
Adam Cohene25af792013-06-06 23:08:25 -07004331 }
4332 if (v instanceof FolderIcon) {
4333 FolderIcon fi = (FolderIcon) v;
4334 fi.getFolder().addItemLocationsInDatabase();
4335 }
4336 }
4337 }
4338
Winson Chunga48487a2012-03-20 16:19:37 -07004339 @Override
Winson Chungeeb5bbc2013-11-13 15:47:05 -08004340 public float getIntrinsicIconScaleFactor() {
4341 return 1f;
4342 }
4343
4344 @Override
Winson Chung043f2af2012-03-01 16:09:54 -08004345 public boolean supportsFlingToDelete() {
4346 return true;
4347 }
4348
Winson Chunga48487a2012-03-20 16:19:37 -07004349 @Override
Mathew Inwood1eeb3fc2013-11-25 17:01:34 +00004350 public boolean supportsAppInfoDropTarget() {
4351 return false;
4352 }
4353
4354 @Override
4355 public boolean supportsDeleteDropTarget() {
4356 return true;
4357 }
4358
4359 @Override
Winson Chunga48487a2012-03-20 16:19:37 -07004360 public void onFlingToDelete(DragObject d, int x, int y, PointF vec) {
4361 // Do nothing
4362 }
4363
4364 @Override
4365 public void onFlingToDeleteCompleted() {
4366 // Do nothing
4367 }
4368
Michael Jurka0280c3b2010-09-17 15:00:07 -07004369 public boolean isDropEnabled() {
4370 return true;
4371 }
4372
Michael Jurka0142d492010-08-25 17:46:15 -07004373 @Override
Adam Cohen1462de32012-07-24 22:34:36 -07004374 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
4375 // We don't dispatch restoreInstanceState to our children using this code path.
Dave Hawkeya8881582013-09-17 15:55:33 -06004376 // Some pages will be restored immediately as their items are bound immediately, and
Adam Cohen1462de32012-07-24 22:34:36 -07004377 // others we will need to wait until after their items are bound.
4378 mSavedStates = container;
4379 }
4380
4381 public void restoreInstanceStateForChild(int child) {
4382 if (mSavedStates != null) {
4383 mRestoredPages.add(child);
4384 CellLayout cl = (CellLayout) getChildAt(child);
Adam Cohenb0ee0812013-12-03 10:51:45 -08004385 if (cl != null) {
4386 cl.restoreInstanceState(mSavedStates);
4387 }
Adam Cohen1462de32012-07-24 22:34:36 -07004388 }
4389 }
4390
4391 public void restoreInstanceStateForRemainingPages() {
4392 int count = getChildCount();
4393 for (int i = 0; i < count; i++) {
4394 if (!mRestoredPages.contains(i)) {
4395 restoreInstanceStateForChild(i);
4396 }
4397 }
4398 mRestoredPages.clear();
Winson Chungd8e596d2013-10-21 17:14:12 -07004399 mSavedStates = null;
Adam Cohen1462de32012-07-24 22:34:36 -07004400 }
4401
4402 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004403 public void scrollLeft() {
Adam Cohen6c5891a2014-07-09 23:53:15 -07004404 if (!workspaceInModalState() && !mIsSwitchingState) {
Michael Jurka0142d492010-08-25 17:46:15 -07004405 super.scrollLeft();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004406 }
Adam Cohen95bb8002011-07-03 23:40:28 -07004407 Folder openFolder = getOpenFolder();
4408 if (openFolder != null) {
4409 openFolder.completeDragExit();
4410 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004411 }
4412
Michael Jurka0142d492010-08-25 17:46:15 -07004413 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004414 public void scrollRight() {
Adam Cohen6c5891a2014-07-09 23:53:15 -07004415 if (!workspaceInModalState() && !mIsSwitchingState) {
Michael Jurka0142d492010-08-25 17:46:15 -07004416 super.scrollRight();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004417 }
Adam Cohen95bb8002011-07-03 23:40:28 -07004418 Folder openFolder = getOpenFolder();
4419 if (openFolder != null) {
4420 openFolder.completeDragExit();
4421 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004422 }
4423
Patrick Dubroy1262e362010-10-06 15:49:50 -07004424 @Override
Winson Chung3e0839e2011-10-03 15:15:18 -07004425 public boolean onEnterScrollArea(int x, int y, int direction) {
Winson Chung1c4cf4a2011-07-29 14:49:10 -07004426 // Ignore the scroll area if we are dragging over the hot seat
Daniel Sandlercc8befa2013-06-11 14:45:48 -04004427 boolean isPortrait = !LauncherAppState.isScreenLandscape(getContext());
Winson Chung10bfc6e2012-03-28 15:41:26 -07004428 if (mLauncher.getHotseat() != null && isPortrait) {
Winson Chung1c4cf4a2011-07-29 14:49:10 -07004429 Rect r = new Rect();
4430 mLauncher.getHotseat().getHitRect(r);
4431 if (r.contains(x, y)) {
Winson Chung3e0839e2011-10-03 15:15:18 -07004432 return false;
Winson Chung1c4cf4a2011-07-29 14:49:10 -07004433 }
4434 }
4435
Winson Chung3e0839e2011-10-03 15:15:18 -07004436 boolean result = false;
Adam Cohen6c5891a2014-07-09 23:53:15 -07004437 if (!workspaceInModalState() && !mIsSwitchingState && getOpenFolder() == null) {
Michael Jurkad718d6a2010-10-14 15:35:17 -07004438 mInScrollArea = true;
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08004439
Winson Chung360e63f2012-04-27 13:48:05 -07004440 final int page = getNextPage() +
Winson Chungaa15ffe2012-01-18 15:45:28 -08004441 (direction == DragController.SCROLL_LEFT ? -1 : 1);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07004442 // We always want to exit the current layout to ensure parity of enter / exit
4443 setCurrentDropLayout(null);
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08004444
Winson Chungaa15ffe2012-01-18 15:45:28 -08004445 if (0 <= page && page < getChildCount()) {
Winson Chung482a5b62013-07-31 17:19:51 -07004446 // Ensure that we are not dragging over to the custom content screen
4447 if (getScreenIdForPageIndex(page) == CUSTOM_CONTENT_SCREEN_ID) {
4448 return false;
4449 }
4450
Winson Chungaa15ffe2012-01-18 15:45:28 -08004451 CellLayout layout = (CellLayout) getChildAt(page);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07004452 setCurrentDragOverlappingLayout(layout);
Winson Chungc07918d2011-07-01 15:35:26 -07004453
4454 // Workspace is responsible for drawing the edge glow on adjacent pages,
4455 // so we need to redraw the workspace when this may have changed.
4456 invalidate();
Winson Chung3e0839e2011-10-03 15:15:18 -07004457 result = true;
Michael Jurkad718d6a2010-10-14 15:35:17 -07004458 }
Patrick Dubroy1262e362010-10-06 15:49:50 -07004459 }
Winson Chung3e0839e2011-10-03 15:15:18 -07004460 return result;
Patrick Dubroy1262e362010-10-06 15:49:50 -07004461 }
4462
4463 @Override
Winson Chung3e0839e2011-10-03 15:15:18 -07004464 public boolean onExitScrollArea() {
4465 boolean result = false;
Michael Jurkad718d6a2010-10-14 15:35:17 -07004466 if (mInScrollArea) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07004467 invalidate();
4468 CellLayout layout = getCurrentDropLayout();
4469 setCurrentDropLayout(layout);
4470 setCurrentDragOverlappingLayout(layout);
4471
Adam Cohen8a18afc2011-12-13 15:57:01 -08004472 result = true;
Winson Chungc07918d2011-07-01 15:35:26 -07004473 mInScrollArea = false;
Patrick Dubroy1262e362010-10-06 15:49:50 -07004474 }
Winson Chung3e0839e2011-10-03 15:15:18 -07004475 return result;
Patrick Dubroy1262e362010-10-06 15:49:50 -07004476 }
4477
Winson Chungc07918d2011-07-01 15:35:26 -07004478 private void onResetScrollArea() {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07004479 setCurrentDragOverlappingLayout(null);
Winson Chungc07918d2011-07-01 15:35:26 -07004480 mInScrollArea = false;
4481 }
4482
Winson Chung3d503fb2011-07-13 17:25:49 -07004483 /**
4484 * Returns a specific CellLayout
4485 */
4486 CellLayout getParentCellLayoutForView(View v) {
4487 ArrayList<CellLayout> layouts = getWorkspaceAndHotseatCellLayouts();
4488 for (CellLayout layout : layouts) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004489 if (layout.getShortcutsAndWidgets().indexOfChild(v) > -1) {
Winson Chung3d503fb2011-07-13 17:25:49 -07004490 return layout;
4491 }
4492 }
4493 return null;
4494 }
4495
4496 /**
4497 * Returns a list of all the CellLayouts in the workspace.
4498 */
4499 ArrayList<CellLayout> getWorkspaceAndHotseatCellLayouts() {
4500 ArrayList<CellLayout> layouts = new ArrayList<CellLayout>();
4501 int screenCount = getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004502 for (int screen = 0; screen < screenCount; screen++) {
Winson Chung3d503fb2011-07-13 17:25:49 -07004503 layouts.add(((CellLayout) getChildAt(screen)));
4504 }
4505 if (mLauncher.getHotseat() != null) {
4506 layouts.add(mLauncher.getHotseat().getLayout());
4507 }
4508 return layouts;
4509 }
4510
4511 /**
4512 * We should only use this to search for specific children. Do not use this method to modify
Michael Jurkaa52570f2012-03-20 03:18:20 -07004513 * ShortcutsAndWidgetsContainer directly. Includes ShortcutAndWidgetContainers from
4514 * the hotseat and workspace pages
Winson Chung3d503fb2011-07-13 17:25:49 -07004515 */
Michael Jurkaa52570f2012-03-20 03:18:20 -07004516 ArrayList<ShortcutAndWidgetContainer> getAllShortcutAndWidgetContainers() {
4517 ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
4518 new ArrayList<ShortcutAndWidgetContainer>();
Winson Chung3d503fb2011-07-13 17:25:49 -07004519 int screenCount = getChildCount();
4520 for (int screen = 0; screen < screenCount; screen++) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004521 childrenLayouts.add(((CellLayout) getChildAt(screen)).getShortcutsAndWidgets());
Winson Chung3d503fb2011-07-13 17:25:49 -07004522 }
4523 if (mLauncher.getHotseat() != null) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004524 childrenLayouts.add(mLauncher.getHotseat().getLayout().getShortcutsAndWidgets());
Winson Chung3d503fb2011-07-13 17:25:49 -07004525 }
4526 return childrenLayouts;
4527 }
4528
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004529 public Folder getFolderForTag(final Object tag) {
Sunny Goyalff572272014-07-23 13:58:07 -07004530 return (Folder) getFirstMatch(new ItemOperator() {
4531
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004532 @Override
4533 public boolean evaluate(ItemInfo info, View v, View parent) {
Sunny Goyalff572272014-07-23 13:58:07 -07004534 return (v instanceof Folder) && (((Folder) v).getInfo() == tag)
4535 && ((Folder) v).getInfo().opened;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004536 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004537 });
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004538 }
4539
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004540 public View getViewForTag(final Object tag) {
Sunny Goyalff572272014-07-23 13:58:07 -07004541 return getFirstMatch(new ItemOperator() {
4542
4543 @Override
4544 public boolean evaluate(ItemInfo info, View v, View parent) {
4545 return info == tag;
4546 }
4547 });
4548 }
4549
4550 public LauncherAppWidgetHostView getWidgetForAppWidgetId(final int appWidgetId) {
4551 return (LauncherAppWidgetHostView) getFirstMatch(new ItemOperator() {
4552
4553 @Override
4554 public boolean evaluate(ItemInfo info, View v, View parent) {
4555 return (info instanceof LauncherAppWidgetInfo) &&
4556 ((LauncherAppWidgetInfo) info).appWidgetId == appWidgetId;
4557 }
4558 });
4559 }
4560
4561 private View getFirstMatch(final ItemOperator operator) {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004562 final View[] value = new View[1];
Sunny Goyal651077b2014-06-30 14:15:31 -07004563 mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004564 @Override
4565 public boolean evaluate(ItemInfo info, View v, View parent) {
Sunny Goyalff572272014-07-23 13:58:07 -07004566 if (operator.evaluate(info, v, parent)) {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004567 value[0] = v;
4568 return true;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004569 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004570 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004571 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004572 });
4573 return value[0];
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004574 }
4575
Adam Cohendf035382011-04-11 17:22:04 -07004576 void clearDropTargets() {
Sunny Goyal651077b2014-06-30 14:15:31 -07004577 mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004578 @Override
4579 public boolean evaluate(ItemInfo info, View v, View parent) {
Adam Cohendf035382011-04-11 17:22:04 -07004580 if (v instanceof DropTarget) {
4581 mDragController.removeDropTarget((DropTarget) v);
4582 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004583 // not done, process all the shortcuts
4584 return false;
Adam Cohendf035382011-04-11 17:22:04 -07004585 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004586 });
Adam Cohendf035382011-04-11 17:22:04 -07004587 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004588
Sunny Goyal1a745e82014-10-02 15:58:31 -07004589 public void disableShortcutsByPackageName(final ArrayList<String> packages,
4590 final UserHandleCompat user, final int reason) {
4591 final HashSet<String> packageNames = new HashSet<String>();
4592 packageNames.addAll(packages);
4593
4594 mapOverItems(MAP_RECURSE, new ItemOperator() {
4595 @Override
4596 public boolean evaluate(ItemInfo info, View v, View parent) {
4597 if (info instanceof ShortcutInfo && v instanceof BubbleTextView) {
4598 ShortcutInfo shortcutInfo = (ShortcutInfo) info;
4599 ComponentName cn = shortcutInfo.getTargetComponent();
4600 if (user.equals(shortcutInfo.user) && cn != null
4601 && packageNames.contains(cn.getPackageName())) {
4602 shortcutInfo.isDisabled |= reason;
4603 BubbleTextView shortcut = (BubbleTextView) v;
4604 shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache, true, false);
4605
4606 if (parent != null) {
4607 parent.invalidate();
4608 }
4609 }
4610 }
4611 // process all the shortcuts
4612 return false;
4613 }
4614 });
4615 }
4616
Winson Chung83892cc2013-05-01 16:53:33 -07004617 // Removes ALL items that match a given package name, this is usually called when a package
4618 // has been removed and we want to remove all components (widgets, shortcuts, apps) that
4619 // belong to that package.
Kenny Guyed131872014-04-30 03:02:21 +01004620 void removeItemsByPackageName(final ArrayList<String> packages, final UserHandleCompat user) {
Winson Chung64359a52013-07-08 17:17:08 -07004621 final HashSet<String> packageNames = new HashSet<String>();
Winson Chungcd810732012-06-18 16:45:43 -07004622 packageNames.addAll(packages);
Joe Onorato64e6be72010-03-05 15:05:52 -05004623
Winson Chung64359a52013-07-08 17:17:08 -07004624 // Filter out all the ItemInfos that this is going to affect
4625 final HashSet<ItemInfo> infos = new HashSet<ItemInfo>();
4626 final HashSet<ComponentName> cns = new HashSet<ComponentName>();
Winson Chung83892cc2013-05-01 16:53:33 -07004627 ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
4628 for (CellLayout layoutParent : cellLayouts) {
4629 ViewGroup layout = layoutParent.getShortcutsAndWidgets();
4630 int childCount = layout.getChildCount();
4631 for (int i = 0; i < childCount; ++i) {
4632 View view = layout.getChildAt(i);
Winson Chung64359a52013-07-08 17:17:08 -07004633 infos.add((ItemInfo) view.getTag());
Winson Chung83892cc2013-05-01 16:53:33 -07004634 }
4635 }
Winson Chung64359a52013-07-08 17:17:08 -07004636 LauncherModel.ItemInfoFilter filter = new LauncherModel.ItemInfoFilter() {
4637 @Override
4638 public boolean filterItem(ItemInfo parent, ItemInfo info,
4639 ComponentName cn) {
Kenny Guyed131872014-04-30 03:02:21 +01004640 if (packageNames.contains(cn.getPackageName())
4641 && info.user.equals(user)) {
Winson Chung64359a52013-07-08 17:17:08 -07004642 cns.add(cn);
4643 return true;
4644 }
4645 return false;
4646 }
4647 };
4648 LauncherModel.filterItemInfos(infos, filter);
Winson Chung83892cc2013-05-01 16:53:33 -07004649
Winson Chung64359a52013-07-08 17:17:08 -07004650 // Remove the affected components
Kenny Guyed131872014-04-30 03:02:21 +01004651 removeItemsByComponentName(cns, user);
Winson Chung83892cc2013-05-01 16:53:33 -07004652 }
4653
Sunny Goyal4390ace2014-10-13 11:33:11 -07004654 /**
4655 * Removes items that match the item info specified. When applications are removed
4656 * as a part of an update, this is called to ensure that other widgets and application
4657 * shortcuts are not removed.
4658 */
Kenny Guyed131872014-04-30 03:02:21 +01004659 void removeItemsByComponentName(final HashSet<ComponentName> componentNames,
4660 final UserHandleCompat user) {
Winson Chung3d503fb2011-07-13 17:25:49 -07004661 ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
4662 for (final CellLayout layoutParent: cellLayouts) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004663 final ViewGroup layout = layoutParent.getShortcutsAndWidgets();
Romain Guy574d20e2009-06-01 15:34:04 -07004664
Winson Chung64359a52013-07-08 17:17:08 -07004665 final HashMap<ItemInfo, View> children = new HashMap<ItemInfo, View>();
4666 for (int j = 0; j < layout.getChildCount(); j++) {
4667 final View view = layout.getChildAt(j);
4668 children.put((ItemInfo) view.getTag(), view);
4669 }
Winson Chungaafa03c2010-06-11 17:34:16 -07004670
Winson Chung64359a52013-07-08 17:17:08 -07004671 final ArrayList<View> childrenToRemove = new ArrayList<View>();
4672 final HashMap<FolderInfo, ArrayList<ShortcutInfo>> folderAppsToRemove =
4673 new HashMap<FolderInfo, ArrayList<ShortcutInfo>>();
4674 LauncherModel.ItemInfoFilter filter = new LauncherModel.ItemInfoFilter() {
4675 @Override
4676 public boolean filterItem(ItemInfo parent, ItemInfo info,
4677 ComponentName cn) {
4678 if (parent instanceof FolderInfo) {
Kenny Guyed131872014-04-30 03:02:21 +01004679 if (componentNames.contains(cn) && info.user.equals(user)) {
Winson Chung64359a52013-07-08 17:17:08 -07004680 FolderInfo folder = (FolderInfo) parent;
4681 ArrayList<ShortcutInfo> appsToRemove;
4682 if (folderAppsToRemove.containsKey(folder)) {
4683 appsToRemove = folderAppsToRemove.get(folder);
4684 } else {
4685 appsToRemove = new ArrayList<ShortcutInfo>();
4686 folderAppsToRemove.put(folder, appsToRemove);
Romain Guy629de3e2010-01-13 12:20:59 -08004687 }
Winson Chung64359a52013-07-08 17:17:08 -07004688 appsToRemove.add((ShortcutInfo) info);
4689 return true;
4690 }
4691 } else {
Kenny Guyed131872014-04-30 03:02:21 +01004692 if (componentNames.contains(cn) && info.user.equals(user)) {
Winson Chung64359a52013-07-08 17:17:08 -07004693 childrenToRemove.add(children.get(info));
4694 return true;
Romain Guy574d20e2009-06-01 15:34:04 -07004695 }
4696 }
Winson Chung64359a52013-07-08 17:17:08 -07004697 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004698 }
Winson Chung64359a52013-07-08 17:17:08 -07004699 };
4700 LauncherModel.filterItemInfos(children.keySet(), filter);
4701
4702 // Remove all the apps from their folders
4703 for (FolderInfo folder : folderAppsToRemove.keySet()) {
4704 ArrayList<ShortcutInfo> appsToRemove = folderAppsToRemove.get(folder);
4705 for (ShortcutInfo info : appsToRemove) {
4706 folder.remove(info);
4707 }
4708 }
4709
4710 // Remove all the other children
4711 for (View child : childrenToRemove) {
4712 // Note: We can not remove the view directly from CellLayoutChildren as this
4713 // does not re-mark the spaces as unoccupied.
4714 layoutParent.removeViewInLayout(child);
4715 if (child instanceof DropTarget) {
4716 mDragController.removeDropTarget((DropTarget) child);
4717 }
4718 }
4719
4720 if (childrenToRemove.size() > 0) {
4721 layout.requestLayout();
4722 layout.invalidate();
4723 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004724 }
Winson Chung2efec4e2012-05-03 12:31:48 -07004725
Winson Chung64359a52013-07-08 17:17:08 -07004726 // Strip all the empty screens
4727 stripEmptyScreens();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004728 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07004729
Sunny Goyal651077b2014-06-30 14:15:31 -07004730 interface ItemOperator {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004731 /**
Sunny Goyal651077b2014-06-30 14:15:31 -07004732 * Process the next itemInfo, possibly with side-effect on {@link ItemOperator#value}.
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004733 *
4734 * @param info info for the shortcut
4735 * @param view view for the shortcut
4736 * @param parent containing folder, or null
4737 * @return true if done, false to continue the map
4738 */
4739 public boolean evaluate(ItemInfo info, View view, View parent);
4740 }
4741
4742 /**
Sunny Goyal651077b2014-06-30 14:15:31 -07004743 * Map the operator over the shortcuts and widgets, return the first-non-null value.
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004744 *
4745 * @param recurse true: iterate over folder children. false: op get the folders themselves.
4746 * @param op the operator to map over the shortcuts
4747 */
Sunny Goyal651077b2014-06-30 14:15:31 -07004748 void mapOverItems(boolean recurse, ItemOperator op) {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004749 ArrayList<ShortcutAndWidgetContainer> containers = getAllShortcutAndWidgetContainers();
4750 final int containerCount = containers.size();
4751 for (int containerIdx = 0; containerIdx < containerCount; containerIdx++) {
4752 ShortcutAndWidgetContainer container = containers.get(containerIdx);
4753 // map over all the shortcuts on the workspace
4754 final int itemCount = container.getChildCount();
4755 for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
4756 View item = container.getChildAt(itemIdx);
4757 ItemInfo info = (ItemInfo) item.getTag();
4758 if (recurse && info instanceof FolderInfo && item instanceof FolderIcon) {
4759 FolderIcon folder = (FolderIcon) item;
4760 ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
4761 // map over all the children in the folder
4762 final int childCount = folderChildren.size();
4763 for (int childIdx = 0; childIdx < childCount; childIdx++) {
4764 View child = folderChildren.get(childIdx);
4765 info = (ItemInfo) child.getTag();
4766 if (op.evaluate(info, child, folder)) {
4767 return;
4768 }
4769 }
4770 } else {
4771 if (op.evaluate(info, item, null)) {
4772 return;
4773 }
4774 }
4775 }
4776 }
4777 }
4778
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07004779 void updateShortcuts(ArrayList<ShortcutInfo> shortcuts) {
4780 final HashSet<ShortcutInfo> updates = new HashSet<ShortcutInfo>(shortcuts);
4781 mapOverItems(MAP_RECURSE, new ItemOperator() {
4782 @Override
4783 public boolean evaluate(ItemInfo info, View v, View parent) {
4784 if (info instanceof ShortcutInfo && v instanceof BubbleTextView &&
4785 updates.contains(info)) {
Sunny Goyal4390ace2014-10-13 11:33:11 -07004786 ShortcutInfo si = (ShortcutInfo) info;
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07004787 BubbleTextView shortcut = (BubbleTextView) v;
Sunny Goyal4390ace2014-10-13 11:33:11 -07004788 boolean oldPromiseState = shortcut.getCompoundDrawables()[1]
4789 instanceof PreloadIconDrawable;
4790 shortcut.applyFromShortcutInfo(si, mIconCache, true,
4791 si.isPromise() != oldPromiseState);
Sunny Goyalb50cc8c2014-10-06 16:23:56 -07004792
4793 if (parent != null) {
4794 parent.invalidate();
4795 }
4796 }
4797 // process all the shortcuts
4798 return false;
4799 }
4800 });
4801 }
4802
Sunny Goyale7b8cd92014-08-27 14:04:33 -07004803 public void removeAbandonedPromise(String packageName, UserHandleCompat user) {
4804 ArrayList<String> packages = new ArrayList<String>(1);
4805 packages.add(packageName);
4806 LauncherModel.deletePackageFromDatabase(mLauncher, packageName, user);
4807 removeItemsByPackageName(packages, user);
Chris Wren40c5ed32014-06-24 18:24:23 -04004808 }
4809
Sunny Goyala22666f2014-09-18 13:25:15 -07004810 public void updatePackageBadge(final String packageName, final UserHandleCompat user) {
4811 mapOverItems(MAP_RECURSE, new ItemOperator() {
4812 @Override
4813 public boolean evaluate(ItemInfo info, View v, View parent) {
4814 if (info instanceof ShortcutInfo && v instanceof BubbleTextView) {
4815 ShortcutInfo shortcutInfo = (ShortcutInfo) info;
4816 ComponentName cn = shortcutInfo.getTargetComponent();
4817 if (user.equals(shortcutInfo.user) && cn != null
4818 && shortcutInfo.isPromise()
4819 && packageName.equals(cn.getPackageName())) {
4820 if (shortcutInfo.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
4821 // For auto install apps update the icon as well as label.
4822 mIconCache.getTitleAndIcon(shortcutInfo,
4823 shortcutInfo.promisedIntent, user, true);
4824 } else {
4825 // Only update the icon for restored apps.
4826 shortcutInfo.updateIcon(mIconCache);
4827 }
4828 BubbleTextView shortcut = (BubbleTextView) v;
4829 shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache, true, false);
4830
4831 if (parent != null) {
4832 parent.invalidate();
4833 }
4834 }
4835 }
4836 // process all the shortcuts
4837 return false;
4838 }
4839 });
4840 }
4841
Sunny Goyale755d462014-07-22 13:48:29 -07004842 public void updatePackageState(ArrayList<PackageInstallInfo> installInfos) {
Sunny Goyale755d462014-07-22 13:48:29 -07004843 for (final PackageInstallInfo installInfo : installInfos) {
Sunny Goyal4390ace2014-10-13 11:33:11 -07004844 if (installInfo.state == PackageInstallerCompat.STATUS_INSTALLED) {
4845 continue;
4846 }
4847
Sunny Goyale755d462014-07-22 13:48:29 -07004848 mapOverItems(MAP_RECURSE, new ItemOperator() {
4849 @Override
4850 public boolean evaluate(ItemInfo info, View v, View parent) {
Sunny Goyal34942622014-08-29 17:20:55 -07004851 if (info instanceof ShortcutInfo && v instanceof BubbleTextView) {
4852 ShortcutInfo si = (ShortcutInfo) info;
4853 ComponentName cn = si.getTargetComponent();
4854 if (si.isPromise() && (cn != null)
4855 && installInfo.packageName.equals(cn.getPackageName())) {
4856 si.setInstallProgress(installInfo.progress);
4857 if (installInfo.state == PackageInstallerCompat.STATUS_FAILED) {
4858 // Mark this info as broken.
4859 si.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
4860 }
4861 ((BubbleTextView)v).applyState(false);
4862 }
Sunny Goyal0fc1be12014-08-11 17:05:23 -07004863 } else if (v instanceof PendingAppWidgetHostView
4864 && info instanceof LauncherAppWidgetInfo
4865 && ((LauncherAppWidgetInfo) info).providerName.getPackageName()
4866 .equals(installInfo.packageName)) {
4867 ((LauncherAppWidgetInfo) info).installProgress = installInfo.progress;
4868 ((PendingAppWidgetHostView) v).applyState();
Sunny Goyale755d462014-07-22 13:48:29 -07004869 }
Sunny Goyal0fc1be12014-08-11 17:05:23 -07004870
Sunny Goyale755d462014-07-22 13:48:29 -07004871 // process all the shortcuts
4872 return false;
4873 }
4874 });
Sunny Goyal651077b2014-06-30 14:15:31 -07004875 }
4876 }
4877
Sunny Goyal4390ace2014-10-13 11:33:11 -07004878 void widgetsRestored(ArrayList<LauncherAppWidgetInfo> changedInfo) {
Sunny Goyal0fc1be12014-08-11 17:05:23 -07004879 if (!changedInfo.isEmpty()) {
4880 DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo,
4881 mLauncher.getAppWidgetHost());
Adam Cohen59400422014-03-05 18:07:04 -08004882 if (LauncherModel.getProviderInfo(getContext(),
Sunny Goyal0fc1be12014-08-11 17:05:23 -07004883 changedInfo.get(0).providerName) != null) {
4884 // Re-inflate the widgets which have changed status
4885 widgetRefresh.run();
4886 } else {
4887 // widgetRefresh will automatically run when the packages are updated.
Sunny Goyal4390ace2014-10-13 11:33:11 -07004888 // For now just update the progress bars
4889 for (LauncherAppWidgetInfo info : changedInfo) {
4890 if (info.hostView instanceof PendingAppWidgetHostView) {
4891 info.installProgress = 100;
4892 ((PendingAppWidgetHostView) info.hostView).applyState();
4893 }
4894 }
Sunny Goyal0fc1be12014-08-11 17:05:23 -07004895 }
Sunny Goyalff572272014-07-23 13:58:07 -07004896 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07004897 }
4898
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004899 private void moveToScreen(int page, boolean animate) {
Adam Cohen6c5891a2014-07-09 23:53:15 -07004900 if (!workspaceInModalState()) {
Winson Chungde1af762011-07-21 16:44:07 -07004901 if (animate) {
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004902 snapToPage(page);
Winson Chungde1af762011-07-21 16:44:07 -07004903 } else {
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004904 setCurrentPage(page);
Winson Chungde1af762011-07-21 16:44:07 -07004905 }
Joe Onoratoc45b1682010-01-11 18:48:40 -05004906 }
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004907 View child = getChildAt(page);
Winson Chung64359a52013-07-08 17:17:08 -07004908 if (child != null) {
4909 child.requestFocus();
4910 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004911 }
4912
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004913 void moveToDefaultScreen(boolean animate) {
4914 moveToScreen(mDefaultPage, animate);
4915 }
4916
4917 void moveToCustomContentScreen(boolean animate) {
4918 if (hasCustomContent()) {
4919 int ccIndex = getPageIndexForScreenId(CUSTOM_CONTENT_SCREEN_ID);
4920 if (animate) {
4921 snapToPage(ccIndex);
4922 } else {
4923 setCurrentPage(ccIndex);
4924 }
4925 View child = getChildAt(ccIndex);
4926 if (child != null) {
4927 child.requestFocus();
4928 }
4929 }
Adam Cohen76128b62013-11-14 16:43:59 +00004930 exitWidgetResizeMode();
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004931 }
4932
Michael Jurka0142d492010-08-25 17:46:15 -07004933 @Override
Winson Chung7819a562013-09-19 15:55:45 -07004934 protected PageIndicator.PageMarkerResources getPageIndicatorMarker(int pageIndex) {
4935 long screenId = getScreenIdForPageIndex(pageIndex);
4936 if (screenId == EXTRA_EMPTY_SCREEN_ID) {
Michael Jurkafe0ace32013-10-03 01:05:14 -07004937 int count = mScreenOrder.size() - numCustomPages();
Winson Chung7819a562013-09-19 15:55:45 -07004938 if (count > 1) {
Winson Chungba4e52f2013-10-01 17:22:13 -07004939 return new PageIndicator.PageMarkerResources(R.drawable.ic_pageindicator_current,
Winson Chung7819a562013-09-19 15:55:45 -07004940 R.drawable.ic_pageindicator_add);
4941 }
Winson Chung82dfe582013-07-26 15:07:49 -07004942 }
Winson Chung7819a562013-09-19 15:55:45 -07004943
Winson Chung82dfe582013-07-26 15:07:49 -07004944 return super.getPageIndicatorMarker(pageIndex);
4945 }
4946
4947 @Override
Michael Jurka0142d492010-08-25 17:46:15 -07004948 public void syncPages() {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004949 }
Michael Jurka0142d492010-08-25 17:46:15 -07004950
4951 @Override
Winson Chungf314b0e2011-08-16 11:54:27 -07004952 public void syncPageItems(int page, boolean immediate) {
Michael Jurka0142d492010-08-25 17:46:15 -07004953 }
Winson Chung6a0f57d2011-06-29 20:10:49 -07004954
Adam Cohen53805212013-10-01 10:39:23 -07004955 protected String getPageIndicatorDescription() {
4956 String settings = getResources().getString(R.string.settings_button_text);
4957 return getCurrentPageDescription() + ", " + settings;
4958 }
4959
Winson Chung6a0f57d2011-06-29 20:10:49 -07004960 protected String getCurrentPageDescription() {
4961 int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
Adam Cohen53805212013-10-01 10:39:23 -07004962 int delta = numCustomPages();
4963 if (hasCustomContent() && getNextPage() == 0) {
4964 return mCustomContentDescription;
4965 }
Michael Jurka8b805b12012-04-18 14:23:14 -07004966 return String.format(getContext().getString(R.string.workspace_scroll_format),
Adam Cohen53805212013-10-01 10:39:23 -07004967 page + 1 - delta, getChildCount() - delta);
Winson Chung6a0f57d2011-06-29 20:10:49 -07004968 }
Adam Cohen8dfcba42011-07-07 16:38:18 -07004969
4970 public void getLocationInDragLayer(int[] loc) {
4971 mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
4972 }
Sunny Goyal0fc1be12014-08-11 17:05:23 -07004973
4974 /**
4975 * Used as a workaround to ensure that the AppWidgetService receives the
4976 * PACKAGE_ADDED broadcast before updating widgets.
4977 */
4978 private class DeferredWidgetRefresh implements Runnable {
4979 private final ArrayList<LauncherAppWidgetInfo> mInfos;
4980 private final LauncherAppWidgetHost mHost;
4981 private final Handler mHandler;
4982
4983 private boolean mRefreshPending;
4984
4985 public DeferredWidgetRefresh(ArrayList<LauncherAppWidgetInfo> infos,
4986 LauncherAppWidgetHost host) {
4987 mInfos = infos;
4988 mHost = host;
4989 mHandler = new Handler();
4990 mRefreshPending = true;
4991
4992 mHost.addProviderChangeListener(this);
4993 // Force refresh after 10 seconds, if we don't get the provider changed event.
4994 // This could happen when the provider is no longer available in the app.
4995 mHandler.postDelayed(this, 10000);
4996 }
4997
4998 @Override
4999 public void run() {
5000 mHost.removeProviderChangeListener(this);
5001 mHandler.removeCallbacks(this);
5002
5003 if (!mRefreshPending) {
5004 return;
5005 }
5006
5007 mRefreshPending = false;
5008
5009 for (LauncherAppWidgetInfo info : mInfos) {
5010 if (info.hostView instanceof PendingAppWidgetHostView) {
5011 PendingAppWidgetHostView view = (PendingAppWidgetHostView) info.hostView;
5012 mLauncher.removeAppWidget(info);
5013
5014 CellLayout cl = (CellLayout) view.getParent().getParent();
5015 // Remove the current widget
5016 cl.removeView(view);
5017 mLauncher.bindAppWidget(info);
5018 }
5019 }
5020 }
5021 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08005022}