blob: a819cb7a6a80f07b3a56867a4e1a4e3d26b64cf0 [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
Adam Cohen716b51e2011-06-30 12:09:54 -070019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.TimeInterpolator;
22import android.animation.ValueAnimator;
23import android.animation.ValueAnimator.AnimatorUpdateListener;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080024import android.content.Context;
Adam Cohen716b51e2011-06-30 12:09:54 -070025import android.content.res.Resources;
Winson Chung360e63f2012-04-27 13:48:05 -070026import android.graphics.Canvas;
Adam Cohen67882692011-03-11 15:29:03 -080027import android.graphics.Rect;
Winson Chung360e63f2012-04-27 13:48:05 -070028import android.graphics.drawable.Drawable;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080029import android.util.AttributeSet;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080030import android.view.KeyEvent;
Michael Jurka0e260592010-06-30 17:07:39 -070031import android.view.MotionEvent;
Romain Guyea3763c2010-01-11 18:02:04 -080032import android.view.View;
Michael Jurka8b805b12012-04-18 14:23:14 -070033import android.view.ViewGroup;
Adam Cohen76fc0852011-06-17 13:26:23 -070034import android.view.ViewParent;
Adam Cohen3371da02011-10-25 21:38:29 -070035import android.view.accessibility.AccessibilityEvent;
36import android.view.accessibility.AccessibilityManager;
Adam Cohen716b51e2011-06-30 12:09:54 -070037import android.view.animation.DecelerateInterpolator;
Adam Cohen3e8f8112011-07-02 18:03:00 -070038import android.view.animation.Interpolator;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080039import android.widget.FrameLayout;
Adam Cohenac8c8762011-07-13 11:15:27 -070040import android.widget.TextView;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080041
Adam Cohen716b51e2011-06-30 12:09:54 -070042import com.android.launcher.R;
43
Adam Cohen1d9af7d2011-06-22 15:26:58 -070044import java.util.ArrayList;
Adam Cohen67882692011-03-11 15:29:03 -080045
The Android Open Source Project31dd5032009-03-03 19:32:27 -080046/**
Michael Jurka0e260592010-06-30 17:07:39 -070047 * A ViewGroup that coordinates dragging across its descendants
The Android Open Source Project31dd5032009-03-03 19:32:27 -080048 */
Michael Jurka8b805b12012-04-18 14:23:14 -070049public class DragLayer extends FrameLayout implements ViewGroup.OnHierarchyChangeListener {
Adam Cohen120980b2010-12-08 11:05:37 -080050 private DragController mDragController;
51 private int[] mTmpXY = new int[2];
The Android Open Source Project31dd5032009-03-03 19:32:27 -080052
Adam Cohen716b51e2011-06-30 12:09:54 -070053 private int mXDown, mYDown;
54 private Launcher mLauncher;
55
Adam Cohen67882692011-03-11 15:29:03 -080056 // Variables relating to resizing widgets
57 private final ArrayList<AppWidgetResizeFrame> mResizeFrames =
58 new ArrayList<AppWidgetResizeFrame>();
59 private AppWidgetResizeFrame mCurrentResizeFrame;
Adam Cohen716b51e2011-06-30 12:09:54 -070060
61 // Variables relating to animation of views after drop
62 private ValueAnimator mDropAnim = null;
Adam Cohen8dfcba42011-07-07 16:38:18 -070063 private ValueAnimator mFadeOutAnim = null;
64 private TimeInterpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
Winson Chung7bd1bbb2012-02-13 18:29:29 -080065 private DragView mDropView = null;
Adam Cohen6441de02011-12-14 14:25:32 -080066 private int mAnchorViewInitialScrollX = 0;
67 private View mAnchorView = null;
Adam Cohen8dfcba42011-07-07 16:38:18 -070068
Adam Cohen3371da02011-10-25 21:38:29 -070069 private boolean mHoverPointClosesFolder = false;
70 private Rect mHitRect = new Rect();
Adam Cohen21b41102011-11-01 17:29:52 -070071 private int mWorkspaceIndex = -1;
Adam Cohenebea84d2011-11-09 17:20:41 -080072 private int mQsbIndex = -1;
Adam Cohened66b2b2012-01-23 17:28:51 -080073 public static final int ANIMATION_END_DISAPPEAR = 0;
74 public static final int ANIMATION_END_FADE_OUT = 1;
75 public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
Adam Cohen67882692011-03-11 15:29:03 -080076
The Android Open Source Project31dd5032009-03-03 19:32:27 -080077 /**
78 * Used to create a new DragLayer from XML.
79 *
80 * @param context The application's context.
Michael Jurka0e260592010-06-30 17:07:39 -070081 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080082 */
83 public DragLayer(Context context, AttributeSet attrs) {
84 super(context, attrs);
Winson Chungbe62afa2011-02-03 23:14:57 -080085
86 // Disable multitouch across the workspace/all apps/customize tray
87 setMotionEventSplittingEnabled(false);
Adam Cohen21b41102011-11-01 17:29:52 -070088 setChildrenDrawingOrderEnabled(true);
Michael Jurka8b805b12012-04-18 14:23:14 -070089 setOnHierarchyChangeListener(this);
Winson Chung360e63f2012-04-27 13:48:05 -070090
91 mLeftHoverDrawable = getResources().getDrawable(R.drawable.page_hover_left_holo);
92 mRightHoverDrawable = getResources().getDrawable(R.drawable.page_hover_right_holo);
The Android Open Source Project31dd5032009-03-03 19:32:27 -080093 }
94
Winson Chung4c98d922011-05-31 16:50:48 -070095 public void setup(Launcher launcher, DragController controller) {
96 mLauncher = launcher;
Joe Onorato00acb122009-08-04 16:04:30 -040097 mDragController = controller;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080098 }
Adam Cohen76fc0852011-06-17 13:26:23 -070099
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800100 @Override
101 public boolean dispatchKeyEvent(KeyEvent event) {
Joe Onorato00acb122009-08-04 16:04:30 -0400102 return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800103 }
104
Adam Cohen3371da02011-10-25 21:38:29 -0700105 private boolean isEventOverFolderTextRegion(Folder folder, MotionEvent ev) {
106 getDescendantRectRelativeToSelf(folder.getEditTextRegion(), mHitRect);
107 if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
108 return true;
109 }
110 return false;
111 }
112
113 private boolean isEventOverFolder(Folder folder, MotionEvent ev) {
114 getDescendantRectRelativeToSelf(folder, mHitRect);
115 if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
116 return true;
117 }
118 return false;
119 }
120
Adam Cohen76fc0852011-06-17 13:26:23 -0700121 private boolean handleTouchDown(MotionEvent ev, boolean intercept) {
Adam Cohen67882692011-03-11 15:29:03 -0800122 Rect hitRect = new Rect();
123 int x = (int) ev.getX();
124 int y = (int) ev.getY();
Adam Cohen1b607ed2011-03-03 17:26:50 -0800125
Adam Cohen67882692011-03-11 15:29:03 -0800126 for (AppWidgetResizeFrame child: mResizeFrames) {
127 child.getHitRect(hitRect);
128 if (hitRect.contains(x, y)) {
129 if (child.beginResizeIfPointInRegion(x - child.getLeft(), y - child.getTop())) {
130 mCurrentResizeFrame = child;
131 mXDown = x;
132 mYDown = y;
133 requestDisallowInterceptTouchEvent(true);
134 return true;
135 }
Patrick Dubroyd1837cc2011-03-07 13:38:54 -0800136 }
Adam Cohen1b607ed2011-03-03 17:26:50 -0800137 }
Adam Cohen76fc0852011-06-17 13:26:23 -0700138
Adam Cohen1d9af7d2011-06-22 15:26:58 -0700139 Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
Winson Chung7d7541e2011-09-16 20:14:36 -0700140 if (currentFolder != null && !mLauncher.isFolderClingVisible() && intercept) {
Adam Cohen1d9af7d2011-06-22 15:26:58 -0700141 if (currentFolder.isEditingName()) {
Adam Cohen3371da02011-10-25 21:38:29 -0700142 if (!isEventOverFolderTextRegion(currentFolder, ev)) {
Adam Cohen1d9af7d2011-06-22 15:26:58 -0700143 currentFolder.dismissEditingName();
Adam Cohen76fc0852011-06-17 13:26:23 -0700144 return true;
Adam Cohen2801caf2011-05-13 20:57:39 -0700145 }
146 }
Adam Cohen76fc0852011-06-17 13:26:23 -0700147
Adam Cohen1d9af7d2011-06-22 15:26:58 -0700148 getDescendantRectRelativeToSelf(currentFolder, hitRect);
Adam Cohen3371da02011-10-25 21:38:29 -0700149 if (!isEventOverFolder(currentFolder, ev)) {
Adam Cohen76fc0852011-06-17 13:26:23 -0700150 mLauncher.closeFolder();
151 return true;
152 }
Adam Cohen2801caf2011-05-13 20:57:39 -0700153 }
Adam Cohen67882692011-03-11 15:29:03 -0800154 return false;
155 }
156
157 @Override
158 public boolean onInterceptTouchEvent(MotionEvent ev) {
159 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
Adam Cohen76fc0852011-06-17 13:26:23 -0700160 if (handleTouchDown(ev, true)) {
Adam Cohen67882692011-03-11 15:29:03 -0800161 return true;
162 }
163 }
164 clearAllResizeFrames();
Joe Onorato4db52312009-10-06 11:17:43 -0700165 return mDragController.onInterceptTouchEvent(ev);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800166 }
167
168 @Override
Adam Cohen3371da02011-10-25 21:38:29 -0700169 public boolean onInterceptHoverEvent(MotionEvent ev) {
Michael Jurka023c71e2012-10-19 17:32:24 +0200170 if (mLauncher == null || mLauncher.getWorkspace() == null) {
171 return false;
172 }
Adam Cohen3371da02011-10-25 21:38:29 -0700173 Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
174 if (currentFolder == null) {
175 return false;
176 } else {
Michael Jurka8b805b12012-04-18 14:23:14 -0700177 AccessibilityManager accessibilityManager = (AccessibilityManager)
178 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
179 if (accessibilityManager.isTouchExplorationEnabled()) {
Adam Cohen3371da02011-10-25 21:38:29 -0700180 final int action = ev.getAction();
181 boolean isOverFolder;
182 switch (action) {
183 case MotionEvent.ACTION_HOVER_ENTER:
184 isOverFolder = isEventOverFolder(currentFolder, ev);
185 if (!isOverFolder) {
186 sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
187 mHoverPointClosesFolder = true;
188 return true;
189 } else if (isOverFolder) {
190 mHoverPointClosesFolder = false;
191 } else {
192 return true;
193 }
194 case MotionEvent.ACTION_HOVER_MOVE:
195 isOverFolder = isEventOverFolder(currentFolder, ev);
196 if (!isOverFolder && !mHoverPointClosesFolder) {
197 sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
198 mHoverPointClosesFolder = true;
199 return true;
200 } else if (isOverFolder) {
201 mHoverPointClosesFolder = false;
202 } else {
203 return true;
204 }
205 }
206 }
207 }
208 return false;
209 }
210
211 private void sendTapOutsideFolderAccessibilityEvent(boolean isEditingName) {
Michael Jurka8b805b12012-04-18 14:23:14 -0700212 AccessibilityManager accessibilityManager = (AccessibilityManager)
213 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
214 if (accessibilityManager.isEnabled()) {
Adam Cohen3371da02011-10-25 21:38:29 -0700215 int stringId = isEditingName ? R.string.folder_tap_to_rename : R.string.folder_tap_to_close;
216 AccessibilityEvent event = AccessibilityEvent.obtain(
217 AccessibilityEvent.TYPE_VIEW_FOCUSED);
218 onInitializeAccessibilityEvent(event);
Michael Jurka8b805b12012-04-18 14:23:14 -0700219 event.getText().add(getContext().getString(stringId));
220 accessibilityManager.sendAccessibilityEvent(event);
Adam Cohen3371da02011-10-25 21:38:29 -0700221 }
222 }
223
224 @Override
225 public boolean onHoverEvent(MotionEvent ev) {
226 // If we've received this, we've already done the necessary handling
227 // in onInterceptHoverEvent. Return true to consume the event.
228 return false;
229 }
230
231 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800232 public boolean onTouchEvent(MotionEvent ev) {
Adam Cohen67882692011-03-11 15:29:03 -0800233 boolean handled = false;
234 int action = ev.getAction();
235
236 int x = (int) ev.getX();
237 int y = (int) ev.getY();
238
239 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
240 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
Adam Cohen76fc0852011-06-17 13:26:23 -0700241 if (handleTouchDown(ev, false)) {
Adam Cohen67882692011-03-11 15:29:03 -0800242 return true;
243 }
244 }
245 }
246
247 if (mCurrentResizeFrame != null) {
248 handled = true;
249 switch (action) {
250 case MotionEvent.ACTION_MOVE:
251 mCurrentResizeFrame.visualizeResizeForDelta(x - mXDown, y - mYDown);
252 break;
253 case MotionEvent.ACTION_CANCEL:
254 case MotionEvent.ACTION_UP:
Adam Cohenbebf0422012-04-11 18:06:28 -0700255 mCurrentResizeFrame.visualizeResizeForDelta(x - mXDown, y - mYDown);
256 mCurrentResizeFrame.onTouchUp();
Adam Cohen67882692011-03-11 15:29:03 -0800257 mCurrentResizeFrame = null;
258 }
259 }
260 if (handled) return true;
Joe Onorato4db52312009-10-06 11:17:43 -0700261 return mDragController.onTouchEvent(ev);
Romain Guy91a9c962009-06-12 13:52:17 -0700262 }
Romain Guyea3763c2010-01-11 18:02:04 -0800263
Adam Cohen35e7e642011-07-17 14:47:18 -0700264 /**
265 * Determine the rect of the descendant in this DragLayer's coordinates
266 *
267 * @param descendant The descendant whose coordinates we want to find.
268 * @param r The rect into which to place the results.
269 * @return The factor by which this descendant is scaled relative to this DragLayer.
270 */
Adam Cohenac8c8762011-07-13 11:15:27 -0700271 public float getDescendantRectRelativeToSelf(View descendant, Rect r) {
Adam Cohen716b51e2011-06-30 12:09:54 -0700272 mTmpXY[0] = 0;
273 mTmpXY[1] = 0;
Adam Cohenac8c8762011-07-13 11:15:27 -0700274 float scale = getDescendantCoordRelativeToSelf(descendant, mTmpXY);
Adam Cohen8dfcba42011-07-07 16:38:18 -0700275 r.set(mTmpXY[0], mTmpXY[1],
276 mTmpXY[0] + descendant.getWidth(), mTmpXY[1] + descendant.getHeight());
Adam Cohenac8c8762011-07-13 11:15:27 -0700277 return scale;
Adam Cohen716b51e2011-06-30 12:09:54 -0700278 }
Adam Cohen76fc0852011-06-17 13:26:23 -0700279
Adam Cohen307fe232012-08-16 17:55:58 -0700280 public float getLocationInDragLayer(View child, int[] loc) {
Adam Cohen35e7e642011-07-17 14:47:18 -0700281 loc[0] = 0;
282 loc[1] = 0;
Adam Cohen307fe232012-08-16 17:55:58 -0700283 return getDescendantCoordRelativeToSelf(child, loc);
Adam Cohen35e7e642011-07-17 14:47:18 -0700284 }
285
286 /**
287 * Given a coordinate relative to the descendant, find the coordinate in this DragLayer's
288 * coordinates.
289 *
290 * @param descendant The descendant to which the passed coordinate is relative.
291 * @param coord The coordinate that we want mapped.
Adam Cohen307fe232012-08-16 17:55:58 -0700292 * @return The factor by which this descendant is scaled relative to this DragLayer. Caution
293 * this scale factor is assumed to be equal in X and Y, and so if at any point this
294 * assumption fails, we will need to return a pair of scale factors.
Adam Cohen35e7e642011-07-17 14:47:18 -0700295 */
Adam Cohenb7e16182011-07-15 17:55:02 -0700296 public float getDescendantCoordRelativeToSelf(View descendant, int[] coord) {
Winson Chung557d6ed2011-07-08 15:34:52 -0700297 float scale = 1.0f;
298 float[] pt = {coord[0], coord[1]};
299 descendant.getMatrix().mapPoints(pt);
300 scale *= descendant.getScaleX();
301 pt[0] += descendant.getLeft();
302 pt[1] += descendant.getTop();
Adam Cohen76fc0852011-06-17 13:26:23 -0700303 ViewParent viewParent = descendant.getParent();
304 while (viewParent instanceof View && viewParent != this) {
305 final View view = (View)viewParent;
Winson Chung557d6ed2011-07-08 15:34:52 -0700306 view.getMatrix().mapPoints(pt);
307 scale *= view.getScaleX();
308 pt[0] += view.getLeft() - view.getScrollX();
309 pt[1] += view.getTop() - view.getScrollY();
Adam Cohen76fc0852011-06-17 13:26:23 -0700310 viewParent = view.getParent();
311 }
Adam Cohenfc53cd22011-07-20 15:45:11 -0700312 coord[0] = (int) Math.round(pt[0]);
313 coord[1] = (int) Math.round(pt[1]);
Winson Chung557d6ed2011-07-08 15:34:52 -0700314 return scale;
Adam Cohen76fc0852011-06-17 13:26:23 -0700315 }
316
Adam Cohen3e8f8112011-07-02 18:03:00 -0700317 public void getViewRectRelativeToSelf(View v, Rect r) {
318 int[] loc = new int[2];
Adam Cohen8dfcba42011-07-07 16:38:18 -0700319 getLocationInWindow(loc);
Adam Cohen3e8f8112011-07-02 18:03:00 -0700320 int x = loc[0];
321 int y = loc[1];
Adam Cohen716b51e2011-06-30 12:09:54 -0700322
Adam Cohen8dfcba42011-07-07 16:38:18 -0700323 v.getLocationInWindow(loc);
Adam Cohen3e8f8112011-07-02 18:03:00 -0700324 int vX = loc[0];
325 int vY = loc[1];
Adam Cohen716b51e2011-06-30 12:09:54 -0700326
Adam Cohen3e8f8112011-07-02 18:03:00 -0700327 int left = vX - x;
328 int top = vY - y;
329 r.set(left, top, left + v.getMeasuredWidth(), top + v.getMeasuredHeight());
Adam Cohen716b51e2011-06-30 12:09:54 -0700330 }
331
Romain Guyea3763c2010-01-11 18:02:04 -0800332 @Override
333 public boolean dispatchUnhandledMove(View focused, int direction) {
334 return mDragController.dispatchUnhandledMove(focused, direction);
335 }
Adam Cohen120980b2010-12-08 11:05:37 -0800336
Adam Cohen67882692011-03-11 15:29:03 -0800337 public static class LayoutParams extends FrameLayout.LayoutParams {
338 public int x, y;
339 public boolean customPosition = false;
340
341 /**
342 * {@inheritDoc}
343 */
344 public LayoutParams(int width, int height) {
345 super(width, height);
346 }
347
348 public void setWidth(int width) {
349 this.width = width;
350 }
351
352 public int getWidth() {
353 return width;
354 }
355
356 public void setHeight(int height) {
357 this.height = height;
358 }
359
360 public int getHeight() {
361 return height;
362 }
363
364 public void setX(int x) {
365 this.x = x;
366 }
367
368 public int getX() {
369 return x;
370 }
371
372 public void setY(int y) {
373 this.y = y;
374 }
375
376 public int getY() {
377 return y;
378 }
379 }
380
381 protected void onLayout(boolean changed, int l, int t, int r, int b) {
382 super.onLayout(changed, l, t, r, b);
383 int count = getChildCount();
384 for (int i = 0; i < count; i++) {
385 View child = getChildAt(i);
386 final FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) child.getLayoutParams();
387 if (flp instanceof LayoutParams) {
388 final LayoutParams lp = (LayoutParams) flp;
389 if (lp.customPosition) {
390 child.layout(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height);
391 }
392 }
393 }
394 }
395
396 public void clearAllResizeFrames() {
397 if (mResizeFrames.size() > 0) {
398 for (AppWidgetResizeFrame frame: mResizeFrames) {
Adam Cohenbebf0422012-04-11 18:06:28 -0700399 frame.commitResize();
Adam Cohen67882692011-03-11 15:29:03 -0800400 removeView(frame);
401 }
402 mResizeFrames.clear();
403 }
404 }
405
406 public boolean hasResizeFrames() {
407 return mResizeFrames.size() > 0;
408 }
409
410 public boolean isWidgetBeingResized() {
411 return mCurrentResizeFrame != null;
412 }
413
414 public void addResizeFrame(ItemInfo itemInfo, LauncherAppWidgetHostView widget,
415 CellLayout cellLayout) {
416 AppWidgetResizeFrame resizeFrame = new AppWidgetResizeFrame(getContext(),
Michael Jurka3a9fced2012-04-13 14:44:29 -0700417 widget, cellLayout, this);
Adam Cohen67882692011-03-11 15:29:03 -0800418
419 LayoutParams lp = new LayoutParams(-1, -1);
420 lp.customPosition = true;
421
422 addView(resizeFrame, lp);
423 mResizeFrames.add(resizeFrame);
424
425 resizeFrame.snapToWidget(false);
426 }
Adam Cohen716b51e2011-06-30 12:09:54 -0700427
428 public void animateViewIntoPosition(DragView dragView, final View child) {
Winson Chung557d6ed2011-07-08 15:34:52 -0700429 animateViewIntoPosition(dragView, child, null);
430 }
Adam Cohenac8c8762011-07-13 11:15:27 -0700431
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800432 public void animateViewIntoPosition(DragView dragView, final int[] pos, float alpha,
433 float scaleX, float scaleY, int animationEndStyle, Runnable onFinishRunnable,
434 int duration) {
Adam Cohenb7e16182011-07-15 17:55:02 -0700435 Rect r = new Rect();
436 getViewRectRelativeToSelf(dragView, r);
437 final int fromX = r.left;
438 final int fromY = r.top;
439
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800440 animateViewIntoPosition(dragView, fromX, fromY, pos[0], pos[1], alpha, 1, 1, scaleX, scaleY,
Adam Cohened66b2b2012-01-23 17:28:51 -0800441 onFinishRunnable, animationEndStyle, duration, null);
442 }
443
Winson Chung557d6ed2011-07-08 15:34:52 -0700444 public void animateViewIntoPosition(DragView dragView, final View child,
445 final Runnable onFinishAnimationRunnable) {
Adam Cohen6441de02011-12-14 14:25:32 -0800446 animateViewIntoPosition(dragView, child, -1, onFinishAnimationRunnable, null);
Adam Cohened51cc92011-08-01 20:28:08 -0700447 }
448
449 public void animateViewIntoPosition(DragView dragView, final View child, int duration,
Adam Cohen6441de02011-12-14 14:25:32 -0800450 final Runnable onFinishAnimationRunnable, View anchorView) {
Michael Jurkaa52570f2012-03-20 03:18:20 -0700451 ShortcutAndWidgetContainer parentChildren = (ShortcutAndWidgetContainer) child.getParent();
Adam Cohen716b51e2011-06-30 12:09:54 -0700452 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
Winson Chungeecf02d2012-03-02 17:14:58 -0800453 parentChildren.measureChild(child);
Adam Cohen716b51e2011-06-30 12:09:54 -0700454
Adam Cohen3e8f8112011-07-02 18:03:00 -0700455 Rect r = new Rect();
456 getViewRectRelativeToSelf(dragView, r);
Adam Cohen716b51e2011-06-30 12:09:54 -0700457
458 int coord[] = new int[2];
Adam Cohen307fe232012-08-16 17:55:58 -0700459 float childScale = child.getScaleX();
460 coord[0] = lp.x + (int) (child.getMeasuredWidth() * (1 - childScale) / 2);
461 coord[1] = lp.y + (int) (child.getMeasuredHeight() * (1 - childScale) / 2);
Winson Chungeecf02d2012-03-02 17:14:58 -0800462
Adam Cohen8dfcba42011-07-07 16:38:18 -0700463 // Since the child hasn't necessarily been laid out, we force the lp to be updated with
Adam Cohenac8c8762011-07-13 11:15:27 -0700464 // the correct coordinates (above) and use these to determine the final location
Winson Chung557d6ed2011-07-08 15:34:52 -0700465 float scale = getDescendantCoordRelativeToSelf((View) child.getParent(), coord);
Adam Cohen307fe232012-08-16 17:55:58 -0700466 // We need to account for the scale of the child itself, as the above only accounts for
467 // for the scale in parents.
468 scale *= childScale;
Adam Cohenac8c8762011-07-13 11:15:27 -0700469 int toX = coord[0];
470 int toY = coord[1];
Andrew Flynn4497ebf2012-05-09 11:28:00 -0700471 if (child instanceof TextView) {
472 TextView tv = (TextView) child;
473
474 // The child may be scaled (always about the center of the view) so to account for it,
475 // we have to offset the position by the scaled size. Once we do that, we can center
476 // the drag view about the scaled child view.
477 toY += Math.round(scale * tv.getPaddingTop());
478 toY -= dragView.getMeasuredHeight() * (1 - scale) / 2;
479 toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2;
480 } else if (child instanceof FolderIcon) {
Winson Chungea359c62011-08-03 17:06:35 -0700481 // Account for holographic blur padding on the drag view
Adam Cohen307fe232012-08-16 17:55:58 -0700482 toY -= scale * Workspace.DRAG_BITMAP_PADDING / 2;
483 toY -= (1 - scale) * dragView.getMeasuredHeight() / 2;
Winson Chungea359c62011-08-03 17:06:35 -0700484 // Center in the x coordinate about the target's drawable
485 toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2;
Adam Cohenac8c8762011-07-13 11:15:27 -0700486 } else {
487 toY -= (Math.round(scale * (dragView.getHeight() - child.getMeasuredHeight()))) / 2;
488 toX -= (Math.round(scale * (dragView.getMeasuredWidth()
489 - child.getMeasuredWidth()))) / 2;
490 }
491
492 final int fromX = r.left;
493 final int fromY = r.top;
Adam Cohen716b51e2011-06-30 12:09:54 -0700494 child.setVisibility(INVISIBLE);
Adam Cohen8dfcba42011-07-07 16:38:18 -0700495 Runnable onCompleteRunnable = new Runnable() {
496 public void run() {
497 child.setVisibility(VISIBLE);
Adam Cohend41fbf52012-02-16 23:53:59 -0800498 if (onFinishAnimationRunnable != null) {
499 onFinishAnimationRunnable.run();
500 }
Adam Cohen8dfcba42011-07-07 16:38:18 -0700501 }
502 };
Adam Cohened66b2b2012-01-23 17:28:51 -0800503 animateViewIntoPosition(dragView, fromX, fromY, toX, toY, 1, 1, 1, scale, scale,
Adam Cohend41fbf52012-02-16 23:53:59 -0800504 onCompleteRunnable, ANIMATION_END_DISAPPEAR, duration, anchorView);
Winson Chung557d6ed2011-07-08 15:34:52 -0700505 }
506
Adam Cohend41fbf52012-02-16 23:53:59 -0800507 public void animateViewIntoPosition(final DragView view, final int fromX, final int fromY,
Adam Cohened66b2b2012-01-23 17:28:51 -0800508 final int toX, final int toY, float finalAlpha, float initScaleX, float initScaleY,
509 float finalScaleX, float finalScaleY, Runnable onCompleteRunnable,
510 int animationEndStyle, int duration, View anchorView) {
Adam Cohen8dfcba42011-07-07 16:38:18 -0700511 Rect from = new Rect(fromX, fromY, fromX +
512 view.getMeasuredWidth(), fromY + view.getMeasuredHeight());
Adam Cohen3e8f8112011-07-02 18:03:00 -0700513 Rect to = new Rect(toX, toY, toX + view.getMeasuredWidth(), toY + view.getMeasuredHeight());
Adam Cohened66b2b2012-01-23 17:28:51 -0800514 animateView(view, from, to, finalAlpha, initScaleX, initScaleY, finalScaleX, finalScaleY, duration,
515 null, null, onCompleteRunnable, animationEndStyle, anchorView);
Adam Cohen3e8f8112011-07-02 18:03:00 -0700516 }
517
Adam Cohenb7e16182011-07-15 17:55:02 -0700518 /**
519 * This method animates a view at the end of a drag and drop animation.
520 *
521 * @param view The view to be animated. This view is drawn directly into DragLayer, and so
522 * doesn't need to be a child of DragLayer.
523 * @param from The initial location of the view. Only the left and top parameters are used.
524 * @param to The final location of the view. Only the left and top parameters are used. This
525 * location doesn't account for scaling, and so should be centered about the desired
526 * final location (including scaling).
527 * @param finalAlpha The final alpha of the view, in case we want it to fade as it animates.
528 * @param finalScale The final scale of the view. The view is scaled about its center.
529 * @param duration The duration of the animation.
530 * @param motionInterpolator The interpolator to use for the location of the view.
531 * @param alphaInterpolator The interpolator to use for the alpha of the view.
532 * @param onCompleteRunnable Optional runnable to run on animation completion.
533 * @param fadeOut Whether or not to fade out the view once the animation completes. If true,
534 * the runnable will execute after the view is faded out.
Adam Cohen6441de02011-12-14 14:25:32 -0800535 * @param anchorView If not null, this represents the view which the animated view stays
536 * anchored to in case scrolling is currently taking place. Note: currently this is
537 * only used for the X dimension for the case of the workspace.
Adam Cohenb7e16182011-07-15 17:55:02 -0700538 */
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800539 public void animateView(final DragView view, final Rect from, final Rect to,
540 final float finalAlpha, final float initScaleX, final float initScaleY,
541 final float finalScaleX, final float finalScaleY, int duration,
542 final Interpolator motionInterpolator, final Interpolator alphaInterpolator,
543 final Runnable onCompleteRunnable, final int animationEndStyle, View anchorView) {
544
Adam Cohen716b51e2011-06-30 12:09:54 -0700545 // Calculate the duration of the animation based on the object's distance
Adam Cohen3e8f8112011-07-02 18:03:00 -0700546 final float dist = (float) Math.sqrt(Math.pow(to.left - from.left, 2) +
547 Math.pow(to.top - from.top, 2));
Adam Cohen716b51e2011-06-30 12:09:54 -0700548 final Resources res = getResources();
549 final float maxDist = (float) res.getInteger(R.integer.config_dropAnimMaxDist);
Adam Cohen3e8f8112011-07-02 18:03:00 -0700550
551 // If duration < 0, this is a cue to compute the duration based on the distance
552 if (duration < 0) {
553 duration = res.getInteger(R.integer.config_dropAnimMaxDuration);
554 if (dist < maxDist) {
Adam Cohen8dfcba42011-07-07 16:38:18 -0700555 duration *= mCubicEaseOutInterpolator.getInterpolation(dist / maxDist);
Adam Cohen3e8f8112011-07-02 18:03:00 -0700556 }
Winson Chung61b0c692012-02-23 16:31:13 -0800557 duration = Math.max(duration, res.getInteger(R.integer.config_dropAnimMinDuration));
Adam Cohen716b51e2011-06-30 12:09:54 -0700558 }
559
Winson Chung043f2af2012-03-01 16:09:54 -0800560 // Fall back to cubic ease out interpolator for the animation if none is specified
561 TimeInterpolator interpolator = null;
Adam Cohen3e8f8112011-07-02 18:03:00 -0700562 if (alphaInterpolator == null || motionInterpolator == null) {
Winson Chung043f2af2012-03-01 16:09:54 -0800563 interpolator = mCubicEaseOutInterpolator;
Adam Cohen3e8f8112011-07-02 18:03:00 -0700564 }
Adam Cohen716b51e2011-06-30 12:09:54 -0700565
Winson Chung043f2af2012-03-01 16:09:54 -0800566 // Animate the view
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800567 final float initAlpha = view.getAlpha();
Winson Chung043f2af2012-03-01 16:09:54 -0800568 final float dropViewScale = view.getScaleX();
569 AnimatorUpdateListener updateCb = new AnimatorUpdateListener() {
570 @Override
Adam Cohen716b51e2011-06-30 12:09:54 -0700571 public void onAnimationUpdate(ValueAnimator animation) {
572 final float percent = (Float) animation.getAnimatedValue();
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800573 final int width = view.getMeasuredWidth();
574 final int height = view.getMeasuredHeight();
Adam Cohen716b51e2011-06-30 12:09:54 -0700575
Adam Cohen3e8f8112011-07-02 18:03:00 -0700576 float alphaPercent = alphaInterpolator == null ? percent :
577 alphaInterpolator.getInterpolation(percent);
578 float motionPercent = motionInterpolator == null ? percent :
579 motionInterpolator.getInterpolation(percent);
580
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800581 float initialScaleX = initScaleX * dropViewScale;
582 float initialScaleY = initScaleY * dropViewScale;
583 float scaleX = finalScaleX * percent + initialScaleX * (1 - percent);
584 float scaleY = finalScaleY * percent + initialScaleY * (1 - percent);
585 float alpha = finalAlpha * alphaPercent + initAlpha * (1 - alphaPercent);
586
587 float fromLeft = from.left + (initialScaleX - 1f) * width / 2;
588 float fromTop = from.top + (initialScaleY - 1f) * height / 2;
589
590 int x = (int) (fromLeft + Math.round(((to.left - fromLeft) * motionPercent)));
591 int y = (int) (fromTop + Math.round(((to.top - fromTop) * motionPercent)));
592
593 int xPos = x - mDropView.getScrollX() + (mAnchorView != null
594 ? (mAnchorViewInitialScrollX - mAnchorView.getScrollX()) : 0);
595 int yPos = y - mDropView.getScrollY();
596
597 mDropView.setTranslationX(xPos);
598 mDropView.setTranslationY(yPos);
599 mDropView.setScaleX(scaleX);
600 mDropView.setScaleY(scaleY);
601 mDropView.setAlpha(alpha);
Adam Cohen716b51e2011-06-30 12:09:54 -0700602 }
Winson Chung043f2af2012-03-01 16:09:54 -0800603 };
604 animateView(view, updateCb, duration, interpolator, onCompleteRunnable, animationEndStyle,
605 anchorView);
606 }
607
608 public void animateView(final DragView view, AnimatorUpdateListener updateCb, int duration,
609 TimeInterpolator interpolator, final Runnable onCompleteRunnable,
610 final int animationEndStyle, View anchorView) {
611 // Clean up the previous animations
612 if (mDropAnim != null) mDropAnim.cancel();
613 if (mFadeOutAnim != null) mFadeOutAnim.cancel();
614
615 // Show the drop view if it was previously hidden
616 mDropView = view;
617 mDropView.cancelAnimation();
618 mDropView.resetLayoutParams();
619
620 // Set the anchor view if the page is scrolling
621 if (anchorView != null) {
622 mAnchorViewInitialScrollX = anchorView.getScrollX();
623 }
624 mAnchorView = anchorView;
625
626 // Create and start the animation
627 mDropAnim = new ValueAnimator();
628 mDropAnim.setInterpolator(interpolator);
629 mDropAnim.setDuration(duration);
630 mDropAnim.setFloatValues(0f, 1f);
631 mDropAnim.addUpdateListener(updateCb);
Adam Cohen3e8f8112011-07-02 18:03:00 -0700632 mDropAnim.addListener(new AnimatorListenerAdapter() {
633 public void onAnimationEnd(Animator animation) {
Adam Cohen8dfcba42011-07-07 16:38:18 -0700634 if (onCompleteRunnable != null) {
635 onCompleteRunnable.run();
636 }
Adam Cohened66b2b2012-01-23 17:28:51 -0800637 switch (animationEndStyle) {
638 case ANIMATION_END_DISAPPEAR:
639 clearAnimatedView();
640 break;
641 case ANIMATION_END_FADE_OUT:
Adam Cohen8dfcba42011-07-07 16:38:18 -0700642 fadeOutDragView();
Adam Cohened66b2b2012-01-23 17:28:51 -0800643 break;
644 case ANIMATION_END_REMAIN_VISIBLE:
645 break;
Adam Cohen3e8f8112011-07-02 18:03:00 -0700646 }
647 }
648 });
Adam Cohen716b51e2011-06-30 12:09:54 -0700649 mDropAnim.start();
650 }
651
Adam Cohened66b2b2012-01-23 17:28:51 -0800652 public void clearAnimatedView() {
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800653 if (mDropAnim != null) {
654 mDropAnim.cancel();
655 }
656 if (mDropView != null) {
Winson Chung043f2af2012-03-01 16:09:54 -0800657 mDragController.onDeferredEndDrag(mDropView);
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800658 }
Adam Cohened66b2b2012-01-23 17:28:51 -0800659 mDropView = null;
Adam Cohened66b2b2012-01-23 17:28:51 -0800660 invalidate();
661 }
662
663 public View getAnimatedView() {
664 return mDropView;
665 }
666
Adam Cohen8dfcba42011-07-07 16:38:18 -0700667 private void fadeOutDragView() {
668 mFadeOutAnim = new ValueAnimator();
669 mFadeOutAnim.setDuration(150);
670 mFadeOutAnim.setFloatValues(0f, 1f);
671 mFadeOutAnim.removeAllUpdateListeners();
672 mFadeOutAnim.addUpdateListener(new AnimatorUpdateListener() {
673 public void onAnimationUpdate(ValueAnimator animation) {
674 final float percent = (Float) animation.getAnimatedValue();
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800675
676 float alpha = 1 - percent;
677 mDropView.setAlpha(alpha);
Adam Cohen8dfcba42011-07-07 16:38:18 -0700678 }
679 });
680 mFadeOutAnim.addListener(new AnimatorListenerAdapter() {
681 public void onAnimationEnd(Animator animation) {
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800682 if (mDropView != null) {
Winson Chung043f2af2012-03-01 16:09:54 -0800683 mDragController.onDeferredEndDrag(mDropView);
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800684 }
Adam Cohen8dfcba42011-07-07 16:38:18 -0700685 mDropView = null;
Adam Cohened66b2b2012-01-23 17:28:51 -0800686 invalidate();
Adam Cohen8dfcba42011-07-07 16:38:18 -0700687 }
688 });
689 mFadeOutAnim.start();
690 }
691
Adam Cohen716b51e2011-06-30 12:09:54 -0700692 @Override
Michael Jurka8b805b12012-04-18 14:23:14 -0700693 public void onChildViewAdded(View parent, View child) {
Adam Cohen21b41102011-11-01 17:29:52 -0700694 updateChildIndices();
695 }
696
697 @Override
Michael Jurka8b805b12012-04-18 14:23:14 -0700698 public void onChildViewRemoved(View parent, View child) {
Adam Cohen21b41102011-11-01 17:29:52 -0700699 updateChildIndices();
700 }
701
702 private void updateChildIndices() {
703 if (mLauncher != null) {
704 mWorkspaceIndex = indexOfChild(mLauncher.getWorkspace());
Adam Cohenebea84d2011-11-09 17:20:41 -0800705 mQsbIndex = indexOfChild(mLauncher.getSearchBar());
Adam Cohen21b41102011-11-01 17:29:52 -0700706 }
707 }
708
709 @Override
710 protected int getChildDrawingOrder(int childCount, int i) {
Adam Cohene29c55a2012-08-23 12:18:16 -0700711 // TODO: We have turned off this custom drawing order because it now effects touch
712 // dispatch order. We need to sort that issue out and then decide how to go about this.
713 if (true || LauncherApplication.isScreenLandscape(getContext()) ||
714 mWorkspaceIndex == -1 || mQsbIndex == -1 ||
Adam Cohen21b41102011-11-01 17:29:52 -0700715 mLauncher.getWorkspace().isDrawingBackgroundGradient()) {
716 return i;
717 }
718
719 // This ensures that the workspace is drawn above the hotseat and qsb,
720 // except when the workspace is drawing a background gradient, in which
721 // case we want the workspace to stay behind these elements.
Adam Cohenebea84d2011-11-09 17:20:41 -0800722 if (i == mQsbIndex) {
Adam Cohen21b41102011-11-01 17:29:52 -0700723 return mWorkspaceIndex;
724 } else if (i == mWorkspaceIndex) {
Adam Cohenebea84d2011-11-09 17:20:41 -0800725 return mQsbIndex;
Adam Cohen21b41102011-11-01 17:29:52 -0700726 } else {
727 return i;
728 }
729 }
Winson Chung360e63f2012-04-27 13:48:05 -0700730
731 private boolean mInScrollArea;
732 private Drawable mLeftHoverDrawable;
733 private Drawable mRightHoverDrawable;
734
735 void onEnterScrollArea(int direction) {
736 mInScrollArea = true;
737 invalidate();
738 }
739
740 void onExitScrollArea() {
741 mInScrollArea = false;
742 invalidate();
743 }
744
Winson Chungfe1fe262013-04-01 16:52:31 -0700745 /**
746 * Note: this is a reimplementation of View.isLayoutRtl() since that is currently hidden api.
747 */
748 private boolean isLayoutRtl() {
749 return (getLayoutDirection() == LAYOUT_DIRECTION_RTL);
750 }
751
Winson Chung360e63f2012-04-27 13:48:05 -0700752 @Override
753 protected void dispatchDraw(Canvas canvas) {
754 super.dispatchDraw(canvas);
755
756 if (mInScrollArea && !LauncherApplication.isScreenLarge()) {
757 Workspace workspace = mLauncher.getWorkspace();
758 int width = workspace.getWidth();
759 Rect childRect = new Rect();
760 getDescendantRectRelativeToSelf(workspace.getChildAt(0), childRect);
761
762 int page = workspace.getNextPage();
Winson Chungfe1fe262013-04-01 16:52:31 -0700763 final boolean isRtl = isLayoutRtl();
764 CellLayout leftPage = (CellLayout) workspace.getChildAt(isRtl ? page + 1 : page - 1);
765 CellLayout rightPage = (CellLayout) workspace.getChildAt(isRtl ? page - 1 : page + 1);
Winson Chung360e63f2012-04-27 13:48:05 -0700766
767 if (leftPage != null && leftPage.getIsDragOverlapping()) {
768 mLeftHoverDrawable.setBounds(0, childRect.top,
769 mLeftHoverDrawable.getIntrinsicWidth(), childRect.bottom);
770 mLeftHoverDrawable.draw(canvas);
771 } else if (rightPage != null && rightPage.getIsDragOverlapping()) {
772 mRightHoverDrawable.setBounds(width - mRightHoverDrawable.getIntrinsicWidth(),
773 childRect.top, width, childRect.bottom);
774 mRightHoverDrawable.draw(canvas);
775 }
776 }
777 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800778}