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

Commit f18a7b0b authored by Bill Lin's avatar Bill Lin
Browse files

1/ Make One handed mode support multiple user switch

Previously OHM did not support for multile user switch
Now we hook SysUI onUserSwitch callback to update
latest one handed mode functionality by currentUserId

Test: atest SystemUITests
Test: atest WMShellUnitTests
Test: manually switch user and observe verify different OHM config
      for different users
Bug: 182278800
Change-Id: I8ae7d0aedcafc8f477f41d2703df2df53839a89f
parent b6521e0a
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -89,4 +89,9 @@ public interface OneHanded {
     * Receive onConfigurationChanged() events
     */
    void onConfigChanged(Configuration newConfig);

    /**
     * Notifies when user switch complete
     */
    void onUserSwitch(int userId);
}
+55 −23
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.wm.shell.onehanded;

import static android.os.UserHandle.USER_CURRENT;
import static android.os.UserHandle.myUserId;
import static android.view.Display.DEFAULT_DISPLAY;

import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
@@ -74,6 +75,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
    private volatile boolean mIsSwipeToNotificationEnabled;
    private boolean mTaskChangeToExit;
    private boolean mLockedDisabled;
    private int mUserId;
    private float mOffSetFraction;

    private Context mContext;
@@ -141,7 +143,8 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
                    }
                    if (enabled) {
                        final int mOneHandedTimeout = mOneHandedSettingsUtil
                                .getSettingsOneHandedModeTimeout(mContext.getContentResolver());
                                .getSettingsOneHandedModeTimeout(
                                        mContext.getContentResolver(), mUserId);
                        final int timeout = mAccessibilityManager
                                .getRecommendedTimeoutMillis(mOneHandedTimeout * 1000
                                        /* align with A11y timeout millis */,
@@ -149,7 +152,8 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
                        mTimeoutHandler.setTimeout(timeout / 1000);
                    } else {
                        mTimeoutHandler.setTimeout(mOneHandedSettingsUtil
                                .getSettingsOneHandedModeTimeout(mContext.getContentResolver()));
                                .getSettingsOneHandedModeTimeout(
                                        mContext.getContentResolver(), mUserId));
                    }
                }
            };
@@ -247,12 +251,13 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
                R.fraction.config_one_handed_offset, 1, 1);
        final int sysPropPercentageConfig = SystemProperties.getInt(
                ONE_HANDED_MODE_OFFSET_PERCENTAGE, Math.round(offsetPercentageConfig * 100.0f));
        mUserId = myUserId();
        mOffSetFraction = sysPropPercentageConfig / 100.0f;
        mIsOneHandedEnabled = mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
                context.getContentResolver());
                context.getContentResolver(), mUserId);
        mIsSwipeToNotificationEnabled =
                mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
                        context.getContentResolver());
                        context.getContentResolver(), mUserId);
        mTimeoutHandler = timeoutHandler;

        mEnabledObserver = getObserver(this::onEnabledSettingChanged);
@@ -262,9 +267,8 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
                getObserver(this::onSwipeToNotificationEnabledSettingChanged);

        mDisplayController.addDisplayChangingController(mRotationController);

        setupCallback();
        setupSettingObservers();
        registerSettingObservers(mUserId);
        setupTimeoutListener();
        setupGesturalOverlay();
        updateSettings();
@@ -379,27 +383,38 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
        }
    }

    private void setupSettingObservers() {
    private void registerSettingObservers(int newUserId) {
        mOneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_ENABLED,
                mContext.getContentResolver(), mEnabledObserver);
                mContext.getContentResolver(), mEnabledObserver, newUserId);
        mOneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
                mContext.getContentResolver(), mTimeoutObserver);
                mContext.getContentResolver(), mTimeoutObserver, newUserId);
        mOneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.TAPS_APP_TO_EXIT,
                mContext.getContentResolver(), mTaskChangeExitObserver);
                mContext.getContentResolver(), mTaskChangeExitObserver, newUserId);
        mOneHandedSettingsUtil.registerSettingsKeyObserver(
                Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
                mContext.getContentResolver(), mSwipeToNotificationEnabledObserver);
                mContext.getContentResolver(), mSwipeToNotificationEnabledObserver, newUserId);
    }

    private void unregisterSettingObservers() {
        mOneHandedSettingsUtil.unregisterSettingsKeyObserver(mContext.getContentResolver(),
                mEnabledObserver);
        mOneHandedSettingsUtil.unregisterSettingsKeyObserver(mContext.getContentResolver(),
                mTimeoutObserver);
        mOneHandedSettingsUtil.unregisterSettingsKeyObserver(mContext.getContentResolver(),
                mTaskChangeExitObserver);
        mOneHandedSettingsUtil.unregisterSettingsKeyObserver(mContext.getContentResolver(),
                mSwipeToNotificationEnabledObserver);
    }

    private void updateSettings() {
        setOneHandedEnabled(mOneHandedSettingsUtil
                .getSettingsOneHandedModeEnabled(mContext.getContentResolver()));
                .getSettingsOneHandedModeEnabled(mContext.getContentResolver(), mUserId));
        mTimeoutHandler.setTimeout(mOneHandedSettingsUtil
                .getSettingsOneHandedModeTimeout(mContext.getContentResolver()));
                .getSettingsOneHandedModeTimeout(mContext.getContentResolver(), mUserId));
        setTaskChangeToExit(mOneHandedSettingsUtil
                .getSettingsTapsAppToExit(mContext.getContentResolver()));
                .getSettingsTapsAppToExit(mContext.getContentResolver(), mUserId));
        setSwipeToNotificationEnabled(mOneHandedSettingsUtil
                .getSettingsSwipeToNotificationEnabled(mContext.getContentResolver()));
                .getSettingsSwipeToNotificationEnabled(mContext.getContentResolver(), mUserId));
    }

    private void updateDisplayLayout(int displayId) {
@@ -420,7 +435,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
    @VisibleForTesting
    void onEnabledSettingChanged() {
        final boolean enabled = mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
                mContext.getContentResolver());
                mContext.getContentResolver(), mUserId);
        mOneHandedUiEventLogger.writeEvent(enabled
                ? OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_ENABLED_ON
                : OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_ENABLED_OFF);
@@ -430,13 +445,13 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
        // Also checks swipe to notification settings since they all need gesture overlay.
        setEnabledGesturalOverlay(
                enabled || mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
                        mContext.getContentResolver()));
                        mContext.getContentResolver(), mUserId));
    }

    @VisibleForTesting
    void onTimeoutSettingChanged() {
        final int newTimeout = mOneHandedSettingsUtil.getSettingsOneHandedModeTimeout(
                mContext.getContentResolver());
                mContext.getContentResolver(), mUserId);
        int metricsId = OneHandedUiEventLogger.OneHandedSettingsTogglesEvent.INVALID.getId();
        switch (newTimeout) {
            case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER:
@@ -465,7 +480,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
    @VisibleForTesting
    void onTaskChangeExitSettingChanged() {
        final boolean enabled = mOneHandedSettingsUtil.getSettingsTapsAppToExit(
                mContext.getContentResolver());
                mContext.getContentResolver(), mUserId);
        mOneHandedUiEventLogger.writeEvent(enabled
                ? OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_ON
                : OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_OFF);
@@ -477,13 +492,13 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
    void onSwipeToNotificationEnabledSettingChanged() {
        final boolean enabled =
                mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
                        mContext.getContentResolver());
                        mContext.getContentResolver(), mUserId);
        setSwipeToNotificationEnabled(enabled);

        // Also checks one handed mode settings since they all need gesture overlay.
        setEnabledGesturalOverlay(
                enabled || mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
                        mContext.getContentResolver()));
                        mContext.getContentResolver(), mUserId));
    }

    private void setupTimeoutListener() {
@@ -524,7 +539,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>

    private void setupGesturalOverlay() {
        if (!mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
                mContext.getContentResolver())) {
                mContext.getContentResolver(), mUserId)) {
            return;
        }

@@ -572,6 +587,14 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
        }
    }

    private void onUserSwitch(int newUserId) {
        unregisterSettingObservers();
        mUserId = newUserId;
        registerSettingObservers(newUserId);
        updateSettings();
        updateOneHandedEnabled();
    }

    public void dump(@NonNull PrintWriter pw) {
        final String innerPrefix = "  ";
        pw.println(TAG + "States: ");
@@ -579,6 +602,8 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
        pw.println(mOffSetFraction);
        pw.print(innerPrefix + "mLockedDisabled=");
        pw.println(mLockedDisabled);
        pw.print(innerPrefix + "mUserId=");
        pw.println(mUserId);

        if (mBackgroundPanelOrganizer != null) {
            mBackgroundPanelOrganizer.dump(pw);
@@ -604,7 +629,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
            mTutorialHandler.dump(pw);
        }

        mOneHandedSettingsUtil.dump(pw, innerPrefix, mContext.getContentResolver());
        mOneHandedSettingsUtil.dump(pw, innerPrefix, mContext.getContentResolver(), mUserId);

        if (mOverlayManager != null) {
            OverlayInfo info = null;
@@ -703,6 +728,13 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
                OneHandedController.this.onConfigChanged(newConfig);
            });
        }

        @Override
        public void onUserSwitch(int userId) {
            mMainExecutor.execute(() -> {
                OneHandedController.this.onUserSwitch(userId);
            });
        }
    }

    /**
+32 −24
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ import android.database.ContentObserver;
import android.net.Uri;
import android.provider.Settings;

import androidx.annotation.Nullable;

import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -65,14 +67,16 @@ public final class OneHandedSettingsUtil {
     * @param key       Setting key to monitor in observer
     * @param resolver  ContentResolver of context
     * @param observer  Observer from caller
     * @param newUserId New user id to be registered
     * @return uri key for observing
     */
    @Nullable
    public Uri registerSettingsKeyObserver(String key, ContentResolver resolver,
            ContentObserver observer) {
            ContentObserver observer, int newUserId) {
        Uri uriKey = null;
        uriKey = Settings.Secure.getUriFor(key);
        if (resolver != null && uriKey != null) {
            resolver.registerContentObserver(uriKey, false, observer);
            resolver.registerContentObserver(uriKey, false, observer, newUserId);
        }
        return uriKey;
    }
@@ -95,9 +99,9 @@ public final class OneHandedSettingsUtil {
     *
     * @return enable or disable one handed mode flag.
     */
    public boolean getSettingsOneHandedModeEnabled(ContentResolver resolver) {
        return Settings.Secure.getInt(resolver,
                Settings.Secure.ONE_HANDED_MODE_ENABLED, 0 /* Disabled */) == 1;
    public boolean getSettingsOneHandedModeEnabled(ContentResolver resolver, int userId) {
        return Settings.Secure.getIntForUser(resolver,
                Settings.Secure.ONE_HANDED_MODE_ENABLED, 0 /* Disabled */, userId) == 1;
    }

    /**
@@ -105,40 +109,44 @@ public final class OneHandedSettingsUtil {
     *
     * @return enable or disable taps app exit.
     */
    public boolean getSettingsTapsAppToExit(ContentResolver resolver) {
        return Settings.Secure.getInt(resolver,
                Settings.Secure.TAPS_APP_TO_EXIT, 0) == 1;
    public boolean getSettingsTapsAppToExit(ContentResolver resolver, int userId) {
        return Settings.Secure.getIntForUser(resolver,
                Settings.Secure.TAPS_APP_TO_EXIT, 0, userId) == 1;
    }

    /**
     * Query timeout value from Settings provider.
     * Default is {@link OneHandedSettingsUtil#ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS}
     * Query timeout value from Settings provider. Default is
     * {@link OneHandedSettingsUtil#ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS}
     *
     * @return timeout value in seconds.
     */
    public @OneHandedTimeout int getSettingsOneHandedModeTimeout(ContentResolver resolver) {
        return Settings.Secure.getInt(resolver,
                Settings.Secure.ONE_HANDED_MODE_TIMEOUT, ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
    public @OneHandedTimeout int getSettingsOneHandedModeTimeout(ContentResolver resolver,
            int userId) {
        return Settings.Secure.getIntForUser(resolver,
                Settings.Secure.ONE_HANDED_MODE_TIMEOUT, ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS,
                userId);
    }

    /**
     * Returns whether swipe bottom to notification gesture enabled or not.
     */
    public boolean getSettingsSwipeToNotificationEnabled(ContentResolver resolver) {
    public boolean getSettingsSwipeToNotificationEnabled(ContentResolver resolver, int userId) {
        return Settings.Secure.getInt(resolver,
                Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 0 /* Default OFF */) == 1;
    }

    void dump(PrintWriter pw, String prefix, ContentResolver resolver) {
    void dump(PrintWriter pw, String prefix, ContentResolver resolver,
            int userId) {
        final String innerPrefix = prefix + "  ";
        pw.println(prefix + TAG);
        pw.println(innerPrefix + TAG);
        pw.print(innerPrefix + "isOneHandedModeEnable=");
        pw.println(getSettingsOneHandedModeEnabled(resolver));
        pw.println(getSettingsOneHandedModeEnabled(resolver, userId));
        pw.print(innerPrefix + "oneHandedTimeOut=");
        pw.println(getSettingsOneHandedModeTimeout(resolver));
        pw.println(getSettingsOneHandedModeTimeout(resolver, userId));
        pw.print(innerPrefix + "tapsAppToExit=");
        pw.println(getSettingsTapsAppToExit(resolver));
        pw.println(getSettingsTapsAppToExit(resolver, userId));
    }

    public OneHandedSettingsUtil() {
    }
}
+7 −4
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import static org.mockito.Mockito.when;
import android.content.om.IOverlayManager;
import android.graphics.Rect;
import android.os.Handler;
import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
import android.util.ArrayMap;
import android.view.Display;
@@ -53,6 +54,8 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class OneHandedControllerTest extends OneHandedTestCase {
    private int mCurrentUser = UserHandle.myUserId();

    Display mDisplay;
    DisplayLayout mDisplayLayout;
    OneHandedController mSpiedOneHandedController;
@@ -100,13 +103,13 @@ public class OneHandedControllerTest extends OneHandedTestCase {
        when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
        when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
        when(mMockBackgroundOrganizer.getBackgroundSurface()).thenReturn(mMockLeash);
        when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any())).thenReturn(
        when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(
                mDefaultEnabled);
        when(mMockSettingsUitl.getSettingsOneHandedModeTimeout(any())).thenReturn(
        when(mMockSettingsUitl.getSettingsOneHandedModeTimeout(any(), anyInt())).thenReturn(
                OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
        when(mMockSettingsUitl.getSettingsTapsAppToExit(any())).thenReturn(
        when(mMockSettingsUitl.getSettingsTapsAppToExit(any(), anyInt())).thenReturn(
                mDefaultTapAppToExitEnabled);
        when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any())).thenReturn(
        when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any(), anyInt())).thenReturn(
                mDefaultSwipeToNotificationEnabled);

        when(mMockDisplayAreaOrganizer.getLastDisplayBounds()).thenReturn(
+5 −0
Original line number Diff line number Diff line
@@ -296,6 +296,11 @@ public final class WMShell extends SystemUI
                }
                oneHanded.stopOneHanded();
            }

            @Override
            public void onUserSwitchComplete(int userId) {
                oneHanded.onUserSwitch(userId);
            }
        };
        mKeyguardUpdateMonitor.registerCallback(mOneHandedKeyguardCallback);