diff --git a/services/java/com/android/server/accessibility/ScreenMagnifier.java b/services/java/com/android/server/accessibility/ScreenMagnifier.java index d6482fa0c6320..ffd4adb6c8467 100644 --- a/services/java/com/android/server/accessibility/ScreenMagnifier.java +++ b/services/java/com/android/server/accessibility/ScreenMagnifier.java @@ -28,7 +28,6 @@ import android.content.IntentFilter; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.PixelFormat; -import android.graphics.PointF; import android.graphics.PorterDuff.Mode; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -40,18 +39,20 @@ import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.provider.Settings; -import android.util.MathUtils; import android.util.Property; import android.util.Slog; import android.view.Display; import android.view.DisplayInfo; +import android.view.GestureDetector; +import android.view.GestureDetector.SimpleOnGestureListener; import android.view.Gravity; import android.view.IDisplayContentChangeListener; import android.view.IWindowManager; import android.view.MotionEvent; import android.view.MotionEvent.PointerCoords; import android.view.MotionEvent.PointerProperties; -import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener; +import android.view.ScaleGestureDetector; +import android.view.ScaleGestureDetector.OnScaleGestureListener; import android.view.Surface; import android.view.View; import android.view.ViewConfiguration; @@ -119,7 +120,6 @@ public final class ScreenMagnifier implements EventStreamTransformation { private static final boolean DEBUG_VIEWPORT_WINDOW = false; private static final boolean DEBUG_WINDOW_TRANSITIONS = false; private static final boolean DEBUG_ROTATION = false; - private static final boolean DEBUG_GESTURE_DETECTOR = false; private static final boolean DEBUG_MAGNIFICATION_CONTROLLER = false; private static final String LOG_TAG = ScreenMagnifier.class.getSimpleName(); @@ -141,7 +141,7 @@ public final class ScreenMagnifier implements EventStreamTransformation { private final DisplayProvider mDisplayProvider; private final DetectingStateHandler mDetectingStateHandler = new DetectingStateHandler(); - private final GestureDetector mGestureDetector; + private final MagnifiedContentInteractonStateHandler mMagnifiedContentInteractonStateHandler; private final StateViewportDraggingHandler mStateViewportDraggingHandler = new StateViewportDraggingHandler(); @@ -196,14 +196,15 @@ public final class ScreenMagnifier implements EventStreamTransformation { mScreenStateObserver = new ScreenStateObserver(mContext, mViewport, mMagnificationController); - mGestureDetector = new GestureDetector(context); + mMagnifiedContentInteractonStateHandler = new MagnifiedContentInteractonStateHandler( + context); transitionToState(STATE_DETECTING); } @Override public void onMotionEvent(MotionEvent event, int policyFlags) { - mGestureDetector.onMotionEvent(event); + mMagnifiedContentInteractonStateHandler.onMotionEvent(event); switch (mCurrentState) { case STATE_DELEGATING: { handleMotionEventStateDelegating(event, policyFlags); @@ -215,9 +216,9 @@ public final class ScreenMagnifier implements EventStreamTransformation { mStateViewportDraggingHandler.onMotionEvent(event, policyFlags); } break; case STATE_MAGNIFIED_INTERACTION: { - // Handled by the gesture detector. Since the detector - // needs all touch events to work properly we cannot - // call it only for this state. + // mMagnifiedContentInteractonStateHandler handles events only + // if this is the current state since it uses ScaleGestureDetecotr + // and a GestureDetector which need well formed event stream. } break; default: { throw new IllegalStateException("Unknown state: " + mCurrentState); @@ -242,7 +243,7 @@ public final class ScreenMagnifier implements EventStreamTransformation { mCurrentState = STATE_DETECTING; mDetectingStateHandler.clear(); mStateViewportDraggingHandler.clear(); - mGestureDetector.clear(); + mMagnifiedContentInteractonStateHandler.clear(); if (mNext != null) { mNext.clear(); } @@ -347,46 +348,27 @@ public final class ScreenMagnifier implements EventStreamTransformation { mCurrentState = state; } - private final class GestureDetector implements OnScaleGestureListener { + private final class MagnifiedContentInteractonStateHandler + extends SimpleOnGestureListener implements OnScaleGestureListener { private static final float MIN_SCALE = 1.3f; private static final float MAX_SCALE = 5.0f; - private static final float DETECT_SCALING_THRESHOLD = 0.30f; - private static final int DETECT_PANNING_THRESHOLD_DIP = 30; - - private final float mScaledDetectPanningThreshold; - private final ScaleGestureDetector mScaleGestureDetector; + private final GestureDetector mGestureDetector; - private final PointF mPrevFocus = new PointF(Float.NaN, Float.NaN); - private final PointF mInitialFocus = new PointF(Float.NaN, Float.NaN); + private float mScaleFocusX = -1; + private float mScaleFocusY = -1; - private float mCurrScale = Float.NaN; - private float mCurrScaleFactor = 1.0f; - private float mPrevScaleFactor = 1.0f; - private float mCurrPan; - private float mPrevPan; - - private float mScaleFocusX = Float.NaN; - private float mScaleFocusY = Float.NaN; - - private boolean mScaling; - private boolean mPanning; - - public GestureDetector(Context context) { - final float density = context.getResources().getDisplayMetrics().density; - mScaledDetectPanningThreshold = DETECT_PANNING_THRESHOLD_DIP * density; - mScaleGestureDetector = new ScaleGestureDetector(this); + public MagnifiedContentInteractonStateHandler(Context context) { + mScaleGestureDetector = new ScaleGestureDetector(context, this); + mGestureDetector = new GestureDetector(context, this); } public void onMotionEvent(MotionEvent event) { mScaleGestureDetector.onTouchEvent(event); - switch (mCurrentState) { - case STATE_DETECTING: - case STATE_DELEGATING: - case STATE_VIEWPORT_DRAGGING: { - return; - } + mGestureDetector.onTouchEvent(event); + if (mCurrentState != STATE_MAGNIFIED_INTERACTION) { + return; } if (event.getActionMasked() == MotionEvent.ACTION_UP) { clear(); @@ -403,121 +385,57 @@ public final class ScreenMagnifier implements EventStreamTransformation { } @Override - public boolean onScale(ScaleGestureDetector detector) { - switch (mCurrentState) { - case STATE_DETECTING: - case STATE_DELEGATING: - case STATE_VIEWPORT_DRAGGING: { - return true; - } - case STATE_MAGNIFIED_INTERACTION: { - mCurrScaleFactor = mScaleGestureDetector.getScaleFactor(); - final float scaleDelta = Math.abs(1.0f - mCurrScaleFactor * mPrevScaleFactor); - if (DEBUG_GESTURE_DETECTOR) { - Slog.i(LOG_TAG, "scaleDelta: " + scaleDelta); - } - if (!mScaling && scaleDelta > DETECT_SCALING_THRESHOLD) { - mScaling = true; - clearContextualState(); - return true; - } - if (mScaling) { - performScale(detector); - } - mCurrPan = (float) MathUtils.dist( - mScaleGestureDetector.getFocusX(), - mScaleGestureDetector.getFocusY(), - mInitialFocus.x, mInitialFocus.y); - final float panDelta = mCurrPan + mPrevPan; - if (DEBUG_GESTURE_DETECTOR) { - Slog.i(LOG_TAG, "panDelta: " + panDelta); - } - if (!mPanning && panDelta > mScaledDetectPanningThreshold) { - mPanning = true; - clearContextualState(); - return true; - } - if (mPanning) { - performPan(detector); - } - } break; - } - return false; - } - - @Override - public boolean onScaleBegin(ScaleGestureDetector detector) { - mPrevScaleFactor *= mCurrScaleFactor; - mCurrScale = Float.NaN; - mPrevPan += mCurrPan; - mPrevFocus.x = mInitialFocus.x = detector.getFocusX(); - mPrevFocus.y = mInitialFocus.y = detector.getFocusY(); - return true; - } - - @Override - public void onScaleEnd(ScaleGestureDetector detector) { - clearContextualState(); - } - - public void clear() { - clearContextualState(); - mScaling = false; - mPanning = false; - } - - private void clearContextualState() { - mCurrScaleFactor = 1.0f; - mPrevScaleFactor = 1.0f; - mPrevPan = 0; - mCurrPan = 0; - mInitialFocus.set(Float.NaN, Float.NaN); - mPrevFocus.set(Float.NaN, Float.NaN); - mCurrScale = Float.NaN; - mScaleFocusX = Float.NaN; - mScaleFocusY = Float.NaN; - } - - private void performPan(ScaleGestureDetector detector) { - if (Float.compare(mPrevFocus.x, Float.NaN) == 0 - && Float.compare(mPrevFocus.y, Float.NaN) == 0) { - mPrevFocus.set(detector.getFocusX(), detector.getFocusY()); - return; + public boolean onScroll(MotionEvent first, MotionEvent second, float distanceX, + float distanceY) { + if (mCurrentState != STATE_MAGNIFIED_INTERACTION) { + return true; } final float scale = mMagnificationController.getScale(); - final float scrollX = (detector.getFocusX() - mPrevFocus.x) / scale; - final float scrollY = (detector.getFocusY() - mPrevFocus.y) / scale; - final float centerX = mMagnificationController.getMagnifiedRegionCenterX() - - scrollX; - final float centerY = mMagnificationController.getMagnifiedRegionCenterY() - - scrollY; + final float scrollX = distanceX / scale; + final float scrollY = distanceY / scale; + final float centerX = mMagnificationController.getMagnifiedRegionCenterX() + scrollX; + final float centerY = mMagnificationController.getMagnifiedRegionCenterY() + scrollY; if (DEBUG_PANNING) { Slog.i(LOG_TAG, "Panned content by scrollX: " + scrollX + " scrollY: " + scrollY); } mMagnificationController.setMagnifiedRegionCenter(centerX, centerY, false); - mPrevFocus.set(detector.getFocusX(), detector.getFocusY()); + return true; } - private void performScale(ScaleGestureDetector detector) { - if (Float.compare(mCurrScale, Float.NaN) == 0) { - mCurrScale = mMagnificationController.getScale(); - return; + @Override + public boolean onScale(ScaleGestureDetector detector) { + if (mCurrentState != STATE_MAGNIFIED_INTERACTION) { + return true; } - final float totalScaleFactor = mPrevScaleFactor * detector.getScaleFactor(); - final float newScale = mCurrScale * totalScaleFactor; - final float normalizedNewScale = Math.min(Math.max(newScale, MIN_SCALE), - MAX_SCALE); + final float newScale = mMagnificationController.getScale() + * detector.getScaleFactor(); + final float normalizedNewScale = Math.min(Math.max(newScale, MIN_SCALE), MAX_SCALE); if (DEBUG_SCALING) { Slog.i(LOG_TAG, "normalizedNewScale: " + normalizedNewScale); } - if (Float.compare(mScaleFocusX, Float.NaN) == 0 - && Float.compare(mScaleFocusY, Float.NaN) == 0) { + if (mScaleFocusX < 0 && mScaleFocusY < 0) { mScaleFocusX = detector.getFocusX(); mScaleFocusY = detector.getFocusY(); } mMagnificationController.setScale(normalizedNewScale, mScaleFocusX, mScaleFocusY, false); + return true; + } + + @Override + public boolean onScaleBegin(ScaleGestureDetector detector) { + return (mCurrentState == STATE_MAGNIFIED_INTERACTION); + } + + @Override + public void onScaleEnd(ScaleGestureDetector detector) { + clear(); + } + + private void clear() { + mScaleFocusX = -1; + mScaleFocusY = -1; } } @@ -1870,482 +1788,4 @@ public final class ScreenMagnifier implements EventStreamTransformation { updateDisplayInfo(); } } - - /** - * The listener for receiving notifications when gestures occur. - * If you want to listen for all the different gestures then implement - * this interface. If you only want to listen for a subset it might - * be easier to extend {@link SimpleOnScaleGestureListener}. - * - * An application will receive events in the following order: - *
Applications should pass a complete and consistent event stream to this method. - * A complete and consistent event stream involves all MotionEvents from the initial - * ACTION_DOWN to the final ACTION_UP or ACTION_CANCEL.
- * - * @param event The event to process - * @return true if the event was processed and the detector wants to receive the - * rest of the MotionEvents in this event stream. - */ - public boolean onTouchEvent(MotionEvent event) { - boolean streamEnded = false; - boolean contextChanged = false; - int excludedPtrIdx = -1; - final int action = event.getActionMasked(); - switch (action) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_POINTER_DOWN: { - contextChanged = true; - } break; - case MotionEvent.ACTION_POINTER_UP: { - contextChanged = true; - excludedPtrIdx = event.getActionIndex(); - } break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: { - streamEnded = true; - } break; - } - - if (mInProgress && (contextChanged || streamEnded)) { - mListener.onScaleEnd(this); - mInProgress = false; - mPrevSpan = 0; - mPrevSpanX = 0; - mPrevSpanY = 0; - return true; - } - - final long currTime = mCurrTime; - - mFocusX = 0; - mFocusY = 0; - mCurrSpan = 0; - mCurrSpanX = 0; - mCurrSpanY = 0; - mCurrTime = 0; - mPrevTime = 0; - - if (!streamEnded) { - MinCircleFinder.Circle circle = - mMinCircleFinder.computeMinCircleAroundPointers(event); - mFocusX = circle.centerX; - mFocusY = circle.centerY; - - double sumSlope = 0; - final int pointerCount = event.getPointerCount(); - for (int i = 0; i < pointerCount; i++) { - if (i == excludedPtrIdx) { - continue; - } - float x = event.getX(i) - mFocusX; - float y = event.getY(i) - mFocusY; - if (x == 0) { - x += 0.1f; - } - sumSlope += y / x; - } - final double avgSlope = sumSlope - / ((excludedPtrIdx < 0) ? pointerCount : pointerCount - 1); - - double angle = Math.atan(avgSlope); - mCurrSpan = 2 * circle.radius; - mCurrSpanX = (float) Math.abs((Math.cos(angle) * mCurrSpan)); - mCurrSpanY = (float) Math.abs((Math.sin(angle) * mCurrSpan)); - } - - if (contextChanged || mPrevSpan == 0 || mPrevSpanX == 0 || mPrevSpanY == 0) { - mPrevSpan = mCurrSpan; - mPrevSpanX = mCurrSpanX; - mPrevSpanY = mCurrSpanY; - } - - if (!mInProgress && mCurrSpan != 0 && !streamEnded) { - mInProgress = mListener.onScaleBegin(this); - } - - if (mInProgress) { - mPrevTime = (currTime != 0) ? currTime : event.getEventTime(); - mCurrTime = event.getEventTime(); - if (mCurrSpan == 0) { - mListener.onScaleEnd(this); - mInProgress = false; - } else { - if (mListener.onScale(this)) { - mPrevSpanX = mCurrSpanX; - mPrevSpanY = mCurrSpanY; - mPrevSpan = mCurrSpan; - } - } - } - - return true; - } - - /** - * Returns {@code true} if a scale gesture is in progress. - */ - public boolean isInProgress() { - return mInProgress; - } - - /** - * Get the X coordinate of the current gesture's focal point. - * If a gesture is in progress, the focal point is between - * each of the pointers forming the gesture. - * - * If {@link #isInProgress()} would return false, the result of this - * function is undefined. - * - * @return X coordinate of the focal point in pixels. - */ - public float getFocusX() { - return mFocusX; - } - - /** - * Get the Y coordinate of the current gesture's focal point. - * If a gesture is in progress, the focal point is between - * each of the pointers forming the gesture. - * - * If {@link #isInProgress()} would return false, the result of this - * function is undefined. - * - * @return Y coordinate of the focal point in pixels. - */ - public float getFocusY() { - return mFocusY; - } - - /** - * Return the average distance between each of the pointers forming the - * gesture in progress through the focal point. - * - * @return Distance between pointers in pixels. - */ - public float getCurrentSpan() { - return mCurrSpan; - } - - /** - * Return the average X distance between each of the pointers forming the - * gesture in progress through the focal point. - * - * @return Distance between pointers in pixels. - */ - public float getCurrentSpanX() { - return mCurrSpanX; - } - - /** - * Return the average Y distance between each of the pointers forming the - * gesture in progress through the focal point. - * - * @return Distance between pointers in pixels. - */ - public float getCurrentSpanY() { - return mCurrSpanY; - } - - /** - * Return the previous average distance between each of the pointers forming the - * gesture in progress through the focal point. - * - * @return Previous distance between pointers in pixels. - */ - public float getPreviousSpan() { - return mPrevSpan; - } - - /** - * Return the previous average X distance between each of the pointers forming the - * gesture in progress through the focal point. - * - * @return Previous distance between pointers in pixels. - */ - public float getPreviousSpanX() { - return mPrevSpanX; - } - - /** - * Return the previous average Y distance between each of the pointers forming the - * gesture in progress through the focal point. - * - * @return Previous distance between pointers in pixels. - */ - public float getPreviousSpanY() { - return mPrevSpanY; - } - - /** - * Return the scaling factor from the previous scale event to the current - * event. This value is defined as - * ({@link #getCurrentSpan()} / {@link #getPreviousSpan()}). - * - * @return The current scaling factor. - */ - public float getScaleFactor() { - return mPrevSpan > 0 ? mCurrSpan / mPrevSpan : 1; - } - - /** - * Return the time difference in milliseconds between the previous - * accepted scaling event and the current scaling event. - * - * @return Time difference since the last scaling event in milliseconds. - */ - public long getTimeDelta() { - return mCurrTime - mPrevTime; - } - - /** - * Return the event time of the current event being processed. - * - * @return Current event time in milliseconds. - */ - public long getEventTime() { - return mCurrTime; - } - } - - private static final class MinCircleFinder { - private final ArrayList