Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d3e313ea authored by ryanlwlin's avatar ryanlwlin
Browse files

Add WindowMagnificationGestureHandler for magnification

WindowMagnificationGestureHandler detects gestures to determine the
user is going to manipulate window magnifier. If the user is going to
interact with current UI, it will sends the delayed events, otherwise
it will change the scale or move the window magnifer based on user's
gesture.

Bug: 146400227
Test: manual test & WindowMagnificationManagerTest
Test atest WindowMagnificationGestureHandlerTest

Change-Id: I185005d1ebb00a4d3956104fc78fab7d46777d4d
parent 08087d07
Loading
Loading
Loading
Loading
+4 −1
Original line number Original line Diff line number Diff line
@@ -1054,6 +1054,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
            // The user changed.
            // The user changed.
            mCurrentUserId = userId;
            mCurrentUserId = userId;


            if (mWindowMagnificationMgr != null) {
                mWindowMagnificationMgr.setUserId(mCurrentUserId);
            }
            AccessibilityUserState userState = getCurrentUserStateLocked();
            AccessibilityUserState userState = getCurrentUserStateLocked();


            readConfigurationForUserStateLocked(userState);
            readConfigurationForUserStateLocked(userState);
@@ -2641,7 +2644,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
    WindowMagnificationManager getWindowMagnificationMgr() {
    WindowMagnificationManager getWindowMagnificationMgr() {
        synchronized (mLock) {
        synchronized (mLock) {
            if (mWindowMagnificationMgr == null) {
            if (mWindowMagnificationMgr == null) {
                mWindowMagnificationMgr = new WindowMagnificationManager();
                mWindowMagnificationMgr = new WindowMagnificationManager(mContext, mCurrentUserId);
            }
            }
            return mWindowMagnificationMgr;
            return mWindowMagnificationMgr;
        }
        }
+4 −2
Original line number Original line Diff line number Diff line
@@ -24,8 +24,9 @@ import android.view.ViewConfiguration;
/**
/**
 * This class matches multi-tap gestures. The number of taps for each instance is specified in the
 * This class matches multi-tap gestures. The number of taps for each instance is specified in the
 * constructor.
 * constructor.
 * @hide
 */
 */
class MultiTap extends GestureMatcher {
public class MultiTap extends GestureMatcher {


    // Maximum reasonable number of taps.
    // Maximum reasonable number of taps.
    public static final int MAX_TAPS = 10;
    public static final int MAX_TAPS = 10;
@@ -40,7 +41,8 @@ class MultiTap extends GestureMatcher {
    float mBaseX;
    float mBaseX;
    float mBaseY;
    float mBaseY;


    MultiTap(Context context, int taps, int gesture, GestureMatcher.StateChangeListener listener) {
    public MultiTap(Context context, int taps, int gesture,
            GestureMatcher.StateChangeListener listener) {
        super(gesture, new Handler(context.getMainLooper()), listener);
        super(gesture, new Handler(context.getMainLooper()), listener);
        mTargetTaps = taps;
        mTargetTaps = taps;
        mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
        mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
+3 −2
Original line number Original line Diff line number Diff line
@@ -22,9 +22,10 @@ import android.view.MotionEvent;
/**
/**
 * This class matches gestures of the form multi-tap and hold. The number of taps for each instance
 * This class matches gestures of the form multi-tap and hold. The number of taps for each instance
 * is specified in the constructor.
 * is specified in the constructor.
 * @hide
 */
 */
class MultiTapAndHold extends MultiTap {
public class MultiTapAndHold extends MultiTap {
    MultiTapAndHold(
    public MultiTapAndHold(
            Context context, int taps, int gesture, GestureMatcher.StateChangeListener listener) {
            Context context, int taps, int gesture, GestureMatcher.StateChangeListener listener) {
        super(context, taps, gesture, listener);
        super(context, taps, gesture, listener);
    }
    }
+6 −0
Original line number Original line Diff line number Diff line
@@ -35,6 +35,8 @@ class MagnificationGestureMatcher {
    private static final int GESTURE_BASE = 100;
    private static final int GESTURE_BASE = 100;
    public static final int GESTURE_TWO_FINGER_DOWN = GESTURE_BASE + 1;
    public static final int GESTURE_TWO_FINGER_DOWN = GESTURE_BASE + 1;
    public static final int GESTURE_SWIPE = GESTURE_BASE + 2;
    public static final int GESTURE_SWIPE = GESTURE_BASE + 2;
    public static final int GESTURE_SINGLE_TAP = GESTURE_BASE + 3;
    public static final int GESTURE_SINGLE_TAP_AND_HOLD = GESTURE_BASE + 4;


    @IntDef(prefix = {"GESTURE_MAGNIFICATION_"}, value = {
    @IntDef(prefix = {"GESTURE_MAGNIFICATION_"}, value = {
            GESTURE_TWO_FINGER_DOWN,
            GESTURE_TWO_FINGER_DOWN,
@@ -55,6 +57,10 @@ class MagnificationGestureMatcher {
                return "GESTURE_SWIPE";
                return "GESTURE_SWIPE";
            case GESTURE_TWO_FINGER_DOWN:
            case GESTURE_TWO_FINGER_DOWN:
                return "GESTURE_TWO_FINGER_DOWN";
                return "GESTURE_TWO_FINGER_DOWN";
            case GESTURE_SINGLE_TAP:
                return "GESTURE_SINGLE_TAP";
            case GESTURE_SINGLE_TAP_AND_HOLD:
                return "GESTURE_SINGLE_TAP_AND_HOLD";
        }
        }
        return "none";
        return "none";
    }
    }
+96 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2020 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.magnification;

import static android.view.MotionEvent.ACTION_DOWN;

import android.content.Context;
import android.os.SystemClock;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewConfiguration;

import com.android.internal.R;

import java.util.List;

/**
 * Responsible for dispatching delayed events.
 */
class MotionEventDispatcherDelegate {

    private static final String TAG = MotionEventDispatcherDelegate.class.getSimpleName();
    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);

    private final EventDispatcher mEventDispatcher;
    private final int mMultiTapMaxDelay;
    private long mLastDelegatedDownEventTime;

    interface  EventDispatcher {
        void dispatchMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags);
    }

    MotionEventDispatcherDelegate(Context context,
            EventDispatcher eventDispatcher) {
        mEventDispatcher = eventDispatcher;
        mMultiTapMaxDelay = ViewConfiguration.getDoubleTapTimeout()
                + context.getResources().getInteger(
                R.integer.config_screen_magnification_multi_tap_adjustment);
    }

    void sendDelayedMotionEvents(List<MotionEventInfo> delayedEventQueue,
            long lastDetectingDownEventTime) {
        if (delayedEventQueue == null) {
            return;
        }
        // Adjust down time to prevent subsequent modules being misleading, and also limit
        // the maximum offset to mMultiTapMaxDelay to prevent the down time of 2nd tap is
        // in the future when multi-tap happens.
        final long offset = Math.min(
                SystemClock.uptimeMillis() - lastDetectingDownEventTime, mMultiTapMaxDelay);

        for (MotionEventInfo info: delayedEventQueue) {
            info.mEvent.setDownTime(info.mEvent.getDownTime() + offset);
            dispatchMotionEvent(info.mEvent, info.mRawEvent, info.mPolicyFlags);
            info.recycle();
        }
    }

    void dispatchMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
        // Ensure that the state at the end of delegation is consistent with the last delegated
        // UP/DOWN event in queue: still delegating if pointer is down, detecting otherwise
        if (event.getActionMasked() == ACTION_DOWN) {
            mLastDelegatedDownEventTime = event.getDownTime();
            if (DBG) {
                Log.d(TAG, "dispatchMotionEvent mLastDelegatedDownEventTime time = "
                        + mLastDelegatedDownEventTime);
            }
        }

        // 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.
        if (DBG) {
            Log.d(TAG, "dispatchMotionEvent original down time = " + event.getDownTime());
        }
        event.setDownTime(mLastDelegatedDownEventTime);
        mEventDispatcher.dispatchMotionEvent(event, rawEvent, policyFlags);
    }
}
Loading