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

Commit 921a2d79 authored by Roy Chou's avatar Roy Chou
Browse files

feat(magnification): do not always turn off magnification when features...

feat(magnification): do not always turn off magnification when features changing in AccessibilityInputFilter

Per b/384855743, when talkback on/off the AccessibilityInputFilter
recreates the MagnificationGestureHandler, but when handler#onDestroy
it reset the magnification. This also happens when turn on/off the s2s
FAB. The behavior may not be expected since the user action is not
related to the magnification, and the service still support the
magnification functionality, so maybe we should keep the magnification
zooming for these scenario.

Thus, when AccessibilityInputFilter try to destroy and recreate a new
MagnificationGestureHandler, we first check the magnification status
and decide whether the destory needs to reset the magnification. If not,
in MagnificationGestureHandler#onDestroy, we will not reset the binded
magnification controller.

Bug: 417633371
Test: manually flip flags
      atest AccessibilityInputFilterTest
Flag: com.android.server.accessibility.only_reset_magnification_if_needed_when_destroy_handler
Change-Id: Ia33330ca628ab8102f044667d0b15e9fcd379d4e
parent 5d4bb243
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -236,6 +236,16 @@ flag {
    }
}

flag {
    name: "only_reset_magnification_if_needed_when_destroy_handler"
    namespace: "accessibility"
    description: "Don't reset the activated magnification if the magnification feature is still enabled when AccessibilityInputFilter recreates gesture handler."
    bug: "417633371"
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}

flag {
    name: "pointer_up_motion_event_in_touch_exploration"
    namespace: "accessibility"
+75 −20
Original line number Diff line number Diff line
@@ -81,6 +81,16 @@ public class AccessibilityInputFilter extends InputFilter implements EventStream

    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    /**
     * Flag for disabling all InputFilter features.
     *
     * <p>
     * This flag is used to disable all the enabled features, so it should not be used with other
     * flags.
     * <p>
     */
    private static final int FLAG_FEATURE_NONE = 0x00000000;

    /**
     * Flag for enabling the screen magnification feature.
     *
@@ -199,8 +209,7 @@ public class AccessibilityInputFilter extends InputFilter implements EventStream

    private final SparseArray<TouchExplorer> mTouchExplorer = new SparseArray<>(0);

    private final SparseArray<MagnificationGestureHandler> mMagnificationGestureHandler =
            new SparseArray<>(0);
    private final SparseArray<MagnificationGestureHandler> mMagnificationGestureHandler;

    @Nullable
    private FullScreenMagnificationPointerMotionEventFilter
@@ -292,16 +301,18 @@ public class AccessibilityInputFilter extends InputFilter implements EventStream
    }

    AccessibilityInputFilter(Context context, AccessibilityManagerService service) {
        this(context, service, new SparseArray<>(0));
        this(context, service, new SparseArray<>(0), new SparseArray<>(0));
    }

    AccessibilityInputFilter(Context context, AccessibilityManagerService service,
            SparseArray<EventStreamTransformation> eventHandler) {
            SparseArray<EventStreamTransformation> eventHandler,
            SparseArray<MagnificationGestureHandler> magnificationGestureHandler) {
        super(context.getMainLooper());
        mContext = context;
        mAms = service;
        mPm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        mEventHandler = eventHandler;
        mMagnificationGestureHandler = magnificationGestureHandler;
    }

    @Override
@@ -310,7 +321,7 @@ public class AccessibilityInputFilter extends InputFilter implements EventStream
            Slog.d(TAG, "Accessibility input filter installed.");
        }
        mInstalled = true;
        disableFeatures();
        disableFeatures(/* featuresToBeEnabled= */ FLAG_FEATURE_NONE);
        enableFeatures();
        mAms.onInputFilterInstalled(true);
        super.onInstalled();
@@ -322,7 +333,7 @@ public class AccessibilityInputFilter extends InputFilter implements EventStream
            Slog.d(TAG, "Accessibility input filter uninstalled.");
        }
        mInstalled = false;
        disableFeatures();
        disableFeatures(/* featuresToBeEnabled= */ FLAG_FEATURE_NONE);
        mAms.onInputFilterInstalled(false);
        super.onUninstalled();
    }
@@ -615,7 +626,7 @@ public class AccessibilityInputFilter extends InputFilter implements EventStream
            return;
        }
        if (mInstalled) {
            disableFeatures();
            disableFeatures(/* featuresToBeEnabled= */ enabledFeatures);
        }
        mUserId = userId;
        mEnabledFeatures = enabledFeatures;
@@ -731,7 +742,7 @@ public class AccessibilityInputFilter extends InputFilter implements EventStream
                    });
        }

        if (isAnyMagnificationEnabled()) {
        if (isAnyMagnificationEnabled(mEnabledFeatures)) {
            final MagnificationGestureHandler magnificationGestureHandler =
                    createMagnificationGestureHandler(displayId, displayContext);
            addFirstEventHandler(displayId, magnificationGestureHandler);
@@ -770,22 +781,30 @@ public class AccessibilityInputFilter extends InputFilter implements EventStream
            addFirstEventHandler(Display.DEFAULT_DISPLAY, mMouseKeysInterceptor);
        }

        if (Flags.enableMagnificationKeyboardControl() && isAnyMagnificationEnabled()) {
        if (Flags.enableMagnificationKeyboardControl()
                && isAnyMagnificationEnabled(mEnabledFeatures)) {
            mMagnificationKeyHandler = new MagnificationKeyHandler(
                    mAms.getMagnificationController());
            addFirstEventHandler(Display.DEFAULT_DISPLAY, mMagnificationKeyHandler);
        }
    }

    private boolean isAnyMagnificationEnabled() {
        return (mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
                || ((mEnabledFeatures & FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP) != 0)
                || ((mEnabledFeatures & FLAG_FEATURE_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP) != 0)
                || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0);
    /**
     * Checks if any magnification feature is enabled.
     *
     * @param enabledFeatures An integer bitmask representing all enabled accessibility features.
     * @return {@code true} if at least one magnification feature flag is set,
     *         {@code false} otherwise.
     */
    private boolean isAnyMagnificationEnabled(int enabledFeatures) {
        return (enabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
                || ((enabledFeatures & FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP) != 0)
                || ((enabledFeatures & FLAG_FEATURE_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP) != 0)
                || ((enabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0);
    }

    private boolean isAnyFullScreenMagnificationEnabled() {
        if (!isAnyMagnificationEnabled()) {
        if (!isAnyMagnificationEnabled(mEnabledFeatures)) {
            return false;
        }
        for (final Display display : mAms.getValidDisplayList()) {
@@ -815,11 +834,19 @@ public class AccessibilityInputFilter extends InputFilter implements EventStream
        mEventHandler.put(displayId, eventHandler);
    }

    private void disableFeatures() {
    /**
     * Disables accessibility features, potentially with different handling based on
     * features that will be enabled subsequently.
     *
     * @param featuresToBeEnabled Features that are expected to be enabled *after* the disabling
     *                            operation has finished.
     *                            See {@link #disableFeaturesForDisplay(int, int)}
     */
    private void disableFeatures(int featuresToBeEnabled) {
        final ArrayList<Display> displaysList = mAms.getValidDisplayList();

        for (int i = displaysList.size() - 1; i >= 0; i--) {
            disableFeaturesForDisplay(displaysList.get(i).getDisplayId());
            disableFeaturesForDisplay(displaysList.get(i).getDisplayId(), featuresToBeEnabled);
        }
        mAms.setMotionEventInjectors(null);
        disableDisplayIndependentFeatures();
@@ -829,7 +856,24 @@ public class AccessibilityInputFilter extends InputFilter implements EventStream
        registerPointerMotionFilter(/* enabled= */ false);
    }

    private void disableFeaturesForDisplay(int displayId) {
    /**
     * Disables accessibility features specifically for a given display.
     *
     * <p>
     * The {@code featuresToBeEnabled} parameter influences the disabling process.
     * It provides context about which features are expected to be active immediately
     * after this disabling operation completes. This allows for conditional logic during
     * disablement; for example, certain states might not be fully reset if the corresponding
     * feature is intended to remain active or be re-enabled shortly. An example is
     * not resetting magnification if the magnification feature flag is present in
     * {@code featuresToBeEnabled}, even when its gesture handler is being destroyed.
     * </p>
     *
     * @param displayId The ID of the display for which features should be disabled.
     * @param featuresToBeEnabled Features that are expected to be enabled *after* the disabling
     *                            operation has finished.
     */
    private void disableFeaturesForDisplay(int displayId, int featuresToBeEnabled) {
        if (DEBUG) {
            Slog.i(TAG, "disableFeaturesForDisplay() : display Id = " + displayId);
        }
@@ -848,7 +892,16 @@ public class AccessibilityInputFilter extends InputFilter implements EventStream

        final MagnificationGestureHandler handler = mMagnificationGestureHandler.get(displayId);
        if (handler != null) {
            handler.onDestroy();
            if (Flags.onlyResetMagnificationIfNeededWhenDestroyHandler()) {
                // With the given enabledFeatures parameter if the magnification feature is still
                // enabled, which means after the disabling there is a recreating coming, so the
                // magnification reset is not needed.
                handler.onDestroy(
                        /* resetMagnification= */ !isAnyMagnificationEnabled(featuresToBeEnabled));
            } else {
                // The old behavior is always resetting the magnification when destroying handler
                handler.onDestroy(/* resetMagnification= */ true);
            }
            mMagnificationGestureHandler.remove(displayId);
        }

@@ -857,15 +910,17 @@ public class AccessibilityInputFilter extends InputFilter implements EventStream
            mEventHandler.remove(displayId);
        }
    }

    void enableFeaturesForDisplayIfInstalled(Display display) {
        if (mInstalled) {
            resetStreamStateForDisplay(display.getDisplayId());
            enableFeaturesForDisplay(display);
        }
    }

    void disableFeaturesForDisplayIfInstalled(int displayId) {
        if (mInstalled) {
            disableFeaturesForDisplay(displayId);
            disableFeaturesForDisplay(displayId, /* featuresToBeEnabled= */ FLAG_FEATURE_NONE);
            resetStreamStateForDisplay(displayId);
        }
    }
+7 −4
Original line number Diff line number Diff line
@@ -378,7 +378,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
    }

    @Override
    public void onDestroy() {
    public void onDestroy(boolean resetMagnification) {
        if (DEBUG_STATE_TRANSITIONS) {
            Slog.i(mLogTag, "onDestroy(); delayed = "
                    + MotionEventInfo.toString(mDetectingState.mDelayedEventQueue));
@@ -389,9 +389,12 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
            mScreenStateReceiver.unregister();
        }
        mPromptController.onDestroy();
        // Check if need to reset when MagnificationGestureHandler is the last magnifying service.
        if (resetMagnification) {
            // Check if need to reset when MagnificationGestureHandler is the last magnifying
            // service.
            mFullScreenMagnificationController.resetIfNeeded(
                    mDisplayId, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
        }
        mFullScreenMagnificationController.removeInfoChangedCallback(
                mMagnificationInfoChangedCallback);
        clearAndTransitionToStateDetecting();
+13 −0
Original line number Diff line number Diff line
@@ -113,6 +113,19 @@ public abstract class MagnificationGestureHandler extends BaseEventStreamTransfo
        mDebugOutputEventHistory = DEBUG_EVENT_STREAM ? new ArrayDeque<>() : null;
    }

    @Override
    public void onDestroy() {
        onDestroy(/* resetMagnification= */ true);
    }

    /**
     * Destroys this magnification gesture handler.
     * @param resetMagnification Whether to reset the connected magnification when destroying.
     */
    public void onDestroy(boolean resetMagnification) {
        super.onDestroy();
    }

    @Override
    public final void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
        if (DEBUG_ALL) {
+4 −2
Original line number Diff line number Diff line
@@ -170,12 +170,14 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
    }

    @Override
    public void onDestroy() {
    public void onDestroy(boolean resetMagnification) {
        if (DEBUG_ALL) {
            Slog.i(mLogTag, "onDestroy(); delayed = "
                    + mDetectingState.toString());
        }
        if (resetMagnification) {
            mMagnificationConnectionManager.disableWindowMagnification(mDisplayId, true);
        }
        resetToDetectState();
    }

Loading