1037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner/*
2037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner * Copyright (C) 2013 The Android Open Source Project
3037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner *
4037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner * Licensed under the Apache License, Version 2.0 (the "License");
5037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner * you may not use this file except in compliance with the License.
6037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner * You may obtain a copy of the License at
7037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner *
8037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner *      http://www.apache.org/licenses/LICENSE-2.0
9037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner *
10037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner * Unless required by applicable law or agreed to in writing, software
11037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner * distributed under the License is distributed on an "AS IS" BASIS,
12037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner * See the License for the specific language governing permissions and
14037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner * limitations under the License.
15037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner */
16037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner
17037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautnerpackage com.android.server.wm;
18037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner
1941aab00c3e762c6b648483ee7b45b162e4da7f7fJun Mukaiimport android.graphics.Rect;
20037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautnerimport android.graphics.Region;
21037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautnerimport android.view.DisplayInfo;
22b15758ab7a6481717d0d29612e870d7241061c31Chong Zhangimport android.view.GestureDetector;
23caca1114b2cf78551b2008156986a644d15317efJun Mukaiimport android.view.InputDevice;
24037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautnerimport android.view.MotionEvent;
25037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautnerimport android.view.WindowManagerPolicy.PointerEventListener;
26037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner
27037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautnerimport com.android.server.wm.WindowManagerService.H;
28037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner
29f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wrightimport static android.view.PointerIcon.TYPE_NOT_SPECIFIED;
30f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wrightimport static android.view.PointerIcon.TYPE_DEFAULT;
31f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wrightimport static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
32f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wrightimport static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
33f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wrightimport static android.view.PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW;
34f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wrightimport static android.view.PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW;
3541aab00c3e762c6b648483ee7b45b162e4da7f7fJun Mukai
36e4a0c5722b1d8db95dfc842d716452dbbf02c86dWale Ogunwalepublic class TaskTapPointerEventListener implements PointerEventListener {
37037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner
3833d8e739206b7a5ed99cf9bf73d63f9b6c0d2a4etingna_sung    final private Region mTouchExcludeRegion = new Region();
39037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner    private final WindowManagerService mService;
40037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner    private final DisplayContent mDisplayContent;
4141aab00c3e762c6b648483ee7b45b162e4da7f7fJun Mukai    private final Rect mTmpRect = new Rect();
42b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang    private final Region mNonResizeableRegion = new Region();
43b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang    private boolean mTwoFingerScrolling;
44b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang    private boolean mInGestureDetection;
45b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang    private GestureDetector mGestureDetector;
46f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wright    private int mPointerIconType = TYPE_NOT_SPECIFIED;
47037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner
48e4a0c5722b1d8db95dfc842d716452dbbf02c86dWale Ogunwale    public TaskTapPointerEventListener(WindowManagerService service,
49037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner            DisplayContent displayContent) {
50037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner        mService = service;
51037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner        mDisplayContent = displayContent;
52037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner    }
53037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner
54b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang    // initialize the object, note this must be done outside WindowManagerService
55b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang    // ctor, otherwise it may cause recursion as some code in GestureDetector ctor
56b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang    // depends on WMS being already created.
57b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang    void init() {
58b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang        mGestureDetector = new GestureDetector(
59b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang                mService.mContext, new TwoFingerScrollListener(), mService.mH);
60b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang    }
61b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang
62037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner    @Override
63037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner    public void onPointerEvent(MotionEvent motionEvent) {
64b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang        doGestureDetection(motionEvent);
65b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang
66037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner        final int action = motionEvent.getAction();
67037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner        switch (action & MotionEvent.ACTION_MASK) {
688e89b31a62fb9ec5ad33908c5e8e9c7ab2fd949fChong Zhang            case MotionEvent.ACTION_DOWN: {
699817dd098dcb4e37e1bec8b1e44ddb25a89cf8ecChong Zhang                final int x = (int) motionEvent.getX();
709817dd098dcb4e37e1bec8b1e44ddb25a89cf8ecChong Zhang                final int y = (int) motionEvent.getY();
718e89b31a62fb9ec5ad33908c5e8e9c7ab2fd949fChong Zhang
728e89b31a62fb9ec5ad33908c5e8e9c7ab2fd949fChong Zhang                synchronized (this) {
738e89b31a62fb9ec5ad33908c5e8e9c7ab2fd949fChong Zhang                    if (!mTouchExcludeRegion.contains(x, y)) {
749817dd098dcb4e37e1bec8b1e44ddb25a89cf8ecChong Zhang                        mService.mH.obtainMessage(H.TAP_OUTSIDE_TASK,
759817dd098dcb4e37e1bec8b1e44ddb25a89cf8ecChong Zhang                                x, y, mDisplayContent).sendToTarget();
768e89b31a62fb9ec5ad33908c5e8e9c7ab2fd949fChong Zhang                    }
778e89b31a62fb9ec5ad33908c5e8e9c7ab2fd949fChong Zhang                }
78037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner                break;
798e89b31a62fb9ec5ad33908c5e8e9c7ab2fd949fChong Zhang            }
808e89b31a62fb9ec5ad33908c5e8e9c7ab2fd949fChong Zhang
818e89b31a62fb9ec5ad33908c5e8e9c7ab2fd949fChong Zhang            case MotionEvent.ACTION_MOVE: {
82b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang                if (motionEvent.getPointerCount() != 2) {
83b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang                    stopTwoFingerScroll();
84b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang                }
85037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner                break;
868e89b31a62fb9ec5ad33908c5e8e9c7ab2fd949fChong Zhang            }
878e89b31a62fb9ec5ad33908c5e8e9c7ab2fd949fChong Zhang
8841aab00c3e762c6b648483ee7b45b162e4da7f7fJun Mukai            case MotionEvent.ACTION_HOVER_MOVE: {
8941aab00c3e762c6b648483ee7b45b162e4da7f7fJun Mukai                final int x = (int) motionEvent.getX();
9041aab00c3e762c6b648483ee7b45b162e4da7f7fJun Mukai                final int y = (int) motionEvent.getY();
91d8ceb85512f8dc2dac6ef07fc72f89a75095e3d7Chong Zhang                final Task task = mDisplayContent.findTaskForControlPoint(x, y);
92caca1114b2cf78551b2008156986a644d15317efJun Mukai                InputDevice inputDevice = motionEvent.getDevice();
93caca1114b2cf78551b2008156986a644d15317efJun Mukai                if (task == null || inputDevice == null) {
94f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wright                    mPointerIconType = TYPE_NOT_SPECIFIED;
9541aab00c3e762c6b648483ee7b45b162e4da7f7fJun Mukai                    break;
9641aab00c3e762c6b648483ee7b45b162e4da7f7fJun Mukai                }
97d8ceb85512f8dc2dac6ef07fc72f89a75095e3d7Chong Zhang                task.getDimBounds(mTmpRect);
9841aab00c3e762c6b648483ee7b45b162e4da7f7fJun Mukai                if (!mTmpRect.isEmpty() && !mTmpRect.contains(x, y)) {
99f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wright                    int iconType = TYPE_DEFAULT;
10041aab00c3e762c6b648483ee7b45b162e4da7f7fJun Mukai                    if (x < mTmpRect.left) {
101f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wright                        iconType =
102f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wright                            (y < mTmpRect.top) ? TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW :
103f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wright                            (y > mTmpRect.bottom) ? TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW :
104f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wright                            TYPE_HORIZONTAL_DOUBLE_ARROW;
10541aab00c3e762c6b648483ee7b45b162e4da7f7fJun Mukai                    } else if (x > mTmpRect.right) {
106f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wright                        iconType =
107f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wright                            (y < mTmpRect.top) ? TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW :
108f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wright                            (y > mTmpRect.bottom) ? TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW :
109f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wright                            TYPE_HORIZONTAL_DOUBLE_ARROW;
11041aab00c3e762c6b648483ee7b45b162e4da7f7fJun Mukai                    } else if (y < mTmpRect.top || y > mTmpRect.bottom) {
111f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wright                        iconType = TYPE_VERTICAL_DOUBLE_ARROW;
11241aab00c3e762c6b648483ee7b45b162e4da7f7fJun Mukai                    }
113f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wright                    if (mPointerIconType != iconType) {
114f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wright                        mPointerIconType = iconType;
115f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wright                        inputDevice.setPointerType(iconType);
11641aab00c3e762c6b648483ee7b45b162e4da7f7fJun Mukai                    }
11741aab00c3e762c6b648483ee7b45b162e4da7f7fJun Mukai                } else {
118f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wright                    mPointerIconType = TYPE_NOT_SPECIFIED;
11941aab00c3e762c6b648483ee7b45b162e4da7f7fJun Mukai                }
12041aab00c3e762c6b648483ee7b45b162e4da7f7fJun Mukai            } break;
12141aab00c3e762c6b648483ee7b45b162e4da7f7fJun Mukai
12241aab00c3e762c6b648483ee7b45b162e4da7f7fJun Mukai            case MotionEvent.ACTION_HOVER_EXIT:
123f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wright                mPointerIconType = TYPE_NOT_SPECIFIED;
124caca1114b2cf78551b2008156986a644d15317efJun Mukai                InputDevice inputDevice = motionEvent.getDevice();
125caca1114b2cf78551b2008156986a644d15317efJun Mukai                if (inputDevice != null) {
126f9d9ce7705475874c82af04eb9b208a7fb556792Michael Wright                    inputDevice.setPointerType(TYPE_DEFAULT);
127caca1114b2cf78551b2008156986a644d15317efJun Mukai                }
12841aab00c3e762c6b648483ee7b45b162e4da7f7fJun Mukai                break;
12941aab00c3e762c6b648483ee7b45b162e4da7f7fJun Mukai
130037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner            case MotionEvent.ACTION_UP:
131037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner            case MotionEvent.ACTION_POINTER_UP: {
132b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang                stopTwoFingerScroll();
133037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner                break;
134037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner            }
135037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner        }
136037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner    }
13733d8e739206b7a5ed99cf9bf73d63f9b6c0d2a4etingna_sung
138b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang    private void doGestureDetection(MotionEvent motionEvent) {
139b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang        if (mGestureDetector == null || mNonResizeableRegion.isEmpty()) {
140b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang            return;
141b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang        }
142b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang        final int action = motionEvent.getAction() & MotionEvent.ACTION_MASK;
143b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang        final int x = (int) motionEvent.getX();
144b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang        final int y = (int) motionEvent.getY();
145b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang        final boolean isTouchInside = mNonResizeableRegion.contains(x, y);
146b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang        if (mInGestureDetection || action == MotionEvent.ACTION_DOWN && isTouchInside) {
147b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang            // If we receive the following actions, or the pointer goes out of the area
148b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang            // we're interested in, stop detecting and cancel the current detection.
149b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang            mInGestureDetection = isTouchInside
150b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang                    && action != MotionEvent.ACTION_UP
151b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang                    && action != MotionEvent.ACTION_POINTER_UP
152b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang                    && action != MotionEvent.ACTION_CANCEL;
153b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang            if (mInGestureDetection) {
154b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang                mGestureDetector.onTouchEvent(motionEvent);
155b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang            } else {
156b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang                MotionEvent cancelEvent = motionEvent.copy();
157b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang                cancelEvent.cancel();
158b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang                mGestureDetector.onTouchEvent(cancelEvent);
159b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang                stopTwoFingerScroll();
160b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang            }
161b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang        }
162b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang    }
163b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang
164b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang    private void onTwoFingerScroll(MotionEvent e) {
165b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang        final int x = (int)e.getX(0);
166b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang        final int y = (int)e.getY(0);
167b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang        if (!mTwoFingerScrolling) {
168b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang            mTwoFingerScrolling = true;
169b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang            mService.mH.obtainMessage(
170b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang                    H.TWO_FINGER_SCROLL_START, x, y, mDisplayContent).sendToTarget();
171b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang        }
172b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang    }
173b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang
174b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang    private void stopTwoFingerScroll() {
175b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang        if (mTwoFingerScrolling) {
176b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang            mTwoFingerScrolling = false;
177b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang            mService.mH.obtainMessage(H.FINISH_TASK_POSITIONING).sendToTarget();
178b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang        }
179b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang    }
180b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang
181b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang    private final class TwoFingerScrollListener extends GestureDetector.SimpleOnGestureListener {
182b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang        @Override
183b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang        public boolean onScroll(MotionEvent e1, MotionEvent e2,
184b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang                float distanceX, float distanceY) {
185b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang            if (e2.getPointerCount() == 2) {
186b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang                onTwoFingerScroll(e2);
187b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang                return true;
188b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang            }
189b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang            stopTwoFingerScroll();
190b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang            return false;
191b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang        }
192b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang    }
193b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang
194b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang    void setTouchExcludeRegion(Region newRegion, Region nonResizeableRegion) {
19533d8e739206b7a5ed99cf9bf73d63f9b6c0d2a4etingna_sung        synchronized (this) {
19633d8e739206b7a5ed99cf9bf73d63f9b6c0d2a4etingna_sung           mTouchExcludeRegion.set(newRegion);
197b15758ab7a6481717d0d29612e870d7241061c31Chong Zhang           mNonResizeableRegion.set(nonResizeableRegion);
19833d8e739206b7a5ed99cf9bf73d63f9b6c0d2a4etingna_sung        }
19933d8e739206b7a5ed99cf9bf73d63f9b6c0d2a4etingna_sung    }
200037aa8d434984840691378f3cc7d99d63dcc4076Craig Mautner}
201