blob: af109d4195cf250467bef185155dae6661029bbd [file] [log] [blame]
Craig Mautner037aa8d2013-06-07 10:35:44 -07001/*
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
Jun Mukai41aab002015-10-01 23:18:47 -070019import android.graphics.Rect;
Craig Mautner037aa8d2013-06-07 10:35:44 -070020import android.graphics.Region;
21import android.view.DisplayInfo;
Chong Zhangb15758a2015-11-17 12:12:03 -080022import android.view.GestureDetector;
Craig Mautner037aa8d2013-06-07 10:35:44 -070023import android.view.MotionEvent;
24import android.view.WindowManagerPolicy.PointerEventListener;
25
26import com.android.server.wm.WindowManagerService.H;
27
Jun Mukai41aab002015-10-01 23:18:47 -070028import static android.view.PointerIcon.STYLE_NOT_SPECIFIED;
29import static android.view.PointerIcon.STYLE_DEFAULT;
30import static android.view.PointerIcon.STYLE_HORIZONTAL_DOUBLE_ARROW;
31import static android.view.PointerIcon.STYLE_VERTICAL_DOUBLE_ARROW;
32import static android.view.PointerIcon.STYLE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW;
33import static android.view.PointerIcon.STYLE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW;
34
Wale Ogunwalee4a0c572015-06-30 08:40:31 -070035public class TaskTapPointerEventListener implements PointerEventListener {
Craig Mautner037aa8d2013-06-07 10:35:44 -070036 private static final int TAP_TIMEOUT_MSEC = 300;
37 private static final float TAP_MOTION_SLOP_INCHES = 0.125f;
38
39 private final int mMotionSlop;
40 private float mDownX;
41 private float mDownY;
42 private int mPointerId;
tingna_sung33d8e732014-10-25 21:32:40 +080043 final private Region mTouchExcludeRegion = new Region();
Craig Mautner037aa8d2013-06-07 10:35:44 -070044 private final WindowManagerService mService;
45 private final DisplayContent mDisplayContent;
Jun Mukai41aab002015-10-01 23:18:47 -070046 private final Rect mTmpRect = new Rect();
Chong Zhangb15758a2015-11-17 12:12:03 -080047 private final Region mNonResizeableRegion = new Region();
48 private boolean mTwoFingerScrolling;
49 private boolean mInGestureDetection;
50 private GestureDetector mGestureDetector;
Jun Mukai41aab002015-10-01 23:18:47 -070051 private int mPointerIconShape = STYLE_NOT_SPECIFIED;
Craig Mautner037aa8d2013-06-07 10:35:44 -070052
Wale Ogunwalee4a0c572015-06-30 08:40:31 -070053 public TaskTapPointerEventListener(WindowManagerService service,
Craig Mautner037aa8d2013-06-07 10:35:44 -070054 DisplayContent displayContent) {
55 mService = service;
56 mDisplayContent = displayContent;
Craig Mautner037aa8d2013-06-07 10:35:44 -070057 DisplayInfo info = displayContent.getDisplayInfo();
58 mMotionSlop = (int)(info.logicalDensityDpi * TAP_MOTION_SLOP_INCHES);
59 }
60
Chong Zhangb15758a2015-11-17 12:12:03 -080061 // initialize the object, note this must be done outside WindowManagerService
62 // ctor, otherwise it may cause recursion as some code in GestureDetector ctor
63 // depends on WMS being already created.
64 void init() {
65 mGestureDetector = new GestureDetector(
66 mService.mContext, new TwoFingerScrollListener(), mService.mH);
67 }
68
Craig Mautner037aa8d2013-06-07 10:35:44 -070069 @Override
70 public void onPointerEvent(MotionEvent motionEvent) {
Chong Zhangb15758a2015-11-17 12:12:03 -080071 doGestureDetection(motionEvent);
72
Craig Mautner037aa8d2013-06-07 10:35:44 -070073 final int action = motionEvent.getAction();
74 switch (action & MotionEvent.ACTION_MASK) {
Chong Zhang8e89b312015-09-09 15:09:30 -070075 case MotionEvent.ACTION_DOWN: {
Craig Mautner037aa8d2013-06-07 10:35:44 -070076 mPointerId = motionEvent.getPointerId(0);
77 mDownX = motionEvent.getX();
78 mDownY = motionEvent.getY();
Chong Zhang8e89b312015-09-09 15:09:30 -070079
80 final int x = (int) mDownX;
81 final int y = (int) mDownY;
82 synchronized (this) {
83 if (!mTouchExcludeRegion.contains(x, y)) {
84 mService.mH.obtainMessage(H.TAP_DOWN_OUTSIDE_TASK, x, y,
85 mDisplayContent).sendToTarget();
86 }
87 }
Craig Mautner037aa8d2013-06-07 10:35:44 -070088 break;
Chong Zhang8e89b312015-09-09 15:09:30 -070089 }
90
91 case MotionEvent.ACTION_MOVE: {
Craig Mautner037aa8d2013-06-07 10:35:44 -070092 if (mPointerId >= 0) {
93 int index = motionEvent.findPointerIndex(mPointerId);
94 if ((motionEvent.getEventTime() - motionEvent.getDownTime()) > TAP_TIMEOUT_MSEC
tingna_sung4f65ca02014-10-25 21:52:47 +080095 || index < 0
tingna_sungf2ad1a442014-10-21 17:09:57 +080096 || Math.abs(motionEvent.getX(index) - mDownX) > mMotionSlop
97 || Math.abs(motionEvent.getY(index) - mDownY) > mMotionSlop) {
Craig Mautner037aa8d2013-06-07 10:35:44 -070098 mPointerId = -1;
99 }
100 }
Chong Zhangb15758a2015-11-17 12:12:03 -0800101 if (motionEvent.getPointerCount() != 2) {
102 stopTwoFingerScroll();
103 }
Craig Mautner037aa8d2013-06-07 10:35:44 -0700104 break;
Chong Zhang8e89b312015-09-09 15:09:30 -0700105 }
106
Jun Mukai41aab002015-10-01 23:18:47 -0700107 case MotionEvent.ACTION_HOVER_MOVE: {
108 final int x = (int) motionEvent.getX();
109 final int y = (int) motionEvent.getY();
Chong Zhangd8ceb852015-11-11 14:53:41 -0800110 final Task task = mDisplayContent.findTaskForControlPoint(x, y);
111 if (task == null) {
Jun Mukai02812ba2015-12-04 11:11:17 -0800112 mPointerIconShape = STYLE_NOT_SPECIFIED;
Jun Mukai41aab002015-10-01 23:18:47 -0700113 break;
114 }
Chong Zhangd8ceb852015-11-11 14:53:41 -0800115 task.getDimBounds(mTmpRect);
Jun Mukai41aab002015-10-01 23:18:47 -0700116 if (!mTmpRect.isEmpty() && !mTmpRect.contains(x, y)) {
117 int iconShape = STYLE_DEFAULT;
118 if (x < mTmpRect.left) {
119 iconShape =
120 (y < mTmpRect.top) ? STYLE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW :
121 (y > mTmpRect.bottom) ? STYLE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW :
122 STYLE_HORIZONTAL_DOUBLE_ARROW;
123 } else if (x > mTmpRect.right) {
124 iconShape =
125 (y < mTmpRect.top) ? STYLE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW :
126 (y > mTmpRect.bottom) ? STYLE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW :
127 STYLE_HORIZONTAL_DOUBLE_ARROW;
128 } else if (y < mTmpRect.top || y > mTmpRect.bottom) {
129 iconShape = STYLE_VERTICAL_DOUBLE_ARROW;
130 }
131 if (mPointerIconShape != iconShape) {
132 mPointerIconShape = iconShape;
133 motionEvent.getDevice().setPointerShape(iconShape);
134 }
135 } else {
136 mPointerIconShape = STYLE_NOT_SPECIFIED;
137 }
138 } break;
139
140 case MotionEvent.ACTION_HOVER_EXIT:
Jun Mukai02812ba2015-12-04 11:11:17 -0800141 mPointerIconShape = STYLE_NOT_SPECIFIED;
Jun Mukai41aab002015-10-01 23:18:47 -0700142 motionEvent.getDevice().setPointerShape(STYLE_DEFAULT);
143 break;
144
Craig Mautner037aa8d2013-06-07 10:35:44 -0700145 case MotionEvent.ACTION_UP:
146 case MotionEvent.ACTION_POINTER_UP: {
147 int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
148 >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
149 // Extract the index of the pointer that left the touch sensor
150 if (mPointerId == motionEvent.getPointerId(index)) {
151 final int x = (int)motionEvent.getX(index);
152 final int y = (int)motionEvent.getY(index);
tingna_sung33d8e732014-10-25 21:32:40 +0800153 synchronized(this) {
154 if ((motionEvent.getEventTime() - motionEvent.getDownTime())
155 < TAP_TIMEOUT_MSEC
156 && Math.abs(x - mDownX) < mMotionSlop
157 && Math.abs(y - mDownY) < mMotionSlop
158 && !mTouchExcludeRegion.contains(x, y)) {
Wale Ogunwalee4a0c572015-06-30 08:40:31 -0700159 mService.mH.obtainMessage(H.TAP_OUTSIDE_TASK, x, y,
tingna_sung33d8e732014-10-25 21:32:40 +0800160 mDisplayContent).sendToTarget();
161 }
Craig Mautner037aa8d2013-06-07 10:35:44 -0700162 }
163 mPointerId = -1;
164 }
Chong Zhangb15758a2015-11-17 12:12:03 -0800165 stopTwoFingerScroll();
Craig Mautner037aa8d2013-06-07 10:35:44 -0700166 break;
167 }
168 }
169 }
tingna_sung33d8e732014-10-25 21:32:40 +0800170
Chong Zhangb15758a2015-11-17 12:12:03 -0800171 private void doGestureDetection(MotionEvent motionEvent) {
172 if (mGestureDetector == null || mNonResizeableRegion.isEmpty()) {
173 return;
174 }
175 final int action = motionEvent.getAction() & MotionEvent.ACTION_MASK;
176 final int x = (int) motionEvent.getX();
177 final int y = (int) motionEvent.getY();
178 final boolean isTouchInside = mNonResizeableRegion.contains(x, y);
179 if (mInGestureDetection || action == MotionEvent.ACTION_DOWN && isTouchInside) {
180 // If we receive the following actions, or the pointer goes out of the area
181 // we're interested in, stop detecting and cancel the current detection.
182 mInGestureDetection = isTouchInside
183 && action != MotionEvent.ACTION_UP
184 && action != MotionEvent.ACTION_POINTER_UP
185 && action != MotionEvent.ACTION_CANCEL;
186 if (mInGestureDetection) {
187 mGestureDetector.onTouchEvent(motionEvent);
188 } else {
189 MotionEvent cancelEvent = motionEvent.copy();
190 cancelEvent.cancel();
191 mGestureDetector.onTouchEvent(cancelEvent);
192 stopTwoFingerScroll();
193 }
194 }
195 }
196
197 private void onTwoFingerScroll(MotionEvent e) {
198 final int x = (int)e.getX(0);
199 final int y = (int)e.getY(0);
200 if (!mTwoFingerScrolling) {
201 mTwoFingerScrolling = true;
202 mService.mH.obtainMessage(
203 H.TWO_FINGER_SCROLL_START, x, y, mDisplayContent).sendToTarget();
204 }
205 }
206
207 private void stopTwoFingerScroll() {
208 if (mTwoFingerScrolling) {
209 mTwoFingerScrolling = false;
210 mService.mH.obtainMessage(H.FINISH_TASK_POSITIONING).sendToTarget();
211 }
212 }
213
214 private final class TwoFingerScrollListener extends GestureDetector.SimpleOnGestureListener {
215 @Override
216 public boolean onScroll(MotionEvent e1, MotionEvent e2,
217 float distanceX, float distanceY) {
218 if (e2.getPointerCount() == 2) {
219 onTwoFingerScroll(e2);
220 return true;
221 }
222 stopTwoFingerScroll();
223 return false;
224 }
225 }
226
227 void setTouchExcludeRegion(Region newRegion, Region nonResizeableRegion) {
tingna_sung33d8e732014-10-25 21:32:40 +0800228 synchronized (this) {
229 mTouchExcludeRegion.set(newRegion);
Chong Zhangb15758a2015-11-17 12:12:03 -0800230 mNonResizeableRegion.set(nonResizeableRegion);
tingna_sung33d8e732014-10-25 21:32:40 +0800231 }
232 }
Craig Mautner037aa8d2013-06-07 10:35:44 -0700233}