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