blob: 91f97fa4a9b6aaf3f0f6a1bb66ddf2f5e0842077 [file] [log] [blame]
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Daniel Sandler325dc232013-06-05 22:57:57 -040017package com.android.launcher3;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080018
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;
Dave Hawkey26279c62013-11-07 14:26:19 -070030import android.view.KeyEvent;
31import android.view.MotionEvent;
32import android.view.View;
33import android.view.ViewGroup;
Adam Cohen3371da02011-10-25 21:38:29 -070034import android.view.accessibility.AccessibilityEvent;
35import android.view.accessibility.AccessibilityManager;
Adam Cohen716b51e2011-06-30 12:09:54 -070036import android.view.animation.DecelerateInterpolator;
Adam Cohen3e8f8112011-07-02 18:03:00 -070037import android.view.animation.Interpolator;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080038import android.widget.FrameLayout;
Adam Cohenac8c8762011-07-13 11:15:27 -070039import android.widget.TextView;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080040
Adam Cohenbc927f92014-10-28 16:16:02 -070041import com.android.launcher3.InsettableFrameLayout.LayoutParams;
Adam Cohen091440a2015-03-18 14:16:05 -070042import com.android.launcher3.util.Thunk;
Adam Cohenbc927f92014-10-28 16:16:02 -070043
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 */
Adam Cohena6d04922014-10-23 17:28:30 -070049public class DragLayer extends InsettableFrameLayout {
Adam Cohen091440a2015-03-18 14:16:05 -070050 @Thunk DragController mDragController;
Adam Cohen120980b2010-12-08 11:05:37 -080051 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);
Adam Cohen091440a2015-03-18 14:16:05 -070065 @Thunk DragView mDropView = null;
66 @Thunk int mAnchorViewInitialScrollX = 0;
67 @Thunk 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 Cohened66b2b2012-01-23 17:28:51 -080071 public static final int ANIMATION_END_DISAPPEAR = 0;
72 public static final int ANIMATION_END_FADE_OUT = 1;
73 public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
Adam Cohen67882692011-03-11 15:29:03 -080074
Adam Cohenb0f3d742013-10-08 19:16:14 -070075 private TouchCompleteListener mTouchCompleteListener;
76
Adam Cohen432609a2014-03-13 17:03:22 -070077 private View mOverlayView;
78 private int mTopViewIndex;
Adam Cohen2d783ce2014-04-07 14:11:48 -070079 private int mChildCountOnLastUpdate = -1;
Jorim Jaggi55bd9722014-01-16 15:30:42 -080080
Adam Cohen6c5891a2014-07-09 23:53:15 -070081 // Darkening scrim
82 private Drawable mBackground;
83 private float mBackgroundAlpha = 0;
84
Adam Cohen8cc05502014-08-22 18:32:15 -070085 // Related to adjacent page hints
86 private boolean mInScrollArea;
87 private boolean mShowPageHints;
88 private Drawable mLeftHoverDrawable;
89 private Drawable mRightHoverDrawable;
90 private Drawable mLeftHoverDrawableActive;
91 private Drawable mRightHoverDrawableActive;
92
Adam Cohenc2d6e892014-10-16 09:49:24 -070093 private boolean mBlockTouches = false;
94
The Android Open Source Project31dd5032009-03-03 19:32:27 -080095 /**
96 * Used to create a new DragLayer from XML.
97 *
98 * @param context The application's context.
Michael Jurka0e260592010-06-30 17:07:39 -070099 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800100 */
101 public DragLayer(Context context, AttributeSet attrs) {
102 super(context, attrs);
Winson Chungbe62afa2011-02-03 23:14:57 -0800103
104 // Disable multitouch across the workspace/all apps/customize tray
105 setMotionEventSplittingEnabled(false);
Adam Cohen21b41102011-11-01 17:29:52 -0700106 setChildrenDrawingOrderEnabled(true);
Winson Chung360e63f2012-04-27 13:48:05 -0700107
Adam Cohen6c5891a2014-07-09 23:53:15 -0700108 final Resources res = getResources();
Adam Cohenc50438c2014-08-19 17:43:05 -0700109 mLeftHoverDrawable = res.getDrawable(R.drawable.page_hover_left);
110 mRightHoverDrawable = res.getDrawable(R.drawable.page_hover_right);
111 mLeftHoverDrawableActive = res.getDrawable(R.drawable.page_hover_left_active);
112 mRightHoverDrawableActive = res.getDrawable(R.drawable.page_hover_right_active);
Adam Cohen6c5891a2014-07-09 23:53:15 -0700113 mBackground = res.getDrawable(R.drawable.apps_customize_bg);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800114 }
115
Winson Chung4c98d922011-05-31 16:50:48 -0700116 public void setup(Launcher launcher, DragController controller) {
117 mLauncher = launcher;
Joe Onorato00acb122009-08-04 16:04:30 -0400118 mDragController = controller;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800119 }
Adam Cohen76fc0852011-06-17 13:26:23 -0700120
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800121 @Override
122 public boolean dispatchKeyEvent(KeyEvent event) {
Joe Onorato00acb122009-08-04 16:04:30 -0400123 return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800124 }
125
Adam Cohenb670f192014-04-08 15:34:17 -0700126 public void showOverlayView(View overlayView) {
Adam Cohen432609a2014-03-13 17:03:22 -0700127 LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
Adam Cohenb670f192014-04-08 15:34:17 -0700128 mOverlayView = overlayView;
129 addView(overlayView, lp);
130
131 // ensure that the overlay view stays on top. we can't use drawing order for this
132 // because in API level 16 touch dispatch doesn't respect drawing order.
133 mOverlayView.bringToFront();
Adam Cohen432609a2014-03-13 17:03:22 -0700134 }
135
136 public void dismissOverlayView() {
137 removeView(mOverlayView);
138 }
139
Adam Cohen3371da02011-10-25 21:38:29 -0700140 private boolean isEventOverFolderTextRegion(Folder folder, MotionEvent ev) {
141 getDescendantRectRelativeToSelf(folder.getEditTextRegion(), mHitRect);
142 if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
143 return true;
144 }
145 return false;
146 }
147
148 private boolean isEventOverFolder(Folder folder, MotionEvent ev) {
149 getDescendantRectRelativeToSelf(folder, mHitRect);
150 if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
151 return true;
152 }
153 return false;
154 }
155
Adam Cohenc2d6e892014-10-16 09:49:24 -0700156 public void setBlockTouch(boolean block) {
157 mBlockTouches = block;
158 }
159
Adam Cohen76fc0852011-06-17 13:26:23 -0700160 private boolean handleTouchDown(MotionEvent ev, boolean intercept) {
Adam Cohen67882692011-03-11 15:29:03 -0800161 Rect hitRect = new Rect();
162 int x = (int) ev.getX();
163 int y = (int) ev.getY();
Adam Cohen1b607ed2011-03-03 17:26:50 -0800164
Adam Cohenc2d6e892014-10-16 09:49:24 -0700165 if (mBlockTouches) {
166 return true;
167 }
168
Adam Cohen67882692011-03-11 15:29:03 -0800169 for (AppWidgetResizeFrame child: mResizeFrames) {
170 child.getHitRect(hitRect);
171 if (hitRect.contains(x, y)) {
172 if (child.beginResizeIfPointInRegion(x - child.getLeft(), y - child.getTop())) {
173 mCurrentResizeFrame = child;
174 mXDown = x;
175 mYDown = y;
176 requestDisallowInterceptTouchEvent(true);
177 return true;
178 }
Patrick Dubroyd1837cc2011-03-07 13:38:54 -0800179 }
Adam Cohen1b607ed2011-03-03 17:26:50 -0800180 }
Adam Cohen76fc0852011-06-17 13:26:23 -0700181
Adam Cohen1d9af7d2011-06-22 15:26:58 -0700182 Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
Sunny Goyal424418b2014-08-22 16:09:37 -0700183 if (currentFolder != null && intercept) {
Adam Cohen1d9af7d2011-06-22 15:26:58 -0700184 if (currentFolder.isEditingName()) {
Adam Cohen3371da02011-10-25 21:38:29 -0700185 if (!isEventOverFolderTextRegion(currentFolder, ev)) {
Adam Cohen1d9af7d2011-06-22 15:26:58 -0700186 currentFolder.dismissEditingName();
Adam Cohen76fc0852011-06-17 13:26:23 -0700187 return true;
Adam Cohen2801caf2011-05-13 20:57:39 -0700188 }
189 }
Adam Cohen76fc0852011-06-17 13:26:23 -0700190
Adam Cohen1d9af7d2011-06-22 15:26:58 -0700191 getDescendantRectRelativeToSelf(currentFolder, hitRect);
Adam Cohen3371da02011-10-25 21:38:29 -0700192 if (!isEventOverFolder(currentFolder, ev)) {
Adam Cohen76fc0852011-06-17 13:26:23 -0700193 mLauncher.closeFolder();
194 return true;
195 }
Adam Cohen2801caf2011-05-13 20:57:39 -0700196 }
Adam Cohen67882692011-03-11 15:29:03 -0800197 return false;
198 }
199
200 @Override
201 public boolean onInterceptTouchEvent(MotionEvent ev) {
Adam Cohenb0f3d742013-10-08 19:16:14 -0700202 int action = ev.getAction();
203
204 if (action == MotionEvent.ACTION_DOWN) {
Adam Cohen76fc0852011-06-17 13:26:23 -0700205 if (handleTouchDown(ev, true)) {
Adam Cohen67882692011-03-11 15:29:03 -0800206 return true;
207 }
Adam Cohenb0f3d742013-10-08 19:16:14 -0700208 } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
209 if (mTouchCompleteListener != null) {
210 mTouchCompleteListener.onTouchComplete();
211 }
212 mTouchCompleteListener = null;
Adam Cohen67882692011-03-11 15:29:03 -0800213 }
214 clearAllResizeFrames();
Joe Onorato4db52312009-10-06 11:17:43 -0700215 return mDragController.onInterceptTouchEvent(ev);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800216 }
217
218 @Override
Adam Cohen3371da02011-10-25 21:38:29 -0700219 public boolean onInterceptHoverEvent(MotionEvent ev) {
Michael Jurka023c71e2012-10-19 17:32:24 +0200220 if (mLauncher == null || mLauncher.getWorkspace() == null) {
221 return false;
222 }
Adam Cohen3371da02011-10-25 21:38:29 -0700223 Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
224 if (currentFolder == null) {
225 return false;
226 } else {
Michael Jurka8b805b12012-04-18 14:23:14 -0700227 AccessibilityManager accessibilityManager = (AccessibilityManager)
228 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
229 if (accessibilityManager.isTouchExplorationEnabled()) {
Adam Cohen3371da02011-10-25 21:38:29 -0700230 final int action = ev.getAction();
231 boolean isOverFolder;
232 switch (action) {
233 case MotionEvent.ACTION_HOVER_ENTER:
234 isOverFolder = isEventOverFolder(currentFolder, ev);
235 if (!isOverFolder) {
236 sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
237 mHoverPointClosesFolder = true;
238 return true;
Adam Cohen3371da02011-10-25 21:38:29 -0700239 }
Mihail Dumitrescu693fbc72014-01-03 12:40:22 +0000240 mHoverPointClosesFolder = false;
241 break;
Adam Cohen3371da02011-10-25 21:38:29 -0700242 case MotionEvent.ACTION_HOVER_MOVE:
243 isOverFolder = isEventOverFolder(currentFolder, ev);
244 if (!isOverFolder && !mHoverPointClosesFolder) {
245 sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
246 mHoverPointClosesFolder = true;
247 return true;
Mihail Dumitrescu693fbc72014-01-03 12:40:22 +0000248 } else if (!isOverFolder) {
Adam Cohen3371da02011-10-25 21:38:29 -0700249 return true;
250 }
Mihail Dumitrescu693fbc72014-01-03 12:40:22 +0000251 mHoverPointClosesFolder = false;
Adam Cohen3371da02011-10-25 21:38:29 -0700252 }
253 }
254 }
255 return false;
256 }
257
258 private void sendTapOutsideFolderAccessibilityEvent(boolean isEditingName) {
Michael Jurka8b805b12012-04-18 14:23:14 -0700259 AccessibilityManager accessibilityManager = (AccessibilityManager)
260 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
261 if (accessibilityManager.isEnabled()) {
Adam Cohen3371da02011-10-25 21:38:29 -0700262 int stringId = isEditingName ? R.string.folder_tap_to_rename : R.string.folder_tap_to_close;
263 AccessibilityEvent event = AccessibilityEvent.obtain(
264 AccessibilityEvent.TYPE_VIEW_FOCUSED);
265 onInitializeAccessibilityEvent(event);
Michael Jurka8b805b12012-04-18 14:23:14 -0700266 event.getText().add(getContext().getString(stringId));
267 accessibilityManager.sendAccessibilityEvent(event);
Adam Cohen3371da02011-10-25 21:38:29 -0700268 }
269 }
270
271 @Override
Winson Chung83ca4802013-04-12 15:10:52 -0700272 public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
273 Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
274 if (currentFolder != null) {
275 if (child == currentFolder) {
276 return super.onRequestSendAccessibilityEvent(child, event);
277 }
278 // Skip propagating onRequestSendAccessibilityEvent all for other children
279 // when a folder is open
280 return false;
281 }
282 return super.onRequestSendAccessibilityEvent(child, event);
283 }
284
285 @Override
286 public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) {
287 Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
288 if (currentFolder != null) {
289 // Only add the folder as a child for accessibility when it is open
290 childrenForAccessibility.add(currentFolder);
291 } else {
292 super.addChildrenForAccessibility(childrenForAccessibility);
293 }
294 }
295
296 @Override
Adam Cohen3371da02011-10-25 21:38:29 -0700297 public boolean onHoverEvent(MotionEvent ev) {
298 // If we've received this, we've already done the necessary handling
299 // in onInterceptHoverEvent. Return true to consume the event.
300 return false;
301 }
302
303 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800304 public boolean onTouchEvent(MotionEvent ev) {
Adam Cohen67882692011-03-11 15:29:03 -0800305 boolean handled = false;
306 int action = ev.getAction();
307
308 int x = (int) ev.getX();
309 int y = (int) ev.getY();
310
Adam Cohenc2d6e892014-10-16 09:49:24 -0700311 if (mBlockTouches) {
312 return true;
313 }
314
Adam Cohenb0f3d742013-10-08 19:16:14 -0700315 if (action == MotionEvent.ACTION_DOWN) {
316 if (handleTouchDown(ev, false)) {
317 return true;
Adam Cohen67882692011-03-11 15:29:03 -0800318 }
Adam Cohenb0f3d742013-10-08 19:16:14 -0700319 } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
320 if (mTouchCompleteListener != null) {
321 mTouchCompleteListener.onTouchComplete();
322 }
323 mTouchCompleteListener = null;
Adam Cohen67882692011-03-11 15:29:03 -0800324 }
325
326 if (mCurrentResizeFrame != null) {
327 handled = true;
328 switch (action) {
329 case MotionEvent.ACTION_MOVE:
330 mCurrentResizeFrame.visualizeResizeForDelta(x - mXDown, y - mYDown);
331 break;
332 case MotionEvent.ACTION_CANCEL:
333 case MotionEvent.ACTION_UP:
Adam Cohenbebf0422012-04-11 18:06:28 -0700334 mCurrentResizeFrame.visualizeResizeForDelta(x - mXDown, y - mYDown);
335 mCurrentResizeFrame.onTouchUp();
Adam Cohen67882692011-03-11 15:29:03 -0800336 mCurrentResizeFrame = null;
337 }
338 }
339 if (handled) return true;
Joe Onorato4db52312009-10-06 11:17:43 -0700340 return mDragController.onTouchEvent(ev);
Romain Guy91a9c962009-06-12 13:52:17 -0700341 }
Romain Guyea3763c2010-01-11 18:02:04 -0800342
Adam Cohen35e7e642011-07-17 14:47:18 -0700343 /**
344 * Determine the rect of the descendant in this DragLayer's coordinates
345 *
346 * @param descendant The descendant whose coordinates we want to find.
347 * @param r The rect into which to place the results.
348 * @return The factor by which this descendant is scaled relative to this DragLayer.
349 */
Adam Cohenac8c8762011-07-13 11:15:27 -0700350 public float getDescendantRectRelativeToSelf(View descendant, Rect r) {
Adam Cohen716b51e2011-06-30 12:09:54 -0700351 mTmpXY[0] = 0;
352 mTmpXY[1] = 0;
Adam Cohenac8c8762011-07-13 11:15:27 -0700353 float scale = getDescendantCoordRelativeToSelf(descendant, mTmpXY);
Adam Cohen7d30a372013-07-01 17:03:59 -0700354
Adam Cohen8dfcba42011-07-07 16:38:18 -0700355 r.set(mTmpXY[0], mTmpXY[1],
Adam Cohen7d30a372013-07-01 17:03:59 -0700356 (int) (mTmpXY[0] + scale * descendant.getMeasuredWidth()),
357 (int) (mTmpXY[1] + scale * descendant.getMeasuredHeight()));
Adam Cohenac8c8762011-07-13 11:15:27 -0700358 return scale;
Adam Cohen716b51e2011-06-30 12:09:54 -0700359 }
Adam Cohen76fc0852011-06-17 13:26:23 -0700360
Adam Cohen307fe232012-08-16 17:55:58 -0700361 public float getLocationInDragLayer(View child, int[] loc) {
Adam Cohen35e7e642011-07-17 14:47:18 -0700362 loc[0] = 0;
363 loc[1] = 0;
Adam Cohen307fe232012-08-16 17:55:58 -0700364 return getDescendantCoordRelativeToSelf(child, loc);
Adam Cohen35e7e642011-07-17 14:47:18 -0700365 }
366
Adam Cohen7d30a372013-07-01 17:03:59 -0700367 public float getDescendantCoordRelativeToSelf(View descendant, int[] coord) {
368 return getDescendantCoordRelativeToSelf(descendant, coord, false);
369 }
370
Adam Cohen35e7e642011-07-17 14:47:18 -0700371 /**
372 * Given a coordinate relative to the descendant, find the coordinate in this DragLayer's
373 * coordinates.
374 *
375 * @param descendant The descendant to which the passed coordinate is relative.
376 * @param coord The coordinate that we want mapped.
Adam Cohen7d30a372013-07-01 17:03:59 -0700377 * @param includeRootScroll Whether or not to account for the scroll of the root descendant:
378 * sometimes this is relevant as in a child's coordinates within the root descendant.
Adam Cohen307fe232012-08-16 17:55:58 -0700379 * @return The factor by which this descendant is scaled relative to this DragLayer. Caution
380 * this scale factor is assumed to be equal in X and Y, and so if at any point this
381 * assumption fails, we will need to return a pair of scale factors.
Adam Cohen35e7e642011-07-17 14:47:18 -0700382 */
Adam Cohen7d30a372013-07-01 17:03:59 -0700383 public float getDescendantCoordRelativeToSelf(View descendant, int[] coord,
384 boolean includeRootScroll) {
Winson Chungc763c4e2013-07-19 13:49:06 -0700385 return Utilities.getDescendantCoordRelativeToParent(descendant, this,
Winson Chungc9ca2982013-07-19 12:07:38 -0700386 coord, includeRootScroll);
387 }
388
Adam Cohen7d30a372013-07-01 17:03:59 -0700389 /**
390 * Inverse of {@link #getDescendantCoordRelativeToSelf(View, int[])}.
391 */
392 public float mapCoordInSelfToDescendent(View descendant, int[] coord) {
Winson Chungc763c4e2013-07-19 13:49:06 -0700393 return Utilities.mapCoordInSelfToDescendent(descendant, this, coord);
Adam Cohen76fc0852011-06-17 13:26:23 -0700394 }
395
Adam Cohen3e8f8112011-07-02 18:03:00 -0700396 public void getViewRectRelativeToSelf(View v, Rect r) {
397 int[] loc = new int[2];
Adam Cohen8dfcba42011-07-07 16:38:18 -0700398 getLocationInWindow(loc);
Adam Cohen3e8f8112011-07-02 18:03:00 -0700399 int x = loc[0];
400 int y = loc[1];
Adam Cohen716b51e2011-06-30 12:09:54 -0700401
Adam Cohen8dfcba42011-07-07 16:38:18 -0700402 v.getLocationInWindow(loc);
Adam Cohen3e8f8112011-07-02 18:03:00 -0700403 int vX = loc[0];
404 int vY = loc[1];
Adam Cohen716b51e2011-06-30 12:09:54 -0700405
Adam Cohen3e8f8112011-07-02 18:03:00 -0700406 int left = vX - x;
407 int top = vY - y;
408 r.set(left, top, left + v.getMeasuredWidth(), top + v.getMeasuredHeight());
Adam Cohen716b51e2011-06-30 12:09:54 -0700409 }
410
Romain Guyea3763c2010-01-11 18:02:04 -0800411 @Override
412 public boolean dispatchUnhandledMove(View focused, int direction) {
413 return mDragController.dispatchUnhandledMove(focused, direction);
414 }
Adam Cohen120980b2010-12-08 11:05:37 -0800415
Adam Cohenbc927f92014-10-28 16:16:02 -0700416 @Override
417 public LayoutParams generateLayoutParams(AttributeSet attrs) {
418 return new LayoutParams(getContext(), attrs);
419 }
420
421 @Override
422 protected LayoutParams generateDefaultLayoutParams() {
423 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
424 }
425
426 // Override to allow type-checking of LayoutParams.
427 @Override
428 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
429 return p instanceof LayoutParams;
430 }
431
432 @Override
433 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
434 return new LayoutParams(p);
435 }
436
437 public static class LayoutParams extends InsettableFrameLayout.LayoutParams {
Adam Cohen67882692011-03-11 15:29:03 -0800438 public int x, y;
439 public boolean customPosition = false;
440
Adam Cohenbc927f92014-10-28 16:16:02 -0700441 public LayoutParams(Context c, AttributeSet attrs) {
442 super(c, attrs);
443 }
444
Adam Cohen67882692011-03-11 15:29:03 -0800445 public LayoutParams(int width, int height) {
446 super(width, height);
447 }
448
Adam Cohenbc927f92014-10-28 16:16:02 -0700449 public LayoutParams(ViewGroup.LayoutParams lp) {
450 super(lp);
451 }
452
Adam Cohen67882692011-03-11 15:29:03 -0800453 public void setWidth(int width) {
454 this.width = width;
455 }
456
457 public int getWidth() {
458 return width;
459 }
460
461 public void setHeight(int height) {
462 this.height = height;
463 }
464
465 public int getHeight() {
466 return height;
467 }
468
469 public void setX(int x) {
470 this.x = x;
471 }
472
473 public int getX() {
474 return x;
475 }
476
477 public void setY(int y) {
478 this.y = y;
479 }
480
481 public int getY() {
482 return y;
483 }
484 }
485
486 protected void onLayout(boolean changed, int l, int t, int r, int b) {
487 super.onLayout(changed, l, t, r, b);
488 int count = getChildCount();
489 for (int i = 0; i < count; i++) {
490 View child = getChildAt(i);
491 final FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) child.getLayoutParams();
492 if (flp instanceof LayoutParams) {
493 final LayoutParams lp = (LayoutParams) flp;
494 if (lp.customPosition) {
495 child.layout(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height);
496 }
497 }
498 }
499 }
500
501 public void clearAllResizeFrames() {
502 if (mResizeFrames.size() > 0) {
503 for (AppWidgetResizeFrame frame: mResizeFrames) {
Adam Cohenbebf0422012-04-11 18:06:28 -0700504 frame.commitResize();
Adam Cohen67882692011-03-11 15:29:03 -0800505 removeView(frame);
506 }
507 mResizeFrames.clear();
508 }
509 }
510
511 public boolean hasResizeFrames() {
512 return mResizeFrames.size() > 0;
513 }
514
515 public boolean isWidgetBeingResized() {
516 return mCurrentResizeFrame != null;
517 }
518
519 public void addResizeFrame(ItemInfo itemInfo, LauncherAppWidgetHostView widget,
520 CellLayout cellLayout) {
521 AppWidgetResizeFrame resizeFrame = new AppWidgetResizeFrame(getContext(),
Michael Jurka3a9fced2012-04-13 14:44:29 -0700522 widget, cellLayout, this);
Adam Cohen67882692011-03-11 15:29:03 -0800523
524 LayoutParams lp = new LayoutParams(-1, -1);
525 lp.customPosition = true;
526
527 addView(resizeFrame, lp);
528 mResizeFrames.add(resizeFrame);
529
530 resizeFrame.snapToWidget(false);
531 }
Adam Cohen716b51e2011-06-30 12:09:54 -0700532
533 public void animateViewIntoPosition(DragView dragView, final View child) {
Adam Cohen28f852a2013-10-15 14:34:05 -0700534 animateViewIntoPosition(dragView, child, null, null);
Winson Chung557d6ed2011-07-08 15:34:52 -0700535 }
Adam Cohenac8c8762011-07-13 11:15:27 -0700536
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800537 public void animateViewIntoPosition(DragView dragView, final int[] pos, float alpha,
538 float scaleX, float scaleY, int animationEndStyle, Runnable onFinishRunnable,
539 int duration) {
Adam Cohenb7e16182011-07-15 17:55:02 -0700540 Rect r = new Rect();
541 getViewRectRelativeToSelf(dragView, r);
542 final int fromX = r.left;
543 final int fromY = r.top;
544
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800545 animateViewIntoPosition(dragView, fromX, fromY, pos[0], pos[1], alpha, 1, 1, scaleX, scaleY,
Adam Cohened66b2b2012-01-23 17:28:51 -0800546 onFinishRunnable, animationEndStyle, duration, null);
547 }
548
Winson Chung557d6ed2011-07-08 15:34:52 -0700549 public void animateViewIntoPosition(DragView dragView, final View child,
Adam Cohen28f852a2013-10-15 14:34:05 -0700550 final Runnable onFinishAnimationRunnable, View anchorView) {
551 animateViewIntoPosition(dragView, child, -1, onFinishAnimationRunnable, anchorView);
Adam Cohened51cc92011-08-01 20:28:08 -0700552 }
553
554 public void animateViewIntoPosition(DragView dragView, final View child, int duration,
Adam Cohen6441de02011-12-14 14:25:32 -0800555 final Runnable onFinishAnimationRunnable, View anchorView) {
Michael Jurkaa52570f2012-03-20 03:18:20 -0700556 ShortcutAndWidgetContainer parentChildren = (ShortcutAndWidgetContainer) child.getParent();
Adam Cohen716b51e2011-06-30 12:09:54 -0700557 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
Winson Chungeecf02d2012-03-02 17:14:58 -0800558 parentChildren.measureChild(child);
Adam Cohen716b51e2011-06-30 12:09:54 -0700559
Adam Cohen3e8f8112011-07-02 18:03:00 -0700560 Rect r = new Rect();
561 getViewRectRelativeToSelf(dragView, r);
Adam Cohen716b51e2011-06-30 12:09:54 -0700562
563 int coord[] = new int[2];
Adam Cohen307fe232012-08-16 17:55:58 -0700564 float childScale = child.getScaleX();
565 coord[0] = lp.x + (int) (child.getMeasuredWidth() * (1 - childScale) / 2);
566 coord[1] = lp.y + (int) (child.getMeasuredHeight() * (1 - childScale) / 2);
Winson Chungeecf02d2012-03-02 17:14:58 -0800567
Adam Cohen8dfcba42011-07-07 16:38:18 -0700568 // Since the child hasn't necessarily been laid out, we force the lp to be updated with
Adam Cohenac8c8762011-07-13 11:15:27 -0700569 // the correct coordinates (above) and use these to determine the final location
Winson Chung557d6ed2011-07-08 15:34:52 -0700570 float scale = getDescendantCoordRelativeToSelf((View) child.getParent(), coord);
Adam Cohen307fe232012-08-16 17:55:58 -0700571 // We need to account for the scale of the child itself, as the above only accounts for
572 // for the scale in parents.
573 scale *= childScale;
Adam Cohenac8c8762011-07-13 11:15:27 -0700574 int toX = coord[0];
575 int toY = coord[1];
Winson Chungeeb5bbc2013-11-13 15:47:05 -0800576 float toScale = scale;
Andrew Flynn4497ebf2012-05-09 11:28:00 -0700577 if (child instanceof TextView) {
578 TextView tv = (TextView) child;
Winson Chungeeb5bbc2013-11-13 15:47:05 -0800579 // Account for the source scale of the icon (ie. from AllApps to Workspace, in which
580 // the workspace may have smaller icon bounds).
581 toScale = scale / dragView.getIntrinsicIconScaleFactor();
Andrew Flynn4497ebf2012-05-09 11:28:00 -0700582
583 // The child may be scaled (always about the center of the view) so to account for it,
584 // we have to offset the position by the scaled size. Once we do that, we can center
585 // the drag view about the scaled child view.
Winson Chungeeb5bbc2013-11-13 15:47:05 -0800586 toY += Math.round(toScale * tv.getPaddingTop());
587 toY -= dragView.getMeasuredHeight() * (1 - toScale) / 2;
Sunny Goyal95abbb32014-08-04 10:53:22 -0700588 if (dragView.getDragVisualizeOffset() != null) {
589 toY -= Math.round(toScale * dragView.getDragVisualizeOffset().y);
590 }
591
Andrew Flynn4497ebf2012-05-09 11:28:00 -0700592 toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2;
593 } else if (child instanceof FolderIcon) {
Winson Chungea359c62011-08-03 17:06:35 -0700594 // Account for holographic blur padding on the drag view
Winson Chung5f8afe62013-08-12 16:19:28 -0700595 toY += Math.round(scale * (child.getPaddingTop() - dragView.getDragRegionTop()));
Adam Cohen307fe232012-08-16 17:55:58 -0700596 toY -= scale * Workspace.DRAG_BITMAP_PADDING / 2;
597 toY -= (1 - scale) * dragView.getMeasuredHeight() / 2;
Winson Chungea359c62011-08-03 17:06:35 -0700598 // Center in the x coordinate about the target's drawable
599 toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2;
Adam Cohenac8c8762011-07-13 11:15:27 -0700600 } else {
601 toY -= (Math.round(scale * (dragView.getHeight() - child.getMeasuredHeight()))) / 2;
602 toX -= (Math.round(scale * (dragView.getMeasuredWidth()
603 - child.getMeasuredWidth()))) / 2;
604 }
605
606 final int fromX = r.left;
607 final int fromY = r.top;
Adam Cohen716b51e2011-06-30 12:09:54 -0700608 child.setVisibility(INVISIBLE);
Adam Cohen8dfcba42011-07-07 16:38:18 -0700609 Runnable onCompleteRunnable = new Runnable() {
610 public void run() {
611 child.setVisibility(VISIBLE);
Adam Cohend41fbf52012-02-16 23:53:59 -0800612 if (onFinishAnimationRunnable != null) {
613 onFinishAnimationRunnable.run();
614 }
Adam Cohen8dfcba42011-07-07 16:38:18 -0700615 }
616 };
Winson Chungeeb5bbc2013-11-13 15:47:05 -0800617 animateViewIntoPosition(dragView, fromX, fromY, toX, toY, 1, 1, 1, toScale, toScale,
Adam Cohend41fbf52012-02-16 23:53:59 -0800618 onCompleteRunnable, ANIMATION_END_DISAPPEAR, duration, anchorView);
Winson Chung557d6ed2011-07-08 15:34:52 -0700619 }
620
Adam Cohend41fbf52012-02-16 23:53:59 -0800621 public void animateViewIntoPosition(final DragView view, final int fromX, final int fromY,
Adam Cohened66b2b2012-01-23 17:28:51 -0800622 final int toX, final int toY, float finalAlpha, float initScaleX, float initScaleY,
623 float finalScaleX, float finalScaleY, Runnable onCompleteRunnable,
624 int animationEndStyle, int duration, View anchorView) {
Adam Cohen8dfcba42011-07-07 16:38:18 -0700625 Rect from = new Rect(fromX, fromY, fromX +
626 view.getMeasuredWidth(), fromY + view.getMeasuredHeight());
Adam Cohen3e8f8112011-07-02 18:03:00 -0700627 Rect to = new Rect(toX, toY, toX + view.getMeasuredWidth(), toY + view.getMeasuredHeight());
Adam Cohened66b2b2012-01-23 17:28:51 -0800628 animateView(view, from, to, finalAlpha, initScaleX, initScaleY, finalScaleX, finalScaleY, duration,
629 null, null, onCompleteRunnable, animationEndStyle, anchorView);
Adam Cohen3e8f8112011-07-02 18:03:00 -0700630 }
631
Adam Cohenb7e16182011-07-15 17:55:02 -0700632 /**
633 * This method animates a view at the end of a drag and drop animation.
634 *
635 * @param view The view to be animated. This view is drawn directly into DragLayer, and so
636 * doesn't need to be a child of DragLayer.
637 * @param from The initial location of the view. Only the left and top parameters are used.
638 * @param to The final location of the view. Only the left and top parameters are used. This
639 * location doesn't account for scaling, and so should be centered about the desired
640 * final location (including scaling).
641 * @param finalAlpha The final alpha of the view, in case we want it to fade as it animates.
642 * @param finalScale The final scale of the view. The view is scaled about its center.
643 * @param duration The duration of the animation.
644 * @param motionInterpolator The interpolator to use for the location of the view.
645 * @param alphaInterpolator The interpolator to use for the alpha of the view.
646 * @param onCompleteRunnable Optional runnable to run on animation completion.
647 * @param fadeOut Whether or not to fade out the view once the animation completes. If true,
648 * the runnable will execute after the view is faded out.
Adam Cohen6441de02011-12-14 14:25:32 -0800649 * @param anchorView If not null, this represents the view which the animated view stays
650 * anchored to in case scrolling is currently taking place. Note: currently this is
651 * only used for the X dimension for the case of the workspace.
Adam Cohenb7e16182011-07-15 17:55:02 -0700652 */
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800653 public void animateView(final DragView view, final Rect from, final Rect to,
654 final float finalAlpha, final float initScaleX, final float initScaleY,
655 final float finalScaleX, final float finalScaleY, int duration,
656 final Interpolator motionInterpolator, final Interpolator alphaInterpolator,
657 final Runnable onCompleteRunnable, final int animationEndStyle, View anchorView) {
658
Adam Cohen716b51e2011-06-30 12:09:54 -0700659 // Calculate the duration of the animation based on the object's distance
Sunny Goyalf7a29e82015-04-24 15:20:43 -0700660 final float dist = (float) Math.hypot(to.left - from.left, to.top - from.top);
Adam Cohen716b51e2011-06-30 12:09:54 -0700661 final Resources res = getResources();
662 final float maxDist = (float) res.getInteger(R.integer.config_dropAnimMaxDist);
Adam Cohen3e8f8112011-07-02 18:03:00 -0700663
664 // If duration < 0, this is a cue to compute the duration based on the distance
665 if (duration < 0) {
666 duration = res.getInteger(R.integer.config_dropAnimMaxDuration);
667 if (dist < maxDist) {
Adam Cohen8dfcba42011-07-07 16:38:18 -0700668 duration *= mCubicEaseOutInterpolator.getInterpolation(dist / maxDist);
Adam Cohen3e8f8112011-07-02 18:03:00 -0700669 }
Winson Chung61b0c692012-02-23 16:31:13 -0800670 duration = Math.max(duration, res.getInteger(R.integer.config_dropAnimMinDuration));
Adam Cohen716b51e2011-06-30 12:09:54 -0700671 }
672
Winson Chung043f2af2012-03-01 16:09:54 -0800673 // Fall back to cubic ease out interpolator for the animation if none is specified
674 TimeInterpolator interpolator = null;
Adam Cohen3e8f8112011-07-02 18:03:00 -0700675 if (alphaInterpolator == null || motionInterpolator == null) {
Winson Chung043f2af2012-03-01 16:09:54 -0800676 interpolator = mCubicEaseOutInterpolator;
Adam Cohen3e8f8112011-07-02 18:03:00 -0700677 }
Adam Cohen716b51e2011-06-30 12:09:54 -0700678
Winson Chung043f2af2012-03-01 16:09:54 -0800679 // Animate the view
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800680 final float initAlpha = view.getAlpha();
Winson Chung043f2af2012-03-01 16:09:54 -0800681 final float dropViewScale = view.getScaleX();
682 AnimatorUpdateListener updateCb = new AnimatorUpdateListener() {
683 @Override
Adam Cohen716b51e2011-06-30 12:09:54 -0700684 public void onAnimationUpdate(ValueAnimator animation) {
685 final float percent = (Float) animation.getAnimatedValue();
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800686 final int width = view.getMeasuredWidth();
687 final int height = view.getMeasuredHeight();
Adam Cohen716b51e2011-06-30 12:09:54 -0700688
Adam Cohen3e8f8112011-07-02 18:03:00 -0700689 float alphaPercent = alphaInterpolator == null ? percent :
690 alphaInterpolator.getInterpolation(percent);
691 float motionPercent = motionInterpolator == null ? percent :
692 motionInterpolator.getInterpolation(percent);
693
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800694 float initialScaleX = initScaleX * dropViewScale;
695 float initialScaleY = initScaleY * dropViewScale;
696 float scaleX = finalScaleX * percent + initialScaleX * (1 - percent);
697 float scaleY = finalScaleY * percent + initialScaleY * (1 - percent);
698 float alpha = finalAlpha * alphaPercent + initAlpha * (1 - alphaPercent);
699
700 float fromLeft = from.left + (initialScaleX - 1f) * width / 2;
701 float fromTop = from.top + (initialScaleY - 1f) * height / 2;
702
703 int x = (int) (fromLeft + Math.round(((to.left - fromLeft) * motionPercent)));
704 int y = (int) (fromTop + Math.round(((to.top - fromTop) * motionPercent)));
705
Adam Cohen28f852a2013-10-15 14:34:05 -0700706 int anchorAdjust = mAnchorView == null ? 0 : (int) (mAnchorView.getScaleX() *
707 (mAnchorViewInitialScrollX - mAnchorView.getScrollX()));
708
709 int xPos = x - mDropView.getScrollX() + anchorAdjust;
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800710 int yPos = y - mDropView.getScrollY();
711
712 mDropView.setTranslationX(xPos);
713 mDropView.setTranslationY(yPos);
714 mDropView.setScaleX(scaleX);
715 mDropView.setScaleY(scaleY);
716 mDropView.setAlpha(alpha);
Adam Cohen716b51e2011-06-30 12:09:54 -0700717 }
Winson Chung043f2af2012-03-01 16:09:54 -0800718 };
719 animateView(view, updateCb, duration, interpolator, onCompleteRunnable, animationEndStyle,
720 anchorView);
721 }
722
723 public void animateView(final DragView view, AnimatorUpdateListener updateCb, int duration,
724 TimeInterpolator interpolator, final Runnable onCompleteRunnable,
725 final int animationEndStyle, View anchorView) {
726 // Clean up the previous animations
727 if (mDropAnim != null) mDropAnim.cancel();
728 if (mFadeOutAnim != null) mFadeOutAnim.cancel();
729
730 // Show the drop view if it was previously hidden
731 mDropView = view;
732 mDropView.cancelAnimation();
733 mDropView.resetLayoutParams();
734
735 // Set the anchor view if the page is scrolling
736 if (anchorView != null) {
737 mAnchorViewInitialScrollX = anchorView.getScrollX();
738 }
739 mAnchorView = anchorView;
740
741 // Create and start the animation
742 mDropAnim = new ValueAnimator();
743 mDropAnim.setInterpolator(interpolator);
744 mDropAnim.setDuration(duration);
745 mDropAnim.setFloatValues(0f, 1f);
746 mDropAnim.addUpdateListener(updateCb);
Adam Cohen3e8f8112011-07-02 18:03:00 -0700747 mDropAnim.addListener(new AnimatorListenerAdapter() {
748 public void onAnimationEnd(Animator animation) {
Adam Cohen8dfcba42011-07-07 16:38:18 -0700749 if (onCompleteRunnable != null) {
750 onCompleteRunnable.run();
751 }
Adam Cohened66b2b2012-01-23 17:28:51 -0800752 switch (animationEndStyle) {
753 case ANIMATION_END_DISAPPEAR:
754 clearAnimatedView();
755 break;
756 case ANIMATION_END_FADE_OUT:
Adam Cohen8dfcba42011-07-07 16:38:18 -0700757 fadeOutDragView();
Adam Cohened66b2b2012-01-23 17:28:51 -0800758 break;
759 case ANIMATION_END_REMAIN_VISIBLE:
760 break;
Adam Cohen3e8f8112011-07-02 18:03:00 -0700761 }
762 }
763 });
Adam Cohen716b51e2011-06-30 12:09:54 -0700764 mDropAnim.start();
765 }
766
Adam Cohened66b2b2012-01-23 17:28:51 -0800767 public void clearAnimatedView() {
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800768 if (mDropAnim != null) {
769 mDropAnim.cancel();
770 }
771 if (mDropView != null) {
Adam Cohen2d783ce2014-04-07 14:11:48 -0700772 mDragController.onDeferredEndDrag(mDropView);
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800773 }
Adam Cohened66b2b2012-01-23 17:28:51 -0800774 mDropView = null;
Adam Cohened66b2b2012-01-23 17:28:51 -0800775 invalidate();
776 }
777
778 public View getAnimatedView() {
779 return mDropView;
780 }
781
Adam Cohen091440a2015-03-18 14:16:05 -0700782 @Thunk void fadeOutDragView() {
Adam Cohen8dfcba42011-07-07 16:38:18 -0700783 mFadeOutAnim = new ValueAnimator();
784 mFadeOutAnim.setDuration(150);
785 mFadeOutAnim.setFloatValues(0f, 1f);
786 mFadeOutAnim.removeAllUpdateListeners();
787 mFadeOutAnim.addUpdateListener(new AnimatorUpdateListener() {
788 public void onAnimationUpdate(ValueAnimator animation) {
789 final float percent = (Float) animation.getAnimatedValue();
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800790
791 float alpha = 1 - percent;
792 mDropView.setAlpha(alpha);
Adam Cohen8dfcba42011-07-07 16:38:18 -0700793 }
794 });
795 mFadeOutAnim.addListener(new AnimatorListenerAdapter() {
796 public void onAnimationEnd(Animator animation) {
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800797 if (mDropView != null) {
Winson Chung043f2af2012-03-01 16:09:54 -0800798 mDragController.onDeferredEndDrag(mDropView);
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800799 }
Adam Cohen8dfcba42011-07-07 16:38:18 -0700800 mDropView = null;
Adam Cohened66b2b2012-01-23 17:28:51 -0800801 invalidate();
Adam Cohen8dfcba42011-07-07 16:38:18 -0700802 }
803 });
804 mFadeOutAnim.start();
805 }
806
Adam Cohen716b51e2011-06-30 12:09:54 -0700807 @Override
Michael Jurka8b805b12012-04-18 14:23:14 -0700808 public void onChildViewAdded(View parent, View child) {
Adam Cohena6d04922014-10-23 17:28:30 -0700809 super.onChildViewAdded(parent, child);
Adam Cohenb670f192014-04-08 15:34:17 -0700810 if (mOverlayView != null) {
811 // ensure that the overlay view stays on top. we can't use drawing order for this
812 // because in API level 16 touch dispatch doesn't respect drawing order.
813 mOverlayView.bringToFront();
814 }
Adam Cohen21b41102011-11-01 17:29:52 -0700815 updateChildIndices();
816 }
817
818 @Override
Michael Jurka8b805b12012-04-18 14:23:14 -0700819 public void onChildViewRemoved(View parent, View child) {
Adam Cohen21b41102011-11-01 17:29:52 -0700820 updateChildIndices();
821 }
822
Adam Cohen432609a2014-03-13 17:03:22 -0700823 @Override
824 public void bringChildToFront(View child) {
825 super.bringChildToFront(child);
Adam Cohenb670f192014-04-08 15:34:17 -0700826 if (child != mOverlayView && mOverlayView != null) {
827 // ensure that the overlay view stays on top. we can't use drawing order for this
828 // because in API level 16 touch dispatch doesn't respect drawing order.
829 mOverlayView.bringToFront();
830 }
Adam Cohen432609a2014-03-13 17:03:22 -0700831 updateChildIndices();
832 }
833
Adam Cohen21b41102011-11-01 17:29:52 -0700834 private void updateChildIndices() {
Adam Cohen432609a2014-03-13 17:03:22 -0700835 mTopViewIndex = -1;
Jorim Jaggi55bd9722014-01-16 15:30:42 -0800836 int childCount = getChildCount();
837 for (int i = 0; i < childCount; i++) {
Adam Cohenb670f192014-04-08 15:34:17 -0700838 if (getChildAt(i) instanceof DragView) {
Adam Cohen432609a2014-03-13 17:03:22 -0700839 mTopViewIndex = i;
Jorim Jaggi55bd9722014-01-16 15:30:42 -0800840 }
Adam Cohen21b41102011-11-01 17:29:52 -0700841 }
Adam Cohen2d783ce2014-04-07 14:11:48 -0700842 mChildCountOnLastUpdate = childCount;
Adam Cohen21b41102011-11-01 17:29:52 -0700843 }
844
845 @Override
846 protected int getChildDrawingOrder(int childCount, int i) {
Adam Cohen2d783ce2014-04-07 14:11:48 -0700847 if (mChildCountOnLastUpdate != childCount) {
848 // between platform versions 17 and 18, behavior for onChildViewRemoved / Added changed.
849 // Pre-18, the child was not added / removed by the time of those callbacks. We need to
850 // force update our representation of things here to avoid crashing on pre-18 devices
851 // in certain instances.
852 updateChildIndices();
853 }
854
Adam Cohen432609a2014-03-13 17:03:22 -0700855 // i represents the current draw iteration
856 if (mTopViewIndex == -1) {
857 // in general we do nothing
Adam Cohen21b41102011-11-01 17:29:52 -0700858 return i;
Adam Cohen432609a2014-03-13 17:03:22 -0700859 } else if (i == childCount - 1) {
860 // if we have a top index, we return it when drawing last item (highest z-order)
861 return mTopViewIndex;
862 } else if (i < mTopViewIndex) {
Jorim Jaggi55bd9722014-01-16 15:30:42 -0800863 return i;
Adam Cohen21b41102011-11-01 17:29:52 -0700864 } else {
Adam Cohen432609a2014-03-13 17:03:22 -0700865 // for indexes greater than the top index, we fetch one item above to shift for the
866 // displacement of the top index
867 return i + 1;
Adam Cohen21b41102011-11-01 17:29:52 -0700868 }
869 }
Winson Chung360e63f2012-04-27 13:48:05 -0700870
Winson Chung360e63f2012-04-27 13:48:05 -0700871 void onEnterScrollArea(int direction) {
872 mInScrollArea = true;
873 invalidate();
874 }
875
876 void onExitScrollArea() {
877 mInScrollArea = false;
878 invalidate();
879 }
880
Adam Cohenc50438c2014-08-19 17:43:05 -0700881 void showPageHints() {
882 mShowPageHints = true;
883 invalidate();
884 }
885
886 void hidePageHints() {
887 mShowPageHints = false;
888 invalidate();
889 }
890
Winson Chungfe1fe262013-04-01 16:52:31 -0700891 /**
892 * Note: this is a reimplementation of View.isLayoutRtl() since that is currently hidden api.
893 */
894 private boolean isLayoutRtl() {
895 return (getLayoutDirection() == LAYOUT_DIRECTION_RTL);
896 }
897
Winson Chung360e63f2012-04-27 13:48:05 -0700898 @Override
899 protected void dispatchDraw(Canvas canvas) {
Adam Cohen6c5891a2014-07-09 23:53:15 -0700900 // Draw the background gradient below children.
901 if (mBackground != null && mBackgroundAlpha > 0.0f) {
902 int alpha = (int) (mBackgroundAlpha * 255);
903 mBackground.setAlpha(alpha);
904 mBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
905 mBackground.draw(canvas);
906 }
907
Winson Chung360e63f2012-04-27 13:48:05 -0700908 super.dispatchDraw(canvas);
Adam Cohen8cc05502014-08-22 18:32:15 -0700909 }
Winson Chung360e63f2012-04-27 13:48:05 -0700910
Adam Cohen8cc05502014-08-22 18:32:15 -0700911 private void drawPageHints(Canvas canvas) {
Adam Cohenc50438c2014-08-19 17:43:05 -0700912 if (mShowPageHints) {
Winson Chung360e63f2012-04-27 13:48:05 -0700913 Workspace workspace = mLauncher.getWorkspace();
Adam Cohen61a9a5c2013-08-14 13:32:04 -0700914 int width = getMeasuredWidth();
Winson Chung360e63f2012-04-27 13:48:05 -0700915 Rect childRect = new Rect();
Adam Cohenc50438c2014-08-19 17:43:05 -0700916 getDescendantRectRelativeToSelf(workspace.getChildAt(workspace.getChildCount() - 1),
917 childRect);
Winson Chung360e63f2012-04-27 13:48:05 -0700918
919 int page = workspace.getNextPage();
Winson Chungfe1fe262013-04-01 16:52:31 -0700920 final boolean isRtl = isLayoutRtl();
921 CellLayout leftPage = (CellLayout) workspace.getChildAt(isRtl ? page + 1 : page - 1);
922 CellLayout rightPage = (CellLayout) workspace.getChildAt(isRtl ? page - 1 : page + 1);
Winson Chung360e63f2012-04-27 13:48:05 -0700923
Adam Cohenc50438c2014-08-19 17:43:05 -0700924 if (leftPage != null && leftPage.isDragTarget()) {
925 Drawable left = mInScrollArea && leftPage.getIsDragOverlapping() ?
926 mLeftHoverDrawableActive : mLeftHoverDrawable;
927 left.setBounds(0, childRect.top,
928 left.getIntrinsicWidth(), childRect.bottom);
929 left.draw(canvas);
930 }
931 if (rightPage != null && rightPage.isDragTarget()) {
932 Drawable right = mInScrollArea && rightPage.getIsDragOverlapping() ?
933 mRightHoverDrawableActive : mRightHoverDrawable;
Adam Cohenc50438c2014-08-19 17:43:05 -0700934 right.setBounds(width - right.getIntrinsicWidth(),
Winson Chung360e63f2012-04-27 13:48:05 -0700935 childRect.top, width, childRect.bottom);
Adam Cohenc50438c2014-08-19 17:43:05 -0700936 right.draw(canvas);
Winson Chung360e63f2012-04-27 13:48:05 -0700937 }
938 }
939 }
Adam Cohenb0f3d742013-10-08 19:16:14 -0700940
Adam Cohen8cc05502014-08-22 18:32:15 -0700941 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
942 boolean ret = super.drawChild(canvas, child, drawingTime);
943
944 // We want to draw the page hints above the workspace, but below the drag view.
945 if (child instanceof Workspace) {
946 drawPageHints(canvas);
947 }
948 return ret;
949 }
950
Adam Cohen6c5891a2014-07-09 23:53:15 -0700951 public void setBackgroundAlpha(float alpha) {
952 if (alpha != mBackgroundAlpha) {
953 mBackgroundAlpha = alpha;
954 invalidate();
955 }
956 }
957
958 public float getBackgroundAlpha() {
959 return mBackgroundAlpha;
960 }
961
Adam Cohenb0f3d742013-10-08 19:16:14 -0700962 public void setTouchCompleteListener(TouchCompleteListener listener) {
963 mTouchCompleteListener = listener;
964 }
965
966 public interface TouchCompleteListener {
967 public void onTouchComplete();
968 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800969}