blob: 5c69ae3915b386833a4961ecddcd20781400abde [file] [log] [blame]
Winsonbe7607a2015-10-01 17:24:51 -07001/*
2 * Copyright (C) 2014 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
17package com.android.systemui.recents.views;
18
Winsonc694a502016-03-10 16:35:03 -080019import android.app.ActivityManager;
Winsonbe7607a2015-10-01 17:24:51 -070020import android.graphics.Point;
Jorim Jaggi19cf2972016-04-07 23:26:10 -070021import android.graphics.Rect;
Winson131d53a2016-06-14 13:51:59 -070022import android.view.InputDevice;
Winsonbe7607a2015-10-01 17:24:51 -070023import android.view.MotionEvent;
Winson131d53a2016-06-14 13:51:59 -070024import android.view.PointerIcon;
Matthew Ng9e177f02017-09-21 11:10:32 -070025import android.view.View;
Winson3e874742016-01-07 10:08:17 -080026import android.view.ViewConfiguration;
Winson231bc9c2016-02-09 12:31:00 -080027import android.view.ViewDebug;
Winsonc0d70582016-01-29 10:24:39 -080028
Jorim Jaggi19cf2972016-04-07 23:26:10 -070029import com.android.internal.policy.DividerSnapAlgorithm;
Winson53ec42c2015-10-28 15:55:35 -070030import com.android.systemui.recents.Recents;
Winsonbe7607a2015-10-01 17:24:51 -070031import com.android.systemui.recents.events.EventBus;
Jorim Jaggi19cf2972016-04-07 23:26:10 -070032import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
Matthew Ng3975de52016-10-21 14:53:18 -070033import com.android.systemui.recents.events.activity.HideRecentsEvent;
Matthew Ng9e177f02017-09-21 11:10:32 -070034import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
Winson9b001392016-04-08 14:54:02 -070035import com.android.systemui.recents.events.ui.HideIncompatibleAppOverlayEvent;
36import com.android.systemui.recents.events.ui.ShowIncompatibleAppOverlayEvent;
Winsoneca4ab62015-11-04 10:50:28 -080037import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
Winsonbe7607a2015-10-01 17:24:51 -070038import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
39import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
Winsoneca4ab62015-11-04 10:50:28 -080040import com.android.systemui.recents.events.ui.dragndrop.DragStartInitializeDropTargetsEvent;
Winsond759a712015-11-30 18:43:43 -080041import com.android.systemui.recents.misc.SystemServicesProxy;
Winson Chung6519c1b2017-10-13 17:12:56 -070042import com.android.systemui.shared.recents.model.Task;
Winsonbe7607a2015-10-01 17:24:51 -070043
Winsoneca4ab62015-11-04 10:50:28 -080044import java.util.ArrayList;
45
Winsonbe7607a2015-10-01 17:24:51 -070046/**
Winsonbe7607a2015-10-01 17:24:51 -070047 * Handles touch events for a RecentsView.
48 */
Winsoneca4ab62015-11-04 10:50:28 -080049public class RecentsViewTouchHandler {
50
Winsonbe7607a2015-10-01 17:24:51 -070051 private RecentsView mRv;
52
Winson231bc9c2016-02-09 12:31:00 -080053 @ViewDebug.ExportedProperty(deepExport=true, prefix="drag_task")
Winsonbe7607a2015-10-01 17:24:51 -070054 private Task mDragTask;
Winson231bc9c2016-02-09 12:31:00 -080055 @ViewDebug.ExportedProperty(deepExport=true, prefix="drag_task_view_")
Winsonbe7607a2015-10-01 17:24:51 -070056 private TaskView mTaskView;
Winsonbe7607a2015-10-01 17:24:51 -070057
Winson231bc9c2016-02-09 12:31:00 -080058 @ViewDebug.ExportedProperty(category="recents")
Winson479f7442015-11-25 15:16:27 -080059 private Point mTaskViewOffset = new Point();
Winson231bc9c2016-02-09 12:31:00 -080060 @ViewDebug.ExportedProperty(category="recents")
Winsonbe7607a2015-10-01 17:24:51 -070061 private Point mDownPos = new Point();
Winson231bc9c2016-02-09 12:31:00 -080062 @ViewDebug.ExportedProperty(category="recents")
Winson3e874742016-01-07 10:08:17 -080063 private boolean mDragRequested;
Winson231bc9c2016-02-09 12:31:00 -080064 @ViewDebug.ExportedProperty(category="recents")
Winson3e874742016-01-07 10:08:17 -080065 private boolean mIsDragging;
66 private float mDragSlop;
Winson131d53a2016-06-14 13:51:59 -070067 private int mDeviceId = -1;
Winsoneca4ab62015-11-04 10:50:28 -080068
69 private DropTarget mLastDropTarget;
Jorim Jaggi19cf2972016-04-07 23:26:10 -070070 private DividerSnapAlgorithm mDividerSnapAlgorithm;
Winsoneca4ab62015-11-04 10:50:28 -080071 private ArrayList<DropTarget> mDropTargets = new ArrayList<>();
Winson Chung6519c1b2017-10-13 17:12:56 -070072 private ArrayList<DockState> mVisibleDockStates = new ArrayList<>();
Winsonbe7607a2015-10-01 17:24:51 -070073
74 public RecentsViewTouchHandler(RecentsView rv) {
75 mRv = rv;
Winson3e874742016-01-07 10:08:17 -080076 mDragSlop = ViewConfiguration.get(rv.getContext()).getScaledTouchSlop();
Jorim Jaggi19cf2972016-04-07 23:26:10 -070077 updateSnapAlgorithm();
78 }
79
80 private void updateSnapAlgorithm() {
81 Rect insets = new Rect();
82 SystemServicesProxy.getInstance(mRv.getContext()).getStableInsets(insets);
83 mDividerSnapAlgorithm = DividerSnapAlgorithm.create(mRv.getContext(), insets);
Winsonbe7607a2015-10-01 17:24:51 -070084 }
85
Winson882072b2015-10-12 11:26:33 -070086 /**
Winsoneca4ab62015-11-04 10:50:28 -080087 * Registers a new drop target for the current drag only.
88 */
89 public void registerDropTargetForCurrentDrag(DropTarget target) {
90 mDropTargets.add(target);
91 }
92
93 /**
Winson036693c2016-01-14 17:16:06 -080094 * Returns the set of visible dock states for this current drag.
95 */
Winson Chung6519c1b2017-10-13 17:12:56 -070096 public ArrayList<DockState> getVisibleDockStates() {
Winson036693c2016-01-14 17:16:06 -080097 return mVisibleDockStates;
98 }
99
Winsonbe7607a2015-10-01 17:24:51 -0700100 /** Touch preprocessing for handling below */
101 public boolean onInterceptTouchEvent(MotionEvent ev) {
Matthew Ng9e177f02017-09-21 11:10:32 -0700102 return handleTouchEvent(ev) || mDragRequested;
Winsonbe7607a2015-10-01 17:24:51 -0700103 }
104
105 /** Handles touch events once we have intercepted them */
106 public boolean onTouchEvent(MotionEvent ev) {
Winson4165d3362015-10-10 14:40:35 -0700107 handleTouchEvent(ev);
Winson Chung2cf6ad82017-11-09 17:36:59 -0800108 if (ev.getAction() == MotionEvent.ACTION_UP && mRv.getStack().getTaskCount() == 0) {
Matthew Ng3975de52016-10-21 14:53:18 -0700109 EventBus.getDefault().send(new HideRecentsEvent(false, true));
110 }
111 return true;
Winsonbe7607a2015-10-01 17:24:51 -0700112 }
113
114 /**** Events ****/
115
116 public final void onBusEvent(DragStartEvent event) {
Winsond759a712015-11-30 18:43:43 -0800117 SystemServicesProxy ssp = Recents.getSystemServices();
Winsonbe7607a2015-10-01 17:24:51 -0700118 mRv.getParent().requestDisallowInterceptTouchEvent(true);
Winson3e874742016-01-07 10:08:17 -0800119 mDragRequested = true;
120 // We defer starting the actual drag handling until the user moves past the drag slop
121 mIsDragging = false;
Winsonbe7607a2015-10-01 17:24:51 -0700122 mDragTask = event.task;
123 mTaskView = event.taskView;
Winsoneca4ab62015-11-04 10:50:28 -0800124 mDropTargets.clear();
Winsonbe7607a2015-10-01 17:24:51 -0700125
Winson479f7442015-11-25 15:16:27 -0800126 int[] recentsViewLocation = new int[2];
127 mRv.getLocationInWindow(recentsViewLocation);
128 mTaskViewOffset.set(mTaskView.getLeft() - recentsViewLocation[0] + event.tlOffset.x,
129 mTaskView.getTop() - recentsViewLocation[1] + event.tlOffset.y);
Matthew Ngb7035f32017-01-19 11:04:50 -0800130
131 // Change space coordinates relative to the view to RecentsView when user initiates a touch
132 if (event.isUserTouchInitiated) {
133 float x = mDownPos.x - mTaskViewOffset.x;
134 float y = mDownPos.y - mTaskViewOffset.y;
135 mTaskView.setTranslationX(x);
136 mTaskView.setTranslationY(y);
137 }
Winsoneca4ab62015-11-04 10:50:28 -0800138
Winson036693c2016-01-14 17:16:06 -0800139 mVisibleDockStates.clear();
Erik Wolsheimer9be3a062017-05-31 14:59:57 -0700140 if (ActivityManager.supportsMultiWindow(mRv.getContext()) && !ssp.hasDockedTask()
Jorim Jaggi19cf2972016-04-07 23:26:10 -0700141 && mDividerSnapAlgorithm.isSplitScreenFeasible()) {
Winsonf21c3dac2016-04-13 18:24:12 -0700142 Recents.logDockAttempt(mRv.getContext(), event.task.getTopComponent(),
143 event.task.resizeMode);
Winson931845f2016-02-24 19:38:41 -0800144 if (!event.task.isDockable) {
Winson9b001392016-04-08 14:54:02 -0700145 EventBus.getDefault().send(new ShowIncompatibleAppOverlayEvent());
Winson931845f2016-02-24 19:38:41 -0800146 } else {
147 // Add the dock state drop targets (these take priority)
Winson Chung6519c1b2017-10-13 17:12:56 -0700148 DockState[] dockStates = Recents.getConfiguration()
Matthew Ngb7035f32017-01-19 11:04:50 -0800149 .getDockStatesForCurrentOrientation();
Winson Chung6519c1b2017-10-13 17:12:56 -0700150 for (DockState dockState : dockStates) {
Winson931845f2016-02-24 19:38:41 -0800151 registerDropTargetForCurrentDrag(dockState);
Winson99ef4582016-04-18 16:57:27 -0700152 dockState.update(mRv.getContext());
Winson931845f2016-02-24 19:38:41 -0800153 mVisibleDockStates.add(dockState);
154 }
Winsoneca4ab62015-11-04 10:50:28 -0800155 }
156 }
157
158 // Request other drop targets to register themselves
Winsond9529612016-01-28 13:29:49 -0800159 EventBus.getDefault().send(new DragStartInitializeDropTargetsEvent(event.task,
160 event.taskView, this));
Winson131d53a2016-06-14 13:51:59 -0700161 if (mDeviceId != -1) {
Winson Chungdf424552017-03-03 11:17:14 -0800162 InputDevice device = InputDevice.getDevice(mDeviceId);
163 if (device != null) {
164 device.setPointerType(PointerIcon.TYPE_GRABBING);
165 }
Winson131d53a2016-06-14 13:51:59 -0700166 }
Winsonbe7607a2015-10-01 17:24:51 -0700167 }
168
169 public final void onBusEvent(DragEndEvent event) {
Winson9b001392016-04-08 14:54:02 -0700170 if (!mDragTask.isDockable) {
171 EventBus.getDefault().send(new HideIncompatibleAppOverlayEvent());
172 }
Winson3e874742016-01-07 10:08:17 -0800173 mDragRequested = false;
Winsonbe7607a2015-10-01 17:24:51 -0700174 mDragTask = null;
175 mTaskView = null;
Winsoneca4ab62015-11-04 10:50:28 -0800176 mLastDropTarget = null;
Winson4165d3362015-10-10 14:40:35 -0700177 }
178
Jorim Jaggi19cf2972016-04-07 23:26:10 -0700179 public final void onBusEvent(ConfigurationChangedEvent event) {
Winson9832f8f2016-04-26 11:30:59 -0700180 if (event.fromDisplayDensityChange || event.fromDeviceOrientationChange) {
Jorim Jaggi25160db2016-04-18 16:03:36 -0700181 updateSnapAlgorithm();
182 }
Jorim Jaggi19cf2972016-04-07 23:26:10 -0700183 }
184
Matthew Ng9e177f02017-09-21 11:10:32 -0700185 void cancelStackActionButtonClick() {
186 mRv.getStackActionButton().setPressed(false);
187 }
188
189 private boolean isWithinStackActionButton(float x, float y) {
190 Rect rect = mRv.getStackActionButtonBoundsFromStackLayout();
191 return mRv.getStackActionButton().getVisibility() == View.VISIBLE &&
192 mRv.getStackActionButton().pointInView(x - rect.left, y - rect.top, 0 /* slop */);
193 }
194
195 private void changeStackActionButtonDrawableHotspot(float x, float y) {
196 Rect rect = mRv.getStackActionButtonBoundsFromStackLayout();
197 mRv.getStackActionButton().drawableHotspotChanged(x - rect.left, y - rect.top);
198 }
199
Winson4165d3362015-10-10 14:40:35 -0700200 /**
201 * Handles dragging touch events
Winson4165d3362015-10-10 14:40:35 -0700202 */
Matthew Ng9e177f02017-09-21 11:10:32 -0700203 private boolean handleTouchEvent(MotionEvent ev) {
Jorim Jaggia20eeda2016-04-21 00:07:29 -0700204 int action = ev.getActionMasked();
Matthew Ng9e177f02017-09-21 11:10:32 -0700205 boolean consumed = false;
206 float evX = ev.getX();
207 float evY = ev.getY();
Jorim Jaggia20eeda2016-04-21 00:07:29 -0700208 switch (action) {
Winson4165d3362015-10-10 14:40:35 -0700209 case MotionEvent.ACTION_DOWN:
Matthew Ng9e177f02017-09-21 11:10:32 -0700210 mDownPos.set((int) evX, (int) evY);
Winson131d53a2016-06-14 13:51:59 -0700211 mDeviceId = ev.getDeviceId();
Matthew Ng9e177f02017-09-21 11:10:32 -0700212
213 if (isWithinStackActionButton(evX, evY)) {
214 changeStackActionButtonDrawableHotspot(evX, evY);
215 mRv.getStackActionButton().setPressed(true);
216 }
Winson4165d3362015-10-10 14:40:35 -0700217 break;
218 case MotionEvent.ACTION_MOVE: {
Winson3e874742016-01-07 10:08:17 -0800219 float x = evX - mTaskViewOffset.x;
220 float y = evY - mTaskViewOffset.y;
Winson4165d3362015-10-10 14:40:35 -0700221
Matthew Ng9e177f02017-09-21 11:10:32 -0700222 if (mRv.getStackActionButton().isPressed() && isWithinStackActionButton(evX, evY)) {
223 changeStackActionButtonDrawableHotspot(evX, evY);
224 }
225
Winson3e874742016-01-07 10:08:17 -0800226 if (mDragRequested) {
227 if (!mIsDragging) {
228 mIsDragging = Math.hypot(evX - mDownPos.x, evY - mDownPos.y) > mDragSlop;
Winson4165d3362015-10-10 14:40:35 -0700229 }
Winson3e874742016-01-07 10:08:17 -0800230 if (mIsDragging) {
231 int width = mRv.getMeasuredWidth();
232 int height = mRv.getMeasuredHeight();
233
234 DropTarget currentDropTarget = null;
235
236 // Give priority to the current drop target to retain the touch handling
237 if (mLastDropTarget != null) {
238 if (mLastDropTarget.acceptsDrop((int) evX, (int) evY, width, height,
Winson08deff02016-08-05 13:58:31 -0700239 mRv.mSystemInsets, true /* isCurrentTarget */)) {
Winson3e874742016-01-07 10:08:17 -0800240 currentDropTarget = mLastDropTarget;
241 }
242 }
243
244 // Otherwise, find the next target to handle this event
245 if (currentDropTarget == null) {
246 for (DropTarget target : mDropTargets) {
247 if (target.acceptsDrop((int) evX, (int) evY, width, height,
Winson08deff02016-08-05 13:58:31 -0700248 mRv.mSystemInsets, false /* isCurrentTarget */)) {
Winson3e874742016-01-07 10:08:17 -0800249 currentDropTarget = target;
250 break;
251 }
252 }
253 }
254 if (mLastDropTarget != currentDropTarget) {
255 mLastDropTarget = currentDropTarget;
256 EventBus.getDefault().send(new DragDropTargetChangedEvent(mDragTask,
257 currentDropTarget));
258 }
Winson4165d3362015-10-10 14:40:35 -0700259 }
Winson479f7442015-11-25 15:16:27 -0800260 mTaskView.setTranslationX(x);
261 mTaskView.setTranslationY(y);
Winson4165d3362015-10-10 14:40:35 -0700262 }
263 break;
264 }
265 case MotionEvent.ACTION_UP:
266 case MotionEvent.ACTION_CANCEL: {
Matthew Ng9e177f02017-09-21 11:10:32 -0700267 if (mRv.getStackActionButton().isPressed() && isWithinStackActionButton(evX, evY)) {
268 EventBus.getDefault().send(new DismissAllTaskViewsEvent());
269 consumed = true;
270 }
271 cancelStackActionButtonClick();
Winson3e874742016-01-07 10:08:17 -0800272 if (mDragRequested) {
Jorim Jaggif300bad2016-04-25 13:00:27 -0700273 boolean cancelled = action == MotionEvent.ACTION_CANCEL;
274 if (cancelled) {
275 EventBus.getDefault().send(new DragDropTargetChangedEvent(mDragTask, null));
276 }
Winson479f7442015-11-25 15:16:27 -0800277 EventBus.getDefault().send(new DragEndEvent(mDragTask, mTaskView,
Jorim Jaggif300bad2016-04-25 13:00:27 -0700278 !cancelled ? mLastDropTarget : null));
Winson09d8a182015-10-10 14:50:23 -0700279 break;
280 }
Winson131d53a2016-06-14 13:51:59 -0700281 mDeviceId = -1;
Winson4165d3362015-10-10 14:40:35 -0700282 }
283 }
Matthew Ng9e177f02017-09-21 11:10:32 -0700284 return consumed;
Winsonbe7607a2015-10-01 17:24:51 -0700285 }
286}