blob: b3712cde54c12bfc2fe48b77877985596501384a [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;
Adam Cohen4caf2982013-08-20 18:54:31 -070046import android.net.Uri;
Michael Jurkaa6a05472013-11-13 17:59:46 +010047import android.os.AsyncTask;
Joe Onorato956091b2010-02-19 12:47:40 -080048import android.os.IBinder;
Adam Powell495f2892010-04-16 16:40:55 -070049import android.os.Parcelable;
Adam Cohen53805212013-10-01 10:39:23 -070050import android.support.v4.view.ViewCompat;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080051import android.util.AttributeSet;
Daniel Sandler291ad122010-05-24 16:03:53 -040052import android.util.Log;
Adam Cohen1462de32012-07-24 22:34:36 -070053import android.util.SparseArray;
Michael Jurkacc07e7a2013-08-26 20:56:35 +020054import android.view.Choreographer;
Winson Chunga34abf82010-11-12 12:10:35 -080055import android.view.Display;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080056import android.view.MotionEvent;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080057import android.view.View;
Winson Chung6e314082011-01-27 16:46:51 -080058import android.view.ViewGroup;
Adam Cohen53805212013-10-01 10:39:23 -070059import android.view.accessibility.AccessibilityManager;
Patrick Dubroycd68ff52010-10-28 17:57:05 -070060import android.view.animation.DecelerateInterpolator;
Michael Jurkacc07e7a2013-08-26 20:56:35 +020061import android.view.animation.Interpolator;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070062import android.widget.TextView;
Kenny Guyed131872014-04-30 03:02:21 +010063
64import com.android.launcher3.compat.UserHandleCompat;
Daniel Sandler325dc232013-06-05 22:57:57 -040065import com.android.launcher3.FolderIcon.FolderRingAnimator;
Adam Cohenbffe7452013-07-22 18:21:45 -070066import com.android.launcher3.Launcher.CustomContentCallbacks;
Daniel Sandler325dc232013-06-05 22:57:57 -040067import com.android.launcher3.LauncherSettings.Favorites;
Romain Guyedcce092010-03-04 13:03:17 -080068
Adam Cohen716b51e2011-06-30 12:09:54 -070069import java.util.ArrayList;
Adam Cohendcd297f2013-06-18 13:13:40 -070070import java.util.HashMap;
Adam Cohen716b51e2011-06-30 12:09:54 -070071import java.util.HashSet;
Michael Jurka8bd65f52012-06-25 14:56:22 -070072import java.util.Iterator;
Adam Cohen716b51e2011-06-30 12:09:54 -070073
The Android Open Source Project31dd5032009-03-03 19:32:27 -080074/**
Michael Jurka0142d492010-08-25 17:46:15 -070075 * The workspace is a wide area with a wallpaper and a finite number of pages.
76 * Each page contains a number of icons, folders or widgets the user can
Winson Chungaafa03c2010-06-11 17:34:16 -070077 * interact with. A workspace is meant to be used with a fixed width only.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080078 */
Michael Jurka0142d492010-08-25 17:46:15 -070079public class Workspace extends SmoothPagedView
Michael Jurkad74c9842011-07-10 12:44:21 -070080 implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
John Spurlock77e1f472013-09-11 10:09:51 -040081 DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener,
82 Insettable {
Joe Onorato3a8820b2009-11-10 15:06:42 -080083 private static final String TAG = "Launcher.Workspace";
Michael Jurka0142d492010-08-25 17:46:15 -070084
Adam Cohenf34bab52010-09-30 14:11:56 -070085 // Y rotation to apply to the workspace screens
Adam Cohenb5ba0972011-09-07 18:02:31 -070086 private static final float WORKSPACE_OVERSCROLL_ROTATION = 24f;
Adam Cohena985e592010-09-09 11:23:48 -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 Wrenaeff7ea2014-02-14 16:59:24 -0500101 private static final boolean MAP_NO_RECURSE = false;
102 private static final boolean MAP_RECURSE = true;
103
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;
Winson Chung9171e6d2010-11-17 17:39:27 -0800112 private Drawable mBackground;
Michael Jurka25356e72011-03-03 14:53:11 -0800113 boolean mDrawBackground = true;
Adam Cohenf34bab52010-09-30 14:11:56 -0700114 private float mBackgroundAlpha = 0;
115
Adam Cohen3d1b2b42013-08-14 15:57:58 -0700116 private static final long CUSTOM_CONTENT_GESTURE_DELAY = 200;
117 private long mTouchDownTime = -1;
118 private long mCustomContentShowTime = -1;
119
Adam Cohen22cba7f2013-07-19 16:14:00 -0700120 private LayoutTransition mLayoutTransition;
Dianne Hackborn8f573952009-08-10 23:21:09 -0700121 private final WallpaperManager mWallpaperManager;
Michael Jurka9c6fbed2011-03-02 17:41:34 -0800122 private IBinder mWindowToken;
Winson Chungaafa03c2010-06-11 17:34:16 -0700123
Winson Chung9e6a0a22013-08-27 11:58:12 -0700124 private int mOriginalDefaultPage;
Michael Jurka0142d492010-08-25 17:46:15 -0700125 private int mDefaultPage;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800126
Adam Cohen5084cba2013-09-03 12:01:16 -0700127 private ShortcutAndWidgetContainer mDragSourceInternal;
Adam Cohen53805212013-10-01 10:39:23 -0700128 private static boolean sAccessibilityEnabled;
Adam Cohen5084cba2013-09-03 12:01:16 -0700129
Adam Cohendcd297f2013-06-18 13:13:40 -0700130 // The screen id used for the empty screen always present to the right.
Adrian Roos8f3f6832014-04-28 15:45:52 +0200131 final static long EXTRA_EMPTY_SCREEN_ID = -201;
Adam Cohendcd297f2013-06-18 13:13:40 -0700132 private final static long CUSTOM_CONTENT_SCREEN_ID = -301;
133
134 private HashMap<Long, CellLayout> mWorkspaceScreens = new HashMap<Long, CellLayout>();
135 private ArrayList<Long> mScreenOrder = new ArrayList<Long>();
136
Adam Cohenad4e15c2013-10-17 16:21:35 -0700137 private Runnable mRemoveEmptyScreenRunnable;
Adam Cohen689ff162014-05-08 17:27:56 -0700138 private boolean mDeferRemoveExtraEmptyScreen = false;
Adam Cohenad4e15c2013-10-17 16:21:35 -0700139
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800140 /**
141 * CellInfo for the cell that is currently being dragged
142 */
143 private CellLayout.CellInfo mDragInfo;
Winson Chungaafa03c2010-06-11 17:34:16 -0700144
Jeff Sharkey70864282009-04-07 21:08:40 -0700145 /**
146 * Target drop area calculated during last acceptDrop call.
147 */
Adam Cohenc0dcf592011-06-01 15:30:43 -0700148 private int[] mTargetCell = new int[2];
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700149 private int mDragOverX = -1;
150 private int mDragOverY = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800151
Adam Cohena897f392012-04-27 18:12:05 -0700152 static Rect mLandscapeCellLayoutMetrics = null;
153 static Rect mPortraitCellLayoutMetrics = null;
154
Adam Cohenbffe7452013-07-22 18:21:45 -0700155 CustomContentCallbacks mCustomContentCallbacks;
156 boolean mCustomContentShowing;
Adam Cohenc36fa5c2013-08-29 11:54:42 -0700157 private float mLastCustomContentScrollProgress = -1f;
Adam Cohen53805212013-10-01 10:39:23 -0700158 private String mCustomContentDescription = "";
Adam Cohenbffe7452013-07-22 18:21:45 -0700159
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700160 /**
161 * The CellLayout that is currently being dragged over
162 */
163 private CellLayout mDragTargetLayout = null;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700164 /**
165 * The CellLayout that we will show as glowing
166 */
167 private CellLayout mDragOverlappingLayout = null;
168
169 /**
170 * The CellLayout which will be dropped to
171 */
172 private CellLayout mDropToLayout = null;
173
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800174 private Launcher mLauncher;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800175 private IconCache mIconCache;
Joe Onorato00acb122009-08-04 16:04:30 -0400176 private DragController mDragController;
Winson Chungaafa03c2010-06-11 17:34:16 -0700177
Michael Jurka4516c112010-10-07 15:13:47 -0700178 // These are temporary variables to prevent having to allocate a new object just to
179 // 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 -0800180 private int[] mTempCell = new int[2];
Adam Cohen7d30a372013-07-01 17:03:59 -0700181 private int[] mTempPt = new int[2];
Jeff Sharkey70864282009-04-07 21:08:40 -0700182 private int[] mTempEstimate = new int[2];
Adam Cohene3e27a82011-04-15 12:07:39 -0700183 private float[] mDragViewVisualCenter = new float[2];
Michael Jurka4516c112010-10-07 15:13:47 -0700184 private float[] mTempCellLayoutCenterCoordinates = new float[2];
Michael Jurka0280c3b2010-09-17 15:00:07 -0700185 private Matrix mTempInverseMatrix = new Matrix();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800186
Michael Jurkac2f7f472010-12-14 15:34:42 -0800187 private SpringLoadedDragController mSpringLoadedDragController;
Winson Chungb26f3d62011-06-02 10:49:29 -0700188 private float mSpringLoadedShrinkFactor;
Adam Cohenf358a4b2013-07-23 16:47:31 -0700189 private float mOverviewModeShrinkFactor;
Michael Jurkad3ef3062010-11-23 16:23:58 -0800190
Patrick Dubroy1262e362010-10-06 15:49:50 -0700191 // State variable that indicates whether the pages are small (ie when you're
Michael Jurkadee05892010-07-27 10:01:56 -0700192 // in all apps or customize mode)
Michael Jurkad74c9842011-07-10 12:44:21 -0700193
Adam Cohenf358a4b2013-07-23 16:47:31 -0700194 enum State { NORMAL, SPRING_LOADED, SMALL, OVERVIEW};
Adam Cohen7777d962011-08-18 18:58:38 -0700195 private State mState = State.NORMAL;
Michael Jurkad74c9842011-07-10 12:44:21 -0700196 private boolean mIsSwitchingState = false;
Michael Jurkad74c9842011-07-10 12:44:21 -0700197
Michael Jurkad74c9842011-07-10 12:44:21 -0700198 boolean mAnimatingViewIntoPlace = false;
199 boolean mIsDragOccuring = false;
200 boolean mChildrenLayersEnabled = true;
Michael Jurkadee05892010-07-27 10:01:56 -0700201
Adam Cohenaccfd562013-07-12 14:40:40 -0700202 private boolean mStripScreensOnPageStopMoving = false;
203
Patrick Dubroy54fa3b92010-11-17 12:18:45 -0800204 /** Is the user is dragging an item near the edge of a page? */
Patrick Dubroy1262e362010-10-06 15:49:50 -0700205 private boolean mInScrollArea = false;
206
Daniel Sandlere4f98912013-06-25 15:13:26 -0400207 private HolographicOutlineHelper mOutlineHelper;
Joe Onorato4be866d2010-10-10 11:26:02 -0700208 private Bitmap mDragOutline = null;
Patrick Dubroy8e58e912010-10-14 13:21:48 -0700209 private final Rect mTempRect = new Rect();
210 private final int[] mTempXY = new int[2];
Michael Jurkad51f33a2012-06-28 15:35:26 -0700211 private int[] mTempVisiblePagesRange = new int[2];
Michael Jurkab06d95f2012-04-02 06:26:53 -0700212 private boolean mOverscrollTransformsSet;
Adam Cohena29f5042013-09-26 14:29:35 -0700213 private float mLastOverscrollPivotX;
Michael Jurkaf8304f02012-04-26 13:33:26 -0700214 public static final int DRAG_BITMAP_PADDING = 2;
Michael Jurka869390b2012-05-06 15:55:19 -0700215 private boolean mWorkspaceFadeInAdjacentScreens;
Joe Onorato4be866d2010-10-10 11:26:02 -0700216
Michael Jurkaab1983f2011-01-18 15:50:17 -0800217 WallpaperOffsetInterpolator mWallpaperOffset;
Michael Jurka2a4f4922014-01-29 16:32:39 +0100218 private boolean mWallpaperIsLiveWallpaper;
219 private int mNumPagesForWallpaperParallax;
220 private float mLastSetWallpaperOffsetSteps = 0;
221
Adam Cohen26976d92011-03-22 15:33:33 -0700222 private Runnable mDelayedResizeRunnable;
Winson Chungf0c6ae02012-03-21 16:10:31 -0700223 private Runnable mDelayedSnapToPageRunnable;
Michael Jurka84f2ce72012-04-13 15:08:01 -0700224 private Point mDisplaySize = new Point();
Adam Cohen94309882012-06-08 16:24:56 -0700225 private int mCameraDistance;
Michael Jurkac5b262c2011-01-12 20:24:50 -0800226
Adam Cohen19072da2011-05-31 14:30:45 -0700227 // Variables relating to the creation of user folders by hovering shortcuts over shortcuts
Adam Cohen482ed822012-03-02 14:15:13 -0800228 private static final int FOLDER_CREATION_TIMEOUT = 0;
Adam Cohenfa3c58f2013-12-06 16:10:55 -0800229 public static final int REORDER_TIMEOUT = 350;
Adam Cohen19072da2011-05-31 14:30:45 -0700230 private final Alarm mFolderCreationAlarm = new Alarm();
Adam Cohen482ed822012-03-02 14:15:13 -0800231 private final Alarm mReorderAlarm = new Alarm();
Adam Cohen19072da2011-05-31 14:30:45 -0700232 private FolderRingAnimator mDragFolderRingAnimator = null;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700233 private FolderIcon mDragOverFolderIcon = null;
Adam Cohen19072da2011-05-31 14:30:45 -0700234 private boolean mCreateUserFolderOnDrop = false;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700235 private boolean mAddToExistingFolderOnDrop = false;
236 private DropTarget.DragEnforcer mDragEnforcer;
Adam Cohen3aff81c2012-05-16 21:01:01 -0700237 private float mMaxDistanceForFolderCreation;
Adam Cohen073a46f2011-05-17 16:28:09 -0700238
Adam Cohenf8d28232011-02-01 21:47:00 -0800239 // Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget)
240 private float mXDown;
241 private float mYDown;
242 final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
243 final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
244 final static float TOUCH_SLOP_DAMPING_FACTOR = 4;
245
Adam Cohened66b2b2012-01-23 17:28:51 -0800246 // Relating to the animation of items being dropped externally
Adam Cohend41fbf52012-02-16 23:53:59 -0800247 public static final int ANIMATE_INTO_POSITION_AND_DISAPPEAR = 0;
248 public static final int ANIMATE_INTO_POSITION_AND_REMAIN = 1;
249 public static final int ANIMATE_INTO_POSITION_AND_RESIZE = 2;
250 public static final int COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION = 3;
251 public static final int CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION = 4;
Adam Cohened66b2b2012-01-23 17:28:51 -0800252
Adam Cohen482ed822012-03-02 14:15:13 -0800253 // Related to dragging, folder creation and reordering
254 private static final int DRAG_MODE_NONE = 0;
255 private static final int DRAG_MODE_CREATE_FOLDER = 1;
256 private static final int DRAG_MODE_ADD_TO_FOLDER = 2;
257 private static final int DRAG_MODE_REORDER = 3;
258 private int mDragMode = DRAG_MODE_NONE;
259 private int mLastReorderX = -1;
260 private int mLastReorderY = -1;
261
Adam Cohen1462de32012-07-24 22:34:36 -0700262 private SparseArray<Parcelable> mSavedStates;
263 private final ArrayList<Integer> mRestoredPages = new ArrayList<Integer>();
264
Adam Cohen4b285c52011-07-21 14:24:06 -0700265 // These variables are used for storing the initial and final values during workspace animations
Adam Cohened51cc92011-08-01 20:28:08 -0700266 private int mSavedScrollX;
267 private float mSavedRotationY;
268 private float mSavedTranslationX;
Adam Cohen7d30a372013-07-01 17:03:59 -0700269
270 private float mCurrentScale;
271 private float mNewScale;
Adam Cohen4b285c52011-07-21 14:24:06 -0700272 private float[] mOldBackgroundAlphas;
Adam Cohen4b285c52011-07-21 14:24:06 -0700273 private float[] mOldAlphas;
Adam Cohen4b285c52011-07-21 14:24:06 -0700274 private float[] mNewBackgroundAlphas;
Adam Cohen4b285c52011-07-21 14:24:06 -0700275 private float[] mNewAlphas;
Adam Cohendcd297f2013-06-18 13:13:40 -0700276 private int mLastChildCount = -1;
Winson Chung70442722012-02-10 15:43:22 -0800277 private float mTransitionProgress;
Adam Cohen4b285c52011-07-21 14:24:06 -0700278
Michael Jurka1e2f4652013-07-08 18:03:46 -0700279 private Runnable mDeferredAction;
280 private boolean mDeferDropAfterUninstall;
281 private boolean mUninstallSuccessful;
282
Romain Guyeeacd562012-10-10 18:47:33 -0700283 private final Runnable mBindPages = new Runnable() {
284 @Override
285 public void run() {
286 mLauncher.getModel().bindRemainingSynchronousPages();
287 }
288 };
289
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800290 /**
291 * Used to inflate the Workspace from XML.
292 *
293 * @param context The application's context.
Michael Jurka0142d492010-08-25 17:46:15 -0700294 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800295 */
296 public Workspace(Context context, AttributeSet attrs) {
297 this(context, attrs, 0);
298 }
299
300 /**
301 * Used to inflate the Workspace from XML.
302 *
303 * @param context The application's context.
Michael Jurka0142d492010-08-25 17:46:15 -0700304 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800305 * @param defStyle Unused.
306 */
307 public Workspace(Context context, AttributeSet attrs, int defStyle) {
308 super(context, attrs, defStyle);
Michael Jurka0142d492010-08-25 17:46:15 -0700309 mContentIsRefreshable = false;
Michael Jurka5f1c5092010-09-03 14:15:02 -0700310
Daniel Sandlere4f98912013-06-25 15:13:26 -0400311 mOutlineHelper = HolographicOutlineHelper.obtain(context);
312
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700313 mDragEnforcer = new DropTarget.DragEnforcer(context);
Winson Chungf0ea4d32011-06-06 14:27:16 -0700314 // With workspace, data is available straight from the get-go
315 setDataIsReady();
316
Michael Jurka8bc66c72012-06-21 08:36:45 -0700317 mLauncher = (Launcher) context;
Winson Chung867ca622012-02-21 15:48:35 -0800318 final Resources res = getResources();
Adam Cohen3b185e22013-10-29 14:45:58 -0700319 mWorkspaceFadeInAdjacentScreens = LauncherAppState.getInstance().getDynamicGrid().
320 getDeviceProfile().shouldFadeAdjacentWorkspaceScreens();
Michael Jurka869390b2012-05-06 15:55:19 -0700321 mFadeInAdjacentScreens = false;
Dianne Hackborn8f573952009-08-10 23:21:09 -0700322 mWallpaperManager = WallpaperManager.getInstance(context);
Winson Chungaafa03c2010-06-11 17:34:16 -0700323
Winson Chungc82d2622013-11-06 13:23:29 -0800324 LauncherAppState app = LauncherAppState.getInstance();
325 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
Winson Chungaafa03c2010-06-11 17:34:16 -0700326 TypedArray a = context.obtainStyledAttributes(attrs,
327 R.styleable.Workspace, defStyle, 0);
Winson Chungb26f3d62011-06-02 10:49:29 -0700328 mSpringLoadedShrinkFactor =
329 res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
Winson Chungc82d2622013-11-06 13:23:29 -0800330 mOverviewModeShrinkFactor = grid.getOverviewModeScale();
Adam Cohen94309882012-06-08 16:24:56 -0700331 mCameraDistance = res.getInteger(R.integer.config_cameraDistance);
Winson Chung9e6a0a22013-08-27 11:58:12 -0700332 mOriginalDefaultPage = mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800333 a.recycle();
334
Michael Jurka8b805b12012-04-18 14:23:14 -0700335 setOnHierarchyChangeListener(this);
Joe Onorato0d44e942009-11-16 18:20:51 -0800336 setHapticFeedbackEnabled(false);
Michael Jurka0142d492010-08-25 17:46:15 -0700337
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800338 initWorkspace();
Winson Chungc35afb22011-02-23 13:01:49 -0800339
340 // Disable multitouch across the workspace/all apps/customize tray
341 setMotionEventSplittingEnabled(true);
Adam Cohen53805212013-10-01 10:39:23 -0700342 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800343 }
344
John Spurlock77e1f472013-09-11 10:09:51 -0400345 @Override
346 public void setInsets(Rect insets) {
347 mInsets.set(insets);
Adam Cohen6400b842013-11-26 15:45:38 -0800348
349 CellLayout customScreen = getScreenWithId(CUSTOM_CONTENT_SCREEN_ID);
350 if (customScreen != null) {
351 View customContent = customScreen.getShortcutsAndWidgets().getChildAt(0);
352 if (customContent instanceof Insettable) {
353 ((Insettable) customContent).setInsets(mInsets);
354 }
355 }
John Spurlock77e1f472013-09-11 10:09:51 -0400356 }
357
Michael Jurka038f9d82011-11-03 13:50:45 -0700358 // estimate the size of a widget with spans hSpan, vSpan. return MAX_VALUE for each
359 // dimension if unsuccessful
360 public int[] estimateItemSize(int hSpan, int vSpan,
Adam Cohend41fbf52012-02-16 23:53:59 -0800361 ItemInfo itemInfo, boolean springLoaded) {
Michael Jurka038f9d82011-11-03 13:50:45 -0700362 int[] size = new int[2];
363 if (getChildCount() > 0) {
Winson Chungad7db6e2013-10-08 14:01:06 -0700364 // Use the first non-custom page to estimate the child position
365 CellLayout cl = (CellLayout) getChildAt(numCustomPages());
Adam Cohend41fbf52012-02-16 23:53:59 -0800366 Rect r = estimateItemPosition(cl, itemInfo, 0, 0, hSpan, vSpan);
367 size[0] = r.width();
368 size[1] = r.height();
Michael Jurka038f9d82011-11-03 13:50:45 -0700369 if (springLoaded) {
370 size[0] *= mSpringLoadedShrinkFactor;
371 size[1] *= mSpringLoadedShrinkFactor;
372 }
373 return size;
374 } else {
375 size[0] = Integer.MAX_VALUE;
376 size[1] = Integer.MAX_VALUE;
377 return size;
378 }
379 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700380
Adam Cohend41fbf52012-02-16 23:53:59 -0800381 public Rect estimateItemPosition(CellLayout cl, ItemInfo pendingInfo,
Michael Jurka038f9d82011-11-03 13:50:45 -0700382 int hCell, int vCell, int hSpan, int vSpan) {
Adam Cohend41fbf52012-02-16 23:53:59 -0800383 Rect r = new Rect();
Michael Jurka038f9d82011-11-03 13:50:45 -0700384 cl.cellToRect(hCell, vCell, hSpan, vSpan, r);
Michael Jurka038f9d82011-11-03 13:50:45 -0700385 return r;
386 }
387
Adam Cohen5084cba2013-09-03 12:01:16 -0700388 public void onDragStart(final DragSource source, Object info, int dragAction) {
Michael Jurkad74c9842011-07-10 12:44:21 -0700389 mIsDragOccuring = true;
Michael Jurka3a0469d2012-06-21 09:38:41 -0700390 updateChildrenLayersEnabled(false);
Winson Chung641d71d2012-04-26 15:58:01 -0700391 mLauncher.lockScreenOrientation();
Sandeep Siddharthaab2d9d72013-09-17 13:18:24 -0700392 mLauncher.onInteractionBegin();
Michael Jurkaa3d30ad2012-05-08 13:43:43 -0700393 setChildrenBackgroundAlphaMultipliers(1f);
Winson Chungf561bdf2012-05-03 11:20:19 -0700394 // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging
395 InstallShortcutReceiver.enableInstallQueue();
396 UninstallShortcutReceiver.enableUninstallQueue();
Adam Cohen5084cba2013-09-03 12:01:16 -0700397 post(new Runnable() {
398 @Override
399 public void run() {
400 if (mIsDragOccuring) {
Adam Cohen689ff162014-05-08 17:27:56 -0700401 mDeferRemoveExtraEmptyScreen = false;
Adam Cohen5084cba2013-09-03 12:01:16 -0700402 addExtraEmptyScreenOnDrag();
403 }
404 }
405 });
Michael Jurkad74c9842011-07-10 12:44:21 -0700406 }
407
Adam Cohen689ff162014-05-08 17:27:56 -0700408
409 public void deferRemoveExtraEmptyScreen() {
410 mDeferRemoveExtraEmptyScreen = true;
411 }
412
Michael Jurkad74c9842011-07-10 12:44:21 -0700413 public void onDragEnd() {
Adam Cohen689ff162014-05-08 17:27:56 -0700414 System.out.println("onDrag end workspace");
415
416 if (!mDeferRemoveExtraEmptyScreen) {
417 removeExtraEmptyScreen(true, mDragSourceInternal != null);
418 }
419
Michael Jurkad74c9842011-07-10 12:44:21 -0700420 mIsDragOccuring = false;
Michael Jurka3a0469d2012-06-21 09:38:41 -0700421 updateChildrenLayersEnabled(false);
Winson Chung4b919f82012-05-01 10:44:08 -0700422 mLauncher.unlockScreenOrientation(false);
Winson Chungf561bdf2012-05-03 11:20:19 -0700423
424 // Re-enable any Un/InstallShortcutReceiver and now process any queued items
425 InstallShortcutReceiver.disableAndFlushInstallQueue(getContext());
426 UninstallShortcutReceiver.disableAndFlushUninstallQueue(getContext());
Adam Cohen5084cba2013-09-03 12:01:16 -0700427
Adam Cohen5084cba2013-09-03 12:01:16 -0700428 mDragSourceInternal = null;
Sandeep Siddharthaab2d9d72013-09-17 13:18:24 -0700429 mLauncher.onInteractionEnd();
Michael Jurkad74c9842011-07-10 12:44:21 -0700430 }
431
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800432 /**
433 * Initializes various states for this workspace.
434 */
Michael Jurka0142d492010-08-25 17:46:15 -0700435 protected void initWorkspace() {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800436 Context context = getContext();
Michael Jurka0142d492010-08-25 17:46:15 -0700437 mCurrentPage = mDefaultPage;
438 Launcher.setScreen(mCurrentPage);
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400439 LauncherAppState app = LauncherAppState.getInstance();
Winson Chung5f8afe62013-08-12 16:19:28 -0700440 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800441 mIconCache = app.getIconCache();
Patrick Dubroycd68ff52010-10-28 17:57:05 -0700442 setWillNotDraw(false);
Romain Guyce3cbd12013-02-25 15:00:36 -0800443 setClipChildren(false);
444 setClipToPadding(false);
Adam Cohen7777d962011-08-18 18:58:38 -0700445 setChildrenDrawnWithCacheEnabled(true);
Derek Prothrodadd9842014-01-17 13:43:50 -0500446
Winson Chungc82d2622013-11-06 13:23:29 -0800447 setMinScale(mOverviewModeShrinkFactor);
Adam Cohen22cba7f2013-07-19 16:14:00 -0700448 setupLayoutTransition();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800449
Adam Cohen482ed822012-03-02 14:15:13 -0800450 final Resources res = getResources();
Winson Chungb4b7fa72010-11-18 14:38:53 -0800451 try {
Winson Chungfd3385f2011-06-15 19:51:24 -0700452 mBackground = res.getDrawable(R.drawable.apps_customize_bg);
Winson Chungb4b7fa72010-11-18 14:38:53 -0800453 } catch (Resources.NotFoundException e) {
454 // In this case, we will skip drawing background protection
455 }
Winson Chung9171e6d2010-11-17 17:39:27 -0800456
Michael Jurkaab1983f2011-01-18 15:50:17 -0800457 mWallpaperOffset = new WallpaperOffsetInterpolator();
Adam Cohencff6af82011-09-13 14:51:53 -0700458 Display display = mLauncher.getWindowManager().getDefaultDisplay();
Michael Jurka84f2ce72012-04-13 15:08:01 -0700459 display.getSize(mDisplaySize);
Adam Cohen265b9a62011-12-07 14:37:18 -0800460
Winson Chung5f8afe62013-08-12 16:19:28 -0700461 mMaxDistanceForFolderCreation = (0.55f * grid.iconSizePx);
Adam Cohen265b9a62011-12-07 14:37:18 -0800462 mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity);
Michael Jurkaa6a05472013-11-13 17:59:46 +0100463
464 // Set the wallpaper dimensions when Launcher starts up
465 setWallpaperDimension();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800466 }
467
Adam Cohen22cba7f2013-07-19 16:14:00 -0700468 private void setupLayoutTransition() {
469 // We want to show layout transitions when pages are deleted, to close the gap.
470 mLayoutTransition = new LayoutTransition();
471 mLayoutTransition.enableTransitionType(LayoutTransition.DISAPPEARING);
472 mLayoutTransition.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
473 mLayoutTransition.disableTransitionType(LayoutTransition.APPEARING);
474 mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
475 setLayoutTransition(mLayoutTransition);
476 }
477
Winson Chung964df6b2013-10-11 15:55:37 -0700478 void enableLayoutTransitions() {
479 setLayoutTransition(mLayoutTransition);
480 }
481 void disableLayoutTransitions() {
482 setLayoutTransition(null);
483 }
484
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800485 @Override
Adam Cohenf34bab52010-09-30 14:11:56 -0700486 protected int getScrollMode() {
Winson Chungb26f3d62011-06-02 10:49:29 -0700487 return SmoothPagedView.X_LARGE_MODE;
Adam Cohenf34bab52010-09-30 14:11:56 -0700488 }
489
Michael Jurka08ee7702011-08-11 16:53:35 -0700490 @Override
Michael Jurka8b805b12012-04-18 14:23:14 -0700491 public void onChildViewAdded(View parent, View child) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800492 if (!(child instanceof CellLayout)) {
493 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
494 }
Adam Cohen2801caf2011-05-13 20:57:39 -0700495 CellLayout cl = ((CellLayout) child);
496 cl.setOnInterceptTouchListener(this);
Adam Cohen2801caf2011-05-13 20:57:39 -0700497 cl.setClickable(true);
Adam Cohen53805212013-10-01 10:39:23 -0700498 cl.setImportantForAccessibility(ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO);
Winson Chungf70696d2013-06-25 16:19:53 -0700499 super.onChildViewAdded(parent, child);
Michael Jurka8b805b12012-04-18 14:23:14 -0700500 }
501
Michael Jurka920d7f42012-05-14 16:29:55 -0700502 protected boolean shouldDrawChild(View child) {
503 final CellLayout cl = (CellLayout) child;
504 return super.shouldDrawChild(child) &&
Winson Chungf4bd2362013-10-07 17:11:27 -0700505 (mIsSwitchingState ||
506 cl.getShortcutsAndWidgets().getAlpha() > 0 ||
Michael Jurka920d7f42012-05-14 16:29:55 -0700507 cl.getBackgroundAlpha() > 0);
508 }
509
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800510 /**
511 * @return The open folder on the current screen, or null if there is none
512 */
513 Folder getOpenFolder() {
Adam Cohen716b51e2011-06-30 12:09:54 -0700514 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohen8e776a62011-06-28 18:10:06 -0700515 int count = dragLayer.getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800516 for (int i = 0; i < count; i++) {
Adam Cohen8e776a62011-06-28 18:10:06 -0700517 View child = dragLayer.getChildAt(i);
Winson Chungaafa03c2010-06-11 17:34:16 -0700518 if (child instanceof Folder) {
519 Folder folder = (Folder) child;
520 if (folder.getInfo().opened)
521 return folder;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800522 }
523 }
524 return null;
525 }
526
Patrick Dubroya0aa0122011-02-24 11:42:23 -0800527 boolean isTouchActive() {
528 return mTouchState != TOUCH_STATE_REST;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800529 }
530
Winson Chung9e6a0a22013-08-27 11:58:12 -0700531 public void removeAllWorkspaceScreens() {
Winson Chung964df6b2013-10-11 15:55:37 -0700532 // Disable all layout transitions before removing all pages to ensure that we don't get the
533 // transition animations competing with us changing the scroll when we add pages or the
534 // custom content screen
535 disableLayoutTransitions();
536
537 // Since we increment the current page when we call addCustomContentPage via bindScreens
538 // (and other places), we need to adjust the current page back when we clear the pages
539 if (hasCustomContent()) {
540 removeCustomContentPage();
541 }
542
Winson Chung9e6a0a22013-08-27 11:58:12 -0700543 // Remove the pages and clear the screen models
544 removeAllViews();
545 mScreenOrder.clear();
546 mWorkspaceScreens.clear();
Winson Chung964df6b2013-10-11 15:55:37 -0700547
548 // Re-enable the layout transitions
549 enableLayoutTransitions();
Winson Chung9e6a0a22013-08-27 11:58:12 -0700550 }
551
Adam Cohen89bddfa2013-08-20 11:57:13 -0700552 public long insertNewWorkspaceScreenBeforeEmptyScreen(long screenId) {
Winson Chung64359a52013-07-08 17:17:08 -0700553 // Find the index to insert this view into. If the empty screen exists, then
554 // insert it before that.
555 int insertIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
556 if (insertIndex < 0) {
557 insertIndex = mScreenOrder.size();
558 }
Adam Cohen89bddfa2013-08-20 11:57:13 -0700559 return insertNewWorkspaceScreen(screenId, insertIndex);
Adam Cohendcd297f2013-06-18 13:13:40 -0700560 }
561
Adam Cohen89bddfa2013-08-20 11:57:13 -0700562 public long insertNewWorkspaceScreen(long screenId) {
563 return insertNewWorkspaceScreen(screenId, getChildCount());
Winson Chung64359a52013-07-08 17:17:08 -0700564 }
565
Adam Cohen89bddfa2013-08-20 11:57:13 -0700566 public long insertNewWorkspaceScreen(long screenId, int insertIndex) {
Winson Chunga90303b2013-11-15 13:05:06 -0800567 // Log to disk
568 Launcher.addDumpLog(TAG, "11683562 - insertNewWorkspaceScreen(): " + screenId +
569 " at index: " + insertIndex, true);
570
Adam Cohen5084cba2013-09-03 12:01:16 -0700571 if (mWorkspaceScreens.containsKey(screenId)) {
572 throw new RuntimeException("Screen id " + screenId + " already exists!");
573 }
574
Adam Cohendcd297f2013-06-18 13:13:40 -0700575 CellLayout newScreen = (CellLayout)
576 mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null);
577
Adam Cohen7d30a372013-07-01 17:03:59 -0700578 newScreen.setOnLongClickListener(mLongClickListener);
Adam Cohenf358a4b2013-07-23 16:47:31 -0700579 newScreen.setOnClickListener(mLauncher);
Winson Chung837b2412013-09-09 14:43:51 -0700580 newScreen.setSoundEffectsEnabled(false);
Adam Cohendcd297f2013-06-18 13:13:40 -0700581 mWorkspaceScreens.put(screenId, newScreen);
Winson Chung64359a52013-07-08 17:17:08 -0700582 mScreenOrder.add(insertIndex, screenId);
583 addView(newScreen, insertIndex);
Adam Cohendcd297f2013-06-18 13:13:40 -0700584 return screenId;
585 }
586
Derek Prothrodadd9842014-01-17 13:43:50 -0500587 public void createCustomContentContainer() {
Adam Cohendcd297f2013-06-18 13:13:40 -0700588 CellLayout customScreen = (CellLayout)
Adam Cohen41eb4702013-06-27 15:08:59 -0700589 mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null);
Winson Chung59a488a2013-12-10 12:32:14 -0800590 customScreen.disableBackground();
Adam Cohendcd297f2013-06-18 13:13:40 -0700591
Adam Cohendcd297f2013-06-18 13:13:40 -0700592 mWorkspaceScreens.put(CUSTOM_CONTENT_SCREEN_ID, customScreen);
593 mScreenOrder.add(0, CUSTOM_CONTENT_SCREEN_ID);
Adam Cohenedb40762013-07-18 16:45:45 -0700594
Adam Cohen2702ea02013-08-15 16:17:42 -0700595 // We want no padding on the custom content
596 customScreen.setPadding(0, 0, 0, 0);
597
Adam Cohenedb40762013-07-18 16:45:45 -0700598 addFullScreenPage(customScreen);
Michael Jurkaee0ce2b2013-07-02 17:24:35 -0700599
Adam Cohendcd297f2013-06-18 13:13:40 -0700600 // Ensure that the current page and default page are maintained.
Winson Chung9e6a0a22013-08-27 11:58:12 -0700601 mDefaultPage = mOriginalDefaultPage + 1;
Winson Chung82e5c982013-10-09 12:04:50 -0700602
603 // Update the custom content hint
Winson Chunga6945242014-01-08 14:04:34 -0800604 mLauncher.getLauncherClings().updateCustomContentHintVisibility();
Adam Cohen21cd0022013-10-09 18:57:02 -0700605 if (mRestorePage != INVALID_RESTORE_PAGE) {
606 mRestorePage = mRestorePage + 1;
607 } else {
608 setCurrentPage(getCurrentPage() + 1);
609 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700610 }
611
Dave Hawkeya8881582013-09-17 15:55:33 -0600612 public void removeCustomContentPage() {
Dave Hawkeya8881582013-09-17 15:55:33 -0600613 CellLayout customScreen = getScreenWithId(CUSTOM_CONTENT_SCREEN_ID);
614 if (customScreen == null) {
615 throw new RuntimeException("Expected custom content screen to exist");
616 }
617
618 mWorkspaceScreens.remove(CUSTOM_CONTENT_SCREEN_ID);
619 mScreenOrder.remove(CUSTOM_CONTENT_SCREEN_ID);
620 removeView(customScreen);
Adam Cohenbb6fda62013-10-10 12:48:52 -0700621
622 if (mCustomContentCallbacks != null) {
623 mCustomContentCallbacks.onScrollProgressChanged(0);
624 mCustomContentCallbacks.onHide();
625 }
626
Dave Hawkeya8881582013-09-17 15:55:33 -0600627 mCustomContentCallbacks = null;
628
629 // Ensure that the current page and default page are maintained.
630 mDefaultPage = mOriginalDefaultPage - 1;
Winson Chung82e5c982013-10-09 12:04:50 -0700631
632 // Update the custom content hint
Winson Chunga6945242014-01-08 14:04:34 -0800633 mLauncher.getLauncherClings().updateCustomContentHintVisibility();
Adam Cohen21cd0022013-10-09 18:57:02 -0700634 if (mRestorePage != INVALID_RESTORE_PAGE) {
635 mRestorePage = mRestorePage - 1;
636 } else {
637 setCurrentPage(getCurrentPage() - 1);
638 }
Dave Hawkeya8881582013-09-17 15:55:33 -0600639 }
640
Adam Cohen53805212013-10-01 10:39:23 -0700641 public void addToCustomContentPage(View customContent, CustomContentCallbacks callbacks,
642 String description) {
Winson Chung98ca0f72013-07-29 12:58:51 -0700643 if (getPageIndexForScreenId(CUSTOM_CONTENT_SCREEN_ID) < 0) {
644 throw new RuntimeException("Expected custom content screen to exist");
645 }
646
647 // Add the custom content to the full screen custom page
648 CellLayout customScreen = getScreenWithId(CUSTOM_CONTENT_SCREEN_ID);
649 int spanX = customScreen.getCountX();
650 int spanY = customScreen.getCountY();
651 CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, spanX, spanY);
652 lp.canReorder = false;
653 lp.isFullscreen = true;
John Spurlock77e1f472013-09-11 10:09:51 -0400654 if (customContent instanceof Insettable) {
655 ((Insettable)customContent).setInsets(mInsets);
656 }
Adam Cohen166ebd42013-11-26 14:55:45 -0800657
658 // Verify that the child is removed from any existing parent.
659 if (customContent.getParent() instanceof ViewGroup) {
660 ViewGroup parent = (ViewGroup) customContent.getParent();
661 parent.removeView(customContent);
662 }
Adam Cohen225ad9b2013-10-07 13:03:00 -0700663 customScreen.removeAllViews();
Winson Chung98ca0f72013-07-29 12:58:51 -0700664 customScreen.addViewToCellLayout(customContent, 0, 0, lp, true);
Adam Cohen53805212013-10-01 10:39:23 -0700665 mCustomContentDescription = description;
Winson Chung98ca0f72013-07-29 12:58:51 -0700666
667 mCustomContentCallbacks = callbacks;
668 }
669
Adam Cohen5084cba2013-09-03 12:01:16 -0700670 public void addExtraEmptyScreenOnDrag() {
Winson Chunga90303b2013-11-15 13:05:06 -0800671 // Log to disk
672 Launcher.addDumpLog(TAG, "11683562 - addExtraEmptyScreenOnDrag()", true);
673
Adam Cohen5084cba2013-09-03 12:01:16 -0700674 boolean lastChildOnScreen = false;
675 boolean childOnFinalScreen = false;
676
Adam Cohenad4e15c2013-10-17 16:21:35 -0700677 // Cancel any pending removal of empty screen
678 mRemoveEmptyScreenRunnable = null;
679
Adam Cohen5084cba2013-09-03 12:01:16 -0700680 if (mDragSourceInternal != null) {
681 if (mDragSourceInternal.getChildCount() == 1) {
682 lastChildOnScreen = true;
683 }
684 CellLayout cl = (CellLayout) mDragSourceInternal.getParent();
685 if (indexOfChild(cl) == getChildCount() - 1) {
686 childOnFinalScreen = true;
687 }
688 }
689
690 // If this is the last item on the final screen
691 if (lastChildOnScreen && childOnFinalScreen) {
692 return;
693 }
694 if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) {
695 insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID);
696 }
697 }
698
699 public boolean addExtraEmptyScreen() {
Winson Chunga90303b2013-11-15 13:05:06 -0800700 // Log to disk
701 Launcher.addDumpLog(TAG, "11683562 - addExtraEmptyScreen()", true);
702
Adam Cohen5084cba2013-09-03 12:01:16 -0700703 if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) {
704 insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID);
705 return true;
706 }
707 return false;
708 }
709
Adam Cohenad4e15c2013-10-17 16:21:35 -0700710 private void convertFinalScreenToEmptyScreenIfNecessary() {
Winson Chunga90303b2013-11-15 13:05:06 -0800711 // Log to disk
712 Launcher.addDumpLog(TAG, "11683562 - convertFinalScreenToEmptyScreenIfNecessary()", true);
713
Adam Cohendcb173d2014-04-01 13:33:58 -0700714 if (mLauncher.isWorkspaceLoading()) {
715 // Invalid and dangerous operation if workspace is loading
716 Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
717 return;
718 }
719
Adam Cohenad4e15c2013-10-17 16:21:35 -0700720 if (hasExtraEmptyScreen() || mScreenOrder.size() == 0) return;
721 long finalScreenId = mScreenOrder.get(mScreenOrder.size() - 1);
722
723 if (finalScreenId == CUSTOM_CONTENT_SCREEN_ID) return;
724 CellLayout finalScreen = mWorkspaceScreens.get(finalScreenId);
725
726 // If the final screen is empty, convert it to the extra empty screen
Adam Cohen917e3882013-10-31 15:03:35 -0700727 if (finalScreen.getShortcutsAndWidgets().getChildCount() == 0 &&
728 !finalScreen.isDropPending()) {
Adam Cohenad4e15c2013-10-17 16:21:35 -0700729 mWorkspaceScreens.remove(finalScreenId);
730 mScreenOrder.remove(finalScreenId);
731
732 // if this is the last non-custom content screen, convert it to the empty screen
733 mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, finalScreen);
734 mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
Winson Chunga90303b2013-11-15 13:05:06 -0800735
Winson Chungf0716b72013-11-18 16:09:54 -0800736 // Update the model if we have changed any screens
737 mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
Winson Chunga90303b2013-11-15 13:05:06 -0800738 Launcher.addDumpLog(TAG, "11683562 - extra empty screen: " + finalScreenId, true);
Adam Cohen5084cba2013-09-03 12:01:16 -0700739 }
740 }
741
Adam Cohen689ff162014-05-08 17:27:56 -0700742 public void removeExtraEmptyScreen(final boolean animate, boolean stripEmptyScreens) {
743 removeExtraEmptyScreenDelayed(animate, null, 0, stripEmptyScreens);
Adam Cohenad4e15c2013-10-17 16:21:35 -0700744 }
745
Adam Cohen689ff162014-05-08 17:27:56 -0700746 public void removeExtraEmptyScreenDelayed(final boolean animate, final Runnable onComplete,
Adam Cohenad4e15c2013-10-17 16:21:35 -0700747 final int delay, final boolean stripEmptyScreens) {
Winson Chunga90303b2013-11-15 13:05:06 -0800748 // Log to disk
749 Launcher.addDumpLog(TAG, "11683562 - removeExtraEmptyScreen()", true);
Adam Cohendcb173d2014-04-01 13:33:58 -0700750 if (mLauncher.isWorkspaceLoading()) {
751 // Don't strip empty screens if the workspace is still loading
752 Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
753 return;
754 }
755
Adam Cohenad4e15c2013-10-17 16:21:35 -0700756 if (delay > 0) {
757 postDelayed(new Runnable() {
758 @Override
759 public void run() {
Adam Cohen689ff162014-05-08 17:27:56 -0700760 removeExtraEmptyScreenDelayed(animate, onComplete, 0, stripEmptyScreens);
Adam Cohenad4e15c2013-10-17 16:21:35 -0700761 }
Adam Cohenad4e15c2013-10-17 16:21:35 -0700762 }, delay);
763 return;
764 }
765
766 convertFinalScreenToEmptyScreenIfNecessary();
767 if (hasExtraEmptyScreen()) {
768 int emptyIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
769 if (getNextPage() == emptyIndex) {
770 snapToPage(getNextPage() - 1, SNAP_OFF_EMPTY_SCREEN_DURATION);
771 fadeAndRemoveEmptyScreen(SNAP_OFF_EMPTY_SCREEN_DURATION, FADE_EMPTY_SCREEN_DURATION,
772 onComplete, stripEmptyScreens);
773 } else {
774 fadeAndRemoveEmptyScreen(0, FADE_EMPTY_SCREEN_DURATION,
775 onComplete, stripEmptyScreens);
776 }
777 return;
Adam Cohen3a14eeb2013-11-15 16:51:22 +0000778 } else if (stripEmptyScreens) {
779 // If we're not going to strip the empty screens after removing
780 // the extra empty screen, do it right away.
781 stripEmptyScreens();
Adam Cohenad4e15c2013-10-17 16:21:35 -0700782 }
Adam Cohen3a14eeb2013-11-15 16:51:22 +0000783
Adam Cohenad4e15c2013-10-17 16:21:35 -0700784 if (onComplete != null) {
785 onComplete.run();
786 }
787 }
788
789 private void fadeAndRemoveEmptyScreen(int delay, int duration, final Runnable onComplete,
790 final boolean stripEmptyScreens) {
Winson Chunga90303b2013-11-15 13:05:06 -0800791 // Log to disk
792 // XXX: Do we need to update LM workspace screens below?
793 Launcher.addDumpLog(TAG, "11683562 - fadeAndRemoveEmptyScreen()", true);
Adam Cohenad4e15c2013-10-17 16:21:35 -0700794 PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0f);
795 PropertyValuesHolder bgAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", 0f);
796
797 final CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
798
799 mRemoveEmptyScreenRunnable = new Runnable() {
800 @Override
801 public void run() {
802 if (hasExtraEmptyScreen()) {
803 mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
804 mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
805 removeView(cl);
806 if (stripEmptyScreens) {
807 stripEmptyScreens();
808 }
809 }
810 }
811 };
812
813 ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(cl, alpha, bgAlpha);
814 oa.setDuration(duration);
815 oa.setStartDelay(delay);
816 oa.addListener(new AnimatorListenerAdapter() {
817 @Override
818 public void onAnimationEnd(Animator animation) {
819 if (mRemoveEmptyScreenRunnable != null) {
820 mRemoveEmptyScreenRunnable.run();
821 }
822 if (onComplete != null) {
823 onComplete.run();
824 }
825 }
826 });
827 oa.start();
828 }
829
Michael Jurka2f817312013-09-20 03:03:42 +0200830 public boolean hasExtraEmptyScreen() {
831 int nScreens = getChildCount();
Michael Jurkafe0ace32013-10-03 01:05:14 -0700832 nScreens = nScreens - numCustomPages();
Michael Jurka2f817312013-09-20 03:03:42 +0200833 return mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID) && nScreens > 1;
834 }
835
Adam Cohendcd297f2013-06-18 13:13:40 -0700836 public long commitExtraEmptyScreen() {
Winson Chunga90303b2013-11-15 13:05:06 -0800837 // Log to disk
838 Launcher.addDumpLog(TAG, "11683562 - commitExtraEmptyScreen()", true);
Adam Cohendcb173d2014-04-01 13:33:58 -0700839 if (mLauncher.isWorkspaceLoading()) {
840 // Invalid and dangerous operation if workspace is loading
841 Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
842 return -1;
843 }
Winson Chunga90303b2013-11-15 13:05:06 -0800844
Winson Chung89f97052013-09-20 11:32:26 -0700845 int index = getPageIndexForScreenId(EXTRA_EMPTY_SCREEN_ID);
Adam Cohendcd297f2013-06-18 13:13:40 -0700846 CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
Winson Chungc9168342013-06-26 14:54:55 -0700847 mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
Adam Cohendcd297f2013-06-18 13:13:40 -0700848 mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
849
Michael Jurka104c4562013-07-08 18:03:46 -0700850 long newId = LauncherAppState.getLauncherProvider().generateNewScreenId();
Adam Cohendcd297f2013-06-18 13:13:40 -0700851 mWorkspaceScreens.put(newId, cl);
852 mScreenOrder.add(newId);
853
Winson Chung89f97052013-09-20 11:32:26 -0700854 // Update the page indicator marker
855 if (getPageIndicator() != null) {
856 getPageIndicator().updateMarker(index, getPageIndicatorMarker(index));
857 }
858
Winson Chung64359a52013-07-08 17:17:08 -0700859 // Update the model for the new screen
860 mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
861
Adam Cohendcd297f2013-06-18 13:13:40 -0700862 return newId;
863 }
864
Adam Cohendcd297f2013-06-18 13:13:40 -0700865 public CellLayout getScreenWithId(long screenId) {
866 CellLayout layout = mWorkspaceScreens.get(screenId);
867 return layout;
868 }
869
870 public long getIdForScreen(CellLayout layout) {
871 Iterator<Long> iter = mWorkspaceScreens.keySet().iterator();
872 while (iter.hasNext()) {
873 long id = iter.next();
874 if (mWorkspaceScreens.get(id) == layout) {
875 return id;
876 }
877 }
878 return -1;
879 }
880
881 public int getPageIndexForScreenId(long screenId) {
882 return indexOfChild(mWorkspaceScreens.get(screenId));
883 }
884
885 public long getScreenIdForPageIndex(int index) {
Winson Chung5f8afe62013-08-12 16:19:28 -0700886 if (0 <= index && index < mScreenOrder.size()) {
887 return mScreenOrder.get(index);
888 }
889 return -1;
Adam Cohendcd297f2013-06-18 13:13:40 -0700890 }
891
Winson Chungc9168342013-06-26 14:54:55 -0700892 ArrayList<Long> getScreenOrder() {
893 return mScreenOrder;
894 }
895
Adam Cohendcd297f2013-06-18 13:13:40 -0700896 public void stripEmptyScreens() {
Winson Chunga90303b2013-11-15 13:05:06 -0800897 // Log to disk
898 Launcher.addDumpLog(TAG, "11683562 - stripEmptyScreens()", true);
899
Adam Cohen65e43032014-03-03 11:37:21 -0800900 if (mLauncher.isWorkspaceLoading()) {
Adam Cohendcb173d2014-04-01 13:33:58 -0700901 // Don't strip empty screens if the workspace is still loading.
902 // This is dangerous and can result in data loss.
Adam Cohen517a7f52014-03-01 12:12:59 -0800903 Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
904 return;
905 }
906
Adam Cohenaccfd562013-07-12 14:40:40 -0700907 if (isPageMoving()) {
908 mStripScreensOnPageStopMoving = true;
909 return;
910 }
911
912 int currentPage = getNextPage();
Adam Cohendcd297f2013-06-18 13:13:40 -0700913 ArrayList<Long> removeScreens = new ArrayList<Long>();
914 for (Long id: mWorkspaceScreens.keySet()) {
915 CellLayout cl = mWorkspaceScreens.get(id);
Winson Chungc9168342013-06-26 14:54:55 -0700916 if (id >= 0 && cl.getShortcutsAndWidgets().getChildCount() == 0) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700917 removeScreens.add(id);
918 }
919 }
920
Adam Cohen5084cba2013-09-03 12:01:16 -0700921 // We enforce at least one page to add new items to. In the case that we remove the last
922 // such screen, we convert the last screen to the empty screen
Michael Jurkafe0ace32013-10-03 01:05:14 -0700923 int minScreens = 1 + numCustomPages();
Adam Cohen5084cba2013-09-03 12:01:16 -0700924
Adam Cohendcd297f2013-06-18 13:13:40 -0700925 int pageShift = 0;
926 for (Long id: removeScreens) {
Winson Chunga90303b2013-11-15 13:05:06 -0800927 Launcher.addDumpLog(TAG, "11683562 - removing id: " + id, true);
Adam Cohendcd297f2013-06-18 13:13:40 -0700928 CellLayout cl = mWorkspaceScreens.get(id);
929 mWorkspaceScreens.remove(id);
930 mScreenOrder.remove(id);
Adam Cohen5084cba2013-09-03 12:01:16 -0700931
932 if (getChildCount() > minScreens) {
933 if (indexOfChild(cl) < currentPage) {
934 pageShift++;
935 }
936 removeView(cl);
937 } else {
938 // if this is the last non-custom content screen, convert it to the empty screen
Adam Cohenad4e15c2013-10-17 16:21:35 -0700939 mRemoveEmptyScreenRunnable = null;
Adam Cohen5084cba2013-09-03 12:01:16 -0700940 mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, cl);
941 mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
Adam Cohendcd297f2013-06-18 13:13:40 -0700942 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700943 }
Winson Chung64359a52013-07-08 17:17:08 -0700944
945 if (!removeScreens.isEmpty()) {
946 // Update the model if we have changed any screens
947 mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
948 }
Adam Cohenaccfd562013-07-12 14:40:40 -0700949
950 if (pageShift >= 0) {
951 setCurrentPage(currentPage - pageShift);
952 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700953 }
954
955 // See implementation for parameter definition.
956 void addInScreen(View child, long container, long screenId,
957 int x, int y, int spanX, int spanY) {
958 addInScreen(child, container, screenId, x, y, spanX, spanY, false, false);
959 }
960
961 // At bind time, we use the rank (screenId) to compute x and y for hotseat items.
962 // See implementation for parameter definition.
963 void addInScreenFromBind(View child, long container, long screenId, int x, int y,
964 int spanX, int spanY) {
965 addInScreen(child, container, screenId, x, y, spanX, spanY, false, true);
966 }
967
968 // See implementation for parameter definition.
969 void addInScreen(View child, long container, long screenId, int x, int y, int spanX, int spanY,
970 boolean insert) {
971 addInScreen(child, container, screenId, x, y, spanX, spanY, insert, false);
Winson Chungaafa03c2010-06-11 17:34:16 -0700972 }
973
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800974 /**
975 * Adds the specified child in the specified screen. The position and dimension of
976 * the child are defined by x, y, spanX and spanY.
977 *
978 * @param child The child to add in one of the workspace's screens.
Adam Cohendcd297f2013-06-18 13:13:40 -0700979 * @param screenId The screen in which to add the child.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800980 * @param x The X position of the child in the screen's grid.
981 * @param y The Y position of the child in the screen's grid.
982 * @param spanX The number of cells spanned horizontally by the child.
983 * @param spanY The number of cells spanned vertically by the child.
984 * @param insert When true, the child is inserted at the beginning of the children list.
Adam Cohendcd297f2013-06-18 13:13:40 -0700985 * @param computeXYFromRank When true, we use the rank (stored in screenId) to compute
986 * the x and y position in which to place hotseat items. Otherwise
987 * we use the x and y position to compute the rank.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800988 */
Adam Cohendcd297f2013-06-18 13:13:40 -0700989 void addInScreen(View child, long container, long screenId, int x, int y, int spanX, int spanY,
990 boolean insert, boolean computeXYFromRank) {
Winson Chung3d503fb2011-07-13 17:25:49 -0700991 if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700992 if (getScreenWithId(screenId) == null) {
993 Log.e(TAG, "Skipping child, screenId " + screenId + " not found");
Winson Chung8481e322013-08-09 16:06:38 -0700994 // DEBUGGING - Print out the stack trace to see where we are adding from
995 new Throwable().printStackTrace();
Winson Chung3d503fb2011-07-13 17:25:49 -0700996 return;
997 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800998 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700999 if (screenId == EXTRA_EMPTY_SCREEN_ID) {
Adam Cohendedbd962013-07-11 14:21:49 -07001000 // This should never happen
1001 throw new RuntimeException("Screen id should not be EXTRA_EMPTY_SCREEN_ID");
Adam Cohendcd297f2013-06-18 13:13:40 -07001002 }
1003
Winson Chung3d503fb2011-07-13 17:25:49 -07001004 final CellLayout layout;
1005 if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1006 layout = mLauncher.getHotseat().getLayout();
Winson Chung4d279d92011-07-21 11:46:32 -07001007 child.setOnKeyListener(null);
Winson Chung3d503fb2011-07-13 17:25:49 -07001008
Adam Cohen099f60d2011-08-23 21:07:26 -07001009 // Hide folder title in the hotseat
1010 if (child instanceof FolderIcon) {
1011 ((FolderIcon) child).setTextVisible(false);
1012 }
1013
Adam Cohendcd297f2013-06-18 13:13:40 -07001014 if (computeXYFromRank) {
1015 x = mLauncher.getHotseat().getCellXFromOrder((int) screenId);
1016 y = mLauncher.getHotseat().getCellYFromOrder((int) screenId);
Winson Chung3d503fb2011-07-13 17:25:49 -07001017 } else {
Adam Cohendcd297f2013-06-18 13:13:40 -07001018 screenId = mLauncher.getHotseat().getOrderInHotseat(x, y);
Winson Chung3d503fb2011-07-13 17:25:49 -07001019 }
1020 } else {
Adam Cohen099f60d2011-08-23 21:07:26 -07001021 // Show folder title if not in the hotseat
1022 if (child instanceof FolderIcon) {
1023 ((FolderIcon) child).setTextVisible(true);
1024 }
Adam Cohendcd297f2013-06-18 13:13:40 -07001025 layout = getScreenWithId(screenId);
Adam Cohenac56cff2011-09-28 20:45:37 -07001026 child.setOnKeyListener(new IconKeyEventListener());
Winson Chung3d503fb2011-07-13 17:25:49 -07001027 }
1028
Adam Cohen96d30a12013-07-16 18:13:21 -07001029 ViewGroup.LayoutParams genericLp = child.getLayoutParams();
Adam Cohened66b2b2012-01-23 17:28:51 -08001030 CellLayout.LayoutParams lp;
1031 if (genericLp == null || !(genericLp instanceof CellLayout.LayoutParams)) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001032 lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
1033 } else {
Adam Cohened66b2b2012-01-23 17:28:51 -08001034 lp = (CellLayout.LayoutParams) genericLp;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001035 lp.cellX = x;
1036 lp.cellY = y;
1037 lp.cellHSpan = spanX;
1038 lp.cellVSpan = spanY;
1039 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001040
Adam Cohen7f4eabe2011-04-21 16:19:16 -07001041 if (spanX < 0 && spanY < 0) {
1042 lp.isLockedToGrid = false;
1043 }
1044
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07001045 // Get the canonical child id to uniquely represent this view in this screen
Adam Cohenc76e1dd2013-11-14 11:09:14 +00001046 ItemInfo info = (ItemInfo) child.getTag();
1047 int childId = mLauncher.getViewIdForItem(info);
1048
Michael Jurkaf3ca3ab2010-10-20 17:08:24 -07001049 boolean markCellsAsOccupied = !(child instanceof Folder);
Winson Chung3d503fb2011-07-13 17:25:49 -07001050 if (!layout.addViewToCellLayout(child, insert ? 0 : -1, childId, lp, markCellsAsOccupied)) {
Winson Chungaafa03c2010-06-11 17:34:16 -07001051 // TODO: This branch occurs when the workspace is adding views
1052 // outside of the defined grid
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07001053 // maybe we should be deleting these items from the LauncherModel?
Adam Cohen4caf2982013-08-20 18:54:31 -07001054 Launcher.addDumpLog(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout", true);
Winson Chungaafa03c2010-06-11 17:34:16 -07001055 }
1056
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001057 if (!(child instanceof Folder)) {
Joe Onorato0d44e942009-11-16 18:20:51 -08001058 child.setHapticFeedbackEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001059 child.setOnLongClickListener(mLongClickListener);
1060 }
Joe Onorato00acb122009-08-04 16:04:30 -04001061 if (child instanceof DropTarget) {
Winson Chungaafa03c2010-06-11 17:34:16 -07001062 mDragController.addDropTarget((DropTarget) child);
Joe Onorato00acb122009-08-04 16:04:30 -04001063 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001064 }
1065
Patrick Dubroyd0ce1ec2011-01-19 18:47:27 -08001066 /**
Patrick Dubroye708c522011-03-01 16:03:43 -08001067 * Called directly from a CellLayout (not by the framework), after we've been added as a
1068 * listener via setOnInterceptTouchEventListener(). This allows us to tell the CellLayout
1069 * that it should intercept touch events, which is not something that is normally supported.
1070 */
1071 @Override
Michael Jurkadee05892010-07-27 10:01:56 -07001072 public boolean onTouch(View v, MotionEvent event) {
Adam Cohenf358a4b2013-07-23 16:47:31 -07001073 return (isSmall() || !isFinishedSwitchingState())
1074 || (!isSmall() && indexOfChild(v) != mCurrentPage);
Patrick Dubroye708c522011-03-01 16:03:43 -08001075 }
1076
Adam Cohenfc53cd22011-07-20 15:45:11 -07001077 public boolean isSwitchingState() {
1078 return mIsSwitchingState;
1079 }
1080
Winson Chung70442722012-02-10 15:43:22 -08001081 /** This differs from isSwitchingState in that we take into account how far the transition
1082 * has completed. */
Winson Chung9b0b2fe2012-02-24 13:03:34 -08001083 public boolean isFinishedSwitchingState() {
Winson Chung70442722012-02-10 15:43:22 -08001084 return !mIsSwitchingState || (mTransitionProgress > 0.5f);
1085 }
1086
Adam Cohended9f8d2010-11-03 13:25:16 -07001087 protected void onWindowVisibilityChanged (int visibility) {
1088 mLauncher.onWindowVisibilityChanged(visibility);
1089 }
1090
Michael Jurka5f1c5092010-09-03 14:15:02 -07001091 @Override
1092 public boolean dispatchUnhandledMove(View focused, int direction) {
Winson Chung70442722012-02-10 15:43:22 -08001093 if (isSmall() || !isFinishedSwitchingState()) {
Michael Jurka5f1c5092010-09-03 14:15:02 -07001094 // when the home screens are shrunken, shouldn't allow side-scrolling
1095 return false;
1096 }
1097 return super.dispatchUnhandledMove(focused, direction);
1098 }
1099
1100 @Override
1101 public boolean onInterceptTouchEvent(MotionEvent ev) {
Michael Jurkad771c962011-08-09 15:00:48 -07001102 switch (ev.getAction() & MotionEvent.ACTION_MASK) {
1103 case MotionEvent.ACTION_DOWN:
Adam Cohenf8d28232011-02-01 21:47:00 -08001104 mXDown = ev.getX();
1105 mYDown = ev.getY();
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001106 mTouchDownTime = System.currentTimeMillis();
Michael Jurkad771c962011-08-09 15:00:48 -07001107 break;
1108 case MotionEvent.ACTION_POINTER_UP:
1109 case MotionEvent.ACTION_UP:
1110 if (mTouchState == TOUCH_STATE_REST) {
1111 final CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage);
1112 if (!currentPage.lastDownOnOccupiedCell()) {
1113 onWallpaperTap(ev);
1114 }
1115 }
Adam Cohenf8d28232011-02-01 21:47:00 -08001116 }
Michael Jurka5f1c5092010-09-03 14:15:02 -07001117 return super.onInterceptTouchEvent(ev);
1118 }
1119
Adam Cohen3d509322012-06-06 14:10:04 -07001120 protected void reinflateWidgetsIfNecessary() {
1121 final int clCount = getChildCount();
1122 for (int i = 0; i < clCount; i++) {
1123 CellLayout cl = (CellLayout) getChildAt(i);
1124 ShortcutAndWidgetContainer swc = cl.getShortcutsAndWidgets();
1125 final int itemCount = swc.getChildCount();
1126 for (int j = 0; j < itemCount; j++) {
1127 View v = swc.getChildAt(j);
1128
1129 if (v.getTag() instanceof LauncherAppWidgetInfo) {
1130 LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
1131 LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) info.hostView;
1132 if (lahv != null && lahv.orientationChangedSincedInflation()) {
1133 mLauncher.removeAppWidget(info);
1134 // Remove the current widget which is inflated with the wrong orientation
1135 cl.removeView(lahv);
1136 mLauncher.bindAppWidget(info);
1137 }
1138 }
1139 }
1140 }
1141 }
1142
Michael Jurka1adf5392010-10-18 18:10:22 -07001143 @Override
1144 protected void determineScrollingStart(MotionEvent ev) {
Winson Chung70442722012-02-10 15:43:22 -08001145 if (!isFinishedSwitchingState()) return;
Adam Cohenf8d28232011-02-01 21:47:00 -08001146
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001147 float deltaX = ev.getX() - mXDown;
1148 float absDeltaX = Math.abs(deltaX);
1149 float absDeltaY = Math.abs(ev.getY() - mYDown);
Adam Cohenf8d28232011-02-01 21:47:00 -08001150
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001151 if (Float.compare(absDeltaX, 0f) == 0) return;
Adam Cohenf8d28232011-02-01 21:47:00 -08001152
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001153 float slope = absDeltaY / absDeltaX;
Winson Chung70442722012-02-10 15:43:22 -08001154 float theta = (float) Math.atan(slope);
Adam Cohenf8d28232011-02-01 21:47:00 -08001155
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001156 if (absDeltaX > mTouchSlop || absDeltaY > mTouchSlop) {
Winson Chung70442722012-02-10 15:43:22 -08001157 cancelCurrentPageLongPress();
1158 }
1159
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001160 boolean passRightSwipesToCustomContent =
1161 (mTouchDownTime - mCustomContentShowTime) > CUSTOM_CONTENT_GESTURE_DELAY;
1162
Adam Cohenaf9b0e52013-09-23 19:27:38 -07001163 boolean swipeInIgnoreDirection = isLayoutRtl() ? deltaX < 0 : deltaX > 0;
1164 if (swipeInIgnoreDirection && getScreenIdForPageIndex(getCurrentPage()) ==
1165 CUSTOM_CONTENT_SCREEN_ID && passRightSwipesToCustomContent) {
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001166 // Pass swipes to the right to the custom content page.
1167 return;
1168 }
1169
Winson Chung70442722012-02-10 15:43:22 -08001170 if (theta > MAX_SWIPE_ANGLE) {
1171 // Above MAX_SWIPE_ANGLE, we don't want to ever start scrolling the workspace
1172 return;
1173 } else if (theta > START_DAMPING_TOUCH_SLOP_ANGLE) {
1174 // Above START_DAMPING_TOUCH_SLOP_ANGLE and below MAX_SWIPE_ANGLE, we want to
1175 // increase the touch slop to make it harder to begin scrolling the workspace. This
1176 // results in vertically scrolling widgets to more easily. The higher the angle, the
1177 // more we increase touch slop.
1178 theta -= START_DAMPING_TOUCH_SLOP_ANGLE;
1179 float extraRatio = (float)
1180 Math.sqrt((theta / (MAX_SWIPE_ANGLE - START_DAMPING_TOUCH_SLOP_ANGLE)));
1181 super.determineScrollingStart(ev, 1 + TOUCH_SLOP_DAMPING_FACTOR * extraRatio);
1182 } else {
1183 // Below START_DAMPING_TOUCH_SLOP_ANGLE, we don't do anything special
1184 super.determineScrollingStart(ev);
Adam Cohenf8d28232011-02-01 21:47:00 -08001185 }
Michael Jurka1adf5392010-10-18 18:10:22 -07001186 }
1187
Patrick Dubroy1262e362010-10-06 15:49:50 -07001188 protected void onPageBeginMoving() {
Winson Chung007c6982011-06-14 13:27:53 -07001189 super.onPageBeginMoving();
1190
Michael Jurkad74c9842011-07-10 12:44:21 -07001191 if (isHardwareAccelerated()) {
Michael Jurka3a0469d2012-06-21 09:38:41 -07001192 updateChildrenLayersEnabled(false);
Michael Jurka0142d492010-08-25 17:46:15 -07001193 } else {
Michael Jurkad74c9842011-07-10 12:44:21 -07001194 if (mNextPage != INVALID_PAGE) {
1195 // we're snapping to a particular screen
1196 enableChildrenCache(mCurrentPage, mNextPage);
1197 } else {
1198 // this is when user is actively dragging a particular screen, they might
1199 // swipe it either left or right (but we won't advance by more than one screen)
1200 enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1);
1201 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001202 }
Winson Chung007c6982011-06-14 13:27:53 -07001203
Winson Chung70442722012-02-10 15:43:22 -08001204 // If we are not fading in adjacent screens, we still need to restore the alpha in case the
1205 // user scrolls while we are transitioning (should not affect dispatchDraw optimizations)
Michael Jurka869390b2012-05-06 15:55:19 -07001206 if (!mWorkspaceFadeInAdjacentScreens) {
Winson Chung70442722012-02-10 15:43:22 -08001207 for (int i = 0; i < getChildCount(); ++i) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07001208 ((CellLayout) getPageAt(i)).setShortcutAndWidgetAlpha(1f);
Winson Chung70442722012-02-10 15:43:22 -08001209 }
1210 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001211 }
1212
Patrick Dubroy1262e362010-10-06 15:49:50 -07001213 protected void onPageEndMoving() {
Winson Chung007c6982011-06-14 13:27:53 -07001214 super.onPageEndMoving();
1215
Michael Jurkad74c9842011-07-10 12:44:21 -07001216 if (isHardwareAccelerated()) {
Michael Jurka3a0469d2012-06-21 09:38:41 -07001217 updateChildrenLayersEnabled(false);
Michael Jurkad74c9842011-07-10 12:44:21 -07001218 } else {
1219 clearChildrenCache();
1220 }
1221
Winson Chung3bc21c32012-01-20 13:59:18 -08001222 if (mDragController.isDragging()) {
1223 if (isSmall()) {
1224 // If we are in springloaded mode, then force an event to check if the current touch
1225 // is under a new page (to scroll to)
Winson Chung25460a12013-04-01 18:21:28 -07001226 mDragController.forceTouchMove();
Winson Chung3bc21c32012-01-20 13:59:18 -08001227 }
Patrick Dubroy1262e362010-10-06 15:49:50 -07001228 }
Adam Cohen26976d92011-03-22 15:33:33 -07001229
1230 if (mDelayedResizeRunnable != null) {
1231 mDelayedResizeRunnable.run();
1232 mDelayedResizeRunnable = null;
1233 }
Winson Chungf0c6ae02012-03-21 16:10:31 -07001234
1235 if (mDelayedSnapToPageRunnable != null) {
1236 mDelayedSnapToPageRunnable.run();
1237 mDelayedSnapToPageRunnable = null;
1238 }
Adam Cohenaccfd562013-07-12 14:40:40 -07001239 if (mStripScreensOnPageStopMoving) {
1240 stripEmptyScreens();
1241 mStripScreensOnPageStopMoving = false;
1242 }
Adam Cohen89cbbb92013-09-25 16:52:48 -07001243 }
1244
1245 @Override
1246 protected void notifyPageSwitchListener() {
1247 super.notifyPageSwitchListener();
Adam Cohen674531f2013-12-13 15:07:14 -08001248 Launcher.setScreen(getNextPage());
Michael Jurka0142d492010-08-25 17:46:15 -07001249
Adam Cohen6ad0e7d2013-07-24 13:55:41 -07001250 if (hasCustomContent() && getNextPage() == 0 && !mCustomContentShowing) {
Adam Cohenbffe7452013-07-22 18:21:45 -07001251 mCustomContentShowing = true;
1252 if (mCustomContentCallbacks != null) {
Selim Cinek3a8a8f72014-01-16 10:38:38 -08001253 mCustomContentCallbacks.onShow(false);
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001254 mCustomContentShowTime = System.currentTimeMillis();
Winson Chung5841efa2013-09-30 18:06:44 -07001255 mLauncher.updateVoiceButtonProxyVisible(false);
Adam Cohenbffe7452013-07-22 18:21:45 -07001256 }
Adam Cohen6ad0e7d2013-07-24 13:55:41 -07001257 } else if (hasCustomContent() && getNextPage() != 0 && mCustomContentShowing) {
Adam Cohenbffe7452013-07-22 18:21:45 -07001258 mCustomContentShowing = false;
1259 if (mCustomContentCallbacks != null) {
1260 mCustomContentCallbacks.onHide();
1261 mLauncher.resetQSBScroll();
Winson Chung5841efa2013-09-30 18:06:44 -07001262 mLauncher.updateVoiceButtonProxyVisible(false);
Adam Cohenbffe7452013-07-22 18:21:45 -07001263 }
1264 }
Adam Cohen53805212013-10-01 10:39:23 -07001265 if (getPageIndicator() != null) {
1266 getPageIndicator().setContentDescription(getPageIndicatorDescription());
1267 }
Chris Craik01f2d7f2013-10-01 14:41:56 -07001268 }
Michael Jurka0142d492010-08-25 17:46:15 -07001269
Adam Cohen6fecd412013-10-02 17:41:50 -07001270 protected CustomContentCallbacks getCustomContentCallbacks() {
1271 return mCustomContentCallbacks;
1272 }
1273
Michael Jurkac5b262c2011-01-12 20:24:50 -08001274 protected void setWallpaperDimension() {
Michael Jurkaa6a05472013-11-13 17:59:46 +01001275 new AsyncTask<Void, Void, Void>() {
1276 public Void doInBackground(Void ... args) {
1277 String spKey = WallpaperCropActivity.getSharedPreferencesKey();
1278 SharedPreferences sp =
1279 mLauncher.getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
Michael Jurka7ad868b2013-12-12 15:04:25 +01001280 LauncherWallpaperPickerActivity.suggestWallpaperDimension(mLauncher.getResources(),
Michael Jurkaa6a05472013-11-13 17:59:46 +01001281 sp, mLauncher.getWindowManager(), mWallpaperManager);
1282 return null;
1283 }
1284 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
Michael Jurkac5b262c2011-01-12 20:24:50 -08001285 }
1286
Winson Chungf0c6ae02012-03-21 16:10:31 -07001287 protected void snapToPage(int whichPage, Runnable r) {
Adam Cohenad4e15c2013-10-17 16:21:35 -07001288 snapToPage(whichPage, SLOW_PAGE_SNAP_ANIMATION_DURATION, r);
1289 }
1290
1291 protected void snapToPage(int whichPage, int duration, Runnable r) {
Winson Chungf0c6ae02012-03-21 16:10:31 -07001292 if (mDelayedSnapToPageRunnable != null) {
1293 mDelayedSnapToPageRunnable.run();
1294 }
1295 mDelayedSnapToPageRunnable = r;
Adam Cohenad4e15c2013-10-17 16:21:35 -07001296 snapToPage(whichPage, duration);
Winson Chungf0c6ae02012-03-21 16:10:31 -07001297 }
1298
Adam Cohendcd297f2013-06-18 13:13:40 -07001299 protected void snapToScreenId(long screenId, Runnable r) {
1300 snapToPage(getPageIndexForScreenId(screenId), r);
1301 }
1302
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001303 class WallpaperOffsetInterpolator implements Choreographer.FrameCallback {
1304 float mFinalOffset = 0.0f;
Michael Jurka046cf342013-09-14 16:40:34 +02001305 float mCurrentOffset = 0.5f; // to force an initial update
Michael Jurkafe09cb72013-08-27 15:48:58 +02001306 boolean mWaitingForUpdate;
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001307 Choreographer mChoreographer;
1308 Interpolator mInterpolator;
1309 boolean mAnimating;
1310 long mAnimationStartTime;
1311 float mAnimationStartOffset;
Michael Jurka2f817312013-09-20 03:03:42 +02001312 private final int ANIMATION_DURATION = 250;
1313 // Don't use all the wallpaper for parallax until you have at least this many pages
Michael Jurkafe0ace32013-10-03 01:05:14 -07001314 private final int MIN_PARALLAX_PAGE_SPAN = 3;
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001315 int mNumScreens;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001316
1317 public WallpaperOffsetInterpolator() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001318 mChoreographer = Choreographer.getInstance();
1319 mInterpolator = new DecelerateInterpolator(1.5f);
Michael Jurkaab1983f2011-01-18 15:50:17 -08001320 }
1321
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001322 @Override
1323 public void doFrame(long frameTimeNanos) {
Michael Jurkafe09cb72013-08-27 15:48:58 +02001324 updateOffset(false);
1325 }
1326
Michael Jurkafe09cb72013-08-27 15:48:58 +02001327 private void updateOffset(boolean force) {
1328 if (mWaitingForUpdate || force) {
1329 mWaitingForUpdate = false;
Michael Jurka1bd90b02013-09-05 23:10:14 +02001330 if (computeScrollOffset() && mWindowToken != null) {
1331 try {
1332 mWallpaperManager.setWallpaperOffsets(mWindowToken,
1333 mWallpaperOffset.getCurrX(), 0.5f);
1334 setWallpaperOffsetSteps();
1335 } catch (IllegalArgumentException e) {
1336 Log.e(TAG, "Error updating wallpaper offset: " + e);
1337 }
Michael Jurkafe09cb72013-08-27 15:48:58 +02001338 }
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001339 }
Michael Jurkaca5b8362011-01-27 13:23:26 -08001340 }
1341
Michael Jurkaab1983f2011-01-18 15:50:17 -08001342 public boolean computeScrollOffset() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001343 final float oldOffset = mCurrentOffset;
1344 if (mAnimating) {
1345 long durationSinceAnimation = System.currentTimeMillis() - mAnimationStartTime;
1346 float t0 = durationSinceAnimation / (float) ANIMATION_DURATION;
1347 float t1 = mInterpolator.getInterpolation(t0);
1348 mCurrentOffset = mAnimationStartOffset +
1349 (mFinalOffset - mAnimationStartOffset) * t1;
1350 mAnimating = durationSinceAnimation < ANIMATION_DURATION;
Michael Jurkaca5b8362011-01-27 13:23:26 -08001351 } else {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001352 mCurrentOffset = mFinalOffset;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001353 }
Michael Jurkaca5b8362011-01-27 13:23:26 -08001354
Michael Jurkafe09cb72013-08-27 15:48:58 +02001355 if (Math.abs(mCurrentOffset - mFinalOffset) > 0.0000001f) {
1356 scheduleUpdate();
1357 }
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001358 if (Math.abs(oldOffset - mCurrentOffset) > 0.0000001f) {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001359 return true;
1360 }
1361 return false;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001362 }
1363
Michael Jurkafe09cb72013-08-27 15:48:58 +02001364 private float wallpaperOffsetForCurrentScroll() {
1365 if (getChildCount() <= 1) {
1366 return 0;
1367 }
Michael Jurka2f817312013-09-20 03:03:42 +02001368
1369 // Exclude the leftmost page
Michael Jurkafe0ace32013-10-03 01:05:14 -07001370 int emptyExtraPages = numEmptyScreensToIgnore();
1371 int firstIndex = numCustomPages();
Michael Jurka2f817312013-09-20 03:03:42 +02001372 // Exclude the last extra empty screen (if we have > MIN_PARALLAX_PAGE_SPAN pages)
Michael Jurkafe0ace32013-10-03 01:05:14 -07001373 int lastIndex = getChildCount() - 1 - emptyExtraPages;
1374 if (isLayoutRtl()) {
1375 int temp = firstIndex;
1376 firstIndex = lastIndex;
1377 lastIndex = temp;
1378 }
Michael Jurka2f817312013-09-20 03:03:42 +02001379
Michael Jurkafe09cb72013-08-27 15:48:58 +02001380 int firstPageScrollX = getScrollForPage(firstIndex);
1381 int scrollRange = getScrollForPage(lastIndex) - firstPageScrollX;
1382 if (scrollRange == 0) {
1383 return 0;
1384 } else {
Michael Jurka2f817312013-09-20 03:03:42 +02001385 // TODO: do different behavior if it's a live wallpaper?
Michael Jurkafe0ace32013-10-03 01:05:14 -07001386 // Sometimes the left parameter of the pages is animated during a layout transition;
1387 // this parameter offsets it to keep the wallpaper from animating as well
Michael Jurkafe0ace32013-10-03 01:05:14 -07001388 int adjustedScroll =
Michael Jurka8fd3adc2013-10-16 13:50:24 -07001389 getScrollX() - firstPageScrollX - getLayoutTransitionOffsetForPage(0);
Michael Jurkafe0ace32013-10-03 01:05:14 -07001390 float offset = Math.min(1, adjustedScroll / (float) scrollRange);
Michael Jurka2f817312013-09-20 03:03:42 +02001391 offset = Math.max(0, offset);
1392 // Don't use up all the wallpaper parallax until you have at least
1393 // MIN_PARALLAX_PAGE_SPAN pages
Michael Jurkafe0ace32013-10-03 01:05:14 -07001394 int numScrollingPages = getNumScreensExcludingEmptyAndCustom();
Michael Jurka2a4f4922014-01-29 16:32:39 +01001395 int parallaxPageSpan;
1396 if (mWallpaperIsLiveWallpaper) {
1397 parallaxPageSpan = numScrollingPages - 1;
1398 } else {
1399 parallaxPageSpan = Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages - 1);
1400 }
1401 mNumPagesForWallpaperParallax = parallaxPageSpan;
1402
Michael Jurkafe0ace32013-10-03 01:05:14 -07001403 // On RTL devices, push the wallpaper offset to the right if we don't have enough
1404 // pages (ie if numScrollingPages < MIN_PARALLAX_PAGE_SPAN)
1405 int padding = isLayoutRtl() ? parallaxPageSpan - numScrollingPages + 1 : 0;
1406 return offset * (padding + numScrollingPages - 1) / parallaxPageSpan;
Michael Jurkafe09cb72013-08-27 15:48:58 +02001407 }
1408 }
1409
Michael Jurkafe0ace32013-10-03 01:05:14 -07001410 private int numEmptyScreensToIgnore() {
1411 int numScrollingPages = getChildCount() - numCustomPages();
1412 if (numScrollingPages >= MIN_PARALLAX_PAGE_SPAN && hasExtraEmptyScreen()) {
Michael Jurka2f817312013-09-20 03:03:42 +02001413 return 1;
1414 } else {
1415 return 0;
1416 }
1417 }
1418
Michael Jurkafe0ace32013-10-03 01:05:14 -07001419 private int getNumScreensExcludingEmptyAndCustom() {
1420 int numScrollingPages = getChildCount() - numEmptyScreensToIgnore() - numCustomPages();
Michael Jurka2f817312013-09-20 03:03:42 +02001421 return numScrollingPages;
1422 }
1423
Michael Jurkafe09cb72013-08-27 15:48:58 +02001424 public void syncWithScroll() {
1425 float offset = wallpaperOffsetForCurrentScroll();
1426 mWallpaperOffset.setFinalX(offset);
1427 updateOffset(true);
1428 }
1429
Michael Jurkaab1983f2011-01-18 15:50:17 -08001430 public float getCurrX() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001431 return mCurrentOffset;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001432 }
1433
1434 public float getFinalX() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001435 return mFinalOffset;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001436 }
1437
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001438 private void animateToFinal() {
1439 mAnimating = true;
1440 mAnimationStartOffset = mCurrentOffset;
1441 mAnimationStartTime = System.currentTimeMillis();
Michael Jurkaab1983f2011-01-18 15:50:17 -08001442 }
1443
Michael Jurkafe09cb72013-08-27 15:48:58 +02001444 private void setWallpaperOffsetSteps() {
1445 // Set wallpaper offset steps (1 / (number of screens - 1))
Michael Jurka2a4f4922014-01-29 16:32:39 +01001446 float xOffset = 1.0f / mNumPagesForWallpaperParallax;
1447 if (xOffset != mLastSetWallpaperOffsetSteps) {
1448 mWallpaperManager.setWallpaperOffsetSteps(xOffset, 1.0f);
1449 mLastSetWallpaperOffsetSteps = xOffset;
1450 }
Michael Jurkafe09cb72013-08-27 15:48:58 +02001451 }
1452
Michael Jurkaab1983f2011-01-18 15:50:17 -08001453 public void setFinalX(float x) {
Michael Jurkafe09cb72013-08-27 15:48:58 +02001454 scheduleUpdate();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001455 mFinalOffset = Math.max(0f, Math.min(x, 1.0f));
Michael Jurkafe0ace32013-10-03 01:05:14 -07001456 if (getNumScreensExcludingEmptyAndCustom() != mNumScreens) {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001457 if (mNumScreens > 0) {
1458 // Don't animate if we're going from 0 screens
1459 animateToFinal();
1460 }
Michael Jurkafe0ace32013-10-03 01:05:14 -07001461 mNumScreens = getNumScreensExcludingEmptyAndCustom();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001462 }
Michael Jurkaab1983f2011-01-18 15:50:17 -08001463 }
1464
Michael Jurkafe09cb72013-08-27 15:48:58 +02001465 private void scheduleUpdate() {
1466 if (!mWaitingForUpdate) {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001467 mChoreographer.postFrameCallback(this);
Michael Jurkafe09cb72013-08-27 15:48:58 +02001468 mWaitingForUpdate = true;
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001469 }
Michael Jurkaab1983f2011-01-18 15:50:17 -08001470 }
1471
1472 public void jumpToFinal() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001473 mCurrentOffset = mFinalOffset;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001474 }
Dianne Hackborn8f573952009-08-10 23:21:09 -07001475 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001476
Michael Jurka340c5f32010-10-21 16:49:19 -07001477 @Override
1478 public void computeScroll() {
1479 super.computeScroll();
Michael Jurkafe09cb72013-08-27 15:48:58 +02001480 mWallpaperOffset.syncWithScroll();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001481 }
1482
Jason Monked05f092014-04-24 10:13:05 -04001483 @Override
1484 public void announceForAccessibility(CharSequence text) {
1485 // Don't announce if apps is on top of us.
1486 if (!mLauncher.isAllAppsVisible()) {
1487 super.announceForAccessibility(text);
1488 }
1489 }
1490
Patrick Dubroy94f78a52011-02-28 17:39:16 -08001491 void showOutlines() {
Michael Jurkad74c9842011-07-10 12:44:21 -07001492 if (!isSmall() && !mIsSwitchingState) {
Winson Chung9171e6d2010-11-17 17:39:27 -08001493 if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
1494 if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
Michael Jurka2ecf9952012-06-18 12:52:28 -07001495 mChildrenOutlineFadeInAnimation = LauncherAnimUtils.ofFloat(this, "childrenOutlineAlpha", 1.0f);
Winson Chung9171e6d2010-11-17 17:39:27 -08001496 mChildrenOutlineFadeInAnimation.setDuration(CHILDREN_OUTLINE_FADE_IN_DURATION);
1497 mChildrenOutlineFadeInAnimation.start();
Michael Jurka3e7c7632010-10-02 16:01:03 -07001498 }
Adam Cohenf34bab52010-09-30 14:11:56 -07001499 }
1500
Patrick Dubroy94f78a52011-02-28 17:39:16 -08001501 void hideOutlines() {
Michael Jurkad74c9842011-07-10 12:44:21 -07001502 if (!isSmall() && !mIsSwitchingState) {
Winson Chung9171e6d2010-11-17 17:39:27 -08001503 if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
1504 if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
Michael Jurka2ecf9952012-06-18 12:52:28 -07001505 mChildrenOutlineFadeOutAnimation = LauncherAnimUtils.ofFloat(this, "childrenOutlineAlpha", 0.0f);
Winson Chung9171e6d2010-11-17 17:39:27 -08001506 mChildrenOutlineFadeOutAnimation.setDuration(CHILDREN_OUTLINE_FADE_OUT_DURATION);
1507 mChildrenOutlineFadeOutAnimation.setStartDelay(CHILDREN_OUTLINE_FADE_OUT_DELAY);
1508 mChildrenOutlineFadeOutAnimation.start();
Michael Jurka3e7c7632010-10-02 16:01:03 -07001509 }
Adam Cohenf34bab52010-09-30 14:11:56 -07001510 }
1511
Patrick Dubroy94f78a52011-02-28 17:39:16 -08001512 public void showOutlinesTemporarily() {
1513 if (!mIsPageMoving && !isTouchActive()) {
1514 snapToPage(mCurrentPage);
1515 }
1516 }
1517
Winson Chung9171e6d2010-11-17 17:39:27 -08001518 public void setChildrenOutlineAlpha(float alpha) {
1519 mChildrenOutlineAlpha = alpha;
Adam Cohenf34bab52010-09-30 14:11:56 -07001520 for (int i = 0; i < getChildCount(); i++) {
1521 CellLayout cl = (CellLayout) getChildAt(i);
1522 cl.setBackgroundAlpha(alpha);
1523 }
1524 }
1525
Winson Chung9171e6d2010-11-17 17:39:27 -08001526 public float getChildrenOutlineAlpha() {
1527 return mChildrenOutlineAlpha;
1528 }
1529
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001530 void disableBackground() {
1531 mDrawBackground = false;
1532 }
1533 void enableBackground() {
1534 mDrawBackground = true;
1535 }
1536
Michael Jurka7bdb25a2011-08-03 15:16:44 -07001537 private void animateBackgroundGradient(float finalAlpha, boolean animated) {
Winson Chungb4b7fa72010-11-18 14:38:53 -08001538 if (mBackground == null) return;
Michael Jurkab9e14972011-07-25 17:57:40 -07001539 if (mBackgroundFadeInAnimation != null) {
1540 mBackgroundFadeInAnimation.cancel();
1541 mBackgroundFadeInAnimation = null;
1542 }
1543 if (mBackgroundFadeOutAnimation != null) {
1544 mBackgroundFadeOutAnimation.cancel();
1545 mBackgroundFadeOutAnimation = null;
1546 }
Michael Jurka7bdb25a2011-08-03 15:16:44 -07001547 float startAlpha = getBackgroundAlpha();
1548 if (finalAlpha != startAlpha) {
1549 if (animated) {
Michael Jurkaf1ad6082013-03-13 12:55:46 +01001550 mBackgroundFadeOutAnimation =
1551 LauncherAnimUtils.ofFloat(this, startAlpha, finalAlpha);
Michael Jurka7bdb25a2011-08-03 15:16:44 -07001552 mBackgroundFadeOutAnimation.addUpdateListener(new AnimatorUpdateListener() {
1553 public void onAnimationUpdate(ValueAnimator animation) {
1554 setBackgroundAlpha(((Float) animation.getAnimatedValue()).floatValue());
1555 }
1556 });
1557 mBackgroundFadeOutAnimation.setInterpolator(new DecelerateInterpolator(1.5f));
1558 mBackgroundFadeOutAnimation.setDuration(BACKGROUND_FADE_OUT_DURATION);
1559 mBackgroundFadeOutAnimation.start();
1560 } else {
1561 setBackgroundAlpha(finalAlpha);
1562 }
Michael Jurkab9e14972011-07-25 17:57:40 -07001563 }
Winson Chung9171e6d2010-11-17 17:39:27 -08001564 }
1565
1566 public void setBackgroundAlpha(float alpha) {
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001567 if (alpha != mBackgroundAlpha) {
1568 mBackgroundAlpha = alpha;
1569 invalidate();
1570 }
Winson Chung9171e6d2010-11-17 17:39:27 -08001571 }
1572
Adam Cohenf34bab52010-09-30 14:11:56 -07001573 public float getBackgroundAlpha() {
1574 return mBackgroundAlpha;
1575 }
1576
Adam Cohen68d73932010-11-15 10:50:58 -08001577 float backgroundAlphaInterpolator(float r) {
1578 float pivotA = 0.1f;
1579 float pivotB = 0.4f;
1580 if (r < pivotA) {
1581 return 0;
1582 } else if (r > pivotB) {
1583 return 1.0f;
1584 } else {
1585 return (r - pivotA)/(pivotB - pivotA);
1586 }
1587 }
1588
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001589 private void updatePageAlphaValues(int screenCenter) {
Michael Jurka869390b2012-05-06 15:55:19 -07001590 boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
1591 if (mWorkspaceFadeInAdjacentScreens &&
1592 mState == State.NORMAL &&
1593 !mIsSwitchingState &&
1594 !isInOverscroll) {
Adam Cohen84a465a2013-11-11 18:49:56 +00001595 for (int i = numCustomPages(); i < getChildCount(); i++) {
Michael Jurka869390b2012-05-06 15:55:19 -07001596 CellLayout child = (CellLayout) getChildAt(i);
1597 if (child != null) {
1598 float scrollProgress = getScrollProgress(screenCenter, child, i);
Adam Cohen73894962011-10-31 13:17:17 -07001599 float alpha = 1 - Math.abs(scrollProgress);
Michael Jurka869390b2012-05-06 15:55:19 -07001600 child.getShortcutsAndWidgets().setAlpha(alpha);
Adam Cohen73894962011-10-31 13:17:17 -07001601 }
Adam Cohenf34bab52010-09-30 14:11:56 -07001602 }
1603 }
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001604 }
1605
1606 private void setChildrenBackgroundAlphaMultipliers(float a) {
1607 for (int i = 0; i < getChildCount(); i++) {
1608 CellLayout child = (CellLayout) getChildAt(i);
1609 child.setBackgroundAlphaMultiplier(a);
1610 }
1611 }
1612
Winson Chung98ca0f72013-07-29 12:58:51 -07001613 public boolean hasCustomContent() {
Adam Cohenedb40762013-07-18 16:45:45 -07001614 return (mScreenOrder.size() > 0 && mScreenOrder.get(0) == CUSTOM_CONTENT_SCREEN_ID);
1615 }
1616
Michael Jurkafe0ace32013-10-03 01:05:14 -07001617 public int numCustomPages() {
1618 return hasCustomContent() ? 1 : 0;
1619 }
1620
Adam Cohenbffe7452013-07-22 18:21:45 -07001621 public boolean isOnOrMovingToCustomContent() {
1622 return hasCustomContent() && getNextPage() == 0;
1623 }
1624
Adam Cohenedb40762013-07-18 16:45:45 -07001625 private void updateStateForCustomContent(int screenCenter) {
Dave Hawkeya8881582013-09-17 15:55:33 -06001626 float translationX = 0;
1627 float progress = 0;
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01001628 if (hasCustomContent()) {
Adam Cohenedb40762013-07-18 16:45:45 -07001629 int index = mScreenOrder.indexOf(CUSTOM_CONTENT_SCREEN_ID);
Adam Cohen564a2e72013-10-09 14:47:32 -07001630
Adam Cohena45de072013-10-11 16:07:47 -07001631 int scrollDelta = getScrollX() - getScrollForPage(index) -
1632 getLayoutTransitionOffsetForPage(index);
1633 float scrollRange = getScrollForPage(index + 1) - getScrollForPage(index);
1634 translationX = scrollRange - scrollDelta;
1635 progress = (scrollRange - scrollDelta) / scrollRange;
Adam Cohenaf9b0e52013-09-23 19:27:38 -07001636
1637 if (isLayoutRtl()) {
1638 translationX = Math.min(0, translationX);
1639 } else {
1640 translationX = Math.max(0, translationX);
1641 }
Adam Cohen955806d2013-07-19 16:36:43 -07001642 progress = Math.max(0, progress);
Dave Hawkeya8881582013-09-17 15:55:33 -06001643 }
Adam Cohenedb40762013-07-18 16:45:45 -07001644
Dave Hawkeya8881582013-09-17 15:55:33 -06001645 if (Float.compare(progress, mLastCustomContentScrollProgress) == 0) return;
Adam Cohen84add1d2013-10-14 16:29:02 -07001646
1647 CellLayout cc = mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID);
1648 if (progress > 0 && cc.getVisibility() != VISIBLE && !isSmall()) {
1649 cc.setVisibility(VISIBLE);
1650 }
1651
Dave Hawkeya8881582013-09-17 15:55:33 -06001652 mLastCustomContentScrollProgress = progress;
Adam Cohenc36fa5c2013-08-29 11:54:42 -07001653
Dave Hawkeya8881582013-09-17 15:55:33 -06001654 setBackgroundAlpha(progress * 0.8f);
Adam Cohen22cba7f2013-07-19 16:14:00 -07001655
Dave Hawkeya8881582013-09-17 15:55:33 -06001656 if (mLauncher.getHotseat() != null) {
1657 mLauncher.getHotseat().setTranslationX(translationX);
1658 }
Adam Cohenc36fa5c2013-08-29 11:54:42 -07001659
Dave Hawkeya8881582013-09-17 15:55:33 -06001660 if (getPageIndicator() != null) {
1661 getPageIndicator().setTranslationX(translationX);
1662 }
Adam Cohenc36fa5c2013-08-29 11:54:42 -07001663
Dave Hawkeya8881582013-09-17 15:55:33 -06001664 if (mCustomContentCallbacks != null) {
1665 mCustomContentCallbacks.onScrollProgressChanged(progress);
Adam Cohenedb40762013-07-18 16:45:45 -07001666 }
1667 }
1668
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001669 @Override
Adam Cohen53805212013-10-01 10:39:23 -07001670 protected OnClickListener getPageIndicatorClickListener() {
1671 AccessibilityManager am = (AccessibilityManager)
1672 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
1673 if (!am.isTouchExplorationEnabled()) {
1674 return null;
1675 }
1676 OnClickListener listener = new OnClickListener() {
1677 @Override
1678 public void onClick(View arg0) {
1679 enterOverviewMode();
1680 }
1681 };
1682 return listener;
1683 }
1684
1685 @Override
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001686 protected void screenScrolled(int screenCenter) {
Winson Chung52aee602013-01-30 12:01:02 -08001687 final boolean isRtl = isLayoutRtl();
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001688 super.screenScrolled(screenCenter);
1689
1690 updatePageAlphaValues(screenCenter);
Adam Cohenedb40762013-07-18 16:45:45 -07001691 updateStateForCustomContent(screenCenter);
Michael Jurkad51f33a2012-06-28 15:35:26 -07001692 enableHwLayersOnVisiblePages();
Adam Cohenf34bab52010-09-30 14:11:56 -07001693
Adam Cohenaf9b0e52013-09-23 19:27:38 -07001694 boolean shouldOverScroll = (mOverScrollX < 0 && (!hasCustomContent() || isLayoutRtl())) ||
1695 (mOverScrollX > mMaxScrollX && (!hasCustomContent() || !isLayoutRtl()));
1696
1697 if (shouldOverScroll) {
Winson Chung52aee602013-01-30 12:01:02 -08001698 int index = 0;
1699 float pivotX = 0f;
1700 final float leftBiasedPivot = 0.25f;
1701 final float rightBiasedPivot = 0.75f;
1702 final int lowerIndex = 0;
1703 final int upperIndex = getChildCount() - 1;
Adam Cohena29f5042013-09-26 14:29:35 -07001704
1705 final boolean isLeftPage = mOverScrollX < 0;
1706 index = (!isRtl && isLeftPage) || (isRtl && !isLeftPage) ? lowerIndex : upperIndex;
1707 pivotX = isLeftPage ? rightBiasedPivot : leftBiasedPivot;
Winson Chung52aee602013-01-30 12:01:02 -08001708
Adam Cohenb5ba0972011-09-07 18:02:31 -07001709 CellLayout cl = (CellLayout) getChildAt(index);
1710 float scrollProgress = getScrollProgress(screenCenter, cl, index);
Winson Chung52aee602013-01-30 12:01:02 -08001711 cl.setOverScrollAmount(Math.abs(scrollProgress), isLeftPage);
1712 float rotation = -WORKSPACE_OVERSCROLL_ROTATION * scrollProgress;
Adam Cohenb5ba0972011-09-07 18:02:31 -07001713 cl.setRotationY(rotation);
Adam Cohena29f5042013-09-26 14:29:35 -07001714
1715 if (!mOverscrollTransformsSet || Float.compare(mLastOverscrollPivotX, pivotX) != 0) {
Michael Jurkab06d95f2012-04-02 06:26:53 -07001716 mOverscrollTransformsSet = true;
Adam Cohena29f5042013-09-26 14:29:35 -07001717 mLastOverscrollPivotX = pivotX;
Adam Cohen94309882012-06-08 16:24:56 -07001718 cl.setCameraDistance(mDensity * mCameraDistance);
Winson Chung52aee602013-01-30 12:01:02 -08001719 cl.setPivotX(cl.getMeasuredWidth() * pivotX);
Michael Jurkab06d95f2012-04-02 06:26:53 -07001720 cl.setPivotY(cl.getMeasuredHeight() * 0.5f);
1721 cl.setOverscrollTransformsDirty(true);
1722 }
Adam Cohenb5ba0972011-09-07 18:02:31 -07001723 } else {
Adam Cohen4de09742013-12-12 16:16:39 -08001724 if (mOverscrollTransformsSet && getChildCount() > 0) {
Michael Jurkab06d95f2012-04-02 06:26:53 -07001725 mOverscrollTransformsSet = false;
Adam Cohenebea84d2011-11-09 17:20:41 -08001726 ((CellLayout) getChildAt(0)).resetOverscrollTransforms();
1727 ((CellLayout) getChildAt(getChildCount() - 1)).resetOverscrollTransforms();
Adam Cohen7842d7f2011-09-12 15:28:15 -07001728 }
Adam Cohenb5ba0972011-09-07 18:02:31 -07001729 }
1730 }
1731
1732 @Override
Adam Cohenb5ba0972011-09-07 18:02:31 -07001733 protected void overScroll(float amount) {
Michael Jurka869390b2012-05-06 15:55:19 -07001734 acceleratedOverScroll(amount);
Adam Cohenb5ba0972011-09-07 18:02:31 -07001735 }
1736
Joe Onorato00acb122009-08-04 16:04:30 -04001737 protected void onAttachedToWindow() {
1738 super.onAttachedToWindow();
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001739 mWindowToken = getWindowToken();
Joe Onorato956091b2010-02-19 12:47:40 -08001740 computeScroll();
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001741 mDragController.setWindowToken(mWindowToken);
1742 }
1743
1744 protected void onDetachedFromWindow() {
Romain Guye6661952013-08-08 19:13:22 -07001745 super.onDetachedFromWindow();
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001746 mWindowToken = null;
Joe Onorato00acb122009-08-04 16:04:30 -04001747 }
1748
Adam Cohen53805212013-10-01 10:39:23 -07001749 protected void onResume() {
1750 if (getPageIndicator() != null) {
1751 // In case accessibility state has changed, we need to perform this on every
1752 // attach to window
Adam Cohend36d9472013-10-10 15:32:41 -07001753 OnClickListener listener = getPageIndicatorClickListener();
1754 if (listener != null) {
1755 getPageIndicator().setOnClickListener(listener);
1756 }
Adam Cohen53805212013-10-01 10:39:23 -07001757 }
1758 AccessibilityManager am = (AccessibilityManager)
1759 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
1760 sAccessibilityEnabled = am.isEnabled();
Michael Jurkaa6a05472013-11-13 17:59:46 +01001761
1762 // Update wallpaper dimensions if they were changed since last onResume
1763 // (we also always set the wallpaper dimensions in the constructor)
1764 if (LauncherAppState.getInstance().hasWallpaperChangedSinceLastCheck()) {
1765 setWallpaperDimension();
1766 }
Michael Jurka2a4f4922014-01-29 16:32:39 +01001767 mWallpaperIsLiveWallpaper = mWallpaperManager.getWallpaperInfo() != null;
1768 // Force the wallpaper offset steps to be set again, because another app might have changed
1769 // them
1770 mLastSetWallpaperOffsetSteps = 0f;
Adam Cohen53805212013-10-01 10:39:23 -07001771 }
1772
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001773 @Override
Michael Jurka28750fb2010-09-24 17:43:49 -07001774 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Michael Jurkac5b262c2011-01-12 20:24:50 -08001775 if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
Michael Jurkafe09cb72013-08-27 15:48:58 +02001776 mWallpaperOffset.syncWithScroll();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001777 mWallpaperOffset.jumpToFinal();
Michael Jurkac5b262c2011-01-12 20:24:50 -08001778 }
Michael Jurka0142d492010-08-25 17:46:15 -07001779 super.onLayout(changed, left, top, right, bottom);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001780 }
1781
1782 @Override
Winson Chung9171e6d2010-11-17 17:39:27 -08001783 protected void onDraw(Canvas canvas) {
1784 // Draw the background gradient if necessary
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001785 if (mBackground != null && mBackgroundAlpha > 0.0f && mDrawBackground) {
Winson Chung8d006d52010-11-29 15:55:29 -08001786 int alpha = (int) (mBackgroundAlpha * 255);
Winson Chungf0ea4d32011-06-06 14:27:16 -07001787 mBackground.setAlpha(alpha);
Michael Jurka8b805b12012-04-18 14:23:14 -07001788 mBackground.setBounds(getScrollX(), 0, getScrollX() + getMeasuredWidth(),
Winson Chungf0ea4d32011-06-06 14:27:16 -07001789 getMeasuredHeight());
1790 mBackground.draw(canvas);
Winson Chung9171e6d2010-11-17 17:39:27 -08001791 }
Adam Cohen073a46f2011-05-17 16:28:09 -07001792
Winson Chung9171e6d2010-11-17 17:39:27 -08001793 super.onDraw(canvas);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001794
1795 // Call back to LauncherModel to finish binding after the first draw
Romain Guyeeacd562012-10-10 18:47:33 -07001796 post(mBindPages);
Winson Chung9171e6d2010-11-17 17:39:27 -08001797 }
1798
Adam Cohen21b41102011-11-01 17:29:52 -07001799 boolean isDrawingBackgroundGradient() {
1800 return (mBackground != null && mBackgroundAlpha > 0.0f && mDrawBackground);
1801 }
1802
Michael Jurkadfab7f02011-11-18 13:01:04 -08001803 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001804 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
Joe Onorato67886212009-09-14 19:05:05 -04001805 if (!mLauncher.isAllAppsVisible()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001806 final Folder openFolder = getOpenFolder();
1807 if (openFolder != null) {
1808 return openFolder.requestFocus(direction, previouslyFocusedRect);
1809 } else {
Michael Jurka0142d492010-08-25 17:46:15 -07001810 return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001811 }
1812 }
1813 return false;
1814 }
1815
1816 @Override
Winson Chung97d85d22011-04-13 11:27:36 -07001817 public int getDescendantFocusability() {
Michael Jurkad74c9842011-07-10 12:44:21 -07001818 if (isSmall()) {
Winson Chung97d85d22011-04-13 11:27:36 -07001819 return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
1820 }
1821 return super.getDescendantFocusability();
1822 }
1823
1824 @Override
Romain Guyc2e24c02009-06-01 16:11:41 -07001825 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
Joe Onorato67886212009-09-14 19:05:05 -04001826 if (!mLauncher.isAllAppsVisible()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001827 final Folder openFolder = getOpenFolder();
Michael Jurka0142d492010-08-25 17:46:15 -07001828 if (openFolder != null) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001829 openFolder.addFocusables(views, direction);
Michael Jurka0142d492010-08-25 17:46:15 -07001830 } else {
1831 super.addFocusables(views, direction, focusableMode);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001832 }
1833 }
1834 }
1835
Michael Jurkad74c9842011-07-10 12:44:21 -07001836 public boolean isSmall() {
Adam Cohenf358a4b2013-07-23 16:47:31 -07001837 return mState == State.SMALL || mState == State.SPRING_LOADED || mState == State.OVERVIEW;
Michael Jurkad74c9842011-07-10 12:44:21 -07001838 }
1839
Michael Jurka0142d492010-08-25 17:46:15 -07001840 void enableChildrenCache(int fromPage, int toPage) {
1841 if (fromPage > toPage) {
1842 final int temp = fromPage;
1843 fromPage = toPage;
1844 toPage = temp;
Mike Cleron3a2b3f22009-11-05 17:17:42 -08001845 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001846
Michael Jurkadee05892010-07-27 10:01:56 -07001847 final int screenCount = getChildCount();
Adam Powellfea5d022010-04-29 11:42:45 -07001848
Michael Jurka0142d492010-08-25 17:46:15 -07001849 fromPage = Math.max(fromPage, 0);
1850 toPage = Math.min(toPage, screenCount - 1);
Adam Powellfea5d022010-04-29 11:42:45 -07001851
Michael Jurka0142d492010-08-25 17:46:15 -07001852 for (int i = fromPage; i <= toPage; i++) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001853 final CellLayout layout = (CellLayout) getChildAt(i);
1854 layout.setChildrenDrawnWithCacheEnabled(true);
1855 layout.setChildrenDrawingCacheEnabled(true);
1856 }
1857 }
1858
1859 void clearChildrenCache() {
Michael Jurkadee05892010-07-27 10:01:56 -07001860 final int screenCount = getChildCount();
1861 for (int i = 0; i < screenCount; i++) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001862 final CellLayout layout = (CellLayout) getChildAt(i);
1863 layout.setChildrenDrawnWithCacheEnabled(false);
Adam Cohen8182e5b2011-08-01 15:43:31 -07001864 // In software mode, we don't want the items to continue to be drawn into bitmaps
1865 if (!isHardwareAccelerated()) {
1866 layout.setChildrenDrawingCacheEnabled(false);
1867 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001868 }
1869 }
1870
Michael Jurka3a0469d2012-06-21 09:38:41 -07001871 private void updateChildrenLayersEnabled(boolean force) {
Adam Cohenf358a4b2013-07-23 16:47:31 -07001872 boolean small = mState == State.SMALL || mState == State.OVERVIEW || mIsSwitchingState;
Michael Jurka3a0469d2012-06-21 09:38:41 -07001873 boolean enableChildrenLayers = force || small || mAnimatingViewIntoPlace || isPageMoving();
Michael Jurkad74c9842011-07-10 12:44:21 -07001874
1875 if (enableChildrenLayers != mChildrenLayersEnabled) {
1876 mChildrenLayersEnabled = enableChildrenLayers;
Michael Jurkad51f33a2012-06-28 15:35:26 -07001877 if (mChildrenLayersEnabled) {
1878 enableHwLayersOnVisiblePages();
1879 } else {
1880 for (int i = 0; i < getPageCount(); i++) {
1881 final CellLayout cl = (CellLayout) getChildAt(i);
Chris Craik01f2d7f2013-10-01 14:41:56 -07001882 cl.enableHardwareLayer(false);
Michael Jurkad51f33a2012-06-28 15:35:26 -07001883 }
1884 }
1885 }
1886 }
1887
1888 private void enableHwLayersOnVisiblePages() {
1889 if (mChildrenLayersEnabled) {
1890 final int screenCount = getChildCount();
1891 getVisiblePages(mTempVisiblePagesRange);
1892 int leftScreen = mTempVisiblePagesRange[0];
1893 int rightScreen = mTempVisiblePagesRange[1];
1894 if (leftScreen == rightScreen) {
1895 // make sure we're caching at least two pages always
1896 if (rightScreen < screenCount - 1) {
1897 rightScreen++;
1898 } else if (leftScreen > 0) {
1899 leftScreen--;
1900 }
1901 }
Chris Craik01f2d7f2013-10-01 14:41:56 -07001902
1903 final CellLayout customScreen = mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID);
Michael Jurkad51f33a2012-06-28 15:35:26 -07001904 for (int i = 0; i < screenCount; i++) {
Michael Jurka47639b92013-01-14 12:42:27 +01001905 final CellLayout layout = (CellLayout) getPageAt(i);
Chris Craik01f2d7f2013-10-01 14:41:56 -07001906
1907 // enable layers between left and right screen inclusive, except for the
1908 // customScreen, which may animate its content during transitions.
1909 boolean enableLayer = layout != customScreen &&
1910 leftScreen <= i && i <= rightScreen && shouldDrawChild(layout);
1911 layout.enableHardwareLayer(enableLayer);
Michael Jurkad74c9842011-07-10 12:44:21 -07001912 }
Michael Jurkace7e05f2011-02-01 22:02:35 -08001913 }
Michael Jurkace7e05f2011-02-01 22:02:35 -08001914 }
1915
Michael Jurka3a0469d2012-06-21 09:38:41 -07001916 public void buildPageHardwareLayers() {
1917 // force layers to be enabled just for the call to buildLayer
1918 updateChildrenLayersEnabled(true);
1919 if (getWindowToken() != null) {
1920 final int childCount = getChildCount();
1921 for (int i = 0; i < childCount; i++) {
1922 CellLayout cl = (CellLayout) getChildAt(i);
Michael Jurkad51f33a2012-06-28 15:35:26 -07001923 cl.buildHardwareLayer();
Michael Jurka3a0469d2012-06-21 09:38:41 -07001924 }
1925 }
1926 updateChildrenLayersEnabled(false);
1927 }
1928
Jeff Brown1d0867c2010-12-02 18:27:39 -08001929 protected void onWallpaperTap(MotionEvent ev) {
1930 final int[] position = mTempCell;
1931 getLocationOnScreen(position);
1932
1933 int pointerIndex = ev.getActionIndex();
1934 position[0] += (int) ev.getX(pointerIndex);
1935 position[1] += (int) ev.getY(pointerIndex);
1936
1937 mWallpaperManager.sendWallpaperCommand(getWindowToken(),
1938 ev.getAction() == MotionEvent.ACTION_UP
1939 ? WallpaperManager.COMMAND_TAP : WallpaperManager.COMMAND_SECONDARY_TAP,
1940 position[0], position[1], 0, null);
1941 }
1942
Adam Cohen61033d32010-11-15 18:29:44 -08001943 /*
1944 * This interpolator emulates the rate at which the perceived scale of an object changes
1945 * as its distance from a camera increases. When this interpolator is applied to a scale
1946 * animation on a view, it evokes the sense that the object is shrinking due to moving away
Andrew Flynn0dca1ec2012-02-29 13:33:22 -08001947 * from the camera.
Adam Cohen61033d32010-11-15 18:29:44 -08001948 */
1949 static class ZInterpolator implements TimeInterpolator {
1950 private float focalLength;
1951
1952 public ZInterpolator(float foc) {
1953 focalLength = foc;
1954 }
1955
Adam Coheneed565d2010-11-15 11:30:05 -08001956 public float getInterpolation(float input) {
1957 return (1.0f - focalLength / (focalLength + input)) /
Adam Cohen61033d32010-11-15 18:29:44 -08001958 (1.0f - focalLength / (focalLength + 1.0f));
Adam Cohencbbaf982010-11-12 14:50:33 -08001959 }
1960 }
1961
Adam Cohen61033d32010-11-15 18:29:44 -08001962 /*
1963 * The exact reverse of ZInterpolator.
1964 */
1965 static class InverseZInterpolator implements TimeInterpolator {
1966 private ZInterpolator zInterpolator;
1967 public InverseZInterpolator(float foc) {
1968 zInterpolator = new ZInterpolator(foc);
1969 }
Adam Cohencbbaf982010-11-12 14:50:33 -08001970 public float getInterpolation(float input) {
Adam Cohen61033d32010-11-15 18:29:44 -08001971 return 1 - zInterpolator.getInterpolation(1 - input);
Adam Cohencbbaf982010-11-12 14:50:33 -08001972 }
1973 }
1974
Adam Cohen61033d32010-11-15 18:29:44 -08001975 /*
1976 * ZInterpolator compounded with an ease-out.
1977 */
1978 static class ZoomOutInterpolator implements TimeInterpolator {
Adam Cohenb64d36e2011-10-17 21:48:02 -07001979 private final DecelerateInterpolator decelerate = new DecelerateInterpolator(0.75f);
1980 private final ZInterpolator zInterpolator = new ZInterpolator(0.13f);
Adam Cohen61033d32010-11-15 18:29:44 -08001981
1982 public float getInterpolation(float input) {
1983 return decelerate.getInterpolation(zInterpolator.getInterpolation(input));
1984 }
1985 }
1986
1987 /*
1988 * InvereZInterpolator compounded with an ease-out.
1989 */
1990 static class ZoomInInterpolator implements TimeInterpolator {
1991 private final InverseZInterpolator inverseZInterpolator = new InverseZInterpolator(0.35f);
1992 private final DecelerateInterpolator decelerate = new DecelerateInterpolator(3.0f);
1993
1994 public float getInterpolation(float input) {
1995 return decelerate.getInterpolation(inverseZInterpolator.getInterpolation(input));
1996 }
1997 }
1998
Adam Cohen61033d32010-11-15 18:29:44 -08001999 private final ZoomInInterpolator mZoomInInterpolator = new ZoomInInterpolator();
Michael Jurka3e7c7632010-10-02 16:01:03 -07002000
Michael Jurka3e7c7632010-10-02 16:01:03 -07002001 /*
Adam Cohen66396872011-04-15 17:50:36 -07002002 *
2003 * We call these methods (onDragStartedWithItemSpans/onDragStartedWithSize) whenever we
2004 * start a drag in Launcher, regardless of whether the drag has ever entered the Workspace
2005 *
2006 * These methods mark the appropriate pages as accepting drops (which alters their visual
2007 * appearance).
2008 *
2009 */
2010 public void onDragStartedWithItem(View v) {
Adam Cohen66396872011-04-15 17:50:36 -07002011 final Canvas canvas = new Canvas();
2012
Adam Cohen66396872011-04-15 17:50:36 -07002013 // The outline is used to visualize where the item will land if dropped
Adam Cohend41fbf52012-02-16 23:53:59 -08002014 mDragOutline = createDragOutline(v, canvas, DRAG_BITMAP_PADDING);
Adam Cohen66396872011-04-15 17:50:36 -07002015 }
2016
Mathew Inwood85900292014-04-16 14:17:39 +01002017 private Rect getDrawableBounds(Drawable d) {
2018 Rect bounds = new Rect();
2019 d.copyBounds(bounds);
2020 if (bounds.width() == 0 || bounds.height() == 0) {
2021 bounds.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
2022 }
2023 return bounds;
2024 }
2025
Winson Chungbabb53e2014-04-14 17:12:49 -07002026 public void onExternalDragStartedWithItem(View v) {
2027 final Canvas canvas = new Canvas();
2028
2029 // Compose a drag bitmap with the view scaled to the icon size
2030 LauncherAppState app = LauncherAppState.getInstance();
2031 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
2032 int iconSize = grid.iconSizePx;
2033 int bmpWidth = v.getMeasuredWidth();
2034 int bmpHeight = v.getMeasuredHeight();
2035
2036 // If this is a text view, use its drawable instead
2037 if (v instanceof TextView) {
2038 TextView tv = (TextView) v;
2039 Drawable d = tv.getCompoundDrawables()[1];
Mathew Inwood85900292014-04-16 14:17:39 +01002040 Rect bounds = getDrawableBounds(d);
2041 bmpWidth = bounds.width();
2042 bmpHeight = bounds.height();
Winson Chungbabb53e2014-04-14 17:12:49 -07002043 }
2044
2045 // Compose the bitmap to create the icon from
2046 Bitmap b = Bitmap.createBitmap(bmpWidth, bmpHeight,
2047 Bitmap.Config.ARGB_8888);
2048 Canvas c = new Canvas(b);
2049 drawDragView(v, c, 0, true);
2050 c.setBitmap(null);
2051
2052 // The outline is used to visualize where the item will land if dropped
2053 mDragOutline = createDragOutline(b, canvas, DRAG_BITMAP_PADDING, iconSize, iconSize, true);
2054 }
2055
Michael Jurka8c3339b2012-06-14 16:18:21 -07002056 public void onDragStartedWithItem(PendingAddItemInfo info, Bitmap b, boolean clipAlpha) {
Michael Jurkad3ef3062010-11-23 16:23:58 -08002057 final Canvas canvas = new Canvas();
2058
Michael Jurka038f9d82011-11-03 13:50:45 -07002059 int[] size = estimateItemSize(info.spanX, info.spanY, info, false);
Adam Cohen66396872011-04-15 17:50:36 -07002060
Michael Jurkad3ef3062010-11-23 16:23:58 -08002061 // The outline is used to visualize where the item will land if dropped
Adam Cohend41fbf52012-02-16 23:53:59 -08002062 mDragOutline = createDragOutline(b, canvas, DRAG_BITMAP_PADDING, size[0],
Michael Jurka8c3339b2012-06-14 16:18:21 -07002063 size[1], clipAlpha);
Michael Jurka3e7c7632010-10-02 16:01:03 -07002064 }
2065
Patrick Dubroy758a9232011-03-03 19:54:56 -08002066 public void exitWidgetResizeMode() {
Adam Cohen716b51e2011-06-30 12:09:54 -07002067 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohen67882692011-03-11 15:29:03 -08002068 dragLayer.clearAllResizeFrames();
Patrick Dubroy758a9232011-03-03 19:54:56 -08002069 }
2070
Adam Cohen4b285c52011-07-21 14:24:06 -07002071 private void initAnimationArrays() {
2072 final int childCount = getChildCount();
Adam Cohendcd297f2013-06-18 13:13:40 -07002073 if (mLastChildCount == childCount) return;
Adam Cohen7d30a372013-07-01 17:03:59 -07002074
Adam Cohen4b285c52011-07-21 14:24:06 -07002075 mOldBackgroundAlphas = new float[childCount];
Adam Cohen4b285c52011-07-21 14:24:06 -07002076 mOldAlphas = new float[childCount];
Adam Cohen4b285c52011-07-21 14:24:06 -07002077 mNewBackgroundAlphas = new float[childCount];
Adam Cohen4b285c52011-07-21 14:24:06 -07002078 mNewAlphas = new float[childCount];
Adam Cohen4b285c52011-07-21 14:24:06 -07002079 }
2080
Michael Jurka2a4b1a82011-12-07 14:00:02 -08002081 Animator getChangeStateAnimation(final State state, boolean animated) {
Adam Cohenf358a4b2013-07-23 16:47:31 -07002082 return getChangeStateAnimation(state, animated, 0, -1);
Adam Cohen7777d962011-08-18 18:58:38 -07002083 }
2084
Adam Cohenf358a4b2013-07-23 16:47:31 -07002085 @Override
2086 protected void getOverviewModePages(int[] range) {
Michael Jurkafe0ace32013-10-03 01:05:14 -07002087 int start = numCustomPages();
Adam Cohen1003be92013-09-16 14:09:28 -07002088 int end = getChildCount() - 1;
2089
2090 range[0] = Math.max(0, Math.min(start, getChildCount() - 1));
2091 range[1] = Math.max(0, end);
Adam Cohendedbd962013-07-11 14:21:49 -07002092 }
2093
2094 protected void onStartReordering() {
2095 super.onStartReordering();
Adam Cohendedbd962013-07-11 14:21:49 -07002096 showOutlines();
Adam Cohen22cba7f2013-07-19 16:14:00 -07002097 // Reordering handles its own animations, disable the automatic ones.
Winson Chung964df6b2013-10-11 15:55:37 -07002098 disableLayoutTransitions();
Adam Cohendedbd962013-07-11 14:21:49 -07002099 }
2100
2101 protected void onEndReordering() {
2102 super.onEndReordering();
Adam Cohendedbd962013-07-11 14:21:49 -07002103
Adam Cohendcb173d2014-04-01 13:33:58 -07002104 if (mLauncher.isWorkspaceLoading()) {
2105 // Invalid and dangerous operation if workspace is loading
2106 return;
2107 }
2108
Adam Cohen2bf63d52013-09-29 17:46:49 -07002109 hideOutlines();
Adam Cohendedbd962013-07-11 14:21:49 -07002110 mScreenOrder.clear();
Adam Cohen2bf63d52013-09-29 17:46:49 -07002111 int count = getChildCount();
Adam Cohendedbd962013-07-11 14:21:49 -07002112 for (int i = 0; i < count; i++) {
2113 CellLayout cl = ((CellLayout) getChildAt(i));
2114 mScreenOrder.add(getIdForScreen(cl));
2115 }
Winson Chungd64d1762013-08-20 14:37:16 -07002116
Adam Cohendedbd962013-07-11 14:21:49 -07002117 mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
Adam Cohen22cba7f2013-07-19 16:14:00 -07002118
2119 // Re-enable auto layout transitions for page deletion.
Winson Chung964df6b2013-10-11 15:55:37 -07002120 enableLayoutTransitions();
Adam Cohendedbd962013-07-11 14:21:49 -07002121 }
2122
Adam Cohenf358a4b2013-07-23 16:47:31 -07002123 public boolean isInOverviewMode() {
2124 return mState == State.OVERVIEW;
2125 }
2126
Adam Cohen93c97562013-09-26 13:48:01 -07002127 public boolean enterOverviewMode() {
2128 if (mTouchState != TOUCH_STATE_REST) {
2129 return false;
2130 }
Michael Jurka336fd4f2013-09-12 00:05:02 +02002131 enableOverviewMode(true, -1, true);
Adam Cohen93c97562013-09-26 13:48:01 -07002132 return true;
Adam Cohenf358a4b2013-07-23 16:47:31 -07002133 }
2134
Michael Jurka336fd4f2013-09-12 00:05:02 +02002135 public void exitOverviewMode(boolean animated) {
2136 exitOverviewMode(-1, animated);
Adam Cohenf358a4b2013-07-23 16:47:31 -07002137 }
2138
Michael Jurka336fd4f2013-09-12 00:05:02 +02002139 public void exitOverviewMode(int snapPage, boolean animated) {
2140 enableOverviewMode(false, snapPage, animated);
Adam Cohenf358a4b2013-07-23 16:47:31 -07002141 }
2142
Michael Jurka336fd4f2013-09-12 00:05:02 +02002143 private void enableOverviewMode(boolean enable, int snapPage, boolean animated) {
Adam Cohenf358a4b2013-07-23 16:47:31 -07002144 State finalState = Workspace.State.OVERVIEW;
2145 if (!enable) {
2146 finalState = Workspace.State.NORMAL;
2147 }
2148
Michael Jurka336fd4f2013-09-12 00:05:02 +02002149 Animator workspaceAnim = getChangeStateAnimation(finalState, animated, 0, snapPage);
2150 if (workspaceAnim != null) {
Adam Cohenf3434992013-09-16 16:52:59 -07002151 onTransitionPrepare();
Michael Jurka336fd4f2013-09-12 00:05:02 +02002152 workspaceAnim.addListener(new AnimatorListenerAdapter() {
2153 @Override
2154 public void onAnimationEnd(Animator arg0) {
Adam Cohenf3434992013-09-16 16:52:59 -07002155 onTransitionEnd();
Michael Jurka336fd4f2013-09-12 00:05:02 +02002156 }
2157 });
2158 workspaceAnim.start();
2159 }
Adam Cohenf358a4b2013-07-23 16:47:31 -07002160 }
2161
Adam Cohen410f3cd2013-09-22 12:09:32 -07002162 int getOverviewModeTranslationY() {
Winson Chungc82d2622013-11-06 13:23:29 -08002163 LauncherAppState app = LauncherAppState.getInstance();
2164 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
2165 Rect overviewBar = grid.getOverviewModeButtonBarRect();
Adam Cohen410f3cd2013-09-22 12:09:32 -07002166
Winson Chungc82d2622013-11-06 13:23:29 -08002167 int availableHeight = getViewportHeight();
2168 int scaledHeight = (int) (mOverviewModeShrinkFactor * getNormalChildHeight());
2169 int offsetFromTopEdge = (availableHeight - scaledHeight) / 2;
2170 int offsetToCenterInOverview = (availableHeight - mInsets.top - overviewBar.height()
2171 - scaledHeight) / 2;
Adam Cohen410f3cd2013-09-22 12:09:32 -07002172
Winson Chungc82d2622013-11-06 13:23:29 -08002173 return -offsetFromTopEdge + mInsets.top + offsetToCenterInOverview;
Adam Cohen410f3cd2013-09-22 12:09:32 -07002174 }
2175
Winson Chung5841efa2013-09-30 18:06:44 -07002176 boolean shouldVoiceButtonProxyBeVisible() {
2177 if (isOnOrMovingToCustomContent()) {
2178 return false;
2179 }
2180 if (mState != State.NORMAL) {
2181 return false;
2182 }
2183 return true;
2184 }
2185
Adam Cohenedaaa302013-10-01 17:33:27 -07002186 public void updateInteractionForState() {
2187 if (mState != State.NORMAL) {
2188 mLauncher.onInteractionBegin();
2189 } else {
2190 mLauncher.onInteractionEnd();
2191 }
2192 }
2193
2194 private void setState(State state) {
2195 mState = state;
2196 updateInteractionForState();
Adam Cohen53805212013-10-01 10:39:23 -07002197 updateAccessibilityFlags();
2198 }
2199
2200 private void updateAccessibilityFlags() {
2201 int accessible = mState == State.NORMAL ?
2202 ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES :
2203 ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
2204 setImportantForAccessibility(accessible);
Adam Cohenedaaa302013-10-01 17:33:27 -07002205 }
2206
Adam Cohenf358a4b2013-07-23 16:47:31 -07002207 Animator getChangeStateAnimation(final State state, boolean animated, int delay, int snapPage) {
Michael Jurkabdf78552011-10-31 14:34:25 -07002208 if (mState == state) {
Michael Jurka2a4b1a82011-12-07 14:00:02 -08002209 return null;
Michael Jurka99633da2011-07-27 22:40:17 -07002210 }
2211
Winson Chung1b7c1d32011-10-25 12:09:38 -07002212 // Initialize animation arrays for the first time if necessary
2213 initAnimationArrays();
2214
Michael Jurka2ecf9952012-06-18 12:52:28 -07002215 AnimatorSet anim = animated ? LauncherAnimUtils.createAnimatorSet() : null;
Adam Lesinski6b879f02010-11-04 16:15:23 -07002216
Winson Chung1b7c1d32011-10-25 12:09:38 -07002217 final State oldState = mState;
2218 final boolean oldStateIsNormal = (oldState == State.NORMAL);
Michael Jurka4ff7d792012-04-02 03:46:50 -07002219 final boolean oldStateIsSpringLoaded = (oldState == State.SPRING_LOADED);
Winson Chung1b7c1d32011-10-25 12:09:38 -07002220 final boolean oldStateIsSmall = (oldState == State.SMALL);
Adam Cohenf358a4b2013-07-23 16:47:31 -07002221 final boolean oldStateIsOverview = (oldState == State.OVERVIEW);
Adam Cohenedaaa302013-10-01 17:33:27 -07002222 setState(state);
Winson Chung1b7c1d32011-10-25 12:09:38 -07002223 final boolean stateIsNormal = (state == State.NORMAL);
2224 final boolean stateIsSpringLoaded = (state == State.SPRING_LOADED);
2225 final boolean stateIsSmall = (state == State.SMALL);
Adam Cohenf358a4b2013-07-23 16:47:31 -07002226 final boolean stateIsOverview = (state == State.OVERVIEW);
Winson Chung2d75f122013-09-23 16:53:31 -07002227 float finalBackgroundAlpha = (stateIsSpringLoaded || stateIsOverview) ? 1.0f : 0f;
Adam Cohenf358a4b2013-07-23 16:47:31 -07002228 float finalHotseatAndPageIndicatorAlpha = (stateIsOverview || stateIsSmall) ? 0f : 1f;
2229 float finalOverviewPanelAlpha = stateIsOverview ? 1f : 0f;
Winson Chung2d75f122013-09-23 16:53:31 -07002230 float finalSearchBarAlpha = !stateIsNormal ? 0f : 1f;
Adam Cohen410f3cd2013-09-22 12:09:32 -07002231 float finalWorkspaceTranslationY = stateIsOverview ? getOverviewModeTranslationY() : 0;
Adam Cohenf358a4b2013-07-23 16:47:31 -07002232
Winson Chungf4bd2362013-10-07 17:11:27 -07002233 boolean workspaceToAllApps = (oldStateIsNormal && stateIsSmall);
2234 boolean allAppsToWorkspace = (oldStateIsSmall && stateIsNormal);
Adam Cohen58993ad2013-10-11 17:57:38 -07002235 boolean workspaceToOverview = (oldStateIsNormal && stateIsOverview);
2236 boolean overviewToWorkspace = (oldStateIsOverview && stateIsNormal);
2237
Adam Cohen7d30a372013-07-01 17:03:59 -07002238 mNewScale = 1.0f;
Adam Cohenbeff8c62011-08-31 17:46:01 -07002239
Adam Cohenf358a4b2013-07-23 16:47:31 -07002240 if (oldStateIsOverview) {
Adam Cohenf9618852013-11-08 06:45:03 -08002241 disableFreeScroll();
Adam Cohenf358a4b2013-07-23 16:47:31 -07002242 } else if (stateIsOverview) {
2243 enableFreeScroll();
2244 }
2245
Adam Cohen7777d962011-08-18 18:58:38 -07002246 if (state != State.NORMAL) {
Adam Cohenf358a4b2013-07-23 16:47:31 -07002247 if (stateIsSpringLoaded) {
2248 mNewScale = mSpringLoadedShrinkFactor;
2249 } else if (stateIsOverview) {
2250 mNewScale = mOverviewModeShrinkFactor;
2251 } else if (stateIsSmall){
Adam Cohenaf9b0e52013-09-23 19:27:38 -07002252 mNewScale = mOverviewModeShrinkFactor - 0.3f;
Adam Cohenf358a4b2013-07-23 16:47:31 -07002253 }
Winson Chungf4bd2362013-10-07 17:11:27 -07002254 if (workspaceToAllApps) {
Michael Jurka3a0469d2012-06-21 09:38:41 -07002255 updateChildrenLayersEnabled(false);
Adam Cohen7777d962011-08-18 18:58:38 -07002256 }
Adam Cohen7777d962011-08-18 18:58:38 -07002257 }
Winson Chungf4bd2362013-10-07 17:11:27 -07002258
Adam Cohenfa2450a2013-10-11 18:20:27 -07002259 final int duration;
2260 if (workspaceToAllApps) {
2261 duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime);
2262 } else if (workspaceToOverview || overviewToWorkspace) {
2263 duration = getResources().getInteger(R.integer.config_overviewTransitionTime);
2264 } else {
2265 duration = getResources().getInteger(R.integer.config_appsCustomizeWorkspaceShrinkTime);
2266 }
2267
Adam Cohenf9618852013-11-08 06:45:03 -08002268 if (snapPage == -1) {
2269 snapPage = getPageNearestToCenterOfScreen();
2270 }
2271 snapToPage(snapPage, duration, mZoomInInterpolator);
2272
Winson Chung1b7c1d32011-10-25 12:09:38 -07002273 for (int i = 0; i < getChildCount(); i++) {
2274 final CellLayout cl = (CellLayout) getChildAt(i);
Adam Cohenf9618852013-11-08 06:45:03 -08002275 boolean isCurrentPage = (i == snapPage);
Winson Chung2d75f122013-09-23 16:53:31 -07002276 float initialAlpha = cl.getShortcutsAndWidgets().getAlpha();
Adam Cohen3b185e22013-10-29 14:45:58 -07002277 float finalAlpha;
2278 if (stateIsSmall) {
2279 finalAlpha = 0f;
2280 } else if (stateIsNormal && mWorkspaceFadeInAdjacentScreens) {
Adam Cohenf9618852013-11-08 06:45:03 -08002281 finalAlpha = (i == snapPage || i < numCustomPages()) ? 1f : 0f;
Adam Cohen3b185e22013-10-29 14:45:58 -07002282 } else {
2283 finalAlpha = 1f;
2284 }
Winson Chungf4bd2362013-10-07 17:11:27 -07002285
2286 // If we are animating to/from the small state, then hide the side pages and fade the
2287 // current page in
2288 if (!mIsSwitchingState) {
2289 if (workspaceToAllApps || allAppsToWorkspace) {
2290 if (allAppsToWorkspace && isCurrentPage) {
2291 initialAlpha = 0f;
2292 } else if (!isCurrentPage) {
2293 initialAlpha = finalAlpha = 0f;
2294 }
2295 cl.setShortcutAndWidgetAlpha(initialAlpha);
2296 }
2297 }
Adam Cohen7777d962011-08-18 18:58:38 -07002298
Winson Chung1b7c1d32011-10-25 12:09:38 -07002299 mOldAlphas[i] = initialAlpha;
2300 mNewAlphas[i] = finalAlpha;
Adam Cohen7777d962011-08-18 18:58:38 -07002301 if (animated) {
Adam Cohen7777d962011-08-18 18:58:38 -07002302 mOldBackgroundAlphas[i] = cl.getBackgroundAlpha();
Adam Cohen7777d962011-08-18 18:58:38 -07002303 mNewBackgroundAlphas[i] = finalBackgroundAlpha;
Adam Cohen7777d962011-08-18 18:58:38 -07002304 } else {
Winson Chung1b7c1d32011-10-25 12:09:38 -07002305 cl.setBackgroundAlpha(finalBackgroundAlpha);
Michael Jurkaa52570f2012-03-20 03:18:20 -07002306 cl.setShortcutAndWidgetAlpha(finalAlpha);
Adam Cohen7777d962011-08-18 18:58:38 -07002307 }
Michael Jurkadee05892010-07-27 10:01:56 -07002308 }
Winson Chung8d006d52010-11-29 15:55:29 -08002309
Adam Cohen3d411982013-09-24 17:02:06 -07002310 final View searchBar = mLauncher.getQsbBar();
2311 final View overviewPanel = mLauncher.getOverviewPanel();
2312 final View hotseat = mLauncher.getHotseat();
Michael Jurka853f5a42013-11-25 14:32:21 +01002313 final View pageIndicator = getPageIndicator();
Adam Cohen7777d962011-08-18 18:58:38 -07002314 if (animated) {
Adam Cohenfa2450a2013-10-11 18:20:27 -07002315 anim.setDuration(duration);
Adam Cohen7d30a372013-07-01 17:03:59 -07002316 LauncherViewPropertyAnimator scale = new LauncherViewPropertyAnimator(this);
2317 scale.scaleX(mNewScale)
2318 .scaleY(mNewScale)
Adam Cohen410f3cd2013-09-22 12:09:32 -07002319 .translationY(finalWorkspaceTranslationY)
Adam Cohen7d30a372013-07-01 17:03:59 -07002320 .setInterpolator(mZoomInInterpolator);
2321 anim.play(scale);
Michael Jurka7407d2a2011-12-12 21:48:38 -08002322 for (int index = 0; index < getChildCount(); index++) {
2323 final int i = index;
2324 final CellLayout cl = (CellLayout) getChildAt(i);
Michael Jurka869390b2012-05-06 15:55:19 -07002325 float currentAlpha = cl.getShortcutsAndWidgets().getAlpha();
Michael Jurka2a4b1a82011-12-07 14:00:02 -08002326 if (mOldAlphas[i] == 0 && mNewAlphas[i] == 0) {
Michael Jurka7372c592012-01-16 04:21:35 -08002327 cl.setBackgroundAlpha(mNewBackgroundAlphas[i]);
Michael Jurkaa52570f2012-03-20 03:18:20 -07002328 cl.setShortcutAndWidgetAlpha(mNewAlphas[i]);
Michael Jurka7407d2a2011-12-12 21:48:38 -08002329 } else {
Michael Jurka869390b2012-05-06 15:55:19 -07002330 if (mOldAlphas[i] != mNewAlphas[i] || currentAlpha != mNewAlphas[i]) {
Michael Jurka6c8a5792012-03-22 05:24:37 -07002331 LauncherViewPropertyAnimator alphaAnim =
2332 new LauncherViewPropertyAnimator(cl.getShortcutsAndWidgets());
Michael Jurkaa52570f2012-03-20 03:18:20 -07002333 alphaAnim.alpha(mNewAlphas[i])
Michael Jurkaa52570f2012-03-20 03:18:20 -07002334 .setInterpolator(mZoomInInterpolator);
2335 anim.play(alphaAnim);
2336 }
Michael Jurka7407d2a2011-12-12 21:48:38 -08002337 if (mOldBackgroundAlphas[i] != 0 ||
Michael Jurka869390b2012-05-06 15:55:19 -07002338 mNewBackgroundAlphas[i] != 0) {
Michael Jurkaf1ad6082013-03-13 12:55:46 +01002339 ValueAnimator bgAnim =
Adam Cohenfa2450a2013-10-11 18:20:27 -07002340 LauncherAnimUtils.ofFloat(cl, 0f, 1f);
Michael Jurka7407d2a2011-12-12 21:48:38 -08002341 bgAnim.setInterpolator(mZoomInInterpolator);
2342 bgAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
2343 public void onAnimationUpdate(float a, float b) {
Michael Jurka7372c592012-01-16 04:21:35 -08002344 cl.setBackgroundAlpha(
Michael Jurka7407d2a2011-12-12 21:48:38 -08002345 a * mOldBackgroundAlphas[i] +
2346 b * mNewBackgroundAlphas[i]);
Michael Jurka7407d2a2011-12-12 21:48:38 -08002347 }
2348 });
2349 anim.play(bgAnim);
2350 }
Michael Jurka2a4b1a82011-12-07 14:00:02 -08002351 }
2352 }
Michael Jurka853f5a42013-11-25 14:32:21 +01002353 Animator pageIndicatorAlpha = null;
2354 if (pageIndicator != null) {
2355 pageIndicatorAlpha = new LauncherViewPropertyAnimator(pageIndicator)
2356 .alpha(finalHotseatAndPageIndicatorAlpha).withLayer();
Michael Jurka1c00fd02013-12-02 16:11:22 -08002357 pageIndicatorAlpha.addListener(new AlphaUpdateListener(pageIndicator));
Michael Jurka853f5a42013-11-25 14:32:21 +01002358 } else {
2359 // create a dummy animation so we don't need to do null checks later
2360 pageIndicatorAlpha = ValueAnimator.ofFloat(0, 0);
Winson Chung66700732013-08-20 16:56:15 -07002361 }
Michael Jurka1c00fd02013-12-02 16:11:22 -08002362
Michael Jurka853f5a42013-11-25 14:32:21 +01002363 Animator hotseatAlpha = new LauncherViewPropertyAnimator(hotseat)
2364 .alpha(finalHotseatAndPageIndicatorAlpha).withLayer();
Michael Jurka1c00fd02013-12-02 16:11:22 -08002365 hotseatAlpha.addListener(new AlphaUpdateListener(hotseat));
2366
Michael Jurka853f5a42013-11-25 14:32:21 +01002367 Animator searchBarAlpha = new LauncherViewPropertyAnimator(searchBar)
2368 .alpha(finalSearchBarAlpha).withLayer();
Michael Jurka1c00fd02013-12-02 16:11:22 -08002369 searchBarAlpha.addListener(new AlphaUpdateListener(searchBar));
2370
Michael Jurka853f5a42013-11-25 14:32:21 +01002371 Animator overviewPanelAlpha = new LauncherViewPropertyAnimator(overviewPanel)
2372 .alpha(finalOverviewPanelAlpha).withLayer();
Michael Jurka853f5a42013-11-25 14:32:21 +01002373 overviewPanelAlpha.addListener(new AlphaUpdateListener(overviewPanel));
Adam Cohen3d411982013-09-24 17:02:06 -07002374
Adam Cohen58993ad2013-10-11 17:57:38 -07002375 if (workspaceToOverview) {
Michael Jurka853f5a42013-11-25 14:32:21 +01002376 pageIndicatorAlpha.setInterpolator(new DecelerateInterpolator(2));
Adam Cohen58993ad2013-10-11 17:57:38 -07002377 hotseatAlpha.setInterpolator(new DecelerateInterpolator(2));
Michael Jurka853f5a42013-11-25 14:32:21 +01002378 overviewPanelAlpha.setInterpolator(null);
Adam Cohen58993ad2013-10-11 17:57:38 -07002379 } else if (overviewToWorkspace) {
Michael Jurka853f5a42013-11-25 14:32:21 +01002380 pageIndicatorAlpha.setInterpolator(null);
2381 hotseatAlpha.setInterpolator(null);
Adam Cohen58993ad2013-10-11 17:57:38 -07002382 overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2));
2383 }
Michael Jurka853f5a42013-11-25 14:32:21 +01002384 searchBarAlpha.setInterpolator(null);
Adam Cohen3d411982013-09-24 17:02:06 -07002385
Adam Cohenf358a4b2013-07-23 16:47:31 -07002386 anim.play(overviewPanelAlpha);
2387 anim.play(hotseatAlpha);
Adam Cohendbdff6b2013-09-18 19:09:15 -07002388 anim.play(searchBarAlpha);
Adam Cohenf358a4b2013-07-23 16:47:31 -07002389 anim.play(pageIndicatorAlpha);
Michael Jurka2a4b1a82011-12-07 14:00:02 -08002390 anim.setStartDelay(delay);
Adam Cohenf358a4b2013-07-23 16:47:31 -07002391 } else {
Adam Cohen3d411982013-09-24 17:02:06 -07002392 overviewPanel.setAlpha(finalOverviewPanelAlpha);
2393 AlphaUpdateListener.updateVisibility(overviewPanel);
2394 hotseat.setAlpha(finalHotseatAndPageIndicatorAlpha);
2395 AlphaUpdateListener.updateVisibility(hotseat);
Michael Jurka853f5a42013-11-25 14:32:21 +01002396 if (pageIndicator != null) {
2397 pageIndicator.setAlpha(finalHotseatAndPageIndicatorAlpha);
2398 AlphaUpdateListener.updateVisibility(pageIndicator);
Winson Chung66700732013-08-20 16:56:15 -07002399 }
Adam Cohendbdff6b2013-09-18 19:09:15 -07002400 searchBar.setAlpha(finalSearchBarAlpha);
Adam Cohen3f452c82013-09-19 11:57:17 -07002401 AlphaUpdateListener.updateVisibility(searchBar);
2402 updateCustomContentVisibility();
Adam Cohen410f3cd2013-09-22 12:09:32 -07002403 setScaleX(mNewScale);
2404 setScaleY(mNewScale);
2405 setTranslationY(finalWorkspaceTranslationY);
Adam Cohendbdff6b2013-09-18 19:09:15 -07002406 }
Winson Chung5841efa2013-09-30 18:06:44 -07002407 mLauncher.updateVoiceButtonProxyVisible(false);
Adam Cohen7777d962011-08-18 18:58:38 -07002408
Winson Chung1b7c1d32011-10-25 12:09:38 -07002409 if (stateIsSpringLoaded) {
Michael Jurka7bdb25a2011-08-03 15:16:44 -07002410 // Right now we're covered by Apps Customize
2411 // Show the background gradient immediately, so the gradient will
2412 // be showing once AppsCustomize disappears
2413 animateBackgroundGradient(getResources().getInteger(
2414 R.integer.config_appsCustomizeSpringLoadedBgAlpha) / 100f, false);
Adam Cohendbdff6b2013-09-18 19:09:15 -07002415 } else if (stateIsOverview) {
2416 animateBackgroundGradient(getResources().getInteger(
2417 R.integer.config_appsCustomizeSpringLoadedBgAlpha) / 100f, true);
Michael Jurka7bdb25a2011-08-03 15:16:44 -07002418 } else {
2419 // Fade the background gradient away
Adam Cohened307df2013-10-02 09:37:31 -07002420 animateBackgroundGradient(0f, animated);
Michael Jurka7bdb25a2011-08-03 15:16:44 -07002421 }
Michael Jurka2a4b1a82011-12-07 14:00:02 -08002422 return anim;
Michael Jurkadee05892010-07-27 10:01:56 -07002423 }
2424
Adam Cohena5f4e482013-10-11 12:10:28 -07002425 static class AlphaUpdateListener implements AnimatorUpdateListener, AnimatorListener {
Adam Cohenf358a4b2013-07-23 16:47:31 -07002426 View view;
2427 public AlphaUpdateListener(View v) {
2428 view = v;
2429 }
2430
2431 @Override
2432 public void onAnimationUpdate(ValueAnimator arg0) {
Michael Jurka336fd4f2013-09-12 00:05:02 +02002433 updateVisibility(view);
2434 }
2435
2436 public static void updateVisibility(View view) {
Adam Cohen53805212013-10-01 10:39:23 -07002437 // We want to avoid the extra layout pass by setting the views to GONE unless
2438 // accessibility is on, in which case not setting them to GONE causes a glitch.
2439 int invisibleState = sAccessibilityEnabled ? GONE : INVISIBLE;
2440 if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != invisibleState) {
2441 view.setVisibility(invisibleState);
Adam Cohenf358a4b2013-07-23 16:47:31 -07002442 } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD
2443 && view.getVisibility() != VISIBLE) {
2444 view.setVisibility(VISIBLE);
2445 }
2446 }
Adam Cohena5f4e482013-10-11 12:10:28 -07002447
2448 @Override
2449 public void onAnimationCancel(Animator arg0) {
2450 }
2451
2452 @Override
2453 public void onAnimationEnd(Animator arg0) {
2454 updateVisibility(view);
2455 }
2456
2457 @Override
2458 public void onAnimationRepeat(Animator arg0) {
2459 }
2460
2461 @Override
2462 public void onAnimationStart(Animator arg0) {
2463 // We want the views to be visible for animation, so fade-in/out is visible
2464 view.setVisibility(VISIBLE);
2465 }
Adam Cohenf358a4b2013-07-23 16:47:31 -07002466 }
2467
Michael Jurkabed61d22012-02-14 22:51:29 -08002468 @Override
Michael Jurkaa35e35a2012-04-26 15:04:28 -07002469 public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
Adam Cohenf3434992013-09-16 16:52:59 -07002470 onTransitionPrepare();
Michael Jurkabed61d22012-02-14 22:51:29 -08002471 }
2472
2473 @Override
Michael Jurkaa35e35a2012-04-26 15:04:28 -07002474 public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
2475 }
2476
2477 @Override
Winson Chung70442722012-02-10 15:43:22 -08002478 public void onLauncherTransitionStep(Launcher l, float t) {
2479 mTransitionProgress = t;
2480 }
2481
2482 @Override
Michael Jurkabed61d22012-02-14 22:51:29 -08002483 public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
Adam Cohenf3434992013-09-16 16:52:59 -07002484 onTransitionEnd();
2485 }
2486
2487 private void onTransitionPrepare() {
2488 mIsSwitchingState = true;
Winson Chungf4bd2362013-10-07 17:11:27 -07002489
2490 // Invalidate here to ensure that the pages are rendered during the state change transition.
2491 invalidate();
2492
Adam Cohenf3434992013-09-16 16:52:59 -07002493 updateChildrenLayersEnabled(false);
Adam Cohen3f452c82013-09-19 11:57:17 -07002494 hideCustomContentIfNecessary();
2495 }
2496
2497 void updateCustomContentVisibility() {
2498 int visibility = mState == Workspace.State.NORMAL ? VISIBLE : INVISIBLE;
2499 if (hasCustomContent()) {
2500 mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(visibility);
2501 }
2502 }
2503
2504 void showCustomContentIfNecessary() {
2505 boolean show = mState == Workspace.State.NORMAL;
2506 if (show && hasCustomContent()) {
2507 mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(VISIBLE);
2508 }
2509 }
2510
2511 void hideCustomContentIfNecessary() {
2512 boolean hide = mState != Workspace.State.NORMAL;
2513 if (hide && hasCustomContent()) {
Adam Cohen1774a8a2013-11-15 10:56:16 +00002514 disableLayoutTransitions();
Adam Cohen3f452c82013-09-19 11:57:17 -07002515 mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(INVISIBLE);
Adam Cohen1774a8a2013-11-15 10:56:16 +00002516 enableLayoutTransitions();
Adam Cohenf3434992013-09-16 16:52:59 -07002517 }
2518 }
2519
2520 private void onTransitionEnd() {
Michael Jurkabed61d22012-02-14 22:51:29 -08002521 mIsSwitchingState = false;
Michael Jurka3a0469d2012-06-21 09:38:41 -07002522 updateChildrenLayersEnabled(false);
Michael Jurkabed61d22012-02-14 22:51:29 -08002523 // The code in getChangeStateAnimation to determine initialAlpha and finalAlpha will ensure
2524 // ensure that only the current page is visible during (and subsequently, after) the
2525 // transition animation. If fade adjacent pages is disabled, then re-enable the page
2526 // visibility after the transition animation.
Michael Jurka869390b2012-05-06 15:55:19 -07002527 if (!mWorkspaceFadeInAdjacentScreens) {
Michael Jurkabed61d22012-02-14 22:51:29 -08002528 for (int i = 0; i < getChildCount(); i++) {
2529 final CellLayout cl = (CellLayout) getChildAt(i);
Michael Jurkaa52570f2012-03-20 03:18:20 -07002530 cl.setShortcutAndWidgetAlpha(1f);
Michael Jurkabed61d22012-02-14 22:51:29 -08002531 }
Adam Cohen76128b62013-11-14 16:43:59 +00002532 } else {
2533 for (int i = 0; i < numCustomPages(); i++) {
2534 final CellLayout cl = (CellLayout) getChildAt(i);
2535 cl.setShortcutAndWidgetAlpha(1f);
2536 }
Michael Jurkabed61d22012-02-14 22:51:29 -08002537 }
Adam Cohen3f452c82013-09-19 11:57:17 -07002538 showCustomContentIfNecessary();
Michael Jurkabed61d22012-02-14 22:51:29 -08002539 }
2540
2541 @Override
2542 public View getContent() {
2543 return this;
2544 }
2545
Joe Onorato4be866d2010-10-10 11:26:02 -07002546 /**
2547 * Draw the View v into the given Canvas.
2548 *
2549 * @param v the view to draw
2550 * @param destCanvas the canvas to draw on
2551 * @param padding the horizontal and vertical padding to use when drawing
2552 */
Adam Cohenac8c8762011-07-13 11:15:27 -07002553 private void drawDragView(View v, Canvas destCanvas, int padding, boolean pruneToDrawable) {
Joe Onorato4be866d2010-10-10 11:26:02 -07002554 final Rect clipRect = mTempRect;
2555 v.getDrawingRect(clipRect);
2556
Adam Cohen099f60d2011-08-23 21:07:26 -07002557 boolean textVisible = false;
2558
Adam Cohenac8c8762011-07-13 11:15:27 -07002559 destCanvas.save();
2560 if (v instanceof TextView && pruneToDrawable) {
2561 Drawable d = ((TextView) v).getCompoundDrawables()[1];
Mathew Inwood85900292014-04-16 14:17:39 +01002562 Rect bounds = getDrawableBounds(d);
2563 clipRect.set(0, 0, bounds.width() + padding, bounds.height() + padding);
Adam Cohenac8c8762011-07-13 11:15:27 -07002564 destCanvas.translate(padding / 2, padding / 2);
2565 d.draw(destCanvas);
2566 } else {
2567 if (v instanceof FolderIcon) {
Adam Cohen099f60d2011-08-23 21:07:26 -07002568 // For FolderIcons the text can bleed into the icon area, and so we need to
2569 // hide the text completely (which can't be achieved by clipping).
2570 if (((FolderIcon) v).getTextVisible()) {
2571 ((FolderIcon) v).setTextVisible(false);
2572 textVisible = true;
2573 }
Adam Cohenac8c8762011-07-13 11:15:27 -07002574 } else if (v instanceof BubbleTextView) {
2575 final BubbleTextView tv = (BubbleTextView) v;
2576 clipRect.bottom = tv.getExtendedPaddingTop() - (int) BubbleTextView.PADDING_V +
2577 tv.getLayout().getLineTop(0);
2578 } else if (v instanceof TextView) {
2579 final TextView tv = (TextView) v;
2580 clipRect.bottom = tv.getExtendedPaddingTop() - tv.getCompoundDrawablePadding() +
2581 tv.getLayout().getLineTop(0);
2582 }
2583 destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2);
2584 destCanvas.clipRect(clipRect, Op.REPLACE);
2585 v.draw(destCanvas);
Adam Cohen099f60d2011-08-23 21:07:26 -07002586
2587 // Restore text visibility of FolderIcon if necessary
2588 if (textVisible) {
2589 ((FolderIcon) v).setTextVisible(true);
2590 }
Adam Cohenac8c8762011-07-13 11:15:27 -07002591 }
2592 destCanvas.restore();
2593 }
2594
2595 /**
2596 * Returns a new bitmap to show when the given View is being dragged around.
2597 * Responsibility for the bitmap is transferred to the caller.
2598 */
2599 public Bitmap createDragBitmap(View v, Canvas canvas, int padding) {
Adam Cohenac8c8762011-07-13 11:15:27 -07002600 Bitmap b;
2601
2602 if (v instanceof TextView) {
2603 Drawable d = ((TextView) v).getCompoundDrawables()[1];
Mathew Inwood85900292014-04-16 14:17:39 +01002604 Rect bounds = getDrawableBounds(d);
2605 b = Bitmap.createBitmap(bounds.width() + padding,
2606 bounds.height() + padding, Bitmap.Config.ARGB_8888);
Adam Cohenac8c8762011-07-13 11:15:27 -07002607 } else {
2608 b = Bitmap.createBitmap(
2609 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
Joe Onorato4be866d2010-10-10 11:26:02 -07002610 }
2611
Adam Cohenac8c8762011-07-13 11:15:27 -07002612 canvas.setBitmap(b);
2613 drawDragView(v, canvas, padding, true);
Adam Cohenaaf473c2011-08-03 12:02:47 -07002614 canvas.setBitmap(null);
Joe Onorato4be866d2010-10-10 11:26:02 -07002615
Adam Cohenac8c8762011-07-13 11:15:27 -07002616 return b;
Joe Onorato4be866d2010-10-10 11:26:02 -07002617 }
2618
2619 /**
2620 * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
2621 * Responsibility for the bitmap is transferred to the caller.
2622 */
2623 private Bitmap createDragOutline(View v, Canvas canvas, int padding) {
Adam Cohen410f3cd2013-09-22 12:09:32 -07002624 final int outlineColor = getResources().getColor(R.color.outline_color);
Joe Onorato4be866d2010-10-10 11:26:02 -07002625 final Bitmap b = Bitmap.createBitmap(
2626 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
2627
2628 canvas.setBitmap(b);
Winson Chungb8c69f32011-10-19 21:36:08 -07002629 drawDragView(v, canvas, padding, true);
Adam Cohen5bb50bd2010-12-03 11:39:55 -08002630 mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor);
Adam Cohenaaf473c2011-08-03 12:02:47 -07002631 canvas.setBitmap(null);
Joe Onorato4be866d2010-10-10 11:26:02 -07002632 return b;
2633 }
2634
2635 /**
Michael Jurkad3ef3062010-11-23 16:23:58 -08002636 * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
2637 * Responsibility for the bitmap is transferred to the caller.
2638 */
Peter Ng8db70002011-10-25 15:40:08 -07002639 private Bitmap createDragOutline(Bitmap orig, Canvas canvas, int padding, int w, int h,
Michael Jurka8c3339b2012-06-14 16:18:21 -07002640 boolean clipAlpha) {
Adam Cohen410f3cd2013-09-22 12:09:32 -07002641 final int outlineColor = getResources().getColor(R.color.outline_color);
Michael Jurkaf12c75c2011-01-25 22:41:40 -08002642 final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Michael Jurkad3ef3062010-11-23 16:23:58 -08002643 canvas.setBitmap(b);
Michael Jurkaf12c75c2011-01-25 22:41:40 -08002644
2645 Rect src = new Rect(0, 0, orig.getWidth(), orig.getHeight());
2646 float scaleFactor = Math.min((w - padding) / (float) orig.getWidth(),
2647 (h - padding) / (float) orig.getHeight());
2648 int scaledWidth = (int) (scaleFactor * orig.getWidth());
2649 int scaledHeight = (int) (scaleFactor * orig.getHeight());
2650 Rect dst = new Rect(0, 0, scaledWidth, scaledHeight);
2651
2652 // center the image
2653 dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2);
2654
Winson Chung1120e032011-11-22 16:11:31 -08002655 canvas.drawBitmap(orig, src, dst, null);
Peter Ng8db70002011-10-25 15:40:08 -07002656 mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor,
Michael Jurka8c3339b2012-06-14 16:18:21 -07002657 clipAlpha);
Adam Cohenaaf473c2011-08-03 12:02:47 -07002658 canvas.setBitmap(null);
Michael Jurkad3ef3062010-11-23 16:23:58 -08002659
2660 return b;
2661 }
2662
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002663 void startDrag(CellLayout.CellInfo cellInfo) {
2664 View child = cellInfo.cell;
Winson Chungaafa03c2010-06-11 17:34:16 -07002665
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002666 // Make sure the drag was started by a long press as opposed to a long click.
Bjorn Bringert7984c942009-12-09 15:38:25 +00002667 if (!child.isInTouchMode()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002668 return;
2669 }
Winson Chungaafa03c2010-06-11 17:34:16 -07002670
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002671 mDragInfo = cellInfo;
Adam Cohend41fbf52012-02-16 23:53:59 -08002672 child.setVisibility(INVISIBLE);
Adam Cohen482ed822012-03-02 14:15:13 -08002673 CellLayout layout = (CellLayout) child.getParent().getParent();
2674 layout.prepareChildForDrag(child);
Joe Onorato4be866d2010-10-10 11:26:02 -07002675
2676 child.clearFocus();
2677 child.setPressed(false);
2678
2679 final Canvas canvas = new Canvas();
2680
2681 // The outline is used to visualize where the item will land if dropped
Adam Cohend41fbf52012-02-16 23:53:59 -08002682 mDragOutline = createDragOutline(child, canvas, DRAG_BITMAP_PADDING);
Adam Cohenac8c8762011-07-13 11:15:27 -07002683 beginDragShared(child, this);
2684 }
2685
2686 public void beginDragShared(View child, DragSource source) {
Anjali Koppal62d18ed2014-03-10 17:04:03 -07002687 mLauncher.onDragStarted(child);
Joe Onorato4be866d2010-10-10 11:26:02 -07002688 // The drag bitmap follows the touch point around on the screen
Adam Cohend41fbf52012-02-16 23:53:59 -08002689 final Bitmap b = createDragBitmap(child, new Canvas(), DRAG_BITMAP_PADDING);
Joe Onorato4be866d2010-10-10 11:26:02 -07002690
2691 final int bmpWidth = b.getWidth();
Winson Chungeecf02d2012-03-02 17:14:58 -08002692 final int bmpHeight = b.getHeight();
Adam Cohene3e27a82011-04-15 12:07:39 -07002693
Adam Cohen307fe232012-08-16 17:55:58 -07002694 float scale = mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY);
Winson Chungbabb53e2014-04-14 17:12:49 -07002695 int dragLayerX = Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2);
2696 int dragLayerY = Math.round(mTempXY[1] - (bmpHeight - scale * bmpHeight) / 2
Winson Chungeecf02d2012-03-02 17:14:58 -08002697 - DRAG_BITMAP_PADDING / 2);
Adam Cohene3e27a82011-04-15 12:07:39 -07002698
Winson Chung5f8afe62013-08-12 16:19:28 -07002699 LauncherAppState app = LauncherAppState.getInstance();
2700 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
Winson Chungb8c69f32011-10-19 21:36:08 -07002701 Point dragVisualizeOffset = null;
Adam Cohene3e27a82011-04-15 12:07:39 -07002702 Rect dragRect = null;
Winson Chungb8c69f32011-10-19 21:36:08 -07002703 if (child instanceof BubbleTextView || child instanceof PagedViewIcon) {
Winson Chung5f8afe62013-08-12 16:19:28 -07002704 int iconSize = grid.iconSizePx;
Adam Cohene3e27a82011-04-15 12:07:39 -07002705 int top = child.getPaddingTop();
2706 int left = (bmpWidth - iconSize) / 2;
2707 int right = left + iconSize;
2708 int bottom = top + iconSize;
Adam Cohenfc53cd22011-07-20 15:45:11 -07002709 dragLayerY += top;
Winson Chungb8c69f32011-10-19 21:36:08 -07002710 // Note: The drag region is used to calculate drag layer offsets, but the
2711 // dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
Winson Chung5f8afe62013-08-12 16:19:28 -07002712 dragVisualizeOffset = new Point(-DRAG_BITMAP_PADDING / 2, DRAG_BITMAP_PADDING / 2);
Adam Cohene3e27a82011-04-15 12:07:39 -07002713 dragRect = new Rect(left, top, right, bottom);
Adam Cohen0e4857c2011-06-30 17:05:14 -07002714 } else if (child instanceof FolderIcon) {
Winson Chung5f8afe62013-08-12 16:19:28 -07002715 int previewSize = grid.folderIconSizePx;
2716 dragRect = new Rect(0, child.getPaddingTop(), child.getWidth(), previewSize);
Adam Cohene3e27a82011-04-15 12:07:39 -07002717 }
2718
Winson Chung1e9cbfe2011-09-30 16:52:26 -07002719 // Clear the pressed state if necessary
2720 if (child instanceof BubbleTextView) {
2721 BubbleTextView icon = (BubbleTextView) child;
2722 icon.clearPressedOrFocusedBackground();
Winson Chungc6c03672014-03-06 10:12:02 -08002723 } else if (child instanceof FolderIcon) {
2724 // Dismiss the folder cling if we haven't already
2725 mLauncher.getLauncherClings().markFolderClingDismissed();
Winson Chung1e9cbfe2011-09-30 16:52:26 -07002726 }
2727
Adam Cohen2f32ad22013-11-13 11:29:49 +00002728 if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) {
2729 String msg = "Drag started with a view that has no tag set. This "
2730 + "will cause a crash (issue 11627249) down the line. "
2731 + "View: " + child + " tag: " + child.getTag();
2732 throw new IllegalStateException(msg);
2733 }
2734
Winson Chungeeb5bbc2013-11-13 15:47:05 -08002735 DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
Adam Cohen307fe232012-08-16 17:55:58 -07002736 DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale);
Winson Chungeeb5bbc2013-11-13 15:47:05 -08002737 dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
Adam Cohen5084cba2013-09-03 12:01:16 -07002738
2739 if (child.getParent() instanceof ShortcutAndWidgetContainer) {
2740 mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent();
2741 }
2742
Joe Onorato4be866d2010-10-10 11:26:02 -07002743 b.recycle();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002744 }
2745
Winson Chungbabb53e2014-04-14 17:12:49 -07002746 public void beginExternalDragShared(View child, DragSource source) {
2747 LauncherAppState app = LauncherAppState.getInstance();
2748 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
2749 int iconSize = grid.iconSizePx;
2750
2751 // Notify launcher of drag start
2752 mLauncher.onDragStarted(child);
2753
2754 // Compose a new drag bitmap that is of the icon size
2755 final Bitmap tmpB = createDragBitmap(child, new Canvas(), DRAG_BITMAP_PADDING);
2756 Bitmap b = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
2757 Paint p = new Paint();
2758 p.setFilterBitmap(true);
2759 Canvas c = new Canvas(b);
2760 c.drawBitmap(tmpB, new Rect(0, 0, tmpB.getWidth(), tmpB.getHeight()),
2761 new Rect(0, 0, iconSize, iconSize), p);
2762 c.setBitmap(null);
2763
2764 // Find the child's location on the screen
2765 int bmpWidth = tmpB.getWidth();
2766 float iconScale = (float) bmpWidth / iconSize;
2767 float scale = mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY) * iconScale;
2768 int dragLayerX = Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2);
2769 int dragLayerY = Math.round(mTempXY[1]);
2770
2771 // Note: The drag region is used to calculate drag layer offsets, but the
2772 // dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
2773 Point dragVisualizeOffset = new Point(-DRAG_BITMAP_PADDING / 2, DRAG_BITMAP_PADDING / 2);
2774 Rect dragRect = new Rect(0, 0, iconSize, iconSize);
2775
2776 if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) {
2777 String msg = "Drag started with a view that has no tag set. This "
2778 + "will cause a crash (issue 11627249) down the line. "
2779 + "View: " + child + " tag: " + child.getTag();
2780 throw new IllegalStateException(msg);
2781 }
2782
2783 // Start the drag
2784 DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
2785 DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale);
2786 dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
2787
2788 // Recycle temporary bitmaps
2789 tmpB.recycle();
2790 }
2791
Adam Cohendcd297f2013-06-18 13:13:40 -07002792 void addApplicationShortcut(ShortcutInfo info, CellLayout target, long container, long screenId,
Winson Chung3d503fb2011-07-13 17:25:49 -07002793 int cellX, int cellY, boolean insertAtFirst, int intersectX, int intersectY) {
2794 View view = mLauncher.createShortcut(R.layout.application, target, (ShortcutInfo) info);
Michael Jurka0280c3b2010-09-17 15:00:07 -07002795
2796 final int[] cellXY = new int[2];
Winson Chung3d503fb2011-07-13 17:25:49 -07002797 target.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY);
Adam Cohendcd297f2013-06-18 13:13:40 -07002798 addInScreen(view, container, screenId, cellXY[0], cellXY[1], 1, 1, insertAtFirst);
Adam Cohen7c4c5102013-06-14 17:42:35 -07002799
Adam Cohendcd297f2013-06-18 13:13:40 -07002800 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId, cellXY[0],
Winson Chung3d503fb2011-07-13 17:25:49 -07002801 cellXY[1]);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002802 }
2803
Adam Cohen4b285c52011-07-21 14:24:06 -07002804 public boolean transitionStateShouldAllowDrop() {
Adam Cohen1b36dc32012-02-13 19:27:37 -08002805 return ((!isSwitchingState() || mTransitionProgress > 0.5f) && mState != State.SMALL);
Adam Cohen4b285c52011-07-21 14:24:06 -07002806 }
2807
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002808 /**
2809 * {@inheritDoc}
2810 */
Adam Cohencb3382b2011-05-24 14:07:08 -07002811 public boolean acceptDrop(DragObject d) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002812 // If it's an external drop (e.g. from All Apps), check if it should be accepted
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002813 CellLayout dropTargetLayout = mDropToLayout;
Adam Cohencb3382b2011-05-24 14:07:08 -07002814 if (d.dragSource != this) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002815 // Don't accept the drop if we're not over a screen at time of drop
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002816 if (dropTargetLayout == null) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002817 return false;
2818 }
Adam Cohen4b285c52011-07-21 14:24:06 -07002819 if (!transitionStateShouldAllowDrop()) return false;
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002820
Adam Cohena65beee2011-06-27 21:32:23 -07002821 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset,
2822 d.dragView, mDragViewVisualCenter);
2823
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07002824 // We want the point to be mapped to the dragTarget.
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002825 if (mLauncher.isHotseatLayout(dropTargetLayout)) {
Adam Cohen307fe232012-08-16 17:55:58 -07002826 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07002827 } else {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002828 mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null);
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07002829 }
2830
Winson Chung557d6ed2011-07-08 15:34:52 -07002831 int spanX = 1;
2832 int spanY = 1;
Winson Chung557d6ed2011-07-08 15:34:52 -07002833 if (mDragInfo != null) {
2834 final CellLayout.CellInfo dragCellInfo = mDragInfo;
2835 spanX = dragCellInfo.spanX;
2836 spanY = dragCellInfo.spanY;
Winson Chung557d6ed2011-07-08 15:34:52 -07002837 } else {
2838 final ItemInfo dragInfo = (ItemInfo) d.dragInfo;
2839 spanX = dragInfo.spanX;
2840 spanY = dragInfo.spanY;
2841 }
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002842
Adam Cohend41fbf52012-02-16 23:53:59 -08002843 int minSpanX = spanX;
2844 int minSpanY = spanY;
2845 if (d.dragInfo instanceof PendingAddWidgetInfo) {
2846 minSpanX = ((PendingAddWidgetInfo) d.dragInfo).minSpanX;
2847 minSpanY = ((PendingAddWidgetInfo) d.dragInfo).minSpanY;
2848 }
Adam Cohen482ed822012-03-02 14:15:13 -08002849
Adam Cohena65beee2011-06-27 21:32:23 -07002850 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002851 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, dropTargetLayout,
Adam Cohenf0777b92012-02-28 14:02:45 -08002852 mTargetCell);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002853 float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08002854 mDragViewVisualCenter[1], mTargetCell);
Adam Cohenec0d61d2014-02-07 16:34:51 -08002855 if (mCreateUserFolderOnDrop && willCreateUserFolder((ItemInfo) d.dragInfo,
2856 dropTargetLayout, mTargetCell, distance, true)) {
Adam Cohena65beee2011-06-27 21:32:23 -07002857 return true;
2858 }
Adam Cohenec0d61d2014-02-07 16:34:51 -08002859
2860 if (mAddToExistingFolderOnDrop && willAddToExistingUserFolder((ItemInfo) d.dragInfo,
2861 dropTargetLayout, mTargetCell, distance)) {
Adam Cohena65beee2011-06-27 21:32:23 -07002862 return true;
2863 }
2864
Adam Cohen482ed822012-03-02 14:15:13 -08002865 int[] resultSpan = new int[2];
Adam Cohenfa3c58f2013-12-06 16:10:55 -08002866 mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08002867 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
2868 null, mTargetCell, resultSpan, CellLayout.MODE_ACCEPT_DROP);
2869 boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
2870
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002871 // Don't accept the drop if there's no room for the item
Adam Cohen482ed822012-03-02 14:15:13 -08002872 if (!foundCell) {
Winson Chung96ef4092011-11-22 12:25:14 -08002873 // Don't show the message if we are dropping on the AllApps button and the hotseat
2874 // is full
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002875 boolean isHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
Winson Chung93eef082012-03-23 15:59:27 -07002876 if (mTargetCell != null && isHotseat) {
Winson Chung96ef4092011-11-22 12:25:14 -08002877 Hotseat hotseat = mLauncher.getHotseat();
Andrew Flynn0dca1ec2012-02-29 13:33:22 -08002878 if (hotseat.isAllAppsButtonRank(
Winson Chung96ef4092011-11-22 12:25:14 -08002879 hotseat.getOrderInHotseat(mTargetCell[0], mTargetCell[1]))) {
2880 return false;
2881 }
2882 }
2883
Winson Chung93eef082012-03-23 15:59:27 -07002884 mLauncher.showOutOfSpaceMessage(isHotseat);
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002885 return false;
2886 }
2887 }
Adam Cohendedbd962013-07-11 14:21:49 -07002888
2889 long screenId = getIdForScreen(dropTargetLayout);
2890 if (screenId == EXTRA_EMPTY_SCREEN_ID) {
2891 commitExtraEmptyScreen();
2892 }
2893
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002894 return true;
2895 }
2896
Adam Cohen482ed822012-03-02 14:15:13 -08002897 boolean willCreateUserFolder(ItemInfo info, CellLayout target, int[] targetCell, float
2898 distance, boolean considerTimeout) {
2899 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohena65beee2011-06-27 21:32:23 -07002900 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
2901
Adam Cohen19f37922012-03-21 11:59:11 -07002902 if (dropOverView != null) {
2903 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
2904 if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) {
2905 return false;
2906 }
2907 }
2908
Winson Chung3d503fb2011-07-13 17:25:49 -07002909 boolean hasntMoved = false;
2910 if (mDragInfo != null) {
Adam Cohen482ed822012-03-02 14:15:13 -08002911 hasntMoved = dropOverView == mDragInfo.cell;
Winson Chung3d503fb2011-07-13 17:25:49 -07002912 }
Adam Cohene0310962011-04-18 16:15:31 -07002913
Adam Cohena65beee2011-06-27 21:32:23 -07002914 if (dropOverView == null || hasntMoved || (considerTimeout && !mCreateUserFolderOnDrop)) {
2915 return false;
2916 }
Adam Cohene0310962011-04-18 16:15:31 -07002917
Adam Cohena65beee2011-06-27 21:32:23 -07002918 boolean aboveShortcut = (dropOverView.getTag() instanceof ShortcutInfo);
Adam Cohene0310962011-04-18 16:15:31 -07002919 boolean willBecomeShortcut =
Adam Cohenc0dcf592011-06-01 15:30:43 -07002920 (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
2921 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT);
Adam Cohene0310962011-04-18 16:15:31 -07002922
2923 return (aboveShortcut && willBecomeShortcut);
2924 }
2925
Adam Cohen482ed822012-03-02 14:15:13 -08002926 boolean willAddToExistingUserFolder(Object dragInfo, CellLayout target, int[] targetCell,
2927 float distance) {
2928 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohena65beee2011-06-27 21:32:23 -07002929 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohen19f37922012-03-21 11:59:11 -07002930
2931 if (dropOverView != null) {
2932 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
2933 if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) {
2934 return false;
2935 }
2936 }
2937
Adam Cohena65beee2011-06-27 21:32:23 -07002938 if (dropOverView instanceof FolderIcon) {
2939 FolderIcon fi = (FolderIcon) dropOverView;
2940 if (fi.acceptDrop(dragInfo)) {
2941 return true;
2942 }
2943 }
2944 return false;
2945 }
2946
Winson Chung3d503fb2011-07-13 17:25:49 -07002947 boolean createUserFolderIfNecessary(View newView, long container, CellLayout target,
Adam Cohen482ed822012-03-02 14:15:13 -08002948 int[] targetCell, float distance, boolean external, DragView dragView,
2949 Runnable postAnimationRunnable) {
2950 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohenc0dcf592011-06-01 15:30:43 -07002951 View v = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohen19f37922012-03-21 11:59:11 -07002952
Winson Chungec9a0a42011-07-20 20:42:13 -07002953 boolean hasntMoved = false;
2954 if (mDragInfo != null) {
2955 CellLayout cellParent = getParentCellLayoutForView(mDragInfo.cell);
2956 hasntMoved = (mDragInfo.cellX == targetCell[0] &&
2957 mDragInfo.cellY == targetCell[1]) && (cellParent == target);
2958 }
Adam Cohen10b17372011-04-15 14:21:25 -07002959
Adam Cohen19072da2011-05-31 14:30:45 -07002960 if (v == null || hasntMoved || !mCreateUserFolderOnDrop) return false;
2961 mCreateUserFolderOnDrop = false;
Adam Cohendcd297f2013-06-18 13:13:40 -07002962 final long screenId = (targetCell == null) ? mDragInfo.screenId : getIdForScreen(target);
Adam Cohen10b17372011-04-15 14:21:25 -07002963
2964 boolean aboveShortcut = (v.getTag() instanceof ShortcutInfo);
2965 boolean willBecomeShortcut = (newView.getTag() instanceof ShortcutInfo);
2966
2967 if (aboveShortcut && willBecomeShortcut) {
2968 ShortcutInfo sourceInfo = (ShortcutInfo) newView.getTag();
2969 ShortcutInfo destInfo = (ShortcutInfo) v.getTag();
2970 // if the drag started here, we need to remove it from the workspace
2971 if (!external) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002972 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
Adam Cohen10b17372011-04-15 14:21:25 -07002973 }
2974
Adam Cohend0445262011-07-04 23:53:22 -07002975 Rect folderLocation = new Rect();
Adam Cohenac8c8762011-07-13 11:15:27 -07002976 float scale = mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation);
Adam Cohen10b17372011-04-15 14:21:25 -07002977 target.removeView(v);
Adam Cohend0445262011-07-04 23:53:22 -07002978
Winson Chung3d503fb2011-07-13 17:25:49 -07002979 FolderIcon fi =
Adam Cohendcd297f2013-06-18 13:13:40 -07002980 mLauncher.addFolder(target, container, screenId, targetCell[0], targetCell[1]);
Adam Cohena9cf38f2011-05-02 15:36:58 -07002981 destInfo.cellX = -1;
2982 destInfo.cellY = -1;
2983 sourceInfo.cellX = -1;
2984 sourceInfo.cellY = -1;
Adam Cohend0445262011-07-04 23:53:22 -07002985
Adam Cohen558baaf2011-08-15 15:22:57 -07002986 // If the dragView is null, we can't animate
2987 boolean animate = dragView != null;
2988 if (animate) {
2989 fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale,
2990 postAnimationRunnable);
2991 } else {
2992 fi.addItem(destInfo);
2993 fi.addItem(sourceInfo);
2994 }
Adam Cohen10b17372011-04-15 14:21:25 -07002995 return true;
2996 }
2997 return false;
2998 }
2999
Adam Cohena65beee2011-06-27 21:32:23 -07003000 boolean addToExistingFolderIfNecessary(View newView, CellLayout target, int[] targetCell,
Adam Cohen482ed822012-03-02 14:15:13 -08003001 float distance, DragObject d, boolean external) {
3002 if (distance > mMaxDistanceForFolderCreation) return false;
3003
Adam Cohena65beee2011-06-27 21:32:23 -07003004 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003005 if (!mAddToExistingFolderOnDrop) return false;
3006 mAddToExistingFolderOnDrop = false;
Adam Cohen19f37922012-03-21 11:59:11 -07003007
Adam Cohenc0dcf592011-06-01 15:30:43 -07003008 if (dropOverView instanceof FolderIcon) {
3009 FolderIcon fi = (FolderIcon) dropOverView;
Adam Cohen3e8f8112011-07-02 18:03:00 -07003010 if (fi.acceptDrop(d.dragInfo)) {
3011 fi.onDrop(d);
Adam Cohenc0dcf592011-06-01 15:30:43 -07003012
3013 // if the drag started here, we need to remove it from the workspace
3014 if (!external) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003015 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
Adam Cohenc0dcf592011-06-01 15:30:43 -07003016 }
3017 return true;
3018 }
3019 }
3020 return false;
3021 }
3022
Adam Cohend41fbf52012-02-16 23:53:59 -08003023 public void onDrop(final DragObject d) {
Adam Cohencb3382b2011-05-24 14:07:08 -07003024 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, d.dragView,
Adam Cohene3e27a82011-04-15 12:07:39 -07003025 mDragViewVisualCenter);
3026
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003027 CellLayout dropTargetLayout = mDropToLayout;
3028
Adam Cohene3e27a82011-04-15 12:07:39 -07003029 // We want the point to be mapped to the dragTarget.
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003030 if (dropTargetLayout != null) {
3031 if (mLauncher.isHotseatLayout(dropTargetLayout)) {
Adam Cohenbb00ff22012-07-12 15:43:01 -07003032 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
Winson Chung3d503fb2011-07-13 17:25:49 -07003033 } else {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003034 mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null);
Winson Chung3d503fb2011-07-13 17:25:49 -07003035 }
Adam Cohenba781612011-05-09 14:37:39 -07003036 }
Michael Jurkac6ee42e2010-09-30 12:04:50 -07003037
Adam Cohened51cc92011-08-01 20:28:08 -07003038 int snapScreen = -1;
Adam Cohend41fbf52012-02-16 23:53:59 -08003039 boolean resizeOnDrop = false;
Adam Cohencb3382b2011-05-24 14:07:08 -07003040 if (d.dragSource != this) {
Adam Cohene3e27a82011-04-15 12:07:39 -07003041 final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0],
3042 (int) mDragViewVisualCenter[1] };
Adam Cohen3e8f8112011-07-02 18:03:00 -07003043 onDropExternal(touchXY, d.dragInfo, dropTargetLayout, false, d);
Patrick Dubroyce34a972010-10-19 10:34:32 -07003044 } else if (mDragInfo != null) {
Patrick Dubroyce34a972010-10-19 10:34:32 -07003045 final View cell = mDragInfo.cell;
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08003046
Adam Cohend41fbf52012-02-16 23:53:59 -08003047 Runnable resizeRunnable = null;
Michael Jurka1e2f4652013-07-08 18:03:46 -07003048 if (dropTargetLayout != null && !d.cancelled) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003049 // Move internally
Winson Chung40e882b2011-07-21 19:01:11 -07003050 boolean hasMovedLayouts = (getParentCellLayoutForView(cell) != dropTargetLayout);
3051 boolean hasMovedIntoHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
3052 long container = hasMovedIntoHotseat ?
Winson Chung3d503fb2011-07-13 17:25:49 -07003053 LauncherSettings.Favorites.CONTAINER_HOTSEAT :
3054 LauncherSettings.Favorites.CONTAINER_DESKTOP;
Adam Cohendcd297f2013-06-18 13:13:40 -07003055 long screenId = (mTargetCell[0] < 0) ?
3056 mDragInfo.screenId : getIdForScreen(dropTargetLayout);
Adam Cohenc0dcf592011-06-01 15:30:43 -07003057 int spanX = mDragInfo != null ? mDragInfo.spanX : 1;
3058 int spanY = mDragInfo != null ? mDragInfo.spanY : 1;
3059 // First we find the cell nearest to point at which the item is
3060 // dropped, without any consideration to whether there is an item there.
Adam Cohen482ed822012-03-02 14:15:13 -08003061
Adam Cohenc0dcf592011-06-01 15:30:43 -07003062 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], (int)
3063 mDragViewVisualCenter[1], spanX, spanY, dropTargetLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08003064 float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
3065 mDragViewVisualCenter[1], mTargetCell);
3066
Adam Cohenc0dcf592011-06-01 15:30:43 -07003067 // If the item being dropped is a shortcut and the nearest drop
Adam Cohen76078c42011-06-09 15:06:52 -07003068 // cell also contains a shortcut, then create a folder with the two shortcuts.
Winson Chung1c4cf4a2011-07-29 14:49:10 -07003069 if (!mInScrollArea && createUserFolderIfNecessary(cell, container,
Adam Cohen482ed822012-03-02 14:15:13 -08003070 dropTargetLayout, mTargetCell, distance, false, d.dragView, null)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07003071 return;
3072 }
3073
Adam Cohen482ed822012-03-02 14:15:13 -08003074 if (addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell,
3075 distance, d, false)) {
Adam Cohendf035382011-04-11 17:22:04 -07003076 return;
3077 }
3078
3079 // Aside from the special case where we're dropping a shortcut onto a shortcut,
3080 // we need to find the nearest cell location that is vacant
Adam Cohend41fbf52012-02-16 23:53:59 -08003081 ItemInfo item = (ItemInfo) d.dragInfo;
3082 int minSpanX = item.spanX;
3083 int minSpanY = item.spanY;
3084 if (item.minSpanX > 0 && item.minSpanY > 0) {
3085 minSpanX = item.minSpanX;
3086 minSpanY = item.minSpanY;
3087 }
Adam Cohen482ed822012-03-02 14:15:13 -08003088
Adam Cohend41fbf52012-02-16 23:53:59 -08003089 int[] resultSpan = new int[2];
Adam Cohenfa3c58f2013-12-06 16:10:55 -08003090 mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08003091 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, cell,
3092 mTargetCell, resultSpan, CellLayout.MODE_ON_DROP);
3093
Adam Cohend41fbf52012-02-16 23:53:59 -08003094 boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
Adam Cohen9e05a5e2012-09-10 15:53:09 -07003095
Adam Cohenaaa5c212012-10-05 18:14:31 -07003096 // if the widget resizes on drop
Adam Cohen9e05a5e2012-09-10 15:53:09 -07003097 if (foundCell && (cell instanceof AppWidgetHostView) &&
Adam Cohenaaa5c212012-10-05 18:14:31 -07003098 (resultSpan[0] != item.spanX || resultSpan[1] != item.spanY)) {
Adam Cohend41fbf52012-02-16 23:53:59 -08003099 resizeOnDrop = true;
3100 item.spanX = resultSpan[0];
3101 item.spanY = resultSpan[1];
Adam Cohen7bdfc972012-05-22 16:50:35 -07003102 AppWidgetHostView awhv = (AppWidgetHostView) cell;
3103 AppWidgetResizeFrame.updateWidgetSizeRanges(awhv, mLauncher, resultSpan[0],
3104 resultSpan[1]);
Adam Cohend41fbf52012-02-16 23:53:59 -08003105 }
Adam Cohendf035382011-04-11 17:22:04 -07003106
Adam Cohendcd297f2013-06-18 13:13:40 -07003107 if (getScreenIdForPageIndex(mCurrentPage) != screenId && !hasMovedIntoHotseat) {
3108 snapScreen = getPageIndexForScreenId(screenId);
3109 snapToPage(snapScreen);
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08003110 }
3111
Adam Cohend41fbf52012-02-16 23:53:59 -08003112 if (foundCell) {
3113 final ItemInfo info = (ItemInfo) cell.getTag();
Winson Chung40e882b2011-07-21 19:01:11 -07003114 if (hasMovedLayouts) {
Patrick Dubroy94383362010-10-29 15:03:24 -07003115 // Reparent the view
Winson Chung3d503fb2011-07-13 17:25:49 -07003116 getParentCellLayoutForView(cell).removeView(cell);
Adam Cohendcd297f2013-06-18 13:13:40 -07003117 addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1],
Adam Cohend41fbf52012-02-16 23:53:59 -08003118 info.spanX, info.spanY);
Patrick Dubroy94383362010-10-29 15:03:24 -07003119 }
3120
3121 // update the item's position after drop
Patrick Dubroy94383362010-10-29 15:03:24 -07003122 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
Adam Cohen19f37922012-03-21 11:59:11 -07003123 lp.cellX = lp.tmpCellX = mTargetCell[0];
3124 lp.cellY = lp.tmpCellY = mTargetCell[1];
Adam Cohend41fbf52012-02-16 23:53:59 -08003125 lp.cellHSpan = item.spanX;
3126 lp.cellVSpan = item.spanY;
Adam Cohen19f37922012-03-21 11:59:11 -07003127 lp.isLockedToGrid = true;
Patrick Dubroy94383362010-10-29 15:03:24 -07003128
Winson Chung3d503fb2011-07-13 17:25:49 -07003129 if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
3130 cell instanceof LauncherAppWidgetHostView) {
Adam Cohend4844c32011-02-18 19:25:06 -08003131 final CellLayout cellLayout = dropTargetLayout;
3132 // We post this call so that the widget has a chance to be placed
3133 // in its final location
3134
3135 final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell;
3136 AppWidgetProviderInfo pinfo = hostView.getAppWidgetInfo();
Adam Cohenc0a5df92012-04-02 16:53:26 -07003137 if (pinfo != null &&
3138 pinfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) {
Adam Cohend41fbf52012-02-16 23:53:59 -08003139 final Runnable addResizeFrame = new Runnable() {
Adam Cohend4844c32011-02-18 19:25:06 -08003140 public void run() {
Adam Cohen716b51e2011-06-30 12:09:54 -07003141 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohenc0dcf592011-06-01 15:30:43 -07003142 dragLayer.addResizeFrame(info, hostView, cellLayout);
Adam Cohend4844c32011-02-18 19:25:06 -08003143 }
Adam Cohen26976d92011-03-22 15:33:33 -07003144 };
Adam Cohend41fbf52012-02-16 23:53:59 -08003145 resizeRunnable = (new Runnable() {
Adam Cohen26976d92011-03-22 15:33:33 -07003146 public void run() {
3147 if (!isPageMoving()) {
Adam Cohend41fbf52012-02-16 23:53:59 -08003148 addResizeFrame.run();
Adam Cohen26976d92011-03-22 15:33:33 -07003149 } else {
Adam Cohend41fbf52012-02-16 23:53:59 -08003150 mDelayedResizeRunnable = addResizeFrame;
Adam Cohen26976d92011-03-22 15:33:33 -07003151 }
3152 }
Adam Cohend4844c32011-02-18 19:25:06 -08003153 });
3154 }
3155 }
3156
Adam Cohen949debe2013-09-29 14:22:48 -07003157 LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId, lp.cellX,
3158 lp.cellY, item.spanX, item.spanY);
Adam Cohend41fbf52012-02-16 23:53:59 -08003159 } else {
3160 // If we can't find a drop location, we return the item to its original position
3161 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
3162 mTargetCell[0] = lp.cellX;
3163 mTargetCell[1] = lp.cellY;
Adam Cohenf1dcdf62012-05-10 16:51:52 -07003164 CellLayout layout = (CellLayout) cell.getParent().getParent();
3165 layout.markCellsAsOccupiedForView(cell);
Patrick Dubroycd68ff52010-10-28 17:57:05 -07003166 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003167 }
Patrick Dubroyce34a972010-10-19 10:34:32 -07003168
Michael Jurka8c920dd2011-01-20 14:16:56 -08003169 final CellLayout parent = (CellLayout) cell.getParent().getParent();
Adam Cohend41fbf52012-02-16 23:53:59 -08003170 final Runnable finalResizeRunnable = resizeRunnable;
Patrick Dubroyce34a972010-10-19 10:34:32 -07003171 // Prepare it to be animated into its new position
3172 // This must be called after the view has been re-parented
Adam Cohend41fbf52012-02-16 23:53:59 -08003173 final Runnable onCompleteRunnable = new Runnable() {
Michael Jurkad74c9842011-07-10 12:44:21 -07003174 @Override
3175 public void run() {
3176 mAnimatingViewIntoPlace = false;
Michael Jurka3a0469d2012-06-21 09:38:41 -07003177 updateChildrenLayersEnabled(false);
Adam Cohend41fbf52012-02-16 23:53:59 -08003178 if (finalResizeRunnable != null) {
3179 finalResizeRunnable.run();
3180 }
Michael Jurkad74c9842011-07-10 12:44:21 -07003181 }
3182 };
3183 mAnimatingViewIntoPlace = true;
Adam Cohenfc53cd22011-07-20 15:45:11 -07003184 if (d.dragView.hasDrawn()) {
Adam Cohend41fbf52012-02-16 23:53:59 -08003185 final ItemInfo info = (ItemInfo) cell.getTag();
3186 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET) {
3187 int animationType = resizeOnDrop ? ANIMATE_INTO_POSITION_AND_RESIZE :
3188 ANIMATE_INTO_POSITION_AND_DISAPPEAR;
3189 animateWidgetDrop(info, parent, d.dragView,
3190 onCompleteRunnable, animationType, cell, false);
3191 } else {
Adam Cohen85b467b2012-02-29 15:38:46 -08003192 int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION;
3193 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration,
3194 onCompleteRunnable, this);
Adam Cohend41fbf52012-02-16 23:53:59 -08003195 }
Adam Cohenfc53cd22011-07-20 15:45:11 -07003196 } else {
Winson Chung7bd1bbb2012-02-13 18:29:29 -08003197 d.deferDragViewCleanupPostAnimation = false;
Adam Cohenfc53cd22011-07-20 15:45:11 -07003198 cell.setVisibility(VISIBLE);
3199 }
Adam Cohen716b51e2011-06-30 12:09:54 -07003200 parent.onDropChild(cell);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003201 }
3202 }
3203
Adam Cohendcd297f2013-06-18 13:13:40 -07003204 public void setFinalScrollForPageChange(int pageIndex) {
3205 CellLayout cl = (CellLayout) getChildAt(pageIndex);
3206 if (cl != null) {
Adam Cohened51cc92011-08-01 20:28:08 -07003207 mSavedScrollX = getScrollX();
Adam Cohened51cc92011-08-01 20:28:08 -07003208 mSavedTranslationX = cl.getTranslationX();
3209 mSavedRotationY = cl.getRotationY();
Adam Cohenedb40762013-07-18 16:45:45 -07003210 final int newX = getScrollForPage(pageIndex);
Adam Cohened51cc92011-08-01 20:28:08 -07003211 setScrollX(newX);
3212 cl.setTranslationX(0f);
3213 cl.setRotationY(0f);
3214 }
3215 }
3216
Adam Cohendcd297f2013-06-18 13:13:40 -07003217 public void resetFinalScrollForPageChange(int pageIndex) {
3218 if (pageIndex >= 0) {
3219 CellLayout cl = (CellLayout) getChildAt(pageIndex);
Adam Cohened51cc92011-08-01 20:28:08 -07003220 setScrollX(mSavedScrollX);
3221 cl.setTranslationX(mSavedTranslationX);
3222 cl.setRotationY(mSavedRotationY);
3223 }
3224 }
3225
Adam Cohen76078c42011-06-09 15:06:52 -07003226 public void getViewLocationRelativeToSelf(View v, int[] location) {
Adam Cohen8dfcba42011-07-07 16:38:18 -07003227 getLocationInWindow(location);
Adam Cohene3e27a82011-04-15 12:07:39 -07003228 int x = location[0];
3229 int y = location[1];
3230
Adam Cohen8dfcba42011-07-07 16:38:18 -07003231 v.getLocationInWindow(location);
Adam Cohene3e27a82011-04-15 12:07:39 -07003232 int vX = location[0];
3233 int vY = location[1];
3234
3235 location[0] = vX - x;
3236 location[1] = vY - y;
3237 }
3238
Adam Cohencb3382b2011-05-24 14:07:08 -07003239 public void onDragEnter(DragObject d) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003240 mDragEnforcer.onDragEnter();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003241 mCreateUserFolderOnDrop = false;
3242 mAddToExistingFolderOnDrop = false;
3243
3244 mDropToLayout = null;
3245 CellLayout layout = getCurrentDropLayout();
3246 setCurrentDropLayout(layout);
3247 setCurrentDragOverlappingLayout(layout);
Winson Chungb26f3d62011-06-02 10:49:29 -07003248
Winson Chungc07918d2011-07-01 15:35:26 -07003249 // Because we don't have space in the Phone UI (the CellLayouts run to the edge) we
3250 // don't need to show the outlines
Daniel Sandlere4f98912013-06-25 15:13:26 -04003251 if (LauncherAppState.getInstance().isScreenLarge()) {
Winson Chungc07918d2011-07-01 15:35:26 -07003252 showOutlines();
Michael Jurkad718d6a2010-10-14 15:35:17 -07003253 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003254 }
3255
Winson Chungfe411c82013-09-26 16:07:17 -07003256 /** Return a rect that has the cellWidth/cellHeight (left, top), and
3257 * widthGap/heightGap (right, bottom) */
Adam Cohena897f392012-04-27 18:12:05 -07003258 static Rect getCellLayoutMetrics(Launcher launcher, int orientation) {
Winson Chung5f8afe62013-08-12 16:19:28 -07003259 LauncherAppState app = LauncherAppState.getInstance();
3260 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
3261
Adam Cohena897f392012-04-27 18:12:05 -07003262 Resources res = launcher.getResources();
3263 Display display = launcher.getWindowManager().getDefaultDisplay();
3264 Point smallestSize = new Point();
3265 Point largestSize = new Point();
3266 display.getCurrentSizeRange(smallestSize, largestSize);
Winson Chung892c74d2013-08-22 16:15:50 -07003267 int countX = (int) grid.numColumns;
3268 int countY = (int) grid.numRows;
Adam Cohena897f392012-04-27 18:12:05 -07003269 if (orientation == CellLayout.LANDSCAPE) {
3270 if (mLandscapeCellLayoutMetrics == null) {
Winson Chung5f8afe62013-08-12 16:19:28 -07003271 Rect padding = grid.getWorkspacePadding(CellLayout.LANDSCAPE);
Winson Chungdcd27ba2013-12-11 15:28:15 -08003272 int width = largestSize.x - padding.left - padding.right;
3273 int height = smallestSize.y - padding.top - padding.bottom;
Adam Cohena897f392012-04-27 18:12:05 -07003274 mLandscapeCellLayoutMetrics = new Rect();
Winson Chungfe411c82013-09-26 16:07:17 -07003275 mLandscapeCellLayoutMetrics.set(
3276 grid.calculateCellWidth(width, countX),
3277 grid.calculateCellHeight(height, countY), 0, 0);
Adam Cohena897f392012-04-27 18:12:05 -07003278 }
3279 return mLandscapeCellLayoutMetrics;
3280 } else if (orientation == CellLayout.PORTRAIT) {
3281 if (mPortraitCellLayoutMetrics == null) {
Winson Chung5f8afe62013-08-12 16:19:28 -07003282 Rect padding = grid.getWorkspacePadding(CellLayout.PORTRAIT);
Winson Chungdcd27ba2013-12-11 15:28:15 -08003283 int width = smallestSize.x - padding.left - padding.right;
3284 int height = largestSize.y - padding.top - padding.bottom;
Adam Cohena897f392012-04-27 18:12:05 -07003285 mPortraitCellLayoutMetrics = new Rect();
Winson Chungfe411c82013-09-26 16:07:17 -07003286 mPortraitCellLayoutMetrics.set(
3287 grid.calculateCellWidth(width, countX),
3288 grid.calculateCellHeight(height, countY), 0, 0);
Adam Cohena897f392012-04-27 18:12:05 -07003289 }
3290 return mPortraitCellLayoutMetrics;
3291 }
3292 return null;
3293 }
3294
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003295 public void onDragExit(DragObject d) {
3296 mDragEnforcer.onDragExit();
Winson Chungc07918d2011-07-01 15:35:26 -07003297
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003298 // Here we store the final page that will be dropped to, if the workspace in fact
3299 // receives the drop
3300 if (mInScrollArea) {
Winson Chungcc1cfe42012-06-18 15:09:17 -07003301 if (isPageMoving()) {
3302 // If the user drops while the page is scrolling, we should use that page as the
3303 // destination instead of the page that is being hovered over.
3304 mDropToLayout = (CellLayout) getPageAt(getNextPage());
3305 } else {
3306 mDropToLayout = mDragOverlappingLayout;
3307 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003308 } else {
3309 mDropToLayout = mDragTargetLayout;
3310 }
3311
3312 if (mDragMode == DRAG_MODE_CREATE_FOLDER) {
3313 mCreateUserFolderOnDrop = true;
3314 } else if (mDragMode == DRAG_MODE_ADD_TO_FOLDER) {
3315 mAddToExistingFolderOnDrop = true;
Adam Cohen482ed822012-03-02 14:15:13 -08003316 }
3317
Winson Chungc07918d2011-07-01 15:35:26 -07003318 // Reset the scroll area and previous drag target
3319 onResetScrollArea();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003320 setCurrentDropLayout(null);
3321 setCurrentDragOverlappingLayout(null);
Winson Chungc07918d2011-07-01 15:35:26 -07003322
Adam Cohen74c28d12011-11-18 14:17:11 -08003323 mSpringLoadedDragController.cancel();
Winson Chungc07918d2011-07-01 15:35:26 -07003324
3325 if (!mIsPageMoving) {
3326 hideOutlines();
3327 }
3328 }
3329
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003330 void setCurrentDropLayout(CellLayout layout) {
3331 if (mDragTargetLayout != null) {
3332 mDragTargetLayout.revertTempState();
3333 mDragTargetLayout.onDragExit();
3334 }
3335 mDragTargetLayout = layout;
3336 if (mDragTargetLayout != null) {
3337 mDragTargetLayout.onDragEnter();
3338 }
3339 cleanupReorder(true);
3340 cleanupFolderCreation();
3341 setCurrentDropOverCell(-1, -1);
3342 }
3343
3344 void setCurrentDragOverlappingLayout(CellLayout layout) {
3345 if (mDragOverlappingLayout != null) {
3346 mDragOverlappingLayout.setIsDragOverlapping(false);
3347 }
3348 mDragOverlappingLayout = layout;
3349 if (mDragOverlappingLayout != null) {
3350 mDragOverlappingLayout.setIsDragOverlapping(true);
3351 }
3352 invalidate();
3353 }
3354
3355 void setCurrentDropOverCell(int x, int y) {
3356 if (x != mDragOverX || y != mDragOverY) {
3357 mDragOverX = x;
3358 mDragOverY = y;
3359 setDragMode(DRAG_MODE_NONE);
3360 }
3361 }
3362
3363 void setDragMode(int dragMode) {
3364 if (dragMode != mDragMode) {
3365 if (dragMode == DRAG_MODE_NONE) {
3366 cleanupAddToFolder();
3367 // We don't want to cancel the re-order alarm every time the target cell changes
3368 // as this feels to slow / unresponsive.
3369 cleanupReorder(false);
3370 cleanupFolderCreation();
3371 } else if (dragMode == DRAG_MODE_ADD_TO_FOLDER) {
3372 cleanupReorder(true);
3373 cleanupFolderCreation();
3374 } else if (dragMode == DRAG_MODE_CREATE_FOLDER) {
3375 cleanupAddToFolder();
3376 cleanupReorder(true);
3377 } else if (dragMode == DRAG_MODE_REORDER) {
3378 cleanupAddToFolder();
3379 cleanupFolderCreation();
3380 }
3381 mDragMode = dragMode;
3382 }
3383 }
3384
3385 private void cleanupFolderCreation() {
3386 if (mDragFolderRingAnimator != null) {
3387 mDragFolderRingAnimator.animateToNaturalState();
Adam Cohen8ec05f92013-10-13 12:29:03 -07003388 mDragFolderRingAnimator = null;
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003389 }
Adam Cohen8ec05f92013-10-13 12:29:03 -07003390 mFolderCreationAlarm.setOnAlarmListener(null);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003391 mFolderCreationAlarm.cancelAlarm();
3392 }
3393
3394 private void cleanupAddToFolder() {
3395 if (mDragOverFolderIcon != null) {
3396 mDragOverFolderIcon.onDragExit(null);
3397 mDragOverFolderIcon = null;
3398 }
3399 }
3400
3401 private void cleanupReorder(boolean cancelAlarm) {
3402 // Any pending reorders are canceled
3403 if (cancelAlarm) {
3404 mReorderAlarm.cancelAlarm();
3405 }
3406 mLastReorderX = -1;
3407 mLastReorderY = -1;
Winson Chungc07918d2011-07-01 15:35:26 -07003408 }
3409
Michael Jurka4516c112010-10-07 15:13:47 -07003410 /*
3411 *
3412 * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
3413 * coordinate space. The argument xy is modified with the return result.
3414 *
3415 * if cachedInverseMatrix is not null, this method will just use that matrix instead of
Michael Jurkad718d6a2010-10-14 15:35:17 -07003416 * computing it itself; we use this to avoid redundant matrix inversions in
Michael Jurka4516c112010-10-07 15:13:47 -07003417 * findMatchingPageForDragOver
3418 *
3419 */
3420 void mapPointFromSelfToChild(View v, float[] xy, Matrix cachedInverseMatrix) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003421 xy[0] = xy[0] - v.getLeft();
3422 xy[1] = xy[1] - v.getTop();
Michael Jurka4516c112010-10-07 15:13:47 -07003423 }
3424
Adam Cohen7d30a372013-07-01 17:03:59 -07003425 boolean isPointInSelfOverHotseat(int x, int y, Rect r) {
3426 if (r == null) {
3427 r = new Rect();
3428 }
3429 mTempPt[0] = x;
3430 mTempPt[1] = y;
3431 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempPt, true);
Winson Chungabedd9f2013-09-24 15:41:09 -07003432
3433 LauncherAppState app = LauncherAppState.getInstance();
3434 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
3435 r = grid.getHotseatRect();
Adam Cohen7d30a372013-07-01 17:03:59 -07003436 if (r.contains(mTempPt[0], mTempPt[1])) {
3437 return true;
3438 }
3439 return false;
3440 }
Winson Chung3d503fb2011-07-13 17:25:49 -07003441
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003442 void mapPointFromSelfToHotseatLayout(Hotseat hotseat, float[] xy) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003443 mTempPt[0] = (int) xy[0];
3444 mTempPt[1] = (int) xy[1];
3445 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempPt, true);
Winson Chung156ab5b2013-07-12 14:14:16 -07003446 mLauncher.getDragLayer().mapCoordInSelfToDescendent(hotseat.getLayout(), mTempPt);
Adam Cohen7d30a372013-07-01 17:03:59 -07003447
3448 xy[0] = mTempPt[0];
3449 xy[1] = mTempPt[1];
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003450 }
3451
Winson Chung3d503fb2011-07-13 17:25:49 -07003452 /*
Michael Jurka4516c112010-10-07 15:13:47 -07003453 *
3454 * Convert the 2D coordinate xy from this CellLayout's coordinate space to
3455 * the parent View's coordinate space. The argument xy is modified with the return result.
3456 *
3457 */
3458 void mapPointFromChildToSelf(View v, float[] xy) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003459 xy[0] += v.getLeft();
3460 xy[1] += v.getTop();
Michael Jurka4516c112010-10-07 15:13:47 -07003461 }
3462
Adam Cohene3e27a82011-04-15 12:07:39 -07003463 static private float squaredDistance(float[] point1, float[] point2) {
Michael Jurka4516c112010-10-07 15:13:47 -07003464 float distanceX = point1[0] - point2[0];
3465 float distanceY = point2[1] - point2[1];
3466 return distanceX * distanceX + distanceY * distanceY;
Adam Cohene3e27a82011-04-15 12:07:39 -07003467 }
Michael Jurka0280c3b2010-09-17 15:00:07 -07003468
Michael Jurka4516c112010-10-07 15:13:47 -07003469 /*
3470 *
Michael Jurka4516c112010-10-07 15:13:47 -07003471 * This method returns the CellLayout that is currently being dragged to. In order to drag
3472 * to a CellLayout, either the touch point must be directly over the CellLayout, or as a second
3473 * strategy, we see if the dragView is overlapping any CellLayout and choose the closest one
3474 *
3475 * Return null if no CellLayout is currently being dragged over
3476 *
3477 */
3478 private CellLayout findMatchingPageForDragOver(
Adam Cohen00618752011-07-20 12:06:04 -07003479 DragView dragView, float originX, float originY, boolean exact) {
Michael Jurka4516c112010-10-07 15:13:47 -07003480 // We loop through all the screens (ie CellLayouts) and see which ones overlap
3481 // with the item being dragged and then choose the one that's closest to the touch point
Michael Jurkaa63c4522010-08-19 13:52:27 -07003482 final int screenCount = getChildCount();
3483 CellLayout bestMatchingScreen = null;
Michael Jurka0280c3b2010-09-17 15:00:07 -07003484 float smallestDistSoFar = Float.MAX_VALUE;
Michael Jurka4516c112010-10-07 15:13:47 -07003485
Michael Jurkaa63c4522010-08-19 13:52:27 -07003486 for (int i = 0; i < screenCount; i++) {
Adam Cohendf041bc2013-09-22 12:32:22 -07003487 // The custom content screen is not a valid drag over option
3488 if (mScreenOrder.get(i) == CUSTOM_CONTENT_SCREEN_ID) {
3489 continue;
3490 }
3491
Winson Chung3d503fb2011-07-13 17:25:49 -07003492 CellLayout cl = (CellLayout) getChildAt(i);
Michael Jurkaa63c4522010-08-19 13:52:27 -07003493
Adam Cohen00618752011-07-20 12:06:04 -07003494 final float[] touchXy = {originX, originY};
Michael Jurka4516c112010-10-07 15:13:47 -07003495 // Transform the touch coordinates to the CellLayout's local coordinates
3496 // If the touch point is within the bounds of the cell layout, we can return immediately
Michael Jurka0280c3b2010-09-17 15:00:07 -07003497 cl.getMatrix().invert(mTempInverseMatrix);
Michael Jurka4516c112010-10-07 15:13:47 -07003498 mapPointFromSelfToChild(cl, touchXy, mTempInverseMatrix);
Michael Jurkaa63c4522010-08-19 13:52:27 -07003499
Michael Jurka4516c112010-10-07 15:13:47 -07003500 if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() &&
3501 touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) {
3502 return cl;
3503 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07003504
Winson Chung96ef4092011-11-22 12:25:14 -08003505 if (!exact) {
Michael Jurka4516c112010-10-07 15:13:47 -07003506 // Get the center of the cell layout in screen coordinates
3507 final float[] cellLayoutCenter = mTempCellLayoutCenterCoordinates;
3508 cellLayoutCenter[0] = cl.getWidth()/2;
3509 cellLayoutCenter[1] = cl.getHeight()/2;
3510 mapPointFromChildToSelf(cl, cellLayoutCenter);
Michael Jurka0280c3b2010-09-17 15:00:07 -07003511
Adam Cohen00618752011-07-20 12:06:04 -07003512 touchXy[0] = originX;
3513 touchXy[1] = originY;
Michael Jurka0280c3b2010-09-17 15:00:07 -07003514
Michael Jurka4516c112010-10-07 15:13:47 -07003515 // Calculate the distance between the center of the CellLayout
3516 // and the touch point
3517 float dist = squaredDistance(touchXy, cellLayoutCenter);
3518
3519 if (dist < smallestDistSoFar) {
Michael Jurka0280c3b2010-09-17 15:00:07 -07003520 smallestDistSoFar = dist;
Michael Jurkaa63c4522010-08-19 13:52:27 -07003521 bestMatchingScreen = cl;
Michael Jurkaa63c4522010-08-19 13:52:27 -07003522 }
Michael Jurka4516c112010-10-07 15:13:47 -07003523 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07003524 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07003525 return bestMatchingScreen;
3526 }
3527
Adam Cohene3e27a82011-04-15 12:07:39 -07003528 // This is used to compute the visual center of the dragView. This point is then
3529 // used to visualize drop locations and determine where to drop an item. The idea is that
3530 // the visual center represents the user's interpretation of where the item is, and hence
3531 // is the appropriate point to use when determining drop location.
3532 private float[] getDragViewVisualCenter(int x, int y, int xOffset, int yOffset,
3533 DragView dragView, float[] recycle) {
3534 float res[];
3535 if (recycle == null) {
3536 res = new float[2];
3537 } else {
3538 res = recycle;
3539 }
3540
3541 // First off, the drag view has been shifted in a way that is not represented in the
3542 // x and y values or the x/yOffsets. Here we account for that shift.
3543 x += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetX);
3544 y += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY);
3545
3546 // These represent the visual top and left of drag view if a dragRect was provided.
3547 // If a dragRect was not provided, then they correspond to the actual view left and
3548 // top, as the dragRect is in that case taken to be the entire dragView.
3549 // R.dimen.dragViewOffsetY.
3550 int left = x - xOffset;
3551 int top = y - yOffset;
3552
3553 // In order to find the visual center, we shift by half the dragRect
3554 res[0] = left + dragView.getDragRegion().width() / 2;
3555 res[1] = top + dragView.getDragRegion().height() / 2;
3556
3557 return res;
3558 }
3559
Winson Chungea359c62011-08-03 17:06:35 -07003560 private boolean isDragWidget(DragObject d) {
3561 return (d.dragInfo instanceof LauncherAppWidgetInfo ||
3562 d.dragInfo instanceof PendingAddWidgetInfo);
3563 }
3564 private boolean isExternalDragWidget(DragObject d) {
3565 return d.dragSource != this && isDragWidget(d);
3566 }
3567
Adam Cohencb3382b2011-05-24 14:07:08 -07003568 public void onDragOver(DragObject d) {
Winson Chungc07918d2011-07-01 15:35:26 -07003569 // Skip drag over events while we are dragging over side pages
Adam Cohen82ac8a22012-02-14 16:27:49 -08003570 if (mInScrollArea || mIsSwitchingState || mState == State.SMALL) return;
Winson Chungc07918d2011-07-01 15:35:26 -07003571
Winson Chung4afe9b32011-07-27 17:46:20 -07003572 Rect r = new Rect();
Winson Chung3d503fb2011-07-13 17:25:49 -07003573 CellLayout layout = null;
Winson Chungc07918d2011-07-01 15:35:26 -07003574 ItemInfo item = (ItemInfo) d.dragInfo;
3575
3576 // Ensure that we have proper spans for the item that we are dropping
3577 if (item.spanX < 0 || item.spanY < 0) throw new RuntimeException("Improper spans found");
Adam Cohen00618752011-07-20 12:06:04 -07003578 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset,
Winson Chung4afe9b32011-07-27 17:46:20 -07003579 d.dragView, mDragViewVisualCenter);
Adam Cohen00618752011-07-20 12:06:04 -07003580
Adam Cohen482ed822012-03-02 14:15:13 -08003581 final View child = (mDragInfo == null) ? null : mDragInfo.cell;
Winson Chungc07918d2011-07-01 15:35:26 -07003582 // Identify whether we have dragged over a side page
Michael Jurkad74c9842011-07-10 12:44:21 -07003583 if (isSmall()) {
Winson Chungea359c62011-08-03 17:06:35 -07003584 if (mLauncher.getHotseat() != null && !isExternalDragWidget(d)) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003585 if (isPointInSelfOverHotseat(d.x, d.y, r)) {
Winson Chung4afe9b32011-07-27 17:46:20 -07003586 layout = mLauncher.getHotseat().getLayout();
3587 }
3588 }
3589 if (layout == null) {
Winson Chung96ef4092011-11-22 12:25:14 -08003590 layout = findMatchingPageForDragOver(d.dragView, d.x, d.y, false);
Winson Chung4afe9b32011-07-27 17:46:20 -07003591 }
Winson Chungc07918d2011-07-01 15:35:26 -07003592 if (layout != mDragTargetLayout) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003593 setCurrentDropLayout(layout);
3594 setCurrentDragOverlappingLayout(layout);
Winson Chungc07918d2011-07-01 15:35:26 -07003595
Michael Jurkad74c9842011-07-10 12:44:21 -07003596 boolean isInSpringLoadedMode = (mState == State.SPRING_LOADED);
Winson Chungc07918d2011-07-01 15:35:26 -07003597 if (isInSpringLoadedMode) {
Winson Chung4afe9b32011-07-27 17:46:20 -07003598 if (mLauncher.isHotseatLayout(layout)) {
3599 mSpringLoadedDragController.cancel();
3600 } else {
3601 mSpringLoadedDragController.setAlarm(mDragTargetLayout);
3602 }
Winson Chungc07918d2011-07-01 15:35:26 -07003603 }
3604 }
3605 } else {
Winson Chung3d503fb2011-07-13 17:25:49 -07003606 // Test to see if we are over the hotseat otherwise just use the current page
Winson Chungea359c62011-08-03 17:06:35 -07003607 if (mLauncher.getHotseat() != null && !isDragWidget(d)) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003608 if (isPointInSelfOverHotseat(d.x, d.y, r)) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003609 layout = mLauncher.getHotseat().getLayout();
3610 }
3611 }
3612 if (layout == null) {
3613 layout = getCurrentDropLayout();
3614 }
Winson Chungc07918d2011-07-01 15:35:26 -07003615 if (layout != mDragTargetLayout) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003616 setCurrentDropLayout(layout);
3617 setCurrentDragOverlappingLayout(layout);
Winson Chungc07918d2011-07-01 15:35:26 -07003618 }
3619 }
3620
3621 // Handle the drag over
3622 if (mDragTargetLayout != null) {
Winson Chungc07918d2011-07-01 15:35:26 -07003623 // We want the point to be mapped to the dragTarget.
Winson Chung3d503fb2011-07-13 17:25:49 -07003624 if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003625 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
Winson Chung3d503fb2011-07-13 17:25:49 -07003626 } else {
3627 mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
3628 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003629
Winson Chungc07918d2011-07-01 15:35:26 -07003630 ItemInfo info = (ItemInfo) d.dragInfo;
Patrick Dubroy1262e362010-10-06 15:49:50 -07003631
Adam Cohen74c54912013-09-29 14:48:04 -07003632 int minSpanX = item.spanX;
3633 int minSpanY = item.spanY;
3634 if (item.minSpanX > 0 && item.minSpanY > 0) {
3635 minSpanX = item.minSpanX;
3636 minSpanY = item.minSpanY;
3637 }
3638
Winson Chungc07918d2011-07-01 15:35:26 -07003639 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
Adam Cohen74c54912013-09-29 14:48:04 -07003640 (int) mDragViewVisualCenter[1], minSpanX, minSpanY,
Adam Cohend024f982012-05-23 18:26:45 -07003641 mDragTargetLayout, mTargetCell);
Adam Cohen74c54912013-09-29 14:48:04 -07003642 int reorderX = mTargetCell[0];
3643 int reorderY = mTargetCell[1];
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003644
3645 setCurrentDropOverCell(mTargetCell[0], mTargetCell[1]);
3646
Adam Cohen482ed822012-03-02 14:15:13 -08003647 float targetCellDistance = mDragTargetLayout.getDistanceFromCell(
3648 mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell);
3649
Winson Chungc07918d2011-07-01 15:35:26 -07003650 final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0],
3651 mTargetCell[1]);
Winson Chung785d2eb2011-04-14 16:08:02 -07003652
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003653 manageFolderFeedback(info, mDragTargetLayout, mTargetCell,
3654 targetCellDistance, dragOverView);
Adam Cohen482ed822012-03-02 14:15:13 -08003655
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003656 boolean nearestDropOccupied = mDragTargetLayout.isNearestDropLocationOccupied((int)
3657 mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], item.spanX,
3658 item.spanY, child, mTargetCell);
3659
3660 if (!nearestDropOccupied) {
Adam Cohen19f37922012-03-21 11:59:11 -07003661 mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
3662 (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
3663 mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, false,
3664 d.dragView.getDragVisualizeOffset(), d.dragView.getDragRegion());
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003665 } else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
Adam Cohen74c54912013-09-29 14:48:04 -07003666 && !mReorderAlarm.alarmPending() && (mLastReorderX != reorderX ||
3667 mLastReorderY != reorderY)) {
Adam Cohend024f982012-05-23 18:26:45 -07003668
Adam Cohenfa3c58f2013-12-06 16:10:55 -08003669 int[] resultSpan = new int[2];
3670 mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0],
3671 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, item.spanX, item.spanY,
3672 child, mTargetCell, resultSpan, CellLayout.MODE_SHOW_REORDER_HINT);
3673
Adam Cohen19f37922012-03-21 11:59:11 -07003674 // Otherwise, if we aren't adding to or creating a folder and there's no pending
3675 // reorder, then we schedule a reorder
Adam Cohen482ed822012-03-02 14:15:13 -08003676 ReorderAlarmListener listener = new ReorderAlarmListener(mDragViewVisualCenter,
3677 minSpanX, minSpanY, item.spanX, item.spanY, d.dragView, child);
3678 mReorderAlarm.setOnAlarmListener(listener);
3679 mReorderAlarm.setAlarm(REORDER_TIMEOUT);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003680 }
3681
3682 if (mDragMode == DRAG_MODE_CREATE_FOLDER || mDragMode == DRAG_MODE_ADD_TO_FOLDER ||
3683 !nearestDropOccupied) {
3684 if (mDragTargetLayout != null) {
3685 mDragTargetLayout.revertTempState();
Michael Jurkad3ef3062010-11-23 16:23:58 -08003686 }
3687 }
Adam Cohen482ed822012-03-02 14:15:13 -08003688 }
3689 }
3690
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003691 private void manageFolderFeedback(ItemInfo info, CellLayout targetLayout,
3692 int[] targetCell, float distance, View dragOverView) {
Adam Cohen482ed822012-03-02 14:15:13 -08003693 boolean userFolderPending = willCreateUserFolder(info, targetLayout, targetCell, distance,
3694 false);
3695
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003696 if (mDragMode == DRAG_MODE_NONE && userFolderPending &&
3697 !mFolderCreationAlarm.alarmPending()) {
Adam Cohen482ed822012-03-02 14:15:13 -08003698 mFolderCreationAlarm.setOnAlarmListener(new
3699 FolderCreationAlarmListener(targetLayout, targetCell[0], targetCell[1]));
3700 mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003701 return;
Adam Cohen482ed822012-03-02 14:15:13 -08003702 }
3703
3704 boolean willAddToFolder =
3705 willAddToExistingUserFolder(info, targetLayout, targetCell, distance);
3706
3707 if (willAddToFolder && mDragMode == DRAG_MODE_NONE) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003708 mDragOverFolderIcon = ((FolderIcon) dragOverView);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003709 mDragOverFolderIcon.onDragEnter(info);
Adam Cohen482ed822012-03-02 14:15:13 -08003710 if (targetLayout != null) {
3711 targetLayout.clearDragOutlines();
Winson Chungc07918d2011-07-01 15:35:26 -07003712 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003713 setDragMode(DRAG_MODE_ADD_TO_FOLDER);
3714 return;
Patrick Dubroy976ebec2010-08-04 20:03:37 -07003715 }
Adam Cohen482ed822012-03-02 14:15:13 -08003716
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003717 if (mDragMode == DRAG_MODE_ADD_TO_FOLDER && !willAddToFolder) {
3718 setDragMode(DRAG_MODE_NONE);
3719 }
3720 if (mDragMode == DRAG_MODE_CREATE_FOLDER && !userFolderPending) {
3721 setDragMode(DRAG_MODE_NONE);
Adam Cohen482ed822012-03-02 14:15:13 -08003722 }
3723
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003724 return;
Adam Cohenc0dcf592011-06-01 15:30:43 -07003725 }
3726
Adam Cohen19072da2011-05-31 14:30:45 -07003727 class FolderCreationAlarmListener implements OnAlarmListener {
Adam Cohen69ce2e52011-07-03 19:25:21 -07003728 CellLayout layout;
3729 int cellX;
3730 int cellY;
Adam Cohen19072da2011-05-31 14:30:45 -07003731
Adam Cohen69ce2e52011-07-03 19:25:21 -07003732 public FolderCreationAlarmListener(CellLayout layout, int cellX, int cellY) {
3733 this.layout = layout;
3734 this.cellX = cellX;
3735 this.cellY = cellY;
Adam Cohen19072da2011-05-31 14:30:45 -07003736 }
3737
3738 public void onAlarm(Alarm alarm) {
Adam Cohen8ec05f92013-10-13 12:29:03 -07003739 if (mDragFolderRingAnimator != null) {
3740 // This shouldn't happen ever, but just in case, make sure we clean up the mess.
3741 mDragFolderRingAnimator.animateToNaturalState();
Adam Cohen19072da2011-05-31 14:30:45 -07003742 }
Adam Cohen8ec05f92013-10-13 12:29:03 -07003743 mDragFolderRingAnimator = new FolderRingAnimator(mLauncher, null);
Adam Cohen69ce2e52011-07-03 19:25:21 -07003744 mDragFolderRingAnimator.setCell(cellX, cellY);
3745 mDragFolderRingAnimator.setCellLayout(layout);
Adam Cohen19072da2011-05-31 14:30:45 -07003746 mDragFolderRingAnimator.animateToAcceptState();
Adam Cohen69ce2e52011-07-03 19:25:21 -07003747 layout.showFolderAccept(mDragFolderRingAnimator);
3748 layout.clearDragOutlines();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003749 setDragMode(DRAG_MODE_CREATE_FOLDER);
Adam Cohen482ed822012-03-02 14:15:13 -08003750 }
3751 }
3752
3753 class ReorderAlarmListener implements OnAlarmListener {
3754 float[] dragViewCenter;
3755 int minSpanX, minSpanY, spanX, spanY;
3756 DragView dragView;
3757 View child;
3758
3759 public ReorderAlarmListener(float[] dragViewCenter, int minSpanX, int minSpanY, int spanX,
3760 int spanY, DragView dragView, View child) {
3761 this.dragViewCenter = dragViewCenter;
3762 this.minSpanX = minSpanX;
3763 this.minSpanY = minSpanY;
3764 this.spanX = spanX;
3765 this.spanY = spanY;
3766 this.child = child;
3767 this.dragView = dragView;
3768 }
3769
3770 public void onAlarm(Alarm alarm) {
3771 int[] resultSpan = new int[2];
Adam Cohen19f37922012-03-21 11:59:11 -07003772 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
Adam Cohen74c54912013-09-29 14:48:04 -07003773 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, mDragTargetLayout,
3774 mTargetCell);
Adam Cohen19f37922012-03-21 11:59:11 -07003775 mLastReorderX = mTargetCell[0];
3776 mLastReorderY = mTargetCell[1];
3777
Adam Cohenfa3c58f2013-12-06 16:10:55 -08003778 mTargetCell = mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08003779 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
3780 child, mTargetCell, resultSpan, CellLayout.MODE_DRAG_OVER);
3781
Adam Cohen19f37922012-03-21 11:59:11 -07003782 if (mTargetCell[0] < 0 || mTargetCell[1] < 0) {
3783 mDragTargetLayout.revertTempState();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003784 } else {
3785 setDragMode(DRAG_MODE_REORDER);
Adam Cohen19f37922012-03-21 11:59:11 -07003786 }
Adam Cohen482ed822012-03-02 14:15:13 -08003787
Adam Cohen482ed822012-03-02 14:15:13 -08003788 boolean resize = resultSpan[0] != spanX || resultSpan[1] != spanY;
3789 mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
3790 (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
3791 mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], resize,
3792 dragView.getDragVisualizeOffset(), dragView.getDragRegion());
Adam Cohen19072da2011-05-31 14:30:45 -07003793 }
3794 }
3795
Winson Chunga34abf82010-11-12 12:10:35 -08003796 @Override
Adam Cohen7d30a372013-07-01 17:03:59 -07003797 public void getHitRectRelativeToDragLayer(Rect outRect) {
Winson Chunga34abf82010-11-12 12:10:35 -08003798 // We want the workspace to have the whole area of the display (it will find the correct
3799 // cell layout to drop to in the existing drag/drop logic.
Adam Cohen7d30a372013-07-01 17:03:59 -07003800 mLauncher.getDragLayer().getDescendantRectRelativeToSelf(this, outRect);
Winson Chunga34abf82010-11-12 12:10:35 -08003801 }
3802
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07003803 /**
3804 * Add the item specified by dragInfo to the given layout.
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07003805 * @return true if successful
3806 */
Adam Cohen120980b2010-12-08 11:05:37 -08003807 public boolean addExternalItemToScreen(ItemInfo dragInfo, CellLayout layout) {
3808 if (layout.findCellForSpan(mTempEstimate, dragInfo.spanX, dragInfo.spanY)) {
Patrick Dubroybbaa75c2011-03-08 18:47:40 -08003809 onDropExternal(dragInfo.dropPos, (ItemInfo) dragInfo, (CellLayout) layout, false);
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07003810 return true;
3811 }
Winson Chung93eef082012-03-23 15:59:27 -07003812 mLauncher.showOutOfSpaceMessage(mLauncher.isHotseatLayout(layout));
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07003813 return false;
3814 }
3815
Adam Cohend5e42732011-03-28 17:33:39 -07003816 private void onDropExternal(int[] touchXY, Object dragInfo,
3817 CellLayout cellLayout, boolean insertAtFirst) {
Adam Cohene3e27a82011-04-15 12:07:39 -07003818 onDropExternal(touchXY, dragInfo, cellLayout, insertAtFirst, null);
Adam Cohend5e42732011-03-28 17:33:39 -07003819 }
3820
Adam Cohen120980b2010-12-08 11:05:37 -08003821 /**
3822 * Drop an item that didn't originate on one of the workspace screens.
3823 * It may have come from Launcher (e.g. from all apps or customize), or it may have
3824 * come from another app altogether.
3825 *
3826 * NOTE: This can also be called when we are outside of a drag event, when we want
3827 * to add an item to one of the workspace screens.
3828 */
Winson Chung557d6ed2011-07-08 15:34:52 -07003829 private void onDropExternal(final int[] touchXY, final Object dragInfo,
3830 final CellLayout cellLayout, boolean insertAtFirst, DragObject d) {
3831 final Runnable exitSpringLoadedRunnable = new Runnable() {
3832 @Override
3833 public void run() {
Adam Cohen689ff162014-05-08 17:27:56 -07003834 mLauncher.exitSpringLoadedDragModeDelayed(true,
3835 Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
Winson Chung557d6ed2011-07-08 15:34:52 -07003836 }
3837 };
Adam Cohenb7e16182011-07-15 17:55:02 -07003838
3839 ItemInfo info = (ItemInfo) dragInfo;
3840 int spanX = info.spanX;
3841 int spanY = info.spanY;
3842 if (mDragInfo != null) {
3843 spanX = mDragInfo.spanX;
3844 spanY = mDragInfo.spanY;
3845 }
3846
Winson Chung3d503fb2011-07-13 17:25:49 -07003847 final long container = mLauncher.isHotseatLayout(cellLayout) ?
3848 LauncherSettings.Favorites.CONTAINER_HOTSEAT :
3849 LauncherSettings.Favorites.CONTAINER_DESKTOP;
Adam Cohendcd297f2013-06-18 13:13:40 -07003850 final long screenId = getIdForScreen(cellLayout);
3851 if (!mLauncher.isHotseatLayout(cellLayout)
3852 && screenId != getScreenIdForPageIndex(mCurrentPage)
Winson Chung3d503fb2011-07-13 17:25:49 -07003853 && mState != State.SPRING_LOADED) {
Adam Cohendcd297f2013-06-18 13:13:40 -07003854 snapToScreenId(screenId, null);
Adam Cohen76078c42011-06-09 15:06:52 -07003855 }
Adam Cohenb7e16182011-07-15 17:55:02 -07003856
3857 if (info instanceof PendingAddItemInfo) {
3858 final PendingAddItemInfo pendingInfo = (PendingAddItemInfo) dragInfo;
3859
Adam Cohen558baaf2011-08-15 15:22:57 -07003860 boolean findNearestVacantCell = true;
3861 if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
3862 mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
3863 cellLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08003864 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
3865 mDragViewVisualCenter[1], mTargetCell);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003866 if (willCreateUserFolder((ItemInfo) d.dragInfo, cellLayout, mTargetCell,
Adam Cohen482ed822012-03-02 14:15:13 -08003867 distance, true) || willAddToExistingUserFolder((ItemInfo) d.dragInfo,
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003868 cellLayout, mTargetCell, distance)) {
Adam Cohen558baaf2011-08-15 15:22:57 -07003869 findNearestVacantCell = false;
3870 }
3871 }
Adam Cohen482ed822012-03-02 14:15:13 -08003872
Adam Cohend41fbf52012-02-16 23:53:59 -08003873 final ItemInfo item = (ItemInfo) d.dragInfo;
Adam Cohenaaa5c212012-10-05 18:14:31 -07003874 boolean updateWidgetSize = false;
Adam Cohen558baaf2011-08-15 15:22:57 -07003875 if (findNearestVacantCell) {
Adam Cohen482ed822012-03-02 14:15:13 -08003876 int minSpanX = item.spanX;
3877 int minSpanY = item.spanY;
3878 if (item.minSpanX > 0 && item.minSpanY > 0) {
3879 minSpanX = item.minSpanX;
3880 minSpanY = item.minSpanY;
3881 }
Adam Cohend41fbf52012-02-16 23:53:59 -08003882 int[] resultSpan = new int[2];
Adam Cohenfa3c58f2013-12-06 16:10:55 -08003883 mTargetCell = cellLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08003884 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, info.spanX, info.spanY,
3885 null, mTargetCell, resultSpan, CellLayout.MODE_ON_DROP_EXTERNAL);
Adam Cohen9e05a5e2012-09-10 15:53:09 -07003886
3887 if (resultSpan[0] != item.spanX || resultSpan[1] != item.spanY) {
3888 updateWidgetSize = true;
3889 }
Adam Cohend41fbf52012-02-16 23:53:59 -08003890 item.spanX = resultSpan[0];
3891 item.spanY = resultSpan[1];
Adam Cohen558baaf2011-08-15 15:22:57 -07003892 }
3893
Adam Cohenb7e16182011-07-15 17:55:02 -07003894 Runnable onAnimationCompleteRunnable = new Runnable() {
Winson Chung557d6ed2011-07-08 15:34:52 -07003895 @Override
3896 public void run() {
Adam Cohen689ff162014-05-08 17:27:56 -07003897 // Normally removeExtraEmptyScreen is called in Workspace#onDragEnd, but when
3898 // adding an item that may not be dropped right away (due to a config activity)
3899 // we defer the removal until the activity returns.
3900 deferRemoveExtraEmptyScreen();
3901
Winson Chung557d6ed2011-07-08 15:34:52 -07003902 // When dragging and dropping from customization tray, we deal with creating
3903 // widgets/shortcuts/folders in a slightly different way
Adam Cohenb7e16182011-07-15 17:55:02 -07003904 switch (pendingInfo.itemType) {
Winson Chung557d6ed2011-07-08 15:34:52 -07003905 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
Adam Cohend41fbf52012-02-16 23:53:59 -08003906 int span[] = new int[2];
3907 span[0] = item.spanX;
3908 span[1] = item.spanY;
Adam Cohenb7e16182011-07-15 17:55:02 -07003909 mLauncher.addAppWidgetFromDrop((PendingAddWidgetInfo) pendingInfo,
Adam Cohendcd297f2013-06-18 13:13:40 -07003910 container, screenId, mTargetCell, span, null);
Winson Chung557d6ed2011-07-08 15:34:52 -07003911 break;
3912 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
Adam Cohenb7e16182011-07-15 17:55:02 -07003913 mLauncher.processShortcutFromDrop(pendingInfo.componentName,
Adam Cohendcd297f2013-06-18 13:13:40 -07003914 container, screenId, mTargetCell, null);
Winson Chung557d6ed2011-07-08 15:34:52 -07003915 break;
3916 default:
Adam Cohenb7e16182011-07-15 17:55:02 -07003917 throw new IllegalStateException("Unknown item type: " +
3918 pendingInfo.itemType);
Winson Chung557d6ed2011-07-08 15:34:52 -07003919 }
Winson Chung557d6ed2011-07-08 15:34:52 -07003920 }
Adam Cohenb7e16182011-07-15 17:55:02 -07003921 };
Adam Cohend41fbf52012-02-16 23:53:59 -08003922 View finalView = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
3923 ? ((PendingAddWidgetInfo) pendingInfo).boundWidget : null;
Adam Cohen9e05a5e2012-09-10 15:53:09 -07003924
3925 if (finalView instanceof AppWidgetHostView && updateWidgetSize) {
3926 AppWidgetHostView awhv = (AppWidgetHostView) finalView;
3927 AppWidgetResizeFrame.updateWidgetSizeRanges(awhv, mLauncher, item.spanX,
3928 item.spanY);
3929 }
3930
Adam Cohend41fbf52012-02-16 23:53:59 -08003931 int animationStyle = ANIMATE_INTO_POSITION_AND_DISAPPEAR;
3932 if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET &&
3933 ((PendingAddWidgetInfo) pendingInfo).info.configure != null) {
3934 animationStyle = ANIMATE_INTO_POSITION_AND_REMAIN;
3935 }
3936 animateWidgetDrop(info, cellLayout, d.dragView, onAnimationCompleteRunnable,
3937 animationStyle, finalView, true);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07003938 } else {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003939 // This is for other drag/drop cases, like dragging from All Apps
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003940 View view = null;
3941
3942 switch (info.itemType) {
3943 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
3944 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
Michael Jurkaeadbfc52013-09-04 00:45:37 +02003945 if (info.container == NO_ID && info instanceof AppInfo) {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003946 // Came from all apps -- make a copy
Michael Jurkaeadbfc52013-09-04 00:45:37 +02003947 info = new ShortcutInfo((AppInfo) info);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003948 }
3949 view = mLauncher.createShortcut(R.layout.application, cellLayout,
3950 (ShortcutInfo) info);
3951 break;
Adam Cohendf2cc412011-04-27 16:56:57 -07003952 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
Adam Cohenc0dcf592011-06-01 15:30:43 -07003953 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, cellLayout,
3954 (FolderInfo) info, mIconCache);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003955 break;
3956 default:
3957 throw new IllegalStateException("Unknown item type: " + info.itemType);
3958 }
3959
Adam Cohenc0dcf592011-06-01 15:30:43 -07003960 // First we find the cell nearest to point at which the item is
3961 // dropped, without any consideration to whether there is an item there.
3962 if (touchXY != null) {
3963 mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
3964 cellLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08003965 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
3966 mDragViewVisualCenter[1], mTargetCell);
Winson Chung557d6ed2011-07-08 15:34:52 -07003967 d.postAnimationRunnable = exitSpringLoadedRunnable;
Adam Cohen482ed822012-03-02 14:15:13 -08003968 if (createUserFolderIfNecessary(view, container, cellLayout, mTargetCell, distance,
3969 true, d.dragView, d.postAnimationRunnable)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07003970 return;
3971 }
Adam Cohen482ed822012-03-02 14:15:13 -08003972 if (addToExistingFolderIfNecessary(view, cellLayout, mTargetCell, distance, d,
3973 true)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07003974 return;
3975 }
Adam Cohen10b17372011-04-15 14:21:25 -07003976 }
3977
Michael Jurkac4e772e2011-02-10 13:32:01 -08003978 if (touchXY != null) {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003979 // when dragging and dropping, just find the closest free spot
Adam Cohenfa3c58f2013-12-06 16:10:55 -08003980 mTargetCell = cellLayout.performReorder((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08003981 (int) mDragViewVisualCenter[1], 1, 1, 1, 1,
Adam Cohenea889a22012-03-27 16:45:39 -07003982 null, mTargetCell, null, CellLayout.MODE_ON_DROP_EXTERNAL);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003983 } else {
3984 cellLayout.findCellForSpan(mTargetCell, 1, 1);
3985 }
Adam Cohendcd297f2013-06-18 13:13:40 -07003986 addInScreen(view, container, screenId, mTargetCell[0], mTargetCell[1], info.spanX,
Winson Chung3d503fb2011-07-13 17:25:49 -07003987 info.spanY, insertAtFirst);
Adam Cohen716b51e2011-06-30 12:09:54 -07003988 cellLayout.onDropChild(view);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07003989 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
Michael Jurkaa52570f2012-03-20 03:18:20 -07003990 cellLayout.getShortcutsAndWidgets().measureChild(view);
Adam Cohend5e42732011-03-28 17:33:39 -07003991
Adam Cohendcd297f2013-06-18 13:13:40 -07003992 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId,
Winson Chungaafa03c2010-06-11 17:34:16 -07003993 lp.cellX, lp.cellY);
Adam Cohen3e8f8112011-07-02 18:03:00 -07003994
3995 if (d.dragView != null) {
Adam Cohen4b285c52011-07-21 14:24:06 -07003996 // We wrap the animation call in the temporary set and reset of the current
3997 // cellLayout to its final transform -- this means we animate the drag view to
3998 // the correct final location.
3999 setFinalTransitionTransform(cellLayout);
Winson Chung557d6ed2011-07-08 15:34:52 -07004000 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view,
Adam Cohen28f852a2013-10-15 14:34:05 -07004001 exitSpringLoadedRunnable, this);
Adam Cohen4b285c52011-07-21 14:24:06 -07004002 resetTransitionTransform(cellLayout);
Adam Cohen3e8f8112011-07-02 18:03:00 -07004003 }
Joe Onorato00acb122009-08-04 16:04:30 -04004004 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004005 }
Winson Chungaafa03c2010-06-11 17:34:16 -07004006
Adam Cohend41fbf52012-02-16 23:53:59 -08004007 public Bitmap createWidgetBitmap(ItemInfo widgetInfo, View layout) {
Adam Cohened66b2b2012-01-23 17:28:51 -08004008 int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(widgetInfo.spanX,
4009 widgetInfo.spanY, widgetInfo, false);
Adam Cohend41fbf52012-02-16 23:53:59 -08004010 int visibility = layout.getVisibility();
Adam Cohened66b2b2012-01-23 17:28:51 -08004011 layout.setVisibility(VISIBLE);
4012
4013 int width = MeasureSpec.makeMeasureSpec(unScaledSize[0], MeasureSpec.EXACTLY);
4014 int height = MeasureSpec.makeMeasureSpec(unScaledSize[1], MeasureSpec.EXACTLY);
4015 Bitmap b = Bitmap.createBitmap(unScaledSize[0], unScaledSize[1],
4016 Bitmap.Config.ARGB_8888);
4017 Canvas c = new Canvas(b);
4018
4019 layout.measure(width, height);
4020 layout.layout(0, 0, unScaledSize[0], unScaledSize[1]);
Adam Cohened66b2b2012-01-23 17:28:51 -08004021 layout.draw(c);
4022 c.setBitmap(null);
Adam Cohend41fbf52012-02-16 23:53:59 -08004023 layout.setVisibility(visibility);
Adam Cohened66b2b2012-01-23 17:28:51 -08004024 return b;
4025 }
4026
Adam Cohend41fbf52012-02-16 23:53:59 -08004027 private void getFinalPositionForDropAnimation(int[] loc, float[] scaleXY,
Adam Cohen9d5b7d82012-05-09 18:00:44 -07004028 DragView dragView, CellLayout layout, ItemInfo info, int[] targetCell,
Michael Jurkaa2fe7862012-05-29 05:58:17 -07004029 boolean external, boolean scale) {
Adam Cohened66b2b2012-01-23 17:28:51 -08004030 // Now we animate the dragView, (ie. the widget or shortcut preview) into its final
4031 // location and size on the home screen.
Adam Cohend41fbf52012-02-16 23:53:59 -08004032 int spanX = info.spanX;
4033 int spanY = info.spanY;
Adam Cohened66b2b2012-01-23 17:28:51 -08004034
Adam Cohend41fbf52012-02-16 23:53:59 -08004035 Rect r = estimateItemPosition(layout, info, targetCell[0], targetCell[1], spanX, spanY);
4036 loc[0] = r.left;
4037 loc[1] = r.top;
4038
4039 setFinalTransitionTransform(layout);
4040 float cellLayoutScale =
Adam Cohen7d30a372013-07-01 17:03:59 -07004041 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(layout, loc, true);
Adam Cohend41fbf52012-02-16 23:53:59 -08004042 resetTransitionTransform(layout);
Michael Jurkaa2fe7862012-05-29 05:58:17 -07004043
4044 float dragViewScaleX;
4045 float dragViewScaleY;
4046 if (scale) {
4047 dragViewScaleX = (1.0f * r.width()) / dragView.getMeasuredWidth();
4048 dragViewScaleY = (1.0f * r.height()) / dragView.getMeasuredHeight();
4049 } else {
4050 dragViewScaleX = 1f;
4051 dragViewScaleY = 1f;
4052 }
Adam Cohend41fbf52012-02-16 23:53:59 -08004053
Adam Cohened66b2b2012-01-23 17:28:51 -08004054 // The animation will scale the dragView about its center, so we need to center about
4055 // the final location.
4056 loc[0] -= (dragView.getMeasuredWidth() - cellLayoutScale * r.width()) / 2;
4057 loc[1] -= (dragView.getMeasuredHeight() - cellLayoutScale * r.height()) / 2;
4058
Adam Cohend41fbf52012-02-16 23:53:59 -08004059 scaleXY[0] = dragViewScaleX * cellLayoutScale;
4060 scaleXY[1] = dragViewScaleY * cellLayoutScale;
4061 }
4062
4063 public void animateWidgetDrop(ItemInfo info, CellLayout cellLayout, DragView dragView,
4064 final Runnable onCompleteRunnable, int animationType, final View finalView,
4065 boolean external) {
4066 Rect from = new Rect();
4067 mLauncher.getDragLayer().getViewRectRelativeToSelf(dragView, from);
4068
4069 int[] finalPos = new int[2];
4070 float scaleXY[] = new float[2];
Michael Jurkaa2fe7862012-05-29 05:58:17 -07004071 boolean scalePreview = !(info instanceof PendingAddShortcutInfo);
Adam Cohend41fbf52012-02-16 23:53:59 -08004072 getFinalPositionForDropAnimation(finalPos, scaleXY, dragView, cellLayout, info, mTargetCell,
Michael Jurkaa2fe7862012-05-29 05:58:17 -07004073 external, scalePreview);
Adam Cohened66b2b2012-01-23 17:28:51 -08004074
4075 Resources res = mLauncher.getResources();
Adam Cohenad4e15c2013-10-17 16:21:35 -07004076 final int duration = res.getInteger(R.integer.config_dropAnimMaxDuration) - 200;
Adam Cohened66b2b2012-01-23 17:28:51 -08004077
Adam Cohend41fbf52012-02-16 23:53:59 -08004078 // In the case where we've prebound the widget, we remove it from the DragLayer
4079 if (finalView instanceof AppWidgetHostView && external) {
Adam Cohen68f681b2012-05-28 15:01:16 -07004080 Log.d(TAG, "6557954 Animate widget drop, final view is appWidgetHostView");
Adam Cohend41fbf52012-02-16 23:53:59 -08004081 mLauncher.getDragLayer().removeView(finalView);
4082 }
4083 if ((animationType == ANIMATE_INTO_POSITION_AND_RESIZE || external) && finalView != null) {
4084 Bitmap crossFadeBitmap = createWidgetBitmap(info, finalView);
Adam Cohened66b2b2012-01-23 17:28:51 -08004085 dragView.setCrossFadeBitmap(crossFadeBitmap);
4086 dragView.crossFade((int) (duration * 0.8f));
Adam Cohend41fbf52012-02-16 23:53:59 -08004087 } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET && external) {
4088 scaleXY[0] = scaleXY[1] = Math.min(scaleXY[0], scaleXY[1]);
Adam Cohened66b2b2012-01-23 17:28:51 -08004089 }
4090
Adam Cohend41fbf52012-02-16 23:53:59 -08004091 DragLayer dragLayer = mLauncher.getDragLayer();
Winson Chung7bd1bbb2012-02-13 18:29:29 -08004092 if (animationType == CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION) {
Adam Cohend41fbf52012-02-16 23:53:59 -08004093 mLauncher.getDragLayer().animateViewIntoPosition(dragView, finalPos, 0f, 0.1f, 0.1f,
Adam Cohened66b2b2012-01-23 17:28:51 -08004094 DragLayer.ANIMATION_END_DISAPPEAR, onCompleteRunnable, duration);
4095 } else {
Adam Cohend41fbf52012-02-16 23:53:59 -08004096 int endStyle;
4097 if (animationType == ANIMATE_INTO_POSITION_AND_REMAIN) {
4098 endStyle = DragLayer.ANIMATION_END_REMAIN_VISIBLE;
4099 } else {
4100 endStyle = DragLayer.ANIMATION_END_DISAPPEAR;;
4101 }
4102
4103 Runnable onComplete = new Runnable() {
4104 @Override
4105 public void run() {
4106 if (finalView != null) {
4107 finalView.setVisibility(VISIBLE);
4108 }
4109 if (onCompleteRunnable != null) {
4110 onCompleteRunnable.run();
4111 }
4112 }
4113 };
4114 dragLayer.animateViewIntoPosition(dragView, from.left, from.top, finalPos[0],
4115 finalPos[1], 1, 1, 1, scaleXY[0], scaleXY[1], onComplete, endStyle,
4116 duration, this);
Adam Cohened66b2b2012-01-23 17:28:51 -08004117 }
4118 }
4119
Adam Cohen4b285c52011-07-21 14:24:06 -07004120 public void setFinalTransitionTransform(CellLayout layout) {
4121 if (isSwitchingState()) {
Adam Cohen7d30a372013-07-01 17:03:59 -07004122 mCurrentScale = getScaleX();
4123 setScaleX(mNewScale);
4124 setScaleY(mNewScale);
Adam Cohen4b285c52011-07-21 14:24:06 -07004125 }
4126 }
4127 public void resetTransitionTransform(CellLayout layout) {
4128 if (isSwitchingState()) {
Adam Cohen7d30a372013-07-01 17:03:59 -07004129 setScaleX(mCurrentScale);
4130 setScaleY(mCurrentScale);
Adam Cohen4b285c52011-07-21 14:24:06 -07004131 }
4132 }
4133
Jeff Sharkey70864282009-04-07 21:08:40 -07004134 /**
4135 * Return the current {@link CellLayout}, correctly picking the destination
4136 * screen while a scroll is in progress.
4137 */
Patrick Dubroy5f445422011-02-18 14:35:21 -08004138 public CellLayout getCurrentDropLayout() {
Winson Chung360e63f2012-04-27 13:48:05 -07004139 return (CellLayout) getChildAt(getNextPage());
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004140 }
4141
Jeff Sharkey70864282009-04-07 21:08:40 -07004142 /**
Michael Jurka0280c3b2010-09-17 15:00:07 -07004143 * Return the current CellInfo describing our current drag; this method exists
4144 * so that Launcher can sync this object with the correct info when the activity is created/
4145 * destroyed
4146 *
4147 */
4148 public CellLayout.CellInfo getDragInfo() {
4149 return mDragInfo;
4150 }
4151
Winson Chung9b9fb962013-11-15 15:39:34 -08004152 public int getCurrentPageOffsetFromCustomContent() {
Adam Cohen21cd0022013-10-09 18:57:02 -07004153 return getNextPage() - numCustomPages();
4154 }
4155
Michael Jurka0280c3b2010-09-17 15:00:07 -07004156 /**
Jeff Sharkey70864282009-04-07 21:08:40 -07004157 * Calculate the nearest cell where the given object would be dropped.
Adam Cohene3e27a82011-04-15 12:07:39 -07004158 *
4159 * pixelX and pixelY should be in the coordinate system of layout
Jeff Sharkey70864282009-04-07 21:08:40 -07004160 */
Adam Cohendf035382011-04-11 17:22:04 -07004161 private int[] findNearestArea(int pixelX, int pixelY,
Adam Cohene3e27a82011-04-15 12:07:39 -07004162 int spanX, int spanY, CellLayout layout, int[] recycle) {
Adam Cohendf035382011-04-11 17:22:04 -07004163 return layout.findNearestArea(
Adam Cohene3e27a82011-04-15 12:07:39 -07004164 pixelX, pixelY, spanX, spanY, recycle);
Adam Cohendf035382011-04-11 17:22:04 -07004165 }
4166
Adam Cohencff6af82011-09-13 14:51:53 -07004167 void setup(DragController dragController) {
Michael Jurkac2f7f472010-12-14 15:34:42 -08004168 mSpringLoadedDragController = new SpringLoadedDragController(mLauncher);
Joe Onorato00acb122009-08-04 16:04:30 -04004169 mDragController = dragController;
Michael Jurkad74c9842011-07-10 12:44:21 -07004170
Michael Jurkad74c9842011-07-10 12:44:21 -07004171 // hardware layers on children are enabled on startup, but should be disabled until
4172 // needed
Michael Jurka3a0469d2012-06-21 09:38:41 -07004173 updateChildrenLayersEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004174 }
4175
Patrick Dubroye3887cc2011-01-20 10:43:40 -08004176 /**
4177 * Called at the end of a drag which originated on the workspace.
4178 */
Michael Jurka1e2f4652013-07-08 18:03:46 -07004179 public void onDropCompleted(final View target, final DragObject d,
4180 final boolean isFlingToDelete, final boolean success) {
4181 if (mDeferDropAfterUninstall) {
4182 mDeferredAction = new Runnable() {
Adam Cohenad4e15c2013-10-17 16:21:35 -07004183 public void run() {
4184 onDropCompleted(target, d, isFlingToDelete, success);
4185 mDeferredAction = null;
4186 }
4187 };
Michael Jurka1e2f4652013-07-08 18:03:46 -07004188 return;
4189 }
4190
4191 boolean beingCalledAfterUninstall = mDeferredAction != null;
4192
4193 if (success && !(beingCalledAfterUninstall && !mUninstallSuccessful)) {
4194 if (target != this && mDragInfo != null) {
Daniel Sandlerb56e95a2013-09-04 23:13:39 -04004195 CellLayout parentCell = getParentCellLayoutForView(mDragInfo.cell);
4196 if (parentCell != null) {
4197 parentCell.removeView(mDragInfo.cell);
4198 }
Michael Jurka1e2f4652013-07-08 18:03:46 -07004199 if (mDragInfo.cell instanceof DropTarget) {
4200 mDragController.removeDropTarget((DropTarget) mDragInfo.cell);
Joe Onorato00acb122009-08-04 16:04:30 -04004201 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004202 }
Patrick Dubroyce34a972010-10-19 10:34:32 -07004203 } else if (mDragInfo != null) {
Winson Chung3d503fb2011-07-13 17:25:49 -07004204 CellLayout cellLayout;
4205 if (mLauncher.isHotseatLayout(target)) {
4206 cellLayout = mLauncher.getHotseat().getLayout();
4207 } else {
Adam Cohendcd297f2013-06-18 13:13:40 -07004208 cellLayout = getScreenWithId(mDragInfo.screenId);
Winson Chung3d503fb2011-07-13 17:25:49 -07004209 }
Jorim Jaggieedb00a2014-01-13 13:45:07 -08004210 if (cellLayout == null && LauncherAppState.isDogfoodBuild()) {
4211 throw new RuntimeException("Invalid state: cellLayout == null in "
4212 + "Workspace#onDropCompleted. Please file a bug. ");
4213 }
4214 if (cellLayout != null) {
4215 cellLayout.onDropChild(mDragInfo.cell);
4216 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004217 }
Michael Jurka1e2f4652013-07-08 18:03:46 -07004218 if ((d.cancelled || (beingCalledAfterUninstall && !mUninstallSuccessful))
4219 && mDragInfo.cell != null) {
4220 mDragInfo.cell.setVisibility(VISIBLE);
Adam Cohen36cc09b2011-09-29 17:33:15 -07004221 }
Joe Onorato4be866d2010-10-10 11:26:02 -07004222 mDragOutline = null;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004223 mDragInfo = null;
4224 }
4225
Michael Jurka1e2f4652013-07-08 18:03:46 -07004226 public void deferCompleteDropAfterUninstallActivity() {
4227 mDeferDropAfterUninstall = true;
4228 }
4229
4230 /// maybe move this into a smaller part
4231 public void onUninstallActivityReturned(boolean success) {
4232 mDeferDropAfterUninstall = false;
4233 mUninstallSuccessful = success;
4234 if (mDeferredAction != null) {
4235 mDeferredAction.run();
4236 }
4237 }
4238
Adam Cohenea889a22012-03-27 16:45:39 -07004239 void updateItemLocationsInDatabase(CellLayout cl) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004240 int count = cl.getShortcutsAndWidgets().getChildCount();
Adam Cohend3461712012-03-29 17:25:17 -07004241
Adam Cohendcd297f2013-06-18 13:13:40 -07004242 long screenId = getIdForScreen(cl);
Adam Cohend3461712012-03-29 17:25:17 -07004243 int container = Favorites.CONTAINER_DESKTOP;
4244
4245 if (mLauncher.isHotseatLayout(cl)) {
Adam Cohendcd297f2013-06-18 13:13:40 -07004246 screenId = -1;
Adam Cohend3461712012-03-29 17:25:17 -07004247 container = Favorites.CONTAINER_HOTSEAT;
4248 }
4249
Adam Cohen482ed822012-03-02 14:15:13 -08004250 for (int i = 0; i < count; i++) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004251 View v = cl.getShortcutsAndWidgets().getChildAt(i);
Adam Cohen482ed822012-03-02 14:15:13 -08004252 ItemInfo info = (ItemInfo) v.getTag();
Adam Cohen2acce882012-03-28 19:03:19 -07004253 // Null check required as the AllApps button doesn't have an item info
Adam Cohen487f7dd2012-06-28 18:12:10 -07004254 if (info != null && info.requiresDbUpdate) {
4255 info.requiresDbUpdate = false;
Adam Cohendcd297f2013-06-18 13:13:40 -07004256 LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId, info.cellX,
Adam Cohenbebf0422012-04-11 18:06:28 -07004257 info.cellY, info.spanX, info.spanY);
Adam Cohen2acce882012-03-28 19:03:19 -07004258 }
Adam Cohen482ed822012-03-02 14:15:13 -08004259 }
4260 }
4261
Adam Cohen4caf2982013-08-20 18:54:31 -07004262 ArrayList<ComponentName> getUniqueComponents(boolean stripDuplicates, ArrayList<ComponentName> duplicates) {
Adam Cohene25af792013-06-06 23:08:25 -07004263 ArrayList<ComponentName> uniqueIntents = new ArrayList<ComponentName>();
Adam Cohen4caf2982013-08-20 18:54:31 -07004264 getUniqueIntents((CellLayout) mLauncher.getHotseat().getLayout(), uniqueIntents, duplicates, false);
Adam Cohene25af792013-06-06 23:08:25 -07004265 int count = getChildCount();
4266 for (int i = 0; i < count; i++) {
4267 CellLayout cl = (CellLayout) getChildAt(i);
Adam Cohen4caf2982013-08-20 18:54:31 -07004268 getUniqueIntents(cl, uniqueIntents, duplicates, false);
Adam Cohene25af792013-06-06 23:08:25 -07004269 }
4270 return uniqueIntents;
4271 }
4272
Adam Cohen4caf2982013-08-20 18:54:31 -07004273 void getUniqueIntents(CellLayout cl, ArrayList<ComponentName> uniqueIntents,
4274 ArrayList<ComponentName> duplicates, boolean stripDuplicates) {
Adam Cohene25af792013-06-06 23:08:25 -07004275 int count = cl.getShortcutsAndWidgets().getChildCount();
4276
4277 ArrayList<View> children = new ArrayList<View>();
4278 for (int i = 0; i < count; i++) {
4279 View v = cl.getShortcutsAndWidgets().getChildAt(i);
4280 children.add(v);
4281 }
4282
4283 for (int i = 0; i < count; i++) {
4284 View v = children.get(i);
4285 ItemInfo info = (ItemInfo) v.getTag();
4286 // Null check required as the AllApps button doesn't have an item info
4287 if (info instanceof ShortcutInfo) {
4288 ShortcutInfo si = (ShortcutInfo) info;
4289 ComponentName cn = si.intent.getComponent();
4290
Adam Cohen4caf2982013-08-20 18:54:31 -07004291 Uri dataUri = si.intent.getData();
4292 // If dataUri is not null / empty or if this component isn't one that would
4293 // have previously showed up in the AllApps list, then this is a widget-type
4294 // shortcut, so ignore it.
4295 if (dataUri != null && !dataUri.equals(Uri.EMPTY)) {
Adam Cohen99894d92013-06-14 11:22:59 -07004296 continue;
4297 }
Adam Cohena0b57492013-06-14 15:33:35 -07004298
Adam Cohene25af792013-06-06 23:08:25 -07004299 if (!uniqueIntents.contains(cn)) {
4300 uniqueIntents.add(cn);
4301 } else {
Adam Cohen4caf2982013-08-20 18:54:31 -07004302 if (stripDuplicates) {
4303 cl.removeViewInLayout(v);
4304 LauncherModel.deleteItemFromDatabase(mLauncher, si);
4305 }
4306 if (duplicates != null) {
4307 duplicates.add(cn);
4308 }
Adam Cohene25af792013-06-06 23:08:25 -07004309 }
4310 }
4311 if (v instanceof FolderIcon) {
4312 FolderIcon fi = (FolderIcon) v;
4313 ArrayList<View> items = fi.getFolder().getItemsInReadingOrder();
4314 for (int j = 0; j < items.size(); j++) {
4315 if (items.get(j).getTag() instanceof ShortcutInfo) {
4316 ShortcutInfo si = (ShortcutInfo) items.get(j).getTag();
4317 ComponentName cn = si.intent.getComponent();
4318
Adam Cohen4caf2982013-08-20 18:54:31 -07004319 Uri dataUri = si.intent.getData();
4320 // If dataUri is not null / empty or if this component isn't one that would
4321 // have previously showed up in the AllApps list, then this is a widget-type
4322 // shortcut, so ignore it.
4323 if (dataUri != null && !dataUri.equals(Uri.EMPTY)) {
Adam Cohen99894d92013-06-14 11:22:59 -07004324 continue;
4325 }
Adam Cohen4caf2982013-08-20 18:54:31 -07004326
Adam Cohene25af792013-06-06 23:08:25 -07004327 if (!uniqueIntents.contains(cn)) {
4328 uniqueIntents.add(cn);
Adam Cohen4caf2982013-08-20 18:54:31 -07004329 } else {
4330 if (stripDuplicates) {
4331 fi.getFolderInfo().remove(si);
4332 LauncherModel.deleteItemFromDatabase(mLauncher, si);
4333 }
4334 if (duplicates != null) {
4335 duplicates.add(cn);
4336 }
Adam Cohene25af792013-06-06 23:08:25 -07004337 }
4338 }
4339 }
4340 }
4341 }
4342 }
4343
4344 void saveWorkspaceToDb() {
4345 saveWorkspaceScreenToDb((CellLayout) mLauncher.getHotseat().getLayout());
4346 int count = getChildCount();
4347 for (int i = 0; i < count; i++) {
4348 CellLayout cl = (CellLayout) getChildAt(i);
4349 saveWorkspaceScreenToDb(cl);
4350 }
4351 }
4352
4353 void saveWorkspaceScreenToDb(CellLayout cl) {
4354 int count = cl.getShortcutsAndWidgets().getChildCount();
4355
Adam Cohendcd297f2013-06-18 13:13:40 -07004356 long screenId = getIdForScreen(cl);
Adam Cohene25af792013-06-06 23:08:25 -07004357 int container = Favorites.CONTAINER_DESKTOP;
4358
Winson Chungf70696d2013-06-25 16:19:53 -07004359 Hotseat hotseat = mLauncher.getHotseat();
Adam Cohene25af792013-06-06 23:08:25 -07004360 if (mLauncher.isHotseatLayout(cl)) {
Adam Cohendcd297f2013-06-18 13:13:40 -07004361 screenId = -1;
Adam Cohene25af792013-06-06 23:08:25 -07004362 container = Favorites.CONTAINER_HOTSEAT;
4363 }
4364
4365 for (int i = 0; i < count; i++) {
4366 View v = cl.getShortcutsAndWidgets().getChildAt(i);
4367 ItemInfo info = (ItemInfo) v.getTag();
4368 // Null check required as the AllApps button doesn't have an item info
4369 if (info != null) {
Winson Chungf70696d2013-06-25 16:19:53 -07004370 int cellX = info.cellX;
4371 int cellY = info.cellY;
4372 if (container == Favorites.CONTAINER_HOTSEAT) {
4373 cellX = hotseat.getCellXFromOrder((int) info.screenId);
4374 cellY = hotseat.getCellYFromOrder((int) info.screenId);
4375 }
4376 LauncherModel.addItemToDatabase(mLauncher, info, container, screenId, cellX,
4377 cellY, false);
Adam Cohene25af792013-06-06 23:08:25 -07004378 }
4379 if (v instanceof FolderIcon) {
4380 FolderIcon fi = (FolderIcon) v;
4381 fi.getFolder().addItemLocationsInDatabase();
4382 }
4383 }
4384 }
4385
Winson Chunga48487a2012-03-20 16:19:37 -07004386 @Override
Winson Chungeeb5bbc2013-11-13 15:47:05 -08004387 public float getIntrinsicIconScaleFactor() {
4388 return 1f;
4389 }
4390
4391 @Override
Winson Chung043f2af2012-03-01 16:09:54 -08004392 public boolean supportsFlingToDelete() {
4393 return true;
4394 }
4395
Winson Chunga48487a2012-03-20 16:19:37 -07004396 @Override
Mathew Inwood1eeb3fc2013-11-25 17:01:34 +00004397 public boolean supportsAppInfoDropTarget() {
4398 return false;
4399 }
4400
4401 @Override
4402 public boolean supportsDeleteDropTarget() {
4403 return true;
4404 }
4405
4406 @Override
Winson Chunga48487a2012-03-20 16:19:37 -07004407 public void onFlingToDelete(DragObject d, int x, int y, PointF vec) {
4408 // Do nothing
4409 }
4410
4411 @Override
4412 public void onFlingToDeleteCompleted() {
4413 // Do nothing
4414 }
4415
Michael Jurka0280c3b2010-09-17 15:00:07 -07004416 public boolean isDropEnabled() {
4417 return true;
4418 }
4419
Michael Jurka0142d492010-08-25 17:46:15 -07004420 @Override
4421 protected void onRestoreInstanceState(Parcelable state) {
4422 super.onRestoreInstanceState(state);
4423 Launcher.setScreen(mCurrentPage);
4424 }
4425
4426 @Override
Adam Cohen1462de32012-07-24 22:34:36 -07004427 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
4428 // We don't dispatch restoreInstanceState to our children using this code path.
Dave Hawkeya8881582013-09-17 15:55:33 -06004429 // Some pages will be restored immediately as their items are bound immediately, and
Adam Cohen1462de32012-07-24 22:34:36 -07004430 // others we will need to wait until after their items are bound.
4431 mSavedStates = container;
4432 }
4433
4434 public void restoreInstanceStateForChild(int child) {
4435 if (mSavedStates != null) {
4436 mRestoredPages.add(child);
4437 CellLayout cl = (CellLayout) getChildAt(child);
Adam Cohenb0ee0812013-12-03 10:51:45 -08004438 if (cl != null) {
4439 cl.restoreInstanceState(mSavedStates);
4440 }
Adam Cohen1462de32012-07-24 22:34:36 -07004441 }
4442 }
4443
4444 public void restoreInstanceStateForRemainingPages() {
4445 int count = getChildCount();
4446 for (int i = 0; i < count; i++) {
4447 if (!mRestoredPages.contains(i)) {
4448 restoreInstanceStateForChild(i);
4449 }
4450 }
4451 mRestoredPages.clear();
Winson Chungd8e596d2013-10-21 17:14:12 -07004452 mSavedStates = null;
Adam Cohen1462de32012-07-24 22:34:36 -07004453 }
4454
4455 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004456 public void scrollLeft() {
Michael Jurkad74c9842011-07-10 12:44:21 -07004457 if (!isSmall() && !mIsSwitchingState) {
Michael Jurka0142d492010-08-25 17:46:15 -07004458 super.scrollLeft();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004459 }
Adam Cohen95bb8002011-07-03 23:40:28 -07004460 Folder openFolder = getOpenFolder();
4461 if (openFolder != null) {
4462 openFolder.completeDragExit();
4463 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004464 }
4465
Michael Jurka0142d492010-08-25 17:46:15 -07004466 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004467 public void scrollRight() {
Michael Jurkad74c9842011-07-10 12:44:21 -07004468 if (!isSmall() && !mIsSwitchingState) {
Michael Jurka0142d492010-08-25 17:46:15 -07004469 super.scrollRight();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004470 }
Adam Cohen95bb8002011-07-03 23:40:28 -07004471 Folder openFolder = getOpenFolder();
4472 if (openFolder != null) {
4473 openFolder.completeDragExit();
4474 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004475 }
4476
Patrick Dubroy1262e362010-10-06 15:49:50 -07004477 @Override
Winson Chung3e0839e2011-10-03 15:15:18 -07004478 public boolean onEnterScrollArea(int x, int y, int direction) {
Winson Chung1c4cf4a2011-07-29 14:49:10 -07004479 // Ignore the scroll area if we are dragging over the hot seat
Daniel Sandlercc8befa2013-06-11 14:45:48 -04004480 boolean isPortrait = !LauncherAppState.isScreenLandscape(getContext());
Winson Chung10bfc6e2012-03-28 15:41:26 -07004481 if (mLauncher.getHotseat() != null && isPortrait) {
Winson Chung1c4cf4a2011-07-29 14:49:10 -07004482 Rect r = new Rect();
4483 mLauncher.getHotseat().getHitRect(r);
4484 if (r.contains(x, y)) {
Winson Chung3e0839e2011-10-03 15:15:18 -07004485 return false;
Winson Chung1c4cf4a2011-07-29 14:49:10 -07004486 }
4487 }
4488
Winson Chung3e0839e2011-10-03 15:15:18 -07004489 boolean result = false;
Adam Cohen61a9a5c2013-08-14 13:32:04 -07004490 if (!isSmall() && !mIsSwitchingState && getOpenFolder() == null) {
Michael Jurkad718d6a2010-10-14 15:35:17 -07004491 mInScrollArea = true;
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08004492
Winson Chung360e63f2012-04-27 13:48:05 -07004493 final int page = getNextPage() +
Winson Chungaa15ffe2012-01-18 15:45:28 -08004494 (direction == DragController.SCROLL_LEFT ? -1 : 1);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07004495 // We always want to exit the current layout to ensure parity of enter / exit
4496 setCurrentDropLayout(null);
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08004497
Winson Chungaa15ffe2012-01-18 15:45:28 -08004498 if (0 <= page && page < getChildCount()) {
Winson Chung482a5b62013-07-31 17:19:51 -07004499 // Ensure that we are not dragging over to the custom content screen
4500 if (getScreenIdForPageIndex(page) == CUSTOM_CONTENT_SCREEN_ID) {
4501 return false;
4502 }
4503
Winson Chungaa15ffe2012-01-18 15:45:28 -08004504 CellLayout layout = (CellLayout) getChildAt(page);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07004505 setCurrentDragOverlappingLayout(layout);
Winson Chungc07918d2011-07-01 15:35:26 -07004506
4507 // Workspace is responsible for drawing the edge glow on adjacent pages,
4508 // so we need to redraw the workspace when this may have changed.
4509 invalidate();
Winson Chung3e0839e2011-10-03 15:15:18 -07004510 result = true;
Michael Jurkad718d6a2010-10-14 15:35:17 -07004511 }
Patrick Dubroy1262e362010-10-06 15:49:50 -07004512 }
Winson Chung3e0839e2011-10-03 15:15:18 -07004513 return result;
Patrick Dubroy1262e362010-10-06 15:49:50 -07004514 }
4515
4516 @Override
Winson Chung3e0839e2011-10-03 15:15:18 -07004517 public boolean onExitScrollArea() {
4518 boolean result = false;
Michael Jurkad718d6a2010-10-14 15:35:17 -07004519 if (mInScrollArea) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07004520 invalidate();
4521 CellLayout layout = getCurrentDropLayout();
4522 setCurrentDropLayout(layout);
4523 setCurrentDragOverlappingLayout(layout);
4524
Adam Cohen8a18afc2011-12-13 15:57:01 -08004525 result = true;
Winson Chungc07918d2011-07-01 15:35:26 -07004526 mInScrollArea = false;
Patrick Dubroy1262e362010-10-06 15:49:50 -07004527 }
Winson Chung3e0839e2011-10-03 15:15:18 -07004528 return result;
Patrick Dubroy1262e362010-10-06 15:49:50 -07004529 }
4530
Winson Chungc07918d2011-07-01 15:35:26 -07004531 private void onResetScrollArea() {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07004532 setCurrentDragOverlappingLayout(null);
Winson Chungc07918d2011-07-01 15:35:26 -07004533 mInScrollArea = false;
4534 }
4535
Winson Chung3d503fb2011-07-13 17:25:49 -07004536 /**
4537 * Returns a specific CellLayout
4538 */
4539 CellLayout getParentCellLayoutForView(View v) {
4540 ArrayList<CellLayout> layouts = getWorkspaceAndHotseatCellLayouts();
4541 for (CellLayout layout : layouts) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004542 if (layout.getShortcutsAndWidgets().indexOfChild(v) > -1) {
Winson Chung3d503fb2011-07-13 17:25:49 -07004543 return layout;
4544 }
4545 }
4546 return null;
4547 }
4548
4549 /**
4550 * Returns a list of all the CellLayouts in the workspace.
4551 */
4552 ArrayList<CellLayout> getWorkspaceAndHotseatCellLayouts() {
4553 ArrayList<CellLayout> layouts = new ArrayList<CellLayout>();
4554 int screenCount = getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004555 for (int screen = 0; screen < screenCount; screen++) {
Winson Chung3d503fb2011-07-13 17:25:49 -07004556 layouts.add(((CellLayout) getChildAt(screen)));
4557 }
4558 if (mLauncher.getHotseat() != null) {
4559 layouts.add(mLauncher.getHotseat().getLayout());
4560 }
4561 return layouts;
4562 }
4563
4564 /**
4565 * We should only use this to search for specific children. Do not use this method to modify
Michael Jurkaa52570f2012-03-20 03:18:20 -07004566 * ShortcutsAndWidgetsContainer directly. Includes ShortcutAndWidgetContainers from
4567 * the hotseat and workspace pages
Winson Chung3d503fb2011-07-13 17:25:49 -07004568 */
Michael Jurkaa52570f2012-03-20 03:18:20 -07004569 ArrayList<ShortcutAndWidgetContainer> getAllShortcutAndWidgetContainers() {
4570 ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
4571 new ArrayList<ShortcutAndWidgetContainer>();
Winson Chung3d503fb2011-07-13 17:25:49 -07004572 int screenCount = getChildCount();
4573 for (int screen = 0; screen < screenCount; screen++) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004574 childrenLayouts.add(((CellLayout) getChildAt(screen)).getShortcutsAndWidgets());
Winson Chung3d503fb2011-07-13 17:25:49 -07004575 }
4576 if (mLauncher.getHotseat() != null) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004577 childrenLayouts.add(mLauncher.getHotseat().getLayout().getShortcutsAndWidgets());
Winson Chung3d503fb2011-07-13 17:25:49 -07004578 }
4579 return childrenLayouts;
4580 }
4581
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004582 public Folder getFolderForTag(final Object tag) {
4583 final Folder[] value = new Folder[1];
4584 mapOverShortcuts(MAP_NO_RECURSE, new ShortcutOperator() {
4585 @Override
4586 public boolean evaluate(ItemInfo info, View v, View parent) {
4587 if (v instanceof Folder) {
4588 Folder f = (Folder) v;
Winson Chungaafa03c2010-06-11 17:34:16 -07004589 if (f.getInfo() == tag && f.getInfo().opened) {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004590 value[0] = f;
4591 return true;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004592 }
4593 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004594 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004595 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004596 });
4597 return value[0];
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004598 }
4599
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004600 public View getViewForTag(final Object tag) {
4601 final View[] value = new View[1];
4602 mapOverShortcuts(MAP_NO_RECURSE, new ShortcutOperator() {
4603 @Override
4604 public boolean evaluate(ItemInfo info, View v, View parent) {
4605 if (v.getTag() == tag) {
4606 value[0] = v;
4607 return true;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004608 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004609 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004610 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004611 });
4612 return value[0];
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004613 }
4614
Adam Cohendf035382011-04-11 17:22:04 -07004615 void clearDropTargets() {
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004616 mapOverShortcuts(MAP_NO_RECURSE, new ShortcutOperator() {
4617 @Override
4618 public boolean evaluate(ItemInfo info, View v, View parent) {
Adam Cohendf035382011-04-11 17:22:04 -07004619 if (v instanceof DropTarget) {
4620 mDragController.removeDropTarget((DropTarget) v);
4621 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004622 // not done, process all the shortcuts
4623 return false;
Adam Cohendf035382011-04-11 17:22:04 -07004624 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004625 });
Adam Cohendf035382011-04-11 17:22:04 -07004626 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004627
Winson Chung83892cc2013-05-01 16:53:33 -07004628 // Removes ALL items that match a given package name, this is usually called when a package
4629 // has been removed and we want to remove all components (widgets, shortcuts, apps) that
4630 // belong to that package.
Kenny Guyed131872014-04-30 03:02:21 +01004631 void removeItemsByPackageName(final ArrayList<String> packages, final UserHandleCompat user) {
Winson Chung64359a52013-07-08 17:17:08 -07004632 final HashSet<String> packageNames = new HashSet<String>();
Winson Chungcd810732012-06-18 16:45:43 -07004633 packageNames.addAll(packages);
Joe Onorato64e6be72010-03-05 15:05:52 -05004634
Winson Chung64359a52013-07-08 17:17:08 -07004635 // Filter out all the ItemInfos that this is going to affect
4636 final HashSet<ItemInfo> infos = new HashSet<ItemInfo>();
4637 final HashSet<ComponentName> cns = new HashSet<ComponentName>();
Winson Chung83892cc2013-05-01 16:53:33 -07004638 ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
4639 for (CellLayout layoutParent : cellLayouts) {
4640 ViewGroup layout = layoutParent.getShortcutsAndWidgets();
4641 int childCount = layout.getChildCount();
4642 for (int i = 0; i < childCount; ++i) {
4643 View view = layout.getChildAt(i);
Winson Chung64359a52013-07-08 17:17:08 -07004644 infos.add((ItemInfo) view.getTag());
Winson Chung83892cc2013-05-01 16:53:33 -07004645 }
4646 }
Winson Chung64359a52013-07-08 17:17:08 -07004647 LauncherModel.ItemInfoFilter filter = new LauncherModel.ItemInfoFilter() {
4648 @Override
4649 public boolean filterItem(ItemInfo parent, ItemInfo info,
4650 ComponentName cn) {
Kenny Guyed131872014-04-30 03:02:21 +01004651 if (packageNames.contains(cn.getPackageName())
4652 && info.user.equals(user)) {
Winson Chung64359a52013-07-08 17:17:08 -07004653 cns.add(cn);
4654 return true;
4655 }
4656 return false;
4657 }
4658 };
4659 LauncherModel.filterItemInfos(infos, filter);
Winson Chung83892cc2013-05-01 16:53:33 -07004660
Winson Chung64359a52013-07-08 17:17:08 -07004661 // Remove the affected components
Kenny Guyed131872014-04-30 03:02:21 +01004662 removeItemsByComponentName(cns, user);
Winson Chung83892cc2013-05-01 16:53:33 -07004663 }
4664
4665 // Removes items that match the application info specified, when applications are removed
4666 // as a part of an update, this is called to ensure that other widgets and application
4667 // shortcuts are not removed.
Kenny Guyed131872014-04-30 03:02:21 +01004668 void removeItemsByApplicationInfo(final ArrayList<AppInfo> appInfos, UserHandleCompat user) {
Winson Chung83892cc2013-05-01 16:53:33 -07004669 // Just create a hash table of all the specific components that this will affect
4670 HashSet<ComponentName> cns = new HashSet<ComponentName>();
Michael Jurkaeadbfc52013-09-04 00:45:37 +02004671 for (AppInfo info : appInfos) {
Winson Chung83892cc2013-05-01 16:53:33 -07004672 cns.add(info.componentName);
4673 }
4674
4675 // Remove all the things
Kenny Guyed131872014-04-30 03:02:21 +01004676 removeItemsByComponentName(cns, user);
Winson Chung83892cc2013-05-01 16:53:33 -07004677 }
4678
Kenny Guyed131872014-04-30 03:02:21 +01004679 void removeItemsByComponentName(final HashSet<ComponentName> componentNames,
4680 final UserHandleCompat user) {
Winson Chung3d503fb2011-07-13 17:25:49 -07004681 ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
4682 for (final CellLayout layoutParent: cellLayouts) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004683 final ViewGroup layout = layoutParent.getShortcutsAndWidgets();
Romain Guy574d20e2009-06-01 15:34:04 -07004684
Winson Chung64359a52013-07-08 17:17:08 -07004685 final HashMap<ItemInfo, View> children = new HashMap<ItemInfo, View>();
4686 for (int j = 0; j < layout.getChildCount(); j++) {
4687 final View view = layout.getChildAt(j);
4688 children.put((ItemInfo) view.getTag(), view);
4689 }
Winson Chungaafa03c2010-06-11 17:34:16 -07004690
Winson Chung64359a52013-07-08 17:17:08 -07004691 final ArrayList<View> childrenToRemove = new ArrayList<View>();
4692 final HashMap<FolderInfo, ArrayList<ShortcutInfo>> folderAppsToRemove =
4693 new HashMap<FolderInfo, ArrayList<ShortcutInfo>>();
4694 LauncherModel.ItemInfoFilter filter = new LauncherModel.ItemInfoFilter() {
4695 @Override
4696 public boolean filterItem(ItemInfo parent, ItemInfo info,
4697 ComponentName cn) {
4698 if (parent instanceof FolderInfo) {
Kenny Guyed131872014-04-30 03:02:21 +01004699 if (componentNames.contains(cn) && info.user.equals(user)) {
Winson Chung64359a52013-07-08 17:17:08 -07004700 FolderInfo folder = (FolderInfo) parent;
4701 ArrayList<ShortcutInfo> appsToRemove;
4702 if (folderAppsToRemove.containsKey(folder)) {
4703 appsToRemove = folderAppsToRemove.get(folder);
4704 } else {
4705 appsToRemove = new ArrayList<ShortcutInfo>();
4706 folderAppsToRemove.put(folder, appsToRemove);
Romain Guy629de3e2010-01-13 12:20:59 -08004707 }
Winson Chung64359a52013-07-08 17:17:08 -07004708 appsToRemove.add((ShortcutInfo) info);
4709 return true;
4710 }
4711 } else {
Kenny Guyed131872014-04-30 03:02:21 +01004712 if (componentNames.contains(cn) && info.user.equals(user)) {
Winson Chung64359a52013-07-08 17:17:08 -07004713 childrenToRemove.add(children.get(info));
4714 return true;
Romain Guy574d20e2009-06-01 15:34:04 -07004715 }
4716 }
Winson Chung64359a52013-07-08 17:17:08 -07004717 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004718 }
Winson Chung64359a52013-07-08 17:17:08 -07004719 };
4720 LauncherModel.filterItemInfos(children.keySet(), filter);
4721
4722 // Remove all the apps from their folders
4723 for (FolderInfo folder : folderAppsToRemove.keySet()) {
4724 ArrayList<ShortcutInfo> appsToRemove = folderAppsToRemove.get(folder);
4725 for (ShortcutInfo info : appsToRemove) {
4726 folder.remove(info);
4727 }
4728 }
4729
4730 // Remove all the other children
4731 for (View child : childrenToRemove) {
4732 // Note: We can not remove the view directly from CellLayoutChildren as this
4733 // does not re-mark the spaces as unoccupied.
4734 layoutParent.removeViewInLayout(child);
4735 if (child instanceof DropTarget) {
4736 mDragController.removeDropTarget((DropTarget) child);
4737 }
4738 }
4739
4740 if (childrenToRemove.size() > 0) {
4741 layout.requestLayout();
4742 layout.invalidate();
4743 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004744 }
Winson Chung2efec4e2012-05-03 12:31:48 -07004745
Winson Chung64359a52013-07-08 17:17:08 -07004746 // Strip all the empty screens
4747 stripEmptyScreens();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004748 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07004749
Winson Chung8c903312013-10-16 17:51:49 -07004750 private void updateShortcut(HashMap<ComponentName, AppInfo> appsMap, ItemInfo info,
4751 View child) {
4752 ComponentName cn = info.getIntent().getComponent();
Chris Wrenb6d4c282014-01-27 14:17:08 -05004753 if (info.getRestoredIntent() != null) {
4754 cn = info.getRestoredIntent().getComponent();
4755 }
Winson Chung8c903312013-10-16 17:51:49 -07004756 if (cn != null) {
Chris Wrenb6d4c282014-01-27 14:17:08 -05004757 AppInfo appInfo = appsMap.get(cn);
Winson Chung8c903312013-10-16 17:51:49 -07004758 if ((appInfo != null) && LauncherModel.isShortcutInfoUpdateable(info)) {
4759 ShortcutInfo shortcutInfo = (ShortcutInfo) info;
4760 BubbleTextView shortcut = (BubbleTextView) child;
Chris Wrenb6d4c282014-01-27 14:17:08 -05004761 shortcutInfo.restore();
Winson Chung8c903312013-10-16 17:51:49 -07004762 shortcutInfo.updateIcon(mIconCache);
4763 shortcutInfo.title = appInfo.title.toString();
4764 shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache);
4765 }
4766 }
4767 }
4768
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004769 interface ShortcutOperator {
4770 /**
4771 * Process the next shortcut, possibly with side-effect on {@link ShortcutOperator#value}.
4772 *
4773 * @param info info for the shortcut
4774 * @param view view for the shortcut
4775 * @param parent containing folder, or null
4776 * @return true if done, false to continue the map
4777 */
4778 public boolean evaluate(ItemInfo info, View view, View parent);
4779 }
4780
4781 /**
4782 * Map the operator over the shortcuts, return the first-non-null value.
4783 *
4784 * @param recurse true: iterate over folder children. false: op get the folders themselves.
4785 * @param op the operator to map over the shortcuts
4786 */
4787 void mapOverShortcuts(boolean recurse, ShortcutOperator op) {
4788 ArrayList<ShortcutAndWidgetContainer> containers = getAllShortcutAndWidgetContainers();
4789 final int containerCount = containers.size();
4790 for (int containerIdx = 0; containerIdx < containerCount; containerIdx++) {
4791 ShortcutAndWidgetContainer container = containers.get(containerIdx);
4792 // map over all the shortcuts on the workspace
4793 final int itemCount = container.getChildCount();
4794 for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
4795 View item = container.getChildAt(itemIdx);
4796 ItemInfo info = (ItemInfo) item.getTag();
4797 if (recurse && info instanceof FolderInfo && item instanceof FolderIcon) {
4798 FolderIcon folder = (FolderIcon) item;
4799 ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
4800 // map over all the children in the folder
4801 final int childCount = folderChildren.size();
4802 for (int childIdx = 0; childIdx < childCount; childIdx++) {
4803 View child = folderChildren.get(childIdx);
4804 info = (ItemInfo) child.getTag();
4805 if (op.evaluate(info, child, folder)) {
4806 return;
4807 }
4808 }
4809 } else {
4810 if (op.evaluate(info, item, null)) {
4811 return;
4812 }
4813 }
4814 }
4815 }
4816 }
4817
Michael Jurkaeadbfc52013-09-04 00:45:37 +02004818 void updateShortcuts(ArrayList<AppInfo> apps) {
Winson Chung8c903312013-10-16 17:51:49 -07004819 // Create a map of the apps to test against
4820 final HashMap<ComponentName, AppInfo> appsMap = new HashMap<ComponentName, AppInfo>();
4821 for (AppInfo ai : apps) {
4822 appsMap.put(ai.componentName, ai);
4823 }
4824
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004825 mapOverShortcuts(MAP_RECURSE, new ShortcutOperator() {
4826 @Override
4827 public boolean evaluate(ItemInfo info, View v, View parent) {
4828 if (info instanceof ShortcutInfo) {
Winson Chung8c903312013-10-16 17:51:49 -07004829 updateShortcut(appsMap, info, v);
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004830 if (parent != null) {
4831 parent.invalidate();
4832 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07004833 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004834 // process all the shortcuts
4835 return false;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07004836 }
Chris Wrenaeff7ea2014-02-14 16:59:24 -05004837 });
4838 }
4839
4840 public void updatePackageState(final String pkgName, final int state) {
4841 mapOverShortcuts(MAP_RECURSE, new ShortcutOperator() {
4842 @Override
4843 public boolean evaluate(ItemInfo info, View v, View parent) {
4844 if (info instanceof ShortcutInfo
4845 && ((ShortcutInfo) info).isPromiseFor(pkgName)
4846 && v instanceof BubbleTextView) {
4847 ((BubbleTextView)v).setState(state);
4848 }
4849 // process all the shortcuts
4850 return false;
4851 }
4852 });
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07004853 }
4854
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004855 private void moveToScreen(int page, boolean animate) {
Winson Chungde1af762011-07-21 16:44:07 -07004856 if (!isSmall()) {
4857 if (animate) {
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004858 snapToPage(page);
Winson Chungde1af762011-07-21 16:44:07 -07004859 } else {
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004860 setCurrentPage(page);
Winson Chungde1af762011-07-21 16:44:07 -07004861 }
Joe Onoratoc45b1682010-01-11 18:48:40 -05004862 }
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004863 View child = getChildAt(page);
Winson Chung64359a52013-07-08 17:17:08 -07004864 if (child != null) {
4865 child.requestFocus();
4866 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004867 }
4868
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004869 void moveToDefaultScreen(boolean animate) {
4870 moveToScreen(mDefaultPage, animate);
4871 }
4872
4873 void moveToCustomContentScreen(boolean animate) {
4874 if (hasCustomContent()) {
4875 int ccIndex = getPageIndexForScreenId(CUSTOM_CONTENT_SCREEN_ID);
4876 if (animate) {
4877 snapToPage(ccIndex);
4878 } else {
4879 setCurrentPage(ccIndex);
4880 }
4881 View child = getChildAt(ccIndex);
4882 if (child != null) {
4883 child.requestFocus();
4884 }
4885 }
Adam Cohen76128b62013-11-14 16:43:59 +00004886 exitWidgetResizeMode();
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004887 }
4888
Michael Jurka0142d492010-08-25 17:46:15 -07004889 @Override
Winson Chung7819a562013-09-19 15:55:45 -07004890 protected PageIndicator.PageMarkerResources getPageIndicatorMarker(int pageIndex) {
4891 long screenId = getScreenIdForPageIndex(pageIndex);
4892 if (screenId == EXTRA_EMPTY_SCREEN_ID) {
Michael Jurkafe0ace32013-10-03 01:05:14 -07004893 int count = mScreenOrder.size() - numCustomPages();
Winson Chung7819a562013-09-19 15:55:45 -07004894 if (count > 1) {
Winson Chungba4e52f2013-10-01 17:22:13 -07004895 return new PageIndicator.PageMarkerResources(R.drawable.ic_pageindicator_current,
Winson Chung7819a562013-09-19 15:55:45 -07004896 R.drawable.ic_pageindicator_add);
4897 }
Winson Chung82dfe582013-07-26 15:07:49 -07004898 }
Winson Chung7819a562013-09-19 15:55:45 -07004899
Winson Chung82dfe582013-07-26 15:07:49 -07004900 return super.getPageIndicatorMarker(pageIndex);
4901 }
4902
4903 @Override
Michael Jurka0142d492010-08-25 17:46:15 -07004904 public void syncPages() {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004905 }
Michael Jurka0142d492010-08-25 17:46:15 -07004906
4907 @Override
Winson Chungf314b0e2011-08-16 11:54:27 -07004908 public void syncPageItems(int page, boolean immediate) {
Michael Jurka0142d492010-08-25 17:46:15 -07004909 }
Winson Chung6a0f57d2011-06-29 20:10:49 -07004910
Adam Cohen53805212013-10-01 10:39:23 -07004911 protected String getPageIndicatorDescription() {
4912 String settings = getResources().getString(R.string.settings_button_text);
4913 return getCurrentPageDescription() + ", " + settings;
4914 }
4915
Winson Chung6a0f57d2011-06-29 20:10:49 -07004916 protected String getCurrentPageDescription() {
4917 int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
Adam Cohen53805212013-10-01 10:39:23 -07004918 int delta = numCustomPages();
4919 if (hasCustomContent() && getNextPage() == 0) {
4920 return mCustomContentDescription;
4921 }
Michael Jurka8b805b12012-04-18 14:23:14 -07004922 return String.format(getContext().getString(R.string.workspace_scroll_format),
Adam Cohen53805212013-10-01 10:39:23 -07004923 page + 1 - delta, getChildCount() - delta);
Winson Chung6a0f57d2011-06-29 20:10:49 -07004924 }
Adam Cohen8dfcba42011-07-07 16:38:18 -07004925
4926 public void getLocationInDragLayer(int[] loc) {
4927 mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
4928 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004929}