• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.systemui.recents.views;
18 
19 import android.app.ActivityManager;
20 import android.content.res.Configuration;
21 import android.graphics.Point;
22 import android.graphics.Rect;
23 import android.view.MotionEvent;
24 import android.view.ViewConfiguration;
25 import android.view.ViewDebug;
26 
27 import com.android.internal.policy.DividerSnapAlgorithm;
28 import com.android.systemui.recents.Recents;
29 import com.android.systemui.recents.RecentsConfiguration;
30 import com.android.systemui.recents.events.EventBus;
31 import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
32 import com.android.systemui.recents.events.ui.HideIncompatibleAppOverlayEvent;
33 import com.android.systemui.recents.events.ui.ShowIncompatibleAppOverlayEvent;
34 import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
35 import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
36 import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
37 import com.android.systemui.recents.events.ui.dragndrop.DragStartInitializeDropTargetsEvent;
38 import com.android.systemui.recents.misc.SystemServicesProxy;
39 import com.android.systemui.recents.model.Task;
40 import com.android.systemui.recents.model.TaskStack;
41 
42 import java.util.ArrayList;
43 
44 /**
45  * Represents the dock regions for each orientation.
46  */
47 class DockRegion {
48     public static TaskStack.DockState[] PHONE_LANDSCAPE = {
49             // We only allow docking to the left in landscape for now on small devices
50             TaskStack.DockState.LEFT
51     };
52     public static TaskStack.DockState[] PHONE_PORTRAIT = {
53             // We only allow docking to the top for now on small devices
54             TaskStack.DockState.TOP
55     };
56     public static TaskStack.DockState[] TABLET_LANDSCAPE = {
57             TaskStack.DockState.LEFT,
58             TaskStack.DockState.RIGHT
59     };
60     public static TaskStack.DockState[] TABLET_PORTRAIT = PHONE_PORTRAIT;
61 }
62 
63 /**
64  * Handles touch events for a RecentsView.
65  */
66 public class RecentsViewTouchHandler {
67 
68     private RecentsView mRv;
69 
70     @ViewDebug.ExportedProperty(deepExport=true, prefix="drag_task")
71     private Task mDragTask;
72     @ViewDebug.ExportedProperty(deepExport=true, prefix="drag_task_view_")
73     private TaskView mTaskView;
74 
75     @ViewDebug.ExportedProperty(category="recents")
76     private Point mTaskViewOffset = new Point();
77     @ViewDebug.ExportedProperty(category="recents")
78     private Point mDownPos = new Point();
79     @ViewDebug.ExportedProperty(category="recents")
80     private boolean mDragRequested;
81     @ViewDebug.ExportedProperty(category="recents")
82     private boolean mIsDragging;
83     private float mDragSlop;
84 
85     private DropTarget mLastDropTarget;
86     private DividerSnapAlgorithm mDividerSnapAlgorithm;
87     private ArrayList<DropTarget> mDropTargets = new ArrayList<>();
88     private ArrayList<TaskStack.DockState> mVisibleDockStates = new ArrayList<>();
89 
RecentsViewTouchHandler(RecentsView rv)90     public RecentsViewTouchHandler(RecentsView rv) {
91         mRv = rv;
92         mDragSlop = ViewConfiguration.get(rv.getContext()).getScaledTouchSlop();
93         updateSnapAlgorithm();
94     }
95 
updateSnapAlgorithm()96     private void updateSnapAlgorithm() {
97         Rect insets = new Rect();
98         SystemServicesProxy.getInstance(mRv.getContext()).getStableInsets(insets);
99         mDividerSnapAlgorithm = DividerSnapAlgorithm.create(mRv.getContext(), insets);
100     }
101 
102     /**
103      * Registers a new drop target for the current drag only.
104      */
registerDropTargetForCurrentDrag(DropTarget target)105     public void registerDropTargetForCurrentDrag(DropTarget target) {
106         mDropTargets.add(target);
107     }
108 
109     /**
110      * Returns the preferred dock states for the current orientation.
111      */
getDockStatesForCurrentOrientation()112     public TaskStack.DockState[] getDockStatesForCurrentOrientation() {
113         boolean isLandscape = mRv.getResources().getConfiguration().orientation ==
114                 Configuration.ORIENTATION_LANDSCAPE;
115         RecentsConfiguration config = Recents.getConfiguration();
116         if (config.isLargeScreen) {
117             return isLandscape ? DockRegion.TABLET_LANDSCAPE : DockRegion.TABLET_PORTRAIT;
118         } else {
119             return isLandscape ? DockRegion.PHONE_LANDSCAPE : DockRegion.PHONE_PORTRAIT;
120         }
121     }
122 
123     /**
124      * Returns the set of visible dock states for this current drag.
125      */
getVisibleDockStates()126     public ArrayList<TaskStack.DockState> getVisibleDockStates() {
127         return mVisibleDockStates;
128     }
129 
130     /** Touch preprocessing for handling below */
onInterceptTouchEvent(MotionEvent ev)131     public boolean onInterceptTouchEvent(MotionEvent ev) {
132         handleTouchEvent(ev);
133         return mDragRequested;
134     }
135 
136     /** Handles touch events once we have intercepted them */
onTouchEvent(MotionEvent ev)137     public boolean onTouchEvent(MotionEvent ev) {
138         handleTouchEvent(ev);
139         return mDragRequested;
140     }
141 
142     /**** Events ****/
143 
onBusEvent(DragStartEvent event)144     public final void onBusEvent(DragStartEvent event) {
145         SystemServicesProxy ssp = Recents.getSystemServices();
146         mRv.getParent().requestDisallowInterceptTouchEvent(true);
147         mDragRequested = true;
148         // We defer starting the actual drag handling until the user moves past the drag slop
149         mIsDragging = false;
150         mDragTask = event.task;
151         mTaskView = event.taskView;
152         mDropTargets.clear();
153 
154         int[] recentsViewLocation = new int[2];
155         mRv.getLocationInWindow(recentsViewLocation);
156         mTaskViewOffset.set(mTaskView.getLeft() - recentsViewLocation[0] + event.tlOffset.x,
157                 mTaskView.getTop() - recentsViewLocation[1] + event.tlOffset.y);
158         float x = mDownPos.x - mTaskViewOffset.x;
159         float y = mDownPos.y - mTaskViewOffset.y;
160         mTaskView.setTranslationX(x);
161         mTaskView.setTranslationY(y);
162 
163         mVisibleDockStates.clear();
164         if (ActivityManager.supportsMultiWindow() && !ssp.hasDockedTask()
165                 && mDividerSnapAlgorithm.isSplitScreenFeasible()) {
166             Recents.logDockAttempt(mRv.getContext(), event.task.getTopComponent(),
167                     event.task.resizeMode);
168             if (!event.task.isDockable) {
169                 EventBus.getDefault().send(new ShowIncompatibleAppOverlayEvent());
170             } else {
171                 // Add the dock state drop targets (these take priority)
172                 TaskStack.DockState[] dockStates = getDockStatesForCurrentOrientation();
173                 for (TaskStack.DockState dockState : dockStates) {
174                     registerDropTargetForCurrentDrag(dockState);
175                     dockState.update(mRv.getContext());
176                     mVisibleDockStates.add(dockState);
177                 }
178             }
179         }
180 
181         // Request other drop targets to register themselves
182         EventBus.getDefault().send(new DragStartInitializeDropTargetsEvent(event.task,
183                 event.taskView, this));
184     }
185 
onBusEvent(DragEndEvent event)186     public final void onBusEvent(DragEndEvent event) {
187         if (!mDragTask.isDockable) {
188             EventBus.getDefault().send(new HideIncompatibleAppOverlayEvent());
189         }
190         mDragRequested = false;
191         mDragTask = null;
192         mTaskView = null;
193         mLastDropTarget = null;
194     }
195 
onBusEvent(ConfigurationChangedEvent event)196     public final void onBusEvent(ConfigurationChangedEvent event) {
197         if (event.fromDisplayDensityChange || event.fromDeviceOrientationChange) {
198             updateSnapAlgorithm();
199         }
200     }
201 
202     /**
203      * Handles dragging touch events
204      */
handleTouchEvent(MotionEvent ev)205     private void handleTouchEvent(MotionEvent ev) {
206         int action = ev.getActionMasked();
207         switch (action) {
208             case MotionEvent.ACTION_DOWN:
209                 mDownPos.set((int) ev.getX(), (int) ev.getY());
210                 break;
211             case MotionEvent.ACTION_MOVE: {
212                 float evX = ev.getX();
213                 float evY = ev.getY();
214                 float x = evX - mTaskViewOffset.x;
215                 float y = evY - mTaskViewOffset.y;
216 
217                 if (mDragRequested) {
218                     if (!mIsDragging) {
219                         mIsDragging = Math.hypot(evX - mDownPos.x, evY - mDownPos.y) > mDragSlop;
220                     }
221                     if (mIsDragging) {
222                         int width = mRv.getMeasuredWidth();
223                         int height = mRv.getMeasuredHeight();
224 
225                         DropTarget currentDropTarget = null;
226 
227                         // Give priority to the current drop target to retain the touch handling
228                         if (mLastDropTarget != null) {
229                             if (mLastDropTarget.acceptsDrop((int) evX, (int) evY, width, height,
230                                     mRv.mSystemInsets, true /* isCurrentTarget */)) {
231                                 currentDropTarget = mLastDropTarget;
232                             }
233                         }
234 
235                         // Otherwise, find the next target to handle this event
236                         if (currentDropTarget == null) {
237                             for (DropTarget target : mDropTargets) {
238                                 if (target.acceptsDrop((int) evX, (int) evY, width, height,
239                                         mRv.mSystemInsets, false /* isCurrentTarget */)) {
240                                     currentDropTarget = target;
241                                     break;
242                                 }
243                             }
244                         }
245                         if (mLastDropTarget != currentDropTarget) {
246                             mLastDropTarget = currentDropTarget;
247                             EventBus.getDefault().send(new DragDropTargetChangedEvent(mDragTask,
248                                     currentDropTarget));
249                         }
250 
251                     }
252 
253                     mTaskView.setTranslationX(x);
254                     mTaskView.setTranslationY(y);
255                 }
256                 break;
257             }
258             case MotionEvent.ACTION_UP:
259             case MotionEvent.ACTION_CANCEL: {
260                 if (mDragRequested) {
261                     boolean cancelled = action == MotionEvent.ACTION_CANCEL;
262                     if (cancelled) {
263                         EventBus.getDefault().send(new DragDropTargetChangedEvent(mDragTask, null));
264                     }
265                     EventBus.getDefault().send(new DragEndEvent(mDragTask, mTaskView,
266                             !cancelled ? mLastDropTarget : null));
267                     break;
268                 }
269             }
270         }
271     }
272 }
273