From 9d7132a35dac4231491cb3d352dc79e7613555ee Mon Sep 17 00:00:00 2001 From: Nick Chameyev Date: Mon, 14 Jun 2021 21:51:03 +0100 Subject: [PATCH] Update lock screen user switcher on settings update and re-create view stub after disabling multi-user NotificationPanelViewController inflates user switcher view stub after enabling multi-user and removes user switcher view after disabling this setting. After enabling it again it crashed when trying to inflate view stub as the view was removed. Fixed by re-inserting view stub after disabling user switcher. Another issue was that user switcher is not updated immediately after changing the multi-user setting and after switching displays. Fixed by adding settings observer and listening for screen size changes in the controller. Test: atest com.android.systemui.statusbar.phone.NotificationPanelViewTest Fixes: 186728895 Change-Id: I1957abf952aff938406f4cabf4c83cd2977099e8 --- .../phone/ConfigurationControllerImpl.kt | 10 +++ .../NotificationPanelViewController.java | 69 +++++++++++++-- .../policy/ConfigurationController.java | 1 + .../phone/NotificationPanelViewTest.java | 86 ++++++++++++++++++- 4 files changed, 159 insertions(+), 7 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt index 54ef623e95ab8..b148eeba2cf55 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt @@ -27,6 +27,7 @@ class ConfigurationControllerImpl(context: Context) : ConfigurationController { private val listeners: MutableList = ArrayList() private val lastConfig = Configuration() private var density: Int = 0 + private var smallestScreenWidth: Int = 0 private var fontScale: Float = 0.toFloat() private val inCarMode: Boolean private var uiMode: Int = 0 @@ -38,6 +39,7 @@ class ConfigurationControllerImpl(context: Context) : ConfigurationController { this.context = context fontScale = currentConfig.fontScale density = currentConfig.densityDpi + smallestScreenWidth = currentConfig.smallestScreenWidthDp inCarMode = currentConfig.uiMode and Configuration.UI_MODE_TYPE_MASK == Configuration.UI_MODE_TYPE_CAR uiMode = currentConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK @@ -72,6 +74,14 @@ class ConfigurationControllerImpl(context: Context) : ConfigurationController { this.fontScale = fontScale } + val smallestScreenWidth = newConfig.smallestScreenWidthDp + if (smallestScreenWidth != this.smallestScreenWidth) { + this.smallestScreenWidth = smallestScreenWidth + listeners.filterForEach({ this.listeners.contains(it) }) { + it.onSmallestScreenWidthChanged() + } + } + val localeList = newConfig.locales if (localeList != this.localeList) { this.localeList = localeList diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 35d1526c4c593..15a13ee9fb9c9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -36,9 +36,11 @@ import android.animation.ValueAnimator; import android.app.ActivityManager; import android.app.Fragment; import android.app.StatusBarManager; +import android.content.ContentResolver; import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.content.res.Resources; +import android.database.ContentObserver; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; @@ -50,10 +52,12 @@ import android.graphics.Region; import android.graphics.drawable.Drawable; import android.hardware.biometrics.BiometricSourceType; import android.os.Bundle; +import android.os.Handler; import android.os.PowerManager; import android.os.SystemClock; import android.os.UserManager; import android.os.VibrationEffect; +import android.provider.Settings; import android.util.Log; import android.util.MathUtils; import android.view.LayoutInflater; @@ -210,6 +214,8 @@ public class NotificationPanelViewController extends PanelViewController { new MyOnHeadsUpChangedListener(); private final HeightListener mHeightListener = new HeightListener(); private final ConfigurationListener mConfigurationListener = new ConfigurationListener(); + private final SettingsChangeObserver mSettingsChangeObserver; + @VisibleForTesting final StatusBarStateListener mStatusBarStateListener = new StatusBarStateListener(); private final BiometricUnlockController mBiometricUnlockController; @@ -594,6 +600,8 @@ public class NotificationPanelViewController extends PanelViewController { private int mScreenCornerRadius; private boolean mQSAnimatingHiddenFromCollapsed; + private final ContentResolver mContentResolver; + private final Executor mUiExecutor; private final SecureSettings mSecureSettings; @@ -635,6 +643,7 @@ public class NotificationPanelViewController extends PanelViewController { @Inject public NotificationPanelViewController(NotificationPanelView view, @Main Resources resources, + @Main Handler handler, LayoutInflater layoutInflater, NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler, DynamicPrivacyController dynamicPrivacyController, @@ -678,6 +687,7 @@ public class NotificationPanelViewController extends PanelViewController { TapAgainViewController tapAgainViewController, NavigationModeController navigationModeController, FragmentService fragmentService, + ContentResolver contentResolver, QuickAccessWalletController quickAccessWalletController, @Main Executor uiExecutor, SecureSettings secureSettings, @@ -704,15 +714,12 @@ public class NotificationPanelViewController extends PanelViewController { mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory; mDepthController = notificationShadeDepthController; mFeatureFlags = featureFlags; + mContentResolver = contentResolver; mKeyguardQsUserSwitchComponentFactory = keyguardQsUserSwitchComponentFactory; mKeyguardUserSwitcherComponentFactory = keyguardUserSwitcherComponentFactory; mQSDetailDisplayer = qsDetailDisplayer; mFragmentService = fragmentService; - mKeyguardUserSwitcherEnabled = mResources.getBoolean( - com.android.internal.R.bool.config_keyguardUserSwitcher); - mKeyguardQsUserSwitchEnabled = - mKeyguardUserSwitcherEnabled && mResources.getBoolean( - R.bool.config_keyguard_user_switch_opens_qs_details); + mSettingsChangeObserver = new SettingsChangeObserver(handler); mShouldUseSplitNotificationShade = Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources); mView.setWillNotDraw(!DEBUG); @@ -795,6 +802,7 @@ public class NotificationPanelViewController extends PanelViewController { } mMaxKeyguardNotifications = resources.getInteger(R.integer.keyguard_max_notification_count); + updateUserSwitcherFlags(); onFinishInflate(); } @@ -1034,6 +1042,10 @@ public class NotificationPanelViewController extends PanelViewController { view = mLayoutInflater.inflate(layoutId, mView, false); mView.addView(view, index); } else { + // Add the stub back so we can re-inflate it again if necessary + ViewStub stub = new ViewStub(mView.getContext(), layoutId); + stub.setId(stubId); + mView.addView(stub, index); view = null; } } else if (enabled) { @@ -1061,6 +1073,7 @@ public class NotificationPanelViewController extends PanelViewController { updateResources(); // Re-inflate the keyguard user switcher group. + updateUserSwitcherFlags(); boolean isUserSwitcherEnabled = mUserManager.isUserSwitcherEnabled(); boolean showQsUserSwitch = mKeyguardQsUserSwitchEnabled && isUserSwitcherEnabled; boolean showKeyguardUserSwitcher = @@ -3895,6 +3908,26 @@ public class NotificationPanelViewController extends PanelViewController { return false; } + private void updateUserSwitcherFlags() { + mKeyguardUserSwitcherEnabled = mResources.getBoolean( + com.android.internal.R.bool.config_keyguardUserSwitcher); + mKeyguardQsUserSwitchEnabled = + mKeyguardUserSwitcherEnabled && mResources.getBoolean( + R.bool.config_keyguard_user_switch_opens_qs_details); + } + + private void registerSettingsChangeListener() { + mContentResolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.USER_SWITCHER_ENABLED), + /* notifyForDescendants */ false, + mSettingsChangeObserver + ); + } + + private void unregisterSettingsChangeListener() { + mContentResolver.unregisterContentObserver(mSettingsChangeObserver); + } + private class OnHeightChangedListener implements ExpandableView.OnHeightChangedListener { @Override public void onHeightChanged(ExpandableView view, boolean needsAnimation) { @@ -4215,6 +4248,15 @@ public class NotificationPanelViewController extends PanelViewController { reInflateViews(); } + @Override + public void onSmallestScreenWidthChanged() { + if (DEBUG) Log.d(TAG, "onSmallestScreenWidthChanged"); + + // Can affect multi-user switcher visibility as it depends on screen size by default: + // it is enabled only for devices with large screens (see config_keyguardUserSwitcher) + reInflateViews(); + } + @Override public void onOverlayChanged() { if (DEBUG) Log.d(TAG, "onOverlayChanged"); @@ -4228,6 +4270,21 @@ public class NotificationPanelViewController extends PanelViewController { } } + private class SettingsChangeObserver extends ContentObserver { + + SettingsChangeObserver(Handler handler) { + super(handler); + } + + @Override + public void onChange(boolean selfChange) { + if (DEBUG) Log.d(TAG, "onSettingsChanged"); + + // Can affect multi-user switcher visibility + reInflateViews(); + } + } + private class StatusBarStateListener implements StateListener { @Override public void onStateChanged(int statusBarState) { @@ -4343,10 +4400,12 @@ public class NotificationPanelViewController extends PanelViewController { mConfigurationListener.onThemeChanged(); mFalsingManager.addTapListener(mFalsingTapListener); mKeyguardIndicationController.init(); + registerSettingsChangeListener(); } @Override public void onViewDetachedFromWindow(View v) { + unregisterSettingsChangeListener(); mFragmentService.getFragmentHostManager(mView) .removeTagListener(QS.TAG, mFragmentListener); mStatusBarStateController.removeCallback(mStatusBarStateListener); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java index 0a6cf7be736f9..c2bd87c6276f5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java @@ -33,6 +33,7 @@ public interface ConfigurationController extends CallbackController captor = ArgumentCaptor.forClass(View.class); + verify(mView, atLeastOnce()).addView(captor.capture(), anyInt()); + final View userSwitcherStub = CollectionUtils.find(captor.getAllValues(), + view -> view.getId() == R.id.keyguard_user_switcher_stub); + assertThat(userSwitcherStub).isNotNull(); + assertThat(userSwitcherStub).isInstanceOf(ViewStub.class); + } + + @Test + public void testChangeSmallestScreenWidthAndUserSwitchEnabled_inflatesUserSwitchView() { + givenViewAttached(); + when(mView.findViewById(R.id.keyguard_user_switcher_view)).thenReturn(null); + updateSmallestScreenWidth(300); + when(mResources.getBoolean( + com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true); + when(mUserManager.isUserSwitcherEnabled()).thenReturn(true); + + updateSmallestScreenWidth(800); + + verify(mUserSwitcherStubView).inflate(); + } + @Test public void testSplitShadeLayout_isAlignedToGuideline() { enableSplitShade(); @@ -682,6 +743,12 @@ public class NotificationPanelViewTest extends SysuiTestCase { return mFalsingManager.getTapListeners().get(0); } + private void givenViewAttached() { + for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) { + listener.onViewAttachedToWindow(mView); + } + } + private View newViewWithId(int id) { View view = new View(mContext); view.setId(id); @@ -704,6 +771,21 @@ public class NotificationPanelViewTest extends SysuiTestCase { mNotificationPanelViewController.updateResources(); } + private void updateMultiUserSetting(boolean enabled) { + when(mUserManager.isUserSwitcherEnabled()).thenReturn(enabled); + final ArgumentCaptor observerCaptor = + ArgumentCaptor.forClass(ContentObserver.class); + verify(mContentResolver) + .registerContentObserver(any(), anyBoolean(), observerCaptor.capture()); + observerCaptor.getValue().onChange(/* selfChange */ false); + } + + private void updateSmallestScreenWidth(int smallestScreenWidthDp) { + Configuration configuration = new Configuration(); + configuration.smallestScreenWidthDp = smallestScreenWidthDp; + mConfigurationController.onConfigurationChanged(configuration); + } + private void onTouchEvent(MotionEvent ev) { mTouchHandler.onTouch(mView, ev); }