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