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