blob: fb556d23cdd3dba378fb66122bb3d4e15a1ccb53 [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm;
import android.graphics.Rect;
import android.graphics.Region;
import android.view.DisplayInfo;
import android.view.GestureDetector;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.WindowManagerPolicy.PointerEventListener;
import com.android.server.wm.WindowManagerService.H;
import static android.view.PointerIcon.TYPE_NOT_SPECIFIED;
import static android.view.PointerIcon.TYPE_DEFAULT;
import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
import static android.view.PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW;
import static android.view.PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW;
public class TaskTapPointerEventListener implements PointerEventListener {
final private Region mTouchExcludeRegion = new Region();
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
private final Rect mTmpRect = new Rect();
private final Region mNonResizeableRegion = new Region();
private boolean mTwoFingerScrolling;
private boolean mInGestureDetection;
private GestureDetector mGestureDetector;
private int mPointerIconType = TYPE_NOT_SPECIFIED;
public TaskTapPointerEventListener(WindowManagerService service,
DisplayContent displayContent) {
mService = service;
mDisplayContent = displayContent;
}
// initialize the object, note this must be done outside WindowManagerService
// ctor, otherwise it may cause recursion as some code in GestureDetector ctor
// depends on WMS being already created.
void init() {
mGestureDetector = new GestureDetector(
mService.mContext, new TwoFingerScrollListener(), mService.mH);
}
@Override
public void onPointerEvent(MotionEvent motionEvent) {
doGestureDetection(motionEvent);
final int action = motionEvent.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final int x = (int) motionEvent.getX();
final int y = (int) motionEvent.getY();
synchronized (this) {
if (!mTouchExcludeRegion.contains(x, y)) {
mService.mH.obtainMessage(H.TAP_OUTSIDE_TASK,
x, y, mDisplayContent).sendToTarget();
}
}
break;
}
case MotionEvent.ACTION_MOVE: {
if (motionEvent.getPointerCount() != 2) {
stopTwoFingerScroll();
}
break;
}
case MotionEvent.ACTION_HOVER_MOVE: {
final int x = (int) motionEvent.getX();
final int y = (int) motionEvent.getY();
final Task task = mDisplayContent.findTaskForControlPoint(x, y);
InputDevice inputDevice = motionEvent.getDevice();
if (task == null || inputDevice == null) {
mPointerIconType = TYPE_NOT_SPECIFIED;
break;
}
task.getDimBounds(mTmpRect);
if (!mTmpRect.isEmpty() && !mTmpRect.contains(x, y)) {
int iconType = TYPE_DEFAULT;
if (x < mTmpRect.left) {
iconType =
(y < mTmpRect.top) ? TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW :
(y > mTmpRect.bottom) ? TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW :
TYPE_HORIZONTAL_DOUBLE_ARROW;
} else if (x > mTmpRect.right) {
iconType =
(y < mTmpRect.top) ? TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW :
(y > mTmpRect.bottom) ? TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW :
TYPE_HORIZONTAL_DOUBLE_ARROW;
} else if (y < mTmpRect.top || y > mTmpRect.bottom) {
iconType = TYPE_VERTICAL_DOUBLE_ARROW;
}
if (mPointerIconType != iconType) {
mPointerIconType = iconType;
inputDevice.setPointerType(iconType);
}
} else {
mPointerIconType = TYPE_NOT_SPECIFIED;
}
} break;
case MotionEvent.ACTION_HOVER_EXIT:
mPointerIconType = TYPE_NOT_SPECIFIED;
InputDevice inputDevice = motionEvent.getDevice();
if (inputDevice != null) {
inputDevice.setPointerType(TYPE_DEFAULT);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP: {
stopTwoFingerScroll();
break;
}
}
}
private void doGestureDetection(MotionEvent motionEvent) {
if (mGestureDetector == null || mNonResizeableRegion.isEmpty()) {
return;
}
final int action = motionEvent.getAction() & MotionEvent.ACTION_MASK;
final int x = (int) motionEvent.getX();
final int y = (int) motionEvent.getY();
final boolean isTouchInside = mNonResizeableRegion.contains(x, y);
if (mInGestureDetection || action == MotionEvent.ACTION_DOWN && isTouchInside) {
// If we receive the following actions, or the pointer goes out of the area
// we're interested in, stop detecting and cancel the current detection.
mInGestureDetection = isTouchInside
&& action != MotionEvent.ACTION_UP
&& action != MotionEvent.ACTION_POINTER_UP
&& action != MotionEvent.ACTION_CANCEL;
if (mInGestureDetection) {
mGestureDetector.onTouchEvent(motionEvent);
} else {
MotionEvent cancelEvent = motionEvent.copy();
cancelEvent.cancel();
mGestureDetector.onTouchEvent(cancelEvent);
stopTwoFingerScroll();
}
}
}
private void onTwoFingerScroll(MotionEvent e) {
final int x = (int)e.getX(0);
final int y = (int)e.getY(0);
if (!mTwoFingerScrolling) {
mTwoFingerScrolling = true;
mService.mH.obtainMessage(
H.TWO_FINGER_SCROLL_START, x, y, mDisplayContent).sendToTarget();
}
}
private void stopTwoFingerScroll() {
if (mTwoFingerScrolling) {
mTwoFingerScrolling = false;
mService.mH.obtainMessage(H.FINISH_TASK_POSITIONING).sendToTarget();
}
}
private final class TwoFingerScrollListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
if (e2.getPointerCount() == 2) {
onTwoFingerScroll(e2);
return true;
}
stopTwoFingerScroll();
return false;
}
}
void setTouchExcludeRegion(Region newRegion, Region nonResizeableRegion) {
synchronized (this) {
mTouchExcludeRegion.set(newRegion);
mNonResizeableRegion.set(nonResizeableRegion);
}
}
}