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

Commit 302afd61 authored by Yongshun Liu's avatar Yongshun Liu Committed by Android (Google) Code Review
Browse files

Merge "a11y: Throttle onMouseMove in MagnificationController" into main

parents eadb4ff8 bea505e1
Loading
Loading
Loading
Loading
+15 −0
Original line number Original line Diff line number Diff line
@@ -91,6 +91,21 @@ public final class AccessibilityUtils {
    @VisibleForTesting
    @VisibleForTesting
    public static final String MENU_SERVICE_RELATIVE_CLASS_NAME = ".AccessibilityMenuService";
    public static final String MENU_SERVICE_RELATIVE_CLASS_NAME = ".AccessibilityMenuService";


    /**
     * The delay time in milliseconds for showing the magnification button.
     * @hide
     */
    public static final long MAGNIFICATION_SHOW_BUTTON_DELAY_MS = 300;

    /**
     * The interval in milliseconds for throttling UI changes and reduce IPC to show/hide the
     * magnification mode switch button. It's set to be the same as
     * {@link #MAGNIFICATION_SHOW_BUTTON_DELAY_MS} so that users won't notice a delay.
     * @hide
     */
    public static final long MAGNIFICATION_HANDLE_UI_CHANGE_INTERVAL_MS =
            MAGNIFICATION_SHOW_BUTTON_DELAY_MS;

    /**
    /**
     * {@link ComponentName} for the Accessibility Menu {@link AccessibilityService} as provided
     * {@link ComponentName} for the Accessibility Menu {@link AccessibilityService} as provided
     * inside the system build, used for automatic migration to this version of the service.
     * inside the system build, used for automatic migration to this version of the service.
+2 −2
Original line number Original line Diff line number Diff line
@@ -45,6 +45,7 @@ import android.window.InputTransferToken;


import androidx.annotation.NonNull;
import androidx.annotation.NonNull;


import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.LauncherProxyService;
import com.android.systemui.LauncherProxyService;
@@ -72,7 +73,6 @@ import javax.inject.Inject;
public class MagnificationImpl implements Magnification, CommandQueue.Callbacks {
public class MagnificationImpl implements Magnification, CommandQueue.Callbacks {
    private static final String TAG = "Magnification";
    private static final String TAG = "Magnification";


    @VisibleForTesting static final int DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS = 300;
    private static final int MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL = 1;
    private static final int MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL = 1;


    private final ModeSwitchesController mModeSwitchesController;
    private final ModeSwitchesController mModeSwitchesController;
@@ -429,7 +429,7 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
        mHandler.sendMessageDelayed(
        mHandler.sendMessageDelayed(
                mHandler.obtainMessage(
                mHandler.obtainMessage(
                        MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL, displayId, magnificationMode),
                        MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL, displayId, magnificationMode),
                DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS);
                AccessibilityUtils.MAGNIFICATION_SHOW_BUTTON_DELAY_MS);
    }
    }


    @MainThread
    @MainThread
+8 −9
Original line number Original line Diff line number Diff line
@@ -16,8 +16,6 @@


package com.android.systemui.accessibility;
package com.android.systemui.accessibility;


import static com.android.systemui.accessibility.MagnificationImpl.DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertTrue;
@@ -44,6 +42,7 @@ import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.filters.SmallTest;


import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.systemui.LauncherProxyService;
import com.android.systemui.LauncherProxyService;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.model.SysUiState;
import com.android.systemui.model.SysUiState;
@@ -200,10 +199,10 @@ public class IMagnificationConnectionTest extends SysuiTestCase {
        // showMagnificationButton request to Magnification.
        // showMagnificationButton request to Magnification.
        processAllPendingMessages();
        processAllPendingMessages();


        // The delayed message would be processed after DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS.
        // The delayed message would be processed after
        // So call this processAllPendingMessages with a timeout to verify the showButton
        // AccessibilityUtils.MAGNIFICATION_SHOW_BUTTON_DELAY_MS. So call this
        // will be called.
        // processAllPendingMessages with a timeout to verify the showButton will be called.
        int timeout = DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS + 100;
        long timeout = AccessibilityUtils.MAGNIFICATION_SHOW_BUTTON_DELAY_MS + 100;
        processAllPendingMessages(timeout);
        processAllPendingMessages(timeout);
        verify(mModeSwitchesController).showButton(TEST_DISPLAY,
        verify(mModeSwitchesController).showButton(TEST_DISPLAY,
                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
@@ -222,7 +221,7 @@ public class IMagnificationConnectionTest extends SysuiTestCase {


        // The isMagnificationSettingsShowing will be checked after timeout, so
        // The isMagnificationSettingsShowing will be checked after timeout, so
        // process all message after a timeout here to verify the showButton will not be called.
        // process all message after a timeout here to verify the showButton will not be called.
        processAllPendingMessages(DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS + 100);
        processAllPendingMessages(AccessibilityUtils.MAGNIFICATION_SHOW_BUTTON_DELAY_MS + 100);
        verify(mModeSwitchesController, never()).showButton(TEST_DISPLAY,
        verify(mModeSwitchesController, never()).showButton(TEST_DISPLAY,
                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
    }
    }
@@ -249,7 +248,7 @@ public class IMagnificationConnectionTest extends SysuiTestCase {


        // Call this processAllPendingMessages with a timeout to ensure the delayed show button
        // Call this processAllPendingMessages with a timeout to ensure the delayed show button
        // message should be removed and thus the showButton will not be called after timeout.
        // message should be removed and thus the showButton will not be called after timeout.
        int timeout = DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS + 100;
        long timeout = AccessibilityUtils.MAGNIFICATION_SHOW_BUTTON_DELAY_MS + 100;
        processAllPendingMessages(/* timeForwardMs= */ timeout);
        processAllPendingMessages(/* timeForwardMs= */ timeout);
        verify(mModeSwitchesController, never()).showButton(TEST_DISPLAY,
        verify(mModeSwitchesController, never()).showButton(TEST_DISPLAY,
                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
@@ -281,7 +280,7 @@ public class IMagnificationConnectionTest extends SysuiTestCase {
        processAllPendingMessages(/* timeForwardMs=*/ 0);
        processAllPendingMessages(/* timeForwardMs=*/ 0);
    }
    }


    private void processAllPendingMessages(int timeForwardMs) {
    private void processAllPendingMessages(long timeForwardMs) {
        if (timeForwardMs > 0) {
        if (timeForwardMs > 0) {
            mTestableLooper.moveTimeForward(timeForwardMs);
            mTestableLooper.moveTimeForward(timeForwardMs);
        }
        }
+10 −0
Original line number Original line Diff line number Diff line
@@ -55,6 +55,7 @@ import android.view.ViewConfiguration;
import android.view.accessibility.MagnificationAnimationCallback;
import android.view.accessibility.MagnificationAnimationCallback;


import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -125,6 +126,7 @@ public class MagnificationController implements MagnificationConnectionManager.C
    // in multiple directions at once (for example, up + left), tracking last
    // in multiple directions at once (for example, up + left), tracking last
    // panned time ensures that panning doesn't occur too frequently.
    // panned time ensures that panning doesn't occur too frequently.
    private long mLastPannedTime = 0;
    private long mLastPannedTime = 0;
    private long mLastMouseMoveTriggeredUiChangeTime = 0;
    private boolean mRepeatKeysEnabled = true;
    private boolean mRepeatKeysEnabled = true;


    private @ZoomDirection int mActiveZoomDirection = ZOOM_DIRECTION_IN;
    private @ZoomDirection int mActiveZoomDirection = ZOOM_DIRECTION_IN;
@@ -386,16 +388,24 @@ public class MagnificationController implements MagnificationConnectionManager.C


    @Override
    @Override
    public void onTouchInteractionStart(int displayId, int mode) {
    public void onTouchInteractionStart(int displayId, int mode) {
        // TODO(435498747): Add throttling for touch similarly to mouse events.
        handleUserInteractionChanged(displayId, mode);
        handleUserInteractionChanged(displayId, mode);
    }
    }


    @Override
    @Override
    public void onTouchInteractionEnd(int displayId, int mode) {
    public void onTouchInteractionEnd(int displayId, int mode) {
        // TODO(435498747): Add throttling for touch similarly to mouse events.
        handleUserInteractionChanged(displayId, mode);
        handleUserInteractionChanged(displayId, mode);
    }
    }


    @Override
    @Override
    public void onMouseMove(int displayId, int mode) {
    public void onMouseMove(int displayId, int mode) {
        final long currentTime = mSystemClock.uptimeMillis();
        if (currentTime - mLastMouseMoveTriggeredUiChangeTime
                < AccessibilityUtils.MAGNIFICATION_HANDLE_UI_CHANGE_INTERVAL_MS) {
            return;
        }
        mLastMouseMoveTriggeredUiChangeTime = currentTime;
        handleUserInteractionChanged(displayId, mode);
        handleUserInteractionChanged(displayId, mode);
    }
    }


+42 −0
Original line number Original line Diff line number Diff line
@@ -39,6 +39,7 @@ import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.floatThat;
import static org.mockito.ArgumentMatchers.floatThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.never;
@@ -81,6 +82,7 @@ import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import androidx.test.runner.AndroidJUnit4;


import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.LocalServices;
import com.android.server.LocalServices;
@@ -2192,6 +2194,46 @@ public class MagnificationControllerTest {
                eq(TEST_DISPLAY));
                eq(TEST_DISPLAY));
    }
    }


    @Test
    public void onMouseMove_withThrottle_shouldRateLimit() throws RemoteException {
        mMagnificationController.setMagnificationCapabilities(
                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
        setMagnificationEnabled(MODE_FULLSCREEN);
        clearInvocations(mMagnificationConnectionManager);

        // The first call should go through and trigger a UI update.
        mMagnificationController.onMouseMove(TEST_DISPLAY, MODE_FULLSCREEN);
        verify(mMagnificationConnectionManager).showMagnificationButton(
                TEST_DISPLAY, MODE_FULLSCREEN);
        clearInvocations(mMagnificationConnectionManager);

        // Subsequent calls within the throttle period should be ignored.
        mMagnificationController.onMouseMove(TEST_DISPLAY, MODE_FULLSCREEN);
        mMagnificationController.onMouseMove(TEST_DISPLAY, MODE_FULLSCREEN);
        verify(mMagnificationConnectionManager, never()).showMagnificationButton(
                TEST_DISPLAY, MODE_FULLSCREEN);
    }

    @Test
    public void onMouseMove_withThrottle_shouldNotRateLimitAfterDelay() throws RemoteException {
        mMagnificationController.setMagnificationCapabilities(
                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
        setMagnificationEnabled(MODE_FULLSCREEN);
        clearInvocations(mMagnificationConnectionManager);

        // The first call should go through and trigger a UI update.
        mMagnificationController.onMouseMove(TEST_DISPLAY, MODE_FULLSCREEN);
        verify(mMagnificationConnectionManager).showMagnificationButton(
                TEST_DISPLAY, MODE_FULLSCREEN);
        clearInvocations(mMagnificationConnectionManager);

        // Advance time past the throttle period. The next call should now go through.
        mSystemClock.advanceTime(AccessibilityUtils.MAGNIFICATION_HANDLE_UI_CHANGE_INTERVAL_MS + 1);
        mMagnificationController.onMouseMove(TEST_DISPLAY, MODE_FULLSCREEN);
        verify(mMagnificationConnectionManager).showMagnificationButton(
                TEST_DISPLAY, MODE_FULLSCREEN);
    }

    @Test
    @Test
    public void enableWindowMode_showMagnificationButton()
    public void enableWindowMode_showMagnificationButton()
            throws RemoteException {
            throws RemoteException {