blob: ec85af3995fa754bce334205f5e4105a419fa844 [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 Cohen716b51e2011-06-30 12:09:54 -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;
Adam Powell495f2892010-04-16 16:40:55 -070036import android.content.pm.PackageManager;
Patrick Dubroy7247f632010-08-04 16:02:59 -070037import android.content.res.Resources;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080038import android.content.res.TypedArray;
Joe Onorato4be866d2010-10-10 11:26:02 -070039import android.graphics.Bitmap;
Adam Lesinski6b879f02010-11-04 16:15:23 -070040import android.graphics.Camera;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080041import android.graphics.Canvas;
Adam Cohenac8c8762011-07-13 11:15:27 -070042import android.graphics.Color;
Michael Jurkaa63c4522010-08-19 13:52:27 -070043import android.graphics.Matrix;
Winson Chunga9abd0e2010-10-27 17:18:37 -070044import android.graphics.Paint;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080045import android.graphics.Rect;
Winson Chunga9abd0e2010-10-27 17:18:37 -070046import android.graphics.RectF;
Joe Onorato4be866d2010-10-10 11:26:02 -070047import android.graphics.Region.Op;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070048import android.graphics.drawable.Drawable;
Joe Onorato956091b2010-02-19 12:47:40 -080049import android.os.IBinder;
Adam Powell495f2892010-04-16 16:40:55 -070050import android.os.Parcelable;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080051import android.util.AttributeSet;
Winson Chungb26f3d62011-06-02 10:49:29 -070052import android.util.DisplayMetrics;
Daniel Sandler291ad122010-05-24 16:03:53 -040053import android.util.Log;
Winson Chung580e2772010-11-10 16:03:00 -080054import android.util.Pair;
Winson Chunga34abf82010-11-12 12:10:35 -080055import android.view.Display;
Winson Chunga9abd0e2010-10-27 17:18:37 -070056import android.view.DragEvent;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080057import android.view.MotionEvent;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080058import android.view.View;
Winson Chung6e314082011-01-27 16:46:51 -080059import android.view.ViewGroup;
Patrick Dubroycd68ff52010-10-28 17:57:05 -070060import android.view.animation.DecelerateInterpolator;
Winson Chunga6427b12011-07-27 10:53:39 -070061import android.widget.ImageView;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070062import android.widget.TextView;
Winson Chunga9abd0e2010-10-27 17:18:37 -070063import android.widget.Toast;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080064
Adam Cohen66396872011-04-15 17:50:36 -070065import com.android.launcher.R;
Adam Cohen19072da2011-05-31 14:30:45 -070066import com.android.launcher2.FolderIcon.FolderRingAnimator;
Adam Cohen66396872011-04-15 17:50:36 -070067import com.android.launcher2.InstallWidgetReceiver.WidgetMimeTypeHandlerData;
Romain Guyedcce092010-03-04 13:03:17 -080068
Adam Cohen716b51e2011-06-30 12:09:54 -070069import java.util.ArrayList;
70import java.util.HashSet;
71import java.util.List;
72
The Android Open Source Project31dd5032009-03-03 19:32:27 -080073/**
Michael Jurka0142d492010-08-25 17:46:15 -070074 * The workspace is a wide area with a wallpaper and a finite number of pages.
75 * Each page contains a number of icons, folders or widgets the user can
Winson Chungaafa03c2010-06-11 17:34:16 -070076 * interact with. A workspace is meant to be used with a fixed width only.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080077 */
Michael Jurka0142d492010-08-25 17:46:15 -070078public class Workspace extends SmoothPagedView
Michael Jurkad74c9842011-07-10 12:44:21 -070079 implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
80 DragController.DragListener {
Romain Guye47f55c2009-11-11 19:21:22 -080081 @SuppressWarnings({"UnusedDeclaration"})
Joe Onorato3a8820b2009-11-10 15:06:42 -080082 private static final String TAG = "Launcher.Workspace";
Michael Jurka0142d492010-08-25 17:46:15 -070083
Adam Cohenf34bab52010-09-30 14:11:56 -070084 // Y rotation to apply to the workspace screens
85 private static final float WORKSPACE_ROTATION = 12.5f;
Adam Cohena985e592010-09-09 11:23:48 -070086
87 // These are extra scale factors to apply to the mini home screens
88 // so as to achieve the desired transform
Adam Cohen1b0aaac2010-10-28 11:11:18 -070089 private static final float EXTRA_SCALE_FACTOR_0 = 0.972f;
Adam Cohena985e592010-09-09 11:23:48 -070090 private static final float EXTRA_SCALE_FACTOR_1 = 1.0f;
Adam Cohen1b0aaac2010-10-28 11:11:18 -070091 private static final float EXTRA_SCALE_FACTOR_2 = 1.10f;
Adam Cohena985e592010-09-09 11:23:48 -070092
Adam Cohen68d73932010-11-15 10:50:58 -080093 private static final int CHILDREN_OUTLINE_FADE_OUT_DELAY = 0;
94 private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375;
Winson Chung9171e6d2010-11-17 17:39:27 -080095 private static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100;
Adam Cohenf34bab52010-09-30 14:11:56 -070096
Winson Chungf135c6c2010-11-18 16:32:08 -080097 private static final int BACKGROUND_FADE_OUT_DURATION = 350;
Winson Chung9171e6d2010-11-17 17:39:27 -080098 private static final int BACKGROUND_FADE_IN_DURATION = 350;
99
Adam Cohened51cc92011-08-01 20:28:08 -0700100 private static final int ADJACENT_SCREEN_DROP_DURATION = 300;
101
Winson Chung9171e6d2010-11-17 17:39:27 -0800102 // These animators are used to fade the children's outlines
103 private ObjectAnimator mChildrenOutlineFadeInAnimation;
104 private ObjectAnimator mChildrenOutlineFadeOutAnimation;
105 private float mChildrenOutlineAlpha = 0;
106
107 // These properties refer to the background protection gradient used for AllApps and Customize
Michael Jurkae0f5a612011-02-07 16:45:41 -0800108 private ValueAnimator mBackgroundFadeInAnimation;
109 private ValueAnimator mBackgroundFadeOutAnimation;
Winson Chung9171e6d2010-11-17 17:39:27 -0800110 private Drawable mBackground;
Michael Jurka25356e72011-03-03 14:53:11 -0800111 boolean mDrawBackground = true;
Adam Cohenf34bab52010-09-30 14:11:56 -0700112 private float mBackgroundAlpha = 0;
Adam Cohen68d73932010-11-15 10:50:58 -0800113 private float mOverScrollMaxBackgroundAlpha = 0.0f;
Adam Cohene0f66b52010-11-23 15:06:07 -0800114 private int mOverScrollPageIndex = -1;
Winson Chunga6427b12011-07-27 10:53:39 -0700115 private AnimatorSet mDividerAnimator;
Adam Cohenf34bab52010-09-30 14:11:56 -0700116
Dianne Hackborn8f573952009-08-10 23:21:09 -0700117 private final WallpaperManager mWallpaperManager;
Michael Jurka9c6fbed2011-03-02 17:41:34 -0800118 private IBinder mWindowToken;
Winson Chungaafa03c2010-06-11 17:34:16 -0700119
Michael Jurka0142d492010-08-25 17:46:15 -0700120 private int mDefaultPage;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800121
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800122 /**
123 * CellInfo for the cell that is currently being dragged
124 */
125 private CellLayout.CellInfo mDragInfo;
Winson Chungaafa03c2010-06-11 17:34:16 -0700126
Jeff Sharkey70864282009-04-07 21:08:40 -0700127 /**
128 * Target drop area calculated during last acceptDrop call.
129 */
Adam Cohenc0dcf592011-06-01 15:30:43 -0700130 private int[] mTargetCell = new int[2];
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800131
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700132 /**
133 * The CellLayout that is currently being dragged over
134 */
135 private CellLayout mDragTargetLayout = null;
136
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800137 private Launcher mLauncher;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800138 private IconCache mIconCache;
Joe Onorato00acb122009-08-04 16:04:30 -0400139 private DragController mDragController;
Winson Chungaafa03c2010-06-11 17:34:16 -0700140
Michael Jurka4516c112010-10-07 15:13:47 -0700141 // These are temporary variables to prevent having to allocate a new object just to
142 // 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 -0800143 private int[] mTempCell = new int[2];
Jeff Sharkey70864282009-04-07 21:08:40 -0700144 private int[] mTempEstimate = new int[2];
Adam Cohene3e27a82011-04-15 12:07:39 -0700145 private float[] mDragViewVisualCenter = new float[2];
Michael Jurkaa63c4522010-08-19 13:52:27 -0700146 private float[] mTempDragCoordinates = new float[2];
Michael Jurka4516c112010-10-07 15:13:47 -0700147 private float[] mTempCellLayoutCenterCoordinates = new float[2];
Michael Jurkaa63c4522010-08-19 13:52:27 -0700148 private float[] mTempDragBottomRightCoordinates = new float[2];
Michael Jurka0280c3b2010-09-17 15:00:07 -0700149 private Matrix mTempInverseMatrix = new Matrix();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800150
Michael Jurkac2f7f472010-12-14 15:34:42 -0800151 private SpringLoadedDragController mSpringLoadedDragController;
Winson Chungb26f3d62011-06-02 10:49:29 -0700152 private float mSpringLoadedShrinkFactor;
Michael Jurkad3ef3062010-11-23 16:23:58 -0800153
Adam Cohend22015c2010-07-26 22:02:18 -0700154 private static final int DEFAULT_CELL_COUNT_X = 4;
155 private static final int DEFAULT_CELL_COUNT_Y = 4;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800156
Patrick Dubroy1262e362010-10-06 15:49:50 -0700157 // State variable that indicates whether the pages are small (ie when you're
Michael Jurkadee05892010-07-27 10:01:56 -0700158 // in all apps or customize mode)
Michael Jurkad74c9842011-07-10 12:44:21 -0700159
160 enum State { NORMAL, SPRING_LOADED, SMALL };
161 private State mState;
162 private boolean mIsSwitchingState = false;
163
164 private boolean mSwitchStateAfterFirstLayout = false;
165 private State mStateAfterFirstLayout;
166
167 private AnimatorSet mAnimator;
Michael Jurkabea15192010-11-17 12:33:46 -0800168 private AnimatorListener mShrinkAnimationListener;
Michael Jurkab8e14472010-12-20 16:06:10 -0800169 private AnimatorListener mUnshrinkAnimationListener;
Winson Chungde1af762011-07-21 16:44:07 -0700170 // Working around the face that cancelled animations may not actually be cancelled if they
171 // are cancelled before starting
172 private boolean mShrinkAnimationEnabled;
173 private boolean mUnshrinkAnimationEnabled;
Michael Jurkad74c9842011-07-10 12:44:21 -0700174
175 boolean mAnimatingViewIntoPlace = false;
176 boolean mIsDragOccuring = false;
177 boolean mChildrenLayersEnabled = true;
Michael Jurkadee05892010-07-27 10:01:56 -0700178
Patrick Dubroy54fa3b92010-11-17 12:18:45 -0800179 /** Is the user is dragging an item near the edge of a page? */
Patrick Dubroy1262e362010-10-06 15:49:50 -0700180 private boolean mInScrollArea = false;
181
Patrick Dubroy8e58e912010-10-14 13:21:48 -0700182 private final HolographicOutlineHelper mOutlineHelper = new HolographicOutlineHelper();
Joe Onorato4be866d2010-10-10 11:26:02 -0700183 private Bitmap mDragOutline = null;
Patrick Dubroy8e58e912010-10-14 13:21:48 -0700184 private final Rect mTempRect = new Rect();
185 private final int[] mTempXY = new int[2];
Joe Onorato4be866d2010-10-10 11:26:02 -0700186
Winson Chunga9abd0e2010-10-27 17:18:37 -0700187 // Paint used to draw external drop outline
188 private final Paint mExternalDragOutlinePaint = new Paint();
189
Adam Lesinski6b879f02010-11-04 16:15:23 -0700190 // Camera and Matrix used to determine the final position of a neighboring CellLayout
191 private final Matrix mMatrix = new Matrix();
192 private final Camera mCamera = new Camera();
193 private final float mTempFloat2[] = new float[2];
194
Michael Jurkac5b262c2011-01-12 20:24:50 -0800195 enum WallpaperVerticalOffset { TOP, MIDDLE, BOTTOM };
196 int mWallpaperWidth;
197 int mWallpaperHeight;
Michael Jurkaab1983f2011-01-18 15:50:17 -0800198 WallpaperOffsetInterpolator mWallpaperOffset;
Michael Jurkac5b262c2011-01-12 20:24:50 -0800199 boolean mUpdateWallpaperOffsetImmediately = false;
Michael Jurkaab1983f2011-01-18 15:50:17 -0800200 boolean mSyncWallpaperOffsetWithScroll = true;
Adam Cohen26976d92011-03-22 15:33:33 -0700201 private Runnable mDelayedResizeRunnable;
Michael Jurkac5b262c2011-01-12 20:24:50 -0800202
Adam Cohen19072da2011-05-31 14:30:45 -0700203 // Variables relating to the creation of user folders by hovering shortcuts over shortcuts
Adam Cohenc0dcf592011-06-01 15:30:43 -0700204 private static final int FOLDER_CREATION_TIMEOUT = 250;
Adam Cohen19072da2011-05-31 14:30:45 -0700205 private final Alarm mFolderCreationAlarm = new Alarm();
206 private FolderRingAnimator mDragFolderRingAnimator = null;
207 private View mLastDragOverView = null;
208 private boolean mCreateUserFolderOnDrop = false;
Adam Cohen073a46f2011-05-17 16:28:09 -0700209
Adam Cohenf8d28232011-02-01 21:47:00 -0800210 // Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget)
211 private float mXDown;
212 private float mYDown;
213 final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
214 final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
215 final static float TOUCH_SLOP_DAMPING_FACTOR = 4;
216
Adam Cohen4b285c52011-07-21 14:24:06 -0700217 // These variables are used for storing the initial and final values during workspace animations
Adam Cohened51cc92011-08-01 20:28:08 -0700218 private int mSavedScrollX;
219 private float mSavedRotationY;
220 private float mSavedTranslationX;
Adam Cohen4b285c52011-07-21 14:24:06 -0700221 private float mCurrentScaleX;
222 private float mCurrentScaleY;
223 private float mCurrentRotationY;
224 private float mCurrentTranslationX;
225 private float mCurrentTranslationY;
226 private float[] mOldTranslationXs;
227 private float[] mOldTranslationYs;
228 private float[] mOldScaleXs;
229 private float[] mOldScaleYs;
230 private float[] mOldBackgroundAlphas;
231 private float[] mOldBackgroundAlphaMultipliers;
232 private float[] mOldAlphas;
233 private float[] mOldRotationYs;
234 private float[] mNewTranslationXs;
235 private float[] mNewTranslationYs;
236 private float[] mNewScaleXs;
237 private float[] mNewScaleYs;
238 private float[] mNewBackgroundAlphas;
239 private float[] mNewBackgroundAlphaMultipliers;
240 private float[] mNewAlphas;
241 private float[] mNewRotationYs;
242 private float mTransitionProgress;
243
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800244 /**
245 * Used to inflate the Workspace from XML.
246 *
247 * @param context The application's context.
Michael Jurka0142d492010-08-25 17:46:15 -0700248 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800249 */
250 public Workspace(Context context, AttributeSet attrs) {
251 this(context, attrs, 0);
252 }
253
254 /**
255 * Used to inflate the Workspace from XML.
256 *
257 * @param context The application's context.
Michael Jurka0142d492010-08-25 17:46:15 -0700258 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800259 * @param defStyle Unused.
260 */
261 public Workspace(Context context, AttributeSet attrs, int defStyle) {
262 super(context, attrs, defStyle);
Michael Jurka0142d492010-08-25 17:46:15 -0700263 mContentIsRefreshable = false;
Michael Jurka5f1c5092010-09-03 14:15:02 -0700264
Winson Chungf0ea4d32011-06-06 14:27:16 -0700265 // With workspace, data is available straight from the get-go
266 setDataIsReady();
267
Michael Jurkaa2eb1702011-05-12 14:57:05 -0700268 if (!LauncherApplication.isScreenLarge()) {
Winson Chung4afe9b32011-07-27 17:46:20 -0700269 mCenterPagesVertically = false;
270 if (!LauncherApplication.isScreenLandscape(context)) {
271 mFadeInAdjacentScreens = false;
272 }
Michael Jurka5f1c5092010-09-03 14:15:02 -0700273 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800274
Dianne Hackborn8f573952009-08-10 23:21:09 -0700275 mWallpaperManager = WallpaperManager.getInstance(context);
Winson Chungaafa03c2010-06-11 17:34:16 -0700276
Michael Jurkaf6440da2011-04-05 14:50:34 -0700277 int cellCountX = DEFAULT_CELL_COUNT_X;
278 int cellCountY = DEFAULT_CELL_COUNT_Y;
279
Winson Chungaafa03c2010-06-11 17:34:16 -0700280 TypedArray a = context.obtainStyledAttributes(attrs,
281 R.styleable.Workspace, defStyle, 0);
Michael Jurkaf6440da2011-04-05 14:50:34 -0700282
Winson Chungb26f3d62011-06-02 10:49:29 -0700283 final Resources res = context.getResources();
Michael Jurkaf6440da2011-04-05 14:50:34 -0700284 if (LauncherApplication.isScreenLarge()) {
Michael Jurkaf61249b2011-05-27 16:27:15 -0700285 // Determine number of rows/columns dynamically
286 // TODO: This code currently fails on tablets with an aspect ratio < 1.3.
287 // Around that ratio we should make cells the same size in portrait and
288 // landscape
Michael Jurkaf6440da2011-04-05 14:50:34 -0700289 TypedArray actionBarSizeTypedArray =
290 context.obtainStyledAttributes(new int[] { android.R.attr.actionBarSize });
Michael Jurkaf61249b2011-05-27 16:27:15 -0700291 final float actionBarHeight = actionBarSizeTypedArray.getDimension(0, 0f);
292 final float systemBarHeight = res.getDimension(R.dimen.status_bar_height);
293 final float smallestScreenDim = res.getConfiguration().smallestScreenWidthDp;
Michael Jurkaf6440da2011-04-05 14:50:34 -0700294
Michael Jurkaf61249b2011-05-27 16:27:15 -0700295 cellCountX = 1;
296 while (CellLayout.widthInPortrait(res, cellCountX + 1) <= smallestScreenDim) {
297 cellCountX++;
Michael Jurkaf6440da2011-04-05 14:50:34 -0700298 }
Michael Jurkaf6440da2011-04-05 14:50:34 -0700299
Michael Jurkaf61249b2011-05-27 16:27:15 -0700300 cellCountY = 1;
301 while (actionBarHeight + CellLayout.heightInLandscape(res, cellCountY + 1)
302 <= smallestScreenDim - systemBarHeight) {
303 cellCountY++;
Michael Jurkaf6440da2011-04-05 14:50:34 -0700304 }
Michael Jurkaf6440da2011-04-05 14:50:34 -0700305 }
306
Winson Chungb26f3d62011-06-02 10:49:29 -0700307 mSpringLoadedShrinkFactor =
308 res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
309
Michael Jurkaf6440da2011-04-05 14:50:34 -0700310 // if the value is manually specified, use that instead
311 cellCountX = a.getInt(R.styleable.Workspace_cellCountX, cellCountX);
312 cellCountY = a.getInt(R.styleable.Workspace_cellCountY, cellCountY);
Michael Jurka0142d492010-08-25 17:46:15 -0700313 mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800314 a.recycle();
315
Adam Cohend22015c2010-07-26 22:02:18 -0700316 LauncherModel.updateWorkspaceLayoutCells(cellCountX, cellCountY);
Joe Onorato0d44e942009-11-16 18:20:51 -0800317 setHapticFeedbackEnabled(false);
Michael Jurka0142d492010-08-25 17:46:15 -0700318
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800319 initWorkspace();
Winson Chungc35afb22011-02-23 13:01:49 -0800320
321 // Disable multitouch across the workspace/all apps/customize tray
322 setMotionEventSplittingEnabled(true);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800323 }
324
Michael Jurkad74c9842011-07-10 12:44:21 -0700325 public void onDragStart(DragSource source, Object info, int dragAction) {
326 mIsDragOccuring = true;
327 updateChildrenLayersEnabled();
Adam Cohen95383fe2011-07-17 18:06:55 -0700328 mLauncher.lockScreenOrientation();
Michael Jurkad74c9842011-07-10 12:44:21 -0700329 }
330
331 public void onDragEnd() {
332 mIsDragOccuring = false;
333 updateChildrenLayersEnabled();
Adam Cohen95383fe2011-07-17 18:06:55 -0700334 mLauncher.unlockScreenOrientation();
Michael Jurkad74c9842011-07-10 12:44:21 -0700335 }
336
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800337 /**
338 * Initializes various states for this workspace.
339 */
Michael Jurka0142d492010-08-25 17:46:15 -0700340 protected void initWorkspace() {
Joe Onorato0589f0f2010-02-08 13:44:00 -0800341 Context context = getContext();
Michael Jurka0142d492010-08-25 17:46:15 -0700342 mCurrentPage = mDefaultPage;
343 Launcher.setScreen(mCurrentPage);
Joe Onorato0589f0f2010-02-08 13:44:00 -0800344 LauncherApplication app = (LauncherApplication)context.getApplicationContext();
345 mIconCache = app.getIconCache();
Winson Chunga9abd0e2010-10-27 17:18:37 -0700346 mExternalDragOutlinePaint.setAntiAlias(true);
Patrick Dubroycd68ff52010-10-28 17:57:05 -0700347 setWillNotDraw(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800348
Winson Chungb4b7fa72010-11-18 14:38:53 -0800349 try {
350 final Resources res = getResources();
Winson Chungfd3385f2011-06-15 19:51:24 -0700351 mBackground = res.getDrawable(R.drawable.apps_customize_bg);
Winson Chungb4b7fa72010-11-18 14:38:53 -0800352 } catch (Resources.NotFoundException e) {
353 // In this case, we will skip drawing background protection
354 }
Winson Chung9171e6d2010-11-17 17:39:27 -0800355
Michael Jurka8edd75c2010-12-17 20:15:06 -0800356 mUnshrinkAnimationListener = new AnimatorListenerAdapter() {
Michael Jurka3c4c20f2010-10-28 15:36:06 -0700357 @Override
Michael Jurkac0e8fca2010-10-06 16:41:29 -0700358 public void onAnimationStart(Animator animation) {
Michael Jurkad74c9842011-07-10 12:44:21 -0700359 mIsSwitchingState = true;
Michael Jurkac0e8fca2010-10-06 16:41:29 -0700360 }
Michael Jurka0bb85632011-01-26 00:00:44 -0800361
Michael Jurka3c4c20f2010-10-28 15:36:06 -0700362 @Override
Michael Jurka8edd75c2010-12-17 20:15:06 -0800363 public void onAnimationEnd(Animator animation) {
Michael Jurkad74c9842011-07-10 12:44:21 -0700364 mIsSwitchingState = false;
Michael Jurkaab1983f2011-01-18 15:50:17 -0800365 mSyncWallpaperOffsetWithScroll = true;
Michael Jurkaca5b8362011-01-27 13:23:26 -0800366 mWallpaperOffset.setOverrideHorizontalCatchupConstant(false);
Patrick Dubroy60b7c532011-01-16 17:19:32 -0800367 mAnimator = null;
Michael Jurkad74c9842011-07-10 12:44:21 -0700368 updateChildrenLayersEnabled();
Michael Jurkabea15192010-11-17 12:33:46 -0800369 }
370 };
371 mShrinkAnimationListener = new AnimatorListenerAdapter() {
372 @Override
Michael Jurkace7e05f2011-02-01 22:02:35 -0800373 public void onAnimationStart(Animator animation) {
Michael Jurkad74c9842011-07-10 12:44:21 -0700374 mIsSwitchingState = true;
Michael Jurkace7e05f2011-02-01 22:02:35 -0800375 }
376 @Override
Michael Jurkabea15192010-11-17 12:33:46 -0800377 public void onAnimationEnd(Animator animation) {
Michael Jurkad74c9842011-07-10 12:44:21 -0700378 mIsSwitchingState = false;
Michael Jurkaca5b8362011-01-27 13:23:26 -0800379 mWallpaperOffset.setOverrideHorizontalCatchupConstant(false);
Patrick Dubroy60b7c532011-01-16 17:19:32 -0800380 mAnimator = null;
Michael Jurkadee05892010-07-27 10:01:56 -0700381 }
Michael Jurkadee05892010-07-27 10:01:56 -0700382 };
Michael Jurka0142d492010-08-25 17:46:15 -0700383 mSnapVelocity = 600;
Michael Jurkaab1983f2011-01-18 15:50:17 -0800384 mWallpaperOffset = new WallpaperOffsetInterpolator();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800385 }
386
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800387 @Override
Adam Cohenf34bab52010-09-30 14:11:56 -0700388 protected int getScrollMode() {
Winson Chungb26f3d62011-06-02 10:49:29 -0700389 return SmoothPagedView.X_LARGE_MODE;
Adam Cohenf34bab52010-09-30 14:11:56 -0700390 }
391
Michael Jurkae5fb0f22011-04-11 13:27:46 -0700392 private void onAddView(View child) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800393 if (!(child instanceof CellLayout)) {
394 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
395 }
Adam Cohen2801caf2011-05-13 20:57:39 -0700396 CellLayout cl = ((CellLayout) child);
397 cl.setOnInterceptTouchListener(this);
Adam Cohen2801caf2011-05-13 20:57:39 -0700398 cl.setClickable(true);
399 cl.enableHardwareLayers();
Michael Jurkae5fb0f22011-04-11 13:27:46 -0700400 }
Adam Cohen2801caf2011-05-13 20:57:39 -0700401
Michael Jurkae5fb0f22011-04-11 13:27:46 -0700402 @Override
403 public void addView(View child, int index, LayoutParams params) {
404 onAddView(child);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800405 super.addView(child, index, params);
406 }
407
408 @Override
409 public void addView(View child) {
Michael Jurkae5fb0f22011-04-11 13:27:46 -0700410 onAddView(child);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800411 super.addView(child);
412 }
413
414 @Override
415 public void addView(View child, int index) {
Michael Jurkae5fb0f22011-04-11 13:27:46 -0700416 onAddView(child);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800417 super.addView(child, index);
418 }
419
420 @Override
421 public void addView(View child, int width, int height) {
Michael Jurkae5fb0f22011-04-11 13:27:46 -0700422 onAddView(child);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800423 super.addView(child, width, height);
424 }
425
426 @Override
427 public void addView(View child, LayoutParams params) {
Michael Jurkae5fb0f22011-04-11 13:27:46 -0700428 onAddView(child);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800429 super.addView(child, params);
430 }
431
432 /**
433 * @return The open folder on the current screen, or null if there is none
434 */
435 Folder getOpenFolder() {
Adam Cohen716b51e2011-06-30 12:09:54 -0700436 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohen8e776a62011-06-28 18:10:06 -0700437 int count = dragLayer.getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800438 for (int i = 0; i < count; i++) {
Adam Cohen8e776a62011-06-28 18:10:06 -0700439 View child = dragLayer.getChildAt(i);
Winson Chungaafa03c2010-06-11 17:34:16 -0700440 if (child instanceof Folder) {
441 Folder folder = (Folder) child;
442 if (folder.getInfo().opened)
443 return folder;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800444 }
445 }
446 return null;
447 }
448
Patrick Dubroya0aa0122011-02-24 11:42:23 -0800449 boolean isTouchActive() {
450 return mTouchState != TOUCH_STATE_REST;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800451 }
452
453 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800454 * Adds the specified child in the specified screen. The position and dimension of
455 * the child are defined by x, y, spanX and spanY.
456 *
457 * @param child The child to add in one of the workspace's screens.
458 * @param screen The screen in which to add the child.
459 * @param x The X position of the child in the screen's grid.
460 * @param y The Y position of the child in the screen's grid.
461 * @param spanX The number of cells spanned horizontally by the child.
462 * @param spanY The number of cells spanned vertically by the child.
463 */
Winson Chung3d503fb2011-07-13 17:25:49 -0700464 void addInScreen(View child, long container, int screen, int x, int y, int spanX, int spanY) {
465 addInScreen(child, container, screen, x, y, spanX, spanY, false);
Winson Chungaafa03c2010-06-11 17:34:16 -0700466 }
467
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800468 /**
469 * Adds the specified child in the specified screen. The position and dimension of
470 * the child are defined by x, y, spanX and spanY.
471 *
472 * @param child The child to add in one of the workspace's screens.
473 * @param screen The screen in which to add the child.
474 * @param x The X position of the child in the screen's grid.
475 * @param y The Y position of the child in the screen's grid.
476 * @param spanX The number of cells spanned horizontally by the child.
477 * @param spanY The number of cells spanned vertically by the child.
478 * @param insert When true, the child is inserted at the beginning of the children list.
479 */
Winson Chung3d503fb2011-07-13 17:25:49 -0700480 void addInScreen(View child, long container, int screen, int x, int y, int spanX, int spanY,
481 boolean insert) {
482 if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
483 if (screen < 0 || screen >= getChildCount()) {
484 Log.e(TAG, "The screen must be >= 0 and < " + getChildCount()
485 + " (was " + screen + "); skipping child");
486 return;
487 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800488 }
489
Winson Chung3d503fb2011-07-13 17:25:49 -0700490 final CellLayout layout;
491 if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
492 layout = mLauncher.getHotseat().getLayout();
Winson Chung4d279d92011-07-21 11:46:32 -0700493 child.setOnKeyListener(null);
Winson Chung3d503fb2011-07-13 17:25:49 -0700494
495 if (screen < 0) {
496 screen = mLauncher.getHotseat().getOrderInHotseat(x, y);
497 } else {
498 // Note: We do this to ensure that the hotseat is always laid out in the orientation
499 // of the hotseat in order regardless of which orientation they were added
500 x = mLauncher.getHotseat().getCellXFromOrder(screen);
501 y = mLauncher.getHotseat().getCellYFromOrder(screen);
502 }
503 } else {
504 layout = (CellLayout) getChildAt(screen);
Winson Chung4d279d92011-07-21 11:46:32 -0700505 child.setOnKeyListener(new BubbleTextViewKeyEventListener());
Winson Chung3d503fb2011-07-13 17:25:49 -0700506 }
507
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800508 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
509 if (lp == null) {
510 lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
511 } else {
512 lp.cellX = x;
513 lp.cellY = y;
514 lp.cellHSpan = spanX;
515 lp.cellVSpan = spanY;
516 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700517
Adam Cohen7f4eabe2011-04-21 16:19:16 -0700518 if (spanX < 0 && spanY < 0) {
519 lp.isLockedToGrid = false;
520 }
521
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700522 // Get the canonical child id to uniquely represent this view in this screen
Winson Chung3d503fb2011-07-13 17:25:49 -0700523 int childId = LauncherModel.getCellLayoutChildId(container, screen, x, y, spanX, spanY);
Michael Jurkaf3ca3ab2010-10-20 17:08:24 -0700524 boolean markCellsAsOccupied = !(child instanceof Folder);
Winson Chung3d503fb2011-07-13 17:25:49 -0700525 if (!layout.addViewToCellLayout(child, insert ? 0 : -1, childId, lp, markCellsAsOccupied)) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700526 // TODO: This branch occurs when the workspace is adding views
527 // outside of the defined grid
Patrick Dubroy6569f2c2010-07-12 14:25:18 -0700528 // maybe we should be deleting these items from the LauncherModel?
Winson Chungaafa03c2010-06-11 17:34:16 -0700529 Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
530 }
531
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800532 if (!(child instanceof Folder)) {
Joe Onorato0d44e942009-11-16 18:20:51 -0800533 child.setHapticFeedbackEnabled(false);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800534 child.setOnLongClickListener(mLongClickListener);
535 }
Joe Onorato00acb122009-08-04 16:04:30 -0400536 if (child instanceof DropTarget) {
Winson Chungaafa03c2010-06-11 17:34:16 -0700537 mDragController.addDropTarget((DropTarget) child);
Joe Onorato00acb122009-08-04 16:04:30 -0400538 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800539 }
540
Patrick Dubroyd0ce1ec2011-01-19 18:47:27 -0800541 /**
542 * Check if the point (x, y) hits a given page.
543 */
544 private boolean hitsPage(int index, float x, float y) {
545 final View page = getChildAt(index);
546 if (page != null) {
547 float[] localXY = { x, y };
548 mapPointFromSelfToChild(page, localXY);
549 return (localXY[0] >= 0 && localXY[0] < page.getWidth()
550 && localXY[1] >= 0 && localXY[1] < page.getHeight());
551 }
552 return false;
553 }
554
555 @Override
556 protected boolean hitsPreviousPage(float x, float y) {
557 // mNextPage is set to INVALID_PAGE whenever we are stationary.
558 // Calculating "next page" this way ensures that you scroll to whatever page you tap on
559 final int current = (mNextPage == INVALID_PAGE) ? mCurrentPage : mNextPage;
560 return hitsPage(current - 1, x, y);
561 }
562
563 @Override
564 protected boolean hitsNextPage(float x, float y) {
565 // mNextPage is set to INVALID_PAGE whenever we are stationary.
566 // Calculating "next page" this way ensures that you scroll to whatever page you tap on
567 final int current = (mNextPage == INVALID_PAGE) ? mCurrentPage : mNextPage;
568 return hitsPage(current + 1, x, y);
569 }
570
Patrick Dubroye708c522011-03-01 16:03:43 -0800571 /**
572 * Called directly from a CellLayout (not by the framework), after we've been added as a
573 * listener via setOnInterceptTouchEventListener(). This allows us to tell the CellLayout
574 * that it should intercept touch events, which is not something that is normally supported.
575 */
576 @Override
Michael Jurkadee05892010-07-27 10:01:56 -0700577 public boolean onTouch(View v, MotionEvent event) {
Michael Jurkad74c9842011-07-10 12:44:21 -0700578 return (isSmall() || mIsSwitchingState);
Patrick Dubroye708c522011-03-01 16:03:43 -0800579 }
580
Adam Cohenfc53cd22011-07-20 15:45:11 -0700581 public boolean isSwitchingState() {
582 return mIsSwitchingState;
583 }
584
Adam Cohended9f8d2010-11-03 13:25:16 -0700585 protected void onWindowVisibilityChanged (int visibility) {
586 mLauncher.onWindowVisibilityChanged(visibility);
587 }
588
Michael Jurka5f1c5092010-09-03 14:15:02 -0700589 @Override
590 public boolean dispatchUnhandledMove(View focused, int direction) {
Michael Jurkad74c9842011-07-10 12:44:21 -0700591 if (isSmall() || mIsSwitchingState) {
Michael Jurka5f1c5092010-09-03 14:15:02 -0700592 // when the home screens are shrunken, shouldn't allow side-scrolling
593 return false;
594 }
595 return super.dispatchUnhandledMove(focused, direction);
596 }
597
598 @Override
599 public boolean onInterceptTouchEvent(MotionEvent ev) {
Adam Cohenf8d28232011-02-01 21:47:00 -0800600 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
601 mXDown = ev.getX();
602 mYDown = ev.getY();
603 }
604
Michael Jurka5f1c5092010-09-03 14:15:02 -0700605 return super.onInterceptTouchEvent(ev);
606 }
607
Michael Jurka1adf5392010-10-18 18:10:22 -0700608 @Override
609 protected void determineScrollingStart(MotionEvent ev) {
Michael Jurkad74c9842011-07-10 12:44:21 -0700610 if (!isSmall() && !mIsSwitchingState) {
Adam Cohen69c42da2011-02-03 16:38:07 -0800611 float deltaX = Math.abs(ev.getX() - mXDown);
612 float deltaY = Math.abs(ev.getY() - mYDown);
Adam Cohenf8d28232011-02-01 21:47:00 -0800613
Adam Cohen69c42da2011-02-03 16:38:07 -0800614 if (Float.compare(deltaX, 0f) == 0) return;
Adam Cohenf8d28232011-02-01 21:47:00 -0800615
Adam Cohen69c42da2011-02-03 16:38:07 -0800616 float slope = deltaY / deltaX;
617 float theta = (float) Math.atan(slope);
Adam Cohenf8d28232011-02-01 21:47:00 -0800618
Adam Cohen69c42da2011-02-03 16:38:07 -0800619 if (deltaX > mTouchSlop || deltaY > mTouchSlop) {
620 cancelCurrentPageLongPress();
621 }
Adam Cohenf8d28232011-02-01 21:47:00 -0800622
Adam Cohen69c42da2011-02-03 16:38:07 -0800623 if (theta > MAX_SWIPE_ANGLE) {
624 // Above MAX_SWIPE_ANGLE, we don't want to ever start scrolling the workspace
625 return;
626 } else if (theta > START_DAMPING_TOUCH_SLOP_ANGLE) {
627 // Above START_DAMPING_TOUCH_SLOP_ANGLE and below MAX_SWIPE_ANGLE, we want to
628 // increase the touch slop to make it harder to begin scrolling the workspace. This
629 // results in vertically scrolling widgets to more easily. The higher the angle, the
630 // more we increase touch slop.
631 theta -= START_DAMPING_TOUCH_SLOP_ANGLE;
632 float extraRatio = (float)
633 Math.sqrt((theta / (MAX_SWIPE_ANGLE - START_DAMPING_TOUCH_SLOP_ANGLE)));
634 super.determineScrollingStart(ev, 1 + TOUCH_SLOP_DAMPING_FACTOR * extraRatio);
635 } else {
636 // Below START_DAMPING_TOUCH_SLOP_ANGLE, we don't do anything special
637 super.determineScrollingStart(ev);
638 }
Adam Cohenf8d28232011-02-01 21:47:00 -0800639 }
Michael Jurka1adf5392010-10-18 18:10:22 -0700640 }
641
Winson Chung007c6982011-06-14 13:27:53 -0700642 @Override
643 protected boolean isScrollingIndicatorEnabled() {
Michael Jurkad74c9842011-07-10 12:44:21 -0700644 return mState != State.SPRING_LOADED;
Winson Chung007c6982011-06-14 13:27:53 -0700645 }
646
Patrick Dubroy1262e362010-10-06 15:49:50 -0700647 protected void onPageBeginMoving() {
Winson Chung007c6982011-06-14 13:27:53 -0700648 super.onPageBeginMoving();
649
Michael Jurkad74c9842011-07-10 12:44:21 -0700650 if (isHardwareAccelerated()) {
651 updateChildrenLayersEnabled();
Michael Jurka0142d492010-08-25 17:46:15 -0700652 } else {
Michael Jurkad74c9842011-07-10 12:44:21 -0700653 if (mNextPage != INVALID_PAGE) {
654 // we're snapping to a particular screen
655 enableChildrenCache(mCurrentPage, mNextPage);
656 } else {
657 // this is when user is actively dragging a particular screen, they might
658 // swipe it either left or right (but we won't advance by more than one screen)
659 enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1);
660 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800661 }
Winson Chung007c6982011-06-14 13:27:53 -0700662
663 // Only show page outlines as we pan if we are on large screen
664 if (LauncherApplication.isScreenLarge()) {
665 showOutlines();
666 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800667 }
668
Patrick Dubroy1262e362010-10-06 15:49:50 -0700669 protected void onPageEndMoving() {
Winson Chung007c6982011-06-14 13:27:53 -0700670 super.onPageEndMoving();
671
Michael Jurkad74c9842011-07-10 12:44:21 -0700672 if (isHardwareAccelerated()) {
673 updateChildrenLayersEnabled();
674 } else {
675 clearChildrenCache();
676 }
677
Patrick Dubroy1262e362010-10-06 15:49:50 -0700678 // Hide the outlines, as long as we're not dragging
679 if (!mDragController.dragging()) {
Winson Chung007c6982011-06-14 13:27:53 -0700680 // Only hide page outlines as we pan if we are on large screen
681 if (LauncherApplication.isScreenLarge()) {
682 hideOutlines();
683 }
Patrick Dubroy1262e362010-10-06 15:49:50 -0700684 }
Adam Cohen68d73932010-11-15 10:50:58 -0800685 mOverScrollMaxBackgroundAlpha = 0.0f;
Adam Cohene0f66b52010-11-23 15:06:07 -0800686 mOverScrollPageIndex = -1;
Adam Cohen26976d92011-03-22 15:33:33 -0700687
688 if (mDelayedResizeRunnable != null) {
689 mDelayedResizeRunnable.run();
690 mDelayedResizeRunnable = null;
691 }
Michael Jurka0142d492010-08-25 17:46:15 -0700692 }
693
694 @Override
695 protected void notifyPageSwitchListener() {
696 super.notifyPageSwitchListener();
Michael Jurka0142d492010-08-25 17:46:15 -0700697 Launcher.setScreen(mCurrentPage);
698 };
699
Michael Jurkac5b262c2011-01-12 20:24:50 -0800700 // As a ratio of screen height, the total distance we want the parallax effect to span
701 // vertically
702 private float wallpaperTravelToScreenHeightRatio(int width, int height) {
703 return 1.1f;
704 }
705
706 // As a ratio of screen height, the total distance we want the parallax effect to span
707 // horizontally
708 private float wallpaperTravelToScreenWidthRatio(int width, int height) {
709 float aspectRatio = width / (float) height;
710
711 // At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width
712 // At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width
713 // We will use these two data points to extrapolate how much the wallpaper parallax effect
714 // to span (ie travel) at any aspect ratio:
715
716 final float ASPECT_RATIO_LANDSCAPE = 16/10f;
717 final float ASPECT_RATIO_PORTRAIT = 10/16f;
718 final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f;
719 final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f;
720
721 // To find out the desired width at different aspect ratios, we use the following two
722 // formulas, where the coefficient on x is the aspect ratio (width/height):
723 // (16/10)x + y = 1.5
724 // (10/16)x + y = 1.2
725 // We solve for x and y and end up with a final formula:
726 final float x =
727 (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) /
728 (ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT);
729 final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT;
730 return x * aspectRatio + y;
731 }
732
733 // The range of scroll values for Workspace
734 private int getScrollRange() {
735 return getChildOffset(getChildCount() - 1) - getChildOffset(0);
736 }
737
738 protected void setWallpaperDimension() {
Michael Jurkac5b262c2011-01-12 20:24:50 -0800739 Display display = mLauncher.getWindowManager().getDefaultDisplay();
Winson Chung201bc822011-06-20 15:41:53 -0700740 DisplayMetrics displayMetrics = new DisplayMetrics();
741 display.getRealMetrics(displayMetrics);
742 final int maxDim = Math.max(displayMetrics.widthPixels, displayMetrics.heightPixels);
743 final int minDim = Math.min(displayMetrics.widthPixels, displayMetrics.heightPixels);
Michael Jurkac5b262c2011-01-12 20:24:50 -0800744
745 // We need to ensure that there is enough extra space in the wallpaper for the intended
746 // parallax effects
747 mWallpaperWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim));
748 mWallpaperHeight = (int)(maxDim * wallpaperTravelToScreenHeightRatio(maxDim, minDim));
Winson Chungf7640c82011-02-28 13:47:29 -0800749 new Thread("setWallpaperDimension") {
750 public void run() {
751 mWallpaperManager.suggestDesiredDimensions(mWallpaperWidth, mWallpaperHeight);
752 }
753 }.start();
Michael Jurkac5b262c2011-01-12 20:24:50 -0800754 }
755
Michael Jurkaab1983f2011-01-18 15:50:17 -0800756 public void setVerticalWallpaperOffset(float offset) {
757 mWallpaperOffset.setFinalY(offset);
758 }
759 public float getVerticalWallpaperOffset() {
760 return mWallpaperOffset.getCurrY();
761 }
762 public void setHorizontalWallpaperOffset(float offset) {
763 mWallpaperOffset.setFinalX(offset);
764 }
765 public float getHorizontalWallpaperOffset() {
766 return mWallpaperOffset.getCurrX();
Michael Jurkac5b262c2011-01-12 20:24:50 -0800767 }
768
Michael Jurkaab1983f2011-01-18 15:50:17 -0800769 private float wallpaperOffsetForCurrentScroll() {
770 Display display = mLauncher.getWindowManager().getDefaultDisplay();
Michael Jurkaf70c7812011-03-07 16:53:12 -0800771 final boolean isStaticWallpaper = (mWallpaperManager.getWallpaperInfo() == null);
Michael Jurkaab1983f2011-01-18 15:50:17 -0800772 // The wallpaper travel width is how far, from left to right, the wallpaper will move
773 // at this orientation (for example, in portrait mode we don't move all the way to the
774 // edges of the wallpaper, or otherwise the parallax effect would be too strong)
775 int wallpaperTravelWidth = (int) (display.getWidth() *
776 wallpaperTravelToScreenWidthRatio(display.getWidth(), display.getHeight()));
Michael Jurkaf70c7812011-03-07 16:53:12 -0800777 if (!isStaticWallpaper) {
778 wallpaperTravelWidth = mWallpaperWidth;
779 }
Michael Jurkaab1983f2011-01-18 15:50:17 -0800780
Michael Jurkaab1983f2011-01-18 15:50:17 -0800781 // Set wallpaper offset steps (1 / (number of screens - 1))
782 // We have 3 vertical offset states (centered, and then top/bottom aligned
783 // for all apps/customize)
784 mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 1.0f / (3 - 1));
785
Michael Jurka09d6eb02011-01-28 16:05:09 -0800786 int scrollRange = getScrollRange();
787 float scrollProgressOffset = 0;
788
789 // Account for overscroll: you only see the absolute edge of the wallpaper if
790 // you overscroll as far as you can in landscape mode. Only do this for static wallpapers
791 // because live wallpapers (and probably 3rd party wallpaper providers) rely on the offset
792 // being even intervals from 0 to 1 (eg [0, 0.25, 0.5, 0.75, 1])
Michael Jurka09d6eb02011-01-28 16:05:09 -0800793 if (isStaticWallpaper) {
794 int overscrollOffset = (int) (maxOverScroll() * display.getWidth());
795 scrollProgressOffset += overscrollOffset / (float) getScrollRange();
796 scrollRange += 2 * overscrollOffset;
797 }
798
Michael Jurkaab1983f2011-01-18 15:50:17 -0800799 float scrollProgress =
Michael Jurka09d6eb02011-01-28 16:05:09 -0800800 mScrollX / (float) scrollRange + scrollProgressOffset;
Michael Jurkaab1983f2011-01-18 15:50:17 -0800801 float offsetInDips = wallpaperTravelWidth * scrollProgress +
Michael Jurkaf70c7812011-03-07 16:53:12 -0800802 (mWallpaperWidth - wallpaperTravelWidth) / 2; // center it
Michael Jurkaab1983f2011-01-18 15:50:17 -0800803 float offset = offsetInDips / (float) mWallpaperWidth;
804 return offset;
805 }
806 private void syncWallpaperOffsetWithScroll() {
Winson Chungb46a2d12011-04-22 14:13:05 -0700807 final boolean enableWallpaperEffects = isHardwareAccelerated();
808 if (enableWallpaperEffects) {
Michael Jurkaab1983f2011-01-18 15:50:17 -0800809 mWallpaperOffset.setFinalX(wallpaperOffsetForCurrentScroll());
Michael Jurkac5b262c2011-01-12 20:24:50 -0800810 }
811 }
812
813 public void updateWallpaperOffsetImmediately() {
814 mUpdateWallpaperOffsetImmediately = true;
815 }
816
Michael Jurkaab1983f2011-01-18 15:50:17 -0800817 private void updateWallpaperOffsets() {
818 boolean updateNow = false;
819 boolean keepUpdating = true;
820 if (mUpdateWallpaperOffsetImmediately) {
821 updateNow = true;
822 keepUpdating = false;
823 mWallpaperOffset.jumpToFinal();
824 mUpdateWallpaperOffsetImmediately = false;
Michael Jurkac5b262c2011-01-12 20:24:50 -0800825 } else {
Michael Jurkaab1983f2011-01-18 15:50:17 -0800826 updateNow = keepUpdating = mWallpaperOffset.computeScrollOffset();
Michael Jurkac5b262c2011-01-12 20:24:50 -0800827 }
Michael Jurkaab1983f2011-01-18 15:50:17 -0800828 if (updateNow) {
Michael Jurka9c6fbed2011-03-02 17:41:34 -0800829 if (mWindowToken != null) {
830 mWallpaperManager.setWallpaperOffsets(mWindowToken,
Michael Jurkaab1983f2011-01-18 15:50:17 -0800831 mWallpaperOffset.getCurrX(), mWallpaperOffset.getCurrY());
832 }
Michael Jurkac5b262c2011-01-12 20:24:50 -0800833 }
Michael Jurkaab1983f2011-01-18 15:50:17 -0800834 if (keepUpdating) {
Michael Jurka2763be32011-02-24 11:19:57 -0800835 fastInvalidate();
Michael Jurkac5b262c2011-01-12 20:24:50 -0800836 }
Michael Jurkaab1983f2011-01-18 15:50:17 -0800837 }
838
839 class WallpaperOffsetInterpolator {
840 float mFinalHorizontalWallpaperOffset = 0.0f;
841 float mFinalVerticalWallpaperOffset = 0.5f;
842 float mHorizontalWallpaperOffset = 0.0f;
843 float mVerticalWallpaperOffset = 0.5f;
844 long mLastWallpaperOffsetUpdateTime;
Michael Jurkaca5b8362011-01-27 13:23:26 -0800845 boolean mIsMovingFast;
846 boolean mOverrideHorizontalCatchupConstant;
847 float mHorizontalCatchupConstant = 0.35f;
848 float mVerticalCatchupConstant = 0.35f;
Michael Jurkaab1983f2011-01-18 15:50:17 -0800849
850 public WallpaperOffsetInterpolator() {
851 }
852
Michael Jurkaca5b8362011-01-27 13:23:26 -0800853 public void setOverrideHorizontalCatchupConstant(boolean override) {
854 mOverrideHorizontalCatchupConstant = override;
855 }
856
857 public void setHorizontalCatchupConstant(float f) {
858 mHorizontalCatchupConstant = f;
859 }
860
861 public void setVerticalCatchupConstant(float f) {
862 mVerticalCatchupConstant = f;
863 }
864
Michael Jurkaab1983f2011-01-18 15:50:17 -0800865 public boolean computeScrollOffset() {
866 if (Float.compare(mHorizontalWallpaperOffset, mFinalHorizontalWallpaperOffset) == 0 &&
867 Float.compare(mVerticalWallpaperOffset, mFinalVerticalWallpaperOffset) == 0) {
Michael Jurkaca5b8362011-01-27 13:23:26 -0800868 mIsMovingFast = false;
Michael Jurkaab1983f2011-01-18 15:50:17 -0800869 return false;
870 }
871 Display display = mLauncher.getWindowManager().getDefaultDisplay();
872 boolean isLandscape = display.getWidth() > display.getHeight();
873
874 long currentTime = System.currentTimeMillis();
875 long timeSinceLastUpdate = currentTime - mLastWallpaperOffsetUpdateTime;
876 timeSinceLastUpdate = Math.min((long) (1000/30f), timeSinceLastUpdate);
877 timeSinceLastUpdate = Math.max(1L, timeSinceLastUpdate);
Michael Jurkaca5b8362011-01-27 13:23:26 -0800878
879 float xdiff = Math.abs(mFinalHorizontalWallpaperOffset - mHorizontalWallpaperOffset);
880 if (!mIsMovingFast && xdiff > 0.07) {
881 mIsMovingFast = true;
882 }
883
884 float fractionToCatchUpIn1MsHorizontal;
885 if (mOverrideHorizontalCatchupConstant) {
886 fractionToCatchUpIn1MsHorizontal = mHorizontalCatchupConstant;
887 } else if (mIsMovingFast) {
888 fractionToCatchUpIn1MsHorizontal = isLandscape ? 0.5f : 0.75f;
889 } else {
890 // slow
891 fractionToCatchUpIn1MsHorizontal = isLandscape ? 0.27f : 0.5f;
892 }
893 float fractionToCatchUpIn1MsVertical = mVerticalCatchupConstant;
894
Michael Jurkaca5b8362011-01-27 13:23:26 -0800895 fractionToCatchUpIn1MsHorizontal /= 33f;
896 fractionToCatchUpIn1MsVertical /= 33f;
Michael Jurkaab1983f2011-01-18 15:50:17 -0800897
898 final float UPDATE_THRESHOLD = 0.00001f;
899 float hOffsetDelta = mFinalHorizontalWallpaperOffset - mHorizontalWallpaperOffset;
900 float vOffsetDelta = mFinalVerticalWallpaperOffset - mVerticalWallpaperOffset;
Michael Jurkaca5b8362011-01-27 13:23:26 -0800901 boolean jumpToFinalValue = Math.abs(hOffsetDelta) < UPDATE_THRESHOLD &&
902 Math.abs(vOffsetDelta) < UPDATE_THRESHOLD;
Michael Jurkaab1983f2011-01-18 15:50:17 -0800903 if (jumpToFinalValue) {
904 mHorizontalWallpaperOffset = mFinalHorizontalWallpaperOffset;
905 mVerticalWallpaperOffset = mFinalVerticalWallpaperOffset;
906 } else {
907 float percentToCatchUpVertical =
Michael Jurkaca5b8362011-01-27 13:23:26 -0800908 Math.min(1.0f, timeSinceLastUpdate * fractionToCatchUpIn1MsVertical);
Michael Jurkaab1983f2011-01-18 15:50:17 -0800909 float percentToCatchUpHorizontal =
Michael Jurkaca5b8362011-01-27 13:23:26 -0800910 Math.min(1.0f, timeSinceLastUpdate * fractionToCatchUpIn1MsHorizontal);
Michael Jurkaab1983f2011-01-18 15:50:17 -0800911 mHorizontalWallpaperOffset += percentToCatchUpHorizontal * hOffsetDelta;
912 mVerticalWallpaperOffset += percentToCatchUpVertical * vOffsetDelta;
913 }
Michael Jurkaca5b8362011-01-27 13:23:26 -0800914
Michael Jurkaab1983f2011-01-18 15:50:17 -0800915 mLastWallpaperOffsetUpdateTime = System.currentTimeMillis();
916 return true;
917 }
918
919 public float getCurrX() {
920 return mHorizontalWallpaperOffset;
921 }
922
923 public float getFinalX() {
924 return mFinalHorizontalWallpaperOffset;
925 }
926
927 public float getCurrY() {
928 return mVerticalWallpaperOffset;
929 }
930
931 public float getFinalY() {
932 return mFinalVerticalWallpaperOffset;
933 }
934
935 public void setFinalX(float x) {
936 mFinalHorizontalWallpaperOffset = Math.max(0f, Math.min(x, 1.0f));
937 }
938
939 public void setFinalY(float y) {
940 mFinalVerticalWallpaperOffset = Math.max(0f, Math.min(y, 1.0f));
941 }
942
943 public void jumpToFinal() {
944 mHorizontalWallpaperOffset = mFinalHorizontalWallpaperOffset;
945 mVerticalWallpaperOffset = mFinalVerticalWallpaperOffset;
946 }
Dianne Hackborn8f573952009-08-10 23:21:09 -0700947 }
Winson Chungaafa03c2010-06-11 17:34:16 -0700948
Michael Jurka340c5f32010-10-21 16:49:19 -0700949 @Override
950 public void computeScroll() {
951 super.computeScroll();
Michael Jurkaab1983f2011-01-18 15:50:17 -0800952 if (mSyncWallpaperOffsetWithScroll) {
953 syncWallpaperOffsetWithScroll();
954 }
Michael Jurka340c5f32010-10-21 16:49:19 -0700955 }
956
Patrick Dubroy94f78a52011-02-28 17:39:16 -0800957 void showOutlines() {
Michael Jurkad74c9842011-07-10 12:44:21 -0700958 if (!isSmall() && !mIsSwitchingState) {
Winson Chung9171e6d2010-11-17 17:39:27 -0800959 if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
960 if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
961 mChildrenOutlineFadeInAnimation = ObjectAnimator.ofFloat(this, "childrenOutlineAlpha", 1.0f);
962 mChildrenOutlineFadeInAnimation.setDuration(CHILDREN_OUTLINE_FADE_IN_DURATION);
963 mChildrenOutlineFadeInAnimation.start();
Michael Jurka3e7c7632010-10-02 16:01:03 -0700964 }
Adam Cohenf34bab52010-09-30 14:11:56 -0700965 }
966
Patrick Dubroy94f78a52011-02-28 17:39:16 -0800967 void hideOutlines() {
Michael Jurkad74c9842011-07-10 12:44:21 -0700968 if (!isSmall() && !mIsSwitchingState) {
Winson Chung9171e6d2010-11-17 17:39:27 -0800969 if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
970 if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
971 mChildrenOutlineFadeOutAnimation = ObjectAnimator.ofFloat(this, "childrenOutlineAlpha", 0.0f);
972 mChildrenOutlineFadeOutAnimation.setDuration(CHILDREN_OUTLINE_FADE_OUT_DURATION);
973 mChildrenOutlineFadeOutAnimation.setStartDelay(CHILDREN_OUTLINE_FADE_OUT_DELAY);
974 mChildrenOutlineFadeOutAnimation.start();
Michael Jurka3e7c7632010-10-02 16:01:03 -0700975 }
Adam Cohenf34bab52010-09-30 14:11:56 -0700976 }
977
Patrick Dubroy94f78a52011-02-28 17:39:16 -0800978 public void showOutlinesTemporarily() {
979 if (!mIsPageMoving && !isTouchActive()) {
980 snapToPage(mCurrentPage);
981 }
982 }
983
Winson Chung9171e6d2010-11-17 17:39:27 -0800984 public void setChildrenOutlineAlpha(float alpha) {
985 mChildrenOutlineAlpha = alpha;
Adam Cohenf34bab52010-09-30 14:11:56 -0700986 for (int i = 0; i < getChildCount(); i++) {
987 CellLayout cl = (CellLayout) getChildAt(i);
988 cl.setBackgroundAlpha(alpha);
989 }
990 }
991
Winson Chung9171e6d2010-11-17 17:39:27 -0800992 public float getChildrenOutlineAlpha() {
993 return mChildrenOutlineAlpha;
994 }
995
Michael Jurka9c6fbed2011-03-02 17:41:34 -0800996 void disableBackground() {
997 mDrawBackground = false;
998 }
999 void enableBackground() {
1000 mDrawBackground = true;
1001 }
1002
Michael Jurkab9e14972011-07-25 17:57:40 -07001003 private void showBackgroundGradientForAllApps(boolean animated) {
1004 showBackgroundGradient(animated);
Winson Chung8d006d52010-11-29 15:55:29 -08001005 }
1006
Michael Jurkab9e14972011-07-25 17:57:40 -07001007 private void showBackgroundGradient(boolean animated) {
Winson Chungb4b7fa72010-11-18 14:38:53 -08001008 if (mBackground == null) return;
Michael Jurkab9e14972011-07-25 17:57:40 -07001009 if (mBackgroundFadeOutAnimation != null) {
1010 mBackgroundFadeOutAnimation.cancel();
1011 mBackgroundFadeOutAnimation = null;
1012 }
1013 if (mBackgroundFadeInAnimation != null) {
1014 mBackgroundFadeInAnimation.cancel();
1015 mBackgroundFadeInAnimation = null;
1016 }
1017 final float finalAlpha = 1f;
1018 if (animated) {
1019 mBackgroundFadeInAnimation = ValueAnimator.ofFloat(getBackgroundAlpha(), finalAlpha);
1020 mBackgroundFadeInAnimation.addUpdateListener(new AnimatorUpdateListener() {
1021 public void onAnimationUpdate(ValueAnimator animation) {
1022 setBackgroundAlpha(((Float) animation.getAnimatedValue()).floatValue());
1023 }
1024 });
1025 mBackgroundFadeInAnimation.setInterpolator(new DecelerateInterpolator(1.5f));
1026 mBackgroundFadeInAnimation.setDuration(BACKGROUND_FADE_IN_DURATION);
1027 mBackgroundFadeInAnimation.start();
1028 } else {
1029 setBackgroundAlpha(finalAlpha);
1030 }
Winson Chung9171e6d2010-11-17 17:39:27 -08001031 }
1032
Michael Jurkab9e14972011-07-25 17:57:40 -07001033 private void hideBackgroundGradient(float finalAlpha, boolean animated) {
Winson Chungb4b7fa72010-11-18 14:38:53 -08001034 if (mBackground == null) return;
Michael Jurkab9e14972011-07-25 17:57:40 -07001035 if (mBackgroundFadeInAnimation != null) {
1036 mBackgroundFadeInAnimation.cancel();
1037 mBackgroundFadeInAnimation = null;
1038 }
1039 if (mBackgroundFadeOutAnimation != null) {
1040 mBackgroundFadeOutAnimation.cancel();
1041 mBackgroundFadeOutAnimation = null;
1042 }
1043 if (animated) {
1044 mBackgroundFadeOutAnimation = ValueAnimator.ofFloat(getBackgroundAlpha(), finalAlpha);
1045 mBackgroundFadeOutAnimation.addUpdateListener(new AnimatorUpdateListener() {
1046 public void onAnimationUpdate(ValueAnimator animation) {
1047 setBackgroundAlpha(((Float) animation.getAnimatedValue()).floatValue());
1048 }
1049 });
1050 mBackgroundFadeOutAnimation.setInterpolator(new DecelerateInterpolator(1.5f));
1051 mBackgroundFadeOutAnimation.setDuration(BACKGROUND_FADE_OUT_DURATION);
1052 mBackgroundFadeOutAnimation.start();
1053 } else {
1054 setBackgroundAlpha(finalAlpha);
1055 }
Winson Chung9171e6d2010-11-17 17:39:27 -08001056 }
1057
1058 public void setBackgroundAlpha(float alpha) {
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001059 if (alpha != mBackgroundAlpha) {
1060 mBackgroundAlpha = alpha;
1061 invalidate();
1062 }
Winson Chung9171e6d2010-11-17 17:39:27 -08001063 }
1064
Adam Cohenf34bab52010-09-30 14:11:56 -07001065 public float getBackgroundAlpha() {
1066 return mBackgroundAlpha;
1067 }
1068
Adam Lesinski6b879f02010-11-04 16:15:23 -07001069 /**
1070 * Due to 3D transformations, if two CellLayouts are theoretically touching each other,
1071 * on the xy plane, when one is rotated along the y-axis, the gap between them is perceived
1072 * as being larger. This method computes what offset the rotated view should be translated
1073 * in order to minimize this perceived gap.
1074 * @param degrees Angle of the view
1075 * @param width Width of the view
1076 * @param height Height of the view
1077 * @return Offset to be used in a View.setTranslationX() call
1078 */
1079 private float getOffsetXForRotation(float degrees, int width, int height) {
1080 mMatrix.reset();
1081 mCamera.save();
1082 mCamera.rotateY(Math.abs(degrees));
1083 mCamera.getMatrix(mMatrix);
1084 mCamera.restore();
1085
1086 mMatrix.preTranslate(-width * 0.5f, -height * 0.5f);
1087 mMatrix.postTranslate(width * 0.5f, height * 0.5f);
1088 mTempFloat2[0] = width;
1089 mTempFloat2[1] = height;
1090 mMatrix.mapPoints(mTempFloat2);
1091 return (width - mTempFloat2[0]) * (degrees > 0.0f ? 1.0f : -1.0f);
1092 }
1093
Adam Cohen68d73932010-11-15 10:50:58 -08001094 float backgroundAlphaInterpolator(float r) {
1095 float pivotA = 0.1f;
1096 float pivotB = 0.4f;
1097 if (r < pivotA) {
1098 return 0;
1099 } else if (r > pivotB) {
1100 return 1.0f;
1101 } else {
1102 return (r - pivotA)/(pivotB - pivotA);
1103 }
1104 }
1105
1106 float overScrollBackgroundAlphaInterpolator(float r) {
Adam Cohene0f66b52010-11-23 15:06:07 -08001107 float threshold = 0.08f;
Adam Cohen68d73932010-11-15 10:50:58 -08001108
1109 if (r > mOverScrollMaxBackgroundAlpha) {
1110 mOverScrollMaxBackgroundAlpha = r;
1111 } else if (r < mOverScrollMaxBackgroundAlpha) {
1112 r = mOverScrollMaxBackgroundAlpha;
1113 }
1114
1115 return Math.min(r / threshold, 1.0f);
1116 }
1117
Adam Cohenf34bab52010-09-30 14:11:56 -07001118 @Override
1119 protected void screenScrolled(int screenCenter) {
Winson Chung007c6982011-06-14 13:27:53 -07001120 super.screenScrolled(screenCenter);
1121
Adam Cohenc3bc2602011-03-30 11:49:12 -07001122 // If the screen is not xlarge, then don't rotate the CellLayouts
Winson Chung785d2eb2011-04-14 16:08:02 -07001123 // NOTE: If we don't update the side pages alpha, then we should not hide the side pages.
1124 // see unshrink().
Michael Jurkaa2eb1702011-05-12 14:57:05 -07001125 if (!LauncherApplication.isScreenLarge()) return;
Adam Cohenc3bc2602011-03-30 11:49:12 -07001126
Adam Cohen21f12b52010-10-07 17:15:37 -07001127 final int halfScreenSize = getMeasuredWidth() / 2;
Adam Cohen68d73932010-11-15 10:50:58 -08001128
Adam Cohen21f12b52010-10-07 17:15:37 -07001129 for (int i = 0; i < getChildCount(); i++) {
Adam Cohen1b0aaac2010-10-28 11:11:18 -07001130 CellLayout cl = (CellLayout) getChildAt(i);
1131 if (cl != null) {
Michael Jurkad3ef3062010-11-23 16:23:58 -08001132 int totalDistance = getScaledMeasuredWidth(cl) + mPageSpacing;
Adam Cohen21f12b52010-10-07 17:15:37 -07001133 int delta = screenCenter - (getChildOffset(i) -
1134 getRelativeChildOffset(i) + halfScreenSize);
Adam Cohenf34bab52010-09-30 14:11:56 -07001135
Adam Cohenddb82192010-11-10 16:32:54 -08001136 float scrollProgress = delta / (totalDistance * 1.0f);
Adam Cohen21f12b52010-10-07 17:15:37 -07001137 scrollProgress = Math.min(scrollProgress, 1.0f);
1138 scrollProgress = Math.max(scrollProgress, -1.0f);
Adam Cohenf34bab52010-09-30 14:11:56 -07001139
Michael Jurka7cfc2822011-08-02 20:19:24 -07001140 if (mState != State.SPRING_LOADED) {
1141 // If the current page (i) is being overscrolled, we use a different
1142 // set of rules for setting the background alpha multiplier.
1143 if ((mScrollX < 0 && i == 0) || (mScrollX > mMaxScrollX &&
1144 i == getChildCount() -1 )) {
1145 cl.setBackgroundAlphaMultiplier(
1146 overScrollBackgroundAlphaInterpolator(Math.abs(scrollProgress)));
1147 mOverScrollPageIndex = i;
1148 } else if (mOverScrollPageIndex != i) {
1149 cl.setBackgroundAlphaMultiplier(
1150 backgroundAlphaInterpolator(Math.abs(scrollProgress)));
1151 }
Adam Cohene0f66b52010-11-23 15:06:07 -08001152 }
Adam Cohen1b0aaac2010-10-28 11:11:18 -07001153
Adam Cohen21f12b52010-10-07 17:15:37 -07001154 float rotation = WORKSPACE_ROTATION * scrollProgress;
Adam Lesinski6b879f02010-11-04 16:15:23 -07001155 float translationX = getOffsetXForRotation(rotation, cl.getWidth(), cl.getHeight());
1156 cl.setTranslationX(translationX);
Adam Cohen68d73932010-11-15 10:50:58 -08001157
Adam Cohen1b0aaac2010-10-28 11:11:18 -07001158 cl.setRotationY(rotation);
Adam Cohenf34bab52010-09-30 14:11:56 -07001159 }
1160 }
1161 }
1162
Joe Onorato00acb122009-08-04 16:04:30 -04001163 protected void onAttachedToWindow() {
1164 super.onAttachedToWindow();
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001165 mWindowToken = getWindowToken();
Joe Onorato956091b2010-02-19 12:47:40 -08001166 computeScroll();
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001167 mDragController.setWindowToken(mWindowToken);
1168 }
1169
1170 protected void onDetachedFromWindow() {
1171 mWindowToken = null;
Joe Onorato00acb122009-08-04 16:04:30 -04001172 }
1173
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001174 @Override
Michael Jurka28750fb2010-09-24 17:43:49 -07001175 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Michael Jurkac5b262c2011-01-12 20:24:50 -08001176 if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
1177 mUpdateWallpaperOffsetImmediately = true;
1178 }
Michael Jurka0142d492010-08-25 17:46:15 -07001179 super.onLayout(changed, left, top, right, bottom);
Winson Chungaafa03c2010-06-11 17:34:16 -07001180
Michael Jurka79212d82010-07-30 16:36:20 -07001181 // if shrinkToBottom() is called on initialization, it has to be deferred
1182 // until after the first call to onLayout so that it has the correct width
Michael Jurkad74c9842011-07-10 12:44:21 -07001183 if (mSwitchStateAfterFirstLayout) {
1184 mSwitchStateAfterFirstLayout = false;
Adam Cohen696a6792010-12-09 11:46:38 -08001185 // shrink can trigger a synchronous onLayout call, so we
1186 // post this to avoid a stack overflow / tangled onLayout calls
1187 post(new Runnable() {
1188 public void run() {
Michael Jurkad74c9842011-07-10 12:44:21 -07001189 shrink(mStateAfterFirstLayout, false);
Adam Cohen696a6792010-12-09 11:46:38 -08001190 }
1191 });
Michael Jurka79212d82010-07-30 16:36:20 -07001192 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001193 }
1194
1195 @Override
Winson Chung9171e6d2010-11-17 17:39:27 -08001196 protected void onDraw(Canvas canvas) {
Michael Jurkaab1983f2011-01-18 15:50:17 -08001197 updateWallpaperOffsets();
Michael Jurkac5b262c2011-01-12 20:24:50 -08001198
Winson Chung9171e6d2010-11-17 17:39:27 -08001199 // Draw the background gradient if necessary
Michael Jurka9c6fbed2011-03-02 17:41:34 -08001200 if (mBackground != null && mBackgroundAlpha > 0.0f && mDrawBackground) {
Winson Chung8d006d52010-11-29 15:55:29 -08001201 int alpha = (int) (mBackgroundAlpha * 255);
Winson Chungf0ea4d32011-06-06 14:27:16 -07001202 mBackground.setAlpha(alpha);
1203 mBackground.setBounds(mScrollX, 0, mScrollX + getMeasuredWidth(),
1204 getMeasuredHeight());
1205 mBackground.draw(canvas);
Winson Chung9171e6d2010-11-17 17:39:27 -08001206 }
Adam Cohen073a46f2011-05-17 16:28:09 -07001207
Winson Chung9171e6d2010-11-17 17:39:27 -08001208 super.onDraw(canvas);
1209 }
1210
1211 @Override
Michael Jurka0142d492010-08-25 17:46:15 -07001212 protected void dispatchDraw(Canvas canvas) {
Winson Chung557d6ed2011-07-08 15:34:52 -07001213 super.dispatchDraw(canvas);
Patrick Dubroycd68ff52010-10-28 17:57:05 -07001214
Winson Chung557d6ed2011-07-08 15:34:52 -07001215 if (mInScrollArea && !LauncherApplication.isScreenLarge()) {
1216 final int width = getWidth();
1217 final int height = getHeight();
1218 final int pageHeight = getChildAt(0).getHeight();
Patrick Dubroy0207c522010-11-03 22:12:02 -07001219
Winson Chung557d6ed2011-07-08 15:34:52 -07001220 // This determines the height of the glowing edge: 90% of the page height
1221 final int padding = (int) ((height - pageHeight) * 0.5f + pageHeight * 0.1f);
Patrick Dubroy0207c522010-11-03 22:12:02 -07001222
Winson Chung557d6ed2011-07-08 15:34:52 -07001223 final CellLayout leftPage = (CellLayout) getChildAt(mCurrentPage - 1);
1224 final CellLayout rightPage = (CellLayout) getChildAt(mCurrentPage + 1);
Patrick Dubroy0207c522010-11-03 22:12:02 -07001225
Winson Chung557d6ed2011-07-08 15:34:52 -07001226 if (leftPage != null && leftPage.getIsDragOverlapping()) {
1227 final Drawable d = getResources().getDrawable(R.drawable.page_hover_left_holo);
1228 d.setBounds(mScrollX, padding, mScrollX + d.getIntrinsicWidth(), height - padding);
1229 d.draw(canvas);
1230 } else if (rightPage != null && rightPage.getIsDragOverlapping()) {
1231 final Drawable d = getResources().getDrawable(R.drawable.page_hover_right_holo);
1232 d.setBounds(mScrollX + width - d.getIntrinsicWidth(), padding, mScrollX + width, height - padding);
1233 d.draw(canvas);
Patrick Dubroy0207c522010-11-03 22:12:02 -07001234 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001235 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001236 }
1237
1238 @Override
1239 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
Joe Onorato67886212009-09-14 19:05:05 -04001240 if (!mLauncher.isAllAppsVisible()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001241 final Folder openFolder = getOpenFolder();
1242 if (openFolder != null) {
1243 return openFolder.requestFocus(direction, previouslyFocusedRect);
1244 } else {
Michael Jurka0142d492010-08-25 17:46:15 -07001245 return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001246 }
1247 }
1248 return false;
1249 }
1250
1251 @Override
Winson Chung97d85d22011-04-13 11:27:36 -07001252 public int getDescendantFocusability() {
Michael Jurkad74c9842011-07-10 12:44:21 -07001253 if (isSmall()) {
Winson Chung97d85d22011-04-13 11:27:36 -07001254 return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
1255 }
1256 return super.getDescendantFocusability();
1257 }
1258
1259 @Override
Romain Guyc2e24c02009-06-01 16:11:41 -07001260 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
Joe Onorato67886212009-09-14 19:05:05 -04001261 if (!mLauncher.isAllAppsVisible()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001262 final Folder openFolder = getOpenFolder();
Michael Jurka0142d492010-08-25 17:46:15 -07001263 if (openFolder != null) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001264 openFolder.addFocusables(views, direction);
Michael Jurka0142d492010-08-25 17:46:15 -07001265 } else {
1266 super.addFocusables(views, direction, focusableMode);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001267 }
1268 }
1269 }
1270
Michael Jurkad74c9842011-07-10 12:44:21 -07001271 public boolean isSmall() {
Adam Cohen3384c7b2011-07-17 15:48:21 -07001272 return mState == State.SMALL || mState == State.SPRING_LOADED;
Michael Jurkad74c9842011-07-10 12:44:21 -07001273 }
1274
Michael Jurka0142d492010-08-25 17:46:15 -07001275 void enableChildrenCache(int fromPage, int toPage) {
1276 if (fromPage > toPage) {
1277 final int temp = fromPage;
1278 fromPage = toPage;
1279 toPage = temp;
Mike Cleron3a2b3f22009-11-05 17:17:42 -08001280 }
Winson Chungaafa03c2010-06-11 17:34:16 -07001281
Michael Jurkadee05892010-07-27 10:01:56 -07001282 final int screenCount = getChildCount();
Adam Powellfea5d022010-04-29 11:42:45 -07001283
Michael Jurka0142d492010-08-25 17:46:15 -07001284 fromPage = Math.max(fromPage, 0);
1285 toPage = Math.min(toPage, screenCount - 1);
Adam Powellfea5d022010-04-29 11:42:45 -07001286
Michael Jurka0142d492010-08-25 17:46:15 -07001287 for (int i = fromPage; i <= toPage; i++) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001288 final CellLayout layout = (CellLayout) getChildAt(i);
1289 layout.setChildrenDrawnWithCacheEnabled(true);
1290 layout.setChildrenDrawingCacheEnabled(true);
1291 }
1292 }
1293
1294 void clearChildrenCache() {
Michael Jurkadee05892010-07-27 10:01:56 -07001295 final int screenCount = getChildCount();
1296 for (int i = 0; i < screenCount; i++) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001297 final CellLayout layout = (CellLayout) getChildAt(i);
1298 layout.setChildrenDrawnWithCacheEnabled(false);
Adam Cohen8182e5b2011-08-01 15:43:31 -07001299 // In software mode, we don't want the items to continue to be drawn into bitmaps
1300 if (!isHardwareAccelerated()) {
1301 layout.setChildrenDrawingCacheEnabled(false);
1302 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001303 }
1304 }
1305
Michael Jurkad74c9842011-07-10 12:44:21 -07001306 private void updateChildrenLayersEnabled() {
1307 boolean small =
1308 isSmall() || mIsSwitchingState || mState == State.SPRING_LOADED;
1309 boolean dragging = mAnimatingViewIntoPlace || mIsDragOccuring;
1310 boolean enableChildrenLayers = small || dragging || isPageMoving();
1311
1312 if (enableChildrenLayers != mChildrenLayersEnabled) {
1313 mChildrenLayersEnabled = enableChildrenLayers;
1314 for (int i = 0; i < getPageCount(); i++) {
1315 ((ViewGroup)getChildAt(i)).setChildrenLayersEnabled(enableChildrenLayers);
1316 }
Michael Jurkace7e05f2011-02-01 22:02:35 -08001317 }
Michael Jurkace7e05f2011-02-01 22:02:35 -08001318 }
1319
Jeff Brown1d0867c2010-12-02 18:27:39 -08001320 @Override
1321 protected void onWallpaperTap(MotionEvent ev) {
1322 final int[] position = mTempCell;
1323 getLocationOnScreen(position);
1324
1325 int pointerIndex = ev.getActionIndex();
1326 position[0] += (int) ev.getX(pointerIndex);
1327 position[1] += (int) ev.getY(pointerIndex);
1328
1329 mWallpaperManager.sendWallpaperCommand(getWindowToken(),
1330 ev.getAction() == MotionEvent.ACTION_UP
1331 ? WallpaperManager.COMMAND_TAP : WallpaperManager.COMMAND_SECONDARY_TAP,
1332 position[0], position[1], 0, null);
1333 }
1334
Adam Cohena985e592010-09-09 11:23:48 -07001335 private float getYScaleForScreen(int screen) {
1336 int x = Math.abs(screen - 2);
1337
1338 // TODO: This should be generalized for use with arbitrary rotation angles.
1339 switch(x) {
1340 case 0: return EXTRA_SCALE_FACTOR_0;
1341 case 1: return EXTRA_SCALE_FACTOR_1;
1342 case 2: return EXTRA_SCALE_FACTOR_2;
1343 }
1344 return 1.0f;
1345 }
1346
Michael Jurkad74c9842011-07-10 12:44:21 -07001347 public void shrink(State shrinkState) {
Michael Jurkaaf91de02010-11-23 16:23:58 -08001348 shrink(shrinkState, true);
1349 }
Winson Chung9171e6d2010-11-17 17:39:27 -08001350
Michael Jurkaaf91de02010-11-23 16:23:58 -08001351 // we use this to shrink the workspace for the all apps view and the customize view
Winson Chung4afe9b32011-07-27 17:46:20 -07001352 public void shrink(final State shrinkState, boolean animated) {
Michael Jurka883f55b2010-10-21 15:47:14 -07001353 if (mFirstLayout) {
1354 // (mFirstLayout == "first layout has not happened yet")
1355 // if we get a call to shrink() as part of our initialization (for example, if
1356 // Launcher is started in All Apps mode) then we need to wait for a layout call
1357 // to get our width so we can layout the mini-screen views correctly
Michael Jurkad74c9842011-07-10 12:44:21 -07001358 mSwitchStateAfterFirstLayout = true;
1359 mStateAfterFirstLayout = shrinkState;
Michael Jurka883f55b2010-10-21 15:47:14 -07001360 return;
1361 }
Michael Jurkad74c9842011-07-10 12:44:21 -07001362
Michael Jurka3e7c7632010-10-02 16:01:03 -07001363 // Stop any scrolling, move to the current page right away
Michael Jurkaa997ac22010-10-27 18:10:55 -07001364 setCurrentPage((mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage);
Michael Jurka3e7c7632010-10-02 16:01:03 -07001365
Adam Cohen1b0aaac2010-10-28 11:11:18 -07001366 CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage);
Patrick Dubroy0dfcf682011-01-18 17:08:11 -08001367 if (currentPage == null) {
1368 Log.w(TAG, "currentPage is NULL! mCurrentPage " + mCurrentPage
1369 + " mNextPage " + mNextPage);
1370 return;
1371 }
Adam Cohenddb82192010-11-10 16:32:54 -08001372 if (currentPage.getBackgroundAlphaMultiplier() < 1.0f) {
1373 currentPage.setBackgroundAlpha(0.0f);
1374 }
Adam Cohen1b0aaac2010-10-28 11:11:18 -07001375 currentPage.setBackgroundAlphaMultiplier(1.0f);
1376
Michael Jurkad74c9842011-07-10 12:44:21 -07001377 mState = shrinkState;
1378 updateChildrenLayersEnabled();
Patrick Dubroy0dfcf682011-01-18 17:08:11 -08001379
1380 // we intercept and reject all touch events when we're small, so be sure to reset the state
1381 mTouchState = TOUCH_STATE_REST;
1382 mActivePointerId = INVALID_POINTER;
1383
Patrick Dubroy7247f632010-08-04 16:02:59 -07001384 final Resources res = getResources();
Michael Jurkadee05892010-07-27 10:01:56 -07001385 final int screenWidth = getWidth();
Michael Jurka213d9632010-07-28 11:29:25 -07001386 final int screenHeight = getHeight();
Michael Jurka0142d492010-08-25 17:46:15 -07001387
Patrick Dubroy244d74c2011-05-19 16:48:48 -07001388 // How much the workspace shrinks when we enter all apps or customization mode
1389 final float shrinkFactor = res.getInteger(R.integer.config_workspaceShrinkPercent) / 100.0f;
1390
Michael Jurka0142d492010-08-25 17:46:15 -07001391 // Making the assumption that all pages have the same width as the 0th
1392 final int pageWidth = getChildAt(0).getMeasuredWidth();
1393 final int pageHeight = getChildAt(0).getMeasuredHeight();
1394
Patrick Dubroy244d74c2011-05-19 16:48:48 -07001395 final int scaledPageWidth = (int) (shrinkFactor * pageWidth);
1396 final int scaledPageHeight = (int) (shrinkFactor * pageHeight);
Adam Cohena985e592010-09-09 11:23:48 -07001397 final float extraScaledSpacing = res.getDimension(R.dimen.smallScreenExtraSpacing);
Michael Jurkadee05892010-07-27 10:01:56 -07001398
1399 final int screenCount = getChildCount();
Adam Cohena985e592010-09-09 11:23:48 -07001400 float totalWidth = screenCount * scaledPageWidth + (screenCount - 1) * extraScaledSpacing;
Michael Jurka213d9632010-07-28 11:29:25 -07001401
Michael Jurkac1a036b2011-07-25 17:04:23 -07001402 // We shrink and disappear to nothing
Winson Chungdf4b83d2010-10-20 17:49:27 -07001403 boolean isPortrait = getMeasuredHeight() > getMeasuredWidth();
Michael Jurkac1a036b2011-07-25 17:04:23 -07001404 float y = screenHeight - scaledPageHeight - (isPortrait ?
Winson Chungef0066b2010-10-21 11:55:00 -07001405 getResources().getDimension(R.dimen.allAppsSmallScreenVerticalMarginPortrait) :
1406 getResources().getDimension(R.dimen.allAppsSmallScreenVerticalMarginLandscape));
Michael Jurkac1a036b2011-07-25 17:04:23 -07001407 float finalAlpha = 0.0f;
Michael Jurkadee05892010-07-27 10:01:56 -07001408
Michael Jurkad74c9842011-07-10 12:44:21 -07001409 int duration = res.getInteger(R.integer.config_appsCustomizeWorkspaceShrinkTime);
Adam Cohenf16e5712011-01-13 13:31:45 -08001410
Michael Jurkadee05892010-07-27 10:01:56 -07001411 // We animate all the screens to the centered position in workspace
1412 // At the same time, the screens become greyed/dimmed
1413
1414 // newX is initialized to the left-most position of the centered screens
Michael Jurka742574b2011-02-02 23:51:01 -08001415 float x = mScroller.getFinalX() + screenWidth / 2 - totalWidth / 2;
Adam Cohena985e592010-09-09 11:23:48 -07001416
1417 // We are going to scale about the center of the view, so we need to adjust the positions
1418 // of the views accordingly
Michael Jurka742574b2011-02-02 23:51:01 -08001419 x -= (pageWidth - scaledPageWidth) / 2.0f;
1420 y -= (pageHeight - scaledPageHeight) / 2.0f;
Michael Jurka3c4c20f2010-10-28 15:36:06 -07001421
1422 if (mAnimator != null) {
1423 mAnimator.cancel();
1424 }
Adam Cohenf16e5712011-01-13 13:31:45 -08001425
Michael Jurka3c4c20f2010-10-28 15:36:06 -07001426 mAnimator = new AnimatorSet();
Winson Chungde1af762011-07-21 16:44:07 -07001427 // Workaround the AnimatorSet cancel bug...
1428 mUnshrinkAnimationEnabled = false;
1429 mShrinkAnimationEnabled = true;
Michael Jurka742574b2011-02-02 23:51:01 -08001430
Adam Cohen4b285c52011-07-21 14:24:06 -07001431 initAnimationArrays();
Michael Jurka742574b2011-02-02 23:51:01 -08001432
Michael Jurkadee05892010-07-27 10:01:56 -07001433 for (int i = 0; i < screenCount; i++) {
Adam Cohenf16e5712011-01-13 13:31:45 -08001434 final CellLayout cl = (CellLayout) getChildAt(i);
Adam Cohena985e592010-09-09 11:23:48 -07001435
Adam Cohenf34bab52010-09-30 14:11:56 -07001436 float rotation = (-i + 2) * WORKSPACE_ROTATION;
Adam Cohena985e592010-09-09 11:23:48 -07001437 float rotationScaleX = (float) (1.0f / Math.cos(Math.PI * rotation / 180.0f));
1438 float rotationScaleY = getYScaleForScreen(i);
1439
Adam Cohen4b285c52011-07-21 14:24:06 -07001440 mOldAlphas[i] = cl.getAlpha();
1441 mNewAlphas[i] = finalAlpha;
1442 if (animated && (mOldAlphas[i] != 0f || mNewAlphas[i] != 0f)) {
Michael Jurkaabded662011-03-04 12:06:57 -08001443 // if the CellLayout will be visible during the animation, force building its
1444 // hardware layer immediately so we don't see a blip later in the animation
1445 cl.buildChildrenLayer();
1446 }
Michael Jurka3e5ad582011-02-03 22:46:34 -08001447 if (animated) {
Adam Cohen4b285c52011-07-21 14:24:06 -07001448 mOldTranslationXs[i] = cl.getX();
1449 mOldTranslationYs[i] = cl.getY();
1450 mOldScaleXs[i] = cl.getScaleX();
1451 mOldScaleYs[i] = cl.getScaleY();
1452 mOldBackgroundAlphas[i] = cl.getBackgroundAlpha();
1453 mOldRotationYs[i] = cl.getRotationY();
1454 mNewTranslationXs[i] = x;
1455 mNewTranslationYs[i] = y;
Michael Jurkac1a036b2011-07-25 17:04:23 -07001456 mNewScaleXs[i] = shrinkFactor * rotationScaleX;
1457 mNewScaleYs[i] = shrinkFactor * rotationScaleY;
Adam Cohen4b285c52011-07-21 14:24:06 -07001458 mNewBackgroundAlphas[i] = finalAlpha;
1459 mNewRotationYs[i] = rotation;
Michael Jurka79212d82010-07-30 16:36:20 -07001460 } else {
Michael Jurka742574b2011-02-02 23:51:01 -08001461 cl.setX((int)x);
1462 cl.setY((int)y);
Michael Jurkac1a036b2011-07-25 17:04:23 -07001463 cl.setScaleX(shrinkFactor * rotationScaleX);
1464 cl.setScaleY(shrinkFactor * rotationScaleY);
Michael Jurka883f55b2010-10-21 15:47:14 -07001465 cl.setBackgroundAlpha(finalAlpha);
Michael Jurkac1a036b2011-07-25 17:04:23 -07001466 cl.setFastAlpha(finalAlpha);
Adam Cohena985e592010-09-09 11:23:48 -07001467 cl.setRotationY(rotation);
Michael Jurka3e5ad582011-02-03 22:46:34 -08001468 mShrinkAnimationListener.onAnimationEnd(null);
Michael Jurka79212d82010-07-30 16:36:20 -07001469 }
Michael Jurkadee05892010-07-27 10:01:56 -07001470 // increment newX for the next screen
Michael Jurka742574b2011-02-02 23:51:01 -08001471 x += scaledPageWidth + extraScaledSpacing;
Michael Jurkadee05892010-07-27 10:01:56 -07001472 }
Michael Jurkaab1983f2011-01-18 15:50:17 -08001473
1474 float wallpaperOffset = 0.5f;
1475 Display display = mLauncher.getWindowManager().getDefaultDisplay();
1476 int wallpaperTravelHeight = (int) (display.getHeight() *
1477 wallpaperTravelToScreenHeightRatio(display.getWidth(), display.getHeight()));
1478 float offsetFromCenter = (wallpaperTravelHeight / (float) mWallpaperHeight) / 2f;
Michael Jurkaca5b8362011-01-27 13:23:26 -08001479 boolean isLandscape = display.getWidth() > display.getHeight();
1480
Michael Jurkaff0e01b2011-07-08 17:08:51 -07001481 // on phones, don't scroll the wallpaper horizontally or vertically when switching
1482 // to/from all apps
1483 final boolean enableWallpaperEffects =
1484 isHardwareAccelerated() && LauncherApplication.isScreenLarge();
Winson Chungb46a2d12011-04-22 14:13:05 -07001485 if (enableWallpaperEffects) {
1486 switch (shrinkState) {
1487 // animating in
Winson Chungb46a2d12011-04-22 14:13:05 -07001488 case SPRING_LOADED:
1489 wallpaperOffset = 0.5f;
1490 mWallpaperOffset.setVerticalCatchupConstant(isLandscape ? 0.34f : 0.32f);
1491 break;
Michael Jurkad74c9842011-07-10 12:44:21 -07001492 case SMALL:
Winson Chungb46a2d12011-04-22 14:13:05 -07001493 // allapps
1494 wallpaperOffset = 0.5f - offsetFromCenter;
1495 mWallpaperOffset.setVerticalCatchupConstant(isLandscape ? 0.34f : 0.32f);
1496 break;
1497 }
Michael Jurkaab1983f2011-01-18 15:50:17 -08001498 }
1499
Michael Jurka742574b2011-02-02 23:51:01 -08001500 setLayoutScale(1.0f);
Michael Jurkaca5b8362011-01-27 13:23:26 -08001501 if (animated) {
Winson Chungb46a2d12011-04-22 14:13:05 -07001502 if (enableWallpaperEffects) {
1503 mWallpaperOffset.setHorizontalCatchupConstant(0.46f);
1504 mWallpaperOffset.setOverrideHorizontalCatchupConstant(true);
1505 }
Michael Jurkaca5b8362011-01-27 13:23:26 -08001506
Michael Jurkaab1983f2011-01-18 15:50:17 -08001507 mSyncWallpaperOffsetWithScroll = false;
Michael Jurkaab1983f2011-01-18 15:50:17 -08001508
Michael Jurka742574b2011-02-02 23:51:01 -08001509 ValueAnimator animWithInterpolator =
1510 ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
1511 animWithInterpolator.setInterpolator(mZoomOutInterpolator);
1512
1513 final float oldHorizontalWallpaperOffset = getHorizontalWallpaperOffset();
1514 final float oldVerticalWallpaperOffset = getVerticalWallpaperOffset();
1515 final float newHorizontalWallpaperOffset = 0.5f;
1516 final float newVerticalWallpaperOffset = wallpaperOffset;
Michael Jurkac2e26a02011-03-24 15:20:26 -07001517 animWithInterpolator.addUpdateListener(new LauncherAnimatorUpdateListener() {
1518 public void onAnimationUpdate(float a, float b) {
Winson Chungde1af762011-07-21 16:44:07 -07001519 if (!mShrinkAnimationEnabled) return;
Adam Cohen4b285c52011-07-21 14:24:06 -07001520 mTransitionProgress = b;
Michael Jurkac4e772e2011-02-10 13:32:01 -08001521 if (b == 0f) {
1522 // an optimization, and required for correct behavior.
1523 return;
1524 }
Michael Jurkaa71cb172011-07-10 15:04:55 -07001525 invalidate();
Winson Chungb46a2d12011-04-22 14:13:05 -07001526 if (enableWallpaperEffects) {
1527 setHorizontalWallpaperOffset(
Michael Jurka742574b2011-02-02 23:51:01 -08001528 a * oldHorizontalWallpaperOffset + b * newHorizontalWallpaperOffset);
Winson Chungb46a2d12011-04-22 14:13:05 -07001529 setVerticalWallpaperOffset(
Michael Jurka742574b2011-02-02 23:51:01 -08001530 a * oldVerticalWallpaperOffset + b * newVerticalWallpaperOffset);
Winson Chungb46a2d12011-04-22 14:13:05 -07001531 }
Michael Jurka742574b2011-02-02 23:51:01 -08001532 for (int i = 0; i < screenCount; i++) {
Michael Jurka742574b2011-02-02 23:51:01 -08001533 final CellLayout cl = (CellLayout) getChildAt(i);
1534 cl.fastInvalidate();
Adam Cohen4b285c52011-07-21 14:24:06 -07001535 cl.setFastX(a * mOldTranslationXs[i] + b * mNewTranslationXs[i]);
1536 cl.setFastY(a * mOldTranslationYs[i] + b * mNewTranslationYs[i]);
1537 cl.setFastScaleX(a * mOldScaleXs[i] + b * mNewScaleXs[i]);
1538 cl.setFastScaleY(a * mOldScaleYs[i] + b * mNewScaleYs[i]);
Michael Jurka742574b2011-02-02 23:51:01 -08001539 cl.setFastBackgroundAlpha(
Adam Cohen4b285c52011-07-21 14:24:06 -07001540 a * mOldBackgroundAlphas[i] + b * mNewBackgroundAlphas[i]);
1541 cl.setFastAlpha(a * mOldAlphas[i] + b * mNewAlphas[i]);
1542 cl.setFastRotationY(a * mOldRotationYs[i] + b * mNewRotationYs[i]);
Michael Jurka742574b2011-02-02 23:51:01 -08001543 }
1544 }
1545 });
1546 mAnimator.playTogether(animWithInterpolator);
Michael Jurkabea15192010-11-17 12:33:46 -08001547 mAnimator.addListener(mShrinkAnimationListener);
Michael Jurka3c4c20f2010-10-28 15:36:06 -07001548 mAnimator.start();
Winson Chungb46a2d12011-04-22 14:13:05 -07001549 } else if (enableWallpaperEffects) {
Michael Jurkaab1983f2011-01-18 15:50:17 -08001550 setVerticalWallpaperOffset(wallpaperOffset);
1551 setHorizontalWallpaperOffset(0.5f);
1552 updateWallpaperOffsetImmediately();
Michael Jurka3c4c20f2010-10-28 15:36:06 -07001553 }
Michael Jurkadee05892010-07-27 10:01:56 -07001554 setChildrenDrawnWithCacheEnabled(true);
Winson Chung8d006d52010-11-29 15:55:29 -08001555
Michael Jurkab9e14972011-07-25 17:57:40 -07001556 showBackgroundGradientForAllApps(animated);
Michael Jurkadee05892010-07-27 10:01:56 -07001557 }
1558
Michael Jurkac1a036b2011-07-25 17:04:23 -07001559 @Override
1560 protected void updateAdjacentPagesAlpha() {
1561 if (!isSmall()) {
1562 super.updateAdjacentPagesAlpha();
1563 }
1564 }
1565
Adam Cohen61033d32010-11-15 18:29:44 -08001566 /*
1567 * This interpolator emulates the rate at which the perceived scale of an object changes
1568 * as its distance from a camera increases. When this interpolator is applied to a scale
1569 * animation on a view, it evokes the sense that the object is shrinking due to moving away
1570 * from the camera.
1571 */
1572 static class ZInterpolator implements TimeInterpolator {
1573 private float focalLength;
1574
1575 public ZInterpolator(float foc) {
1576 focalLength = foc;
1577 }
1578
Adam Coheneed565d2010-11-15 11:30:05 -08001579 public float getInterpolation(float input) {
1580 return (1.0f - focalLength / (focalLength + input)) /
Adam Cohen61033d32010-11-15 18:29:44 -08001581 (1.0f - focalLength / (focalLength + 1.0f));
Adam Cohencbbaf982010-11-12 14:50:33 -08001582 }
1583 }
1584
Adam Cohen61033d32010-11-15 18:29:44 -08001585 /*
1586 * The exact reverse of ZInterpolator.
1587 */
1588 static class InverseZInterpolator implements TimeInterpolator {
1589 private ZInterpolator zInterpolator;
1590 public InverseZInterpolator(float foc) {
1591 zInterpolator = new ZInterpolator(foc);
1592 }
Adam Cohencbbaf982010-11-12 14:50:33 -08001593 public float getInterpolation(float input) {
Adam Cohen61033d32010-11-15 18:29:44 -08001594 return 1 - zInterpolator.getInterpolation(1 - input);
Adam Cohencbbaf982010-11-12 14:50:33 -08001595 }
1596 }
1597
Adam Cohen61033d32010-11-15 18:29:44 -08001598 /*
1599 * ZInterpolator compounded with an ease-out.
1600 */
1601 static class ZoomOutInterpolator implements TimeInterpolator {
1602 private final ZInterpolator zInterpolator = new ZInterpolator(0.2f);
Michael Jurkaea573482011-02-03 19:48:18 -08001603 private final DecelerateInterpolator decelerate = new DecelerateInterpolator(1.8f);
Adam Cohen61033d32010-11-15 18:29:44 -08001604
1605 public float getInterpolation(float input) {
1606 return decelerate.getInterpolation(zInterpolator.getInterpolation(input));
1607 }
1608 }
1609
1610 /*
1611 * InvereZInterpolator compounded with an ease-out.
1612 */
1613 static class ZoomInInterpolator implements TimeInterpolator {
1614 private final InverseZInterpolator inverseZInterpolator = new InverseZInterpolator(0.35f);
1615 private final DecelerateInterpolator decelerate = new DecelerateInterpolator(3.0f);
1616
1617 public float getInterpolation(float input) {
1618 return decelerate.getInterpolation(inverseZInterpolator.getInterpolation(input));
1619 }
1620 }
1621
1622 private final ZoomOutInterpolator mZoomOutInterpolator = new ZoomOutInterpolator();
1623 private final ZoomInInterpolator mZoomInInterpolator = new ZoomInInterpolator();
Michael Jurka3e7c7632010-10-02 16:01:03 -07001624
Michael Jurka3e7c7632010-10-02 16:01:03 -07001625 /*
Adam Cohen66396872011-04-15 17:50:36 -07001626 *
1627 * We call these methods (onDragStartedWithItemSpans/onDragStartedWithSize) whenever we
1628 * start a drag in Launcher, regardless of whether the drag has ever entered the Workspace
1629 *
1630 * These methods mark the appropriate pages as accepting drops (which alters their visual
1631 * appearance).
1632 *
1633 */
1634 public void onDragStartedWithItem(View v) {
Adam Cohen66396872011-04-15 17:50:36 -07001635 final Canvas canvas = new Canvas();
1636
1637 // We need to add extra padding to the bitmap to make room for the glow effect
1638 final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;
1639
1640 // The outline is used to visualize where the item will land if dropped
1641 mDragOutline = createDragOutline(v, canvas, bitmapPadding);
1642 }
1643
Michael Jurkad3ef3062010-11-23 16:23:58 -08001644 public void onDragStartedWithItemSpans(int spanX, int spanY, Bitmap b) {
Michael Jurkad3ef3062010-11-23 16:23:58 -08001645 final Canvas canvas = new Canvas();
1646
1647 // We need to add extra padding to the bitmap to make room for the glow effect
Michael Jurka38b4f7c2010-12-14 16:46:39 -08001648 final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;
Michael Jurkad3ef3062010-11-23 16:23:58 -08001649
Michael Jurkaf12c75c2011-01-25 22:41:40 -08001650 CellLayout cl = (CellLayout) getChildAt(0);
Adam Cohen66396872011-04-15 17:50:36 -07001651
1652 int[] size = cl.cellSpansToSize(spanX, spanY);
1653
Michael Jurkad3ef3062010-11-23 16:23:58 -08001654 // The outline is used to visualize where the item will land if dropped
Adam Cohen66396872011-04-15 17:50:36 -07001655 mDragOutline = createDragOutline(b, canvas, bitmapPadding, size[0], size[1]);
Michael Jurka3e7c7632010-10-02 16:01:03 -07001656 }
1657
1658 // we call this method whenever a drag and drop in Launcher finishes, even if Workspace was
1659 // never dragged over
Patrick Dubroy7bccb422011-01-20 14:50:55 -08001660 public void onDragStopped(boolean success) {
1661 // In the success case, DragController has already called onDragExit()
1662 if (!success) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07001663 doDragExit(null);
Patrick Dubroy7bccb422011-01-20 14:50:55 -08001664 }
Michael Jurka3e7c7632010-10-02 16:01:03 -07001665 }
1666
Michael Jurka9c6de3d2010-11-23 16:23:58 -08001667 // We call this when we trigger an unshrink by clicking on the CellLayout cl
1668 public void unshrink(CellLayout clThatWasClicked) {
Michael Jurkad3ef3062010-11-23 16:23:58 -08001669 unshrink(clThatWasClicked, false);
1670 }
1671
1672 public void unshrink(CellLayout clThatWasClicked, boolean springLoaded) {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08001673 int newCurrentPage = indexOfChild(clThatWasClicked);
Michael Jurkad74c9842011-07-10 12:44:21 -07001674 if (isSmall()) {
Michael Jurkad3ef3062010-11-23 16:23:58 -08001675 if (springLoaded) {
Winson Chungb26f3d62011-06-02 10:49:29 -07001676 setLayoutScale(mSpringLoadedShrinkFactor);
Michael Jurkad3ef3062010-11-23 16:23:58 -08001677 }
Michael Jurka8c920dd2011-01-20 14:16:56 -08001678 scrollToNewPageWithoutMovingPages(newCurrentPage);
Michael Jurkad3ef3062010-11-23 16:23:58 -08001679 unshrink(true, springLoaded);
1680 }
1681 }
1682
1683
1684 public void enterSpringLoadedDragMode(CellLayout clThatWasClicked) {
Michael Jurkad3ef3062010-11-23 16:23:58 -08001685 unshrink(clThatWasClicked, true);
Michael Jurkad3ef3062010-11-23 16:23:58 -08001686 }
1687
Michael Jurkad74c9842011-07-10 12:44:21 -07001688 public void exitSpringLoadedDragMode(State shrinkState) {
Michael Jurkad3ef3062010-11-23 16:23:58 -08001689 shrink(shrinkState);
Michael Jurkadee05892010-07-27 10:01:56 -07001690 }
1691
Patrick Dubroy758a9232011-03-03 19:54:56 -08001692 public void exitWidgetResizeMode() {
Adam Cohen716b51e2011-06-30 12:09:54 -07001693 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohen67882692011-03-11 15:29:03 -08001694 dragLayer.clearAllResizeFrames();
Patrick Dubroy758a9232011-03-03 19:54:56 -08001695 }
1696
Michael Jurka4cb37242010-08-09 21:05:32 -07001697 void unshrink(boolean animated) {
Michael Jurkad3ef3062010-11-23 16:23:58 -08001698 unshrink(animated, false);
1699 }
1700
Adam Cohen4b285c52011-07-21 14:24:06 -07001701 private void initAnimationArrays() {
1702 final int childCount = getChildCount();
1703 if (mOldTranslationXs != null) return;
1704 mOldTranslationXs = new float[childCount];
1705 mOldTranslationYs = new float[childCount];
1706 mOldScaleXs = new float[childCount];
1707 mOldScaleYs = new float[childCount];
1708 mOldBackgroundAlphas = new float[childCount];
1709 mOldBackgroundAlphaMultipliers = new float[childCount];
1710 mOldAlphas = new float[childCount];
1711 mOldRotationYs = new float[childCount];
1712 mNewTranslationXs = new float[childCount];
1713 mNewTranslationYs = new float[childCount];
1714 mNewScaleXs = new float[childCount];
1715 mNewScaleYs = new float[childCount];
1716 mNewBackgroundAlphas = new float[childCount];
1717 mNewBackgroundAlphaMultipliers = new float[childCount];
1718 mNewAlphas = new float[childCount];
1719 mNewRotationYs = new float[childCount];
1720 }
1721
Winson Chung4afe9b32011-07-27 17:46:20 -07001722 void unshrink(boolean animated, final boolean springLoaded) {
Michael Jurka99633da2011-07-27 22:40:17 -07001723 if (mFirstLayout) {
1724 // (mFirstLayout == "first layout has not happened yet")
1725 // cancel any pending shrinks that were set earlier
1726 mSwitchStateAfterFirstLayout = false;
1727 mStateAfterFirstLayout = State.NORMAL;
1728 return;
1729 }
1730
Michael Jurkad74c9842011-07-10 12:44:21 -07001731 if (isSmall()) {
Michael Jurkad3ef3062010-11-23 16:23:58 -08001732 float finalScaleFactor = 1.0f;
1733 float finalBackgroundAlpha = 0.0f;
1734 if (springLoaded) {
Winson Chungb26f3d62011-06-02 10:49:29 -07001735 finalScaleFactor = mSpringLoadedShrinkFactor;
Michael Jurkad3ef3062010-11-23 16:23:58 -08001736 finalBackgroundAlpha = 1.0f;
Adam Cohen3384c7b2011-07-17 15:48:21 -07001737 mState = State.SPRING_LOADED;
Michael Jurkad3ef3062010-11-23 16:23:58 -08001738 } else {
Adam Cohen3384c7b2011-07-17 15:48:21 -07001739 mState = State.NORMAL;
Michael Jurkad3ef3062010-11-23 16:23:58 -08001740 }
Michael Jurka3c4c20f2010-10-28 15:36:06 -07001741 if (mAnimator != null) {
1742 mAnimator.cancel();
1743 }
Adam Lesinski6b879f02010-11-04 16:15:23 -07001744
Michael Jurka3c4c20f2010-10-28 15:36:06 -07001745 mAnimator = new AnimatorSet();
Winson Chungde1af762011-07-21 16:44:07 -07001746
1747 // Workaround the AnimatorSet cancel bug...
1748 mShrinkAnimationEnabled = false;
1749 mUnshrinkAnimationEnabled = true;
1750
Michael Jurkab0f28bd2010-07-30 11:58:59 -07001751 final int screenCount = getChildCount();
Adam Cohen4b285c52011-07-21 14:24:06 -07001752 initAnimationArrays();
Michael Jurka4cb37242010-08-09 21:05:32 -07001753
1754 final int duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime);
Michael Jurkab0f28bd2010-07-30 11:58:59 -07001755 for (int i = 0; i < screenCount; i++) {
Patrick Dubroy7247f632010-08-04 16:02:59 -07001756 final CellLayout cl = (CellLayout)getChildAt(i);
Winson Chung785d2eb2011-04-14 16:08:02 -07001757 float finalAlphaValue = 0f;
1758 float rotation = 0f;
Winson Chung785d2eb2011-04-14 16:08:02 -07001759
Winson Chung4afe9b32011-07-27 17:46:20 -07001760 // Set the final alpha depending on whether we are fading side pages. On phone ui,
1761 // we don't do any of the rotation, or the fading alpha in portrait. See the
1762 // ctor and screenScrolled().
Michael Jurka7cfc2822011-08-02 20:19:24 -07001763 if (mFadeInAdjacentScreens && !springLoaded) {
Winson Chung4afe9b32011-07-27 17:46:20 -07001764 finalAlphaValue = (i == mCurrentPage) ? 1f : 0f;
1765 } else {
1766 finalAlphaValue = 1f;
1767 }
1768
1769 if (LauncherApplication.isScreenLarge()) {
Winson Chung785d2eb2011-04-14 16:08:02 -07001770 if (i < mCurrentPage) {
1771 rotation = WORKSPACE_ROTATION;
1772 } else if (i > mCurrentPage) {
1773 rotation = -WORKSPACE_ROTATION;
1774 }
Winson Chung785d2eb2011-04-14 16:08:02 -07001775 }
Winson Chungb26f3d62011-06-02 10:49:29 -07001776 float finalAlphaMultiplierValue = 1f;
Adam Cohenf34bab52010-09-30 14:11:56 -07001777
Winson Chung63257c12011-05-05 17:06:13 -07001778 float translation = 0f;
1779
1780 // If the screen is not xlarge, then don't rotate the CellLayouts
1781 // NOTE: If we don't update the side pages alpha, then we should not hide the side
1782 // pages. see unshrink().
Michael Jurkaa2eb1702011-05-12 14:57:05 -07001783 if (LauncherApplication.isScreenLarge()) {
Winson Chung63257c12011-05-05 17:06:13 -07001784 translation = getOffsetXForRotation(rotation, cl.getWidth(), cl.getHeight());
1785 }
Adam Cohencbbaf982010-11-12 14:50:33 -08001786
Adam Cohen4b285c52011-07-21 14:24:06 -07001787 mOldAlphas[i] = cl.getAlpha();
1788 mNewAlphas[i] = finalAlphaValue;
Michael Jurka3e5ad582011-02-03 22:46:34 -08001789 if (animated) {
Adam Cohen4b285c52011-07-21 14:24:06 -07001790 mOldTranslationXs[i] = cl.getTranslationX();
1791 mOldTranslationYs[i] = cl.getTranslationY();
1792 mOldScaleXs[i] = cl.getScaleX();
1793 mOldScaleYs[i] = cl.getScaleY();
1794 mOldBackgroundAlphas[i] = cl.getBackgroundAlpha();
1795 mOldBackgroundAlphaMultipliers[i] = cl.getBackgroundAlphaMultiplier();
1796 mOldRotationYs[i] = cl.getRotationY();
Adam Cohen4904f662011-01-17 16:50:28 -08001797
Adam Cohen4b285c52011-07-21 14:24:06 -07001798 mNewTranslationXs[i] = translation;
1799 mNewTranslationYs[i] = 0f;
1800 mNewScaleXs[i] = finalScaleFactor;
1801 mNewScaleYs[i] = finalScaleFactor;
1802 mNewBackgroundAlphas[i] = finalBackgroundAlpha;
1803 mNewBackgroundAlphaMultipliers[i] = finalAlphaMultiplierValue;
1804 mNewRotationYs[i] = rotation;
Michael Jurka4cb37242010-08-09 21:05:32 -07001805 } else {
Adam Lesinski6b879f02010-11-04 16:15:23 -07001806 cl.setTranslationX(translation);
Michael Jurka4cb37242010-08-09 21:05:32 -07001807 cl.setTranslationY(0.0f);
Michael Jurkad3ef3062010-11-23 16:23:58 -08001808 cl.setScaleX(finalScaleFactor);
1809 cl.setScaleY(finalScaleFactor);
Michael Jurka5f1c5092010-09-03 14:15:02 -07001810 cl.setBackgroundAlpha(0.0f);
Winson Chungae222ab2011-01-10 11:45:27 -08001811 cl.setBackgroundAlphaMultiplier(finalAlphaMultiplierValue);
Adam Cohenf34bab52010-09-30 14:11:56 -07001812 cl.setAlpha(finalAlphaValue);
1813 cl.setRotationY(rotation);
Michael Jurka3e5ad582011-02-03 22:46:34 -08001814 mUnshrinkAnimationListener.onAnimationEnd(null);
Michael Jurka4cb37242010-08-09 21:05:32 -07001815 }
Michael Jurkab0f28bd2010-07-30 11:58:59 -07001816 }
Winson Chung1c4cf4a2011-07-29 14:49:10 -07001817
1818 // Unshrink the hotset the same amount we are unshrinking the screens
1819 if (mLauncher.getHotseat() != null) {
1820 View hotseat = mLauncher.getHotseat().getLayout();
1821 hotseat.setScaleX(finalScaleFactor);
1822 hotseat.setScaleY(finalScaleFactor);
1823 }
1824
Michael Jurkaca5b8362011-01-27 13:23:26 -08001825 Display display = mLauncher.getWindowManager().getDefaultDisplay();
1826 boolean isLandscape = display.getWidth() > display.getHeight();
Michael Jurkaff0e01b2011-07-08 17:08:51 -07001827 // on phones, don't scroll the wallpaper horizontally or vertically when switching
1828 // to/from all apps
1829 final boolean enableWallpaperEffects =
1830 isHardwareAccelerated() && LauncherApplication.isScreenLarge();
Winson Chungb46a2d12011-04-22 14:13:05 -07001831 if (enableWallpaperEffects) {
Michael Jurkad74c9842011-07-10 12:44:21 -07001832 switch (mState) {
Winson Chungb46a2d12011-04-22 14:13:05 -07001833 // animating out
Winson Chungb46a2d12011-04-22 14:13:05 -07001834 case SPRING_LOADED:
1835 if (animated) {
1836 mWallpaperOffset.setHorizontalCatchupConstant(isLandscape ? 0.49f : 0.46f);
1837 mWallpaperOffset.setVerticalCatchupConstant(isLandscape ? 0.49f : 0.46f);
1838 mWallpaperOffset.setOverrideHorizontalCatchupConstant(true);
1839 }
1840 break;
Michael Jurkad74c9842011-07-10 12:44:21 -07001841 case SMALL:
Winson Chungb46a2d12011-04-22 14:13:05 -07001842 // all apps
1843 if (animated) {
1844 mWallpaperOffset.setHorizontalCatchupConstant(isLandscape ? 0.65f : 0.65f);
1845 mWallpaperOffset.setVerticalCatchupConstant(isLandscape ? 0.65f : 0.65f);
1846 mWallpaperOffset.setOverrideHorizontalCatchupConstant(true);
1847 }
1848 break;
1849 }
Michael Jurkaca5b8362011-01-27 13:23:26 -08001850 }
Michael Jurkaab1983f2011-01-18 15:50:17 -08001851 if (animated) {
Michael Jurka742574b2011-02-02 23:51:01 -08001852 ValueAnimator animWithInterpolator =
1853 ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
1854 animWithInterpolator.setInterpolator(mZoomInInterpolator);
Adam Lesinski6b879f02010-11-04 16:15:23 -07001855
Winson Chung63257c12011-05-05 17:06:13 -07001856 final float oldHorizontalWallpaperOffset = enableWallpaperEffects ?
1857 getHorizontalWallpaperOffset() : 0;
1858 final float oldVerticalWallpaperOffset = enableWallpaperEffects ?
1859 getVerticalWallpaperOffset() : 0;
1860 final float newHorizontalWallpaperOffset = enableWallpaperEffects ?
1861 wallpaperOffsetForCurrentScroll() : 0;
1862 final float newVerticalWallpaperOffset = enableWallpaperEffects ? 0.5f : 0;
Michael Jurkac2e26a02011-03-24 15:20:26 -07001863 animWithInterpolator.addUpdateListener(new LauncherAnimatorUpdateListener() {
1864 public void onAnimationUpdate(float a, float b) {
Winson Chungde1af762011-07-21 16:44:07 -07001865 if (!mUnshrinkAnimationEnabled) return;
Adam Cohen4b285c52011-07-21 14:24:06 -07001866 mTransitionProgress = b;
Michael Jurkac4e772e2011-02-10 13:32:01 -08001867 if (b == 0f) {
1868 // an optimization, but not required
1869 return;
1870 }
Michael Jurka280969c2011-07-11 11:26:10 -07001871 invalidate();
Winson Chungb46a2d12011-04-22 14:13:05 -07001872 if (enableWallpaperEffects) {
1873 setHorizontalWallpaperOffset(a * oldHorizontalWallpaperOffset
1874 + b * newHorizontalWallpaperOffset);
1875 setVerticalWallpaperOffset(a * oldVerticalWallpaperOffset
1876 + b * newVerticalWallpaperOffset);
1877 }
Michael Jurka742574b2011-02-02 23:51:01 -08001878 for (int i = 0; i < screenCount; i++) {
Michael Jurka742574b2011-02-02 23:51:01 -08001879 final CellLayout cl = (CellLayout) getChildAt(i);
1880 cl.fastInvalidate();
1881 cl.setFastTranslationX(
Adam Cohen4b285c52011-07-21 14:24:06 -07001882 a * mOldTranslationXs[i] + b * mNewTranslationXs[i]);
Michael Jurka742574b2011-02-02 23:51:01 -08001883 cl.setFastTranslationY(
Adam Cohen4b285c52011-07-21 14:24:06 -07001884 a * mOldTranslationYs[i] + b * mNewTranslationYs[i]);
1885 cl.setFastScaleX(a * mOldScaleXs[i] + b * mNewScaleXs[i]);
1886 cl.setFastScaleY(a * mOldScaleYs[i] + b * mNewScaleYs[i]);
Michael Jurka742574b2011-02-02 23:51:01 -08001887 cl.setFastBackgroundAlpha(
Adam Cohen4b285c52011-07-21 14:24:06 -07001888 a * mOldBackgroundAlphas[i] + b * mNewBackgroundAlphas[i]);
1889 cl.setBackgroundAlphaMultiplier(a * mOldBackgroundAlphaMultipliers[i] +
1890 b * mNewBackgroundAlphaMultipliers[i]);
1891 cl.setFastAlpha(a * mOldAlphas[i] + b * mNewAlphas[i]);
Michael Jurka742574b2011-02-02 23:51:01 -08001892 }
1893 }
1894 });
1895
1896 ValueAnimator rotationAnim =
1897 ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
1898 rotationAnim.setInterpolator(new DecelerateInterpolator(2.0f));
Michael Jurkac2e26a02011-03-24 15:20:26 -07001899 rotationAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
1900 public void onAnimationUpdate(float a, float b) {
Winson Chungde1af762011-07-21 16:44:07 -07001901 if (!mUnshrinkAnimationEnabled) return;
Michael Jurka742574b2011-02-02 23:51:01 -08001902 // don't invalidate workspace because we did it above
Michael Jurkac4e772e2011-02-10 13:32:01 -08001903 if (b == 0f) {
1904 // an optimization, but not required
1905 return;
1906 }
Michael Jurka742574b2011-02-02 23:51:01 -08001907 for (int i = 0; i < screenCount; i++) {
Michael Jurka742574b2011-02-02 23:51:01 -08001908 final CellLayout cl = (CellLayout) getChildAt(i);
Adam Cohen4b285c52011-07-21 14:24:06 -07001909 cl.setFastRotationY(a * mOldRotationYs[i] + b * mNewRotationYs[i]);
Michael Jurka742574b2011-02-02 23:51:01 -08001910 }
1911 }
1912 });
1913
1914 mAnimator.playTogether(animWithInterpolator, rotationAnim);
Michael Jurkac86756c2010-10-10 15:15:47 -07001915 // If we call this when we're not animated, onAnimationEnd is never called on
1916 // the listener; make sure we only use the listener when we're actually animating
Michael Jurka3c4c20f2010-10-28 15:36:06 -07001917 mAnimator.addListener(mUnshrinkAnimationListener);
1918 mAnimator.start();
Michael Jurkaca5b8362011-01-27 13:23:26 -08001919 } else {
Winson Chungb46a2d12011-04-22 14:13:05 -07001920 if (enableWallpaperEffects) {
1921 setHorizontalWallpaperOffset(wallpaperOffsetForCurrentScroll());
1922 setVerticalWallpaperOffset(0.5f);
1923 updateWallpaperOffsetImmediately();
1924 }
Michael Jurkac86756c2010-10-10 15:15:47 -07001925 }
Michael Jurkadee05892010-07-27 10:01:56 -07001926 }
Winson Chung8d006d52010-11-29 15:55:29 -08001927
Winson Chung557d6ed2011-07-08 15:34:52 -07001928 hideBackgroundGradient(springLoaded ? getResources().getInteger(
Michael Jurkab9e14972011-07-25 17:57:40 -07001929 R.integer.config_appsCustomizeSpringLoadedBgAlpha) / 100f : 0f, animated);
Michael Jurkadee05892010-07-27 10:01:56 -07001930 }
1931
Joe Onorato4be866d2010-10-10 11:26:02 -07001932 /**
1933 * Draw the View v into the given Canvas.
1934 *
1935 * @param v the view to draw
1936 * @param destCanvas the canvas to draw on
1937 * @param padding the horizontal and vertical padding to use when drawing
1938 */
Adam Cohenac8c8762011-07-13 11:15:27 -07001939 private void drawDragView(View v, Canvas destCanvas, int padding, boolean pruneToDrawable) {
Joe Onorato4be866d2010-10-10 11:26:02 -07001940 final Rect clipRect = mTempRect;
1941 v.getDrawingRect(clipRect);
1942
Adam Cohenac8c8762011-07-13 11:15:27 -07001943 destCanvas.save();
1944 if (v instanceof TextView && pruneToDrawable) {
1945 Drawable d = ((TextView) v).getCompoundDrawables()[1];
1946 clipRect.set(0, 0, d.getIntrinsicWidth() + padding, d.getIntrinsicHeight() + padding);
1947 destCanvas.translate(padding / 2, padding / 2);
1948 d.draw(destCanvas);
1949 } else {
1950 if (v instanceof FolderIcon) {
1951 clipRect.bottom = getResources().getDimensionPixelSize(R.dimen.folder_preview_size);
1952 } else if (v instanceof BubbleTextView) {
1953 final BubbleTextView tv = (BubbleTextView) v;
1954 clipRect.bottom = tv.getExtendedPaddingTop() - (int) BubbleTextView.PADDING_V +
1955 tv.getLayout().getLineTop(0);
1956 } else if (v instanceof TextView) {
1957 final TextView tv = (TextView) v;
1958 clipRect.bottom = tv.getExtendedPaddingTop() - tv.getCompoundDrawablePadding() +
1959 tv.getLayout().getLineTop(0);
1960 }
1961 destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2);
1962 destCanvas.clipRect(clipRect, Op.REPLACE);
1963 v.draw(destCanvas);
1964 }
1965 destCanvas.restore();
1966 }
1967
1968 /**
1969 * Returns a new bitmap to show when the given View is being dragged around.
1970 * Responsibility for the bitmap is transferred to the caller.
1971 */
1972 public Bitmap createDragBitmap(View v, Canvas canvas, int padding) {
Winson Chungea359c62011-08-03 17:06:35 -07001973 final int outlineColor = getResources().getColor(android.R.color.holo_blue_light);
Adam Cohenac8c8762011-07-13 11:15:27 -07001974 Bitmap b;
1975
1976 if (v instanceof TextView) {
1977 Drawable d = ((TextView) v).getCompoundDrawables()[1];
1978 b = Bitmap.createBitmap(d.getIntrinsicWidth() + padding,
1979 d.getIntrinsicHeight() + padding, Bitmap.Config.ARGB_8888);
1980 } else {
1981 b = Bitmap.createBitmap(
1982 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
Joe Onorato4be866d2010-10-10 11:26:02 -07001983 }
1984
Adam Cohenac8c8762011-07-13 11:15:27 -07001985 canvas.setBitmap(b);
1986 drawDragView(v, canvas, padding, true);
1987 mOutlineHelper.applyOuterBlur(b, canvas, outlineColor);
Adam Cohenaaf473c2011-08-03 12:02:47 -07001988 canvas.setBitmap(null);
Joe Onorato4be866d2010-10-10 11:26:02 -07001989
Adam Cohenac8c8762011-07-13 11:15:27 -07001990 return b;
Joe Onorato4be866d2010-10-10 11:26:02 -07001991 }
1992
1993 /**
1994 * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
1995 * Responsibility for the bitmap is transferred to the caller.
1996 */
1997 private Bitmap createDragOutline(View v, Canvas canvas, int padding) {
Winson Chungea359c62011-08-03 17:06:35 -07001998 final int outlineColor = getResources().getColor(android.R.color.holo_blue_light);
Joe Onorato4be866d2010-10-10 11:26:02 -07001999 final Bitmap b = Bitmap.createBitmap(
2000 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
2001
2002 canvas.setBitmap(b);
Adam Cohenac8c8762011-07-13 11:15:27 -07002003 drawDragView(v, canvas, padding, false);
Adam Cohen5bb50bd2010-12-03 11:39:55 -08002004 mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor);
Adam Cohenaaf473c2011-08-03 12:02:47 -07002005 canvas.setBitmap(null);
Joe Onorato4be866d2010-10-10 11:26:02 -07002006 return b;
2007 }
2008
2009 /**
Michael Jurkad3ef3062010-11-23 16:23:58 -08002010 * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
2011 * Responsibility for the bitmap is transferred to the caller.
2012 */
Michael Jurkaf12c75c2011-01-25 22:41:40 -08002013 private Bitmap createDragOutline(Bitmap orig, Canvas canvas, int padding, int w, int h) {
Winson Chungea359c62011-08-03 17:06:35 -07002014 final int outlineColor = getResources().getColor(android.R.color.holo_blue_light);
Michael Jurkaf12c75c2011-01-25 22:41:40 -08002015 final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Michael Jurkad3ef3062010-11-23 16:23:58 -08002016 canvas.setBitmap(b);
Michael Jurkaf12c75c2011-01-25 22:41:40 -08002017
2018 Rect src = new Rect(0, 0, orig.getWidth(), orig.getHeight());
2019 float scaleFactor = Math.min((w - padding) / (float) orig.getWidth(),
2020 (h - padding) / (float) orig.getHeight());
2021 int scaledWidth = (int) (scaleFactor * orig.getWidth());
2022 int scaledHeight = (int) (scaleFactor * orig.getHeight());
2023 Rect dst = new Rect(0, 0, scaledWidth, scaledHeight);
2024
2025 // center the image
2026 dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2);
2027
2028 Paint p = new Paint();
2029 p.setFilterBitmap(true);
2030 canvas.drawBitmap(orig, src, dst, p);
Michael Jurkad3ef3062010-11-23 16:23:58 -08002031 mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor);
Adam Cohenaaf473c2011-08-03 12:02:47 -07002032 canvas.setBitmap(null);
Michael Jurkad3ef3062010-11-23 16:23:58 -08002033
2034 return b;
2035 }
2036
2037 /**
Winson Chunga9abd0e2010-10-27 17:18:37 -07002038 * Creates a drag outline to represent a drop (that we don't have the actual information for
2039 * yet). May be changed in the future to alter the drop outline slightly depending on the
2040 * clip description mime data.
2041 */
2042 private Bitmap createExternalDragOutline(Canvas canvas, int padding) {
2043 Resources r = getResources();
Winson Chungea359c62011-08-03 17:06:35 -07002044 final int outlineColor = r.getColor(android.R.color.holo_blue_light);
Winson Chunga9abd0e2010-10-27 17:18:37 -07002045 final int iconWidth = r.getDimensionPixelSize(R.dimen.workspace_cell_width);
2046 final int iconHeight = r.getDimensionPixelSize(R.dimen.workspace_cell_height);
2047 final int rectRadius = r.getDimensionPixelSize(R.dimen.external_drop_icon_rect_radius);
2048 final int inset = (int) (Math.min(iconWidth, iconHeight) * 0.2f);
2049 final Bitmap b = Bitmap.createBitmap(
2050 iconWidth + padding, iconHeight + padding, Bitmap.Config.ARGB_8888);
2051
2052 canvas.setBitmap(b);
2053 canvas.drawRoundRect(new RectF(inset, inset, iconWidth - inset, iconHeight - inset),
2054 rectRadius, rectRadius, mExternalDragOutlinePaint);
Adam Cohen5bb50bd2010-12-03 11:39:55 -08002055 mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor);
Adam Cohenaaf473c2011-08-03 12:02:47 -07002056 canvas.setBitmap(null);
Winson Chunga9abd0e2010-10-27 17:18:37 -07002057 return b;
2058 }
2059
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002060 void startDrag(CellLayout.CellInfo cellInfo) {
2061 View child = cellInfo.cell;
Winson Chungaafa03c2010-06-11 17:34:16 -07002062
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002063 // Make sure the drag was started by a long press as opposed to a long click.
Bjorn Bringert7984c942009-12-09 15:38:25 +00002064 if (!child.isInTouchMode()) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002065 return;
2066 }
Winson Chungaafa03c2010-06-11 17:34:16 -07002067
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002068 mDragInfo = cellInfo;
Adam Cohenfc53cd22011-07-20 15:45:11 -07002069 child.setVisibility(GONE);
Joe Onorato4be866d2010-10-10 11:26:02 -07002070
2071 child.clearFocus();
2072 child.setPressed(false);
2073
2074 final Canvas canvas = new Canvas();
2075
Patrick Dubroy8e58e912010-10-14 13:21:48 -07002076 // We need to add extra padding to the bitmap to make room for the glow effect
Michael Jurka38b4f7c2010-12-14 16:46:39 -08002077 final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;
Patrick Dubroy8e58e912010-10-14 13:21:48 -07002078
Joe Onorato4be866d2010-10-10 11:26:02 -07002079 // The outline is used to visualize where the item will land if dropped
Patrick Dubroy8e58e912010-10-14 13:21:48 -07002080 mDragOutline = createDragOutline(child, canvas, bitmapPadding);
Adam Cohenac8c8762011-07-13 11:15:27 -07002081 beginDragShared(child, this);
2082 }
2083
2084 public void beginDragShared(View child, DragSource source) {
2085 // We need to add extra padding to the bitmap to make room for the glow effect
2086 final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;
Joe Onorato4be866d2010-10-10 11:26:02 -07002087
2088 // The drag bitmap follows the touch point around on the screen
Adam Cohenac8c8762011-07-13 11:15:27 -07002089 final Bitmap b = createDragBitmap(child, new Canvas(), bitmapPadding);
Joe Onorato4be866d2010-10-10 11:26:02 -07002090
2091 final int bmpWidth = b.getWidth();
Adam Cohene3e27a82011-04-15 12:07:39 -07002092
Adam Cohen8dfcba42011-07-07 16:38:18 -07002093 mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY);
2094 final int dragLayerX = (int) mTempXY[0] + (child.getWidth() - bmpWidth) / 2;
Adam Cohenfc53cd22011-07-20 15:45:11 -07002095 int dragLayerY = mTempXY[1] - bitmapPadding / 2;
Adam Cohene3e27a82011-04-15 12:07:39 -07002096
2097 Rect dragRect = null;
Adam Cohen0e4857c2011-06-30 17:05:14 -07002098 if (child instanceof BubbleTextView) {
Adam Cohene3e27a82011-04-15 12:07:39 -07002099 int iconSize = getResources().getDimensionPixelSize(R.dimen.app_icon_size);
2100 int top = child.getPaddingTop();
2101 int left = (bmpWidth - iconSize) / 2;
2102 int right = left + iconSize;
2103 int bottom = top + iconSize;
Adam Cohenfc53cd22011-07-20 15:45:11 -07002104 dragLayerY += top;
Adam Cohene3e27a82011-04-15 12:07:39 -07002105 dragRect = new Rect(left, top, right, bottom);
Adam Cohen0e4857c2011-06-30 17:05:14 -07002106 } else if (child instanceof FolderIcon) {
2107 int previewSize = getResources().getDimensionPixelSize(R.dimen.folder_preview_size);
2108 dragRect = new Rect(0, 0, child.getWidth(), previewSize);
Adam Cohene3e27a82011-04-15 12:07:39 -07002109 }
2110
Adam Cohenac8c8762011-07-13 11:15:27 -07002111 mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
Adam Cohene3e27a82011-04-15 12:07:39 -07002112 DragController.DRAG_ACTION_MOVE, dragRect);
Joe Onorato4be866d2010-10-10 11:26:02 -07002113 b.recycle();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002114 }
2115
Winson Chung3d503fb2011-07-13 17:25:49 -07002116 void addApplicationShortcut(ShortcutInfo info, CellLayout target, long container, int screen,
2117 int cellX, int cellY, boolean insertAtFirst, int intersectX, int intersectY) {
2118 View view = mLauncher.createShortcut(R.layout.application, target, (ShortcutInfo) info);
Michael Jurka0280c3b2010-09-17 15:00:07 -07002119
2120 final int[] cellXY = new int[2];
Winson Chung3d503fb2011-07-13 17:25:49 -07002121 target.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY);
2122 addInScreen(view, container, screen, cellXY[0], cellXY[1], 1, 1, insertAtFirst);
2123 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screen, cellXY[0],
2124 cellXY[1]);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002125 }
2126
Adam Cohen4b285c52011-07-21 14:24:06 -07002127 public boolean transitionStateShouldAllowDrop() {
2128 return (!isSwitchingState() || mTransitionProgress > 0.5f);
2129 }
2130
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002131 /**
2132 * {@inheritDoc}
2133 */
Adam Cohencb3382b2011-05-24 14:07:08 -07002134 public boolean acceptDrop(DragObject d) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002135 // If it's an external drop (e.g. from All Apps), check if it should be accepted
Adam Cohencb3382b2011-05-24 14:07:08 -07002136 if (d.dragSource != this) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002137 // Don't accept the drop if we're not over a screen at time of drop
Adam Cohena65beee2011-06-27 21:32:23 -07002138 if (mDragTargetLayout == null) {
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002139 return false;
2140 }
Adam Cohen4b285c52011-07-21 14:24:06 -07002141 if (!transitionStateShouldAllowDrop()) return false;
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002142
Adam Cohena65beee2011-06-27 21:32:23 -07002143 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset,
2144 d.dragView, mDragViewVisualCenter);
2145
Winson Chung557d6ed2011-07-08 15:34:52 -07002146 int spanX = 1;
2147 int spanY = 1;
2148 View ignoreView = null;
2149 if (mDragInfo != null) {
2150 final CellLayout.CellInfo dragCellInfo = mDragInfo;
2151 spanX = dragCellInfo.spanX;
2152 spanY = dragCellInfo.spanY;
2153 ignoreView = dragCellInfo.cell;
2154 } else {
2155 final ItemInfo dragInfo = (ItemInfo) d.dragInfo;
2156 spanX = dragInfo.spanX;
2157 spanY = dragInfo.spanY;
2158 }
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002159
Adam Cohena65beee2011-06-27 21:32:23 -07002160 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
2161 (int) mDragViewVisualCenter[1], spanX, spanY, mDragTargetLayout, mTargetCell);
Adam Cohena65beee2011-06-27 21:32:23 -07002162 if (willCreateUserFolder((ItemInfo) d.dragInfo, mDragTargetLayout, mTargetCell, true)) {
2163 return true;
2164 }
2165 if (willAddToExistingUserFolder((ItemInfo) d.dragInfo, mDragTargetLayout,
2166 mTargetCell)) {
2167 return true;
2168 }
2169
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002170
2171 // Don't accept the drop if there's no room for the item
2172 if (!mDragTargetLayout.findCellForSpanIgnoring(null, spanX, spanY, ignoreView)) {
2173 mLauncher.showOutOfSpaceMessage();
2174 return false;
2175 }
2176 }
2177 return true;
2178 }
2179
Adam Cohena65beee2011-06-27 21:32:23 -07002180 boolean willCreateUserFolder(ItemInfo info, CellLayout target, int[] targetCell,
2181 boolean considerTimeout) {
2182 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
2183
Winson Chung3d503fb2011-07-13 17:25:49 -07002184 boolean hasntMoved = false;
2185 if (mDragInfo != null) {
2186 CellLayout cellParent = getParentCellLayoutForView(mDragInfo.cell);
2187 hasntMoved = (mDragInfo.cellX == targetCell[0] &&
2188 mDragInfo.cellY == targetCell[1]) && (cellParent == target);
2189 }
Adam Cohene0310962011-04-18 16:15:31 -07002190
Adam Cohena65beee2011-06-27 21:32:23 -07002191 if (dropOverView == null || hasntMoved || (considerTimeout && !mCreateUserFolderOnDrop)) {
2192 return false;
2193 }
Adam Cohene0310962011-04-18 16:15:31 -07002194
Adam Cohena65beee2011-06-27 21:32:23 -07002195 boolean aboveShortcut = (dropOverView.getTag() instanceof ShortcutInfo);
Adam Cohene0310962011-04-18 16:15:31 -07002196 boolean willBecomeShortcut =
Adam Cohenc0dcf592011-06-01 15:30:43 -07002197 (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
2198 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT);
Adam Cohene0310962011-04-18 16:15:31 -07002199
2200 return (aboveShortcut && willBecomeShortcut);
2201 }
2202
Adam Cohena65beee2011-06-27 21:32:23 -07002203 boolean willAddToExistingUserFolder(Object dragInfo, CellLayout target, int[] targetCell) {
2204 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
2205 if (dropOverView instanceof FolderIcon) {
2206 FolderIcon fi = (FolderIcon) dropOverView;
2207 if (fi.acceptDrop(dragInfo)) {
2208 return true;
2209 }
2210 }
2211 return false;
2212 }
2213
Winson Chung3d503fb2011-07-13 17:25:49 -07002214 boolean createUserFolderIfNecessary(View newView, long container, CellLayout target,
Winson Chung557d6ed2011-07-08 15:34:52 -07002215 int[] targetCell, boolean external, DragView dragView, Runnable postAnimationRunnable) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07002216 View v = target.getChildAt(targetCell[0], targetCell[1]);
Winson Chungec9a0a42011-07-20 20:42:13 -07002217 boolean hasntMoved = false;
2218 if (mDragInfo != null) {
2219 CellLayout cellParent = getParentCellLayoutForView(mDragInfo.cell);
2220 hasntMoved = (mDragInfo.cellX == targetCell[0] &&
2221 mDragInfo.cellY == targetCell[1]) && (cellParent == target);
2222 }
Adam Cohen10b17372011-04-15 14:21:25 -07002223
Adam Cohen19072da2011-05-31 14:30:45 -07002224 if (v == null || hasntMoved || !mCreateUserFolderOnDrop) return false;
2225 mCreateUserFolderOnDrop = false;
Adam Cohenc0dcf592011-06-01 15:30:43 -07002226 final int screen = (targetCell == null) ? mDragInfo.screen : indexOfChild(target);
Adam Cohen10b17372011-04-15 14:21:25 -07002227
2228 boolean aboveShortcut = (v.getTag() instanceof ShortcutInfo);
2229 boolean willBecomeShortcut = (newView.getTag() instanceof ShortcutInfo);
2230
2231 if (aboveShortcut && willBecomeShortcut) {
2232 ShortcutInfo sourceInfo = (ShortcutInfo) newView.getTag();
2233 ShortcutInfo destInfo = (ShortcutInfo) v.getTag();
2234 // if the drag started here, we need to remove it from the workspace
2235 if (!external) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002236 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
Adam Cohen10b17372011-04-15 14:21:25 -07002237 }
2238
Adam Cohend0445262011-07-04 23:53:22 -07002239 Rect folderLocation = new Rect();
Adam Cohenac8c8762011-07-13 11:15:27 -07002240 float scale = mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation);
Adam Cohen10b17372011-04-15 14:21:25 -07002241 target.removeView(v);
Adam Cohend0445262011-07-04 23:53:22 -07002242
Winson Chung3d503fb2011-07-13 17:25:49 -07002243 FolderIcon fi =
2244 mLauncher.addFolder(target, container, screen, targetCell[0], targetCell[1]);
Adam Cohena9cf38f2011-05-02 15:36:58 -07002245 destInfo.cellX = -1;
2246 destInfo.cellY = -1;
2247 sourceInfo.cellX = -1;
2248 sourceInfo.cellY = -1;
Adam Cohend0445262011-07-04 23:53:22 -07002249
Adam Cohenac8c8762011-07-13 11:15:27 -07002250 fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale,
Winson Chung557d6ed2011-07-08 15:34:52 -07002251 postAnimationRunnable);
Adam Cohen10b17372011-04-15 14:21:25 -07002252 return true;
2253 }
2254 return false;
2255 }
2256
Adam Cohena65beee2011-06-27 21:32:23 -07002257 boolean addToExistingFolderIfNecessary(View newView, CellLayout target, int[] targetCell,
Adam Cohen3e8f8112011-07-02 18:03:00 -07002258 DragObject d, boolean external) {
Adam Cohena65beee2011-06-27 21:32:23 -07002259 View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
Adam Cohenc0dcf592011-06-01 15:30:43 -07002260 if (dropOverView instanceof FolderIcon) {
2261 FolderIcon fi = (FolderIcon) dropOverView;
Adam Cohen3e8f8112011-07-02 18:03:00 -07002262 if (fi.acceptDrop(d.dragInfo)) {
2263 fi.onDrop(d);
Adam Cohenc0dcf592011-06-01 15:30:43 -07002264
2265 // if the drag started here, we need to remove it from the workspace
2266 if (!external) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002267 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
Adam Cohenc0dcf592011-06-01 15:30:43 -07002268 }
2269 return true;
2270 }
2271 }
2272 return false;
2273 }
2274
Adam Cohencb3382b2011-05-24 14:07:08 -07002275 public void onDrop(DragObject d) {
Adam Cohencb3382b2011-05-24 14:07:08 -07002276 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, d.dragView,
Adam Cohene3e27a82011-04-15 12:07:39 -07002277 mDragViewVisualCenter);
2278
2279 // We want the point to be mapped to the dragTarget.
Adam Cohenba781612011-05-09 14:37:39 -07002280 if (mDragTargetLayout != null) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002281 if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
2282 mapPointFromSelfToSibling(mLauncher.getHotseat(), mDragViewVisualCenter);
2283 } else {
2284 mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
2285 }
Adam Cohenba781612011-05-09 14:37:39 -07002286 }
Michael Jurkac6ee42e2010-09-30 12:04:50 -07002287
Adam Cohen76078c42011-06-09 15:06:52 -07002288 CellLayout dropTargetLayout = mDragTargetLayout;
Michael Jurkab09cac52011-01-05 20:23:39 -08002289
Adam Cohened51cc92011-08-01 20:28:08 -07002290 int snapScreen = -1;
Adam Cohencb3382b2011-05-24 14:07:08 -07002291 if (d.dragSource != this) {
Adam Cohene3e27a82011-04-15 12:07:39 -07002292 final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0],
2293 (int) mDragViewVisualCenter[1] };
Adam Cohen3e8f8112011-07-02 18:03:00 -07002294 onDropExternal(touchXY, d.dragInfo, dropTargetLayout, false, d);
Patrick Dubroyce34a972010-10-19 10:34:32 -07002295 } else if (mDragInfo != null) {
Patrick Dubroyce34a972010-10-19 10:34:32 -07002296 final View cell = mDragInfo.cell;
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08002297
Winson Chungea359c62011-08-03 17:06:35 -07002298 if (dropTargetLayout != null) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002299 // Move internally
Winson Chung40e882b2011-07-21 19:01:11 -07002300 boolean hasMovedLayouts = (getParentCellLayoutForView(cell) != dropTargetLayout);
2301 boolean hasMovedIntoHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
2302 long container = hasMovedIntoHotseat ?
Winson Chung3d503fb2011-07-13 17:25:49 -07002303 LauncherSettings.Favorites.CONTAINER_HOTSEAT :
2304 LauncherSettings.Favorites.CONTAINER_DESKTOP;
2305 int screen = (mTargetCell[0] < 0) ?
2306 mDragInfo.screen : indexOfChild(dropTargetLayout);
Adam Cohenc0dcf592011-06-01 15:30:43 -07002307 int spanX = mDragInfo != null ? mDragInfo.spanX : 1;
2308 int spanY = mDragInfo != null ? mDragInfo.spanY : 1;
2309 // First we find the cell nearest to point at which the item is
2310 // dropped, without any consideration to whether there is an item there.
2311 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], (int)
2312 mDragViewVisualCenter[1], spanX, spanY, dropTargetLayout, mTargetCell);
2313 // If the item being dropped is a shortcut and the nearest drop
Adam Cohen76078c42011-06-09 15:06:52 -07002314 // cell also contains a shortcut, then create a folder with the two shortcuts.
Winson Chung1c4cf4a2011-07-29 14:49:10 -07002315 if (!mInScrollArea && createUserFolderIfNecessary(cell, container,
Winson Chung3d503fb2011-07-13 17:25:49 -07002316 dropTargetLayout, mTargetCell, false, d.dragView, null)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07002317 return;
2318 }
2319
Adam Cohen3e8f8112011-07-02 18:03:00 -07002320 if (addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell, d, false)) {
Adam Cohendf035382011-04-11 17:22:04 -07002321 return;
2322 }
2323
2324 // Aside from the special case where we're dropping a shortcut onto a shortcut,
2325 // we need to find the nearest cell location that is vacant
Adam Cohene3e27a82011-04-15 12:07:39 -07002326 mTargetCell = findNearestVacantArea((int) mDragViewVisualCenter[0],
2327 (int) mDragViewVisualCenter[1], mDragInfo.spanX, mDragInfo.spanY, cell,
2328 dropTargetLayout, mTargetCell);
Adam Cohendf035382011-04-11 17:22:04 -07002329
Michael Jurka7cfc2822011-08-02 20:19:24 -07002330 if (mCurrentPage != screen && !hasMovedIntoHotseat) {
Adam Cohened51cc92011-08-01 20:28:08 -07002331 snapScreen = screen;
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08002332 snapToPage(screen);
2333 }
2334
Adam Cohenc0dcf592011-06-01 15:30:43 -07002335 if (mTargetCell[0] >= 0 && mTargetCell[1] >= 0) {
Winson Chung40e882b2011-07-21 19:01:11 -07002336 if (hasMovedLayouts) {
Patrick Dubroy94383362010-10-29 15:03:24 -07002337 // Reparent the view
Winson Chung3d503fb2011-07-13 17:25:49 -07002338 getParentCellLayoutForView(cell).removeView(cell);
2339 addInScreen(cell, container, screen, mTargetCell[0], mTargetCell[1],
2340 mDragInfo.spanX, mDragInfo.spanY);
Patrick Dubroy94383362010-10-29 15:03:24 -07002341 }
2342
2343 // update the item's position after drop
2344 final ItemInfo info = (ItemInfo) cell.getTag();
2345 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08002346 dropTargetLayout.onMove(cell, mTargetCell[0], mTargetCell[1]);
Patrick Dubroy94383362010-10-29 15:03:24 -07002347 lp.cellX = mTargetCell[0];
2348 lp.cellY = mTargetCell[1];
Winson Chung3d503fb2011-07-13 17:25:49 -07002349 cell.setId(LauncherModel.getCellLayoutChildId(container, mDragInfo.screen,
Patrick Dubroy94383362010-10-29 15:03:24 -07002350 mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY));
2351
Winson Chung3d503fb2011-07-13 17:25:49 -07002352 if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
2353 cell instanceof LauncherAppWidgetHostView) {
Adam Cohend4844c32011-02-18 19:25:06 -08002354 final CellLayout cellLayout = dropTargetLayout;
2355 // We post this call so that the widget has a chance to be placed
2356 // in its final location
2357
2358 final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell;
2359 AppWidgetProviderInfo pinfo = hostView.getAppWidgetInfo();
Adam Cohen27c09b02011-02-28 14:45:11 -08002360 if (pinfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE) {
Adam Cohen26976d92011-03-22 15:33:33 -07002361 final Runnable resizeRunnable = new Runnable() {
Adam Cohend4844c32011-02-18 19:25:06 -08002362 public void run() {
Adam Cohen716b51e2011-06-30 12:09:54 -07002363 DragLayer dragLayer = mLauncher.getDragLayer();
Adam Cohenc0dcf592011-06-01 15:30:43 -07002364 dragLayer.addResizeFrame(info, hostView, cellLayout);
Adam Cohend4844c32011-02-18 19:25:06 -08002365 }
Adam Cohen26976d92011-03-22 15:33:33 -07002366 };
2367 post(new Runnable() {
2368 public void run() {
2369 if (!isPageMoving()) {
2370 resizeRunnable.run();
2371 } else {
2372 mDelayedResizeRunnable = resizeRunnable;
2373 }
2374 }
Adam Cohend4844c32011-02-18 19:25:06 -08002375 });
2376 }
2377 }
2378
Winson Chung3d503fb2011-07-13 17:25:49 -07002379 LauncherModel.moveItemInDatabase(mLauncher, info, container, screen, lp.cellX,
2380 lp.cellY);
Patrick Dubroycd68ff52010-10-28 17:57:05 -07002381 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002382 }
Patrick Dubroyce34a972010-10-19 10:34:32 -07002383
Michael Jurka8c920dd2011-01-20 14:16:56 -08002384 final CellLayout parent = (CellLayout) cell.getParent().getParent();
Patrick Dubroyce34a972010-10-19 10:34:32 -07002385
2386 // Prepare it to be animated into its new position
2387 // This must be called after the view has been re-parented
Michael Jurkad74c9842011-07-10 12:44:21 -07002388 final Runnable disableHardwareLayersRunnable = new Runnable() {
2389 @Override
2390 public void run() {
2391 mAnimatingViewIntoPlace = false;
2392 updateChildrenLayersEnabled();
2393 }
2394 };
2395 mAnimatingViewIntoPlace = true;
Adam Cohenfc53cd22011-07-20 15:45:11 -07002396 if (d.dragView.hasDrawn()) {
Adam Cohened51cc92011-08-01 20:28:08 -07002397 int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION;
2398 setFinalScrollForPageChange(snapScreen);
2399 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration,
Adam Cohenfc53cd22011-07-20 15:45:11 -07002400 disableHardwareLayersRunnable);
Adam Cohened51cc92011-08-01 20:28:08 -07002401 resetFinalScrollForPageChange(snapScreen);
Adam Cohenfc53cd22011-07-20 15:45:11 -07002402 } else {
2403 cell.setVisibility(VISIBLE);
2404 }
Adam Cohen716b51e2011-06-30 12:09:54 -07002405 parent.onDropChild(cell);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002406 }
2407 }
2408
Adam Cohened51cc92011-08-01 20:28:08 -07002409 public void setFinalScrollForPageChange(int screen) {
2410 if (screen >= 0) {
2411 mSavedScrollX = getScrollX();
2412 CellLayout cl = (CellLayout) getChildAt(screen);
2413 mSavedTranslationX = cl.getTranslationX();
2414 mSavedRotationY = cl.getRotationY();
2415 final int newX = getChildOffset(screen) - getRelativeChildOffset(screen);
2416 setScrollX(newX);
2417 cl.setTranslationX(0f);
2418 cl.setRotationY(0f);
2419 }
2420 }
2421
2422 public void resetFinalScrollForPageChange(int screen) {
2423 if (screen >= 0) {
2424 CellLayout cl = (CellLayout) getChildAt(screen);
2425 setScrollX(mSavedScrollX);
2426 cl.setTranslationX(mSavedTranslationX);
2427 cl.setRotationY(mSavedRotationY);
2428 }
2429 }
2430
Adam Cohen76078c42011-06-09 15:06:52 -07002431 public void getViewLocationRelativeToSelf(View v, int[] location) {
Adam Cohen8dfcba42011-07-07 16:38:18 -07002432 getLocationInWindow(location);
Adam Cohene3e27a82011-04-15 12:07:39 -07002433 int x = location[0];
2434 int y = location[1];
2435
Adam Cohen8dfcba42011-07-07 16:38:18 -07002436 v.getLocationInWindow(location);
Adam Cohene3e27a82011-04-15 12:07:39 -07002437 int vX = location[0];
2438 int vY = location[1];
2439
2440 location[0] = vX - x;
2441 location[1] = vY - y;
2442 }
2443
Adam Cohencb3382b2011-05-24 14:07:08 -07002444 public void onDragEnter(DragObject d) {
Winson Chungb26f3d62011-06-02 10:49:29 -07002445 if (mDragTargetLayout != null) {
Winson Chungc07918d2011-07-01 15:35:26 -07002446 mDragTargetLayout.setIsDragOverlapping(false);
Winson Chungb26f3d62011-06-02 10:49:29 -07002447 mDragTargetLayout.onDragExit();
Winson Chungb26f3d62011-06-02 10:49:29 -07002448 }
Winson Chungc07918d2011-07-01 15:35:26 -07002449 mDragTargetLayout = getCurrentDropLayout();
2450 mDragTargetLayout.setIsDragOverlapping(true);
2451 mDragTargetLayout.onDragEnter();
Winson Chungb26f3d62011-06-02 10:49:29 -07002452
Winson Chungc07918d2011-07-01 15:35:26 -07002453 // Because we don't have space in the Phone UI (the CellLayouts run to the edge) we
2454 // don't need to show the outlines
2455 if (LauncherApplication.isScreenLarge()) {
2456 showOutlines();
Michael Jurkad718d6a2010-10-14 15:35:17 -07002457 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002458 }
2459
Winson Chungc07918d2011-07-01 15:35:26 -07002460 private void doDragExit(DragObject d) {
2461 // Clean up folders
2462 cleanupFolderCreation(d);
2463
2464 // Reset the scroll area and previous drag target
2465 onResetScrollArea();
2466
2467 if (mDragTargetLayout != null) {
2468 mDragTargetLayout.setIsDragOverlapping(false);
2469 mDragTargetLayout.onDragExit();
2470 }
Winson Chungc07918d2011-07-01 15:35:26 -07002471 mLastDragOverView = null;
2472
2473 if (!mIsPageMoving) {
2474 hideOutlines();
2475 }
2476 }
2477
2478 public void onDragExit(DragObject d) {
2479 doDragExit(d);
2480 }
2481
Adam Cohencb3382b2011-05-24 14:07:08 -07002482 public DropTarget getDropTargetDelegate(DragObject d) {
Patrick Dubroy440c3602010-07-13 17:50:32 -07002483 return null;
2484 }
2485
Winson Chunga9abd0e2010-10-27 17:18:37 -07002486 /**
Winson Chung580e2772010-11-10 16:03:00 -08002487 * Tests to see if the drop will be accepted by Launcher, and if so, includes additional data
2488 * in the returned structure related to the widgets that match the drop (or a null list if it is
2489 * a shortcut drop). If the drop is not accepted then a null structure is returned.
2490 */
2491 private Pair<Integer, List<WidgetMimeTypeHandlerData>> validateDrag(DragEvent event) {
2492 final LauncherModel model = mLauncher.getModel();
2493 final ClipDescription desc = event.getClipDescription();
2494 final int mimeTypeCount = desc.getMimeTypeCount();
2495 for (int i = 0; i < mimeTypeCount; ++i) {
2496 final String mimeType = desc.getMimeType(i);
2497 if (mimeType.equals(InstallShortcutReceiver.SHORTCUT_MIMETYPE)) {
2498 return new Pair<Integer, List<WidgetMimeTypeHandlerData>>(i, null);
2499 } else {
2500 final List<WidgetMimeTypeHandlerData> widgets =
2501 model.resolveWidgetsForMimeType(mContext, mimeType);
2502 if (widgets.size() > 0) {
2503 return new Pair<Integer, List<WidgetMimeTypeHandlerData>>(i, widgets);
2504 }
2505 }
2506 }
2507 return null;
2508 }
2509
2510 /**
Winson Chunga9abd0e2010-10-27 17:18:37 -07002511 * Global drag and drop handler
2512 */
2513 @Override
2514 public boolean onDragEvent(DragEvent event) {
Winson Chung68846fd2010-10-29 11:00:27 -07002515 final ClipDescription desc = event.getClipDescription();
Winson Chunga9abd0e2010-10-27 17:18:37 -07002516 final CellLayout layout = (CellLayout) getChildAt(mCurrentPage);
2517 final int[] pos = new int[2];
2518 layout.getLocationOnScreen(pos);
2519 // We need to offset the drag coordinates to layout coordinate space
2520 final int x = (int) event.getX() - pos[0];
2521 final int y = (int) event.getY() - pos[1];
2522
2523 switch (event.getAction()) {
Winson Chung580e2772010-11-10 16:03:00 -08002524 case DragEvent.ACTION_DRAG_STARTED: {
2525 // Validate this drag
2526 Pair<Integer, List<WidgetMimeTypeHandlerData>> test = validateDrag(event);
2527 if (test != null) {
2528 boolean isShortcut = (test.second == null);
2529 if (isShortcut) {
2530 // Check if we have enough space on this screen to add a new shortcut
2531 if (!layout.findCellForSpan(pos, 1, 1)) {
Winson Chung557d6ed2011-07-08 15:34:52 -07002532 mLauncher.showOutOfSpaceMessage();
Winson Chung580e2772010-11-10 16:03:00 -08002533 return false;
2534 }
2535 }
2536 } else {
2537 // Show error message if we couldn't accept any of the items
2538 Toast.makeText(mContext, mContext.getString(R.string.external_drop_widget_error),
Winson Chunga9abd0e2010-10-27 17:18:37 -07002539 Toast.LENGTH_SHORT).show();
2540 return false;
2541 }
2542
Winson Chung68846fd2010-10-29 11:00:27 -07002543 // Create the drag outline
2544 // We need to add extra padding to the bitmap to make room for the glow effect
2545 final Canvas canvas = new Canvas();
Michael Jurka38b4f7c2010-12-14 16:46:39 -08002546 final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;
Winson Chung68846fd2010-10-29 11:00:27 -07002547 mDragOutline = createExternalDragOutline(canvas, bitmapPadding);
Winson Chunga9abd0e2010-10-27 17:18:37 -07002548
Winson Chung68846fd2010-10-29 11:00:27 -07002549 // Show the current page outlines to indicate that we can accept this drop
2550 showOutlines();
Michael Jurka33945b22010-12-21 18:19:38 -08002551 layout.setIsDragOccuring(true);
Winson Chung68846fd2010-10-29 11:00:27 -07002552 layout.onDragEnter();
2553 layout.visualizeDropLocation(null, mDragOutline, x, y, 1, 1);
Winson Chunga9abd0e2010-10-27 17:18:37 -07002554
Winson Chung68846fd2010-10-29 11:00:27 -07002555 return true;
Winson Chung580e2772010-11-10 16:03:00 -08002556 }
Winson Chunga9abd0e2010-10-27 17:18:37 -07002557 case DragEvent.ACTION_DRAG_LOCATION:
2558 // Visualize the drop location
2559 layout.visualizeDropLocation(null, mDragOutline, x, y, 1, 1);
2560 return true;
Winson Chung580e2772010-11-10 16:03:00 -08002561 case DragEvent.ACTION_DROP: {
Winson Chunga9abd0e2010-10-27 17:18:37 -07002562 // Try and add any shortcuts
Winson Chunga9abd0e2010-10-27 17:18:37 -07002563 final LauncherModel model = mLauncher.getModel();
2564 final ClipData data = event.getClipData();
Winson Chung55cef262010-10-28 14:14:18 -07002565
Winson Chung68846fd2010-10-29 11:00:27 -07002566 // We assume that the mime types are ordered in descending importance of
2567 // representation. So we enumerate the list of mime types and alert the
2568 // user if any widgets can handle the drop. Only the most preferred
2569 // representation will be handled.
2570 pos[0] = x;
2571 pos[1] = y;
Winson Chung580e2772010-11-10 16:03:00 -08002572 Pair<Integer, List<WidgetMimeTypeHandlerData>> test = validateDrag(event);
2573 if (test != null) {
2574 final int index = test.first;
2575 final List<WidgetMimeTypeHandlerData> widgets = test.second;
2576 final boolean isShortcut = (widgets == null);
2577 final String mimeType = desc.getMimeType(index);
2578 if (isShortcut) {
Dianne Hackborn0d5aad72011-01-17 15:28:58 -08002579 final Intent intent = data.getItemAt(index).getIntent();
Winson Chung68846fd2010-10-29 11:00:27 -07002580 Object info = model.infoFromShortcutIntent(mContext, intent, data.getIcon());
Michael Jurkac4e772e2011-02-10 13:32:01 -08002581 onDropExternal(new int[] { x, y }, info, layout, false);
Winson Chung68846fd2010-10-29 11:00:27 -07002582 } else {
Winson Chung580e2772010-11-10 16:03:00 -08002583 if (widgets.size() == 1) {
Winson Chung68846fd2010-10-29 11:00:27 -07002584 // If there is only one item, then go ahead and add and configure
2585 // that widget
2586 final AppWidgetProviderInfo widgetInfo = widgets.get(0).widgetInfo;
2587 final PendingAddWidgetInfo createInfo =
2588 new PendingAddWidgetInfo(widgetInfo, mimeType, data);
Winson Chung3d503fb2011-07-13 17:25:49 -07002589 mLauncher.addAppWidgetFromDrop(createInfo,
2590 LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentPage, null, pos);
Winson Chung580e2772010-11-10 16:03:00 -08002591 } else {
Winson Chung68846fd2010-10-29 11:00:27 -07002592 // Show the widget picker dialog if there is more than one widget
2593 // that can handle this data type
2594 final InstallWidgetReceiver.WidgetListAdapter adapter =
2595 new InstallWidgetReceiver.WidgetListAdapter(mLauncher, mimeType,
2596 data, widgets, layout, mCurrentPage, pos);
2597 final AlertDialog.Builder builder =
2598 new AlertDialog.Builder(mContext);
2599 builder.setAdapter(adapter, adapter);
2600 builder.setCancelable(true);
2601 builder.setTitle(mContext.getString(
2602 R.string.external_drop_widget_pick_title));
2603 builder.setIcon(R.drawable.ic_no_applications);
2604 builder.show();
Winson Chung55cef262010-10-28 14:14:18 -07002605 }
Winson Chunga9abd0e2010-10-27 17:18:37 -07002606 }
2607 }
Winson Chunga9abd0e2010-10-27 17:18:37 -07002608 return true;
Winson Chung580e2772010-11-10 16:03:00 -08002609 }
Winson Chunga9abd0e2010-10-27 17:18:37 -07002610 case DragEvent.ACTION_DRAG_ENDED:
2611 // Hide the page outlines after the drop
Michael Jurka33945b22010-12-21 18:19:38 -08002612 layout.setIsDragOccuring(false);
Winson Chunga9abd0e2010-10-27 17:18:37 -07002613 layout.onDragExit();
2614 hideOutlines();
2615 return true;
2616 }
2617 return super.onDragEvent(event);
2618 }
2619
Michael Jurka4516c112010-10-07 15:13:47 -07002620 /*
2621 *
2622 * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
2623 * coordinate space. The argument xy is modified with the return result.
2624 *
2625 */
2626 void mapPointFromSelfToChild(View v, float[] xy) {
2627 mapPointFromSelfToChild(v, xy, null);
2628 }
2629
2630 /*
2631 *
2632 * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
2633 * coordinate space. The argument xy is modified with the return result.
2634 *
2635 * if cachedInverseMatrix is not null, this method will just use that matrix instead of
Michael Jurkad718d6a2010-10-14 15:35:17 -07002636 * computing it itself; we use this to avoid redundant matrix inversions in
Michael Jurka4516c112010-10-07 15:13:47 -07002637 * findMatchingPageForDragOver
2638 *
2639 */
2640 void mapPointFromSelfToChild(View v, float[] xy, Matrix cachedInverseMatrix) {
2641 if (cachedInverseMatrix == null) {
2642 v.getMatrix().invert(mTempInverseMatrix);
2643 cachedInverseMatrix = mTempInverseMatrix;
2644 }
2645 xy[0] = xy[0] + mScrollX - v.getLeft();
2646 xy[1] = xy[1] + mScrollY - v.getTop();
2647 cachedInverseMatrix.mapPoints(xy);
2648 }
2649
2650 /*
Winson Chung3d503fb2011-07-13 17:25:49 -07002651 * Maps a point from the Workspace's coordinate system to another sibling view's. (Workspace
2652 * covers the full screen)
2653 */
2654 void mapPointFromSelfToSibling(View v, float[] xy) {
2655 xy[0] = xy[0] - v.getLeft();
2656 xy[1] = xy[1] - v.getTop();
2657 }
2658
2659 /*
Michael Jurka4516c112010-10-07 15:13:47 -07002660 *
2661 * Convert the 2D coordinate xy from this CellLayout's coordinate space to
2662 * the parent View's coordinate space. The argument xy is modified with the return result.
2663 *
2664 */
2665 void mapPointFromChildToSelf(View v, float[] xy) {
2666 v.getMatrix().mapPoints(xy);
2667 xy[0] -= (mScrollX - v.getLeft());
2668 xy[1] -= (mScrollY - v.getTop());
2669 }
2670
Adam Cohene3e27a82011-04-15 12:07:39 -07002671 static private float squaredDistance(float[] point1, float[] point2) {
Michael Jurka4516c112010-10-07 15:13:47 -07002672 float distanceX = point1[0] - point2[0];
2673 float distanceY = point2[1] - point2[1];
2674 return distanceX * distanceX + distanceY * distanceY;
Adam Cohene3e27a82011-04-15 12:07:39 -07002675 }
Michael Jurka0280c3b2010-09-17 15:00:07 -07002676
Michael Jurka4516c112010-10-07 15:13:47 -07002677 /*
2678 *
2679 * Returns true if the passed CellLayout cl overlaps with dragView
2680 *
2681 */
2682 boolean overlaps(CellLayout cl, DragView dragView,
2683 int dragViewX, int dragViewY, Matrix cachedInverseMatrix) {
2684 // Transform the coordinates of the item being dragged to the CellLayout's coordinates
2685 final float[] draggedItemTopLeft = mTempDragCoordinates;
Michael Jurkaf12c75c2011-01-25 22:41:40 -08002686 draggedItemTopLeft[0] = dragViewX;
2687 draggedItemTopLeft[1] = dragViewY;
Michael Jurka4516c112010-10-07 15:13:47 -07002688 final float[] draggedItemBottomRight = mTempDragBottomRightCoordinates;
Michael Jurkaf12c75c2011-01-25 22:41:40 -08002689 draggedItemBottomRight[0] = draggedItemTopLeft[0] + dragView.getDragRegionWidth();
2690 draggedItemBottomRight[1] = draggedItemTopLeft[1] + dragView.getDragRegionHeight();
Michael Jurkaa63c4522010-08-19 13:52:27 -07002691
Michael Jurka4516c112010-10-07 15:13:47 -07002692 // Transform the dragged item's top left coordinates
2693 // to the CellLayout's local coordinates
2694 mapPointFromSelfToChild(cl, draggedItemTopLeft, cachedInverseMatrix);
2695 float overlapRegionLeft = Math.max(0f, draggedItemTopLeft[0]);
2696 float overlapRegionTop = Math.max(0f, draggedItemTopLeft[1]);
2697
2698 if (overlapRegionLeft <= cl.getWidth() && overlapRegionTop >= 0) {
2699 // Transform the dragged item's bottom right coordinates
2700 // to the CellLayout's local coordinates
2701 mapPointFromSelfToChild(cl, draggedItemBottomRight, cachedInverseMatrix);
2702 float overlapRegionRight = Math.min(cl.getWidth(), draggedItemBottomRight[0]);
2703 float overlapRegionBottom = Math.min(cl.getHeight(), draggedItemBottomRight[1]);
2704
2705 if (overlapRegionRight >= 0 && overlapRegionBottom <= cl.getHeight()) {
2706 float overlap = (overlapRegionRight - overlapRegionLeft) *
2707 (overlapRegionBottom - overlapRegionTop);
2708 if (overlap > 0) {
2709 return true;
2710 }
2711 }
2712 }
2713 return false;
2714 }
2715
2716 /*
2717 *
2718 * This method returns the CellLayout that is currently being dragged to. In order to drag
2719 * to a CellLayout, either the touch point must be directly over the CellLayout, or as a second
2720 * strategy, we see if the dragView is overlapping any CellLayout and choose the closest one
2721 *
2722 * Return null if no CellLayout is currently being dragged over
2723 *
2724 */
2725 private CellLayout findMatchingPageForDragOver(
Adam Cohen00618752011-07-20 12:06:04 -07002726 DragView dragView, float originX, float originY, boolean exact) {
Michael Jurka4516c112010-10-07 15:13:47 -07002727 // We loop through all the screens (ie CellLayouts) and see which ones overlap
2728 // with the item being dragged and then choose the one that's closest to the touch point
Michael Jurkaa63c4522010-08-19 13:52:27 -07002729 final int screenCount = getChildCount();
2730 CellLayout bestMatchingScreen = null;
Michael Jurka0280c3b2010-09-17 15:00:07 -07002731 float smallestDistSoFar = Float.MAX_VALUE;
Michael Jurka4516c112010-10-07 15:13:47 -07002732
Michael Jurkaa63c4522010-08-19 13:52:27 -07002733 for (int i = 0; i < screenCount; i++) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002734 CellLayout cl = (CellLayout) getChildAt(i);
Michael Jurkaa63c4522010-08-19 13:52:27 -07002735
Adam Cohen00618752011-07-20 12:06:04 -07002736 final float[] touchXy = {originX, originY};
Michael Jurka4516c112010-10-07 15:13:47 -07002737 // Transform the touch coordinates to the CellLayout's local coordinates
2738 // If the touch point is within the bounds of the cell layout, we can return immediately
Michael Jurka0280c3b2010-09-17 15:00:07 -07002739 cl.getMatrix().invert(mTempInverseMatrix);
Michael Jurka4516c112010-10-07 15:13:47 -07002740 mapPointFromSelfToChild(cl, touchXy, mTempInverseMatrix);
Michael Jurkaa63c4522010-08-19 13:52:27 -07002741
Michael Jurka4516c112010-10-07 15:13:47 -07002742 if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() &&
2743 touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) {
2744 return cl;
2745 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07002746
Adam Cohen00618752011-07-20 12:06:04 -07002747 if (!exact && overlaps(cl, dragView, (int) originX, (int) originY, mTempInverseMatrix)) {
Michael Jurka4516c112010-10-07 15:13:47 -07002748 // Get the center of the cell layout in screen coordinates
2749 final float[] cellLayoutCenter = mTempCellLayoutCenterCoordinates;
2750 cellLayoutCenter[0] = cl.getWidth()/2;
2751 cellLayoutCenter[1] = cl.getHeight()/2;
2752 mapPointFromChildToSelf(cl, cellLayoutCenter);
Michael Jurka0280c3b2010-09-17 15:00:07 -07002753
Adam Cohen00618752011-07-20 12:06:04 -07002754 touchXy[0] = originX;
2755 touchXy[1] = originY;
Michael Jurka0280c3b2010-09-17 15:00:07 -07002756
Michael Jurka4516c112010-10-07 15:13:47 -07002757 // Calculate the distance between the center of the CellLayout
2758 // and the touch point
2759 float dist = squaredDistance(touchXy, cellLayoutCenter);
2760
2761 if (dist < smallestDistSoFar) {
Michael Jurka0280c3b2010-09-17 15:00:07 -07002762 smallestDistSoFar = dist;
Michael Jurkaa63c4522010-08-19 13:52:27 -07002763 bestMatchingScreen = cl;
Michael Jurkaa63c4522010-08-19 13:52:27 -07002764 }
Michael Jurka4516c112010-10-07 15:13:47 -07002765 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07002766 }
Michael Jurkaa63c4522010-08-19 13:52:27 -07002767 return bestMatchingScreen;
2768 }
2769
Adam Cohene3e27a82011-04-15 12:07:39 -07002770 // This is used to compute the visual center of the dragView. This point is then
2771 // used to visualize drop locations and determine where to drop an item. The idea is that
2772 // the visual center represents the user's interpretation of where the item is, and hence
2773 // is the appropriate point to use when determining drop location.
2774 private float[] getDragViewVisualCenter(int x, int y, int xOffset, int yOffset,
2775 DragView dragView, float[] recycle) {
2776 float res[];
2777 if (recycle == null) {
2778 res = new float[2];
2779 } else {
2780 res = recycle;
2781 }
2782
2783 // First off, the drag view has been shifted in a way that is not represented in the
2784 // x and y values or the x/yOffsets. Here we account for that shift.
2785 x += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetX);
2786 y += getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY);
2787
2788 // These represent the visual top and left of drag view if a dragRect was provided.
2789 // If a dragRect was not provided, then they correspond to the actual view left and
2790 // top, as the dragRect is in that case taken to be the entire dragView.
2791 // R.dimen.dragViewOffsetY.
2792 int left = x - xOffset;
2793 int top = y - yOffset;
2794
2795 // In order to find the visual center, we shift by half the dragRect
2796 res[0] = left + dragView.getDragRegion().width() / 2;
2797 res[1] = top + dragView.getDragRegion().height() / 2;
2798
2799 return res;
2800 }
2801
Winson Chungea359c62011-08-03 17:06:35 -07002802 private boolean isDragWidget(DragObject d) {
2803 return (d.dragInfo instanceof LauncherAppWidgetInfo ||
2804 d.dragInfo instanceof PendingAddWidgetInfo);
2805 }
2806 private boolean isExternalDragWidget(DragObject d) {
2807 return d.dragSource != this && isDragWidget(d);
2808 }
2809
Adam Cohencb3382b2011-05-24 14:07:08 -07002810 public void onDragOver(DragObject d) {
Winson Chungc07918d2011-07-01 15:35:26 -07002811 // Skip drag over events while we are dragging over side pages
2812 if (mInScrollArea) return;
Michael Jurkad74c9842011-07-10 12:44:21 -07002813 if (mIsSwitchingState) return;
Winson Chungc07918d2011-07-01 15:35:26 -07002814
Winson Chung4afe9b32011-07-27 17:46:20 -07002815 Rect r = new Rect();
Winson Chung3d503fb2011-07-13 17:25:49 -07002816 CellLayout layout = null;
Winson Chungc07918d2011-07-01 15:35:26 -07002817 ItemInfo item = (ItemInfo) d.dragInfo;
2818
2819 // Ensure that we have proper spans for the item that we are dropping
2820 if (item.spanX < 0 || item.spanY < 0) throw new RuntimeException("Improper spans found");
Adam Cohen00618752011-07-20 12:06:04 -07002821 mDragViewVisualCenter = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset,
Winson Chung4afe9b32011-07-27 17:46:20 -07002822 d.dragView, mDragViewVisualCenter);
Adam Cohen00618752011-07-20 12:06:04 -07002823
Winson Chungc07918d2011-07-01 15:35:26 -07002824 // Identify whether we have dragged over a side page
Michael Jurkad74c9842011-07-10 12:44:21 -07002825 if (isSmall()) {
Winson Chungea359c62011-08-03 17:06:35 -07002826 if (mLauncher.getHotseat() != null && !isExternalDragWidget(d)) {
Winson Chung4afe9b32011-07-27 17:46:20 -07002827 mLauncher.getHotseat().getHitRect(r);
2828 if (r.contains(d.x, d.y)) {
2829 layout = mLauncher.getHotseat().getLayout();
2830 }
2831 }
2832 if (layout == null) {
2833 layout = findMatchingPageForDragOver(d.dragView, mDragViewVisualCenter[0],
Adam Cohen00618752011-07-20 12:06:04 -07002834 mDragViewVisualCenter[1], true);
Winson Chung4afe9b32011-07-27 17:46:20 -07002835 }
Winson Chungc07918d2011-07-01 15:35:26 -07002836 if (layout != mDragTargetLayout) {
2837 // Cancel all intermediate folder states
2838 cleanupFolderCreation(d);
2839
2840 if (mDragTargetLayout != null) {
2841 mDragTargetLayout.setIsDragOverlapping(false);
2842 mDragTargetLayout.onDragExit();
2843 }
2844 mDragTargetLayout = layout;
2845 if (mDragTargetLayout != null) {
2846 mDragTargetLayout.setIsDragOverlapping(true);
2847 mDragTargetLayout.onDragEnter();
2848 } else {
2849 mLastDragOverView = null;
2850 }
2851
Michael Jurkad74c9842011-07-10 12:44:21 -07002852 boolean isInSpringLoadedMode = (mState == State.SPRING_LOADED);
Winson Chungc07918d2011-07-01 15:35:26 -07002853 if (isInSpringLoadedMode) {
Winson Chung4afe9b32011-07-27 17:46:20 -07002854 if (mLauncher.isHotseatLayout(layout)) {
2855 mSpringLoadedDragController.cancel();
2856 } else {
2857 mSpringLoadedDragController.setAlarm(mDragTargetLayout);
2858 }
Winson Chungc07918d2011-07-01 15:35:26 -07002859 }
2860 }
2861 } else {
Winson Chung3d503fb2011-07-13 17:25:49 -07002862 // Test to see if we are over the hotseat otherwise just use the current page
Winson Chungea359c62011-08-03 17:06:35 -07002863 if (mLauncher.getHotseat() != null && !isDragWidget(d)) {
Winson Chung3d503fb2011-07-13 17:25:49 -07002864 mLauncher.getHotseat().getHitRect(r);
2865 if (r.contains(d.x, d.y)) {
2866 layout = mLauncher.getHotseat().getLayout();
2867 }
2868 }
2869 if (layout == null) {
2870 layout = getCurrentDropLayout();
2871 }
Winson Chungc07918d2011-07-01 15:35:26 -07002872 if (layout != mDragTargetLayout) {
2873 if (mDragTargetLayout != null) {
2874 mDragTargetLayout.setIsDragOverlapping(false);
2875 mDragTargetLayout.onDragExit();
2876 }
2877 mDragTargetLayout = layout;
2878 mDragTargetLayout.setIsDragOverlapping(true);
2879 mDragTargetLayout.onDragEnter();
2880 }
2881 }
2882
2883 // Handle the drag over
2884 if (mDragTargetLayout != null) {
2885 final View child = (mDragInfo == null) ? null : mDragInfo.cell;
Adam Cohene3e27a82011-04-15 12:07:39 -07002886
Winson Chungc07918d2011-07-01 15:35:26 -07002887 // We want the point to be mapped to the dragTarget.
Winson Chung3d503fb2011-07-13 17:25:49 -07002888 if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
2889 mapPointFromSelfToSibling(mLauncher.getHotseat(), mDragViewVisualCenter);
2890 } else {
2891 mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
2892 }
Winson Chungc07918d2011-07-01 15:35:26 -07002893 ItemInfo info = (ItemInfo) d.dragInfo;
Patrick Dubroy1262e362010-10-06 15:49:50 -07002894
Winson Chungc07918d2011-07-01 15:35:26 -07002895 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
2896 (int) mDragViewVisualCenter[1], 1, 1, mDragTargetLayout, mTargetCell);
2897 final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0],
2898 mTargetCell[1]);
Winson Chung785d2eb2011-04-14 16:08:02 -07002899
Winson Chungc07918d2011-07-01 15:35:26 -07002900 boolean userFolderPending = willCreateUserFolder(info, mDragTargetLayout,
2901 mTargetCell, false);
2902 boolean isOverFolder = dragOverView instanceof FolderIcon;
2903 if (dragOverView != mLastDragOverView) {
2904 cancelFolderCreation();
2905 if (mLastDragOverView != null && mLastDragOverView instanceof FolderIcon) {
2906 ((FolderIcon) mLastDragOverView).onDragExit(d.dragInfo);
Michael Jurkad3ef3062010-11-23 16:23:58 -08002907 }
2908 }
Michael Jurkad718d6a2010-10-14 15:35:17 -07002909
Winson Chungc07918d2011-07-01 15:35:26 -07002910 if (userFolderPending && dragOverView != mLastDragOverView) {
2911 mFolderCreationAlarm.setOnAlarmListener(new
2912 FolderCreationAlarmListener(mDragTargetLayout, mTargetCell[0], mTargetCell[1]));
2913 mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT);
2914 }
Michael Jurkad718d6a2010-10-14 15:35:17 -07002915
Winson Chungc07918d2011-07-01 15:35:26 -07002916 if (dragOverView != mLastDragOverView && isOverFolder) {
2917 ((FolderIcon) dragOverView).onDragEnter(d.dragInfo);
Michael Jurkad3ef3062010-11-23 16:23:58 -08002918 if (mDragTargetLayout != null) {
Winson Chungc07918d2011-07-01 15:35:26 -07002919 mDragTargetLayout.clearDragOutlines();
Michael Jurkad718d6a2010-10-14 15:35:17 -07002920 }
Patrick Dubroy1262e362010-10-06 15:49:50 -07002921 }
Winson Chungc07918d2011-07-01 15:35:26 -07002922 mLastDragOverView = dragOverView;
2923
2924 if (!mCreateUserFolderOnDrop && !isOverFolder) {
2925 mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
2926 (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
2927 item.spanX, item.spanY);
2928 }
Patrick Dubroy976ebec2010-08-04 20:03:37 -07002929 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002930 }
2931
Winson Chungc07918d2011-07-01 15:35:26 -07002932 private void cleanupFolderCreation(DragObject d) {
2933 if (mDragFolderRingAnimator != null && mCreateUserFolderOnDrop) {
2934 mDragFolderRingAnimator.animateToNaturalState();
2935 }
2936 if (mLastDragOverView != null && mLastDragOverView instanceof FolderIcon) {
2937 if (d != null) {
2938 ((FolderIcon) mLastDragOverView).onDragExit(d.dragInfo);
2939 }
2940 }
2941 mFolderCreationAlarm.cancelAlarm();
2942 }
2943
Adam Cohenc0dcf592011-06-01 15:30:43 -07002944 private void cancelFolderCreation() {
2945 if (mDragFolderRingAnimator != null && mCreateUserFolderOnDrop) {
2946 mDragFolderRingAnimator.animateToNaturalState();
2947 }
2948 mCreateUserFolderOnDrop = false;
2949 mFolderCreationAlarm.cancelAlarm();
2950 }
2951
Adam Cohen19072da2011-05-31 14:30:45 -07002952 class FolderCreationAlarmListener implements OnAlarmListener {
Adam Cohen69ce2e52011-07-03 19:25:21 -07002953 CellLayout layout;
2954 int cellX;
2955 int cellY;
Adam Cohen19072da2011-05-31 14:30:45 -07002956
Adam Cohen69ce2e52011-07-03 19:25:21 -07002957 public FolderCreationAlarmListener(CellLayout layout, int cellX, int cellY) {
2958 this.layout = layout;
2959 this.cellX = cellX;
2960 this.cellY = cellY;
Adam Cohen19072da2011-05-31 14:30:45 -07002961 }
2962
2963 public void onAlarm(Alarm alarm) {
Adam Cohen19072da2011-05-31 14:30:45 -07002964 if (mDragFolderRingAnimator == null) {
2965 mDragFolderRingAnimator = new FolderRingAnimator(mLauncher, null);
2966 }
Adam Cohen69ce2e52011-07-03 19:25:21 -07002967 mDragFolderRingAnimator.setCell(cellX, cellY);
2968 mDragFolderRingAnimator.setCellLayout(layout);
Adam Cohen19072da2011-05-31 14:30:45 -07002969 mDragFolderRingAnimator.animateToAcceptState();
Adam Cohen69ce2e52011-07-03 19:25:21 -07002970 layout.showFolderAccept(mDragFolderRingAnimator);
2971 layout.clearDragOutlines();
Adam Cohen19072da2011-05-31 14:30:45 -07002972 mCreateUserFolderOnDrop = true;
Adam Cohen19072da2011-05-31 14:30:45 -07002973 }
2974 }
2975
Winson Chunga34abf82010-11-12 12:10:35 -08002976 @Override
2977 public void getHitRect(Rect outRect) {
2978 // We want the workspace to have the whole area of the display (it will find the correct
2979 // cell layout to drop to in the existing drag/drop logic.
2980 final Display d = mLauncher.getWindowManager().getDefaultDisplay();
2981 outRect.set(0, 0, d.getWidth(), d.getHeight());
2982 }
2983
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07002984 /**
2985 * Add the item specified by dragInfo to the given layout.
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07002986 * @return true if successful
2987 */
Adam Cohen120980b2010-12-08 11:05:37 -08002988 public boolean addExternalItemToScreen(ItemInfo dragInfo, CellLayout layout) {
2989 if (layout.findCellForSpan(mTempEstimate, dragInfo.spanX, dragInfo.spanY)) {
Patrick Dubroybbaa75c2011-03-08 18:47:40 -08002990 onDropExternal(dragInfo.dropPos, (ItemInfo) dragInfo, (CellLayout) layout, false);
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07002991 return true;
2992 }
Michael Jurka0280c3b2010-09-17 15:00:07 -07002993 mLauncher.showOutOfSpaceMessage();
Patrick Dubroy2b9ff372010-09-07 17:49:27 -07002994 return false;
2995 }
2996
Adam Cohend5e42732011-03-28 17:33:39 -07002997 private void onDropExternal(int[] touchXY, Object dragInfo,
2998 CellLayout cellLayout, boolean insertAtFirst) {
Adam Cohene3e27a82011-04-15 12:07:39 -07002999 onDropExternal(touchXY, dragInfo, cellLayout, insertAtFirst, null);
Adam Cohend5e42732011-03-28 17:33:39 -07003000 }
3001
Adam Cohen120980b2010-12-08 11:05:37 -08003002 /**
3003 * Drop an item that didn't originate on one of the workspace screens.
3004 * It may have come from Launcher (e.g. from all apps or customize), or it may have
3005 * come from another app altogether.
3006 *
3007 * NOTE: This can also be called when we are outside of a drag event, when we want
3008 * to add an item to one of the workspace screens.
3009 */
Winson Chung557d6ed2011-07-08 15:34:52 -07003010 private void onDropExternal(final int[] touchXY, final Object dragInfo,
3011 final CellLayout cellLayout, boolean insertAtFirst, DragObject d) {
3012 final Runnable exitSpringLoadedRunnable = new Runnable() {
3013 @Override
3014 public void run() {
Winson Chung6a3fd3f2011-08-02 14:03:26 -07003015 mLauncher.exitSpringLoadedDragModeDelayed(true, false);
Winson Chung557d6ed2011-07-08 15:34:52 -07003016 }
3017 };
Adam Cohenb7e16182011-07-15 17:55:02 -07003018
3019 ItemInfo info = (ItemInfo) dragInfo;
3020 int spanX = info.spanX;
3021 int spanY = info.spanY;
3022 if (mDragInfo != null) {
3023 spanX = mDragInfo.spanX;
3024 spanY = mDragInfo.spanY;
3025 }
3026
Winson Chung3d503fb2011-07-13 17:25:49 -07003027 final long container = mLauncher.isHotseatLayout(cellLayout) ?
3028 LauncherSettings.Favorites.CONTAINER_HOTSEAT :
3029 LauncherSettings.Favorites.CONTAINER_DESKTOP;
Winson Chung557d6ed2011-07-08 15:34:52 -07003030 final int screen = indexOfChild(cellLayout);
Winson Chung3d503fb2011-07-13 17:25:49 -07003031 if (!mLauncher.isHotseatLayout(cellLayout) && screen != mCurrentPage
3032 && mState != State.SPRING_LOADED) {
Adam Cohen76078c42011-06-09 15:06:52 -07003033 snapToPage(screen);
3034 }
Adam Cohenb7e16182011-07-15 17:55:02 -07003035
3036 if (info instanceof PendingAddItemInfo) {
3037 final PendingAddItemInfo pendingInfo = (PendingAddItemInfo) dragInfo;
3038
Adam Cohenfbba09b2011-07-18 21:43:05 -07003039 mTargetCell = findNearestVacantArea(touchXY[0], touchXY[1], spanX, spanY, null,
3040 cellLayout, mTargetCell);
Adam Cohenb7e16182011-07-15 17:55:02 -07003041 Runnable onAnimationCompleteRunnable = new Runnable() {
Winson Chung557d6ed2011-07-08 15:34:52 -07003042 @Override
3043 public void run() {
3044 // When dragging and dropping from customization tray, we deal with creating
3045 // widgets/shortcuts/folders in a slightly different way
Adam Cohenb7e16182011-07-15 17:55:02 -07003046 switch (pendingInfo.itemType) {
Winson Chung557d6ed2011-07-08 15:34:52 -07003047 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
Adam Cohenb7e16182011-07-15 17:55:02 -07003048 mLauncher.addAppWidgetFromDrop((PendingAddWidgetInfo) pendingInfo,
Winson Chung3d503fb2011-07-13 17:25:49 -07003049 container, screen, mTargetCell, null);
Winson Chung557d6ed2011-07-08 15:34:52 -07003050 break;
3051 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
Adam Cohenb7e16182011-07-15 17:55:02 -07003052 mLauncher.processShortcutFromDrop(pendingInfo.componentName,
Winson Chung3d503fb2011-07-13 17:25:49 -07003053 container, screen, mTargetCell, null);
Winson Chung557d6ed2011-07-08 15:34:52 -07003054 break;
3055 default:
Adam Cohenb7e16182011-07-15 17:55:02 -07003056 throw new IllegalStateException("Unknown item type: " +
3057 pendingInfo.itemType);
Winson Chung557d6ed2011-07-08 15:34:52 -07003058 }
3059 cellLayout.onDragExit();
3060 }
Adam Cohenb7e16182011-07-15 17:55:02 -07003061 };
3062
3063 // Now we animate the dragView, (ie. the widget or shortcut preview) into its final
3064 // location and size on the home screen.
Adam Cohenb7e16182011-07-15 17:55:02 -07003065 int loc[] = new int[2];
3066 cellLayout.cellToPoint(mTargetCell[0], mTargetCell[1], loc);
3067
3068 RectF r = new RectF();
3069 cellLayout.cellToRect(mTargetCell[0], mTargetCell[1], spanX, spanY, r);
Adam Cohen4b285c52011-07-21 14:24:06 -07003070 setFinalTransitionTransform(cellLayout);
Adam Cohenb7e16182011-07-15 17:55:02 -07003071 float cellLayoutScale =
3072 mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(cellLayout, loc);
Adam Cohen4b285c52011-07-21 14:24:06 -07003073 resetTransitionTransform(cellLayout);
Adam Cohenb7e16182011-07-15 17:55:02 -07003074
3075 float dragViewScale = r.width() / d.dragView.getMeasuredWidth();
3076 // The animation will scale the dragView about its center, so we need to center about
3077 // the final location.
3078 loc[0] -= (d.dragView.getMeasuredWidth() - cellLayoutScale * r.width()) / 2;
3079 loc[1] -= (d.dragView.getMeasuredHeight() - cellLayoutScale * r.height()) / 2;
3080
3081 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, loc,
3082 dragViewScale * cellLayoutScale, onAnimationCompleteRunnable);
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07003083 } else {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003084 // This is for other drag/drop cases, like dragging from All Apps
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003085 View view = null;
3086
3087 switch (info.itemType) {
3088 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
3089 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
3090 if (info.container == NO_ID && info instanceof ApplicationInfo) {
3091 // Came from all apps -- make a copy
3092 info = new ShortcutInfo((ApplicationInfo) info);
3093 }
3094 view = mLauncher.createShortcut(R.layout.application, cellLayout,
3095 (ShortcutInfo) info);
3096 break;
Adam Cohendf2cc412011-04-27 16:56:57 -07003097 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
Adam Cohenc0dcf592011-06-01 15:30:43 -07003098 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, cellLayout,
3099 (FolderInfo) info, mIconCache);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003100 break;
3101 default:
3102 throw new IllegalStateException("Unknown item type: " + info.itemType);
3103 }
3104
Adam Cohenc0dcf592011-06-01 15:30:43 -07003105 // First we find the cell nearest to point at which the item is
3106 // dropped, without any consideration to whether there is an item there.
3107 if (touchXY != null) {
3108 mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
3109 cellLayout, mTargetCell);
Winson Chung557d6ed2011-07-08 15:34:52 -07003110 d.postAnimationRunnable = exitSpringLoadedRunnable;
Winson Chung3d503fb2011-07-13 17:25:49 -07003111 if (createUserFolderIfNecessary(view, container, cellLayout, mTargetCell, true,
3112 d.dragView, d.postAnimationRunnable)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07003113 return;
3114 }
Adam Cohen3e8f8112011-07-02 18:03:00 -07003115 if (addToExistingFolderIfNecessary(view, cellLayout, mTargetCell, d, true)) {
Adam Cohenc0dcf592011-06-01 15:30:43 -07003116 return;
3117 }
Adam Cohen10b17372011-04-15 14:21:25 -07003118 }
3119
Michael Jurkac4e772e2011-02-10 13:32:01 -08003120 if (touchXY != null) {
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003121 // when dragging and dropping, just find the closest free spot
Adam Cohenc0dcf592011-06-01 15:30:43 -07003122 mTargetCell = findNearestVacantArea(touchXY[0], touchXY[1], 1, 1, null,
3123 cellLayout, mTargetCell);
Michael Jurka9c6de3d2010-11-23 16:23:58 -08003124 } else {
3125 cellLayout.findCellForSpan(mTargetCell, 1, 1);
3126 }
Winson Chung3d503fb2011-07-13 17:25:49 -07003127 addInScreen(view, container, screen, mTargetCell[0], mTargetCell[1], info.spanX,
3128 info.spanY, insertAtFirst);
Adam Cohen716b51e2011-06-30 12:09:54 -07003129 cellLayout.onDropChild(view);
Michael Jurkad3ef3062010-11-23 16:23:58 -08003130 cellLayout.animateDrop();
Patrick Dubroy6569f2c2010-07-12 14:25:18 -07003131 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
Adam Cohend5e42732011-03-28 17:33:39 -07003132 cellLayout.getChildrenLayout().measureChild(view);
3133
Winson Chung3d503fb2011-07-13 17:25:49 -07003134 LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screen,
Winson Chungaafa03c2010-06-11 17:34:16 -07003135 lp.cellX, lp.cellY);
Adam Cohen3e8f8112011-07-02 18:03:00 -07003136
3137 if (d.dragView != null) {
Adam Cohen4b285c52011-07-21 14:24:06 -07003138 // We wrap the animation call in the temporary set and reset of the current
3139 // cellLayout to its final transform -- this means we animate the drag view to
3140 // the correct final location.
3141 setFinalTransitionTransform(cellLayout);
Winson Chung557d6ed2011-07-08 15:34:52 -07003142 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view,
Adam Cohen4b285c52011-07-21 14:24:06 -07003143 exitSpringLoadedRunnable);
3144 resetTransitionTransform(cellLayout);
Adam Cohen3e8f8112011-07-02 18:03:00 -07003145 }
Joe Onorato00acb122009-08-04 16:04:30 -04003146 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003147 }
Winson Chungaafa03c2010-06-11 17:34:16 -07003148
Adam Cohen4b285c52011-07-21 14:24:06 -07003149 public void setFinalTransitionTransform(CellLayout layout) {
3150 if (isSwitchingState()) {
3151 int index = indexOfChild(layout);
3152 mCurrentScaleX = layout.getScaleX();
3153 mCurrentScaleY = layout.getScaleY();
3154 mCurrentTranslationX = layout.getTranslationX();
3155 mCurrentTranslationY = layout.getTranslationY();
3156 mCurrentRotationY = layout.getRotationY();
3157 layout.setScaleX(mNewScaleXs[index]);
3158 layout.setScaleY(mNewScaleYs[index]);
3159 layout.setTranslationX(mNewTranslationXs[index]);
3160 layout.setTranslationY(mNewTranslationYs[index]);
3161 layout.setRotationY(mNewRotationYs[index]);
3162 }
3163 }
3164 public void resetTransitionTransform(CellLayout layout) {
3165 if (isSwitchingState()) {
Adam Cohen4b285c52011-07-21 14:24:06 -07003166 mCurrentScaleX = layout.getScaleX();
3167 mCurrentScaleY = layout.getScaleY();
3168 mCurrentTranslationX = layout.getTranslationX();
3169 mCurrentTranslationY = layout.getTranslationY();
3170 mCurrentRotationY = layout.getRotationY();
3171 layout.setScaleX(mCurrentScaleX);
3172 layout.setScaleY(mCurrentScaleY);
3173 layout.setTranslationX(mCurrentTranslationX);
3174 layout.setTranslationY(mCurrentTranslationY);
3175 layout.setRotationY(mCurrentRotationY);
3176 }
3177 }
3178
Jeff Sharkey70864282009-04-07 21:08:40 -07003179 /**
3180 * Return the current {@link CellLayout}, correctly picking the destination
3181 * screen while a scroll is in progress.
3182 */
Patrick Dubroy5f445422011-02-18 14:35:21 -08003183 public CellLayout getCurrentDropLayout() {
3184 return (CellLayout) getChildAt(mNextPage == INVALID_PAGE ? mCurrentPage : mNextPage);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003185 }
3186
Jeff Sharkey70864282009-04-07 21:08:40 -07003187 /**
Michael Jurka0280c3b2010-09-17 15:00:07 -07003188 * Return the current CellInfo describing our current drag; this method exists
3189 * so that Launcher can sync this object with the correct info when the activity is created/
3190 * destroyed
3191 *
3192 */
3193 public CellLayout.CellInfo getDragInfo() {
3194 return mDragInfo;
3195 }
3196
3197 /**
Jeff Sharkey70864282009-04-07 21:08:40 -07003198 * Calculate the nearest cell where the given object would be dropped.
Adam Cohene3e27a82011-04-15 12:07:39 -07003199 *
3200 * pixelX and pixelY should be in the coordinate system of layout
Jeff Sharkey70864282009-04-07 21:08:40 -07003201 */
Michael Jurka6a1435d2010-09-27 17:35:12 -07003202 private int[] findNearestVacantArea(int pixelX, int pixelY,
Jeff Sharkey70864282009-04-07 21:08:40 -07003203 int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
Michael Jurka0280c3b2010-09-17 15:00:07 -07003204 return layout.findNearestVacantArea(
Adam Cohene3e27a82011-04-15 12:07:39 -07003205 pixelX, pixelY, spanX, spanY, ignoreView, recycle);
Jeff Sharkey70864282009-04-07 21:08:40 -07003206 }
Winson Chungaafa03c2010-06-11 17:34:16 -07003207
Adam Cohendf035382011-04-11 17:22:04 -07003208 /**
3209 * Calculate the nearest cell where the given object would be dropped.
Adam Cohene3e27a82011-04-15 12:07:39 -07003210 *
3211 * pixelX and pixelY should be in the coordinate system of layout
Adam Cohendf035382011-04-11 17:22:04 -07003212 */
3213 private int[] findNearestArea(int pixelX, int pixelY,
Adam Cohene3e27a82011-04-15 12:07:39 -07003214 int spanX, int spanY, CellLayout layout, int[] recycle) {
Adam Cohendf035382011-04-11 17:22:04 -07003215 return layout.findNearestArea(
Adam Cohene3e27a82011-04-15 12:07:39 -07003216 pixelX, pixelY, spanX, spanY, recycle);
Adam Cohendf035382011-04-11 17:22:04 -07003217 }
3218
Winson Chung4c98d922011-05-31 16:50:48 -07003219 void setup(Launcher launcher, DragController dragController) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003220 mLauncher = launcher;
Michael Jurkac2f7f472010-12-14 15:34:42 -08003221 mSpringLoadedDragController = new SpringLoadedDragController(mLauncher);
Joe Onorato00acb122009-08-04 16:04:30 -04003222 mDragController = dragController;
Michael Jurkad74c9842011-07-10 12:44:21 -07003223
Michael Jurkad74c9842011-07-10 12:44:21 -07003224 // hardware layers on children are enabled on startup, but should be disabled until
3225 // needed
3226 updateChildrenLayersEnabled();
3227 setWallpaperDimension();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003228 }
3229
Patrick Dubroye3887cc2011-01-20 10:43:40 -08003230 /**
3231 * Called at the end of a drag which originated on the workspace.
3232 */
Adam Cohenc0dcf592011-06-01 15:30:43 -07003233 public void onDropCompleted(View target, DragObject d, boolean success) {
Winson Chungaafa03c2010-06-11 17:34:16 -07003234 if (success) {
Michael Jurkad74c9842011-07-10 12:44:21 -07003235 if (target != this) {
3236 if (mDragInfo != null) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003237 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
Michael Jurkad74c9842011-07-10 12:44:21 -07003238 if (mDragInfo.cell instanceof DropTarget) {
3239 mDragController.removeDropTarget((DropTarget) mDragInfo.cell);
3240 }
Joe Onorato00acb122009-08-04 16:04:30 -04003241 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003242 }
Patrick Dubroyce34a972010-10-19 10:34:32 -07003243 } else if (mDragInfo != null) {
Patrick Dubroye3887cc2011-01-20 10:43:40 -08003244 // NOTE: When 'success' is true, onDragExit is called by the DragController before
3245 // calling onDropCompleted(). We call it ourselves here, but maybe this should be
3246 // moved into DragController.cancelDrag().
Adam Cohenc0dcf592011-06-01 15:30:43 -07003247 doDragExit(null);
Winson Chung3d503fb2011-07-13 17:25:49 -07003248 CellLayout cellLayout;
3249 if (mLauncher.isHotseatLayout(target)) {
3250 cellLayout = mLauncher.getHotseat().getLayout();
3251 } else {
3252 cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
3253 }
3254 cellLayout.onDropChild(mDragInfo.cell);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003255 }
Joe Onorato4be866d2010-10-10 11:26:02 -07003256 mDragOutline = null;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003257 mDragInfo = null;
3258 }
3259
Michael Jurka0280c3b2010-09-17 15:00:07 -07003260 public boolean isDropEnabled() {
3261 return true;
3262 }
3263
Michael Jurka0142d492010-08-25 17:46:15 -07003264 @Override
3265 protected void onRestoreInstanceState(Parcelable state) {
3266 super.onRestoreInstanceState(state);
3267 Launcher.setScreen(mCurrentPage);
3268 }
3269
3270 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003271 public void scrollLeft() {
Michael Jurkad74c9842011-07-10 12:44:21 -07003272 if (!isSmall() && !mIsSwitchingState) {
Michael Jurka0142d492010-08-25 17:46:15 -07003273 super.scrollLeft();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003274 }
Adam Cohen95bb8002011-07-03 23:40:28 -07003275 Folder openFolder = getOpenFolder();
3276 if (openFolder != null) {
3277 openFolder.completeDragExit();
3278 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003279 }
3280
Michael Jurka0142d492010-08-25 17:46:15 -07003281 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003282 public void scrollRight() {
Michael Jurkad74c9842011-07-10 12:44:21 -07003283 if (!isSmall() && !mIsSwitchingState) {
Michael Jurka0142d492010-08-25 17:46:15 -07003284 super.scrollRight();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003285 }
Adam Cohen95bb8002011-07-03 23:40:28 -07003286 Folder openFolder = getOpenFolder();
3287 if (openFolder != null) {
3288 openFolder.completeDragExit();
3289 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003290 }
3291
Patrick Dubroy1262e362010-10-06 15:49:50 -07003292 @Override
Winson Chung1c4cf4a2011-07-29 14:49:10 -07003293 public void onEnterScrollArea(int x, int y, int direction) {
3294 // Ignore the scroll area if we are dragging over the hot seat
3295 if (mLauncher.getHotseat() != null) {
3296 Rect r = new Rect();
3297 mLauncher.getHotseat().getHitRect(r);
3298 if (r.contains(x, y)) {
3299 return;
3300 }
3301 }
3302
Michael Jurkad74c9842011-07-10 12:44:21 -07003303 if (!isSmall() && !mIsSwitchingState) {
Michael Jurkad718d6a2010-10-14 15:35:17 -07003304 mInScrollArea = true;
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08003305
3306 final int page = mCurrentPage + (direction == DragController.SCROLL_LEFT ? -1 : 1);
3307 final CellLayout layout = (CellLayout) getChildAt(page);
Adam Cohenc0dcf592011-06-01 15:30:43 -07003308 cancelFolderCreation();
Patrick Dubroy54fa3b92010-11-17 12:18:45 -08003309
3310 if (layout != null) {
Winson Chungc07918d2011-07-01 15:35:26 -07003311 // Exit the current layout and mark the overlapping layout
Patrick Dubroy0207c522010-11-03 22:12:02 -07003312 if (mDragTargetLayout != null) {
Winson Chungc07918d2011-07-01 15:35:26 -07003313 mDragTargetLayout.setIsDragOverlapping(false);
Patrick Dubroy0207c522010-11-03 22:12:02 -07003314 mDragTargetLayout.onDragExit();
Patrick Dubroy0207c522010-11-03 22:12:02 -07003315 }
Winson Chungc07918d2011-07-01 15:35:26 -07003316 mDragTargetLayout = layout;
3317 mDragTargetLayout.setIsDragOverlapping(true);
3318
3319 // Workspace is responsible for drawing the edge glow on adjacent pages,
3320 // so we need to redraw the workspace when this may have changed.
3321 invalidate();
Michael Jurkad718d6a2010-10-14 15:35:17 -07003322 }
Patrick Dubroy1262e362010-10-06 15:49:50 -07003323 }
3324 }
3325
3326 @Override
3327 public void onExitScrollArea() {
Michael Jurkad718d6a2010-10-14 15:35:17 -07003328 if (mInScrollArea) {
Winson Chungc07918d2011-07-01 15:35:26 -07003329 if (mDragTargetLayout != null) {
3330 // Unmark the overlapping layout and re-enter the current layout
3331 mDragTargetLayout.setIsDragOverlapping(false);
3332 mDragTargetLayout = getCurrentDropLayout();
3333 mDragTargetLayout.onDragEnter();
3334
3335 // Workspace is responsible for drawing the edge glow on adjacent pages,
3336 // so we need to redraw the workspace when this may have changed.
3337 invalidate();
3338 }
Winson Chungc07918d2011-07-01 15:35:26 -07003339 mInScrollArea = false;
Patrick Dubroy1262e362010-10-06 15:49:50 -07003340 }
3341 }
3342
Winson Chungc07918d2011-07-01 15:35:26 -07003343 private void onResetScrollArea() {
3344 if (mDragTargetLayout != null) {
3345 // Unmark the overlapping layout
3346 mDragTargetLayout.setIsDragOverlapping(false);
3347
3348 // Workspace is responsible for drawing the edge glow on adjacent pages,
3349 // so we need to redraw the workspace when this may have changed.
3350 invalidate();
3351 }
Winson Chungc07918d2011-07-01 15:35:26 -07003352 mInScrollArea = false;
3353 }
3354
Winson Chung3d503fb2011-07-13 17:25:49 -07003355 /**
3356 * Returns a specific CellLayout
3357 */
3358 CellLayout getParentCellLayoutForView(View v) {
3359 ArrayList<CellLayout> layouts = getWorkspaceAndHotseatCellLayouts();
3360 for (CellLayout layout : layouts) {
3361 if (layout.getChildrenLayout().indexOfChild(v) > -1) {
3362 return layout;
3363 }
3364 }
3365 return null;
3366 }
3367
3368 /**
3369 * Returns a list of all the CellLayouts in the workspace.
3370 */
3371 ArrayList<CellLayout> getWorkspaceAndHotseatCellLayouts() {
3372 ArrayList<CellLayout> layouts = new ArrayList<CellLayout>();
3373 int screenCount = getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003374 for (int screen = 0; screen < screenCount; screen++) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003375 layouts.add(((CellLayout) getChildAt(screen)));
3376 }
3377 if (mLauncher.getHotseat() != null) {
3378 layouts.add(mLauncher.getHotseat().getLayout());
3379 }
3380 return layouts;
3381 }
3382
3383 /**
3384 * We should only use this to search for specific children. Do not use this method to modify
3385 * CellLayoutChildren directly.
3386 */
3387 ArrayList<CellLayoutChildren> getWorkspaceAndHotseatCellLayoutChildren() {
3388 ArrayList<CellLayoutChildren> childrenLayouts = new ArrayList<CellLayoutChildren>();
3389 int screenCount = getChildCount();
3390 for (int screen = 0; screen < screenCount; screen++) {
3391 childrenLayouts.add(((CellLayout) getChildAt(screen)).getChildrenLayout());
3392 }
3393 if (mLauncher.getHotseat() != null) {
3394 childrenLayouts.add(mLauncher.getHotseat().getLayout().getChildrenLayout());
3395 }
3396 return childrenLayouts;
3397 }
3398
3399 public Folder getFolderForTag(Object tag) {
3400 ArrayList<CellLayoutChildren> childrenLayouts = getWorkspaceAndHotseatCellLayoutChildren();
3401 for (CellLayoutChildren layout: childrenLayouts) {
3402 int count = layout.getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003403 for (int i = 0; i < count; i++) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003404 View child = layout.getChildAt(i);
Winson Chung3d503fb2011-07-13 17:25:49 -07003405 if (child instanceof Folder) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003406 Folder f = (Folder) child;
Winson Chungaafa03c2010-06-11 17:34:16 -07003407 if (f.getInfo() == tag && f.getInfo().opened) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003408 return f;
3409 }
3410 }
3411 }
3412 }
3413 return null;
3414 }
3415
3416 public View getViewForTag(Object tag) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003417 ArrayList<CellLayoutChildren> childrenLayouts = getWorkspaceAndHotseatCellLayoutChildren();
3418 for (CellLayoutChildren layout: childrenLayouts) {
3419 int count = layout.getChildCount();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003420 for (int i = 0; i < count; i++) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003421 View child = layout.getChildAt(i);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003422 if (child.getTag() == tag) {
3423 return child;
3424 }
3425 }
3426 }
3427 return null;
3428 }
3429
Adam Cohendf035382011-04-11 17:22:04 -07003430 void clearDropTargets() {
Winson Chung3d503fb2011-07-13 17:25:49 -07003431 ArrayList<CellLayoutChildren> childrenLayouts = getWorkspaceAndHotseatCellLayoutChildren();
3432 for (CellLayoutChildren layout: childrenLayouts) {
Adam Cohendf035382011-04-11 17:22:04 -07003433 int childCount = layout.getChildCount();
3434 for (int j = 0; j < childCount; j++) {
3435 View v = layout.getChildAt(j);
3436 if (v instanceof DropTarget) {
3437 mDragController.removeDropTarget((DropTarget) v);
3438 }
3439 }
3440 }
3441 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003442
Joe Onorato64e6be72010-03-05 15:05:52 -05003443 void removeItems(final ArrayList<ApplicationInfo> apps) {
Romain Guy629de3e2010-01-13 12:20:59 -08003444 final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext());
Romain Guy574d20e2009-06-01 15:34:04 -07003445
Joe Onorato64e6be72010-03-05 15:05:52 -05003446 final HashSet<String> packageNames = new HashSet<String>();
3447 final int appCount = apps.size();
3448 for (int i = 0; i < appCount; i++) {
3449 packageNames.add(apps.get(i).componentName.getPackageName());
3450 }
3451
Winson Chung3d503fb2011-07-13 17:25:49 -07003452 ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
3453 for (final CellLayout layoutParent: cellLayouts) {
Winson Chung7a25a9e2011-01-30 13:33:56 -08003454 final ViewGroup layout = layoutParent.getChildrenLayout();
Romain Guy574d20e2009-06-01 15:34:04 -07003455
Romain Guy629de3e2010-01-13 12:20:59 -08003456 // Avoid ANRs by treating each screen separately
3457 post(new Runnable() {
3458 public void run() {
3459 final ArrayList<View> childrenToRemove = new ArrayList<View>();
3460 childrenToRemove.clear();
Winson Chungaafa03c2010-06-11 17:34:16 -07003461
Romain Guy629de3e2010-01-13 12:20:59 -08003462 int childCount = layout.getChildCount();
3463 for (int j = 0; j < childCount; j++) {
3464 final View view = layout.getChildAt(j);
3465 Object tag = view.getTag();
Winson Chungaafa03c2010-06-11 17:34:16 -07003466
Joe Onorato0589f0f2010-02-08 13:44:00 -08003467 if (tag instanceof ShortcutInfo) {
3468 final ShortcutInfo info = (ShortcutInfo) tag;
Romain Guy629de3e2010-01-13 12:20:59 -08003469 final Intent intent = info.intent;
3470 final ComponentName name = intent.getComponent();
Winson Chungaafa03c2010-06-11 17:34:16 -07003471
Joe Onorato64e6be72010-03-05 15:05:52 -05003472 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
3473 for (String packageName: packageNames) {
3474 if (packageName.equals(name.getPackageName())) {
Joe Onorato64e6be72010-03-05 15:05:52 -05003475 LauncherModel.deleteItemFromDatabase(mLauncher, info);
3476 childrenToRemove.add(view);
3477 }
3478 }
Romain Guy629de3e2010-01-13 12:20:59 -08003479 }
Adam Cohendf2cc412011-04-27 16:56:57 -07003480 } else if (tag instanceof FolderInfo) {
3481 final FolderInfo info = (FolderInfo) tag;
Joe Onorato0589f0f2010-02-08 13:44:00 -08003482 final ArrayList<ShortcutInfo> contents = info.contents;
Romain Guy629de3e2010-01-13 12:20:59 -08003483 final int contentsCount = contents.size();
Adam Cohendf1e4e82011-06-24 15:57:39 -07003484 final ArrayList<ShortcutInfo> appsToRemoveFromFolder =
3485 new ArrayList<ShortcutInfo>();
Winson Chungaafa03c2010-06-11 17:34:16 -07003486
Romain Guy629de3e2010-01-13 12:20:59 -08003487 for (int k = 0; k < contentsCount; k++) {
Joe Onorato0589f0f2010-02-08 13:44:00 -08003488 final ShortcutInfo appInfo = contents.get(k);
Romain Guy629de3e2010-01-13 12:20:59 -08003489 final Intent intent = appInfo.intent;
3490 final ComponentName name = intent.getComponent();
Winson Chungaafa03c2010-06-11 17:34:16 -07003491
Joe Onorato64e6be72010-03-05 15:05:52 -05003492 if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
3493 for (String packageName: packageNames) {
3494 if (packageName.equals(name.getPackageName())) {
Adam Cohendf1e4e82011-06-24 15:57:39 -07003495 appsToRemoveFromFolder.add(appInfo);
Joe Onorato64e6be72010-03-05 15:05:52 -05003496 }
3497 }
Romain Guy629de3e2010-01-13 12:20:59 -08003498 }
3499 }
Adam Cohendf1e4e82011-06-24 15:57:39 -07003500 for (ShortcutInfo item: appsToRemoveFromFolder) {
3501 info.remove(item);
3502 LauncherModel.deleteItemFromDatabase(mLauncher, item);
Romain Guy629de3e2010-01-13 12:20:59 -08003503 }
Romain Guy629de3e2010-01-13 12:20:59 -08003504 } else if (tag instanceof LauncherAppWidgetInfo) {
3505 final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag;
3506 final AppWidgetProviderInfo provider =
3507 widgets.getAppWidgetInfo(info.appWidgetId);
Daniel Sandlere1cc6c32010-05-07 11:49:29 -04003508 if (provider != null) {
Joe Onorato64e6be72010-03-05 15:05:52 -05003509 for (String packageName: packageNames) {
3510 if (packageName.equals(provider.provider.getPackageName())) {
Joe Onorato64e6be72010-03-05 15:05:52 -05003511 LauncherModel.deleteItemFromDatabase(mLauncher, info);
Winson Chungaafa03c2010-06-11 17:34:16 -07003512 childrenToRemove.add(view);
Joe Onorato64e6be72010-03-05 15:05:52 -05003513 }
3514 }
Romain Guy629de3e2010-01-13 12:20:59 -08003515 }
Romain Guy574d20e2009-06-01 15:34:04 -07003516 }
3517 }
Winson Chungaafa03c2010-06-11 17:34:16 -07003518
Romain Guy629de3e2010-01-13 12:20:59 -08003519 childCount = childrenToRemove.size();
3520 for (int j = 0; j < childCount; j++) {
3521 View child = childrenToRemove.get(j);
Winson Chung7a25a9e2011-01-30 13:33:56 -08003522 // Note: We can not remove the view directly from CellLayoutChildren as this
3523 // does not re-mark the spaces as unoccupied.
3524 layoutParent.removeViewInLayout(child);
Romain Guy629de3e2010-01-13 12:20:59 -08003525 if (child instanceof DropTarget) {
3526 mDragController.removeDropTarget((DropTarget)child);
3527 }
Romain Guy574d20e2009-06-01 15:34:04 -07003528 }
Winson Chungaafa03c2010-06-11 17:34:16 -07003529
Romain Guy629de3e2010-01-13 12:20:59 -08003530 if (childCount > 0) {
3531 layout.requestLayout();
3532 layout.invalidate();
Romain Guy5c16f3e2010-01-12 17:24:58 -08003533 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003534 }
Romain Guy629de3e2010-01-13 12:20:59 -08003535 });
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003536 }
3537 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07003538
Joe Onorato64e6be72010-03-05 15:05:52 -05003539 void updateShortcuts(ArrayList<ApplicationInfo> apps) {
Winson Chung3d503fb2011-07-13 17:25:49 -07003540 ArrayList<CellLayoutChildren> childrenLayouts = getWorkspaceAndHotseatCellLayoutChildren();
3541 for (CellLayoutChildren layout: childrenLayouts) {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07003542 int childCount = layout.getChildCount();
3543 for (int j = 0; j < childCount; j++) {
3544 final View view = layout.getChildAt(j);
3545 Object tag = view.getTag();
Joe Onorato0589f0f2010-02-08 13:44:00 -08003546 if (tag instanceof ShortcutInfo) {
3547 ShortcutInfo info = (ShortcutInfo)tag;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07003548 // We need to check for ACTION_MAIN otherwise getComponent() might
3549 // return null for some shortcuts (for instance, for shortcuts to
3550 // web pages.)
3551 final Intent intent = info.intent;
3552 final ComponentName name = intent.getComponent();
3553 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
Joe Onorato64e6be72010-03-05 15:05:52 -05003554 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
3555 final int appCount = apps.size();
Winson Chungaafa03c2010-06-11 17:34:16 -07003556 for (int k = 0; k < appCount; k++) {
Joe Onorato64e6be72010-03-05 15:05:52 -05003557 ApplicationInfo app = apps.get(k);
3558 if (app.componentName.equals(name)) {
3559 info.setIcon(mIconCache.getIcon(info.intent));
3560 ((TextView)view).setCompoundDrawablesWithIntrinsicBounds(null,
3561 new FastBitmapDrawable(info.getIcon(mIconCache)),
3562 null, null);
3563 }
3564 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07003565 }
3566 }
3567 }
3568 }
3569 }
3570
Joe Onorato14f122b2009-11-19 14:06:36 -08003571 void moveToDefaultScreen(boolean animate) {
Winson Chungde1af762011-07-21 16:44:07 -07003572 if (!isSmall()) {
3573 if (animate) {
3574 snapToPage(mDefaultPage);
3575 } else {
3576 setCurrentPage(mDefaultPage);
3577 }
Joe Onoratoc45b1682010-01-11 18:48:40 -05003578 }
Michael Jurka0142d492010-08-25 17:46:15 -07003579 getChildAt(mDefaultPage).requestFocus();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003580 }
3581
Michael Jurka0142d492010-08-25 17:46:15 -07003582 @Override
3583 public void syncPages() {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003584 }
Michael Jurka0142d492010-08-25 17:46:15 -07003585
3586 @Override
3587 public void syncPageItems(int page) {
3588 }
Winson Chung6a0f57d2011-06-29 20:10:49 -07003589
3590 @Override
3591 protected String getCurrentPageDescription() {
3592 int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
3593 return String.format(mContext.getString(R.string.workspace_scroll_format),
3594 page + 1, getChildCount());
3595 }
Adam Cohen8dfcba42011-07-07 16:38:18 -07003596
3597 public void getLocationInDragLayer(int[] loc) {
3598 mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
3599 }
Winson Chung32174c82011-07-19 15:47:55 -07003600
3601 /**
3602 * Return true because we want the scrolling indicator to stretch to fit the space.
3603 */
3604 protected boolean hasElasticScrollIndicator() {
3605 return true;
3606 }
Winson Chunga6427b12011-07-27 10:53:39 -07003607
3608 void showDockDivider(boolean immediately) {
3609 final ViewGroup parent = (ViewGroup) getParent();
3610 final View qsbDivider = (ImageView) (parent.findViewById(R.id.qsb_divider));
3611 final View dockDivider = (ImageView) (parent.findViewById(R.id.dock_divider));
3612 if (qsbDivider != null && dockDivider != null) {
3613 qsbDivider.setVisibility(View.VISIBLE);
3614 dockDivider.setVisibility(View.VISIBLE);
3615 if (mDividerAnimator != null) {
3616 mDividerAnimator.cancel();
3617 mDividerAnimator = null;
3618 }
3619 if (immediately) {
3620 qsbDivider.setAlpha(1f);
3621 dockDivider.setAlpha(1f);
3622 } else {
3623 mDividerAnimator = new AnimatorSet();
3624 mDividerAnimator.playTogether(ObjectAnimator.ofFloat(qsbDivider, "alpha", 1f),
3625 ObjectAnimator.ofFloat(dockDivider, "alpha", 1f));
3626 mDividerAnimator.setDuration(sScrollIndicatorFadeInDuration);
3627 mDividerAnimator.start();
3628 }
3629 }
3630 }
3631
3632 void hideDockDivider(boolean immediately) {
3633 final ViewGroup parent = (ViewGroup) getParent();
3634 final View qsbDivider = (ImageView) (parent.findViewById(R.id.qsb_divider));
3635 final View dockDivider = (ImageView) (parent.findViewById(R.id.dock_divider));
3636 if (qsbDivider != null && dockDivider != null) {
3637 if (mDividerAnimator != null) {
3638 mDividerAnimator.cancel();
3639 mDividerAnimator = null;
3640 }
3641 if (immediately) {
3642 qsbDivider.setVisibility(View.GONE);
3643 dockDivider.setVisibility(View.GONE);
3644 qsbDivider.setAlpha(0f);
3645 dockDivider.setAlpha(0f);
3646 } else {
3647 mDividerAnimator = new AnimatorSet();
3648 mDividerAnimator.playTogether(ObjectAnimator.ofFloat(qsbDivider, "alpha", 0f),
3649 ObjectAnimator.ofFloat(dockDivider, "alpha", 0f));
3650 mDividerAnimator.addListener(new AnimatorListenerAdapter() {
3651 private boolean cancelled = false;
3652 @Override
3653 public void onAnimationCancel(android.animation.Animator animation) {
3654 cancelled = true;
3655 }
3656 @Override
3657 public void onAnimationEnd(android.animation.Animator animation) {
3658 if (!cancelled) {
3659 qsbDivider.setVisibility(View.GONE);
3660 dockDivider.setVisibility(View.GONE);
3661 }
3662 }
3663 });
3664 mDividerAnimator.setDuration(sScrollIndicatorFadeOutDuration);
3665 mDividerAnimator.start();
3666 }
3667 }
3668 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08003669}