TaskTapPointerEventListener.java revision f9d9ce7705475874c82af04eb9b208a7fb556792
1/* 2 * Copyright (C) 2013 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.server.wm; 18 19import android.graphics.Rect; 20import android.graphics.Region; 21import android.view.DisplayInfo; 22import android.view.GestureDetector; 23import android.view.InputDevice; 24import android.view.MotionEvent; 25import android.view.WindowManagerPolicy.PointerEventListener; 26 27import com.android.server.wm.WindowManagerService.H; 28 29import static android.view.PointerIcon.TYPE_NOT_SPECIFIED; 30import static android.view.PointerIcon.TYPE_DEFAULT; 31import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW; 32import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW; 33import static android.view.PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW; 34import static android.view.PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW; 35 36public class TaskTapPointerEventListener implements PointerEventListener { 37 38 final private Region mTouchExcludeRegion = new Region(); 39 private final WindowManagerService mService; 40 private final DisplayContent mDisplayContent; 41 private final Rect mTmpRect = new Rect(); 42 private final Region mNonResizeableRegion = new Region(); 43 private boolean mTwoFingerScrolling; 44 private boolean mInGestureDetection; 45 private GestureDetector mGestureDetector; 46 private int mPointerIconType = TYPE_NOT_SPECIFIED; 47 48 public TaskTapPointerEventListener(WindowManagerService service, 49 DisplayContent displayContent) { 50 mService = service; 51 mDisplayContent = displayContent; 52 } 53 54 // initialize the object, note this must be done outside WindowManagerService 55 // ctor, otherwise it may cause recursion as some code in GestureDetector ctor 56 // depends on WMS being already created. 57 void init() { 58 mGestureDetector = new GestureDetector( 59 mService.mContext, new TwoFingerScrollListener(), mService.mH); 60 } 61 62 @Override 63 public void onPointerEvent(MotionEvent motionEvent) { 64 doGestureDetection(motionEvent); 65 66 final int action = motionEvent.getAction(); 67 switch (action & MotionEvent.ACTION_MASK) { 68 case MotionEvent.ACTION_DOWN: { 69 final int x = (int) motionEvent.getX(); 70 final int y = (int) motionEvent.getY(); 71 72 synchronized (this) { 73 if (!mTouchExcludeRegion.contains(x, y)) { 74 mService.mH.obtainMessage(H.TAP_OUTSIDE_TASK, 75 x, y, mDisplayContent).sendToTarget(); 76 } 77 } 78 break; 79 } 80 81 case MotionEvent.ACTION_MOVE: { 82 if (motionEvent.getPointerCount() != 2) { 83 stopTwoFingerScroll(); 84 } 85 break; 86 } 87 88 case MotionEvent.ACTION_HOVER_MOVE: { 89 final int x = (int) motionEvent.getX(); 90 final int y = (int) motionEvent.getY(); 91 final Task task = mDisplayContent.findTaskForControlPoint(x, y); 92 InputDevice inputDevice = motionEvent.getDevice(); 93 if (task == null || inputDevice == null) { 94 mPointerIconType = TYPE_NOT_SPECIFIED; 95 break; 96 } 97 task.getDimBounds(mTmpRect); 98 if (!mTmpRect.isEmpty() && !mTmpRect.contains(x, y)) { 99 int iconType = TYPE_DEFAULT; 100 if (x < mTmpRect.left) { 101 iconType = 102 (y < mTmpRect.top) ? TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW : 103 (y > mTmpRect.bottom) ? TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW : 104 TYPE_HORIZONTAL_DOUBLE_ARROW; 105 } else if (x > mTmpRect.right) { 106 iconType = 107 (y < mTmpRect.top) ? TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW : 108 (y > mTmpRect.bottom) ? TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW : 109 TYPE_HORIZONTAL_DOUBLE_ARROW; 110 } else if (y < mTmpRect.top || y > mTmpRect.bottom) { 111 iconType = TYPE_VERTICAL_DOUBLE_ARROW; 112 } 113 if (mPointerIconType != iconType) { 114 mPointerIconType = iconType; 115 inputDevice.setPointerType(iconType); 116 } 117 } else { 118 mPointerIconType = TYPE_NOT_SPECIFIED; 119 } 120 } break; 121 122 case MotionEvent.ACTION_HOVER_EXIT: 123 mPointerIconType = TYPE_NOT_SPECIFIED; 124 InputDevice inputDevice = motionEvent.getDevice(); 125 if (inputDevice != null) { 126 inputDevice.setPointerType(TYPE_DEFAULT); 127 } 128 break; 129 130 case MotionEvent.ACTION_UP: 131 case MotionEvent.ACTION_POINTER_UP: { 132 stopTwoFingerScroll(); 133 break; 134 } 135 } 136 } 137 138 private void doGestureDetection(MotionEvent motionEvent) { 139 if (mGestureDetector == null || mNonResizeableRegion.isEmpty()) { 140 return; 141 } 142 final int action = motionEvent.getAction() & MotionEvent.ACTION_MASK; 143 final int x = (int) motionEvent.getX(); 144 final int y = (int) motionEvent.getY(); 145 final boolean isTouchInside = mNonResizeableRegion.contains(x, y); 146 if (mInGestureDetection || action == MotionEvent.ACTION_DOWN && isTouchInside) { 147 // If we receive the following actions, or the pointer goes out of the area 148 // we're interested in, stop detecting and cancel the current detection. 149 mInGestureDetection = isTouchInside 150 && action != MotionEvent.ACTION_UP 151 && action != MotionEvent.ACTION_POINTER_UP 152 && action != MotionEvent.ACTION_CANCEL; 153 if (mInGestureDetection) { 154 mGestureDetector.onTouchEvent(motionEvent); 155 } else { 156 MotionEvent cancelEvent = motionEvent.copy(); 157 cancelEvent.cancel(); 158 mGestureDetector.onTouchEvent(cancelEvent); 159 stopTwoFingerScroll(); 160 } 161 } 162 } 163 164 private void onTwoFingerScroll(MotionEvent e) { 165 final int x = (int)e.getX(0); 166 final int y = (int)e.getY(0); 167 if (!mTwoFingerScrolling) { 168 mTwoFingerScrolling = true; 169 mService.mH.obtainMessage( 170 H.TWO_FINGER_SCROLL_START, x, y, mDisplayContent).sendToTarget(); 171 } 172 } 173 174 private void stopTwoFingerScroll() { 175 if (mTwoFingerScrolling) { 176 mTwoFingerScrolling = false; 177 mService.mH.obtainMessage(H.FINISH_TASK_POSITIONING).sendToTarget(); 178 } 179 } 180 181 private final class TwoFingerScrollListener extends GestureDetector.SimpleOnGestureListener { 182 @Override 183 public boolean onScroll(MotionEvent e1, MotionEvent e2, 184 float distanceX, float distanceY) { 185 if (e2.getPointerCount() == 2) { 186 onTwoFingerScroll(e2); 187 return true; 188 } 189 stopTwoFingerScroll(); 190 return false; 191 } 192 } 193 194 void setTouchExcludeRegion(Region newRegion, Region nonResizeableRegion) { 195 synchronized (this) { 196 mTouchExcludeRegion.set(newRegion); 197 mNonResizeableRegion.set(nonResizeableRegion); 198 } 199 } 200} 201