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