blob: f5d0b24921754e2890bf2097df9267ef6450653c [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
Vadim Tryshevfedca432015-08-19 17:55:02 -070017package com.android.launcher3.dragndrop;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080018
Sunny Goyald0030b02017-12-08 15:07:24 -080019import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
20
Adam Cohen716b51e2011-06-30 12:09:54 -070021import android.animation.Animator;
22import android.animation.AnimatorListenerAdapter;
23import android.animation.TimeInterpolator;
24import android.animation.ValueAnimator;
25import android.animation.ValueAnimator.AnimatorUpdateListener;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080026import android.content.Context;
Adam Cohen716b51e2011-06-30 12:09:54 -070027import android.content.res.Resources;
Winson Chung360e63f2012-04-27 13:48:05 -070028import android.graphics.Canvas;
Adam Cohen67882692011-03-11 15:29:03 -080029import android.graphics.Rect;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080030import android.util.AttributeSet;
Dave Hawkey26279c62013-11-07 14:26:19 -070031import android.view.KeyEvent;
32import android.view.MotionEvent;
33import android.view.View;
34import android.view.ViewGroup;
Adam Cohen3371da02011-10-25 21:38:29 -070035import android.view.accessibility.AccessibilityEvent;
36import android.view.accessibility.AccessibilityManager;
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
Sunny Goyal740ac7f2016-09-28 16:47:32 -070041import com.android.launcher3.AbstractFloatingView;
Vadim Tryshevfedca432015-08-19 17:55:02 -070042import com.android.launcher3.CellLayout;
Tony Wickham1bce7fd2016-04-28 17:39:03 -070043import com.android.launcher3.DropTargetBar;
Vadim Tryshevfedca432015-08-19 17:55:02 -070044import com.android.launcher3.InsettableFrameLayout;
Vadim Tryshevfedca432015-08-19 17:55:02 -070045import com.android.launcher3.Launcher;
Adam Cohenf9c184a2016-01-15 16:47:43 -080046import com.android.launcher3.R;
Vadim Tryshevfedca432015-08-19 17:55:02 -070047import com.android.launcher3.ShortcutAndWidgetContainer;
48import com.android.launcher3.Utilities;
Sunny Goyal5bc6b6f2017-10-26 15:36:10 -070049import com.android.launcher3.anim.Interpolators;
Sunny Goyal26119432016-02-18 22:09:23 +000050import com.android.launcher3.folder.Folder;
51import com.android.launcher3.folder.FolderIcon;
Sunny Goyal6639a5d2018-02-28 15:09:36 -080052import com.android.launcher3.graphics.ViewScrim;
Sunny Goyal3333b0c2016-05-09 20:43:21 -070053import com.android.launcher3.keyboard.ViewGroupFocusHelper;
Sunny Goyal85525172017-11-06 13:00:42 -080054import com.android.launcher3.uioverrides.UiFactory;
Adam Cohen091440a2015-03-18 14:16:05 -070055import com.android.launcher3.util.Thunk;
Hyunyoung Song645764e2016-06-06 14:19:02 -070056import com.android.launcher3.util.TouchController;
Adam Cohenbc927f92014-10-28 16:16:02 -070057
Adam Cohen1d9af7d2011-06-22 15:26:58 -070058import java.util.ArrayList;
Adam Cohen67882692011-03-11 15:29:03 -080059
The Android Open Source Project31dd5032009-03-03 19:32:27 -080060/**
Michael Jurka0e260592010-06-30 17:07:39 -070061 * A ViewGroup that coordinates dragging across its descendants
The Android Open Source Project31dd5032009-03-03 19:32:27 -080062 */
Adam Cohena6d04922014-10-23 17:28:30 -070063public class DragLayer extends InsettableFrameLayout {
Sunny Goyal0fa93bc2015-05-13 11:23:24 -070064
Sunny Goyalb1359562015-05-19 19:07:29 -070065 public static final int ANIMATION_END_DISAPPEAR = 0;
Sunny Goyalb1359562015-05-19 19:07:29 -070066 public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
67
Sunny Goyalb1359562015-05-19 19:07:29 -070068 private final int[] mTmpXY = new int[2];
69
Adam Cohen091440a2015-03-18 14:16:05 -070070 @Thunk DragController mDragController;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080071
Adam Cohen716b51e2011-06-30 12:09:54 -070072 private Launcher mLauncher;
73
Adam Cohen716b51e2011-06-30 12:09:54 -070074 // Variables relating to animation of views after drop
75 private ValueAnimator mDropAnim = null;
Sunny Goyal5bc6b6f2017-10-26 15:36:10 -070076 private final TimeInterpolator mCubicEaseOutInterpolator = Interpolators.DEACCEL_1_5;
Adam Cohen091440a2015-03-18 14:16:05 -070077 @Thunk DragView mDropView = null;
78 @Thunk int mAnchorViewInitialScrollX = 0;
79 @Thunk View mAnchorView = null;
Adam Cohen8dfcba42011-07-07 16:38:18 -070080
Adam Cohen3371da02011-10-25 21:38:29 -070081 private boolean mHoverPointClosesFolder = false;
Sunny Goyalb1359562015-05-19 19:07:29 -070082 private final Rect mHitRect = new Rect();
Adam Cohen67882692011-03-11 15:29:03 -080083
Adam Cohenb0f3d742013-10-08 19:16:14 -070084 private TouchCompleteListener mTouchCompleteListener;
85
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;
91
Sunny Goyal85525172017-11-06 13:00:42 -080092 protected TouchController[] mControllers;
Hyunyoung Song645764e2016-06-06 14:19:02 -070093 private TouchController mActiveController;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080094 /**
95 * Used to create a new DragLayer from XML.
96 *
97 * @param context The application's context.
Michael Jurka0e260592010-06-30 17:07:39 -070098 * @param attrs The attributes set containing the Workspace's customization values.
The Android Open Source Project31dd5032009-03-03 19:32:27 -080099 */
100 public DragLayer(Context context, AttributeSet attrs) {
101 super(context, attrs);
Winson Chungbe62afa2011-02-03 23:14:57 -0800102
103 // Disable multitouch across the workspace/all apps/customize tray
104 setMotionEventSplittingEnabled(false);
Adam Cohen21b41102011-11-01 17:29:52 -0700105 setChildrenDrawingOrderEnabled(true);
Winson Chung360e63f2012-04-27 13:48:05 -0700106
Sunny Goyal3333b0c2016-05-09 20:43:21 -0700107 mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800108 }
109
Sunny Goyal7ede6112017-12-05 15:11:21 -0800110 public void setup(Launcher launcher, DragController dragController) {
Winson Chung4c98d922011-05-31 16:50:48 -0700111 mLauncher = launcher;
Hyunyoung Song645764e2016-06-06 14:19:02 -0700112 mDragController = dragController;
Sunny Goyal85525172017-11-06 13:00:42 -0800113 mControllers = UiFactory.createTouchControllers(mLauncher);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800114 }
Adam Cohen76fc0852011-06-17 13:26:23 -0700115
Sunny Goyal3333b0c2016-05-09 20:43:21 -0700116 public ViewGroupFocusHelper getFocusIndicatorHelper() {
117 return mFocusIndicatorHelper;
118 }
119
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800120 @Override
121 public boolean dispatchKeyEvent(KeyEvent event) {
Joe Onorato00acb122009-08-04 16:04:30 -0400122 return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800123 }
124
Hyunyoung Songa9a8a422016-06-15 16:45:48 -0700125 public boolean isEventOverHotseat(MotionEvent ev) {
Tony Wickham1bce7fd2016-04-28 17:39:03 -0700126 return isEventOverView(mLauncher.getHotseat(), ev);
Hyunyoung Songa9a8a422016-06-15 16:45:48 -0700127 }
128
Adam Cohen3371da02011-10-25 21:38:29 -0700129 private boolean isEventOverFolder(Folder folder, MotionEvent ev) {
Tony Wickham1bce7fd2016-04-28 17:39:03 -0700130 return isEventOverView(folder, ev);
Adam Cohen3371da02011-10-25 21:38:29 -0700131 }
132
Sunny Goyalccc414b2015-04-30 12:28:16 -0700133 private boolean isEventOverDropTargetBar(MotionEvent ev) {
Tony Wickham1bce7fd2016-04-28 17:39:03 -0700134 return isEventOverView(mLauncher.getDropTargetBar(), ev);
135 }
136
Hyunyoung Song58a9c4b2016-08-05 13:57:21 -0700137 public boolean isEventOverView(View view, MotionEvent ev) {
Tony Wickham1bce7fd2016-04-28 17:39:03 -0700138 getDescendantRectRelativeToSelf(view, mHitRect);
Sunny Goyal47328fd2016-05-25 18:56:41 -0700139 return mHitRect.contains((int) ev.getX(), (int) ev.getY());
Sunny Goyalccc414b2015-04-30 12:28:16 -0700140 }
141
Adam Cohen67882692011-03-11 15:29:03 -0800142 @Override
Sunny Goyal6639a5d2018-02-28 15:09:36 -0800143 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
144 ViewScrim scrim = ViewScrim.get(child);
145 if (scrim != null) {
146 scrim.draw(canvas, getWidth(), getHeight());
147 }
148 return super.drawChild(canvas, child, drawingTime);
149 }
150
151 @Override
Adam Cohen67882692011-03-11 15:29:03 -0800152 public boolean onInterceptTouchEvent(MotionEvent ev) {
Adam Cohenb0f3d742013-10-08 19:16:14 -0700153 int action = ev.getAction();
154
Sunny Goyal7ede6112017-12-05 15:11:21 -0800155 if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
Adam Cohenb0f3d742013-10-08 19:16:14 -0700156 if (mTouchCompleteListener != null) {
157 mTouchCompleteListener.onTouchComplete();
158 }
159 mTouchCompleteListener = null;
Sunny Goyal7c8a65e2017-12-22 11:11:33 -0800160 } else if (action == MotionEvent.ACTION_DOWN) {
161 mLauncher.finishAutoCancelActionMode();
Adam Cohen67882692011-03-11 15:29:03 -0800162 }
Sunny Goyalfa8a5ac2018-02-07 13:29:42 -0800163 return findActiveController(ev);
164 }
165
166 private boolean findActiveController(MotionEvent ev) {
Tony Wickham98cbd622016-08-04 11:52:18 -0700167 mActiveController = null;
168
Sunny Goyal37920962017-09-28 13:43:24 -0700169 AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mLauncher);
170 if (topView != null && topView.onControllerInterceptTouchEvent(ev)) {
171 mActiveController = topView;
Sunny Goyal6ad72f02016-09-23 11:01:10 -0700172 return true;
Sunny Goyal6ad72f02016-09-23 11:01:10 -0700173 }
174
Winson Chungca845522018-03-05 19:39:25 +0000175 if (mLauncher.getStateManager().getState().disableInteraction) {
176 // You Shall Not Pass!!!
177 return true;
178 }
179
Sunny Goyal6ad72f02016-09-23 11:01:10 -0700180 if (mDragController.onControllerInterceptTouchEvent(ev)) {
Hyunyoung Song645764e2016-06-06 14:19:02 -0700181 mActiveController = mDragController;
Tony Wickhamc5b31962016-03-16 13:53:50 -0700182 return true;
Tony Wickhamdadb3042016-02-24 11:07:00 -0800183 }
Tony Wickham1bce7fd2016-04-28 17:39:03 -0700184
Sunny Goyal85525172017-11-06 13:00:42 -0800185 for (TouchController controller : mControllers) {
186 if (controller.onControllerInterceptTouchEvent(ev)) {
187 mActiveController = controller;
188 return true;
189 }
Hyunyoung Song645764e2016-06-06 14:19:02 -0700190 }
191 return false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800192 }
193
194 @Override
Adam Cohen3371da02011-10-25 21:38:29 -0700195 public boolean onInterceptHoverEvent(MotionEvent ev) {
Michael Jurka023c71e2012-10-19 17:32:24 +0200196 if (mLauncher == null || mLauncher.getWorkspace() == null) {
197 return false;
198 }
Sunny Goyal740ac7f2016-09-28 16:47:32 -0700199 Folder currentFolder = Folder.getOpen(mLauncher);
Adam Cohen3371da02011-10-25 21:38:29 -0700200 if (currentFolder == null) {
201 return false;
202 } else {
Michael Jurka8b805b12012-04-18 14:23:14 -0700203 AccessibilityManager accessibilityManager = (AccessibilityManager)
204 getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
205 if (accessibilityManager.isTouchExplorationEnabled()) {
Adam Cohen3371da02011-10-25 21:38:29 -0700206 final int action = ev.getAction();
Sunny Goyalccc414b2015-04-30 12:28:16 -0700207 boolean isOverFolderOrSearchBar;
Adam Cohen3371da02011-10-25 21:38:29 -0700208 switch (action) {
209 case MotionEvent.ACTION_HOVER_ENTER:
Sunny Goyalccc414b2015-04-30 12:28:16 -0700210 isOverFolderOrSearchBar = isEventOverFolder(currentFolder, ev) ||
211 (isInAccessibleDrag() && isEventOverDropTargetBar(ev));
212 if (!isOverFolderOrSearchBar) {
Adam Cohen3371da02011-10-25 21:38:29 -0700213 sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
214 mHoverPointClosesFolder = true;
215 return true;
Adam Cohen3371da02011-10-25 21:38:29 -0700216 }
Mihail Dumitrescu693fbc72014-01-03 12:40:22 +0000217 mHoverPointClosesFolder = false;
218 break;
Adam Cohen3371da02011-10-25 21:38:29 -0700219 case MotionEvent.ACTION_HOVER_MOVE:
Sunny Goyalccc414b2015-04-30 12:28:16 -0700220 isOverFolderOrSearchBar = isEventOverFolder(currentFolder, ev) ||
221 (isInAccessibleDrag() && isEventOverDropTargetBar(ev));
222 if (!isOverFolderOrSearchBar && !mHoverPointClosesFolder) {
Adam Cohen3371da02011-10-25 21:38:29 -0700223 sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
224 mHoverPointClosesFolder = true;
225 return true;
Sunny Goyalccc414b2015-04-30 12:28:16 -0700226 } else if (!isOverFolderOrSearchBar) {
Adam Cohen3371da02011-10-25 21:38:29 -0700227 return true;
228 }
Mihail Dumitrescu693fbc72014-01-03 12:40:22 +0000229 mHoverPointClosesFolder = false;
Adam Cohen3371da02011-10-25 21:38:29 -0700230 }
231 }
232 }
233 return false;
234 }
235
236 private void sendTapOutsideFolderAccessibilityEvent(boolean isEditingName) {
Sunny Goyal52851aa2016-09-02 10:41:43 -0700237 int stringId = isEditingName ? R.string.folder_tap_to_rename : R.string.folder_tap_to_close;
Sunny Goyald0030b02017-12-08 15:07:24 -0800238 sendCustomAccessibilityEvent(
Sunny Goyal52851aa2016-09-02 10:41:43 -0700239 this, AccessibilityEvent.TYPE_VIEW_FOCUSED, getContext().getString(stringId));
Adam Cohen3371da02011-10-25 21:38:29 -0700240 }
241
Sunny Goyalccc414b2015-04-30 12:28:16 -0700242 private boolean isInAccessibleDrag() {
Sunny Goyalae502842016-06-17 08:43:56 -0700243 return mLauncher.getAccessibilityDelegate().isInAccessibleDrag();
Sunny Goyalccc414b2015-04-30 12:28:16 -0700244 }
245
Adam Cohen3371da02011-10-25 21:38:29 -0700246 @Override
Winson Chung83ca4802013-04-12 15:10:52 -0700247 public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
Sunny Goyal7d552c82016-08-03 10:31:11 -0700248 // Shortcuts can appear above folder
Sunny Goyal740ac7f2016-09-28 16:47:32 -0700249 View topView = AbstractFloatingView.getTopOpenView(mLauncher);
Sunny Goyal7d552c82016-08-03 10:31:11 -0700250 if (topView != null) {
Sunny Goyal52851aa2016-09-02 10:41:43 -0700251 if (child == topView) {
252 return super.onRequestSendAccessibilityEvent(child, event);
253 }
254 if (isInAccessibleDrag() && child instanceof DropTargetBar) {
255 return super.onRequestSendAccessibilityEvent(child, event);
256 }
257 // Skip propagating onRequestSendAccessibilityEvent for all other children
258 // which are not topView
259 return false;
Winson Chung83ca4802013-04-12 15:10:52 -0700260 }
261 return super.onRequestSendAccessibilityEvent(child, event);
262 }
263
264 @Override
265 public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) {
Sunny Goyal740ac7f2016-09-28 16:47:32 -0700266 View topView = AbstractFloatingView.getTopOpenView(mLauncher);
Sunny Goyal52851aa2016-09-02 10:41:43 -0700267 if (topView != null) {
268 // Only add the top view as a child for accessibility when it is open
269 childrenForAccessibility.add(topView);
Sunny Goyalccc414b2015-04-30 12:28:16 -0700270
271 if (isInAccessibleDrag()) {
Sunny Goyal47328fd2016-05-25 18:56:41 -0700272 childrenForAccessibility.add(mLauncher.getDropTargetBar());
Sunny Goyalccc414b2015-04-30 12:28:16 -0700273 }
Winson Chung83ca4802013-04-12 15:10:52 -0700274 } else {
275 super.addChildrenForAccessibility(childrenForAccessibility);
276 }
277 }
278
279 @Override
Adam Cohen3371da02011-10-25 21:38:29 -0700280 public boolean onHoverEvent(MotionEvent ev) {
281 // If we've received this, we've already done the necessary handling
282 // in onInterceptHoverEvent. Return true to consume the event.
283 return false;
284 }
285
286 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800287 public boolean onTouchEvent(MotionEvent ev) {
Adam Cohen67882692011-03-11 15:29:03 -0800288 int action = ev.getAction();
Sunny Goyal37920962017-09-28 13:43:24 -0700289 if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
Adam Cohenb0f3d742013-10-08 19:16:14 -0700290 if (mTouchCompleteListener != null) {
291 mTouchCompleteListener.onTouchComplete();
292 }
293 mTouchCompleteListener = null;
Adam Cohen67882692011-03-11 15:29:03 -0800294 }
295
Hyunyoung Song645764e2016-06-06 14:19:02 -0700296 if (mActiveController != null) {
Sunny Goyal6ad72f02016-09-23 11:01:10 -0700297 return mActiveController.onControllerTouchEvent(ev);
Sunny Goyalfa8a5ac2018-02-07 13:29:42 -0800298 } else {
299 // In case no child view handled the touch event, we may not get onIntercept anymore
300 return findActiveController(ev);
Hyunyoung Song645764e2016-06-06 14:19:02 -0700301 }
Romain Guy91a9c962009-06-12 13:52:17 -0700302 }
Romain Guyea3763c2010-01-11 18:02:04 -0800303
Adam Cohen35e7e642011-07-17 14:47:18 -0700304 /**
305 * Determine the rect of the descendant in this DragLayer's coordinates
306 *
307 * @param descendant The descendant whose coordinates we want to find.
308 * @param r The rect into which to place the results.
309 * @return The factor by which this descendant is scaled relative to this DragLayer.
310 */
Adam Cohenac8c8762011-07-13 11:15:27 -0700311 public float getDescendantRectRelativeToSelf(View descendant, Rect r) {
Adam Cohen716b51e2011-06-30 12:09:54 -0700312 mTmpXY[0] = 0;
313 mTmpXY[1] = 0;
Adam Cohenac8c8762011-07-13 11:15:27 -0700314 float scale = getDescendantCoordRelativeToSelf(descendant, mTmpXY);
Adam Cohen7d30a372013-07-01 17:03:59 -0700315
Adam Cohen8dfcba42011-07-07 16:38:18 -0700316 r.set(mTmpXY[0], mTmpXY[1],
Adam Cohen7d30a372013-07-01 17:03:59 -0700317 (int) (mTmpXY[0] + scale * descendant.getMeasuredWidth()),
318 (int) (mTmpXY[1] + scale * descendant.getMeasuredHeight()));
Adam Cohenac8c8762011-07-13 11:15:27 -0700319 return scale;
Adam Cohen716b51e2011-06-30 12:09:54 -0700320 }
Adam Cohen76fc0852011-06-17 13:26:23 -0700321
Adam Cohen307fe232012-08-16 17:55:58 -0700322 public float getLocationInDragLayer(View child, int[] loc) {
Adam Cohen35e7e642011-07-17 14:47:18 -0700323 loc[0] = 0;
324 loc[1] = 0;
Adam Cohen307fe232012-08-16 17:55:58 -0700325 return getDescendantCoordRelativeToSelf(child, loc);
Adam Cohen35e7e642011-07-17 14:47:18 -0700326 }
327
Adam Cohen7d30a372013-07-01 17:03:59 -0700328 public float getDescendantCoordRelativeToSelf(View descendant, int[] coord) {
329 return getDescendantCoordRelativeToSelf(descendant, coord, false);
330 }
331
Adam Cohen35e7e642011-07-17 14:47:18 -0700332 /**
333 * Given a coordinate relative to the descendant, find the coordinate in this DragLayer's
334 * coordinates.
335 *
336 * @param descendant The descendant to which the passed coordinate is relative.
337 * @param coord The coordinate that we want mapped.
Adam Cohen7d30a372013-07-01 17:03:59 -0700338 * @param includeRootScroll Whether or not to account for the scroll of the root descendant:
339 * sometimes this is relevant as in a child's coordinates within the root descendant.
Adam Cohen307fe232012-08-16 17:55:58 -0700340 * @return The factor by which this descendant is scaled relative to this DragLayer. Caution
341 * this scale factor is assumed to be equal in X and Y, and so if at any point this
342 * assumption fails, we will need to return a pair of scale factors.
Adam Cohen35e7e642011-07-17 14:47:18 -0700343 */
Adam Cohen7d30a372013-07-01 17:03:59 -0700344 public float getDescendantCoordRelativeToSelf(View descendant, int[] coord,
345 boolean includeRootScroll) {
Sunny Goyale8b5d202016-06-13 16:36:59 -0700346 return Utilities.getDescendantCoordRelativeToAncestor(descendant, this,
Winson Chungc9ca2982013-07-19 12:07:38 -0700347 coord, includeRootScroll);
348 }
349
Adam Cohen7d30a372013-07-01 17:03:59 -0700350 /**
351 * Inverse of {@link #getDescendantCoordRelativeToSelf(View, int[])}.
352 */
Sunny Goyal5e0e94f2016-10-28 10:01:00 -0700353 public void mapCoordInSelfToDescendant(View descendant, int[] coord) {
354 Utilities.mapCoordInSelfToDescendant(descendant, this, coord);
Adam Cohen76fc0852011-06-17 13:26:23 -0700355 }
356
Adam Cohen3e8f8112011-07-02 18:03:00 -0700357 public void getViewRectRelativeToSelf(View v, Rect r) {
358 int[] loc = new int[2];
Adam Cohen8dfcba42011-07-07 16:38:18 -0700359 getLocationInWindow(loc);
Adam Cohen3e8f8112011-07-02 18:03:00 -0700360 int x = loc[0];
361 int y = loc[1];
Adam Cohen716b51e2011-06-30 12:09:54 -0700362
Adam Cohen8dfcba42011-07-07 16:38:18 -0700363 v.getLocationInWindow(loc);
Adam Cohen3e8f8112011-07-02 18:03:00 -0700364 int vX = loc[0];
365 int vY = loc[1];
Adam Cohen716b51e2011-06-30 12:09:54 -0700366
Adam Cohen3e8f8112011-07-02 18:03:00 -0700367 int left = vX - x;
368 int top = vY - y;
369 r.set(left, top, left + v.getMeasuredWidth(), top + v.getMeasuredHeight());
Adam Cohen716b51e2011-06-30 12:09:54 -0700370 }
371
Romain Guyea3763c2010-01-11 18:02:04 -0800372 @Override
373 public boolean dispatchUnhandledMove(View focused, int direction) {
Tony Wickham8dd51682016-08-31 13:30:27 -0700374 // Consume the unhandled move if a container is open, to avoid switching pages underneath.
Sunny Goyal740ac7f2016-09-28 16:47:32 -0700375 boolean isContainerOpen = AbstractFloatingView.getTopOpenView(mLauncher) != null;
Tony Wickham8dd51682016-08-31 13:30:27 -0700376 return isContainerOpen || mDragController.dispatchUnhandledMove(focused, direction);
Romain Guyea3763c2010-01-11 18:02:04 -0800377 }
Adam Cohen120980b2010-12-08 11:05:37 -0800378
Adam Cohenbc927f92014-10-28 16:16:02 -0700379 @Override
Sunny Goyal19d32b72018-01-19 14:26:18 -0800380 public boolean dispatchTouchEvent(MotionEvent ev) {
381 ev.offsetLocation(getTranslationX(), 0);
382 try {
383 return super.dispatchTouchEvent(ev);
384 } finally {
385 ev.offsetLocation(-getTranslationX(), 0);
386 }
387 }
388
389 @Override
Adam Cohenbc927f92014-10-28 16:16:02 -0700390 public LayoutParams generateLayoutParams(AttributeSet attrs) {
391 return new LayoutParams(getContext(), attrs);
392 }
393
394 @Override
395 protected LayoutParams generateDefaultLayoutParams() {
396 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
397 }
398
399 // Override to allow type-checking of LayoutParams.
400 @Override
401 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
402 return p instanceof LayoutParams;
403 }
404
405 @Override
406 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
407 return new LayoutParams(p);
408 }
409
410 public static class LayoutParams extends InsettableFrameLayout.LayoutParams {
Adam Cohen67882692011-03-11 15:29:03 -0800411 public int x, y;
412 public boolean customPosition = false;
413
Adam Cohenbc927f92014-10-28 16:16:02 -0700414 public LayoutParams(Context c, AttributeSet attrs) {
415 super(c, attrs);
416 }
417
Adam Cohen67882692011-03-11 15:29:03 -0800418 public LayoutParams(int width, int height) {
419 super(width, height);
420 }
421
Adam Cohenbc927f92014-10-28 16:16:02 -0700422 public LayoutParams(ViewGroup.LayoutParams lp) {
423 super(lp);
424 }
425
Adam Cohen67882692011-03-11 15:29:03 -0800426 public void setWidth(int width) {
427 this.width = width;
428 }
429
430 public int getWidth() {
431 return width;
432 }
433
434 public void setHeight(int height) {
435 this.height = height;
436 }
437
438 public int getHeight() {
439 return height;
440 }
441
442 public void setX(int x) {
443 this.x = x;
444 }
445
446 public int getX() {
447 return x;
448 }
449
450 public void setY(int y) {
451 this.y = y;
452 }
453
454 public int getY() {
455 return y;
456 }
457 }
458
459 protected void onLayout(boolean changed, int l, int t, int r, int b) {
460 super.onLayout(changed, l, t, r, b);
461 int count = getChildCount();
462 for (int i = 0; i < count; i++) {
463 View child = getChildAt(i);
464 final FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) child.getLayoutParams();
465 if (flp instanceof LayoutParams) {
466 final LayoutParams lp = (LayoutParams) flp;
467 if (lp.customPosition) {
468 child.layout(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height);
469 }
470 }
471 }
472 }
473
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800474 public void animateViewIntoPosition(DragView dragView, final int[] pos, float alpha,
475 float scaleX, float scaleY, int animationEndStyle, Runnable onFinishRunnable,
476 int duration) {
Adam Cohenb7e16182011-07-15 17:55:02 -0700477 Rect r = new Rect();
478 getViewRectRelativeToSelf(dragView, r);
479 final int fromX = r.left;
480 final int fromY = r.top;
481
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800482 animateViewIntoPosition(dragView, fromX, fromY, pos[0], pos[1], alpha, 1, 1, scaleX, scaleY,
Adam Cohened66b2b2012-01-23 17:28:51 -0800483 onFinishRunnable, animationEndStyle, duration, null);
484 }
485
Sunny Goyal1797af42017-10-06 13:29:57 -0700486 public void animateViewIntoPosition(DragView dragView, final View child, View anchorView) {
487 animateViewIntoPosition(dragView, child, -1, anchorView);
Adam Cohened51cc92011-08-01 20:28:08 -0700488 }
489
490 public void animateViewIntoPosition(DragView dragView, final View child, int duration,
Sunny Goyal1797af42017-10-06 13:29:57 -0700491 View anchorView) {
Michael Jurkaa52570f2012-03-20 03:18:20 -0700492 ShortcutAndWidgetContainer parentChildren = (ShortcutAndWidgetContainer) child.getParent();
Adam Cohen716b51e2011-06-30 12:09:54 -0700493 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
Winson Chungeecf02d2012-03-02 17:14:58 -0800494 parentChildren.measureChild(child);
Adam Cohen716b51e2011-06-30 12:09:54 -0700495
Adam Cohen3e8f8112011-07-02 18:03:00 -0700496 Rect r = new Rect();
497 getViewRectRelativeToSelf(dragView, r);
Adam Cohen716b51e2011-06-30 12:09:54 -0700498
499 int coord[] = new int[2];
Adam Cohen307fe232012-08-16 17:55:58 -0700500 float childScale = child.getScaleX();
501 coord[0] = lp.x + (int) (child.getMeasuredWidth() * (1 - childScale) / 2);
502 coord[1] = lp.y + (int) (child.getMeasuredHeight() * (1 - childScale) / 2);
Winson Chungeecf02d2012-03-02 17:14:58 -0800503
Adam Cohen8dfcba42011-07-07 16:38:18 -0700504 // Since the child hasn't necessarily been laid out, we force the lp to be updated with
Adam Cohenac8c8762011-07-13 11:15:27 -0700505 // the correct coordinates (above) and use these to determine the final location
Winson Chung557d6ed2011-07-08 15:34:52 -0700506 float scale = getDescendantCoordRelativeToSelf((View) child.getParent(), coord);
Adam Cohen307fe232012-08-16 17:55:58 -0700507 // We need to account for the scale of the child itself, as the above only accounts for
508 // for the scale in parents.
509 scale *= childScale;
Adam Cohenac8c8762011-07-13 11:15:27 -0700510 int toX = coord[0];
511 int toY = coord[1];
Winson Chungeeb5bbc2013-11-13 15:47:05 -0800512 float toScale = scale;
Andrew Flynn4497ebf2012-05-09 11:28:00 -0700513 if (child instanceof TextView) {
514 TextView tv = (TextView) child;
Winson Chungeeb5bbc2013-11-13 15:47:05 -0800515 // Account for the source scale of the icon (ie. from AllApps to Workspace, in which
516 // the workspace may have smaller icon bounds).
517 toScale = scale / dragView.getIntrinsicIconScaleFactor();
Andrew Flynn4497ebf2012-05-09 11:28:00 -0700518
519 // The child may be scaled (always about the center of the view) so to account for it,
520 // we have to offset the position by the scaled size. Once we do that, we can center
521 // the drag view about the scaled child view.
Winson Chungeeb5bbc2013-11-13 15:47:05 -0800522 toY += Math.round(toScale * tv.getPaddingTop());
523 toY -= dragView.getMeasuredHeight() * (1 - toScale) / 2;
Sunny Goyal95abbb32014-08-04 10:53:22 -0700524 if (dragView.getDragVisualizeOffset() != null) {
525 toY -= Math.round(toScale * dragView.getDragVisualizeOffset().y);
526 }
527
Andrew Flynn4497ebf2012-05-09 11:28:00 -0700528 toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2;
529 } else if (child instanceof FolderIcon) {
Winson Chungea359c62011-08-03 17:06:35 -0700530 // Account for holographic blur padding on the drag view
Winson Chung5f8afe62013-08-12 16:19:28 -0700531 toY += Math.round(scale * (child.getPaddingTop() - dragView.getDragRegionTop()));
Jon Mirandac9c57632016-10-12 20:35:59 -0700532 toY -= scale * dragView.getBlurSizeOutline() / 2;
Adam Cohen307fe232012-08-16 17:55:58 -0700533 toY -= (1 - scale) * dragView.getMeasuredHeight() / 2;
Winson Chungea359c62011-08-03 17:06:35 -0700534 // Center in the x coordinate about the target's drawable
535 toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2;
Adam Cohenac8c8762011-07-13 11:15:27 -0700536 } else {
537 toY -= (Math.round(scale * (dragView.getHeight() - child.getMeasuredHeight()))) / 2;
538 toX -= (Math.round(scale * (dragView.getMeasuredWidth()
539 - child.getMeasuredWidth()))) / 2;
540 }
541
542 final int fromX = r.left;
543 final int fromY = r.top;
Adam Cohen716b51e2011-06-30 12:09:54 -0700544 child.setVisibility(INVISIBLE);
Sunny Goyal6639a5d2018-02-28 15:09:36 -0800545 Runnable onCompleteRunnable = () -> child.setVisibility(VISIBLE);
Winson Chungeeb5bbc2013-11-13 15:47:05 -0800546 animateViewIntoPosition(dragView, fromX, fromY, toX, toY, 1, 1, 1, toScale, toScale,
Adam Cohend41fbf52012-02-16 23:53:59 -0800547 onCompleteRunnable, ANIMATION_END_DISAPPEAR, duration, anchorView);
Winson Chung557d6ed2011-07-08 15:34:52 -0700548 }
549
Adam Cohend41fbf52012-02-16 23:53:59 -0800550 public void animateViewIntoPosition(final DragView view, final int fromX, final int fromY,
Adam Cohened66b2b2012-01-23 17:28:51 -0800551 final int toX, final int toY, float finalAlpha, float initScaleX, float initScaleY,
552 float finalScaleX, float finalScaleY, Runnable onCompleteRunnable,
553 int animationEndStyle, int duration, View anchorView) {
Adam Cohen8dfcba42011-07-07 16:38:18 -0700554 Rect from = new Rect(fromX, fromY, fromX +
555 view.getMeasuredWidth(), fromY + view.getMeasuredHeight());
Adam Cohen3e8f8112011-07-02 18:03:00 -0700556 Rect to = new Rect(toX, toY, toX + view.getMeasuredWidth(), toY + view.getMeasuredHeight());
Adam Cohened66b2b2012-01-23 17:28:51 -0800557 animateView(view, from, to, finalAlpha, initScaleX, initScaleY, finalScaleX, finalScaleY, duration,
558 null, null, onCompleteRunnable, animationEndStyle, anchorView);
Adam Cohen3e8f8112011-07-02 18:03:00 -0700559 }
560
Adam Cohenb7e16182011-07-15 17:55:02 -0700561 /**
562 * This method animates a view at the end of a drag and drop animation.
563 *
564 * @param view The view to be animated. This view is drawn directly into DragLayer, and so
565 * doesn't need to be a child of DragLayer.
566 * @param from The initial location of the view. Only the left and top parameters are used.
567 * @param to The final location of the view. Only the left and top parameters are used. This
568 * location doesn't account for scaling, and so should be centered about the desired
569 * final location (including scaling).
570 * @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 -0700571 * @param finalScaleX The final scale of the view. The view is scaled about its center.
572 * @param finalScaleY The final scale of the view. The view is scaled about its center.
Adam Cohenb7e16182011-07-15 17:55:02 -0700573 * @param duration The duration of the animation.
574 * @param motionInterpolator The interpolator to use for the location of the view.
575 * @param alphaInterpolator The interpolator to use for the alpha of the view.
576 * @param onCompleteRunnable Optional runnable to run on animation completion.
Sunny Goyal6178f132016-07-11 17:30:03 -0700577 * @param animationEndStyle Whether or not to fade out the view once the animation completes.
578 * {@link #ANIMATION_END_DISAPPEAR} or {@link #ANIMATION_END_REMAIN_VISIBLE}.
Adam Cohen6441de02011-12-14 14:25:32 -0800579 * @param anchorView If not null, this represents the view which the animated view stays
580 * anchored to in case scrolling is currently taking place. Note: currently this is
581 * only used for the X dimension for the case of the workspace.
Adam Cohenb7e16182011-07-15 17:55:02 -0700582 */
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800583 public void animateView(final DragView view, final Rect from, final Rect to,
584 final float finalAlpha, final float initScaleX, final float initScaleY,
585 final float finalScaleX, final float finalScaleY, int duration,
586 final Interpolator motionInterpolator, final Interpolator alphaInterpolator,
587 final Runnable onCompleteRunnable, final int animationEndStyle, View anchorView) {
588
Adam Cohen716b51e2011-06-30 12:09:54 -0700589 // Calculate the duration of the animation based on the object's distance
Sunny Goyalf7a29e82015-04-24 15:20:43 -0700590 final float dist = (float) Math.hypot(to.left - from.left, to.top - from.top);
Adam Cohen716b51e2011-06-30 12:09:54 -0700591 final Resources res = getResources();
592 final float maxDist = (float) res.getInteger(R.integer.config_dropAnimMaxDist);
Adam Cohen3e8f8112011-07-02 18:03:00 -0700593
594 // If duration < 0, this is a cue to compute the duration based on the distance
595 if (duration < 0) {
Sunny Goyaldec3a902017-01-25 18:23:36 -0800596 duration = res.getInteger(R.integer.config_dropAnimMaxDuration);
597 if (dist < maxDist) {
598 duration *= mCubicEaseOutInterpolator.getInterpolation(dist / maxDist);
Adam Cohen3e8f8112011-07-02 18:03:00 -0700599 }
Sunny Goyaldec3a902017-01-25 18:23:36 -0800600 duration = Math.max(duration, res.getInteger(R.integer.config_dropAnimMinDuration));
Adam Cohen716b51e2011-06-30 12:09:54 -0700601 }
602
Winson Chung043f2af2012-03-01 16:09:54 -0800603 // Fall back to cubic ease out interpolator for the animation if none is specified
604 TimeInterpolator interpolator = null;
Adam Cohen3e8f8112011-07-02 18:03:00 -0700605 if (alphaInterpolator == null || motionInterpolator == null) {
Winson Chung043f2af2012-03-01 16:09:54 -0800606 interpolator = mCubicEaseOutInterpolator;
Adam Cohen3e8f8112011-07-02 18:03:00 -0700607 }
Adam Cohen716b51e2011-06-30 12:09:54 -0700608
Winson Chung043f2af2012-03-01 16:09:54 -0800609 // Animate the view
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800610 final float initAlpha = view.getAlpha();
Winson Chung043f2af2012-03-01 16:09:54 -0800611 final float dropViewScale = view.getScaleX();
612 AnimatorUpdateListener updateCb = new AnimatorUpdateListener() {
613 @Override
Adam Cohen716b51e2011-06-30 12:09:54 -0700614 public void onAnimationUpdate(ValueAnimator animation) {
615 final float percent = (Float) animation.getAnimatedValue();
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800616 final int width = view.getMeasuredWidth();
617 final int height = view.getMeasuredHeight();
Adam Cohen716b51e2011-06-30 12:09:54 -0700618
Adam Cohen3e8f8112011-07-02 18:03:00 -0700619 float alphaPercent = alphaInterpolator == null ? percent :
620 alphaInterpolator.getInterpolation(percent);
621 float motionPercent = motionInterpolator == null ? percent :
622 motionInterpolator.getInterpolation(percent);
623
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800624 float initialScaleX = initScaleX * dropViewScale;
625 float initialScaleY = initScaleY * dropViewScale;
626 float scaleX = finalScaleX * percent + initialScaleX * (1 - percent);
627 float scaleY = finalScaleY * percent + initialScaleY * (1 - percent);
628 float alpha = finalAlpha * alphaPercent + initAlpha * (1 - alphaPercent);
629
630 float fromLeft = from.left + (initialScaleX - 1f) * width / 2;
631 float fromTop = from.top + (initialScaleY - 1f) * height / 2;
632
633 int x = (int) (fromLeft + Math.round(((to.left - fromLeft) * motionPercent)));
634 int y = (int) (fromTop + Math.round(((to.top - fromTop) * motionPercent)));
635
Adam Cohen28f852a2013-10-15 14:34:05 -0700636 int anchorAdjust = mAnchorView == null ? 0 : (int) (mAnchorView.getScaleX() *
637 (mAnchorViewInitialScrollX - mAnchorView.getScrollX()));
638
639 int xPos = x - mDropView.getScrollX() + anchorAdjust;
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800640 int yPos = y - mDropView.getScrollY();
641
642 mDropView.setTranslationX(xPos);
643 mDropView.setTranslationY(yPos);
644 mDropView.setScaleX(scaleX);
645 mDropView.setScaleY(scaleY);
646 mDropView.setAlpha(alpha);
Adam Cohen716b51e2011-06-30 12:09:54 -0700647 }
Winson Chung043f2af2012-03-01 16:09:54 -0800648 };
649 animateView(view, updateCb, duration, interpolator, onCompleteRunnable, animationEndStyle,
650 anchorView);
651 }
652
653 public void animateView(final DragView view, AnimatorUpdateListener updateCb, int duration,
654 TimeInterpolator interpolator, final Runnable onCompleteRunnable,
655 final int animationEndStyle, View anchorView) {
656 // Clean up the previous animations
657 if (mDropAnim != null) mDropAnim.cancel();
Winson Chung043f2af2012-03-01 16:09:54 -0800658
659 // Show the drop view if it was previously hidden
660 mDropView = view;
661 mDropView.cancelAnimation();
Vadim Tryshevd6c8e7e2015-07-08 13:40:14 -0700662 mDropView.requestLayout();
Winson Chung043f2af2012-03-01 16:09:54 -0800663
664 // Set the anchor view if the page is scrolling
665 if (anchorView != null) {
666 mAnchorViewInitialScrollX = anchorView.getScrollX();
667 }
668 mAnchorView = anchorView;
669
670 // Create and start the animation
671 mDropAnim = new ValueAnimator();
672 mDropAnim.setInterpolator(interpolator);
673 mDropAnim.setDuration(duration);
674 mDropAnim.setFloatValues(0f, 1f);
675 mDropAnim.addUpdateListener(updateCb);
Adam Cohen3e8f8112011-07-02 18:03:00 -0700676 mDropAnim.addListener(new AnimatorListenerAdapter() {
677 public void onAnimationEnd(Animator animation) {
Adam Cohen8dfcba42011-07-07 16:38:18 -0700678 if (onCompleteRunnable != null) {
679 onCompleteRunnable.run();
680 }
Adam Cohened66b2b2012-01-23 17:28:51 -0800681 switch (animationEndStyle) {
682 case ANIMATION_END_DISAPPEAR:
683 clearAnimatedView();
684 break;
Adam Cohened66b2b2012-01-23 17:28:51 -0800685 case ANIMATION_END_REMAIN_VISIBLE:
686 break;
Adam Cohen3e8f8112011-07-02 18:03:00 -0700687 }
688 }
689 });
Adam Cohen716b51e2011-06-30 12:09:54 -0700690 mDropAnim.start();
691 }
692
Adam Cohened66b2b2012-01-23 17:28:51 -0800693 public void clearAnimatedView() {
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800694 if (mDropAnim != null) {
695 mDropAnim.cancel();
696 }
697 if (mDropView != null) {
Adam Cohen2d783ce2014-04-07 14:11:48 -0700698 mDragController.onDeferredEndDrag(mDropView);
Winson Chung7bd1bbb2012-02-13 18:29:29 -0800699 }
Adam Cohened66b2b2012-01-23 17:28:51 -0800700 mDropView = null;
Adam Cohened66b2b2012-01-23 17:28:51 -0800701 invalidate();
702 }
703
704 public View getAnimatedView() {
705 return mDropView;
706 }
707
Adam Cohen716b51e2011-06-30 12:09:54 -0700708 @Override
Jon Miranda758d5042017-11-08 14:38:23 -0800709 public void onViewAdded(View child) {
710 super.onViewAdded(child);
Adam Cohen21b41102011-11-01 17:29:52 -0700711 updateChildIndices();
Tony865ee022018-03-06 23:50:03 +0000712 UiFactory.onLauncherStateOrFocusChanged(mLauncher);
Adam Cohen21b41102011-11-01 17:29:52 -0700713 }
714
715 @Override
Jon Miranda758d5042017-11-08 14:38:23 -0800716 public void onViewRemoved(View child) {
717 super.onViewRemoved(child);
Adam Cohen21b41102011-11-01 17:29:52 -0700718 updateChildIndices();
Tony865ee022018-03-06 23:50:03 +0000719 UiFactory.onLauncherStateOrFocusChanged(mLauncher);
Adam Cohen21b41102011-11-01 17:29:52 -0700720 }
721
Adam Cohen432609a2014-03-13 17:03:22 -0700722 @Override
723 public void bringChildToFront(View child) {
724 super.bringChildToFront(child);
725 updateChildIndices();
726 }
727
Adam Cohen21b41102011-11-01 17:29:52 -0700728 private void updateChildIndices() {
Adam Cohen432609a2014-03-13 17:03:22 -0700729 mTopViewIndex = -1;
Jorim Jaggi55bd9722014-01-16 15:30:42 -0800730 int childCount = getChildCount();
731 for (int i = 0; i < childCount; i++) {
Adam Cohenb670f192014-04-08 15:34:17 -0700732 if (getChildAt(i) instanceof DragView) {
Adam Cohen432609a2014-03-13 17:03:22 -0700733 mTopViewIndex = i;
Jorim Jaggi55bd9722014-01-16 15:30:42 -0800734 }
Adam Cohen21b41102011-11-01 17:29:52 -0700735 }
Adam Cohen2d783ce2014-04-07 14:11:48 -0700736 mChildCountOnLastUpdate = childCount;
Adam Cohen21b41102011-11-01 17:29:52 -0700737 }
738
739 @Override
740 protected int getChildDrawingOrder(int childCount, int i) {
Adam Cohen2d783ce2014-04-07 14:11:48 -0700741 if (mChildCountOnLastUpdate != childCount) {
742 // between platform versions 17 and 18, behavior for onChildViewRemoved / Added changed.
743 // Pre-18, the child was not added / removed by the time of those callbacks. We need to
744 // force update our representation of things here to avoid crashing on pre-18 devices
745 // in certain instances.
746 updateChildIndices();
747 }
748
Adam Cohen432609a2014-03-13 17:03:22 -0700749 // i represents the current draw iteration
750 if (mTopViewIndex == -1) {
751 // in general we do nothing
Adam Cohen21b41102011-11-01 17:29:52 -0700752 return i;
Adam Cohen432609a2014-03-13 17:03:22 -0700753 } else if (i == childCount - 1) {
754 // if we have a top index, we return it when drawing last item (highest z-order)
755 return mTopViewIndex;
756 } else if (i < mTopViewIndex) {
Jorim Jaggi55bd9722014-01-16 15:30:42 -0800757 return i;
Adam Cohen21b41102011-11-01 17:29:52 -0700758 } else {
Adam Cohen432609a2014-03-13 17:03:22 -0700759 // for indexes greater than the top index, we fetch one item above to shift for the
760 // displacement of the top index
761 return i + 1;
Adam Cohen21b41102011-11-01 17:29:52 -0700762 }
763 }
Winson Chung360e63f2012-04-27 13:48:05 -0700764
Winson Chung360e63f2012-04-27 13:48:05 -0700765 @Override
766 protected void dispatchDraw(Canvas canvas) {
Sunny Goyal0fa93bc2015-05-13 11:23:24 -0700767 // Draw the background below children.
Sunny Goyal3333b0c2016-05-09 20:43:21 -0700768 mFocusIndicatorHelper.draw(canvas);
Winson Chung360e63f2012-04-27 13:48:05 -0700769 super.dispatchDraw(canvas);
Adam Cohen8cc05502014-08-22 18:32:15 -0700770 }
Winson Chung360e63f2012-04-27 13:48:05 -0700771
Sunny Goyal52851aa2016-09-02 10:41:43 -0700772 @Override
773 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
Sunny Goyal740ac7f2016-09-28 16:47:32 -0700774 View topView = AbstractFloatingView.getTopOpenView(mLauncher);
Sunny Goyal52851aa2016-09-02 10:41:43 -0700775 if (topView != null) {
776 return topView.requestFocus(direction, previouslyFocusedRect);
777 } else {
778 return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
779 }
780 }
781
782 @Override
783 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
Sunny Goyal740ac7f2016-09-28 16:47:32 -0700784 View topView = AbstractFloatingView.getTopOpenView(mLauncher);
Sunny Goyal52851aa2016-09-02 10:41:43 -0700785 if (topView != null) {
786 topView.addFocusables(views, direction);
787 } else {
788 super.addFocusables(views, direction, focusableMode);
789 }
790 }
791
Adam Cohenb0f3d742013-10-08 19:16:14 -0700792 public void setTouchCompleteListener(TouchCompleteListener listener) {
793 mTouchCompleteListener = listener;
794 }
795
796 public interface TouchCompleteListener {
Sunny Goyal7ede6112017-12-05 15:11:21 -0800797 void onTouchComplete();
Adam Cohenb0f3d742013-10-08 19:16:14 -0700798 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800799}