blob: 905ad8a898c952609c322a48ee21371a2d1f18c8 [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
Joe Onoratoa5902522009-07-30 13:37:37 -070017package com.android.launcher2;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080018
Michael Jurka0280c3b2010-09-17 15:00:07 -070019import android.animation.Animator;
Chet Haaseb1254a62010-09-07 13:35:00 -070020import android.animation.AnimatorSet;
Michael Jurka0280c3b2010-09-17 15:00:07 -070021import android.animation.ObjectAnimator;
Patrick Dubroycd68ff52010-10-28 17:57:05 -070022import android.animation.TimeInterpolator;
23import android.animation.ValueAnimator;
24import android.animation.ValueAnimator.AnimatorUpdateListener;
Dianne Hackborn8f573952009-08-10 23:21:09 -070025import android.app.WallpaperManager;
Michael Jurkabed61d22012-02-14 22:51:29 -080026import android.appwidget.AppWidgetHostView;
Romain Guy629de3e2010-01-13 12:20:59 -080027import android.appwidget.AppWidgetManager;
28import android.appwidget.AppWidgetProviderInfo;
Adam Powell495f2892010-04-16 16:40:55 -070029import android.content.ComponentName;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080030import android.content.Context;
31import android.content.Intent;
Winson Chung2efec4e2012-05-03 12:31:48 -070032import android.content.SharedPreferences;
Patrick Dubroy7247f632010-08-04 16:02:59 -070033import android.content.res.Resources;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080034import android.content.res.TypedArray;
Joe Onorato4be866d2010-10-10 11:26:02 -070035import android.graphics.Bitmap;
Adam Lesinski6b879f02010-11-04 16:15:23 -070036import android.graphics.Camera;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080037import android.graphics.Canvas;
Michael Jurkaa63c4522010-08-19 13:52:27 -070038import android.graphics.Matrix;
Winson Chunga9abd0e2010-10-27 17:18:37 -070039import android.graphics.Paint;
Winson Chungb8c69f32011-10-19 21:36:08 -070040import android.graphics.Point;
Winson Chung043f2af2012-03-01 16:09:54 -080041import android.graphics.PointF;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080042import android.graphics.Rect;
Joe Onorato4be866d2010-10-10 11:26:02 -070043import android.graphics.Region.Op;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070044import android.graphics.drawable.Drawable;
Joe Onorato956091b2010-02-19 12:47:40 -080045import android.os.IBinder;
Adam Powell495f2892010-04-16 16:40:55 -070046import android.os.Parcelable;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080047import android.util.AttributeSet;
Winson Chungb26f3d62011-06-02 10:49:29 -070048import android.util.DisplayMetrics;
Daniel Sandler291ad122010-05-24 16:03:53 -040049import android.util.Log;
Winson Chunga34abf82010-11-12 12:10:35 -080050import android.view.Display;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080051import android.view.MotionEvent;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080052import android.view.View;
Winson Chung6e314082011-01-27 16:46:51 -080053import android.view.ViewGroup;
Patrick Dubroycd68ff52010-10-28 17:57:05 -070054import android.view.animation.DecelerateInterpolator;
Winson Chunga6427b12011-07-27 10:53:39 -070055import android.widget.ImageView;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070056import android.widget.TextView;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080057
Adam Cohen66396872011-04-15 17:50:36 -070058import com.android.launcher.R;
Adam Cohen19072da2011-05-31 14:30:45 -070059import com.android.launcher2.FolderIcon.FolderRingAnimator;
Adam Cohen482ed822012-03-02 14:15:13 -080060import com.android.launcher2.LauncherSettings.Favorites;
Romain Guyedcce092010-03-04 13:03:17 -080061
Winson Chung2efec4e2012-05-03 12:31:48 -070062import java.net.URISyntaxException;
Adam Cohen716b51e2011-06-30 12:09:54 -070063import java.util.ArrayList;
64import java.util.HashSet;
Winson Chung2efec4e2012-05-03 12:31:48 -070065import java.util.Iterator;
66import java.util.Set;
Adam Cohen716b51e2011-06-30 12:09:54 -070067
The Android Open Source Project31dd5032009-03-03 19:32:27 -080068/**
Michael Jurka0142d492010-08-25 17:46:15 -070069 * The workspace is a wide area with a wallpaper and a finite number of pages.
70 * Each page contains a number of icons, folders or widgets the user can
Winson Chungaafa03c2010-06-11 17:34:16 -070071 * interact with. A workspace is meant to be used with a fixed width only.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080072 */
Michael Jurka0142d492010-08-25 17:46:15 -070073public class Workspace extends SmoothPagedView
Michael Jurkad74c9842011-07-10 12:44:21 -070074 implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
Michael Jurka8b805b12012-04-18 14:23:14 -070075 DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener {
Joe Onorato3a8820b2009-11-10 15:06:42 -080076 private static final String TAG = "Launcher.Workspace";
Michael Jurka0142d492010-08-25 17:46:15 -070077
Adam Cohenf34bab52010-09-30 14:11:56 -070078 // Y rotation to apply to the workspace screens
Adam Cohenb5ba0972011-09-07 18:02:31 -070079 private static final float WORKSPACE_OVERSCROLL_ROTATION = 24f;
Adam Cohencff6af82011-09-13 14:51:53 -070080 private static float CAMERA_DISTANCE = 6500;
Adam Cohena985e592010-09-09 11:23:48 -070081
Adam Cohen68d73932010-11-15 10:50:58 -080082 private static final int CHILDREN_OUTLINE_FADE_OUT_DELAY = 0;
83 private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375;
Winson Chung9171e6d2010-11-17 17:39:27 -080084 private static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100;
Adam Cohenf34bab52010-09-30 14:11:56 -070085
Winson Chungf135c6c2010-11-18 16:32:08 -080086 private static final int BACKGROUND_FADE_OUT_DURATION = 350;
Adam Cohened51cc92011-08-01 20:28:08 -070087 private static final int ADJACENT_SCREEN_DROP_DURATION = 300;
Adam Cohen265b9a62011-12-07 14:37:18 -080088 private static final int FLING_THRESHOLD_VELOCITY = 500;
Adam Cohened51cc92011-08-01 20:28:08 -070089
Winson Chung9171e6d2010-11-17 17:39:27 -080090 // These animators are used to fade the children's outlines
91 private ObjectAnimator mChildrenOutlineFadeInAnimation;
92 private ObjectAnimator mChildrenOutlineFadeOutAnimation;
93 private float mChildrenOutlineAlpha = 0;
94
95 // These properties refer to the background protection gradient used for AllApps and Customize
Michael Jurkae0f5a612011-02-07 16:45:41 -080096 private ValueAnimator mBackgroundFadeInAnimation;
97 private ValueAnimator mBackgroundFadeOutAnimation;
Winson Chung9171e6d2010-11-17 17:39:27 -080098 private Drawable mBackground;
Michael Jurka25356e72011-03-03 14:53:11 -080099 boolean mDrawBackground = true;
Adam Cohenf34bab52010-09-30 14:11:56 -0700100 private float mBackgroundAlpha = 0;
Adam Cohen68d73932010-11-15 10:50:58 -0800101 private float mOverScrollMaxBackgroundAlpha = 0.0f;
Adam Cohenf34bab52010-09-30 14:11:56 -0700102
Adam Cohenbeff8c62011-08-31 17:46:01 -0700103 private float mWallpaperScrollRatio = 1.0f;
104
Dianne Hackborn8f573952009-08-10 23:21:09 -0700105 private final WallpaperManager mWallpaperManager;
Michael Jurka9c6fbed2011-03-02 17:41:34 -0800106 private IBinder mWindowToken;
Michael Jurka16706982011-09-26 16:39:22 -0700107 private static final float WALLPAPER_SCREENS_SPAN = 2f;
Winson Chungaafa03c2010-06-11 17:34:16 -0700108
Michael Jurka0142d492010-08-25 17:46:15 -0700109 private int mDefaultPage;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800110
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800111 /**
112 * CellInfo for the cell that is currently being dragged
113 */
114 private CellLayout.CellInfo mDragInfo;
Winson Chungaafa03c2010-06-11 17:34:16 -0700115
Jeff Sharkey70864282009-04-07 21:08:40 -0700116 /**
117 * Target drop area calculated during last acceptDrop call.
118 */
Adam Cohenc0dcf592011-06-01 15:30:43 -0700119 private int[] mTargetCell = new int[2];
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700120 private int mDragOverX = -1;
121 private int mDragOverY = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800122
Adam Cohena897f392012-04-27 18:12:05 -0700123 static Rect mLandscapeCellLayoutMetrics = null;
124 static Rect mPortraitCellLayoutMetrics = null;
125
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700126 /**
127 * The CellLayout that is currently being dragged over
128 */
129 private CellLayout mDragTargetLayout = null;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700130 /**
131 * The CellLayout that we will show as glowing
132 */
133 private CellLayout mDragOverlappingLayout = null;
134
135 /**
136 * The CellLayout which will be dropped to
137 */
138 private CellLayout mDropToLayout = null;
139
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800140 private Launcher mLauncher;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800141 private IconCache mIconCache;
Joe Onorato00acb122009-08-04 16:04:30 -0400142 private DragController mDragController;
Winson Chungaafa03c2010-06-11 17:34:16 -0700143
Michael Jurka4516c112010-10-07 15:13:47 -0700144 // These are temporary variables to prevent having to allocate a new object just to
145 // 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 -0800146 private int[] mTempCell = new int[2];
Jeff Sharkey70864282009-04-07 21:08:40 -0700147 private int[] mTempEstimate = new int[2];
Adam Cohene3e27a82011-04-15 12:07:39 -0700148 private float[] mDragViewVisualCenter = new float[2];
Michael Jurkaa63c4522010-08-19 13:52:27 -0700149 private float[] mTempDragCoordinates = new float[2];
Michael Jurka4516c112010-10-07 15:13:47 -0700150 private float[] mTempCellLayoutCenterCoordinates = new float[2];
Michael Jurkaa63c4522010-08-19 13:52:27 -0700151 private float[] mTempDragBottomRightCoordinates = new float[2];
Michael Jurka0280c3b2010-09-17 15:00:07 -0700152 private Matrix mTempInverseMatrix = new Matrix();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800153
Michael Jurkac2f7f472010-12-14 15:34:42 -0800154 private SpringLoadedDragController mSpringLoadedDragController;
Winson Chungb26f3d62011-06-02 10:49:29 -0700155 private float mSpringLoadedShrinkFactor;
Michael Jurkad3ef3062010-11-23 16:23:58 -0800156
Adam Cohend22015c2010-07-26 22:02:18 -0700157 private static final int DEFAULT_CELL_COUNT_X = 4;
158 private static final int DEFAULT_CELL_COUNT_Y = 4;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800159
Patrick Dubroy1262e362010-10-06 15:49:50 -0700160 // State variable that indicates whether the pages are small (ie when you're
Michael Jurkadee05892010-07-27 10:01:56 -0700161 // in all apps or customize mode)
Michael Jurkad74c9842011-07-10 12:44:21 -0700162
163 enum State { NORMAL, SPRING_LOADED, SMALL };
Adam Cohen7777d962011-08-18 18:58:38 -0700164 private State mState = State.NORMAL;
Michael Jurkad74c9842011-07-10 12:44:21 -0700165 private boolean mIsSwitchingState = false;
Michael Jurkad74c9842011-07-10 12:44:21 -0700166
Michael Jurkad74c9842011-07-10 12:44:21 -0700167 boolean mAnimatingViewIntoPlace = false;
168 boolean mIsDragOccuring = false;
169 boolean mChildrenLayersEnabled = true;
Michael Jurkadee05892010-07-27 10:01:56 -0700170
Patrick Dubroy54fa3b92010-11-17 12:18:45 -0800171 /** Is the user is dragging an item near the edge of a page? */
Patrick Dubroy1262e362010-10-06 15:49:50 -0700172 private boolean mInScrollArea = false;
173
Patrick Dubroy8e58e912010-10-14 13:21:48 -0700174 private final HolographicOutlineHelper mOutlineHelper = new HolographicOutlineHelper();
Joe Onorato4be866d2010-10-10 11:26:02 -0700175 private Bitmap mDragOutline = null;
Patrick Dubroy8e58e912010-10-14 13:21:48 -0700176 private final Rect mTempRect = new Rect();
177 private final int[] mTempXY = new int[2];
Adam Cohen21b41102011-11-01 17:29:52 -0700178 private float mOverscrollFade = 0;
Michael Jurkab06d95f2012-04-02 06:26:53 -0700179 private boolean mOverscrollTransformsSet;
Michael Jurkaf8304f02012-04-26 13:33:26 -0700180 public static final int DRAG_BITMAP_PADDING = 2;
Michael Jurka869390b2012-05-06 15:55:19 -0700181 private boolean mWorkspaceFadeInAdjacentScreens;
Joe Onorato4be866d2010-10-10 11:26:02 -0700182
Adam Lesinski6b879f02010-11-04 16:15:23 -0700183 // Camera and Matrix used to determine the final position of a neighboring CellLayout
184 private final Matrix mMatrix = new Matrix();
185 private final Camera mCamera = new Camera();
186 private final float mTempFloat2[] = new float[2];
187
Michael Jurkac5b262c2011-01-12 20:24:50 -0800188 enum WallpaperVerticalOffset { TOP, MIDDLE, BOTTOM };
189 int mWallpaperWidth;
190 int mWallpaperHeight;
Michael Jurkaab1983f2011-01-18 15:50:17 -0800191 WallpaperOffsetInterpolator mWallpaperOffset;
Michael Jurkac5b262c2011-01-12 20:24:50 -0800192 boolean mUpdateWallpaperOffsetImmediately = false;
Adam Cohen26976d92011-03-22 15:33:33 -0700193 private Runnable mDelayedResizeRunnable;
Winson Chungf0c6ae02012-03-21 16:10:31 -0700194 private Runnable mDelayedSnapToPageRunnable;
Michael Jurka84f2ce72012-04-13 15:08:01 -0700195 private Point mDisplaySize = new Point();
Michael Jurka95515372012-02-29 14:13:01 -0800196 private boolean mIsStaticWallpaper;
Adam Cohencff6af82011-09-13 14:51:53 -0700197 private int mWallpaperTravelWidth;
Winson Chung8aad6102012-05-11 16:27:49 -0700198 private int mSpringLoadedPageSpacing;
Michael Jurkac5b262c2011-01-12 20:24:50 -0800199
Adam Cohen19072da2011-05-31 14:30:45 -0700200 // Variables relating to the creation of user folders by hovering shortcuts over shortcuts
Adam Cohen482ed822012-03-02 14:15:13 -0800201 private static final int FOLDER_CREATION_TIMEOUT = 0;
202 private static final int REORDER_TIMEOUT = 250;
Adam Cohen19072da2011-05-31 14:30:45 -0700203 private final Alarm mFolderCreationAlarm = new Alarm();
Adam Cohen482ed822012-03-02 14:15:13 -0800204 private final Alarm mReorderAlarm = new Alarm();
Adam Cohen19072da2011-05-31 14:30:45 -0700205 private FolderRingAnimator mDragFolderRingAnimator = null;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700206 private FolderIcon mDragOverFolderIcon = null;
Adam Cohen19072da2011-05-31 14:30:45 -0700207 private boolean mCreateUserFolderOnDrop = false;
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700208 private boolean mAddToExistingFolderOnDrop = false;
209 private DropTarget.DragEnforcer mDragEnforcer;
Adam Cohen3aff81c2012-05-16 21:01:01 -0700210 private float mMaxDistanceForFolderCreation;
Adam Cohen073a46f2011-05-17 16:28:09 -0700211
Adam Cohenf8d28232011-02-01 21:47:00 -0800212 // Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget)
213 private float mXDown;
214 private float mYDown;
215 final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
216 final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
217 final static float TOUCH_SLOP_DAMPING_FACTOR = 4;
218
Adam Cohened66b2b2012-01-23 17:28:51 -0800219 // Relating to the animation of items being dropped externally
Adam Cohend41fbf52012-02-16 23:53:59 -0800220 public static final int ANIMATE_INTO_POSITION_AND_DISAPPEAR = 0;
221 public static final int ANIMATE_INTO_POSITION_AND_REMAIN = 1;
222 public static final int ANIMATE_INTO_POSITION_AND_RESIZE = 2;
223 public static final int COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION = 3;
224 public static final int CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION = 4;
Adam Cohened66b2b2012-01-23 17:28:51 -0800225
Adam Cohen482ed822012-03-02 14:15:13 -0800226 // Related to dragging, folder creation and reordering
227 private static final int DRAG_MODE_NONE = 0;
228 private static final int DRAG_MODE_CREATE_FOLDER = 1;
229 private static final int DRAG_MODE_ADD_TO_FOLDER = 2;
230 private static final int DRAG_MODE_REORDER = 3;
231 private int mDragMode = DRAG_MODE_NONE;
232 private int mLastReorderX = -1;
233 private int mLastReorderY = -1;
234
Adam Cohen4b285c52011-07-21 14:24:06 -0700235 // These variables are used for storing the initial and final values during workspace animations
Adam Cohened51cc92011-08-01 20:28:08 -0700236 private int mSavedScrollX;
237 private float mSavedRotationY;
238 private float mSavedTranslationX;
Adam Cohen4b285c52011-07-21 14:24:06 -0700239 private float mCurrentScaleX;
240 private float mCurrentScaleY;
241 private float mCurrentRotationY;
242 private float mCurrentTranslationX;
243 private float mCurrentTranslationY;
244 private float[] mOldTranslationXs;
245 private float[] mOldTranslationYs;
246 private float[] mOldScaleXs;
247 private float[] mOldScaleYs;
248 private float[] mOldBackgroundAlphas;
Adam Cohen4b285c52011-07-21 14:24:06 -0700249 private float[] mOldAlphas;
Adam Cohen4b285c52011-07-21 14:24:06 -0700250 private float[] mNewTranslationXs;
251 private float[] mNewTranslationYs;
252 private float[] mNewScaleXs;
253 private float[] mNewScaleYs;
254 private float[] mNewBackgroundAlphas;
Adam Cohen4b285c52011-07-21 14:24:06 -0700255 private float[] mNewAlphas;
256 private float[] mNewRotationYs;
Winson Chung70442722012-02-10 15:43:22 -0800257 private float mTransitionProgress;
Adam Cohen4b285c52011-07-21 14:24:06 -0700258
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800259 /**
260 * Used to inflate the Workspace from XML.
261 *
262 * @param context The application's context.
Michael Jurka0142d492010-08-25 17:46:15 -0700263 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800264 */
265 public Workspace(Context context, AttributeSet attrs) {
266 this(context, attrs, 0);
267 }
268
269 /**
270 * Used to inflate the Workspace from XML.
271 *
272 * @param context The application's context.
Michael Jurka0142d492010-08-25 17:46:15 -0700273 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800274 * @param defStyle Unused.
275 */
276 public Workspace(Context context, AttributeSet attrs, int defStyle) {
277 super(context, attrs, defStyle);
Michael Jurka0142d492010-08-25 17:46:15 -0700278 mContentIsRefreshable = false;
Michael Jurka5f1c5092010-09-03 14:15:02 -0700279
Adam Cohenc6cc61d2012-04-04 12:47:08 -0700280 mDragEnforcer = new DropTarget.DragEnforcer(context);
Winson Chungf0ea4d32011-06-06 14:27:16 -0700281 // With workspace, data is available straight from the get-go
282 setDataIsReady();
283
Winson Chung867ca622012-02-21 15:48:35 -0800284 final Resources res = getResources();
Michael Jurka869390b2012-05-06 15:55:19 -0700285 mWorkspaceFadeInAdjacentScreens = res.getBoolean(R.bool.config_workspaceFadeAdjacentScreens);
286 mFadeInAdjacentScreens = false;
Dianne Hackborn8f573952009-08-10 23:21:09 -0700287 mWallpaperManager = WallpaperManager.getInstance(context);
Winson Chungaafa03c2010-06-11 17:34:16 -0700288
Michael Jurkaf6440da2011-04-05 14:50:34 -0700289 int cellCountX = DEFAULT_CELL_COUNT_X;
290 int cellCountY = DEFAULT_CELL_COUNT_Y;
291
Winson Chungaafa03c2010-06-11 17:34:16 -0700292 TypedArray a = context.obtainStyledAttributes(attrs,
293 R.styleable.Workspace, defStyle, 0);
Michael Jurkaf6440da2011-04-05 14:50:34 -0700294
295 if (LauncherApplication.isScreenLarge()) {
Michael Jurkaf61249b2011-05-27 16:27:15 -0700296 // Determine number of rows/columns dynamically
297 // TODO: This code currently fails on tablets with an aspect ratio < 1.3.
298 // Around that ratio we should make cells the same size in portrait and
299 // landscape
Michael Jurkaf6440da2011-04-05 14:50:34 -0700300 TypedArray actionBarSizeTypedArray =
301 context.obtainStyledAttributes(new int[] { android.R.attr.actionBarSize });
Winson Chunga61dc422012-02-14 14:45:18 -0800302 DisplayMetrics displayMetrics = res.getDisplayMetrics();
Michael Jurkaf61249b2011-05-27 16:27:15 -0700303 final float actionBarHeight = actionBarSizeTypedArray.getDimension(0, 0f);
304 final float systemBarHeight = res.getDimension(R.dimen.status_bar_height);
Winson Chunga61dc422012-02-14 14:45:18 -0800305 final float smallestScreenDim = res.getConfiguration().smallestScreenWidthDp *
306 displayMetrics.density;
Michael Jurkaf6440da2011-04-05 14:50:34 -0700307
Michael Jurkaf61249b2011-05-27 16:27:15 -0700308 cellCountX = 1;
309 while (CellLayout.widthInPortrait(res, cellCountX + 1) <= smallestScreenDim) {
310 cellCountX++;
Michael Jurkaf6440da2011-04-05 14:50:34 -0700311 }
Michael Jurkaf6440da2011-04-05 14:50:34 -0700312
Michael Jurkaf61249b2011-05-27 16:27:15 -0700313 cellCountY = 1;
314 while (actionBarHeight + CellLayout.heightInLandscape(res, cellCountY + 1)
315 <= smallestScreenDim - systemBarHeight) {
316 cellCountY++;
Michael Jurkaf6440da2011-04-05 14:50:34 -0700317 }
Michael Jurkaf6440da2011-04-05 14:50:34 -0700318 }
319
Winson Chungb26f3d62011-06-02 10:49:29 -0700320 mSpringLoadedShrinkFactor =
321 res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
Winson Chung8aad6102012-05-11 16:27:49 -0700322 mSpringLoadedPageSpacing =
323 res.getDimensionPixelSize(R.dimen.workspace_spring_loaded_page_spacing);
Winson Chungb26f3d62011-06-02 10:49:29 -0700324
Michael Jurkaf6440da2011-04-05 14:50:34 -0700325 // if the value is manually specified, use that instead
326 cellCountX = a.getInt(R.styleable.Workspace_cellCountX, cellCountX);
327 cellCountY = a.getInt(R.styleable.Workspace_cellCountY, cellCountY);
Michael Jurka0142d492010-08-25 17:46:15 -0700328 mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800329 a.recycle();
330
Michael Jurka8b805b12012-04-18 14:23:14 -0700331 setOnHierarchyChangeListener(this);
332
Adam Cohend22015c2010-07-26 22:02:18 -0700333 LauncherModel.updateWorkspaceLayoutCells(cellCountX, cellCountY);
Joe Onorato0d44e942009-11-16 18:20:51 -0800334 setHapticFeedbackEnabled(false);
Michael Jurka0142d492010-08-25 17:46:15 -0700335
Adam Cohencff6af82011-09-13 14:51:53 -0700336 mLauncher = (Launcher) context;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800337 initWorkspace();
Winson Chungc35afb22011-02-23 13:01:49 -0800338
339 // Disable multitouch across the workspace/all apps/customize tray
340 setMotionEventSplittingEnabled(true);
Svetoslav Ganov08055f62012-05-15 11:06:36 -0700341
342 // Unless otherwise specified this view is important for accessibility.
343 if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
344 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
345 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800346 }
347
Michael Jurka038f9d82011-11-03 13:50:45 -0700348 // estimate the size of a widget with spans hSpan, vSpan. return MAX_VALUE for each
349 // dimension if unsuccessful
350 public int[] estimateItemSize(int hSpan, int vSpan,
Adam Cohend41fbf52012-02-16 23:53:59 -0800351 ItemInfo itemInfo, boolean springLoaded) {
Michael Jurka038f9d82011-11-03 13:50:45 -0700352 int[] size = new int[2];
353 if (getChildCount() > 0) {
354 CellLayout cl = (CellLayout) mLauncher.getWorkspace().getChildAt(0);
Adam Cohend41fbf52012-02-16 23:53:59 -0800355 Rect r = estimateItemPosition(cl, itemInfo, 0, 0, hSpan, vSpan);
356 size[0] = r.width();
357 size[1] = r.height();
Michael Jurka038f9d82011-11-03 13:50:45 -0700358 if (springLoaded) {
359 size[0] *= mSpringLoadedShrinkFactor;
360 size[1] *= mSpringLoadedShrinkFactor;
361 }
362 return size;
363 } else {
364 size[0] = Integer.MAX_VALUE;
365 size[1] = Integer.MAX_VALUE;
366 return size;
367 }
368 }
Adam Cohend41fbf52012-02-16 23:53:59 -0800369 public Rect estimateItemPosition(CellLayout cl, ItemInfo pendingInfo,
Michael Jurka038f9d82011-11-03 13:50:45 -0700370 int hCell, int vCell, int hSpan, int vSpan) {
Adam Cohend41fbf52012-02-16 23:53:59 -0800371 Rect r = new Rect();
Michael Jurka038f9d82011-11-03 13:50:45 -0700372 cl.cellToRect(hCell, vCell, hSpan, vSpan, r);
Michael Jurka038f9d82011-11-03 13:50:45 -0700373 return r;
374 }
375
Michael Jurkac1f5d262011-09-30 19:32:27 -0700376 public void buildPageHardwareLayers() {
377 if (getWindowToken() != null) {
378 final int childCount = getChildCount();
379 for (int i = 0; i < childCount; i++) {
380 CellLayout cl = (CellLayout) getChildAt(i);
Michael Jurkaa52570f2012-03-20 03:18:20 -0700381 cl.getShortcutsAndWidgets().buildLayer();
Michael Jurkac1f5d262011-09-30 19:32:27 -0700382 }
383 }
384 }
385
Michael Jurkad74c9842011-07-10 12:44:21 -0700386 public void onDragStart(DragSource source, Object info, int dragAction) {
387 mIsDragOccuring = true;
388 updateChildrenLayersEnabled();
Winson Chung641d71d2012-04-26 15:58:01 -0700389 mLauncher.lockScreenOrientation();
Michael Jurkaa3d30ad2012-05-08 13:43:43 -0700390 setChildrenBackgroundAlphaMultipliers(1f);
Winson Chungf561bdf2012-05-03 11:20:19 -0700391 // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging
392 InstallShortcutReceiver.enableInstallQueue();
393 UninstallShortcutReceiver.enableUninstallQueue();
Michael Jurkad74c9842011-07-10 12:44:21 -0700394 }
395
396 public void onDragEnd() {
397 mIsDragOccuring = false;
398 updateChildrenLayersEnabled();
Winson Chung4b919f82012-05-01 10:44:08 -0700399 mLauncher.unlockScreenOrientation(false);
Winson Chungf561bdf2012-05-03 11:20:19 -0700400
401 // Re-enable any Un/InstallShortcutReceiver and now process any queued items
402 InstallShortcutReceiver.disableAndFlushInstallQueue(getContext());
403 UninstallShortcutReceiver.disableAndFlushUninstallQueue(getContext());
Michael Jurkad74c9842011-07-10 12:44:21 -0700404 }
405
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800406 /**
407 * Initializes various states for this workspace.
408 */
Michael Jurka0142d492010-08-25 17:46:15 -0700409 protected void initWorkspace() {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800410 Context context = getContext();
Michael Jurka0142d492010-08-25 17:46:15 -0700411 mCurrentPage = mDefaultPage;
412 Launcher.setScreen(mCurrentPage);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800413 LauncherApplication app = (LauncherApplication)context.getApplicationContext();
414 mIconCache = app.getIconCache();
Patrick Dubroycd68ff52010-10-28 17:57:05 -0700415 setWillNotDraw(false);
Adam Cohen7777d962011-08-18 18:58:38 -0700416 setChildrenDrawnWithCacheEnabled(true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800417
Adam Cohen482ed822012-03-02 14:15:13 -0800418 final Resources res = getResources();
Winson Chungb4b7fa72010-11-18 14:38:53 -0800419 try {
Winson Chungfd3385f2011-06-15 19:51:24 -0700420 mBackground = res.getDrawable(R.drawable.apps_customize_bg);
Winson Chungb4b7fa72010-11-18 14:38:53 -0800421 } catch (Resources.NotFoundException e) {
422 // In this case, we will skip drawing background protection
423 }
Winson Chung9171e6d2010-11-17 17:39:27 -0800424
Michael Jurkaab1983f2011-01-18 15:50:17 -0800425 mWallpaperOffset = new WallpaperOffsetInterpolator();
Adam Cohencff6af82011-09-13 14:51:53 -0700426 Display display = mLauncher.getWindowManager().getDefaultDisplay();
Michael Jurka84f2ce72012-04-13 15:08:01 -0700427 display.getSize(mDisplaySize);
428 mWallpaperTravelWidth = (int) (mDisplaySize.x *
429 wallpaperTravelToScreenWidthRatio(mDisplaySize.x, mDisplaySize.y));
Adam Cohen265b9a62011-12-07 14:37:18 -0800430
Adam Cohen3aff81c2012-05-16 21:01:01 -0700431 mMaxDistanceForFolderCreation = (0.55f * res.getDimensionPixelSize(R.dimen.app_icon_size));
Adam Cohen265b9a62011-12-07 14:37:18 -0800432 mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800433 }
434
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800435 @Override
Adam Cohenf34bab52010-09-30 14:11:56 -0700436 protected int getScrollMode() {
Winson Chungb26f3d62011-06-02 10:49:29 -0700437 return SmoothPagedView.X_LARGE_MODE;
Adam Cohenf34bab52010-09-30 14:11:56 -0700438 }
439
Michael Jurka08ee7702011-08-11 16:53:35 -0700440 @Override
Michael Jurka8b805b12012-04-18 14:23:14 -0700441 public void onChildViewAdded(View parent, View child) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800442 if (!(child instanceof CellLayout)) {
443 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
444 }
Adam Cohen2801caf2011-05-13 20:57:39 -0700445 CellLayout cl = ((CellLayout) child);
446 cl.setOnInterceptTouchListener(this);
Adam Cohen2801caf2011-05-13 20:57:39 -0700447 cl.setClickable(true);
448 cl.enableHardwareLayers();
alanv51944f12012-05-07 17:31:32 -0700449 cl.setContentDescription(getContext().getString(
450 R.string.workspace_description_format, getChildCount()));
Michael Jurkae5fb0f22011-04-11 13:27:46 -0700451 }
Adam Cohen2801caf2011-05-13 20:57:39 -0700452
Michael Jurka8b805b12012-04-18 14:23:14 -0700453 @Override
454 public void onChildViewRemoved(View parent, View child) {
455 }
456
Michael Jurka920d7f42012-05-14 16:29:55 -0700457 protected boolean shouldDrawChild(View child) {
458 final CellLayout cl = (CellLayout) child;
459 return super.shouldDrawChild(child) &&
460 (cl.getShortcutsAndWidgets().getAlpha() > 0 ||
461 cl.getBackgroundAlpha() > 0);
462 }
463
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800464 /**
465 * @return The open folder on the current screen, or null if there is none
466 */
467 Folder getOpenFolder() {
Adam Cohen716b51e2011-06-30 12:09:54 -0700468 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohen8e776a62011-06-28 18:10:06 -0700469 int count = dragLayer.getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800470 for (int i = 0; i < count; i++) {
Adam Cohen8e776a62011-06-28 18:10:06 -0700471 View child = dragLayer.getChildAt(i);
Winson Chungaafa03c2010-06-11 17:34:16 -0700472 if (child instanceof Folder) {
473 Folder folder = (Folder) child;
474 if (folder.getInfo().opened)
475 return folder;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800476 }
477 }
478 return null;
479 }
480
Patrick Dubroya0aa0122011-02-24 11:42:23 -0800481 boolean isTouchActive() {
482 return mTouchState != TOUCH_STATE_REST;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800483 }
484
485 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800486 * Adds the specified child in the specified screen. The position and dimension of
487 * the child are defined by x, y, spanX and spanY.
488 *
489 * @param child The child to add in one of the workspace's screens.
490 * @param screen The screen in which to add the child.
491 * @param x The X position of the child in the screen's grid.
492 * @param y The Y position of the child in the screen's grid.
493 * @param spanX The number of cells spanned horizontally by the child.
494 * @param spanY The number of cells spanned vertically by the child.
495 */
Winson Chung3d503fb2011-07-13 17:25:49 -0700496 void addInScreen(View child, long container, int screen, int x, int y, int spanX, int spanY) {
497 addInScreen(child, container, screen, x, y, spanX, spanY, false);
Winson Chungaafa03c2010-06-11 17:34:16 -0700498 }
499
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800500 /**
501 * Adds the specified child in the specified screen. The position and dimension of
502 * the child are defined by x, y, spanX and spanY.
503 *
504 * @param child The child to add in one of the workspace's screens.
505 * @param screen The screen in which to add the child.
506 * @param x The X position of the child in the screen's grid.
507 * @param y The Y position of the child in the screen's grid.
508 * @param spanX The number of cells spanned horizontally by the child.
509 * @param spanY The number of cells spanned vertically by the child.
510 * @param insert When true, the child is inserted at the beginning of the children list.
511 */
Winson Chung3d503fb2011-07-13 17:25:49 -0700512 void addInScreen(View child, long container, int screen, int x, int y, int spanX, int spanY,
513 boolean insert) {
514 if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
515 if (screen < 0 || screen >= getChildCount()) {
516 Log.e(TAG, "The screen must be >= 0 and < " + getChildCount()
517 + " (was " + screen + "); skipping child");
518 return;
519 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800520 }
521
Winson Chung3d503fb2011-07-13 17:25:49 -0700522 final CellLayout layout;
523 if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
524 layout = mLauncher.getHotseat().getLayout();
Winson Chung4d279d92011-07-21 11:46:32 -0700525 child.setOnKeyListener(null);
Winson Chung3d503fb2011-07-13 17:25:49 -0700526
Adam Cohen099f60d2011-08-23 21:07:26 -0700527 // Hide folder title in the hotseat
528 if (child instanceof FolderIcon) {
529 ((FolderIcon) child).setTextVisible(false);
530 }
531
Winson Chung3d503fb2011-07-13 17:25:49 -0700532 if (screen < 0) {
533 screen = mLauncher.getHotseat().getOrderInHotseat(x, y);
534 } else {
535 // Note: We do this to ensure that the hotseat is always laid out in the orientation
536 // of the hotseat in order regardless of which orientation they were added
537 x = mLauncher.getHotseat().getCellXFromOrder(screen);
538 y = mLauncher.getHotseat().getCellYFromOrder(screen);
539 }
540 } else {
Adam Cohen099f60d2011-08-23 21:07:26 -0700541 // Show folder title if not in the hotseat
542 if (child instanceof FolderIcon) {
543 ((FolderIcon) child).setTextVisible(true);
544 }
545
Winson Chung3d503fb2011-07-13 17:25:49 -0700546 layout = (CellLayout) getChildAt(screen);
Adam Cohenac56cff2011-09-28 20:45:37 -0700547 child.setOnKeyListener(new IconKeyEventListener());
Winson Chung3d503fb2011-07-13 17:25:49 -0700548 }
549
Adam Cohened66b2b2012-01-23 17:28:51 -0800550 LayoutParams genericLp = child.getLayoutParams();
551 CellLayout.LayoutParams lp;
552 if (genericLp == null || !(genericLp instanceof CellLayout.LayoutParams)) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800553 lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
554 } else {
Adam Cohened66b2b2012-01-23 17:28:51 -0800555 lp = (CellLayout.LayoutParams) genericLp;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800556 lp.cellX = x;
557 lp.cellY = y;
558 lp.cellHSpan = spanX;
559 lp.cellVSpan = spanY;
560 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700561
Adam Cohen7f4eabe2011-04-21 16:19:16 -0700562 if (spanX < 0 && spanY < 0) {
563 lp.isLockedToGrid = false;
564 }
565
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700566 // Get the canonical child id to uniquely represent this view in this screen
Winson Chung3d503fb2011-07-13 17:25:49 -0700567 int childId = LauncherModel.getCellLayoutChildId(container, screen, x, y, spanX, spanY);
Michael Jurkaf3ca3ab2010-10-20 17:08:24 -0700568 boolean markCellsAsOccupied = !(child instanceof Folder);
Winson Chung3d503fb2011-07-13 17:25:49 -0700569 if (!layout.addViewToCellLayout(child, insert ? 0 : -1, childId, lp, markCellsAsOccupied)) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700570 // TODO: This branch occurs when the workspace is adding views
571 // outside of the defined grid
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700572 // maybe we should be deleting these items from the LauncherModel?
Winson Chungaafa03c2010-06-11 17:34:16 -0700573 Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
574 }
575
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800576 if (!(child instanceof Folder)) {
Joe Onorato0d44e942009-11-16 18:20:51 -0800577 child.setHapticFeedbackEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800578 child.setOnLongClickListener(mLongClickListener);
579 }
Joe Onorato00acb122009-08-04 16:04:30 -0400580 if (child instanceof DropTarget) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700581 mDragController.addDropTarget((DropTarget) child);
Joe Onorato00acb122009-08-04 16:04:30 -0400582 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800583 }
584
Patrick Dubroyd0ce1ec2011-01-19 18:47:27 -0800585 /**
586 * Check if the point (x, y) hits a given page.
587 */
588 private boolean hitsPage(int index, float x, float y) {
589 final View page = getChildAt(index);
590 if (page != null) {
591 float[] localXY = { x, y };
592 mapPointFromSelfToChild(page, localXY);
593 return (localXY[0] >= 0 && localXY[0] < page.getWidth()
594 && localXY[1] >= 0 && localXY[1] < page.getHeight());
595 }
596 return false;
597 }
598
599 @Override
600 protected boolean hitsPreviousPage(float x, float y) {
601 // mNextPage is set to INVALID_PAGE whenever we are stationary.
602 // Calculating "next page" this way ensures that you scroll to whatever page you tap on
603 final int current = (mNextPage == INVALID_PAGE) ? mCurrentPage : mNextPage;
Michael Jurkab72094f2011-09-14 14:53:19 -0700604
605 // Only allow tap to next page on large devices, where there's significant margin outside
606 // the active workspace
607 return LauncherApplication.isScreenLarge() && hitsPage(current - 1, x, y);
Patrick Dubroyd0ce1ec2011-01-19 18:47:27 -0800608 }
609
610 @Override
611 protected boolean hitsNextPage(float x, float y) {
612 // mNextPage is set to INVALID_PAGE whenever we are stationary.
613 // Calculating "next page" this way ensures that you scroll to whatever page you tap on
614 final int current = (mNextPage == INVALID_PAGE) ? mCurrentPage : mNextPage;
Michael Jurkab72094f2011-09-14 14:53:19 -0700615
616 // Only allow tap to next page on large devices, where there's significant margin outside
617 // the active workspace
618 return LauncherApplication.isScreenLarge() && hitsPage(current + 1, x, y);
Patrick Dubroyd0ce1ec2011-01-19 18:47:27 -0800619 }
620
Patrick Dubroye708c522011-03-01 16:03:43 -0800621 /**
622 * Called directly from a CellLayout (not by the framework), after we've been added as a
623 * listener via setOnInterceptTouchEventListener(). This allows us to tell the CellLayout
624 * that it should intercept touch events, which is not something that is normally supported.
625 */
626 @Override
Michael Jurkadee05892010-07-27 10:01:56 -0700627 public boolean onTouch(View v, MotionEvent event) {
Winson Chung70442722012-02-10 15:43:22 -0800628 return (isSmall() || !isFinishedSwitchingState());
Patrick Dubroye708c522011-03-01 16:03:43 -0800629 }
630
Adam Cohenfc53cd22011-07-20 15:45:11 -0700631 public boolean isSwitchingState() {
632 return mIsSwitchingState;
633 }
634
Winson Chung70442722012-02-10 15:43:22 -0800635 /** This differs from isSwitchingState in that we take into account how far the transition
636 * has completed. */
Winson Chung9b0b2fe2012-02-24 13:03:34 -0800637 public boolean isFinishedSwitchingState() {
Winson Chung70442722012-02-10 15:43:22 -0800638 return !mIsSwitchingState || (mTransitionProgress > 0.5f);
639 }
640
Adam Cohended9f8d2010-11-03 13:25:16 -0700641 protected void onWindowVisibilityChanged (int visibility) {
642 mLauncher.onWindowVisibilityChanged(visibility);
643 }
644
Michael Jurka5f1c5092010-09-03 14:15:02 -0700645 @Override
646 public boolean dispatchUnhandledMove(View focused, int direction) {
Winson Chung70442722012-02-10 15:43:22 -0800647 if (isSmall() || !isFinishedSwitchingState()) {
Michael Jurka5f1c5092010-09-03 14:15:02 -0700648 // when the home screens are shrunken, shouldn't allow side-scrolling
649 return false;
650 }
651 return super.dispatchUnhandledMove(focused, direction);
652 }
653
654 @Override
655 public boolean onInterceptTouchEvent(MotionEvent ev) {
Michael Jurkad771c962011-08-09 15:00:48 -0700656 switch (ev.getAction() & MotionEvent.ACTION_MASK) {
657 case MotionEvent.ACTION_DOWN:
Adam Cohenf8d28232011-02-01 21:47:00 -0800658 mXDown = ev.getX();
659 mYDown = ev.getY();
Michael Jurkad771c962011-08-09 15:00:48 -0700660 break;
661 case MotionEvent.ACTION_POINTER_UP:
662 case MotionEvent.ACTION_UP:
663 if (mTouchState == TOUCH_STATE_REST) {
664 final CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage);
665 if (!currentPage.lastDownOnOccupiedCell()) {
666 onWallpaperTap(ev);
667 }
668 }
Adam Cohenf8d28232011-02-01 21:47:00 -0800669 }
Michael Jurka5f1c5092010-09-03 14:15:02 -0700670 return super.onInterceptTouchEvent(ev);
671 }
672
Michael Jurka1adf5392010-10-18 18:10:22 -0700673 @Override
674 protected void determineScrollingStart(MotionEvent ev) {
Winson Chung70442722012-02-10 15:43:22 -0800675 if (isSmall()) return;
676 if (!isFinishedSwitchingState()) return;
Adam Cohenf8d28232011-02-01 21:47:00 -0800677
Winson Chung70442722012-02-10 15:43:22 -0800678 float deltaX = Math.abs(ev.getX() - mXDown);
679 float deltaY = Math.abs(ev.getY() - mYDown);
Adam Cohenf8d28232011-02-01 21:47:00 -0800680
Winson Chung70442722012-02-10 15:43:22 -0800681 if (Float.compare(deltaX, 0f) == 0) return;
Adam Cohenf8d28232011-02-01 21:47:00 -0800682
Winson Chung70442722012-02-10 15:43:22 -0800683 float slope = deltaY / deltaX;
684 float theta = (float) Math.atan(slope);
Adam Cohenf8d28232011-02-01 21:47:00 -0800685
Winson Chung70442722012-02-10 15:43:22 -0800686 if (deltaX > mTouchSlop || deltaY > mTouchSlop) {
687 cancelCurrentPageLongPress();
688 }
689
690 if (theta > MAX_SWIPE_ANGLE) {
691 // Above MAX_SWIPE_ANGLE, we don't want to ever start scrolling the workspace
692 return;
693 } else if (theta > START_DAMPING_TOUCH_SLOP_ANGLE) {
694 // Above START_DAMPING_TOUCH_SLOP_ANGLE and below MAX_SWIPE_ANGLE, we want to
695 // increase the touch slop to make it harder to begin scrolling the workspace. This
696 // results in vertically scrolling widgets to more easily. The higher the angle, the
697 // more we increase touch slop.
698 theta -= START_DAMPING_TOUCH_SLOP_ANGLE;
699 float extraRatio = (float)
700 Math.sqrt((theta / (MAX_SWIPE_ANGLE - START_DAMPING_TOUCH_SLOP_ANGLE)));
701 super.determineScrollingStart(ev, 1 + TOUCH_SLOP_DAMPING_FACTOR * extraRatio);
702 } else {
703 // Below START_DAMPING_TOUCH_SLOP_ANGLE, we don't do anything special
704 super.determineScrollingStart(ev);
Adam Cohenf8d28232011-02-01 21:47:00 -0800705 }
Michael Jurka1adf5392010-10-18 18:10:22 -0700706 }
707
Winson Chung007c6982011-06-14 13:27:53 -0700708 @Override
709 protected boolean isScrollingIndicatorEnabled() {
Winson Chung48846472012-03-27 15:38:05 -0700710 return super.isScrollingIndicatorEnabled() && (mState != State.SPRING_LOADED);
Winson Chung007c6982011-06-14 13:27:53 -0700711 }
712
Patrick Dubroy1262e362010-10-06 15:49:50 -0700713 protected void onPageBeginMoving() {
Winson Chung007c6982011-06-14 13:27:53 -0700714 super.onPageBeginMoving();
715
Michael Jurkad74c9842011-07-10 12:44:21 -0700716 if (isHardwareAccelerated()) {
717 updateChildrenLayersEnabled();
Michael Jurka0142d492010-08-25 17:46:15 -0700718 } else {
Michael Jurkad74c9842011-07-10 12:44:21 -0700719 if (mNextPage != INVALID_PAGE) {
720 // we're snapping to a particular screen
721 enableChildrenCache(mCurrentPage, mNextPage);
722 } else {
723 // this is when user is actively dragging a particular screen, they might
724 // swipe it either left or right (but we won't advance by more than one screen)
725 enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1);
726 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800727 }
Winson Chung007c6982011-06-14 13:27:53 -0700728
729 // Only show page outlines as we pan if we are on large screen
730 if (LauncherApplication.isScreenLarge()) {
731 showOutlines();
Michael Jurka95515372012-02-29 14:13:01 -0800732 mIsStaticWallpaper = mWallpaperManager.getWallpaperInfo() == null;
Winson Chung007c6982011-06-14 13:27:53 -0700733 }
Winson Chung1afedc32012-01-23 16:14:56 -0800734
Winson Chung70442722012-02-10 15:43:22 -0800735 // If we are not fading in adjacent screens, we still need to restore the alpha in case the
736 // user scrolls while we are transitioning (should not affect dispatchDraw optimizations)
Michael Jurka869390b2012-05-06 15:55:19 -0700737 if (!mWorkspaceFadeInAdjacentScreens) {
Winson Chung70442722012-02-10 15:43:22 -0800738 for (int i = 0; i < getChildCount(); ++i) {
Michael Jurkaa52570f2012-03-20 03:18:20 -0700739 ((CellLayout) getPageAt(i)).setShortcutAndWidgetAlpha(1f);
Winson Chung70442722012-02-10 15:43:22 -0800740 }
741 }
742
Winson Chung1afedc32012-01-23 16:14:56 -0800743 // Show the scroll indicator as you pan the page
744 showScrollingIndicator(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800745 }
746
Patrick Dubroy1262e362010-10-06 15:49:50 -0700747 protected void onPageEndMoving() {
Winson Chung007c6982011-06-14 13:27:53 -0700748 super.onPageEndMoving();
749
Michael Jurkad74c9842011-07-10 12:44:21 -0700750 if (isHardwareAccelerated()) {
751 updateChildrenLayersEnabled();
752 } else {
753 clearChildrenCache();
754 }
755
Winson Chung3bc21c32012-01-20 13:59:18 -0800756
757 if (mDragController.isDragging()) {
758 if (isSmall()) {
759 // If we are in springloaded mode, then force an event to check if the current touch
760 // is under a new page (to scroll to)
761 mDragController.forceMoveEvent();
762 }
763 } else {
764 // If we are not mid-dragging, hide the page outlines if we are on a large screen
Winson Chung007c6982011-06-14 13:27:53 -0700765 if (LauncherApplication.isScreenLarge()) {
766 hideOutlines();
767 }
Winson Chung1afedc32012-01-23 16:14:56 -0800768
769 // Hide the scroll indicator as you pan the page
770 if (!mDragController.isDragging()) {
771 hideScrollingIndicator(false);
772 }
Patrick Dubroy1262e362010-10-06 15:49:50 -0700773 }
Adam Cohen68d73932010-11-15 10:50:58 -0800774 mOverScrollMaxBackgroundAlpha = 0.0f;
Adam Cohen26976d92011-03-22 15:33:33 -0700775
776 if (mDelayedResizeRunnable != null) {
777 mDelayedResizeRunnable.run();
778 mDelayedResizeRunnable = null;
779 }
Winson Chungf0c6ae02012-03-21 16:10:31 -0700780
781 if (mDelayedSnapToPageRunnable != null) {
782 mDelayedSnapToPageRunnable.run();
783 mDelayedSnapToPageRunnable = null;
784 }
Michael Jurka0142d492010-08-25 17:46:15 -0700785 }
786
787 @Override
788 protected void notifyPageSwitchListener() {
789 super.notifyPageSwitchListener();
Michael Jurka0142d492010-08-25 17:46:15 -0700790 Launcher.setScreen(mCurrentPage);
791 };
792
Michael Jurkac5b262c2011-01-12 20:24:50 -0800793 // As a ratio of screen height, the total distance we want the parallax effect to span
Michael Jurkac5b262c2011-01-12 20:24:50 -0800794 // horizontally
795 private float wallpaperTravelToScreenWidthRatio(int width, int height) {
796 float aspectRatio = width / (float) height;
797
798 // At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width
799 // At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width
800 // We will use these two data points to extrapolate how much the wallpaper parallax effect
801 // to span (ie travel) at any aspect ratio:
802
803 final float ASPECT_RATIO_LANDSCAPE = 16/10f;
804 final float ASPECT_RATIO_PORTRAIT = 10/16f;
805 final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f;
806 final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f;
807
808 // To find out the desired width at different aspect ratios, we use the following two
809 // formulas, where the coefficient on x is the aspect ratio (width/height):
810 // (16/10)x + y = 1.5
811 // (10/16)x + y = 1.2
812 // We solve for x and y and end up with a final formula:
813 final float x =
814 (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) /
815 (ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT);
816 final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT;
817 return x * aspectRatio + y;
818 }
819
820 // The range of scroll values for Workspace
821 private int getScrollRange() {
822 return getChildOffset(getChildCount() - 1) - getChildOffset(0);
823 }
824
825 protected void setWallpaperDimension() {
Winson Chung201bc822011-06-20 15:41:53 -0700826 DisplayMetrics displayMetrics = new DisplayMetrics();
Adam Cohencff6af82011-09-13 14:51:53 -0700827 mLauncher.getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics);
Winson Chung201bc822011-06-20 15:41:53 -0700828 final int maxDim = Math.max(displayMetrics.widthPixels, displayMetrics.heightPixels);
829 final int minDim = Math.min(displayMetrics.widthPixels, displayMetrics.heightPixels);
Michael Jurkac5b262c2011-01-12 20:24:50 -0800830
831 // We need to ensure that there is enough extra space in the wallpaper for the intended
832 // parallax effects
Michael Jurkaa6471802011-09-22 14:39:16 -0700833 if (LauncherApplication.isScreenLarge()) {
834 mWallpaperWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim));
Michael Jurka193080b2011-10-28 16:08:53 -0700835 mWallpaperHeight = maxDim;
Michael Jurkaa6471802011-09-22 14:39:16 -0700836 } else {
Michael Jurka16706982011-09-26 16:39:22 -0700837 mWallpaperWidth = Math.max((int) (minDim * WALLPAPER_SCREENS_SPAN), maxDim);
Michael Jurkaa6471802011-09-22 14:39:16 -0700838 mWallpaperHeight = maxDim;
839 }
Winson Chungf7640c82011-02-28 13:47:29 -0800840 new Thread("setWallpaperDimension") {
841 public void run() {
842 mWallpaperManager.suggestDesiredDimensions(mWallpaperWidth, mWallpaperHeight);
843 }
844 }.start();
Michael Jurkac5b262c2011-01-12 20:24:50 -0800845 }
846
Michael Jurkaab1983f2011-01-18 15:50:17 -0800847 private float wallpaperOffsetForCurrentScroll() {
Michael Jurkaab1983f2011-01-18 15:50:17 -0800848 // Set wallpaper offset steps (1 / (number of screens - 1))
Michael Jurka193080b2011-10-28 16:08:53 -0700849 mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 1.0f);
Michael Jurkaab1983f2011-01-18 15:50:17 -0800850
Michael Jurka193080b2011-10-28 16:08:53 -0700851 // For the purposes of computing the scrollRange and overScrollOffset, we assume
852 // that mLayoutScale is 1. This means that when we're in spring-loaded mode,
Adam Cohenbeff8c62011-08-31 17:46:01 -0700853 // there's no discrepancy between the wallpaper offset for a given page.
854 float layoutScale = mLayoutScale;
855 mLayoutScale = 1f;
Michael Jurka09d6eb02011-01-28 16:05:09 -0800856 int scrollRange = getScrollRange();
Michael Jurka09d6eb02011-01-28 16:05:09 -0800857
Adam Cohenbeff8c62011-08-31 17:46:01 -0700858 // Again, we adjust the wallpaper offset to be consistent between values of mLayoutScale
Michael Jurka8b805b12012-04-18 14:23:14 -0700859 float adjustedScrollX = Math.max(0, Math.min(getScrollX(), mMaxScrollX));
Adam Cohenb5ba0972011-09-07 18:02:31 -0700860 adjustedScrollX *= mWallpaperScrollRatio;
Adam Cohenbeff8c62011-08-31 17:46:01 -0700861 mLayoutScale = layoutScale;
862
Michael Jurkaab1983f2011-01-18 15:50:17 -0800863 float scrollProgress =
Michael Jurka193080b2011-10-28 16:08:53 -0700864 adjustedScrollX / (float) scrollRange;
Michael Jurka95515372012-02-29 14:13:01 -0800865
866 if (LauncherApplication.isScreenLarge() && mIsStaticWallpaper) {
867 // The wallpaper travel width is how far, from left to right, the wallpaper will move
868 // at this orientation. On tablets in portrait mode we don't move all the way to the
869 // edges of the wallpaper, or otherwise the parallax effect would be too strong.
870 int wallpaperTravelWidth = Math.min(mWallpaperTravelWidth, mWallpaperWidth);
871
872 float offsetInDips = wallpaperTravelWidth * scrollProgress +
873 (mWallpaperWidth - wallpaperTravelWidth) / 2; // center it
874 float offset = offsetInDips / (float) mWallpaperWidth;
875 return offset;
876 } else {
877 return scrollProgress;
878 }
Michael Jurkaab1983f2011-01-18 15:50:17 -0800879 }
Michael Jurka95515372012-02-29 14:13:01 -0800880
Michael Jurkaab1983f2011-01-18 15:50:17 -0800881 private void syncWallpaperOffsetWithScroll() {
Winson Chungb46a2d12011-04-22 14:13:05 -0700882 final boolean enableWallpaperEffects = isHardwareAccelerated();
883 if (enableWallpaperEffects) {
Michael Jurkaab1983f2011-01-18 15:50:17 -0800884 mWallpaperOffset.setFinalX(wallpaperOffsetForCurrentScroll());
Michael Jurkac5b262c2011-01-12 20:24:50 -0800885 }
886 }
887
888 public void updateWallpaperOffsetImmediately() {
889 mUpdateWallpaperOffsetImmediately = true;
890 }
891
Michael Jurkaab1983f2011-01-18 15:50:17 -0800892 private void updateWallpaperOffsets() {
893 boolean updateNow = false;
894 boolean keepUpdating = true;
895 if (mUpdateWallpaperOffsetImmediately) {
896 updateNow = true;
897 keepUpdating = false;
898 mWallpaperOffset.jumpToFinal();
899 mUpdateWallpaperOffsetImmediately = false;
Michael Jurkac5b262c2011-01-12 20:24:50 -0800900 } else {
Michael Jurkaab1983f2011-01-18 15:50:17 -0800901 updateNow = keepUpdating = mWallpaperOffset.computeScrollOffset();
Michael Jurkac5b262c2011-01-12 20:24:50 -0800902 }
Michael Jurkaab1983f2011-01-18 15:50:17 -0800903 if (updateNow) {
Michael Jurka9c6fbed2011-03-02 17:41:34 -0800904 if (mWindowToken != null) {
905 mWallpaperManager.setWallpaperOffsets(mWindowToken,
Michael Jurkaab1983f2011-01-18 15:50:17 -0800906 mWallpaperOffset.getCurrX(), mWallpaperOffset.getCurrY());
907 }
Michael Jurkac5b262c2011-01-12 20:24:50 -0800908 }
Michael Jurkaab1983f2011-01-18 15:50:17 -0800909 if (keepUpdating) {
Michael Jurka7372c592012-01-16 04:21:35 -0800910 invalidate();
Michael Jurkac5b262c2011-01-12 20:24:50 -0800911 }
Michael Jurkaab1983f2011-01-18 15:50:17 -0800912 }
913
Adam Cohenbeff8c62011-08-31 17:46:01 -0700914 @Override
915 protected void updateCurrentPageScroll() {
916 super.updateCurrentPageScroll();
Adam Cohen5037a0b2011-09-01 13:10:40 -0700917 computeWallpaperScrollRatio(mCurrentPage);
Adam Cohenbeff8c62011-08-31 17:46:01 -0700918 }
919
920 @Override
921 protected void snapToPage(int whichPage) {
922 super.snapToPage(whichPage);
Adam Cohen5037a0b2011-09-01 13:10:40 -0700923 computeWallpaperScrollRatio(whichPage);
Adam Cohenbeff8c62011-08-31 17:46:01 -0700924 }
925
Winson Chungf0c6ae02012-03-21 16:10:31 -0700926 @Override
927 protected void snapToPage(int whichPage, int duration) {
928 super.snapToPage(whichPage, duration);
929 computeWallpaperScrollRatio(whichPage);
930 }
931
932 protected void snapToPage(int whichPage, Runnable r) {
933 if (mDelayedSnapToPageRunnable != null) {
934 mDelayedSnapToPageRunnable.run();
935 }
936 mDelayedSnapToPageRunnable = r;
937 snapToPage(whichPage, SLOW_PAGE_SNAP_ANIMATION_DURATION);
938 }
939
Adam Cohen5037a0b2011-09-01 13:10:40 -0700940 private void computeWallpaperScrollRatio(int page) {
Adam Cohenbeff8c62011-08-31 17:46:01 -0700941 // Here, we determine what the desired scroll would be with and without a layout scale,
942 // and compute a ratio between the two. This allows us to adjust the wallpaper offset
943 // as though there is no layout scale.
944 float layoutScale = mLayoutScale;
Adam Cohen5037a0b2011-09-01 13:10:40 -0700945 int scaled = getChildOffset(page) - getRelativeChildOffset(page);
Adam Cohenbeff8c62011-08-31 17:46:01 -0700946 mLayoutScale = 1.0f;
Adam Cohen5037a0b2011-09-01 13:10:40 -0700947 float unscaled = getChildOffset(page) - getRelativeChildOffset(page);
Adam Cohenbeff8c62011-08-31 17:46:01 -0700948 mLayoutScale = layoutScale;
949 if (scaled > 0) {
950 mWallpaperScrollRatio = (1.0f * unscaled) / scaled;
951 } else {
952 mWallpaperScrollRatio = 1f;
953 }
954 }
955
Michael Jurkaab1983f2011-01-18 15:50:17 -0800956 class WallpaperOffsetInterpolator {
957 float mFinalHorizontalWallpaperOffset = 0.0f;
958 float mFinalVerticalWallpaperOffset = 0.5f;
959 float mHorizontalWallpaperOffset = 0.0f;
960 float mVerticalWallpaperOffset = 0.5f;
961 long mLastWallpaperOffsetUpdateTime;
Michael Jurkaca5b8362011-01-27 13:23:26 -0800962 boolean mIsMovingFast;
963 boolean mOverrideHorizontalCatchupConstant;
964 float mHorizontalCatchupConstant = 0.35f;
965 float mVerticalCatchupConstant = 0.35f;
Michael Jurkaab1983f2011-01-18 15:50:17 -0800966
967 public WallpaperOffsetInterpolator() {
968 }
969
Michael Jurkaca5b8362011-01-27 13:23:26 -0800970 public void setOverrideHorizontalCatchupConstant(boolean override) {
971 mOverrideHorizontalCatchupConstant = override;
972 }
973
974 public void setHorizontalCatchupConstant(float f) {
975 mHorizontalCatchupConstant = f;
976 }
977
978 public void setVerticalCatchupConstant(float f) {
979 mVerticalCatchupConstant = f;
980 }
981
Michael Jurkaab1983f2011-01-18 15:50:17 -0800982 public boolean computeScrollOffset() {
983 if (Float.compare(mHorizontalWallpaperOffset, mFinalHorizontalWallpaperOffset) == 0 &&
984 Float.compare(mVerticalWallpaperOffset, mFinalVerticalWallpaperOffset) == 0) {
Michael Jurkaca5b8362011-01-27 13:23:26 -0800985 mIsMovingFast = false;
Michael Jurkaab1983f2011-01-18 15:50:17 -0800986 return false;
987 }
Michael Jurka84f2ce72012-04-13 15:08:01 -0700988 boolean isLandscape = mDisplaySize.x > mDisplaySize.y;
Michael Jurkaab1983f2011-01-18 15:50:17 -0800989
990 long currentTime = System.currentTimeMillis();
991 long timeSinceLastUpdate = currentTime - mLastWallpaperOffsetUpdateTime;
992 timeSinceLastUpdate = Math.min((long) (1000/30f), timeSinceLastUpdate);
993 timeSinceLastUpdate = Math.max(1L, timeSinceLastUpdate);
Michael Jurkaca5b8362011-01-27 13:23:26 -0800994
995 float xdiff = Math.abs(mFinalHorizontalWallpaperOffset - mHorizontalWallpaperOffset);
996 if (!mIsMovingFast && xdiff > 0.07) {
997 mIsMovingFast = true;
998 }
999
1000 float fractionToCatchUpIn1MsHorizontal;
1001 if (mOverrideHorizontalCatchupConstant) {
1002 fractionToCatchUpIn1MsHorizontal = mHorizontalCatchupConstant;
1003 } else if (mIsMovingFast) {
1004 fractionToCatchUpIn1MsHorizontal = isLandscape ? 0.5f : 0.75f;
1005 } else {
1006 // slow
1007 fractionToCatchUpIn1MsHorizontal = isLandscape ? 0.27f : 0.5f;
1008 }
1009 float fractionToCatchUpIn1MsVertical = mVerticalCatchupConstant;
1010
Michael Jurkaca5b8362011-01-27 13:23:26 -08001011 fractionToCatchUpIn1MsHorizontal /= 33f;
1012 fractionToCatchUpIn1MsVertical /= 33f;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001013
1014 final float UPDATE_THRESHOLD = 0.00001f;
1015 float hOffsetDelta = mFinalHorizontalWallpaperOffset - mHorizontalWallpaperOffset;
1016 float vOffsetDelta = mFinalVerticalWallpaperOffset - mVerticalWallpaperOffset;
Michael Jurkaca5b8362011-01-27 13:23:26 -08001017 boolean jumpToFinalValue = Math.abs(hOffsetDelta) < UPDATE_THRESHOLD &&
1018 Math.abs(vOffsetDelta) < UPDATE_THRESHOLD;
Michael Jurkaa6471802011-09-22 14:39:16 -07001019
1020 // Don't have any lag between workspace and wallpaper on non-large devices
1021 if (!LauncherApplication.isScreenLarge() || jumpToFinalValue) {
Michael Jurkaab1983f2011-01-18 15:50:17 -08001022 mHorizontalWallpaperOffset = mFinalHorizontalWallpaperOffset;
1023 mVerticalWallpaperOffset = mFinalVerticalWallpaperOffset;
1024 } else {
1025 float percentToCatchUpVertical =
Michael Jurkaca5b8362011-01-27 13:23:26 -08001026 Math.min(1.0f, timeSinceLastUpdate * fractionToCatchUpIn1MsVertical);
Michael Jurkaab1983f2011-01-18 15:50:17 -08001027 float percentToCatchUpHorizontal =
Michael Jurkaca5b8362011-01-27 13:23:26 -08001028 Math.min(1.0f, timeSinceLastUpdate * fractionToCatchUpIn1MsHorizontal);
Michael Jurkaab1983f2011-01-18 15:50:17 -08001029 mHorizontalWallpaperOffset += percentToCatchUpHorizontal * hOffsetDelta;
1030 mVerticalWallpaperOffset += percentToCatchUpVertical * vOffsetDelta;
1031 }
Michael Jurkaca5b8362011-01-27 13:23:26 -08001032
Michael Jurkaab1983f2011-01-18 15:50:17 -08001033 mLastWallpaperOffsetUpdateTime = System.currentTimeMillis();
1034 return true;
1035 }
1036
1037 public float getCurrX() {
1038 return mHorizontalWallpaperOffset;
1039 }
1040
1041 public float getFinalX() {
1042 return mFinalHorizontalWallpaperOffset;
1043 }
1044
1045 public float getCurrY() {
1046 return mVerticalWallpaperOffset;
1047 }
1048
1049 public float getFinalY() {
1050 return mFinalVerticalWallpaperOffset;
1051 }
1052
1053 public void setFinalX(float x) {
1054 mFinalHorizontalWallpaperOffset = Math.max(0f, Math.min(x, 1.0f));
1055 }
1056
1057 public void setFinalY(float y) {
1058 mFinalVerticalWallpaperOffset = Math.max(0f, Math.min(y, 1.0f));
1059 }
1060
1061 public void jumpToFinal() {
1062 mHorizontalWallpaperOffset = mFinalHorizontalWallpaperOffset;
1063 mVerticalWallpaperOffset = mFinalVerticalWallpaperOffset;
1064 }
Dianne Hackborn8f573952009-08-10 23:21:09 -07001065 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001066
Michael Jurka340c5f32010-10-21 16:49:19 -07001067 @Override
1068 public void computeScroll() {
1069 super.computeScroll();
Adam Cohenbeff8c62011-08-31 17:46:01 -07001070 syncWallpaperOffsetWithScroll();
Michael Jurka340c5f32010-10-21 16:49:19 -07001071 }
1072
Patrick Dubroy94f78a52011-02-28 17:39:16 -08001073 void showOutlines() {
Michael Jurkad74c9842011-07-10 12:44:21 -07001074 if (!isSmall() && !mIsSwitchingState) {
Winson Chung9171e6d2010-11-17 17:39:27 -08001075 if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
1076 if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
1077 mChildrenOutlineFadeInAnimation = ObjectAnimator.ofFloat(this, "childrenOutlineAlpha", 1.0f);
1078 mChildrenOutlineFadeInAnimation.setDuration(CHILDREN_OUTLINE_FADE_IN_DURATION);
1079 mChildrenOutlineFadeInAnimation.start();
Michael Jurka3e7c7632010-10-02 16:01:03 -07001080 }
Adam Cohenf34bab52010-09-30 14:11:56 -07001081 }
1082
Patrick Dubroy94f78a52011-02-28 17:39:16 -08001083 void hideOutlines() {
Michael Jurkad74c9842011-07-10 12:44:21 -07001084 if (!isSmall() && !mIsSwitchingState) {
Winson Chung9171e6d2010-11-17 17:39:27 -08001085 if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
1086 if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
1087 mChildrenOutlineFadeOutAnimation = ObjectAnimator.ofFloat(this, "childrenOutlineAlpha", 0.0f);
1088 mChildrenOutlineFadeOutAnimation.setDuration(CHILDREN_OUTLINE_FADE_OUT_DURATION);
1089 mChildrenOutlineFadeOutAnimation.setStartDelay(CHILDREN_OUTLINE_FADE_OUT_DELAY);
1090 mChildrenOutlineFadeOutAnimation.start();
Michael Jurka3e7c7632010-10-02 16:01:03 -07001091 }
Adam Cohenf34bab52010-09-30 14:11:56 -07001092 }
1093
Patrick Dubroy94f78a52011-02-28 17:39:16 -08001094 public void showOutlinesTemporarily() {
1095 if (!mIsPageMoving && !isTouchActive()) {
1096 snapToPage(mCurrentPage);
1097 }
1098 }
1099
Winson Chung9171e6d2010-11-17 17:39:27 -08001100 public void setChildrenOutlineAlpha(float alpha) {
1101 mChildrenOutlineAlpha = alpha;
Adam Cohenf34bab52010-09-30 14:11:56 -07001102 for (int i = 0; i < getChildCount(); i++) {
1103 CellLayout cl = (CellLayout) getChildAt(i);
1104 cl.setBackgroundAlpha(alpha);
1105 }
1106 }
1107
Winson Chung9171e6d2010-11-17 17:39:27 -08001108 public float getChildrenOutlineAlpha() {
1109 return mChildrenOutlineAlpha;
1110 }
1111
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001112 void disableBackground() {
1113 mDrawBackground = false;
1114 }
1115 void enableBackground() {
1116 mDrawBackground = true;
1117 }
1118
Michael Jurka7bdb25a2011-08-03 15:16:44 -07001119 private void animateBackgroundGradient(float finalAlpha, boolean animated) {
Winson Chungb4b7fa72010-11-18 14:38:53 -08001120 if (mBackground == null) return;
Michael Jurkab9e14972011-07-25 17:57:40 -07001121 if (mBackgroundFadeInAnimation != null) {
1122 mBackgroundFadeInAnimation.cancel();
1123 mBackgroundFadeInAnimation = null;
1124 }
1125 if (mBackgroundFadeOutAnimation != null) {
1126 mBackgroundFadeOutAnimation.cancel();
1127 mBackgroundFadeOutAnimation = null;
1128 }
Michael Jurka7bdb25a2011-08-03 15:16:44 -07001129 float startAlpha = getBackgroundAlpha();
1130 if (finalAlpha != startAlpha) {
1131 if (animated) {
1132 mBackgroundFadeOutAnimation = ValueAnimator.ofFloat(startAlpha, finalAlpha);
1133 mBackgroundFadeOutAnimation.addUpdateListener(new AnimatorUpdateListener() {
1134 public void onAnimationUpdate(ValueAnimator animation) {
1135 setBackgroundAlpha(((Float) animation.getAnimatedValue()).floatValue());
1136 }
1137 });
1138 mBackgroundFadeOutAnimation.setInterpolator(new DecelerateInterpolator(1.5f));
1139 mBackgroundFadeOutAnimation.setDuration(BACKGROUND_FADE_OUT_DURATION);
1140 mBackgroundFadeOutAnimation.start();
1141 } else {
1142 setBackgroundAlpha(finalAlpha);
1143 }
Michael Jurkab9e14972011-07-25 17:57:40 -07001144 }
Winson Chung9171e6d2010-11-17 17:39:27 -08001145 }
1146
1147 public void setBackgroundAlpha(float alpha) {
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001148 if (alpha != mBackgroundAlpha) {
1149 mBackgroundAlpha = alpha;
1150 invalidate();
1151 }
Winson Chung9171e6d2010-11-17 17:39:27 -08001152 }
1153
Adam Cohenf34bab52010-09-30 14:11:56 -07001154 public float getBackgroundAlpha() {
1155 return mBackgroundAlpha;
1156 }
1157
Adam Lesinski6b879f02010-11-04 16:15:23 -07001158 /**
1159 * Due to 3D transformations, if two CellLayouts are theoretically touching each other,
1160 * on the xy plane, when one is rotated along the y-axis, the gap between them is perceived
1161 * as being larger. This method computes what offset the rotated view should be translated
1162 * in order to minimize this perceived gap.
1163 * @param degrees Angle of the view
1164 * @param width Width of the view
1165 * @param height Height of the view
1166 * @return Offset to be used in a View.setTranslationX() call
1167 */
1168 private float getOffsetXForRotation(float degrees, int width, int height) {
1169 mMatrix.reset();
1170 mCamera.save();
1171 mCamera.rotateY(Math.abs(degrees));
1172 mCamera.getMatrix(mMatrix);
1173 mCamera.restore();
1174
1175 mMatrix.preTranslate(-width * 0.5f, -height * 0.5f);
1176 mMatrix.postTranslate(width * 0.5f, height * 0.5f);
1177 mTempFloat2[0] = width;
1178 mTempFloat2[1] = height;
1179 mMatrix.mapPoints(mTempFloat2);
1180 return (width - mTempFloat2[0]) * (degrees > 0.0f ? 1.0f : -1.0f);
1181 }
1182
Adam Cohen68d73932010-11-15 10:50:58 -08001183 float backgroundAlphaInterpolator(float r) {
1184 float pivotA = 0.1f;
1185 float pivotB = 0.4f;
1186 if (r < pivotA) {
1187 return 0;
1188 } else if (r > pivotB) {
1189 return 1.0f;
1190 } else {
1191 return (r - pivotA)/(pivotB - pivotA);
1192 }
1193 }
1194
1195 float overScrollBackgroundAlphaInterpolator(float r) {
Adam Cohene0f66b52010-11-23 15:06:07 -08001196 float threshold = 0.08f;
Adam Cohen68d73932010-11-15 10:50:58 -08001197
1198 if (r > mOverScrollMaxBackgroundAlpha) {
1199 mOverScrollMaxBackgroundAlpha = r;
1200 } else if (r < mOverScrollMaxBackgroundAlpha) {
1201 r = mOverScrollMaxBackgroundAlpha;
1202 }
1203
1204 return Math.min(r / threshold, 1.0f);
1205 }
1206
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001207 private void updatePageAlphaValues(int screenCenter) {
Michael Jurka869390b2012-05-06 15:55:19 -07001208 boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
1209 if (mWorkspaceFadeInAdjacentScreens &&
1210 mState == State.NORMAL &&
1211 !mIsSwitchingState &&
1212 !isInOverscroll) {
1213 for (int i = 0; i < getChildCount(); i++) {
1214 CellLayout child = (CellLayout) getChildAt(i);
1215 if (child != null) {
1216 float scrollProgress = getScrollProgress(screenCenter, child, i);
Adam Cohen73894962011-10-31 13:17:17 -07001217 float alpha = 1 - Math.abs(scrollProgress);
Michael Jurka869390b2012-05-06 15:55:19 -07001218 child.getShortcutsAndWidgets().setAlpha(alpha);
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001219 if (!mIsDragOccuring) {
1220 child.setBackgroundAlphaMultiplier(
1221 backgroundAlphaInterpolator(Math.abs(scrollProgress)));
1222 } else {
1223 child.setBackgroundAlphaMultiplier(1f);
1224 }
Adam Cohen73894962011-10-31 13:17:17 -07001225 }
Adam Cohenf34bab52010-09-30 14:11:56 -07001226 }
1227 }
Michael Jurkaa3d30ad2012-05-08 13:43:43 -07001228 }
1229
1230 private void setChildrenBackgroundAlphaMultipliers(float a) {
1231 for (int i = 0; i < getChildCount(); i++) {
1232 CellLayout child = (CellLayout) getChildAt(i);
1233 child.setBackgroundAlphaMultiplier(a);
1234 }
1235 }
1236
1237 @Override
1238 protected void screenScrolled(int screenCenter) {
1239 super.screenScrolled(screenCenter);
1240
1241 updatePageAlphaValues(screenCenter);
Adam Cohenf34bab52010-09-30 14:11:56 -07001242
Adam Cohenebea84d2011-11-09 17:20:41 -08001243 if (mOverScrollX < 0 || mOverScrollX > mMaxScrollX) {
1244 int index = mOverScrollX < 0 ? 0 : getChildCount() - 1;
Adam Cohenb5ba0972011-09-07 18:02:31 -07001245 CellLayout cl = (CellLayout) getChildAt(index);
1246 float scrollProgress = getScrollProgress(screenCenter, cl, index);
1247 cl.setOverScrollAmount(Math.abs(scrollProgress), index == 0);
Adam Cohenb5ba0972011-09-07 18:02:31 -07001248 float rotation = - WORKSPACE_OVERSCROLL_ROTATION * scrollProgress;
Adam Cohenb5ba0972011-09-07 18:02:31 -07001249 cl.setRotationY(rotation);
Adam Cohen21b41102011-11-01 17:29:52 -07001250 setFadeForOverScroll(Math.abs(scrollProgress));
Michael Jurkab06d95f2012-04-02 06:26:53 -07001251 if (!mOverscrollTransformsSet) {
1252 mOverscrollTransformsSet = true;
1253 cl.setCameraDistance(mDensity * CAMERA_DISTANCE);
1254 cl.setPivotX(cl.getMeasuredWidth() * (index == 0 ? 0.75f : 0.25f));
1255 cl.setPivotY(cl.getMeasuredHeight() * 0.5f);
1256 cl.setOverscrollTransformsDirty(true);
1257 }
Adam Cohenb5ba0972011-09-07 18:02:31 -07001258 } else {
Adam Cohen21b41102011-11-01 17:29:52 -07001259 if (mOverscrollFade != 0) {
1260 setFadeForOverScroll(0);
1261 }
Michael Jurkab06d95f2012-04-02 06:26:53 -07001262 if (mOverscrollTransformsSet) {
1263 mOverscrollTransformsSet = false;
Adam Cohenebea84d2011-11-09 17:20:41 -08001264 ((CellLayout) getChildAt(0)).resetOverscrollTransforms();
1265 ((CellLayout) getChildAt(getChildCount() - 1)).resetOverscrollTransforms();
Adam Cohen7842d7f2011-09-12 15:28:15 -07001266 }
Adam Cohenb5ba0972011-09-07 18:02:31 -07001267 }
1268 }
1269
1270 @Override
Adam Cohenb5ba0972011-09-07 18:02:31 -07001271 protected void overScroll(float amount) {
Michael Jurka869390b2012-05-06 15:55:19 -07001272 acceleratedOverScroll(amount);
Adam Cohenb5ba0972011-09-07 18:02:31 -07001273 }
1274
Joe Onorato00acb122009-08-04 16:04:30 -04001275 protected void onAttachedToWindow() {
1276 super.onAttachedToWindow();
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001277 mWindowToken = getWindowToken();
Joe Onorato956091b2010-02-19 12:47:40 -08001278 computeScroll();
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001279 mDragController.setWindowToken(mWindowToken);
1280 }
1281
1282 protected void onDetachedFromWindow() {
1283 mWindowToken = null;
Joe Onorato00acb122009-08-04 16:04:30 -04001284 }
1285
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001286 @Override
Michael Jurka28750fb2010-09-24 17:43:49 -07001287 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Michael Jurkac5b262c2011-01-12 20:24:50 -08001288 if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
1289 mUpdateWallpaperOffsetImmediately = true;
1290 }
Michael Jurka0142d492010-08-25 17:46:15 -07001291 super.onLayout(changed, left, top, right, bottom);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001292 }
1293
1294 @Override
Winson Chung9171e6d2010-11-17 17:39:27 -08001295 protected void onDraw(Canvas canvas) {
Michael Jurkaab1983f2011-01-18 15:50:17 -08001296 updateWallpaperOffsets();
Michael Jurkac5b262c2011-01-12 20:24:50 -08001297
Winson Chung9171e6d2010-11-17 17:39:27 -08001298 // Draw the background gradient if necessary
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001299 if (mBackground != null && mBackgroundAlpha > 0.0f && mDrawBackground) {
Winson Chung8d006d52010-11-29 15:55:29 -08001300 int alpha = (int) (mBackgroundAlpha * 255);
Winson Chungf0ea4d32011-06-06 14:27:16 -07001301 mBackground.setAlpha(alpha);
Michael Jurka8b805b12012-04-18 14:23:14 -07001302 mBackground.setBounds(getScrollX(), 0, getScrollX() + getMeasuredWidth(),
Winson Chungf0ea4d32011-06-06 14:27:16 -07001303 getMeasuredHeight());
1304 mBackground.draw(canvas);
Winson Chung9171e6d2010-11-17 17:39:27 -08001305 }
Adam Cohen073a46f2011-05-17 16:28:09 -07001306
Winson Chung9171e6d2010-11-17 17:39:27 -08001307 super.onDraw(canvas);
1308 }
1309
Adam Cohen21b41102011-11-01 17:29:52 -07001310 boolean isDrawingBackgroundGradient() {
1311 return (mBackground != null && mBackgroundAlpha > 0.0f && mDrawBackground);
1312 }
1313
Michael Jurkadfab7f02011-11-18 13:01:04 -08001314 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001315 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
Joe Onorato67886212009-09-14 19:05:05 -04001316 if (!mLauncher.isAllAppsVisible()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001317 final Folder openFolder = getOpenFolder();
1318 if (openFolder != null) {
1319 return openFolder.requestFocus(direction, previouslyFocusedRect);
1320 } else {
Michael Jurka0142d492010-08-25 17:46:15 -07001321 return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001322 }
1323 }
1324 return false;
1325 }
1326
1327 @Override
Winson Chung97d85d22011-04-13 11:27:36 -07001328 public int getDescendantFocusability() {
Michael Jurkad74c9842011-07-10 12:44:21 -07001329 if (isSmall()) {
Winson Chung97d85d22011-04-13 11:27:36 -07001330 return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
1331 }
1332 return super.getDescendantFocusability();
1333 }
1334
1335 @Override
Romain Guyc2e24c02009-06-01 16:11:41 -07001336 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
Joe Onorato67886212009-09-14 19:05:05 -04001337 if (!mLauncher.isAllAppsVisible()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001338 final Folder openFolder = getOpenFolder();
Michael Jurka0142d492010-08-25 17:46:15 -07001339 if (openFolder != null) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001340 openFolder.addFocusables(views, direction);
Michael Jurka0142d492010-08-25 17:46:15 -07001341 } else {
1342 super.addFocusables(views, direction, focusableMode);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001343 }
1344 }
1345 }
1346
Michael Jurkad74c9842011-07-10 12:44:21 -07001347 public boolean isSmall() {
Adam Cohen3384c7b2011-07-17 15:48:21 -07001348 return mState == State.SMALL || mState == State.SPRING_LOADED;
Michael Jurkad74c9842011-07-10 12:44:21 -07001349 }
1350
Michael Jurka0142d492010-08-25 17:46:15 -07001351 void enableChildrenCache(int fromPage, int toPage) {
1352 if (fromPage > toPage) {
1353 final int temp = fromPage;
1354 fromPage = toPage;
1355 toPage = temp;
Mike Cleron3a2b3f22009-11-05 17:17:42 -08001356 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001357
Michael Jurkadee05892010-07-27 10:01:56 -07001358 final int screenCount = getChildCount();
Adam Powellfea5d022010-04-29 11:42:45 -07001359
Michael Jurka0142d492010-08-25 17:46:15 -07001360 fromPage = Math.max(fromPage, 0);
1361 toPage = Math.min(toPage, screenCount - 1);
Adam Powellfea5d022010-04-29 11:42:45 -07001362
Michael Jurka0142d492010-08-25 17:46:15 -07001363 for (int i = fromPage; i <= toPage; i++) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001364 final CellLayout layout = (CellLayout) getChildAt(i);
1365 layout.setChildrenDrawnWithCacheEnabled(true);
1366 layout.setChildrenDrawingCacheEnabled(true);
1367 }
1368 }
1369
1370 void clearChildrenCache() {
Michael Jurkadee05892010-07-27 10:01:56 -07001371 final int screenCount = getChildCount();
1372 for (int i = 0; i < screenCount; i++) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001373 final CellLayout layout = (CellLayout) getChildAt(i);
1374 layout.setChildrenDrawnWithCacheEnabled(false);
Adam Cohen8182e5b2011-08-01 15:43:31 -07001375 // In software mode, we don't want the items to continue to be drawn into bitmaps
1376 if (!isHardwareAccelerated()) {
1377 layout.setChildrenDrawingCacheEnabled(false);
1378 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001379 }
1380 }
1381
Michael Jurkad74c9842011-07-10 12:44:21 -07001382 private void updateChildrenLayersEnabled() {
Adam Cohen19f37922012-03-21 11:59:11 -07001383 boolean small = mState == State.SMALL || mIsSwitchingState;
1384 boolean enableChildrenLayers = small || mAnimatingViewIntoPlace || isPageMoving();
Michael Jurkad74c9842011-07-10 12:44:21 -07001385
1386 if (enableChildrenLayers != mChildrenLayersEnabled) {
1387 mChildrenLayersEnabled = enableChildrenLayers;
Michael Jurka80c69852011-12-16 14:16:32 -08001388 for (int i = 0; i < getPageCount(); i++) {
1389 ((ViewGroup)getChildAt(i)).setChildrenLayersEnabled(mChildrenLayersEnabled);
Michael Jurkad74c9842011-07-10 12:44:21 -07001390 }
Michael Jurkace7e05f2011-02-01 22:02:35 -08001391 }
Michael Jurkace7e05f2011-02-01 22:02:35 -08001392 }
1393
Jeff Brown1d0867c2010-12-02 18:27:39 -08001394 protected void onWallpaperTap(MotionEvent ev) {
1395 final int[] position = mTempCell;
1396 getLocationOnScreen(position);
1397
1398 int pointerIndex = ev.getActionIndex();
1399 position[0] += (int) ev.getX(pointerIndex);
1400 position[1] += (int) ev.getY(pointerIndex);
1401
1402 mWallpaperManager.sendWallpaperCommand(getWindowToken(),
1403 ev.getAction() == MotionEvent.ACTION_UP
1404 ? WallpaperManager.COMMAND_TAP : WallpaperManager.COMMAND_SECONDARY_TAP,
1405 position[0], position[1], 0, null);
1406 }
1407
Adam Cohen61033d32010-11-15 18:29:44 -08001408 /*
1409 * This interpolator emulates the rate at which the perceived scale of an object changes
1410 * as its distance from a camera increases. When this interpolator is applied to a scale
1411 * animation on a view, it evokes the sense that the object is shrinking due to moving away
Andrew Flynn0dca1ec2012-02-29 13:33:22 -08001412 * from the camera.
Adam Cohen61033d32010-11-15 18:29:44 -08001413 */
1414 static class ZInterpolator implements TimeInterpolator {
1415 private float focalLength;
1416
1417 public ZInterpolator(float foc) {
1418 focalLength = foc;
1419 }
1420
Adam Coheneed565d2010-11-15 11:30:05 -08001421 public float getInterpolation(float input) {
1422 return (1.0f - focalLength / (focalLength + input)) /
Adam Cohen61033d32010-11-15 18:29:44 -08001423 (1.0f - focalLength / (focalLength + 1.0f));
Adam Cohencbbaf982010-11-12 14:50:33 -08001424 }
1425 }
1426
Adam Cohen61033d32010-11-15 18:29:44 -08001427 /*
1428 * The exact reverse of ZInterpolator.
1429 */
1430 static class InverseZInterpolator implements TimeInterpolator {
1431 private ZInterpolator zInterpolator;
1432 public InverseZInterpolator(float foc) {
1433 zInterpolator = new ZInterpolator(foc);
1434 }
Adam Cohencbbaf982010-11-12 14:50:33 -08001435 public float getInterpolation(float input) {
Adam Cohen61033d32010-11-15 18:29:44 -08001436 return 1 - zInterpolator.getInterpolation(1 - input);
Adam Cohencbbaf982010-11-12 14:50:33 -08001437 }
1438 }
1439
Adam Cohen61033d32010-11-15 18:29:44 -08001440 /*
1441 * ZInterpolator compounded with an ease-out.
1442 */
1443 static class ZoomOutInterpolator implements TimeInterpolator {
Adam Cohenb64d36e2011-10-17 21:48:02 -07001444 private final DecelerateInterpolator decelerate = new DecelerateInterpolator(0.75f);
1445 private final ZInterpolator zInterpolator = new ZInterpolator(0.13f);
Adam Cohen61033d32010-11-15 18:29:44 -08001446
1447 public float getInterpolation(float input) {
1448 return decelerate.getInterpolation(zInterpolator.getInterpolation(input));
1449 }
1450 }
1451
1452 /*
1453 * InvereZInterpolator compounded with an ease-out.
1454 */
1455 static class ZoomInInterpolator implements TimeInterpolator {
1456 private final InverseZInterpolator inverseZInterpolator = new InverseZInterpolator(0.35f);
1457 private final DecelerateInterpolator decelerate = new DecelerateInterpolator(3.0f);
1458
1459 public float getInterpolation(float input) {
1460 return decelerate.getInterpolation(inverseZInterpolator.getInterpolation(input));
1461 }
1462 }
1463
Adam Cohen61033d32010-11-15 18:29:44 -08001464 private final ZoomInInterpolator mZoomInInterpolator = new ZoomInInterpolator();
Michael Jurka3e7c7632010-10-02 16:01:03 -07001465
Michael Jurka3e7c7632010-10-02 16:01:03 -07001466 /*
Adam Cohen66396872011-04-15 17:50:36 -07001467 *
1468 * We call these methods (onDragStartedWithItemSpans/onDragStartedWithSize) whenever we
1469 * start a drag in Launcher, regardless of whether the drag has ever entered the Workspace
1470 *
1471 * These methods mark the appropriate pages as accepting drops (which alters their visual
1472 * appearance).
1473 *
1474 */
1475 public void onDragStartedWithItem(View v) {
Adam Cohen66396872011-04-15 17:50:36 -07001476 final Canvas canvas = new Canvas();
1477
Adam Cohen66396872011-04-15 17:50:36 -07001478 // The outline is used to visualize where the item will land if dropped
Adam Cohend41fbf52012-02-16 23:53:59 -08001479 mDragOutline = createDragOutline(v, canvas, DRAG_BITMAP_PADDING);
Adam Cohen66396872011-04-15 17:50:36 -07001480 }
1481
Michael Jurka038f9d82011-11-03 13:50:45 -07001482 public void onDragStartedWithItem(PendingAddItemInfo info, Bitmap b, Paint alphaClipPaint) {
Michael Jurkad3ef3062010-11-23 16:23:58 -08001483 final Canvas canvas = new Canvas();
1484
Michael Jurka038f9d82011-11-03 13:50:45 -07001485 int[] size = estimateItemSize(info.spanX, info.spanY, info, false);
Adam Cohen66396872011-04-15 17:50:36 -07001486
Michael Jurkad3ef3062010-11-23 16:23:58 -08001487 // The outline is used to visualize where the item will land if dropped
Adam Cohend41fbf52012-02-16 23:53:59 -08001488 mDragOutline = createDragOutline(b, canvas, DRAG_BITMAP_PADDING, size[0],
1489 size[1], alphaClipPaint);
Michael Jurka3e7c7632010-10-02 16:01:03 -07001490 }
1491
Patrick Dubroy758a9232011-03-03 19:54:56 -08001492 public void exitWidgetResizeMode() {
Adam Cohen716b51e2011-06-30 12:09:54 -07001493 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohen67882692011-03-11 15:29:03 -08001494 dragLayer.clearAllResizeFrames();
Patrick Dubroy758a9232011-03-03 19:54:56 -08001495 }
1496
Adam Cohen4b285c52011-07-21 14:24:06 -07001497 private void initAnimationArrays() {
1498 final int childCount = getChildCount();
1499 if (mOldTranslationXs != null) return;
1500 mOldTranslationXs = new float[childCount];
1501 mOldTranslationYs = new float[childCount];
1502 mOldScaleXs = new float[childCount];
1503 mOldScaleYs = new float[childCount];
1504 mOldBackgroundAlphas = new float[childCount];
Adam Cohen4b285c52011-07-21 14:24:06 -07001505 mOldAlphas = new float[childCount];
Adam Cohen4b285c52011-07-21 14:24:06 -07001506 mNewTranslationXs = new float[childCount];
1507 mNewTranslationYs = new float[childCount];
1508 mNewScaleXs = new float[childCount];
1509 mNewScaleYs = new float[childCount];
1510 mNewBackgroundAlphas = new float[childCount];
Adam Cohen4b285c52011-07-21 14:24:06 -07001511 mNewAlphas = new float[childCount];
1512 mNewRotationYs = new float[childCount];
1513 }
1514
Michael Jurka2a4b1a82011-12-07 14:00:02 -08001515 Animator getChangeStateAnimation(final State state, boolean animated) {
1516 return getChangeStateAnimation(state, animated, 0);
Adam Cohen7777d962011-08-18 18:58:38 -07001517 }
1518
Michael Jurka2a4b1a82011-12-07 14:00:02 -08001519 Animator getChangeStateAnimation(final State state, boolean animated, int delay) {
Michael Jurkabdf78552011-10-31 14:34:25 -07001520 if (mState == state) {
Michael Jurka2a4b1a82011-12-07 14:00:02 -08001521 return null;
Michael Jurka99633da2011-07-27 22:40:17 -07001522 }
1523
Winson Chung1b7c1d32011-10-25 12:09:38 -07001524 // Initialize animation arrays for the first time if necessary
1525 initAnimationArrays();
1526
Michael Jurka2a4b1a82011-12-07 14:00:02 -08001527 AnimatorSet anim = animated ? new AnimatorSet() : null;
Adam Lesinski6b879f02010-11-04 16:15:23 -07001528
Adam Cohen7777d962011-08-18 18:58:38 -07001529 // Stop any scrolling, move to the current page right away
Winson Chung360e63f2012-04-27 13:48:05 -07001530 setCurrentPage(getNextPage());
Winson Chungde1af762011-07-21 16:44:07 -07001531
Winson Chung1b7c1d32011-10-25 12:09:38 -07001532 final State oldState = mState;
1533 final boolean oldStateIsNormal = (oldState == State.NORMAL);
Michael Jurka4ff7d792012-04-02 03:46:50 -07001534 final boolean oldStateIsSpringLoaded = (oldState == State.SPRING_LOADED);
Winson Chung1b7c1d32011-10-25 12:09:38 -07001535 final boolean oldStateIsSmall = (oldState == State.SMALL);
Adam Cohen7777d962011-08-18 18:58:38 -07001536 mState = state;
Winson Chung1b7c1d32011-10-25 12:09:38 -07001537 final boolean stateIsNormal = (state == State.NORMAL);
1538 final boolean stateIsSpringLoaded = (state == State.SPRING_LOADED);
1539 final boolean stateIsSmall = (state == State.SMALL);
1540 float finalScaleFactor = 1.0f;
1541 float finalBackgroundAlpha = stateIsSpringLoaded ? 1.0f : 0f;
1542 float translationX = 0;
1543 float translationY = 0;
Adam Cohen7777d962011-08-18 18:58:38 -07001544 boolean zoomIn = true;
Adam Cohenbeff8c62011-08-31 17:46:01 -07001545
Adam Cohen7777d962011-08-18 18:58:38 -07001546 if (state != State.NORMAL) {
Winson Chung1b7c1d32011-10-25 12:09:38 -07001547 finalScaleFactor = mSpringLoadedShrinkFactor - (stateIsSmall ? 0.1f : 0);
Winson Chung8aad6102012-05-11 16:27:49 -07001548 setPageSpacing(mSpringLoadedPageSpacing);
Winson Chung1b7c1d32011-10-25 12:09:38 -07001549 if (oldStateIsNormal && stateIsSmall) {
Adam Cohen7777d962011-08-18 18:58:38 -07001550 zoomIn = false;
Adam Cohen7777d962011-08-18 18:58:38 -07001551 setLayoutScale(finalScaleFactor);
1552 updateChildrenLayersEnabled();
Michael Jurkaca5b8362011-01-27 13:23:26 -08001553 } else {
Adam Cohencff6af82011-09-13 14:51:53 -07001554 finalBackgroundAlpha = 1.0f;
Adam Cohen7777d962011-08-18 18:58:38 -07001555 setLayoutScale(finalScaleFactor);
1556 }
1557 } else {
Winson Chung8aad6102012-05-11 16:27:49 -07001558 setPageSpacing(PagedView.AUTOMATIC_PAGE_SPACING);
Adam Cohen7777d962011-08-18 18:58:38 -07001559 setLayoutScale(1.0f);
Adam Cohen7777d962011-08-18 18:58:38 -07001560 }
1561
Andrew Flynn0dca1ec2012-02-29 13:33:22 -08001562 final int duration = zoomIn ?
Adam Cohen7777d962011-08-18 18:58:38 -07001563 getResources().getInteger(R.integer.config_workspaceUnshrinkTime) :
1564 getResources().getInteger(R.integer.config_appsCustomizeWorkspaceShrinkTime);
Winson Chung1b7c1d32011-10-25 12:09:38 -07001565 for (int i = 0; i < getChildCount(); i++) {
1566 final CellLayout cl = (CellLayout) getChildAt(i);
Michael Jurka869390b2012-05-06 15:55:19 -07001567 float finalAlpha = (!mWorkspaceFadeInAdjacentScreens || stateIsSpringLoaded ||
Winson Chung1b7c1d32011-10-25 12:09:38 -07001568 (i == mCurrentPage)) ? 1f : 0f;
Michael Jurka869390b2012-05-06 15:55:19 -07001569 float currentAlpha = cl.getShortcutsAndWidgets().getAlpha();
1570 float initialAlpha = currentAlpha;
Adam Cohen7777d962011-08-18 18:58:38 -07001571
Winson Chung1b7c1d32011-10-25 12:09:38 -07001572 // Determine the pages alpha during the state transition
1573 if ((oldStateIsSmall && stateIsNormal) ||
1574 (oldStateIsNormal && stateIsSmall)) {
1575 // To/from workspace - only show the current page unless the transition is not
Michael Jurka4ff7d792012-04-02 03:46:50 -07001576 // animated and the animation end callback below doesn't run;
1577 // or, if we're in spring-loaded mode
1578 if (i == mCurrentPage || !animated || oldStateIsSpringLoaded) {
Winson Chung1b7c1d32011-10-25 12:09:38 -07001579 finalAlpha = 1f;
Winson Chung1b7c1d32011-10-25 12:09:38 -07001580 } else {
1581 initialAlpha = 0f;
1582 finalAlpha = 0f;
1583 }
Adam Cohen7777d962011-08-18 18:58:38 -07001584 }
1585
Winson Chung1b7c1d32011-10-25 12:09:38 -07001586 mOldAlphas[i] = initialAlpha;
1587 mNewAlphas[i] = finalAlpha;
Adam Cohen7777d962011-08-18 18:58:38 -07001588 if (animated) {
1589 mOldTranslationXs[i] = cl.getTranslationX();
1590 mOldTranslationYs[i] = cl.getTranslationY();
1591 mOldScaleXs[i] = cl.getScaleX();
1592 mOldScaleYs[i] = cl.getScaleY();
1593 mOldBackgroundAlphas[i] = cl.getBackgroundAlpha();
Adam Cohen7777d962011-08-18 18:58:38 -07001594
1595 mNewTranslationXs[i] = translationX;
1596 mNewTranslationYs[i] = translationY;
1597 mNewScaleXs[i] = finalScaleFactor;
1598 mNewScaleYs[i] = finalScaleFactor;
1599 mNewBackgroundAlphas[i] = finalBackgroundAlpha;
Adam Cohen7777d962011-08-18 18:58:38 -07001600 } else {
1601 cl.setTranslationX(translationX);
1602 cl.setTranslationY(translationY);
1603 cl.setScaleX(finalScaleFactor);
1604 cl.setScaleY(finalScaleFactor);
Winson Chung1b7c1d32011-10-25 12:09:38 -07001605 cl.setBackgroundAlpha(finalBackgroundAlpha);
Michael Jurkaa52570f2012-03-20 03:18:20 -07001606 cl.setShortcutAndWidgetAlpha(finalAlpha);
Adam Cohen7777d962011-08-18 18:58:38 -07001607 }
Michael Jurkadee05892010-07-27 10:01:56 -07001608 }
Winson Chung8d006d52010-11-29 15:55:29 -08001609
Adam Cohen7777d962011-08-18 18:58:38 -07001610 if (animated) {
Michael Jurka7407d2a2011-12-12 21:48:38 -08001611 for (int index = 0; index < getChildCount(); index++) {
1612 final int i = index;
1613 final CellLayout cl = (CellLayout) getChildAt(i);
Michael Jurka869390b2012-05-06 15:55:19 -07001614 float currentAlpha = cl.getShortcutsAndWidgets().getAlpha();
Michael Jurka2a4b1a82011-12-07 14:00:02 -08001615 if (mOldAlphas[i] == 0 && mNewAlphas[i] == 0) {
Michael Jurka7372c592012-01-16 04:21:35 -08001616 cl.setTranslationX(mNewTranslationXs[i]);
1617 cl.setTranslationY(mNewTranslationYs[i]);
1618 cl.setScaleX(mNewScaleXs[i]);
1619 cl.setScaleY(mNewScaleYs[i]);
1620 cl.setBackgroundAlpha(mNewBackgroundAlphas[i]);
Michael Jurkaa52570f2012-03-20 03:18:20 -07001621 cl.setShortcutAndWidgetAlpha(mNewAlphas[i]);
Michael Jurka7372c592012-01-16 04:21:35 -08001622 cl.setRotationY(mNewRotationYs[i]);
Michael Jurka7407d2a2011-12-12 21:48:38 -08001623 } else {
1624 LauncherViewPropertyAnimator a = new LauncherViewPropertyAnimator(cl);
1625 a.translationX(mNewTranslationXs[i])
1626 .translationY(mNewTranslationYs[i])
1627 .scaleX(mNewScaleXs[i])
1628 .scaleY(mNewScaleYs[i])
1629 .setDuration(duration)
1630 .setInterpolator(mZoomInInterpolator);
Michael Jurka7407d2a2011-12-12 21:48:38 -08001631 anim.play(a);
Michael Jurkaa52570f2012-03-20 03:18:20 -07001632
Michael Jurka869390b2012-05-06 15:55:19 -07001633 if (mOldAlphas[i] != mNewAlphas[i] || currentAlpha != mNewAlphas[i]) {
Michael Jurka6c8a5792012-03-22 05:24:37 -07001634 LauncherViewPropertyAnimator alphaAnim =
1635 new LauncherViewPropertyAnimator(cl.getShortcutsAndWidgets());
Michael Jurkaa52570f2012-03-20 03:18:20 -07001636 alphaAnim.alpha(mNewAlphas[i])
1637 .setDuration(duration)
1638 .setInterpolator(mZoomInInterpolator);
1639 anim.play(alphaAnim);
1640 }
Michael Jurka7407d2a2011-12-12 21:48:38 -08001641 if (mOldBackgroundAlphas[i] != 0 ||
Michael Jurka869390b2012-05-06 15:55:19 -07001642 mNewBackgroundAlphas[i] != 0) {
Michael Jurka7407d2a2011-12-12 21:48:38 -08001643 ValueAnimator bgAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
1644 bgAnim.setInterpolator(mZoomInInterpolator);
1645 bgAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
1646 public void onAnimationUpdate(float a, float b) {
Michael Jurka7372c592012-01-16 04:21:35 -08001647 cl.setBackgroundAlpha(
Michael Jurka7407d2a2011-12-12 21:48:38 -08001648 a * mOldBackgroundAlphas[i] +
1649 b * mNewBackgroundAlphas[i]);
Michael Jurka7407d2a2011-12-12 21:48:38 -08001650 }
1651 });
1652 anim.play(bgAnim);
1653 }
Michael Jurka2a4b1a82011-12-07 14:00:02 -08001654 }
1655 }
Michael Jurkab99d1af2012-04-25 17:28:27 -07001656 buildPageHardwareLayers();
Michael Jurka2a4b1a82011-12-07 14:00:02 -08001657 anim.setStartDelay(delay);
Adam Cohen7777d962011-08-18 18:58:38 -07001658 }
1659
Winson Chung1b7c1d32011-10-25 12:09:38 -07001660 if (stateIsSpringLoaded) {
Michael Jurka7bdb25a2011-08-03 15:16:44 -07001661 // Right now we're covered by Apps Customize
1662 // Show the background gradient immediately, so the gradient will
1663 // be showing once AppsCustomize disappears
1664 animateBackgroundGradient(getResources().getInteger(
1665 R.integer.config_appsCustomizeSpringLoadedBgAlpha) / 100f, false);
1666 } else {
1667 // Fade the background gradient away
1668 animateBackgroundGradient(0f, true);
1669 }
Michael Jurka2a4b1a82011-12-07 14:00:02 -08001670 return anim;
Michael Jurkadee05892010-07-27 10:01:56 -07001671 }
1672
Michael Jurkabed61d22012-02-14 22:51:29 -08001673 @Override
Michael Jurkaa35e35a2012-04-26 15:04:28 -07001674 public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
Michael Jurkabed61d22012-02-14 22:51:29 -08001675 mIsSwitchingState = true;
Michael Jurka72db6d72012-03-23 08:06:02 -07001676 cancelScrollingIndicatorAnimations();
Michael Jurkabed61d22012-02-14 22:51:29 -08001677 }
1678
1679 @Override
Michael Jurkaa35e35a2012-04-26 15:04:28 -07001680 public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
1681 }
1682
1683 @Override
Winson Chung70442722012-02-10 15:43:22 -08001684 public void onLauncherTransitionStep(Launcher l, float t) {
1685 mTransitionProgress = t;
1686 }
1687
1688 @Override
Michael Jurkabed61d22012-02-14 22:51:29 -08001689 public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
1690 mIsSwitchingState = false;
1691 mWallpaperOffset.setOverrideHorizontalCatchupConstant(false);
1692 updateChildrenLayersEnabled();
1693 // The code in getChangeStateAnimation to determine initialAlpha and finalAlpha will ensure
1694 // ensure that only the current page is visible during (and subsequently, after) the
1695 // transition animation. If fade adjacent pages is disabled, then re-enable the page
1696 // visibility after the transition animation.
Michael Jurka869390b2012-05-06 15:55:19 -07001697 if (!mWorkspaceFadeInAdjacentScreens) {
Michael Jurkabed61d22012-02-14 22:51:29 -08001698 for (int i = 0; i < getChildCount(); i++) {
1699 final CellLayout cl = (CellLayout) getChildAt(i);
Michael Jurkaa52570f2012-03-20 03:18:20 -07001700 cl.setShortcutAndWidgetAlpha(1f);
Michael Jurkabed61d22012-02-14 22:51:29 -08001701 }
1702 }
1703 }
1704
1705 @Override
1706 public View getContent() {
1707 return this;
1708 }
1709
Joe Onorato4be866d2010-10-10 11:26:02 -07001710 /**
1711 * Draw the View v into the given Canvas.
1712 *
1713 * @param v the view to draw
1714 * @param destCanvas the canvas to draw on
1715 * @param padding the horizontal and vertical padding to use when drawing
1716 */
Adam Cohenac8c8762011-07-13 11:15:27 -07001717 private void drawDragView(View v, Canvas destCanvas, int padding, boolean pruneToDrawable) {
Joe Onorato4be866d2010-10-10 11:26:02 -07001718 final Rect clipRect = mTempRect;
1719 v.getDrawingRect(clipRect);
1720
Adam Cohen099f60d2011-08-23 21:07:26 -07001721 boolean textVisible = false;
1722
Adam Cohenac8c8762011-07-13 11:15:27 -07001723 destCanvas.save();
1724 if (v instanceof TextView && pruneToDrawable) {
1725 Drawable d = ((TextView) v).getCompoundDrawables()[1];
1726 clipRect.set(0, 0, d.getIntrinsicWidth() + padding, d.getIntrinsicHeight() + padding);
1727 destCanvas.translate(padding / 2, padding / 2);
1728 d.draw(destCanvas);
1729 } else {
1730 if (v instanceof FolderIcon) {
Adam Cohen099f60d2011-08-23 21:07:26 -07001731 // For FolderIcons the text can bleed into the icon area, and so we need to
1732 // hide the text completely (which can't be achieved by clipping).
1733 if (((FolderIcon) v).getTextVisible()) {
1734 ((FolderIcon) v).setTextVisible(false);
1735 textVisible = true;
1736 }
Adam Cohenac8c8762011-07-13 11:15:27 -07001737 } else if (v instanceof BubbleTextView) {
1738 final BubbleTextView tv = (BubbleTextView) v;
1739 clipRect.bottom = tv.getExtendedPaddingTop() - (int) BubbleTextView.PADDING_V +
1740 tv.getLayout().getLineTop(0);
1741 } else if (v instanceof TextView) {
1742 final TextView tv = (TextView) v;
1743 clipRect.bottom = tv.getExtendedPaddingTop() - tv.getCompoundDrawablePadding() +
1744 tv.getLayout().getLineTop(0);
1745 }
1746 destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2);
1747 destCanvas.clipRect(clipRect, Op.REPLACE);
1748 v.draw(destCanvas);
Adam Cohen099f60d2011-08-23 21:07:26 -07001749
1750 // Restore text visibility of FolderIcon if necessary
1751 if (textVisible) {
1752 ((FolderIcon) v).setTextVisible(true);
1753 }
Adam Cohenac8c8762011-07-13 11:15:27 -07001754 }
1755 destCanvas.restore();
1756 }
1757
1758 /**
1759 * Returns a new bitmap to show when the given View is being dragged around.
1760 * Responsibility for the bitmap is transferred to the caller.
1761 */
1762 public Bitmap createDragBitmap(View v, Canvas canvas, int padding) {
Adam Cohenac8c8762011-07-13 11:15:27 -07001763 Bitmap b;
1764
1765 if (v instanceof TextView) {
1766 Drawable d = ((TextView) v).getCompoundDrawables()[1];
1767 b = Bitmap.createBitmap(d.getIntrinsicWidth() + padding,
1768 d.getIntrinsicHeight() + padding, Bitmap.Config.ARGB_8888);
1769 } else {
1770 b = Bitmap.createBitmap(
1771 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
Joe Onorato4be866d2010-10-10 11:26:02 -07001772 }
1773
Adam Cohenac8c8762011-07-13 11:15:27 -07001774 canvas.setBitmap(b);
1775 drawDragView(v, canvas, padding, true);
Adam Cohenaaf473c2011-08-03 12:02:47 -07001776 canvas.setBitmap(null);
Joe Onorato4be866d2010-10-10 11:26:02 -07001777
Adam Cohenac8c8762011-07-13 11:15:27 -07001778 return b;
Joe Onorato4be866d2010-10-10 11:26:02 -07001779 }
1780
1781 /**
1782 * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
1783 * Responsibility for the bitmap is transferred to the caller.
1784 */
1785 private Bitmap createDragOutline(View v, Canvas canvas, int padding) {
Winson Chungea359c62011-08-03 17:06:35 -07001786 final int outlineColor = getResources().getColor(android.R.color.holo_blue_light);
Joe Onorato4be866d2010-10-10 11:26:02 -07001787 final Bitmap b = Bitmap.createBitmap(
1788 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
1789
1790 canvas.setBitmap(b);
Winson Chungb8c69f32011-10-19 21:36:08 -07001791 drawDragView(v, canvas, padding, true);
Adam Cohen5bb50bd2010-12-03 11:39:55 -08001792 mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor);
Adam Cohenaaf473c2011-08-03 12:02:47 -07001793 canvas.setBitmap(null);
Joe Onorato4be866d2010-10-10 11:26:02 -07001794 return b;
1795 }
1796
1797 /**
Michael Jurkad3ef3062010-11-23 16:23:58 -08001798 * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
1799 * Responsibility for the bitmap is transferred to the caller.
1800 */
Peter Ng8db70002011-10-25 15:40:08 -07001801 private Bitmap createDragOutline(Bitmap orig, Canvas canvas, int padding, int w, int h,
1802 Paint alphaClipPaint) {
Winson Chungea359c62011-08-03 17:06:35 -07001803 final int outlineColor = getResources().getColor(android.R.color.holo_blue_light);
Michael Jurkaf12c75c2011-01-25 22:41:40 -08001804 final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Michael Jurkad3ef3062010-11-23 16:23:58 -08001805 canvas.setBitmap(b);
Michael Jurkaf12c75c2011-01-25 22:41:40 -08001806
1807 Rect src = new Rect(0, 0, orig.getWidth(), orig.getHeight());
1808 float scaleFactor = Math.min((w - padding) / (float) orig.getWidth(),
1809 (h - padding) / (float) orig.getHeight());
1810 int scaledWidth = (int) (scaleFactor * orig.getWidth());
1811 int scaledHeight = (int) (scaleFactor * orig.getHeight());
1812 Rect dst = new Rect(0, 0, scaledWidth, scaledHeight);
1813
1814 // center the image
1815 dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2);
1816
Winson Chung1120e032011-11-22 16:11:31 -08001817 canvas.drawBitmap(orig, src, dst, null);
Peter Ng8db70002011-10-25 15:40:08 -07001818 mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor,
1819 alphaClipPaint);
Adam Cohenaaf473c2011-08-03 12:02:47 -07001820 canvas.setBitmap(null);
Michael Jurkad3ef3062010-11-23 16:23:58 -08001821
1822 return b;
1823 }
1824
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001825 void startDrag(CellLayout.CellInfo cellInfo) {
1826 View child = cellInfo.cell;
Winson Chungaafa03c2010-06-11 17:34:16 -07001827
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001828 // Make sure the drag was started by a long press as opposed to a long click.
Bjorn Bringert7984c942009-12-09 15:38:25 +00001829 if (!child.isInTouchMode()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001830 return;
1831 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001832
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001833 mDragInfo = cellInfo;
Adam Cohend41fbf52012-02-16 23:53:59 -08001834 child.setVisibility(INVISIBLE);
Adam Cohen482ed822012-03-02 14:15:13 -08001835 CellLayout layout = (CellLayout) child.getParent().getParent();
1836 layout.prepareChildForDrag(child);
Joe Onorato4be866d2010-10-10 11:26:02 -07001837
1838 child.clearFocus();
1839 child.setPressed(false);
1840
1841 final Canvas canvas = new Canvas();
1842
1843 // The outline is used to visualize where the item will land if dropped
Adam Cohend41fbf52012-02-16 23:53:59 -08001844 mDragOutline = createDragOutline(child, canvas, DRAG_BITMAP_PADDING);
Adam Cohenac8c8762011-07-13 11:15:27 -07001845 beginDragShared(child, this);
1846 }
1847
1848 public void beginDragShared(View child, DragSource source) {
Winson Chungb8c69f32011-10-19 21:36:08 -07001849 Resources r = getResources();
1850
Joe Onorato4be866d2010-10-10 11:26:02 -07001851 // The drag bitmap follows the touch point around on the screen
Adam Cohend41fbf52012-02-16 23:53:59 -08001852 final Bitmap b = createDragBitmap(child, new Canvas(), DRAG_BITMAP_PADDING);
Joe Onorato4be866d2010-10-10 11:26:02 -07001853
1854 final int bmpWidth = b.getWidth();
Winson Chungeecf02d2012-03-02 17:14:58 -08001855 final int bmpHeight = b.getHeight();
Adam Cohene3e27a82011-04-15 12:07:39 -07001856
Adam Cohen8dfcba42011-07-07 16:38:18 -07001857 mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY);
Winson Chungeecf02d2012-03-02 17:14:58 -08001858 int dragLayerX =
1859 Math.round(mTempXY[0] - (bmpWidth - child.getScaleX() * child.getWidth()) / 2);
1860 int dragLayerY =
1861 Math.round(mTempXY[1] - (bmpHeight - child.getScaleY() * bmpHeight) / 2
1862 - DRAG_BITMAP_PADDING / 2);
Adam Cohene3e27a82011-04-15 12:07:39 -07001863
Winson Chungb8c69f32011-10-19 21:36:08 -07001864 Point dragVisualizeOffset = null;
Adam Cohene3e27a82011-04-15 12:07:39 -07001865 Rect dragRect = null;
Winson Chungb8c69f32011-10-19 21:36:08 -07001866 if (child instanceof BubbleTextView || child instanceof PagedViewIcon) {
1867 int iconSize = r.getDimensionPixelSize(R.dimen.app_icon_size);
1868 int iconPaddingTop = r.getDimensionPixelSize(R.dimen.app_icon_padding_top);
Adam Cohene3e27a82011-04-15 12:07:39 -07001869 int top = child.getPaddingTop();
1870 int left = (bmpWidth - iconSize) / 2;
1871 int right = left + iconSize;
1872 int bottom = top + iconSize;
Adam Cohenfc53cd22011-07-20 15:45:11 -07001873 dragLayerY += top;
Winson Chungb8c69f32011-10-19 21:36:08 -07001874 // Note: The drag region is used to calculate drag layer offsets, but the
1875 // dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
Adam Cohend41fbf52012-02-16 23:53:59 -08001876 dragVisualizeOffset = new Point(-DRAG_BITMAP_PADDING / 2,
1877 iconPaddingTop - DRAG_BITMAP_PADDING / 2);
Adam Cohene3e27a82011-04-15 12:07:39 -07001878 dragRect = new Rect(left, top, right, bottom);
Adam Cohen0e4857c2011-06-30 17:05:14 -07001879 } else if (child instanceof FolderIcon) {
Winson Chungb8c69f32011-10-19 21:36:08 -07001880 int previewSize = r.getDimensionPixelSize(R.dimen.folder_preview_size);
Adam Cohen0e4857c2011-06-30 17:05:14 -07001881 dragRect = new Rect(0, 0, child.getWidth(), previewSize);
Adam Cohene3e27a82011-04-15 12:07:39 -07001882 }
1883
Winson Chung1e9cbfe2011-09-30 16:52:26 -07001884 // Clear the pressed state if necessary
1885 if (child instanceof BubbleTextView) {
1886 BubbleTextView icon = (BubbleTextView) child;
1887 icon.clearPressedOrFocusedBackground();
1888 }
1889
Adam Cohenac8c8762011-07-13 11:15:27 -07001890 mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
Winson Chungeecf02d2012-03-02 17:14:58 -08001891 DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, child.getScaleX());
Joe Onorato4be866d2010-10-10 11:26:02 -07001892 b.recycle();
Winson Chung1afedc32012-01-23 16:14:56 -08001893
1894 // Show the scrolling indicator when you pick up an item
1895 showScrollingIndicator(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001896 }
1897
Winson Chung3d503fb2011-07-13 17:25:49 -07001898 void addApplicationShortcut(ShortcutInfo info, CellLayout target, long container, int screen,
1899 int cellX, int cellY, boolean insertAtFirst, int intersectX, int intersectY) {
1900 View view = mLauncher.createShortcut(R.layout.application, target, (ShortcutInfo) info);
Michael Jurka0280c3b2010-09-17 15:00:07 -07001901
1902 final int[] cellXY = new int[2];
Winson Chung3d503fb2011-07-13 17:25:49 -07001903 target.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY);
1904 addInScreen(view, container, screen, cellXY[0], cellXY[1], 1, 1, insertAtFirst);
1905 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screen, cellXY[0],
1906 cellXY[1]);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001907 }
1908
Adam Cohen4b285c52011-07-21 14:24:06 -07001909 public boolean transitionStateShouldAllowDrop() {
Adam Cohen1b36dc32012-02-13 19:27:37 -08001910 return ((!isSwitchingState() || mTransitionProgress > 0.5f) && mState != State.SMALL);
Adam Cohen4b285c52011-07-21 14:24:06 -07001911 }
1912
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001913 /**
1914 * {@inheritDoc}
1915 */
Adam Cohencb3382b2011-05-24 14:07:08 -07001916 public boolean acceptDrop(DragObject d) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001917 // If it's an external drop (e.g. from All Apps), check if it should be accepted
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001918 CellLayout dropTargetLayout = mDropToLayout;
Adam Cohencb3382b2011-05-24 14:07:08 -07001919 if (d.dragSource != this) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001920 // Don't accept the drop if we're not over a screen at time of drop
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001921 if (dropTargetLayout == null) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001922 return false;
1923 }
Adam Cohen4b285c52011-07-21 14:24:06 -07001924 if (!transitionStateShouldAllowDrop()) return false;
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001925
Adam Cohena65beee2011-06-27 21:32:23 -07001926 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset,
1927 d.dragView, mDragViewVisualCenter);
1928
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07001929 // We want the point to be mapped to the dragTarget.
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001930 if (mLauncher.isHotseatLayout(dropTargetLayout)) {
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07001931 mapPointFromSelfToSibling(mLauncher.getHotseat(), mDragViewVisualCenter);
1932 } else {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001933 mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null);
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07001934 }
1935
Winson Chung557d6ed2011-07-08 15:34:52 -07001936 int spanX = 1;
1937 int spanY = 1;
Winson Chung557d6ed2011-07-08 15:34:52 -07001938 if (mDragInfo != null) {
1939 final CellLayout.CellInfo dragCellInfo = mDragInfo;
1940 spanX = dragCellInfo.spanX;
1941 spanY = dragCellInfo.spanY;
Winson Chung557d6ed2011-07-08 15:34:52 -07001942 } else {
1943 final ItemInfo dragInfo = (ItemInfo) d.dragInfo;
1944 spanX = dragInfo.spanX;
1945 spanY = dragInfo.spanY;
1946 }
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001947
Adam Cohend41fbf52012-02-16 23:53:59 -08001948 int minSpanX = spanX;
1949 int minSpanY = spanY;
1950 if (d.dragInfo instanceof PendingAddWidgetInfo) {
1951 minSpanX = ((PendingAddWidgetInfo) d.dragInfo).minSpanX;
1952 minSpanY = ((PendingAddWidgetInfo) d.dragInfo).minSpanY;
1953 }
Adam Cohen482ed822012-03-02 14:15:13 -08001954
Adam Cohena65beee2011-06-27 21:32:23 -07001955 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001956 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, dropTargetLayout,
Adam Cohenf0777b92012-02-28 14:02:45 -08001957 mTargetCell);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001958 float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08001959 mDragViewVisualCenter[1], mTargetCell);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001960 if (willCreateUserFolder((ItemInfo) d.dragInfo, dropTargetLayout,
Adam Cohen482ed822012-03-02 14:15:13 -08001961 mTargetCell, distance, true)) {
Adam Cohena65beee2011-06-27 21:32:23 -07001962 return true;
1963 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001964 if (willAddToExistingUserFolder((ItemInfo) d.dragInfo, dropTargetLayout,
Adam Cohen482ed822012-03-02 14:15:13 -08001965 mTargetCell, distance)) {
Adam Cohena65beee2011-06-27 21:32:23 -07001966 return true;
1967 }
1968
Adam Cohen482ed822012-03-02 14:15:13 -08001969 int[] resultSpan = new int[2];
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001970 mTargetCell = dropTargetLayout.createArea((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08001971 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
1972 null, mTargetCell, resultSpan, CellLayout.MODE_ACCEPT_DROP);
1973 boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
1974
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001975 // Don't accept the drop if there's no room for the item
Adam Cohen482ed822012-03-02 14:15:13 -08001976 if (!foundCell) {
Winson Chung96ef4092011-11-22 12:25:14 -08001977 // Don't show the message if we are dropping on the AllApps button and the hotseat
1978 // is full
Adam Cohenc6cc61d2012-04-04 12:47:08 -07001979 boolean isHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
Winson Chung93eef082012-03-23 15:59:27 -07001980 if (mTargetCell != null && isHotseat) {
Winson Chung96ef4092011-11-22 12:25:14 -08001981 Hotseat hotseat = mLauncher.getHotseat();
Andrew Flynn0dca1ec2012-02-29 13:33:22 -08001982 if (hotseat.isAllAppsButtonRank(
Winson Chung96ef4092011-11-22 12:25:14 -08001983 hotseat.getOrderInHotseat(mTargetCell[0], mTargetCell[1]))) {
1984 return false;
1985 }
1986 }
1987
Winson Chung93eef082012-03-23 15:59:27 -07001988 mLauncher.showOutOfSpaceMessage(isHotseat);
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001989 return false;
1990 }
1991 }
1992 return true;
1993 }
1994
Adam Cohen482ed822012-03-02 14:15:13 -08001995 boolean willCreateUserFolder(ItemInfo info, CellLayout target, int[] targetCell, float
1996 distance, boolean considerTimeout) {
1997 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohena65beee2011-06-27 21:32:23 -07001998 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
1999
Adam Cohen19f37922012-03-21 11:59:11 -07002000 if (dropOverView != null) {
2001 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
2002 if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) {
2003 return false;
2004 }
2005 }
2006
Winson Chung3d503fb2011-07-13 17:25:49 -07002007 boolean hasntMoved = false;
2008 if (mDragInfo != null) {
Adam Cohen482ed822012-03-02 14:15:13 -08002009 hasntMoved = dropOverView == mDragInfo.cell;
Winson Chung3d503fb2011-07-13 17:25:49 -07002010 }
Adam Cohene0310962011-04-18 16:15:31 -07002011
Adam Cohena65beee2011-06-27 21:32:23 -07002012 if (dropOverView == null || hasntMoved || (considerTimeout && !mCreateUserFolderOnDrop)) {
2013 return false;
2014 }
Adam Cohene0310962011-04-18 16:15:31 -07002015
Adam Cohena65beee2011-06-27 21:32:23 -07002016 boolean aboveShortcut = (dropOverView.getTag() instanceof ShortcutInfo);
Adam Cohene0310962011-04-18 16:15:31 -07002017 boolean willBecomeShortcut =
Adam Cohenc0dcf592011-06-01 15:30:43 -07002018 (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
2019 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT);
Adam Cohene0310962011-04-18 16:15:31 -07002020
2021 return (aboveShortcut && willBecomeShortcut);
2022 }
2023
Adam Cohen482ed822012-03-02 14:15:13 -08002024 boolean willAddToExistingUserFolder(Object dragInfo, CellLayout target, int[] targetCell,
2025 float distance) {
2026 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohena65beee2011-06-27 21:32:23 -07002027 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohen19f37922012-03-21 11:59:11 -07002028
2029 if (dropOverView != null) {
2030 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
2031 if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) {
2032 return false;
2033 }
2034 }
2035
Adam Cohena65beee2011-06-27 21:32:23 -07002036 if (dropOverView instanceof FolderIcon) {
2037 FolderIcon fi = (FolderIcon) dropOverView;
2038 if (fi.acceptDrop(dragInfo)) {
2039 return true;
2040 }
2041 }
2042 return false;
2043 }
2044
Winson Chung3d503fb2011-07-13 17:25:49 -07002045 boolean createUserFolderIfNecessary(View newView, long container, CellLayout target,
Adam Cohen482ed822012-03-02 14:15:13 -08002046 int[] targetCell, float distance, boolean external, DragView dragView,
2047 Runnable postAnimationRunnable) {
2048 if (distance > mMaxDistanceForFolderCreation) return false;
Adam Cohenc0dcf592011-06-01 15:30:43 -07002049 View v = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohen19f37922012-03-21 11:59:11 -07002050
Winson Chungec9a0a42011-07-20 20:42:13 -07002051 boolean hasntMoved = false;
2052 if (mDragInfo != null) {
2053 CellLayout cellParent = getParentCellLayoutForView(mDragInfo.cell);
2054 hasntMoved = (mDragInfo.cellX == targetCell[0] &&
2055 mDragInfo.cellY == targetCell[1]) && (cellParent == target);
2056 }
Adam Cohen10b17372011-04-15 14:21:25 -07002057
Adam Cohen19072da2011-05-31 14:30:45 -07002058 if (v == null || hasntMoved || !mCreateUserFolderOnDrop) return false;
2059 mCreateUserFolderOnDrop = false;
Adam Cohenc0dcf592011-06-01 15:30:43 -07002060 final int screen = (targetCell == null) ? mDragInfo.screen : indexOfChild(target);
Adam Cohen10b17372011-04-15 14:21:25 -07002061
2062 boolean aboveShortcut = (v.getTag() instanceof ShortcutInfo);
2063 boolean willBecomeShortcut = (newView.getTag() instanceof ShortcutInfo);
2064
2065 if (aboveShortcut && willBecomeShortcut) {
2066 ShortcutInfo sourceInfo = (ShortcutInfo) newView.getTag();
2067 ShortcutInfo destInfo = (ShortcutInfo) v.getTag();
2068 // if the drag started here, we need to remove it from the workspace
2069 if (!external) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002070 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
Adam Cohen10b17372011-04-15 14:21:25 -07002071 }
2072
Adam Cohend0445262011-07-04 23:53:22 -07002073 Rect folderLocation = new Rect();
Adam Cohenac8c8762011-07-13 11:15:27 -07002074 float scale = mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation);
Adam Cohen10b17372011-04-15 14:21:25 -07002075 target.removeView(v);
Adam Cohend0445262011-07-04 23:53:22 -07002076
Winson Chung3d503fb2011-07-13 17:25:49 -07002077 FolderIcon fi =
2078 mLauncher.addFolder(target, container, screen, targetCell[0], targetCell[1]);
Adam Cohena9cf38f2011-05-02 15:36:58 -07002079 destInfo.cellX = -1;
2080 destInfo.cellY = -1;
2081 sourceInfo.cellX = -1;
2082 sourceInfo.cellY = -1;
Adam Cohend0445262011-07-04 23:53:22 -07002083
Adam Cohen558baaf2011-08-15 15:22:57 -07002084 // If the dragView is null, we can't animate
2085 boolean animate = dragView != null;
2086 if (animate) {
2087 fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale,
2088 postAnimationRunnable);
2089 } else {
2090 fi.addItem(destInfo);
2091 fi.addItem(sourceInfo);
2092 }
Adam Cohen10b17372011-04-15 14:21:25 -07002093 return true;
2094 }
2095 return false;
2096 }
2097
Adam Cohena65beee2011-06-27 21:32:23 -07002098 boolean addToExistingFolderIfNecessary(View newView, CellLayout target, int[] targetCell,
Adam Cohen482ed822012-03-02 14:15:13 -08002099 float distance, DragObject d, boolean external) {
2100 if (distance > mMaxDistanceForFolderCreation) return false;
2101
Adam Cohena65beee2011-06-27 21:32:23 -07002102 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002103 if (!mAddToExistingFolderOnDrop) return false;
2104 mAddToExistingFolderOnDrop = false;
Adam Cohen19f37922012-03-21 11:59:11 -07002105
Adam Cohenc0dcf592011-06-01 15:30:43 -07002106 if (dropOverView instanceof FolderIcon) {
2107 FolderIcon fi = (FolderIcon) dropOverView;
Adam Cohen3e8f8112011-07-02 18:03:00 -07002108 if (fi.acceptDrop(d.dragInfo)) {
2109 fi.onDrop(d);
Adam Cohenc0dcf592011-06-01 15:30:43 -07002110
2111 // if the drag started here, we need to remove it from the workspace
2112 if (!external) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002113 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
Adam Cohenc0dcf592011-06-01 15:30:43 -07002114 }
2115 return true;
2116 }
2117 }
2118 return false;
2119 }
2120
Adam Cohend41fbf52012-02-16 23:53:59 -08002121 public void onDrop(final DragObject d) {
Adam Cohencb3382b2011-05-24 14:07:08 -07002122 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, d.dragView,
Adam Cohene3e27a82011-04-15 12:07:39 -07002123 mDragViewVisualCenter);
2124
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002125 CellLayout dropTargetLayout = mDropToLayout;
2126
Adam Cohene3e27a82011-04-15 12:07:39 -07002127 // We want the point to be mapped to the dragTarget.
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002128 if (dropTargetLayout != null) {
2129 if (mLauncher.isHotseatLayout(dropTargetLayout)) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002130 mapPointFromSelfToSibling(mLauncher.getHotseat(), mDragViewVisualCenter);
2131 } else {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002132 mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null);
Winson Chung3d503fb2011-07-13 17:25:49 -07002133 }
Adam Cohenba781612011-05-09 14:37:39 -07002134 }
Michael Jurkac6ee42e2010-09-30 12:04:50 -07002135
Adam Cohened51cc92011-08-01 20:28:08 -07002136 int snapScreen = -1;
Adam Cohend41fbf52012-02-16 23:53:59 -08002137 boolean resizeOnDrop = false;
Adam Cohencb3382b2011-05-24 14:07:08 -07002138 if (d.dragSource != this) {
Adam Cohene3e27a82011-04-15 12:07:39 -07002139 final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0],
2140 (int) mDragViewVisualCenter[1] };
Adam Cohen3e8f8112011-07-02 18:03:00 -07002141 onDropExternal(touchXY, d.dragInfo, dropTargetLayout, false, d);
Patrick Dubroyce34a972010-10-19 10:34:32 -07002142 } else if (mDragInfo != null) {
Patrick Dubroyce34a972010-10-19 10:34:32 -07002143 final View cell = mDragInfo.cell;
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08002144
Adam Cohend41fbf52012-02-16 23:53:59 -08002145 Runnable resizeRunnable = null;
Winson Chungea359c62011-08-03 17:06:35 -07002146 if (dropTargetLayout != null) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002147 // Move internally
Winson Chung40e882b2011-07-21 19:01:11 -07002148 boolean hasMovedLayouts = (getParentCellLayoutForView(cell) != dropTargetLayout);
2149 boolean hasMovedIntoHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
2150 long container = hasMovedIntoHotseat ?
Winson Chung3d503fb2011-07-13 17:25:49 -07002151 LauncherSettings.Favorites.CONTAINER_HOTSEAT :
2152 LauncherSettings.Favorites.CONTAINER_DESKTOP;
2153 int screen = (mTargetCell[0] < 0) ?
2154 mDragInfo.screen : indexOfChild(dropTargetLayout);
Adam Cohenc0dcf592011-06-01 15:30:43 -07002155 int spanX = mDragInfo != null ? mDragInfo.spanX : 1;
2156 int spanY = mDragInfo != null ? mDragInfo.spanY : 1;
2157 // First we find the cell nearest to point at which the item is
2158 // dropped, without any consideration to whether there is an item there.
Adam Cohen482ed822012-03-02 14:15:13 -08002159
Adam Cohenc0dcf592011-06-01 15:30:43 -07002160 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], (int)
2161 mDragViewVisualCenter[1], spanX, spanY, dropTargetLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08002162 float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
2163 mDragViewVisualCenter[1], mTargetCell);
2164
Adam Cohenc0dcf592011-06-01 15:30:43 -07002165 // If the item being dropped is a shortcut and the nearest drop
Adam Cohen76078c42011-06-09 15:06:52 -07002166 // cell also contains a shortcut, then create a folder with the two shortcuts.
Winson Chung1c4cf4a2011-07-29 14:49:10 -07002167 if (!mInScrollArea && createUserFolderIfNecessary(cell, container,
Adam Cohen482ed822012-03-02 14:15:13 -08002168 dropTargetLayout, mTargetCell, distance, false, d.dragView, null)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07002169 return;
2170 }
2171
Adam Cohen482ed822012-03-02 14:15:13 -08002172 if (addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell,
2173 distance, d, false)) {
Adam Cohendf035382011-04-11 17:22:04 -07002174 return;
2175 }
2176
2177 // Aside from the special case where we're dropping a shortcut onto a shortcut,
2178 // we need to find the nearest cell location that is vacant
Adam Cohend41fbf52012-02-16 23:53:59 -08002179 ItemInfo item = (ItemInfo) d.dragInfo;
2180 int minSpanX = item.spanX;
2181 int minSpanY = item.spanY;
2182 if (item.minSpanX > 0 && item.minSpanY > 0) {
2183 minSpanX = item.minSpanX;
2184 minSpanY = item.minSpanY;
2185 }
Adam Cohen482ed822012-03-02 14:15:13 -08002186
Adam Cohend41fbf52012-02-16 23:53:59 -08002187 int[] resultSpan = new int[2];
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002188 mTargetCell = dropTargetLayout.createArea((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08002189 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY, cell,
2190 mTargetCell, resultSpan, CellLayout.MODE_ON_DROP);
2191
Adam Cohend41fbf52012-02-16 23:53:59 -08002192 boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;
2193 if (foundCell && (resultSpan[0] != item.spanX || resultSpan[1] != item.spanY)) {
2194 resizeOnDrop = true;
2195 item.spanX = resultSpan[0];
2196 item.spanY = resultSpan[1];
2197 }
Adam Cohendf035382011-04-11 17:22:04 -07002198
Michael Jurka7cfc2822011-08-02 20:19:24 -07002199 if (mCurrentPage != screen && !hasMovedIntoHotseat) {
Adam Cohened51cc92011-08-01 20:28:08 -07002200 snapScreen = screen;
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08002201 snapToPage(screen);
2202 }
2203
Adam Cohend41fbf52012-02-16 23:53:59 -08002204 if (foundCell) {
2205 final ItemInfo info = (ItemInfo) cell.getTag();
Winson Chung40e882b2011-07-21 19:01:11 -07002206 if (hasMovedLayouts) {
Patrick Dubroy94383362010-10-29 15:03:24 -07002207 // Reparent the view
Winson Chung3d503fb2011-07-13 17:25:49 -07002208 getParentCellLayoutForView(cell).removeView(cell);
2209 addInScreen(cell, container, screen, mTargetCell[0], mTargetCell[1],
Adam Cohend41fbf52012-02-16 23:53:59 -08002210 info.spanX, info.spanY);
Patrick Dubroy94383362010-10-29 15:03:24 -07002211 }
2212
2213 // update the item's position after drop
Patrick Dubroy94383362010-10-29 15:03:24 -07002214 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
Adam Cohen19f37922012-03-21 11:59:11 -07002215 lp.cellX = lp.tmpCellX = mTargetCell[0];
2216 lp.cellY = lp.tmpCellY = mTargetCell[1];
Adam Cohend41fbf52012-02-16 23:53:59 -08002217 lp.cellHSpan = item.spanX;
2218 lp.cellVSpan = item.spanY;
Adam Cohen19f37922012-03-21 11:59:11 -07002219 lp.isLockedToGrid = true;
Winson Chung3d503fb2011-07-13 17:25:49 -07002220 cell.setId(LauncherModel.getCellLayoutChildId(container, mDragInfo.screen,
Patrick Dubroy94383362010-10-29 15:03:24 -07002221 mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY));
2222
Winson Chung3d503fb2011-07-13 17:25:49 -07002223 if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
2224 cell instanceof LauncherAppWidgetHostView) {
Adam Cohend4844c32011-02-18 19:25:06 -08002225 final CellLayout cellLayout = dropTargetLayout;
2226 // We post this call so that the widget has a chance to be placed
2227 // in its final location
2228
2229 final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell;
2230 AppWidgetProviderInfo pinfo = hostView.getAppWidgetInfo();
Adam Cohenc0a5df92012-04-02 16:53:26 -07002231 if (pinfo != null &&
2232 pinfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) {
Adam Cohend41fbf52012-02-16 23:53:59 -08002233 final Runnable addResizeFrame = new Runnable() {
Adam Cohend4844c32011-02-18 19:25:06 -08002234 public void run() {
Adam Cohen716b51e2011-06-30 12:09:54 -07002235 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohenc0dcf592011-06-01 15:30:43 -07002236 dragLayer.addResizeFrame(info, hostView, cellLayout);
Adam Cohend4844c32011-02-18 19:25:06 -08002237 }
Adam Cohen26976d92011-03-22 15:33:33 -07002238 };
Adam Cohend41fbf52012-02-16 23:53:59 -08002239 resizeRunnable = (new Runnable() {
Adam Cohen26976d92011-03-22 15:33:33 -07002240 public void run() {
2241 if (!isPageMoving()) {
Adam Cohend41fbf52012-02-16 23:53:59 -08002242 addResizeFrame.run();
Adam Cohen26976d92011-03-22 15:33:33 -07002243 } else {
Adam Cohend41fbf52012-02-16 23:53:59 -08002244 mDelayedResizeRunnable = addResizeFrame;
Adam Cohen26976d92011-03-22 15:33:33 -07002245 }
2246 }
Adam Cohend4844c32011-02-18 19:25:06 -08002247 });
2248 }
2249 }
2250
Winson Chung3d503fb2011-07-13 17:25:49 -07002251 LauncherModel.moveItemInDatabase(mLauncher, info, container, screen, lp.cellX,
2252 lp.cellY);
Adam Cohend41fbf52012-02-16 23:53:59 -08002253 } else {
2254 // If we can't find a drop location, we return the item to its original position
2255 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
2256 mTargetCell[0] = lp.cellX;
2257 mTargetCell[1] = lp.cellY;
Adam Cohenf1dcdf62012-05-10 16:51:52 -07002258 CellLayout layout = (CellLayout) cell.getParent().getParent();
2259 layout.markCellsAsOccupiedForView(cell);
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002260 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002261 }
Patrick Dubroyce34a972010-10-19 10:34:32 -07002262
Michael Jurka8c920dd2011-01-20 14:16:56 -08002263 final CellLayout parent = (CellLayout) cell.getParent().getParent();
Adam Cohend41fbf52012-02-16 23:53:59 -08002264 final Runnable finalResizeRunnable = resizeRunnable;
Patrick Dubroyce34a972010-10-19 10:34:32 -07002265 // Prepare it to be animated into its new position
2266 // This must be called after the view has been re-parented
Adam Cohend41fbf52012-02-16 23:53:59 -08002267 final Runnable onCompleteRunnable = new Runnable() {
Michael Jurkad74c9842011-07-10 12:44:21 -07002268 @Override
2269 public void run() {
2270 mAnimatingViewIntoPlace = false;
2271 updateChildrenLayersEnabled();
Adam Cohend41fbf52012-02-16 23:53:59 -08002272 if (finalResizeRunnable != null) {
2273 finalResizeRunnable.run();
2274 }
Michael Jurkad74c9842011-07-10 12:44:21 -07002275 }
2276 };
2277 mAnimatingViewIntoPlace = true;
Adam Cohenfc53cd22011-07-20 15:45:11 -07002278 if (d.dragView.hasDrawn()) {
Adam Cohend41fbf52012-02-16 23:53:59 -08002279 final ItemInfo info = (ItemInfo) cell.getTag();
2280 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET) {
2281 int animationType = resizeOnDrop ? ANIMATE_INTO_POSITION_AND_RESIZE :
2282 ANIMATE_INTO_POSITION_AND_DISAPPEAR;
2283 animateWidgetDrop(info, parent, d.dragView,
2284 onCompleteRunnable, animationType, cell, false);
2285 } else {
Adam Cohen85b467b2012-02-29 15:38:46 -08002286 int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION;
2287 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration,
2288 onCompleteRunnable, this);
Adam Cohend41fbf52012-02-16 23:53:59 -08002289 }
Adam Cohenfc53cd22011-07-20 15:45:11 -07002290 } else {
Winson Chung7bd1bbb2012-02-13 18:29:29 -08002291 d.deferDragViewCleanupPostAnimation = false;
Adam Cohenfc53cd22011-07-20 15:45:11 -07002292 cell.setVisibility(VISIBLE);
2293 }
Adam Cohen716b51e2011-06-30 12:09:54 -07002294 parent.onDropChild(cell);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002295 }
2296 }
2297
Adam Cohened51cc92011-08-01 20:28:08 -07002298 public void setFinalScrollForPageChange(int screen) {
2299 if (screen >= 0) {
2300 mSavedScrollX = getScrollX();
2301 CellLayout cl = (CellLayout) getChildAt(screen);
2302 mSavedTranslationX = cl.getTranslationX();
2303 mSavedRotationY = cl.getRotationY();
2304 final int newX = getChildOffset(screen) - getRelativeChildOffset(screen);
2305 setScrollX(newX);
2306 cl.setTranslationX(0f);
2307 cl.setRotationY(0f);
2308 }
2309 }
2310
2311 public void resetFinalScrollForPageChange(int screen) {
2312 if (screen >= 0) {
2313 CellLayout cl = (CellLayout) getChildAt(screen);
2314 setScrollX(mSavedScrollX);
2315 cl.setTranslationX(mSavedTranslationX);
2316 cl.setRotationY(mSavedRotationY);
2317 }
2318 }
2319
Adam Cohen76078c42011-06-09 15:06:52 -07002320 public void getViewLocationRelativeToSelf(View v, int[] location) {
Adam Cohen8dfcba42011-07-07 16:38:18 -07002321 getLocationInWindow(location);
Adam Cohene3e27a82011-04-15 12:07:39 -07002322 int x = location[0];
2323 int y = location[1];
2324
Adam Cohen8dfcba42011-07-07 16:38:18 -07002325 v.getLocationInWindow(location);
Adam Cohene3e27a82011-04-15 12:07:39 -07002326 int vX = location[0];
2327 int vY = location[1];
2328
2329 location[0] = vX - x;
2330 location[1] = vY - y;
2331 }
2332
Adam Cohencb3382b2011-05-24 14:07:08 -07002333 public void onDragEnter(DragObject d) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002334 mDragEnforcer.onDragEnter();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002335 mCreateUserFolderOnDrop = false;
2336 mAddToExistingFolderOnDrop = false;
2337
2338 mDropToLayout = null;
2339 CellLayout layout = getCurrentDropLayout();
2340 setCurrentDropLayout(layout);
2341 setCurrentDragOverlappingLayout(layout);
Winson Chungb26f3d62011-06-02 10:49:29 -07002342
Winson Chungc07918d2011-07-01 15:35:26 -07002343 // Because we don't have space in the Phone UI (the CellLayouts run to the edge) we
2344 // don't need to show the outlines
2345 if (LauncherApplication.isScreenLarge()) {
2346 showOutlines();
Michael Jurkad718d6a2010-10-14 15:35:17 -07002347 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002348 }
2349
Adam Cohena897f392012-04-27 18:12:05 -07002350 static Rect getCellLayoutMetrics(Launcher launcher, int orientation) {
2351 Resources res = launcher.getResources();
2352 Display display = launcher.getWindowManager().getDefaultDisplay();
2353 Point smallestSize = new Point();
2354 Point largestSize = new Point();
2355 display.getCurrentSizeRange(smallestSize, largestSize);
2356 if (orientation == CellLayout.LANDSCAPE) {
2357 if (mLandscapeCellLayoutMetrics == null) {
2358 int paddingLeft = res.getDimensionPixelSize(R.dimen.workspace_left_padding_land);
2359 int paddingRight = res.getDimensionPixelSize(R.dimen.workspace_right_padding_land);
2360 int paddingTop = res.getDimensionPixelSize(R.dimen.workspace_top_padding_land);
2361 int paddingBottom = res.getDimensionPixelSize(R.dimen.workspace_bottom_padding_land);
2362 int width = largestSize.x - paddingLeft - paddingRight;
2363 int height = smallestSize.y - paddingTop - paddingBottom;
2364 mLandscapeCellLayoutMetrics = new Rect();
2365 CellLayout.getMetrics(mLandscapeCellLayoutMetrics, res,
2366 width, height, LauncherModel.getCellCountX(), LauncherModel.getCellCountY(),
2367 orientation);
2368 }
2369 return mLandscapeCellLayoutMetrics;
2370 } else if (orientation == CellLayout.PORTRAIT) {
2371 if (mPortraitCellLayoutMetrics == null) {
2372 int paddingLeft = res.getDimensionPixelSize(R.dimen.workspace_left_padding_land);
2373 int paddingRight = res.getDimensionPixelSize(R.dimen.workspace_right_padding_land);
2374 int paddingTop = res.getDimensionPixelSize(R.dimen.workspace_top_padding_land);
2375 int paddingBottom = res.getDimensionPixelSize(R.dimen.workspace_bottom_padding_land);
2376 int width = smallestSize.x - paddingLeft - paddingRight;
2377 int height = largestSize.y - paddingTop - paddingBottom;
2378 mPortraitCellLayoutMetrics = new Rect();
2379 CellLayout.getMetrics(mPortraitCellLayoutMetrics, res,
2380 width, height, LauncherModel.getCellCountX(), LauncherModel.getCellCountY(),
2381 orientation);
2382 }
2383 return mPortraitCellLayoutMetrics;
2384 }
2385 return null;
2386 }
2387
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002388 public void onDragExit(DragObject d) {
2389 mDragEnforcer.onDragExit();
Winson Chungc07918d2011-07-01 15:35:26 -07002390
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002391 // Here we store the final page that will be dropped to, if the workspace in fact
2392 // receives the drop
2393 if (mInScrollArea) {
2394 mDropToLayout = mDragOverlappingLayout;
2395 } else {
2396 mDropToLayout = mDragTargetLayout;
2397 }
2398
2399 if (mDragMode == DRAG_MODE_CREATE_FOLDER) {
2400 mCreateUserFolderOnDrop = true;
2401 } else if (mDragMode == DRAG_MODE_ADD_TO_FOLDER) {
2402 mAddToExistingFolderOnDrop = true;
Adam Cohen482ed822012-03-02 14:15:13 -08002403 }
2404
Winson Chungc07918d2011-07-01 15:35:26 -07002405 // Reset the scroll area and previous drag target
2406 onResetScrollArea();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002407 setCurrentDropLayout(null);
2408 setCurrentDragOverlappingLayout(null);
Winson Chungc07918d2011-07-01 15:35:26 -07002409
Adam Cohen74c28d12011-11-18 14:17:11 -08002410 mSpringLoadedDragController.cancel();
Winson Chungc07918d2011-07-01 15:35:26 -07002411
2412 if (!mIsPageMoving) {
2413 hideOutlines();
2414 }
2415 }
2416
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002417 void setCurrentDropLayout(CellLayout layout) {
2418 if (mDragTargetLayout != null) {
2419 mDragTargetLayout.revertTempState();
2420 mDragTargetLayout.onDragExit();
2421 }
2422 mDragTargetLayout = layout;
2423 if (mDragTargetLayout != null) {
2424 mDragTargetLayout.onDragEnter();
2425 }
2426 cleanupReorder(true);
2427 cleanupFolderCreation();
2428 setCurrentDropOverCell(-1, -1);
2429 }
2430
2431 void setCurrentDragOverlappingLayout(CellLayout layout) {
2432 if (mDragOverlappingLayout != null) {
2433 mDragOverlappingLayout.setIsDragOverlapping(false);
2434 }
2435 mDragOverlappingLayout = layout;
2436 if (mDragOverlappingLayout != null) {
2437 mDragOverlappingLayout.setIsDragOverlapping(true);
2438 }
2439 invalidate();
2440 }
2441
2442 void setCurrentDropOverCell(int x, int y) {
2443 if (x != mDragOverX || y != mDragOverY) {
2444 mDragOverX = x;
2445 mDragOverY = y;
2446 setDragMode(DRAG_MODE_NONE);
2447 }
2448 }
2449
2450 void setDragMode(int dragMode) {
2451 if (dragMode != mDragMode) {
2452 if (dragMode == DRAG_MODE_NONE) {
2453 cleanupAddToFolder();
2454 // We don't want to cancel the re-order alarm every time the target cell changes
2455 // as this feels to slow / unresponsive.
2456 cleanupReorder(false);
2457 cleanupFolderCreation();
2458 } else if (dragMode == DRAG_MODE_ADD_TO_FOLDER) {
2459 cleanupReorder(true);
2460 cleanupFolderCreation();
2461 } else if (dragMode == DRAG_MODE_CREATE_FOLDER) {
2462 cleanupAddToFolder();
2463 cleanupReorder(true);
2464 } else if (dragMode == DRAG_MODE_REORDER) {
2465 cleanupAddToFolder();
2466 cleanupFolderCreation();
2467 }
2468 mDragMode = dragMode;
2469 }
2470 }
2471
2472 private void cleanupFolderCreation() {
2473 if (mDragFolderRingAnimator != null) {
2474 mDragFolderRingAnimator.animateToNaturalState();
2475 }
2476 mFolderCreationAlarm.cancelAlarm();
2477 }
2478
2479 private void cleanupAddToFolder() {
2480 if (mDragOverFolderIcon != null) {
2481 mDragOverFolderIcon.onDragExit(null);
2482 mDragOverFolderIcon = null;
2483 }
2484 }
2485
2486 private void cleanupReorder(boolean cancelAlarm) {
2487 // Any pending reorders are canceled
2488 if (cancelAlarm) {
2489 mReorderAlarm.cancelAlarm();
2490 }
2491 mLastReorderX = -1;
2492 mLastReorderY = -1;
Winson Chungc07918d2011-07-01 15:35:26 -07002493 }
2494
Adam Cohencb3382b2011-05-24 14:07:08 -07002495 public DropTarget getDropTargetDelegate(DragObject d) {
Patrick Dubroy440c3602010-07-13 17:50:32 -07002496 return null;
2497 }
2498
Michael Jurka4516c112010-10-07 15:13:47 -07002499 /*
2500 *
2501 * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
2502 * coordinate space. The argument xy is modified with the return result.
2503 *
2504 */
2505 void mapPointFromSelfToChild(View v, float[] xy) {
2506 mapPointFromSelfToChild(v, xy, null);
2507 }
2508
2509 /*
2510 *
2511 * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
2512 * coordinate space. The argument xy is modified with the return result.
2513 *
2514 * if cachedInverseMatrix is not null, this method will just use that matrix instead of
Michael Jurkad718d6a2010-10-14 15:35:17 -07002515 * computing it itself; we use this to avoid redundant matrix inversions in
Michael Jurka4516c112010-10-07 15:13:47 -07002516 * findMatchingPageForDragOver
2517 *
2518 */
2519 void mapPointFromSelfToChild(View v, float[] xy, Matrix cachedInverseMatrix) {
2520 if (cachedInverseMatrix == null) {
2521 v.getMatrix().invert(mTempInverseMatrix);
2522 cachedInverseMatrix = mTempInverseMatrix;
2523 }
Michael Jurka8b805b12012-04-18 14:23:14 -07002524 int scrollX = getScrollX();
Winson Chung3bc21c32012-01-20 13:59:18 -08002525 if (mNextPage != INVALID_PAGE) {
2526 scrollX = mScroller.getFinalX();
2527 }
2528 xy[0] = xy[0] + scrollX - v.getLeft();
Michael Jurka8b805b12012-04-18 14:23:14 -07002529 xy[1] = xy[1] + getScrollY() - v.getTop();
Michael Jurka4516c112010-10-07 15:13:47 -07002530 cachedInverseMatrix.mapPoints(xy);
2531 }
2532
2533 /*
Winson Chung3d503fb2011-07-13 17:25:49 -07002534 * Maps a point from the Workspace's coordinate system to another sibling view's. (Workspace
2535 * covers the full screen)
2536 */
2537 void mapPointFromSelfToSibling(View v, float[] xy) {
2538 xy[0] = xy[0] - v.getLeft();
2539 xy[1] = xy[1] - v.getTop();
2540 }
2541
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002542 void mapPointFromSelfToHotseatLayout(Hotseat hotseat, float[] xy) {
2543 xy[0] = xy[0] - hotseat.getLeft() - hotseat.getLayout().getLeft();
2544 xy[1] = xy[1] - hotseat.getTop() - hotseat.getLayout().getTop();
2545 }
2546
Winson Chung3d503fb2011-07-13 17:25:49 -07002547 /*
Michael Jurka4516c112010-10-07 15:13:47 -07002548 *
2549 * Convert the 2D coordinate xy from this CellLayout's coordinate space to
2550 * the parent View's coordinate space. The argument xy is modified with the return result.
2551 *
2552 */
2553 void mapPointFromChildToSelf(View v, float[] xy) {
2554 v.getMatrix().mapPoints(xy);
Michael Jurka8b805b12012-04-18 14:23:14 -07002555 int scrollX = getScrollX();
Winson Chung3bc21c32012-01-20 13:59:18 -08002556 if (mNextPage != INVALID_PAGE) {
2557 scrollX = mScroller.getFinalX();
2558 }
2559 xy[0] -= (scrollX - v.getLeft());
Michael Jurka8b805b12012-04-18 14:23:14 -07002560 xy[1] -= (getScrollY() - v.getTop());
Michael Jurka4516c112010-10-07 15:13:47 -07002561 }
2562
Adam Cohene3e27a82011-04-15 12:07:39 -07002563 static private float squaredDistance(float[] point1, float[] point2) {
Michael Jurka4516c112010-10-07 15:13:47 -07002564 float distanceX = point1[0] - point2[0];
2565 float distanceY = point2[1] - point2[1];
2566 return distanceX * distanceX + distanceY * distanceY;
Adam Cohene3e27a82011-04-15 12:07:39 -07002567 }
Michael Jurka0280c3b2010-09-17 15:00:07 -07002568
Michael Jurka4516c112010-10-07 15:13:47 -07002569 /*
2570 *
2571 * Returns true if the passed CellLayout cl overlaps with dragView
2572 *
2573 */
2574 boolean overlaps(CellLayout cl, DragView dragView,
2575 int dragViewX, int dragViewY, Matrix cachedInverseMatrix) {
2576 // Transform the coordinates of the item being dragged to the CellLayout's coordinates
2577 final float[] draggedItemTopLeft = mTempDragCoordinates;
Michael Jurkaf12c75c2011-01-25 22:41:40 -08002578 draggedItemTopLeft[0] = dragViewX;
2579 draggedItemTopLeft[1] = dragViewY;
Michael Jurka4516c112010-10-07 15:13:47 -07002580 final float[] draggedItemBottomRight = mTempDragBottomRightCoordinates;
Michael Jurkaf12c75c2011-01-25 22:41:40 -08002581 draggedItemBottomRight[0] = draggedItemTopLeft[0] + dragView.getDragRegionWidth();
2582 draggedItemBottomRight[1] = draggedItemTopLeft[1] + dragView.getDragRegionHeight();
Michael Jurkaa63c4522010-08-19 13:52:27 -07002583
Michael Jurka4516c112010-10-07 15:13:47 -07002584 // Transform the dragged item's top left coordinates
2585 // to the CellLayout's local coordinates
2586 mapPointFromSelfToChild(cl, draggedItemTopLeft, cachedInverseMatrix);
2587 float overlapRegionLeft = Math.max(0f, draggedItemTopLeft[0]);
2588 float overlapRegionTop = Math.max(0f, draggedItemTopLeft[1]);
2589
2590 if (overlapRegionLeft <= cl.getWidth() && overlapRegionTop >= 0) {
2591 // Transform the dragged item's bottom right coordinates
2592 // to the CellLayout's local coordinates
2593 mapPointFromSelfToChild(cl, draggedItemBottomRight, cachedInverseMatrix);
2594 float overlapRegionRight = Math.min(cl.getWidth(), draggedItemBottomRight[0]);
2595 float overlapRegionBottom = Math.min(cl.getHeight(), draggedItemBottomRight[1]);
2596
2597 if (overlapRegionRight >= 0 && overlapRegionBottom <= cl.getHeight()) {
2598 float overlap = (overlapRegionRight - overlapRegionLeft) *
2599 (overlapRegionBottom - overlapRegionTop);
2600 if (overlap > 0) {
2601 return true;
2602 }
2603 }
2604 }
2605 return false;
2606 }
2607
2608 /*
2609 *
2610 * This method returns the CellLayout that is currently being dragged to. In order to drag
2611 * to a CellLayout, either the touch point must be directly over the CellLayout, or as a second
2612 * strategy, we see if the dragView is overlapping any CellLayout and choose the closest one
2613 *
2614 * Return null if no CellLayout is currently being dragged over
2615 *
2616 */
2617 private CellLayout findMatchingPageForDragOver(
Adam Cohen00618752011-07-20 12:06:04 -07002618 DragView dragView, float originX, float originY, boolean exact) {
Michael Jurka4516c112010-10-07 15:13:47 -07002619 // We loop through all the screens (ie CellLayouts) and see which ones overlap
2620 // with the item being dragged and then choose the one that's closest to the touch point
Michael Jurkaa63c4522010-08-19 13:52:27 -07002621 final int screenCount = getChildCount();
2622 CellLayout bestMatchingScreen = null;
Michael Jurka0280c3b2010-09-17 15:00:07 -07002623 float smallestDistSoFar = Float.MAX_VALUE;
Michael Jurka4516c112010-10-07 15:13:47 -07002624
Michael Jurkaa63c4522010-08-19 13:52:27 -07002625 for (int i = 0; i < screenCount; i++) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002626 CellLayout cl = (CellLayout) getChildAt(i);
Michael Jurkaa63c4522010-08-19 13:52:27 -07002627
Adam Cohen00618752011-07-20 12:06:04 -07002628 final float[] touchXy = {originX, originY};
Michael Jurka4516c112010-10-07 15:13:47 -07002629 // Transform the touch coordinates to the CellLayout's local coordinates
2630 // If the touch point is within the bounds of the cell layout, we can return immediately
Michael Jurka0280c3b2010-09-17 15:00:07 -07002631 cl.getMatrix().invert(mTempInverseMatrix);
Michael Jurka4516c112010-10-07 15:13:47 -07002632 mapPointFromSelfToChild(cl, touchXy, mTempInverseMatrix);
Michael Jurkaa63c4522010-08-19 13:52:27 -07002633
Michael Jurka4516c112010-10-07 15:13:47 -07002634 if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() &&
2635 touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) {
2636 return cl;
2637 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07002638
Winson Chung96ef4092011-11-22 12:25:14 -08002639 if (!exact) {
Michael Jurka4516c112010-10-07 15:13:47 -07002640 // Get the center of the cell layout in screen coordinates
2641 final float[] cellLayoutCenter = mTempCellLayoutCenterCoordinates;
2642 cellLayoutCenter[0] = cl.getWidth()/2;
2643 cellLayoutCenter[1] = cl.getHeight()/2;
2644 mapPointFromChildToSelf(cl, cellLayoutCenter);
Michael Jurka0280c3b2010-09-17 15:00:07 -07002645
Adam Cohen00618752011-07-20 12:06:04 -07002646 touchXy[0] = originX;
2647 touchXy[1] = originY;
Michael Jurka0280c3b2010-09-17 15:00:07 -07002648
Michael Jurka4516c112010-10-07 15:13:47 -07002649 // Calculate the distance between the center of the CellLayout
2650 // and the touch point
2651 float dist = squaredDistance(touchXy, cellLayoutCenter);
2652
2653 if (dist < smallestDistSoFar) {
Michael Jurka0280c3b2010-09-17 15:00:07 -07002654 smallestDistSoFar = dist;
Michael Jurkaa63c4522010-08-19 13:52:27 -07002655 bestMatchingScreen = cl;
Michael Jurkaa63c4522010-08-19 13:52:27 -07002656 }
Michael Jurka4516c112010-10-07 15:13:47 -07002657 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07002658 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07002659 return bestMatchingScreen;
2660 }
2661
Adam Cohene3e27a82011-04-15 12:07:39 -07002662 // This is used to compute the visual center of the dragView. This point is then
2663 // used to visualize drop locations and determine where to drop an item. The idea is that
2664 // the visual center represents the user's interpretation of where the item is, and hence
2665 // is the appropriate point to use when determining drop location.
2666 private float[] getDragViewVisualCenter(int x, int y, int xOffset, int yOffset,
2667 DragView dragView, float[] recycle) {
2668 float res[];
2669 if (recycle == null) {
2670 res = new float[2];
2671 } else {
2672 res = recycle;
2673 }
2674
2675 // First off, the drag view has been shifted in a way that is not represented in the
2676 // x and y values or the x/yOffsets. Here we account for that shift.
2677 x += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetX);
2678 y += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY);
2679
2680 // These represent the visual top and left of drag view if a dragRect was provided.
2681 // If a dragRect was not provided, then they correspond to the actual view left and
2682 // top, as the dragRect is in that case taken to be the entire dragView.
2683 // R.dimen.dragViewOffsetY.
2684 int left = x - xOffset;
2685 int top = y - yOffset;
2686
2687 // In order to find the visual center, we shift by half the dragRect
2688 res[0] = left + dragView.getDragRegion().width() / 2;
2689 res[1] = top + dragView.getDragRegion().height() / 2;
2690
2691 return res;
2692 }
2693
Winson Chungea359c62011-08-03 17:06:35 -07002694 private boolean isDragWidget(DragObject d) {
2695 return (d.dragInfo instanceof LauncherAppWidgetInfo ||
2696 d.dragInfo instanceof PendingAddWidgetInfo);
2697 }
2698 private boolean isExternalDragWidget(DragObject d) {
2699 return d.dragSource != this && isDragWidget(d);
2700 }
2701
Adam Cohencb3382b2011-05-24 14:07:08 -07002702 public void onDragOver(DragObject d) {
Winson Chungc07918d2011-07-01 15:35:26 -07002703 // Skip drag over events while we are dragging over side pages
Adam Cohen82ac8a22012-02-14 16:27:49 -08002704 if (mInScrollArea || mIsSwitchingState || mState == State.SMALL) return;
Winson Chungc07918d2011-07-01 15:35:26 -07002705
Winson Chung4afe9b32011-07-27 17:46:20 -07002706 Rect r = new Rect();
Winson Chung3d503fb2011-07-13 17:25:49 -07002707 CellLayout layout = null;
Winson Chungc07918d2011-07-01 15:35:26 -07002708 ItemInfo item = (ItemInfo) d.dragInfo;
2709
2710 // Ensure that we have proper spans for the item that we are dropping
2711 if (item.spanX < 0 || item.spanY < 0) throw new RuntimeException("Improper spans found");
Adam Cohen00618752011-07-20 12:06:04 -07002712 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset,
Winson Chung4afe9b32011-07-27 17:46:20 -07002713 d.dragView, mDragViewVisualCenter);
Adam Cohen00618752011-07-20 12:06:04 -07002714
Adam Cohen482ed822012-03-02 14:15:13 -08002715 final View child = (mDragInfo == null) ? null : mDragInfo.cell;
Winson Chungc07918d2011-07-01 15:35:26 -07002716 // Identify whether we have dragged over a side page
Michael Jurkad74c9842011-07-10 12:44:21 -07002717 if (isSmall()) {
Winson Chungea359c62011-08-03 17:06:35 -07002718 if (mLauncher.getHotseat() != null && !isExternalDragWidget(d)) {
Winson Chung4afe9b32011-07-27 17:46:20 -07002719 mLauncher.getHotseat().getHitRect(r);
2720 if (r.contains(d.x, d.y)) {
2721 layout = mLauncher.getHotseat().getLayout();
2722 }
2723 }
2724 if (layout == null) {
Winson Chung96ef4092011-11-22 12:25:14 -08002725 layout = findMatchingPageForDragOver(d.dragView, d.x, d.y, false);
Winson Chung4afe9b32011-07-27 17:46:20 -07002726 }
Winson Chungc07918d2011-07-01 15:35:26 -07002727 if (layout != mDragTargetLayout) {
Winson Chungc07918d2011-07-01 15:35:26 -07002728
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002729 setCurrentDropLayout(layout);
2730 setCurrentDragOverlappingLayout(layout);
Winson Chungc07918d2011-07-01 15:35:26 -07002731
Michael Jurkad74c9842011-07-10 12:44:21 -07002732 boolean isInSpringLoadedMode = (mState == State.SPRING_LOADED);
Winson Chungc07918d2011-07-01 15:35:26 -07002733 if (isInSpringLoadedMode) {
Winson Chung4afe9b32011-07-27 17:46:20 -07002734 if (mLauncher.isHotseatLayout(layout)) {
2735 mSpringLoadedDragController.cancel();
2736 } else {
2737 mSpringLoadedDragController.setAlarm(mDragTargetLayout);
2738 }
Winson Chungc07918d2011-07-01 15:35:26 -07002739 }
2740 }
2741 } else {
Winson Chung3d503fb2011-07-13 17:25:49 -07002742 // Test to see if we are over the hotseat otherwise just use the current page
Winson Chungea359c62011-08-03 17:06:35 -07002743 if (mLauncher.getHotseat() != null && !isDragWidget(d)) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002744 mLauncher.getHotseat().getHitRect(r);
2745 if (r.contains(d.x, d.y)) {
2746 layout = mLauncher.getHotseat().getLayout();
2747 }
2748 }
2749 if (layout == null) {
2750 layout = getCurrentDropLayout();
2751 }
Winson Chungc07918d2011-07-01 15:35:26 -07002752 if (layout != mDragTargetLayout) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002753 setCurrentDropLayout(layout);
2754 setCurrentDragOverlappingLayout(layout);
Winson Chungc07918d2011-07-01 15:35:26 -07002755 }
2756 }
2757
2758 // Handle the drag over
2759 if (mDragTargetLayout != null) {
Winson Chungc07918d2011-07-01 15:35:26 -07002760 // We want the point to be mapped to the dragTarget.
Winson Chung3d503fb2011-07-13 17:25:49 -07002761 if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002762 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
Winson Chung3d503fb2011-07-13 17:25:49 -07002763 } else {
2764 mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
2765 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002766
Winson Chungc07918d2011-07-01 15:35:26 -07002767 ItemInfo info = (ItemInfo) d.dragInfo;
Patrick Dubroy1262e362010-10-06 15:49:50 -07002768
Winson Chungc07918d2011-07-01 15:35:26 -07002769 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
2770 (int) mDragViewVisualCenter[1], 1, 1, mDragTargetLayout, mTargetCell);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002771
2772 setCurrentDropOverCell(mTargetCell[0], mTargetCell[1]);
2773
Adam Cohen482ed822012-03-02 14:15:13 -08002774 float targetCellDistance = mDragTargetLayout.getDistanceFromCell(
2775 mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell);
2776
Winson Chungc07918d2011-07-01 15:35:26 -07002777 final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0],
2778 mTargetCell[1]);
Winson Chung785d2eb2011-04-14 16:08:02 -07002779
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002780 manageFolderFeedback(info, mDragTargetLayout, mTargetCell,
2781 targetCellDistance, dragOverView);
Adam Cohen482ed822012-03-02 14:15:13 -08002782
2783 int minSpanX = item.spanX;
2784 int minSpanY = item.spanY;
2785 if (item.minSpanX > 0 && item.minSpanY > 0) {
2786 minSpanX = item.minSpanX;
2787 minSpanY = item.minSpanY;
2788 }
2789
Adam Cohen19f37922012-03-21 11:59:11 -07002790 int[] reorderPosition = new int[2];
2791 reorderPosition = findNearestArea((int) mDragViewVisualCenter[0],
2792 (int) mDragViewVisualCenter[1], item.spanX, item.spanY, mDragTargetLayout,
2793 reorderPosition);
2794
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002795 boolean nearestDropOccupied = mDragTargetLayout.isNearestDropLocationOccupied((int)
2796 mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], item.spanX,
2797 item.spanY, child, mTargetCell);
2798
2799 if (!nearestDropOccupied) {
Adam Cohen19f37922012-03-21 11:59:11 -07002800 mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
2801 (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
2802 mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, false,
2803 d.dragView.getDragVisualizeOffset(), d.dragView.getDragRegion());
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002804 } else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
2805 && !mReorderAlarm.alarmPending() && (mLastReorderX != reorderPosition[0] ||
2806 mLastReorderY != reorderPosition[1])) {
Adam Cohen19f37922012-03-21 11:59:11 -07002807 // Otherwise, if we aren't adding to or creating a folder and there's no pending
2808 // reorder, then we schedule a reorder
Adam Cohen482ed822012-03-02 14:15:13 -08002809 ReorderAlarmListener listener = new ReorderAlarmListener(mDragViewVisualCenter,
2810 minSpanX, minSpanY, item.spanX, item.spanY, d.dragView, child);
2811 mReorderAlarm.setOnAlarmListener(listener);
2812 mReorderAlarm.setAlarm(REORDER_TIMEOUT);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002813 }
2814
2815 if (mDragMode == DRAG_MODE_CREATE_FOLDER || mDragMode == DRAG_MODE_ADD_TO_FOLDER ||
2816 !nearestDropOccupied) {
2817 if (mDragTargetLayout != null) {
2818 mDragTargetLayout.revertTempState();
Michael Jurkad3ef3062010-11-23 16:23:58 -08002819 }
2820 }
Adam Cohen482ed822012-03-02 14:15:13 -08002821 }
2822 }
2823
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002824 private void manageFolderFeedback(ItemInfo info, CellLayout targetLayout,
2825 int[] targetCell, float distance, View dragOverView) {
Adam Cohen482ed822012-03-02 14:15:13 -08002826 boolean userFolderPending = willCreateUserFolder(info, targetLayout, targetCell, distance,
2827 false);
2828
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002829 if (mDragMode == DRAG_MODE_NONE && userFolderPending &&
2830 !mFolderCreationAlarm.alarmPending()) {
Adam Cohen482ed822012-03-02 14:15:13 -08002831 mFolderCreationAlarm.setOnAlarmListener(new
2832 FolderCreationAlarmListener(targetLayout, targetCell[0], targetCell[1]));
2833 mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002834 return;
Adam Cohen482ed822012-03-02 14:15:13 -08002835 }
2836
2837 boolean willAddToFolder =
2838 willAddToExistingUserFolder(info, targetLayout, targetCell, distance);
2839
2840 if (willAddToFolder && mDragMode == DRAG_MODE_NONE) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002841 mDragOverFolderIcon = ((FolderIcon) dragOverView);
2842 mAddToExistingFolderOnDrop = true;
2843 mDragOverFolderIcon.onDragEnter(info);
Adam Cohen482ed822012-03-02 14:15:13 -08002844 if (targetLayout != null) {
2845 targetLayout.clearDragOutlines();
Winson Chungc07918d2011-07-01 15:35:26 -07002846 }
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002847 setDragMode(DRAG_MODE_ADD_TO_FOLDER);
2848 return;
Patrick Dubroy976ebec2010-08-04 20:03:37 -07002849 }
Adam Cohen482ed822012-03-02 14:15:13 -08002850
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002851 if (mDragMode == DRAG_MODE_ADD_TO_FOLDER && !willAddToFolder) {
2852 setDragMode(DRAG_MODE_NONE);
2853 }
2854 if (mDragMode == DRAG_MODE_CREATE_FOLDER && !userFolderPending) {
2855 setDragMode(DRAG_MODE_NONE);
Adam Cohen482ed822012-03-02 14:15:13 -08002856 }
2857
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002858 return;
Adam Cohenc0dcf592011-06-01 15:30:43 -07002859 }
2860
Adam Cohen19072da2011-05-31 14:30:45 -07002861 class FolderCreationAlarmListener implements OnAlarmListener {
Adam Cohen69ce2e52011-07-03 19:25:21 -07002862 CellLayout layout;
2863 int cellX;
2864 int cellY;
Adam Cohen19072da2011-05-31 14:30:45 -07002865
Adam Cohen69ce2e52011-07-03 19:25:21 -07002866 public FolderCreationAlarmListener(CellLayout layout, int cellX, int cellY) {
2867 this.layout = layout;
2868 this.cellX = cellX;
2869 this.cellY = cellY;
Adam Cohen19072da2011-05-31 14:30:45 -07002870 }
2871
2872 public void onAlarm(Alarm alarm) {
Adam Cohen19072da2011-05-31 14:30:45 -07002873 if (mDragFolderRingAnimator == null) {
2874 mDragFolderRingAnimator = new FolderRingAnimator(mLauncher, null);
2875 }
Adam Cohen69ce2e52011-07-03 19:25:21 -07002876 mDragFolderRingAnimator.setCell(cellX, cellY);
2877 mDragFolderRingAnimator.setCellLayout(layout);
Adam Cohen19072da2011-05-31 14:30:45 -07002878 mDragFolderRingAnimator.animateToAcceptState();
Adam Cohen69ce2e52011-07-03 19:25:21 -07002879 layout.showFolderAccept(mDragFolderRingAnimator);
2880 layout.clearDragOutlines();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002881 setDragMode(DRAG_MODE_CREATE_FOLDER);
Adam Cohen482ed822012-03-02 14:15:13 -08002882 }
2883 }
2884
2885 class ReorderAlarmListener implements OnAlarmListener {
2886 float[] dragViewCenter;
2887 int minSpanX, minSpanY, spanX, spanY;
2888 DragView dragView;
2889 View child;
2890
2891 public ReorderAlarmListener(float[] dragViewCenter, int minSpanX, int minSpanY, int spanX,
2892 int spanY, DragView dragView, View child) {
2893 this.dragViewCenter = dragViewCenter;
2894 this.minSpanX = minSpanX;
2895 this.minSpanY = minSpanY;
2896 this.spanX = spanX;
2897 this.spanY = spanY;
2898 this.child = child;
2899 this.dragView = dragView;
2900 }
2901
2902 public void onAlarm(Alarm alarm) {
2903 int[] resultSpan = new int[2];
Adam Cohen19f37922012-03-21 11:59:11 -07002904 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
2905 (int) mDragViewVisualCenter[1], spanX, spanY, mDragTargetLayout, mTargetCell);
2906 mLastReorderX = mTargetCell[0];
2907 mLastReorderY = mTargetCell[1];
2908
Adam Cohen482ed822012-03-02 14:15:13 -08002909 mTargetCell = mDragTargetLayout.createArea((int) mDragViewVisualCenter[0],
2910 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, spanX, spanY,
2911 child, mTargetCell, resultSpan, CellLayout.MODE_DRAG_OVER);
2912
Adam Cohen19f37922012-03-21 11:59:11 -07002913 if (mTargetCell[0] < 0 || mTargetCell[1] < 0) {
2914 mDragTargetLayout.revertTempState();
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002915 } else {
2916 setDragMode(DRAG_MODE_REORDER);
Adam Cohen19f37922012-03-21 11:59:11 -07002917 }
Adam Cohen482ed822012-03-02 14:15:13 -08002918
Adam Cohen482ed822012-03-02 14:15:13 -08002919 boolean resize = resultSpan[0] != spanX || resultSpan[1] != spanY;
2920 mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
2921 (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
2922 mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], resize,
2923 dragView.getDragVisualizeOffset(), dragView.getDragRegion());
Adam Cohen19072da2011-05-31 14:30:45 -07002924 }
2925 }
2926
Winson Chunga34abf82010-11-12 12:10:35 -08002927 @Override
2928 public void getHitRect(Rect outRect) {
2929 // We want the workspace to have the whole area of the display (it will find the correct
2930 // cell layout to drop to in the existing drag/drop logic.
Michael Jurka84f2ce72012-04-13 15:08:01 -07002931 outRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
Winson Chunga34abf82010-11-12 12:10:35 -08002932 }
2933
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07002934 /**
2935 * Add the item specified by dragInfo to the given layout.
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07002936 * @return true if successful
2937 */
Adam Cohen120980b2010-12-08 11:05:37 -08002938 public boolean addExternalItemToScreen(ItemInfo dragInfo, CellLayout layout) {
2939 if (layout.findCellForSpan(mTempEstimate, dragInfo.spanX, dragInfo.spanY)) {
Patrick Dubroybbaa75c2011-03-08 18:47:40 -08002940 onDropExternal(dragInfo.dropPos, (ItemInfo) dragInfo, (CellLayout) layout, false);
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07002941 return true;
2942 }
Winson Chung93eef082012-03-23 15:59:27 -07002943 mLauncher.showOutOfSpaceMessage(mLauncher.isHotseatLayout(layout));
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07002944 return false;
2945 }
2946
Adam Cohend5e42732011-03-28 17:33:39 -07002947 private void onDropExternal(int[] touchXY, Object dragInfo,
2948 CellLayout cellLayout, boolean insertAtFirst) {
Adam Cohene3e27a82011-04-15 12:07:39 -07002949 onDropExternal(touchXY, dragInfo, cellLayout, insertAtFirst, null);
Adam Cohend5e42732011-03-28 17:33:39 -07002950 }
2951
Adam Cohen120980b2010-12-08 11:05:37 -08002952 /**
2953 * Drop an item that didn't originate on one of the workspace screens.
2954 * It may have come from Launcher (e.g. from all apps or customize), or it may have
2955 * come from another app altogether.
2956 *
2957 * NOTE: This can also be called when we are outside of a drag event, when we want
2958 * to add an item to one of the workspace screens.
2959 */
Winson Chung557d6ed2011-07-08 15:34:52 -07002960 private void onDropExternal(final int[] touchXY, final Object dragInfo,
2961 final CellLayout cellLayout, boolean insertAtFirst, DragObject d) {
2962 final Runnable exitSpringLoadedRunnable = new Runnable() {
2963 @Override
2964 public void run() {
Adam Cohened66b2b2012-01-23 17:28:51 -08002965 mLauncher.exitSpringLoadedDragModeDelayed(true, false, null);
Winson Chung557d6ed2011-07-08 15:34:52 -07002966 }
2967 };
Adam Cohenb7e16182011-07-15 17:55:02 -07002968
2969 ItemInfo info = (ItemInfo) dragInfo;
2970 int spanX = info.spanX;
2971 int spanY = info.spanY;
2972 if (mDragInfo != null) {
2973 spanX = mDragInfo.spanX;
2974 spanY = mDragInfo.spanY;
2975 }
2976
Winson Chung3d503fb2011-07-13 17:25:49 -07002977 final long container = mLauncher.isHotseatLayout(cellLayout) ?
2978 LauncherSettings.Favorites.CONTAINER_HOTSEAT :
2979 LauncherSettings.Favorites.CONTAINER_DESKTOP;
Winson Chung557d6ed2011-07-08 15:34:52 -07002980 final int screen = indexOfChild(cellLayout);
Winson Chung3d503fb2011-07-13 17:25:49 -07002981 if (!mLauncher.isHotseatLayout(cellLayout) && screen != mCurrentPage
2982 && mState != State.SPRING_LOADED) {
Adam Cohen76078c42011-06-09 15:06:52 -07002983 snapToPage(screen);
2984 }
Adam Cohenb7e16182011-07-15 17:55:02 -07002985
2986 if (info instanceof PendingAddItemInfo) {
2987 final PendingAddItemInfo pendingInfo = (PendingAddItemInfo) dragInfo;
2988
Adam Cohen558baaf2011-08-15 15:22:57 -07002989 boolean findNearestVacantCell = true;
2990 if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
2991 mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
2992 cellLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08002993 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
2994 mDragViewVisualCenter[1], mTargetCell);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002995 if (willCreateUserFolder((ItemInfo) d.dragInfo, cellLayout, mTargetCell,
Adam Cohen482ed822012-03-02 14:15:13 -08002996 distance, true) || willAddToExistingUserFolder((ItemInfo) d.dragInfo,
Adam Cohenc6cc61d2012-04-04 12:47:08 -07002997 cellLayout, mTargetCell, distance)) {
Adam Cohen558baaf2011-08-15 15:22:57 -07002998 findNearestVacantCell = false;
2999 }
3000 }
Adam Cohen482ed822012-03-02 14:15:13 -08003001
Adam Cohend41fbf52012-02-16 23:53:59 -08003002 final ItemInfo item = (ItemInfo) d.dragInfo;
Adam Cohen558baaf2011-08-15 15:22:57 -07003003 if (findNearestVacantCell) {
Adam Cohen482ed822012-03-02 14:15:13 -08003004 int minSpanX = item.spanX;
3005 int minSpanY = item.spanY;
3006 if (item.minSpanX > 0 && item.minSpanY > 0) {
3007 minSpanX = item.minSpanX;
3008 minSpanY = item.minSpanY;
3009 }
Adam Cohend41fbf52012-02-16 23:53:59 -08003010 int[] resultSpan = new int[2];
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003011 mTargetCell = cellLayout.createArea((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08003012 (int) mDragViewVisualCenter[1], minSpanX, minSpanY, info.spanX, info.spanY,
3013 null, mTargetCell, resultSpan, CellLayout.MODE_ON_DROP_EXTERNAL);
Adam Cohend41fbf52012-02-16 23:53:59 -08003014 item.spanX = resultSpan[0];
3015 item.spanY = resultSpan[1];
Adam Cohen558baaf2011-08-15 15:22:57 -07003016 }
3017
Adam Cohenb7e16182011-07-15 17:55:02 -07003018 Runnable onAnimationCompleteRunnable = new Runnable() {
Winson Chung557d6ed2011-07-08 15:34:52 -07003019 @Override
3020 public void run() {
3021 // When dragging and dropping from customization tray, we deal with creating
3022 // widgets/shortcuts/folders in a slightly different way
Adam Cohenb7e16182011-07-15 17:55:02 -07003023 switch (pendingInfo.itemType) {
Winson Chung557d6ed2011-07-08 15:34:52 -07003024 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
Adam Cohend41fbf52012-02-16 23:53:59 -08003025 int span[] = new int[2];
3026 span[0] = item.spanX;
3027 span[1] = item.spanY;
Adam Cohenb7e16182011-07-15 17:55:02 -07003028 mLauncher.addAppWidgetFromDrop((PendingAddWidgetInfo) pendingInfo,
Adam Cohend41fbf52012-02-16 23:53:59 -08003029 container, screen, mTargetCell, span, null);
Winson Chung557d6ed2011-07-08 15:34:52 -07003030 break;
3031 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
Adam Cohenb7e16182011-07-15 17:55:02 -07003032 mLauncher.processShortcutFromDrop(pendingInfo.componentName,
Winson Chung3d503fb2011-07-13 17:25:49 -07003033 container, screen, mTargetCell, null);
Winson Chung557d6ed2011-07-08 15:34:52 -07003034 break;
3035 default:
Adam Cohenb7e16182011-07-15 17:55:02 -07003036 throw new IllegalStateException("Unknown item type: " +
3037 pendingInfo.itemType);
Winson Chung557d6ed2011-07-08 15:34:52 -07003038 }
Winson Chung557d6ed2011-07-08 15:34:52 -07003039 }
Adam Cohenb7e16182011-07-15 17:55:02 -07003040 };
Adam Cohend41fbf52012-02-16 23:53:59 -08003041 View finalView = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
3042 ? ((PendingAddWidgetInfo) pendingInfo).boundWidget : null;
3043 int animationStyle = ANIMATE_INTO_POSITION_AND_DISAPPEAR;
3044 if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET &&
3045 ((PendingAddWidgetInfo) pendingInfo).info.configure != null) {
3046 animationStyle = ANIMATE_INTO_POSITION_AND_REMAIN;
3047 }
3048 animateWidgetDrop(info, cellLayout, d.dragView, onAnimationCompleteRunnable,
3049 animationStyle, finalView, true);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07003050 } else {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003051 // This is for other drag/drop cases, like dragging from All Apps
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003052 View view = null;
3053
3054 switch (info.itemType) {
3055 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
3056 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
3057 if (info.container == NO_ID && info instanceof ApplicationInfo) {
3058 // Came from all apps -- make a copy
Michael Jurkac9d95c52011-08-29 14:03:34 -07003059 info = new ShortcutInfo((ApplicationInfo) info);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003060 }
3061 view = mLauncher.createShortcut(R.layout.application, cellLayout,
3062 (ShortcutInfo) info);
3063 break;
Adam Cohendf2cc412011-04-27 16:56:57 -07003064 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
Adam Cohenc0dcf592011-06-01 15:30:43 -07003065 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, cellLayout,
3066 (FolderInfo) info, mIconCache);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003067 break;
3068 default:
3069 throw new IllegalStateException("Unknown item type: " + info.itemType);
3070 }
3071
Adam Cohenc0dcf592011-06-01 15:30:43 -07003072 // First we find the cell nearest to point at which the item is
3073 // dropped, without any consideration to whether there is an item there.
3074 if (touchXY != null) {
3075 mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
3076 cellLayout, mTargetCell);
Adam Cohen482ed822012-03-02 14:15:13 -08003077 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
3078 mDragViewVisualCenter[1], mTargetCell);
Winson Chung557d6ed2011-07-08 15:34:52 -07003079 d.postAnimationRunnable = exitSpringLoadedRunnable;
Adam Cohen482ed822012-03-02 14:15:13 -08003080 if (createUserFolderIfNecessary(view, container, cellLayout, mTargetCell, distance,
3081 true, d.dragView, d.postAnimationRunnable)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07003082 return;
3083 }
Adam Cohen482ed822012-03-02 14:15:13 -08003084 if (addToExistingFolderIfNecessary(view, cellLayout, mTargetCell, distance, d,
3085 true)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07003086 return;
3087 }
Adam Cohen10b17372011-04-15 14:21:25 -07003088 }
3089
Michael Jurkac4e772e2011-02-10 13:32:01 -08003090 if (touchXY != null) {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003091 // when dragging and dropping, just find the closest free spot
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003092 mTargetCell = cellLayout.createArea((int) mDragViewVisualCenter[0],
Adam Cohen482ed822012-03-02 14:15:13 -08003093 (int) mDragViewVisualCenter[1], 1, 1, 1, 1,
Adam Cohenea889a22012-03-27 16:45:39 -07003094 null, mTargetCell, null, CellLayout.MODE_ON_DROP_EXTERNAL);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003095 } else {
3096 cellLayout.findCellForSpan(mTargetCell, 1, 1);
3097 }
Winson Chung3d503fb2011-07-13 17:25:49 -07003098 addInScreen(view, container, screen, mTargetCell[0], mTargetCell[1], info.spanX,
3099 info.spanY, insertAtFirst);
Adam Cohen716b51e2011-06-30 12:09:54 -07003100 cellLayout.onDropChild(view);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07003101 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
Michael Jurkaa52570f2012-03-20 03:18:20 -07003102 cellLayout.getShortcutsAndWidgets().measureChild(view);
Adam Cohend5e42732011-03-28 17:33:39 -07003103
Andrew Flynn0dca1ec2012-02-29 13:33:22 -08003104
Winson Chung3d503fb2011-07-13 17:25:49 -07003105 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screen,
Winson Chungaafa03c2010-06-11 17:34:16 -07003106 lp.cellX, lp.cellY);
Adam Cohen3e8f8112011-07-02 18:03:00 -07003107
3108 if (d.dragView != null) {
Adam Cohen4b285c52011-07-21 14:24:06 -07003109 // We wrap the animation call in the temporary set and reset of the current
3110 // cellLayout to its final transform -- this means we animate the drag view to
3111 // the correct final location.
3112 setFinalTransitionTransform(cellLayout);
Winson Chung557d6ed2011-07-08 15:34:52 -07003113 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view,
Adam Cohen4b285c52011-07-21 14:24:06 -07003114 exitSpringLoadedRunnable);
3115 resetTransitionTransform(cellLayout);
Adam Cohen3e8f8112011-07-02 18:03:00 -07003116 }
Joe Onorato00acb122009-08-04 16:04:30 -04003117 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003118 }
Winson Chungaafa03c2010-06-11 17:34:16 -07003119
Adam Cohend41fbf52012-02-16 23:53:59 -08003120 public Bitmap createWidgetBitmap(ItemInfo widgetInfo, View layout) {
Adam Cohened66b2b2012-01-23 17:28:51 -08003121 int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(widgetInfo.spanX,
3122 widgetInfo.spanY, widgetInfo, false);
Adam Cohend41fbf52012-02-16 23:53:59 -08003123 int visibility = layout.getVisibility();
Adam Cohened66b2b2012-01-23 17:28:51 -08003124 layout.setVisibility(VISIBLE);
3125
3126 int width = MeasureSpec.makeMeasureSpec(unScaledSize[0], MeasureSpec.EXACTLY);
3127 int height = MeasureSpec.makeMeasureSpec(unScaledSize[1], MeasureSpec.EXACTLY);
3128 Bitmap b = Bitmap.createBitmap(unScaledSize[0], unScaledSize[1],
3129 Bitmap.Config.ARGB_8888);
3130 Canvas c = new Canvas(b);
3131
3132 layout.measure(width, height);
3133 layout.layout(0, 0, unScaledSize[0], unScaledSize[1]);
Adam Cohened66b2b2012-01-23 17:28:51 -08003134 layout.draw(c);
3135 c.setBitmap(null);
Adam Cohend41fbf52012-02-16 23:53:59 -08003136 layout.setVisibility(visibility);
Adam Cohened66b2b2012-01-23 17:28:51 -08003137 return b;
3138 }
3139
Adam Cohend41fbf52012-02-16 23:53:59 -08003140 private void getFinalPositionForDropAnimation(int[] loc, float[] scaleXY,
Adam Cohen9d5b7d82012-05-09 18:00:44 -07003141 DragView dragView, CellLayout layout, ItemInfo info, int[] targetCell,
Adam Cohend41fbf52012-02-16 23:53:59 -08003142 boolean external) {
Adam Cohened66b2b2012-01-23 17:28:51 -08003143 // Now we animate the dragView, (ie. the widget or shortcut preview) into its final
3144 // location and size on the home screen.
Adam Cohend41fbf52012-02-16 23:53:59 -08003145 int spanX = info.spanX;
3146 int spanY = info.spanY;
Adam Cohened66b2b2012-01-23 17:28:51 -08003147
Adam Cohend41fbf52012-02-16 23:53:59 -08003148 Rect r = estimateItemPosition(layout, info, targetCell[0], targetCell[1], spanX, spanY);
3149 loc[0] = r.left;
3150 loc[1] = r.top;
3151
3152 setFinalTransitionTransform(layout);
3153 float cellLayoutScale =
3154 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(layout, loc);
3155 resetTransitionTransform(layout);
3156 float dragViewScaleX = (1.0f * r.width()) / dragView.getMeasuredWidth();
3157 float dragViewScaleY = (1.0f * r.height()) / dragView.getMeasuredHeight();
3158
Adam Cohened66b2b2012-01-23 17:28:51 -08003159 // The animation will scale the dragView about its center, so we need to center about
3160 // the final location.
3161 loc[0] -= (dragView.getMeasuredWidth() - cellLayoutScale * r.width()) / 2;
3162 loc[1] -= (dragView.getMeasuredHeight() - cellLayoutScale * r.height()) / 2;
3163
Adam Cohend41fbf52012-02-16 23:53:59 -08003164 scaleXY[0] = dragViewScaleX * cellLayoutScale;
3165 scaleXY[1] = dragViewScaleY * cellLayoutScale;
3166 }
3167
3168 public void animateWidgetDrop(ItemInfo info, CellLayout cellLayout, DragView dragView,
3169 final Runnable onCompleteRunnable, int animationType, final View finalView,
3170 boolean external) {
3171 Rect from = new Rect();
3172 mLauncher.getDragLayer().getViewRectRelativeToSelf(dragView, from);
3173
3174 int[] finalPos = new int[2];
3175 float scaleXY[] = new float[2];
3176 getFinalPositionForDropAnimation(finalPos, scaleXY, dragView, cellLayout, info, mTargetCell,
Adam Cohen9d5b7d82012-05-09 18:00:44 -07003177 external);
Adam Cohened66b2b2012-01-23 17:28:51 -08003178
3179 Resources res = mLauncher.getResources();
3180 int duration = res.getInteger(R.integer.config_dropAnimMaxDuration) - 200;
3181
Adam Cohend41fbf52012-02-16 23:53:59 -08003182 // In the case where we've prebound the widget, we remove it from the DragLayer
3183 if (finalView instanceof AppWidgetHostView && external) {
3184 mLauncher.getDragLayer().removeView(finalView);
3185 }
3186 if ((animationType == ANIMATE_INTO_POSITION_AND_RESIZE || external) && finalView != null) {
3187 Bitmap crossFadeBitmap = createWidgetBitmap(info, finalView);
Adam Cohened66b2b2012-01-23 17:28:51 -08003188 dragView.setCrossFadeBitmap(crossFadeBitmap);
3189 dragView.crossFade((int) (duration * 0.8f));
Adam Cohend41fbf52012-02-16 23:53:59 -08003190 } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET && external) {
3191 scaleXY[0] = scaleXY[1] = Math.min(scaleXY[0], scaleXY[1]);
Adam Cohened66b2b2012-01-23 17:28:51 -08003192 }
3193
Adam Cohend41fbf52012-02-16 23:53:59 -08003194 DragLayer dragLayer = mLauncher.getDragLayer();
Winson Chung7bd1bbb2012-02-13 18:29:29 -08003195 if (animationType == CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION) {
Adam Cohend41fbf52012-02-16 23:53:59 -08003196 mLauncher.getDragLayer().animateViewIntoPosition(dragView, finalPos, 0f, 0.1f, 0.1f,
Adam Cohened66b2b2012-01-23 17:28:51 -08003197 DragLayer.ANIMATION_END_DISAPPEAR, onCompleteRunnable, duration);
3198 } else {
Adam Cohend41fbf52012-02-16 23:53:59 -08003199 int endStyle;
3200 if (animationType == ANIMATE_INTO_POSITION_AND_REMAIN) {
3201 endStyle = DragLayer.ANIMATION_END_REMAIN_VISIBLE;
3202 } else {
3203 endStyle = DragLayer.ANIMATION_END_DISAPPEAR;;
3204 }
3205
3206 Runnable onComplete = new Runnable() {
3207 @Override
3208 public void run() {
3209 if (finalView != null) {
3210 finalView.setVisibility(VISIBLE);
3211 }
3212 if (onCompleteRunnable != null) {
3213 onCompleteRunnable.run();
3214 }
3215 }
3216 };
3217 dragLayer.animateViewIntoPosition(dragView, from.left, from.top, finalPos[0],
3218 finalPos[1], 1, 1, 1, scaleXY[0], scaleXY[1], onComplete, endStyle,
3219 duration, this);
Adam Cohened66b2b2012-01-23 17:28:51 -08003220 }
3221 }
3222
Adam Cohen4b285c52011-07-21 14:24:06 -07003223 public void setFinalTransitionTransform(CellLayout layout) {
3224 if (isSwitchingState()) {
3225 int index = indexOfChild(layout);
3226 mCurrentScaleX = layout.getScaleX();
3227 mCurrentScaleY = layout.getScaleY();
3228 mCurrentTranslationX = layout.getTranslationX();
3229 mCurrentTranslationY = layout.getTranslationY();
3230 mCurrentRotationY = layout.getRotationY();
3231 layout.setScaleX(mNewScaleXs[index]);
3232 layout.setScaleY(mNewScaleYs[index]);
3233 layout.setTranslationX(mNewTranslationXs[index]);
3234 layout.setTranslationY(mNewTranslationYs[index]);
3235 layout.setRotationY(mNewRotationYs[index]);
3236 }
3237 }
3238 public void resetTransitionTransform(CellLayout layout) {
3239 if (isSwitchingState()) {
Adam Cohen4b285c52011-07-21 14:24:06 -07003240 mCurrentScaleX = layout.getScaleX();
3241 mCurrentScaleY = layout.getScaleY();
3242 mCurrentTranslationX = layout.getTranslationX();
3243 mCurrentTranslationY = layout.getTranslationY();
3244 mCurrentRotationY = layout.getRotationY();
3245 layout.setScaleX(mCurrentScaleX);
3246 layout.setScaleY(mCurrentScaleY);
3247 layout.setTranslationX(mCurrentTranslationX);
3248 layout.setTranslationY(mCurrentTranslationY);
3249 layout.setRotationY(mCurrentRotationY);
3250 }
3251 }
3252
Jeff Sharkey70864282009-04-07 21:08:40 -07003253 /**
3254 * Return the current {@link CellLayout}, correctly picking the destination
3255 * screen while a scroll is in progress.
3256 */
Patrick Dubroy5f445422011-02-18 14:35:21 -08003257 public CellLayout getCurrentDropLayout() {
Winson Chung360e63f2012-04-27 13:48:05 -07003258 return (CellLayout) getChildAt(getNextPage());
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003259 }
3260
Jeff Sharkey70864282009-04-07 21:08:40 -07003261 /**
Michael Jurka0280c3b2010-09-17 15:00:07 -07003262 * Return the current CellInfo describing our current drag; this method exists
3263 * so that Launcher can sync this object with the correct info when the activity is created/
3264 * destroyed
3265 *
3266 */
3267 public CellLayout.CellInfo getDragInfo() {
3268 return mDragInfo;
3269 }
3270
3271 /**
Jeff Sharkey70864282009-04-07 21:08:40 -07003272 * Calculate the nearest cell where the given object would be dropped.
Adam Cohene3e27a82011-04-15 12:07:39 -07003273 *
3274 * pixelX and pixelY should be in the coordinate system of layout
Jeff Sharkey70864282009-04-07 21:08:40 -07003275 */
Adam Cohendf035382011-04-11 17:22:04 -07003276 private int[] findNearestArea(int pixelX, int pixelY,
Adam Cohene3e27a82011-04-15 12:07:39 -07003277 int spanX, int spanY, CellLayout layout, int[] recycle) {
Adam Cohendf035382011-04-11 17:22:04 -07003278 return layout.findNearestArea(
Adam Cohene3e27a82011-04-15 12:07:39 -07003279 pixelX, pixelY, spanX, spanY, recycle);
Adam Cohendf035382011-04-11 17:22:04 -07003280 }
3281
Adam Cohencff6af82011-09-13 14:51:53 -07003282 void setup(DragController dragController) {
Michael Jurkac2f7f472010-12-14 15:34:42 -08003283 mSpringLoadedDragController = new SpringLoadedDragController(mLauncher);
Joe Onorato00acb122009-08-04 16:04:30 -04003284 mDragController = dragController;
Michael Jurkad74c9842011-07-10 12:44:21 -07003285
Michael Jurkad74c9842011-07-10 12:44:21 -07003286 // hardware layers on children are enabled on startup, but should be disabled until
3287 // needed
3288 updateChildrenLayersEnabled();
3289 setWallpaperDimension();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003290 }
3291
Patrick Dubroye3887cc2011-01-20 10:43:40 -08003292 /**
3293 * Called at the end of a drag which originated on the workspace.
3294 */
Winson Chunga48487a2012-03-20 16:19:37 -07003295 public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete,
3296 boolean success) {
Winson Chungaafa03c2010-06-11 17:34:16 -07003297 if (success) {
Michael Jurkad74c9842011-07-10 12:44:21 -07003298 if (target != this) {
3299 if (mDragInfo != null) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003300 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
Michael Jurkad74c9842011-07-10 12:44:21 -07003301 if (mDragInfo.cell instanceof DropTarget) {
3302 mDragController.removeDropTarget((DropTarget) mDragInfo.cell);
3303 }
Joe Onorato00acb122009-08-04 16:04:30 -04003304 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003305 }
Patrick Dubroyce34a972010-10-19 10:34:32 -07003306 } else if (mDragInfo != null) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003307 CellLayout cellLayout;
3308 if (mLauncher.isHotseatLayout(target)) {
3309 cellLayout = mLauncher.getHotseat().getLayout();
3310 } else {
3311 cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
3312 }
3313 cellLayout.onDropChild(mDragInfo.cell);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003314 }
Adam Cohen36cc09b2011-09-29 17:33:15 -07003315 if (d.cancelled && mDragInfo.cell != null) {
3316 mDragInfo.cell.setVisibility(VISIBLE);
3317 }
Joe Onorato4be866d2010-10-10 11:26:02 -07003318 mDragOutline = null;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003319 mDragInfo = null;
Winson Chung1afedc32012-01-23 16:14:56 -08003320
3321 // Hide the scrolling indicator after you pick up an item
3322 hideScrollingIndicator(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003323 }
3324
Adam Cohenea889a22012-03-27 16:45:39 -07003325 void updateItemLocationsInDatabase(CellLayout cl) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07003326 int count = cl.getShortcutsAndWidgets().getChildCount();
Adam Cohend3461712012-03-29 17:25:17 -07003327
Adam Cohen482ed822012-03-02 14:15:13 -08003328 int screen = indexOfChild(cl);
Adam Cohend3461712012-03-29 17:25:17 -07003329 int container = Favorites.CONTAINER_DESKTOP;
3330
3331 if (mLauncher.isHotseatLayout(cl)) {
3332 screen = -1;
3333 container = Favorites.CONTAINER_HOTSEAT;
3334 }
3335
Adam Cohen482ed822012-03-02 14:15:13 -08003336 for (int i = 0; i < count; i++) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07003337 View v = cl.getShortcutsAndWidgets().getChildAt(i);
Adam Cohen482ed822012-03-02 14:15:13 -08003338 ItemInfo info = (ItemInfo) v.getTag();
Adam Cohen2acce882012-03-28 19:03:19 -07003339 // Null check required as the AllApps button doesn't have an item info
3340 if (info != null) {
Adam Cohenbebf0422012-04-11 18:06:28 -07003341 LauncherModel.modifyItemInDatabase(mLauncher, info, container, screen, info.cellX,
3342 info.cellY, info.spanX, info.spanY);
Adam Cohen2acce882012-03-28 19:03:19 -07003343 }
Adam Cohen482ed822012-03-02 14:15:13 -08003344 }
3345 }
3346
Winson Chunga48487a2012-03-20 16:19:37 -07003347 @Override
Winson Chung043f2af2012-03-01 16:09:54 -08003348 public boolean supportsFlingToDelete() {
3349 return true;
3350 }
3351
Winson Chunga48487a2012-03-20 16:19:37 -07003352 @Override
3353 public void onFlingToDelete(DragObject d, int x, int y, PointF vec) {
3354 // Do nothing
3355 }
3356
3357 @Override
3358 public void onFlingToDeleteCompleted() {
3359 // Do nothing
3360 }
3361
Michael Jurka0280c3b2010-09-17 15:00:07 -07003362 public boolean isDropEnabled() {
3363 return true;
3364 }
3365
Michael Jurka0142d492010-08-25 17:46:15 -07003366 @Override
3367 protected void onRestoreInstanceState(Parcelable state) {
3368 super.onRestoreInstanceState(state);
3369 Launcher.setScreen(mCurrentPage);
3370 }
3371
3372 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003373 public void scrollLeft() {
Michael Jurkad74c9842011-07-10 12:44:21 -07003374 if (!isSmall() && !mIsSwitchingState) {
Michael Jurka0142d492010-08-25 17:46:15 -07003375 super.scrollLeft();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003376 }
Adam Cohen95bb8002011-07-03 23:40:28 -07003377 Folder openFolder = getOpenFolder();
3378 if (openFolder != null) {
3379 openFolder.completeDragExit();
3380 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003381 }
3382
Michael Jurka0142d492010-08-25 17:46:15 -07003383 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003384 public void scrollRight() {
Michael Jurkad74c9842011-07-10 12:44:21 -07003385 if (!isSmall() && !mIsSwitchingState) {
Michael Jurka0142d492010-08-25 17:46:15 -07003386 super.scrollRight();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003387 }
Adam Cohen95bb8002011-07-03 23:40:28 -07003388 Folder openFolder = getOpenFolder();
3389 if (openFolder != null) {
3390 openFolder.completeDragExit();
3391 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003392 }
3393
Patrick Dubroy1262e362010-10-06 15:49:50 -07003394 @Override
Winson Chung3e0839e2011-10-03 15:15:18 -07003395 public boolean onEnterScrollArea(int x, int y, int direction) {
Winson Chung1c4cf4a2011-07-29 14:49:10 -07003396 // Ignore the scroll area if we are dragging over the hot seat
Winson Chung10bfc6e2012-03-28 15:41:26 -07003397 boolean isPortrait = !LauncherApplication.isScreenLandscape(getContext());
3398 if (mLauncher.getHotseat() != null && isPortrait) {
Winson Chung1c4cf4a2011-07-29 14:49:10 -07003399 Rect r = new Rect();
3400 mLauncher.getHotseat().getHitRect(r);
3401 if (r.contains(x, y)) {
Winson Chung3e0839e2011-10-03 15:15:18 -07003402 return false;
Winson Chung1c4cf4a2011-07-29 14:49:10 -07003403 }
3404 }
3405
Winson Chung3e0839e2011-10-03 15:15:18 -07003406 boolean result = false;
Michael Jurkad74c9842011-07-10 12:44:21 -07003407 if (!isSmall() && !mIsSwitchingState) {
Michael Jurkad718d6a2010-10-14 15:35:17 -07003408 mInScrollArea = true;
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08003409
Winson Chung360e63f2012-04-27 13:48:05 -07003410 final int page = getNextPage() +
Winson Chungaa15ffe2012-01-18 15:45:28 -08003411 (direction == DragController.SCROLL_LEFT ? -1 : 1);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003412
3413 // We always want to exit the current layout to ensure parity of enter / exit
3414 setCurrentDropLayout(null);
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08003415
Winson Chungaa15ffe2012-01-18 15:45:28 -08003416 if (0 <= page && page < getChildCount()) {
3417 CellLayout layout = (CellLayout) getChildAt(page);
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003418 setCurrentDragOverlappingLayout(layout);
Winson Chungc07918d2011-07-01 15:35:26 -07003419
3420 // Workspace is responsible for drawing the edge glow on adjacent pages,
3421 // so we need to redraw the workspace when this may have changed.
3422 invalidate();
Winson Chung3e0839e2011-10-03 15:15:18 -07003423 result = true;
Michael Jurkad718d6a2010-10-14 15:35:17 -07003424 }
Patrick Dubroy1262e362010-10-06 15:49:50 -07003425 }
Winson Chung3e0839e2011-10-03 15:15:18 -07003426 return result;
Patrick Dubroy1262e362010-10-06 15:49:50 -07003427 }
3428
3429 @Override
Winson Chung3e0839e2011-10-03 15:15:18 -07003430 public boolean onExitScrollArea() {
3431 boolean result = false;
Michael Jurkad718d6a2010-10-14 15:35:17 -07003432 if (mInScrollArea) {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003433 invalidate();
3434 CellLayout layout = getCurrentDropLayout();
3435 setCurrentDropLayout(layout);
3436 setCurrentDragOverlappingLayout(layout);
3437
Adam Cohen8a18afc2011-12-13 15:57:01 -08003438 result = true;
Winson Chungc07918d2011-07-01 15:35:26 -07003439 mInScrollArea = false;
Patrick Dubroy1262e362010-10-06 15:49:50 -07003440 }
Winson Chung3e0839e2011-10-03 15:15:18 -07003441 return result;
Patrick Dubroy1262e362010-10-06 15:49:50 -07003442 }
3443
Winson Chungc07918d2011-07-01 15:35:26 -07003444 private void onResetScrollArea() {
Adam Cohenc6cc61d2012-04-04 12:47:08 -07003445 setCurrentDragOverlappingLayout(null);
Winson Chungc07918d2011-07-01 15:35:26 -07003446 mInScrollArea = false;
3447 }
3448
Winson Chung3d503fb2011-07-13 17:25:49 -07003449 /**
3450 * Returns a specific CellLayout
3451 */
3452 CellLayout getParentCellLayoutForView(View v) {
3453 ArrayList<CellLayout> layouts = getWorkspaceAndHotseatCellLayouts();
3454 for (CellLayout layout : layouts) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07003455 if (layout.getShortcutsAndWidgets().indexOfChild(v) > -1) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003456 return layout;
3457 }
3458 }
3459 return null;
3460 }
3461
3462 /**
3463 * Returns a list of all the CellLayouts in the workspace.
3464 */
3465 ArrayList<CellLayout> getWorkspaceAndHotseatCellLayouts() {
3466 ArrayList<CellLayout> layouts = new ArrayList<CellLayout>();
3467 int screenCount = getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003468 for (int screen = 0; screen < screenCount; screen++) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003469 layouts.add(((CellLayout) getChildAt(screen)));
3470 }
3471 if (mLauncher.getHotseat() != null) {
3472 layouts.add(mLauncher.getHotseat().getLayout());
3473 }
3474 return layouts;
3475 }
3476
3477 /**
3478 * We should only use this to search for specific children. Do not use this method to modify
Michael Jurkaa52570f2012-03-20 03:18:20 -07003479 * ShortcutsAndWidgetsContainer directly. Includes ShortcutAndWidgetContainers from
3480 * the hotseat and workspace pages
Winson Chung3d503fb2011-07-13 17:25:49 -07003481 */
Michael Jurkaa52570f2012-03-20 03:18:20 -07003482 ArrayList<ShortcutAndWidgetContainer> getAllShortcutAndWidgetContainers() {
3483 ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
3484 new ArrayList<ShortcutAndWidgetContainer>();
Winson Chung3d503fb2011-07-13 17:25:49 -07003485 int screenCount = getChildCount();
3486 for (int screen = 0; screen < screenCount; screen++) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07003487 childrenLayouts.add(((CellLayout) getChildAt(screen)).getShortcutsAndWidgets());
Winson Chung3d503fb2011-07-13 17:25:49 -07003488 }
3489 if (mLauncher.getHotseat() != null) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07003490 childrenLayouts.add(mLauncher.getHotseat().getLayout().getShortcutsAndWidgets());
Winson Chung3d503fb2011-07-13 17:25:49 -07003491 }
3492 return childrenLayouts;
3493 }
3494
3495 public Folder getFolderForTag(Object tag) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07003496 ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
3497 getAllShortcutAndWidgetContainers();
3498 for (ShortcutAndWidgetContainer layout: childrenLayouts) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003499 int count = layout.getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003500 for (int i = 0; i < count; i++) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003501 View child = layout.getChildAt(i);
Winson Chung3d503fb2011-07-13 17:25:49 -07003502 if (child instanceof Folder) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003503 Folder f = (Folder) child;
Winson Chungaafa03c2010-06-11 17:34:16 -07003504 if (f.getInfo() == tag && f.getInfo().opened) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003505 return f;
3506 }
3507 }
3508 }
3509 }
3510 return null;
3511 }
3512
3513 public View getViewForTag(Object tag) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07003514 ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
3515 getAllShortcutAndWidgetContainers();
3516 for (ShortcutAndWidgetContainer layout: childrenLayouts) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003517 int count = layout.getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003518 for (int i = 0; i < count; i++) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003519 View child = layout.getChildAt(i);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003520 if (child.getTag() == tag) {
3521 return child;
3522 }
3523 }
3524 }
3525 return null;
3526 }
3527
Adam Cohendf035382011-04-11 17:22:04 -07003528 void clearDropTargets() {
Michael Jurkaa52570f2012-03-20 03:18:20 -07003529 ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
3530 getAllShortcutAndWidgetContainers();
3531 for (ShortcutAndWidgetContainer layout: childrenLayouts) {
Adam Cohendf035382011-04-11 17:22:04 -07003532 int childCount = layout.getChildCount();
3533 for (int j = 0; j < childCount; j++) {
3534 View v = layout.getChildAt(j);
3535 if (v instanceof DropTarget) {
3536 mDragController.removeDropTarget((DropTarget) v);
3537 }
3538 }
3539 }
3540 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003541
Joe Onorato64e6be72010-03-05 15:05:52 -05003542 void removeItems(final ArrayList<ApplicationInfo> apps) {
Romain Guy629de3e2010-01-13 12:20:59 -08003543 final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext());
Romain Guy574d20e2009-06-01 15:34:04 -07003544
Joe Onorato64e6be72010-03-05 15:05:52 -05003545 final HashSet<String> packageNames = new HashSet<String>();
3546 final int appCount = apps.size();
3547 for (int i = 0; i < appCount; i++) {
3548 packageNames.add(apps.get(i).componentName.getPackageName());
3549 }
3550
Winson Chung3d503fb2011-07-13 17:25:49 -07003551 ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
3552 for (final CellLayout layoutParent: cellLayouts) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07003553 final ViewGroup layout = layoutParent.getShortcutsAndWidgets();
Romain Guy574d20e2009-06-01 15:34:04 -07003554
Romain Guy629de3e2010-01-13 12:20:59 -08003555 // Avoid ANRs by treating each screen separately
3556 post(new Runnable() {
3557 public void run() {
3558 final ArrayList<View> childrenToRemove = new ArrayList<View>();
3559 childrenToRemove.clear();
Winson Chungaafa03c2010-06-11 17:34:16 -07003560
Romain Guy629de3e2010-01-13 12:20:59 -08003561 int childCount = layout.getChildCount();
3562 for (int j = 0; j < childCount; j++) {
3563 final View view = layout.getChildAt(j);
3564 Object tag = view.getTag();
Winson Chungaafa03c2010-06-11 17:34:16 -07003565
Joe Onorato0589f0f2010-02-08 13:44:00 -08003566 if (tag instanceof ShortcutInfo) {
3567 final ShortcutInfo info = (ShortcutInfo) tag;
Romain Guy629de3e2010-01-13 12:20:59 -08003568 final Intent intent = info.intent;
3569 final ComponentName name = intent.getComponent();
Winson Chungaafa03c2010-06-11 17:34:16 -07003570
Winson Chung11a49372012-04-27 15:12:38 -07003571 if (name != null) {
Winson Chung2efec4e2012-05-03 12:31:48 -07003572 if (packageNames.contains(name.getPackageName())) {
3573 LauncherModel.deleteItemFromDatabase(mLauncher, info);
3574 childrenToRemove.add(view);
Joe Onorato64e6be72010-03-05 15:05:52 -05003575 }
Romain Guy629de3e2010-01-13 12:20:59 -08003576 }
Adam Cohendf2cc412011-04-27 16:56:57 -07003577 } else if (tag instanceof FolderInfo) {
3578 final FolderInfo info = (FolderInfo) tag;
Joe Onorato0589f0f2010-02-08 13:44:00 -08003579 final ArrayList<ShortcutInfo> contents = info.contents;
Romain Guy629de3e2010-01-13 12:20:59 -08003580 final int contentsCount = contents.size();
Adam Cohendf1e4e82011-06-24 15:57:39 -07003581 final ArrayList<ShortcutInfo> appsToRemoveFromFolder =
3582 new ArrayList<ShortcutInfo>();
Winson Chungaafa03c2010-06-11 17:34:16 -07003583
Romain Guy629de3e2010-01-13 12:20:59 -08003584 for (int k = 0; k < contentsCount; k++) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08003585 final ShortcutInfo appInfo = contents.get(k);
Romain Guy629de3e2010-01-13 12:20:59 -08003586 final Intent intent = appInfo.intent;
3587 final ComponentName name = intent.getComponent();
Winson Chungaafa03c2010-06-11 17:34:16 -07003588
Winson Chung11a49372012-04-27 15:12:38 -07003589 if (name != null) {
Winson Chung2efec4e2012-05-03 12:31:48 -07003590 if (packageNames.contains(name.getPackageName())) {
3591 appsToRemoveFromFolder.add(appInfo);
Joe Onorato64e6be72010-03-05 15:05:52 -05003592 }
Romain Guy629de3e2010-01-13 12:20:59 -08003593 }
3594 }
Adam Cohendf1e4e82011-06-24 15:57:39 -07003595 for (ShortcutInfo item: appsToRemoveFromFolder) {
3596 info.remove(item);
3597 LauncherModel.deleteItemFromDatabase(mLauncher, item);
Romain Guy629de3e2010-01-13 12:20:59 -08003598 }
Romain Guy629de3e2010-01-13 12:20:59 -08003599 } else if (tag instanceof LauncherAppWidgetInfo) {
3600 final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag;
Winson Chung11a49372012-04-27 15:12:38 -07003601 final ComponentName provider = info.providerName;
Daniel Sandlere1cc6c32010-05-07 11:49:29 -04003602 if (provider != null) {
Winson Chung2efec4e2012-05-03 12:31:48 -07003603 if (packageNames.contains(provider.getPackageName())) {
3604 LauncherModel.deleteItemFromDatabase(mLauncher, info);
3605 childrenToRemove.add(view);
Joe Onorato64e6be72010-03-05 15:05:52 -05003606 }
Romain Guy629de3e2010-01-13 12:20:59 -08003607 }
Romain Guy574d20e2009-06-01 15:34:04 -07003608 }
3609 }
Winson Chungaafa03c2010-06-11 17:34:16 -07003610
Romain Guy629de3e2010-01-13 12:20:59 -08003611 childCount = childrenToRemove.size();
3612 for (int j = 0; j < childCount; j++) {
3613 View child = childrenToRemove.get(j);
Winson Chung7a25a9e2011-01-30 13:33:56 -08003614 // Note: We can not remove the view directly from CellLayoutChildren as this
3615 // does not re-mark the spaces as unoccupied.
3616 layoutParent.removeViewInLayout(child);
Romain Guy629de3e2010-01-13 12:20:59 -08003617 if (child instanceof DropTarget) {
3618 mDragController.removeDropTarget((DropTarget)child);
3619 }
Romain Guy574d20e2009-06-01 15:34:04 -07003620 }
Winson Chungaafa03c2010-06-11 17:34:16 -07003621
Romain Guy629de3e2010-01-13 12:20:59 -08003622 if (childCount > 0) {
3623 layout.requestLayout();
3624 layout.invalidate();
Romain Guy5c16f3e2010-01-12 17:24:58 -08003625 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003626 }
Romain Guy629de3e2010-01-13 12:20:59 -08003627 });
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003628 }
Winson Chung2efec4e2012-05-03 12:31:48 -07003629
3630 // It is no longer the case the BubbleTextViews correspond 1:1 with the workspace items in
3631 // the database (and LauncherModel) since shortcuts are not added and animated in until
3632 // the user returns to launcher. As a result, we really should be cleaning up the Db
3633 // regardless of whether the item was added or not (unlike the logic above). This is only
3634 // relevant for direct workspace items.
3635 post(new Runnable() {
3636 @Override
3637 public void run() {
3638 String spKey = LauncherApplication.getSharedPreferencesKey();
3639 SharedPreferences sp = getContext().getSharedPreferences(spKey,
3640 Context.MODE_PRIVATE);
3641 Set<String> newApps = sp.getStringSet(InstallShortcutReceiver.NEW_APPS_LIST_KEY,
3642 null);
3643
3644 for (String packageName: packageNames) {
3645 // Remove all items that have the same package, but were not removed above
3646 ArrayList<ShortcutInfo> infos =
3647 mLauncher.getModel().getShortcutInfosForPackage(packageName);
3648 for (ShortcutInfo info : infos) {
3649 LauncherModel.deleteItemFromDatabase(mLauncher, info);
3650 }
3651 // Remove all queued items that match the same package
3652 if (newApps != null) {
3653 for (String intentStr : newApps) {
3654 try {
3655 Intent intent = Intent.parseUri(intentStr, 0);
Winson Chungbe365162012-05-07 10:34:12 -07003656 String pn = ItemInfo.getPackageName(intent);
3657 if (packageNames.contains(pn)) {
Winson Chung2efec4e2012-05-03 12:31:48 -07003658 newApps.remove(intentStr);
3659 }
3660 } catch (URISyntaxException e) {}
3661 }
3662 }
3663 }
3664 }
3665 });
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003666 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07003667
Joe Onorato64e6be72010-03-05 15:05:52 -05003668 void updateShortcuts(ArrayList<ApplicationInfo> apps) {
Michael Jurkaa52570f2012-03-20 03:18:20 -07003669 ArrayList<ShortcutAndWidgetContainer> childrenLayouts = getAllShortcutAndWidgetContainers();
3670 for (ShortcutAndWidgetContainer layout: childrenLayouts) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07003671 int childCount = layout.getChildCount();
3672 for (int j = 0; j < childCount; j++) {
3673 final View view = layout.getChildAt(j);
3674 Object tag = view.getTag();
Joe Onorato0589f0f2010-02-08 13:44:00 -08003675 if (tag instanceof ShortcutInfo) {
Michael Jurkae384aff2012-03-09 15:59:25 -08003676 ShortcutInfo info = (ShortcutInfo) tag;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07003677 // We need to check for ACTION_MAIN otherwise getComponent() might
3678 // return null for some shortcuts (for instance, for shortcuts to
3679 // web pages.)
3680 final Intent intent = info.intent;
3681 final ComponentName name = intent.getComponent();
3682 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
Joe Onorato64e6be72010-03-05 15:05:52 -05003683 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
3684 final int appCount = apps.size();
Winson Chungaafa03c2010-06-11 17:34:16 -07003685 for (int k = 0; k < appCount; k++) {
Joe Onorato64e6be72010-03-05 15:05:52 -05003686 ApplicationInfo app = apps.get(k);
3687 if (app.componentName.equals(name)) {
Michael Jurkae384aff2012-03-09 15:59:25 -08003688 BubbleTextView shortcut = (BubbleTextView) view;
3689 info.updateIcon(mIconCache);
3690 info.title = app.title.toString();
3691 shortcut.applyFromShortcutInfo(info, mIconCache);
3692 }
Joe Onorato64e6be72010-03-05 15:05:52 -05003693 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07003694 }
3695 }
3696 }
3697 }
3698 }
3699
Joe Onorato14f122b2009-11-19 14:06:36 -08003700 void moveToDefaultScreen(boolean animate) {
Winson Chungde1af762011-07-21 16:44:07 -07003701 if (!isSmall()) {
3702 if (animate) {
3703 snapToPage(mDefaultPage);
3704 } else {
3705 setCurrentPage(mDefaultPage);
3706 }
Joe Onoratoc45b1682010-01-11 18:48:40 -05003707 }
Michael Jurka0142d492010-08-25 17:46:15 -07003708 getChildAt(mDefaultPage).requestFocus();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003709 }
3710
Michael Jurka0142d492010-08-25 17:46:15 -07003711 @Override
3712 public void syncPages() {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003713 }
Michael Jurka0142d492010-08-25 17:46:15 -07003714
3715 @Override
Winson Chungf314b0e2011-08-16 11:54:27 -07003716 public void syncPageItems(int page, boolean immediate) {
Michael Jurka0142d492010-08-25 17:46:15 -07003717 }
Winson Chung6a0f57d2011-06-29 20:10:49 -07003718
3719 @Override
3720 protected String getCurrentPageDescription() {
3721 int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
Michael Jurka8b805b12012-04-18 14:23:14 -07003722 return String.format(getContext().getString(R.string.workspace_scroll_format),
Winson Chung6a0f57d2011-06-29 20:10:49 -07003723 page + 1, getChildCount());
3724 }
Adam Cohen8dfcba42011-07-07 16:38:18 -07003725
3726 public void getLocationInDragLayer(int[] loc) {
3727 mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
3728 }
Winson Chung32174c82011-07-19 15:47:55 -07003729
Adam Cohen21b41102011-11-01 17:29:52 -07003730 void setFadeForOverScroll(float fade) {
3731 if (!isScrollingIndicatorEnabled()) return;
3732
3733 mOverscrollFade = fade;
3734 float reducedFade = 0.5f + 0.5f * (1 - fade);
3735 final ViewGroup parent = (ViewGroup) getParent();
Winson Chunge96e5cb2011-11-28 13:02:56 -08003736 final ImageView qsbDivider = (ImageView) (parent.findViewById(R.id.qsb_divider));
Adam Cohen21b41102011-11-01 17:29:52 -07003737 final ImageView dockDivider = (ImageView) (parent.findViewById(R.id.dock_divider));
Michael Jurkaafaa0502011-12-13 18:22:50 -08003738 final View scrollIndicator = getScrollingIndicator();
Adam Cohen21b41102011-11-01 17:29:52 -07003739
3740 cancelScrollingIndicatorAnimations();
Winson Chunge96e5cb2011-11-28 13:02:56 -08003741 if (qsbDivider != null) qsbDivider.setAlpha(reducedFade);
3742 if (dockDivider != null) dockDivider.setAlpha(reducedFade);
Adam Cohen21b41102011-11-01 17:29:52 -07003743 scrollIndicator.setAlpha(1 - fade);
3744 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003745}