Merge "Various minor magnification improvements"

This commit is contained in:
TreeHugger Robot
2017-10-13 05:12:44 +00:00
committed by Android (Google) Code Review
9 changed files with 259 additions and 344 deletions

View File

@@ -365,6 +365,11 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
/* do nothing */
}
@Override
public EventStreamTransformation getNext() {
return null;
}
@Override
public void clearEvents(int inputSource) {
/* do nothing */

View File

@@ -23,15 +23,12 @@ import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
/**
@@ -55,11 +52,10 @@ import android.view.accessibility.AccessibilityManager;
*
* Each instance is associated to a single user (and it does not handle user switch itself).
*/
public class AutoclickController implements EventStreamTransformation {
public class AutoclickController extends BaseEventStreamTransformation {
private static final String LOG_TAG = AutoclickController.class.getSimpleName();
private EventStreamTransformation mNext;
private final Context mContext;
private final int mUserId;
@@ -88,9 +84,7 @@ public class AutoclickController implements EventStreamTransformation {
mClickScheduler.cancel();
}
if (mNext != null) {
mNext.onMotionEvent(event, rawEvent, policyFlags);
}
super.onMotionEvent(event, rawEvent, policyFlags);
}
@Override
@@ -103,21 +97,7 @@ public class AutoclickController implements EventStreamTransformation {
}
}
if (mNext != null) {
mNext.onKeyEvent(event, policyFlags);
}
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
if (mNext != null) {
mNext.onAccessibilityEvent(event);
}
}
@Override
public void setNext(EventStreamTransformation next) {
mNext = next;
super.onKeyEvent(event, policyFlags);
}
@Override
@@ -126,9 +106,7 @@ public class AutoclickController implements EventStreamTransformation {
mClickScheduler.cancel();
}
if (mNext != null) {
mNext.clearEvents(inputSource);
}
super.clearEvents(inputSource);
}
@Override
@@ -418,7 +396,7 @@ public class AutoclickController implements EventStreamTransformation {
* Creates and forwards click event sequence.
*/
private void sendClick() {
if (mLastMotionEvent == null || mNext == null) {
if (mLastMotionEvent == null || getNext() == null) {
return;
}
@@ -448,10 +426,10 @@ public class AutoclickController implements EventStreamTransformation {
MotionEvent upEvent = MotionEvent.obtain(downEvent);
upEvent.setAction(MotionEvent.ACTION_UP);
mNext.onMotionEvent(downEvent, downEvent, mEventPolicyFlags);
AutoclickController.super.onMotionEvent(downEvent, downEvent, mEventPolicyFlags);
downEvent.recycle();
mNext.onMotionEvent(upEvent, upEvent, mEventPolicyFlags);
AutoclickController.super.onMotionEvent(upEvent, upEvent, mEventPolicyFlags);
upEvent.recycle();
}

View File

@@ -0,0 +1,31 @@
/*
** Copyright 2017, 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.accessibility;
abstract class BaseEventStreamTransformation implements EventStreamTransformation {
private EventStreamTransformation mNext;
@Override
public void setNext(EventStreamTransformation next) {
mNext = next;
}
@Override
public EventStreamTransformation getNext() {
return mNext;
}
}

View File

@@ -65,7 +65,12 @@ interface EventStreamTransformation {
* @param rawEvent The raw motion event.
* @param policyFlags Policy flags for the event.
*/
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags);
default void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
EventStreamTransformation next = getNext();
if (next != null) {
next.onMotionEvent(event, rawEvent, policyFlags);
}
}
/**
* Receives a key event.
@@ -73,14 +78,24 @@ interface EventStreamTransformation {
* @param event The key event.
* @param policyFlags Policy flags for the event.
*/
public void onKeyEvent(KeyEvent event, int policyFlags);
default void onKeyEvent(KeyEvent event, int policyFlags) {
EventStreamTransformation next = getNext();
if (next != null) {
next.onKeyEvent(event, policyFlags);
}
}
/**
* Receives an accessibility event.
*
* @param event The accessibility event.
*/
public void onAccessibilityEvent(AccessibilityEvent event);
default void onAccessibilityEvent(AccessibilityEvent event) {
EventStreamTransformation next = getNext();
if (next != null) {
next.onAccessibilityEvent(event);
}
};
/**
* Sets the next transformation.
@@ -89,15 +104,27 @@ interface EventStreamTransformation {
*/
public void setNext(EventStreamTransformation next);
/**
* Gets the next transformation.
*
* @return The next transformation.
*/
public EventStreamTransformation getNext();
/**
* Clears internal state associated with events from specific input source.
*
* @param inputSource The input source class for which transformation state should be cleared.
*/
public void clearEvents(int inputSource);
default void clearEvents(int inputSource) {
EventStreamTransformation next = getNext();
if (next != null) {
next.clearEvents(inputSource);
}
}
/**
* Destroys this transformation.
*/
public void onDestroy();
default void onDestroy() {}
}

View File

@@ -22,14 +22,12 @@ import android.os.SystemClock;
import android.util.Pools;
import android.util.Slog;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.WindowManagerPolicy;
import android.view.accessibility.AccessibilityEvent;
/**
* Intercepts key events and forwards them to accessibility manager service.
*/
public class KeyboardInterceptor implements EventStreamTransformation, Handler.Callback {
public class KeyboardInterceptor extends BaseEventStreamTransformation implements Handler.Callback {
private static final int MESSAGE_PROCESS_QUEUED_EVENTS = 1;
private static final String LOG_TAG = "KeyboardInterceptor";
@@ -37,7 +35,6 @@ public class KeyboardInterceptor implements EventStreamTransformation, Handler.C
private final WindowManagerPolicy mPolicy;
private final Handler mHandler;
private EventStreamTransformation mNext;
private KeyEventHolder mEventQueueStart;
private KeyEventHolder mEventQueueEnd;
@@ -64,13 +61,6 @@ public class KeyboardInterceptor implements EventStreamTransformation, Handler.C
mHandler = handler;
}
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
if (mNext != null) {
mNext.onMotionEvent(event, rawEvent, policyFlags);
}
}
@Override
public void onKeyEvent(KeyEvent event, int policyFlags) {
/*
@@ -89,29 +79,6 @@ public class KeyboardInterceptor implements EventStreamTransformation, Handler.C
mAms.notifyKeyEvent(event, policyFlags);
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
if (mNext != null) {
mNext.onAccessibilityEvent(event);
}
}
@Override
public void setNext(EventStreamTransformation next) {
mNext = next;
}
@Override
public void clearEvents(int inputSource) {
if (mNext != null) {
mNext.clearEvents(inputSource);
}
}
@Override
public void onDestroy() {
}
@Override
public boolean handleMessage(Message msg) {
if (msg.what != MESSAGE_PROCESS_QUEUED_EVENTS) {

View File

@@ -42,14 +42,12 @@ import android.util.Slog;
import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.OnScaleGestureListener;
import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
import com.android.internal.annotations.VisibleForTesting;
@@ -102,7 +100,7 @@ import com.android.internal.annotations.VisibleForTesting;
* 7. The magnification scale will be persisted in settings and in the cloud.
*/
@SuppressWarnings("WeakerAccess")
class MagnificationGestureHandler implements EventStreamTransformation {
class MagnificationGestureHandler extends BaseEventStreamTransformation {
private static final String LOG_TAG = "MagnificationEventHandler";
private static final boolean DEBUG_ALL = false;
@@ -110,13 +108,13 @@ class MagnificationGestureHandler implements EventStreamTransformation {
private static final boolean DEBUG_DETECTING = false || DEBUG_ALL;
private static final boolean DEBUG_PANNING = false || DEBUG_ALL;
/** @see #handleMotionEventStateDelegating */
/** @see DelegatingState */
@VisibleForTesting static final int STATE_DELEGATING = 1;
/** @see DetectingStateHandler */
/** @see DetectingState */
@VisibleForTesting static final int STATE_DETECTING = 2;
/** @see ViewportDraggingStateHandler */
/** @see ViewportDraggingState */
@VisibleForTesting static final int STATE_VIEWPORT_DRAGGING = 3;
/** @see PanningScalingStateHandler */
/** @see PanningScalingState */
@VisibleForTesting static final int STATE_PANNING_SCALING = 4;
private static final float MIN_SCALE = 2.0f;
@@ -124,9 +122,10 @@ class MagnificationGestureHandler implements EventStreamTransformation {
@VisibleForTesting final MagnificationController mMagnificationController;
@VisibleForTesting final DetectingStateHandler mDetectingStateHandler;
@VisibleForTesting final PanningScalingStateHandler mPanningScalingStateHandler;
@VisibleForTesting final ViewportDraggingStateHandler mViewportDraggingStateHandler;
@VisibleForTesting final DelegatingState mDelegatingState;
@VisibleForTesting final DetectingState mDetectingState;
@VisibleForTesting final PanningScalingState mPanningScalingState;
@VisibleForTesting final ViewportDraggingState mViewportDraggingState;
private final ScreenStateReceiver mScreenStateReceiver;
@@ -138,21 +137,12 @@ class MagnificationGestureHandler implements EventStreamTransformation {
final boolean mDetectTripleTap;
/**
* Whether {@link #mShortcutTriggered shortcut} is enabled
* Whether {@link DetectingState#mShortcutTriggered shortcut} is enabled
*/
final boolean mDetectShortcutTrigger;
EventStreamTransformation mNext;
@VisibleForTesting int mCurrentState;
@VisibleForTesting int mPreviousState;
@VisibleForTesting boolean mShortcutTriggered;
/**
* Time of last {@link MotionEvent#ACTION_DOWN} while in {@link #STATE_DELEGATING}
*/
long mDelegatingStateDownTime;
@VisibleForTesting State mCurrentState;
@VisibleForTesting State mPreviousState;
private PointerCoords[] mTempPointerCoords;
private PointerProperties[] mTempPointerProperties;
@@ -174,10 +164,10 @@ class MagnificationGestureHandler implements EventStreamTransformation {
boolean detectShortcutTrigger) {
mMagnificationController = magnificationController;
mDetectingStateHandler = new DetectingStateHandler(context);
mViewportDraggingStateHandler = new ViewportDraggingStateHandler();
mPanningScalingStateHandler =
new PanningScalingStateHandler(context);
mDelegatingState = new DelegatingState();
mDetectingState = new DetectingState(context);
mViewportDraggingState = new ViewportDraggingState();
mPanningScalingState = new PanningScalingState(context);
mDetectTripleTap = detectTripleTap;
mDetectShortcutTrigger = detectShortcutTrigger;
@@ -189,7 +179,7 @@ class MagnificationGestureHandler implements EventStreamTransformation {
mScreenStateReceiver = null;
}
transitionTo(STATE_DETECTING);
transitionTo(mDetectingState);
}
@Override
@@ -199,52 +189,17 @@ class MagnificationGestureHandler implements EventStreamTransformation {
dispatchTransformedEvent(event, rawEvent, policyFlags);
return;
}
// Local copy to avoid dispatching the same event to more than one state handler
// in case mPanningScalingStateHandler changes mCurrentState
int currentState = mCurrentState;
mPanningScalingStateHandler.onMotionEvent(event, rawEvent, policyFlags);
switch (currentState) {
case STATE_DELEGATING: {
handleMotionEventStateDelegating(event, rawEvent, policyFlags);
}
break;
case STATE_DETECTING: {
mDetectingStateHandler.onMotionEvent(event, rawEvent, policyFlags);
}
break;
case STATE_VIEWPORT_DRAGGING: {
mViewportDraggingStateHandler.onMotionEvent(event, rawEvent, policyFlags);
}
break;
case STATE_PANNING_SCALING: {
// mPanningScalingStateHandler handles events only
// if this is the current state since it uses ScaleGestureDetector
// and a GestureDetector which need well formed event stream.
}
break;
default: {
throw new IllegalStateException("Unknown state: " + currentState);
}
}
handleEventWith(mCurrentState, event, rawEvent, policyFlags);
}
@Override
public void onKeyEvent(KeyEvent event, int policyFlags) {
if (mNext != null) {
mNext.onKeyEvent(event, policyFlags);
}
}
private void handleEventWith(State stateHandler,
MotionEvent event, MotionEvent rawEvent, int policyFlags) {
// To keep InputEventConsistencyVerifiers within GestureDetectors happy
mPanningScalingState.mScrollGestureDetector.onTouchEvent(event);
mPanningScalingState.mScaleGestureDetector.onTouchEvent(event);
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
if (mNext != null) {
mNext.onAccessibilityEvent(event);
}
}
@Override
public void setNext(EventStreamTransformation next) {
mNext = next;
stateHandler.onMotionEvent(event, rawEvent, policyFlags);
}
@Override
@@ -253,9 +208,7 @@ class MagnificationGestureHandler implements EventStreamTransformation {
clearAndTransitionToStateDetecting();
}
if (mNext != null) {
mNext.clearEvents(inputSource);
}
super.clearEvents(inputSource);
}
@Override
@@ -272,60 +225,20 @@ class MagnificationGestureHandler implements EventStreamTransformation {
if (wasMagnifying) {
clearAndTransitionToStateDetecting();
} else {
toggleShortcutTriggered();
mDetectingState.toggleShortcutTriggered();
}
}
}
private void toggleShortcutTriggered() {
setShortcutTriggered(!mShortcutTriggered);
}
private void setShortcutTriggered(boolean state) {
if (mShortcutTriggered == state) {
return;
}
mShortcutTriggered = state;
mMagnificationController.setForceShowMagnifiableBounds(state);
}
void clearAndTransitionToStateDetecting() {
setShortcutTriggered(false);
mCurrentState = STATE_DETECTING;
mDetectingStateHandler.clear();
mViewportDraggingStateHandler.clear();
mPanningScalingStateHandler.clear();
}
private void handleMotionEventStateDelegating(MotionEvent event,
MotionEvent rawEvent, int policyFlags) {
if (event.getActionMasked() == ACTION_UP) {
transitionTo(STATE_DETECTING);
}
delegateEvent(event, rawEvent, policyFlags);
}
void delegateEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mDelegatingStateDownTime = event.getDownTime();
}
if (mNext != null) {
// We cache some events to see if the user wants to trigger magnification.
// If no magnification is triggered we inject these events with adjusted
// time and down time to prevent subsequent transformations being confused
// by stale events. After the cached events, which always have a down, are
// injected we need to also update the down time of all subsequent non cached
// events. All delegated events cached and non-cached are delivered here.
event.setDownTime(mDelegatingStateDownTime);
dispatchTransformedEvent(event, rawEvent, policyFlags);
}
mCurrentState = mDelegatingState;
mDetectingState.clear();
mViewportDraggingState.clear();
mPanningScalingState.clear();
}
private void dispatchTransformedEvent(MotionEvent event, MotionEvent rawEvent,
int policyFlags) {
if (mNext == null) return; // Nowhere to dispatch to
// If the touchscreen event is within the magnified portion of the screen we have
// to change its location to be where the user thinks he is poking the
// UI which may have been magnified and panned.
@@ -351,7 +264,7 @@ class MagnificationGestureHandler implements EventStreamTransformation {
coords, 0, 0, 1.0f, 1.0f, event.getDeviceId(), 0, event.getSource(),
event.getFlags());
}
mNext.onMotionEvent(event, rawEvent, policyFlags);
super.onMotionEvent(event, rawEvent, policyFlags);
}
private PointerCoords[] getTempPointerCoordsWithMinSize(int size) {
@@ -386,9 +299,10 @@ class MagnificationGestureHandler implements EventStreamTransformation {
return mTempPointerProperties;
}
private void transitionTo(int state) {
private void transitionTo(State state) {
if (DEBUG_STATE_TRANSITIONS) {
Slog.i(LOG_TAG, (stateToString(mCurrentState) + " -> " + stateToString(state)
Slog.i(LOG_TAG,
(State.nameOf(mCurrentState) + " -> " + State.nameOf(state)
+ " at " + asList(copyOfRange(new RuntimeException().getStackTrace(), 1, 5)))
.replace(getClass().getName(), ""));
}
@@ -396,40 +310,42 @@ class MagnificationGestureHandler implements EventStreamTransformation {
mCurrentState = state;
}
private static String stateToString(int state) {
switch (state) {
case STATE_DELEGATING: return "STATE_DELEGATING";
case STATE_DETECTING: return "STATE_DETECTING";
case STATE_VIEWPORT_DRAGGING: return "STATE_VIEWPORT_DRAGGING";
case STATE_PANNING_SCALING: return "STATE_PANNING_SCALING";
case 0: return "0";
default: throw new IllegalArgumentException("Unknown state: " + state);
}
}
private interface MotionEventHandler {
interface State {
void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags);
void clear();
default void clear() {}
default String name() {
return getClass().getSimpleName();
}
static String nameOf(@Nullable State s) {
return s != null ? s.name() : "null";
}
}
/**
* This class determines if the user is performing a scale or pan gesture.
*
* Unlike when {@link ViewportDraggingState dragging the viewport}, in panning mode the viewport
* moves in the same direction as the fingers, and allows to easily and precisely scale the
* magnification level.
* This makes it the preferred mode for one-off adjustments, due to its precision and ease of
* triggering.
*
* @see #STATE_PANNING_SCALING
*/
final class PanningScalingStateHandler extends SimpleOnGestureListener
implements OnScaleGestureListener, MotionEventHandler {
final class PanningScalingState extends SimpleOnGestureListener
implements OnScaleGestureListener, State {
private final ScaleGestureDetector mScaleGestureDetector;
private final GestureDetector mGestureDetector;
private final GestureDetector mScrollGestureDetector;
final float mScalingThreshold;
float mInitialScaleFactor = -1;
boolean mScaling;
public PanningScalingStateHandler(Context context) {
public PanningScalingState(Context context) {
final TypedValue scaleValue = new TypedValue();
context.getResources().getValue(
com.android.internal.R.dimen.config_screen_magnification_scaling_threshold,
@@ -437,35 +353,27 @@ class MagnificationGestureHandler implements EventStreamTransformation {
mScalingThreshold = scaleValue.getFloat();
mScaleGestureDetector = new ScaleGestureDetector(context, this);
mScaleGestureDetector.setQuickScaleEnabled(false);
mGestureDetector = new GestureDetector(context, this);
mScrollGestureDetector = new GestureDetector(context, this);
}
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
// Dispatches #onScaleBegin, #onScale, #onScaleEnd
mScaleGestureDetector.onTouchEvent(event);
// Dispatches #onScroll
mGestureDetector.onTouchEvent(event);
if (mCurrentState != STATE_PANNING_SCALING) {
return;
}
int action = event.getActionMasked();
if (action == ACTION_POINTER_UP
&& event.getPointerCount() == 2 // includes the pointer currently being released
&& mPreviousState == STATE_VIEWPORT_DRAGGING) {
&& mPreviousState == mViewportDraggingState) {
persistScaleAndTransitionTo(STATE_VIEWPORT_DRAGGING);
persistScaleAndTransitionTo(mViewportDraggingState);
} else if (action == ACTION_UP) {
persistScaleAndTransitionTo(STATE_DETECTING);
persistScaleAndTransitionTo(mDetectingState);
}
}
public void persistScaleAndTransitionTo(int state) {
public void persistScaleAndTransitionTo(State state) {
mMagnificationController.persistScale();
clear();
transitionTo(state);
@@ -474,7 +382,7 @@ class MagnificationGestureHandler implements EventStreamTransformation {
@Override
public boolean onScroll(MotionEvent first, MotionEvent second,
float distanceX, float distanceY) {
if (mCurrentState != STATE_PANNING_SCALING) {
if (mCurrentState != mPanningScalingState) {
return true;
}
if (DEBUG_PANNING) {
@@ -483,7 +391,7 @@ class MagnificationGestureHandler implements EventStreamTransformation {
}
mMagnificationController.offsetMagnifiedRegion(distanceX, distanceY,
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
return true;
return /* event consumed: */ true;
}
@Override
@@ -525,12 +433,12 @@ class MagnificationGestureHandler implements EventStreamTransformation {
final float pivotY = detector.getFocusY();
mMagnificationController.setScale(scale, pivotX, pivotY, false,
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
return true;
return /* handled: */ true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
return (mCurrentState == STATE_PANNING_SCALING);
return /* continue recognizing: */ (mCurrentState == mPanningScalingState);
}
@Override
@@ -546,7 +454,7 @@ class MagnificationGestureHandler implements EventStreamTransformation {
@Override
public String toString() {
return "MagnifiedContentInteractionStateHandler{" +
return "PanningScalingState{" +
"mInitialScaleFactor=" + mInitialScaleFactor +
", mScaling=" + mScaling +
'}';
@@ -558,9 +466,13 @@ class MagnificationGestureHandler implements EventStreamTransformation {
* determined that the user is performing a single-finger drag of the
* magnification viewport.
*
* Unlike when {@link PanningScalingState panning}, the viewport moves in the opposite direction
* of the finger, and any part of the screen is reachable without lifting the finger.
* This makes it the preferable mode for tasks like reading text spanning full screen width.
*
* @see #STATE_VIEWPORT_DRAGGING
*/
final class ViewportDraggingStateHandler implements MotionEventHandler {
final class ViewportDraggingState implements State {
/** Whether to disable zoom after dragging ends */
boolean mZoomedInBeforeDrag;
@@ -572,7 +484,7 @@ class MagnificationGestureHandler implements EventStreamTransformation {
switch (action) {
case ACTION_POINTER_DOWN: {
clear();
transitionTo(STATE_PANNING_SCALING);
transitionTo(mPanningScalingState);
}
break;
case ACTION_MOVE: {
@@ -594,7 +506,7 @@ class MagnificationGestureHandler implements EventStreamTransformation {
case ACTION_UP: {
if (!mZoomedInBeforeDrag) zoomOff();
clear();
transitionTo(STATE_DETECTING);
transitionTo(mDetectingState);
}
break;
@@ -613,25 +525,53 @@ class MagnificationGestureHandler implements EventStreamTransformation {
@Override
public String toString() {
return "ViewportDraggingStateHandler{" +
return "ViewportDraggingState{" +
"mZoomedInBeforeDrag=" + mZoomedInBeforeDrag +
", mLastMoveOutsideMagnifiedRegion=" + mLastMoveOutsideMagnifiedRegion +
'}';
}
}
final class DelegatingState implements State {
/**
* Time of last {@link MotionEvent#ACTION_DOWN} while in {@link #STATE_DELEGATING}
*/
public long mLastDelegatedDownEventTime;
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
if (event.getActionMasked() == ACTION_UP) {
transitionTo(mDetectingState);
}
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mLastDelegatedDownEventTime = event.getDownTime();
}
if (getNext() != null) {
// We cache some events to see if the user wants to trigger magnification.
// If no magnification is triggered we inject these events with adjusted
// time and down time to prevent subsequent transformations being confused
// by stale events. After the cached events, which always have a down, are
// injected we need to also update the down time of all subsequent non cached
// events. All delegated events cached and non-cached are delivered here.
event.setDownTime(mLastDelegatedDownEventTime);
dispatchTransformedEvent(event, rawEvent, policyFlags);
}
}
}
/**
* This class handles motion events when the event dispatch has not yet
* determined what the user is doing. It watches for various tap events.
*
* @see #STATE_DETECTING
*/
final class DetectingStateHandler implements MotionEventHandler, Handler.Callback {
final class DetectingState implements State, Handler.Callback {
private static final int MESSAGE_ON_TRIPLE_TAP_AND_HOLD = 1;
private static final int MESSAGE_TRANSITION_TO_DELEGATING_STATE = 2;
final int mLongTapMinDelay = ViewConfiguration.getJumpTapTimeout();
final int mLongTapMinDelay;
final int mSwipeMinDistance;
final int mMultiTapMaxDelay;
final int mMultiTapMaxDistance;
@@ -642,9 +582,12 @@ class MagnificationGestureHandler implements EventStreamTransformation {
private MotionEvent mLastUp;
private MotionEvent mPreLastUp;
@VisibleForTesting boolean mShortcutTriggered;
Handler mHandler = new Handler(this);
public DetectingStateHandler(Context context) {
public DetectingState(Context context) {
mLongTapMinDelay = ViewConfiguration.getLongPressTimeout();
mMultiTapMaxDelay = ViewConfiguration.getDoubleTapTimeout()
+ context.getResources().getInteger(
com.android.internal.R.integer.config_screen_magnification_multi_tap_adjustment);
@@ -661,7 +604,7 @@ class MagnificationGestureHandler implements EventStreamTransformation {
}
break;
case MESSAGE_TRANSITION_TO_DELEGATING_STATE: {
transitionToDelegatingState(/* andClear */ true);
transitionToDelegatingStateAndClear();
}
break;
default: {
@@ -682,12 +625,12 @@ class MagnificationGestureHandler implements EventStreamTransformation {
if (!mMagnificationController.magnificationRegionContains(
event.getX(), event.getY())) {
transitionToDelegatingState(/* andClear */ !mShortcutTriggered);
transitionToDelegatingStateAndClear();
} else if (isMultiTapTriggered(2 /* taps */)) {
// 3tap and hold
delayedTransitionToDraggingState(event);
afterLongTapTimeoutTransitionToDraggingState(event);
} else if (mDetectTripleTap
// If magnified, delay an ACTION_DOWN for mMultiTapMaxDelay
@@ -695,21 +638,21 @@ class MagnificationGestureHandler implements EventStreamTransformation {
// STATE_PANNING_SCALING(triggerable with ACTION_POINTER_DOWN)
|| mMagnificationController.isMagnifying()) {
delayedTransitionToDelegatingState();
afterMultiTapTimeoutTransitionToDelegatingState();
} else {
// Delegate pending events without delay
transitionToDelegatingState(/* andClear */ true);
transitionToDelegatingStateAndClear();
}
}
break;
case ACTION_POINTER_DOWN: {
if (mMagnificationController.isMagnifying()) {
transitionTo(STATE_PANNING_SCALING);
transitionTo(mPanningScalingState);
clear();
} else {
transitionToDelegatingState(/* andClear */ true);
transitionToDelegatingStateAndClear();
}
}
break;
@@ -722,7 +665,7 @@ class MagnificationGestureHandler implements EventStreamTransformation {
&& !isMultiTapTriggered(2 /* taps */)) {
// Swipe detected - delegate skipping timeout
transitionToDelegatingState(/* andClear */ true);
transitionToDelegatingStateAndClear();
}
}
break;
@@ -733,7 +676,7 @@ class MagnificationGestureHandler implements EventStreamTransformation {
if (!mMagnificationController.magnificationRegionContains(
event.getX(), event.getY())) {
transitionToDelegatingState(/* andClear */ !mShortcutTriggered);
transitionToDelegatingStateAndClear();
} else if (isMultiTapTriggered(3 /* taps */)) {
@@ -742,12 +685,11 @@ class MagnificationGestureHandler implements EventStreamTransformation {
} else if (
// Possible to be false on: 3tap&drag -> scale -> PTR_UP -> UP
isFingerDown()
//TODO long tap should never happen here
&& (timeBetween(mLastDown, /* mLastUp */ event) >= mLongTapMinDelay)
|| distance(mLastDown, /* mLastUp */ event)
>= mSwipeMinDistance) {
//TODO long tap should never happen here
&& ((timeBetween(mLastDown, mLastUp) >= mLongTapMinDelay)
|| (distance(mLastDown, mLastUp) >= mSwipeMinDistance))) {
transitionToDelegatingState(/* andClear */ true);
transitionToDelegatingStateAndClear();
}
}
@@ -796,14 +738,14 @@ class MagnificationGestureHandler implements EventStreamTransformation {
}
/** -> {@link #STATE_DELEGATING} */
public void delayedTransitionToDelegatingState() {
public void afterMultiTapTimeoutTransitionToDelegatingState() {
mHandler.sendEmptyMessageDelayed(
MESSAGE_TRANSITION_TO_DELEGATING_STATE,
mMultiTapMaxDelay);
}
/** -> {@link #STATE_VIEWPORT_DRAGGING} */
public void delayedTransitionToDraggingState(MotionEvent event) {
public void afterLongTapTimeoutTransitionToDraggingState(MotionEvent event) {
mHandler.sendMessageDelayed(
mHandler.obtainMessage(MESSAGE_ON_TRIPLE_TAP_AND_HOLD, event),
ViewConfiguration.getLongPressTimeout());
@@ -846,11 +788,7 @@ class MagnificationGestureHandler implements EventStreamTransformation {
MotionEventInfo info = mDelayedEventQueue;
mDelayedEventQueue = info.mNext;
// Because MagnifiedInteractionStateHandler requires well-formed event stream
mPanningScalingStateHandler.onMotionEvent(
info.event, info.rawEvent, info.policyFlags);
delegateEvent(info.event, info.rawEvent, info.policyFlags);
handleEventWith(mDelegatingState, info.event, info.rawEvent, info.policyFlags);
info.recycle();
}
@@ -868,10 +806,10 @@ class MagnificationGestureHandler implements EventStreamTransformation {
mLastUp = null;
}
void transitionToDelegatingState(boolean andClear) {
transitionTo(STATE_DELEGATING);
void transitionToDelegatingStateAndClear() {
transitionTo(mDelegatingState);
sendDelayedMotionEvents();
if (andClear) clear();
clear();
}
private void onTripleTap(MotionEvent up) {
@@ -895,21 +833,35 @@ class MagnificationGestureHandler implements EventStreamTransformation {
if (DEBUG_DETECTING) Slog.i(LOG_TAG, "onTripleTapAndHold()");
clear();
mViewportDraggingStateHandler.mZoomedInBeforeDrag =
mViewportDraggingState.mZoomedInBeforeDrag =
mMagnificationController.isMagnifying();
zoomOn(down.getX(), down.getY());
transitionTo(STATE_VIEWPORT_DRAGGING);
transitionTo(mViewportDraggingState);
}
@Override
public String toString() {
return "DetectingStateHandler{" +
return "DetectingState{" +
"tapCount()=" + tapCount() +
", mShortcutTriggered=" + mShortcutTriggered +
", mDelayedEventQueue=" + MotionEventInfo.toString(mDelayedEventQueue) +
'}';
}
void toggleShortcutTriggered() {
setShortcutTriggered(!mShortcutTriggered);
}
void setShortcutTriggered(boolean state) {
if (mShortcutTriggered == state) {
return;
}
mShortcutTriggered = state;
mMagnificationController.setForceShowMagnifiableBounds(state);
}
}
private void zoomOn(float centerX, float centerY) {
@@ -935,16 +887,15 @@ class MagnificationGestureHandler implements EventStreamTransformation {
@Override
public String toString() {
return "MagnificationGestureHandler{" +
"mDetectingStateHandler=" + mDetectingStateHandler +
", mMagnifiedInteractionStateHandler=" + mPanningScalingStateHandler +
", mViewportDraggingStateHandler=" + mViewportDraggingStateHandler +
return "MagnificationGesture{" +
"mDetectingState=" + mDetectingState +
", mDelegatingState=" + mDelegatingState +
", mMagnifiedInteractionState=" + mPanningScalingState +
", mViewportDraggingState=" + mViewportDraggingState +
", mDetectTripleTap=" + mDetectTripleTap +
", mDetectShortcutTrigger=" + mDetectShortcutTrigger +
", mCurrentState=" + stateToString(mCurrentState) +
", mPreviousState=" + stateToString(mPreviousState) +
", mShortcutTriggered=" + mShortcutTriggered +
", mDelegatingStateDownTime=" + mDelegatingStateDownTime +
", mCurrentState=" + State.nameOf(mCurrentState) +
", mPreviousState=" + State.nameOf(mPreviousState) +
", mMagnificationController=" + mMagnificationController +
'}';
}
@@ -1051,7 +1002,7 @@ class MagnificationGestureHandler implements EventStreamTransformation {
@Override
public void onReceive(Context context, Intent intent) {
mGestureHandler.setShortcutTriggered(false);
mGestureHandler.mDetectingState.setShortcutTriggered(false);
}
}
}

View File

@@ -30,10 +30,9 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.WindowManagerPolicy;
import android.view.accessibility.AccessibilityEvent;
import com.android.internal.os.SomeArgs;
import java.util.ArrayList;
@@ -45,7 +44,7 @@ import java.util.List;
* <p>
* All methods except {@code injectEvents} must be called only from the main thread.
*/
public class MotionEventInjector implements EventStreamTransformation, Handler.Callback {
public class MotionEventInjector extends BaseEventStreamTransformation implements Handler.Callback {
private static final String LOG_TAG = "MotionEventInjector";
private static final int MESSAGE_SEND_MOTION_EVENT = 1;
private static final int MESSAGE_INJECT_EVENTS = 2;
@@ -68,7 +67,6 @@ public class MotionEventInjector implements EventStreamTransformation, Handler.C
private final Handler mHandler;
private final SparseArray<Boolean> mOpenGesturesInProgress = new SparseArray<>();
private EventStreamTransformation mNext;
private IAccessibilityServiceClient mServiceInterfaceForCurrentGesture;
private IntArray mSequencesInProgress = new IntArray(5);
private boolean mIsDestroyed = false;
@@ -116,25 +114,6 @@ public class MotionEventInjector implements EventStreamTransformation, Handler.C
sendMotionEventToNext(event, rawEvent, policyFlags);
}
@Override
public void onKeyEvent(KeyEvent event, int policyFlags) {
if (mNext != null) {
mNext.onKeyEvent(event, policyFlags);
}
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
if (mNext != null) {
mNext.onAccessibilityEvent(event);
}
}
@Override
public void setNext(EventStreamTransformation next) {
mNext = next;
}
@Override
public void clearEvents(int inputSource) {
/*
@@ -187,7 +166,7 @@ public class MotionEventInjector implements EventStreamTransformation, Handler.C
return;
}
if (mNext == null) {
if (getNext() == null) {
notifyService(serviceInterface, sequence, false);
return;
}
@@ -292,8 +271,8 @@ public class MotionEventInjector implements EventStreamTransformation, Handler.C
private void sendMotionEventToNext(MotionEvent event, MotionEvent rawEvent,
int policyFlags) {
if (mNext != null) {
mNext.onMotionEvent(event, rawEvent, policyFlags);
if (getNext() != null) {
super.onMotionEvent(event, rawEvent, policyFlags);
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mOpenGesturesInProgress.put(event.getSource(), true);
}
@@ -305,7 +284,7 @@ public class MotionEventInjector implements EventStreamTransformation, Handler.C
}
private void cancelAnyGestureInProgress(int source) {
if ((mNext != null) && mOpenGesturesInProgress.get(source, false)) {
if ((getNext() != null) && mOpenGesturesInProgress.get(source, false)) {
long now = SystemClock.uptimeMillis();
MotionEvent cancelEvent =
obtainMotionEvent(now, now, MotionEvent.ACTION_CANCEL, getLastTouchPoints(), 1);

View File

@@ -55,7 +55,8 @@ import java.util.List;
*
* @hide
*/
class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDetector.Listener {
class TouchExplorer extends BaseEventStreamTransformation
implements AccessibilityGestureDetector.Listener {
private static final boolean DEBUG = false;
@@ -131,9 +132,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe
// the two dragging pointers as opposed to use the location of the primary one.
private final int mScaledMinPointerDistanceToUseMiddleLocation;
// The handler to which to delegate events.
private EventStreamTransformation mNext;
// Helper class to track received pointers.
private final ReceivedPointerTracker mReceivedPointerTracker;
@@ -198,9 +196,7 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe
if (inputSource == InputDevice.SOURCE_TOUCHSCREEN) {
clear();
}
if (mNext != null) {
mNext.clearEvents(inputSource);
}
super.clearEvents(inputSource);
}
@Override
@@ -257,17 +253,10 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe
mAms.onTouchInteractionEnd();
}
@Override
public void setNext(EventStreamTransformation next) {
mNext = next;
}
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
if (!event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
if (mNext != null) {
mNext.onMotionEvent(event, rawEvent, policyFlags);
}
super.onMotionEvent(event, rawEvent, policyFlags);
return;
}
@@ -310,13 +299,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe
}
}
@Override
public void onKeyEvent(KeyEvent event, int policyFlags) {
if (mNext != null) {
mNext.onKeyEvent(event, policyFlags);
}
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
final int eventType = event.getEventType();
@@ -353,9 +335,7 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe
mLastTouchedWindowId = event.getWindowId();
} break;
}
if (mNext != null) {
mNext.onAccessibilityEvent(event);
}
super.onAccessibilityEvent(event);
}
@Override
@@ -969,12 +949,10 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe
// Make sure that the user will see the event.
policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER;
if (mNext != null) {
// TODO: For now pass null for the raw event since the touch
// explorer is the last event transformation and it does
// not care about the raw event.
mNext.onMotionEvent(event, null, policyFlags);
}
// TODO: For now pass null for the raw event since the touch
// explorer is the last event transformation and it does
// not care about the raw event.
super.onMotionEvent(event, null, policyFlags);
mInjectedPointerTracker.onMotionEvent(event);

View File

@@ -16,7 +16,6 @@
package com.android.server.accessibility;
import static android.util.ExceptionUtils.propagate;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
@@ -105,8 +104,8 @@ public class MagnificationGestureHandlerTest {
MagnificationGestureHandler h = new MagnificationGestureHandler(
mContext, mMagnificationController,
detectTripleTap, detectShortcutTrigger);
mHandler = new TestHandler(h.mDetectingStateHandler, mClock);
h.mDetectingStateHandler.mHandler = mHandler;
mHandler = new TestHandler(h.mDetectingState, mClock);
h.mDetectingState.mHandler = mHandler;
h.setNext(strictMock(EventStreamTransformation.class));
return h;
}
@@ -229,7 +228,7 @@ public class MagnificationGestureHandlerTest {
allowEventDelegation();
tap();
// no fast forward
verify(mMgh.mNext, times(2)).onMotionEvent(any(), any(), anyInt());
verify(mMgh.getNext(), times(2)).onMotionEvent(any(), any(), anyInt());
}
private void assertTransition(int fromState, Runnable transitionAction, int toState) {
@@ -250,7 +249,7 @@ public class MagnificationGestureHandlerTest {
}
private void allowEventDelegation() {
doNothing().when(mMgh.mNext).onMotionEvent(any(), any(), anyInt());
doNothing().when(mMgh.getNext()).onMotionEvent(any(), any(), anyInt());
}
private void fastForward1sec() {
@@ -272,7 +271,7 @@ public class MagnificationGestureHandlerTest {
case STATE_IDLE: {
check(tapCount() < 2, state);
check(!mMgh.mShortcutTriggered, state);
check(!mMgh.mDetectingState.mShortcutTriggered, state);
check(!isZoomed(), state);
} break;
case STATE_ZOOMED: {
@@ -288,28 +287,28 @@ public class MagnificationGestureHandlerTest {
check(tapCount() == 2, state);
} break;
case STATE_DRAGGING: {
check(mMgh.mCurrentState == MagnificationGestureHandler.STATE_VIEWPORT_DRAGGING,
check(mMgh.mCurrentState == mMgh.mViewportDraggingState,
state);
check(mMgh.mViewportDraggingStateHandler.mZoomedInBeforeDrag, state);
check(mMgh.mViewportDraggingState.mZoomedInBeforeDrag, state);
} break;
case STATE_DRAGGING_TMP: {
check(mMgh.mCurrentState == MagnificationGestureHandler.STATE_VIEWPORT_DRAGGING,
check(mMgh.mCurrentState == mMgh.mViewportDraggingState,
state);
check(!mMgh.mViewportDraggingStateHandler.mZoomedInBeforeDrag, state);
check(!mMgh.mViewportDraggingState.mZoomedInBeforeDrag, state);
} break;
case STATE_SHORTCUT_TRIGGERED: {
check(mMgh.mShortcutTriggered, state);
check(mMgh.mDetectingState.mShortcutTriggered, state);
check(!isZoomed(), state);
} break;
case STATE_PANNING: {
check(mMgh.mCurrentState == MagnificationGestureHandler.STATE_PANNING_SCALING,
check(mMgh.mCurrentState == mMgh.mPanningScalingState,
state);
check(!mMgh.mPanningScalingStateHandler.mScaling, state);
check(!mMgh.mPanningScalingState.mScaling, state);
} break;
case STATE_SCALING_AND_PANNING: {
check(mMgh.mCurrentState == MagnificationGestureHandler.STATE_PANNING_SCALING,
check(mMgh.mCurrentState == mMgh.mPanningScalingState,
state);
check(mMgh.mPanningScalingStateHandler.mScaling, state);
check(mMgh.mPanningScalingState.mScaling, state);
} break;
default: throw new IllegalArgumentException("Illegal state: " + state);
}
@@ -432,7 +431,7 @@ public class MagnificationGestureHandlerTest {
}
private int tapCount() {
return mMgh.mDetectingStateHandler.tapCount();
return mMgh.mDetectingState.tapCount();
}
private static String stateToString(int state) {
@@ -492,7 +491,7 @@ public class MagnificationGestureHandlerTest {
}
private long defaultDownTime() {
MotionEvent lastDown = mMgh.mDetectingStateHandler.mLastDown;
MotionEvent lastDown = mMgh.mDetectingState.mLastDown;
return lastDown == null ? mClock.now() - 1 : lastDown.getDownTime();
}