blob: d742d429a252bbe578b00625e439c15b4477e6e2 [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;
34import android.content.Intent;
Michael Jurkaadc574c2013-09-12 00:05:02 +020035import android.content.SharedPreferences;
Patrick Dubroy7247f632010-08-04 16:02:59 -070036import android.content.res.Resources;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080037import android.content.res.TypedArray;
Joe Onorato4be866d2010-10-10 11:26:02 -070038import android.graphics.Bitmap;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080039import android.graphics.Canvas;
Michael Jurkaa63c4522010-08-19 13:52:27 -070040import android.graphics.Matrix;
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;
Joe Onorato956091b2010-02-19 12:47:40 -080047import android.os.IBinder;
Adam Powell495f2892010-04-16 16:40:55 -070048import android.os.Parcelable;
Adam Cohen53805212013-10-01 10:39:23 -070049import android.support.v4.view.ViewCompat;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080050import android.util.AttributeSet;
Daniel Sandler291ad122010-05-24 16:03:53 -040051import android.util.Log;
Adam Cohen1462de32012-07-24 22:34:36 -070052import android.util.SparseArray;
Michael Jurkacc07e7a2013-08-26 20:56:35 +020053import android.view.Choreographer;
Winson Chunga34abf82010-11-12 12:10:35 -080054import android.view.Display;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080055import android.view.MotionEvent;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080056import android.view.View;
Winson Chung6e314082011-01-27 16:46:51 -080057import android.view.ViewGroup;
Adam Cohend36d9472013-10-10 15:32:41 -070058import android.view.View.OnClickListener;
Adam Cohen53805212013-10-01 10:39:23 -070059import android.view.accessibility.AccessibilityEvent;
60import android.view.accessibility.AccessibilityManager;
61import android.view.accessibility.AccessibilityNodeInfo;
Patrick Dubroycd68ff52010-10-28 17:57:05 -070062import android.view.animation.DecelerateInterpolator;
Michael Jurkacc07e7a2013-08-26 20:56:35 +020063import android.view.animation.Interpolator;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070064import android.widget.TextView;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080065
Daniel Sandler325dc232013-06-05 22:57:57 -040066import com.android.launcher3.FolderIcon.FolderRingAnimator;
Adam Cohenbffe7452013-07-22 18:21:45 -070067import com.android.launcher3.Launcher.CustomContentCallbacks;
Daniel Sandler325dc232013-06-05 22:57:57 -040068import com.android.launcher3.LauncherSettings.Favorites;
Romain Guyedcce092010-03-04 13:03:17 -080069
Adam Cohen716b51e2011-06-30 12:09:54 -070070import java.util.ArrayList;
Adam Cohendcd297f2013-06-18 13:13:40 -070071import java.util.HashMap;
Adam Cohen716b51e2011-06-30 12:09:54 -070072import java.util.HashSet;
Michael Jurka8bd65f52012-06-25 14:56:22 -070073import java.util.Iterator;
Adam Cohen716b51e2011-06-30 12:09:54 -070074
The Android Open Source Project31dd5032009-03-03 19:32:27 -080075/**
Michael Jurka0142d492010-08-25 17:46:15 -070076 * The workspace is a wide area with a wallpaper and a finite number of pages.
77 * Each page contains a number of icons, folders or widgets the user can
Winson Chungaafa03c2010-06-11 17:34:16 -070078 * interact with. A workspace is meant to be used with a fixed width only.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080079 */
Michael Jurka0142d492010-08-25 17:46:15 -070080public class Workspace extends SmoothPagedView
Michael Jurkad74c9842011-07-10 12:44:21 -070081 implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
John Spurlock77e1f472013-09-11 10:09:51 -040082 DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener,
83 Insettable {
Joe Onorato3a8820b2009-11-10 15:06:42 -080084 private static final String TAG = "Launcher.Workspace";
Michael Jurka0142d492010-08-25 17:46:15 -070085
Adam Cohenf34bab52010-09-30 14:11:56 -070086 // Y rotation to apply to the workspace screens
Adam Cohenb5ba0972011-09-07 18:02:31 -070087 private static final float WORKSPACE_OVERSCROLL_ROTATION = 24f;
Adam Cohena985e592010-09-09 11:23:48 -070088
Adam Cohen68d73932010-11-15 10:50:58 -080089 private static final int CHILDREN_OUTLINE_FADE_OUT_DELAY = 0;
90 private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375;
Winson Chung9171e6d2010-11-17 17:39:27 -080091 private static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100;
Adam Cohenf34bab52010-09-30 14:11:56 -070092
Adam Cohenad4e15c2013-10-17 16:21:35 -070093 protected static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400;
94 protected static final int FADE_EMPTY_SCREEN_DURATION = 150;
95
Winson Chungf135c6c2010-11-18 16:32:08 -080096 private static final int BACKGROUND_FADE_OUT_DURATION = 350;
Adam Cohened51cc92011-08-01 20:28:08 -070097 private static final int ADJACENT_SCREEN_DROP_DURATION = 300;
Adam Cohen265b9a62011-12-07 14:37:18 -080098 private static final int FLING_THRESHOLD_VELOCITY = 500;
Adam Cohened51cc92011-08-01 20:28:08 -070099
Adam Cohenf358a4b2013-07-23 16:47:31 -0700100 private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f;
101
Winson Chung9171e6d2010-11-17 17:39:27 -0800102 // These animators are used to fade the children's outlines
103 private ObjectAnimator mChildrenOutlineFadeInAnimation;
104 private ObjectAnimator mChildrenOutlineFadeOutAnimation;
105 private float mChildrenOutlineAlpha = 0;
106
107 // These properties refer to the background protection gradient used for AllApps and Customize
Michael Jurkae0f5a612011-02-07 16:45:41 -0800108 private ValueAnimator mBackgroundFadeInAnimation;
109 private ValueAnimator mBackgroundFadeOutAnimation;
Winson Chung9171e6d2010-11-17 17:39:27 -0800110 private Drawable mBackground;
Michael Jurka25356e72011-03-03 14:53:11 -0800111 boolean mDrawBackground = true;
Adam Cohenf34bab52010-09-30 14:11:56 -0700112 private float mBackgroundAlpha = 0;
113
Adam Cohen3d1b2b42013-08-14 15:57:58 -0700114 private static final long CUSTOM_CONTENT_GESTURE_DELAY = 200;
115 private long mTouchDownTime = -1;
116 private long mCustomContentShowTime = -1;
117
Adam Cohen22cba7f2013-07-19 16:14:00 -0700118 private LayoutTransition mLayoutTransition;
Dianne Hackborn8f573952009-08-10 23:21:09 -0700119 private final WallpaperManager mWallpaperManager;
Michael Jurka9c6fbed2011-03-02 17:41:34 -0800120 private IBinder mWindowToken;
Winson Chungaafa03c2010-06-11 17:34:16 -0700121
Winson Chung9e6a0a22013-08-27 11:58:12 -0700122 private int mOriginalDefaultPage;
Michael Jurka0142d492010-08-25 17:46:15 -0700123 private int mDefaultPage;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800124
Adam Cohen5084cba2013-09-03 12:01:16 -0700125 private ShortcutAndWidgetContainer mDragSourceInternal;
Adam Cohen53805212013-10-01 10:39:23 -0700126 private static boolean sAccessibilityEnabled;
Adam Cohen5084cba2013-09-03 12:01:16 -0700127
Adam Cohendcd297f2013-06-18 13:13:40 -0700128 // The screen id used for the empty screen always present to the right.
129 private final static long EXTRA_EMPTY_SCREEN_ID = -201;
130 private final static long CUSTOM_CONTENT_SCREEN_ID = -301;
131
132 private HashMap<Long, CellLayout> mWorkspaceScreens = new HashMap<Long, CellLayout>();
133 private ArrayList<Long> mScreenOrder = new ArrayList<Long>();
134
Adam Cohenad4e15c2013-10-17 16:21:35 -0700135 private Runnable mRemoveEmptyScreenRunnable;
136
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800137 /**
138 * CellInfo for the cell that is currently being dragged
139 */
140 private CellLayout.CellInfo mDragInfo;
Winson Chungaafa03c2010-06-11 17:34:16 -0700141
Jeff Sharkey70864282009-04-07 21:08:40 -0700142 /**
143 * Target drop area calculated during last acceptDrop call.
144 */
Adam Cohenc0dcf592011-06-01 15:30:43 -0700145 private int[] mTargetCell = new int[2];
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700146 private int mDragOverX = -1;
147 private int mDragOverY = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800148
Adam Cohena897f392012-04-27 18:12:05 -0700149 static Rect mLandscapeCellLayoutMetrics = null;
150 static Rect mPortraitCellLayoutMetrics = null;
151
Adam Cohenbffe7452013-07-22 18:21:45 -0700152 CustomContentCallbacks mCustomContentCallbacks;
153 boolean mCustomContentShowing;
Adam Cohenc36fa5c2013-08-29 11:54:42 -0700154 private float mLastCustomContentScrollProgress = -1f;
Adam Cohen53805212013-10-01 10:39:23 -0700155 private String mCustomContentDescription = "";
Adam Cohenbffe7452013-07-22 18:21:45 -0700156
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700157 /**
158 * The CellLayout that is currently being dragged over
159 */
160 private CellLayout mDragTargetLayout = null;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700161 /**
162 * The CellLayout that we will show as glowing
163 */
164 private CellLayout mDragOverlappingLayout = null;
165
166 /**
167 * The CellLayout which will be dropped to
168 */
169 private CellLayout mDropToLayout = null;
170
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800171 private Launcher mLauncher;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800172 private IconCache mIconCache;
Joe Onorato00acb122009-08-04 16:04:30 -0400173 private DragController mDragController;
Winson Chungaafa03c2010-06-11 17:34:16 -0700174
Michael Jurka4516c112010-10-07 15:13:47 -0700175 // These are temporary variables to prevent having to allocate a new object just to
176 // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800177 private int[] mTempCell = new int[2];
Adam Cohen7d30a372013-07-01 17:03:59 -0700178 private int[] mTempPt = new int[2];
Jeff Sharkey70864282009-04-07 21:08:40 -0700179 private int[] mTempEstimate = new int[2];
Adam Cohene3e27a82011-04-15 12:07:39 -0700180 private float[] mDragViewVisualCenter = new float[2];
Michael Jurka4516c112010-10-07 15:13:47 -0700181 private float[] mTempCellLayoutCenterCoordinates = new float[2];
Michael Jurka0280c3b2010-09-17 15:00:07 -0700182 private Matrix mTempInverseMatrix = new Matrix();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800183
Michael Jurkac2f7f472010-12-14 15:34:42 -0800184 private SpringLoadedDragController mSpringLoadedDragController;
Winson Chungb26f3d62011-06-02 10:49:29 -0700185 private float mSpringLoadedShrinkFactor;
Adam Cohenf358a4b2013-07-23 16:47:31 -0700186 private float mOverviewModeShrinkFactor;
Adam Cohen410f3cd2013-09-22 12:09:32 -0700187 private int mOverviewModePageOffset;
Michael Jurkad3ef3062010-11-23 16:23:58 -0800188
Patrick Dubroy1262e362010-10-06 15:49:50 -0700189 // State variable that indicates whether the pages are small (ie when you're
Michael Jurkadee05892010-07-27 10:01:56 -0700190 // in all apps or customize mode)
Michael Jurkad74c9842011-07-10 12:44:21 -0700191
Adam Cohenf358a4b2013-07-23 16:47:31 -0700192 enum State { NORMAL, SPRING_LOADED, SMALL, OVERVIEW};
Adam Cohen7777d962011-08-18 18:58:38 -0700193 private State mState = State.NORMAL;
Michael Jurkad74c9842011-07-10 12:44:21 -0700194 private boolean mIsSwitchingState = false;
Michael Jurkad74c9842011-07-10 12:44:21 -0700195
Michael Jurkad74c9842011-07-10 12:44:21 -0700196 boolean mAnimatingViewIntoPlace = false;
197 boolean mIsDragOccuring = false;
198 boolean mChildrenLayersEnabled = true;
Michael Jurkadee05892010-07-27 10:01:56 -0700199
Adam Cohenaccfd562013-07-12 14:40:40 -0700200 private boolean mStripScreensOnPageStopMoving = false;
201
Patrick Dubroy54fa3b92010-11-17 12:18:45 -0800202 /** Is the user is dragging an item near the edge of a page? */
Patrick Dubroy1262e362010-10-06 15:49:50 -0700203 private boolean mInScrollArea = false;
204
Daniel Sandlere4f98912013-06-25 15:13:26 -0400205 private HolographicOutlineHelper mOutlineHelper;
Joe Onorato4be866d2010-10-10 11:26:02 -0700206 private Bitmap mDragOutline = null;
Patrick Dubroy8e58e912010-10-14 13:21:48 -0700207 private final Rect mTempRect = new Rect();
208 private final int[] mTempXY = new int[2];
Michael Jurkad51f33a2012-06-28 15:35:26 -0700209 private int[] mTempVisiblePagesRange = new int[2];
Michael Jurkab06d95f2012-04-02 06:26:53 -0700210 private boolean mOverscrollTransformsSet;
Adam Cohena29f5042013-09-26 14:29:35 -0700211 private float mLastOverscrollPivotX;
Michael Jurkaf8304f02012-04-26 13:33:26 -0700212 public static final int DRAG_BITMAP_PADDING = 2;
Michael Jurka869390b2012-05-06 15:55:19 -0700213 private boolean mWorkspaceFadeInAdjacentScreens;
Joe Onorato4be866d2010-10-10 11:26:02 -0700214
Michael Jurkaab1983f2011-01-18 15:50:17 -0800215 WallpaperOffsetInterpolator mWallpaperOffset;
Adam Cohen26976d92011-03-22 15:33:33 -0700216 private Runnable mDelayedResizeRunnable;
Winson Chungf0c6ae02012-03-21 16:10:31 -0700217 private Runnable mDelayedSnapToPageRunnable;
Michael Jurka84f2ce72012-04-13 15:08:01 -0700218 private Point mDisplaySize = new Point();
Adam Cohen94309882012-06-08 16:24:56 -0700219 private int mCameraDistance;
Michael Jurkac5b262c2011-01-12 20:24:50 -0800220
Adam Cohen19072da2011-05-31 14:30:45 -0700221 // Variables relating to the creation of user folders by hovering shortcuts over shortcuts
Adam Cohen482ed822012-03-02 14:15:13 -0800222 private static final int FOLDER_CREATION_TIMEOUT = 0;
223 private static final int REORDER_TIMEOUT = 250;
Adam Cohen19072da2011-05-31 14:30:45 -0700224 private final Alarm mFolderCreationAlarm = new Alarm();
Adam Cohen482ed822012-03-02 14:15:13 -0800225 private final Alarm mReorderAlarm = new Alarm();
Adam Cohen19072da2011-05-31 14:30:45 -0700226 private FolderRingAnimator mDragFolderRingAnimator = null;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700227 private FolderIcon mDragOverFolderIcon = null;
Adam Cohen19072da2011-05-31 14:30:45 -0700228 private boolean mCreateUserFolderOnDrop = false;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700229 private boolean mAddToExistingFolderOnDrop = false;
230 private DropTarget.DragEnforcer mDragEnforcer;
Adam Cohen3aff81c2012-05-16 21:01:01 -0700231 private float mMaxDistanceForFolderCreation;
Adam Cohen073a46f2011-05-17 16:28:09 -0700232
Adam Cohenf8d28232011-02-01 21:47:00 -0800233 // Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget)
234 private float mXDown;
235 private float mYDown;
236 final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
237 final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
238 final static float TOUCH_SLOP_DAMPING_FACTOR = 4;
239
Adam Cohened66b2b2012-01-23 17:28:51 -0800240 // Relating to the animation of items being dropped externally
Adam Cohend41fbf52012-02-16 23:53:59 -0800241 public static final int ANIMATE_INTO_POSITION_AND_DISAPPEAR = 0;
242 public static final int ANIMATE_INTO_POSITION_AND_REMAIN = 1;
243 public static final int ANIMATE_INTO_POSITION_AND_RESIZE = 2;
244 public static final int COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION = 3;
245 public static final int CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION = 4;
Adam Cohened66b2b2012-01-23 17:28:51 -0800246
Adam Cohen482ed822012-03-02 14:15:13 -0800247 // Related to dragging, folder creation and reordering
248 private static final int DRAG_MODE_NONE = 0;
249 private static final int DRAG_MODE_CREATE_FOLDER = 1;
250 private static final int DRAG_MODE_ADD_TO_FOLDER = 2;
251 private static final int DRAG_MODE_REORDER = 3;
252 private int mDragMode = DRAG_MODE_NONE;
253 private int mLastReorderX = -1;
254 private int mLastReorderY = -1;
255
Adam Cohen1462de32012-07-24 22:34:36 -0700256 private SparseArray<Parcelable> mSavedStates;
257 private final ArrayList<Integer> mRestoredPages = new ArrayList<Integer>();
258
Adam Cohen4b285c52011-07-21 14:24:06 -0700259 // These variables are used for storing the initial and final values during workspace animations
Adam Cohened51cc92011-08-01 20:28:08 -0700260 private int mSavedScrollX;
261 private float mSavedRotationY;
262 private float mSavedTranslationX;
Adam Cohen7d30a372013-07-01 17:03:59 -0700263
264 private float mCurrentScale;
265 private float mNewScale;
Adam Cohen4b285c52011-07-21 14:24:06 -0700266 private float[] mOldBackgroundAlphas;
Adam Cohen4b285c52011-07-21 14:24:06 -0700267 private float[] mOldAlphas;
Adam Cohen4b285c52011-07-21 14:24:06 -0700268 private float[] mNewBackgroundAlphas;
Adam Cohen4b285c52011-07-21 14:24:06 -0700269 private float[] mNewAlphas;
Adam Cohendcd297f2013-06-18 13:13:40 -0700270 private int mLastChildCount = -1;
Winson Chung70442722012-02-10 15:43:22 -0800271 private float mTransitionProgress;
Adam Cohen4b285c52011-07-21 14:24:06 -0700272
Michael Jurka1e2f4652013-07-08 18:03:46 -0700273 private Runnable mDeferredAction;
274 private boolean mDeferDropAfterUninstall;
275 private boolean mUninstallSuccessful;
276
Romain Guyeeacd562012-10-10 18:47:33 -0700277 private final Runnable mBindPages = new Runnable() {
278 @Override
279 public void run() {
280 mLauncher.getModel().bindRemainingSynchronousPages();
281 }
282 };
283
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800284 /**
285 * Used to inflate the Workspace from XML.
286 *
287 * @param context The application's context.
Michael Jurka0142d492010-08-25 17:46:15 -0700288 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800289 */
290 public Workspace(Context context, AttributeSet attrs) {
291 this(context, attrs, 0);
292 }
293
294 /**
295 * Used to inflate the Workspace from XML.
296 *
297 * @param context The application's context.
Michael Jurka0142d492010-08-25 17:46:15 -0700298 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800299 * @param defStyle Unused.
300 */
301 public Workspace(Context context, AttributeSet attrs, int defStyle) {
302 super(context, attrs, defStyle);
Michael Jurka0142d492010-08-25 17:46:15 -0700303 mContentIsRefreshable = false;
Michael Jurka5f1c5092010-09-03 14:15:02 -0700304
Daniel Sandlere4f98912013-06-25 15:13:26 -0400305 mOutlineHelper = HolographicOutlineHelper.obtain(context);
306
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700307 mDragEnforcer = new DropTarget.DragEnforcer(context);
Winson Chungf0ea4d32011-06-06 14:27:16 -0700308 // With workspace, data is available straight from the get-go
309 setDataIsReady();
310
Michael Jurka8bc66c72012-06-21 08:36:45 -0700311 mLauncher = (Launcher) context;
Winson Chung867ca622012-02-21 15:48:35 -0800312 final Resources res = getResources();
Michael Jurka869390b2012-05-06 15:55:19 -0700313 mWorkspaceFadeInAdjacentScreens = res.getBoolean(R.bool.config_workspaceFadeAdjacentScreens);
314 mFadeInAdjacentScreens = false;
Dianne Hackborn8f573952009-08-10 23:21:09 -0700315 mWallpaperManager = WallpaperManager.getInstance(context);
Winson Chungaafa03c2010-06-11 17:34:16 -0700316
317 TypedArray a = context.obtainStyledAttributes(attrs,
318 R.styleable.Workspace, defStyle, 0);
Winson Chungb26f3d62011-06-02 10:49:29 -0700319 mSpringLoadedShrinkFactor =
320 res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
Adam Cohenf358a4b2013-07-23 16:47:31 -0700321 mOverviewModeShrinkFactor =
322 res.getInteger(R.integer.config_workspaceOverviewShrinkPercentage) / 100.0f;
Adam Cohen410f3cd2013-09-22 12:09:32 -0700323 mOverviewModePageOffset = res.getDimensionPixelSize(R.dimen.overview_mode_page_offset);
Adam Cohen94309882012-06-08 16:24:56 -0700324 mCameraDistance = res.getInteger(R.integer.config_cameraDistance);
Winson Chung9e6a0a22013-08-27 11:58:12 -0700325 mOriginalDefaultPage = mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800326 a.recycle();
327
Michael Jurka8b805b12012-04-18 14:23:14 -0700328 setOnHierarchyChangeListener(this);
Joe Onorato0d44e942009-11-16 18:20:51 -0800329 setHapticFeedbackEnabled(false);
Michael Jurka0142d492010-08-25 17:46:15 -0700330
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800331 initWorkspace();
Winson Chungc35afb22011-02-23 13:01:49 -0800332
333 // Disable multitouch across the workspace/all apps/customize tray
334 setMotionEventSplittingEnabled(true);
Adam Cohen53805212013-10-01 10:39:23 -0700335 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800336 }
337
John Spurlock77e1f472013-09-11 10:09:51 -0400338 @Override
339 public void setInsets(Rect insets) {
340 mInsets.set(insets);
341 }
342
Michael Jurka038f9d82011-11-03 13:50:45 -0700343 // estimate the size of a widget with spans hSpan, vSpan. return MAX_VALUE for each
344 // dimension if unsuccessful
345 public int[] estimateItemSize(int hSpan, int vSpan,
Adam Cohend41fbf52012-02-16 23:53:59 -0800346 ItemInfo itemInfo, boolean springLoaded) {
Michael Jurka038f9d82011-11-03 13:50:45 -0700347 int[] size = new int[2];
348 if (getChildCount() > 0) {
Winson Chungad7db6e2013-10-08 14:01:06 -0700349 // Use the first non-custom page to estimate the child position
350 CellLayout cl = (CellLayout) getChildAt(numCustomPages());
Adam Cohend41fbf52012-02-16 23:53:59 -0800351 Rect r = estimateItemPosition(cl, itemInfo, 0, 0, hSpan, vSpan);
352 size[0] = r.width();
353 size[1] = r.height();
Michael Jurka038f9d82011-11-03 13:50:45 -0700354 if (springLoaded) {
355 size[0] *= mSpringLoadedShrinkFactor;
356 size[1] *= mSpringLoadedShrinkFactor;
357 }
358 return size;
359 } else {
360 size[0] = Integer.MAX_VALUE;
361 size[1] = Integer.MAX_VALUE;
362 return size;
363 }
364 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700365
Adam Cohend41fbf52012-02-16 23:53:59 -0800366 public Rect estimateItemPosition(CellLayout cl, ItemInfo pendingInfo,
Michael Jurka038f9d82011-11-03 13:50:45 -0700367 int hCell, int vCell, int hSpan, int vSpan) {
Adam Cohend41fbf52012-02-16 23:53:59 -0800368 Rect r = new Rect();
Michael Jurka038f9d82011-11-03 13:50:45 -0700369 cl.cellToRect(hCell, vCell, hSpan, vSpan, r);
Michael Jurka038f9d82011-11-03 13:50:45 -0700370 return r;
371 }
372
Adam Cohen5084cba2013-09-03 12:01:16 -0700373 public void onDragStart(final DragSource source, Object info, int dragAction) {
Michael Jurkad74c9842011-07-10 12:44:21 -0700374 mIsDragOccuring = true;
Michael Jurka3a0469d2012-06-21 09:38:41 -0700375 updateChildrenLayersEnabled(false);
Winson Chung641d71d2012-04-26 15:58:01 -0700376 mLauncher.lockScreenOrientation();
Sandeep Siddharthaab2d9d72013-09-17 13:18:24 -0700377 mLauncher.onInteractionBegin();
Michael Jurkaa3d30ad2012-05-08 13:43:43 -0700378 setChildrenBackgroundAlphaMultipliers(1f);
Winson Chungf561bdf2012-05-03 11:20:19 -0700379 // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging
380 InstallShortcutReceiver.enableInstallQueue();
381 UninstallShortcutReceiver.enableUninstallQueue();
Adam Cohen5084cba2013-09-03 12:01:16 -0700382 post(new Runnable() {
383 @Override
384 public void run() {
385 if (mIsDragOccuring) {
386 addExtraEmptyScreenOnDrag();
387 }
388 }
389 });
Michael Jurkad74c9842011-07-10 12:44:21 -0700390 }
391
392 public void onDragEnd() {
393 mIsDragOccuring = false;
Michael Jurka3a0469d2012-06-21 09:38:41 -0700394 updateChildrenLayersEnabled(false);
Winson Chung4b919f82012-05-01 10:44:08 -0700395 mLauncher.unlockScreenOrientation(false);
Winson Chungf561bdf2012-05-03 11:20:19 -0700396
397 // Re-enable any Un/InstallShortcutReceiver and now process any queued items
398 InstallShortcutReceiver.disableAndFlushInstallQueue(getContext());
399 UninstallShortcutReceiver.disableAndFlushUninstallQueue(getContext());
Adam Cohen5084cba2013-09-03 12:01:16 -0700400
Adam Cohen5084cba2013-09-03 12:01:16 -0700401 mDragSourceInternal = null;
Sandeep Siddharthaab2d9d72013-09-17 13:18:24 -0700402 mLauncher.onInteractionEnd();
Michael Jurkad74c9842011-07-10 12:44:21 -0700403 }
404
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800405 /**
406 * Initializes various states for this workspace.
407 */
Michael Jurka0142d492010-08-25 17:46:15 -0700408 protected void initWorkspace() {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800409 Context context = getContext();
Michael Jurka0142d492010-08-25 17:46:15 -0700410 mCurrentPage = mDefaultPage;
411 Launcher.setScreen(mCurrentPage);
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400412 LauncherAppState app = LauncherAppState.getInstance();
Winson Chung5f8afe62013-08-12 16:19:28 -0700413 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800414 mIconCache = app.getIconCache();
Patrick Dubroycd68ff52010-10-28 17:57:05 -0700415 setWillNotDraw(false);
Romain Guyce3cbd12013-02-25 15:00:36 -0800416 setClipChildren(false);
417 setClipToPadding(false);
Adam Cohen7777d962011-08-18 18:58:38 -0700418 setChildrenDrawnWithCacheEnabled(true);
Adam Cohen410f3cd2013-09-22 12:09:32 -0700419
420 // This is a bit of a hack to account for the fact that we translate the workspace
421 // up a bit, and still need to draw the background covering the whole screen.
422 setMinScale(mOverviewModeShrinkFactor - 0.2f);
Adam Cohen22cba7f2013-07-19 16:14:00 -0700423 setupLayoutTransition();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800424
Adam Cohen482ed822012-03-02 14:15:13 -0800425 final Resources res = getResources();
Winson Chungb4b7fa72010-11-18 14:38:53 -0800426 try {
Winson Chungfd3385f2011-06-15 19:51:24 -0700427 mBackground = res.getDrawable(R.drawable.apps_customize_bg);
Winson Chungb4b7fa72010-11-18 14:38:53 -0800428 } catch (Resources.NotFoundException e) {
429 // In this case, we will skip drawing background protection
430 }
Winson Chung9171e6d2010-11-17 17:39:27 -0800431
Michael Jurkaab1983f2011-01-18 15:50:17 -0800432 mWallpaperOffset = new WallpaperOffsetInterpolator();
Adam Cohencff6af82011-09-13 14:51:53 -0700433 Display display = mLauncher.getWindowManager().getDefaultDisplay();
Michael Jurka84f2ce72012-04-13 15:08:01 -0700434 display.getSize(mDisplaySize);
Adam Cohen265b9a62011-12-07 14:37:18 -0800435
Winson Chung5f8afe62013-08-12 16:19:28 -0700436 mMaxDistanceForFolderCreation = (0.55f * grid.iconSizePx);
Adam Cohen265b9a62011-12-07 14:37:18 -0800437 mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800438 }
439
Adam Cohen22cba7f2013-07-19 16:14:00 -0700440 private void setupLayoutTransition() {
441 // We want to show layout transitions when pages are deleted, to close the gap.
442 mLayoutTransition = new LayoutTransition();
443 mLayoutTransition.enableTransitionType(LayoutTransition.DISAPPEARING);
444 mLayoutTransition.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
445 mLayoutTransition.disableTransitionType(LayoutTransition.APPEARING);
446 mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
447 setLayoutTransition(mLayoutTransition);
448 }
449
Winson Chung964df6b2013-10-11 15:55:37 -0700450 void enableLayoutTransitions() {
451 setLayoutTransition(mLayoutTransition);
452 }
453 void disableLayoutTransitions() {
454 setLayoutTransition(null);
455 }
456
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800457 @Override
Adam Cohenf34bab52010-09-30 14:11:56 -0700458 protected int getScrollMode() {
Winson Chungb26f3d62011-06-02 10:49:29 -0700459 return SmoothPagedView.X_LARGE_MODE;
Adam Cohenf34bab52010-09-30 14:11:56 -0700460 }
461
Michael Jurka08ee7702011-08-11 16:53:35 -0700462 @Override
Michael Jurka8b805b12012-04-18 14:23:14 -0700463 public void onChildViewAdded(View parent, View child) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800464 if (!(child instanceof CellLayout)) {
465 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
466 }
Adam Cohen2801caf2011-05-13 20:57:39 -0700467 CellLayout cl = ((CellLayout) child);
468 cl.setOnInterceptTouchListener(this);
Adam Cohen2801caf2011-05-13 20:57:39 -0700469 cl.setClickable(true);
Adam Cohen53805212013-10-01 10:39:23 -0700470 cl.setImportantForAccessibility(ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO);
Winson Chungf70696d2013-06-25 16:19:53 -0700471 super.onChildViewAdded(parent, child);
Michael Jurka8b805b12012-04-18 14:23:14 -0700472 }
473
Michael Jurka920d7f42012-05-14 16:29:55 -0700474 protected boolean shouldDrawChild(View child) {
475 final CellLayout cl = (CellLayout) child;
476 return super.shouldDrawChild(child) &&
Winson Chungf4bd2362013-10-07 17:11:27 -0700477 (mIsSwitchingState ||
478 cl.getShortcutsAndWidgets().getAlpha() > 0 ||
Michael Jurka920d7f42012-05-14 16:29:55 -0700479 cl.getBackgroundAlpha() > 0);
480 }
481
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800482 /**
483 * @return The open folder on the current screen, or null if there is none
484 */
485 Folder getOpenFolder() {
Adam Cohen716b51e2011-06-30 12:09:54 -0700486 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohen8e776a62011-06-28 18:10:06 -0700487 int count = dragLayer.getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800488 for (int i = 0; i < count; i++) {
Adam Cohen8e776a62011-06-28 18:10:06 -0700489 View child = dragLayer.getChildAt(i);
Winson Chungaafa03c2010-06-11 17:34:16 -0700490 if (child instanceof Folder) {
491 Folder folder = (Folder) child;
492 if (folder.getInfo().opened)
493 return folder;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800494 }
495 }
496 return null;
497 }
498
Patrick Dubroya0aa0122011-02-24 11:42:23 -0800499 boolean isTouchActive() {
500 return mTouchState != TOUCH_STATE_REST;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800501 }
502
Winson Chung9e6a0a22013-08-27 11:58:12 -0700503 public void removeAllWorkspaceScreens() {
Winson Chung964df6b2013-10-11 15:55:37 -0700504 // Disable all layout transitions before removing all pages to ensure that we don't get the
505 // transition animations competing with us changing the scroll when we add pages or the
506 // custom content screen
507 disableLayoutTransitions();
508
509 // Since we increment the current page when we call addCustomContentPage via bindScreens
510 // (and other places), we need to adjust the current page back when we clear the pages
511 if (hasCustomContent()) {
512 removeCustomContentPage();
513 }
514
Winson Chung9e6a0a22013-08-27 11:58:12 -0700515 // Remove the pages and clear the screen models
516 removeAllViews();
517 mScreenOrder.clear();
518 mWorkspaceScreens.clear();
Winson Chung964df6b2013-10-11 15:55:37 -0700519
520 // Re-enable the layout transitions
521 enableLayoutTransitions();
Winson Chung9e6a0a22013-08-27 11:58:12 -0700522 }
523
Adam Cohen89bddfa2013-08-20 11:57:13 -0700524 public long insertNewWorkspaceScreenBeforeEmptyScreen(long screenId) {
Winson Chung64359a52013-07-08 17:17:08 -0700525 // Find the index to insert this view into. If the empty screen exists, then
526 // insert it before that.
527 int insertIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
528 if (insertIndex < 0) {
529 insertIndex = mScreenOrder.size();
530 }
Adam Cohen89bddfa2013-08-20 11:57:13 -0700531 return insertNewWorkspaceScreen(screenId, insertIndex);
Adam Cohendcd297f2013-06-18 13:13:40 -0700532 }
533
Adam Cohen89bddfa2013-08-20 11:57:13 -0700534 public long insertNewWorkspaceScreen(long screenId) {
535 return insertNewWorkspaceScreen(screenId, getChildCount());
Winson Chung64359a52013-07-08 17:17:08 -0700536 }
537
Adam Cohen89bddfa2013-08-20 11:57:13 -0700538 public long insertNewWorkspaceScreen(long screenId, int insertIndex) {
Adam Cohen5084cba2013-09-03 12:01:16 -0700539 if (mWorkspaceScreens.containsKey(screenId)) {
540 throw new RuntimeException("Screen id " + screenId + " already exists!");
541 }
542
Adam Cohendcd297f2013-06-18 13:13:40 -0700543 CellLayout newScreen = (CellLayout)
544 mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null);
545
Adam Cohen7d30a372013-07-01 17:03:59 -0700546 newScreen.setOnLongClickListener(mLongClickListener);
Adam Cohenf358a4b2013-07-23 16:47:31 -0700547 newScreen.setOnClickListener(mLauncher);
Winson Chung837b2412013-09-09 14:43:51 -0700548 newScreen.setSoundEffectsEnabled(false);
Adam Cohendcd297f2013-06-18 13:13:40 -0700549 mWorkspaceScreens.put(screenId, newScreen);
Winson Chung64359a52013-07-08 17:17:08 -0700550 mScreenOrder.add(insertIndex, screenId);
551 addView(newScreen, insertIndex);
Adam Cohendcd297f2013-06-18 13:13:40 -0700552 return screenId;
553 }
554
Winson Chung98ca0f72013-07-29 12:58:51 -0700555 public void createCustomContentPage() {
Adam Cohendcd297f2013-06-18 13:13:40 -0700556 CellLayout customScreen = (CellLayout)
Adam Cohen41eb4702013-06-27 15:08:59 -0700557 mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null);
Adam Cohendcd297f2013-06-18 13:13:40 -0700558
Adam Cohendcd297f2013-06-18 13:13:40 -0700559 mWorkspaceScreens.put(CUSTOM_CONTENT_SCREEN_ID, customScreen);
560 mScreenOrder.add(0, CUSTOM_CONTENT_SCREEN_ID);
Adam Cohenedb40762013-07-18 16:45:45 -0700561
Adam Cohen2702ea02013-08-15 16:17:42 -0700562 // We want no padding on the custom content
563 customScreen.setPadding(0, 0, 0, 0);
564
Adam Cohenedb40762013-07-18 16:45:45 -0700565 addFullScreenPage(customScreen);
Michael Jurkaee0ce2b2013-07-02 17:24:35 -0700566
Adam Cohendcd297f2013-06-18 13:13:40 -0700567 // Ensure that the current page and default page are maintained.
Winson Chung9e6a0a22013-08-27 11:58:12 -0700568 mDefaultPage = mOriginalDefaultPage + 1;
Winson Chung82e5c982013-10-09 12:04:50 -0700569
570 // Update the custom content hint
571 mLauncher.updateCustomContentHintVisibility();
Adam Cohen21cd0022013-10-09 18:57:02 -0700572 if (mRestorePage != INVALID_RESTORE_PAGE) {
573 mRestorePage = mRestorePage + 1;
574 } else {
575 setCurrentPage(getCurrentPage() + 1);
576 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700577 }
578
Dave Hawkeya8881582013-09-17 15:55:33 -0600579 public void removeCustomContentPage() {
Dave Hawkeya8881582013-09-17 15:55:33 -0600580 CellLayout customScreen = getScreenWithId(CUSTOM_CONTENT_SCREEN_ID);
581 if (customScreen == null) {
582 throw new RuntimeException("Expected custom content screen to exist");
583 }
584
585 mWorkspaceScreens.remove(CUSTOM_CONTENT_SCREEN_ID);
586 mScreenOrder.remove(CUSTOM_CONTENT_SCREEN_ID);
587 removeView(customScreen);
Adam Cohenbb6fda62013-10-10 12:48:52 -0700588
589 if (mCustomContentCallbacks != null) {
590 mCustomContentCallbacks.onScrollProgressChanged(0);
591 mCustomContentCallbacks.onHide();
592 }
593
Dave Hawkeya8881582013-09-17 15:55:33 -0600594 mCustomContentCallbacks = null;
595
596 // Ensure that the current page and default page are maintained.
597 mDefaultPage = mOriginalDefaultPage - 1;
Winson Chung82e5c982013-10-09 12:04:50 -0700598
599 // Update the custom content hint
600 mLauncher.updateCustomContentHintVisibility();
Adam Cohen21cd0022013-10-09 18:57:02 -0700601 if (mRestorePage != INVALID_RESTORE_PAGE) {
602 mRestorePage = mRestorePage - 1;
603 } else {
604 setCurrentPage(getCurrentPage() - 1);
605 }
Dave Hawkeya8881582013-09-17 15:55:33 -0600606 }
607
Adam Cohen53805212013-10-01 10:39:23 -0700608 public void addToCustomContentPage(View customContent, CustomContentCallbacks callbacks,
609 String description) {
Winson Chung98ca0f72013-07-29 12:58:51 -0700610 if (getPageIndexForScreenId(CUSTOM_CONTENT_SCREEN_ID) < 0) {
611 throw new RuntimeException("Expected custom content screen to exist");
612 }
613
614 // Add the custom content to the full screen custom page
615 CellLayout customScreen = getScreenWithId(CUSTOM_CONTENT_SCREEN_ID);
616 int spanX = customScreen.getCountX();
617 int spanY = customScreen.getCountY();
618 CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, spanX, spanY);
619 lp.canReorder = false;
620 lp.isFullscreen = true;
John Spurlock77e1f472013-09-11 10:09:51 -0400621 if (customContent instanceof Insettable) {
622 ((Insettable)customContent).setInsets(mInsets);
623 }
Adam Cohen225ad9b2013-10-07 13:03:00 -0700624 customScreen.removeAllViews();
Winson Chung98ca0f72013-07-29 12:58:51 -0700625 customScreen.addViewToCellLayout(customContent, 0, 0, lp, true);
Adam Cohen53805212013-10-01 10:39:23 -0700626 mCustomContentDescription = description;
Winson Chung98ca0f72013-07-29 12:58:51 -0700627
628 mCustomContentCallbacks = callbacks;
629 }
630
Adam Cohen5084cba2013-09-03 12:01:16 -0700631 public void addExtraEmptyScreenOnDrag() {
632 boolean lastChildOnScreen = false;
633 boolean childOnFinalScreen = false;
634
Adam Cohenad4e15c2013-10-17 16:21:35 -0700635 // Cancel any pending removal of empty screen
636 mRemoveEmptyScreenRunnable = null;
637
Adam Cohen5084cba2013-09-03 12:01:16 -0700638 if (mDragSourceInternal != null) {
639 if (mDragSourceInternal.getChildCount() == 1) {
640 lastChildOnScreen = true;
641 }
642 CellLayout cl = (CellLayout) mDragSourceInternal.getParent();
643 if (indexOfChild(cl) == getChildCount() - 1) {
644 childOnFinalScreen = true;
645 }
646 }
647
648 // If this is the last item on the final screen
649 if (lastChildOnScreen && childOnFinalScreen) {
650 return;
651 }
652 if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) {
653 insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID);
654 }
655 }
656
657 public boolean addExtraEmptyScreen() {
658 if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) {
659 insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID);
660 return true;
661 }
662 return false;
663 }
664
Adam Cohenad4e15c2013-10-17 16:21:35 -0700665 private void convertFinalScreenToEmptyScreenIfNecessary() {
666 if (hasExtraEmptyScreen() || mScreenOrder.size() == 0) return;
667 long finalScreenId = mScreenOrder.get(mScreenOrder.size() - 1);
668
669 if (finalScreenId == CUSTOM_CONTENT_SCREEN_ID) return;
670 CellLayout finalScreen = mWorkspaceScreens.get(finalScreenId);
671
672 // If the final screen is empty, convert it to the extra empty screen
673 if (finalScreen.getShortcutsAndWidgets().getChildCount() == 0) {
674 mWorkspaceScreens.remove(finalScreenId);
675 mScreenOrder.remove(finalScreenId);
676
677 // if this is the last non-custom content screen, convert it to the empty screen
678 mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, finalScreen);
679 mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
Adam Cohen5084cba2013-09-03 12:01:16 -0700680 }
681 }
682
Adam Cohenad4e15c2013-10-17 16:21:35 -0700683 public void removeExtraEmptyScreen(final boolean animate, final Runnable onComplete) {
684 removeExtraEmptyScreen(animate, onComplete, 0, false);
685 }
686
687 public void removeExtraEmptyScreen(final boolean animate, final Runnable onComplete,
688 final int delay, final boolean stripEmptyScreens) {
689 if (delay > 0) {
690 postDelayed(new Runnable() {
691 @Override
692 public void run() {
693 removeExtraEmptyScreen(animate, onComplete, 0, stripEmptyScreens);
694 }
695
696 }, delay);
697 return;
698 }
699
700 convertFinalScreenToEmptyScreenIfNecessary();
701 if (hasExtraEmptyScreen()) {
702 int emptyIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
703 if (getNextPage() == emptyIndex) {
704 snapToPage(getNextPage() - 1, SNAP_OFF_EMPTY_SCREEN_DURATION);
705 fadeAndRemoveEmptyScreen(SNAP_OFF_EMPTY_SCREEN_DURATION, FADE_EMPTY_SCREEN_DURATION,
706 onComplete, stripEmptyScreens);
707 } else {
708 fadeAndRemoveEmptyScreen(0, FADE_EMPTY_SCREEN_DURATION,
709 onComplete, stripEmptyScreens);
710 }
711 return;
712 }
713 if (onComplete != null) {
714 onComplete.run();
715 }
716 }
717
718 private void fadeAndRemoveEmptyScreen(int delay, int duration, final Runnable onComplete,
719 final boolean stripEmptyScreens) {
720 PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0f);
721 PropertyValuesHolder bgAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", 0f);
722
723 final CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
724
725 mRemoveEmptyScreenRunnable = new Runnable() {
726 @Override
727 public void run() {
728 if (hasExtraEmptyScreen()) {
729 mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
730 mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
731 removeView(cl);
732 if (stripEmptyScreens) {
733 stripEmptyScreens();
734 }
735 }
736 }
737 };
738
739 ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(cl, alpha, bgAlpha);
740 oa.setDuration(duration);
741 oa.setStartDelay(delay);
742 oa.addListener(new AnimatorListenerAdapter() {
743 @Override
744 public void onAnimationEnd(Animator animation) {
745 if (mRemoveEmptyScreenRunnable != null) {
746 mRemoveEmptyScreenRunnable.run();
747 }
748 if (onComplete != null) {
749 onComplete.run();
750 }
751 }
752 });
753 oa.start();
754 }
755
Michael Jurka2f817312013-09-20 03:03:42 +0200756 public boolean hasExtraEmptyScreen() {
757 int nScreens = getChildCount();
Michael Jurkafe0ace32013-10-03 01:05:14 -0700758 nScreens = nScreens - numCustomPages();
Michael Jurka2f817312013-09-20 03:03:42 +0200759 return mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID) && nScreens > 1;
760 }
761
Adam Cohendcd297f2013-06-18 13:13:40 -0700762 public long commitExtraEmptyScreen() {
Winson Chung89f97052013-09-20 11:32:26 -0700763 int index = getPageIndexForScreenId(EXTRA_EMPTY_SCREEN_ID);
Adam Cohendcd297f2013-06-18 13:13:40 -0700764 CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
Winson Chungc9168342013-06-26 14:54:55 -0700765 mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
Adam Cohendcd297f2013-06-18 13:13:40 -0700766 mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
767
Michael Jurka104c4562013-07-08 18:03:46 -0700768 long newId = LauncherAppState.getLauncherProvider().generateNewScreenId();
Adam Cohendcd297f2013-06-18 13:13:40 -0700769 mWorkspaceScreens.put(newId, cl);
770 mScreenOrder.add(newId);
771
Winson Chung89f97052013-09-20 11:32:26 -0700772 // Update the page indicator marker
773 if (getPageIndicator() != null) {
774 getPageIndicator().updateMarker(index, getPageIndicatorMarker(index));
775 }
776
Winson Chung64359a52013-07-08 17:17:08 -0700777 // Update the model for the new screen
778 mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
779
Adam Cohendcd297f2013-06-18 13:13:40 -0700780 return newId;
781 }
782
Adam Cohendcd297f2013-06-18 13:13:40 -0700783 public CellLayout getScreenWithId(long screenId) {
784 CellLayout layout = mWorkspaceScreens.get(screenId);
785 return layout;
786 }
787
788 public long getIdForScreen(CellLayout layout) {
789 Iterator<Long> iter = mWorkspaceScreens.keySet().iterator();
790 while (iter.hasNext()) {
791 long id = iter.next();
792 if (mWorkspaceScreens.get(id) == layout) {
793 return id;
794 }
795 }
796 return -1;
797 }
798
799 public int getPageIndexForScreenId(long screenId) {
800 return indexOfChild(mWorkspaceScreens.get(screenId));
801 }
802
803 public long getScreenIdForPageIndex(int index) {
Winson Chung5f8afe62013-08-12 16:19:28 -0700804 if (0 <= index && index < mScreenOrder.size()) {
805 return mScreenOrder.get(index);
806 }
807 return -1;
Adam Cohendcd297f2013-06-18 13:13:40 -0700808 }
809
Winson Chungc9168342013-06-26 14:54:55 -0700810 ArrayList<Long> getScreenOrder() {
811 return mScreenOrder;
812 }
813
Adam Cohendcd297f2013-06-18 13:13:40 -0700814 public void stripEmptyScreens() {
Adam Cohenaccfd562013-07-12 14:40:40 -0700815 if (isPageMoving()) {
816 mStripScreensOnPageStopMoving = true;
817 return;
818 }
819
820 int currentPage = getNextPage();
Adam Cohendcd297f2013-06-18 13:13:40 -0700821 ArrayList<Long> removeScreens = new ArrayList<Long>();
822 for (Long id: mWorkspaceScreens.keySet()) {
823 CellLayout cl = mWorkspaceScreens.get(id);
Winson Chungc9168342013-06-26 14:54:55 -0700824 if (id >= 0 && cl.getShortcutsAndWidgets().getChildCount() == 0) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700825 removeScreens.add(id);
826 }
827 }
828
Adam Cohen5084cba2013-09-03 12:01:16 -0700829 // We enforce at least one page to add new items to. In the case that we remove the last
830 // such screen, we convert the last screen to the empty screen
Michael Jurkafe0ace32013-10-03 01:05:14 -0700831 int minScreens = 1 + numCustomPages();
Adam Cohen5084cba2013-09-03 12:01:16 -0700832
Adam Cohendcd297f2013-06-18 13:13:40 -0700833 int pageShift = 0;
834 for (Long id: removeScreens) {
835 CellLayout cl = mWorkspaceScreens.get(id);
836 mWorkspaceScreens.remove(id);
837 mScreenOrder.remove(id);
Adam Cohen5084cba2013-09-03 12:01:16 -0700838
839 if (getChildCount() > minScreens) {
840 if (indexOfChild(cl) < currentPage) {
841 pageShift++;
842 }
843 removeView(cl);
844 } else {
845 // if this is the last non-custom content screen, convert it to the empty screen
Adam Cohenad4e15c2013-10-17 16:21:35 -0700846 mRemoveEmptyScreenRunnable = null;
Adam Cohen5084cba2013-09-03 12:01:16 -0700847 mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, cl);
848 mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
Adam Cohendcd297f2013-06-18 13:13:40 -0700849 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700850 }
Winson Chung64359a52013-07-08 17:17:08 -0700851
852 if (!removeScreens.isEmpty()) {
853 // Update the model if we have changed any screens
854 mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
855 }
Adam Cohenaccfd562013-07-12 14:40:40 -0700856
857 if (pageShift >= 0) {
858 setCurrentPage(currentPage - pageShift);
859 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700860 }
861
862 // See implementation for parameter definition.
863 void addInScreen(View child, long container, long screenId,
864 int x, int y, int spanX, int spanY) {
865 addInScreen(child, container, screenId, x, y, spanX, spanY, false, false);
866 }
867
868 // At bind time, we use the rank (screenId) to compute x and y for hotseat items.
869 // See implementation for parameter definition.
870 void addInScreenFromBind(View child, long container, long screenId, int x, int y,
871 int spanX, int spanY) {
872 addInScreen(child, container, screenId, x, y, spanX, spanY, false, true);
873 }
874
875 // See implementation for parameter definition.
876 void addInScreen(View child, long container, long screenId, int x, int y, int spanX, int spanY,
877 boolean insert) {
878 addInScreen(child, container, screenId, x, y, spanX, spanY, insert, false);
Winson Chungaafa03c2010-06-11 17:34:16 -0700879 }
880
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800881 /**
882 * Adds the specified child in the specified screen. The position and dimension of
883 * the child are defined by x, y, spanX and spanY.
884 *
885 * @param child The child to add in one of the workspace's screens.
Adam Cohendcd297f2013-06-18 13:13:40 -0700886 * @param screenId The screen in which to add the child.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800887 * @param x The X position of the child in the screen's grid.
888 * @param y The Y position of the child in the screen's grid.
889 * @param spanX The number of cells spanned horizontally by the child.
890 * @param spanY The number of cells spanned vertically by the child.
891 * @param insert When true, the child is inserted at the beginning of the children list.
Adam Cohendcd297f2013-06-18 13:13:40 -0700892 * @param computeXYFromRank When true, we use the rank (stored in screenId) to compute
893 * the x and y position in which to place hotseat items. Otherwise
894 * we use the x and y position to compute the rank.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800895 */
Adam Cohendcd297f2013-06-18 13:13:40 -0700896 void addInScreen(View child, long container, long screenId, int x, int y, int spanX, int spanY,
897 boolean insert, boolean computeXYFromRank) {
Winson Chung3d503fb2011-07-13 17:25:49 -0700898 if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700899 if (getScreenWithId(screenId) == null) {
900 Log.e(TAG, "Skipping child, screenId " + screenId + " not found");
Winson Chung8481e322013-08-09 16:06:38 -0700901 // DEBUGGING - Print out the stack trace to see where we are adding from
902 new Throwable().printStackTrace();
Winson Chung3d503fb2011-07-13 17:25:49 -0700903 return;
904 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800905 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700906 if (screenId == EXTRA_EMPTY_SCREEN_ID) {
Adam Cohendedbd962013-07-11 14:21:49 -0700907 // This should never happen
908 throw new RuntimeException("Screen id should not be EXTRA_EMPTY_SCREEN_ID");
Adam Cohendcd297f2013-06-18 13:13:40 -0700909 }
910
Winson Chung3d503fb2011-07-13 17:25:49 -0700911 final CellLayout layout;
912 if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
913 layout = mLauncher.getHotseat().getLayout();
Winson Chung4d279d92011-07-21 11:46:32 -0700914 child.setOnKeyListener(null);
Winson Chung3d503fb2011-07-13 17:25:49 -0700915
Adam Cohen099f60d2011-08-23 21:07:26 -0700916 // Hide folder title in the hotseat
917 if (child instanceof FolderIcon) {
918 ((FolderIcon) child).setTextVisible(false);
919 }
920
Adam Cohendcd297f2013-06-18 13:13:40 -0700921 if (computeXYFromRank) {
922 x = mLauncher.getHotseat().getCellXFromOrder((int) screenId);
923 y = mLauncher.getHotseat().getCellYFromOrder((int) screenId);
Winson Chung3d503fb2011-07-13 17:25:49 -0700924 } else {
Adam Cohendcd297f2013-06-18 13:13:40 -0700925 screenId = mLauncher.getHotseat().getOrderInHotseat(x, y);
Winson Chung3d503fb2011-07-13 17:25:49 -0700926 }
927 } else {
Adam Cohen099f60d2011-08-23 21:07:26 -0700928 // Show folder title if not in the hotseat
929 if (child instanceof FolderIcon) {
930 ((FolderIcon) child).setTextVisible(true);
931 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700932 layout = getScreenWithId(screenId);
Adam Cohenac56cff2011-09-28 20:45:37 -0700933 child.setOnKeyListener(new IconKeyEventListener());
Winson Chung3d503fb2011-07-13 17:25:49 -0700934 }
935
Adam Cohen96d30a12013-07-16 18:13:21 -0700936 ViewGroup.LayoutParams genericLp = child.getLayoutParams();
Adam Cohened66b2b2012-01-23 17:28:51 -0800937 CellLayout.LayoutParams lp;
938 if (genericLp == null || !(genericLp instanceof CellLayout.LayoutParams)) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800939 lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
940 } else {
Adam Cohened66b2b2012-01-23 17:28:51 -0800941 lp = (CellLayout.LayoutParams) genericLp;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800942 lp.cellX = x;
943 lp.cellY = y;
944 lp.cellHSpan = spanX;
945 lp.cellVSpan = spanY;
946 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700947
Adam Cohen7f4eabe2011-04-21 16:19:16 -0700948 if (spanX < 0 && spanY < 0) {
949 lp.isLockedToGrid = false;
950 }
951
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700952 // Get the canonical child id to uniquely represent this view in this screen
Adam Cohendcd297f2013-06-18 13:13:40 -0700953 int childId = LauncherModel.getCellLayoutChildId(container, screenId, x, y, spanX, spanY);
Michael Jurkaf3ca3ab2010-10-20 17:08:24 -0700954 boolean markCellsAsOccupied = !(child instanceof Folder);
Winson Chung3d503fb2011-07-13 17:25:49 -0700955 if (!layout.addViewToCellLayout(child, insert ? 0 : -1, childId, lp, markCellsAsOccupied)) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700956 // TODO: This branch occurs when the workspace is adding views
957 // outside of the defined grid
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700958 // maybe we should be deleting these items from the LauncherModel?
Adam Cohen4caf2982013-08-20 18:54:31 -0700959 Launcher.addDumpLog(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout", true);
Winson Chungaafa03c2010-06-11 17:34:16 -0700960 }
961
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800962 if (!(child instanceof Folder)) {
Joe Onorato0d44e942009-11-16 18:20:51 -0800963 child.setHapticFeedbackEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800964 child.setOnLongClickListener(mLongClickListener);
965 }
Joe Onorato00acb122009-08-04 16:04:30 -0400966 if (child instanceof DropTarget) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700967 mDragController.addDropTarget((DropTarget) child);
Joe Onorato00acb122009-08-04 16:04:30 -0400968 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800969 }
970
Patrick Dubroyd0ce1ec2011-01-19 18:47:27 -0800971 /**
Patrick Dubroye708c522011-03-01 16:03:43 -0800972 * Called directly from a CellLayout (not by the framework), after we've been added as a
973 * listener via setOnInterceptTouchEventListener(). This allows us to tell the CellLayout
974 * that it should intercept touch events, which is not something that is normally supported.
975 */
976 @Override
Michael Jurkadee05892010-07-27 10:01:56 -0700977 public boolean onTouch(View v, MotionEvent event) {
Adam Cohenf358a4b2013-07-23 16:47:31 -0700978 return (isSmall() || !isFinishedSwitchingState())
979 || (!isSmall() && indexOfChild(v) != mCurrentPage);
Patrick Dubroye708c522011-03-01 16:03:43 -0800980 }
981
Adam Cohenfc53cd22011-07-20 15:45:11 -0700982 public boolean isSwitchingState() {
983 return mIsSwitchingState;
984 }
985
Winson Chung70442722012-02-10 15:43:22 -0800986 /** This differs from isSwitchingState in that we take into account how far the transition
987 * has completed. */
Winson Chung9b0b2fe2012-02-24 13:03:34 -0800988 public boolean isFinishedSwitchingState() {
Winson Chung70442722012-02-10 15:43:22 -0800989 return !mIsSwitchingState || (mTransitionProgress > 0.5f);
990 }
991
Adam Cohended9f8d2010-11-03 13:25:16 -0700992 protected void onWindowVisibilityChanged (int visibility) {
993 mLauncher.onWindowVisibilityChanged(visibility);
994 }
995
Michael Jurka5f1c5092010-09-03 14:15:02 -0700996 @Override
997 public boolean dispatchUnhandledMove(View focused, int direction) {
Winson Chung70442722012-02-10 15:43:22 -0800998 if (isSmall() || !isFinishedSwitchingState()) {
Michael Jurka5f1c5092010-09-03 14:15:02 -0700999 // when the home screens are shrunken, shouldn't allow side-scrolling
1000 return false;
1001 }
1002 return super.dispatchUnhandledMove(focused, direction);
1003 }
1004
1005 @Override
1006 public boolean onInterceptTouchEvent(MotionEvent ev) {
Michael Jurkad771c962011-08-09 15:00:48 -07001007 switch (ev.getAction() & MotionEvent.ACTION_MASK) {
1008 case MotionEvent.ACTION_DOWN:
Adam Cohenf8d28232011-02-01 21:47:00 -08001009 mXDown = ev.getX();
1010 mYDown = ev.getY();
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001011 mTouchDownTime = System.currentTimeMillis();
Michael Jurkad771c962011-08-09 15:00:48 -07001012 break;
1013 case MotionEvent.ACTION_POINTER_UP:
1014 case MotionEvent.ACTION_UP:
1015 if (mTouchState == TOUCH_STATE_REST) {
1016 final CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage);
1017 if (!currentPage.lastDownOnOccupiedCell()) {
1018 onWallpaperTap(ev);
1019 }
1020 }
Adam Cohenf8d28232011-02-01 21:47:00 -08001021 }
Michael Jurka5f1c5092010-09-03 14:15:02 -07001022 return super.onInterceptTouchEvent(ev);
1023 }
1024
Adam Cohen3d509322012-06-06 14:10:04 -07001025 protected void reinflateWidgetsIfNecessary() {
1026 final int clCount = getChildCount();
1027 for (int i = 0; i < clCount; i++) {
1028 CellLayout cl = (CellLayout) getChildAt(i);
1029 ShortcutAndWidgetContainer swc = cl.getShortcutsAndWidgets();
1030 final int itemCount = swc.getChildCount();
1031 for (int j = 0; j < itemCount; j++) {
1032 View v = swc.getChildAt(j);
1033
1034 if (v.getTag() instanceof LauncherAppWidgetInfo) {
1035 LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
1036 LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) info.hostView;
1037 if (lahv != null && lahv.orientationChangedSincedInflation()) {
1038 mLauncher.removeAppWidget(info);
1039 // Remove the current widget which is inflated with the wrong orientation
1040 cl.removeView(lahv);
1041 mLauncher.bindAppWidget(info);
1042 }
1043 }
1044 }
1045 }
1046 }
1047
Michael Jurka1adf5392010-10-18 18:10:22 -07001048 @Override
1049 protected void determineScrollingStart(MotionEvent ev) {
Winson Chung70442722012-02-10 15:43:22 -08001050 if (!isFinishedSwitchingState()) return;
Adam Cohenf8d28232011-02-01 21:47:00 -08001051
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001052 float deltaX = ev.getX() - mXDown;
1053 float absDeltaX = Math.abs(deltaX);
1054 float absDeltaY = Math.abs(ev.getY() - mYDown);
Adam Cohenf8d28232011-02-01 21:47:00 -08001055
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001056 if (Float.compare(absDeltaX, 0f) == 0) return;
Adam Cohenf8d28232011-02-01 21:47:00 -08001057
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001058 float slope = absDeltaY / absDeltaX;
Winson Chung70442722012-02-10 15:43:22 -08001059 float theta = (float) Math.atan(slope);
Adam Cohenf8d28232011-02-01 21:47:00 -08001060
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001061 if (absDeltaX > mTouchSlop || absDeltaY > mTouchSlop) {
Winson Chung70442722012-02-10 15:43:22 -08001062 cancelCurrentPageLongPress();
1063 }
1064
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001065 boolean passRightSwipesToCustomContent =
1066 (mTouchDownTime - mCustomContentShowTime) > CUSTOM_CONTENT_GESTURE_DELAY;
1067
Adam Cohenaf9b0e52013-09-23 19:27:38 -07001068 boolean swipeInIgnoreDirection = isLayoutRtl() ? deltaX < 0 : deltaX > 0;
1069 if (swipeInIgnoreDirection && getScreenIdForPageIndex(getCurrentPage()) ==
1070 CUSTOM_CONTENT_SCREEN_ID && passRightSwipesToCustomContent) {
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001071 // Pass swipes to the right to the custom content page.
1072 return;
1073 }
1074
Winson Chung70442722012-02-10 15:43:22 -08001075 if (theta > MAX_SWIPE_ANGLE) {
1076 // Above MAX_SWIPE_ANGLE, we don't want to ever start scrolling the workspace
1077 return;
1078 } else if (theta > START_DAMPING_TOUCH_SLOP_ANGLE) {
1079 // Above START_DAMPING_TOUCH_SLOP_ANGLE and below MAX_SWIPE_ANGLE, we want to
1080 // increase the touch slop to make it harder to begin scrolling the workspace. This
1081 // results in vertically scrolling widgets to more easily. The higher the angle, the
1082 // more we increase touch slop.
1083 theta -= START_DAMPING_TOUCH_SLOP_ANGLE;
1084 float extraRatio = (float)
1085 Math.sqrt((theta / (MAX_SWIPE_ANGLE - START_DAMPING_TOUCH_SLOP_ANGLE)));
1086 super.determineScrollingStart(ev, 1 + TOUCH_SLOP_DAMPING_FACTOR * extraRatio);
1087 } else {
1088 // Below START_DAMPING_TOUCH_SLOP_ANGLE, we don't do anything special
1089 super.determineScrollingStart(ev);
Adam Cohenf8d28232011-02-01 21:47:00 -08001090 }
Michael Jurka1adf5392010-10-18 18:10:22 -07001091 }
1092
Patrick Dubroy1262e362010-10-06 15:49:50 -07001093 protected void onPageBeginMoving() {
Winson Chung007c6982011-06-14 13:27:53 -07001094 super.onPageBeginMoving();
1095
Michael Jurkad74c9842011-07-10 12:44:21 -07001096 if (isHardwareAccelerated()) {
Michael Jurka3a0469d2012-06-21 09:38:41 -07001097 updateChildrenLayersEnabled(false);
Michael Jurka0142d492010-08-25 17:46:15 -07001098 } else {
Michael Jurkad74c9842011-07-10 12:44:21 -07001099 if (mNextPage != INVALID_PAGE) {
1100 // we're snapping to a particular screen
1101 enableChildrenCache(mCurrentPage, mNextPage);
1102 } else {
1103 // this is when user is actively dragging a particular screen, they might
1104 // swipe it either left or right (but we won't advance by more than one screen)
1105 enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1);
1106 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001107 }
Winson Chung007c6982011-06-14 13:27:53 -07001108
1109 // Only show page outlines as we pan if we are on large screen
Daniel Sandlere4f98912013-06-25 15:13:26 -04001110 if (LauncherAppState.getInstance().isScreenLarge()) {
Winson Chung007c6982011-06-14 13:27:53 -07001111 showOutlines();
1112 }
Winson Chung1afedc32012-01-23 16:14:56 -08001113
Winson Chung70442722012-02-10 15:43:22 -08001114 // If we are not fading in adjacent screens, we still need to restore the alpha in case the
1115 // user scrolls while we are transitioning (should not affect dispatchDraw optimizations)
Michael Jurka869390b2012-05-06 15:55:19 -07001116 if (!mWorkspaceFadeInAdjacentScreens) {
Winson Chung70442722012-02-10 15:43:22 -08001117 for (int i = 0; i < getChildCount(); ++i) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07001118 ((CellLayout) getPageAt(i)).setShortcutAndWidgetAlpha(1f);
Winson Chung70442722012-02-10 15:43:22 -08001119 }
1120 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001121 }
1122
Patrick Dubroy1262e362010-10-06 15:49:50 -07001123 protected void onPageEndMoving() {
Winson Chung007c6982011-06-14 13:27:53 -07001124 super.onPageEndMoving();
1125
Michael Jurkad74c9842011-07-10 12:44:21 -07001126 if (isHardwareAccelerated()) {
Michael Jurka3a0469d2012-06-21 09:38:41 -07001127 updateChildrenLayersEnabled(false);
Michael Jurkad74c9842011-07-10 12:44:21 -07001128 } else {
1129 clearChildrenCache();
1130 }
1131
Winson Chung3bc21c32012-01-20 13:59:18 -08001132 if (mDragController.isDragging()) {
1133 if (isSmall()) {
1134 // If we are in springloaded mode, then force an event to check if the current touch
1135 // is under a new page (to scroll to)
Winson Chung25460a12013-04-01 18:21:28 -07001136 mDragController.forceTouchMove();
Winson Chung3bc21c32012-01-20 13:59:18 -08001137 }
1138 } else {
1139 // If we are not mid-dragging, hide the page outlines if we are on a large screen
Daniel Sandlere4f98912013-06-25 15:13:26 -04001140 if (LauncherAppState.getInstance().isScreenLarge()) {
Winson Chung007c6982011-06-14 13:27:53 -07001141 hideOutlines();
1142 }
Patrick Dubroy1262e362010-10-06 15:49:50 -07001143 }
Adam Cohen26976d92011-03-22 15:33:33 -07001144
1145 if (mDelayedResizeRunnable != null) {
1146 mDelayedResizeRunnable.run();
1147 mDelayedResizeRunnable = null;
1148 }
Winson Chungf0c6ae02012-03-21 16:10:31 -07001149
1150 if (mDelayedSnapToPageRunnable != null) {
1151 mDelayedSnapToPageRunnable.run();
1152 mDelayedSnapToPageRunnable = null;
1153 }
Adam Cohenaccfd562013-07-12 14:40:40 -07001154 if (mStripScreensOnPageStopMoving) {
1155 stripEmptyScreens();
1156 mStripScreensOnPageStopMoving = false;
1157 }
Adam Cohen89cbbb92013-09-25 16:52:48 -07001158 }
1159
1160 @Override
1161 protected void notifyPageSwitchListener() {
1162 super.notifyPageSwitchListener();
1163 Launcher.setScreen(mCurrentPage);
Michael Jurka0142d492010-08-25 17:46:15 -07001164
Adam Cohen6ad0e7d2013-07-24 13:55:41 -07001165 if (hasCustomContent() && getNextPage() == 0 && !mCustomContentShowing) {
Adam Cohenbffe7452013-07-22 18:21:45 -07001166 mCustomContentShowing = true;
1167 if (mCustomContentCallbacks != null) {
1168 mCustomContentCallbacks.onShow();
Adam Cohen3d1b2b42013-08-14 15:57:58 -07001169 mCustomContentShowTime = System.currentTimeMillis();
Winson Chung5841efa2013-09-30 18:06:44 -07001170 mLauncher.updateVoiceButtonProxyVisible(false);
Adam Cohenbffe7452013-07-22 18:21:45 -07001171 }
Adam Cohen6ad0e7d2013-07-24 13:55:41 -07001172 } else if (hasCustomContent() && getNextPage() != 0 && mCustomContentShowing) {
Adam Cohenbffe7452013-07-22 18:21:45 -07001173 mCustomContentShowing = false;
1174 if (mCustomContentCallbacks != null) {
1175 mCustomContentCallbacks.onHide();
1176 mLauncher.resetQSBScroll();
Winson Chung5841efa2013-09-30 18:06:44 -07001177 mLauncher.updateVoiceButtonProxyVisible(false);
Adam Cohenbffe7452013-07-22 18:21:45 -07001178 }
1179 }
Adam Cohen53805212013-10-01 10:39:23 -07001180 if (getPageIndicator() != null) {
1181 getPageIndicator().setContentDescription(getPageIndicatorDescription());
1182 }
Chris Craik01f2d7f2013-10-01 14:41:56 -07001183 }
Michael Jurka0142d492010-08-25 17:46:15 -07001184
Adam Cohen6fecd412013-10-02 17:41:50 -07001185 protected CustomContentCallbacks getCustomContentCallbacks() {
1186 return mCustomContentCallbacks;
1187 }
1188
Michael Jurkac5b262c2011-01-12 20:24:50 -08001189 protected void setWallpaperDimension() {
Michael Jurkaadc574c2013-09-12 00:05:02 +02001190 String spKey = WallpaperCropActivity.getSharedPreferencesKey();
1191 SharedPreferences sp = mLauncher.getSharedPreferences(spKey, Context.MODE_PRIVATE);
Michael Jurka104c4562013-07-08 18:03:46 -07001192 WallpaperPickerActivity.suggestWallpaperDimension(mLauncher.getResources(),
Michael Jurkaadc574c2013-09-12 00:05:02 +02001193 sp, mLauncher.getWindowManager(), mWallpaperManager);
Michael Jurkac5b262c2011-01-12 20:24:50 -08001194 }
1195
Winson Chungf0c6ae02012-03-21 16:10:31 -07001196 protected void snapToPage(int whichPage, Runnable r) {
Adam Cohenad4e15c2013-10-17 16:21:35 -07001197 snapToPage(whichPage, SLOW_PAGE_SNAP_ANIMATION_DURATION, r);
1198 }
1199
1200 protected void snapToPage(int whichPage, int duration, Runnable r) {
Winson Chungf0c6ae02012-03-21 16:10:31 -07001201 if (mDelayedSnapToPageRunnable != null) {
1202 mDelayedSnapToPageRunnable.run();
1203 }
1204 mDelayedSnapToPageRunnable = r;
Adam Cohenad4e15c2013-10-17 16:21:35 -07001205 snapToPage(whichPage, duration);
Winson Chungf0c6ae02012-03-21 16:10:31 -07001206 }
1207
Adam Cohendcd297f2013-06-18 13:13:40 -07001208 protected void snapToScreenId(long screenId, Runnable r) {
1209 snapToPage(getPageIndexForScreenId(screenId), r);
1210 }
1211
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001212 class WallpaperOffsetInterpolator implements Choreographer.FrameCallback {
1213 float mFinalOffset = 0.0f;
Michael Jurka046cf342013-09-14 16:40:34 +02001214 float mCurrentOffset = 0.5f; // to force an initial update
Michael Jurkafe09cb72013-08-27 15:48:58 +02001215 boolean mWaitingForUpdate;
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001216 Choreographer mChoreographer;
1217 Interpolator mInterpolator;
1218 boolean mAnimating;
1219 long mAnimationStartTime;
1220 float mAnimationStartOffset;
Michael Jurka2f817312013-09-20 03:03:42 +02001221 private final int ANIMATION_DURATION = 250;
1222 // Don't use all the wallpaper for parallax until you have at least this many pages
Michael Jurkafe0ace32013-10-03 01:05:14 -07001223 private final int MIN_PARALLAX_PAGE_SPAN = 3;
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001224 int mNumScreens;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001225
1226 public WallpaperOffsetInterpolator() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001227 mChoreographer = Choreographer.getInstance();
1228 mInterpolator = new DecelerateInterpolator(1.5f);
Michael Jurkaab1983f2011-01-18 15:50:17 -08001229 }
1230
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001231 @Override
1232 public void doFrame(long frameTimeNanos) {
Michael Jurkafe09cb72013-08-27 15:48:58 +02001233 updateOffset(false);
1234 }
1235
Michael Jurkafe09cb72013-08-27 15:48:58 +02001236 private void updateOffset(boolean force) {
1237 if (mWaitingForUpdate || force) {
1238 mWaitingForUpdate = false;
Michael Jurka1bd90b02013-09-05 23:10:14 +02001239 if (computeScrollOffset() && mWindowToken != null) {
1240 try {
1241 mWallpaperManager.setWallpaperOffsets(mWindowToken,
1242 mWallpaperOffset.getCurrX(), 0.5f);
1243 setWallpaperOffsetSteps();
1244 } catch (IllegalArgumentException e) {
1245 Log.e(TAG, "Error updating wallpaper offset: " + e);
1246 }
Michael Jurkafe09cb72013-08-27 15:48:58 +02001247 }
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001248 }
Michael Jurkaca5b8362011-01-27 13:23:26 -08001249 }
1250
Michael Jurkaab1983f2011-01-18 15:50:17 -08001251 public boolean computeScrollOffset() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001252 final float oldOffset = mCurrentOffset;
1253 if (mAnimating) {
1254 long durationSinceAnimation = System.currentTimeMillis() - mAnimationStartTime;
1255 float t0 = durationSinceAnimation / (float) ANIMATION_DURATION;
1256 float t1 = mInterpolator.getInterpolation(t0);
1257 mCurrentOffset = mAnimationStartOffset +
1258 (mFinalOffset - mAnimationStartOffset) * t1;
1259 mAnimating = durationSinceAnimation < ANIMATION_DURATION;
Michael Jurkaca5b8362011-01-27 13:23:26 -08001260 } else {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001261 mCurrentOffset = mFinalOffset;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001262 }
Michael Jurkaca5b8362011-01-27 13:23:26 -08001263
Michael Jurkafe09cb72013-08-27 15:48:58 +02001264 if (Math.abs(mCurrentOffset - mFinalOffset) > 0.0000001f) {
1265 scheduleUpdate();
1266 }
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001267 if (Math.abs(oldOffset - mCurrentOffset) > 0.0000001f) {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001268 return true;
1269 }
1270 return false;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001271 }
1272
Michael Jurkafe09cb72013-08-27 15:48:58 +02001273 private float wallpaperOffsetForCurrentScroll() {
1274 if (getChildCount() <= 1) {
1275 return 0;
1276 }
Michael Jurka2f817312013-09-20 03:03:42 +02001277
1278 // Exclude the leftmost page
Michael Jurkafe0ace32013-10-03 01:05:14 -07001279 int emptyExtraPages = numEmptyScreensToIgnore();
1280 int firstIndex = numCustomPages();
Michael Jurka2f817312013-09-20 03:03:42 +02001281 // Exclude the last extra empty screen (if we have > MIN_PARALLAX_PAGE_SPAN pages)
Michael Jurkafe0ace32013-10-03 01:05:14 -07001282 int lastIndex = getChildCount() - 1 - emptyExtraPages;
1283 if (isLayoutRtl()) {
1284 int temp = firstIndex;
1285 firstIndex = lastIndex;
1286 lastIndex = temp;
1287 }
Michael Jurka2f817312013-09-20 03:03:42 +02001288
Michael Jurkafe09cb72013-08-27 15:48:58 +02001289 int firstPageScrollX = getScrollForPage(firstIndex);
1290 int scrollRange = getScrollForPage(lastIndex) - firstPageScrollX;
1291 if (scrollRange == 0) {
1292 return 0;
1293 } else {
Michael Jurka2f817312013-09-20 03:03:42 +02001294 // TODO: do different behavior if it's a live wallpaper?
Michael Jurkafe0ace32013-10-03 01:05:14 -07001295 // Sometimes the left parameter of the pages is animated during a layout transition;
1296 // this parameter offsets it to keep the wallpaper from animating as well
Michael Jurkafe0ace32013-10-03 01:05:14 -07001297 int adjustedScroll =
Michael Jurka8fd3adc2013-10-16 13:50:24 -07001298 getScrollX() - firstPageScrollX - getLayoutTransitionOffsetForPage(0);
Michael Jurkafe0ace32013-10-03 01:05:14 -07001299 float offset = Math.min(1, adjustedScroll / (float) scrollRange);
Michael Jurka2f817312013-09-20 03:03:42 +02001300 offset = Math.max(0, offset);
1301 // Don't use up all the wallpaper parallax until you have at least
1302 // MIN_PARALLAX_PAGE_SPAN pages
Michael Jurkafe0ace32013-10-03 01:05:14 -07001303 int numScrollingPages = getNumScreensExcludingEmptyAndCustom();
1304 int parallaxPageSpan = Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages - 1);
1305 // On RTL devices, push the wallpaper offset to the right if we don't have enough
1306 // pages (ie if numScrollingPages < MIN_PARALLAX_PAGE_SPAN)
1307 int padding = isLayoutRtl() ? parallaxPageSpan - numScrollingPages + 1 : 0;
1308 return offset * (padding + numScrollingPages - 1) / parallaxPageSpan;
Michael Jurkafe09cb72013-08-27 15:48:58 +02001309 }
1310 }
1311
Michael Jurkafe0ace32013-10-03 01:05:14 -07001312 private int numEmptyScreensToIgnore() {
1313 int numScrollingPages = getChildCount() - numCustomPages();
1314 if (numScrollingPages >= MIN_PARALLAX_PAGE_SPAN && hasExtraEmptyScreen()) {
Michael Jurka2f817312013-09-20 03:03:42 +02001315 return 1;
1316 } else {
1317 return 0;
1318 }
1319 }
1320
Michael Jurkafe0ace32013-10-03 01:05:14 -07001321 private int getNumScreensExcludingEmptyAndCustom() {
1322 int numScrollingPages = getChildCount() - numEmptyScreensToIgnore() - numCustomPages();
Michael Jurka2f817312013-09-20 03:03:42 +02001323 return numScrollingPages;
1324 }
1325
Michael Jurkafe09cb72013-08-27 15:48:58 +02001326 public void syncWithScroll() {
1327 float offset = wallpaperOffsetForCurrentScroll();
1328 mWallpaperOffset.setFinalX(offset);
1329 updateOffset(true);
1330 }
1331
Michael Jurkaab1983f2011-01-18 15:50:17 -08001332 public float getCurrX() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001333 return mCurrentOffset;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001334 }
1335
1336 public float getFinalX() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001337 return mFinalOffset;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001338 }
1339
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001340 private void animateToFinal() {
1341 mAnimating = true;
1342 mAnimationStartOffset = mCurrentOffset;
1343 mAnimationStartTime = System.currentTimeMillis();
Michael Jurkaab1983f2011-01-18 15:50:17 -08001344 }
1345
Michael Jurkafe09cb72013-08-27 15:48:58 +02001346 private void setWallpaperOffsetSteps() {
1347 // Set wallpaper offset steps (1 / (number of screens - 1))
1348 mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 1.0f);
1349 }
1350
Michael Jurkaab1983f2011-01-18 15:50:17 -08001351 public void setFinalX(float x) {
Michael Jurkafe09cb72013-08-27 15:48:58 +02001352 scheduleUpdate();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001353 mFinalOffset = Math.max(0f, Math.min(x, 1.0f));
Michael Jurkafe0ace32013-10-03 01:05:14 -07001354 if (getNumScreensExcludingEmptyAndCustom() != mNumScreens) {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001355 if (mNumScreens > 0) {
1356 // Don't animate if we're going from 0 screens
1357 animateToFinal();
1358 }
Michael Jurkafe0ace32013-10-03 01:05:14 -07001359 mNumScreens = getNumScreensExcludingEmptyAndCustom();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001360 }
Michael Jurkaab1983f2011-01-18 15:50:17 -08001361 }
1362
Michael Jurkafe09cb72013-08-27 15:48:58 +02001363 private void scheduleUpdate() {
1364 if (!mWaitingForUpdate) {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001365 mChoreographer.postFrameCallback(this);
Michael Jurkafe09cb72013-08-27 15:48:58 +02001366 mWaitingForUpdate = true;
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001367 }
Michael Jurkaab1983f2011-01-18 15:50:17 -08001368 }
1369
1370 public void jumpToFinal() {
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001371 mCurrentOffset = mFinalOffset;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001372 }
Dianne Hackborn8f573952009-08-10 23:21:09 -07001373 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001374
Michael Jurka340c5f32010-10-21 16:49:19 -07001375 @Override
1376 public void computeScroll() {
1377 super.computeScroll();
Michael Jurkafe09cb72013-08-27 15:48:58 +02001378 mWallpaperOffset.syncWithScroll();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001379 }
1380
Patrick Dubroy94f78a52011-02-28 17:39:16 -08001381 void showOutlines() {
Michael Jurkad74c9842011-07-10 12:44:21 -07001382 if (!isSmall() && !mIsSwitchingState) {
Winson Chung9171e6d2010-11-17 17:39:27 -08001383 if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
1384 if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
Michael Jurka2ecf9952012-06-18 12:52:28 -07001385 mChildrenOutlineFadeInAnimation = LauncherAnimUtils.ofFloat(this, "childrenOutlineAlpha", 1.0f);
Winson Chung9171e6d2010-11-17 17:39:27 -08001386 mChildrenOutlineFadeInAnimation.setDuration(CHILDREN_OUTLINE_FADE_IN_DURATION);
1387 mChildrenOutlineFadeInAnimation.start();
Michael Jurka3e7c7632010-10-02 16:01:03 -07001388 }
Adam Cohenf34bab52010-09-30 14:11:56 -07001389 }
1390
Patrick Dubroy94f78a52011-02-28 17:39:16 -08001391 void hideOutlines() {
Michael Jurkad74c9842011-07-10 12:44:21 -07001392 if (!isSmall() && !mIsSwitchingState) {
Winson Chung9171e6d2010-11-17 17:39:27 -08001393 if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
1394 if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
Michael Jurka2ecf9952012-06-18 12:52:28 -07001395 mChildrenOutlineFadeOutAnimation = LauncherAnimUtils.ofFloat(this, "childrenOutlineAlpha", 0.0f);
Winson Chung9171e6d2010-11-17 17:39:27 -08001396 mChildrenOutlineFadeOutAnimation.setDuration(CHILDREN_OUTLINE_FADE_OUT_DURATION);
1397 mChildrenOutlineFadeOutAnimation.setStartDelay(CHILDREN_OUTLINE_FADE_OUT_DELAY);
1398 mChildrenOutlineFadeOutAnimation.start();
Michael Jurka3e7c7632010-10-02 16:01:03 -07001399 }
Adam Cohenf34bab52010-09-30 14:11:56 -07001400 }
1401
Patrick Dubroy94f78a52011-02-28 17:39:16 -08001402 public void showOutlinesTemporarily() {
1403 if (!mIsPageMoving && !isTouchActive()) {
1404 snapToPage(mCurrentPage);
1405 }
1406 }
1407
Winson Chung9171e6d2010-11-17 17:39:27 -08001408 public void setChildrenOutlineAlpha(float alpha) {
1409 mChildrenOutlineAlpha = alpha;
Adam Cohenf34bab52010-09-30 14:11:56 -07001410 for (int i = 0; i < getChildCount(); i++) {
1411 CellLayout cl = (CellLayout) getChildAt(i);
1412 cl.setBackgroundAlpha(alpha);
1413 }
1414 }
1415
Winson Chung9171e6d2010-11-17 17:39:27 -08001416 public float getChildrenOutlineAlpha() {
1417 return mChildrenOutlineAlpha;
1418 }
1419
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001420 void disableBackground() {
1421 mDrawBackground = false;
1422 }
1423 void enableBackground() {
1424 mDrawBackground = true;
1425 }
1426
Michael Jurka7bdb25a2011-08-03 15:16:44 -07001427 private void animateBackgroundGradient(float finalAlpha, boolean animated) {
Winson Chungb4b7fa72010-11-18 14:38:53 -08001428 if (mBackground == null) return;
Michael Jurkab9e14972011-07-25 17:57:40 -07001429 if (mBackgroundFadeInAnimation != null) {
1430 mBackgroundFadeInAnimation.cancel();
1431 mBackgroundFadeInAnimation = null;
1432 }
1433 if (mBackgroundFadeOutAnimation != null) {
1434 mBackgroundFadeOutAnimation.cancel();
1435 mBackgroundFadeOutAnimation = null;
1436 }
Michael Jurka7bdb25a2011-08-03 15:16:44 -07001437 float startAlpha = getBackgroundAlpha();
1438 if (finalAlpha != startAlpha) {
1439 if (animated) {
Michael Jurkaf1ad6082013-03-13 12:55:46 +01001440 mBackgroundFadeOutAnimation =
1441 LauncherAnimUtils.ofFloat(this, startAlpha, finalAlpha);
Michael Jurka7bdb25a2011-08-03 15:16:44 -07001442 mBackgroundFadeOutAnimation.addUpdateListener(new AnimatorUpdateListener() {
1443 public void onAnimationUpdate(ValueAnimator animation) {
1444 setBackgroundAlpha(((Float) animation.getAnimatedValue()).floatValue());
1445 }
1446 });
1447 mBackgroundFadeOutAnimation.setInterpolator(new DecelerateInterpolator(1.5f));
1448 mBackgroundFadeOutAnimation.setDuration(BACKGROUND_FADE_OUT_DURATION);
1449 mBackgroundFadeOutAnimation.start();
1450 } else {
1451 setBackgroundAlpha(finalAlpha);
1452 }
Michael Jurkab9e14972011-07-25 17:57:40 -07001453 }
Winson Chung9171e6d2010-11-17 17:39:27 -08001454 }
1455
1456 public void setBackgroundAlpha(float alpha) {
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001457 if (alpha != mBackgroundAlpha) {
1458 mBackgroundAlpha = alpha;
1459 invalidate();
1460 }
Winson Chung9171e6d2010-11-17 17:39:27 -08001461 }
1462
Adam Cohenf34bab52010-09-30 14:11:56 -07001463 public float getBackgroundAlpha() {
1464 return mBackgroundAlpha;
1465 }
1466
Adam Cohen68d73932010-11-15 10:50:58 -08001467 float backgroundAlphaInterpolator(float r) {
1468 float pivotA = 0.1f;
1469 float pivotB = 0.4f;
1470 if (r < pivotA) {
1471 return 0;
1472 } else if (r > pivotB) {
1473 return 1.0f;
1474 } else {
1475 return (r - pivotA)/(pivotB - pivotA);
1476 }
1477 }
1478
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001479 private void updatePageAlphaValues(int screenCenter) {
Michael Jurka869390b2012-05-06 15:55:19 -07001480 boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
1481 if (mWorkspaceFadeInAdjacentScreens &&
1482 mState == State.NORMAL &&
1483 !mIsSwitchingState &&
1484 !isInOverscroll) {
1485 for (int i = 0; i < getChildCount(); i++) {
1486 CellLayout child = (CellLayout) getChildAt(i);
1487 if (child != null) {
1488 float scrollProgress = getScrollProgress(screenCenter, child, i);
Adam Cohen73894962011-10-31 13:17:17 -07001489 float alpha = 1 - Math.abs(scrollProgress);
Michael Jurka869390b2012-05-06 15:55:19 -07001490 child.getShortcutsAndWidgets().setAlpha(alpha);
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001491 if (!mIsDragOccuring) {
1492 child.setBackgroundAlphaMultiplier(
1493 backgroundAlphaInterpolator(Math.abs(scrollProgress)));
1494 } else {
1495 child.setBackgroundAlphaMultiplier(1f);
1496 }
Adam Cohen73894962011-10-31 13:17:17 -07001497 }
Adam Cohenf34bab52010-09-30 14:11:56 -07001498 }
1499 }
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001500 }
1501
1502 private void setChildrenBackgroundAlphaMultipliers(float a) {
1503 for (int i = 0; i < getChildCount(); i++) {
1504 CellLayout child = (CellLayout) getChildAt(i);
1505 child.setBackgroundAlphaMultiplier(a);
1506 }
1507 }
1508
Winson Chung98ca0f72013-07-29 12:58:51 -07001509 public boolean hasCustomContent() {
Adam Cohenedb40762013-07-18 16:45:45 -07001510 return (mScreenOrder.size() > 0 && mScreenOrder.get(0) == CUSTOM_CONTENT_SCREEN_ID);
1511 }
1512
Michael Jurkafe0ace32013-10-03 01:05:14 -07001513 public int numCustomPages() {
1514 return hasCustomContent() ? 1 : 0;
1515 }
1516
Adam Cohenbffe7452013-07-22 18:21:45 -07001517 public boolean isOnOrMovingToCustomContent() {
1518 return hasCustomContent() && getNextPage() == 0;
1519 }
1520
Adam Cohenedb40762013-07-18 16:45:45 -07001521 private void updateStateForCustomContent(int screenCenter) {
Dave Hawkeya8881582013-09-17 15:55:33 -06001522 float translationX = 0;
1523 float progress = 0;
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01001524 if (hasCustomContent()) {
Adam Cohenedb40762013-07-18 16:45:45 -07001525 int index = mScreenOrder.indexOf(CUSTOM_CONTENT_SCREEN_ID);
Adam Cohen564a2e72013-10-09 14:47:32 -07001526
Adam Cohena45de072013-10-11 16:07:47 -07001527 int scrollDelta = getScrollX() - getScrollForPage(index) -
1528 getLayoutTransitionOffsetForPage(index);
1529 float scrollRange = getScrollForPage(index + 1) - getScrollForPage(index);
1530 translationX = scrollRange - scrollDelta;
1531 progress = (scrollRange - scrollDelta) / scrollRange;
Adam Cohenaf9b0e52013-09-23 19:27:38 -07001532
1533 if (isLayoutRtl()) {
1534 translationX = Math.min(0, translationX);
1535 } else {
1536 translationX = Math.max(0, translationX);
1537 }
Adam Cohen955806d2013-07-19 16:36:43 -07001538 progress = Math.max(0, progress);
Dave Hawkeya8881582013-09-17 15:55:33 -06001539 }
Adam Cohenedb40762013-07-18 16:45:45 -07001540
Dave Hawkeya8881582013-09-17 15:55:33 -06001541 if (Float.compare(progress, mLastCustomContentScrollProgress) == 0) return;
Adam Cohen84add1d2013-10-14 16:29:02 -07001542
1543 CellLayout cc = mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID);
1544 if (progress > 0 && cc.getVisibility() != VISIBLE && !isSmall()) {
1545 cc.setVisibility(VISIBLE);
1546 }
1547
Dave Hawkeya8881582013-09-17 15:55:33 -06001548 mLastCustomContentScrollProgress = progress;
Adam Cohenc36fa5c2013-08-29 11:54:42 -07001549
Dave Hawkeya8881582013-09-17 15:55:33 -06001550 setBackgroundAlpha(progress * 0.8f);
Adam Cohen22cba7f2013-07-19 16:14:00 -07001551
Dave Hawkeya8881582013-09-17 15:55:33 -06001552 if (mLauncher.getHotseat() != null) {
1553 mLauncher.getHotseat().setTranslationX(translationX);
1554 }
Adam Cohenc36fa5c2013-08-29 11:54:42 -07001555
Dave Hawkeya8881582013-09-17 15:55:33 -06001556 if (getPageIndicator() != null) {
1557 getPageIndicator().setTranslationX(translationX);
1558 }
Adam Cohenc36fa5c2013-08-29 11:54:42 -07001559
Dave Hawkeya8881582013-09-17 15:55:33 -06001560 if (mCustomContentCallbacks != null) {
1561 mCustomContentCallbacks.onScrollProgressChanged(progress);
Adam Cohenedb40762013-07-18 16:45:45 -07001562 }
1563 }
1564
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001565 @Override
Adam Cohen53805212013-10-01 10:39:23 -07001566 protected OnClickListener getPageIndicatorClickListener() {
1567 AccessibilityManager am = (AccessibilityManager)
1568 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
1569 if (!am.isTouchExplorationEnabled()) {
1570 return null;
1571 }
1572 OnClickListener listener = new OnClickListener() {
1573 @Override
1574 public void onClick(View arg0) {
1575 enterOverviewMode();
1576 }
1577 };
1578 return listener;
1579 }
1580
1581 @Override
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001582 protected void screenScrolled(int screenCenter) {
Winson Chung52aee602013-01-30 12:01:02 -08001583 final boolean isRtl = isLayoutRtl();
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001584 super.screenScrolled(screenCenter);
1585
1586 updatePageAlphaValues(screenCenter);
Adam Cohenedb40762013-07-18 16:45:45 -07001587 updateStateForCustomContent(screenCenter);
Michael Jurkad51f33a2012-06-28 15:35:26 -07001588 enableHwLayersOnVisiblePages();
Adam Cohenf34bab52010-09-30 14:11:56 -07001589
Adam Cohenaf9b0e52013-09-23 19:27:38 -07001590 boolean shouldOverScroll = (mOverScrollX < 0 && (!hasCustomContent() || isLayoutRtl())) ||
1591 (mOverScrollX > mMaxScrollX && (!hasCustomContent() || !isLayoutRtl()));
1592
1593 if (shouldOverScroll) {
Winson Chung52aee602013-01-30 12:01:02 -08001594 int index = 0;
1595 float pivotX = 0f;
1596 final float leftBiasedPivot = 0.25f;
1597 final float rightBiasedPivot = 0.75f;
1598 final int lowerIndex = 0;
1599 final int upperIndex = getChildCount() - 1;
Adam Cohena29f5042013-09-26 14:29:35 -07001600
1601 final boolean isLeftPage = mOverScrollX < 0;
1602 index = (!isRtl && isLeftPage) || (isRtl && !isLeftPage) ? lowerIndex : upperIndex;
1603 pivotX = isLeftPage ? rightBiasedPivot : leftBiasedPivot;
Winson Chung52aee602013-01-30 12:01:02 -08001604
Adam Cohenb5ba0972011-09-07 18:02:31 -07001605 CellLayout cl = (CellLayout) getChildAt(index);
1606 float scrollProgress = getScrollProgress(screenCenter, cl, index);
Winson Chung52aee602013-01-30 12:01:02 -08001607 cl.setOverScrollAmount(Math.abs(scrollProgress), isLeftPage);
1608 float rotation = -WORKSPACE_OVERSCROLL_ROTATION * scrollProgress;
Adam Cohenb5ba0972011-09-07 18:02:31 -07001609 cl.setRotationY(rotation);
Adam Cohena29f5042013-09-26 14:29:35 -07001610
1611 if (!mOverscrollTransformsSet || Float.compare(mLastOverscrollPivotX, pivotX) != 0) {
Michael Jurkab06d95f2012-04-02 06:26:53 -07001612 mOverscrollTransformsSet = true;
Adam Cohena29f5042013-09-26 14:29:35 -07001613 mLastOverscrollPivotX = pivotX;
Adam Cohen94309882012-06-08 16:24:56 -07001614 cl.setCameraDistance(mDensity * mCameraDistance);
Winson Chung52aee602013-01-30 12:01:02 -08001615 cl.setPivotX(cl.getMeasuredWidth() * pivotX);
Michael Jurkab06d95f2012-04-02 06:26:53 -07001616 cl.setPivotY(cl.getMeasuredHeight() * 0.5f);
1617 cl.setOverscrollTransformsDirty(true);
1618 }
Adam Cohenb5ba0972011-09-07 18:02:31 -07001619 } else {
Michael Jurkab06d95f2012-04-02 06:26:53 -07001620 if (mOverscrollTransformsSet) {
1621 mOverscrollTransformsSet = false;
Adam Cohenebea84d2011-11-09 17:20:41 -08001622 ((CellLayout) getChildAt(0)).resetOverscrollTransforms();
1623 ((CellLayout) getChildAt(getChildCount() - 1)).resetOverscrollTransforms();
Adam Cohen7842d7f2011-09-12 15:28:15 -07001624 }
Adam Cohenb5ba0972011-09-07 18:02:31 -07001625 }
1626 }
1627
1628 @Override
Adam Cohenb5ba0972011-09-07 18:02:31 -07001629 protected void overScroll(float amount) {
Michael Jurka869390b2012-05-06 15:55:19 -07001630 acceleratedOverScroll(amount);
Adam Cohenb5ba0972011-09-07 18:02:31 -07001631 }
1632
Joe Onorato00acb122009-08-04 16:04:30 -04001633 protected void onAttachedToWindow() {
1634 super.onAttachedToWindow();
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001635 mWindowToken = getWindowToken();
Joe Onorato956091b2010-02-19 12:47:40 -08001636 computeScroll();
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001637 mDragController.setWindowToken(mWindowToken);
1638 }
1639
1640 protected void onDetachedFromWindow() {
Romain Guye6661952013-08-08 19:13:22 -07001641 super.onDetachedFromWindow();
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001642 mWindowToken = null;
Joe Onorato00acb122009-08-04 16:04:30 -04001643 }
1644
Adam Cohen53805212013-10-01 10:39:23 -07001645 protected void onResume() {
1646 if (getPageIndicator() != null) {
1647 // In case accessibility state has changed, we need to perform this on every
1648 // attach to window
Adam Cohend36d9472013-10-10 15:32:41 -07001649 OnClickListener listener = getPageIndicatorClickListener();
1650 if (listener != null) {
1651 getPageIndicator().setOnClickListener(listener);
1652 }
Adam Cohen53805212013-10-01 10:39:23 -07001653 }
1654 AccessibilityManager am = (AccessibilityManager)
1655 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
1656 sAccessibilityEnabled = am.isEnabled();
1657 }
1658
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001659 @Override
Michael Jurka28750fb2010-09-24 17:43:49 -07001660 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Michael Jurkac5b262c2011-01-12 20:24:50 -08001661 if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
Michael Jurkafe09cb72013-08-27 15:48:58 +02001662 mWallpaperOffset.syncWithScroll();
Michael Jurkacc07e7a2013-08-26 20:56:35 +02001663 mWallpaperOffset.jumpToFinal();
Michael Jurkac5b262c2011-01-12 20:24:50 -08001664 }
Michael Jurka0142d492010-08-25 17:46:15 -07001665 super.onLayout(changed, left, top, right, bottom);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001666 }
1667
1668 @Override
Winson Chung9171e6d2010-11-17 17:39:27 -08001669 protected void onDraw(Canvas canvas) {
1670 // Draw the background gradient if necessary
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001671 if (mBackground != null && mBackgroundAlpha > 0.0f && mDrawBackground) {
Winson Chung8d006d52010-11-29 15:55:29 -08001672 int alpha = (int) (mBackgroundAlpha * 255);
Winson Chungf0ea4d32011-06-06 14:27:16 -07001673 mBackground.setAlpha(alpha);
Michael Jurka8b805b12012-04-18 14:23:14 -07001674 mBackground.setBounds(getScrollX(), 0, getScrollX() + getMeasuredWidth(),
Winson Chungf0ea4d32011-06-06 14:27:16 -07001675 getMeasuredHeight());
1676 mBackground.draw(canvas);
Winson Chung9171e6d2010-11-17 17:39:27 -08001677 }
Adam Cohen073a46f2011-05-17 16:28:09 -07001678
Winson Chung9171e6d2010-11-17 17:39:27 -08001679 super.onDraw(canvas);
Winson Chungb8b2a5a2012-07-12 17:55:31 -07001680
1681 // Call back to LauncherModel to finish binding after the first draw
Romain Guyeeacd562012-10-10 18:47:33 -07001682 post(mBindPages);
Winson Chung9171e6d2010-11-17 17:39:27 -08001683 }
1684
Adam Cohen21b41102011-11-01 17:29:52 -07001685 boolean isDrawingBackgroundGradient() {
1686 return (mBackground != null && mBackgroundAlpha > 0.0f && mDrawBackground);
1687 }
1688
Michael Jurkadfab7f02011-11-18 13:01:04 -08001689 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001690 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
Joe Onorato67886212009-09-14 19:05:05 -04001691 if (!mLauncher.isAllAppsVisible()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001692 final Folder openFolder = getOpenFolder();
1693 if (openFolder != null) {
1694 return openFolder.requestFocus(direction, previouslyFocusedRect);
1695 } else {
Michael Jurka0142d492010-08-25 17:46:15 -07001696 return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001697 }
1698 }
1699 return false;
1700 }
1701
1702 @Override
Winson Chung97d85d22011-04-13 11:27:36 -07001703 public int getDescendantFocusability() {
Michael Jurkad74c9842011-07-10 12:44:21 -07001704 if (isSmall()) {
Winson Chung97d85d22011-04-13 11:27:36 -07001705 return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
1706 }
1707 return super.getDescendantFocusability();
1708 }
1709
1710 @Override
Romain Guyc2e24c02009-06-01 16:11:41 -07001711 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
Joe Onorato67886212009-09-14 19:05:05 -04001712 if (!mLauncher.isAllAppsVisible()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001713 final Folder openFolder = getOpenFolder();
Michael Jurka0142d492010-08-25 17:46:15 -07001714 if (openFolder != null) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001715 openFolder.addFocusables(views, direction);
Michael Jurka0142d492010-08-25 17:46:15 -07001716 } else {
1717 super.addFocusables(views, direction, focusableMode);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001718 }
1719 }
1720 }
1721
Michael Jurkad74c9842011-07-10 12:44:21 -07001722 public boolean isSmall() {
Adam Cohenf358a4b2013-07-23 16:47:31 -07001723 return mState == State.SMALL || mState == State.SPRING_LOADED || mState == State.OVERVIEW;
Michael Jurkad74c9842011-07-10 12:44:21 -07001724 }
1725
Michael Jurka0142d492010-08-25 17:46:15 -07001726 void enableChildrenCache(int fromPage, int toPage) {
1727 if (fromPage > toPage) {
1728 final int temp = fromPage;
1729 fromPage = toPage;
1730 toPage = temp;
Mike Cleron3a2b3f22009-11-05 17:17:42 -08001731 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001732
Michael Jurkadee05892010-07-27 10:01:56 -07001733 final int screenCount = getChildCount();
Adam Powellfea5d022010-04-29 11:42:45 -07001734
Michael Jurka0142d492010-08-25 17:46:15 -07001735 fromPage = Math.max(fromPage, 0);
1736 toPage = Math.min(toPage, screenCount - 1);
Adam Powellfea5d022010-04-29 11:42:45 -07001737
Michael Jurka0142d492010-08-25 17:46:15 -07001738 for (int i = fromPage; i <= toPage; i++) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001739 final CellLayout layout = (CellLayout) getChildAt(i);
1740 layout.setChildrenDrawnWithCacheEnabled(true);
1741 layout.setChildrenDrawingCacheEnabled(true);
1742 }
1743 }
1744
1745 void clearChildrenCache() {
Michael Jurkadee05892010-07-27 10:01:56 -07001746 final int screenCount = getChildCount();
1747 for (int i = 0; i < screenCount; i++) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001748 final CellLayout layout = (CellLayout) getChildAt(i);
1749 layout.setChildrenDrawnWithCacheEnabled(false);
Adam Cohen8182e5b2011-08-01 15:43:31 -07001750 // In software mode, we don't want the items to continue to be drawn into bitmaps
1751 if (!isHardwareAccelerated()) {
1752 layout.setChildrenDrawingCacheEnabled(false);
1753 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001754 }
1755 }
1756
Michael Jurka3a0469d2012-06-21 09:38:41 -07001757 private void updateChildrenLayersEnabled(boolean force) {
Adam Cohenf358a4b2013-07-23 16:47:31 -07001758 boolean small = mState == State.SMALL || mState == State.OVERVIEW || mIsSwitchingState;
Michael Jurka3a0469d2012-06-21 09:38:41 -07001759 boolean enableChildrenLayers = force || small || mAnimatingViewIntoPlace || isPageMoving();
Michael Jurkad74c9842011-07-10 12:44:21 -07001760
1761 if (enableChildrenLayers != mChildrenLayersEnabled) {
1762 mChildrenLayersEnabled = enableChildrenLayers;
Michael Jurkad51f33a2012-06-28 15:35:26 -07001763 if (mChildrenLayersEnabled) {
1764 enableHwLayersOnVisiblePages();
1765 } else {
1766 for (int i = 0; i < getPageCount(); i++) {
1767 final CellLayout cl = (CellLayout) getChildAt(i);
Chris Craik01f2d7f2013-10-01 14:41:56 -07001768 cl.enableHardwareLayer(false);
Michael Jurkad51f33a2012-06-28 15:35:26 -07001769 }
1770 }
1771 }
1772 }
1773
1774 private void enableHwLayersOnVisiblePages() {
1775 if (mChildrenLayersEnabled) {
1776 final int screenCount = getChildCount();
1777 getVisiblePages(mTempVisiblePagesRange);
1778 int leftScreen = mTempVisiblePagesRange[0];
1779 int rightScreen = mTempVisiblePagesRange[1];
1780 if (leftScreen == rightScreen) {
1781 // make sure we're caching at least two pages always
1782 if (rightScreen < screenCount - 1) {
1783 rightScreen++;
1784 } else if (leftScreen > 0) {
1785 leftScreen--;
1786 }
1787 }
Chris Craik01f2d7f2013-10-01 14:41:56 -07001788
1789 final CellLayout customScreen = mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID);
Michael Jurkad51f33a2012-06-28 15:35:26 -07001790 for (int i = 0; i < screenCount; i++) {
Michael Jurka47639b92013-01-14 12:42:27 +01001791 final CellLayout layout = (CellLayout) getPageAt(i);
Chris Craik01f2d7f2013-10-01 14:41:56 -07001792
1793 // enable layers between left and right screen inclusive, except for the
1794 // customScreen, which may animate its content during transitions.
1795 boolean enableLayer = layout != customScreen &&
1796 leftScreen <= i && i <= rightScreen && shouldDrawChild(layout);
1797 layout.enableHardwareLayer(enableLayer);
Michael Jurkad74c9842011-07-10 12:44:21 -07001798 }
Michael Jurkace7e05f2011-02-01 22:02:35 -08001799 }
Michael Jurkace7e05f2011-02-01 22:02:35 -08001800 }
1801
Michael Jurka3a0469d2012-06-21 09:38:41 -07001802 public void buildPageHardwareLayers() {
1803 // force layers to be enabled just for the call to buildLayer
1804 updateChildrenLayersEnabled(true);
1805 if (getWindowToken() != null) {
1806 final int childCount = getChildCount();
1807 for (int i = 0; i < childCount; i++) {
1808 CellLayout cl = (CellLayout) getChildAt(i);
Michael Jurkad51f33a2012-06-28 15:35:26 -07001809 cl.buildHardwareLayer();
Michael Jurka3a0469d2012-06-21 09:38:41 -07001810 }
1811 }
1812 updateChildrenLayersEnabled(false);
1813 }
1814
Jeff Brown1d0867c2010-12-02 18:27:39 -08001815 protected void onWallpaperTap(MotionEvent ev) {
1816 final int[] position = mTempCell;
1817 getLocationOnScreen(position);
1818
1819 int pointerIndex = ev.getActionIndex();
1820 position[0] += (int) ev.getX(pointerIndex);
1821 position[1] += (int) ev.getY(pointerIndex);
1822
1823 mWallpaperManager.sendWallpaperCommand(getWindowToken(),
1824 ev.getAction() == MotionEvent.ACTION_UP
1825 ? WallpaperManager.COMMAND_TAP : WallpaperManager.COMMAND_SECONDARY_TAP,
1826 position[0], position[1], 0, null);
1827 }
1828
Adam Cohen61033d32010-11-15 18:29:44 -08001829 /*
1830 * This interpolator emulates the rate at which the perceived scale of an object changes
1831 * as its distance from a camera increases. When this interpolator is applied to a scale
1832 * animation on a view, it evokes the sense that the object is shrinking due to moving away
Andrew Flynn0dca1ec2012-02-29 13:33:22 -08001833 * from the camera.
Adam Cohen61033d32010-11-15 18:29:44 -08001834 */
1835 static class ZInterpolator implements TimeInterpolator {
1836 private float focalLength;
1837
1838 public ZInterpolator(float foc) {
1839 focalLength = foc;
1840 }
1841
Adam Coheneed565d2010-11-15 11:30:05 -08001842 public float getInterpolation(float input) {
1843 return (1.0f - focalLength / (focalLength + input)) /
Adam Cohen61033d32010-11-15 18:29:44 -08001844 (1.0f - focalLength / (focalLength + 1.0f));
Adam Cohencbbaf982010-11-12 14:50:33 -08001845 }
1846 }
1847
Adam Cohen61033d32010-11-15 18:29:44 -08001848 /*
1849 * The exact reverse of ZInterpolator.
1850 */
1851 static class InverseZInterpolator implements TimeInterpolator {
1852 private ZInterpolator zInterpolator;
1853 public InverseZInterpolator(float foc) {
1854 zInterpolator = new ZInterpolator(foc);
1855 }
Adam Cohencbbaf982010-11-12 14:50:33 -08001856 public float getInterpolation(float input) {
Adam Cohen61033d32010-11-15 18:29:44 -08001857 return 1 - zInterpolator.getInterpolation(1 - input);
Adam Cohencbbaf982010-11-12 14:50:33 -08001858 }
1859 }
1860
Adam Cohen61033d32010-11-15 18:29:44 -08001861 /*
1862 * ZInterpolator compounded with an ease-out.
1863 */
1864 static class ZoomOutInterpolator implements TimeInterpolator {
Adam Cohenb64d36e2011-10-17 21:48:02 -07001865 private final DecelerateInterpolator decelerate = new DecelerateInterpolator(0.75f);
1866 private final ZInterpolator zInterpolator = new ZInterpolator(0.13f);
Adam Cohen61033d32010-11-15 18:29:44 -08001867
1868 public float getInterpolation(float input) {
1869 return decelerate.getInterpolation(zInterpolator.getInterpolation(input));
1870 }
1871 }
1872
1873 /*
1874 * InvereZInterpolator compounded with an ease-out.
1875 */
1876 static class ZoomInInterpolator implements TimeInterpolator {
1877 private final InverseZInterpolator inverseZInterpolator = new InverseZInterpolator(0.35f);
1878 private final DecelerateInterpolator decelerate = new DecelerateInterpolator(3.0f);
1879
1880 public float getInterpolation(float input) {
1881 return decelerate.getInterpolation(inverseZInterpolator.getInterpolation(input));
1882 }
1883 }
1884
Adam Cohen61033d32010-11-15 18:29:44 -08001885 private final ZoomInInterpolator mZoomInInterpolator = new ZoomInInterpolator();
Michael Jurka3e7c7632010-10-02 16:01:03 -07001886
Michael Jurka3e7c7632010-10-02 16:01:03 -07001887 /*
Adam Cohen66396872011-04-15 17:50:36 -07001888 *
1889 * We call these methods (onDragStartedWithItemSpans/onDragStartedWithSize) whenever we
1890 * start a drag in Launcher, regardless of whether the drag has ever entered the Workspace
1891 *
1892 * These methods mark the appropriate pages as accepting drops (which alters their visual
1893 * appearance).
1894 *
1895 */
1896 public void onDragStartedWithItem(View v) {
Adam Cohen66396872011-04-15 17:50:36 -07001897 final Canvas canvas = new Canvas();
1898
Adam Cohen66396872011-04-15 17:50:36 -07001899 // The outline is used to visualize where the item will land if dropped
Adam Cohend41fbf52012-02-16 23:53:59 -08001900 mDragOutline = createDragOutline(v, canvas, DRAG_BITMAP_PADDING);
Adam Cohen66396872011-04-15 17:50:36 -07001901 }
1902
Michael Jurka8c3339b2012-06-14 16:18:21 -07001903 public void onDragStartedWithItem(PendingAddItemInfo info, Bitmap b, boolean clipAlpha) {
Michael Jurkad3ef3062010-11-23 16:23:58 -08001904 final Canvas canvas = new Canvas();
1905
Michael Jurka038f9d82011-11-03 13:50:45 -07001906 int[] size = estimateItemSize(info.spanX, info.spanY, info, false);
Adam Cohen66396872011-04-15 17:50:36 -07001907
Michael Jurkad3ef3062010-11-23 16:23:58 -08001908 // The outline is used to visualize where the item will land if dropped
Adam Cohend41fbf52012-02-16 23:53:59 -08001909 mDragOutline = createDragOutline(b, canvas, DRAG_BITMAP_PADDING, size[0],
Michael Jurka8c3339b2012-06-14 16:18:21 -07001910 size[1], clipAlpha);
Michael Jurka3e7c7632010-10-02 16:01:03 -07001911 }
1912
Patrick Dubroy758a9232011-03-03 19:54:56 -08001913 public void exitWidgetResizeMode() {
Adam Cohen716b51e2011-06-30 12:09:54 -07001914 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohen67882692011-03-11 15:29:03 -08001915 dragLayer.clearAllResizeFrames();
Patrick Dubroy758a9232011-03-03 19:54:56 -08001916 }
1917
Adam Cohen4b285c52011-07-21 14:24:06 -07001918 private void initAnimationArrays() {
1919 final int childCount = getChildCount();
Adam Cohendcd297f2013-06-18 13:13:40 -07001920 if (mLastChildCount == childCount) return;
Adam Cohen7d30a372013-07-01 17:03:59 -07001921
Adam Cohen4b285c52011-07-21 14:24:06 -07001922 mOldBackgroundAlphas = new float[childCount];
Adam Cohen4b285c52011-07-21 14:24:06 -07001923 mOldAlphas = new float[childCount];
Adam Cohen4b285c52011-07-21 14:24:06 -07001924 mNewBackgroundAlphas = new float[childCount];
Adam Cohen4b285c52011-07-21 14:24:06 -07001925 mNewAlphas = new float[childCount];
Adam Cohen4b285c52011-07-21 14:24:06 -07001926 }
1927
Michael Jurka2a4b1a82011-12-07 14:00:02 -08001928 Animator getChangeStateAnimation(final State state, boolean animated) {
Adam Cohenf358a4b2013-07-23 16:47:31 -07001929 return getChangeStateAnimation(state, animated, 0, -1);
Adam Cohen7777d962011-08-18 18:58:38 -07001930 }
1931
Adam Cohenf358a4b2013-07-23 16:47:31 -07001932 @Override
1933 protected void getOverviewModePages(int[] range) {
Michael Jurkafe0ace32013-10-03 01:05:14 -07001934 int start = numCustomPages();
Adam Cohen1003be92013-09-16 14:09:28 -07001935 int end = getChildCount() - 1;
1936
1937 range[0] = Math.max(0, Math.min(start, getChildCount() - 1));
1938 range[1] = Math.max(0, end);
Adam Cohendedbd962013-07-11 14:21:49 -07001939 }
1940
1941 protected void onStartReordering() {
1942 super.onStartReordering();
Adam Cohendedbd962013-07-11 14:21:49 -07001943 showOutlines();
Adam Cohen22cba7f2013-07-19 16:14:00 -07001944 // Reordering handles its own animations, disable the automatic ones.
Winson Chung964df6b2013-10-11 15:55:37 -07001945 disableLayoutTransitions();
Adam Cohendedbd962013-07-11 14:21:49 -07001946 }
1947
1948 protected void onEndReordering() {
1949 super.onEndReordering();
Adam Cohendedbd962013-07-11 14:21:49 -07001950
Adam Cohen2bf63d52013-09-29 17:46:49 -07001951 hideOutlines();
Adam Cohendedbd962013-07-11 14:21:49 -07001952 mScreenOrder.clear();
Adam Cohen2bf63d52013-09-29 17:46:49 -07001953 int count = getChildCount();
Adam Cohendedbd962013-07-11 14:21:49 -07001954 for (int i = 0; i < count; i++) {
1955 CellLayout cl = ((CellLayout) getChildAt(i));
1956 mScreenOrder.add(getIdForScreen(cl));
1957 }
Winson Chungd64d1762013-08-20 14:37:16 -07001958
Adam Cohendedbd962013-07-11 14:21:49 -07001959 mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
Adam Cohen22cba7f2013-07-19 16:14:00 -07001960
1961 // Re-enable auto layout transitions for page deletion.
Winson Chung964df6b2013-10-11 15:55:37 -07001962 enableLayoutTransitions();
Adam Cohendedbd962013-07-11 14:21:49 -07001963 }
1964
Adam Cohenf358a4b2013-07-23 16:47:31 -07001965 public boolean isInOverviewMode() {
1966 return mState == State.OVERVIEW;
1967 }
1968
Adam Cohen93c97562013-09-26 13:48:01 -07001969 public boolean enterOverviewMode() {
1970 if (mTouchState != TOUCH_STATE_REST) {
1971 return false;
1972 }
Michael Jurka336fd4f2013-09-12 00:05:02 +02001973 enableOverviewMode(true, -1, true);
Adam Cohen93c97562013-09-26 13:48:01 -07001974 return true;
Adam Cohenf358a4b2013-07-23 16:47:31 -07001975 }
1976
Michael Jurka336fd4f2013-09-12 00:05:02 +02001977 public void exitOverviewMode(boolean animated) {
1978 exitOverviewMode(-1, animated);
Adam Cohenf358a4b2013-07-23 16:47:31 -07001979 }
1980
Michael Jurka336fd4f2013-09-12 00:05:02 +02001981 public void exitOverviewMode(int snapPage, boolean animated) {
1982 enableOverviewMode(false, snapPage, animated);
Adam Cohenf358a4b2013-07-23 16:47:31 -07001983 }
1984
Michael Jurka336fd4f2013-09-12 00:05:02 +02001985 private void enableOverviewMode(boolean enable, int snapPage, boolean animated) {
Adam Cohenf358a4b2013-07-23 16:47:31 -07001986 State finalState = Workspace.State.OVERVIEW;
1987 if (!enable) {
1988 finalState = Workspace.State.NORMAL;
1989 }
1990
Michael Jurka336fd4f2013-09-12 00:05:02 +02001991 Animator workspaceAnim = getChangeStateAnimation(finalState, animated, 0, snapPage);
1992 if (workspaceAnim != null) {
Adam Cohenf3434992013-09-16 16:52:59 -07001993 onTransitionPrepare();
Michael Jurka336fd4f2013-09-12 00:05:02 +02001994 workspaceAnim.addListener(new AnimatorListenerAdapter() {
1995 @Override
1996 public void onAnimationEnd(Animator arg0) {
Adam Cohenf3434992013-09-16 16:52:59 -07001997 onTransitionEnd();
Michael Jurka336fd4f2013-09-12 00:05:02 +02001998 }
1999 });
2000 workspaceAnim.start();
2001 }
Adam Cohenf358a4b2013-07-23 16:47:31 -07002002 }
2003
Adam Cohen410f3cd2013-09-22 12:09:32 -07002004 int getOverviewModeTranslationY() {
2005 int childHeight = getNormalChildHeight();
2006 int viewPortHeight = getViewportHeight();
2007 int scaledChildHeight = (int) (mOverviewModeShrinkFactor * childHeight);
2008
2009 int offset = (viewPortHeight - scaledChildHeight) / 2;
2010 int offsetDelta = mOverviewModePageOffset - offset + mInsets.top;
2011
2012 return offsetDelta;
2013 }
2014
Winson Chung5841efa2013-09-30 18:06:44 -07002015 boolean shouldVoiceButtonProxyBeVisible() {
2016 if (isOnOrMovingToCustomContent()) {
2017 return false;
2018 }
2019 if (mState != State.NORMAL) {
2020 return false;
2021 }
2022 return true;
2023 }
2024
Adam Cohenedaaa302013-10-01 17:33:27 -07002025 public void updateInteractionForState() {
2026 if (mState != State.NORMAL) {
2027 mLauncher.onInteractionBegin();
2028 } else {
2029 mLauncher.onInteractionEnd();
2030 }
2031 }
2032
2033 private void setState(State state) {
2034 mState = state;
2035 updateInteractionForState();
Adam Cohen53805212013-10-01 10:39:23 -07002036 updateAccessibilityFlags();
2037 }
2038
2039 private void updateAccessibilityFlags() {
2040 int accessible = mState == State.NORMAL ?
2041 ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES :
2042 ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
2043 setImportantForAccessibility(accessible);
Adam Cohenedaaa302013-10-01 17:33:27 -07002044 }
2045
Adam Cohenf358a4b2013-07-23 16:47:31 -07002046 Animator getChangeStateAnimation(final State state, boolean animated, int delay, int snapPage) {
Michael Jurkabdf78552011-10-31 14:34:25 -07002047 if (mState == state) {
Michael Jurka2a4b1a82011-12-07 14:00:02 -08002048 return null;
Michael Jurka99633da2011-07-27 22:40:17 -07002049 }
2050
Winson Chung1b7c1d32011-10-25 12:09:38 -07002051 // Initialize animation arrays for the first time if necessary
2052 initAnimationArrays();
2053
Michael Jurka2ecf9952012-06-18 12:52:28 -07002054 AnimatorSet anim = animated ? LauncherAnimUtils.createAnimatorSet() : null;
Adam Lesinski6b879f02010-11-04 16:15:23 -07002055
Winson Chung1b7c1d32011-10-25 12:09:38 -07002056 final State oldState = mState;
2057 final boolean oldStateIsNormal = (oldState == State.NORMAL);
Michael Jurka4ff7d792012-04-02 03:46:50 -07002058 final boolean oldStateIsSpringLoaded = (oldState == State.SPRING_LOADED);
Winson Chung1b7c1d32011-10-25 12:09:38 -07002059 final boolean oldStateIsSmall = (oldState == State.SMALL);
Adam Cohenf358a4b2013-07-23 16:47:31 -07002060 final boolean oldStateIsOverview = (oldState == State.OVERVIEW);
Adam Cohenedaaa302013-10-01 17:33:27 -07002061 setState(state);
Winson Chung1b7c1d32011-10-25 12:09:38 -07002062 final boolean stateIsNormal = (state == State.NORMAL);
2063 final boolean stateIsSpringLoaded = (state == State.SPRING_LOADED);
2064 final boolean stateIsSmall = (state == State.SMALL);
Adam Cohenf358a4b2013-07-23 16:47:31 -07002065 final boolean stateIsOverview = (state == State.OVERVIEW);
Winson Chung2d75f122013-09-23 16:53:31 -07002066 float finalBackgroundAlpha = (stateIsSpringLoaded || stateIsOverview) ? 1.0f : 0f;
Adam Cohenf358a4b2013-07-23 16:47:31 -07002067 float finalHotseatAndPageIndicatorAlpha = (stateIsOverview || stateIsSmall) ? 0f : 1f;
2068 float finalOverviewPanelAlpha = stateIsOverview ? 1f : 0f;
Winson Chung2d75f122013-09-23 16:53:31 -07002069 float finalSearchBarAlpha = !stateIsNormal ? 0f : 1f;
Adam Cohen410f3cd2013-09-22 12:09:32 -07002070 float finalWorkspaceTranslationY = stateIsOverview ? getOverviewModeTranslationY() : 0;
Adam Cohenf358a4b2013-07-23 16:47:31 -07002071
Winson Chungf4bd2362013-10-07 17:11:27 -07002072 boolean workspaceToAllApps = (oldStateIsNormal && stateIsSmall);
2073 boolean allAppsToWorkspace = (oldStateIsSmall && stateIsNormal);
Adam Cohen58993ad2013-10-11 17:57:38 -07002074 boolean workspaceToOverview = (oldStateIsNormal && stateIsOverview);
2075 boolean overviewToWorkspace = (oldStateIsOverview && stateIsNormal);
2076
Adam Cohen7d30a372013-07-01 17:03:59 -07002077 mNewScale = 1.0f;
Adam Cohenbeff8c62011-08-31 17:46:01 -07002078
Adam Cohenf358a4b2013-07-23 16:47:31 -07002079 if (oldStateIsOverview) {
2080 disableFreeScroll(snapPage);
2081 } else if (stateIsOverview) {
2082 enableFreeScroll();
2083 }
2084
Adam Cohen7777d962011-08-18 18:58:38 -07002085 if (state != State.NORMAL) {
Adam Cohenf358a4b2013-07-23 16:47:31 -07002086 if (stateIsSpringLoaded) {
2087 mNewScale = mSpringLoadedShrinkFactor;
2088 } else if (stateIsOverview) {
2089 mNewScale = mOverviewModeShrinkFactor;
2090 } else if (stateIsSmall){
Adam Cohenaf9b0e52013-09-23 19:27:38 -07002091 mNewScale = mOverviewModeShrinkFactor - 0.3f;
Adam Cohenf358a4b2013-07-23 16:47:31 -07002092 }
Winson Chungf4bd2362013-10-07 17:11:27 -07002093 if (workspaceToAllApps) {
Michael Jurka3a0469d2012-06-21 09:38:41 -07002094 updateChildrenLayersEnabled(false);
Adam Cohen7777d962011-08-18 18:58:38 -07002095 }
Adam Cohen7777d962011-08-18 18:58:38 -07002096 }
Winson Chungf4bd2362013-10-07 17:11:27 -07002097
Adam Cohenfa2450a2013-10-11 18:20:27 -07002098 final int duration;
2099 if (workspaceToAllApps) {
2100 duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime);
2101 } else if (workspaceToOverview || overviewToWorkspace) {
2102 duration = getResources().getInteger(R.integer.config_overviewTransitionTime);
2103 } else {
2104 duration = getResources().getInteger(R.integer.config_appsCustomizeWorkspaceShrinkTime);
2105 }
2106
Winson Chung1b7c1d32011-10-25 12:09:38 -07002107 for (int i = 0; i < getChildCount(); i++) {
2108 final CellLayout cl = (CellLayout) getChildAt(i);
Winson Chung296ab0f2013-10-07 18:21:59 -07002109 boolean isCurrentPage = (i == getNextPage());
Winson Chung2d75f122013-09-23 16:53:31 -07002110 float initialAlpha = cl.getShortcutsAndWidgets().getAlpha();
Winson Chungf4bd2362013-10-07 17:11:27 -07002111 float finalAlpha = stateIsSmall ? 0f : 1f;
2112
2113 // If we are animating to/from the small state, then hide the side pages and fade the
2114 // current page in
2115 if (!mIsSwitchingState) {
2116 if (workspaceToAllApps || allAppsToWorkspace) {
2117 if (allAppsToWorkspace && isCurrentPage) {
2118 initialAlpha = 0f;
2119 } else if (!isCurrentPage) {
2120 initialAlpha = finalAlpha = 0f;
2121 }
2122 cl.setShortcutAndWidgetAlpha(initialAlpha);
2123 }
2124 }
Adam Cohen7777d962011-08-18 18:58:38 -07002125
Winson Chung1b7c1d32011-10-25 12:09:38 -07002126 mOldAlphas[i] = initialAlpha;
2127 mNewAlphas[i] = finalAlpha;
Adam Cohen7777d962011-08-18 18:58:38 -07002128 if (animated) {
Adam Cohen7777d962011-08-18 18:58:38 -07002129 mOldBackgroundAlphas[i] = cl.getBackgroundAlpha();
Adam Cohen7777d962011-08-18 18:58:38 -07002130 mNewBackgroundAlphas[i] = finalBackgroundAlpha;
Adam Cohen7777d962011-08-18 18:58:38 -07002131 } else {
Winson Chung1b7c1d32011-10-25 12:09:38 -07002132 cl.setBackgroundAlpha(finalBackgroundAlpha);
Michael Jurkaa52570f2012-03-20 03:18:20 -07002133 cl.setShortcutAndWidgetAlpha(finalAlpha);
Adam Cohen7777d962011-08-18 18:58:38 -07002134 }
Michael Jurkadee05892010-07-27 10:01:56 -07002135 }
Winson Chung8d006d52010-11-29 15:55:29 -08002136
Adam Cohen3d411982013-09-24 17:02:06 -07002137 final View searchBar = mLauncher.getQsbBar();
2138 final View overviewPanel = mLauncher.getOverviewPanel();
2139 final View hotseat = mLauncher.getHotseat();
Adam Cohen7777d962011-08-18 18:58:38 -07002140 if (animated) {
Adam Cohenfa2450a2013-10-11 18:20:27 -07002141 anim.setDuration(duration);
Adam Cohen7d30a372013-07-01 17:03:59 -07002142 LauncherViewPropertyAnimator scale = new LauncherViewPropertyAnimator(this);
2143 scale.scaleX(mNewScale)
2144 .scaleY(mNewScale)
Adam Cohen410f3cd2013-09-22 12:09:32 -07002145 .translationY(finalWorkspaceTranslationY)
Adam Cohen7d30a372013-07-01 17:03:59 -07002146 .setInterpolator(mZoomInInterpolator);
2147 anim.play(scale);
Michael Jurka7407d2a2011-12-12 21:48:38 -08002148 for (int index = 0; index < getChildCount(); index++) {
2149 final int i = index;
2150 final CellLayout cl = (CellLayout) getChildAt(i);
Michael Jurka869390b2012-05-06 15:55:19 -07002151 float currentAlpha = cl.getShortcutsAndWidgets().getAlpha();
Michael Jurka2a4b1a82011-12-07 14:00:02 -08002152 if (mOldAlphas[i] == 0 && mNewAlphas[i] == 0) {
Michael Jurka7372c592012-01-16 04:21:35 -08002153 cl.setBackgroundAlpha(mNewBackgroundAlphas[i]);
Michael Jurkaa52570f2012-03-20 03:18:20 -07002154 cl.setShortcutAndWidgetAlpha(mNewAlphas[i]);
Michael Jurka7407d2a2011-12-12 21:48:38 -08002155 } else {
Michael Jurka869390b2012-05-06 15:55:19 -07002156 if (mOldAlphas[i] != mNewAlphas[i] || currentAlpha != mNewAlphas[i]) {
Michael Jurka6c8a5792012-03-22 05:24:37 -07002157 LauncherViewPropertyAnimator alphaAnim =
2158 new LauncherViewPropertyAnimator(cl.getShortcutsAndWidgets());
Michael Jurkaa52570f2012-03-20 03:18:20 -07002159 alphaAnim.alpha(mNewAlphas[i])
Michael Jurkaa52570f2012-03-20 03:18:20 -07002160 .setInterpolator(mZoomInInterpolator);
2161 anim.play(alphaAnim);
2162 }
Michael Jurka7407d2a2011-12-12 21:48:38 -08002163 if (mOldBackgroundAlphas[i] != 0 ||
Michael Jurka869390b2012-05-06 15:55:19 -07002164 mNewBackgroundAlphas[i] != 0) {
Michael Jurkaf1ad6082013-03-13 12:55:46 +01002165 ValueAnimator bgAnim =
Adam Cohenfa2450a2013-10-11 18:20:27 -07002166 LauncherAnimUtils.ofFloat(cl, 0f, 1f);
Michael Jurka7407d2a2011-12-12 21:48:38 -08002167 bgAnim.setInterpolator(mZoomInInterpolator);
2168 bgAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
2169 public void onAnimationUpdate(float a, float b) {
Michael Jurka7372c592012-01-16 04:21:35 -08002170 cl.setBackgroundAlpha(
Michael Jurka7407d2a2011-12-12 21:48:38 -08002171 a * mOldBackgroundAlphas[i] +
2172 b * mNewBackgroundAlphas[i]);
Michael Jurka7407d2a2011-12-12 21:48:38 -08002173 }
2174 });
2175 anim.play(bgAnim);
2176 }
Michael Jurka2a4b1a82011-12-07 14:00:02 -08002177 }
2178 }
Winson Chung66700732013-08-20 16:56:15 -07002179 ObjectAnimator pageIndicatorAlpha = null;
2180 if (getPageIndicator() != null) {
2181 pageIndicatorAlpha = ObjectAnimator.ofFloat(getPageIndicator(), "alpha",
2182 finalHotseatAndPageIndicatorAlpha);
2183 }
Adam Cohen3d411982013-09-24 17:02:06 -07002184 ObjectAnimator hotseatAlpha = ObjectAnimator.ofFloat(hotseat, "alpha",
Adam Cohenf358a4b2013-07-23 16:47:31 -07002185 finalHotseatAndPageIndicatorAlpha);
Adam Cohendbdff6b2013-09-18 19:09:15 -07002186 ObjectAnimator searchBarAlpha = ObjectAnimator.ofFloat(searchBar,
2187 "alpha", finalSearchBarAlpha);
Adam Cohen3d411982013-09-24 17:02:06 -07002188 ObjectAnimator overviewPanelAlpha = ObjectAnimator.ofFloat(overviewPanel,
Adam Cohenf358a4b2013-07-23 16:47:31 -07002189 "alpha", finalOverviewPanelAlpha);
Adam Cohen3d411982013-09-24 17:02:06 -07002190
Adam Cohena5f4e482013-10-11 12:10:28 -07002191 overviewPanelAlpha.addListener(new AlphaUpdateListener(overviewPanel));
2192 hotseatAlpha.addListener(new AlphaUpdateListener(hotseat));
2193 searchBarAlpha.addListener(new AlphaUpdateListener(searchBar));
Adam Cohen3d411982013-09-24 17:02:06 -07002194
Adam Cohen58993ad2013-10-11 17:57:38 -07002195 if (workspaceToOverview) {
2196 hotseatAlpha.setInterpolator(new DecelerateInterpolator(2));
2197 } else if (overviewToWorkspace) {
2198 overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2));
2199 }
2200
Winson Chung66700732013-08-20 16:56:15 -07002201 if (getPageIndicator() != null) {
Adam Cohena5f4e482013-10-11 12:10:28 -07002202 pageIndicatorAlpha.addListener(new AlphaUpdateListener(getPageIndicator()));
Winson Chung66700732013-08-20 16:56:15 -07002203 }
Adam Cohen3d411982013-09-24 17:02:06 -07002204
Adam Cohenf358a4b2013-07-23 16:47:31 -07002205 anim.play(overviewPanelAlpha);
2206 anim.play(hotseatAlpha);
Adam Cohendbdff6b2013-09-18 19:09:15 -07002207 anim.play(searchBarAlpha);
Adam Cohenf358a4b2013-07-23 16:47:31 -07002208 anim.play(pageIndicatorAlpha);
Michael Jurka2a4b1a82011-12-07 14:00:02 -08002209 anim.setStartDelay(delay);
Adam Cohenf358a4b2013-07-23 16:47:31 -07002210 } else {
Adam Cohen3d411982013-09-24 17:02:06 -07002211 overviewPanel.setAlpha(finalOverviewPanelAlpha);
2212 AlphaUpdateListener.updateVisibility(overviewPanel);
2213 hotseat.setAlpha(finalHotseatAndPageIndicatorAlpha);
2214 AlphaUpdateListener.updateVisibility(hotseat);
Winson Chung66700732013-08-20 16:56:15 -07002215 if (getPageIndicator() != null) {
2216 getPageIndicator().setAlpha(finalHotseatAndPageIndicatorAlpha);
Michael Jurka336fd4f2013-09-12 00:05:02 +02002217 AlphaUpdateListener.updateVisibility(getPageIndicator());
Winson Chung66700732013-08-20 16:56:15 -07002218 }
Adam Cohendbdff6b2013-09-18 19:09:15 -07002219 searchBar.setAlpha(finalSearchBarAlpha);
Adam Cohen3f452c82013-09-19 11:57:17 -07002220 AlphaUpdateListener.updateVisibility(searchBar);
2221 updateCustomContentVisibility();
Adam Cohen410f3cd2013-09-22 12:09:32 -07002222 setScaleX(mNewScale);
2223 setScaleY(mNewScale);
2224 setTranslationY(finalWorkspaceTranslationY);
Adam Cohendbdff6b2013-09-18 19:09:15 -07002225 }
Winson Chung5841efa2013-09-30 18:06:44 -07002226 mLauncher.updateVoiceButtonProxyVisible(false);
Adam Cohen7777d962011-08-18 18:58:38 -07002227
Winson Chung1b7c1d32011-10-25 12:09:38 -07002228 if (stateIsSpringLoaded) {
Michael Jurka7bdb25a2011-08-03 15:16:44 -07002229 // Right now we're covered by Apps Customize
2230 // Show the background gradient immediately, so the gradient will
2231 // be showing once AppsCustomize disappears
2232 animateBackgroundGradient(getResources().getInteger(
2233 R.integer.config_appsCustomizeSpringLoadedBgAlpha) / 100f, false);
Adam Cohendbdff6b2013-09-18 19:09:15 -07002234 } else if (stateIsOverview) {
2235 animateBackgroundGradient(getResources().getInteger(
2236 R.integer.config_appsCustomizeSpringLoadedBgAlpha) / 100f, true);
Michael Jurka7bdb25a2011-08-03 15:16:44 -07002237 } else {
2238 // Fade the background gradient away
Adam Cohened307df2013-10-02 09:37:31 -07002239 animateBackgroundGradient(0f, animated);
Michael Jurka7bdb25a2011-08-03 15:16:44 -07002240 }
Michael Jurka2a4b1a82011-12-07 14:00:02 -08002241 return anim;
Michael Jurkadee05892010-07-27 10:01:56 -07002242 }
2243
Adam Cohena5f4e482013-10-11 12:10:28 -07002244 static class AlphaUpdateListener implements AnimatorUpdateListener, AnimatorListener {
Adam Cohenf358a4b2013-07-23 16:47:31 -07002245 View view;
2246 public AlphaUpdateListener(View v) {
2247 view = v;
2248 }
2249
2250 @Override
2251 public void onAnimationUpdate(ValueAnimator arg0) {
Michael Jurka336fd4f2013-09-12 00:05:02 +02002252 updateVisibility(view);
2253 }
2254
2255 public static void updateVisibility(View view) {
Adam Cohen53805212013-10-01 10:39:23 -07002256 // We want to avoid the extra layout pass by setting the views to GONE unless
2257 // accessibility is on, in which case not setting them to GONE causes a glitch.
2258 int invisibleState = sAccessibilityEnabled ? GONE : INVISIBLE;
2259 if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != invisibleState) {
2260 view.setVisibility(invisibleState);
Adam Cohenf358a4b2013-07-23 16:47:31 -07002261 } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD
2262 && view.getVisibility() != VISIBLE) {
2263 view.setVisibility(VISIBLE);
2264 }
2265 }
Adam Cohena5f4e482013-10-11 12:10:28 -07002266
2267 @Override
2268 public void onAnimationCancel(Animator arg0) {
2269 }
2270
2271 @Override
2272 public void onAnimationEnd(Animator arg0) {
2273 updateVisibility(view);
2274 }
2275
2276 @Override
2277 public void onAnimationRepeat(Animator arg0) {
2278 }
2279
2280 @Override
2281 public void onAnimationStart(Animator arg0) {
2282 // We want the views to be visible for animation, so fade-in/out is visible
2283 view.setVisibility(VISIBLE);
2284 }
Adam Cohenf358a4b2013-07-23 16:47:31 -07002285 }
2286
Michael Jurkabed61d22012-02-14 22:51:29 -08002287 @Override
Michael Jurkaa35e35a2012-04-26 15:04:28 -07002288 public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
Adam Cohenf3434992013-09-16 16:52:59 -07002289 onTransitionPrepare();
Michael Jurkabed61d22012-02-14 22:51:29 -08002290 }
2291
2292 @Override
Michael Jurkaa35e35a2012-04-26 15:04:28 -07002293 public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
2294 }
2295
2296 @Override
Winson Chung70442722012-02-10 15:43:22 -08002297 public void onLauncherTransitionStep(Launcher l, float t) {
2298 mTransitionProgress = t;
2299 }
2300
2301 @Override
Michael Jurkabed61d22012-02-14 22:51:29 -08002302 public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
Adam Cohenf3434992013-09-16 16:52:59 -07002303 onTransitionEnd();
2304 }
2305
2306 private void onTransitionPrepare() {
2307 mIsSwitchingState = true;
Winson Chungf4bd2362013-10-07 17:11:27 -07002308
2309 // Invalidate here to ensure that the pages are rendered during the state change transition.
2310 invalidate();
2311
Adam Cohenf3434992013-09-16 16:52:59 -07002312 updateChildrenLayersEnabled(false);
Adam Cohen3f452c82013-09-19 11:57:17 -07002313 hideCustomContentIfNecessary();
2314 }
2315
2316 void updateCustomContentVisibility() {
2317 int visibility = mState == Workspace.State.NORMAL ? VISIBLE : INVISIBLE;
2318 if (hasCustomContent()) {
2319 mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(visibility);
2320 }
2321 }
2322
2323 void showCustomContentIfNecessary() {
2324 boolean show = mState == Workspace.State.NORMAL;
2325 if (show && hasCustomContent()) {
2326 mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(VISIBLE);
2327 }
2328 }
2329
2330 void hideCustomContentIfNecessary() {
2331 boolean hide = mState != Workspace.State.NORMAL;
2332 if (hide && hasCustomContent()) {
2333 mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(INVISIBLE);
Adam Cohenf3434992013-09-16 16:52:59 -07002334 }
2335 }
2336
2337 private void onTransitionEnd() {
Michael Jurkabed61d22012-02-14 22:51:29 -08002338 mIsSwitchingState = false;
Michael Jurka3a0469d2012-06-21 09:38:41 -07002339 updateChildrenLayersEnabled(false);
Michael Jurkabed61d22012-02-14 22:51:29 -08002340 // The code in getChangeStateAnimation to determine initialAlpha and finalAlpha will ensure
2341 // ensure that only the current page is visible during (and subsequently, after) the
2342 // transition animation. If fade adjacent pages is disabled, then re-enable the page
2343 // visibility after the transition animation.
Michael Jurka869390b2012-05-06 15:55:19 -07002344 if (!mWorkspaceFadeInAdjacentScreens) {
Michael Jurkabed61d22012-02-14 22:51:29 -08002345 for (int i = 0; i < getChildCount(); i++) {
2346 final CellLayout cl = (CellLayout) getChildAt(i);
Michael Jurkaa52570f2012-03-20 03:18:20 -07002347 cl.setShortcutAndWidgetAlpha(1f);
Michael Jurkabed61d22012-02-14 22:51:29 -08002348 }
2349 }
Adam Cohen3f452c82013-09-19 11:57:17 -07002350 showCustomContentIfNecessary();
Michael Jurkabed61d22012-02-14 22:51:29 -08002351 }
2352
2353 @Override
2354 public View getContent() {
2355 return this;
2356 }
2357
Joe Onorato4be866d2010-10-10 11:26:02 -07002358 /**
2359 * Draw the View v into the given Canvas.
2360 *
2361 * @param v the view to draw
2362 * @param destCanvas the canvas to draw on
2363 * @param padding the horizontal and vertical padding to use when drawing
2364 */
Adam Cohenac8c8762011-07-13 11:15:27 -07002365 private void drawDragView(View v, Canvas destCanvas, int padding, boolean pruneToDrawable) {
Joe Onorato4be866d2010-10-10 11:26:02 -07002366 final Rect clipRect = mTempRect;
2367 v.getDrawingRect(clipRect);
2368
Adam Cohen099f60d2011-08-23 21:07:26 -07002369 boolean textVisible = false;
2370
Adam Cohenac8c8762011-07-13 11:15:27 -07002371 destCanvas.save();
2372 if (v instanceof TextView && pruneToDrawable) {
2373 Drawable d = ((TextView) v).getCompoundDrawables()[1];
2374 clipRect.set(0, 0, d.getIntrinsicWidth() + padding, d.getIntrinsicHeight() + padding);
2375 destCanvas.translate(padding / 2, padding / 2);
2376 d.draw(destCanvas);
2377 } else {
2378 if (v instanceof FolderIcon) {
Adam Cohen099f60d2011-08-23 21:07:26 -07002379 // For FolderIcons the text can bleed into the icon area, and so we need to
2380 // hide the text completely (which can't be achieved by clipping).
2381 if (((FolderIcon) v).getTextVisible()) {
2382 ((FolderIcon) v).setTextVisible(false);
2383 textVisible = true;
2384 }
Adam Cohenac8c8762011-07-13 11:15:27 -07002385 } else if (v instanceof BubbleTextView) {
2386 final BubbleTextView tv = (BubbleTextView) v;
2387 clipRect.bottom = tv.getExtendedPaddingTop() - (int) BubbleTextView.PADDING_V +
2388 tv.getLayout().getLineTop(0);
2389 } else if (v instanceof TextView) {
2390 final TextView tv = (TextView) v;
2391 clipRect.bottom = tv.getExtendedPaddingTop() - tv.getCompoundDrawablePadding() +
2392 tv.getLayout().getLineTop(0);
2393 }
2394 destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2);
2395 destCanvas.clipRect(clipRect, Op.REPLACE);
2396 v.draw(destCanvas);
Adam Cohen099f60d2011-08-23 21:07:26 -07002397
2398 // Restore text visibility of FolderIcon if necessary
2399 if (textVisible) {
2400 ((FolderIcon) v).setTextVisible(true);
2401 }
Adam Cohenac8c8762011-07-13 11:15:27 -07002402 }
2403 destCanvas.restore();
2404 }
2405
2406 /**
2407 * Returns a new bitmap to show when the given View is being dragged around.
2408 * Responsibility for the bitmap is transferred to the caller.
2409 */
2410 public Bitmap createDragBitmap(View v, Canvas canvas, int padding) {
Adam Cohenac8c8762011-07-13 11:15:27 -07002411 Bitmap b;
2412
2413 if (v instanceof TextView) {
2414 Drawable d = ((TextView) v).getCompoundDrawables()[1];
2415 b = Bitmap.createBitmap(d.getIntrinsicWidth() + padding,
2416 d.getIntrinsicHeight() + padding, Bitmap.Config.ARGB_8888);
2417 } else {
2418 b = Bitmap.createBitmap(
2419 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
Joe Onorato4be866d2010-10-10 11:26:02 -07002420 }
2421
Adam Cohenac8c8762011-07-13 11:15:27 -07002422 canvas.setBitmap(b);
2423 drawDragView(v, canvas, padding, true);
Adam Cohenaaf473c2011-08-03 12:02:47 -07002424 canvas.setBitmap(null);
Joe Onorato4be866d2010-10-10 11:26:02 -07002425
Adam Cohenac8c8762011-07-13 11:15:27 -07002426 return b;
Joe Onorato4be866d2010-10-10 11:26:02 -07002427 }
2428
2429 /**
2430 * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
2431 * Responsibility for the bitmap is transferred to the caller.
2432 */
2433 private Bitmap createDragOutline(View v, Canvas canvas, int padding) {
Adam Cohen410f3cd2013-09-22 12:09:32 -07002434 final int outlineColor = getResources().getColor(R.color.outline_color);
Joe Onorato4be866d2010-10-10 11:26:02 -07002435 final Bitmap b = Bitmap.createBitmap(
2436 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
2437
2438 canvas.setBitmap(b);
Winson Chungb8c69f32011-10-19 21:36:08 -07002439 drawDragView(v, canvas, padding, true);
Adam Cohen5bb50bd2010-12-03 11:39:55 -08002440 mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor);
Adam Cohenaaf473c2011-08-03 12:02:47 -07002441 canvas.setBitmap(null);
Joe Onorato4be866d2010-10-10 11:26:02 -07002442 return b;
2443 }
2444
2445 /**
Michael Jurkad3ef3062010-11-23 16:23:58 -08002446 * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
2447 * Responsibility for the bitmap is transferred to the caller.
2448 */
Peter Ng8db70002011-10-25 15:40:08 -07002449 private Bitmap createDragOutline(Bitmap orig, Canvas canvas, int padding, int w, int h,
Michael Jurka8c3339b2012-06-14 16:18:21 -07002450 boolean clipAlpha) {
Adam Cohen410f3cd2013-09-22 12:09:32 -07002451 final int outlineColor = getResources().getColor(R.color.outline_color);
Michael Jurkaf12c75c2011-01-25 22:41:40 -08002452 final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Michael Jurkad3ef3062010-11-23 16:23:58 -08002453 canvas.setBitmap(b);
Michael Jurkaf12c75c2011-01-25 22:41:40 -08002454
2455 Rect src = new Rect(0, 0, orig.getWidth(), orig.getHeight());
2456 float scaleFactor = Math.min((w - padding) / (float) orig.getWidth(),
2457 (h - padding) / (float) orig.getHeight());
2458 int scaledWidth = (int) (scaleFactor * orig.getWidth());
2459 int scaledHeight = (int) (scaleFactor * orig.getHeight());
2460 Rect dst = new Rect(0, 0, scaledWidth, scaledHeight);
2461
2462 // center the image
2463 dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2);
2464
Winson Chung1120e032011-11-22 16:11:31 -08002465 canvas.drawBitmap(orig, src, dst, null);
Peter Ng8db70002011-10-25 15:40:08 -07002466 mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor,
Michael Jurka8c3339b2012-06-14 16:18:21 -07002467 clipAlpha);
Adam Cohenaaf473c2011-08-03 12:02:47 -07002468 canvas.setBitmap(null);
Michael Jurkad3ef3062010-11-23 16:23:58 -08002469
2470 return b;
2471 }
2472
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002473 void startDrag(CellLayout.CellInfo cellInfo) {
2474 View child = cellInfo.cell;
Winson Chungaafa03c2010-06-11 17:34:16 -07002475
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002476 // Make sure the drag was started by a long press as opposed to a long click.
Bjorn Bringert7984c942009-12-09 15:38:25 +00002477 if (!child.isInTouchMode()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002478 return;
2479 }
Winson Chungaafa03c2010-06-11 17:34:16 -07002480
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002481 mDragInfo = cellInfo;
Adam Cohend41fbf52012-02-16 23:53:59 -08002482 child.setVisibility(INVISIBLE);
Adam Cohen482ed822012-03-02 14:15:13 -08002483 CellLayout layout = (CellLayout) child.getParent().getParent();
2484 layout.prepareChildForDrag(child);
Joe Onorato4be866d2010-10-10 11:26:02 -07002485
2486 child.clearFocus();
2487 child.setPressed(false);
2488
2489 final Canvas canvas = new Canvas();
2490
2491 // The outline is used to visualize where the item will land if dropped
Adam Cohend41fbf52012-02-16 23:53:59 -08002492 mDragOutline = createDragOutline(child, canvas, DRAG_BITMAP_PADDING);
Adam Cohenac8c8762011-07-13 11:15:27 -07002493 beginDragShared(child, this);
2494 }
2495
2496 public void beginDragShared(View child, DragSource source) {
Joe Onorato4be866d2010-10-10 11:26:02 -07002497 // The drag bitmap follows the touch point around on the screen
Adam Cohend41fbf52012-02-16 23:53:59 -08002498 final Bitmap b = createDragBitmap(child, new Canvas(), DRAG_BITMAP_PADDING);
Joe Onorato4be866d2010-10-10 11:26:02 -07002499
2500 final int bmpWidth = b.getWidth();
Winson Chungeecf02d2012-03-02 17:14:58 -08002501 final int bmpHeight = b.getHeight();
Adam Cohene3e27a82011-04-15 12:07:39 -07002502
Adam Cohen307fe232012-08-16 17:55:58 -07002503 float scale = mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY);
Winson Chungeecf02d2012-03-02 17:14:58 -08002504 int dragLayerX =
Adam Cohen307fe232012-08-16 17:55:58 -07002505 Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2);
Winson Chungeecf02d2012-03-02 17:14:58 -08002506 int dragLayerY =
Adam Cohen307fe232012-08-16 17:55:58 -07002507 Math.round(mTempXY[1] - (bmpHeight - scale * bmpHeight) / 2
Winson Chungeecf02d2012-03-02 17:14:58 -08002508 - DRAG_BITMAP_PADDING / 2);
Adam Cohene3e27a82011-04-15 12:07:39 -07002509
Winson Chung5f8afe62013-08-12 16:19:28 -07002510 LauncherAppState app = LauncherAppState.getInstance();
2511 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
Winson Chungb8c69f32011-10-19 21:36:08 -07002512 Point dragVisualizeOffset = null;
Adam Cohene3e27a82011-04-15 12:07:39 -07002513 Rect dragRect = null;
Winson Chungb8c69f32011-10-19 21:36:08 -07002514 if (child instanceof BubbleTextView || child instanceof PagedViewIcon) {
Winson Chung5f8afe62013-08-12 16:19:28 -07002515 int iconSize = grid.iconSizePx;
Adam Cohene3e27a82011-04-15 12:07:39 -07002516 int top = child.getPaddingTop();
2517 int left = (bmpWidth - iconSize) / 2;
2518 int right = left + iconSize;
2519 int bottom = top + iconSize;
Adam Cohenfc53cd22011-07-20 15:45:11 -07002520 dragLayerY += top;
Winson Chungb8c69f32011-10-19 21:36:08 -07002521 // Note: The drag region is used to calculate drag layer offsets, but the
2522 // dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
Winson Chung5f8afe62013-08-12 16:19:28 -07002523 dragVisualizeOffset = new Point(-DRAG_BITMAP_PADDING / 2, DRAG_BITMAP_PADDING / 2);
Adam Cohene3e27a82011-04-15 12:07:39 -07002524 dragRect = new Rect(left, top, right, bottom);
Adam Cohen0e4857c2011-06-30 17:05:14 -07002525 } else if (child instanceof FolderIcon) {
Winson Chung5f8afe62013-08-12 16:19:28 -07002526 int previewSize = grid.folderIconSizePx;
2527 dragRect = new Rect(0, child.getPaddingTop(), child.getWidth(), previewSize);
Adam Cohene3e27a82011-04-15 12:07:39 -07002528 }
2529
Winson Chung1e9cbfe2011-09-30 16:52:26 -07002530 // Clear the pressed state if necessary
2531 if (child instanceof BubbleTextView) {
2532 BubbleTextView icon = (BubbleTextView) child;
2533 icon.clearPressedOrFocusedBackground();
2534 }
2535
Adam Cohenac8c8762011-07-13 11:15:27 -07002536 mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
Adam Cohen307fe232012-08-16 17:55:58 -07002537 DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale);
Adam Cohen5084cba2013-09-03 12:01:16 -07002538
2539 if (child.getParent() instanceof ShortcutAndWidgetContainer) {
2540 mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent();
2541 }
2542
Joe Onorato4be866d2010-10-10 11:26:02 -07002543 b.recycle();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002544 }
2545
Adam Cohendcd297f2013-06-18 13:13:40 -07002546 void addApplicationShortcut(ShortcutInfo info, CellLayout target, long container, long screenId,
Winson Chung3d503fb2011-07-13 17:25:49 -07002547 int cellX, int cellY, boolean insertAtFirst, int intersectX, int intersectY) {
2548 View view = mLauncher.createShortcut(R.layout.application, target, (ShortcutInfo) info);
Michael Jurka0280c3b2010-09-17 15:00:07 -07002549
2550 final int[] cellXY = new int[2];
Winson Chung3d503fb2011-07-13 17:25:49 -07002551 target.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY);
Adam Cohendcd297f2013-06-18 13:13:40 -07002552 addInScreen(view, container, screenId, cellXY[0], cellXY[1], 1, 1, insertAtFirst);
Adam Cohen7c4c5102013-06-14 17:42:35 -07002553
Adam Cohendcd297f2013-06-18 13:13:40 -07002554 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId, cellXY[0],
Winson Chung3d503fb2011-07-13 17:25:49 -07002555 cellXY[1]);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002556 }
2557
Adam Cohen4b285c52011-07-21 14:24:06 -07002558 public boolean transitionStateShouldAllowDrop() {
Adam Cohen1b36dc32012-02-13 19:27:37 -08002559 return ((!isSwitchingState() || mTransitionProgress > 0.5f) && mState != State.SMALL);
Adam Cohen4b285c52011-07-21 14:24:06 -07002560 }
2561
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002562 /**
2563 * {@inheritDoc}
2564 */
Adam Cohencb3382b2011-05-24 14:07:08 -07002565 public boolean acceptDrop(DragObject d) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002566 // If it's an external drop (e.g. from All Apps), check if it should be accepted
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002567 CellLayout dropTargetLayout = mDropToLayout;
Adam Cohencb3382b2011-05-24 14:07:08 -07002568 if (d.dragSource != this) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002569 // Don't accept the drop if we're not over a screen at time of drop
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002570 if (dropTargetLayout == null) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002571 return false;
2572 }
Adam Cohen4b285c52011-07-21 14:24:06 -07002573 if (!transitionStateShouldAllowDrop()) return false;
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002574
Adam Cohena65beee2011-06-27 21:32:23 -07002575 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset,
2576 d.dragView, mDragViewVisualCenter);
2577
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07002578 // We want the point to be mapped to the dragTarget.
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002579 if (mLauncher.isHotseatLayout(dropTargetLayout)) {
Adam Cohen307fe232012-08-16 17:55:58 -07002580 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07002581 } else {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002582 mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null);
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07002583 }
2584
Winson Chung557d6ed2011-07-08 15:34:52 -07002585 int spanX = 1;
2586 int spanY = 1;
Winson Chung557d6ed2011-07-08 15:34:52 -07002587 if (mDragInfo != null) {
2588 final CellLayout.CellInfo dragCellInfo = mDragInfo;
2589 spanX = dragCellInfo.spanX;
2590 spanY = dragCellInfo.spanY;
Winson Chung557d6ed2011-07-08 15:34:52 -07002591 } else {
2592 final ItemInfo dragInfo = (ItemInfo) d.dragInfo;
2593 spanX = dragInfo.spanX;
2594 spanY = dragInfo.spanY;
2595 }
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002596
Adam Cohend41fbf52012-02-16 23:53:59 -08002597 int minSpanX = spanX;
2598 int minSpanY = spanY;
2599 if (d.dragInfo instanceof PendingAddWidgetInfo) {
2600 minSpanX = ((PendingAddWidgetInfo) d.dragInfo).minSpanX;
2601 minSpanY = ((PendingAddWidgetInfo) d.dragInfo).minSpanY;
2602 }
Adam Cohen482ed822012-03-02 14:15:13 -08002603
Adam Cohena65beee2011-06-27 21:32:23 -07002604 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002605 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, dropTargetLayout,
Adam Cohenf0777b92012-02-28 14:02:45 -08002606 mTargetCell);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002607 float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08002608 mDragViewVisualCenter[1], mTargetCell);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002609 if (willCreateUserFolder((ItemInfo) d.dragInfo, dropTargetLayout,
Adam Cohen482ed822012-03-02 14:15:13 -08002610 mTargetCell, distance, true)) {
Adam Cohena65beee2011-06-27 21:32:23 -07002611 return true;
2612 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002613 if (willAddToExistingUserFolder((ItemInfo) d.dragInfo, dropTargetLayout,
Adam Cohen482ed822012-03-02 14:15:13 -08002614 mTargetCell, distance)) {
Adam Cohena65beee2011-06-27 21:32:23 -07002615 return true;
2616 }
2617
Adam Cohen482ed822012-03-02 14:15:13 -08002618 int[] resultSpan = new int[2];
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002619 mTargetCell = dropTargetLayout.createArea((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08002620 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
2621 null, mTargetCell, resultSpan, CellLayout.MODE_ACCEPT_DROP);
2622 boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
2623
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002624 // Don't accept the drop if there's no room for the item
Adam Cohen482ed822012-03-02 14:15:13 -08002625 if (!foundCell) {
Winson Chung96ef4092011-11-22 12:25:14 -08002626 // Don't show the message if we are dropping on the AllApps button and the hotseat
2627 // is full
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002628 boolean isHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
Winson Chung93eef082012-03-23 15:59:27 -07002629 if (mTargetCell != null && isHotseat) {
Winson Chung96ef4092011-11-22 12:25:14 -08002630 Hotseat hotseat = mLauncher.getHotseat();
Andrew Flynn0dca1ec2012-02-29 13:33:22 -08002631 if (hotseat.isAllAppsButtonRank(
Winson Chung96ef4092011-11-22 12:25:14 -08002632 hotseat.getOrderInHotseat(mTargetCell[0], mTargetCell[1]))) {
2633 return false;
2634 }
2635 }
2636
Winson Chung93eef082012-03-23 15:59:27 -07002637 mLauncher.showOutOfSpaceMessage(isHotseat);
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002638 return false;
2639 }
2640 }
Adam Cohendedbd962013-07-11 14:21:49 -07002641
2642 long screenId = getIdForScreen(dropTargetLayout);
2643 if (screenId == EXTRA_EMPTY_SCREEN_ID) {
2644 commitExtraEmptyScreen();
2645 }
2646
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002647 return true;
2648 }
2649
Adam Cohen482ed822012-03-02 14:15:13 -08002650 boolean willCreateUserFolder(ItemInfo info, CellLayout target, int[] targetCell, float
2651 distance, boolean considerTimeout) {
2652 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohena65beee2011-06-27 21:32:23 -07002653 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
2654
Adam Cohen19f37922012-03-21 11:59:11 -07002655 if (dropOverView != null) {
2656 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
2657 if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) {
2658 return false;
2659 }
2660 }
2661
Winson Chung3d503fb2011-07-13 17:25:49 -07002662 boolean hasntMoved = false;
2663 if (mDragInfo != null) {
Adam Cohen482ed822012-03-02 14:15:13 -08002664 hasntMoved = dropOverView == mDragInfo.cell;
Winson Chung3d503fb2011-07-13 17:25:49 -07002665 }
Adam Cohene0310962011-04-18 16:15:31 -07002666
Adam Cohena65beee2011-06-27 21:32:23 -07002667 if (dropOverView == null || hasntMoved || (considerTimeout && !mCreateUserFolderOnDrop)) {
2668 return false;
2669 }
Adam Cohene0310962011-04-18 16:15:31 -07002670
Adam Cohena65beee2011-06-27 21:32:23 -07002671 boolean aboveShortcut = (dropOverView.getTag() instanceof ShortcutInfo);
Adam Cohene0310962011-04-18 16:15:31 -07002672 boolean willBecomeShortcut =
Adam Cohenc0dcf592011-06-01 15:30:43 -07002673 (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
2674 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT);
Adam Cohene0310962011-04-18 16:15:31 -07002675
2676 return (aboveShortcut && willBecomeShortcut);
2677 }
2678
Adam Cohen482ed822012-03-02 14:15:13 -08002679 boolean willAddToExistingUserFolder(Object dragInfo, CellLayout target, int[] targetCell,
2680 float distance) {
2681 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohena65beee2011-06-27 21:32:23 -07002682 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohen19f37922012-03-21 11:59:11 -07002683
2684 if (dropOverView != null) {
2685 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
2686 if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) {
2687 return false;
2688 }
2689 }
2690
Adam Cohena65beee2011-06-27 21:32:23 -07002691 if (dropOverView instanceof FolderIcon) {
2692 FolderIcon fi = (FolderIcon) dropOverView;
2693 if (fi.acceptDrop(dragInfo)) {
2694 return true;
2695 }
2696 }
2697 return false;
2698 }
2699
Winson Chung3d503fb2011-07-13 17:25:49 -07002700 boolean createUserFolderIfNecessary(View newView, long container, CellLayout target,
Adam Cohen482ed822012-03-02 14:15:13 -08002701 int[] targetCell, float distance, boolean external, DragView dragView,
2702 Runnable postAnimationRunnable) {
2703 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohenc0dcf592011-06-01 15:30:43 -07002704 View v = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohen19f37922012-03-21 11:59:11 -07002705
Winson Chungec9a0a42011-07-20 20:42:13 -07002706 boolean hasntMoved = false;
2707 if (mDragInfo != null) {
2708 CellLayout cellParent = getParentCellLayoutForView(mDragInfo.cell);
2709 hasntMoved = (mDragInfo.cellX == targetCell[0] &&
2710 mDragInfo.cellY == targetCell[1]) && (cellParent == target);
2711 }
Adam Cohen10b17372011-04-15 14:21:25 -07002712
Adam Cohen19072da2011-05-31 14:30:45 -07002713 if (v == null || hasntMoved || !mCreateUserFolderOnDrop) return false;
2714 mCreateUserFolderOnDrop = false;
Adam Cohendcd297f2013-06-18 13:13:40 -07002715 final long screenId = (targetCell == null) ? mDragInfo.screenId : getIdForScreen(target);
Adam Cohen10b17372011-04-15 14:21:25 -07002716
2717 boolean aboveShortcut = (v.getTag() instanceof ShortcutInfo);
2718 boolean willBecomeShortcut = (newView.getTag() instanceof ShortcutInfo);
2719
2720 if (aboveShortcut && willBecomeShortcut) {
2721 ShortcutInfo sourceInfo = (ShortcutInfo) newView.getTag();
2722 ShortcutInfo destInfo = (ShortcutInfo) v.getTag();
2723 // if the drag started here, we need to remove it from the workspace
2724 if (!external) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002725 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
Adam Cohen10b17372011-04-15 14:21:25 -07002726 }
2727
Adam Cohend0445262011-07-04 23:53:22 -07002728 Rect folderLocation = new Rect();
Adam Cohenac8c8762011-07-13 11:15:27 -07002729 float scale = mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation);
Adam Cohen10b17372011-04-15 14:21:25 -07002730 target.removeView(v);
Adam Cohend0445262011-07-04 23:53:22 -07002731
Winson Chung3d503fb2011-07-13 17:25:49 -07002732 FolderIcon fi =
Adam Cohendcd297f2013-06-18 13:13:40 -07002733 mLauncher.addFolder(target, container, screenId, targetCell[0], targetCell[1]);
Adam Cohena9cf38f2011-05-02 15:36:58 -07002734 destInfo.cellX = -1;
2735 destInfo.cellY = -1;
2736 sourceInfo.cellX = -1;
2737 sourceInfo.cellY = -1;
Adam Cohend0445262011-07-04 23:53:22 -07002738
Adam Cohen558baaf2011-08-15 15:22:57 -07002739 // If the dragView is null, we can't animate
2740 boolean animate = dragView != null;
2741 if (animate) {
2742 fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale,
2743 postAnimationRunnable);
2744 } else {
2745 fi.addItem(destInfo);
2746 fi.addItem(sourceInfo);
2747 }
Adam Cohen10b17372011-04-15 14:21:25 -07002748 return true;
2749 }
2750 return false;
2751 }
2752
Adam Cohena65beee2011-06-27 21:32:23 -07002753 boolean addToExistingFolderIfNecessary(View newView, CellLayout target, int[] targetCell,
Adam Cohen482ed822012-03-02 14:15:13 -08002754 float distance, DragObject d, boolean external) {
2755 if (distance > mMaxDistanceForFolderCreation) return false;
2756
Adam Cohena65beee2011-06-27 21:32:23 -07002757 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002758 if (!mAddToExistingFolderOnDrop) return false;
2759 mAddToExistingFolderOnDrop = false;
Adam Cohen19f37922012-03-21 11:59:11 -07002760
Adam Cohenc0dcf592011-06-01 15:30:43 -07002761 if (dropOverView instanceof FolderIcon) {
2762 FolderIcon fi = (FolderIcon) dropOverView;
Adam Cohen3e8f8112011-07-02 18:03:00 -07002763 if (fi.acceptDrop(d.dragInfo)) {
2764 fi.onDrop(d);
Adam Cohenc0dcf592011-06-01 15:30:43 -07002765
2766 // if the drag started here, we need to remove it from the workspace
2767 if (!external) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002768 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
Adam Cohenc0dcf592011-06-01 15:30:43 -07002769 }
2770 return true;
2771 }
2772 }
2773 return false;
2774 }
2775
Adam Cohend41fbf52012-02-16 23:53:59 -08002776 public void onDrop(final DragObject d) {
Adam Cohencb3382b2011-05-24 14:07:08 -07002777 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, d.dragView,
Adam Cohene3e27a82011-04-15 12:07:39 -07002778 mDragViewVisualCenter);
2779
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002780 CellLayout dropTargetLayout = mDropToLayout;
2781
Adam Cohene3e27a82011-04-15 12:07:39 -07002782 // We want the point to be mapped to the dragTarget.
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002783 if (dropTargetLayout != null) {
2784 if (mLauncher.isHotseatLayout(dropTargetLayout)) {
Adam Cohenbb00ff22012-07-12 15:43:01 -07002785 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
Winson Chung3d503fb2011-07-13 17:25:49 -07002786 } else {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002787 mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null);
Winson Chung3d503fb2011-07-13 17:25:49 -07002788 }
Adam Cohenba781612011-05-09 14:37:39 -07002789 }
Michael Jurkac6ee42e2010-09-30 12:04:50 -07002790
Adam Cohened51cc92011-08-01 20:28:08 -07002791 int snapScreen = -1;
Adam Cohend41fbf52012-02-16 23:53:59 -08002792 boolean resizeOnDrop = false;
Adam Cohencb3382b2011-05-24 14:07:08 -07002793 if (d.dragSource != this) {
Adam Cohene3e27a82011-04-15 12:07:39 -07002794 final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0],
2795 (int) mDragViewVisualCenter[1] };
Adam Cohen3e8f8112011-07-02 18:03:00 -07002796 onDropExternal(touchXY, d.dragInfo, dropTargetLayout, false, d);
Patrick Dubroyce34a972010-10-19 10:34:32 -07002797 } else if (mDragInfo != null) {
Patrick Dubroyce34a972010-10-19 10:34:32 -07002798 final View cell = mDragInfo.cell;
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08002799
Adam Cohend41fbf52012-02-16 23:53:59 -08002800 Runnable resizeRunnable = null;
Michael Jurka1e2f4652013-07-08 18:03:46 -07002801 if (dropTargetLayout != null && !d.cancelled) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002802 // Move internally
Winson Chung40e882b2011-07-21 19:01:11 -07002803 boolean hasMovedLayouts = (getParentCellLayoutForView(cell) != dropTargetLayout);
2804 boolean hasMovedIntoHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
2805 long container = hasMovedIntoHotseat ?
Winson Chung3d503fb2011-07-13 17:25:49 -07002806 LauncherSettings.Favorites.CONTAINER_HOTSEAT :
2807 LauncherSettings.Favorites.CONTAINER_DESKTOP;
Adam Cohendcd297f2013-06-18 13:13:40 -07002808 long screenId = (mTargetCell[0] < 0) ?
2809 mDragInfo.screenId : getIdForScreen(dropTargetLayout);
Adam Cohenc0dcf592011-06-01 15:30:43 -07002810 int spanX = mDragInfo != null ? mDragInfo.spanX : 1;
2811 int spanY = mDragInfo != null ? mDragInfo.spanY : 1;
2812 // First we find the cell nearest to point at which the item is
2813 // dropped, without any consideration to whether there is an item there.
Adam Cohen482ed822012-03-02 14:15:13 -08002814
Adam Cohenc0dcf592011-06-01 15:30:43 -07002815 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], (int)
2816 mDragViewVisualCenter[1], spanX, spanY, dropTargetLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08002817 float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
2818 mDragViewVisualCenter[1], mTargetCell);
2819
Adam Cohenc0dcf592011-06-01 15:30:43 -07002820 // If the item being dropped is a shortcut and the nearest drop
Adam Cohen76078c42011-06-09 15:06:52 -07002821 // cell also contains a shortcut, then create a folder with the two shortcuts.
Winson Chung1c4cf4a2011-07-29 14:49:10 -07002822 if (!mInScrollArea && createUserFolderIfNecessary(cell, container,
Adam Cohen482ed822012-03-02 14:15:13 -08002823 dropTargetLayout, mTargetCell, distance, false, d.dragView, null)) {
Adam Cohenad4e15c2013-10-17 16:21:35 -07002824 removeExtraEmptyScreen(true, null, 0, true);
Adam Cohenc0dcf592011-06-01 15:30:43 -07002825 return;
2826 }
2827
Adam Cohen482ed822012-03-02 14:15:13 -08002828 if (addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell,
2829 distance, d, false)) {
Adam Cohenad4e15c2013-10-17 16:21:35 -07002830 removeExtraEmptyScreen(true, null, 0, true);
Adam Cohendf035382011-04-11 17:22:04 -07002831 return;
2832 }
2833
2834 // Aside from the special case where we're dropping a shortcut onto a shortcut,
2835 // we need to find the nearest cell location that is vacant
Adam Cohend41fbf52012-02-16 23:53:59 -08002836 ItemInfo item = (ItemInfo) d.dragInfo;
2837 int minSpanX = item.spanX;
2838 int minSpanY = item.spanY;
2839 if (item.minSpanX > 0 && item.minSpanY > 0) {
2840 minSpanX = item.minSpanX;
2841 minSpanY = item.minSpanY;
2842 }
Adam Cohen482ed822012-03-02 14:15:13 -08002843
Adam Cohend41fbf52012-02-16 23:53:59 -08002844 int[] resultSpan = new int[2];
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002845 mTargetCell = dropTargetLayout.createArea((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08002846 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, cell,
2847 mTargetCell, resultSpan, CellLayout.MODE_ON_DROP);
2848
Adam Cohend41fbf52012-02-16 23:53:59 -08002849 boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
Adam Cohen9e05a5e2012-09-10 15:53:09 -07002850
Adam Cohenaaa5c212012-10-05 18:14:31 -07002851 // if the widget resizes on drop
Adam Cohen9e05a5e2012-09-10 15:53:09 -07002852 if (foundCell && (cell instanceof AppWidgetHostView) &&
Adam Cohenaaa5c212012-10-05 18:14:31 -07002853 (resultSpan[0] != item.spanX || resultSpan[1] != item.spanY)) {
Adam Cohend41fbf52012-02-16 23:53:59 -08002854 resizeOnDrop = true;
2855 item.spanX = resultSpan[0];
2856 item.spanY = resultSpan[1];
Adam Cohen7bdfc972012-05-22 16:50:35 -07002857 AppWidgetHostView awhv = (AppWidgetHostView) cell;
2858 AppWidgetResizeFrame.updateWidgetSizeRanges(awhv, mLauncher, resultSpan[0],
2859 resultSpan[1]);
Adam Cohend41fbf52012-02-16 23:53:59 -08002860 }
Adam Cohendf035382011-04-11 17:22:04 -07002861
Adam Cohendcd297f2013-06-18 13:13:40 -07002862 if (getScreenIdForPageIndex(mCurrentPage) != screenId && !hasMovedIntoHotseat) {
2863 snapScreen = getPageIndexForScreenId(screenId);
2864 snapToPage(snapScreen);
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08002865 }
2866
Adam Cohend41fbf52012-02-16 23:53:59 -08002867 if (foundCell) {
2868 final ItemInfo info = (ItemInfo) cell.getTag();
Winson Chung40e882b2011-07-21 19:01:11 -07002869 if (hasMovedLayouts) {
Patrick Dubroy94383362010-10-29 15:03:24 -07002870 // Reparent the view
Winson Chung3d503fb2011-07-13 17:25:49 -07002871 getParentCellLayoutForView(cell).removeView(cell);
Adam Cohendcd297f2013-06-18 13:13:40 -07002872 addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1],
Adam Cohend41fbf52012-02-16 23:53:59 -08002873 info.spanX, info.spanY);
Patrick Dubroy94383362010-10-29 15:03:24 -07002874 }
2875
2876 // update the item's position after drop
Patrick Dubroy94383362010-10-29 15:03:24 -07002877 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
Adam Cohen19f37922012-03-21 11:59:11 -07002878 lp.cellX = lp.tmpCellX = mTargetCell[0];
2879 lp.cellY = lp.tmpCellY = mTargetCell[1];
Adam Cohend41fbf52012-02-16 23:53:59 -08002880 lp.cellHSpan = item.spanX;
2881 lp.cellVSpan = item.spanY;
Adam Cohen19f37922012-03-21 11:59:11 -07002882 lp.isLockedToGrid = true;
Adam Cohendcd297f2013-06-18 13:13:40 -07002883 cell.setId(LauncherModel.getCellLayoutChildId(container, mDragInfo.screenId,
Patrick Dubroy94383362010-10-29 15:03:24 -07002884 mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY));
2885
Winson Chung3d503fb2011-07-13 17:25:49 -07002886 if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
2887 cell instanceof LauncherAppWidgetHostView) {
Adam Cohend4844c32011-02-18 19:25:06 -08002888 final CellLayout cellLayout = dropTargetLayout;
2889 // We post this call so that the widget has a chance to be placed
2890 // in its final location
2891
2892 final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell;
2893 AppWidgetProviderInfo pinfo = hostView.getAppWidgetInfo();
Adam Cohenc0a5df92012-04-02 16:53:26 -07002894 if (pinfo != null &&
2895 pinfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) {
Adam Cohend41fbf52012-02-16 23:53:59 -08002896 final Runnable addResizeFrame = new Runnable() {
Adam Cohend4844c32011-02-18 19:25:06 -08002897 public void run() {
Adam Cohen716b51e2011-06-30 12:09:54 -07002898 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohenc0dcf592011-06-01 15:30:43 -07002899 dragLayer.addResizeFrame(info, hostView, cellLayout);
Adam Cohend4844c32011-02-18 19:25:06 -08002900 }
Adam Cohen26976d92011-03-22 15:33:33 -07002901 };
Adam Cohend41fbf52012-02-16 23:53:59 -08002902 resizeRunnable = (new Runnable() {
Adam Cohen26976d92011-03-22 15:33:33 -07002903 public void run() {
2904 if (!isPageMoving()) {
Adam Cohend41fbf52012-02-16 23:53:59 -08002905 addResizeFrame.run();
Adam Cohen26976d92011-03-22 15:33:33 -07002906 } else {
Adam Cohend41fbf52012-02-16 23:53:59 -08002907 mDelayedResizeRunnable = addResizeFrame;
Adam Cohen26976d92011-03-22 15:33:33 -07002908 }
2909 }
Adam Cohend4844c32011-02-18 19:25:06 -08002910 });
2911 }
2912 }
2913
Adam Cohen949debe2013-09-29 14:22:48 -07002914 LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId, lp.cellX,
2915 lp.cellY, item.spanX, item.spanY);
Adam Cohend41fbf52012-02-16 23:53:59 -08002916 } else {
2917 // If we can't find a drop location, we return the item to its original position
2918 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
2919 mTargetCell[0] = lp.cellX;
2920 mTargetCell[1] = lp.cellY;
Adam Cohenf1dcdf62012-05-10 16:51:52 -07002921 CellLayout layout = (CellLayout) cell.getParent().getParent();
2922 layout.markCellsAsOccupiedForView(cell);
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002923 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002924 }
Patrick Dubroyce34a972010-10-19 10:34:32 -07002925
Michael Jurka8c920dd2011-01-20 14:16:56 -08002926 final CellLayout parent = (CellLayout) cell.getParent().getParent();
Adam Cohend41fbf52012-02-16 23:53:59 -08002927 final Runnable finalResizeRunnable = resizeRunnable;
Patrick Dubroyce34a972010-10-19 10:34:32 -07002928 // Prepare it to be animated into its new position
2929 // This must be called after the view has been re-parented
Adam Cohend41fbf52012-02-16 23:53:59 -08002930 final Runnable onCompleteRunnable = new Runnable() {
Michael Jurkad74c9842011-07-10 12:44:21 -07002931 @Override
2932 public void run() {
2933 mAnimatingViewIntoPlace = false;
Michael Jurka3a0469d2012-06-21 09:38:41 -07002934 updateChildrenLayersEnabled(false);
Adam Cohend41fbf52012-02-16 23:53:59 -08002935 if (finalResizeRunnable != null) {
2936 finalResizeRunnable.run();
2937 }
Adam Cohenad4e15c2013-10-17 16:21:35 -07002938 removeExtraEmptyScreen(true, null, 0, true);
Michael Jurkad74c9842011-07-10 12:44:21 -07002939 }
2940 };
2941 mAnimatingViewIntoPlace = true;
Adam Cohenfc53cd22011-07-20 15:45:11 -07002942 if (d.dragView.hasDrawn()) {
Adam Cohend41fbf52012-02-16 23:53:59 -08002943 final ItemInfo info = (ItemInfo) cell.getTag();
2944 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET) {
2945 int animationType = resizeOnDrop ? ANIMATE_INTO_POSITION_AND_RESIZE :
2946 ANIMATE_INTO_POSITION_AND_DISAPPEAR;
2947 animateWidgetDrop(info, parent, d.dragView,
2948 onCompleteRunnable, animationType, cell, false);
2949 } else {
Adam Cohen85b467b2012-02-29 15:38:46 -08002950 int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION;
2951 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration,
2952 onCompleteRunnable, this);
Adam Cohend41fbf52012-02-16 23:53:59 -08002953 }
Adam Cohenfc53cd22011-07-20 15:45:11 -07002954 } else {
Winson Chung7bd1bbb2012-02-13 18:29:29 -08002955 d.deferDragViewCleanupPostAnimation = false;
Adam Cohenfc53cd22011-07-20 15:45:11 -07002956 cell.setVisibility(VISIBLE);
2957 }
Adam Cohen716b51e2011-06-30 12:09:54 -07002958 parent.onDropChild(cell);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002959 }
2960 }
2961
Adam Cohendcd297f2013-06-18 13:13:40 -07002962 public void setFinalScrollForPageChange(int pageIndex) {
2963 CellLayout cl = (CellLayout) getChildAt(pageIndex);
2964 if (cl != null) {
Adam Cohened51cc92011-08-01 20:28:08 -07002965 mSavedScrollX = getScrollX();
Adam Cohened51cc92011-08-01 20:28:08 -07002966 mSavedTranslationX = cl.getTranslationX();
2967 mSavedRotationY = cl.getRotationY();
Adam Cohenedb40762013-07-18 16:45:45 -07002968 final int newX = getScrollForPage(pageIndex);
Adam Cohened51cc92011-08-01 20:28:08 -07002969 setScrollX(newX);
2970 cl.setTranslationX(0f);
2971 cl.setRotationY(0f);
2972 }
2973 }
2974
Adam Cohendcd297f2013-06-18 13:13:40 -07002975 public void resetFinalScrollForPageChange(int pageIndex) {
2976 if (pageIndex >= 0) {
2977 CellLayout cl = (CellLayout) getChildAt(pageIndex);
Adam Cohened51cc92011-08-01 20:28:08 -07002978 setScrollX(mSavedScrollX);
2979 cl.setTranslationX(mSavedTranslationX);
2980 cl.setRotationY(mSavedRotationY);
2981 }
2982 }
2983
Adam Cohen76078c42011-06-09 15:06:52 -07002984 public void getViewLocationRelativeToSelf(View v, int[] location) {
Adam Cohen8dfcba42011-07-07 16:38:18 -07002985 getLocationInWindow(location);
Adam Cohene3e27a82011-04-15 12:07:39 -07002986 int x = location[0];
2987 int y = location[1];
2988
Adam Cohen8dfcba42011-07-07 16:38:18 -07002989 v.getLocationInWindow(location);
Adam Cohene3e27a82011-04-15 12:07:39 -07002990 int vX = location[0];
2991 int vY = location[1];
2992
2993 location[0] = vX - x;
2994 location[1] = vY - y;
2995 }
2996
Adam Cohencb3382b2011-05-24 14:07:08 -07002997 public void onDragEnter(DragObject d) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002998 mDragEnforcer.onDragEnter();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002999 mCreateUserFolderOnDrop = false;
3000 mAddToExistingFolderOnDrop = false;
3001
3002 mDropToLayout = null;
3003 CellLayout layout = getCurrentDropLayout();
3004 setCurrentDropLayout(layout);
3005 setCurrentDragOverlappingLayout(layout);
Winson Chungb26f3d62011-06-02 10:49:29 -07003006
Winson Chungc07918d2011-07-01 15:35:26 -07003007 // Because we don't have space in the Phone UI (the CellLayouts run to the edge) we
3008 // don't need to show the outlines
Daniel Sandlere4f98912013-06-25 15:13:26 -04003009 if (LauncherAppState.getInstance().isScreenLarge()) {
Winson Chungc07918d2011-07-01 15:35:26 -07003010 showOutlines();
Michael Jurkad718d6a2010-10-14 15:35:17 -07003011 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003012 }
3013
Winson Chungfe411c82013-09-26 16:07:17 -07003014 /** Return a rect that has the cellWidth/cellHeight (left, top), and
3015 * widthGap/heightGap (right, bottom) */
Adam Cohena897f392012-04-27 18:12:05 -07003016 static Rect getCellLayoutMetrics(Launcher launcher, int orientation) {
Winson Chung5f8afe62013-08-12 16:19:28 -07003017 LauncherAppState app = LauncherAppState.getInstance();
3018 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
3019
Adam Cohena897f392012-04-27 18:12:05 -07003020 Resources res = launcher.getResources();
3021 Display display = launcher.getWindowManager().getDefaultDisplay();
3022 Point smallestSize = new Point();
3023 Point largestSize = new Point();
3024 display.getCurrentSizeRange(smallestSize, largestSize);
Winson Chung892c74d2013-08-22 16:15:50 -07003025 int countX = (int) grid.numColumns;
3026 int countY = (int) grid.numRows;
Winson Chungfe411c82013-09-26 16:07:17 -07003027 int constrainedLongEdge = largestSize.y;
3028 int constrainedShortEdge = smallestSize.y;
Adam Cohena897f392012-04-27 18:12:05 -07003029 if (orientation == CellLayout.LANDSCAPE) {
3030 if (mLandscapeCellLayoutMetrics == null) {
Winson Chung5f8afe62013-08-12 16:19:28 -07003031 Rect padding = grid.getWorkspacePadding(CellLayout.LANDSCAPE);
Winson Chungfe411c82013-09-26 16:07:17 -07003032 int width = constrainedLongEdge - padding.left - padding.right;
3033 int height = constrainedShortEdge - padding.top - padding.bottom;
Adam Cohena897f392012-04-27 18:12:05 -07003034 mLandscapeCellLayoutMetrics = new Rect();
Winson Chungfe411c82013-09-26 16:07:17 -07003035 mLandscapeCellLayoutMetrics.set(
3036 grid.calculateCellWidth(width, countX),
3037 grid.calculateCellHeight(height, countY), 0, 0);
Adam Cohena897f392012-04-27 18:12:05 -07003038 }
3039 return mLandscapeCellLayoutMetrics;
3040 } else if (orientation == CellLayout.PORTRAIT) {
3041 if (mPortraitCellLayoutMetrics == null) {
Winson Chung5f8afe62013-08-12 16:19:28 -07003042 Rect padding = grid.getWorkspacePadding(CellLayout.PORTRAIT);
Winson Chungfe411c82013-09-26 16:07:17 -07003043 int width = constrainedShortEdge - padding.left - padding.right;
3044 int height = constrainedLongEdge - padding.top - padding.bottom;
Adam Cohena897f392012-04-27 18:12:05 -07003045 mPortraitCellLayoutMetrics = new Rect();
Winson Chungfe411c82013-09-26 16:07:17 -07003046 mPortraitCellLayoutMetrics.set(
3047 grid.calculateCellWidth(width, countX),
3048 grid.calculateCellHeight(height, countY), 0, 0);
Adam Cohena897f392012-04-27 18:12:05 -07003049 }
3050 return mPortraitCellLayoutMetrics;
3051 }
3052 return null;
3053 }
3054
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003055 public void onDragExit(DragObject d) {
3056 mDragEnforcer.onDragExit();
Winson Chungc07918d2011-07-01 15:35:26 -07003057
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003058 // Here we store the final page that will be dropped to, if the workspace in fact
3059 // receives the drop
3060 if (mInScrollArea) {
Winson Chungcc1cfe42012-06-18 15:09:17 -07003061 if (isPageMoving()) {
3062 // If the user drops while the page is scrolling, we should use that page as the
3063 // destination instead of the page that is being hovered over.
3064 mDropToLayout = (CellLayout) getPageAt(getNextPage());
3065 } else {
3066 mDropToLayout = mDragOverlappingLayout;
3067 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003068 } else {
3069 mDropToLayout = mDragTargetLayout;
3070 }
3071
3072 if (mDragMode == DRAG_MODE_CREATE_FOLDER) {
3073 mCreateUserFolderOnDrop = true;
3074 } else if (mDragMode == DRAG_MODE_ADD_TO_FOLDER) {
3075 mAddToExistingFolderOnDrop = true;
Adam Cohen482ed822012-03-02 14:15:13 -08003076 }
3077
Winson Chungc07918d2011-07-01 15:35:26 -07003078 // Reset the scroll area and previous drag target
3079 onResetScrollArea();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003080 setCurrentDropLayout(null);
3081 setCurrentDragOverlappingLayout(null);
Winson Chungc07918d2011-07-01 15:35:26 -07003082
Adam Cohen74c28d12011-11-18 14:17:11 -08003083 mSpringLoadedDragController.cancel();
Winson Chungc07918d2011-07-01 15:35:26 -07003084
3085 if (!mIsPageMoving) {
3086 hideOutlines();
3087 }
3088 }
3089
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003090 void setCurrentDropLayout(CellLayout layout) {
3091 if (mDragTargetLayout != null) {
3092 mDragTargetLayout.revertTempState();
3093 mDragTargetLayout.onDragExit();
3094 }
3095 mDragTargetLayout = layout;
3096 if (mDragTargetLayout != null) {
3097 mDragTargetLayout.onDragEnter();
3098 }
3099 cleanupReorder(true);
3100 cleanupFolderCreation();
3101 setCurrentDropOverCell(-1, -1);
3102 }
3103
3104 void setCurrentDragOverlappingLayout(CellLayout layout) {
3105 if (mDragOverlappingLayout != null) {
3106 mDragOverlappingLayout.setIsDragOverlapping(false);
3107 }
3108 mDragOverlappingLayout = layout;
3109 if (mDragOverlappingLayout != null) {
3110 mDragOverlappingLayout.setIsDragOverlapping(true);
3111 }
3112 invalidate();
3113 }
3114
3115 void setCurrentDropOverCell(int x, int y) {
3116 if (x != mDragOverX || y != mDragOverY) {
3117 mDragOverX = x;
3118 mDragOverY = y;
3119 setDragMode(DRAG_MODE_NONE);
3120 }
3121 }
3122
3123 void setDragMode(int dragMode) {
3124 if (dragMode != mDragMode) {
3125 if (dragMode == DRAG_MODE_NONE) {
3126 cleanupAddToFolder();
3127 // We don't want to cancel the re-order alarm every time the target cell changes
3128 // as this feels to slow / unresponsive.
3129 cleanupReorder(false);
3130 cleanupFolderCreation();
3131 } else if (dragMode == DRAG_MODE_ADD_TO_FOLDER) {
3132 cleanupReorder(true);
3133 cleanupFolderCreation();
3134 } else if (dragMode == DRAG_MODE_CREATE_FOLDER) {
3135 cleanupAddToFolder();
3136 cleanupReorder(true);
3137 } else if (dragMode == DRAG_MODE_REORDER) {
3138 cleanupAddToFolder();
3139 cleanupFolderCreation();
3140 }
3141 mDragMode = dragMode;
3142 }
3143 }
3144
3145 private void cleanupFolderCreation() {
3146 if (mDragFolderRingAnimator != null) {
3147 mDragFolderRingAnimator.animateToNaturalState();
Adam Cohen8ec05f92013-10-13 12:29:03 -07003148 mDragFolderRingAnimator = null;
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003149 }
Adam Cohen8ec05f92013-10-13 12:29:03 -07003150 mFolderCreationAlarm.setOnAlarmListener(null);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003151 mFolderCreationAlarm.cancelAlarm();
3152 }
3153
3154 private void cleanupAddToFolder() {
3155 if (mDragOverFolderIcon != null) {
3156 mDragOverFolderIcon.onDragExit(null);
3157 mDragOverFolderIcon = null;
3158 }
3159 }
3160
3161 private void cleanupReorder(boolean cancelAlarm) {
3162 // Any pending reorders are canceled
3163 if (cancelAlarm) {
3164 mReorderAlarm.cancelAlarm();
3165 }
3166 mLastReorderX = -1;
3167 mLastReorderY = -1;
Winson Chungc07918d2011-07-01 15:35:26 -07003168 }
3169
Michael Jurka4516c112010-10-07 15:13:47 -07003170 /*
3171 *
3172 * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
3173 * coordinate space. The argument xy is modified with the return result.
3174 *
3175 * if cachedInverseMatrix is not null, this method will just use that matrix instead of
Michael Jurkad718d6a2010-10-14 15:35:17 -07003176 * computing it itself; we use this to avoid redundant matrix inversions in
Michael Jurka4516c112010-10-07 15:13:47 -07003177 * findMatchingPageForDragOver
3178 *
3179 */
3180 void mapPointFromSelfToChild(View v, float[] xy, Matrix cachedInverseMatrix) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003181 xy[0] = xy[0] - v.getLeft();
3182 xy[1] = xy[1] - v.getTop();
Michael Jurka4516c112010-10-07 15:13:47 -07003183 }
3184
Adam Cohen7d30a372013-07-01 17:03:59 -07003185 boolean isPointInSelfOverHotseat(int x, int y, Rect r) {
3186 if (r == null) {
3187 r = new Rect();
3188 }
3189 mTempPt[0] = x;
3190 mTempPt[1] = y;
3191 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempPt, true);
Winson Chungabedd9f2013-09-24 15:41:09 -07003192
3193 LauncherAppState app = LauncherAppState.getInstance();
3194 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
3195 r = grid.getHotseatRect();
Adam Cohen7d30a372013-07-01 17:03:59 -07003196 if (r.contains(mTempPt[0], mTempPt[1])) {
3197 return true;
3198 }
3199 return false;
3200 }
Winson Chung3d503fb2011-07-13 17:25:49 -07003201
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003202 void mapPointFromSelfToHotseatLayout(Hotseat hotseat, float[] xy) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003203 mTempPt[0] = (int) xy[0];
3204 mTempPt[1] = (int) xy[1];
3205 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempPt, true);
Winson Chung156ab5b2013-07-12 14:14:16 -07003206 mLauncher.getDragLayer().mapCoordInSelfToDescendent(hotseat.getLayout(), mTempPt);
Adam Cohen7d30a372013-07-01 17:03:59 -07003207
3208 xy[0] = mTempPt[0];
3209 xy[1] = mTempPt[1];
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003210 }
3211
Winson Chung3d503fb2011-07-13 17:25:49 -07003212 /*
Michael Jurka4516c112010-10-07 15:13:47 -07003213 *
3214 * Convert the 2D coordinate xy from this CellLayout's coordinate space to
3215 * the parent View's coordinate space. The argument xy is modified with the return result.
3216 *
3217 */
3218 void mapPointFromChildToSelf(View v, float[] xy) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003219 xy[0] += v.getLeft();
3220 xy[1] += v.getTop();
Michael Jurka4516c112010-10-07 15:13:47 -07003221 }
3222
Adam Cohene3e27a82011-04-15 12:07:39 -07003223 static private float squaredDistance(float[] point1, float[] point2) {
Michael Jurka4516c112010-10-07 15:13:47 -07003224 float distanceX = point1[0] - point2[0];
3225 float distanceY = point2[1] - point2[1];
3226 return distanceX * distanceX + distanceY * distanceY;
Adam Cohene3e27a82011-04-15 12:07:39 -07003227 }
Michael Jurka0280c3b2010-09-17 15:00:07 -07003228
Michael Jurka4516c112010-10-07 15:13:47 -07003229 /*
3230 *
Michael Jurka4516c112010-10-07 15:13:47 -07003231 * This method returns the CellLayout that is currently being dragged to. In order to drag
3232 * to a CellLayout, either the touch point must be directly over the CellLayout, or as a second
3233 * strategy, we see if the dragView is overlapping any CellLayout and choose the closest one
3234 *
3235 * Return null if no CellLayout is currently being dragged over
3236 *
3237 */
3238 private CellLayout findMatchingPageForDragOver(
Adam Cohen00618752011-07-20 12:06:04 -07003239 DragView dragView, float originX, float originY, boolean exact) {
Michael Jurka4516c112010-10-07 15:13:47 -07003240 // We loop through all the screens (ie CellLayouts) and see which ones overlap
3241 // with the item being dragged and then choose the one that's closest to the touch point
Michael Jurkaa63c4522010-08-19 13:52:27 -07003242 final int screenCount = getChildCount();
3243 CellLayout bestMatchingScreen = null;
Michael Jurka0280c3b2010-09-17 15:00:07 -07003244 float smallestDistSoFar = Float.MAX_VALUE;
Michael Jurka4516c112010-10-07 15:13:47 -07003245
Michael Jurkaa63c4522010-08-19 13:52:27 -07003246 for (int i = 0; i < screenCount; i++) {
Adam Cohendf041bc2013-09-22 12:32:22 -07003247 // The custom content screen is not a valid drag over option
3248 if (mScreenOrder.get(i) == CUSTOM_CONTENT_SCREEN_ID) {
3249 continue;
3250 }
3251
Winson Chung3d503fb2011-07-13 17:25:49 -07003252 CellLayout cl = (CellLayout) getChildAt(i);
Michael Jurkaa63c4522010-08-19 13:52:27 -07003253
Adam Cohen00618752011-07-20 12:06:04 -07003254 final float[] touchXy = {originX, originY};
Michael Jurka4516c112010-10-07 15:13:47 -07003255 // Transform the touch coordinates to the CellLayout's local coordinates
3256 // If the touch point is within the bounds of the cell layout, we can return immediately
Michael Jurka0280c3b2010-09-17 15:00:07 -07003257 cl.getMatrix().invert(mTempInverseMatrix);
Michael Jurka4516c112010-10-07 15:13:47 -07003258 mapPointFromSelfToChild(cl, touchXy, mTempInverseMatrix);
Michael Jurkaa63c4522010-08-19 13:52:27 -07003259
Michael Jurka4516c112010-10-07 15:13:47 -07003260 if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() &&
3261 touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) {
3262 return cl;
3263 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07003264
Winson Chung96ef4092011-11-22 12:25:14 -08003265 if (!exact) {
Michael Jurka4516c112010-10-07 15:13:47 -07003266 // Get the center of the cell layout in screen coordinates
3267 final float[] cellLayoutCenter = mTempCellLayoutCenterCoordinates;
3268 cellLayoutCenter[0] = cl.getWidth()/2;
3269 cellLayoutCenter[1] = cl.getHeight()/2;
3270 mapPointFromChildToSelf(cl, cellLayoutCenter);
Michael Jurka0280c3b2010-09-17 15:00:07 -07003271
Adam Cohen00618752011-07-20 12:06:04 -07003272 touchXy[0] = originX;
3273 touchXy[1] = originY;
Michael Jurka0280c3b2010-09-17 15:00:07 -07003274
Michael Jurka4516c112010-10-07 15:13:47 -07003275 // Calculate the distance between the center of the CellLayout
3276 // and the touch point
3277 float dist = squaredDistance(touchXy, cellLayoutCenter);
3278
3279 if (dist < smallestDistSoFar) {
Michael Jurka0280c3b2010-09-17 15:00:07 -07003280 smallestDistSoFar = dist;
Michael Jurkaa63c4522010-08-19 13:52:27 -07003281 bestMatchingScreen = cl;
Michael Jurkaa63c4522010-08-19 13:52:27 -07003282 }
Michael Jurka4516c112010-10-07 15:13:47 -07003283 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07003284 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07003285 return bestMatchingScreen;
3286 }
3287
Adam Cohene3e27a82011-04-15 12:07:39 -07003288 // This is used to compute the visual center of the dragView. This point is then
3289 // used to visualize drop locations and determine where to drop an item. The idea is that
3290 // the visual center represents the user's interpretation of where the item is, and hence
3291 // is the appropriate point to use when determining drop location.
3292 private float[] getDragViewVisualCenter(int x, int y, int xOffset, int yOffset,
3293 DragView dragView, float[] recycle) {
3294 float res[];
3295 if (recycle == null) {
3296 res = new float[2];
3297 } else {
3298 res = recycle;
3299 }
3300
3301 // First off, the drag view has been shifted in a way that is not represented in the
3302 // x and y values or the x/yOffsets. Here we account for that shift.
3303 x += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetX);
3304 y += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY);
3305
3306 // These represent the visual top and left of drag view if a dragRect was provided.
3307 // If a dragRect was not provided, then they correspond to the actual view left and
3308 // top, as the dragRect is in that case taken to be the entire dragView.
3309 // R.dimen.dragViewOffsetY.
3310 int left = x - xOffset;
3311 int top = y - yOffset;
3312
3313 // In order to find the visual center, we shift by half the dragRect
3314 res[0] = left + dragView.getDragRegion().width() / 2;
3315 res[1] = top + dragView.getDragRegion().height() / 2;
3316
3317 return res;
3318 }
3319
Winson Chungea359c62011-08-03 17:06:35 -07003320 private boolean isDragWidget(DragObject d) {
3321 return (d.dragInfo instanceof LauncherAppWidgetInfo ||
3322 d.dragInfo instanceof PendingAddWidgetInfo);
3323 }
3324 private boolean isExternalDragWidget(DragObject d) {
3325 return d.dragSource != this && isDragWidget(d);
3326 }
3327
Adam Cohencb3382b2011-05-24 14:07:08 -07003328 public void onDragOver(DragObject d) {
Winson Chungc07918d2011-07-01 15:35:26 -07003329 // Skip drag over events while we are dragging over side pages
Adam Cohen82ac8a22012-02-14 16:27:49 -08003330 if (mInScrollArea || mIsSwitchingState || mState == State.SMALL) return;
Winson Chungc07918d2011-07-01 15:35:26 -07003331
Winson Chung4afe9b32011-07-27 17:46:20 -07003332 Rect r = new Rect();
Winson Chung3d503fb2011-07-13 17:25:49 -07003333 CellLayout layout = null;
Winson Chungc07918d2011-07-01 15:35:26 -07003334 ItemInfo item = (ItemInfo) d.dragInfo;
3335
3336 // Ensure that we have proper spans for the item that we are dropping
3337 if (item.spanX < 0 || item.spanY < 0) throw new RuntimeException("Improper spans found");
Adam Cohen00618752011-07-20 12:06:04 -07003338 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset,
Winson Chung4afe9b32011-07-27 17:46:20 -07003339 d.dragView, mDragViewVisualCenter);
Adam Cohen00618752011-07-20 12:06:04 -07003340
Adam Cohen482ed822012-03-02 14:15:13 -08003341 final View child = (mDragInfo == null) ? null : mDragInfo.cell;
Winson Chungc07918d2011-07-01 15:35:26 -07003342 // Identify whether we have dragged over a side page
Michael Jurkad74c9842011-07-10 12:44:21 -07003343 if (isSmall()) {
Winson Chungea359c62011-08-03 17:06:35 -07003344 if (mLauncher.getHotseat() != null && !isExternalDragWidget(d)) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003345 if (isPointInSelfOverHotseat(d.x, d.y, r)) {
Winson Chung4afe9b32011-07-27 17:46:20 -07003346 layout = mLauncher.getHotseat().getLayout();
3347 }
3348 }
3349 if (layout == null) {
Winson Chung96ef4092011-11-22 12:25:14 -08003350 layout = findMatchingPageForDragOver(d.dragView, d.x, d.y, false);
Winson Chung4afe9b32011-07-27 17:46:20 -07003351 }
Winson Chungc07918d2011-07-01 15:35:26 -07003352 if (layout != mDragTargetLayout) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003353 setCurrentDropLayout(layout);
3354 setCurrentDragOverlappingLayout(layout);
Winson Chungc07918d2011-07-01 15:35:26 -07003355
Michael Jurkad74c9842011-07-10 12:44:21 -07003356 boolean isInSpringLoadedMode = (mState == State.SPRING_LOADED);
Winson Chungc07918d2011-07-01 15:35:26 -07003357 if (isInSpringLoadedMode) {
Winson Chung4afe9b32011-07-27 17:46:20 -07003358 if (mLauncher.isHotseatLayout(layout)) {
3359 mSpringLoadedDragController.cancel();
3360 } else {
3361 mSpringLoadedDragController.setAlarm(mDragTargetLayout);
3362 }
Winson Chungc07918d2011-07-01 15:35:26 -07003363 }
3364 }
3365 } else {
Winson Chung3d503fb2011-07-13 17:25:49 -07003366 // Test to see if we are over the hotseat otherwise just use the current page
Winson Chungea359c62011-08-03 17:06:35 -07003367 if (mLauncher.getHotseat() != null && !isDragWidget(d)) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003368 if (isPointInSelfOverHotseat(d.x, d.y, r)) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003369 layout = mLauncher.getHotseat().getLayout();
3370 }
3371 }
3372 if (layout == null) {
3373 layout = getCurrentDropLayout();
3374 }
Winson Chungc07918d2011-07-01 15:35:26 -07003375 if (layout != mDragTargetLayout) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003376 setCurrentDropLayout(layout);
3377 setCurrentDragOverlappingLayout(layout);
Winson Chungc07918d2011-07-01 15:35:26 -07003378 }
3379 }
3380
3381 // Handle the drag over
3382 if (mDragTargetLayout != null) {
Winson Chungc07918d2011-07-01 15:35:26 -07003383 // We want the point to be mapped to the dragTarget.
Winson Chung3d503fb2011-07-13 17:25:49 -07003384 if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003385 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
Winson Chung3d503fb2011-07-13 17:25:49 -07003386 } else {
3387 mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
3388 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003389
Winson Chungc07918d2011-07-01 15:35:26 -07003390 ItemInfo info = (ItemInfo) d.dragInfo;
Patrick Dubroy1262e362010-10-06 15:49:50 -07003391
Adam Cohen74c54912013-09-29 14:48:04 -07003392 int minSpanX = item.spanX;
3393 int minSpanY = item.spanY;
3394 if (item.minSpanX > 0 && item.minSpanY > 0) {
3395 minSpanX = item.minSpanX;
3396 minSpanY = item.minSpanY;
3397 }
3398
Winson Chungc07918d2011-07-01 15:35:26 -07003399 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
Adam Cohen74c54912013-09-29 14:48:04 -07003400 (int) mDragViewVisualCenter[1], minSpanX, minSpanY,
Adam Cohend024f982012-05-23 18:26:45 -07003401 mDragTargetLayout, mTargetCell);
Adam Cohen74c54912013-09-29 14:48:04 -07003402 int reorderX = mTargetCell[0];
3403 int reorderY = mTargetCell[1];
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003404
3405 setCurrentDropOverCell(mTargetCell[0], mTargetCell[1]);
3406
Adam Cohen482ed822012-03-02 14:15:13 -08003407 float targetCellDistance = mDragTargetLayout.getDistanceFromCell(
3408 mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell);
3409
Winson Chungc07918d2011-07-01 15:35:26 -07003410 final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0],
3411 mTargetCell[1]);
Winson Chung785d2eb2011-04-14 16:08:02 -07003412
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003413 manageFolderFeedback(info, mDragTargetLayout, mTargetCell,
3414 targetCellDistance, dragOverView);
Adam Cohen482ed822012-03-02 14:15:13 -08003415
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003416 boolean nearestDropOccupied = mDragTargetLayout.isNearestDropLocationOccupied((int)
3417 mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], item.spanX,
3418 item.spanY, child, mTargetCell);
3419
3420 if (!nearestDropOccupied) {
Adam Cohen19f37922012-03-21 11:59:11 -07003421 mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
3422 (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
3423 mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, false,
3424 d.dragView.getDragVisualizeOffset(), d.dragView.getDragRegion());
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003425 } else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
Adam Cohen74c54912013-09-29 14:48:04 -07003426 && !mReorderAlarm.alarmPending() && (mLastReorderX != reorderX ||
3427 mLastReorderY != reorderY)) {
Adam Cohend024f982012-05-23 18:26:45 -07003428
Adam Cohen19f37922012-03-21 11:59:11 -07003429 // Otherwise, if we aren't adding to or creating a folder and there's no pending
3430 // reorder, then we schedule a reorder
Adam Cohen482ed822012-03-02 14:15:13 -08003431 ReorderAlarmListener listener = new ReorderAlarmListener(mDragViewVisualCenter,
3432 minSpanX, minSpanY, item.spanX, item.spanY, d.dragView, child);
3433 mReorderAlarm.setOnAlarmListener(listener);
3434 mReorderAlarm.setAlarm(REORDER_TIMEOUT);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003435 }
3436
3437 if (mDragMode == DRAG_MODE_CREATE_FOLDER || mDragMode == DRAG_MODE_ADD_TO_FOLDER ||
3438 !nearestDropOccupied) {
3439 if (mDragTargetLayout != null) {
3440 mDragTargetLayout.revertTempState();
Michael Jurkad3ef3062010-11-23 16:23:58 -08003441 }
3442 }
Adam Cohen482ed822012-03-02 14:15:13 -08003443 }
3444 }
3445
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003446 private void manageFolderFeedback(ItemInfo info, CellLayout targetLayout,
3447 int[] targetCell, float distance, View dragOverView) {
Adam Cohen482ed822012-03-02 14:15:13 -08003448 boolean userFolderPending = willCreateUserFolder(info, targetLayout, targetCell, distance,
3449 false);
3450
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003451 if (mDragMode == DRAG_MODE_NONE && userFolderPending &&
3452 !mFolderCreationAlarm.alarmPending()) {
Adam Cohen482ed822012-03-02 14:15:13 -08003453 mFolderCreationAlarm.setOnAlarmListener(new
3454 FolderCreationAlarmListener(targetLayout, targetCell[0], targetCell[1]));
3455 mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003456 return;
Adam Cohen482ed822012-03-02 14:15:13 -08003457 }
3458
3459 boolean willAddToFolder =
3460 willAddToExistingUserFolder(info, targetLayout, targetCell, distance);
3461
3462 if (willAddToFolder && mDragMode == DRAG_MODE_NONE) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003463 mDragOverFolderIcon = ((FolderIcon) dragOverView);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003464 mDragOverFolderIcon.onDragEnter(info);
Adam Cohen482ed822012-03-02 14:15:13 -08003465 if (targetLayout != null) {
3466 targetLayout.clearDragOutlines();
Winson Chungc07918d2011-07-01 15:35:26 -07003467 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003468 setDragMode(DRAG_MODE_ADD_TO_FOLDER);
3469 return;
Patrick Dubroy976ebec2010-08-04 20:03:37 -07003470 }
Adam Cohen482ed822012-03-02 14:15:13 -08003471
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003472 if (mDragMode == DRAG_MODE_ADD_TO_FOLDER && !willAddToFolder) {
3473 setDragMode(DRAG_MODE_NONE);
3474 }
3475 if (mDragMode == DRAG_MODE_CREATE_FOLDER && !userFolderPending) {
3476 setDragMode(DRAG_MODE_NONE);
Adam Cohen482ed822012-03-02 14:15:13 -08003477 }
3478
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003479 return;
Adam Cohenc0dcf592011-06-01 15:30:43 -07003480 }
3481
Adam Cohen19072da2011-05-31 14:30:45 -07003482 class FolderCreationAlarmListener implements OnAlarmListener {
Adam Cohen69ce2e52011-07-03 19:25:21 -07003483 CellLayout layout;
3484 int cellX;
3485 int cellY;
Adam Cohen19072da2011-05-31 14:30:45 -07003486
Adam Cohen69ce2e52011-07-03 19:25:21 -07003487 public FolderCreationAlarmListener(CellLayout layout, int cellX, int cellY) {
3488 this.layout = layout;
3489 this.cellX = cellX;
3490 this.cellY = cellY;
Adam Cohen19072da2011-05-31 14:30:45 -07003491 }
3492
3493 public void onAlarm(Alarm alarm) {
Adam Cohen8ec05f92013-10-13 12:29:03 -07003494 if (mDragFolderRingAnimator != null) {
3495 // This shouldn't happen ever, but just in case, make sure we clean up the mess.
3496 mDragFolderRingAnimator.animateToNaturalState();
Adam Cohen19072da2011-05-31 14:30:45 -07003497 }
Adam Cohen8ec05f92013-10-13 12:29:03 -07003498 mDragFolderRingAnimator = new FolderRingAnimator(mLauncher, null);
Adam Cohen69ce2e52011-07-03 19:25:21 -07003499 mDragFolderRingAnimator.setCell(cellX, cellY);
3500 mDragFolderRingAnimator.setCellLayout(layout);
Adam Cohen19072da2011-05-31 14:30:45 -07003501 mDragFolderRingAnimator.animateToAcceptState();
Adam Cohen69ce2e52011-07-03 19:25:21 -07003502 layout.showFolderAccept(mDragFolderRingAnimator);
3503 layout.clearDragOutlines();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003504 setDragMode(DRAG_MODE_CREATE_FOLDER);
Adam Cohen482ed822012-03-02 14:15:13 -08003505 }
3506 }
3507
3508 class ReorderAlarmListener implements OnAlarmListener {
3509 float[] dragViewCenter;
3510 int minSpanX, minSpanY, spanX, spanY;
3511 DragView dragView;
3512 View child;
3513
3514 public ReorderAlarmListener(float[] dragViewCenter, int minSpanX, int minSpanY, int spanX,
3515 int spanY, DragView dragView, View child) {
3516 this.dragViewCenter = dragViewCenter;
3517 this.minSpanX = minSpanX;
3518 this.minSpanY = minSpanY;
3519 this.spanX = spanX;
3520 this.spanY = spanY;
3521 this.child = child;
3522 this.dragView = dragView;
3523 }
3524
3525 public void onAlarm(Alarm alarm) {
3526 int[] resultSpan = new int[2];
Adam Cohen19f37922012-03-21 11:59:11 -07003527 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
Adam Cohen74c54912013-09-29 14:48:04 -07003528 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, mDragTargetLayout,
3529 mTargetCell);
Adam Cohen19f37922012-03-21 11:59:11 -07003530 mLastReorderX = mTargetCell[0];
3531 mLastReorderY = mTargetCell[1];
3532
Adam Cohen482ed822012-03-02 14:15:13 -08003533 mTargetCell = mDragTargetLayout.createArea((int) mDragViewVisualCenter[0],
3534 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
3535 child, mTargetCell, resultSpan, CellLayout.MODE_DRAG_OVER);
3536
Adam Cohen19f37922012-03-21 11:59:11 -07003537 if (mTargetCell[0] < 0 || mTargetCell[1] < 0) {
3538 mDragTargetLayout.revertTempState();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003539 } else {
3540 setDragMode(DRAG_MODE_REORDER);
Adam Cohen19f37922012-03-21 11:59:11 -07003541 }
Adam Cohen482ed822012-03-02 14:15:13 -08003542
Adam Cohen482ed822012-03-02 14:15:13 -08003543 boolean resize = resultSpan[0] != spanX || resultSpan[1] != spanY;
3544 mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
3545 (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
3546 mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], resize,
3547 dragView.getDragVisualizeOffset(), dragView.getDragRegion());
Adam Cohen19072da2011-05-31 14:30:45 -07003548 }
3549 }
3550
Winson Chunga34abf82010-11-12 12:10:35 -08003551 @Override
Adam Cohen7d30a372013-07-01 17:03:59 -07003552 public void getHitRectRelativeToDragLayer(Rect outRect) {
Winson Chunga34abf82010-11-12 12:10:35 -08003553 // We want the workspace to have the whole area of the display (it will find the correct
3554 // cell layout to drop to in the existing drag/drop logic.
Adam Cohen7d30a372013-07-01 17:03:59 -07003555 mLauncher.getDragLayer().getDescendantRectRelativeToSelf(this, outRect);
Winson Chunga34abf82010-11-12 12:10:35 -08003556 }
3557
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07003558 /**
3559 * Add the item specified by dragInfo to the given layout.
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07003560 * @return true if successful
3561 */
Adam Cohen120980b2010-12-08 11:05:37 -08003562 public boolean addExternalItemToScreen(ItemInfo dragInfo, CellLayout layout) {
3563 if (layout.findCellForSpan(mTempEstimate, dragInfo.spanX, dragInfo.spanY)) {
Patrick Dubroybbaa75c2011-03-08 18:47:40 -08003564 onDropExternal(dragInfo.dropPos, (ItemInfo) dragInfo, (CellLayout) layout, false);
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07003565 return true;
3566 }
Winson Chung93eef082012-03-23 15:59:27 -07003567 mLauncher.showOutOfSpaceMessage(mLauncher.isHotseatLayout(layout));
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07003568 return false;
3569 }
3570
Adam Cohend5e42732011-03-28 17:33:39 -07003571 private void onDropExternal(int[] touchXY, Object dragInfo,
3572 CellLayout cellLayout, boolean insertAtFirst) {
Adam Cohene3e27a82011-04-15 12:07:39 -07003573 onDropExternal(touchXY, dragInfo, cellLayout, insertAtFirst, null);
Adam Cohend5e42732011-03-28 17:33:39 -07003574 }
3575
Adam Cohen120980b2010-12-08 11:05:37 -08003576 /**
3577 * Drop an item that didn't originate on one of the workspace screens.
3578 * It may have come from Launcher (e.g. from all apps or customize), or it may have
3579 * come from another app altogether.
3580 *
3581 * NOTE: This can also be called when we are outside of a drag event, when we want
3582 * to add an item to one of the workspace screens.
3583 */
Winson Chung557d6ed2011-07-08 15:34:52 -07003584 private void onDropExternal(final int[] touchXY, final Object dragInfo,
3585 final CellLayout cellLayout, boolean insertAtFirst, DragObject d) {
3586 final Runnable exitSpringLoadedRunnable = new Runnable() {
3587 @Override
3588 public void run() {
Adam Cohenad4e15c2013-10-17 16:21:35 -07003589 removeExtraEmptyScreen(false, new Runnable() {
3590 @Override
3591 public void run() {
3592 mLauncher.exitSpringLoadedDragModeDelayed(true,
3593 Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
3594 }
3595 });
Winson Chung557d6ed2011-07-08 15:34:52 -07003596 }
3597 };
Adam Cohenb7e16182011-07-15 17:55:02 -07003598
3599 ItemInfo info = (ItemInfo) dragInfo;
3600 int spanX = info.spanX;
3601 int spanY = info.spanY;
3602 if (mDragInfo != null) {
3603 spanX = mDragInfo.spanX;
3604 spanY = mDragInfo.spanY;
3605 }
3606
Winson Chung3d503fb2011-07-13 17:25:49 -07003607 final long container = mLauncher.isHotseatLayout(cellLayout) ?
3608 LauncherSettings.Favorites.CONTAINER_HOTSEAT :
3609 LauncherSettings.Favorites.CONTAINER_DESKTOP;
Adam Cohendcd297f2013-06-18 13:13:40 -07003610 final long screenId = getIdForScreen(cellLayout);
3611 if (!mLauncher.isHotseatLayout(cellLayout)
3612 && screenId != getScreenIdForPageIndex(mCurrentPage)
Winson Chung3d503fb2011-07-13 17:25:49 -07003613 && mState != State.SPRING_LOADED) {
Adam Cohendcd297f2013-06-18 13:13:40 -07003614 snapToScreenId(screenId, null);
Adam Cohen76078c42011-06-09 15:06:52 -07003615 }
Adam Cohenb7e16182011-07-15 17:55:02 -07003616
3617 if (info instanceof PendingAddItemInfo) {
3618 final PendingAddItemInfo pendingInfo = (PendingAddItemInfo) dragInfo;
3619
Adam Cohen558baaf2011-08-15 15:22:57 -07003620 boolean findNearestVacantCell = true;
3621 if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
3622 mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
3623 cellLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08003624 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
3625 mDragViewVisualCenter[1], mTargetCell);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003626 if (willCreateUserFolder((ItemInfo) d.dragInfo, cellLayout, mTargetCell,
Adam Cohen482ed822012-03-02 14:15:13 -08003627 distance, true) || willAddToExistingUserFolder((ItemInfo) d.dragInfo,
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003628 cellLayout, mTargetCell, distance)) {
Adam Cohen558baaf2011-08-15 15:22:57 -07003629 findNearestVacantCell = false;
3630 }
3631 }
Adam Cohen482ed822012-03-02 14:15:13 -08003632
Adam Cohend41fbf52012-02-16 23:53:59 -08003633 final ItemInfo item = (ItemInfo) d.dragInfo;
Adam Cohenaaa5c212012-10-05 18:14:31 -07003634 boolean updateWidgetSize = false;
Adam Cohen558baaf2011-08-15 15:22:57 -07003635 if (findNearestVacantCell) {
Adam Cohen482ed822012-03-02 14:15:13 -08003636 int minSpanX = item.spanX;
3637 int minSpanY = item.spanY;
3638 if (item.minSpanX > 0 && item.minSpanY > 0) {
3639 minSpanX = item.minSpanX;
3640 minSpanY = item.minSpanY;
3641 }
Adam Cohend41fbf52012-02-16 23:53:59 -08003642 int[] resultSpan = new int[2];
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003643 mTargetCell = cellLayout.createArea((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08003644 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, info.spanX, info.spanY,
3645 null, mTargetCell, resultSpan, CellLayout.MODE_ON_DROP_EXTERNAL);
Adam Cohen9e05a5e2012-09-10 15:53:09 -07003646
3647 if (resultSpan[0] != item.spanX || resultSpan[1] != item.spanY) {
3648 updateWidgetSize = true;
3649 }
Adam Cohend41fbf52012-02-16 23:53:59 -08003650 item.spanX = resultSpan[0];
3651 item.spanY = resultSpan[1];
Adam Cohen558baaf2011-08-15 15:22:57 -07003652 }
3653
Adam Cohenb7e16182011-07-15 17:55:02 -07003654 Runnable onAnimationCompleteRunnable = new Runnable() {
Winson Chung557d6ed2011-07-08 15:34:52 -07003655 @Override
3656 public void run() {
3657 // When dragging and dropping from customization tray, we deal with creating
3658 // widgets/shortcuts/folders in a slightly different way
Adam Cohenb7e16182011-07-15 17:55:02 -07003659 switch (pendingInfo.itemType) {
Winson Chung557d6ed2011-07-08 15:34:52 -07003660 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
Adam Cohend41fbf52012-02-16 23:53:59 -08003661 int span[] = new int[2];
3662 span[0] = item.spanX;
3663 span[1] = item.spanY;
Adam Cohenb7e16182011-07-15 17:55:02 -07003664 mLauncher.addAppWidgetFromDrop((PendingAddWidgetInfo) pendingInfo,
Adam Cohendcd297f2013-06-18 13:13:40 -07003665 container, screenId, mTargetCell, span, null);
Winson Chung557d6ed2011-07-08 15:34:52 -07003666 break;
3667 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
Adam Cohenb7e16182011-07-15 17:55:02 -07003668 mLauncher.processShortcutFromDrop(pendingInfo.componentName,
Adam Cohendcd297f2013-06-18 13:13:40 -07003669 container, screenId, mTargetCell, null);
Winson Chung557d6ed2011-07-08 15:34:52 -07003670 break;
3671 default:
Adam Cohenb7e16182011-07-15 17:55:02 -07003672 throw new IllegalStateException("Unknown item type: " +
3673 pendingInfo.itemType);
Winson Chung557d6ed2011-07-08 15:34:52 -07003674 }
Winson Chung557d6ed2011-07-08 15:34:52 -07003675 }
Adam Cohenb7e16182011-07-15 17:55:02 -07003676 };
Adam Cohend41fbf52012-02-16 23:53:59 -08003677 View finalView = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
3678 ? ((PendingAddWidgetInfo) pendingInfo).boundWidget : null;
Adam Cohen9e05a5e2012-09-10 15:53:09 -07003679
3680 if (finalView instanceof AppWidgetHostView && updateWidgetSize) {
3681 AppWidgetHostView awhv = (AppWidgetHostView) finalView;
3682 AppWidgetResizeFrame.updateWidgetSizeRanges(awhv, mLauncher, item.spanX,
3683 item.spanY);
3684 }
3685
Adam Cohend41fbf52012-02-16 23:53:59 -08003686 int animationStyle = ANIMATE_INTO_POSITION_AND_DISAPPEAR;
3687 if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET &&
3688 ((PendingAddWidgetInfo) pendingInfo).info.configure != null) {
3689 animationStyle = ANIMATE_INTO_POSITION_AND_REMAIN;
3690 }
3691 animateWidgetDrop(info, cellLayout, d.dragView, onAnimationCompleteRunnable,
3692 animationStyle, finalView, true);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07003693 } else {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003694 // This is for other drag/drop cases, like dragging from All Apps
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003695 View view = null;
3696
3697 switch (info.itemType) {
3698 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
3699 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
Michael Jurkaeadbfc52013-09-04 00:45:37 +02003700 if (info.container == NO_ID && info instanceof AppInfo) {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003701 // Came from all apps -- make a copy
Michael Jurkaeadbfc52013-09-04 00:45:37 +02003702 info = new ShortcutInfo((AppInfo) info);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003703 }
3704 view = mLauncher.createShortcut(R.layout.application, cellLayout,
3705 (ShortcutInfo) info);
3706 break;
Adam Cohendf2cc412011-04-27 16:56:57 -07003707 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
Adam Cohenc0dcf592011-06-01 15:30:43 -07003708 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, cellLayout,
3709 (FolderInfo) info, mIconCache);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003710 break;
3711 default:
3712 throw new IllegalStateException("Unknown item type: " + info.itemType);
3713 }
3714
Adam Cohenc0dcf592011-06-01 15:30:43 -07003715 // First we find the cell nearest to point at which the item is
3716 // dropped, without any consideration to whether there is an item there.
3717 if (touchXY != null) {
3718 mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
3719 cellLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08003720 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
3721 mDragViewVisualCenter[1], mTargetCell);
Winson Chung557d6ed2011-07-08 15:34:52 -07003722 d.postAnimationRunnable = exitSpringLoadedRunnable;
Adam Cohen482ed822012-03-02 14:15:13 -08003723 if (createUserFolderIfNecessary(view, container, cellLayout, mTargetCell, distance,
3724 true, d.dragView, d.postAnimationRunnable)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07003725 return;
3726 }
Adam Cohen482ed822012-03-02 14:15:13 -08003727 if (addToExistingFolderIfNecessary(view, cellLayout, mTargetCell, distance, d,
3728 true)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07003729 return;
3730 }
Adam Cohen10b17372011-04-15 14:21:25 -07003731 }
3732
Michael Jurkac4e772e2011-02-10 13:32:01 -08003733 if (touchXY != null) {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003734 // when dragging and dropping, just find the closest free spot
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003735 mTargetCell = cellLayout.createArea((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08003736 (int) mDragViewVisualCenter[1], 1, 1, 1, 1,
Adam Cohenea889a22012-03-27 16:45:39 -07003737 null, mTargetCell, null, CellLayout.MODE_ON_DROP_EXTERNAL);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003738 } else {
3739 cellLayout.findCellForSpan(mTargetCell, 1, 1);
3740 }
Adam Cohendcd297f2013-06-18 13:13:40 -07003741 addInScreen(view, container, screenId, mTargetCell[0], mTargetCell[1], info.spanX,
Winson Chung3d503fb2011-07-13 17:25:49 -07003742 info.spanY, insertAtFirst);
Adam Cohen716b51e2011-06-30 12:09:54 -07003743 cellLayout.onDropChild(view);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07003744 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
Michael Jurkaa52570f2012-03-20 03:18:20 -07003745 cellLayout.getShortcutsAndWidgets().measureChild(view);
Adam Cohend5e42732011-03-28 17:33:39 -07003746
Adam Cohendcd297f2013-06-18 13:13:40 -07003747 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId,
Winson Chungaafa03c2010-06-11 17:34:16 -07003748 lp.cellX, lp.cellY);
Adam Cohen3e8f8112011-07-02 18:03:00 -07003749
3750 if (d.dragView != null) {
Adam Cohen4b285c52011-07-21 14:24:06 -07003751 // We wrap the animation call in the temporary set and reset of the current
3752 // cellLayout to its final transform -- this means we animate the drag view to
3753 // the correct final location.
3754 setFinalTransitionTransform(cellLayout);
Winson Chung557d6ed2011-07-08 15:34:52 -07003755 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view,
Adam Cohen28f852a2013-10-15 14:34:05 -07003756 exitSpringLoadedRunnable, this);
Adam Cohen4b285c52011-07-21 14:24:06 -07003757 resetTransitionTransform(cellLayout);
Adam Cohen3e8f8112011-07-02 18:03:00 -07003758 }
Joe Onorato00acb122009-08-04 16:04:30 -04003759 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003760 }
Winson Chungaafa03c2010-06-11 17:34:16 -07003761
Adam Cohend41fbf52012-02-16 23:53:59 -08003762 public Bitmap createWidgetBitmap(ItemInfo widgetInfo, View layout) {
Adam Cohened66b2b2012-01-23 17:28:51 -08003763 int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(widgetInfo.spanX,
3764 widgetInfo.spanY, widgetInfo, false);
Adam Cohend41fbf52012-02-16 23:53:59 -08003765 int visibility = layout.getVisibility();
Adam Cohened66b2b2012-01-23 17:28:51 -08003766 layout.setVisibility(VISIBLE);
3767
3768 int width = MeasureSpec.makeMeasureSpec(unScaledSize[0], MeasureSpec.EXACTLY);
3769 int height = MeasureSpec.makeMeasureSpec(unScaledSize[1], MeasureSpec.EXACTLY);
3770 Bitmap b = Bitmap.createBitmap(unScaledSize[0], unScaledSize[1],
3771 Bitmap.Config.ARGB_8888);
3772 Canvas c = new Canvas(b);
3773
3774 layout.measure(width, height);
3775 layout.layout(0, 0, unScaledSize[0], unScaledSize[1]);
Adam Cohened66b2b2012-01-23 17:28:51 -08003776 layout.draw(c);
3777 c.setBitmap(null);
Adam Cohend41fbf52012-02-16 23:53:59 -08003778 layout.setVisibility(visibility);
Adam Cohened66b2b2012-01-23 17:28:51 -08003779 return b;
3780 }
3781
Adam Cohend41fbf52012-02-16 23:53:59 -08003782 private void getFinalPositionForDropAnimation(int[] loc, float[] scaleXY,
Adam Cohen9d5b7d82012-05-09 18:00:44 -07003783 DragView dragView, CellLayout layout, ItemInfo info, int[] targetCell,
Michael Jurkaa2fe7862012-05-29 05:58:17 -07003784 boolean external, boolean scale) {
Adam Cohened66b2b2012-01-23 17:28:51 -08003785 // Now we animate the dragView, (ie. the widget or shortcut preview) into its final
3786 // location and size on the home screen.
Adam Cohend41fbf52012-02-16 23:53:59 -08003787 int spanX = info.spanX;
3788 int spanY = info.spanY;
Adam Cohened66b2b2012-01-23 17:28:51 -08003789
Adam Cohend41fbf52012-02-16 23:53:59 -08003790 Rect r = estimateItemPosition(layout, info, targetCell[0], targetCell[1], spanX, spanY);
3791 loc[0] = r.left;
3792 loc[1] = r.top;
3793
3794 setFinalTransitionTransform(layout);
3795 float cellLayoutScale =
Adam Cohen7d30a372013-07-01 17:03:59 -07003796 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(layout, loc, true);
Adam Cohend41fbf52012-02-16 23:53:59 -08003797 resetTransitionTransform(layout);
Michael Jurkaa2fe7862012-05-29 05:58:17 -07003798
3799 float dragViewScaleX;
3800 float dragViewScaleY;
3801 if (scale) {
3802 dragViewScaleX = (1.0f * r.width()) / dragView.getMeasuredWidth();
3803 dragViewScaleY = (1.0f * r.height()) / dragView.getMeasuredHeight();
3804 } else {
3805 dragViewScaleX = 1f;
3806 dragViewScaleY = 1f;
3807 }
Adam Cohend41fbf52012-02-16 23:53:59 -08003808
Adam Cohened66b2b2012-01-23 17:28:51 -08003809 // The animation will scale the dragView about its center, so we need to center about
3810 // the final location.
3811 loc[0] -= (dragView.getMeasuredWidth() - cellLayoutScale * r.width()) / 2;
3812 loc[1] -= (dragView.getMeasuredHeight() - cellLayoutScale * r.height()) / 2;
3813
Adam Cohend41fbf52012-02-16 23:53:59 -08003814 scaleXY[0] = dragViewScaleX * cellLayoutScale;
3815 scaleXY[1] = dragViewScaleY * cellLayoutScale;
3816 }
3817
3818 public void animateWidgetDrop(ItemInfo info, CellLayout cellLayout, DragView dragView,
3819 final Runnable onCompleteRunnable, int animationType, final View finalView,
3820 boolean external) {
3821 Rect from = new Rect();
3822 mLauncher.getDragLayer().getViewRectRelativeToSelf(dragView, from);
3823
3824 int[] finalPos = new int[2];
3825 float scaleXY[] = new float[2];
Michael Jurkaa2fe7862012-05-29 05:58:17 -07003826 boolean scalePreview = !(info instanceof PendingAddShortcutInfo);
Adam Cohend41fbf52012-02-16 23:53:59 -08003827 getFinalPositionForDropAnimation(finalPos, scaleXY, dragView, cellLayout, info, mTargetCell,
Michael Jurkaa2fe7862012-05-29 05:58:17 -07003828 external, scalePreview);
Adam Cohened66b2b2012-01-23 17:28:51 -08003829
3830 Resources res = mLauncher.getResources();
Adam Cohenad4e15c2013-10-17 16:21:35 -07003831 final int duration = res.getInteger(R.integer.config_dropAnimMaxDuration) - 200;
Adam Cohened66b2b2012-01-23 17:28:51 -08003832
Adam Cohend41fbf52012-02-16 23:53:59 -08003833 // In the case where we've prebound the widget, we remove it from the DragLayer
3834 if (finalView instanceof AppWidgetHostView && external) {
Adam Cohen68f681b2012-05-28 15:01:16 -07003835 Log.d(TAG, "6557954 Animate widget drop, final view is appWidgetHostView");
Adam Cohend41fbf52012-02-16 23:53:59 -08003836 mLauncher.getDragLayer().removeView(finalView);
3837 }
3838 if ((animationType == ANIMATE_INTO_POSITION_AND_RESIZE || external) && finalView != null) {
3839 Bitmap crossFadeBitmap = createWidgetBitmap(info, finalView);
Adam Cohened66b2b2012-01-23 17:28:51 -08003840 dragView.setCrossFadeBitmap(crossFadeBitmap);
3841 dragView.crossFade((int) (duration * 0.8f));
Adam Cohend41fbf52012-02-16 23:53:59 -08003842 } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET && external) {
3843 scaleXY[0] = scaleXY[1] = Math.min(scaleXY[0], scaleXY[1]);
Adam Cohened66b2b2012-01-23 17:28:51 -08003844 }
3845
Adam Cohend41fbf52012-02-16 23:53:59 -08003846 DragLayer dragLayer = mLauncher.getDragLayer();
Winson Chung7bd1bbb2012-02-13 18:29:29 -08003847 if (animationType == CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION) {
Adam Cohend41fbf52012-02-16 23:53:59 -08003848 mLauncher.getDragLayer().animateViewIntoPosition(dragView, finalPos, 0f, 0.1f, 0.1f,
Adam Cohened66b2b2012-01-23 17:28:51 -08003849 DragLayer.ANIMATION_END_DISAPPEAR, onCompleteRunnable, duration);
3850 } else {
Adam Cohend41fbf52012-02-16 23:53:59 -08003851 int endStyle;
3852 if (animationType == ANIMATE_INTO_POSITION_AND_REMAIN) {
3853 endStyle = DragLayer.ANIMATION_END_REMAIN_VISIBLE;
3854 } else {
3855 endStyle = DragLayer.ANIMATION_END_DISAPPEAR;;
3856 }
3857
3858 Runnable onComplete = new Runnable() {
3859 @Override
3860 public void run() {
3861 if (finalView != null) {
3862 finalView.setVisibility(VISIBLE);
3863 }
3864 if (onCompleteRunnable != null) {
3865 onCompleteRunnable.run();
3866 }
3867 }
3868 };
3869 dragLayer.animateViewIntoPosition(dragView, from.left, from.top, finalPos[0],
3870 finalPos[1], 1, 1, 1, scaleXY[0], scaleXY[1], onComplete, endStyle,
3871 duration, this);
Adam Cohened66b2b2012-01-23 17:28:51 -08003872 }
3873 }
3874
Adam Cohen4b285c52011-07-21 14:24:06 -07003875 public void setFinalTransitionTransform(CellLayout layout) {
3876 if (isSwitchingState()) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003877 mCurrentScale = getScaleX();
3878 setScaleX(mNewScale);
3879 setScaleY(mNewScale);
Adam Cohen4b285c52011-07-21 14:24:06 -07003880 }
3881 }
3882 public void resetTransitionTransform(CellLayout layout) {
3883 if (isSwitchingState()) {
Adam Cohen7d30a372013-07-01 17:03:59 -07003884 setScaleX(mCurrentScale);
3885 setScaleY(mCurrentScale);
Adam Cohen4b285c52011-07-21 14:24:06 -07003886 }
3887 }
3888
Jeff Sharkey70864282009-04-07 21:08:40 -07003889 /**
3890 * Return the current {@link CellLayout}, correctly picking the destination
3891 * screen while a scroll is in progress.
3892 */
Patrick Dubroy5f445422011-02-18 14:35:21 -08003893 public CellLayout getCurrentDropLayout() {
Winson Chung360e63f2012-04-27 13:48:05 -07003894 return (CellLayout) getChildAt(getNextPage());
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003895 }
3896
Jeff Sharkey70864282009-04-07 21:08:40 -07003897 /**
Michael Jurka0280c3b2010-09-17 15:00:07 -07003898 * Return the current CellInfo describing our current drag; this method exists
3899 * so that Launcher can sync this object with the correct info when the activity is created/
3900 * destroyed
3901 *
3902 */
3903 public CellLayout.CellInfo getDragInfo() {
3904 return mDragInfo;
3905 }
3906
Adam Cohen21cd0022013-10-09 18:57:02 -07003907 public int getRestorePage() {
3908 return getNextPage() - numCustomPages();
3909 }
3910
Michael Jurka0280c3b2010-09-17 15:00:07 -07003911 /**
Jeff Sharkey70864282009-04-07 21:08:40 -07003912 * Calculate the nearest cell where the given object would be dropped.
Adam Cohene3e27a82011-04-15 12:07:39 -07003913 *
3914 * pixelX and pixelY should be in the coordinate system of layout
Jeff Sharkey70864282009-04-07 21:08:40 -07003915 */
Adam Cohendf035382011-04-11 17:22:04 -07003916 private int[] findNearestArea(int pixelX, int pixelY,
Adam Cohene3e27a82011-04-15 12:07:39 -07003917 int spanX, int spanY, CellLayout layout, int[] recycle) {
Adam Cohendf035382011-04-11 17:22:04 -07003918 return layout.findNearestArea(
Adam Cohene3e27a82011-04-15 12:07:39 -07003919 pixelX, pixelY, spanX, spanY, recycle);
Adam Cohendf035382011-04-11 17:22:04 -07003920 }
3921
Adam Cohencff6af82011-09-13 14:51:53 -07003922 void setup(DragController dragController) {
Michael Jurkac2f7f472010-12-14 15:34:42 -08003923 mSpringLoadedDragController = new SpringLoadedDragController(mLauncher);
Joe Onorato00acb122009-08-04 16:04:30 -04003924 mDragController = dragController;
Michael Jurkad74c9842011-07-10 12:44:21 -07003925
Michael Jurkad74c9842011-07-10 12:44:21 -07003926 // hardware layers on children are enabled on startup, but should be disabled until
3927 // needed
Michael Jurka3a0469d2012-06-21 09:38:41 -07003928 updateChildrenLayersEnabled(false);
Michael Jurkad74c9842011-07-10 12:44:21 -07003929 setWallpaperDimension();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003930 }
3931
Patrick Dubroye3887cc2011-01-20 10:43:40 -08003932 /**
3933 * Called at the end of a drag which originated on the workspace.
3934 */
Michael Jurka1e2f4652013-07-08 18:03:46 -07003935 public void onDropCompleted(final View target, final DragObject d,
3936 final boolean isFlingToDelete, final boolean success) {
3937 if (mDeferDropAfterUninstall) {
3938 mDeferredAction = new Runnable() {
Adam Cohenad4e15c2013-10-17 16:21:35 -07003939 public void run() {
3940 onDropCompleted(target, d, isFlingToDelete, success);
3941 mDeferredAction = null;
3942 }
3943 };
Michael Jurka1e2f4652013-07-08 18:03:46 -07003944 return;
3945 }
3946
3947 boolean beingCalledAfterUninstall = mDeferredAction != null;
3948
3949 if (success && !(beingCalledAfterUninstall && !mUninstallSuccessful)) {
3950 if (target != this && mDragInfo != null) {
Daniel Sandlerb56e95a2013-09-04 23:13:39 -04003951 CellLayout parentCell = getParentCellLayoutForView(mDragInfo.cell);
3952 if (parentCell != null) {
3953 parentCell.removeView(mDragInfo.cell);
3954 }
Michael Jurka1e2f4652013-07-08 18:03:46 -07003955 if (mDragInfo.cell instanceof DropTarget) {
3956 mDragController.removeDropTarget((DropTarget) mDragInfo.cell);
Joe Onorato00acb122009-08-04 16:04:30 -04003957 }
Adam Cohend6e7aa32013-07-09 15:32:37 -07003958 // If we move the item to anything not on the Workspace, check if any empty
3959 // screens need to be removed. If we dropped back on the workspace, this will
3960 // be done post drop animation.
Adam Cohenad4e15c2013-10-17 16:21:35 -07003961 removeExtraEmptyScreen(true, null, 0, true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003962 }
Patrick Dubroyce34a972010-10-19 10:34:32 -07003963 } else if (mDragInfo != null) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003964 CellLayout cellLayout;
3965 if (mLauncher.isHotseatLayout(target)) {
3966 cellLayout = mLauncher.getHotseat().getLayout();
3967 } else {
Adam Cohendcd297f2013-06-18 13:13:40 -07003968 cellLayout = getScreenWithId(mDragInfo.screenId);
Winson Chung3d503fb2011-07-13 17:25:49 -07003969 }
3970 cellLayout.onDropChild(mDragInfo.cell);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003971 }
Michael Jurka1e2f4652013-07-08 18:03:46 -07003972 if ((d.cancelled || (beingCalledAfterUninstall && !mUninstallSuccessful))
3973 && mDragInfo.cell != null) {
3974 mDragInfo.cell.setVisibility(VISIBLE);
Adam Cohen36cc09b2011-09-29 17:33:15 -07003975 }
Joe Onorato4be866d2010-10-10 11:26:02 -07003976 mDragOutline = null;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003977 mDragInfo = null;
3978 }
3979
Michael Jurka1e2f4652013-07-08 18:03:46 -07003980 public void deferCompleteDropAfterUninstallActivity() {
3981 mDeferDropAfterUninstall = true;
3982 }
3983
3984 /// maybe move this into a smaller part
3985 public void onUninstallActivityReturned(boolean success) {
3986 mDeferDropAfterUninstall = false;
3987 mUninstallSuccessful = success;
3988 if (mDeferredAction != null) {
3989 mDeferredAction.run();
3990 }
3991 }
3992
Adam Cohenea889a22012-03-27 16:45:39 -07003993 void updateItemLocationsInDatabase(CellLayout cl) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07003994 int count = cl.getShortcutsAndWidgets().getChildCount();
Adam Cohend3461712012-03-29 17:25:17 -07003995
Adam Cohendcd297f2013-06-18 13:13:40 -07003996 long screenId = getIdForScreen(cl);
Adam Cohend3461712012-03-29 17:25:17 -07003997 int container = Favorites.CONTAINER_DESKTOP;
3998
3999 if (mLauncher.isHotseatLayout(cl)) {
Adam Cohendcd297f2013-06-18 13:13:40 -07004000 screenId = -1;
Adam Cohend3461712012-03-29 17:25:17 -07004001 container = Favorites.CONTAINER_HOTSEAT;
4002 }
4003
Adam Cohen482ed822012-03-02 14:15:13 -08004004 for (int i = 0; i < count; i++) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004005 View v = cl.getShortcutsAndWidgets().getChildAt(i);
Adam Cohen482ed822012-03-02 14:15:13 -08004006 ItemInfo info = (ItemInfo) v.getTag();
Adam Cohen2acce882012-03-28 19:03:19 -07004007 // Null check required as the AllApps button doesn't have an item info
Adam Cohen487f7dd2012-06-28 18:12:10 -07004008 if (info != null && info.requiresDbUpdate) {
4009 info.requiresDbUpdate = false;
Adam Cohendcd297f2013-06-18 13:13:40 -07004010 LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId, info.cellX,
Adam Cohenbebf0422012-04-11 18:06:28 -07004011 info.cellY, info.spanX, info.spanY);
Adam Cohen2acce882012-03-28 19:03:19 -07004012 }
Adam Cohen482ed822012-03-02 14:15:13 -08004013 }
4014 }
4015
Adam Cohen4caf2982013-08-20 18:54:31 -07004016 ArrayList<ComponentName> getUniqueComponents(boolean stripDuplicates, ArrayList<ComponentName> duplicates) {
Adam Cohene25af792013-06-06 23:08:25 -07004017 ArrayList<ComponentName> uniqueIntents = new ArrayList<ComponentName>();
Adam Cohen4caf2982013-08-20 18:54:31 -07004018 getUniqueIntents((CellLayout) mLauncher.getHotseat().getLayout(), uniqueIntents, duplicates, false);
Adam Cohene25af792013-06-06 23:08:25 -07004019 int count = getChildCount();
4020 for (int i = 0; i < count; i++) {
4021 CellLayout cl = (CellLayout) getChildAt(i);
Adam Cohen4caf2982013-08-20 18:54:31 -07004022 getUniqueIntents(cl, uniqueIntents, duplicates, false);
Adam Cohene25af792013-06-06 23:08:25 -07004023 }
4024 return uniqueIntents;
4025 }
4026
Adam Cohen4caf2982013-08-20 18:54:31 -07004027 void getUniqueIntents(CellLayout cl, ArrayList<ComponentName> uniqueIntents,
4028 ArrayList<ComponentName> duplicates, boolean stripDuplicates) {
Adam Cohene25af792013-06-06 23:08:25 -07004029 int count = cl.getShortcutsAndWidgets().getChildCount();
4030
4031 ArrayList<View> children = new ArrayList<View>();
4032 for (int i = 0; i < count; i++) {
4033 View v = cl.getShortcutsAndWidgets().getChildAt(i);
4034 children.add(v);
4035 }
4036
4037 for (int i = 0; i < count; i++) {
4038 View v = children.get(i);
4039 ItemInfo info = (ItemInfo) v.getTag();
4040 // Null check required as the AllApps button doesn't have an item info
4041 if (info instanceof ShortcutInfo) {
4042 ShortcutInfo si = (ShortcutInfo) info;
4043 ComponentName cn = si.intent.getComponent();
4044
Adam Cohen4caf2982013-08-20 18:54:31 -07004045 Uri dataUri = si.intent.getData();
4046 // If dataUri is not null / empty or if this component isn't one that would
4047 // have previously showed up in the AllApps list, then this is a widget-type
4048 // shortcut, so ignore it.
4049 if (dataUri != null && !dataUri.equals(Uri.EMPTY)) {
Adam Cohen99894d92013-06-14 11:22:59 -07004050 continue;
4051 }
Adam Cohena0b57492013-06-14 15:33:35 -07004052
Adam Cohene25af792013-06-06 23:08:25 -07004053 if (!uniqueIntents.contains(cn)) {
4054 uniqueIntents.add(cn);
4055 } else {
Adam Cohen4caf2982013-08-20 18:54:31 -07004056 if (stripDuplicates) {
4057 cl.removeViewInLayout(v);
4058 LauncherModel.deleteItemFromDatabase(mLauncher, si);
4059 }
4060 if (duplicates != null) {
4061 duplicates.add(cn);
4062 }
Adam Cohene25af792013-06-06 23:08:25 -07004063 }
4064 }
4065 if (v instanceof FolderIcon) {
4066 FolderIcon fi = (FolderIcon) v;
4067 ArrayList<View> items = fi.getFolder().getItemsInReadingOrder();
4068 for (int j = 0; j < items.size(); j++) {
4069 if (items.get(j).getTag() instanceof ShortcutInfo) {
4070 ShortcutInfo si = (ShortcutInfo) items.get(j).getTag();
4071 ComponentName cn = si.intent.getComponent();
4072
Adam Cohen4caf2982013-08-20 18:54:31 -07004073 Uri dataUri = si.intent.getData();
4074 // If dataUri is not null / empty or if this component isn't one that would
4075 // have previously showed up in the AllApps list, then this is a widget-type
4076 // shortcut, so ignore it.
4077 if (dataUri != null && !dataUri.equals(Uri.EMPTY)) {
Adam Cohen99894d92013-06-14 11:22:59 -07004078 continue;
4079 }
Adam Cohen4caf2982013-08-20 18:54:31 -07004080
Adam Cohene25af792013-06-06 23:08:25 -07004081 if (!uniqueIntents.contains(cn)) {
4082 uniqueIntents.add(cn);
Adam Cohen4caf2982013-08-20 18:54:31 -07004083 } else {
4084 if (stripDuplicates) {
4085 fi.getFolderInfo().remove(si);
4086 LauncherModel.deleteItemFromDatabase(mLauncher, si);
4087 }
4088 if (duplicates != null) {
4089 duplicates.add(cn);
4090 }
Adam Cohene25af792013-06-06 23:08:25 -07004091 }
4092 }
4093 }
4094 }
4095 }
4096 }
4097
4098 void saveWorkspaceToDb() {
4099 saveWorkspaceScreenToDb((CellLayout) mLauncher.getHotseat().getLayout());
4100 int count = getChildCount();
4101 for (int i = 0; i < count; i++) {
4102 CellLayout cl = (CellLayout) getChildAt(i);
4103 saveWorkspaceScreenToDb(cl);
4104 }
4105 }
4106
4107 void saveWorkspaceScreenToDb(CellLayout cl) {
4108 int count = cl.getShortcutsAndWidgets().getChildCount();
4109
Adam Cohendcd297f2013-06-18 13:13:40 -07004110 long screenId = getIdForScreen(cl);
Adam Cohene25af792013-06-06 23:08:25 -07004111 int container = Favorites.CONTAINER_DESKTOP;
4112
Winson Chungf70696d2013-06-25 16:19:53 -07004113 Hotseat hotseat = mLauncher.getHotseat();
Adam Cohene25af792013-06-06 23:08:25 -07004114 if (mLauncher.isHotseatLayout(cl)) {
Adam Cohendcd297f2013-06-18 13:13:40 -07004115 screenId = -1;
Adam Cohene25af792013-06-06 23:08:25 -07004116 container = Favorites.CONTAINER_HOTSEAT;
4117 }
4118
4119 for (int i = 0; i < count; i++) {
4120 View v = cl.getShortcutsAndWidgets().getChildAt(i);
4121 ItemInfo info = (ItemInfo) v.getTag();
4122 // Null check required as the AllApps button doesn't have an item info
4123 if (info != null) {
Winson Chungf70696d2013-06-25 16:19:53 -07004124 int cellX = info.cellX;
4125 int cellY = info.cellY;
4126 if (container == Favorites.CONTAINER_HOTSEAT) {
4127 cellX = hotseat.getCellXFromOrder((int) info.screenId);
4128 cellY = hotseat.getCellYFromOrder((int) info.screenId);
4129 }
4130 LauncherModel.addItemToDatabase(mLauncher, info, container, screenId, cellX,
4131 cellY, false);
Adam Cohene25af792013-06-06 23:08:25 -07004132 }
4133 if (v instanceof FolderIcon) {
4134 FolderIcon fi = (FolderIcon) v;
4135 fi.getFolder().addItemLocationsInDatabase();
4136 }
4137 }
4138 }
4139
Winson Chunga48487a2012-03-20 16:19:37 -07004140 @Override
Winson Chung043f2af2012-03-01 16:09:54 -08004141 public boolean supportsFlingToDelete() {
4142 return true;
4143 }
4144
Winson Chunga48487a2012-03-20 16:19:37 -07004145 @Override
4146 public void onFlingToDelete(DragObject d, int x, int y, PointF vec) {
4147 // Do nothing
4148 }
4149
4150 @Override
4151 public void onFlingToDeleteCompleted() {
4152 // Do nothing
4153 }
4154
Michael Jurka0280c3b2010-09-17 15:00:07 -07004155 public boolean isDropEnabled() {
4156 return true;
4157 }
4158
Michael Jurka0142d492010-08-25 17:46:15 -07004159 @Override
4160 protected void onRestoreInstanceState(Parcelable state) {
4161 super.onRestoreInstanceState(state);
4162 Launcher.setScreen(mCurrentPage);
4163 }
4164
4165 @Override
Adam Cohen1462de32012-07-24 22:34:36 -07004166 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
4167 // We don't dispatch restoreInstanceState to our children using this code path.
Dave Hawkeya8881582013-09-17 15:55:33 -06004168 // Some pages will be restored immediately as their items are bound immediately, and
Adam Cohen1462de32012-07-24 22:34:36 -07004169 // others we will need to wait until after their items are bound.
4170 mSavedStates = container;
4171 }
4172
4173 public void restoreInstanceStateForChild(int child) {
4174 if (mSavedStates != null) {
4175 mRestoredPages.add(child);
4176 CellLayout cl = (CellLayout) getChildAt(child);
4177 cl.restoreInstanceState(mSavedStates);
4178 }
4179 }
4180
4181 public void restoreInstanceStateForRemainingPages() {
4182 int count = getChildCount();
4183 for (int i = 0; i < count; i++) {
4184 if (!mRestoredPages.contains(i)) {
4185 restoreInstanceStateForChild(i);
4186 }
4187 }
4188 mRestoredPages.clear();
Winson Chungd8e596d2013-10-21 17:14:12 -07004189 mSavedStates = null;
Adam Cohen1462de32012-07-24 22:34:36 -07004190 }
4191
4192 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004193 public void scrollLeft() {
Michael Jurkad74c9842011-07-10 12:44:21 -07004194 if (!isSmall() && !mIsSwitchingState) {
Michael Jurka0142d492010-08-25 17:46:15 -07004195 super.scrollLeft();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004196 }
Adam Cohen95bb8002011-07-03 23:40:28 -07004197 Folder openFolder = getOpenFolder();
4198 if (openFolder != null) {
4199 openFolder.completeDragExit();
4200 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004201 }
4202
Michael Jurka0142d492010-08-25 17:46:15 -07004203 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004204 public void scrollRight() {
Michael Jurkad74c9842011-07-10 12:44:21 -07004205 if (!isSmall() && !mIsSwitchingState) {
Michael Jurka0142d492010-08-25 17:46:15 -07004206 super.scrollRight();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004207 }
Adam Cohen95bb8002011-07-03 23:40:28 -07004208 Folder openFolder = getOpenFolder();
4209 if (openFolder != null) {
4210 openFolder.completeDragExit();
4211 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004212 }
4213
Patrick Dubroy1262e362010-10-06 15:49:50 -07004214 @Override
Winson Chung3e0839e2011-10-03 15:15:18 -07004215 public boolean onEnterScrollArea(int x, int y, int direction) {
Winson Chung1c4cf4a2011-07-29 14:49:10 -07004216 // Ignore the scroll area if we are dragging over the hot seat
Daniel Sandlercc8befa2013-06-11 14:45:48 -04004217 boolean isPortrait = !LauncherAppState.isScreenLandscape(getContext());
Winson Chung10bfc6e2012-03-28 15:41:26 -07004218 if (mLauncher.getHotseat() != null && isPortrait) {
Winson Chung1c4cf4a2011-07-29 14:49:10 -07004219 Rect r = new Rect();
4220 mLauncher.getHotseat().getHitRect(r);
4221 if (r.contains(x, y)) {
Winson Chung3e0839e2011-10-03 15:15:18 -07004222 return false;
Winson Chung1c4cf4a2011-07-29 14:49:10 -07004223 }
4224 }
4225
Winson Chung3e0839e2011-10-03 15:15:18 -07004226 boolean result = false;
Adam Cohen61a9a5c2013-08-14 13:32:04 -07004227 if (!isSmall() && !mIsSwitchingState && getOpenFolder() == null) {
Michael Jurkad718d6a2010-10-14 15:35:17 -07004228 mInScrollArea = true;
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08004229
Winson Chung360e63f2012-04-27 13:48:05 -07004230 final int page = getNextPage() +
Winson Chungaa15ffe2012-01-18 15:45:28 -08004231 (direction == DragController.SCROLL_LEFT ? -1 : 1);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07004232 // We always want to exit the current layout to ensure parity of enter / exit
4233 setCurrentDropLayout(null);
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08004234
Winson Chungaa15ffe2012-01-18 15:45:28 -08004235 if (0 <= page && page < getChildCount()) {
Winson Chung482a5b62013-07-31 17:19:51 -07004236 // Ensure that we are not dragging over to the custom content screen
4237 if (getScreenIdForPageIndex(page) == CUSTOM_CONTENT_SCREEN_ID) {
4238 return false;
4239 }
4240
Winson Chungaa15ffe2012-01-18 15:45:28 -08004241 CellLayout layout = (CellLayout) getChildAt(page);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07004242 setCurrentDragOverlappingLayout(layout);
Winson Chungc07918d2011-07-01 15:35:26 -07004243
4244 // Workspace is responsible for drawing the edge glow on adjacent pages,
4245 // so we need to redraw the workspace when this may have changed.
4246 invalidate();
Winson Chung3e0839e2011-10-03 15:15:18 -07004247 result = true;
Michael Jurkad718d6a2010-10-14 15:35:17 -07004248 }
Patrick Dubroy1262e362010-10-06 15:49:50 -07004249 }
Winson Chung3e0839e2011-10-03 15:15:18 -07004250 return result;
Patrick Dubroy1262e362010-10-06 15:49:50 -07004251 }
4252
4253 @Override
Winson Chung3e0839e2011-10-03 15:15:18 -07004254 public boolean onExitScrollArea() {
4255 boolean result = false;
Michael Jurkad718d6a2010-10-14 15:35:17 -07004256 if (mInScrollArea) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07004257 invalidate();
4258 CellLayout layout = getCurrentDropLayout();
4259 setCurrentDropLayout(layout);
4260 setCurrentDragOverlappingLayout(layout);
4261
Adam Cohen8a18afc2011-12-13 15:57:01 -08004262 result = true;
Winson Chungc07918d2011-07-01 15:35:26 -07004263 mInScrollArea = false;
Patrick Dubroy1262e362010-10-06 15:49:50 -07004264 }
Winson Chung3e0839e2011-10-03 15:15:18 -07004265 return result;
Patrick Dubroy1262e362010-10-06 15:49:50 -07004266 }
4267
Winson Chungc07918d2011-07-01 15:35:26 -07004268 private void onResetScrollArea() {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07004269 setCurrentDragOverlappingLayout(null);
Winson Chungc07918d2011-07-01 15:35:26 -07004270 mInScrollArea = false;
4271 }
4272
Winson Chung3d503fb2011-07-13 17:25:49 -07004273 /**
4274 * Returns a specific CellLayout
4275 */
4276 CellLayout getParentCellLayoutForView(View v) {
4277 ArrayList<CellLayout> layouts = getWorkspaceAndHotseatCellLayouts();
4278 for (CellLayout layout : layouts) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004279 if (layout.getShortcutsAndWidgets().indexOfChild(v) > -1) {
Winson Chung3d503fb2011-07-13 17:25:49 -07004280 return layout;
4281 }
4282 }
4283 return null;
4284 }
4285
4286 /**
4287 * Returns a list of all the CellLayouts in the workspace.
4288 */
4289 ArrayList<CellLayout> getWorkspaceAndHotseatCellLayouts() {
4290 ArrayList<CellLayout> layouts = new ArrayList<CellLayout>();
4291 int screenCount = getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004292 for (int screen = 0; screen < screenCount; screen++) {
Winson Chung3d503fb2011-07-13 17:25:49 -07004293 layouts.add(((CellLayout) getChildAt(screen)));
4294 }
4295 if (mLauncher.getHotseat() != null) {
4296 layouts.add(mLauncher.getHotseat().getLayout());
4297 }
4298 return layouts;
4299 }
4300
4301 /**
4302 * We should only use this to search for specific children. Do not use this method to modify
Michael Jurkaa52570f2012-03-20 03:18:20 -07004303 * ShortcutsAndWidgetsContainer directly. Includes ShortcutAndWidgetContainers from
4304 * the hotseat and workspace pages
Winson Chung3d503fb2011-07-13 17:25:49 -07004305 */
Michael Jurkaa52570f2012-03-20 03:18:20 -07004306 ArrayList<ShortcutAndWidgetContainer> getAllShortcutAndWidgetContainers() {
4307 ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
4308 new ArrayList<ShortcutAndWidgetContainer>();
Winson Chung3d503fb2011-07-13 17:25:49 -07004309 int screenCount = getChildCount();
4310 for (int screen = 0; screen < screenCount; screen++) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004311 childrenLayouts.add(((CellLayout) getChildAt(screen)).getShortcutsAndWidgets());
Winson Chung3d503fb2011-07-13 17:25:49 -07004312 }
4313 if (mLauncher.getHotseat() != null) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004314 childrenLayouts.add(mLauncher.getHotseat().getLayout().getShortcutsAndWidgets());
Winson Chung3d503fb2011-07-13 17:25:49 -07004315 }
4316 return childrenLayouts;
4317 }
4318
4319 public Folder getFolderForTag(Object tag) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004320 ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
4321 getAllShortcutAndWidgetContainers();
4322 for (ShortcutAndWidgetContainer layout: childrenLayouts) {
Winson Chung3d503fb2011-07-13 17:25:49 -07004323 int count = layout.getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004324 for (int i = 0; i < count; i++) {
Winson Chung3d503fb2011-07-13 17:25:49 -07004325 View child = layout.getChildAt(i);
Winson Chung3d503fb2011-07-13 17:25:49 -07004326 if (child instanceof Folder) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004327 Folder f = (Folder) child;
Winson Chungaafa03c2010-06-11 17:34:16 -07004328 if (f.getInfo() == tag && f.getInfo().opened) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004329 return f;
4330 }
4331 }
4332 }
4333 }
4334 return null;
4335 }
4336
4337 public View getViewForTag(Object tag) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004338 ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
4339 getAllShortcutAndWidgetContainers();
4340 for (ShortcutAndWidgetContainer layout: childrenLayouts) {
Winson Chung3d503fb2011-07-13 17:25:49 -07004341 int count = layout.getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004342 for (int i = 0; i < count; i++) {
Winson Chung3d503fb2011-07-13 17:25:49 -07004343 View child = layout.getChildAt(i);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004344 if (child.getTag() == tag) {
4345 return child;
4346 }
4347 }
4348 }
4349 return null;
4350 }
4351
Adam Cohendf035382011-04-11 17:22:04 -07004352 void clearDropTargets() {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004353 ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
4354 getAllShortcutAndWidgetContainers();
4355 for (ShortcutAndWidgetContainer layout: childrenLayouts) {
Adam Cohendf035382011-04-11 17:22:04 -07004356 int childCount = layout.getChildCount();
4357 for (int j = 0; j < childCount; j++) {
4358 View v = layout.getChildAt(j);
4359 if (v instanceof DropTarget) {
4360 mDragController.removeDropTarget((DropTarget) v);
4361 }
4362 }
4363 }
4364 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004365
Winson Chung83892cc2013-05-01 16:53:33 -07004366 // Removes ALL items that match a given package name, this is usually called when a package
4367 // has been removed and we want to remove all components (widgets, shortcuts, apps) that
4368 // belong to that package.
4369 void removeItemsByPackageName(final ArrayList<String> packages) {
Winson Chung64359a52013-07-08 17:17:08 -07004370 final HashSet<String> packageNames = new HashSet<String>();
Winson Chungcd810732012-06-18 16:45:43 -07004371 packageNames.addAll(packages);
Joe Onorato64e6be72010-03-05 15:05:52 -05004372
Winson Chung64359a52013-07-08 17:17:08 -07004373 // Filter out all the ItemInfos that this is going to affect
4374 final HashSet<ItemInfo> infos = new HashSet<ItemInfo>();
4375 final HashSet<ComponentName> cns = new HashSet<ComponentName>();
Winson Chung83892cc2013-05-01 16:53:33 -07004376 ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
4377 for (CellLayout layoutParent : cellLayouts) {
4378 ViewGroup layout = layoutParent.getShortcutsAndWidgets();
4379 int childCount = layout.getChildCount();
4380 for (int i = 0; i < childCount; ++i) {
4381 View view = layout.getChildAt(i);
Winson Chung64359a52013-07-08 17:17:08 -07004382 infos.add((ItemInfo) view.getTag());
Winson Chung83892cc2013-05-01 16:53:33 -07004383 }
4384 }
Winson Chung64359a52013-07-08 17:17:08 -07004385 LauncherModel.ItemInfoFilter filter = new LauncherModel.ItemInfoFilter() {
4386 @Override
4387 public boolean filterItem(ItemInfo parent, ItemInfo info,
4388 ComponentName cn) {
4389 if (packageNames.contains(cn.getPackageName())) {
4390 cns.add(cn);
4391 return true;
4392 }
4393 return false;
4394 }
4395 };
4396 LauncherModel.filterItemInfos(infos, filter);
Winson Chung83892cc2013-05-01 16:53:33 -07004397
Winson Chung64359a52013-07-08 17:17:08 -07004398 // Remove the affected components
Winson Chung83892cc2013-05-01 16:53:33 -07004399 removeItemsByComponentName(cns);
4400 }
4401
4402 // Removes items that match the application info specified, when applications are removed
4403 // as a part of an update, this is called to ensure that other widgets and application
4404 // shortcuts are not removed.
Michael Jurkaeadbfc52013-09-04 00:45:37 +02004405 void removeItemsByApplicationInfo(final ArrayList<AppInfo> appInfos) {
Winson Chung83892cc2013-05-01 16:53:33 -07004406 // Just create a hash table of all the specific components that this will affect
4407 HashSet<ComponentName> cns = new HashSet<ComponentName>();
Michael Jurkaeadbfc52013-09-04 00:45:37 +02004408 for (AppInfo info : appInfos) {
Winson Chung83892cc2013-05-01 16:53:33 -07004409 cns.add(info.componentName);
4410 }
4411
4412 // Remove all the things
4413 removeItemsByComponentName(cns);
4414 }
4415
4416 void removeItemsByComponentName(final HashSet<ComponentName> componentNames) {
Winson Chung3d503fb2011-07-13 17:25:49 -07004417 ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
4418 for (final CellLayout layoutParent: cellLayouts) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07004419 final ViewGroup layout = layoutParent.getShortcutsAndWidgets();
Romain Guy574d20e2009-06-01 15:34:04 -07004420
Winson Chung64359a52013-07-08 17:17:08 -07004421 final HashMap<ItemInfo, View> children = new HashMap<ItemInfo, View>();
4422 for (int j = 0; j < layout.getChildCount(); j++) {
4423 final View view = layout.getChildAt(j);
4424 children.put((ItemInfo) view.getTag(), view);
4425 }
Winson Chungaafa03c2010-06-11 17:34:16 -07004426
Winson Chung64359a52013-07-08 17:17:08 -07004427 final ArrayList<View> childrenToRemove = new ArrayList<View>();
4428 final HashMap<FolderInfo, ArrayList<ShortcutInfo>> folderAppsToRemove =
4429 new HashMap<FolderInfo, ArrayList<ShortcutInfo>>();
4430 LauncherModel.ItemInfoFilter filter = new LauncherModel.ItemInfoFilter() {
4431 @Override
4432 public boolean filterItem(ItemInfo parent, ItemInfo info,
4433 ComponentName cn) {
4434 if (parent instanceof FolderInfo) {
4435 if (componentNames.contains(cn)) {
4436 FolderInfo folder = (FolderInfo) parent;
4437 ArrayList<ShortcutInfo> appsToRemove;
4438 if (folderAppsToRemove.containsKey(folder)) {
4439 appsToRemove = folderAppsToRemove.get(folder);
4440 } else {
4441 appsToRemove = new ArrayList<ShortcutInfo>();
4442 folderAppsToRemove.put(folder, appsToRemove);
Romain Guy629de3e2010-01-13 12:20:59 -08004443 }
Winson Chung64359a52013-07-08 17:17:08 -07004444 appsToRemove.add((ShortcutInfo) info);
4445 return true;
4446 }
4447 } else {
4448 if (componentNames.contains(cn)) {
4449 childrenToRemove.add(children.get(info));
4450 return true;
Romain Guy574d20e2009-06-01 15:34:04 -07004451 }
4452 }
Winson Chung64359a52013-07-08 17:17:08 -07004453 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004454 }
Winson Chung64359a52013-07-08 17:17:08 -07004455 };
4456 LauncherModel.filterItemInfos(children.keySet(), filter);
4457
4458 // Remove all the apps from their folders
4459 for (FolderInfo folder : folderAppsToRemove.keySet()) {
4460 ArrayList<ShortcutInfo> appsToRemove = folderAppsToRemove.get(folder);
4461 for (ShortcutInfo info : appsToRemove) {
4462 folder.remove(info);
4463 }
4464 }
4465
4466 // Remove all the other children
4467 for (View child : childrenToRemove) {
4468 // Note: We can not remove the view directly from CellLayoutChildren as this
4469 // does not re-mark the spaces as unoccupied.
4470 layoutParent.removeViewInLayout(child);
4471 if (child instanceof DropTarget) {
4472 mDragController.removeDropTarget((DropTarget) child);
4473 }
4474 }
4475
4476 if (childrenToRemove.size() > 0) {
4477 layout.requestLayout();
4478 layout.invalidate();
4479 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004480 }
Winson Chung2efec4e2012-05-03 12:31:48 -07004481
Winson Chung64359a52013-07-08 17:17:08 -07004482 // Strip all the empty screens
4483 stripEmptyScreens();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004484 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07004485
Winson Chung8c903312013-10-16 17:51:49 -07004486 private void updateShortcut(HashMap<ComponentName, AppInfo> appsMap, ItemInfo info,
4487 View child) {
4488 ComponentName cn = info.getIntent().getComponent();
4489 if (cn != null) {
4490 AppInfo appInfo = appsMap.get(info.getIntent().getComponent());
4491 if ((appInfo != null) && LauncherModel.isShortcutInfoUpdateable(info)) {
4492 ShortcutInfo shortcutInfo = (ShortcutInfo) info;
4493 BubbleTextView shortcut = (BubbleTextView) child;
4494 shortcutInfo.updateIcon(mIconCache);
4495 shortcutInfo.title = appInfo.title.toString();
4496 shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache);
4497 }
4498 }
4499 }
4500
Michael Jurkaeadbfc52013-09-04 00:45:37 +02004501 void updateShortcuts(ArrayList<AppInfo> apps) {
Winson Chung8c903312013-10-16 17:51:49 -07004502 // Create a map of the apps to test against
4503 final HashMap<ComponentName, AppInfo> appsMap = new HashMap<ComponentName, AppInfo>();
4504 for (AppInfo ai : apps) {
4505 appsMap.put(ai.componentName, ai);
4506 }
4507
Michael Jurkaa52570f2012-03-20 03:18:20 -07004508 ArrayList<ShortcutAndWidgetContainer> childrenLayouts = getAllShortcutAndWidgetContainers();
4509 for (ShortcutAndWidgetContainer layout: childrenLayouts) {
Winson Chung8c903312013-10-16 17:51:49 -07004510 // Update all the children shortcuts
4511 final HashMap<ItemInfo, View> children = new HashMap<ItemInfo, View>();
4512 for (int j = 0; j < layout.getChildCount(); j++) {
4513 View v = layout.getChildAt(j);
4514 ItemInfo info = (ItemInfo) v.getTag();
4515 if (info instanceof FolderInfo && v instanceof FolderIcon) {
4516 FolderIcon folder = (FolderIcon) v;
4517 ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
4518 for (View fv : folderChildren) {
4519 info = (ItemInfo) fv.getTag();
4520 updateShortcut(appsMap, info, fv);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07004521 }
Winson Chung8c903312013-10-16 17:51:49 -07004522 folder.invalidate();
4523 } else if (info instanceof ShortcutInfo) {
4524 updateShortcut(appsMap, info, v);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07004525 }
4526 }
4527 }
4528 }
4529
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004530 private void moveToScreen(int page, boolean animate) {
Winson Chungde1af762011-07-21 16:44:07 -07004531 if (!isSmall()) {
4532 if (animate) {
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004533 snapToPage(page);
Winson Chungde1af762011-07-21 16:44:07 -07004534 } else {
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004535 setCurrentPage(page);
Winson Chungde1af762011-07-21 16:44:07 -07004536 }
Joe Onoratoc45b1682010-01-11 18:48:40 -05004537 }
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004538 View child = getChildAt(page);
Winson Chung64359a52013-07-08 17:17:08 -07004539 if (child != null) {
4540 child.requestFocus();
4541 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004542 }
4543
Allan Wojciechowskiaf110e82013-09-12 10:48:23 +01004544 void moveToDefaultScreen(boolean animate) {
4545 moveToScreen(mDefaultPage, animate);
4546 }
4547
4548 void moveToCustomContentScreen(boolean animate) {
4549 if (hasCustomContent()) {
4550 int ccIndex = getPageIndexForScreenId(CUSTOM_CONTENT_SCREEN_ID);
4551 if (animate) {
4552 snapToPage(ccIndex);
4553 } else {
4554 setCurrentPage(ccIndex);
4555 }
4556 View child = getChildAt(ccIndex);
4557 if (child != null) {
4558 child.requestFocus();
4559 }
4560 }
4561 }
4562
Michael Jurka0142d492010-08-25 17:46:15 -07004563 @Override
Winson Chung7819a562013-09-19 15:55:45 -07004564 protected PageIndicator.PageMarkerResources getPageIndicatorMarker(int pageIndex) {
4565 long screenId = getScreenIdForPageIndex(pageIndex);
4566 if (screenId == EXTRA_EMPTY_SCREEN_ID) {
Michael Jurkafe0ace32013-10-03 01:05:14 -07004567 int count = mScreenOrder.size() - numCustomPages();
Winson Chung7819a562013-09-19 15:55:45 -07004568 if (count > 1) {
Winson Chungba4e52f2013-10-01 17:22:13 -07004569 return new PageIndicator.PageMarkerResources(R.drawable.ic_pageindicator_current,
Winson Chung7819a562013-09-19 15:55:45 -07004570 R.drawable.ic_pageindicator_add);
4571 }
Winson Chung82dfe582013-07-26 15:07:49 -07004572 }
Winson Chung7819a562013-09-19 15:55:45 -07004573
Winson Chung82dfe582013-07-26 15:07:49 -07004574 return super.getPageIndicatorMarker(pageIndex);
4575 }
4576
4577 @Override
Michael Jurka0142d492010-08-25 17:46:15 -07004578 public void syncPages() {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004579 }
Michael Jurka0142d492010-08-25 17:46:15 -07004580
4581 @Override
Winson Chungf314b0e2011-08-16 11:54:27 -07004582 public void syncPageItems(int page, boolean immediate) {
Michael Jurka0142d492010-08-25 17:46:15 -07004583 }
Winson Chung6a0f57d2011-06-29 20:10:49 -07004584
Adam Cohen53805212013-10-01 10:39:23 -07004585 protected String getPageIndicatorDescription() {
4586 String settings = getResources().getString(R.string.settings_button_text);
4587 return getCurrentPageDescription() + ", " + settings;
4588 }
4589
Winson Chung6a0f57d2011-06-29 20:10:49 -07004590 protected String getCurrentPageDescription() {
4591 int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
Adam Cohen53805212013-10-01 10:39:23 -07004592 int delta = numCustomPages();
4593 if (hasCustomContent() && getNextPage() == 0) {
4594 return mCustomContentDescription;
4595 }
Michael Jurka8b805b12012-04-18 14:23:14 -07004596 return String.format(getContext().getString(R.string.workspace_scroll_format),
Adam Cohen53805212013-10-01 10:39:23 -07004597 page + 1 - delta, getChildCount() - delta);
Winson Chung6a0f57d2011-06-29 20:10:49 -07004598 }
Adam Cohen8dfcba42011-07-07 16:38:18 -07004599
4600 public void getLocationInDragLayer(int[] loc) {
4601 mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
4602 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08004603}