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