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

Commit 35f57381 authored by ryanlwlin's avatar ryanlwlin
Browse files

Support individual magnification scale for each display

Currently we only have a globale magnification scale which
is stored in settings. It ends up the scale is shared between
multi-displays. To provide better experience, we store the scale
for each none-default display until the device reboot,
and the default scale is 2.

Test: atest com.android.server.accessibility.magnification
      atest FullScreenMagnificationGestureHandlerTest
Bug: 194667380
Change-Id: I0638fe90ef262d287cec81919baabdf215bb2e48
parent 7933c5da
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -128,6 +128,7 @@ import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.MagnificationProcessor;
import com.android.server.accessibility.magnification.MagnificationScaleProvider;
import com.android.server.accessibility.magnification.WindowMagnificationManager;
import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -338,7 +339,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
        mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
                mWindowManagerService, this, mSecurityPolicy, this, mTraceManager);
        mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
        mMagnificationController = new MagnificationController(this, mLock, mContext);
        mMagnificationController = new MagnificationController(this, mLock, mContext,
                new MagnificationScaleProvider(mContext));
        mMagnificationProcessor = new MagnificationProcessor(mMagnificationController);
        init();
    }
@@ -1364,6 +1366,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
    }

    private void switchUser(int userId) {
        mMagnificationController.updateUserIdIfNeeded(userId);
        synchronized (mLock) {
            if (mCurrentUserId == userId && mInitialized) {
                return;
@@ -1386,8 +1389,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub

            // The user changed.
            mCurrentUserId = userId;

            mMagnificationController.updateUserIdIfNeeded(mCurrentUserId);
            AccessibilityUserState userState = getCurrentUserStateLocked();

            readConfigurationForUserStateLocked(userState);
@@ -1444,6 +1445,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
        synchronized (mLock) {
            mUserStates.remove(userId);
        }
        getMagnificationController().onUserRemoved(userId);
    }

    // Called only during settings restore; currently supports only the owner user
+21 −58
Original line number Diff line number Diff line
@@ -28,10 +28,8 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.MathUtils;
import android.util.Slog;
@@ -59,7 +57,8 @@ import java.util.Locale;
 * holding the current state of magnification and animation, and it handles
 * communication between the accessibility manager and window manager.
 *
 * Magnification is limited to the range [MIN_SCALE, MAX_SCALE], and can only occur inside the
 * Magnification is limited to the range controlled by
 * {@link MagnificationScaleProvider#constrainScale(float)}, and can only occur inside the
 * magnification region. If a value is out of bounds, it will be adjusted to guarantee these
 * constraints.
 */
@@ -69,13 +68,9 @@ public class FullScreenMagnificationController {

    private static final MagnificationAnimationCallback STUB_ANIMATION_CALLBACK = success -> {
    };
    public static final float MIN_SCALE = 1.0f;
    public static final float MAX_SCALE = 8.0f;

    private static final boolean DEBUG_SET_MAGNIFICATION_SPEC = false;

    private static final float DEFAULT_MAGNIFICATION_SCALE = 2.0f;

    private final Object mLock;

    private final ControllerContext mControllerCtx;
@@ -84,7 +79,7 @@ public class FullScreenMagnificationController {

    private final MagnificationInfoChangedCallback mMagnificationInfoChangedCallback;

    private int mUserId;
    private final MagnificationScaleProvider mScaleProvider;

    private final long mMainThreadId;

@@ -489,7 +484,7 @@ public class FullScreenMagnificationController {
                return false;
            }
            // Constrain scale immediately for use in the pivot calculations.
            scale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
            scale = MagnificationScaleProvider.constrainScale(scale);

            final Rect viewport = mTempRect;
            mMagnificationRegion.getBounds(viewport);
@@ -557,7 +552,7 @@ public class FullScreenMagnificationController {
            // Compute changes.
            boolean changed = false;

            final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
            final float normScale = MagnificationScaleProvider.constrainScale(scale);
            if (Float.compare(mCurrentMagnificationSpec.scale, normScale) != 0) {
                mCurrentMagnificationSpec.scale = normScale;
                changed = true;
@@ -658,12 +653,13 @@ public class FullScreenMagnificationController {
     */
    public FullScreenMagnificationController(@NonNull Context context,
            @NonNull AccessibilityManagerService ams, @NonNull Object lock,
            @NonNull MagnificationInfoChangedCallback magnificationInfoChangedCallback) {
            @NonNull MagnificationInfoChangedCallback magnificationInfoChangedCallback,
            @NonNull MagnificationScaleProvider scaleProvider) {
        this(new ControllerContext(context, ams,
                LocalServices.getService(WindowManagerInternal.class),
                new Handler(context.getMainLooper()),
                context.getResources().getInteger(R.integer.config_longAnimTime)), lock,
                magnificationInfoChangedCallback);
                magnificationInfoChangedCallback, scaleProvider);
    }

    /**
@@ -672,12 +668,14 @@ public class FullScreenMagnificationController {
    @VisibleForTesting
    public FullScreenMagnificationController(@NonNull ControllerContext ctx,
            @NonNull Object lock,
            @NonNull MagnificationInfoChangedCallback magnificationInfoChangedCallback) {
            @NonNull MagnificationInfoChangedCallback magnificationInfoChangedCallback,
            @NonNull MagnificationScaleProvider scaleProvider) {
        mControllerCtx = ctx;
        mLock = lock;
        mMainThreadId = mControllerCtx.getContext().getMainLooper().getThread().getId();
        mScreenStateObserver = new ScreenStateObserver(mControllerCtx.getContext(), this);
        mMagnificationInfoChangedCallback = magnificationInfoChangedCallback;
        mScaleProvider = scaleProvider;
    }

    /**
@@ -1096,18 +1094,9 @@ public class FullScreenMagnificationController {
    /**
     * Persists the default display magnification scale to the current user's settings.
     */
    public void persistScale() {
        // TODO: b/123047354, Need support multi-display?
    public void persistScale(int displayId) {
        final float scale = getScale(Display.DEFAULT_DISPLAY);
        final int userId = mUserId;

        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                mControllerCtx.putMagnificationScale(scale, userId);
                return null;
            }
        }.execute();
        mScaleProvider.putScale(scale, displayId);
    }

    /**
@@ -1117,21 +1106,8 @@ public class FullScreenMagnificationController {
     * @return the previously persisted magnification scale, or the default
     *         scale if none is available
     */
    public float getPersistedScale() {
        return mControllerCtx.getMagnificationScale(mUserId);
    }

    /**
     * Sets the currently active user ID.
     *
     * @param userId the currently active user ID
     */
    public void setUserId(int userId) {
        if (mUserId == userId) {
            return;
        }
        mUserId = userId;
        resetAllIfNeeded(false);
    public float getPersistedScale(int displayId) {
        return mScaleProvider.getScale(displayId);
    }

    /**
@@ -1225,7 +1201,11 @@ public class FullScreenMagnificationController {
        mControllerCtx.getHandler().sendMessage(m);
    }

    private void resetAllIfNeeded(boolean animate) {
    /**
     * Resets magnification on all displays.
     * @param animate reset the magnification with animation
     */
    void resetAllIfNeeded(boolean animate) {
        synchronized (mLock) {
            for (int i = 0; i < mDisplays.size(); i++) {
                resetIfNeeded(mDisplays.keyAt(i), animate);
@@ -1288,8 +1268,8 @@ public class FullScreenMagnificationController {
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("MagnificationController[");
        builder.append("mUserId=").append(mUserId);
        builder.append(", mDisplays=").append(mDisplays);
        builder.append(", mScaleProvider=").append(mScaleProvider);
        builder.append("]");
        return builder.toString();
    }
@@ -1569,23 +1549,6 @@ public class FullScreenMagnificationController {
            return new ValueAnimator();
        }

        /**
         * Write Settings of magnification scale.
         */
        public void putMagnificationScale(float value, int userId) {
            Settings.Secure.putFloatForUser(mContext.getContentResolver(),
                    Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, value, userId);
        }

        /**
         * Get Settings of magnification scale.
         */
        public float getMagnificationScale(int userId) {
            return Settings.Secure.getFloatForUser(mContext.getContentResolver(),
                    Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
                    DEFAULT_MAGNIFICATION_SCALE, userId);
        }

        /**
         * @return Configuration of animation duration.
         */
+4 −4
Original line number Diff line number Diff line
@@ -119,11 +119,11 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
    private static final boolean DEBUG_DETECTING = false | DEBUG_ALL;
    private static final boolean DEBUG_PANNING_SCALING = false | DEBUG_ALL;

    // The MIN_SCALE is different from MagnificationController.MIN_SCALE due
    // The MIN_SCALE is different from MagnificationScaleProvider.MIN_SCALE due
    // to AccessibilityService.MagnificationController#setScale() has
    // different scale range
    private static final float MIN_SCALE = 2.0f;
    private static final float MAX_SCALE = FullScreenMagnificationController.MAX_SCALE;
    private static final float MAX_SCALE = MagnificationScaleProvider.MAX_SCALE;

    @VisibleForTesting final FullScreenMagnificationController mFullScreenMagnificationController;

@@ -341,7 +341,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
        }

        public void persistScaleAndTransitionTo(State state) {
            mFullScreenMagnificationController.persistScale();
            mFullScreenMagnificationController.persistScale(mDisplayId);
            clear();
            transitionTo(state);
        }
@@ -945,7 +945,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
        if (DEBUG_DETECTING) Slog.i(mLogTag, "zoomOn(" + centerX + ", " + centerY + ")");

        final float scale = MathUtils.constrain(
                mFullScreenMagnificationController.getPersistedScale(),
                mFullScreenMagnificationController.getPersistedScale(mDisplayId),
                MIN_SCALE, MAX_SCALE);
        mFullScreenMagnificationController.setScaleAndCenter(mDisplayId,
                scale, centerX, centerY,
+38 −13
Original line number Diff line number Diff line
@@ -23,11 +23,13 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import android.util.SparseArray;
@@ -75,12 +77,15 @@ public class MagnificationController implements WindowMagnificationManager.Callb
    private final SparseArray<DisableMagnificationCallback>
            mMagnificationEndRunnableSparseArray = new SparseArray();

    private final MagnificationScaleProvider mScaleProvider;
    private FullScreenMagnificationController mFullScreenMagnificationController;
    private WindowMagnificationManager mWindowMagnificationMgr;
    private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;

    @GuardedBy("mLock")
    private int mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
    // Track the active user to reset the magnification and get the associated user settings.
    private @UserIdInt int mUserId = UserHandle.USER_SYSTEM;
    @GuardedBy("mLock")
    private boolean mImeWindowVisible = false;
    private long mWindowModeEnabledTime = 0;
@@ -98,17 +103,19 @@ public class MagnificationController implements WindowMagnificationManager.Callb
    }

    public MagnificationController(AccessibilityManagerService ams, Object lock,
            Context context) {
            Context context, MagnificationScaleProvider scaleProvider) {
        mAms = ams;
        mLock = lock;
        mContext = context;
        mScaleProvider = scaleProvider;
    }

    @VisibleForTesting
    public MagnificationController(AccessibilityManagerService ams, Object lock,
            Context context, FullScreenMagnificationController fullScreenMagnificationController,
            WindowMagnificationManager windowMagnificationManager) {
        this(ams, lock, context);
            WindowMagnificationManager windowMagnificationManager,
            MagnificationScaleProvider scaleProvider) {
        this(ams, lock, context, scaleProvider);
        mFullScreenMagnificationController = fullScreenMagnificationController;
        mWindowMagnificationMgr = windowMagnificationManager;
    }
@@ -194,7 +201,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
        final FullScreenMagnificationController screenMagnificationController =
                getFullScreenMagnificationController();
        final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationMgr();
        final float scale = windowMagnificationMgr.getPersistedScale();
        final float scale = mScaleProvider.getScale(displayId);
        final DisableMagnificationCallback animationEndCallback =
                new DisableMagnificationCallback(transitionCallBack, displayId, targetMode,
                        scale, magnificationCenter);
@@ -313,13 +320,23 @@ public class MagnificationController implements WindowMagnificationManager.Callb
     * @param userId the currently active user ID
     */
    public void updateUserIdIfNeeded(int userId) {
        if (mUserId == userId) {
            return;
        }
        mUserId = userId;
        final FullScreenMagnificationController fullMagnificationController;
        final WindowMagnificationManager windowMagnificationManager;
        synchronized (mLock) {
            if (mFullScreenMagnificationController != null) {
                mFullScreenMagnificationController.setUserId(userId);
            fullMagnificationController = mFullScreenMagnificationController;
            windowMagnificationManager = mWindowMagnificationMgr;
        }
            if (mWindowMagnificationMgr != null) {
                mWindowMagnificationMgr.setUserId(userId);

        mScaleProvider.onUserChanged(userId);
        if (fullMagnificationController != null) {
            fullMagnificationController.resetAllIfNeeded(false);
        }
        if (windowMagnificationManager != null) {
            windowMagnificationManager.disableAllWindowMagnifiers();
        }
    }

@@ -337,6 +354,14 @@ public class MagnificationController implements WindowMagnificationManager.Callb
                mWindowMagnificationMgr.onDisplayRemoved(displayId);
            }
        }
        mScaleProvider.onDisplayRemoved(displayId);
    }

    /**
     * Called when the given user is removed.
     */
    public void onUserRemoved(int userId) {
        mScaleProvider.onUserRemoved(userId);
    }

    public void setMagnificationCapabilities(int capabilities) {
@@ -378,8 +403,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
        synchronized (mLock) {
            if (mFullScreenMagnificationController == null) {
                mFullScreenMagnificationController = new FullScreenMagnificationController(mContext,
                        mAms, mLock, this);
                mFullScreenMagnificationController.setUserId(mAms.getCurrentUserIdLocked());
                        mAms, mLock, this, mScaleProvider);
            }
        }
        return mFullScreenMagnificationController;
@@ -404,7 +428,8 @@ public class MagnificationController implements WindowMagnificationManager.Callb
        synchronized (mLock) {
            if (mWindowMagnificationMgr == null) {
                mWindowMagnificationMgr = new WindowMagnificationManager(mContext,
                        mAms.getCurrentUserIdLocked(), this, mAms.getTraceManager());
                        mUserId, this, mAms.getTraceManager(),
                        mScaleProvider);
            }
            return mWindowMagnificationMgr;
        }
+139 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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 android.content.Context;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.MathUtils;
import android.util.SparseArray;
import android.view.Display;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;

/**
 * Supplies setter/getter of the magnification scale for the given display. Only the value of the
 * default play is persisted. It also constraints the range of applied magnification scale between
 * [MIN_SCALE, MAX_SCALE] which is consistent with the range provided by
 * {@code AccessibilityService.MagnificationController#setScale()}.
 */
public class MagnificationScaleProvider {

    @VisibleForTesting
    protected static final float DEFAULT_MAGNIFICATION_SCALE = 2.0f;
    public static final float MIN_SCALE = 1.0f;
    public static final float MAX_SCALE = 8.0f;

    private final Context mContext;
    // Stores the scale for non-default displays.
    @GuardedBy("mLock")
    private final SparseArray<SparseArray<Float>> mUsersScales = new SparseArray();
    private int mCurrentUserId = UserHandle.USER_SYSTEM;
    private final Object mLock = new Object();

    public MagnificationScaleProvider(Context context) {
        mContext = context;
    }

    /**
     *  Stores the user settings scale associated to the given display. Only the scale of the
     *  default display is persistent.
     *
     * @param scale the magnification scale
     * @param displayId the id of the display
     */
    void putScale(float scale, int displayId) {
        if (displayId == Display.DEFAULT_DISPLAY) {
            BackgroundThread.getHandler().post(
                    () -> Settings.Secure.putFloatForUser(mContext.getContentResolver(),
                            Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, scale,
                            mCurrentUserId));
        } else {
            synchronized (mLock) {
                getScalesWithCurrentUser().put(displayId, scale);
            }
        }
    }

    /**
     * Gets the user settings scale with the given display.
     *
     * @param displayId the id of the display
     * @return the magnification scale.
     */
    float getScale(int displayId) {
        if (displayId == Display.DEFAULT_DISPLAY) {
            return Settings.Secure.getFloatForUser(mContext.getContentResolver(),
                    Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
                    DEFAULT_MAGNIFICATION_SCALE, mCurrentUserId);
        } else {
            synchronized (mLock) {
                return getScalesWithCurrentUser().get(displayId, DEFAULT_MAGNIFICATION_SCALE);
            }
        }
    }


    @GuardedBy("mLock")
    private SparseArray<Float> getScalesWithCurrentUser() {
        SparseArray<Float> scales = mUsersScales.get(mCurrentUserId);
        if (scales == null) {
            scales = new SparseArray<>();
            mUsersScales.put(mCurrentUserId, scales);
        }

        return scales;
    }

    void onUserChanged(int userId) {
        synchronized (mLock) {
            mCurrentUserId = userId;
        }
    }

    void onUserRemoved(int userId) {
        synchronized (mLock) {
            mUsersScales.remove(userId);
        }
    }

    void onDisplayRemoved(int displayId) {
        synchronized (mLock) {
            final int userCounts = mUsersScales.size();
            for (int i = userCounts - 1; i >= 0; i--) {
                mUsersScales.get(i).remove(displayId);
            }
        }
    }

    @Override
    public String toString() {
        synchronized (mLock) {
            return "MagnificationScaleProvider{"
                    + "mCurrentUserId=" + mCurrentUserId
                    + "Scale on the default display=" + getScale(Display.DEFAULT_DISPLAY)
                    + "Scales on non-default displays=" + getScalesWithCurrentUser()
                    + '}';
        }
    }

    static float constrainScale(float scale) {
        return MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
    }
}
Loading