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.Region; 20import android.view.DisplayInfo; 21import android.view.MotionEvent; 22import android.view.WindowManagerPolicy.PointerEventListener; 23 24import com.android.server.wm.WindowManagerService.H; 25 26public class StackTapPointerEventListener implements PointerEventListener { 27 private static final int TAP_TIMEOUT_MSEC = 300; 28 private static final float TAP_MOTION_SLOP_INCHES = 0.125f; 29 30 private final int mMotionSlop; 31 private float mDownX; 32 private float mDownY; 33 private int mPointerId; 34 final private Region mTouchExcludeRegion = new Region(); 35 private final WindowManagerService mService; 36 private final DisplayContent mDisplayContent; 37 38 public StackTapPointerEventListener(WindowManagerService service, 39 DisplayContent displayContent) { 40 mService = service; 41 mDisplayContent = displayContent; 42 DisplayInfo info = displayContent.getDisplayInfo(); 43 mMotionSlop = (int)(info.logicalDensityDpi * TAP_MOTION_SLOP_INCHES); 44 } 45 46 @Override 47 public void onPointerEvent(MotionEvent motionEvent) { 48 final int action = motionEvent.getAction(); 49 switch (action & MotionEvent.ACTION_MASK) { 50 case MotionEvent.ACTION_DOWN: 51 mPointerId = motionEvent.getPointerId(0); 52 mDownX = motionEvent.getX(); 53 mDownY = motionEvent.getY(); 54 break; 55 case MotionEvent.ACTION_MOVE: 56 if (mPointerId >= 0) { 57 int index = motionEvent.findPointerIndex(mPointerId); 58 if ((motionEvent.getEventTime() - motionEvent.getDownTime()) > TAP_TIMEOUT_MSEC 59 || index < 0 60 || Math.abs(motionEvent.getX(index) - mDownX) > mMotionSlop 61 || Math.abs(motionEvent.getY(index) - mDownY) > mMotionSlop) { 62 mPointerId = -1; 63 } 64 } 65 break; 66 case MotionEvent.ACTION_UP: 67 case MotionEvent.ACTION_POINTER_UP: { 68 int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) 69 >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 70 // Extract the index of the pointer that left the touch sensor 71 if (mPointerId == motionEvent.getPointerId(index)) { 72 final int x = (int)motionEvent.getX(index); 73 final int y = (int)motionEvent.getY(index); 74 synchronized(this) { 75 if ((motionEvent.getEventTime() - motionEvent.getDownTime()) 76 < TAP_TIMEOUT_MSEC 77 && Math.abs(x - mDownX) < mMotionSlop 78 && Math.abs(y - mDownY) < mMotionSlop 79 && !mTouchExcludeRegion.contains(x, y)) { 80 mService.mH.obtainMessage(H.TAP_OUTSIDE_STACK, x, y, 81 mDisplayContent).sendToTarget(); 82 } 83 } 84 mPointerId = -1; 85 } 86 break; 87 } 88 } 89 } 90 91 void setTouchExcludeRegion(Region newRegion) { 92 synchronized (this) { 93 mTouchExcludeRegion.set(newRegion); 94 } 95 } 96} 97