blob: f61f7e7c201ee9fa884e1a5eee7cbcd0c7ceca12 [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;
Michael Jurka8edd75c2010-12-17 20:15:06 -080020import android.animation.AnimatorListenerAdapter;
Chet Haaseb1254a62010-09-07 13:35:00 -070021import android.animation.AnimatorSet;
Michael Jurka0280c3b2010-09-17 15:00:07 -070022import android.animation.ObjectAnimator;
Patrick Dubroycd68ff52010-10-28 17:57:05 -070023import android.animation.TimeInterpolator;
24import android.animation.ValueAnimator;
Adam Cohencff6af82011-09-13 14:51:53 -070025import android.animation.Animator.AnimatorListener;
Patrick Dubroycd68ff52010-10-28 17:57:05 -070026import android.animation.ValueAnimator.AnimatorUpdateListener;
Winson Chung68846fd2010-10-29 11:00:27 -070027import android.app.AlertDialog;
Dianne Hackborn8f573952009-08-10 23:21:09 -070028import android.app.WallpaperManager;
Romain Guy629de3e2010-01-13 12:20:59 -080029import android.appwidget.AppWidgetManager;
30import android.appwidget.AppWidgetProviderInfo;
Winson Chunga9abd0e2010-10-27 17:18:37 -070031import android.content.ClipData;
32import android.content.ClipDescription;
Adam Powell495f2892010-04-16 16:40:55 -070033import android.content.ComponentName;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080034import android.content.Context;
35import android.content.Intent;
Patrick Dubroy7247f632010-08-04 16:02:59 -070036import android.content.res.Resources;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080037import android.content.res.TypedArray;
Joe Onorato4be866d2010-10-10 11:26:02 -070038import android.graphics.Bitmap;
Adam Lesinski6b879f02010-11-04 16:15:23 -070039import android.graphics.Camera;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080040import android.graphics.Canvas;
Michael Jurkaa63c4522010-08-19 13:52:27 -070041import android.graphics.Matrix;
Winson Chunga9abd0e2010-10-27 17:18:37 -070042import android.graphics.Paint;
Winson Chung70fc4382011-08-08 15:31:33 -070043import android.graphics.PorterDuff;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080044import android.graphics.Rect;
Winson Chunga9abd0e2010-10-27 17:18:37 -070045import android.graphics.RectF;
Joe Onorato4be866d2010-10-10 11:26:02 -070046import android.graphics.Region.Op;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070047import android.graphics.drawable.Drawable;
Joe Onorato956091b2010-02-19 12:47:40 -080048import android.os.IBinder;
Adam Powell495f2892010-04-16 16:40:55 -070049import android.os.Parcelable;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080050import android.util.AttributeSet;
Winson Chungb26f3d62011-06-02 10:49:29 -070051import android.util.DisplayMetrics;
Daniel Sandler291ad122010-05-24 16:03:53 -040052import android.util.Log;
Winson Chung580e2772010-11-10 16:03:00 -080053import android.util.Pair;
Winson Chunga34abf82010-11-12 12:10:35 -080054import android.view.Display;
Winson Chunga9abd0e2010-10-27 17:18:37 -070055import android.view.DragEvent;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080056import android.view.MotionEvent;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080057import android.view.View;
Winson Chung6e314082011-01-27 16:46:51 -080058import android.view.ViewGroup;
Patrick Dubroycd68ff52010-10-28 17:57:05 -070059import android.view.animation.DecelerateInterpolator;
Winson Chunga6427b12011-07-27 10:53:39 -070060import android.widget.ImageView;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070061import android.widget.TextView;
Winson Chunga9abd0e2010-10-27 17:18:37 -070062import android.widget.Toast;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080063
Adam Cohen66396872011-04-15 17:50:36 -070064import com.android.launcher.R;
Adam Cohen19072da2011-05-31 14:30:45 -070065import com.android.launcher2.FolderIcon.FolderRingAnimator;
Adam Cohen66396872011-04-15 17:50:36 -070066import com.android.launcher2.InstallWidgetReceiver.WidgetMimeTypeHandlerData;
Romain Guyedcce092010-03-04 13:03:17 -080067
Adam Cohen716b51e2011-06-30 12:09:54 -070068import java.util.ArrayList;
69import java.util.HashSet;
70import java.util.List;
71
The Android Open Source Project31dd5032009-03-03 19:32:27 -080072/**
Michael Jurka0142d492010-08-25 17:46:15 -070073 * The workspace is a wide area with a wallpaper and a finite number of pages.
74 * Each page contains a number of icons, folders or widgets the user can
Winson Chungaafa03c2010-06-11 17:34:16 -070075 * interact with. A workspace is meant to be used with a fixed width only.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080076 */
Michael Jurka0142d492010-08-25 17:46:15 -070077public class Workspace extends SmoothPagedView
Michael Jurkad74c9842011-07-10 12:44:21 -070078 implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
79 DragController.DragListener {
Romain Guye47f55c2009-11-11 19:21:22 -080080 @SuppressWarnings({"UnusedDeclaration"})
Joe Onorato3a8820b2009-11-10 15:06:42 -080081 private static final String TAG = "Launcher.Workspace";
Michael Jurka0142d492010-08-25 17:46:15 -070082
Adam Cohenf34bab52010-09-30 14:11:56 -070083 // Y rotation to apply to the workspace screens
84 private static final float WORKSPACE_ROTATION = 12.5f;
Adam Cohenb5ba0972011-09-07 18:02:31 -070085 private static final float WORKSPACE_OVERSCROLL_ROTATION = 24f;
Adam Cohencff6af82011-09-13 14:51:53 -070086 private static float CAMERA_DISTANCE = 6500;
Adam Cohena985e592010-09-09 11:23:48 -070087
Adam Cohen68d73932010-11-15 10:50:58 -080088 private static final int CHILDREN_OUTLINE_FADE_OUT_DELAY = 0;
89 private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375;
Winson Chung9171e6d2010-11-17 17:39:27 -080090 private static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100;
Adam Cohenf34bab52010-09-30 14:11:56 -070091
Winson Chungf135c6c2010-11-18 16:32:08 -080092 private static final int BACKGROUND_FADE_OUT_DURATION = 350;
Adam Cohened51cc92011-08-01 20:28:08 -070093 private static final int ADJACENT_SCREEN_DROP_DURATION = 300;
94
Winson Chung9171e6d2010-11-17 17:39:27 -080095 // These animators are used to fade the children's outlines
96 private ObjectAnimator mChildrenOutlineFadeInAnimation;
97 private ObjectAnimator mChildrenOutlineFadeOutAnimation;
98 private float mChildrenOutlineAlpha = 0;
99
100 // These properties refer to the background protection gradient used for AllApps and Customize
Michael Jurkae0f5a612011-02-07 16:45:41 -0800101 private ValueAnimator mBackgroundFadeInAnimation;
102 private ValueAnimator mBackgroundFadeOutAnimation;
Winson Chung9171e6d2010-11-17 17:39:27 -0800103 private Drawable mBackground;
Michael Jurka25356e72011-03-03 14:53:11 -0800104 boolean mDrawBackground = true;
Adam Cohenf34bab52010-09-30 14:11:56 -0700105 private float mBackgroundAlpha = 0;
Adam Cohen68d73932010-11-15 10:50:58 -0800106 private float mOverScrollMaxBackgroundAlpha = 0.0f;
Adam Cohene0f66b52010-11-23 15:06:07 -0800107 private int mOverScrollPageIndex = -1;
Winson Chunga6427b12011-07-27 10:53:39 -0700108 private AnimatorSet mDividerAnimator;
Adam Cohenf34bab52010-09-30 14:11:56 -0700109
Adam Cohenbeff8c62011-08-31 17:46:01 -0700110 private float mWallpaperScrollRatio = 1.0f;
111
Dianne Hackborn8f573952009-08-10 23:21:09 -0700112 private final WallpaperManager mWallpaperManager;
Michael Jurka9c6fbed2011-03-02 17:41:34 -0800113 private IBinder mWindowToken;
Winson Chungaafa03c2010-06-11 17:34:16 -0700114
Michael Jurka0142d492010-08-25 17:46:15 -0700115 private int mDefaultPage;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800116
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800117 /**
118 * CellInfo for the cell that is currently being dragged
119 */
120 private CellLayout.CellInfo mDragInfo;
Winson Chungaafa03c2010-06-11 17:34:16 -0700121
Jeff Sharkey70864282009-04-07 21:08:40 -0700122 /**
123 * Target drop area calculated during last acceptDrop call.
124 */
Adam Cohenc0dcf592011-06-01 15:30:43 -0700125 private int[] mTargetCell = new int[2];
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800126
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700127 /**
128 * The CellLayout that is currently being dragged over
129 */
130 private CellLayout mDragTargetLayout = null;
131
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800132 private Launcher mLauncher;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800133 private IconCache mIconCache;
Joe Onorato00acb122009-08-04 16:04:30 -0400134 private DragController mDragController;
Winson Chungaafa03c2010-06-11 17:34:16 -0700135
Michael Jurka4516c112010-10-07 15:13:47 -0700136 // These are temporary variables to prevent having to allocate a new object just to
137 // 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 -0800138 private int[] mTempCell = new int[2];
Jeff Sharkey70864282009-04-07 21:08:40 -0700139 private int[] mTempEstimate = new int[2];
Adam Cohene3e27a82011-04-15 12:07:39 -0700140 private float[] mDragViewVisualCenter = new float[2];
Michael Jurkaa63c4522010-08-19 13:52:27 -0700141 private float[] mTempDragCoordinates = new float[2];
Michael Jurka4516c112010-10-07 15:13:47 -0700142 private float[] mTempCellLayoutCenterCoordinates = new float[2];
Michael Jurkaa63c4522010-08-19 13:52:27 -0700143 private float[] mTempDragBottomRightCoordinates = new float[2];
Michael Jurka0280c3b2010-09-17 15:00:07 -0700144 private Matrix mTempInverseMatrix = new Matrix();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800145
Michael Jurkac2f7f472010-12-14 15:34:42 -0800146 private SpringLoadedDragController mSpringLoadedDragController;
Winson Chungb26f3d62011-06-02 10:49:29 -0700147 private float mSpringLoadedShrinkFactor;
Michael Jurkad3ef3062010-11-23 16:23:58 -0800148
Adam Cohend22015c2010-07-26 22:02:18 -0700149 private static final int DEFAULT_CELL_COUNT_X = 4;
150 private static final int DEFAULT_CELL_COUNT_Y = 4;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800151
Patrick Dubroy1262e362010-10-06 15:49:50 -0700152 // State variable that indicates whether the pages are small (ie when you're
Michael Jurkadee05892010-07-27 10:01:56 -0700153 // in all apps or customize mode)
Michael Jurkad74c9842011-07-10 12:44:21 -0700154
155 enum State { NORMAL, SPRING_LOADED, SMALL };
Adam Cohen7777d962011-08-18 18:58:38 -0700156 private State mState = State.NORMAL;
Michael Jurkad74c9842011-07-10 12:44:21 -0700157 private boolean mIsSwitchingState = false;
Michael Jurkad74c9842011-07-10 12:44:21 -0700158 private boolean mSwitchStateAfterFirstLayout = false;
159 private State mStateAfterFirstLayout;
160
161 private AnimatorSet mAnimator;
Adam Cohen7777d962011-08-18 18:58:38 -0700162 private AnimatorListener mChangeStateAnimationListener;
Michael Jurkad74c9842011-07-10 12:44:21 -0700163
164 boolean mAnimatingViewIntoPlace = false;
165 boolean mIsDragOccuring = false;
166 boolean mChildrenLayersEnabled = true;
Michael Jurkadee05892010-07-27 10:01:56 -0700167
Patrick Dubroy54fa3b92010-11-17 12:18:45 -0800168 /** Is the user is dragging an item near the edge of a page? */
Patrick Dubroy1262e362010-10-06 15:49:50 -0700169 private boolean mInScrollArea = false;
170
Patrick Dubroy8e58e912010-10-14 13:21:48 -0700171 private final HolographicOutlineHelper mOutlineHelper = new HolographicOutlineHelper();
Joe Onorato4be866d2010-10-10 11:26:02 -0700172 private Bitmap mDragOutline = null;
Patrick Dubroy8e58e912010-10-14 13:21:48 -0700173 private final Rect mTempRect = new Rect();
174 private final int[] mTempXY = new int[2];
Winson Chung70fc4382011-08-08 15:31:33 -0700175 private int mDragViewMultiplyColor;
Joe Onorato4be866d2010-10-10 11:26:02 -0700176
Winson Chunga9abd0e2010-10-27 17:18:37 -0700177 // Paint used to draw external drop outline
178 private final Paint mExternalDragOutlinePaint = new Paint();
179
Adam Lesinski6b879f02010-11-04 16:15:23 -0700180 // Camera and Matrix used to determine the final position of a neighboring CellLayout
181 private final Matrix mMatrix = new Matrix();
182 private final Camera mCamera = new Camera();
183 private final float mTempFloat2[] = new float[2];
184
Michael Jurkac5b262c2011-01-12 20:24:50 -0800185 enum WallpaperVerticalOffset { TOP, MIDDLE, BOTTOM };
186 int mWallpaperWidth;
187 int mWallpaperHeight;
Michael Jurkaab1983f2011-01-18 15:50:17 -0800188 WallpaperOffsetInterpolator mWallpaperOffset;
Michael Jurkac5b262c2011-01-12 20:24:50 -0800189 boolean mUpdateWallpaperOffsetImmediately = false;
Adam Cohen26976d92011-03-22 15:33:33 -0700190 private Runnable mDelayedResizeRunnable;
Adam Cohencff6af82011-09-13 14:51:53 -0700191 private int mDisplayWidth;
192 private int mDisplayHeight;
193 private boolean mIsStaticWallpaper;
194 private int mWallpaperTravelWidth;
Michael Jurkac5b262c2011-01-12 20:24:50 -0800195
Adam Cohen19072da2011-05-31 14:30:45 -0700196 // Variables relating to the creation of user folders by hovering shortcuts over shortcuts
Adam Cohenc0dcf592011-06-01 15:30:43 -0700197 private static final int FOLDER_CREATION_TIMEOUT = 250;
Adam Cohen19072da2011-05-31 14:30:45 -0700198 private final Alarm mFolderCreationAlarm = new Alarm();
199 private FolderRingAnimator mDragFolderRingAnimator = null;
200 private View mLastDragOverView = null;
201 private boolean mCreateUserFolderOnDrop = false;
Adam Cohen073a46f2011-05-17 16:28:09 -0700202
Adam Cohenf8d28232011-02-01 21:47:00 -0800203 // Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget)
204 private float mXDown;
205 private float mYDown;
206 final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
207 final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
208 final static float TOUCH_SLOP_DAMPING_FACTOR = 4;
209
Adam Cohen4b285c52011-07-21 14:24:06 -0700210 // These variables are used for storing the initial and final values during workspace animations
Adam Cohened51cc92011-08-01 20:28:08 -0700211 private int mSavedScrollX;
212 private float mSavedRotationY;
213 private float mSavedTranslationX;
Adam Cohen4b285c52011-07-21 14:24:06 -0700214 private float mCurrentScaleX;
215 private float mCurrentScaleY;
216 private float mCurrentRotationY;
217 private float mCurrentTranslationX;
218 private float mCurrentTranslationY;
219 private float[] mOldTranslationXs;
220 private float[] mOldTranslationYs;
221 private float[] mOldScaleXs;
222 private float[] mOldScaleYs;
223 private float[] mOldBackgroundAlphas;
224 private float[] mOldBackgroundAlphaMultipliers;
225 private float[] mOldAlphas;
226 private float[] mOldRotationYs;
227 private float[] mNewTranslationXs;
228 private float[] mNewTranslationYs;
229 private float[] mNewScaleXs;
230 private float[] mNewScaleYs;
231 private float[] mNewBackgroundAlphas;
232 private float[] mNewBackgroundAlphaMultipliers;
233 private float[] mNewAlphas;
234 private float[] mNewRotationYs;
235 private float mTransitionProgress;
236
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800237 /**
238 * Used to inflate the Workspace from XML.
239 *
240 * @param context The application's context.
Michael Jurka0142d492010-08-25 17:46:15 -0700241 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800242 */
243 public Workspace(Context context, AttributeSet attrs) {
244 this(context, attrs, 0);
245 }
246
247 /**
248 * Used to inflate the Workspace from XML.
249 *
250 * @param context The application's context.
Michael Jurka0142d492010-08-25 17:46:15 -0700251 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800252 * @param defStyle Unused.
253 */
254 public Workspace(Context context, AttributeSet attrs, int defStyle) {
255 super(context, attrs, defStyle);
Michael Jurka0142d492010-08-25 17:46:15 -0700256 mContentIsRefreshable = false;
Michael Jurka5f1c5092010-09-03 14:15:02 -0700257
Winson Chungf0ea4d32011-06-06 14:27:16 -0700258 // With workspace, data is available straight from the get-go
259 setDataIsReady();
260
Winson Chung2b8e4452011-08-26 18:12:08 -0700261 mFadeInAdjacentScreens =
262 getResources().getBoolean(R.bool.config_workspaceFadeAdjacentScreens);
Dianne Hackborn8f573952009-08-10 23:21:09 -0700263 mWallpaperManager = WallpaperManager.getInstance(context);
Winson Chungaafa03c2010-06-11 17:34:16 -0700264
Michael Jurkaf6440da2011-04-05 14:50:34 -0700265 int cellCountX = DEFAULT_CELL_COUNT_X;
266 int cellCountY = DEFAULT_CELL_COUNT_Y;
267
Winson Chungaafa03c2010-06-11 17:34:16 -0700268 TypedArray a = context.obtainStyledAttributes(attrs,
269 R.styleable.Workspace, defStyle, 0);
Michael Jurkaf6440da2011-04-05 14:50:34 -0700270
Winson Chungb26f3d62011-06-02 10:49:29 -0700271 final Resources res = context.getResources();
Michael Jurkaf6440da2011-04-05 14:50:34 -0700272 if (LauncherApplication.isScreenLarge()) {
Michael Jurkaf61249b2011-05-27 16:27:15 -0700273 // Determine number of rows/columns dynamically
274 // TODO: This code currently fails on tablets with an aspect ratio < 1.3.
275 // Around that ratio we should make cells the same size in portrait and
276 // landscape
Michael Jurkaf6440da2011-04-05 14:50:34 -0700277 TypedArray actionBarSizeTypedArray =
278 context.obtainStyledAttributes(new int[] { android.R.attr.actionBarSize });
Michael Jurkaf61249b2011-05-27 16:27:15 -0700279 final float actionBarHeight = actionBarSizeTypedArray.getDimension(0, 0f);
280 final float systemBarHeight = res.getDimension(R.dimen.status_bar_height);
281 final float smallestScreenDim = res.getConfiguration().smallestScreenWidthDp;
Michael Jurkaf6440da2011-04-05 14:50:34 -0700282
Michael Jurkaf61249b2011-05-27 16:27:15 -0700283 cellCountX = 1;
284 while (CellLayout.widthInPortrait(res, cellCountX + 1) <= smallestScreenDim) {
285 cellCountX++;
Michael Jurkaf6440da2011-04-05 14:50:34 -0700286 }
Michael Jurkaf6440da2011-04-05 14:50:34 -0700287
Michael Jurkaf61249b2011-05-27 16:27:15 -0700288 cellCountY = 1;
289 while (actionBarHeight + CellLayout.heightInLandscape(res, cellCountY + 1)
290 <= smallestScreenDim - systemBarHeight) {
291 cellCountY++;
Michael Jurkaf6440da2011-04-05 14:50:34 -0700292 }
Michael Jurkaf6440da2011-04-05 14:50:34 -0700293 }
294
Winson Chungb26f3d62011-06-02 10:49:29 -0700295 mSpringLoadedShrinkFactor =
296 res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
Winson Chung70fc4382011-08-08 15:31:33 -0700297 mDragViewMultiplyColor = res.getColor(R.color.drag_view_multiply_color);
Winson Chungb26f3d62011-06-02 10:49:29 -0700298
Michael Jurkaf6440da2011-04-05 14:50:34 -0700299 // if the value is manually specified, use that instead
300 cellCountX = a.getInt(R.styleable.Workspace_cellCountX, cellCountX);
301 cellCountY = a.getInt(R.styleable.Workspace_cellCountY, cellCountY);
Michael Jurka0142d492010-08-25 17:46:15 -0700302 mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800303 a.recycle();
304
Adam Cohend22015c2010-07-26 22:02:18 -0700305 LauncherModel.updateWorkspaceLayoutCells(cellCountX, cellCountY);
Joe Onorato0d44e942009-11-16 18:20:51 -0800306 setHapticFeedbackEnabled(false);
Michael Jurka0142d492010-08-25 17:46:15 -0700307
Adam Cohencff6af82011-09-13 14:51:53 -0700308 mLauncher = (Launcher) context;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800309 initWorkspace();
Winson Chungc35afb22011-02-23 13:01:49 -0800310
311 // Disable multitouch across the workspace/all apps/customize tray
312 setMotionEventSplittingEnabled(true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800313 }
314
Michael Jurkad74c9842011-07-10 12:44:21 -0700315 public void onDragStart(DragSource source, Object info, int dragAction) {
316 mIsDragOccuring = true;
317 updateChildrenLayersEnabled();
Adam Cohen446e9402011-09-15 18:21:21 -0700318 mLauncher.lockScreenOrientationOnLargeUI();
Michael Jurkad74c9842011-07-10 12:44:21 -0700319 }
320
321 public void onDragEnd() {
322 mIsDragOccuring = false;
323 updateChildrenLayersEnabled();
Adam Cohen446e9402011-09-15 18:21:21 -0700324 mLauncher.unlockScreenOrientationOnLargeUI();
Michael Jurkad74c9842011-07-10 12:44:21 -0700325 }
326
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800327 /**
328 * Initializes various states for this workspace.
329 */
Michael Jurka0142d492010-08-25 17:46:15 -0700330 protected void initWorkspace() {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800331 Context context = getContext();
Michael Jurka0142d492010-08-25 17:46:15 -0700332 mCurrentPage = mDefaultPage;
333 Launcher.setScreen(mCurrentPage);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800334 LauncherApplication app = (LauncherApplication)context.getApplicationContext();
335 mIconCache = app.getIconCache();
Winson Chunga9abd0e2010-10-27 17:18:37 -0700336 mExternalDragOutlinePaint.setAntiAlias(true);
Patrick Dubroycd68ff52010-10-28 17:57:05 -0700337 setWillNotDraw(false);
Adam Cohen7777d962011-08-18 18:58:38 -0700338 setChildrenDrawnWithCacheEnabled(true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800339
Winson Chungb4b7fa72010-11-18 14:38:53 -0800340 try {
341 final Resources res = getResources();
Winson Chungfd3385f2011-06-15 19:51:24 -0700342 mBackground = res.getDrawable(R.drawable.apps_customize_bg);
Winson Chungb4b7fa72010-11-18 14:38:53 -0800343 } catch (Resources.NotFoundException e) {
344 // In this case, we will skip drawing background protection
345 }
Winson Chung9171e6d2010-11-17 17:39:27 -0800346
Adam Cohen7777d962011-08-18 18:58:38 -0700347 mChangeStateAnimationListener = new AnimatorListenerAdapter() {
Michael Jurka3c4c20f2010-10-28 15:36:06 -0700348 @Override
Michael Jurkac0e8fca2010-10-06 16:41:29 -0700349 public void onAnimationStart(Animator animation) {
Michael Jurkad74c9842011-07-10 12:44:21 -0700350 mIsSwitchingState = true;
Michael Jurkac0e8fca2010-10-06 16:41:29 -0700351 }
Michael Jurka0bb85632011-01-26 00:00:44 -0800352
Michael Jurka3c4c20f2010-10-28 15:36:06 -0700353 @Override
Michael Jurka8edd75c2010-12-17 20:15:06 -0800354 public void onAnimationEnd(Animator animation) {
Michael Jurkad74c9842011-07-10 12:44:21 -0700355 mIsSwitchingState = false;
Michael Jurkaca5b8362011-01-27 13:23:26 -0800356 mWallpaperOffset.setOverrideHorizontalCatchupConstant(false);
Patrick Dubroy60b7c532011-01-16 17:19:32 -0800357 mAnimator = null;
Michael Jurkad74c9842011-07-10 12:44:21 -0700358 updateChildrenLayersEnabled();
Michael Jurkabea15192010-11-17 12:33:46 -0800359 }
360 };
Adam Cohen7777d962011-08-18 18:58:38 -0700361
Michael Jurka0142d492010-08-25 17:46:15 -0700362 mSnapVelocity = 600;
Michael Jurkaab1983f2011-01-18 15:50:17 -0800363 mWallpaperOffset = new WallpaperOffsetInterpolator();
Adam Cohencff6af82011-09-13 14:51:53 -0700364 Display display = mLauncher.getWindowManager().getDefaultDisplay();
365 mDisplayWidth = display.getWidth();
366 mDisplayHeight = display.getHeight();
367 mWallpaperTravelWidth = (int) (mDisplayWidth *
368 wallpaperTravelToScreenWidthRatio(mDisplayWidth, mDisplayHeight));
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800369 }
370
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800371 @Override
Adam Cohenf34bab52010-09-30 14:11:56 -0700372 protected int getScrollMode() {
Winson Chungb26f3d62011-06-02 10:49:29 -0700373 return SmoothPagedView.X_LARGE_MODE;
Adam Cohenf34bab52010-09-30 14:11:56 -0700374 }
375
Michael Jurka08ee7702011-08-11 16:53:35 -0700376 @Override
377 protected void onViewAdded(View child) {
378 super.onViewAdded(child);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800379 if (!(child instanceof CellLayout)) {
380 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
381 }
Adam Cohen2801caf2011-05-13 20:57:39 -0700382 CellLayout cl = ((CellLayout) child);
383 cl.setOnInterceptTouchListener(this);
Adam Cohen2801caf2011-05-13 20:57:39 -0700384 cl.setClickable(true);
385 cl.enableHardwareLayers();
Michael Jurkae5fb0f22011-04-11 13:27:46 -0700386 }
Adam Cohen2801caf2011-05-13 20:57:39 -0700387
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800388 /**
389 * @return The open folder on the current screen, or null if there is none
390 */
391 Folder getOpenFolder() {
Adam Cohen716b51e2011-06-30 12:09:54 -0700392 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohen8e776a62011-06-28 18:10:06 -0700393 int count = dragLayer.getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800394 for (int i = 0; i < count; i++) {
Adam Cohen8e776a62011-06-28 18:10:06 -0700395 View child = dragLayer.getChildAt(i);
Winson Chungaafa03c2010-06-11 17:34:16 -0700396 if (child instanceof Folder) {
397 Folder folder = (Folder) child;
398 if (folder.getInfo().opened)
399 return folder;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800400 }
401 }
402 return null;
403 }
404
Patrick Dubroya0aa0122011-02-24 11:42:23 -0800405 boolean isTouchActive() {
406 return mTouchState != TOUCH_STATE_REST;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800407 }
408
409 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800410 * Adds the specified child in the specified screen. The position and dimension of
411 * the child are defined by x, y, spanX and spanY.
412 *
413 * @param child The child to add in one of the workspace's screens.
414 * @param screen The screen in which to add the child.
415 * @param x The X position of the child in the screen's grid.
416 * @param y The Y position of the child in the screen's grid.
417 * @param spanX The number of cells spanned horizontally by the child.
418 * @param spanY The number of cells spanned vertically by the child.
419 */
Winson Chung3d503fb2011-07-13 17:25:49 -0700420 void addInScreen(View child, long container, int screen, int x, int y, int spanX, int spanY) {
421 addInScreen(child, container, screen, x, y, spanX, spanY, false);
Winson Chungaafa03c2010-06-11 17:34:16 -0700422 }
423
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800424 /**
425 * Adds the specified child in the specified screen. The position and dimension of
426 * the child are defined by x, y, spanX and spanY.
427 *
428 * @param child The child to add in one of the workspace's screens.
429 * @param screen The screen in which to add the child.
430 * @param x The X position of the child in the screen's grid.
431 * @param y The Y position of the child in the screen's grid.
432 * @param spanX The number of cells spanned horizontally by the child.
433 * @param spanY The number of cells spanned vertically by the child.
434 * @param insert When true, the child is inserted at the beginning of the children list.
435 */
Winson Chung3d503fb2011-07-13 17:25:49 -0700436 void addInScreen(View child, long container, int screen, int x, int y, int spanX, int spanY,
437 boolean insert) {
438 if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
439 if (screen < 0 || screen >= getChildCount()) {
440 Log.e(TAG, "The screen must be >= 0 and < " + getChildCount()
441 + " (was " + screen + "); skipping child");
442 return;
443 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800444 }
445
Winson Chung3d503fb2011-07-13 17:25:49 -0700446 final CellLayout layout;
447 if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
448 layout = mLauncher.getHotseat().getLayout();
Winson Chung4d279d92011-07-21 11:46:32 -0700449 child.setOnKeyListener(null);
Winson Chung3d503fb2011-07-13 17:25:49 -0700450
Adam Cohen099f60d2011-08-23 21:07:26 -0700451 // Hide folder title in the hotseat
452 if (child instanceof FolderIcon) {
453 ((FolderIcon) child).setTextVisible(false);
454 }
455
Winson Chung3d503fb2011-07-13 17:25:49 -0700456 if (screen < 0) {
457 screen = mLauncher.getHotseat().getOrderInHotseat(x, y);
458 } else {
459 // Note: We do this to ensure that the hotseat is always laid out in the orientation
460 // of the hotseat in order regardless of which orientation they were added
461 x = mLauncher.getHotseat().getCellXFromOrder(screen);
462 y = mLauncher.getHotseat().getCellYFromOrder(screen);
463 }
464 } else {
Adam Cohen099f60d2011-08-23 21:07:26 -0700465 // Show folder title if not in the hotseat
466 if (child instanceof FolderIcon) {
467 ((FolderIcon) child).setTextVisible(true);
468 }
469
Winson Chung3d503fb2011-07-13 17:25:49 -0700470 layout = (CellLayout) getChildAt(screen);
Winson Chung4d279d92011-07-21 11:46:32 -0700471 child.setOnKeyListener(new BubbleTextViewKeyEventListener());
Winson Chung3d503fb2011-07-13 17:25:49 -0700472 }
473
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800474 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
475 if (lp == null) {
476 lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
477 } else {
478 lp.cellX = x;
479 lp.cellY = y;
480 lp.cellHSpan = spanX;
481 lp.cellVSpan = spanY;
482 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700483
Adam Cohen7f4eabe2011-04-21 16:19:16 -0700484 if (spanX < 0 && spanY < 0) {
485 lp.isLockedToGrid = false;
486 }
487
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700488 // Get the canonical child id to uniquely represent this view in this screen
Winson Chung3d503fb2011-07-13 17:25:49 -0700489 int childId = LauncherModel.getCellLayoutChildId(container, screen, x, y, spanX, spanY);
Michael Jurkaf3ca3ab2010-10-20 17:08:24 -0700490 boolean markCellsAsOccupied = !(child instanceof Folder);
Winson Chung3d503fb2011-07-13 17:25:49 -0700491 if (!layout.addViewToCellLayout(child, insert ? 0 : -1, childId, lp, markCellsAsOccupied)) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700492 // TODO: This branch occurs when the workspace is adding views
493 // outside of the defined grid
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700494 // maybe we should be deleting these items from the LauncherModel?
Winson Chungaafa03c2010-06-11 17:34:16 -0700495 Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
496 }
497
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800498 if (!(child instanceof Folder)) {
Joe Onorato0d44e942009-11-16 18:20:51 -0800499 child.setHapticFeedbackEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800500 child.setOnLongClickListener(mLongClickListener);
501 }
Joe Onorato00acb122009-08-04 16:04:30 -0400502 if (child instanceof DropTarget) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700503 mDragController.addDropTarget((DropTarget) child);
Joe Onorato00acb122009-08-04 16:04:30 -0400504 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800505 }
506
Patrick Dubroyd0ce1ec2011-01-19 18:47:27 -0800507 /**
508 * Check if the point (x, y) hits a given page.
509 */
510 private boolean hitsPage(int index, float x, float y) {
511 final View page = getChildAt(index);
512 if (page != null) {
513 float[] localXY = { x, y };
514 mapPointFromSelfToChild(page, localXY);
515 return (localXY[0] >= 0 && localXY[0] < page.getWidth()
516 && localXY[1] >= 0 && localXY[1] < page.getHeight());
517 }
518 return false;
519 }
520
521 @Override
522 protected boolean hitsPreviousPage(float x, float y) {
523 // mNextPage is set to INVALID_PAGE whenever we are stationary.
524 // Calculating "next page" this way ensures that you scroll to whatever page you tap on
525 final int current = (mNextPage == INVALID_PAGE) ? mCurrentPage : mNextPage;
Michael Jurkab72094f2011-09-14 14:53:19 -0700526
527 // Only allow tap to next page on large devices, where there's significant margin outside
528 // the active workspace
529 return LauncherApplication.isScreenLarge() && hitsPage(current - 1, x, y);
Patrick Dubroyd0ce1ec2011-01-19 18:47:27 -0800530 }
531
532 @Override
533 protected boolean hitsNextPage(float x, float y) {
534 // mNextPage is set to INVALID_PAGE whenever we are stationary.
535 // Calculating "next page" this way ensures that you scroll to whatever page you tap on
536 final int current = (mNextPage == INVALID_PAGE) ? mCurrentPage : mNextPage;
Michael Jurkab72094f2011-09-14 14:53:19 -0700537
538 // Only allow tap to next page on large devices, where there's significant margin outside
539 // the active workspace
540 return LauncherApplication.isScreenLarge() && hitsPage(current + 1, x, y);
Patrick Dubroyd0ce1ec2011-01-19 18:47:27 -0800541 }
542
Patrick Dubroye708c522011-03-01 16:03:43 -0800543 /**
544 * Called directly from a CellLayout (not by the framework), after we've been added as a
545 * listener via setOnInterceptTouchEventListener(). This allows us to tell the CellLayout
546 * that it should intercept touch events, which is not something that is normally supported.
547 */
548 @Override
Michael Jurkadee05892010-07-27 10:01:56 -0700549 public boolean onTouch(View v, MotionEvent event) {
Michael Jurkad74c9842011-07-10 12:44:21 -0700550 return (isSmall() || mIsSwitchingState);
Patrick Dubroye708c522011-03-01 16:03:43 -0800551 }
552
Adam Cohenfc53cd22011-07-20 15:45:11 -0700553 public boolean isSwitchingState() {
554 return mIsSwitchingState;
555 }
556
Adam Cohended9f8d2010-11-03 13:25:16 -0700557 protected void onWindowVisibilityChanged (int visibility) {
558 mLauncher.onWindowVisibilityChanged(visibility);
559 }
560
Michael Jurka5f1c5092010-09-03 14:15:02 -0700561 @Override
562 public boolean dispatchUnhandledMove(View focused, int direction) {
Michael Jurkad74c9842011-07-10 12:44:21 -0700563 if (isSmall() || mIsSwitchingState) {
Michael Jurka5f1c5092010-09-03 14:15:02 -0700564 // when the home screens are shrunken, shouldn't allow side-scrolling
565 return false;
566 }
567 return super.dispatchUnhandledMove(focused, direction);
568 }
569
570 @Override
571 public boolean onInterceptTouchEvent(MotionEvent ev) {
Michael Jurkad771c962011-08-09 15:00:48 -0700572 switch (ev.getAction() & MotionEvent.ACTION_MASK) {
573 case MotionEvent.ACTION_DOWN:
Adam Cohenf8d28232011-02-01 21:47:00 -0800574 mXDown = ev.getX();
575 mYDown = ev.getY();
Michael Jurkad771c962011-08-09 15:00:48 -0700576 break;
577 case MotionEvent.ACTION_POINTER_UP:
578 case MotionEvent.ACTION_UP:
579 if (mTouchState == TOUCH_STATE_REST) {
580 final CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage);
581 if (!currentPage.lastDownOnOccupiedCell()) {
582 onWallpaperTap(ev);
583 }
584 }
Adam Cohenf8d28232011-02-01 21:47:00 -0800585 }
Michael Jurka5f1c5092010-09-03 14:15:02 -0700586 return super.onInterceptTouchEvent(ev);
587 }
588
Michael Jurka1adf5392010-10-18 18:10:22 -0700589 @Override
590 protected void determineScrollingStart(MotionEvent ev) {
Michael Jurkad74c9842011-07-10 12:44:21 -0700591 if (!isSmall() && !mIsSwitchingState) {
Adam Cohen69c42da2011-02-03 16:38:07 -0800592 float deltaX = Math.abs(ev.getX() - mXDown);
593 float deltaY = Math.abs(ev.getY() - mYDown);
Adam Cohenf8d28232011-02-01 21:47:00 -0800594
Adam Cohen69c42da2011-02-03 16:38:07 -0800595 if (Float.compare(deltaX, 0f) == 0) return;
Adam Cohenf8d28232011-02-01 21:47:00 -0800596
Adam Cohen69c42da2011-02-03 16:38:07 -0800597 float slope = deltaY / deltaX;
598 float theta = (float) Math.atan(slope);
Adam Cohenf8d28232011-02-01 21:47:00 -0800599
Adam Cohen69c42da2011-02-03 16:38:07 -0800600 if (deltaX > mTouchSlop || deltaY > mTouchSlop) {
601 cancelCurrentPageLongPress();
602 }
Adam Cohenf8d28232011-02-01 21:47:00 -0800603
Adam Cohen69c42da2011-02-03 16:38:07 -0800604 if (theta > MAX_SWIPE_ANGLE) {
605 // Above MAX_SWIPE_ANGLE, we don't want to ever start scrolling the workspace
606 return;
607 } else if (theta > START_DAMPING_TOUCH_SLOP_ANGLE) {
608 // Above START_DAMPING_TOUCH_SLOP_ANGLE and below MAX_SWIPE_ANGLE, we want to
609 // increase the touch slop to make it harder to begin scrolling the workspace. This
610 // results in vertically scrolling widgets to more easily. The higher the angle, the
611 // more we increase touch slop.
612 theta -= START_DAMPING_TOUCH_SLOP_ANGLE;
613 float extraRatio = (float)
614 Math.sqrt((theta / (MAX_SWIPE_ANGLE - START_DAMPING_TOUCH_SLOP_ANGLE)));
615 super.determineScrollingStart(ev, 1 + TOUCH_SLOP_DAMPING_FACTOR * extraRatio);
616 } else {
617 // Below START_DAMPING_TOUCH_SLOP_ANGLE, we don't do anything special
618 super.determineScrollingStart(ev);
619 }
Adam Cohenf8d28232011-02-01 21:47:00 -0800620 }
Michael Jurka1adf5392010-10-18 18:10:22 -0700621 }
622
Winson Chung007c6982011-06-14 13:27:53 -0700623 @Override
624 protected boolean isScrollingIndicatorEnabled() {
Michael Jurkad74c9842011-07-10 12:44:21 -0700625 return mState != State.SPRING_LOADED;
Winson Chung007c6982011-06-14 13:27:53 -0700626 }
627
Patrick Dubroy1262e362010-10-06 15:49:50 -0700628 protected void onPageBeginMoving() {
Winson Chung007c6982011-06-14 13:27:53 -0700629 super.onPageBeginMoving();
Adam Cohencff6af82011-09-13 14:51:53 -0700630 mIsStaticWallpaper = mWallpaperManager.getWallpaperInfo() == null;
Winson Chung007c6982011-06-14 13:27:53 -0700631
Michael Jurkad74c9842011-07-10 12:44:21 -0700632 if (isHardwareAccelerated()) {
633 updateChildrenLayersEnabled();
Michael Jurka0142d492010-08-25 17:46:15 -0700634 } else {
Michael Jurkad74c9842011-07-10 12:44:21 -0700635 if (mNextPage != INVALID_PAGE) {
636 // we're snapping to a particular screen
637 enableChildrenCache(mCurrentPage, mNextPage);
638 } else {
639 // this is when user is actively dragging a particular screen, they might
640 // swipe it either left or right (but we won't advance by more than one screen)
641 enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1);
642 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800643 }
Winson Chung007c6982011-06-14 13:27:53 -0700644
645 // Only show page outlines as we pan if we are on large screen
646 if (LauncherApplication.isScreenLarge()) {
647 showOutlines();
648 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800649 }
650
Patrick Dubroy1262e362010-10-06 15:49:50 -0700651 protected void onPageEndMoving() {
Winson Chung007c6982011-06-14 13:27:53 -0700652 super.onPageEndMoving();
653
Michael Jurkad74c9842011-07-10 12:44:21 -0700654 if (isHardwareAccelerated()) {
655 updateChildrenLayersEnabled();
656 } else {
657 clearChildrenCache();
658 }
659
Patrick Dubroy1262e362010-10-06 15:49:50 -0700660 // Hide the outlines, as long as we're not dragging
661 if (!mDragController.dragging()) {
Winson Chung007c6982011-06-14 13:27:53 -0700662 // Only hide page outlines as we pan if we are on large screen
663 if (LauncherApplication.isScreenLarge()) {
664 hideOutlines();
665 }
Patrick Dubroy1262e362010-10-06 15:49:50 -0700666 }
Adam Cohen68d73932010-11-15 10:50:58 -0800667 mOverScrollMaxBackgroundAlpha = 0.0f;
Adam Cohene0f66b52010-11-23 15:06:07 -0800668 mOverScrollPageIndex = -1;
Adam Cohen26976d92011-03-22 15:33:33 -0700669
670 if (mDelayedResizeRunnable != null) {
671 mDelayedResizeRunnable.run();
672 mDelayedResizeRunnable = null;
673 }
Michael Jurka0142d492010-08-25 17:46:15 -0700674 }
675
676 @Override
677 protected void notifyPageSwitchListener() {
678 super.notifyPageSwitchListener();
Michael Jurka0142d492010-08-25 17:46:15 -0700679 Launcher.setScreen(mCurrentPage);
680 };
681
Michael Jurkac5b262c2011-01-12 20:24:50 -0800682 // As a ratio of screen height, the total distance we want the parallax effect to span
683 // vertically
684 private float wallpaperTravelToScreenHeightRatio(int width, int height) {
685 return 1.1f;
686 }
687
688 // As a ratio of screen height, the total distance we want the parallax effect to span
689 // horizontally
690 private float wallpaperTravelToScreenWidthRatio(int width, int height) {
691 float aspectRatio = width / (float) height;
692
693 // At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width
694 // At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width
695 // We will use these two data points to extrapolate how much the wallpaper parallax effect
696 // to span (ie travel) at any aspect ratio:
697
698 final float ASPECT_RATIO_LANDSCAPE = 16/10f;
699 final float ASPECT_RATIO_PORTRAIT = 10/16f;
700 final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f;
701 final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f;
702
703 // To find out the desired width at different aspect ratios, we use the following two
704 // formulas, where the coefficient on x is the aspect ratio (width/height):
705 // (16/10)x + y = 1.5
706 // (10/16)x + y = 1.2
707 // We solve for x and y and end up with a final formula:
708 final float x =
709 (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) /
710 (ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT);
711 final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT;
712 return x * aspectRatio + y;
713 }
714
715 // The range of scroll values for Workspace
716 private int getScrollRange() {
717 return getChildOffset(getChildCount() - 1) - getChildOffset(0);
718 }
719
720 protected void setWallpaperDimension() {
Winson Chung201bc822011-06-20 15:41:53 -0700721 DisplayMetrics displayMetrics = new DisplayMetrics();
Adam Cohencff6af82011-09-13 14:51:53 -0700722 mLauncher.getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics);
Winson Chung201bc822011-06-20 15:41:53 -0700723 final int maxDim = Math.max(displayMetrics.widthPixels, displayMetrics.heightPixels);
724 final int minDim = Math.min(displayMetrics.widthPixels, displayMetrics.heightPixels);
Michael Jurkac5b262c2011-01-12 20:24:50 -0800725
726 // We need to ensure that there is enough extra space in the wallpaper for the intended
727 // parallax effects
728 mWallpaperWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim));
729 mWallpaperHeight = (int)(maxDim * wallpaperTravelToScreenHeightRatio(maxDim, minDim));
Winson Chungf7640c82011-02-28 13:47:29 -0800730 new Thread("setWallpaperDimension") {
731 public void run() {
732 mWallpaperManager.suggestDesiredDimensions(mWallpaperWidth, mWallpaperHeight);
733 }
734 }.start();
Michael Jurkac5b262c2011-01-12 20:24:50 -0800735 }
736
Michael Jurkaab1983f2011-01-18 15:50:17 -0800737 public void setVerticalWallpaperOffset(float offset) {
738 mWallpaperOffset.setFinalY(offset);
739 }
740 public float getVerticalWallpaperOffset() {
741 return mWallpaperOffset.getCurrY();
742 }
743 public void setHorizontalWallpaperOffset(float offset) {
744 mWallpaperOffset.setFinalX(offset);
745 }
746 public float getHorizontalWallpaperOffset() {
747 return mWallpaperOffset.getCurrX();
Michael Jurkac5b262c2011-01-12 20:24:50 -0800748 }
749
Michael Jurkaab1983f2011-01-18 15:50:17 -0800750 private float wallpaperOffsetForCurrentScroll() {
Adam Cohencff6af82011-09-13 14:51:53 -0700751 final boolean isStaticWallpaper = mIsStaticWallpaper;
Michael Jurkaab1983f2011-01-18 15:50:17 -0800752 // The wallpaper travel width is how far, from left to right, the wallpaper will move
753 // at this orientation (for example, in portrait mode we don't move all the way to the
754 // edges of the wallpaper, or otherwise the parallax effect would be too strong)
Adam Cohencff6af82011-09-13 14:51:53 -0700755 int wallpaperTravelWidth = mWallpaperTravelWidth;
Michael Jurkaf70c7812011-03-07 16:53:12 -0800756 if (!isStaticWallpaper) {
757 wallpaperTravelWidth = mWallpaperWidth;
758 }
Michael Jurkaab1983f2011-01-18 15:50:17 -0800759
Michael Jurkaab1983f2011-01-18 15:50:17 -0800760 // Set wallpaper offset steps (1 / (number of screens - 1))
761 // We have 3 vertical offset states (centered, and then top/bottom aligned
762 // for all apps/customize)
763 mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 1.0f / (3 - 1));
764
Adam Cohenbeff8c62011-08-31 17:46:01 -0700765 // For the purposes of computing the scrollRange and overScrollOffset, we ignore
766 // assume that mLayoutScale is 1. This means that when we're in spring-loaded mode,
767 // there's no discrepancy between the wallpaper offset for a given page.
768 float layoutScale = mLayoutScale;
769 mLayoutScale = 1f;
Michael Jurka09d6eb02011-01-28 16:05:09 -0800770 int scrollRange = getScrollRange();
771 float scrollProgressOffset = 0;
772
Adam Cohenb5ba0972011-09-07 18:02:31 -0700773 // Account for over scroll: you only see the absolute edge of the wallpaper if
774 // you over scroll as far as you can in landscape mode. Only do this for static wallpapers
Michael Jurka09d6eb02011-01-28 16:05:09 -0800775 // because live wallpapers (and probably 3rd party wallpaper providers) rely on the offset
776 // being even intervals from 0 to 1 (eg [0, 0.25, 0.5, 0.75, 1])
Michael Jurka09d6eb02011-01-28 16:05:09 -0800777 if (isStaticWallpaper) {
Adam Cohencff6af82011-09-13 14:51:53 -0700778 int overScrollOffset = (int) (maxOverScroll() * mDisplayWidth);
Adam Cohenb5ba0972011-09-07 18:02:31 -0700779 scrollProgressOffset += overScrollOffset / (float) getScrollRange();
780 scrollRange += 2 * overScrollOffset;
Michael Jurka09d6eb02011-01-28 16:05:09 -0800781 }
782
Adam Cohenbeff8c62011-08-31 17:46:01 -0700783 // Again, we adjust the wallpaper offset to be consistent between values of mLayoutScale
Adam Cohenb5ba0972011-09-07 18:02:31 -0700784 boolean overScrollWallpaper = LauncherApplication.isScreenLarge();
785 float adjustedScrollX = overScrollWallpaper ?
786 mScrollX : Math.max(0, Math.min(mScrollX, mMaxScrollX));
787 adjustedScrollX *= mWallpaperScrollRatio;
Adam Cohenbeff8c62011-08-31 17:46:01 -0700788 mLayoutScale = layoutScale;
789
Michael Jurkaab1983f2011-01-18 15:50:17 -0800790 float scrollProgress =
Adam Cohenbeff8c62011-08-31 17:46:01 -0700791 adjustedScrollX / (float) scrollRange + scrollProgressOffset;
Michael Jurkaab1983f2011-01-18 15:50:17 -0800792 float offsetInDips = wallpaperTravelWidth * scrollProgress +
Michael Jurkaf70c7812011-03-07 16:53:12 -0800793 (mWallpaperWidth - wallpaperTravelWidth) / 2; // center it
Michael Jurkaab1983f2011-01-18 15:50:17 -0800794 float offset = offsetInDips / (float) mWallpaperWidth;
795 return offset;
796 }
797 private void syncWallpaperOffsetWithScroll() {
Winson Chungb46a2d12011-04-22 14:13:05 -0700798 final boolean enableWallpaperEffects = isHardwareAccelerated();
799 if (enableWallpaperEffects) {
Michael Jurkaab1983f2011-01-18 15:50:17 -0800800 mWallpaperOffset.setFinalX(wallpaperOffsetForCurrentScroll());
Michael Jurkac5b262c2011-01-12 20:24:50 -0800801 }
802 }
803
804 public void updateWallpaperOffsetImmediately() {
805 mUpdateWallpaperOffsetImmediately = true;
806 }
807
Michael Jurkaab1983f2011-01-18 15:50:17 -0800808 private void updateWallpaperOffsets() {
809 boolean updateNow = false;
810 boolean keepUpdating = true;
811 if (mUpdateWallpaperOffsetImmediately) {
812 updateNow = true;
813 keepUpdating = false;
814 mWallpaperOffset.jumpToFinal();
815 mUpdateWallpaperOffsetImmediately = false;
Michael Jurkac5b262c2011-01-12 20:24:50 -0800816 } else {
Michael Jurkaab1983f2011-01-18 15:50:17 -0800817 updateNow = keepUpdating = mWallpaperOffset.computeScrollOffset();
Michael Jurkac5b262c2011-01-12 20:24:50 -0800818 }
Michael Jurkaab1983f2011-01-18 15:50:17 -0800819 if (updateNow) {
Michael Jurka9c6fbed2011-03-02 17:41:34 -0800820 if (mWindowToken != null) {
821 mWallpaperManager.setWallpaperOffsets(mWindowToken,
Michael Jurkaab1983f2011-01-18 15:50:17 -0800822 mWallpaperOffset.getCurrX(), mWallpaperOffset.getCurrY());
823 }
Michael Jurkac5b262c2011-01-12 20:24:50 -0800824 }
Michael Jurkaab1983f2011-01-18 15:50:17 -0800825 if (keepUpdating) {
Michael Jurka2763be32011-02-24 11:19:57 -0800826 fastInvalidate();
Michael Jurkac5b262c2011-01-12 20:24:50 -0800827 }
Michael Jurkaab1983f2011-01-18 15:50:17 -0800828 }
829
Adam Cohenbeff8c62011-08-31 17:46:01 -0700830 @Override
831 protected void updateCurrentPageScroll() {
832 super.updateCurrentPageScroll();
Adam Cohen5037a0b2011-09-01 13:10:40 -0700833 computeWallpaperScrollRatio(mCurrentPage);
Adam Cohenbeff8c62011-08-31 17:46:01 -0700834 }
835
836 @Override
837 protected void snapToPage(int whichPage) {
838 super.snapToPage(whichPage);
Adam Cohen5037a0b2011-09-01 13:10:40 -0700839 computeWallpaperScrollRatio(whichPage);
Adam Cohenbeff8c62011-08-31 17:46:01 -0700840 }
841
Adam Cohen5037a0b2011-09-01 13:10:40 -0700842 private void computeWallpaperScrollRatio(int page) {
Adam Cohenbeff8c62011-08-31 17:46:01 -0700843 // Here, we determine what the desired scroll would be with and without a layout scale,
844 // and compute a ratio between the two. This allows us to adjust the wallpaper offset
845 // as though there is no layout scale.
846 float layoutScale = mLayoutScale;
Adam Cohen5037a0b2011-09-01 13:10:40 -0700847 int scaled = getChildOffset(page) - getRelativeChildOffset(page);
Adam Cohenbeff8c62011-08-31 17:46:01 -0700848 mLayoutScale = 1.0f;
Adam Cohen5037a0b2011-09-01 13:10:40 -0700849 float unscaled = getChildOffset(page) - getRelativeChildOffset(page);
Adam Cohenbeff8c62011-08-31 17:46:01 -0700850 mLayoutScale = layoutScale;
851 if (scaled > 0) {
852 mWallpaperScrollRatio = (1.0f * unscaled) / scaled;
853 } else {
854 mWallpaperScrollRatio = 1f;
855 }
856 }
857
Michael Jurkaab1983f2011-01-18 15:50:17 -0800858 class WallpaperOffsetInterpolator {
859 float mFinalHorizontalWallpaperOffset = 0.0f;
860 float mFinalVerticalWallpaperOffset = 0.5f;
861 float mHorizontalWallpaperOffset = 0.0f;
862 float mVerticalWallpaperOffset = 0.5f;
863 long mLastWallpaperOffsetUpdateTime;
Michael Jurkaca5b8362011-01-27 13:23:26 -0800864 boolean mIsMovingFast;
865 boolean mOverrideHorizontalCatchupConstant;
866 float mHorizontalCatchupConstant = 0.35f;
867 float mVerticalCatchupConstant = 0.35f;
Michael Jurkaab1983f2011-01-18 15:50:17 -0800868
869 public WallpaperOffsetInterpolator() {
870 }
871
Michael Jurkaca5b8362011-01-27 13:23:26 -0800872 public void setOverrideHorizontalCatchupConstant(boolean override) {
873 mOverrideHorizontalCatchupConstant = override;
874 }
875
876 public void setHorizontalCatchupConstant(float f) {
877 mHorizontalCatchupConstant = f;
878 }
879
880 public void setVerticalCatchupConstant(float f) {
881 mVerticalCatchupConstant = f;
882 }
883
Michael Jurkaab1983f2011-01-18 15:50:17 -0800884 public boolean computeScrollOffset() {
885 if (Float.compare(mHorizontalWallpaperOffset, mFinalHorizontalWallpaperOffset) == 0 &&
886 Float.compare(mVerticalWallpaperOffset, mFinalVerticalWallpaperOffset) == 0) {
Michael Jurkaca5b8362011-01-27 13:23:26 -0800887 mIsMovingFast = false;
Michael Jurkaab1983f2011-01-18 15:50:17 -0800888 return false;
889 }
Adam Cohencff6af82011-09-13 14:51:53 -0700890 boolean isLandscape = mDisplayWidth > mDisplayHeight;
Michael Jurkaab1983f2011-01-18 15:50:17 -0800891
892 long currentTime = System.currentTimeMillis();
893 long timeSinceLastUpdate = currentTime - mLastWallpaperOffsetUpdateTime;
894 timeSinceLastUpdate = Math.min((long) (1000/30f), timeSinceLastUpdate);
895 timeSinceLastUpdate = Math.max(1L, timeSinceLastUpdate);
Michael Jurkaca5b8362011-01-27 13:23:26 -0800896
897 float xdiff = Math.abs(mFinalHorizontalWallpaperOffset - mHorizontalWallpaperOffset);
898 if (!mIsMovingFast && xdiff > 0.07) {
899 mIsMovingFast = true;
900 }
901
902 float fractionToCatchUpIn1MsHorizontal;
903 if (mOverrideHorizontalCatchupConstant) {
904 fractionToCatchUpIn1MsHorizontal = mHorizontalCatchupConstant;
905 } else if (mIsMovingFast) {
906 fractionToCatchUpIn1MsHorizontal = isLandscape ? 0.5f : 0.75f;
907 } else {
908 // slow
909 fractionToCatchUpIn1MsHorizontal = isLandscape ? 0.27f : 0.5f;
910 }
911 float fractionToCatchUpIn1MsVertical = mVerticalCatchupConstant;
912
Michael Jurkaca5b8362011-01-27 13:23:26 -0800913 fractionToCatchUpIn1MsHorizontal /= 33f;
914 fractionToCatchUpIn1MsVertical /= 33f;
Michael Jurkaab1983f2011-01-18 15:50:17 -0800915
916 final float UPDATE_THRESHOLD = 0.00001f;
917 float hOffsetDelta = mFinalHorizontalWallpaperOffset - mHorizontalWallpaperOffset;
918 float vOffsetDelta = mFinalVerticalWallpaperOffset - mVerticalWallpaperOffset;
Michael Jurkaca5b8362011-01-27 13:23:26 -0800919 boolean jumpToFinalValue = Math.abs(hOffsetDelta) < UPDATE_THRESHOLD &&
920 Math.abs(vOffsetDelta) < UPDATE_THRESHOLD;
Michael Jurkaab1983f2011-01-18 15:50:17 -0800921 if (jumpToFinalValue) {
922 mHorizontalWallpaperOffset = mFinalHorizontalWallpaperOffset;
923 mVerticalWallpaperOffset = mFinalVerticalWallpaperOffset;
924 } else {
925 float percentToCatchUpVertical =
Michael Jurkaca5b8362011-01-27 13:23:26 -0800926 Math.min(1.0f, timeSinceLastUpdate * fractionToCatchUpIn1MsVertical);
Michael Jurkaab1983f2011-01-18 15:50:17 -0800927 float percentToCatchUpHorizontal =
Michael Jurkaca5b8362011-01-27 13:23:26 -0800928 Math.min(1.0f, timeSinceLastUpdate * fractionToCatchUpIn1MsHorizontal);
Michael Jurkaab1983f2011-01-18 15:50:17 -0800929 mHorizontalWallpaperOffset += percentToCatchUpHorizontal * hOffsetDelta;
930 mVerticalWallpaperOffset += percentToCatchUpVertical * vOffsetDelta;
931 }
Michael Jurkaca5b8362011-01-27 13:23:26 -0800932
Michael Jurkaab1983f2011-01-18 15:50:17 -0800933 mLastWallpaperOffsetUpdateTime = System.currentTimeMillis();
934 return true;
935 }
936
937 public float getCurrX() {
938 return mHorizontalWallpaperOffset;
939 }
940
941 public float getFinalX() {
942 return mFinalHorizontalWallpaperOffset;
943 }
944
945 public float getCurrY() {
946 return mVerticalWallpaperOffset;
947 }
948
949 public float getFinalY() {
950 return mFinalVerticalWallpaperOffset;
951 }
952
953 public void setFinalX(float x) {
954 mFinalHorizontalWallpaperOffset = Math.max(0f, Math.min(x, 1.0f));
955 }
956
957 public void setFinalY(float y) {
958 mFinalVerticalWallpaperOffset = Math.max(0f, Math.min(y, 1.0f));
959 }
960
961 public void jumpToFinal() {
962 mHorizontalWallpaperOffset = mFinalHorizontalWallpaperOffset;
963 mVerticalWallpaperOffset = mFinalVerticalWallpaperOffset;
964 }
Dianne Hackborn8f573952009-08-10 23:21:09 -0700965 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700966
Michael Jurka340c5f32010-10-21 16:49:19 -0700967 @Override
968 public void computeScroll() {
969 super.computeScroll();
Adam Cohenbeff8c62011-08-31 17:46:01 -0700970 syncWallpaperOffsetWithScroll();
Michael Jurka340c5f32010-10-21 16:49:19 -0700971 }
972
Patrick Dubroy94f78a52011-02-28 17:39:16 -0800973 void showOutlines() {
Michael Jurkad74c9842011-07-10 12:44:21 -0700974 if (!isSmall() && !mIsSwitchingState) {
Winson Chung9171e6d2010-11-17 17:39:27 -0800975 if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
976 if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
977 mChildrenOutlineFadeInAnimation = ObjectAnimator.ofFloat(this, "childrenOutlineAlpha", 1.0f);
978 mChildrenOutlineFadeInAnimation.setDuration(CHILDREN_OUTLINE_FADE_IN_DURATION);
979 mChildrenOutlineFadeInAnimation.start();
Michael Jurka3e7c7632010-10-02 16:01:03 -0700980 }
Adam Cohenf34bab52010-09-30 14:11:56 -0700981 }
982
Patrick Dubroy94f78a52011-02-28 17:39:16 -0800983 void hideOutlines() {
Michael Jurkad74c9842011-07-10 12:44:21 -0700984 if (!isSmall() && !mIsSwitchingState) {
Winson Chung9171e6d2010-11-17 17:39:27 -0800985 if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
986 if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
987 mChildrenOutlineFadeOutAnimation = ObjectAnimator.ofFloat(this, "childrenOutlineAlpha", 0.0f);
988 mChildrenOutlineFadeOutAnimation.setDuration(CHILDREN_OUTLINE_FADE_OUT_DURATION);
989 mChildrenOutlineFadeOutAnimation.setStartDelay(CHILDREN_OUTLINE_FADE_OUT_DELAY);
990 mChildrenOutlineFadeOutAnimation.start();
Michael Jurka3e7c7632010-10-02 16:01:03 -0700991 }
Adam Cohenf34bab52010-09-30 14:11:56 -0700992 }
993
Patrick Dubroy94f78a52011-02-28 17:39:16 -0800994 public void showOutlinesTemporarily() {
995 if (!mIsPageMoving && !isTouchActive()) {
996 snapToPage(mCurrentPage);
997 }
998 }
999
Winson Chung9171e6d2010-11-17 17:39:27 -08001000 public void setChildrenOutlineAlpha(float alpha) {
1001 mChildrenOutlineAlpha = alpha;
Adam Cohenf34bab52010-09-30 14:11:56 -07001002 for (int i = 0; i < getChildCount(); i++) {
1003 CellLayout cl = (CellLayout) getChildAt(i);
1004 cl.setBackgroundAlpha(alpha);
1005 }
1006 }
1007
Winson Chung9171e6d2010-11-17 17:39:27 -08001008 public float getChildrenOutlineAlpha() {
1009 return mChildrenOutlineAlpha;
1010 }
1011
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001012 void disableBackground() {
1013 mDrawBackground = false;
1014 }
1015 void enableBackground() {
1016 mDrawBackground = true;
1017 }
1018
Michael Jurka7bdb25a2011-08-03 15:16:44 -07001019 private void animateBackgroundGradient(float finalAlpha, boolean animated) {
Winson Chungb4b7fa72010-11-18 14:38:53 -08001020 if (mBackground == null) return;
Michael Jurkab9e14972011-07-25 17:57:40 -07001021 if (mBackgroundFadeInAnimation != null) {
1022 mBackgroundFadeInAnimation.cancel();
1023 mBackgroundFadeInAnimation = null;
1024 }
1025 if (mBackgroundFadeOutAnimation != null) {
1026 mBackgroundFadeOutAnimation.cancel();
1027 mBackgroundFadeOutAnimation = null;
1028 }
Michael Jurka7bdb25a2011-08-03 15:16:44 -07001029 float startAlpha = getBackgroundAlpha();
1030 if (finalAlpha != startAlpha) {
1031 if (animated) {
1032 mBackgroundFadeOutAnimation = ValueAnimator.ofFloat(startAlpha, finalAlpha);
1033 mBackgroundFadeOutAnimation.addUpdateListener(new AnimatorUpdateListener() {
1034 public void onAnimationUpdate(ValueAnimator animation) {
1035 setBackgroundAlpha(((Float) animation.getAnimatedValue()).floatValue());
1036 }
1037 });
1038 mBackgroundFadeOutAnimation.setInterpolator(new DecelerateInterpolator(1.5f));
1039 mBackgroundFadeOutAnimation.setDuration(BACKGROUND_FADE_OUT_DURATION);
1040 mBackgroundFadeOutAnimation.start();
1041 } else {
1042 setBackgroundAlpha(finalAlpha);
1043 }
Michael Jurkab9e14972011-07-25 17:57:40 -07001044 }
Winson Chung9171e6d2010-11-17 17:39:27 -08001045 }
1046
1047 public void setBackgroundAlpha(float alpha) {
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001048 if (alpha != mBackgroundAlpha) {
1049 mBackgroundAlpha = alpha;
1050 invalidate();
1051 }
Winson Chung9171e6d2010-11-17 17:39:27 -08001052 }
1053
Adam Cohenf34bab52010-09-30 14:11:56 -07001054 public float getBackgroundAlpha() {
1055 return mBackgroundAlpha;
1056 }
1057
Adam Lesinski6b879f02010-11-04 16:15:23 -07001058 /**
1059 * Due to 3D transformations, if two CellLayouts are theoretically touching each other,
1060 * on the xy plane, when one is rotated along the y-axis, the gap between them is perceived
1061 * as being larger. This method computes what offset the rotated view should be translated
1062 * in order to minimize this perceived gap.
1063 * @param degrees Angle of the view
1064 * @param width Width of the view
1065 * @param height Height of the view
1066 * @return Offset to be used in a View.setTranslationX() call
1067 */
1068 private float getOffsetXForRotation(float degrees, int width, int height) {
1069 mMatrix.reset();
1070 mCamera.save();
1071 mCamera.rotateY(Math.abs(degrees));
1072 mCamera.getMatrix(mMatrix);
1073 mCamera.restore();
1074
1075 mMatrix.preTranslate(-width * 0.5f, -height * 0.5f);
1076 mMatrix.postTranslate(width * 0.5f, height * 0.5f);
1077 mTempFloat2[0] = width;
1078 mTempFloat2[1] = height;
1079 mMatrix.mapPoints(mTempFloat2);
1080 return (width - mTempFloat2[0]) * (degrees > 0.0f ? 1.0f : -1.0f);
1081 }
1082
Adam Cohen68d73932010-11-15 10:50:58 -08001083 float backgroundAlphaInterpolator(float r) {
1084 float pivotA = 0.1f;
1085 float pivotB = 0.4f;
1086 if (r < pivotA) {
1087 return 0;
1088 } else if (r > pivotB) {
1089 return 1.0f;
1090 } else {
1091 return (r - pivotA)/(pivotB - pivotA);
1092 }
1093 }
1094
1095 float overScrollBackgroundAlphaInterpolator(float r) {
Adam Cohene0f66b52010-11-23 15:06:07 -08001096 float threshold = 0.08f;
Adam Cohen68d73932010-11-15 10:50:58 -08001097
1098 if (r > mOverScrollMaxBackgroundAlpha) {
1099 mOverScrollMaxBackgroundAlpha = r;
1100 } else if (r < mOverScrollMaxBackgroundAlpha) {
1101 r = mOverScrollMaxBackgroundAlpha;
1102 }
1103
1104 return Math.min(r / threshold, 1.0f);
1105 }
1106
Adam Cohenb5ba0972011-09-07 18:02:31 -07001107 private void screenScrolledLargeUI(int screenCenter) {
Adam Cohen21f12b52010-10-07 17:15:37 -07001108 for (int i = 0; i < getChildCount(); i++) {
Adam Cohen1b0aaac2010-10-28 11:11:18 -07001109 CellLayout cl = (CellLayout) getChildAt(i);
1110 if (cl != null) {
Adam Cohenb5ba0972011-09-07 18:02:31 -07001111 float scrollProgress = getScrollProgress(screenCenter, cl, i);
Adam Cohen21f12b52010-10-07 17:15:37 -07001112 float rotation = WORKSPACE_ROTATION * scrollProgress;
Adam Lesinski6b879f02010-11-04 16:15:23 -07001113 float translationX = getOffsetXForRotation(rotation, cl.getWidth(), cl.getHeight());
Adam Cohen68d73932010-11-15 10:50:58 -08001114
Adam Cohenb5ba0972011-09-07 18:02:31 -07001115 // If the current page (i) is being over scrolled, we use a different
1116 // set of rules for setting the background alpha multiplier.
Adam Cohencff6af82011-09-13 14:51:53 -07001117 if (!isSmall()) {
1118 if ((mScrollX < 0 && i == 0) || (mScrollX > mMaxScrollX &&
1119 i == getChildCount() -1 )) {
1120 cl.setBackgroundAlphaMultiplier(
1121 overScrollBackgroundAlphaInterpolator(Math.abs(scrollProgress)));
1122 mOverScrollPageIndex = i;
1123 } else if (mOverScrollPageIndex != i) {
1124 cl.setBackgroundAlphaMultiplier(
1125 backgroundAlphaInterpolator(Math.abs(scrollProgress)));
1126 }
Adam Cohenb5ba0972011-09-07 18:02:31 -07001127 }
Adam Cohenb5ba0972011-09-07 18:02:31 -07001128 cl.setTranslationX(translationX);
Adam Cohen1b0aaac2010-10-28 11:11:18 -07001129 cl.setRotationY(rotation);
Adam Cohenf34bab52010-09-30 14:11:56 -07001130 }
1131 }
1132 }
1133
Adam Cohenb5ba0972011-09-07 18:02:31 -07001134 private void resetCellLayoutTransforms(CellLayout cl, boolean left) {
1135 cl.setTranslationX(0);
1136 cl.setRotationY(0);
1137 cl.setOverScrollAmount(0, left);
1138 cl.setPivotX(cl.getMeasuredWidth() / 2);
1139 cl.setPivotY(cl.getMeasuredHeight() / 2);
1140 }
1141
1142 private void screenScrolledStandardUI(int screenCenter) {
1143 if (mScrollX < 0 || mScrollX > mMaxScrollX) {
1144 int index = mScrollX < 0 ? 0 : getChildCount() - 1;
1145 CellLayout cl = (CellLayout) getChildAt(index);
1146 float scrollProgress = getScrollProgress(screenCenter, cl, index);
1147 cl.setOverScrollAmount(Math.abs(scrollProgress), index == 0);
1148 float translationX = index == 0 ? mScrollX : - (mMaxScrollX - mScrollX);
1149 float rotation = - WORKSPACE_OVERSCROLL_ROTATION * scrollProgress;
Adam Cohencff6af82011-09-13 14:51:53 -07001150 cl.setCameraDistance(mDensity * CAMERA_DISTANCE);
Adam Cohenb5ba0972011-09-07 18:02:31 -07001151 cl.setPivotX(cl.getMeasuredWidth() * (index == 0 ? 0.75f : 0.25f));
1152 cl.setTranslationX(translationX);
1153 cl.setRotationY(rotation);
1154 } else {
Adam Cohen7842d7f2011-09-12 15:28:15 -07001155 // We don't want to mess with the translations during transitions
1156 if (!isSwitchingState()) {
1157 resetCellLayoutTransforms((CellLayout) getChildAt(0), true);
1158 resetCellLayoutTransforms((CellLayout) getChildAt(getChildCount() - 1), false);
1159 }
Adam Cohenb5ba0972011-09-07 18:02:31 -07001160 }
1161 }
1162
1163 @Override
1164 protected void screenScrolled(int screenCenter) {
1165 super.screenScrolled(screenCenter);
1166 if (LauncherApplication.isScreenLarge()) {
1167 screenScrolledLargeUI(screenCenter);
1168 } else {
1169 screenScrolledStandardUI(screenCenter);
1170 }
1171 }
1172
1173 @Override
1174 protected void overScroll(float amount) {
1175 if (LauncherApplication.isScreenLarge()) {
1176 dampedOverScroll(amount);
1177 } else {
1178 acceleratedOverScroll(amount);
1179 }
1180 }
1181
Joe Onorato00acb122009-08-04 16:04:30 -04001182 protected void onAttachedToWindow() {
1183 super.onAttachedToWindow();
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001184 mWindowToken = getWindowToken();
Joe Onorato956091b2010-02-19 12:47:40 -08001185 computeScroll();
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001186 mDragController.setWindowToken(mWindowToken);
1187 }
1188
1189 protected void onDetachedFromWindow() {
1190 mWindowToken = null;
Joe Onorato00acb122009-08-04 16:04:30 -04001191 }
1192
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001193 @Override
Michael Jurka28750fb2010-09-24 17:43:49 -07001194 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Michael Jurkac5b262c2011-01-12 20:24:50 -08001195 if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
1196 mUpdateWallpaperOffsetImmediately = true;
1197 }
Michael Jurka0142d492010-08-25 17:46:15 -07001198 super.onLayout(changed, left, top, right, bottom);
Winson Chungaafa03c2010-06-11 17:34:16 -07001199
Michael Jurka79212d82010-07-30 16:36:20 -07001200 // if shrinkToBottom() is called on initialization, it has to be deferred
1201 // until after the first call to onLayout so that it has the correct width
Michael Jurkad74c9842011-07-10 12:44:21 -07001202 if (mSwitchStateAfterFirstLayout) {
1203 mSwitchStateAfterFirstLayout = false;
Adam Cohen696a6792010-12-09 11:46:38 -08001204 // shrink can trigger a synchronous onLayout call, so we
1205 // post this to avoid a stack overflow / tangled onLayout calls
1206 post(new Runnable() {
1207 public void run() {
Adam Cohen7777d962011-08-18 18:58:38 -07001208 changeState(mStateAfterFirstLayout, false);
Adam Cohen696a6792010-12-09 11:46:38 -08001209 }
1210 });
Michael Jurka79212d82010-07-30 16:36:20 -07001211 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001212 }
1213
1214 @Override
Winson Chung9171e6d2010-11-17 17:39:27 -08001215 protected void onDraw(Canvas canvas) {
Michael Jurkaab1983f2011-01-18 15:50:17 -08001216 updateWallpaperOffsets();
Michael Jurkac5b262c2011-01-12 20:24:50 -08001217
Winson Chung9171e6d2010-11-17 17:39:27 -08001218 // Draw the background gradient if necessary
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001219 if (mBackground != null && mBackgroundAlpha > 0.0f && mDrawBackground) {
Winson Chung8d006d52010-11-29 15:55:29 -08001220 int alpha = (int) (mBackgroundAlpha * 255);
Winson Chungf0ea4d32011-06-06 14:27:16 -07001221 mBackground.setAlpha(alpha);
1222 mBackground.setBounds(mScrollX, 0, mScrollX + getMeasuredWidth(),
1223 getMeasuredHeight());
1224 mBackground.draw(canvas);
Winson Chung9171e6d2010-11-17 17:39:27 -08001225 }
Adam Cohen073a46f2011-05-17 16:28:09 -07001226
Winson Chung9171e6d2010-11-17 17:39:27 -08001227 super.onDraw(canvas);
1228 }
1229
1230 @Override
Michael Jurka0142d492010-08-25 17:46:15 -07001231 protected void dispatchDraw(Canvas canvas) {
Winson Chung557d6ed2011-07-08 15:34:52 -07001232 super.dispatchDraw(canvas);
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001233
Winson Chung557d6ed2011-07-08 15:34:52 -07001234 if (mInScrollArea && !LauncherApplication.isScreenLarge()) {
1235 final int width = getWidth();
1236 final int height = getHeight();
1237 final int pageHeight = getChildAt(0).getHeight();
Patrick Dubroy0207c522010-11-03 22:12:02 -07001238
Winson Chungdea74b72011-09-13 18:06:43 -07001239 // Set the height of the outline to be the height of the page
1240 final int offset = (height - pageHeight - mPaddingTop - mPaddingBottom) / 2;
1241 final int paddingTop = mPaddingTop + offset;
1242 final int paddingBottom = mPaddingBottom + offset;
Patrick Dubroy0207c522010-11-03 22:12:02 -07001243
Winson Chung557d6ed2011-07-08 15:34:52 -07001244 final CellLayout leftPage = (CellLayout) getChildAt(mCurrentPage - 1);
1245 final CellLayout rightPage = (CellLayout) getChildAt(mCurrentPage + 1);
Patrick Dubroy0207c522010-11-03 22:12:02 -07001246
Winson Chung557d6ed2011-07-08 15:34:52 -07001247 if (leftPage != null && leftPage.getIsDragOverlapping()) {
1248 final Drawable d = getResources().getDrawable(R.drawable.page_hover_left_holo);
Winson Chungdea74b72011-09-13 18:06:43 -07001249 d.setBounds(mScrollX, paddingTop, mScrollX + d.getIntrinsicWidth(),
1250 height - paddingBottom);
Winson Chung557d6ed2011-07-08 15:34:52 -07001251 d.draw(canvas);
1252 } else if (rightPage != null && rightPage.getIsDragOverlapping()) {
1253 final Drawable d = getResources().getDrawable(R.drawable.page_hover_right_holo);
Winson Chungdea74b72011-09-13 18:06:43 -07001254 d.setBounds(mScrollX + width - d.getIntrinsicWidth(), paddingTop, mScrollX + width,
1255 height - paddingBottom);
Winson Chung557d6ed2011-07-08 15:34:52 -07001256 d.draw(canvas);
Patrick Dubroy0207c522010-11-03 22:12:02 -07001257 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001258 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001259 }
1260
1261 @Override
1262 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
Joe Onorato67886212009-09-14 19:05:05 -04001263 if (!mLauncher.isAllAppsVisible()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001264 final Folder openFolder = getOpenFolder();
1265 if (openFolder != null) {
1266 return openFolder.requestFocus(direction, previouslyFocusedRect);
1267 } else {
Michael Jurka0142d492010-08-25 17:46:15 -07001268 return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001269 }
1270 }
1271 return false;
1272 }
1273
1274 @Override
Winson Chung97d85d22011-04-13 11:27:36 -07001275 public int getDescendantFocusability() {
Michael Jurkad74c9842011-07-10 12:44:21 -07001276 if (isSmall()) {
Winson Chung97d85d22011-04-13 11:27:36 -07001277 return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
1278 }
1279 return super.getDescendantFocusability();
1280 }
1281
1282 @Override
Romain Guyc2e24c02009-06-01 16:11:41 -07001283 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
Joe Onorato67886212009-09-14 19:05:05 -04001284 if (!mLauncher.isAllAppsVisible()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001285 final Folder openFolder = getOpenFolder();
Michael Jurka0142d492010-08-25 17:46:15 -07001286 if (openFolder != null) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001287 openFolder.addFocusables(views, direction);
Michael Jurka0142d492010-08-25 17:46:15 -07001288 } else {
1289 super.addFocusables(views, direction, focusableMode);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001290 }
1291 }
1292 }
1293
Michael Jurkad74c9842011-07-10 12:44:21 -07001294 public boolean isSmall() {
Adam Cohen3384c7b2011-07-17 15:48:21 -07001295 return mState == State.SMALL || mState == State.SPRING_LOADED;
Michael Jurkad74c9842011-07-10 12:44:21 -07001296 }
1297
Michael Jurka0142d492010-08-25 17:46:15 -07001298 void enableChildrenCache(int fromPage, int toPage) {
1299 if (fromPage > toPage) {
1300 final int temp = fromPage;
1301 fromPage = toPage;
1302 toPage = temp;
Mike Cleron3a2b3f22009-11-05 17:17:42 -08001303 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001304
Michael Jurkadee05892010-07-27 10:01:56 -07001305 final int screenCount = getChildCount();
Adam Powellfea5d022010-04-29 11:42:45 -07001306
Michael Jurka0142d492010-08-25 17:46:15 -07001307 fromPage = Math.max(fromPage, 0);
1308 toPage = Math.min(toPage, screenCount - 1);
Adam Powellfea5d022010-04-29 11:42:45 -07001309
Michael Jurka0142d492010-08-25 17:46:15 -07001310 for (int i = fromPage; i <= toPage; i++) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001311 final CellLayout layout = (CellLayout) getChildAt(i);
1312 layout.setChildrenDrawnWithCacheEnabled(true);
1313 layout.setChildrenDrawingCacheEnabled(true);
1314 }
1315 }
1316
1317 void clearChildrenCache() {
Michael Jurkadee05892010-07-27 10:01:56 -07001318 final int screenCount = getChildCount();
1319 for (int i = 0; i < screenCount; i++) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001320 final CellLayout layout = (CellLayout) getChildAt(i);
1321 layout.setChildrenDrawnWithCacheEnabled(false);
Adam Cohen8182e5b2011-08-01 15:43:31 -07001322 // In software mode, we don't want the items to continue to be drawn into bitmaps
1323 if (!isHardwareAccelerated()) {
1324 layout.setChildrenDrawingCacheEnabled(false);
1325 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001326 }
1327 }
1328
Michael Jurkad74c9842011-07-10 12:44:21 -07001329 private void updateChildrenLayersEnabled() {
Adam Cohen7777d962011-08-18 18:58:38 -07001330 boolean small = isSmall() || mIsSwitchingState;
Michael Jurkad74c9842011-07-10 12:44:21 -07001331 boolean dragging = mAnimatingViewIntoPlace || mIsDragOccuring;
1332 boolean enableChildrenLayers = small || dragging || isPageMoving();
1333
1334 if (enableChildrenLayers != mChildrenLayersEnabled) {
1335 mChildrenLayersEnabled = enableChildrenLayers;
1336 for (int i = 0; i < getPageCount(); i++) {
1337 ((ViewGroup)getChildAt(i)).setChildrenLayersEnabled(enableChildrenLayers);
1338 }
Michael Jurkace7e05f2011-02-01 22:02:35 -08001339 }
Michael Jurkace7e05f2011-02-01 22:02:35 -08001340 }
1341
Jeff Brown1d0867c2010-12-02 18:27:39 -08001342 protected void onWallpaperTap(MotionEvent ev) {
1343 final int[] position = mTempCell;
1344 getLocationOnScreen(position);
1345
1346 int pointerIndex = ev.getActionIndex();
1347 position[0] += (int) ev.getX(pointerIndex);
1348 position[1] += (int) ev.getY(pointerIndex);
1349
1350 mWallpaperManager.sendWallpaperCommand(getWindowToken(),
1351 ev.getAction() == MotionEvent.ACTION_UP
1352 ? WallpaperManager.COMMAND_TAP : WallpaperManager.COMMAND_SECONDARY_TAP,
1353 position[0], position[1], 0, null);
1354 }
1355
Michael Jurkac1a036b2011-07-25 17:04:23 -07001356 @Override
1357 protected void updateAdjacentPagesAlpha() {
1358 if (!isSmall()) {
1359 super.updateAdjacentPagesAlpha();
1360 }
1361 }
1362
Adam Cohen61033d32010-11-15 18:29:44 -08001363 /*
1364 * This interpolator emulates the rate at which the perceived scale of an object changes
1365 * as its distance from a camera increases. When this interpolator is applied to a scale
1366 * animation on a view, it evokes the sense that the object is shrinking due to moving away
1367 * from the camera.
1368 */
1369 static class ZInterpolator implements TimeInterpolator {
1370 private float focalLength;
1371
1372 public ZInterpolator(float foc) {
1373 focalLength = foc;
1374 }
1375
Adam Coheneed565d2010-11-15 11:30:05 -08001376 public float getInterpolation(float input) {
1377 return (1.0f - focalLength / (focalLength + input)) /
Adam Cohen61033d32010-11-15 18:29:44 -08001378 (1.0f - focalLength / (focalLength + 1.0f));
Adam Cohencbbaf982010-11-12 14:50:33 -08001379 }
1380 }
1381
Adam Cohen61033d32010-11-15 18:29:44 -08001382 /*
1383 * The exact reverse of ZInterpolator.
1384 */
1385 static class InverseZInterpolator implements TimeInterpolator {
1386 private ZInterpolator zInterpolator;
1387 public InverseZInterpolator(float foc) {
1388 zInterpolator = new ZInterpolator(foc);
1389 }
Adam Cohencbbaf982010-11-12 14:50:33 -08001390 public float getInterpolation(float input) {
Adam Cohen61033d32010-11-15 18:29:44 -08001391 return 1 - zInterpolator.getInterpolation(1 - input);
Adam Cohencbbaf982010-11-12 14:50:33 -08001392 }
1393 }
1394
Adam Cohen61033d32010-11-15 18:29:44 -08001395 /*
1396 * ZInterpolator compounded with an ease-out.
1397 */
1398 static class ZoomOutInterpolator implements TimeInterpolator {
1399 private final ZInterpolator zInterpolator = new ZInterpolator(0.2f);
Michael Jurkaea573482011-02-03 19:48:18 -08001400 private final DecelerateInterpolator decelerate = new DecelerateInterpolator(1.8f);
Adam Cohen61033d32010-11-15 18:29:44 -08001401
1402 public float getInterpolation(float input) {
1403 return decelerate.getInterpolation(zInterpolator.getInterpolation(input));
1404 }
1405 }
1406
1407 /*
1408 * InvereZInterpolator compounded with an ease-out.
1409 */
1410 static class ZoomInInterpolator implements TimeInterpolator {
1411 private final InverseZInterpolator inverseZInterpolator = new InverseZInterpolator(0.35f);
1412 private final DecelerateInterpolator decelerate = new DecelerateInterpolator(3.0f);
1413
1414 public float getInterpolation(float input) {
1415 return decelerate.getInterpolation(inverseZInterpolator.getInterpolation(input));
1416 }
1417 }
1418
Adam Cohen61033d32010-11-15 18:29:44 -08001419 private final ZoomInInterpolator mZoomInInterpolator = new ZoomInInterpolator();
Michael Jurka3e7c7632010-10-02 16:01:03 -07001420
Michael Jurka3e7c7632010-10-02 16:01:03 -07001421 /*
Adam Cohen66396872011-04-15 17:50:36 -07001422 *
1423 * We call these methods (onDragStartedWithItemSpans/onDragStartedWithSize) whenever we
1424 * start a drag in Launcher, regardless of whether the drag has ever entered the Workspace
1425 *
1426 * These methods mark the appropriate pages as accepting drops (which alters their visual
1427 * appearance).
1428 *
1429 */
1430 public void onDragStartedWithItem(View v) {
Adam Cohen66396872011-04-15 17:50:36 -07001431 final Canvas canvas = new Canvas();
1432
1433 // We need to add extra padding to the bitmap to make room for the glow effect
1434 final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;
1435
1436 // The outline is used to visualize where the item will land if dropped
1437 mDragOutline = createDragOutline(v, canvas, bitmapPadding);
1438 }
1439
Michael Jurkad3ef3062010-11-23 16:23:58 -08001440 public void onDragStartedWithItemSpans(int spanX, int spanY, Bitmap b) {
Michael Jurkad3ef3062010-11-23 16:23:58 -08001441 final Canvas canvas = new Canvas();
1442
1443 // We need to add extra padding to the bitmap to make room for the glow effect
Michael Jurka38b4f7c2010-12-14 16:46:39 -08001444 final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;
Michael Jurkad3ef3062010-11-23 16:23:58 -08001445
Michael Jurkaf12c75c2011-01-25 22:41:40 -08001446 CellLayout cl = (CellLayout) getChildAt(0);
Adam Cohen66396872011-04-15 17:50:36 -07001447
1448 int[] size = cl.cellSpansToSize(spanX, spanY);
1449
Michael Jurkad3ef3062010-11-23 16:23:58 -08001450 // The outline is used to visualize where the item will land if dropped
Adam Cohen66396872011-04-15 17:50:36 -07001451 mDragOutline = createDragOutline(b, canvas, bitmapPadding, size[0], size[1]);
Michael Jurka3e7c7632010-10-02 16:01:03 -07001452 }
1453
1454 // we call this method whenever a drag and drop in Launcher finishes, even if Workspace was
1455 // never dragged over
Patrick Dubroy7bccb422011-01-20 14:50:55 -08001456 public void onDragStopped(boolean success) {
1457 // In the success case, DragController has already called onDragExit()
1458 if (!success) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07001459 doDragExit(null);
Patrick Dubroy7bccb422011-01-20 14:50:55 -08001460 }
Michael Jurka3e7c7632010-10-02 16:01:03 -07001461 }
1462
Patrick Dubroy758a9232011-03-03 19:54:56 -08001463 public void exitWidgetResizeMode() {
Adam Cohen716b51e2011-06-30 12:09:54 -07001464 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohen67882692011-03-11 15:29:03 -08001465 dragLayer.clearAllResizeFrames();
Patrick Dubroy758a9232011-03-03 19:54:56 -08001466 }
1467
Adam Cohen4b285c52011-07-21 14:24:06 -07001468 private void initAnimationArrays() {
1469 final int childCount = getChildCount();
1470 if (mOldTranslationXs != null) return;
1471 mOldTranslationXs = new float[childCount];
1472 mOldTranslationYs = new float[childCount];
1473 mOldScaleXs = new float[childCount];
1474 mOldScaleYs = new float[childCount];
1475 mOldBackgroundAlphas = new float[childCount];
1476 mOldBackgroundAlphaMultipliers = new float[childCount];
1477 mOldAlphas = new float[childCount];
1478 mOldRotationYs = new float[childCount];
1479 mNewTranslationXs = new float[childCount];
1480 mNewTranslationYs = new float[childCount];
1481 mNewScaleXs = new float[childCount];
1482 mNewScaleYs = new float[childCount];
1483 mNewBackgroundAlphas = new float[childCount];
1484 mNewBackgroundAlphaMultipliers = new float[childCount];
1485 mNewAlphas = new float[childCount];
1486 mNewRotationYs = new float[childCount];
1487 }
1488
Adam Cohen7777d962011-08-18 18:58:38 -07001489 public void changeState(State shrinkState) {
1490 changeState(shrinkState, true);
1491 }
1492
Adam Cohen7777d962011-08-18 18:58:38 -07001493 void changeState(final State state, boolean animated) {
Adam Cohencff6af82011-09-13 14:51:53 -07001494 changeState(state, animated, 0);
1495 }
1496
1497 void changeState(final State state, boolean animated, int delay) {
Michael Jurka99633da2011-07-27 22:40:17 -07001498 if (mFirstLayout) {
1499 // (mFirstLayout == "first layout has not happened yet")
1500 // cancel any pending shrinks that were set earlier
1501 mSwitchStateAfterFirstLayout = false;
Adam Cohen7777d962011-08-18 18:58:38 -07001502 mStateAfterFirstLayout = state;
Michael Jurka99633da2011-07-27 22:40:17 -07001503 return;
1504 }
1505
Adam Cohen7777d962011-08-18 18:58:38 -07001506 if (mAnimator != null) {
1507 mAnimator.cancel();
1508 }
Adam Lesinski6b879f02010-11-04 16:15:23 -07001509
Adam Cohen7777d962011-08-18 18:58:38 -07001510 // Stop any scrolling, move to the current page right away
1511 setCurrentPage((mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage);
Winson Chungde1af762011-07-21 16:44:07 -07001512
Adam Cohen7777d962011-08-18 18:58:38 -07001513 float finalScaleFactor = 1.0f;
Adam Cohencff6af82011-09-13 14:51:53 -07001514 float finalBackgroundAlpha = (state == State.SPRING_LOADED) ? 1.0f : 0f;
Adam Cohen7777d962011-08-18 18:58:38 -07001515 boolean normalState = false;
1516 State oldState = mState;
1517 mState = state;
1518 boolean zoomIn = true;
Adam Cohenbeff8c62011-08-31 17:46:01 -07001519
Adam Cohen7777d962011-08-18 18:58:38 -07001520 if (state != State.NORMAL) {
1521 finalScaleFactor = mSpringLoadedShrinkFactor - (state == State.SMALL ? 0.1f : 0);
Adam Cohen7777d962011-08-18 18:58:38 -07001522 if (oldState == State.NORMAL && state == State.SMALL) {
1523 zoomIn = false;
Michael Jurka3e5ad582011-02-03 22:46:34 -08001524 if (animated) {
Adam Cohen7777d962011-08-18 18:58:38 -07001525 hideScrollingIndicator(true);
Michael Jurka4cb37242010-08-09 21:05:32 -07001526 }
Adam Cohen7777d962011-08-18 18:58:38 -07001527 setLayoutScale(finalScaleFactor);
1528 updateChildrenLayersEnabled();
Michael Jurkaca5b8362011-01-27 13:23:26 -08001529 } else {
Adam Cohencff6af82011-09-13 14:51:53 -07001530 finalBackgroundAlpha = 1.0f;
Adam Cohen7777d962011-08-18 18:58:38 -07001531 setLayoutScale(finalScaleFactor);
1532 }
1533 } else {
1534 setLayoutScale(1.0f);
1535 normalState = true;
1536 }
1537
1538 float translationX = 0;
1539 float translationY = 0;
1540
1541 mAnimator = new AnimatorSet();
1542
1543 final int screenCount = getChildCount();
1544 initAnimationArrays();
1545
1546 final int duration = zoomIn ?
1547 getResources().getInteger(R.integer.config_workspaceUnshrinkTime) :
1548 getResources().getInteger(R.integer.config_appsCustomizeWorkspaceShrinkTime);
1549 for (int i = 0; i < screenCount; i++) {
1550 final CellLayout cl = (CellLayout)getChildAt(i);
1551 float finalAlphaValue = 0f;
1552 float rotation = 0f;
1553
1554 // Set the final alpha depending on whether we are fading side pages. On phone ui,
1555 // we don't do any of the rotation, or the fading alpha in portrait. See the
1556 // ctor and screenScrolled().
1557 if (mFadeInAdjacentScreens && normalState) {
1558 finalAlphaValue = (i == mCurrentPage) ? 1f : 0f;
1559 } else {
1560 finalAlphaValue = 1f;
1561 }
1562
1563 if (LauncherApplication.isScreenLarge()) {
1564 if (i < mCurrentPage) {
1565 rotation = WORKSPACE_ROTATION;
1566 } else if (i > mCurrentPage) {
1567 rotation = -WORKSPACE_ROTATION;
Winson Chungb46a2d12011-04-22 14:13:05 -07001568 }
Michael Jurkac86756c2010-10-10 15:15:47 -07001569 }
Adam Cohen7777d962011-08-18 18:58:38 -07001570
1571 float finalAlphaMultiplierValue = 1f;
1572 // If the screen is not xlarge, then don't rotate the CellLayouts
1573 // NOTE: If we don't update the side pages alpha, then we should not hide the side
1574 // pages. see unshrink().
1575 if (LauncherApplication.isScreenLarge()) {
1576 translationX = getOffsetXForRotation(rotation, cl.getWidth(), cl.getHeight());
1577 }
1578
1579 mOldAlphas[i] = cl.getAlpha();
1580 mNewAlphas[i] = finalAlphaValue;
1581 if (animated) {
1582 mOldTranslationXs[i] = cl.getTranslationX();
1583 mOldTranslationYs[i] = cl.getTranslationY();
1584 mOldScaleXs[i] = cl.getScaleX();
1585 mOldScaleYs[i] = cl.getScaleY();
1586 mOldBackgroundAlphas[i] = cl.getBackgroundAlpha();
1587 mOldBackgroundAlphaMultipliers[i] = cl.getBackgroundAlphaMultiplier();
1588 mOldRotationYs[i] = cl.getRotationY();
1589
1590 mNewTranslationXs[i] = translationX;
1591 mNewTranslationYs[i] = translationY;
1592 mNewScaleXs[i] = finalScaleFactor;
1593 mNewScaleYs[i] = finalScaleFactor;
1594 mNewBackgroundAlphas[i] = finalBackgroundAlpha;
1595 mNewBackgroundAlphaMultipliers[i] = finalAlphaMultiplierValue;
1596 mNewRotationYs[i] = rotation;
1597 } else {
1598 cl.setTranslationX(translationX);
1599 cl.setTranslationY(translationY);
1600 cl.setScaleX(finalScaleFactor);
1601 cl.setScaleY(finalScaleFactor);
1602 cl.setBackgroundAlpha(0.0f);
1603 cl.setBackgroundAlphaMultiplier(finalAlphaMultiplierValue);
1604 cl.setAlpha(finalAlphaValue);
1605 cl.setRotationY(rotation);
1606 mChangeStateAnimationListener.onAnimationEnd(null);
1607 }
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) {
1611 ValueAnimator animWithInterpolator =
1612 ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
1613
1614 if (zoomIn) {
1615 animWithInterpolator.setInterpolator(mZoomInInterpolator);
1616 }
1617
1618 animWithInterpolator.addUpdateListener(new LauncherAnimatorUpdateListener() {
1619 public void onAnimationUpdate(float a, float b) {
1620 mTransitionProgress = b;
1621 if (b == 0f) {
1622 // an optimization, but not required
1623 return;
1624 }
1625 invalidate();
1626 for (int i = 0; i < screenCount; i++) {
1627 final CellLayout cl = (CellLayout) getChildAt(i);
1628 cl.fastInvalidate();
1629 cl.setFastTranslationX(a * mOldTranslationXs[i] + b * mNewTranslationXs[i]);
1630 cl.setFastTranslationY(a * mOldTranslationYs[i] + b * mNewTranslationYs[i]);
1631 cl.setFastScaleX(a * mOldScaleXs[i] + b * mNewScaleXs[i]);
1632 cl.setFastScaleY(a * mOldScaleYs[i] + b * mNewScaleYs[i]);
1633 cl.setFastBackgroundAlpha(
1634 a * mOldBackgroundAlphas[i] + b * mNewBackgroundAlphas[i]);
1635 cl.setBackgroundAlphaMultiplier(a * mOldBackgroundAlphaMultipliers[i] +
1636 b * mNewBackgroundAlphaMultipliers[i]);
1637 cl.setFastAlpha(a * mOldAlphas[i] + b * mNewAlphas[i]);
1638 }
1639 }
1640 });
1641
1642 ValueAnimator rotationAnim =
1643 ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
1644 rotationAnim.setInterpolator(new DecelerateInterpolator(2.0f));
1645 rotationAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
1646 public void onAnimationUpdate(float a, float b) {
1647 if (b == 0f) {
1648 // an optimization, but not required
1649 return;
1650 }
1651 for (int i = 0; i < screenCount; i++) {
1652 final CellLayout cl = (CellLayout) getChildAt(i);
1653 cl.setFastRotationY(a * mOldRotationYs[i] + b * mNewRotationYs[i]);
1654 }
1655 }
1656 });
1657
1658 mAnimator.playTogether(animWithInterpolator, rotationAnim);
Adam Cohencff6af82011-09-13 14:51:53 -07001659 mAnimator.setStartDelay(delay);
Adam Cohen7777d962011-08-18 18:58:38 -07001660 // If we call this when we're not animated, onAnimationEnd is never called on
1661 // the listener; make sure we only use the listener when we're actually animating
1662 mAnimator.addListener(mChangeStateAnimationListener);
1663 mAnimator.start();
1664 }
1665
1666 if (state == State.SPRING_LOADED) {
Michael Jurka7bdb25a2011-08-03 15:16:44 -07001667 // Right now we're covered by Apps Customize
1668 // Show the background gradient immediately, so the gradient will
1669 // be showing once AppsCustomize disappears
1670 animateBackgroundGradient(getResources().getInteger(
1671 R.integer.config_appsCustomizeSpringLoadedBgAlpha) / 100f, false);
1672 } else {
1673 // Fade the background gradient away
1674 animateBackgroundGradient(0f, true);
1675 }
Michael Jurkadee05892010-07-27 10:01:56 -07001676 }
1677
Joe Onorato4be866d2010-10-10 11:26:02 -07001678 /**
1679 * Draw the View v into the given Canvas.
1680 *
1681 * @param v the view to draw
1682 * @param destCanvas the canvas to draw on
1683 * @param padding the horizontal and vertical padding to use when drawing
1684 */
Adam Cohenac8c8762011-07-13 11:15:27 -07001685 private void drawDragView(View v, Canvas destCanvas, int padding, boolean pruneToDrawable) {
Joe Onorato4be866d2010-10-10 11:26:02 -07001686 final Rect clipRect = mTempRect;
1687 v.getDrawingRect(clipRect);
1688
Adam Cohen099f60d2011-08-23 21:07:26 -07001689 boolean textVisible = false;
1690
Adam Cohenac8c8762011-07-13 11:15:27 -07001691 destCanvas.save();
1692 if (v instanceof TextView && pruneToDrawable) {
1693 Drawable d = ((TextView) v).getCompoundDrawables()[1];
1694 clipRect.set(0, 0, d.getIntrinsicWidth() + padding, d.getIntrinsicHeight() + padding);
1695 destCanvas.translate(padding / 2, padding / 2);
1696 d.draw(destCanvas);
1697 } else {
1698 if (v instanceof FolderIcon) {
Adam Cohen099f60d2011-08-23 21:07:26 -07001699 // For FolderIcons the text can bleed into the icon area, and so we need to
1700 // hide the text completely (which can't be achieved by clipping).
1701 if (((FolderIcon) v).getTextVisible()) {
1702 ((FolderIcon) v).setTextVisible(false);
1703 textVisible = true;
1704 }
Adam Cohenac8c8762011-07-13 11:15:27 -07001705 } else if (v instanceof BubbleTextView) {
1706 final BubbleTextView tv = (BubbleTextView) v;
1707 clipRect.bottom = tv.getExtendedPaddingTop() - (int) BubbleTextView.PADDING_V +
1708 tv.getLayout().getLineTop(0);
1709 } else if (v instanceof TextView) {
1710 final TextView tv = (TextView) v;
1711 clipRect.bottom = tv.getExtendedPaddingTop() - tv.getCompoundDrawablePadding() +
1712 tv.getLayout().getLineTop(0);
1713 }
1714 destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2);
1715 destCanvas.clipRect(clipRect, Op.REPLACE);
1716 v.draw(destCanvas);
Adam Cohen099f60d2011-08-23 21:07:26 -07001717
1718 // Restore text visibility of FolderIcon if necessary
1719 if (textVisible) {
1720 ((FolderIcon) v).setTextVisible(true);
1721 }
Adam Cohenac8c8762011-07-13 11:15:27 -07001722 }
1723 destCanvas.restore();
1724 }
1725
1726 /**
1727 * Returns a new bitmap to show when the given View is being dragged around.
1728 * Responsibility for the bitmap is transferred to the caller.
1729 */
1730 public Bitmap createDragBitmap(View v, Canvas canvas, int padding) {
Winson Chungea359c62011-08-03 17:06:35 -07001731 final int outlineColor = getResources().getColor(android.R.color.holo_blue_light);
Adam Cohenac8c8762011-07-13 11:15:27 -07001732 Bitmap b;
1733
1734 if (v instanceof TextView) {
1735 Drawable d = ((TextView) v).getCompoundDrawables()[1];
1736 b = Bitmap.createBitmap(d.getIntrinsicWidth() + padding,
1737 d.getIntrinsicHeight() + padding, Bitmap.Config.ARGB_8888);
1738 } else {
1739 b = Bitmap.createBitmap(
1740 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
Joe Onorato4be866d2010-10-10 11:26:02 -07001741 }
1742
Adam Cohenac8c8762011-07-13 11:15:27 -07001743 canvas.setBitmap(b);
1744 drawDragView(v, canvas, padding, true);
1745 mOutlineHelper.applyOuterBlur(b, canvas, outlineColor);
Winson Chung70fc4382011-08-08 15:31:33 -07001746 canvas.drawColor(mDragViewMultiplyColor, PorterDuff.Mode.MULTIPLY);
Adam Cohenaaf473c2011-08-03 12:02:47 -07001747 canvas.setBitmap(null);
Joe Onorato4be866d2010-10-10 11:26:02 -07001748
Adam Cohenac8c8762011-07-13 11:15:27 -07001749 return b;
Joe Onorato4be866d2010-10-10 11:26:02 -07001750 }
1751
1752 /**
1753 * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
1754 * Responsibility for the bitmap is transferred to the caller.
1755 */
1756 private Bitmap createDragOutline(View v, Canvas canvas, int padding) {
Winson Chungea359c62011-08-03 17:06:35 -07001757 final int outlineColor = getResources().getColor(android.R.color.holo_blue_light);
Joe Onorato4be866d2010-10-10 11:26:02 -07001758 final Bitmap b = Bitmap.createBitmap(
1759 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
1760
1761 canvas.setBitmap(b);
Adam Cohenac8c8762011-07-13 11:15:27 -07001762 drawDragView(v, canvas, padding, false);
Adam Cohen5bb50bd2010-12-03 11:39:55 -08001763 mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor);
Adam Cohenaaf473c2011-08-03 12:02:47 -07001764 canvas.setBitmap(null);
Joe Onorato4be866d2010-10-10 11:26:02 -07001765 return b;
1766 }
1767
1768 /**
Michael Jurkad3ef3062010-11-23 16:23:58 -08001769 * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
1770 * Responsibility for the bitmap is transferred to the caller.
1771 */
Michael Jurkaf12c75c2011-01-25 22:41:40 -08001772 private Bitmap createDragOutline(Bitmap orig, Canvas canvas, int padding, int w, int h) {
Winson Chungea359c62011-08-03 17:06:35 -07001773 final int outlineColor = getResources().getColor(android.R.color.holo_blue_light);
Michael Jurkaf12c75c2011-01-25 22:41:40 -08001774 final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Michael Jurkad3ef3062010-11-23 16:23:58 -08001775 canvas.setBitmap(b);
Michael Jurkaf12c75c2011-01-25 22:41:40 -08001776
1777 Rect src = new Rect(0, 0, orig.getWidth(), orig.getHeight());
1778 float scaleFactor = Math.min((w - padding) / (float) orig.getWidth(),
1779 (h - padding) / (float) orig.getHeight());
1780 int scaledWidth = (int) (scaleFactor * orig.getWidth());
1781 int scaledHeight = (int) (scaleFactor * orig.getHeight());
1782 Rect dst = new Rect(0, 0, scaledWidth, scaledHeight);
1783
1784 // center the image
1785 dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2);
1786
1787 Paint p = new Paint();
1788 p.setFilterBitmap(true);
1789 canvas.drawBitmap(orig, src, dst, p);
Michael Jurkad3ef3062010-11-23 16:23:58 -08001790 mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor);
Adam Cohenaaf473c2011-08-03 12:02:47 -07001791 canvas.setBitmap(null);
Michael Jurkad3ef3062010-11-23 16:23:58 -08001792
1793 return b;
1794 }
1795
1796 /**
Winson Chunga9abd0e2010-10-27 17:18:37 -07001797 * Creates a drag outline to represent a drop (that we don't have the actual information for
1798 * yet). May be changed in the future to alter the drop outline slightly depending on the
1799 * clip description mime data.
1800 */
1801 private Bitmap createExternalDragOutline(Canvas canvas, int padding) {
1802 Resources r = getResources();
Winson Chungea359c62011-08-03 17:06:35 -07001803 final int outlineColor = r.getColor(android.R.color.holo_blue_light);
Winson Chunga9abd0e2010-10-27 17:18:37 -07001804 final int iconWidth = r.getDimensionPixelSize(R.dimen.workspace_cell_width);
1805 final int iconHeight = r.getDimensionPixelSize(R.dimen.workspace_cell_height);
1806 final int rectRadius = r.getDimensionPixelSize(R.dimen.external_drop_icon_rect_radius);
1807 final int inset = (int) (Math.min(iconWidth, iconHeight) * 0.2f);
1808 final Bitmap b = Bitmap.createBitmap(
1809 iconWidth + padding, iconHeight + padding, Bitmap.Config.ARGB_8888);
1810
1811 canvas.setBitmap(b);
1812 canvas.drawRoundRect(new RectF(inset, inset, iconWidth - inset, iconHeight - inset),
1813 rectRadius, rectRadius, mExternalDragOutlinePaint);
Adam Cohen5bb50bd2010-12-03 11:39:55 -08001814 mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor);
Adam Cohenaaf473c2011-08-03 12:02:47 -07001815 canvas.setBitmap(null);
Winson Chunga9abd0e2010-10-27 17:18:37 -07001816 return b;
1817 }
1818
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001819 void startDrag(CellLayout.CellInfo cellInfo) {
1820 View child = cellInfo.cell;
Winson Chungaafa03c2010-06-11 17:34:16 -07001821
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001822 // Make sure the drag was started by a long press as opposed to a long click.
Bjorn Bringert7984c942009-12-09 15:38:25 +00001823 if (!child.isInTouchMode()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001824 return;
1825 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001826
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001827 mDragInfo = cellInfo;
Adam Cohenfc53cd22011-07-20 15:45:11 -07001828 child.setVisibility(GONE);
Joe Onorato4be866d2010-10-10 11:26:02 -07001829
1830 child.clearFocus();
1831 child.setPressed(false);
1832
1833 final Canvas canvas = new Canvas();
1834
Patrick Dubroy8e58e912010-10-14 13:21:48 -07001835 // We need to add extra padding to the bitmap to make room for the glow effect
Michael Jurka38b4f7c2010-12-14 16:46:39 -08001836 final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;
Patrick Dubroy8e58e912010-10-14 13:21:48 -07001837
Joe Onorato4be866d2010-10-10 11:26:02 -07001838 // The outline is used to visualize where the item will land if dropped
Patrick Dubroy8e58e912010-10-14 13:21:48 -07001839 mDragOutline = createDragOutline(child, canvas, bitmapPadding);
Adam Cohenac8c8762011-07-13 11:15:27 -07001840 beginDragShared(child, this);
1841 }
1842
1843 public void beginDragShared(View child, DragSource source) {
1844 // We need to add extra padding to the bitmap to make room for the glow effect
1845 final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;
Joe Onorato4be866d2010-10-10 11:26:02 -07001846
1847 // The drag bitmap follows the touch point around on the screen
Adam Cohenac8c8762011-07-13 11:15:27 -07001848 final Bitmap b = createDragBitmap(child, new Canvas(), bitmapPadding);
Joe Onorato4be866d2010-10-10 11:26:02 -07001849
1850 final int bmpWidth = b.getWidth();
Adam Cohene3e27a82011-04-15 12:07:39 -07001851
Adam Cohen8dfcba42011-07-07 16:38:18 -07001852 mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY);
1853 final int dragLayerX = (int) mTempXY[0] + (child.getWidth() - bmpWidth) / 2;
Adam Cohenfc53cd22011-07-20 15:45:11 -07001854 int dragLayerY = mTempXY[1] - bitmapPadding / 2;
Adam Cohene3e27a82011-04-15 12:07:39 -07001855
1856 Rect dragRect = null;
Adam Cohen0e4857c2011-06-30 17:05:14 -07001857 if (child instanceof BubbleTextView) {
Adam Cohene3e27a82011-04-15 12:07:39 -07001858 int iconSize = getResources().getDimensionPixelSize(R.dimen.app_icon_size);
1859 int top = child.getPaddingTop();
1860 int left = (bmpWidth - iconSize) / 2;
1861 int right = left + iconSize;
1862 int bottom = top + iconSize;
Adam Cohenfc53cd22011-07-20 15:45:11 -07001863 dragLayerY += top;
Adam Cohene3e27a82011-04-15 12:07:39 -07001864 dragRect = new Rect(left, top, right, bottom);
Adam Cohen0e4857c2011-06-30 17:05:14 -07001865 } else if (child instanceof FolderIcon) {
1866 int previewSize = getResources().getDimensionPixelSize(R.dimen.folder_preview_size);
1867 dragRect = new Rect(0, 0, child.getWidth(), previewSize);
Adam Cohene3e27a82011-04-15 12:07:39 -07001868 }
1869
Adam Cohenac8c8762011-07-13 11:15:27 -07001870 mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
Adam Cohene3e27a82011-04-15 12:07:39 -07001871 DragController.DRAG_ACTION_MOVE, dragRect);
Joe Onorato4be866d2010-10-10 11:26:02 -07001872 b.recycle();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001873 }
1874
Winson Chung3d503fb2011-07-13 17:25:49 -07001875 void addApplicationShortcut(ShortcutInfo info, CellLayout target, long container, int screen,
1876 int cellX, int cellY, boolean insertAtFirst, int intersectX, int intersectY) {
1877 View view = mLauncher.createShortcut(R.layout.application, target, (ShortcutInfo) info);
Michael Jurka0280c3b2010-09-17 15:00:07 -07001878
1879 final int[] cellXY = new int[2];
Winson Chung3d503fb2011-07-13 17:25:49 -07001880 target.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY);
1881 addInScreen(view, container, screen, cellXY[0], cellXY[1], 1, 1, insertAtFirst);
1882 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screen, cellXY[0],
1883 cellXY[1]);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001884 }
1885
Adam Cohen4b285c52011-07-21 14:24:06 -07001886 public boolean transitionStateShouldAllowDrop() {
1887 return (!isSwitchingState() || mTransitionProgress > 0.5f);
1888 }
1889
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001890 /**
1891 * {@inheritDoc}
1892 */
Adam Cohencb3382b2011-05-24 14:07:08 -07001893 public boolean acceptDrop(DragObject d) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001894 // If it's an external drop (e.g. from All Apps), check if it should be accepted
Adam Cohencb3382b2011-05-24 14:07:08 -07001895 if (d.dragSource != this) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001896 // Don't accept the drop if we're not over a screen at time of drop
Adam Cohena65beee2011-06-27 21:32:23 -07001897 if (mDragTargetLayout == null) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001898 return false;
1899 }
Adam Cohen4b285c52011-07-21 14:24:06 -07001900 if (!transitionStateShouldAllowDrop()) return false;
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001901
Adam Cohena65beee2011-06-27 21:32:23 -07001902 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset,
1903 d.dragView, mDragViewVisualCenter);
1904
Adam Cohen2e9f4fb2011-08-30 22:46:55 -07001905 // We want the point to be mapped to the dragTarget.
1906 if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
1907 mapPointFromSelfToSibling(mLauncher.getHotseat(), mDragViewVisualCenter);
1908 } else {
1909 mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
1910 }
1911
Winson Chung557d6ed2011-07-08 15:34:52 -07001912 int spanX = 1;
1913 int spanY = 1;
1914 View ignoreView = null;
1915 if (mDragInfo != null) {
1916 final CellLayout.CellInfo dragCellInfo = mDragInfo;
1917 spanX = dragCellInfo.spanX;
1918 spanY = dragCellInfo.spanY;
1919 ignoreView = dragCellInfo.cell;
1920 } else {
1921 final ItemInfo dragInfo = (ItemInfo) d.dragInfo;
1922 spanX = dragInfo.spanX;
1923 spanY = dragInfo.spanY;
1924 }
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001925
Adam Cohena65beee2011-06-27 21:32:23 -07001926 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
1927 (int) mDragViewVisualCenter[1], spanX, spanY, mDragTargetLayout, mTargetCell);
Adam Cohena65beee2011-06-27 21:32:23 -07001928 if (willCreateUserFolder((ItemInfo) d.dragInfo, mDragTargetLayout, mTargetCell, true)) {
1929 return true;
1930 }
1931 if (willAddToExistingUserFolder((ItemInfo) d.dragInfo, mDragTargetLayout,
1932 mTargetCell)) {
1933 return true;
1934 }
1935
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001936
1937 // Don't accept the drop if there's no room for the item
1938 if (!mDragTargetLayout.findCellForSpanIgnoring(null, spanX, spanY, ignoreView)) {
1939 mLauncher.showOutOfSpaceMessage();
1940 return false;
1941 }
1942 }
1943 return true;
1944 }
1945
Adam Cohena65beee2011-06-27 21:32:23 -07001946 boolean willCreateUserFolder(ItemInfo info, CellLayout target, int[] targetCell,
1947 boolean considerTimeout) {
1948 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
1949
Winson Chung3d503fb2011-07-13 17:25:49 -07001950 boolean hasntMoved = false;
1951 if (mDragInfo != null) {
1952 CellLayout cellParent = getParentCellLayoutForView(mDragInfo.cell);
1953 hasntMoved = (mDragInfo.cellX == targetCell[0] &&
1954 mDragInfo.cellY == targetCell[1]) && (cellParent == target);
1955 }
Adam Cohene0310962011-04-18 16:15:31 -07001956
Adam Cohena65beee2011-06-27 21:32:23 -07001957 if (dropOverView == null || hasntMoved || (considerTimeout && !mCreateUserFolderOnDrop)) {
1958 return false;
1959 }
Adam Cohene0310962011-04-18 16:15:31 -07001960
Adam Cohena65beee2011-06-27 21:32:23 -07001961 boolean aboveShortcut = (dropOverView.getTag() instanceof ShortcutInfo);
Adam Cohene0310962011-04-18 16:15:31 -07001962 boolean willBecomeShortcut =
Adam Cohenc0dcf592011-06-01 15:30:43 -07001963 (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
1964 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT);
Adam Cohene0310962011-04-18 16:15:31 -07001965
1966 return (aboveShortcut && willBecomeShortcut);
1967 }
1968
Adam Cohena65beee2011-06-27 21:32:23 -07001969 boolean willAddToExistingUserFolder(Object dragInfo, CellLayout target, int[] targetCell) {
1970 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
1971 if (dropOverView instanceof FolderIcon) {
1972 FolderIcon fi = (FolderIcon) dropOverView;
1973 if (fi.acceptDrop(dragInfo)) {
1974 return true;
1975 }
1976 }
1977 return false;
1978 }
1979
Winson Chung3d503fb2011-07-13 17:25:49 -07001980 boolean createUserFolderIfNecessary(View newView, long container, CellLayout target,
Winson Chung557d6ed2011-07-08 15:34:52 -07001981 int[] targetCell, boolean external, DragView dragView, Runnable postAnimationRunnable) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07001982 View v = target.getChildAt(targetCell[0], targetCell[1]);
Winson Chungec9a0a42011-07-20 20:42:13 -07001983 boolean hasntMoved = false;
1984 if (mDragInfo != null) {
1985 CellLayout cellParent = getParentCellLayoutForView(mDragInfo.cell);
1986 hasntMoved = (mDragInfo.cellX == targetCell[0] &&
1987 mDragInfo.cellY == targetCell[1]) && (cellParent == target);
1988 }
Adam Cohen10b17372011-04-15 14:21:25 -07001989
Adam Cohen19072da2011-05-31 14:30:45 -07001990 if (v == null || hasntMoved || !mCreateUserFolderOnDrop) return false;
1991 mCreateUserFolderOnDrop = false;
Adam Cohenc0dcf592011-06-01 15:30:43 -07001992 final int screen = (targetCell == null) ? mDragInfo.screen : indexOfChild(target);
Adam Cohen10b17372011-04-15 14:21:25 -07001993
1994 boolean aboveShortcut = (v.getTag() instanceof ShortcutInfo);
1995 boolean willBecomeShortcut = (newView.getTag() instanceof ShortcutInfo);
1996
1997 if (aboveShortcut && willBecomeShortcut) {
1998 ShortcutInfo sourceInfo = (ShortcutInfo) newView.getTag();
1999 ShortcutInfo destInfo = (ShortcutInfo) v.getTag();
2000 // if the drag started here, we need to remove it from the workspace
2001 if (!external) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002002 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
Adam Cohen10b17372011-04-15 14:21:25 -07002003 }
2004
Adam Cohend0445262011-07-04 23:53:22 -07002005 Rect folderLocation = new Rect();
Adam Cohenac8c8762011-07-13 11:15:27 -07002006 float scale = mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation);
Adam Cohen10b17372011-04-15 14:21:25 -07002007 target.removeView(v);
Adam Cohend0445262011-07-04 23:53:22 -07002008
Winson Chung3d503fb2011-07-13 17:25:49 -07002009 FolderIcon fi =
2010 mLauncher.addFolder(target, container, screen, targetCell[0], targetCell[1]);
Adam Cohena9cf38f2011-05-02 15:36:58 -07002011 destInfo.cellX = -1;
2012 destInfo.cellY = -1;
2013 sourceInfo.cellX = -1;
2014 sourceInfo.cellY = -1;
Adam Cohend0445262011-07-04 23:53:22 -07002015
Adam Cohen558baaf2011-08-15 15:22:57 -07002016 // If the dragView is null, we can't animate
2017 boolean animate = dragView != null;
2018 if (animate) {
2019 fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale,
2020 postAnimationRunnable);
2021 } else {
2022 fi.addItem(destInfo);
2023 fi.addItem(sourceInfo);
2024 }
Adam Cohen10b17372011-04-15 14:21:25 -07002025 return true;
2026 }
2027 return false;
2028 }
2029
Adam Cohena65beee2011-06-27 21:32:23 -07002030 boolean addToExistingFolderIfNecessary(View newView, CellLayout target, int[] targetCell,
Adam Cohen3e8f8112011-07-02 18:03:00 -07002031 DragObject d, boolean external) {
Adam Cohena65beee2011-06-27 21:32:23 -07002032 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohenc0dcf592011-06-01 15:30:43 -07002033 if (dropOverView instanceof FolderIcon) {
2034 FolderIcon fi = (FolderIcon) dropOverView;
Adam Cohen3e8f8112011-07-02 18:03:00 -07002035 if (fi.acceptDrop(d.dragInfo)) {
2036 fi.onDrop(d);
Adam Cohenc0dcf592011-06-01 15:30:43 -07002037
2038 // if the drag started here, we need to remove it from the workspace
2039 if (!external) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002040 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
Adam Cohenc0dcf592011-06-01 15:30:43 -07002041 }
2042 return true;
2043 }
2044 }
2045 return false;
2046 }
2047
Adam Cohencb3382b2011-05-24 14:07:08 -07002048 public void onDrop(DragObject d) {
Adam Cohencb3382b2011-05-24 14:07:08 -07002049 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, d.dragView,
Adam Cohene3e27a82011-04-15 12:07:39 -07002050 mDragViewVisualCenter);
2051
2052 // We want the point to be mapped to the dragTarget.
Adam Cohenba781612011-05-09 14:37:39 -07002053 if (mDragTargetLayout != null) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002054 if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
2055 mapPointFromSelfToSibling(mLauncher.getHotseat(), mDragViewVisualCenter);
2056 } else {
2057 mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
2058 }
Adam Cohenba781612011-05-09 14:37:39 -07002059 }
Michael Jurkac6ee42e2010-09-30 12:04:50 -07002060
Adam Cohen76078c42011-06-09 15:06:52 -07002061 CellLayout dropTargetLayout = mDragTargetLayout;
Michael Jurkab09cac52011-01-05 20:23:39 -08002062
Adam Cohened51cc92011-08-01 20:28:08 -07002063 int snapScreen = -1;
Adam Cohencb3382b2011-05-24 14:07:08 -07002064 if (d.dragSource != this) {
Adam Cohene3e27a82011-04-15 12:07:39 -07002065 final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0],
2066 (int) mDragViewVisualCenter[1] };
Adam Cohen3e8f8112011-07-02 18:03:00 -07002067 onDropExternal(touchXY, d.dragInfo, dropTargetLayout, false, d);
Patrick Dubroyce34a972010-10-19 10:34:32 -07002068 } else if (mDragInfo != null) {
Patrick Dubroyce34a972010-10-19 10:34:32 -07002069 final View cell = mDragInfo.cell;
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08002070
Winson Chungea359c62011-08-03 17:06:35 -07002071 if (dropTargetLayout != null) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002072 // Move internally
Winson Chung40e882b2011-07-21 19:01:11 -07002073 boolean hasMovedLayouts = (getParentCellLayoutForView(cell) != dropTargetLayout);
2074 boolean hasMovedIntoHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
2075 long container = hasMovedIntoHotseat ?
Winson Chung3d503fb2011-07-13 17:25:49 -07002076 LauncherSettings.Favorites.CONTAINER_HOTSEAT :
2077 LauncherSettings.Favorites.CONTAINER_DESKTOP;
2078 int screen = (mTargetCell[0] < 0) ?
2079 mDragInfo.screen : indexOfChild(dropTargetLayout);
Adam Cohenc0dcf592011-06-01 15:30:43 -07002080 int spanX = mDragInfo != null ? mDragInfo.spanX : 1;
2081 int spanY = mDragInfo != null ? mDragInfo.spanY : 1;
2082 // First we find the cell nearest to point at which the item is
2083 // dropped, without any consideration to whether there is an item there.
2084 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], (int)
2085 mDragViewVisualCenter[1], spanX, spanY, dropTargetLayout, mTargetCell);
2086 // If the item being dropped is a shortcut and the nearest drop
Adam Cohen76078c42011-06-09 15:06:52 -07002087 // cell also contains a shortcut, then create a folder with the two shortcuts.
Winson Chung1c4cf4a2011-07-29 14:49:10 -07002088 if (!mInScrollArea && createUserFolderIfNecessary(cell, container,
Winson Chung3d503fb2011-07-13 17:25:49 -07002089 dropTargetLayout, mTargetCell, false, d.dragView, null)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07002090 return;
2091 }
2092
Adam Cohen3e8f8112011-07-02 18:03:00 -07002093 if (addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell, d, false)) {
Adam Cohendf035382011-04-11 17:22:04 -07002094 return;
2095 }
2096
2097 // Aside from the special case where we're dropping a shortcut onto a shortcut,
2098 // we need to find the nearest cell location that is vacant
Adam Cohene3e27a82011-04-15 12:07:39 -07002099 mTargetCell = findNearestVacantArea((int) mDragViewVisualCenter[0],
2100 (int) mDragViewVisualCenter[1], mDragInfo.spanX, mDragInfo.spanY, cell,
2101 dropTargetLayout, mTargetCell);
Adam Cohendf035382011-04-11 17:22:04 -07002102
Michael Jurka7cfc2822011-08-02 20:19:24 -07002103 if (mCurrentPage != screen && !hasMovedIntoHotseat) {
Adam Cohened51cc92011-08-01 20:28:08 -07002104 snapScreen = screen;
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08002105 snapToPage(screen);
2106 }
2107
Adam Cohenc0dcf592011-06-01 15:30:43 -07002108 if (mTargetCell[0] >= 0 && mTargetCell[1] >= 0) {
Winson Chung40e882b2011-07-21 19:01:11 -07002109 if (hasMovedLayouts) {
Patrick Dubroy94383362010-10-29 15:03:24 -07002110 // Reparent the view
Winson Chung3d503fb2011-07-13 17:25:49 -07002111 getParentCellLayoutForView(cell).removeView(cell);
2112 addInScreen(cell, container, screen, mTargetCell[0], mTargetCell[1],
2113 mDragInfo.spanX, mDragInfo.spanY);
Patrick Dubroy94383362010-10-29 15:03:24 -07002114 }
2115
2116 // update the item's position after drop
2117 final ItemInfo info = (ItemInfo) cell.getTag();
2118 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08002119 dropTargetLayout.onMove(cell, mTargetCell[0], mTargetCell[1]);
Patrick Dubroy94383362010-10-29 15:03:24 -07002120 lp.cellX = mTargetCell[0];
2121 lp.cellY = mTargetCell[1];
Winson Chung3d503fb2011-07-13 17:25:49 -07002122 cell.setId(LauncherModel.getCellLayoutChildId(container, mDragInfo.screen,
Patrick Dubroy94383362010-10-29 15:03:24 -07002123 mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY));
2124
Winson Chung3d503fb2011-07-13 17:25:49 -07002125 if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
2126 cell instanceof LauncherAppWidgetHostView) {
Adam Cohend4844c32011-02-18 19:25:06 -08002127 final CellLayout cellLayout = dropTargetLayout;
2128 // We post this call so that the widget has a chance to be placed
2129 // in its final location
2130
2131 final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell;
2132 AppWidgetProviderInfo pinfo = hostView.getAppWidgetInfo();
Adam Cohen27c09b02011-02-28 14:45:11 -08002133 if (pinfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) {
Adam Cohen26976d92011-03-22 15:33:33 -07002134 final Runnable resizeRunnable = new Runnable() {
Adam Cohend4844c32011-02-18 19:25:06 -08002135 public void run() {
Adam Cohen716b51e2011-06-30 12:09:54 -07002136 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohenc0dcf592011-06-01 15:30:43 -07002137 dragLayer.addResizeFrame(info, hostView, cellLayout);
Adam Cohend4844c32011-02-18 19:25:06 -08002138 }
Adam Cohen26976d92011-03-22 15:33:33 -07002139 };
2140 post(new Runnable() {
2141 public void run() {
2142 if (!isPageMoving()) {
2143 resizeRunnable.run();
2144 } else {
2145 mDelayedResizeRunnable = resizeRunnable;
2146 }
2147 }
Adam Cohend4844c32011-02-18 19:25:06 -08002148 });
2149 }
2150 }
2151
Winson Chung3d503fb2011-07-13 17:25:49 -07002152 LauncherModel.moveItemInDatabase(mLauncher, info, container, screen, lp.cellX,
2153 lp.cellY);
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002154 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002155 }
Patrick Dubroyce34a972010-10-19 10:34:32 -07002156
Michael Jurka8c920dd2011-01-20 14:16:56 -08002157 final CellLayout parent = (CellLayout) cell.getParent().getParent();
Patrick Dubroyce34a972010-10-19 10:34:32 -07002158
2159 // Prepare it to be animated into its new position
2160 // This must be called after the view has been re-parented
Michael Jurkad74c9842011-07-10 12:44:21 -07002161 final Runnable disableHardwareLayersRunnable = new Runnable() {
2162 @Override
2163 public void run() {
2164 mAnimatingViewIntoPlace = false;
2165 updateChildrenLayersEnabled();
2166 }
2167 };
2168 mAnimatingViewIntoPlace = true;
Adam Cohenfc53cd22011-07-20 15:45:11 -07002169 if (d.dragView.hasDrawn()) {
Adam Cohened51cc92011-08-01 20:28:08 -07002170 int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION;
2171 setFinalScrollForPageChange(snapScreen);
2172 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration,
Adam Cohenfc53cd22011-07-20 15:45:11 -07002173 disableHardwareLayersRunnable);
Adam Cohened51cc92011-08-01 20:28:08 -07002174 resetFinalScrollForPageChange(snapScreen);
Adam Cohenfc53cd22011-07-20 15:45:11 -07002175 } else {
2176 cell.setVisibility(VISIBLE);
2177 }
Adam Cohen716b51e2011-06-30 12:09:54 -07002178 parent.onDropChild(cell);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002179 }
2180 }
2181
Adam Cohened51cc92011-08-01 20:28:08 -07002182 public void setFinalScrollForPageChange(int screen) {
2183 if (screen >= 0) {
2184 mSavedScrollX = getScrollX();
2185 CellLayout cl = (CellLayout) getChildAt(screen);
2186 mSavedTranslationX = cl.getTranslationX();
2187 mSavedRotationY = cl.getRotationY();
2188 final int newX = getChildOffset(screen) - getRelativeChildOffset(screen);
2189 setScrollX(newX);
2190 cl.setTranslationX(0f);
2191 cl.setRotationY(0f);
2192 }
2193 }
2194
2195 public void resetFinalScrollForPageChange(int screen) {
2196 if (screen >= 0) {
2197 CellLayout cl = (CellLayout) getChildAt(screen);
2198 setScrollX(mSavedScrollX);
2199 cl.setTranslationX(mSavedTranslationX);
2200 cl.setRotationY(mSavedRotationY);
2201 }
2202 }
2203
Adam Cohen76078c42011-06-09 15:06:52 -07002204 public void getViewLocationRelativeToSelf(View v, int[] location) {
Adam Cohen8dfcba42011-07-07 16:38:18 -07002205 getLocationInWindow(location);
Adam Cohene3e27a82011-04-15 12:07:39 -07002206 int x = location[0];
2207 int y = location[1];
2208
Adam Cohen8dfcba42011-07-07 16:38:18 -07002209 v.getLocationInWindow(location);
Adam Cohene3e27a82011-04-15 12:07:39 -07002210 int vX = location[0];
2211 int vY = location[1];
2212
2213 location[0] = vX - x;
2214 location[1] = vY - y;
2215 }
2216
Adam Cohencb3382b2011-05-24 14:07:08 -07002217 public void onDragEnter(DragObject d) {
Winson Chungb26f3d62011-06-02 10:49:29 -07002218 if (mDragTargetLayout != null) {
Winson Chungc07918d2011-07-01 15:35:26 -07002219 mDragTargetLayout.setIsDragOverlapping(false);
Winson Chungb26f3d62011-06-02 10:49:29 -07002220 mDragTargetLayout.onDragExit();
Winson Chungb26f3d62011-06-02 10:49:29 -07002221 }
Winson Chungc07918d2011-07-01 15:35:26 -07002222 mDragTargetLayout = getCurrentDropLayout();
2223 mDragTargetLayout.setIsDragOverlapping(true);
2224 mDragTargetLayout.onDragEnter();
Winson Chungb26f3d62011-06-02 10:49:29 -07002225
Winson Chungc07918d2011-07-01 15:35:26 -07002226 // Because we don't have space in the Phone UI (the CellLayouts run to the edge) we
2227 // don't need to show the outlines
2228 if (LauncherApplication.isScreenLarge()) {
2229 showOutlines();
Michael Jurkad718d6a2010-10-14 15:35:17 -07002230 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002231 }
2232
Winson Chungc07918d2011-07-01 15:35:26 -07002233 private void doDragExit(DragObject d) {
2234 // Clean up folders
2235 cleanupFolderCreation(d);
2236
2237 // Reset the scroll area and previous drag target
2238 onResetScrollArea();
2239
2240 if (mDragTargetLayout != null) {
2241 mDragTargetLayout.setIsDragOverlapping(false);
2242 mDragTargetLayout.onDragExit();
2243 }
Winson Chungc07918d2011-07-01 15:35:26 -07002244 mLastDragOverView = null;
2245
2246 if (!mIsPageMoving) {
2247 hideOutlines();
2248 }
2249 }
2250
2251 public void onDragExit(DragObject d) {
2252 doDragExit(d);
2253 }
2254
Adam Cohencb3382b2011-05-24 14:07:08 -07002255 public DropTarget getDropTargetDelegate(DragObject d) {
Patrick Dubroy440c3602010-07-13 17:50:32 -07002256 return null;
2257 }
2258
Winson Chunga9abd0e2010-10-27 17:18:37 -07002259 /**
Winson Chung580e2772010-11-10 16:03:00 -08002260 * Tests to see if the drop will be accepted by Launcher, and if so, includes additional data
2261 * in the returned structure related to the widgets that match the drop (or a null list if it is
2262 * a shortcut drop). If the drop is not accepted then a null structure is returned.
2263 */
2264 private Pair<Integer, List<WidgetMimeTypeHandlerData>> validateDrag(DragEvent event) {
2265 final LauncherModel model = mLauncher.getModel();
2266 final ClipDescription desc = event.getClipDescription();
2267 final int mimeTypeCount = desc.getMimeTypeCount();
2268 for (int i = 0; i < mimeTypeCount; ++i) {
2269 final String mimeType = desc.getMimeType(i);
2270 if (mimeType.equals(InstallShortcutReceiver.SHORTCUT_MIMETYPE)) {
2271 return new Pair<Integer, List<WidgetMimeTypeHandlerData>>(i, null);
2272 } else {
2273 final List<WidgetMimeTypeHandlerData> widgets =
2274 model.resolveWidgetsForMimeType(mContext, mimeType);
2275 if (widgets.size() > 0) {
2276 return new Pair<Integer, List<WidgetMimeTypeHandlerData>>(i, widgets);
2277 }
2278 }
2279 }
2280 return null;
2281 }
2282
2283 /**
Winson Chunga9abd0e2010-10-27 17:18:37 -07002284 * Global drag and drop handler
2285 */
2286 @Override
2287 public boolean onDragEvent(DragEvent event) {
Winson Chung68846fd2010-10-29 11:00:27 -07002288 final ClipDescription desc = event.getClipDescription();
Winson Chunga9abd0e2010-10-27 17:18:37 -07002289 final CellLayout layout = (CellLayout) getChildAt(mCurrentPage);
2290 final int[] pos = new int[2];
2291 layout.getLocationOnScreen(pos);
2292 // We need to offset the drag coordinates to layout coordinate space
2293 final int x = (int) event.getX() - pos[0];
2294 final int y = (int) event.getY() - pos[1];
2295
2296 switch (event.getAction()) {
Winson Chung580e2772010-11-10 16:03:00 -08002297 case DragEvent.ACTION_DRAG_STARTED: {
2298 // Validate this drag
2299 Pair<Integer, List<WidgetMimeTypeHandlerData>> test = validateDrag(event);
2300 if (test != null) {
2301 boolean isShortcut = (test.second == null);
2302 if (isShortcut) {
2303 // Check if we have enough space on this screen to add a new shortcut
2304 if (!layout.findCellForSpan(pos, 1, 1)) {
Winson Chung557d6ed2011-07-08 15:34:52 -07002305 mLauncher.showOutOfSpaceMessage();
Winson Chung580e2772010-11-10 16:03:00 -08002306 return false;
2307 }
2308 }
2309 } else {
2310 // Show error message if we couldn't accept any of the items
2311 Toast.makeText(mContext, mContext.getString(R.string.external_drop_widget_error),
Winson Chunga9abd0e2010-10-27 17:18:37 -07002312 Toast.LENGTH_SHORT).show();
2313 return false;
2314 }
2315
Winson Chung68846fd2010-10-29 11:00:27 -07002316 // Create the drag outline
2317 // We need to add extra padding to the bitmap to make room for the glow effect
2318 final Canvas canvas = new Canvas();
Michael Jurka38b4f7c2010-12-14 16:46:39 -08002319 final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;
Winson Chung68846fd2010-10-29 11:00:27 -07002320 mDragOutline = createExternalDragOutline(canvas, bitmapPadding);
Winson Chunga9abd0e2010-10-27 17:18:37 -07002321
Winson Chung68846fd2010-10-29 11:00:27 -07002322 // Show the current page outlines to indicate that we can accept this drop
2323 showOutlines();
Michael Jurka33945b22010-12-21 18:19:38 -08002324 layout.setIsDragOccuring(true);
Winson Chung68846fd2010-10-29 11:00:27 -07002325 layout.onDragEnter();
2326 layout.visualizeDropLocation(null, mDragOutline, x, y, 1, 1);
Winson Chunga9abd0e2010-10-27 17:18:37 -07002327
Winson Chung68846fd2010-10-29 11:00:27 -07002328 return true;
Winson Chung580e2772010-11-10 16:03:00 -08002329 }
Winson Chunga9abd0e2010-10-27 17:18:37 -07002330 case DragEvent.ACTION_DRAG_LOCATION:
2331 // Visualize the drop location
2332 layout.visualizeDropLocation(null, mDragOutline, x, y, 1, 1);
2333 return true;
Winson Chung580e2772010-11-10 16:03:00 -08002334 case DragEvent.ACTION_DROP: {
Winson Chunga9abd0e2010-10-27 17:18:37 -07002335 // Try and add any shortcuts
Winson Chunga9abd0e2010-10-27 17:18:37 -07002336 final LauncherModel model = mLauncher.getModel();
2337 final ClipData data = event.getClipData();
Winson Chung55cef262010-10-28 14:14:18 -07002338
Winson Chung68846fd2010-10-29 11:00:27 -07002339 // We assume that the mime types are ordered in descending importance of
2340 // representation. So we enumerate the list of mime types and alert the
2341 // user if any widgets can handle the drop. Only the most preferred
2342 // representation will be handled.
2343 pos[0] = x;
2344 pos[1] = y;
Winson Chung580e2772010-11-10 16:03:00 -08002345 Pair<Integer, List<WidgetMimeTypeHandlerData>> test = validateDrag(event);
2346 if (test != null) {
2347 final int index = test.first;
2348 final List<WidgetMimeTypeHandlerData> widgets = test.second;
2349 final boolean isShortcut = (widgets == null);
2350 final String mimeType = desc.getMimeType(index);
2351 if (isShortcut) {
Dianne Hackborn0d5aad72011-01-17 15:28:58 -08002352 final Intent intent = data.getItemAt(index).getIntent();
Winson Chung68846fd2010-10-29 11:00:27 -07002353 Object info = model.infoFromShortcutIntent(mContext, intent, data.getIcon());
Michael Jurkac4e772e2011-02-10 13:32:01 -08002354 onDropExternal(new int[] { x, y }, info, layout, false);
Winson Chung68846fd2010-10-29 11:00:27 -07002355 } else {
Winson Chung580e2772010-11-10 16:03:00 -08002356 if (widgets.size() == 1) {
Winson Chung68846fd2010-10-29 11:00:27 -07002357 // If there is only one item, then go ahead and add and configure
2358 // that widget
2359 final AppWidgetProviderInfo widgetInfo = widgets.get(0).widgetInfo;
2360 final PendingAddWidgetInfo createInfo =
Michael Jurkac9d95c52011-08-29 14:03:34 -07002361 new PendingAddWidgetInfo(widgetInfo, mimeType, data);
Winson Chung3d503fb2011-07-13 17:25:49 -07002362 mLauncher.addAppWidgetFromDrop(createInfo,
2363 LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentPage, null, pos);
Winson Chung580e2772010-11-10 16:03:00 -08002364 } else {
Winson Chung68846fd2010-10-29 11:00:27 -07002365 // Show the widget picker dialog if there is more than one widget
2366 // that can handle this data type
2367 final InstallWidgetReceiver.WidgetListAdapter adapter =
2368 new InstallWidgetReceiver.WidgetListAdapter(mLauncher, mimeType,
2369 data, widgets, layout, mCurrentPage, pos);
2370 final AlertDialog.Builder builder =
2371 new AlertDialog.Builder(mContext);
2372 builder.setAdapter(adapter, adapter);
2373 builder.setCancelable(true);
2374 builder.setTitle(mContext.getString(
2375 R.string.external_drop_widget_pick_title));
2376 builder.setIcon(R.drawable.ic_no_applications);
2377 builder.show();
Winson Chung55cef262010-10-28 14:14:18 -07002378 }
Winson Chunga9abd0e2010-10-27 17:18:37 -07002379 }
2380 }
Winson Chunga9abd0e2010-10-27 17:18:37 -07002381 return true;
Winson Chung580e2772010-11-10 16:03:00 -08002382 }
Winson Chunga9abd0e2010-10-27 17:18:37 -07002383 case DragEvent.ACTION_DRAG_ENDED:
2384 // Hide the page outlines after the drop
Michael Jurka33945b22010-12-21 18:19:38 -08002385 layout.setIsDragOccuring(false);
Winson Chunga9abd0e2010-10-27 17:18:37 -07002386 layout.onDragExit();
2387 hideOutlines();
2388 return true;
2389 }
2390 return super.onDragEvent(event);
2391 }
2392
Michael Jurka4516c112010-10-07 15:13:47 -07002393 /*
2394 *
2395 * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
2396 * coordinate space. The argument xy is modified with the return result.
2397 *
2398 */
2399 void mapPointFromSelfToChild(View v, float[] xy) {
2400 mapPointFromSelfToChild(v, xy, null);
2401 }
2402
2403 /*
2404 *
2405 * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
2406 * coordinate space. The argument xy is modified with the return result.
2407 *
2408 * if cachedInverseMatrix is not null, this method will just use that matrix instead of
Michael Jurkad718d6a2010-10-14 15:35:17 -07002409 * computing it itself; we use this to avoid redundant matrix inversions in
Michael Jurka4516c112010-10-07 15:13:47 -07002410 * findMatchingPageForDragOver
2411 *
2412 */
2413 void mapPointFromSelfToChild(View v, float[] xy, Matrix cachedInverseMatrix) {
2414 if (cachedInverseMatrix == null) {
2415 v.getMatrix().invert(mTempInverseMatrix);
2416 cachedInverseMatrix = mTempInverseMatrix;
2417 }
2418 xy[0] = xy[0] + mScrollX - v.getLeft();
2419 xy[1] = xy[1] + mScrollY - v.getTop();
2420 cachedInverseMatrix.mapPoints(xy);
2421 }
2422
2423 /*
Winson Chung3d503fb2011-07-13 17:25:49 -07002424 * Maps a point from the Workspace's coordinate system to another sibling view's. (Workspace
2425 * covers the full screen)
2426 */
2427 void mapPointFromSelfToSibling(View v, float[] xy) {
2428 xy[0] = xy[0] - v.getLeft();
2429 xy[1] = xy[1] - v.getTop();
2430 }
2431
2432 /*
Michael Jurka4516c112010-10-07 15:13:47 -07002433 *
2434 * Convert the 2D coordinate xy from this CellLayout's coordinate space to
2435 * the parent View's coordinate space. The argument xy is modified with the return result.
2436 *
2437 */
2438 void mapPointFromChildToSelf(View v, float[] xy) {
2439 v.getMatrix().mapPoints(xy);
2440 xy[0] -= (mScrollX - v.getLeft());
2441 xy[1] -= (mScrollY - v.getTop());
2442 }
2443
Adam Cohene3e27a82011-04-15 12:07:39 -07002444 static private float squaredDistance(float[] point1, float[] point2) {
Michael Jurka4516c112010-10-07 15:13:47 -07002445 float distanceX = point1[0] - point2[0];
2446 float distanceY = point2[1] - point2[1];
2447 return distanceX * distanceX + distanceY * distanceY;
Adam Cohene3e27a82011-04-15 12:07:39 -07002448 }
Michael Jurka0280c3b2010-09-17 15:00:07 -07002449
Michael Jurka4516c112010-10-07 15:13:47 -07002450 /*
2451 *
2452 * Returns true if the passed CellLayout cl overlaps with dragView
2453 *
2454 */
2455 boolean overlaps(CellLayout cl, DragView dragView,
2456 int dragViewX, int dragViewY, Matrix cachedInverseMatrix) {
2457 // Transform the coordinates of the item being dragged to the CellLayout's coordinates
2458 final float[] draggedItemTopLeft = mTempDragCoordinates;
Michael Jurkaf12c75c2011-01-25 22:41:40 -08002459 draggedItemTopLeft[0] = dragViewX;
2460 draggedItemTopLeft[1] = dragViewY;
Michael Jurka4516c112010-10-07 15:13:47 -07002461 final float[] draggedItemBottomRight = mTempDragBottomRightCoordinates;
Michael Jurkaf12c75c2011-01-25 22:41:40 -08002462 draggedItemBottomRight[0] = draggedItemTopLeft[0] + dragView.getDragRegionWidth();
2463 draggedItemBottomRight[1] = draggedItemTopLeft[1] + dragView.getDragRegionHeight();
Michael Jurkaa63c4522010-08-19 13:52:27 -07002464
Michael Jurka4516c112010-10-07 15:13:47 -07002465 // Transform the dragged item's top left coordinates
2466 // to the CellLayout's local coordinates
2467 mapPointFromSelfToChild(cl, draggedItemTopLeft, cachedInverseMatrix);
2468 float overlapRegionLeft = Math.max(0f, draggedItemTopLeft[0]);
2469 float overlapRegionTop = Math.max(0f, draggedItemTopLeft[1]);
2470
2471 if (overlapRegionLeft <= cl.getWidth() && overlapRegionTop >= 0) {
2472 // Transform the dragged item's bottom right coordinates
2473 // to the CellLayout's local coordinates
2474 mapPointFromSelfToChild(cl, draggedItemBottomRight, cachedInverseMatrix);
2475 float overlapRegionRight = Math.min(cl.getWidth(), draggedItemBottomRight[0]);
2476 float overlapRegionBottom = Math.min(cl.getHeight(), draggedItemBottomRight[1]);
2477
2478 if (overlapRegionRight >= 0 && overlapRegionBottom <= cl.getHeight()) {
2479 float overlap = (overlapRegionRight - overlapRegionLeft) *
2480 (overlapRegionBottom - overlapRegionTop);
2481 if (overlap > 0) {
2482 return true;
2483 }
2484 }
2485 }
2486 return false;
2487 }
2488
2489 /*
2490 *
2491 * This method returns the CellLayout that is currently being dragged to. In order to drag
2492 * to a CellLayout, either the touch point must be directly over the CellLayout, or as a second
2493 * strategy, we see if the dragView is overlapping any CellLayout and choose the closest one
2494 *
2495 * Return null if no CellLayout is currently being dragged over
2496 *
2497 */
2498 private CellLayout findMatchingPageForDragOver(
Adam Cohen00618752011-07-20 12:06:04 -07002499 DragView dragView, float originX, float originY, boolean exact) {
Michael Jurka4516c112010-10-07 15:13:47 -07002500 // We loop through all the screens (ie CellLayouts) and see which ones overlap
2501 // with the item being dragged and then choose the one that's closest to the touch point
Michael Jurkaa63c4522010-08-19 13:52:27 -07002502 final int screenCount = getChildCount();
2503 CellLayout bestMatchingScreen = null;
Michael Jurka0280c3b2010-09-17 15:00:07 -07002504 float smallestDistSoFar = Float.MAX_VALUE;
Michael Jurka4516c112010-10-07 15:13:47 -07002505
Michael Jurkaa63c4522010-08-19 13:52:27 -07002506 for (int i = 0; i < screenCount; i++) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002507 CellLayout cl = (CellLayout) getChildAt(i);
Michael Jurkaa63c4522010-08-19 13:52:27 -07002508
Adam Cohen00618752011-07-20 12:06:04 -07002509 final float[] touchXy = {originX, originY};
Michael Jurka4516c112010-10-07 15:13:47 -07002510 // Transform the touch coordinates to the CellLayout's local coordinates
2511 // If the touch point is within the bounds of the cell layout, we can return immediately
Michael Jurka0280c3b2010-09-17 15:00:07 -07002512 cl.getMatrix().invert(mTempInverseMatrix);
Michael Jurka4516c112010-10-07 15:13:47 -07002513 mapPointFromSelfToChild(cl, touchXy, mTempInverseMatrix);
Michael Jurkaa63c4522010-08-19 13:52:27 -07002514
Michael Jurka4516c112010-10-07 15:13:47 -07002515 if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() &&
2516 touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) {
2517 return cl;
2518 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07002519
Adam Cohen00618752011-07-20 12:06:04 -07002520 if (!exact && overlaps(cl, dragView, (int) originX, (int) originY, mTempInverseMatrix)) {
Michael Jurka4516c112010-10-07 15:13:47 -07002521 // Get the center of the cell layout in screen coordinates
2522 final float[] cellLayoutCenter = mTempCellLayoutCenterCoordinates;
2523 cellLayoutCenter[0] = cl.getWidth()/2;
2524 cellLayoutCenter[1] = cl.getHeight()/2;
2525 mapPointFromChildToSelf(cl, cellLayoutCenter);
Michael Jurka0280c3b2010-09-17 15:00:07 -07002526
Adam Cohen00618752011-07-20 12:06:04 -07002527 touchXy[0] = originX;
2528 touchXy[1] = originY;
Michael Jurka0280c3b2010-09-17 15:00:07 -07002529
Michael Jurka4516c112010-10-07 15:13:47 -07002530 // Calculate the distance between the center of the CellLayout
2531 // and the touch point
2532 float dist = squaredDistance(touchXy, cellLayoutCenter);
2533
2534 if (dist < smallestDistSoFar) {
Michael Jurka0280c3b2010-09-17 15:00:07 -07002535 smallestDistSoFar = dist;
Michael Jurkaa63c4522010-08-19 13:52:27 -07002536 bestMatchingScreen = cl;
Michael Jurkaa63c4522010-08-19 13:52:27 -07002537 }
Michael Jurka4516c112010-10-07 15:13:47 -07002538 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07002539 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07002540 return bestMatchingScreen;
2541 }
2542
Adam Cohene3e27a82011-04-15 12:07:39 -07002543 // This is used to compute the visual center of the dragView. This point is then
2544 // used to visualize drop locations and determine where to drop an item. The idea is that
2545 // the visual center represents the user's interpretation of where the item is, and hence
2546 // is the appropriate point to use when determining drop location.
2547 private float[] getDragViewVisualCenter(int x, int y, int xOffset, int yOffset,
2548 DragView dragView, float[] recycle) {
2549 float res[];
2550 if (recycle == null) {
2551 res = new float[2];
2552 } else {
2553 res = recycle;
2554 }
2555
2556 // First off, the drag view has been shifted in a way that is not represented in the
2557 // x and y values or the x/yOffsets. Here we account for that shift.
2558 x += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetX);
2559 y += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY);
2560
2561 // These represent the visual top and left of drag view if a dragRect was provided.
2562 // If a dragRect was not provided, then they correspond to the actual view left and
2563 // top, as the dragRect is in that case taken to be the entire dragView.
2564 // R.dimen.dragViewOffsetY.
2565 int left = x - xOffset;
2566 int top = y - yOffset;
2567
2568 // In order to find the visual center, we shift by half the dragRect
2569 res[0] = left + dragView.getDragRegion().width() / 2;
2570 res[1] = top + dragView.getDragRegion().height() / 2;
2571
2572 return res;
2573 }
2574
Winson Chungea359c62011-08-03 17:06:35 -07002575 private boolean isDragWidget(DragObject d) {
2576 return (d.dragInfo instanceof LauncherAppWidgetInfo ||
2577 d.dragInfo instanceof PendingAddWidgetInfo);
2578 }
2579 private boolean isExternalDragWidget(DragObject d) {
2580 return d.dragSource != this && isDragWidget(d);
2581 }
2582
Adam Cohencb3382b2011-05-24 14:07:08 -07002583 public void onDragOver(DragObject d) {
Winson Chungc07918d2011-07-01 15:35:26 -07002584 // Skip drag over events while we are dragging over side pages
2585 if (mInScrollArea) return;
Michael Jurkad74c9842011-07-10 12:44:21 -07002586 if (mIsSwitchingState) return;
Winson Chungc07918d2011-07-01 15:35:26 -07002587
Winson Chung4afe9b32011-07-27 17:46:20 -07002588 Rect r = new Rect();
Winson Chung3d503fb2011-07-13 17:25:49 -07002589 CellLayout layout = null;
Winson Chungc07918d2011-07-01 15:35:26 -07002590 ItemInfo item = (ItemInfo) d.dragInfo;
2591
2592 // Ensure that we have proper spans for the item that we are dropping
2593 if (item.spanX < 0 || item.spanY < 0) throw new RuntimeException("Improper spans found");
Adam Cohen00618752011-07-20 12:06:04 -07002594 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset,
Winson Chung4afe9b32011-07-27 17:46:20 -07002595 d.dragView, mDragViewVisualCenter);
Adam Cohen00618752011-07-20 12:06:04 -07002596
Winson Chungc07918d2011-07-01 15:35:26 -07002597 // Identify whether we have dragged over a side page
Michael Jurkad74c9842011-07-10 12:44:21 -07002598 if (isSmall()) {
Winson Chungea359c62011-08-03 17:06:35 -07002599 if (mLauncher.getHotseat() != null && !isExternalDragWidget(d)) {
Winson Chung4afe9b32011-07-27 17:46:20 -07002600 mLauncher.getHotseat().getHitRect(r);
2601 if (r.contains(d.x, d.y)) {
2602 layout = mLauncher.getHotseat().getLayout();
2603 }
2604 }
2605 if (layout == null) {
Winson Chungc7da5552011-08-10 15:28:45 -07002606 layout = findMatchingPageForDragOver(d.dragView, d.x, d.y, true);
Winson Chung4afe9b32011-07-27 17:46:20 -07002607 }
Winson Chungc07918d2011-07-01 15:35:26 -07002608 if (layout != mDragTargetLayout) {
2609 // Cancel all intermediate folder states
2610 cleanupFolderCreation(d);
2611
2612 if (mDragTargetLayout != null) {
2613 mDragTargetLayout.setIsDragOverlapping(false);
2614 mDragTargetLayout.onDragExit();
2615 }
2616 mDragTargetLayout = layout;
2617 if (mDragTargetLayout != null) {
2618 mDragTargetLayout.setIsDragOverlapping(true);
2619 mDragTargetLayout.onDragEnter();
2620 } else {
2621 mLastDragOverView = null;
2622 }
2623
Michael Jurkad74c9842011-07-10 12:44:21 -07002624 boolean isInSpringLoadedMode = (mState == State.SPRING_LOADED);
Winson Chungc07918d2011-07-01 15:35:26 -07002625 if (isInSpringLoadedMode) {
Winson Chung4afe9b32011-07-27 17:46:20 -07002626 if (mLauncher.isHotseatLayout(layout)) {
2627 mSpringLoadedDragController.cancel();
2628 } else {
2629 mSpringLoadedDragController.setAlarm(mDragTargetLayout);
2630 }
Winson Chungc07918d2011-07-01 15:35:26 -07002631 }
2632 }
2633 } else {
Winson Chung3d503fb2011-07-13 17:25:49 -07002634 // Test to see if we are over the hotseat otherwise just use the current page
Winson Chungea359c62011-08-03 17:06:35 -07002635 if (mLauncher.getHotseat() != null && !isDragWidget(d)) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002636 mLauncher.getHotseat().getHitRect(r);
2637 if (r.contains(d.x, d.y)) {
2638 layout = mLauncher.getHotseat().getLayout();
2639 }
2640 }
2641 if (layout == null) {
2642 layout = getCurrentDropLayout();
2643 }
Winson Chungc07918d2011-07-01 15:35:26 -07002644 if (layout != mDragTargetLayout) {
2645 if (mDragTargetLayout != null) {
2646 mDragTargetLayout.setIsDragOverlapping(false);
2647 mDragTargetLayout.onDragExit();
2648 }
2649 mDragTargetLayout = layout;
2650 mDragTargetLayout.setIsDragOverlapping(true);
2651 mDragTargetLayout.onDragEnter();
2652 }
2653 }
2654
2655 // Handle the drag over
2656 if (mDragTargetLayout != null) {
2657 final View child = (mDragInfo == null) ? null : mDragInfo.cell;
Adam Cohene3e27a82011-04-15 12:07:39 -07002658
Winson Chungc07918d2011-07-01 15:35:26 -07002659 // We want the point to be mapped to the dragTarget.
Winson Chung3d503fb2011-07-13 17:25:49 -07002660 if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
2661 mapPointFromSelfToSibling(mLauncher.getHotseat(), mDragViewVisualCenter);
2662 } else {
2663 mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
2664 }
Winson Chungc07918d2011-07-01 15:35:26 -07002665 ItemInfo info = (ItemInfo) d.dragInfo;
Patrick Dubroy1262e362010-10-06 15:49:50 -07002666
Winson Chungc07918d2011-07-01 15:35:26 -07002667 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
2668 (int) mDragViewVisualCenter[1], 1, 1, mDragTargetLayout, mTargetCell);
2669 final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0],
2670 mTargetCell[1]);
Winson Chung785d2eb2011-04-14 16:08:02 -07002671
Winson Chungc07918d2011-07-01 15:35:26 -07002672 boolean userFolderPending = willCreateUserFolder(info, mDragTargetLayout,
2673 mTargetCell, false);
2674 boolean isOverFolder = dragOverView instanceof FolderIcon;
2675 if (dragOverView != mLastDragOverView) {
2676 cancelFolderCreation();
2677 if (mLastDragOverView != null && mLastDragOverView instanceof FolderIcon) {
2678 ((FolderIcon) mLastDragOverView).onDragExit(d.dragInfo);
Michael Jurkad3ef3062010-11-23 16:23:58 -08002679 }
2680 }
Michael Jurkad718d6a2010-10-14 15:35:17 -07002681
Winson Chungc07918d2011-07-01 15:35:26 -07002682 if (userFolderPending && dragOverView != mLastDragOverView) {
2683 mFolderCreationAlarm.setOnAlarmListener(new
2684 FolderCreationAlarmListener(mDragTargetLayout, mTargetCell[0], mTargetCell[1]));
2685 mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT);
2686 }
Michael Jurkad718d6a2010-10-14 15:35:17 -07002687
Winson Chungc07918d2011-07-01 15:35:26 -07002688 if (dragOverView != mLastDragOverView && isOverFolder) {
2689 ((FolderIcon) dragOverView).onDragEnter(d.dragInfo);
Michael Jurkad3ef3062010-11-23 16:23:58 -08002690 if (mDragTargetLayout != null) {
Winson Chungc07918d2011-07-01 15:35:26 -07002691 mDragTargetLayout.clearDragOutlines();
Michael Jurkad718d6a2010-10-14 15:35:17 -07002692 }
Patrick Dubroy1262e362010-10-06 15:49:50 -07002693 }
Winson Chungc07918d2011-07-01 15:35:26 -07002694 mLastDragOverView = dragOverView;
2695
2696 if (!mCreateUserFolderOnDrop && !isOverFolder) {
2697 mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
2698 (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
2699 item.spanX, item.spanY);
2700 }
Patrick Dubroy976ebec2010-08-04 20:03:37 -07002701 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002702 }
2703
Winson Chungc07918d2011-07-01 15:35:26 -07002704 private void cleanupFolderCreation(DragObject d) {
2705 if (mDragFolderRingAnimator != null && mCreateUserFolderOnDrop) {
2706 mDragFolderRingAnimator.animateToNaturalState();
2707 }
2708 if (mLastDragOverView != null && mLastDragOverView instanceof FolderIcon) {
2709 if (d != null) {
2710 ((FolderIcon) mLastDragOverView).onDragExit(d.dragInfo);
2711 }
2712 }
2713 mFolderCreationAlarm.cancelAlarm();
2714 }
2715
Adam Cohenc0dcf592011-06-01 15:30:43 -07002716 private void cancelFolderCreation() {
2717 if (mDragFolderRingAnimator != null && mCreateUserFolderOnDrop) {
2718 mDragFolderRingAnimator.animateToNaturalState();
2719 }
2720 mCreateUserFolderOnDrop = false;
2721 mFolderCreationAlarm.cancelAlarm();
2722 }
2723
Adam Cohen19072da2011-05-31 14:30:45 -07002724 class FolderCreationAlarmListener implements OnAlarmListener {
Adam Cohen69ce2e52011-07-03 19:25:21 -07002725 CellLayout layout;
2726 int cellX;
2727 int cellY;
Adam Cohen19072da2011-05-31 14:30:45 -07002728
Adam Cohen69ce2e52011-07-03 19:25:21 -07002729 public FolderCreationAlarmListener(CellLayout layout, int cellX, int cellY) {
2730 this.layout = layout;
2731 this.cellX = cellX;
2732 this.cellY = cellY;
Adam Cohen19072da2011-05-31 14:30:45 -07002733 }
2734
2735 public void onAlarm(Alarm alarm) {
Adam Cohen19072da2011-05-31 14:30:45 -07002736 if (mDragFolderRingAnimator == null) {
2737 mDragFolderRingAnimator = new FolderRingAnimator(mLauncher, null);
2738 }
Adam Cohen69ce2e52011-07-03 19:25:21 -07002739 mDragFolderRingAnimator.setCell(cellX, cellY);
2740 mDragFolderRingAnimator.setCellLayout(layout);
Adam Cohen19072da2011-05-31 14:30:45 -07002741 mDragFolderRingAnimator.animateToAcceptState();
Adam Cohen69ce2e52011-07-03 19:25:21 -07002742 layout.showFolderAccept(mDragFolderRingAnimator);
2743 layout.clearDragOutlines();
Adam Cohen19072da2011-05-31 14:30:45 -07002744 mCreateUserFolderOnDrop = true;
Adam Cohen19072da2011-05-31 14:30:45 -07002745 }
2746 }
2747
Winson Chunga34abf82010-11-12 12:10:35 -08002748 @Override
2749 public void getHitRect(Rect outRect) {
2750 // We want the workspace to have the whole area of the display (it will find the correct
2751 // cell layout to drop to in the existing drag/drop logic.
Adam Cohencff6af82011-09-13 14:51:53 -07002752 outRect.set(0, 0, mDisplayWidth, mDisplayHeight);
Winson Chunga34abf82010-11-12 12:10:35 -08002753 }
2754
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07002755 /**
2756 * Add the item specified by dragInfo to the given layout.
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07002757 * @return true if successful
2758 */
Adam Cohen120980b2010-12-08 11:05:37 -08002759 public boolean addExternalItemToScreen(ItemInfo dragInfo, CellLayout layout) {
2760 if (layout.findCellForSpan(mTempEstimate, dragInfo.spanX, dragInfo.spanY)) {
Patrick Dubroybbaa75c2011-03-08 18:47:40 -08002761 onDropExternal(dragInfo.dropPos, (ItemInfo) dragInfo, (CellLayout) layout, false);
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07002762 return true;
2763 }
Michael Jurka0280c3b2010-09-17 15:00:07 -07002764 mLauncher.showOutOfSpaceMessage();
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07002765 return false;
2766 }
2767
Adam Cohend5e42732011-03-28 17:33:39 -07002768 private void onDropExternal(int[] touchXY, Object dragInfo,
2769 CellLayout cellLayout, boolean insertAtFirst) {
Adam Cohene3e27a82011-04-15 12:07:39 -07002770 onDropExternal(touchXY, dragInfo, cellLayout, insertAtFirst, null);
Adam Cohend5e42732011-03-28 17:33:39 -07002771 }
2772
Adam Cohen120980b2010-12-08 11:05:37 -08002773 /**
2774 * Drop an item that didn't originate on one of the workspace screens.
2775 * It may have come from Launcher (e.g. from all apps or customize), or it may have
2776 * come from another app altogether.
2777 *
2778 * NOTE: This can also be called when we are outside of a drag event, when we want
2779 * to add an item to one of the workspace screens.
2780 */
Winson Chung557d6ed2011-07-08 15:34:52 -07002781 private void onDropExternal(final int[] touchXY, final Object dragInfo,
2782 final CellLayout cellLayout, boolean insertAtFirst, DragObject d) {
2783 final Runnable exitSpringLoadedRunnable = new Runnable() {
2784 @Override
2785 public void run() {
Winson Chung6a3fd3f2011-08-02 14:03:26 -07002786 mLauncher.exitSpringLoadedDragModeDelayed(true, false);
Winson Chung557d6ed2011-07-08 15:34:52 -07002787 }
2788 };
Adam Cohenb7e16182011-07-15 17:55:02 -07002789
2790 ItemInfo info = (ItemInfo) dragInfo;
2791 int spanX = info.spanX;
2792 int spanY = info.spanY;
2793 if (mDragInfo != null) {
2794 spanX = mDragInfo.spanX;
2795 spanY = mDragInfo.spanY;
2796 }
2797
Winson Chung3d503fb2011-07-13 17:25:49 -07002798 final long container = mLauncher.isHotseatLayout(cellLayout) ?
2799 LauncherSettings.Favorites.CONTAINER_HOTSEAT :
2800 LauncherSettings.Favorites.CONTAINER_DESKTOP;
Winson Chung557d6ed2011-07-08 15:34:52 -07002801 final int screen = indexOfChild(cellLayout);
Winson Chung3d503fb2011-07-13 17:25:49 -07002802 if (!mLauncher.isHotseatLayout(cellLayout) && screen != mCurrentPage
2803 && mState != State.SPRING_LOADED) {
Adam Cohen76078c42011-06-09 15:06:52 -07002804 snapToPage(screen);
2805 }
Adam Cohenb7e16182011-07-15 17:55:02 -07002806
2807 if (info instanceof PendingAddItemInfo) {
2808 final PendingAddItemInfo pendingInfo = (PendingAddItemInfo) dragInfo;
2809
Adam Cohen558baaf2011-08-15 15:22:57 -07002810 boolean findNearestVacantCell = true;
2811 if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
2812 mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
2813 cellLayout, mTargetCell);
2814 if (willCreateUserFolder((ItemInfo) d.dragInfo, mDragTargetLayout, mTargetCell,
2815 true) || willAddToExistingUserFolder((ItemInfo) d.dragInfo,
2816 mDragTargetLayout, mTargetCell)) {
2817 findNearestVacantCell = false;
2818 }
2819 }
2820 if (findNearestVacantCell) {
2821 mTargetCell = findNearestVacantArea(touchXY[0], touchXY[1], spanX, spanY, null,
2822 cellLayout, mTargetCell);
2823 }
2824
Adam Cohenb7e16182011-07-15 17:55:02 -07002825 Runnable onAnimationCompleteRunnable = new Runnable() {
Winson Chung557d6ed2011-07-08 15:34:52 -07002826 @Override
2827 public void run() {
2828 // When dragging and dropping from customization tray, we deal with creating
2829 // widgets/shortcuts/folders in a slightly different way
Adam Cohenb7e16182011-07-15 17:55:02 -07002830 switch (pendingInfo.itemType) {
Winson Chung557d6ed2011-07-08 15:34:52 -07002831 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
Adam Cohenb7e16182011-07-15 17:55:02 -07002832 mLauncher.addAppWidgetFromDrop((PendingAddWidgetInfo) pendingInfo,
Winson Chung3d503fb2011-07-13 17:25:49 -07002833 container, screen, mTargetCell, null);
Winson Chung557d6ed2011-07-08 15:34:52 -07002834 break;
2835 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
Adam Cohenb7e16182011-07-15 17:55:02 -07002836 mLauncher.processShortcutFromDrop(pendingInfo.componentName,
Winson Chung3d503fb2011-07-13 17:25:49 -07002837 container, screen, mTargetCell, null);
Winson Chung557d6ed2011-07-08 15:34:52 -07002838 break;
2839 default:
Adam Cohenb7e16182011-07-15 17:55:02 -07002840 throw new IllegalStateException("Unknown item type: " +
2841 pendingInfo.itemType);
Winson Chung557d6ed2011-07-08 15:34:52 -07002842 }
2843 cellLayout.onDragExit();
2844 }
Adam Cohenb7e16182011-07-15 17:55:02 -07002845 };
2846
2847 // Now we animate the dragView, (ie. the widget or shortcut preview) into its final
2848 // location and size on the home screen.
Adam Cohenb7e16182011-07-15 17:55:02 -07002849 int loc[] = new int[2];
2850 cellLayout.cellToPoint(mTargetCell[0], mTargetCell[1], loc);
2851
2852 RectF r = new RectF();
2853 cellLayout.cellToRect(mTargetCell[0], mTargetCell[1], spanX, spanY, r);
Adam Cohen4b285c52011-07-21 14:24:06 -07002854 setFinalTransitionTransform(cellLayout);
Adam Cohenb7e16182011-07-15 17:55:02 -07002855 float cellLayoutScale =
2856 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(cellLayout, loc);
Adam Cohen4b285c52011-07-21 14:24:06 -07002857 resetTransitionTransform(cellLayout);
Adam Cohenb7e16182011-07-15 17:55:02 -07002858
2859 float dragViewScale = r.width() / d.dragView.getMeasuredWidth();
2860 // The animation will scale the dragView about its center, so we need to center about
2861 // the final location.
2862 loc[0] -= (d.dragView.getMeasuredWidth() - cellLayoutScale * r.width()) / 2;
2863 loc[1] -= (d.dragView.getMeasuredHeight() - cellLayoutScale * r.height()) / 2;
2864
2865 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, loc,
2866 dragViewScale * cellLayoutScale, onAnimationCompleteRunnable);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07002867 } else {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08002868 // This is for other drag/drop cases, like dragging from All Apps
Michael Jurka9c6de3d2010-11-23 16:23:58 -08002869 View view = null;
2870
2871 switch (info.itemType) {
2872 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
2873 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
2874 if (info.container == NO_ID && info instanceof ApplicationInfo) {
2875 // Came from all apps -- make a copy
Michael Jurkac9d95c52011-08-29 14:03:34 -07002876 info = new ShortcutInfo((ApplicationInfo) info);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08002877 }
2878 view = mLauncher.createShortcut(R.layout.application, cellLayout,
2879 (ShortcutInfo) info);
2880 break;
Adam Cohendf2cc412011-04-27 16:56:57 -07002881 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
Adam Cohenc0dcf592011-06-01 15:30:43 -07002882 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, cellLayout,
2883 (FolderInfo) info, mIconCache);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08002884 break;
2885 default:
2886 throw new IllegalStateException("Unknown item type: " + info.itemType);
2887 }
2888
Adam Cohenc0dcf592011-06-01 15:30:43 -07002889 // First we find the cell nearest to point at which the item is
2890 // dropped, without any consideration to whether there is an item there.
2891 if (touchXY != null) {
2892 mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
2893 cellLayout, mTargetCell);
Winson Chung557d6ed2011-07-08 15:34:52 -07002894 d.postAnimationRunnable = exitSpringLoadedRunnable;
Winson Chung3d503fb2011-07-13 17:25:49 -07002895 if (createUserFolderIfNecessary(view, container, cellLayout, mTargetCell, true,
2896 d.dragView, d.postAnimationRunnable)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07002897 return;
2898 }
Adam Cohen3e8f8112011-07-02 18:03:00 -07002899 if (addToExistingFolderIfNecessary(view, cellLayout, mTargetCell, d, true)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07002900 return;
2901 }
Adam Cohen10b17372011-04-15 14:21:25 -07002902 }
2903
Michael Jurkac4e772e2011-02-10 13:32:01 -08002904 if (touchXY != null) {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08002905 // when dragging and dropping, just find the closest free spot
Adam Cohenc0dcf592011-06-01 15:30:43 -07002906 mTargetCell = findNearestVacantArea(touchXY[0], touchXY[1], 1, 1, null,
2907 cellLayout, mTargetCell);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08002908 } else {
2909 cellLayout.findCellForSpan(mTargetCell, 1, 1);
2910 }
Winson Chung3d503fb2011-07-13 17:25:49 -07002911 addInScreen(view, container, screen, mTargetCell[0], mTargetCell[1], info.spanX,
2912 info.spanY, insertAtFirst);
Adam Cohen716b51e2011-06-30 12:09:54 -07002913 cellLayout.onDropChild(view);
Michael Jurkad3ef3062010-11-23 16:23:58 -08002914 cellLayout.animateDrop();
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07002915 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
Adam Cohend5e42732011-03-28 17:33:39 -07002916 cellLayout.getChildrenLayout().measureChild(view);
2917
Winson Chung3d503fb2011-07-13 17:25:49 -07002918 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screen,
Winson Chungaafa03c2010-06-11 17:34:16 -07002919 lp.cellX, lp.cellY);
Adam Cohen3e8f8112011-07-02 18:03:00 -07002920
2921 if (d.dragView != null) {
Adam Cohen4b285c52011-07-21 14:24:06 -07002922 // We wrap the animation call in the temporary set and reset of the current
2923 // cellLayout to its final transform -- this means we animate the drag view to
2924 // the correct final location.
2925 setFinalTransitionTransform(cellLayout);
Winson Chung557d6ed2011-07-08 15:34:52 -07002926 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view,
Adam Cohen4b285c52011-07-21 14:24:06 -07002927 exitSpringLoadedRunnable);
2928 resetTransitionTransform(cellLayout);
Adam Cohen3e8f8112011-07-02 18:03:00 -07002929 }
Joe Onorato00acb122009-08-04 16:04:30 -04002930 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002931 }
Winson Chungaafa03c2010-06-11 17:34:16 -07002932
Adam Cohen4b285c52011-07-21 14:24:06 -07002933 public void setFinalTransitionTransform(CellLayout layout) {
2934 if (isSwitchingState()) {
2935 int index = indexOfChild(layout);
2936 mCurrentScaleX = layout.getScaleX();
2937 mCurrentScaleY = layout.getScaleY();
2938 mCurrentTranslationX = layout.getTranslationX();
2939 mCurrentTranslationY = layout.getTranslationY();
2940 mCurrentRotationY = layout.getRotationY();
2941 layout.setScaleX(mNewScaleXs[index]);
2942 layout.setScaleY(mNewScaleYs[index]);
2943 layout.setTranslationX(mNewTranslationXs[index]);
2944 layout.setTranslationY(mNewTranslationYs[index]);
2945 layout.setRotationY(mNewRotationYs[index]);
2946 }
2947 }
2948 public void resetTransitionTransform(CellLayout layout) {
2949 if (isSwitchingState()) {
Adam Cohen4b285c52011-07-21 14:24:06 -07002950 mCurrentScaleX = layout.getScaleX();
2951 mCurrentScaleY = layout.getScaleY();
2952 mCurrentTranslationX = layout.getTranslationX();
2953 mCurrentTranslationY = layout.getTranslationY();
2954 mCurrentRotationY = layout.getRotationY();
2955 layout.setScaleX(mCurrentScaleX);
2956 layout.setScaleY(mCurrentScaleY);
2957 layout.setTranslationX(mCurrentTranslationX);
2958 layout.setTranslationY(mCurrentTranslationY);
2959 layout.setRotationY(mCurrentRotationY);
2960 }
2961 }
2962
Jeff Sharkey70864282009-04-07 21:08:40 -07002963 /**
2964 * Return the current {@link CellLayout}, correctly picking the destination
2965 * screen while a scroll is in progress.
2966 */
Patrick Dubroy5f445422011-02-18 14:35:21 -08002967 public CellLayout getCurrentDropLayout() {
2968 return (CellLayout) getChildAt(mNextPage == INVALID_PAGE ? mCurrentPage : mNextPage);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002969 }
2970
Jeff Sharkey70864282009-04-07 21:08:40 -07002971 /**
Michael Jurka0280c3b2010-09-17 15:00:07 -07002972 * Return the current CellInfo describing our current drag; this method exists
2973 * so that Launcher can sync this object with the correct info when the activity is created/
2974 * destroyed
2975 *
2976 */
2977 public CellLayout.CellInfo getDragInfo() {
2978 return mDragInfo;
2979 }
2980
2981 /**
Jeff Sharkey70864282009-04-07 21:08:40 -07002982 * Calculate the nearest cell where the given object would be dropped.
Adam Cohene3e27a82011-04-15 12:07:39 -07002983 *
2984 * pixelX and pixelY should be in the coordinate system of layout
Jeff Sharkey70864282009-04-07 21:08:40 -07002985 */
Michael Jurka6a1435d2010-09-27 17:35:12 -07002986 private int[] findNearestVacantArea(int pixelX, int pixelY,
Jeff Sharkey70864282009-04-07 21:08:40 -07002987 int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
Michael Jurka0280c3b2010-09-17 15:00:07 -07002988 return layout.findNearestVacantArea(
Adam Cohene3e27a82011-04-15 12:07:39 -07002989 pixelX, pixelY, spanX, spanY, ignoreView, recycle);
Jeff Sharkey70864282009-04-07 21:08:40 -07002990 }
Winson Chungaafa03c2010-06-11 17:34:16 -07002991
Adam Cohendf035382011-04-11 17:22:04 -07002992 /**
2993 * Calculate the nearest cell where the given object would be dropped.
Adam Cohene3e27a82011-04-15 12:07:39 -07002994 *
2995 * pixelX and pixelY should be in the coordinate system of layout
Adam Cohendf035382011-04-11 17:22:04 -07002996 */
2997 private int[] findNearestArea(int pixelX, int pixelY,
Adam Cohene3e27a82011-04-15 12:07:39 -07002998 int spanX, int spanY, CellLayout layout, int[] recycle) {
Adam Cohendf035382011-04-11 17:22:04 -07002999 return layout.findNearestArea(
Adam Cohene3e27a82011-04-15 12:07:39 -07003000 pixelX, pixelY, spanX, spanY, recycle);
Adam Cohendf035382011-04-11 17:22:04 -07003001 }
3002
Adam Cohencff6af82011-09-13 14:51:53 -07003003 void setup(DragController dragController) {
Michael Jurkac2f7f472010-12-14 15:34:42 -08003004 mSpringLoadedDragController = new SpringLoadedDragController(mLauncher);
Joe Onorato00acb122009-08-04 16:04:30 -04003005 mDragController = dragController;
Michael Jurkad74c9842011-07-10 12:44:21 -07003006
Michael Jurkad74c9842011-07-10 12:44:21 -07003007 // hardware layers on children are enabled on startup, but should be disabled until
3008 // needed
3009 updateChildrenLayersEnabled();
3010 setWallpaperDimension();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003011 }
3012
Patrick Dubroye3887cc2011-01-20 10:43:40 -08003013 /**
3014 * Called at the end of a drag which originated on the workspace.
3015 */
Adam Cohenc0dcf592011-06-01 15:30:43 -07003016 public void onDropCompleted(View target, DragObject d, boolean success) {
Winson Chungaafa03c2010-06-11 17:34:16 -07003017 if (success) {
Michael Jurkad74c9842011-07-10 12:44:21 -07003018 if (target != this) {
3019 if (mDragInfo != null) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003020 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
Michael Jurkad74c9842011-07-10 12:44:21 -07003021 if (mDragInfo.cell instanceof DropTarget) {
3022 mDragController.removeDropTarget((DropTarget) mDragInfo.cell);
3023 }
Joe Onorato00acb122009-08-04 16:04:30 -04003024 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003025 }
Patrick Dubroyce34a972010-10-19 10:34:32 -07003026 } else if (mDragInfo != null) {
Patrick Dubroye3887cc2011-01-20 10:43:40 -08003027 // NOTE: When 'success' is true, onDragExit is called by the DragController before
3028 // calling onDropCompleted(). We call it ourselves here, but maybe this should be
3029 // moved into DragController.cancelDrag().
Adam Cohenc0dcf592011-06-01 15:30:43 -07003030 doDragExit(null);
Winson Chung3d503fb2011-07-13 17:25:49 -07003031 CellLayout cellLayout;
3032 if (mLauncher.isHotseatLayout(target)) {
3033 cellLayout = mLauncher.getHotseat().getLayout();
3034 } else {
3035 cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
3036 }
3037 cellLayout.onDropChild(mDragInfo.cell);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003038 }
Joe Onorato4be866d2010-10-10 11:26:02 -07003039 mDragOutline = null;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003040 mDragInfo = null;
3041 }
3042
Michael Jurka0280c3b2010-09-17 15:00:07 -07003043 public boolean isDropEnabled() {
3044 return true;
3045 }
3046
Michael Jurka0142d492010-08-25 17:46:15 -07003047 @Override
3048 protected void onRestoreInstanceState(Parcelable state) {
3049 super.onRestoreInstanceState(state);
3050 Launcher.setScreen(mCurrentPage);
3051 }
3052
3053 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003054 public void scrollLeft() {
Michael Jurkad74c9842011-07-10 12:44:21 -07003055 if (!isSmall() && !mIsSwitchingState) {
Michael Jurka0142d492010-08-25 17:46:15 -07003056 super.scrollLeft();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003057 }
Adam Cohen95bb8002011-07-03 23:40:28 -07003058 Folder openFolder = getOpenFolder();
3059 if (openFolder != null) {
3060 openFolder.completeDragExit();
3061 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003062 }
3063
Michael Jurka0142d492010-08-25 17:46:15 -07003064 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003065 public void scrollRight() {
Michael Jurkad74c9842011-07-10 12:44:21 -07003066 if (!isSmall() && !mIsSwitchingState) {
Michael Jurka0142d492010-08-25 17:46:15 -07003067 super.scrollRight();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003068 }
Adam Cohen95bb8002011-07-03 23:40:28 -07003069 Folder openFolder = getOpenFolder();
3070 if (openFolder != null) {
3071 openFolder.completeDragExit();
3072 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003073 }
3074
Patrick Dubroy1262e362010-10-06 15:49:50 -07003075 @Override
Winson Chung1c4cf4a2011-07-29 14:49:10 -07003076 public void onEnterScrollArea(int x, int y, int direction) {
3077 // Ignore the scroll area if we are dragging over the hot seat
3078 if (mLauncher.getHotseat() != null) {
3079 Rect r = new Rect();
3080 mLauncher.getHotseat().getHitRect(r);
3081 if (r.contains(x, y)) {
3082 return;
3083 }
3084 }
3085
Michael Jurkad74c9842011-07-10 12:44:21 -07003086 if (!isSmall() && !mIsSwitchingState) {
Michael Jurkad718d6a2010-10-14 15:35:17 -07003087 mInScrollArea = true;
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08003088
3089 final int page = mCurrentPage + (direction == DragController.SCROLL_LEFT ? -1 : 1);
3090 final CellLayout layout = (CellLayout) getChildAt(page);
Adam Cohenc0dcf592011-06-01 15:30:43 -07003091 cancelFolderCreation();
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08003092
3093 if (layout != null) {
Winson Chungc07918d2011-07-01 15:35:26 -07003094 // Exit the current layout and mark the overlapping layout
Patrick Dubroy0207c522010-11-03 22:12:02 -07003095 if (mDragTargetLayout != null) {
Winson Chungc07918d2011-07-01 15:35:26 -07003096 mDragTargetLayout.setIsDragOverlapping(false);
Patrick Dubroy0207c522010-11-03 22:12:02 -07003097 mDragTargetLayout.onDragExit();
Patrick Dubroy0207c522010-11-03 22:12:02 -07003098 }
Winson Chungc07918d2011-07-01 15:35:26 -07003099 mDragTargetLayout = layout;
3100 mDragTargetLayout.setIsDragOverlapping(true);
3101
3102 // Workspace is responsible for drawing the edge glow on adjacent pages,
3103 // so we need to redraw the workspace when this may have changed.
3104 invalidate();
Michael Jurkad718d6a2010-10-14 15:35:17 -07003105 }
Patrick Dubroy1262e362010-10-06 15:49:50 -07003106 }
3107 }
3108
3109 @Override
3110 public void onExitScrollArea() {
Michael Jurkad718d6a2010-10-14 15:35:17 -07003111 if (mInScrollArea) {
Winson Chungc07918d2011-07-01 15:35:26 -07003112 if (mDragTargetLayout != null) {
3113 // Unmark the overlapping layout and re-enter the current layout
3114 mDragTargetLayout.setIsDragOverlapping(false);
3115 mDragTargetLayout = getCurrentDropLayout();
3116 mDragTargetLayout.onDragEnter();
3117
3118 // Workspace is responsible for drawing the edge glow on adjacent pages,
3119 // so we need to redraw the workspace when this may have changed.
3120 invalidate();
3121 }
Winson Chungc07918d2011-07-01 15:35:26 -07003122 mInScrollArea = false;
Patrick Dubroy1262e362010-10-06 15:49:50 -07003123 }
3124 }
3125
Winson Chungc07918d2011-07-01 15:35:26 -07003126 private void onResetScrollArea() {
3127 if (mDragTargetLayout != null) {
3128 // Unmark the overlapping layout
3129 mDragTargetLayout.setIsDragOverlapping(false);
3130
3131 // Workspace is responsible for drawing the edge glow on adjacent pages,
3132 // so we need to redraw the workspace when this may have changed.
3133 invalidate();
3134 }
Winson Chungc07918d2011-07-01 15:35:26 -07003135 mInScrollArea = false;
3136 }
3137
Winson Chung3d503fb2011-07-13 17:25:49 -07003138 /**
3139 * Returns a specific CellLayout
3140 */
3141 CellLayout getParentCellLayoutForView(View v) {
3142 ArrayList<CellLayout> layouts = getWorkspaceAndHotseatCellLayouts();
3143 for (CellLayout layout : layouts) {
3144 if (layout.getChildrenLayout().indexOfChild(v) > -1) {
3145 return layout;
3146 }
3147 }
3148 return null;
3149 }
3150
3151 /**
3152 * Returns a list of all the CellLayouts in the workspace.
3153 */
3154 ArrayList<CellLayout> getWorkspaceAndHotseatCellLayouts() {
3155 ArrayList<CellLayout> layouts = new ArrayList<CellLayout>();
3156 int screenCount = getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003157 for (int screen = 0; screen < screenCount; screen++) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003158 layouts.add(((CellLayout) getChildAt(screen)));
3159 }
3160 if (mLauncher.getHotseat() != null) {
3161 layouts.add(mLauncher.getHotseat().getLayout());
3162 }
3163 return layouts;
3164 }
3165
3166 /**
3167 * We should only use this to search for specific children. Do not use this method to modify
3168 * CellLayoutChildren directly.
3169 */
3170 ArrayList<CellLayoutChildren> getWorkspaceAndHotseatCellLayoutChildren() {
3171 ArrayList<CellLayoutChildren> childrenLayouts = new ArrayList<CellLayoutChildren>();
3172 int screenCount = getChildCount();
3173 for (int screen = 0; screen < screenCount; screen++) {
3174 childrenLayouts.add(((CellLayout) getChildAt(screen)).getChildrenLayout());
3175 }
3176 if (mLauncher.getHotseat() != null) {
3177 childrenLayouts.add(mLauncher.getHotseat().getLayout().getChildrenLayout());
3178 }
3179 return childrenLayouts;
3180 }
3181
3182 public Folder getFolderForTag(Object tag) {
3183 ArrayList<CellLayoutChildren> childrenLayouts = getWorkspaceAndHotseatCellLayoutChildren();
3184 for (CellLayoutChildren layout: childrenLayouts) {
3185 int count = layout.getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003186 for (int i = 0; i < count; i++) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003187 View child = layout.getChildAt(i);
Winson Chung3d503fb2011-07-13 17:25:49 -07003188 if (child instanceof Folder) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003189 Folder f = (Folder) child;
Winson Chungaafa03c2010-06-11 17:34:16 -07003190 if (f.getInfo() == tag && f.getInfo().opened) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003191 return f;
3192 }
3193 }
3194 }
3195 }
3196 return null;
3197 }
3198
3199 public View getViewForTag(Object tag) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003200 ArrayList<CellLayoutChildren> childrenLayouts = getWorkspaceAndHotseatCellLayoutChildren();
3201 for (CellLayoutChildren layout: childrenLayouts) {
3202 int count = layout.getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003203 for (int i = 0; i < count; i++) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003204 View child = layout.getChildAt(i);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003205 if (child.getTag() == tag) {
3206 return child;
3207 }
3208 }
3209 }
3210 return null;
3211 }
3212
Adam Cohendf035382011-04-11 17:22:04 -07003213 void clearDropTargets() {
Winson Chung3d503fb2011-07-13 17:25:49 -07003214 ArrayList<CellLayoutChildren> childrenLayouts = getWorkspaceAndHotseatCellLayoutChildren();
3215 for (CellLayoutChildren layout: childrenLayouts) {
Adam Cohendf035382011-04-11 17:22:04 -07003216 int childCount = layout.getChildCount();
3217 for (int j = 0; j < childCount; j++) {
3218 View v = layout.getChildAt(j);
3219 if (v instanceof DropTarget) {
3220 mDragController.removeDropTarget((DropTarget) v);
3221 }
3222 }
3223 }
3224 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003225
Joe Onorato64e6be72010-03-05 15:05:52 -05003226 void removeItems(final ArrayList<ApplicationInfo> apps) {
Romain Guy629de3e2010-01-13 12:20:59 -08003227 final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext());
Romain Guy574d20e2009-06-01 15:34:04 -07003228
Joe Onorato64e6be72010-03-05 15:05:52 -05003229 final HashSet<String> packageNames = new HashSet<String>();
3230 final int appCount = apps.size();
3231 for (int i = 0; i < appCount; i++) {
3232 packageNames.add(apps.get(i).componentName.getPackageName());
3233 }
3234
Winson Chung3d503fb2011-07-13 17:25:49 -07003235 ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
3236 for (final CellLayout layoutParent: cellLayouts) {
Winson Chung7a25a9e2011-01-30 13:33:56 -08003237 final ViewGroup layout = layoutParent.getChildrenLayout();
Romain Guy574d20e2009-06-01 15:34:04 -07003238
Romain Guy629de3e2010-01-13 12:20:59 -08003239 // Avoid ANRs by treating each screen separately
3240 post(new Runnable() {
3241 public void run() {
3242 final ArrayList<View> childrenToRemove = new ArrayList<View>();
3243 childrenToRemove.clear();
Winson Chungaafa03c2010-06-11 17:34:16 -07003244
Romain Guy629de3e2010-01-13 12:20:59 -08003245 int childCount = layout.getChildCount();
3246 for (int j = 0; j < childCount; j++) {
3247 final View view = layout.getChildAt(j);
3248 Object tag = view.getTag();
Winson Chungaafa03c2010-06-11 17:34:16 -07003249
Joe Onorato0589f0f2010-02-08 13:44:00 -08003250 if (tag instanceof ShortcutInfo) {
3251 final ShortcutInfo info = (ShortcutInfo) tag;
Romain Guy629de3e2010-01-13 12:20:59 -08003252 final Intent intent = info.intent;
3253 final ComponentName name = intent.getComponent();
Winson Chungaafa03c2010-06-11 17:34:16 -07003254
Joe Onorato64e6be72010-03-05 15:05:52 -05003255 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
3256 for (String packageName: packageNames) {
3257 if (packageName.equals(name.getPackageName())) {
Joe Onorato64e6be72010-03-05 15:05:52 -05003258 LauncherModel.deleteItemFromDatabase(mLauncher, info);
3259 childrenToRemove.add(view);
3260 }
3261 }
Romain Guy629de3e2010-01-13 12:20:59 -08003262 }
Adam Cohendf2cc412011-04-27 16:56:57 -07003263 } else if (tag instanceof FolderInfo) {
3264 final FolderInfo info = (FolderInfo) tag;
Joe Onorato0589f0f2010-02-08 13:44:00 -08003265 final ArrayList<ShortcutInfo> contents = info.contents;
Romain Guy629de3e2010-01-13 12:20:59 -08003266 final int contentsCount = contents.size();
Adam Cohendf1e4e82011-06-24 15:57:39 -07003267 final ArrayList<ShortcutInfo> appsToRemoveFromFolder =
3268 new ArrayList<ShortcutInfo>();
Winson Chungaafa03c2010-06-11 17:34:16 -07003269
Romain Guy629de3e2010-01-13 12:20:59 -08003270 for (int k = 0; k < contentsCount; k++) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08003271 final ShortcutInfo appInfo = contents.get(k);
Romain Guy629de3e2010-01-13 12:20:59 -08003272 final Intent intent = appInfo.intent;
3273 final ComponentName name = intent.getComponent();
Winson Chungaafa03c2010-06-11 17:34:16 -07003274
Joe Onorato64e6be72010-03-05 15:05:52 -05003275 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
3276 for (String packageName: packageNames) {
3277 if (packageName.equals(name.getPackageName())) {
Adam Cohendf1e4e82011-06-24 15:57:39 -07003278 appsToRemoveFromFolder.add(appInfo);
Joe Onorato64e6be72010-03-05 15:05:52 -05003279 }
3280 }
Romain Guy629de3e2010-01-13 12:20:59 -08003281 }
3282 }
Adam Cohendf1e4e82011-06-24 15:57:39 -07003283 for (ShortcutInfo item: appsToRemoveFromFolder) {
3284 info.remove(item);
3285 LauncherModel.deleteItemFromDatabase(mLauncher, item);
Romain Guy629de3e2010-01-13 12:20:59 -08003286 }
Romain Guy629de3e2010-01-13 12:20:59 -08003287 } else if (tag instanceof LauncherAppWidgetInfo) {
3288 final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag;
3289 final AppWidgetProviderInfo provider =
3290 widgets.getAppWidgetInfo(info.appWidgetId);
Daniel Sandlere1cc6c32010-05-07 11:49:29 -04003291 if (provider != null) {
Joe Onorato64e6be72010-03-05 15:05:52 -05003292 for (String packageName: packageNames) {
3293 if (packageName.equals(provider.provider.getPackageName())) {
Joe Onorato64e6be72010-03-05 15:05:52 -05003294 LauncherModel.deleteItemFromDatabase(mLauncher, info);
Winson Chungaafa03c2010-06-11 17:34:16 -07003295 childrenToRemove.add(view);
Joe Onorato64e6be72010-03-05 15:05:52 -05003296 }
3297 }
Romain Guy629de3e2010-01-13 12:20:59 -08003298 }
Romain Guy574d20e2009-06-01 15:34:04 -07003299 }
3300 }
Winson Chungaafa03c2010-06-11 17:34:16 -07003301
Romain Guy629de3e2010-01-13 12:20:59 -08003302 childCount = childrenToRemove.size();
3303 for (int j = 0; j < childCount; j++) {
3304 View child = childrenToRemove.get(j);
Winson Chung7a25a9e2011-01-30 13:33:56 -08003305 // Note: We can not remove the view directly from CellLayoutChildren as this
3306 // does not re-mark the spaces as unoccupied.
3307 layoutParent.removeViewInLayout(child);
Romain Guy629de3e2010-01-13 12:20:59 -08003308 if (child instanceof DropTarget) {
3309 mDragController.removeDropTarget((DropTarget)child);
3310 }
Romain Guy574d20e2009-06-01 15:34:04 -07003311 }
Winson Chungaafa03c2010-06-11 17:34:16 -07003312
Romain Guy629de3e2010-01-13 12:20:59 -08003313 if (childCount > 0) {
3314 layout.requestLayout();
3315 layout.invalidate();
Romain Guy5c16f3e2010-01-12 17:24:58 -08003316 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003317 }
Romain Guy629de3e2010-01-13 12:20:59 -08003318 });
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003319 }
3320 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07003321
Joe Onorato64e6be72010-03-05 15:05:52 -05003322 void updateShortcuts(ArrayList<ApplicationInfo> apps) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003323 ArrayList<CellLayoutChildren> childrenLayouts = getWorkspaceAndHotseatCellLayoutChildren();
3324 for (CellLayoutChildren layout: childrenLayouts) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07003325 int childCount = layout.getChildCount();
3326 for (int j = 0; j < childCount; j++) {
3327 final View view = layout.getChildAt(j);
3328 Object tag = view.getTag();
Joe Onorato0589f0f2010-02-08 13:44:00 -08003329 if (tag instanceof ShortcutInfo) {
3330 ShortcutInfo info = (ShortcutInfo)tag;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07003331 // We need to check for ACTION_MAIN otherwise getComponent() might
3332 // return null for some shortcuts (for instance, for shortcuts to
3333 // web pages.)
3334 final Intent intent = info.intent;
3335 final ComponentName name = intent.getComponent();
3336 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
Joe Onorato64e6be72010-03-05 15:05:52 -05003337 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
3338 final int appCount = apps.size();
Winson Chungaafa03c2010-06-11 17:34:16 -07003339 for (int k = 0; k < appCount; k++) {
Joe Onorato64e6be72010-03-05 15:05:52 -05003340 ApplicationInfo app = apps.get(k);
3341 if (app.componentName.equals(name)) {
3342 info.setIcon(mIconCache.getIcon(info.intent));
3343 ((TextView)view).setCompoundDrawablesWithIntrinsicBounds(null,
3344 new FastBitmapDrawable(info.getIcon(mIconCache)),
3345 null, null);
3346 }
3347 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07003348 }
3349 }
3350 }
3351 }
3352 }
3353
Joe Onorato14f122b2009-11-19 14:06:36 -08003354 void moveToDefaultScreen(boolean animate) {
Winson Chungde1af762011-07-21 16:44:07 -07003355 if (!isSmall()) {
3356 if (animate) {
3357 snapToPage(mDefaultPage);
3358 } else {
3359 setCurrentPage(mDefaultPage);
3360 }
Joe Onoratoc45b1682010-01-11 18:48:40 -05003361 }
Michael Jurka0142d492010-08-25 17:46:15 -07003362 getChildAt(mDefaultPage).requestFocus();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003363 }
3364
Michael Jurka0142d492010-08-25 17:46:15 -07003365 @Override
3366 public void syncPages() {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003367 }
Michael Jurka0142d492010-08-25 17:46:15 -07003368
3369 @Override
Winson Chungf314b0e2011-08-16 11:54:27 -07003370 public void syncPageItems(int page, boolean immediate) {
Michael Jurka0142d492010-08-25 17:46:15 -07003371 }
Winson Chung6a0f57d2011-06-29 20:10:49 -07003372
3373 @Override
3374 protected String getCurrentPageDescription() {
3375 int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
3376 return String.format(mContext.getString(R.string.workspace_scroll_format),
3377 page + 1, getChildCount());
3378 }
Adam Cohen8dfcba42011-07-07 16:38:18 -07003379
3380 public void getLocationInDragLayer(int[] loc) {
3381 mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
3382 }
Winson Chung32174c82011-07-19 15:47:55 -07003383
Winson Chunga6427b12011-07-27 10:53:39 -07003384 void showDockDivider(boolean immediately) {
3385 final ViewGroup parent = (ViewGroup) getParent();
3386 final View qsbDivider = (ImageView) (parent.findViewById(R.id.qsb_divider));
3387 final View dockDivider = (ImageView) (parent.findViewById(R.id.dock_divider));
3388 if (qsbDivider != null && dockDivider != null) {
3389 qsbDivider.setVisibility(View.VISIBLE);
3390 dockDivider.setVisibility(View.VISIBLE);
3391 if (mDividerAnimator != null) {
3392 mDividerAnimator.cancel();
3393 mDividerAnimator = null;
3394 }
3395 if (immediately) {
3396 qsbDivider.setAlpha(1f);
3397 dockDivider.setAlpha(1f);
3398 } else {
3399 mDividerAnimator = new AnimatorSet();
3400 mDividerAnimator.playTogether(ObjectAnimator.ofFloat(qsbDivider, "alpha", 1f),
3401 ObjectAnimator.ofFloat(dockDivider, "alpha", 1f));
3402 mDividerAnimator.setDuration(sScrollIndicatorFadeInDuration);
3403 mDividerAnimator.start();
3404 }
3405 }
3406 }
3407
3408 void hideDockDivider(boolean immediately) {
3409 final ViewGroup parent = (ViewGroup) getParent();
3410 final View qsbDivider = (ImageView) (parent.findViewById(R.id.qsb_divider));
3411 final View dockDivider = (ImageView) (parent.findViewById(R.id.dock_divider));
3412 if (qsbDivider != null && dockDivider != null) {
3413 if (mDividerAnimator != null) {
3414 mDividerAnimator.cancel();
3415 mDividerAnimator = null;
3416 }
3417 if (immediately) {
3418 qsbDivider.setVisibility(View.GONE);
3419 dockDivider.setVisibility(View.GONE);
3420 qsbDivider.setAlpha(0f);
3421 dockDivider.setAlpha(0f);
3422 } else {
3423 mDividerAnimator = new AnimatorSet();
3424 mDividerAnimator.playTogether(ObjectAnimator.ofFloat(qsbDivider, "alpha", 0f),
3425 ObjectAnimator.ofFloat(dockDivider, "alpha", 0f));
3426 mDividerAnimator.addListener(new AnimatorListenerAdapter() {
3427 private boolean cancelled = false;
3428 @Override
3429 public void onAnimationCancel(android.animation.Animator animation) {
3430 cancelled = true;
3431 }
3432 @Override
3433 public void onAnimationEnd(android.animation.Animator animation) {
3434 if (!cancelled) {
3435 qsbDivider.setVisibility(View.GONE);
3436 dockDivider.setVisibility(View.GONE);
3437 }
3438 }
3439 });
3440 mDividerAnimator.setDuration(sScrollIndicatorFadeOutDuration);
3441 mDividerAnimator.start();
3442 }
3443 }
3444 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003445}