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

Commit b68bbe05 authored by ryanlwlin's avatar ryanlwlin
Browse files

Support individual magnification mode for each display

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

Test: atest com.android.server.accessibility
      atest com.android.systemui.accessibility
Bug: 194667380
Change-Id: I099e1ee945938aac07cc079f68bccbc745d40b08
parent 4d021782
Loading
Loading
Loading
Loading
+17 −9
Original line number Diff line number Diff line
@@ -28,7 +28,6 @@ import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.MathUtils;
import android.view.Gravity;
@@ -75,6 +74,7 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
    private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
    private int mMagnificationMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
    private final LayoutParams mParams;
    private final SwitchListener mSwitchListener;
    @VisibleForTesting
    final Rect mDraggableWindowBounds = new Rect();
    private boolean mIsVisible = false;
@@ -82,17 +82,29 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
    private boolean mSingleTapDetected = false;
    private boolean mToLeftScreenEdge = false;

    MagnificationModeSwitch(@UiContext Context context) {
        this(context, createView(context), new SfVsyncFrameCallbackProvider());
    public interface SwitchListener {
        /**
         * Called when the switch is clicked to change the magnification mode.
         * @param displayId the display id of the display to which the view's window has been
         *                  attached
         * @param magnificationMode the magnification mode
         */
        void onSwitch(int displayId, int magnificationMode);
    }

    MagnificationModeSwitch(@UiContext Context context,
            SwitchListener switchListener) {
        this(context, createView(context), new SfVsyncFrameCallbackProvider(), switchListener);
    }

    @VisibleForTesting
    MagnificationModeSwitch(Context context, @NonNull ImageView imageView,
            SfVsyncFrameCallbackProvider sfVsyncFrameProvider) {
            SfVsyncFrameCallbackProvider sfVsyncFrameProvider, SwitchListener switchListener) {
        mContext = context;
        mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
        mWindowManager = mContext.getSystemService(WindowManager.class);
        mSfVsyncFrameProvider = sfVsyncFrameProvider;
        mSwitchListener = switchListener;
        mParams = createLayoutParams(context);
        mImageView = imageView;
        mImageView.setOnTouchListener(this::onTouch);
@@ -364,11 +376,7 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
                mMagnificationMode ^ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
        mMagnificationMode = newMode;
        mImageView.setImageResource(getIconResId(newMode));
        Settings.Secure.putIntForUser(
                mContext.getContentResolver(),
                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
                newMode,
                UserHandle.USER_CURRENT);
        mSwitchListener.onSwitch(mContext.getDisplayId(), newMode);
    }

    private void handleSingleTap() {
+29 −7
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.systemui.accessibility;

import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;

import static com.android.systemui.accessibility.MagnificationModeSwitch.SwitchListener;

import android.annotation.MainThread;
import android.content.Context;
import android.hardware.display.DisplayManager;
@@ -27,21 +29,24 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.SysUISingleton;

/**
 * A class to control {@link MagnificationModeSwitch}. It should show the button UI with following
 * A class to control {@link MagnificationModeSwitch}. It shows the button UI with following
 * conditions:
 * <ol>
 *   <li> Both full-screen and window magnification mode are capable.</li>
 *   <li> The magnification scale is changed by a user.</li>
 * <ol>
 * The switch action will be handled by {@link #mSwitchListenerDelegate} which informs the system
 * server about the changed mode.
 */
@SysUISingleton
public class ModeSwitchesController {
public class ModeSwitchesController implements SwitchListener {

    private final DisplayIdIndexSupplier<MagnificationModeSwitch> mSwitchSupplier;
    private SwitchListener mSwitchListenerDelegate;

    public ModeSwitchesController(Context context) {
        mSwitchSupplier = new SwitchSupplier(context,
                context.getSystemService(DisplayManager.class));
                context.getSystemService(DisplayManager.class), this::onSwitch);
    }

    @VisibleForTesting
@@ -50,8 +55,8 @@ public class ModeSwitchesController {
    }

    /**
     * Shows a button that a user can click the button to switch magnification mode. And the
     * button would be dismissed automatically after the button is displayed for a period of time.
     * Shows a button that a user can click to switch magnification mode. And the button
     * would be dismissed automatically after the button is displayed for a period of time.
     *
     * @param displayId The logical display id
     * @param mode      The magnification mode
@@ -93,24 +98,41 @@ public class ModeSwitchesController {
                switchController -> switchController.onConfigurationChanged(configDiff));
    }

    @Override
    public void onSwitch(int displayId, int magnificationMode) {
        if (mSwitchListenerDelegate != null) {
            mSwitchListenerDelegate.onSwitch(displayId, magnificationMode);
        }
    }

    public void setSwitchListenerDelegate(SwitchListener switchListenerDelegate) {
        mSwitchListenerDelegate = switchListenerDelegate;
    }

    private static class SwitchSupplier extends DisplayIdIndexSupplier<MagnificationModeSwitch> {

        private final Context mContext;
        private final SwitchListener mSwitchListener;

        /**
         * Supplies the switch for the given display.
         *
         * @param context        Context
         * @param displayManager DisplayManager
         * @param switchListener The callback that will run when the switch is clicked
         */
        SwitchSupplier(Context context, DisplayManager displayManager) {
        SwitchSupplier(Context context, DisplayManager displayManager,
                SwitchListener switchListener) {
            super(displayManager);
            mContext = context;
            mSwitchListener = switchListener;
        }

        @Override
        protected MagnificationModeSwitch createInstance(Display display) {
            final Context uiContext = mContext.createWindowContext(display,
                    TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, /* options */ null);
            return new MagnificationModeSwitch(uiContext);
            return new MagnificationModeSwitch(uiContext, mSwitchListener);
        }
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -243,12 +243,15 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall
            mWindowMagnificationConnectionImpl = new WindowMagnificationConnectionImpl(this,
                    mHandler, mModeSwitchesController);
        }
        mModeSwitchesController.setSwitchListenerDelegate(
                mWindowMagnificationConnectionImpl::onChangeMagnificationMode);
        mAccessibilityManager.setWindowMagnificationConnection(
                mWindowMagnificationConnectionImpl);
    }

    private void clearWindowMagnificationConnection() {
        mAccessibilityManager.setWindowMagnificationConnection(null);
        mModeSwitchesController.setSwitchListenerDelegate(null);
        //TODO: destroy controllers.
    }
}
+10 −0
Original line number Diff line number Diff line
@@ -130,4 +130,14 @@ class WindowMagnificationConnectionImpl extends IWindowMagnificationConnection.S
            }
        }
    }

    void onChangeMagnificationMode(int displayId, int mode) {
        if (mConnectionCallback != null) {
            try {
                mConnectionCallback.onChangeMagnificationMode(displayId, mode);
            } catch (RemoteException e) {
                Log.e(TAG, "Failed to inform changing magnification mode", e);
            }
        }
    }
}
+28 −19
Original line number Diff line number Diff line
@@ -55,10 +55,9 @@ import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Handler;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.SparseIntArray;
import android.view.Choreographer;
import android.view.MotionEvent;
import android.view.View;
@@ -101,6 +100,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
    private AccessibilityManager mAccessibilityManager;
    @Mock
    private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
    private SwitchListenerStub mSwitchListener;
    private TestableWindowManager mWindowManager;
    private ViewPropertyAnimator mViewPropertyAnimator;
    private MagnificationModeSwitch mMagnificationModeSwitch;
@@ -112,6 +112,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        final WindowManager wm = mContext.getSystemService(WindowManager.class);
        mSwitchListener = new SwitchListenerStub();
        mWindowManager = spy(new TestableWindowManager(wm));
        mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
        mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager);
@@ -130,7 +131,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
        }).when(mSfVsyncFrameProvider).postFrameCallback(
                any(Choreographer.FrameCallback.class));
        mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mSpyImageView,
                mSfVsyncFrameProvider);
                mSfVsyncFrameProvider, mSwitchListener);
        assertNotNull(mTouchListener);
    }

@@ -326,8 +327,6 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
    public void performDragging_showMagnificationButton_updateViewLayout() {
        mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
        resetAndStubMockImageViewAndAnimator();
        final int previousMode = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0, UserHandle.USER_CURRENT);

        // Perform dragging
        final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop() + 10;
@@ -345,7 +344,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
        mTouchListener.onTouch(mSpyImageView, obtainMotionEvent(
                downTime, downTime, ACTION_UP, 100 + offset, 100));

        assertModeUnchanged(previousMode);
        assertModeUnchanged();
        assertShowFadingAnimation(FADE_OUT_ALPHA);
    }

@@ -353,8 +352,6 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
    public void performSingleTapActionCanceled_showButtonAnimation() {
        mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
        resetAndStubMockImageViewAndAnimator();
        final int previousMode = Settings.Secure.getInt(mContext.getContentResolver(),
                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);

        final long downTime = SystemClock.uptimeMillis();
        mTouchListener.onTouch(mSpyImageView, obtainMotionEvent(
@@ -363,7 +360,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
        mTouchListener.onTouch(mSpyImageView, obtainMotionEvent(
                downTime, downTime, ACTION_CANCEL, 100, 100));

        assertModeUnchanged(previousMode);
        assertModeUnchanged();
        assertShowFadingAnimation(FADE_OUT_ALPHA);
    }

@@ -371,8 +368,6 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
    public void performDraggingActionCanceled_showButtonAnimation() {
        mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
        resetAndStubMockImageViewAndAnimator();
        final int previousMode = Settings.Secure.getInt(mContext.getContentResolver(),
                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);

        // Perform dragging
        final long downTime = SystemClock.uptimeMillis();
@@ -385,7 +380,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
        mTouchListener.onTouch(mSpyImageView, obtainMotionEvent(
                downTime, downTime, ACTION_CANCEL, 100 + offset, 100));

        assertModeUnchanged(previousMode);
        assertModeUnchanged();
        assertShowFadingAnimation(FADE_OUT_ALPHA);
    }

@@ -529,10 +524,9 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
        assertEquals(expectedY, mWindowManager.getLayoutParamsFromAttachedView().y);
    }

    private void assertModeUnchanged(int expectedMode) {
        final int actualMode = Settings.Secure.getInt(mContext.getContentResolver(),
                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
        assertEquals(expectedMode, actualMode);
    private void assertModeUnchanged() {
        assertEquals(SwitchListenerStub.MODE_INVALID,
                mSwitchListener.getChangedMode(mContext.getDisplayId()));
    }

    private void assertShowFadingAnimation(float alpha) {
@@ -594,9 +588,8 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
        verify(mSpyImageView).setImageResource(
                getIconResId(expectedMode));
        verify(mWindowManager).removeView(mSpyImageView);
        final int actualMode = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0, UserHandle.USER_CURRENT);
        assertEquals(expectedMode, actualMode);
        final int changedMode = mSwitchListener.getChangedMode(mContext.getDisplayId());
        assertEquals(expectedMode, changedMode);
    }

    private MotionEvent obtainMotionEvent(long downTime, long eventTime, int action, float x,
@@ -621,4 +614,20 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
        assertEquals(expectedX, layoutParams.x);
        assertEquals(expectedY, layoutParams.y);
    }

    private static class SwitchListenerStub implements MagnificationModeSwitch.SwitchListener {

        private static final int MODE_INVALID = -1;

        private final SparseIntArray mModes = new SparseIntArray();

        @Override
        public void onSwitch(int displayId, int magnificationMode) {
            mModes.put(displayId, magnificationMode);
        }

        int getChangedMode(int displayId) {
            return mModes.get(displayId, MODE_INVALID);
        }
    }
}
Loading