blob: 3dc512f4422465566f291482a080824550be9431 [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;
Jun Mukaicaca1112015-12-22 14:20:24 -080023import android.view.InputDevice;
Craig Mautner037aa8d2013-06-07 10:35:44 -070024import android.view.MotionEvent;
25import android.view.WindowManagerPolicy.PointerEventListener;
26
27import com.android.server.wm.WindowManagerService.H;
28
Jun Mukai41aab002015-10-01 23:18:47 -070029import static android.view.PointerIcon.STYLE_NOT_SPECIFIED;
30import static android.view.PointerIcon.STYLE_DEFAULT;
31import static android.view.PointerIcon.STYLE_HORIZONTAL_DOUBLE_ARROW;
32import static android.view.PointerIcon.STYLE_VERTICAL_DOUBLE_ARROW;
33import static android.view.PointerIcon.STYLE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW;
34import static android.view.PointerIcon.STYLE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW;
35
Wale Ogunwalee4a0c572015-06-30 08:40:31 -070036public class TaskTapPointerEventListener implements PointerEventListener {
Craig Mautner037aa8d2013-06-07 10:35:44 -070037
tingna_sung33d8e732014-10-25 21:32:40 +080038 final private Region mTouchExcludeRegion = new Region();
Craig Mautner037aa8d2013-06-07 10:35:44 -070039 private final WindowManagerService mService;
40 private final DisplayContent mDisplayContent;
Jun Mukai41aab002015-10-01 23:18:47 -070041 private final Rect mTmpRect = new Rect();
Chong Zhangb15758a2015-11-17 12:12:03 -080042 private final Region mNonResizeableRegion = new Region();
43 private boolean mTwoFingerScrolling;
44 private boolean mInGestureDetection;
45 private GestureDetector mGestureDetector;
Jun Mukai41aab002015-10-01 23:18:47 -070046 private int mPointerIconShape = STYLE_NOT_SPECIFIED;
Craig Mautner037aa8d2013-06-07 10:35:44 -070047
Wale Ogunwalee4a0c572015-06-30 08:40:31 -070048 public TaskTapPointerEventListener(WindowManagerService service,
Craig Mautner037aa8d2013-06-07 10:35:44 -070049 DisplayContent displayContent) {
50 mService = service;
51 mDisplayContent = displayContent;
Craig Mautner037aa8d2013-06-07 10:35:44 -070052 }
53
Chong Zhangb15758a2015-11-17 12:12:03 -080054 // 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
Craig Mautner037aa8d2013-06-07 10:35:44 -070062 @Override
63 public void onPointerEvent(MotionEvent motionEvent) {
Chong Zhangb15758a2015-11-17 12:12:03 -080064 doGestureDetection(motionEvent);
65
Craig Mautner037aa8d2013-06-07 10:35:44 -070066 final int action = motionEvent.getAction();
67 switch (action & MotionEvent.ACTION_MASK) {
Chong Zhang8e89b312015-09-09 15:09:30 -070068 case MotionEvent.ACTION_DOWN: {
Chong Zhang9817dd02016-01-08 13:39:29 -080069 final int x = (int) motionEvent.getX();
70 final int y = (int) motionEvent.getY();
Chong Zhang8e89b312015-09-09 15:09:30 -070071
Chong Zhang8e89b312015-09-09 15:09:30 -070072 synchronized (this) {
73 if (!mTouchExcludeRegion.contains(x, y)) {
Chong Zhang9817dd02016-01-08 13:39:29 -080074 mService.mH.obtainMessage(H.TAP_OUTSIDE_TASK,
75 x, y, mDisplayContent).sendToTarget();
Chong Zhang8e89b312015-09-09 15:09:30 -070076 }
77 }
Craig Mautner037aa8d2013-06-07 10:35:44 -070078 break;
Chong Zhang8e89b312015-09-09 15:09:30 -070079 }
80
81 case MotionEvent.ACTION_MOVE: {
Chong Zhangb15758a2015-11-17 12:12:03 -080082 if (motionEvent.getPointerCount() != 2) {
83 stopTwoFingerScroll();
84 }
Craig Mautner037aa8d2013-06-07 10:35:44 -070085 break;
Chong Zhang8e89b312015-09-09 15:09:30 -070086 }
87
Jun Mukai41aab002015-10-01 23:18:47 -070088 case MotionEvent.ACTION_HOVER_MOVE: {
89 final int x = (int) motionEvent.getX();
90 final int y = (int) motionEvent.getY();
Chong Zhangd8ceb852015-11-11 14:53:41 -080091 final Task task = mDisplayContent.findTaskForControlPoint(x, y);
Jun Mukaicaca1112015-12-22 14:20:24 -080092 InputDevice inputDevice = motionEvent.getDevice();
93 if (task == null || inputDevice == null) {
Jun Mukai02812ba2015-12-04 11:11:17 -080094 mPointerIconShape = STYLE_NOT_SPECIFIED;
Jun Mukai41aab002015-10-01 23:18:47 -070095 break;
96 }
Chong Zhangd8ceb852015-11-11 14:53:41 -080097 task.getDimBounds(mTmpRect);
Jun Mukai41aab002015-10-01 23:18:47 -070098 if (!mTmpRect.isEmpty() && !mTmpRect.contains(x, y)) {
99 int iconShape = STYLE_DEFAULT;
100 if (x < mTmpRect.left) {
101 iconShape =
102 (y < mTmpRect.top) ? STYLE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW :
103 (y > mTmpRect.bottom) ? STYLE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW :
104 STYLE_HORIZONTAL_DOUBLE_ARROW;
105 } else if (x > mTmpRect.right) {
106 iconShape =
107 (y < mTmpRect.top) ? STYLE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW :
108 (y > mTmpRect.bottom) ? STYLE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW :
109 STYLE_HORIZONTAL_DOUBLE_ARROW;
110 } else if (y < mTmpRect.top || y > mTmpRect.bottom) {
111 iconShape = STYLE_VERTICAL_DOUBLE_ARROW;
112 }
113 if (mPointerIconShape != iconShape) {
114 mPointerIconShape = iconShape;
Jun Mukaicaca1112015-12-22 14:20:24 -0800115 inputDevice.setPointerShape(iconShape);
Jun Mukai41aab002015-10-01 23:18:47 -0700116 }
117 } else {
118 mPointerIconShape = STYLE_NOT_SPECIFIED;
119 }
120 } break;
121
122 case MotionEvent.ACTION_HOVER_EXIT:
Jun Mukai02812ba2015-12-04 11:11:17 -0800123 mPointerIconShape = STYLE_NOT_SPECIFIED;
Jun Mukaicaca1112015-12-22 14:20:24 -0800124 InputDevice inputDevice = motionEvent.getDevice();
125 if (inputDevice != null) {
126 inputDevice.setPointerShape(STYLE_DEFAULT);
127 }
Jun Mukai41aab002015-10-01 23:18:47 -0700128 break;
129
Craig Mautner037aa8d2013-06-07 10:35:44 -0700130 case MotionEvent.ACTION_UP:
131 case MotionEvent.ACTION_POINTER_UP: {
Chong Zhangb15758a2015-11-17 12:12:03 -0800132 stopTwoFingerScroll();
Craig Mautner037aa8d2013-06-07 10:35:44 -0700133 break;
134 }
135 }
136 }
tingna_sung33d8e732014-10-25 21:32:40 +0800137
Chong Zhangb15758a2015-11-17 12:12:03 -0800138 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) {
tingna_sung33d8e732014-10-25 21:32:40 +0800195 synchronized (this) {
196 mTouchExcludeRegion.set(newRegion);
Chong Zhangb15758a2015-11-17 12:12:03 -0800197 mNonResizeableRegion.set(nonResizeableRegion);
tingna_sung33d8e732014-10-25 21:32:40 +0800198 }
199 }
Craig Mautner037aa8d2013-06-07 10:35:44 -0700200}