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
This commit is contained in:
Bill Lin
2021-03-12 18:17:39 +08:00
parent b6521e0a8b
commit f18a7b0bc1
5 changed files with 104 additions and 51 deletions

View File

@@ -89,4 +89,9 @@ public interface OneHanded {
* Receive onConfigurationChanged() events
*/
void onConfigChanged(Configuration newConfig);
/**
* Notifies when user switch complete
*/
void onUserSwitch(int userId);
}

View File

@@ -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);
});
}
}
/**

View File

@@ -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;
@@ -62,17 +64,19 @@ public final class OneHandedSettingsUtil {
/**
* Register one handed preference settings observer
*
* @param key Setting key to monitor in observer
* @param resolver ContentResolver of context
* @param observer Observer from caller
* @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;
}
@@ -80,8 +84,8 @@ public final class OneHandedSettingsUtil {
/**
* Unregister one handed preference settings observer
*
* @param resolver ContentResolver of context
* @param observer preference key change observer
* @param resolver ContentResolver of context
* @param observer preference key change observer
*/
public void unregisterSettingsKeyObserver(ContentResolver resolver,
ContentObserver observer) {
@@ -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() {
}
}

View File

@@ -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(

View File

@@ -296,6 +296,11 @@ public final class WMShell extends SystemUI
}
oneHanded.stopOneHanded();
}
@Override
public void onUserSwitchComplete(int userId) {
oneHanded.onUserSwitch(userId);
}
};
mKeyguardUpdateMonitor.registerCallback(mOneHandedKeyguardCallback);