| Index: remoting/android/java/src/org/chromium/chromoting/InputMonitor.java
|
| diff --git a/remoting/android/java/src/org/chromium/chromoting/InputMonitor.java b/remoting/android/java/src/org/chromium/chromoting/InputMonitor.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..10d9406b39b7b9d8d8e7ecd87c10aac44f97e105
|
| --- /dev/null
|
| +++ b/remoting/android/java/src/org/chromium/chromoting/InputMonitor.java
|
| @@ -0,0 +1,292 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +package org.chromium.chromoting;
|
| +
|
| +import android.content.Context;
|
| +import android.graphics.Rect;
|
| +import android.view.GestureDetector;
|
| +import android.view.MotionEvent;
|
| +import android.view.ScaleGestureDetector;
|
| +import android.view.ViewConfiguration;
|
| +
|
| +import org.chromium.chromoting.InputState.DetectedAction;
|
| +import org.chromium.chromoting.InputState.StartAction;
|
| +
|
| +/**
|
| + * A combination of existing Android and chromium motion and touch detectors, and provide a set of
|
| + * {@link Event} when each kind of touch behavior has been detected.
|
| + */
|
| +public final class InputMonitor
|
| + implements GestureDetector.OnGestureListener,
|
| + ScaleGestureDetector.OnScaleGestureListener,
|
| + TapGestureDetector.OnTapListener {
|
| + /** Tap with one or more fingers. */
|
| + private final Event.Raisable<TapEventParameter> mOnTap;
|
| +
|
| + /** Long press and hold with one or more fingers. */
|
| + private final Event.Raisable<TapEventParameter> mOnPressAndHold;
|
| +
|
| + /** Any motion event received. */
|
| + private final Event.Raisable<MotionEvent> mOnTouchEvent;
|
| +
|
| + /** Scroll with two fingers. */
|
| + private final Event.Raisable<TwoPointsEventParameter> mOnScroll;
|
| +
|
| + /** Fling with two fingers. */
|
| + private final Event.Raisable<TwoPointsEventParameter> mOnScrollFling;
|
| +
|
| + /** Fling with one finger. */
|
| + private final Event.Raisable<TwoPointsEventParameter> mOnFling;
|
| +
|
| + /** Scale with two fingers. */
|
| + private final Event.Raisable<ScaleEventParameter> mOnScale;
|
| +
|
| + /** Swipe with three or more fingers. */
|
| + private final Event.Raisable<TwoPointsEventParameter> mOnSwipe;
|
| +
|
| + /** Move with one finger. */
|
| + private final Event.Raisable<TwoPointsEventParameter> mOnMove;
|
| +
|
| + private final InputState.Settable mInputState;
|
| + private final int mEdgeSlopInPx;
|
| + private final float mSwipeThreshold;
|
| + private final GestureDetector mScroller;
|
| + private final ScaleGestureDetector mZoomer;
|
| + private final TapGestureDetector mTapDetector;
|
| + private final SwipePinchDetector mSwipePinchDetector;
|
| +
|
| + private Rect mPanGestureBounds;
|
| +
|
| + InputMonitor(AbstractDesktopView view, Context context) {
|
| + mOnTap = new Event.Raisable<>();
|
| + mOnPressAndHold = new Event.Raisable<>();
|
| + mOnTouchEvent = new Event.Raisable<>();
|
| + mOnScroll = new Event.Raisable<>();
|
| + mOnScrollFling = new Event.Raisable<>();
|
| + mOnFling = new Event.Raisable<>();
|
| + mOnScale = new Event.Raisable<>();
|
| + mOnSwipe = new Event.Raisable<>();
|
| + mOnMove = new Event.Raisable<>();
|
| + mInputState = new InputState.Settable();
|
| + mEdgeSlopInPx = ViewConfiguration.get(context).getScaledEdgeSlop();
|
| + mSwipeThreshold = 40 * context.getResources().getDisplayMetrics().density;
|
| + mScroller = new GestureDetector(context, this, null, false);
|
| + mScroller.setIsLongpressEnabled(false);
|
| + mZoomer = new ScaleGestureDetector(context, this);
|
| + mTapDetector = new TapGestureDetector(context, this);
|
| + mSwipePinchDetector = new SwipePinchDetector(context);
|
| + view.onClientSizeChanged().add(
|
| + new Event.ParameterRunnable<SizeChangedEventParameter>() {
|
| + @Override
|
| + public void run(SizeChangedEventParameter param) {
|
| + handleClientSizeChanged(param);
|
| + }
|
| + });
|
| + // Currently we support only touch events.
|
| + view.onTouch().add(
|
| + new Event.ParameterRunnable<TouchEventParameter>() {
|
| + @Override
|
| + public void run(TouchEventParameter param) {
|
| + handleTouch(param);
|
| + }
|
| + });
|
| + }
|
| +
|
| + // -------------- Getters -------------------------------------------------------------
|
| + public Event<TapEventParameter> onTap() {
|
| + return mOnTap;
|
| + }
|
| +
|
| + public Event<TapEventParameter> onPressAndHold() {
|
| + return mOnPressAndHold;
|
| + }
|
| +
|
| + public Event<MotionEvent> onTouchEvent() {
|
| + return mOnTouchEvent;
|
| + }
|
| +
|
| + public Event<TwoPointsEventParameter> onScroll() {
|
| + return mOnScroll;
|
| + }
|
| +
|
| + public Event<TwoPointsEventParameter> onScrollFling() {
|
| + return mOnScrollFling;
|
| + }
|
| +
|
| + public Event<TwoPointsEventParameter> onFling() {
|
| + return mOnFling;
|
| + }
|
| +
|
| + public Event<ScaleEventParameter> onScale() {
|
| + return mOnScale;
|
| + }
|
| +
|
| + public Event<TwoPointsEventParameter> onSwipe() {
|
| + return mOnSwipe;
|
| + }
|
| +
|
| + public Event<TwoPointsEventParameter> onMove() {
|
| + return mOnMove;
|
| + }
|
| +
|
| + public InputState inputState() {
|
| + return mInputState;
|
| + }
|
| +
|
| + // -------------- Implementations of GestureDetector.OnGestureListener ----------------
|
| +
|
| + /** Called whenever a gesture starts. Always accepts the gesture so it isn't ignored. */
|
| + @Override
|
| + public boolean onDown(MotionEvent e) {
|
| + return true;
|
| + }
|
| +
|
| + /** Called when a fling gesture is recognized. */
|
| + @Override
|
| + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
| + if (mInputState.shouldSuppressFling()) {
|
| + return false;
|
| + }
|
| +
|
| + if (mInputState.isScrollFling()) {
|
| + mInputState.setDetectedAction(DetectedAction.AFTER_SCROLL_FLING);
|
| + mOnScrollFling.raise(new TwoPointsEventParameter(e1, e2, velocityX, velocityY));
|
| + return true;
|
| + }
|
| +
|
| + if (mInputState.shouldSuppressCursorMovement()) {
|
| + return false;
|
| + }
|
| +
|
| + mInputState.setDetectedAction(DetectedAction.FLING);
|
| + mOnFling.raise(new TwoPointsEventParameter(e1, e2, velocityX, velocityY));
|
| + return true;
|
| + }
|
| +
|
| + /** Called when a long-press is triggered for one or more fingers. */
|
| + @Override
|
| + public void onLongPress(MotionEvent e) {}
|
| +
|
| + /** Called when the user drags one or more fingers across the touchscreen. */
|
| + @Override
|
| + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
|
| + if (!isInPanGestureBounds(e1)) {
|
| + // The gesture of scrolling from edge to the center should be handled by Android OS.
|
| + mInputState.setDetectedAction(InputState.DetectedAction.SCROLL_EDGE);
|
| + return false;
|
| + }
|
| +
|
| + if (e2.getPointerCount() >= 3 && !mInputState.swipeCompleted()) {
|
| + if (distanceY > mSwipeThreshold || distanceY < -mSwipeThreshold) {
|
| + mInputState.setDetectedAction(DetectedAction.SWIPE);
|
| + mOnSwipe.raise(new TwoPointsEventParameter(e1, e2, distanceX, distanceY));
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + if (e2.getPointerCount() == 2 && mSwipePinchDetector.isSwiping()) {
|
| + mInputState.setDetectedAction(DetectedAction.SCROLL);
|
| + mOnScroll.raise(new TwoPointsEventParameter(e1, e2, distanceX, distanceY));
|
| + return true;
|
| + }
|
| +
|
| + if (e2.getPointerCount() != 1 || mInputState.shouldSuppressCursorMovement()) {
|
| + return false;
|
| + }
|
| +
|
| + mInputState.setDetectedAction(DetectedAction.MOVE);
|
| + mOnMove.raise(new TwoPointsEventParameter(e1, e2, distanceX, distanceY));
|
| + return true;
|
| + }
|
| +
|
| + /** Called by {@link GestureDetector}, does nothing. */
|
| + @Override
|
| + public void onShowPress(MotionEvent e) {}
|
| +
|
| + /** Called by {@link GestureDetector}, returns false to continue following gesture detection. */
|
| + @Override
|
| + public boolean onSingleTapUp(MotionEvent e) {
|
| + return false;
|
| + }
|
| +
|
| + // --------- Implementations of ScaleGestureDetector.OnScaleGestureListener -----------
|
| + /** Called when the user is in the process of pinch-zooming. */
|
| + @Override
|
| + public boolean onScale(ScaleGestureDetector detector) {
|
| + if (!mSwipePinchDetector.isPinching()) {
|
| + return false;
|
| + }
|
| +
|
| + mInputState.setDetectedAction(DetectedAction.SCALE);
|
| + mOnScale.raise(new ScaleEventParameter(detector.getScaleFactor(),
|
| + detector.getFocusX(),
|
| + detector.getFocusY()));
|
| + return true;
|
| + }
|
| +
|
| + /**
|
| + * Called when the user starts to zoom. Always accepts the zoom so that
|
| + * onScale() can decide whether to respond to it.
|
| + */
|
| + @Override
|
| + public boolean onScaleBegin(ScaleGestureDetector detector) {
|
| + return true;
|
| + }
|
| +
|
| + /** Called when the user is done zooming. Defers to onScale()'s judgement. */
|
| + @Override
|
| + public void onScaleEnd(ScaleGestureDetector detector) {
|
| + onScale(detector);
|
| + }
|
| +
|
| + // -------------- Implementations of TapGestureDetector.OnTapListener ----------------
|
| + /** Called when the user taps the screen with one or more fingers. */
|
| + @Override
|
| + public boolean onTap(int pointerCount, float x, float y) {
|
| + TapEventParameter para = new TapEventParameter(pointerCount, x, y);
|
| + mOnTap.raise(para);
|
| + return para.handled;
|
| + }
|
| +
|
| + /** Called when a long-press is triggered for one or more fingers. */
|
| + @Override
|
| + public void onLongPress(int pointerCount, float x, float y) {
|
| + TapEventParameter para = new TapEventParameter(pointerCount, x, y);
|
| + mOnPressAndHold.raise(para);
|
| + if (para.handled) {
|
| + mInputState.setStartAction(StartAction.LONG_PRESS);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Returns a boolean value to indicate whether the MotionEvent is in the range of
|
| + * {@link mPanGestureBounds}
|
| + */
|
| + private boolean isInPanGestureBounds(MotionEvent e) {
|
| + Preconditions.notNull(e);
|
| + return mPanGestureBounds == null
|
| + || mPanGestureBounds.contains((int) e.getX(), (int) e.getY());
|
| + }
|
| +
|
| + // ---------------------------- Event handlers ---------------------------------------
|
| + private void handleClientSizeChanged(SizeChangedEventParameter parameter) {
|
| + mPanGestureBounds = new Rect(mEdgeSlopInPx,
|
| + mEdgeSlopInPx,
|
| + parameter.width - mEdgeSlopInPx,
|
| + parameter.height - mEdgeSlopInPx);
|
| + }
|
| +
|
| + private void handleTouch(TouchEventParameter parameter) {
|
| + mOnTouchEvent.raise(parameter.event);
|
| +
|
| + boolean handled = mScroller.onTouchEvent(parameter.event);
|
| + handled |= mZoomer.onTouchEvent(parameter.event);
|
| + handled |= mTapDetector.onTouchEvent(parameter.event);
|
| + mSwipePinchDetector.onTouchEvent(parameter.event);
|
| +
|
| + parameter.handled = handled;
|
| + }
|
| +}
|
|
|