blob: f18f900593828bca5172fb1b9bb47d2d5fdc8229 [file] [log] [blame]
Winson Chung7dd3b522018-05-14 10:07:15 -07001
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002/*
3 * Copyright (C) 2008 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
Vadim Tryshevfedca432015-08-19 17:55:02 -070018package com.android.launcher3.dragndrop;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080019
Sunny Goyal1721ccf2021-06-08 12:57:40 -070020import static android.animation.ObjectAnimator.ofFloat;
21
Kateryna Ivanova71203732023-05-24 15:09:00 +000022import static com.android.app.animation.Interpolators.DECELERATE_1_5;
Sunny Goyal1721ccf2021-06-08 12:57:40 -070023import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
24import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
25import static com.android.launcher3.Utilities.mapRange;
26import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
Sunny Goyald0030b02017-12-08 15:07:24 -080027import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
28
Adam Cohen716b51e2011-06-30 12:09:54 -070029import android.animation.Animator;
Sunny Goyal1721ccf2021-06-08 12:57:40 -070030import android.animation.ObjectAnimator;
Adam Cohen716b51e2011-06-30 12:09:54 -070031import android.animation.TimeInterpolator;
Sunny Goyal1721ccf2021-06-08 12:57:40 -070032import android.animation.TypeEvaluator;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080033import android.content.Context;
Adam Cohen716b51e2011-06-30 12:09:54 -070034import android.content.res.Resources;
Winson Chung360e63f2012-04-27 13:48:05 -070035import android.graphics.Canvas;
Adam Cohen67882692011-03-11 15:29:03 -080036import android.graphics.Rect;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080037import android.util.AttributeSet;
Dave Hawkey26279c62013-11-07 14:26:19 -070038import android.view.KeyEvent;
39import android.view.MotionEvent;
40import android.view.View;
Adam Cohen3371da02011-10-25 21:38:29 -070041import android.view.accessibility.AccessibilityEvent;
42import android.view.accessibility.AccessibilityManager;
Adam Cohen3e8f8112011-07-02 18:03:00 -070043import android.view.animation.Interpolator;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080044
Kateryna Ivanova71203732023-05-24 15:09:00 +000045import com.android.app.animation.Interpolators;
Sunny Goyal740ac7f2016-09-28 16:47:32 -070046import com.android.launcher3.AbstractFloatingView;
Tony Wickham1bce7fd2016-04-28 17:39:03 -070047import com.android.launcher3.DropTargetBar;
Vadim Tryshevfedca432015-08-19 17:55:02 -070048import com.android.launcher3.Launcher;
Adam Cohenf9c184a2016-01-15 16:47:43 -080049import com.android.launcher3.R;
Vadim Tryshevfedca432015-08-19 17:55:02 -070050import com.android.launcher3.ShortcutAndWidgetContainer;
Sunny Goyal82379182022-11-02 18:58:49 -070051import com.android.launcher3.Utilities;
Sunny Goyal5d1873a2018-05-08 11:10:44 -070052import com.android.launcher3.Workspace;
Sunny Goyal1721ccf2021-06-08 12:57:40 -070053import com.android.launcher3.anim.PendingAnimation;
54import com.android.launcher3.anim.SpringProperty;
Sebastian Francod4682992022-10-05 13:03:09 -050055import com.android.launcher3.celllayout.CellLayoutLayoutParams;
Sunny Goyal26119432016-02-18 22:09:23 +000056import com.android.launcher3.folder.Folder;
Adam Cohenf7ca3b42021-02-22 11:03:58 -080057import com.android.launcher3.graphics.Scrim;
Sunny Goyal3333b0c2016-05-09 20:43:21 -070058import com.android.launcher3.keyboard.ViewGroupFocusHelper;
Sunny Goyal0b0847b2018-03-14 12:30:11 -070059import com.android.launcher3.views.BaseDragLayer;
Sunny Goyal82379182022-11-02 18:58:49 -070060import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayCallbacks;
Adam Cohenbc927f92014-10-28 16:16:02 -070061
Adam Cohen1d9af7d2011-06-22 15:26:58 -070062import java.util.ArrayList;
Adam Cohen67882692011-03-11 15:29:03 -080063
The Android Open Source Project31dd5032009-03-03 19:32:27 -080064/**
Michael Jurka0e260592010-06-30 17:07:39 -070065 * A ViewGroup that coordinates dragging across its descendants
The Android Open Source Project31dd5032009-03-03 19:32:27 -080066 */
Sunny Goyal82379182022-11-02 18:58:49 -070067public class DragLayer extends BaseDragLayer<Launcher> implements LauncherOverlayCallbacks {
Sunny Goyal0fa93bc2015-05-13 11:23:24 -070068
Sunny Goyal6001ea22018-05-10 16:31:00 -070069 public static final int ALPHA_INDEX_OVERLAY = 0;
Sunny Goyal7b8d63f2022-04-14 16:37:32 -070070 private static final int ALPHA_CHANNEL_COUNT = 1;
Sunny Goyal6001ea22018-05-10 16:31:00 -070071
Sunny Goyalb1359562015-05-19 19:07:29 -070072 public static final int ANIMATION_END_DISAPPEAR = 0;
Sunny Goyalb1359562015-05-19 19:07:29 -070073 public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
74
Sunny Goyal82379182022-11-02 18:58:49 -070075 private final boolean mIsRtl;
76
Sunny Goyaldeb91c42020-03-24 02:17:59 -070077 private DragController mDragController;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080078
Adam Cohen716b51e2011-06-30 12:09:54 -070079 // Variables relating to animation of views after drop
Sunny Goyal1721ccf2021-06-08 12:57:40 -070080 private Animator mDropAnim = null;
Sunny Goyaldeb91c42020-03-24 02:17:59 -070081
Sunny Goyal1721ccf2021-06-08 12:57:40 -070082 private DragView mDropView = null;
Adam Cohen8dfcba42011-07-07 16:38:18 -070083
Adam Cohen3371da02011-10-25 21:38:29 -070084 private boolean mHoverPointClosesFolder = false;
Adam Cohenb0f3d742013-10-08 19:16:14 -070085
Adam Cohen432609a2014-03-13 17:03:22 -070086 private int mTopViewIndex;
Adam Cohen2d783ce2014-04-07 14:11:48 -070087 private int mChildCountOnLastUpdate = -1;
Jorim Jaggi55bd9722014-01-16 15:30:42 -080088
Tony Wickham0f97b782015-12-02 17:55:07 -080089 // Related to adjacent page hints
Sunny Goyal3333b0c2016-05-09 20:43:21 -070090 private final ViewGroupFocusHelper mFocusIndicatorHelper;
Adam Cohenf7ca3b42021-02-22 11:03:58 -080091 private Scrim mWorkspaceDragScrim;
Sunny Goyal3333b0c2016-05-09 20:43:21 -070092
The Android Open Source Project31dd5032009-03-03 19:32:27 -080093 /**
94 * Used to create a new DragLayer from XML.
95 *
96 * @param context The application's context.
Michael Jurka0e260592010-06-30 17:07:39 -070097 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080098 */
99 public DragLayer(Context context, AttributeSet attrs) {
Sunny Goyal6001ea22018-05-10 16:31:00 -0700100 super(context, attrs, ALPHA_CHANNEL_COUNT);
Winson Chungbe62afa2011-02-03 23:14:57 -0800101
102 // Disable multitouch across the workspace/all apps/customize tray
103 setMotionEventSplittingEnabled(false);
Adam Cohen21b41102011-11-01 17:29:52 -0700104 setChildrenDrawingOrderEnabled(true);
Winson Chung360e63f2012-04-27 13:48:05 -0700105
Sunny Goyal3333b0c2016-05-09 20:43:21 -0700106 mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
Sunny Goyal82379182022-11-02 18:58:49 -0700107 mIsRtl = Utilities.isRtl(getResources());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800108 }
109
Shikha Malhotraf78da1b2022-04-11 10:23:18 +0000110 /**
111 * Set up the drag layer with the parameters.
112 */
113 public void setup(DragController dragController, Workspace<?> workspace) {
Hyunyoung Song645764e2016-06-06 14:19:02 -0700114 mDragController = dragController;
Sunny Goyal5d1873a2018-05-08 11:10:44 -0700115 recreateControllers();
Adam Cohenf7ca3b42021-02-22 11:03:58 -0800116 mWorkspaceDragScrim = new Scrim(this);
Sunny Goyal82379182022-11-02 18:58:49 -0700117 workspace.addOverlayCallback(this);
Sunny Goyal5d1873a2018-05-08 11:10:44 -0700118 }
119
Sunny Goyal4e82f5b2020-03-12 12:29:33 -0700120 @Override
Sunny Goyal5d1873a2018-05-08 11:10:44 -0700121 public void recreateControllers() {
Sunny Goyal210e1742019-10-17 12:05:38 -0700122 mControllers = mActivity.createTouchControllers();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800123 }
Adam Cohen76fc0852011-06-17 13:26:23 -0700124
Sunny Goyal3333b0c2016-05-09 20:43:21 -0700125 public ViewGroupFocusHelper getFocusIndicatorHelper() {
126 return mFocusIndicatorHelper;
127 }
128
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800129 @Override
130 public boolean dispatchKeyEvent(KeyEvent event) {
Joe Onorato00acb122009-08-04 16:04:30 -0400131 return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800132 }
133
Sunny Goyalde753212018-05-15 13:55:57 -0700134 private boolean isEventOverAccessibleDropTargetBar(MotionEvent ev) {
135 return isInAccessibleDrag() && isEventOverView(mActivity.getDropTargetBar(), ev);
136 }
137
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800138 @Override
Adam Cohen3371da02011-10-25 21:38:29 -0700139 public boolean onInterceptHoverEvent(MotionEvent ev) {
Sunny Goyal0b0847b2018-03-14 12:30:11 -0700140 if (mActivity == null || mActivity.getWorkspace() == null) {
Michael Jurka023c71e2012-10-19 17:32:24 +0200141 return false;
142 }
Sunny Goyalde753212018-05-15 13:55:57 -0700143 AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
144 if (!(topView instanceof Folder)) {
Adam Cohen3371da02011-10-25 21:38:29 -0700145 return false;
146 } else {
Sunny Goyal0b0847b2018-03-14 12:30:11 -0700147 AccessibilityManager accessibilityManager = (AccessibilityManager)
148 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
Michael Jurka8b805b12012-04-18 14:23:14 -0700149 if (accessibilityManager.isTouchExplorationEnabled()) {
Sunny Goyalde753212018-05-15 13:55:57 -0700150 Folder currentFolder = (Folder) topView;
Adam Cohen3371da02011-10-25 21:38:29 -0700151 final int action = ev.getAction();
Sunny Goyalccc414b2015-04-30 12:28:16 -0700152 boolean isOverFolderOrSearchBar;
Adam Cohen3371da02011-10-25 21:38:29 -0700153 switch (action) {
154 case MotionEvent.ACTION_HOVER_ENTER:
Sunny Goyalde753212018-05-15 13:55:57 -0700155 isOverFolderOrSearchBar = isEventOverView(topView, ev) ||
156 isEventOverAccessibleDropTargetBar(ev);
Sunny Goyalccc414b2015-04-30 12:28:16 -0700157 if (!isOverFolderOrSearchBar) {
Adam Cohen3371da02011-10-25 21:38:29 -0700158 sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
159 mHoverPointClosesFolder = true;
160 return true;
Adam Cohen3371da02011-10-25 21:38:29 -0700161 }
Mihail Dumitrescu693fbc72014-01-03 12:40:22 +0000162 mHoverPointClosesFolder = false;
163 break;
Adam Cohen3371da02011-10-25 21:38:29 -0700164 case MotionEvent.ACTION_HOVER_MOVE:
Sunny Goyalde753212018-05-15 13:55:57 -0700165 isOverFolderOrSearchBar = isEventOverView(topView, ev) ||
166 isEventOverAccessibleDropTargetBar(ev);
Sunny Goyalccc414b2015-04-30 12:28:16 -0700167 if (!isOverFolderOrSearchBar && !mHoverPointClosesFolder) {
Adam Cohen3371da02011-10-25 21:38:29 -0700168 sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
169 mHoverPointClosesFolder = true;
170 return true;
Sunny Goyalccc414b2015-04-30 12:28:16 -0700171 } else if (!isOverFolderOrSearchBar) {
Adam Cohen3371da02011-10-25 21:38:29 -0700172 return true;
173 }
Mihail Dumitrescu693fbc72014-01-03 12:40:22 +0000174 mHoverPointClosesFolder = false;
Adam Cohen3371da02011-10-25 21:38:29 -0700175 }
176 }
177 }
178 return false;
179 }
180
181 private void sendTapOutsideFolderAccessibilityEvent(boolean isEditingName) {
Sunny Goyal52851aa2016-09-02 10:41:43 -0700182 int stringId = isEditingName ? R.string.folder_tap_to_rename : R.string.folder_tap_to_close;
Sunny Goyald0030b02017-12-08 15:07:24 -0800183 sendCustomAccessibilityEvent(
Sunny Goyal52851aa2016-09-02 10:41:43 -0700184 this, AccessibilityEvent.TYPE_VIEW_FOCUSED, getContext().getString(stringId));
Adam Cohen3371da02011-10-25 21:38:29 -0700185 }
186
Sunny Goyal0b0847b2018-03-14 12:30:11 -0700187 @Override
188 public boolean onHoverEvent(MotionEvent ev) {
189 // If we've received this, we've already done the necessary handling
190 // in onInterceptHoverEvent. Return true to consume the event.
191 return false;
192 }
193
194
Sunny Goyalccc414b2015-04-30 12:28:16 -0700195 private boolean isInAccessibleDrag() {
Sunny Goyal0b0847b2018-03-14 12:30:11 -0700196 return mActivity.getAccessibilityDelegate().isInAccessibleDrag();
Sunny Goyalccc414b2015-04-30 12:28:16 -0700197 }
198
Adam Cohen3371da02011-10-25 21:38:29 -0700199 @Override
Winson Chung83ca4802013-04-12 15:10:52 -0700200 public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
Sunny Goyalde753212018-05-15 13:55:57 -0700201 if (isInAccessibleDrag() && child instanceof DropTargetBar) {
202 return true;
Winson Chung83ca4802013-04-12 15:10:52 -0700203 }
204 return super.onRequestSendAccessibilityEvent(child, event);
205 }
206
207 @Override
208 public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) {
Vadim Tryshev17839d52018-05-23 14:45:56 -0700209 View topView = AbstractFloatingView.getTopOpenViewWithType(mActivity,
210 AbstractFloatingView.TYPE_ACCESSIBLE);
Sunny Goyal52851aa2016-09-02 10:41:43 -0700211 if (topView != null) {
Sunny Goyalde753212018-05-15 13:55:57 -0700212 addAccessibleChildToList(topView, childrenForAccessibility);
Sunny Goyalccc414b2015-04-30 12:28:16 -0700213 if (isInAccessibleDrag()) {
Sunny Goyalde753212018-05-15 13:55:57 -0700214 addAccessibleChildToList(mActivity.getDropTargetBar(), childrenForAccessibility);
Sunny Goyalccc414b2015-04-30 12:28:16 -0700215 }
Winson Chung83ca4802013-04-12 15:10:52 -0700216 } else {
217 super.addChildrenForAccessibility(childrenForAccessibility);
218 }
219 }
220
221 @Override
Sunny Goyal19d32b72018-01-19 14:26:18 -0800222 public boolean dispatchTouchEvent(MotionEvent ev) {
223 ev.offsetLocation(getTranslationX(), 0);
224 try {
225 return super.dispatchTouchEvent(ev);
226 } finally {
227 ev.offsetLocation(-getTranslationX(), 0);
228 }
229 }
230
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800231 public void animateViewIntoPosition(DragView dragView, final int[] pos, float alpha,
232 float scaleX, float scaleY, int animationEndStyle, Runnable onFinishRunnable,
233 int duration) {
Sunny Goyal1721ccf2021-06-08 12:57:40 -0700234 animateViewIntoPosition(dragView, pos[0], pos[1], alpha, scaleX, scaleY,
Adam Cohened66b2b2012-01-23 17:28:51 -0800235 onFinishRunnable, animationEndStyle, duration, null);
236 }
237
Sunny Goyal1797af42017-10-06 13:29:57 -0700238 public void animateViewIntoPosition(DragView dragView, final View child, View anchorView) {
239 animateViewIntoPosition(dragView, child, -1, anchorView);
Adam Cohened51cc92011-08-01 20:28:08 -0700240 }
241
242 public void animateViewIntoPosition(DragView dragView, final View child, int duration,
Sunny Goyal1797af42017-10-06 13:29:57 -0700243 View anchorView) {
Adam Cohen65086992020-02-19 08:40:49 -0800244
Michael Jurkaa52570f2012-03-20 03:18:20 -0700245 ShortcutAndWidgetContainer parentChildren = (ShortcutAndWidgetContainer) child.getParent();
Sebastian Francod4682992022-10-05 13:03:09 -0500246 CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
Winson Chungeecf02d2012-03-02 17:14:58 -0800247 parentChildren.measureChild(child);
Adam Cohen65086992020-02-19 08:40:49 -0800248 parentChildren.layoutChild(child);
Adam Cohen716b51e2011-06-30 12:09:54 -0700249
Sunny Goyalae6e3182019-04-30 12:04:37 -0700250 float coord[] = new float[2];
Adam Cohen307fe232012-08-16 17:55:58 -0700251 float childScale = child.getScaleX();
Adam Cohen65086992020-02-19 08:40:49 -0800252
Sunny Goyalae6e3182019-04-30 12:04:37 -0700253 coord[0] = lp.x + (child.getMeasuredWidth() * (1 - childScale) / 2);
254 coord[1] = lp.y + (child.getMeasuredHeight() * (1 - childScale) / 2);
Winson Chungeecf02d2012-03-02 17:14:58 -0800255
Adam Cohen8dfcba42011-07-07 16:38:18 -0700256 // Since the child hasn't necessarily been laid out, we force the lp to be updated with
Adam Cohenac8c8762011-07-13 11:15:27 -0700257 // the correct coordinates (above) and use these to determine the final location
Winson Chung557d6ed2011-07-08 15:34:52 -0700258 float scale = getDescendantCoordRelativeToSelf((View) child.getParent(), coord);
Adam Cohen65086992020-02-19 08:40:49 -0800259
Adam Cohen307fe232012-08-16 17:55:58 -0700260 // We need to account for the scale of the child itself, as the above only accounts for
261 // for the scale in parents.
262 scale *= childScale;
Sunny Goyalae6e3182019-04-30 12:04:37 -0700263 int toX = Math.round(coord[0]);
264 int toY = Math.round(coord[1]);
Adam Cohenc77bc452020-05-07 11:55:19 -0700265
Winson Chungeeb5bbc2013-11-13 15:47:05 -0800266 float toScale = scale;
Andrew Flynn4497ebf2012-05-09 11:28:00 -0700267
Adam Cohen65086992020-02-19 08:40:49 -0800268 if (child instanceof DraggableView) {
Adam Cohenc77bc452020-05-07 11:55:19 -0700269 // This code is fairly subtle. Please verify drag and drop is pixel-perfect in a number
270 // of scenarios before modifying (from all apps, from workspace, different grid-sizes,
271 // shortcuts from in and out of Launcher etc).
Adam Cohen65086992020-02-19 08:40:49 -0800272 DraggableView d = (DraggableView) child;
Adam Cohenc77bc452020-05-07 11:55:19 -0700273 Rect destRect = new Rect();
274 d.getWorkspaceVisualDragBounds(destRect);
275
276 // In most cases this additional scale factor should be a no-op (1). It mainly accounts
277 // for alternate grids where the source and destination icon sizes are different
278 toScale *= ((1f * destRect.width())
279 / (dragView.getMeasuredWidth() - dragView.getBlurSizeOutline()));
Sunny Goyal95abbb32014-08-04 10:53:22 -0700280
Adam Cohen65086992020-02-19 08:40:49 -0800281 // This accounts for the offset of the DragView created by scaling it about its
282 // center as it animates into place.
Adam Cohenc77bc452020-05-07 11:55:19 -0700283 float scaleShiftX = dragView.getMeasuredWidth() * (1 - toScale) / 2;
284 float scaleShiftY = dragView.getMeasuredHeight() * (1 - toScale) / 2;
Adam Cohen65086992020-02-19 08:40:49 -0800285
Adam Cohenc77bc452020-05-07 11:55:19 -0700286 toX += scale * destRect.left - toScale * dragView.getBlurSizeOutline() / 2 - scaleShiftX;
287 toY += scale * destRect.top - toScale * dragView.getBlurSizeOutline() / 2 - scaleShiftY;
Adam Cohenac8c8762011-07-13 11:15:27 -0700288 }
289
Adam Cohen716b51e2011-06-30 12:09:54 -0700290 child.setVisibility(INVISIBLE);
Sunny Goyal6639a5d2018-02-28 15:09:36 -0800291 Runnable onCompleteRunnable = () -> child.setVisibility(VISIBLE);
Sunny Goyal1721ccf2021-06-08 12:57:40 -0700292 animateViewIntoPosition(dragView, toX, toY, 1, toScale, toScale,
Adam Cohend41fbf52012-02-16 23:53:59 -0800293 onCompleteRunnable, ANIMATION_END_DISAPPEAR, duration, anchorView);
Winson Chung557d6ed2011-07-08 15:34:52 -0700294 }
295
Adam Cohenb7e16182011-07-15 17:55:02 -0700296 /**
297 * This method animates a view at the end of a drag and drop animation.
Sunny Goyal1721ccf2021-06-08 12:57:40 -0700298 */
299 public void animateViewIntoPosition(final DragView view,
300 final int toX, final int toY, float finalAlpha,
301 float finalScaleX, float finalScaleY, Runnable onCompleteRunnable,
302 int animationEndStyle, int duration, View anchorView) {
303 Rect to = new Rect(toX, toY, toX + view.getMeasuredWidth(), toY + view.getMeasuredHeight());
304 animateView(view, to, finalAlpha, finalScaleX, finalScaleY, duration,
305 null, onCompleteRunnable, animationEndStyle, anchorView);
306 }
307
308 /**
309 * This method animates a view at the end of a drag and drop animation.
Adam Cohenb7e16182011-07-15 17:55:02 -0700310 * @param view The view to be animated. This view is drawn directly into DragLayer, and so
311 * doesn't need to be a child of DragLayer.
Adam Cohenb7e16182011-07-15 17:55:02 -0700312 * @param to The final location of the view. Only the left and top parameters are used. This
Sunny Goyal1721ccf2021-06-08 12:57:40 -0700313* location doesn't account for scaling, and so should be centered about the desired
314* final location (including scaling).
Adam Cohenb7e16182011-07-15 17:55:02 -0700315 * @param finalAlpha The final alpha of the view, in case we want it to fade as it animates.
Sunny Goyal6178f132016-07-11 17:30:03 -0700316 * @param finalScaleX The final scale of the view. The view is scaled about its center.
317 * @param finalScaleY The final scale of the view. The view is scaled about its center.
Adam Cohenb7e16182011-07-15 17:55:02 -0700318 * @param duration The duration of the animation.
319 * @param motionInterpolator The interpolator to use for the location of the view.
Adam Cohenb7e16182011-07-15 17:55:02 -0700320 * @param onCompleteRunnable Optional runnable to run on animation completion.
Sunny Goyal6178f132016-07-11 17:30:03 -0700321 * @param animationEndStyle Whether or not to fade out the view once the animation completes.
Sunny Goyal1721ccf2021-06-08 12:57:40 -0700322* {@link #ANIMATION_END_DISAPPEAR} or {@link #ANIMATION_END_REMAIN_VISIBLE}.
Adam Cohen6441de02011-12-14 14:25:32 -0800323 * @param anchorView If not null, this represents the view which the animated view stays
Adam Cohenb7e16182011-07-15 17:55:02 -0700324 */
Sunny Goyal1721ccf2021-06-08 12:57:40 -0700325 public void animateView(final DragView view, final Rect to,
326 final float finalAlpha, final float finalScaleX, final float finalScaleY, int duration,
327 final Interpolator motionInterpolator, final Runnable onCompleteRunnable,
328 final int animationEndStyle, View anchorView) {
329 view.cancelAnimation();
330 view.requestLayout();
331
332 final int[] from = getViewLocationRelativeToSelf(view);
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800333
Adam Cohen716b51e2011-06-30 12:09:54 -0700334 // Calculate the duration of the animation based on the object's distance
Sunny Goyal1721ccf2021-06-08 12:57:40 -0700335 final float dist = (float) Math.hypot(to.left - from[0], to.top - from[1]);
Adam Cohen716b51e2011-06-30 12:09:54 -0700336 final Resources res = getResources();
337 final float maxDist = (float) res.getInteger(R.integer.config_dropAnimMaxDist);
Adam Cohen3e8f8112011-07-02 18:03:00 -0700338
339 // If duration < 0, this is a cue to compute the duration based on the distance
340 if (duration < 0) {
Sunny Goyaldec3a902017-01-25 18:23:36 -0800341 duration = res.getInteger(R.integer.config_dropAnimMaxDuration);
342 if (dist < maxDist) {
Kateryna Ivanova71203732023-05-24 15:09:00 +0000343 duration *= DECELERATE_1_5.getInterpolation(dist / maxDist);
Adam Cohen3e8f8112011-07-02 18:03:00 -0700344 }
Sunny Goyaldec3a902017-01-25 18:23:36 -0800345 duration = Math.max(duration, res.getInteger(R.integer.config_dropAnimMinDuration));
Adam Cohen716b51e2011-06-30 12:09:54 -0700346 }
347
Winson Chung043f2af2012-03-01 16:09:54 -0800348 // Fall back to cubic ease out interpolator for the animation if none is specified
Sunny Goyal1721ccf2021-06-08 12:57:40 -0700349 TimeInterpolator interpolator =
Kateryna Ivanova71203732023-05-24 15:09:00 +0000350 motionInterpolator == null ? DECELERATE_1_5 : motionInterpolator;
Adam Cohen716b51e2011-06-30 12:09:54 -0700351
Winson Chung043f2af2012-03-01 16:09:54 -0800352 // Animate the view
Sunny Goyal1721ccf2021-06-08 12:57:40 -0700353 PendingAnimation anim = new PendingAnimation(duration);
354 anim.add(ofFloat(view, View.SCALE_X, finalScaleX), interpolator, SpringProperty.DEFAULT);
355 anim.add(ofFloat(view, View.SCALE_Y, finalScaleY), interpolator, SpringProperty.DEFAULT);
356 anim.setViewAlpha(view, finalAlpha, interpolator);
357 anim.setFloat(view, VIEW_TRANSLATE_Y, to.top, interpolator);
Adam Cohen716b51e2011-06-30 12:09:54 -0700358
Sunny Goyal1721ccf2021-06-08 12:57:40 -0700359 ObjectAnimator xMotion = ofFloat(view, VIEW_TRANSLATE_X, to.left);
360 if (anchorView != null) {
361 final int startScroll = anchorView.getScrollX();
362 TypeEvaluator<Float> evaluator = (f, s, e) -> mapRange(f, s, e)
363 + (anchorView.getScaleX() * (startScroll - anchorView.getScrollX()));
364 xMotion.setEvaluator(evaluator);
365 }
366 anim.add(xMotion, interpolator, SpringProperty.DEFAULT);
367 if (onCompleteRunnable != null) {
368 anim.addListener(forEndCallback(onCompleteRunnable));
369 }
370 playDropAnimation(view, anim.buildAnim(), animationEndStyle);
Winson Chung043f2af2012-03-01 16:09:54 -0800371 }
372
Sunny Goyal1721ccf2021-06-08 12:57:40 -0700373 /**
374 * Runs a previously constructed drop animation
375 */
376 public void playDropAnimation(final DragView view, Animator animator, int animationEndStyle) {
Winson Chung043f2af2012-03-01 16:09:54 -0800377 // Clean up the previous animations
378 if (mDropAnim != null) mDropAnim.cancel();
Winson Chung043f2af2012-03-01 16:09:54 -0800379
380 // Show the drop view if it was previously hidden
381 mDropView = view;
Winson Chung043f2af2012-03-01 16:09:54 -0800382 // Create and start the animation
Sunny Goyal1721ccf2021-06-08 12:57:40 -0700383 mDropAnim = animator;
384 mDropAnim.addListener(forEndCallback(() -> mDropAnim = null));
385 if (animationEndStyle == ANIMATION_END_DISAPPEAR) {
386 mDropAnim.addListener(forEndCallback(this::clearAnimatedView));
387 }
Adam Cohen716b51e2011-06-30 12:09:54 -0700388 mDropAnim.start();
389 }
390
Adam Cohened66b2b2012-01-23 17:28:51 -0800391 public void clearAnimatedView() {
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800392 if (mDropAnim != null) {
393 mDropAnim.cancel();
394 }
Winson Chung7dd3b522018-05-14 10:07:15 -0700395 mDropAnim = null;
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800396 if (mDropView != null) {
Adam Cohen2d783ce2014-04-07 14:11:48 -0700397 mDragController.onDeferredEndDrag(mDropView);
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800398 }
Adam Cohened66b2b2012-01-23 17:28:51 -0800399 mDropView = null;
Adam Cohened66b2b2012-01-23 17:28:51 -0800400 invalidate();
401 }
402
403 public View getAnimatedView() {
404 return mDropView;
405 }
406
Adam Cohen716b51e2011-06-30 12:09:54 -0700407 @Override
Jon Miranda758d5042017-11-08 14:38:23 -0800408 public void onViewAdded(View child) {
409 super.onViewAdded(child);
Adam Cohen21b41102011-11-01 17:29:52 -0700410 updateChildIndices();
Sunny Goyal210e1742019-10-17 12:05:38 -0700411 mActivity.onDragLayerHierarchyChanged();
Adam Cohen21b41102011-11-01 17:29:52 -0700412 }
413
414 @Override
Jon Miranda758d5042017-11-08 14:38:23 -0800415 public void onViewRemoved(View child) {
416 super.onViewRemoved(child);
Adam Cohen21b41102011-11-01 17:29:52 -0700417 updateChildIndices();
Sunny Goyal210e1742019-10-17 12:05:38 -0700418 mActivity.onDragLayerHierarchyChanged();
Adam Cohen21b41102011-11-01 17:29:52 -0700419 }
420
Adam Cohen432609a2014-03-13 17:03:22 -0700421 @Override
422 public void bringChildToFront(View child) {
423 super.bringChildToFront(child);
424 updateChildIndices();
425 }
426
Adam Cohen21b41102011-11-01 17:29:52 -0700427 private void updateChildIndices() {
Adam Cohen432609a2014-03-13 17:03:22 -0700428 mTopViewIndex = -1;
Jorim Jaggi55bd9722014-01-16 15:30:42 -0800429 int childCount = getChildCount();
430 for (int i = 0; i < childCount; i++) {
Adam Cohenb670f192014-04-08 15:34:17 -0700431 if (getChildAt(i) instanceof DragView) {
Adam Cohen432609a2014-03-13 17:03:22 -0700432 mTopViewIndex = i;
Jorim Jaggi55bd9722014-01-16 15:30:42 -0800433 }
Adam Cohen21b41102011-11-01 17:29:52 -0700434 }
Adam Cohen2d783ce2014-04-07 14:11:48 -0700435 mChildCountOnLastUpdate = childCount;
Adam Cohen21b41102011-11-01 17:29:52 -0700436 }
437
438 @Override
439 protected int getChildDrawingOrder(int childCount, int i) {
Adam Cohen2d783ce2014-04-07 14:11:48 -0700440 if (mChildCountOnLastUpdate != childCount) {
441 // between platform versions 17 and 18, behavior for onChildViewRemoved / Added changed.
442 // Pre-18, the child was not added / removed by the time of those callbacks. We need to
443 // force update our representation of things here to avoid crashing on pre-18 devices
444 // in certain instances.
445 updateChildIndices();
446 }
447
Adam Cohen432609a2014-03-13 17:03:22 -0700448 // i represents the current draw iteration
449 if (mTopViewIndex == -1) {
450 // in general we do nothing
Adam Cohen21b41102011-11-01 17:29:52 -0700451 return i;
Adam Cohen432609a2014-03-13 17:03:22 -0700452 } else if (i == childCount - 1) {
453 // if we have a top index, we return it when drawing last item (highest z-order)
454 return mTopViewIndex;
455 } else if (i < mTopViewIndex) {
Jorim Jaggi55bd9722014-01-16 15:30:42 -0800456 return i;
Adam Cohen21b41102011-11-01 17:29:52 -0700457 } else {
Adam Cohen432609a2014-03-13 17:03:22 -0700458 // for indexes greater than the top index, we fetch one item above to shift for the
459 // displacement of the top index
460 return i + 1;
Adam Cohen21b41102011-11-01 17:29:52 -0700461 }
462 }
Winson Chung360e63f2012-04-27 13:48:05 -0700463
Winson Chung360e63f2012-04-27 13:48:05 -0700464 @Override
465 protected void dispatchDraw(Canvas canvas) {
Sunny Goyal0fa93bc2015-05-13 11:23:24 -0700466 // Draw the background below children.
Adam Cohen501e1392021-02-02 16:45:07 -0800467 mWorkspaceDragScrim.draw(canvas);
Sunny Goyal3333b0c2016-05-09 20:43:21 -0700468 mFocusIndicatorHelper.draw(canvas);
Winson Chung360e63f2012-04-27 13:48:05 -0700469 super.dispatchDraw(canvas);
Tony Wickhamc0880512019-07-24 17:32:08 -0700470 }
471
Adam Cohenf7ca3b42021-02-22 11:03:58 -0800472 public Scrim getWorkspaceDragScrim() {
Adam Cohen501e1392021-02-02 16:45:07 -0800473 return mWorkspaceDragScrim;
474 }
Bill Lin945bb342021-06-03 01:29:39 +0800475
Sunny Goyal82379182022-11-02 18:58:49 -0700476 @Override
477 public void onOverlayScrollChanged(float progress) {
Kateryna Ivanova71203732023-05-24 15:09:00 +0000478 float alpha = 1 - Interpolators.DECELERATE_3.getInterpolation(progress);
Sunny Goyal82379182022-11-02 18:58:49 -0700479 float transX = getMeasuredWidth() * progress;
480
481 if (mIsRtl) {
482 transX = -transX;
483 }
484 setTranslationX(transX);
485 getAlphaProperty(ALPHA_INDEX_OVERLAY).setValue(alpha);
486 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800487}