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

Commit 379e238c authored by ryanlwlin's avatar ryanlwlin
Browse files

Implement transitn animation while switching the mode

First we get the current center of magnification bounds and the scale,
then apply them after the disabling animation is completed.

If disabling animation is interrupted, we will fall back to previous
mode or force to switch the mode directly.

Bug: 161669184
Test: atest com.android.server.accessibility.magnification
      atest AccessibilityManagerServiceTest
Change-Id: Ic12d6d907f91bf251aa67a6ab959d4fbcf660f63
parent 6c4ffacb
Loading
Loading
Loading
Loading
+30 −4
Original line number Diff line number Diff line
@@ -120,6 +120,7 @@ import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.accessibility.magnification.FullScreenMagnificationController;
import com.android.server.accessibility.magnification.MagnificationGestureHandler;
import com.android.server.accessibility.magnification.MagnificationTransitionController;
import com.android.server.accessibility.magnification.WindowMagnificationManager;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -258,6 +259,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub

    private Point mTempPoint = new Point();
    private boolean mIsAccessibilityButtonShown;
    private MagnificationTransitionController mMagnificationTransitionController;

    private AccessibilityUserState getCurrentUserStateLocked() {
        return getUserStateLocked(mCurrentUserId);
@@ -302,6 +304,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
        mA11yWindowManager = a11yWindowManager;
        mA11yDisplayListener = a11yDisplayListener;
        mWindowMagnificationMgr = windowMagnificationMgr;
        mMagnificationTransitionController = new MagnificationTransitionController(this, mLock);
        init();
    }

@@ -321,6 +324,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
        mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
                mWindowManagerService, this, mSecurityPolicy, this);
        mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
        mMagnificationTransitionController = new MagnificationTransitionController(this, mLock);
        init();
    }

@@ -1550,10 +1554,27 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
        if (fallBackMagnificationModeSettingsLocked(userState)) {
            return;
        }
        mMagnificationTransitionController.transitionMagnificationModeLocked(
                Display.DEFAULT_DISPLAY, userState.getMagnificationModeLocked(),
                this::onMagnificationTransitionEndedLocked);
    }

    /**
     * Called when the magnification mode transition is completed.
     */
    void onMagnificationTransitionEndedLocked(boolean success) {
        final AccessibilityUserState userState = getCurrentUserStateLocked();
        final int previousMode = userState.getMagnificationModeLocked()
                ^ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
        if (!success && previousMode != 0) {
            userState.setMagnificationModeLocked(previousMode);
            persistMagnificationModeSettingLocked(previousMode);
        } else {
            mMainHandler.sendMessage(obtainMessage(
                    AccessibilityManagerService::notifyRefreshMagnificationModeToInputFilter,
                    this));
        }
    }

    private void notifyRefreshMagnificationModeToInputFilter() {
        synchronized (mLock) {
@@ -2962,7 +2983,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
        getWindowMagnificationMgr().setConnection(connection);
    }

    WindowMagnificationManager getWindowMagnificationMgr() {
    /**
     * Getter of {@link WindowMagnificationManager}.
     *
     * @return WindowMagnificationManager
     */
    public WindowMagnificationManager getWindowMagnificationMgr() {
        synchronized (mLock) {
            if (mWindowMagnificationMgr == null) {
                mWindowMagnificationMgr = new WindowMagnificationManager(mContext, mCurrentUserId);
+237 −0
Original line number 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.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.provider.Settings;
import android.util.Slog;
import android.util.SparseArray;
import android.view.accessibility.MagnificationAnimationCallback;

import com.android.server.accessibility.AccessibilityManagerService;

/**
 * Handles magnification mode transition.
 */
public class MagnificationTransitionController {

    private static final boolean DEBUG = false;
    private static final String TAG = "MagnificationController";
    private final AccessibilityManagerService mAms;
    private final PointF mTempPoint = new PointF();
    private final Object mLock;
    private final SparseArray<DisableMagnificationCallback>
            mMagnificationEndRunnableSparseArray = new SparseArray();


    /**
     * A callback to inform the magnification transition result.
     */
    public interface TransitionCallBack {
        /**
         * Invoked when the transition ends.
         * @param success {@code true} if the transition success.
         */
        void onResult(boolean success);
    }

    public MagnificationTransitionController(AccessibilityManagerService ams, Object lock) {
        mAms = ams;
        mLock = lock;
    }

    /**
     * Transitions to the target Magnification mode with current center of the magnification mode
     * if it is available.
     *
     * @param displayId The logical display
     * @param targetMode The target magnification mode
     * @param transitionCallBack The callback invoked when the transition is finished.
     */
    public void transitionMagnificationModeLocked(int displayId, int targetMode,
            @NonNull TransitionCallBack transitionCallBack) {
        final PointF magnificationCenter = getCurrentMagnificationBoundsCenterLocked(displayId,
                targetMode);
        final DisableMagnificationCallback animationCallback =
                getDisableMagnificationEndRunnableLocked(displayId);
        if (magnificationCenter == null && animationCallback == null) {
            transitionCallBack.onResult(true);
            return;
        }

        if (animationCallback != null) {
            if (animationCallback.mCurrentMode == targetMode) {
                animationCallback.restoreToCurrentMagnificationMode();
                return;
            }
            Slog.w(TAG, "request during transition, abandon current:"
                    + animationCallback.mTargetMode);
            animationCallback.setExpiredAndRemoveFromListLocked();
        }

        if (magnificationCenter == null) {
            Slog.w(TAG, "Invalid center, ignore it");
            transitionCallBack.onResult(true);
            return;
        }
        final FullScreenMagnificationController screenMagnificationController =
                getFullScreenMagnificationController();
        final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationManager();
        final float scale = windowMagnificationMgr.getPersistedScale();
        final DisableMagnificationCallback animationEndCallback =
                new DisableMagnificationCallback(transitionCallBack, displayId, targetMode,
                        scale, magnificationCenter);
        if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
            screenMagnificationController.reset(displayId, animationEndCallback);
        } else {
            windowMagnificationMgr.disableWindowMagnification(displayId, false,
                    animationEndCallback);
        }
        setDisableMagnificationCallbackLocked(displayId, animationEndCallback);
    }

    private DisableMagnificationCallback getDisableMagnificationEndRunnableLocked(
            int displayId) {
        return mMagnificationEndRunnableSparseArray.get(displayId);
    }

    private void setDisableMagnificationCallbackLocked(int displayId,
            @Nullable DisableMagnificationCallback callback) {
        mMagnificationEndRunnableSparseArray.put(displayId, callback);
        if (DEBUG) {
            Slog.d(TAG, "setDisableMagnificationCallbackLocked displayId = " + displayId
                    + ", callback = " + callback);
        }
    }

    private FullScreenMagnificationController getFullScreenMagnificationController() {
        return mAms.getFullScreenMagnificationController();
    }

    private WindowMagnificationManager getWindowMagnificationManager() {
        return mAms.getWindowMagnificationMgr();
    }

    private @Nullable
            PointF getCurrentMagnificationBoundsCenterLocked(int displayId, int targetMode) {
        if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
            final WindowMagnificationManager magnificationManager = getWindowMagnificationManager();
            if (!magnificationManager.isWindowMagnifierEnabled(displayId)) {
                return null;
            }
            mTempPoint.set(magnificationManager.getCenterX(displayId),
                    magnificationManager.getCenterY(displayId));
        } else {
            final FullScreenMagnificationController screenMagnificationController =
                    getFullScreenMagnificationController();
            if (!screenMagnificationController.isMagnifying(displayId)) {
                return null;
            }
            mTempPoint.set(screenMagnificationController.getCenterX(displayId),
                    screenMagnificationController.getCenterY(displayId));
        }
        return mTempPoint;
    }

    private final class DisableMagnificationCallback implements
            MagnificationAnimationCallback {
        private final TransitionCallBack mTransitionCallBack;
        private boolean mExpired = false;
        private final int mDisplayId;
        private final int mTargetMode;
        private final int mCurrentMode;
        private final float mCurrentScale;
        private final PointF mCurrentCenter = new PointF();

        DisableMagnificationCallback(TransitionCallBack transitionCallBack,
                int displayId, int targetMode, float scale, PointF currentCenter) {
            mTransitionCallBack = transitionCallBack;
            mDisplayId = displayId;
            mTargetMode = targetMode;
            mCurrentMode = mTargetMode ^ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
            mCurrentScale = scale;
            mCurrentCenter.set(currentCenter);
        }

        @Override
        public void onResult(boolean success) {
            synchronized (mLock) {
                if (DEBUG) {
                    Slog.d(TAG, "onResult success = " + success);
                }
                if (mExpired) {
                    return;
                }
                setExpiredAndRemoveFromListLocked();
                if (success) {
                    adjustCurrentCenterIfNeededLocked();
                    applyMagnificationModeLocked(mTargetMode);
                }
                mTransitionCallBack.onResult(success);
            }
        }

        private void adjustCurrentCenterIfNeededLocked() {
            if (mTargetMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
                return;
            }
            final Region outRegion = new Region();
            getFullScreenMagnificationController().getMagnificationRegion(mDisplayId, outRegion);
            if (outRegion.contains((int) mCurrentCenter.x, (int) mCurrentCenter.y)) {
                return;
            }
            final Rect bounds = outRegion.getBounds();
            mCurrentCenter.set(bounds.exactCenterX(), bounds.exactCenterY());
        }

        void restoreToCurrentMagnificationMode() {
            synchronized (mLock) {
                if (mExpired) {
                    return;
                }
                setExpiredAndRemoveFromListLocked();
                applyMagnificationModeLocked(mCurrentMode);
                mTransitionCallBack.onResult(true);
            }
        }

        void setExpiredAndRemoveFromListLocked() {
            mExpired = true;
            setDisableMagnificationCallbackLocked(mDisplayId, null);
        }

        private void applyMagnificationModeLocked(int mode) {
            if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
                getFullScreenMagnificationController().setScaleAndCenter(mDisplayId,
                        mCurrentScale, mCurrentCenter.x,
                        mCurrentCenter.y, true,
                        AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
            } else {
                getWindowMagnificationManager().enableWindowMagnification(mDisplayId,
                        mCurrentScale, mCurrentCenter.x,
                        mCurrentCenter.y);
            }
        }
    }
}
+54 −8
Original line number Diff line number Diff line
@@ -302,7 +302,8 @@ public class WindowMagnificationManager implements
     * @param displayId The logical display id.
     * @return {@code true} if the window magnification is enabled.
     */
    boolean isWindowMagnifierEnabled(int displayId) {
    @VisibleForTesting
    public boolean isWindowMagnifierEnabled(int displayId) {
        synchronized (mLock) {
            WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
            if (magnifier == null) {
@@ -397,6 +398,38 @@ public class WindowMagnificationManager implements
                displayId);
    }

    /**
     * Returns the screen-relative X coordinate of the center of the magnified bounds.
     *
     * @param displayId The logical display id
     * @return the X coordinate. {@link Float#NaN} if the window magnification is not enabled.
     */
    float getCenterX(int displayId) {
        synchronized (mLock) {
            WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
            if (magnifier == null) {
                return Float.NaN;
            }
            return magnifier.getCenterX();
        }
    }

    /**
     * Returns the screen-relative Y coordinate of the center of the magnified bounds.
     *
     * @param displayId The logical display id
     * @return the Y coordinate. {@link Float#NaN} if the window magnification is not enabled.
     */
    float getCenterY(int displayId) {
        synchronized (mLock) {
            WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
            if (magnifier == null) {
                return Float.NaN;
            }
            return magnifier.getCenterY();
        }
    }

    /**
     * Creates the windowMagnifier based on the specified display and stores it.
     *
@@ -436,12 +469,14 @@ public class WindowMagnificationManager implements

        @Override
        public void onSourceBoundsChanged(int displayId, Rect sourceBounds) {
            synchronized (mLock) {
                WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
                if (magnifier == null) {
                    magnifier = createWindowMagnifier(displayId);
                }
                magnifier.onSourceBoundsChanged(sourceBounds);
            }
        }

        @Override
        public void binderDied() {
@@ -553,9 +588,20 @@ public class WindowMagnificationManager implements
            mEnabled = false;
        }

        @GuardedBy("mLock")
        public void onSourceBoundsChanged(Rect sourceBounds) {
            mSourceBounds.set(sourceBounds);
        }

        @GuardedBy("mLock")
        float getCenterX() {
            return mEnabled ? mSourceBounds.exactCenterX() : Float.NaN;
        }

        @GuardedBy("mLock")
        float getCenterY() {
            return mEnabled ? mSourceBounds.exactCenterY() : Float.NaN;
        }
    }

    private boolean enableWindowMagnificationInternal(int displayId, float scale, float centerX,
+15 −0
Original line number Diff line number Diff line
@@ -230,4 +230,19 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
        verify(mMockWindowMagnificationMgr, never()).showMagnificationButton(anyInt(),
                anyInt());
    }

    @SmallTest
    public void testOnMagnificationTransitionFailed_capabilitiesIsAll_fallBackToPreviousMode() {
        final AccessibilityUserState userState = mA11yms.mUserStates.get(
                mA11yms.getCurrentUserIdLocked());
        userState.setMagnificationCapabilitiesLocked(
                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
        userState.setMagnificationModeLocked(
                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);

        mA11yms.onMagnificationTransitionEndedLocked(false);

        assertEquals(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
                userState.getMagnificationModeLocked());
    }
}
+279 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading