Add the user switcher chip to the status bar

Add a StatusBarUserSwitcherContainer (plus a view controller) which can
show the current user information and present the user switcher when
tapped. This CL also aggregates all of the current user information into
the StatusBarUserInfoTracker so that the view controllers are
lightweight and reusable in the keyguard and unlocked statusbar.

Test: atest SystemUITests; manual
Bug: 217250837
Change-Id: I5fffb01e191d7bcd95d3a0651208bf0ca8eb45f8
This commit is contained in:
Evan Laird
2022-01-19 11:13:40 -05:00
parent bf10ca0507
commit e431489300
17 changed files with 552 additions and 9 deletions

View File

@@ -0,0 +1,20 @@
<!--
~ Copyright (C) 2022 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/system_neutral1_800" />
<corners android:radius="@dimen/ongoing_call_chip_corner_radius" />
</shape>

View File

@@ -30,14 +30,39 @@
android:id="@+id/status_icon_area"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/system_icons_super_container_margin_start"
android:paddingTop="@dimen/status_bar_padding_top"
android:layout_alignParentEnd="true"
android:gravity="center_vertical|end" >
<com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer
android:id="@+id/user_switcher_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:background="@drawable/status_bar_user_chip_bg"
android:visibility="visible" >
<ImageView android:id="@+id/current_user_avatar"
android:layout_width="@dimen/multi_user_avatar_keyguard_size"
android:layout_height="@dimen/multi_user_avatar_keyguard_size"
android:scaleType="centerInside"
android:paddingEnd="4dp" />
<TextView android:id="@+id/current_user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
/>
</com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer>
<FrameLayout android:id="@+id/system_icons_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginStart="@dimen/system_icons_super_container_margin_start"
android:layout_marginEnd="@dimen/status_bar_padding_end"
android:gravity="center_vertical|end">
<include layout="@layout/system_icons" />

View File

@@ -119,6 +119,32 @@
android:gravity="center_vertical|end"
>
<com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer
android:id="@+id/user_switcher_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:layout_marginEnd="16dp"
android:background="@drawable/status_bar_user_chip_bg"
android:visibility="visible" >
<ImageView android:id="@+id/current_user_avatar"
android:layout_width="@dimen/multi_user_avatar_keyguard_size"
android:layout_height="@dimen/multi_user_avatar_keyguard_size"
android:scaleType="centerInside"
android:paddingEnd="4dp" />
<TextView android:id="@+id/current_user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
/>
</com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer>
<include layout="@layout/system_icons" />
</com.android.keyguard.AlphaOptimizedLinearLayout>
</LinearLayout>

View File

@@ -17,7 +17,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/system_icons"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical">

View File

@@ -32,4 +32,8 @@
<bool name="flag_smartspace">false</bool>
<!-- Whether the user switcher chip shows in the status bar. When true, the multi user
avatar will no longer show on the lockscreen -->
<bool name="flag_user_switcher_chip">false</bool>
</resources>

View File

@@ -20,7 +20,11 @@ import com.android.keyguard.CarrierText;
import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherControllerImpl;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
@@ -39,4 +43,17 @@ public abstract class KeyguardStatusBarViewModule {
static BatteryMeterView getBatteryMeterView(KeyguardStatusBarView view) {
return view.findViewById(R.id.battery);
}
/** */
@Provides
@KeyguardStatusBarViewScope
static StatusBarUserSwitcherContainer getUserSwitcherContainer(KeyguardStatusBarView view) {
return view.findViewById(R.id.user_switcher_container);
}
/** */
@Binds
@KeyguardStatusBarViewScope
abstract StatusBarUserSwitcherController bindStatusBarUserSwitcherController(
StatusBarUserSwitcherControllerImpl controller);
}

View File

@@ -112,8 +112,8 @@ public class Flags {
public static final BooleanFlag COMBINED_STATUS_BAR_SIGNAL_ICONS =
new BooleanFlag(601, false);
public static final BooleanFlag STATUS_BAR_USER_SWITCHER =
new BooleanFlag(602, false);
public static final ResourceBooleanFlag STATUS_BAR_USER_SWITCHER =
new ResourceBooleanFlag(602, R.bool.flag_user_switcher_chip);
/***************************************/
// 700 - dialer/calls

View File

@@ -40,6 +40,8 @@ import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.VisibleForTesting;
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
@@ -67,8 +69,10 @@ public class KeyguardStatusBarView extends RelativeLayout {
private ImageView mMultiUserAvatar;
private BatteryMeterView mBatteryView;
private StatusIconContainer mStatusIconContainer;
private ViewGroup mUserSwitcherContainer;
private boolean mKeyguardUserSwitcherEnabled;
private boolean mKeyguardUserAvatarEnabled;
private boolean mIsPrivacyDotEnabled;
private int mSystemIconsSwitcherHiddenExpandedMargin;
@@ -111,10 +115,15 @@ public class KeyguardStatusBarView extends RelativeLayout {
mCutoutSpace = findViewById(R.id.cutout_space_view);
mStatusIconArea = findViewById(R.id.status_icon_area);
mStatusIconContainer = findViewById(R.id.statusIcons);
mUserSwitcherContainer = findViewById(R.id.user_switcher_container);
mIsPrivacyDotEnabled = mContext.getResources().getBoolean(R.bool.config_enablePrivacyDot);
loadDimens();
}
public ViewGroup getUserSwitcherContainer() {
return mUserSwitcherContainer;
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
@@ -186,6 +195,17 @@ public class KeyguardStatusBarView extends RelativeLayout {
}
private void updateVisibilities() {
// Multi user avatar is disabled in favor of the user switcher chip
if (!mKeyguardUserAvatarEnabled) {
if (mMultiUserAvatar.getParent() == mStatusIconArea) {
mStatusIconArea.removeView(mMultiUserAvatar);
} else if (mMultiUserAvatar.getParent() != null) {
getOverlay().remove(mMultiUserAvatar);
}
return;
}
if (mMultiUserAvatar.getParent() != mStatusIconArea
&& !mKeyguardUserSwitcherEnabled) {
if (mMultiUserAvatar.getParent() != null) {
@@ -346,6 +366,16 @@ public class KeyguardStatusBarView extends RelativeLayout {
mKeyguardUserSwitcherEnabled = enabled;
}
void setKeyguardUserAvatarEnabled(boolean enabled) {
mKeyguardUserAvatarEnabled = enabled;
updateVisibilities();
}
@VisibleForTesting
boolean isKeyguardUserAvatarEnabled() {
return mKeyguardUserAvatarEnabled;
}
private void animateNextLayoutChange() {
final int systemIconsCurrentX = mSystemIconsContainer.getLeft();
final boolean userAvatarVisible = mMultiUserAvatar.getParent() == mStatusIconArea;
@@ -416,9 +446,14 @@ public class KeyguardStatusBarView extends RelativeLayout {
/** Should only be called from {@link KeyguardStatusBarViewController}. */
void onOverlayChanged() {
mCarrierLabel.setTextAppearance(
Utils.getThemeAttr(mContext, com.android.internal.R.attr.textAppearanceSmall));
int theme = Utils.getThemeAttr(mContext, com.android.internal.R.attr.textAppearanceSmall);
mCarrierLabel.setTextAppearance(theme);
mBatteryView.updatePercentView();
TextView userSwitcherName = mUserSwitcherContainer.findViewById(R.id.current_user_name);
if (userSwitcherName != null) {
userSwitcherName.setTextAppearance(theme);
}
}
private void updateIconsAndTextColors(StatusBarIconController.TintedIconManager iconManager) {
@@ -429,6 +464,14 @@ public class KeyguardStatusBarView extends RelativeLayout {
R.color.light_mode_icon_color_single_tone);
float intensity = textColor == Color.WHITE ? 0 : 1;
mCarrierLabel.setTextColor(iconColor);
TextView userSwitcherName = mUserSwitcherContainer.findViewById(R.id.current_user_name);
if (userSwitcherName != null) {
userSwitcherName.setTextColor(Utils.getColorStateListDefaultColor(
mContext,
R.color.light_mode_icon_color_single_tone));
}
if (iconManager != null) {
iconManager.setTint(iconColor);
}

View File

@@ -47,6 +47,9 @@ import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserInfoTracker;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherFeatureController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -95,6 +98,9 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
private final SysuiStatusBarStateController mStatusBarStateController;
private final StatusBarContentInsetsProvider mInsetsProvider;
private final UserManager mUserManager;
private final StatusBarUserSwitcherFeatureController mFeatureController;
private final StatusBarUserSwitcherController mUserSwitcherController;
private final StatusBarUserInfoTracker mStatusBarUserInfoTracker;
private final ConfigurationController.ConfigurationListener mConfigurationListener =
new ConfigurationController.ConfigurationListener() {
@@ -246,7 +252,10 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
BiometricUnlockController biometricUnlockController,
SysuiStatusBarStateController statusBarStateController,
StatusBarContentInsetsProvider statusBarContentInsetsProvider,
UserManager userManager
UserManager userManager,
StatusBarUserSwitcherFeatureController featureController,
StatusBarUserSwitcherController userSwitcherController,
StatusBarUserInfoTracker statusBarUserInfoTracker
) {
super(view);
mCarrierTextController = carrierTextController;
@@ -265,6 +274,9 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
mStatusBarStateController = statusBarStateController;
mInsetsProvider = statusBarContentInsetsProvider;
mUserManager = userManager;
mFeatureController = featureController;
mUserSwitcherController = userSwitcherController;
mStatusBarUserInfoTracker = statusBarUserInfoTracker;
mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
mKeyguardStateController.addCallback(
@@ -286,6 +298,10 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
r.getString(com.android.internal.R.string.status_bar_call_strength)));
mNotificationsHeaderCollideDistance = r.getDimensionPixelSize(
R.dimen.header_notifications_collide_distance);
mView.setKeyguardUserAvatarEnabled(
!mFeatureController.isStatusBarUserSwitcherFeatureEnabled());
mFeatureController.addCallback(enabled -> mView.setKeyguardUserAvatarEnabled(!enabled));
}
@Override
@@ -293,6 +309,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
super.onInit();
mCarrierTextController.init();
mBatteryMeterViewController.init();
mUserSwitcherController.init();
}
@Override
@@ -334,6 +351,9 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
/** Sets whether user switcher is enabled. */
public void setKeyguardUserSwitcherEnabled(boolean enabled) {
mView.setKeyguardUserSwitcherEnabled(enabled);
// We don't have a listener for when the user switcher setting changes, so this is
// where we re-check the state
mStatusBarUserInfoTracker.checkEnabled();
}
/** Sets whether this controller should listen to battery updates. */

View File

@@ -21,15 +21,19 @@ import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import com.android.systemui.R
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.unfold.SysUIUnfoldComponent
import com.android.systemui.unfold.UNFOLD_STATUS_BAR
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.systemui.util.ViewController
import com.android.systemui.util.kotlin.getOrNull
import java.util.Optional
import javax.inject.Inject
import javax.inject.Named
@@ -38,6 +42,7 @@ class PhoneStatusBarViewController private constructor(
view: PhoneStatusBarView,
@Named(UNFOLD_STATUS_BAR) private val progressProvider: ScopedUnfoldTransitionProgressProvider?,
private val moveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?,
private val userSwitcherController: StatusBarUserSwitcherController,
touchEventHandler: PhoneStatusBarView.TouchEventHandler,
private val configurationController: ConfigurationController
) : ViewController<PhoneStatusBarView>(view) {
@@ -89,6 +94,10 @@ class PhoneStatusBarViewController private constructor(
mView.setTouchEventHandler(touchEventHandler)
}
override fun onInit() {
userSwitcherController.init()
}
fun setImportantForAccessibility(mode: Int) {
mView.importantForAccessibility = mode
}
@@ -153,6 +162,7 @@ class PhoneStatusBarViewController private constructor(
private val unfoldComponent: Optional<SysUIUnfoldComponent>,
@Named(UNFOLD_STATUS_BAR)
private val progressProvider: Optional<ScopedUnfoldTransitionProgressProvider>,
private val userSwitcherController: StatusBarUserSwitcherController,
private val configurationController: ConfigurationController
) {
fun create(
@@ -163,6 +173,7 @@ class PhoneStatusBarViewController private constructor(
view,
progressProvider.getOrNull(),
unfoldComponent.getOrNull()?.getStatusBarMoveFromCenterAnimationController(),
userSwitcherController,
touchEventHandler,
configurationController
)

View File

@@ -26,11 +26,15 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherControllerImpl;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.window.StatusBarWindowController;
import javax.inject.Named;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
@@ -80,6 +84,20 @@ public interface StatusBarFragmentModule {
return view.findViewById(R.id.clock);
}
/** */
@Provides
@StatusBarFragmentScope
static StatusBarUserSwitcherContainer provideStatusBarUserSwitcherContainer(
@RootView PhoneStatusBarView view) {
return view.findViewById(R.id.user_switcher_container);
}
/** */
@Binds
@StatusBarFragmentScope
StatusBarUserSwitcherController bindStatusBarUserSwitcherController(
StatusBarUserSwitcherControllerImpl controller);
/** */
@Provides
@StatusBarFragmentScope

View File

@@ -0,0 +1,122 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.phone.userswitcher
import android.graphics.drawable.Drawable
import android.os.UserManager
import com.android.systemui.DejankUtils.whitelistIpcs
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.statusbar.policy.UserInfoController
import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener
import javax.inject.Inject
/**
* Since every user switcher chip will user the exact same information and logic on whether or not
* to show, and what data to show, it makes sense to create a single tracker here
*/
@SysUISingleton
class StatusBarUserInfoTracker @Inject constructor(
private val userInfoController: UserInfoController,
private val userManager: UserManager
) : CallbackController<CurrentUserChipInfoUpdatedListener> {
var currentUserName: String? = null
private set
var currentUserAvatar: Drawable? = null
private set
var userSwitcherEnabled = false
private set
private var listening = false
private val listeners = mutableListOf<CurrentUserChipInfoUpdatedListener>()
private val userInfoChangedListener = OnUserInfoChangedListener { name, picture, _ ->
currentUserAvatar = picture
currentUserName = name
notifyListenersUserInfoChanged()
}
init {
startListening()
}
override fun addCallback(listener: CurrentUserChipInfoUpdatedListener) {
if (listeners.isEmpty()) {
startListening()
}
if (!listeners.contains(listener)) {
listeners.add(listener)
}
}
override fun removeCallback(listener: CurrentUserChipInfoUpdatedListener) {
listeners.remove(listener)
if (listeners.isEmpty()) {
stopListening()
}
}
private fun notifyListenersUserInfoChanged() {
listeners.forEach {
it.onCurrentUserChipInfoUpdated()
}
}
private fun notifyListenersSettingChanged() {
listeners.forEach {
it.onStatusBarUserSwitcherSettingChanged(userSwitcherEnabled)
}
}
private fun startListening() {
listening = true
userInfoController.addCallback(userInfoChangedListener)
}
private fun stopListening() {
listening = false
userInfoController.removeCallback(userInfoChangedListener)
}
private fun checkUserSwitcherEnabled() {
whitelistIpcs {
userSwitcherEnabled = userManager.isUserSwitcherEnabled
}
}
/**
* Force a check to [UserManager.isUserSwitcherEnabled], and update listeners if the value has
* changed
*/
fun checkEnabled() {
val wasEnabled = userSwitcherEnabled
checkUserSwitcherEnabled()
if (wasEnabled != userSwitcherEnabled) {
notifyListenersSettingChanged()
}
}
}
interface CurrentUserChipInfoUpdatedListener {
fun onCurrentUserChipInfoUpdated()
fun onStatusBarUserSwitcherSettingChanged(enabled: Boolean) {}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.phone.userswitcher
import android.content.Context
import android.util.AttributeSet
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import com.android.systemui.R
class StatusBarUserSwitcherContainer(
context: Context?,
attrs: AttributeSet?
) : LinearLayout(context, attrs) {
lateinit var text: TextView
private set
lateinit var avatar: ImageView
private set
override fun onFinishInflate() {
super.onFinishInflate()
text = findViewById(R.id.current_user_name)
avatar = findViewById(R.id.current_user_avatar)
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.phone.userswitcher
import android.view.View
import com.android.systemui.qs.user.UserSwitchDialogController
import com.android.systemui.util.ViewController
import javax.inject.Inject
/**
* ViewController for [StatusBarUserSwitcherContainer]
*/
class StatusBarUserSwitcherControllerImpl @Inject constructor(
view: StatusBarUserSwitcherContainer,
private val tracker: StatusBarUserInfoTracker,
private val featureController: StatusBarUserSwitcherFeatureController,
private val userSwitcherDialogController: UserSwitchDialogController
) : ViewController<StatusBarUserSwitcherContainer>(view),
StatusBarUserSwitcherController {
private val listener = object : CurrentUserChipInfoUpdatedListener {
override fun onCurrentUserChipInfoUpdated() {
updateChip()
}
override fun onStatusBarUserSwitcherSettingChanged(enabled: Boolean) {
updateEnabled()
}
}
private val featureFlagListener = object : OnUserSwitcherPreferenceChangeListener {
override fun onUserSwitcherPreferenceChange(enabled: Boolean) {
updateEnabled()
}
}
override fun onViewAttached() {
tracker.addCallback(listener)
featureController.addCallback(featureFlagListener)
mView.setOnClickListener {
userSwitcherDialogController.showDialog(it)
}
updateEnabled()
}
override fun onViewDetached() {
tracker.removeCallback(listener)
featureController.removeCallback(featureFlagListener)
mView.setOnClickListener(null)
}
private fun updateChip() {
mView.text.text = tracker.currentUserName
mView.avatar.setImageDrawable(tracker.currentUserAvatar)
}
private fun updateEnabled() {
if (featureController.isStatusBarUserSwitcherFeatureEnabled() &&
tracker.userSwitcherEnabled) {
mView.visibility = View.VISIBLE
updateChip()
} else {
mView.visibility = View.GONE
}
}
}
interface StatusBarUserSwitcherController {
fun init()
}
private const val TAG = "SbUserSwitcherController"

View File

@@ -0,0 +1,63 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.phone.userswitcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.policy.CallbackController
import javax.inject.Inject
@SysUISingleton
class StatusBarUserSwitcherFeatureController @Inject constructor(
private val flags: FeatureFlags
) : CallbackController<OnUserSwitcherPreferenceChangeListener> {
private val listeners = mutableListOf<OnUserSwitcherPreferenceChangeListener>()
init {
flags.addListener(Flags.STATUS_BAR_USER_SWITCHER) {
it.requestNoRestart()
notifyListeners()
}
}
fun isStatusBarUserSwitcherFeatureEnabled(): Boolean {
return flags.isEnabled(Flags.STATUS_BAR_USER_SWITCHER)
}
override fun addCallback(listener: OnUserSwitcherPreferenceChangeListener) {
if (!listeners.contains(listener)) {
listeners.add(listener)
}
}
override fun removeCallback(listener: OnUserSwitcherPreferenceChangeListener) {
listeners.remove(listener)
}
private fun notifyListeners() {
val enabled = flags.isEnabled(Flags.STATUS_BAR_USER_SWITCHER)
listeners.forEach {
it.onUserSwitcherPreferenceChange(enabled)
}
}
}
interface OnUserSwitcherPreferenceChangeListener {
fun onUserSwitcherPreferenceChange(enabled: Boolean)
}

View File

@@ -47,6 +47,9 @@ import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserInfoTracker;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherFeatureController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -99,6 +102,12 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
private ArgumentCaptor<ConfigurationListener> mConfigurationListenerCaptor;
@Captor
private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardCallbackCaptor;
@Mock
private StatusBarUserSwitcherFeatureController mStatusBarUserSwitcherFeatureController;
@Mock
private StatusBarUserSwitcherController mStatusBarUserSwitcherController;
@Mock
private StatusBarUserInfoTracker mStatusBarUserInfoTracker;
private TestNotificationPanelViewStateProvider mNotificationPanelViewStateProvider;
private KeyguardStatusBarView mKeyguardStatusBarView;
@@ -117,7 +126,11 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
.inflate(R.layout.keyguard_status_bar, null));
});
mController = new KeyguardStatusBarViewController(
mController = createController();
}
private KeyguardStatusBarViewController createController() {
return new KeyguardStatusBarViewController(
mKeyguardStatusBarView,
mCarrierTextController,
mConfigurationController,
@@ -134,7 +147,10 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
mBiometricUnlockController,
mStatusBarStateController,
mStatusBarContentInsetsProvider,
mUserManager
mUserManager,
mStatusBarUserSwitcherFeatureController,
mStatusBarUserSwitcherController,
mStatusBarUserInfoTracker
);
}
@@ -356,6 +372,32 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
public void testNewUserSwitcherDisablesAvatar_newUiOn() {
// GIVEN the status bar user switcher chip is enabled
when(mStatusBarUserSwitcherFeatureController.isStatusBarUserSwitcherFeatureEnabled())
.thenReturn(true);
// WHEN the controller is created
mController = createController();
// THEN keyguard status bar view avatar is disabled
assertThat(mKeyguardStatusBarView.isKeyguardUserAvatarEnabled()).isFalse();
}
@Test
public void testNewUserSwitcherDisablesAvatar_newUiOff() {
// GIVEN the status bar user switcher chip is disabled
when(mStatusBarUserSwitcherFeatureController.isStatusBarUserSwitcherFeatureEnabled())
.thenReturn(false);
// WHEN the controller is created
mController = createController();
// THEN keyguard status bar view avatar is enabled
assertThat(mKeyguardStatusBarView.isKeyguardUserAvatarEnabled()).isTrue();
}
private void updateStateToNotKeyguard() {
updateStatusBarState(SHADE);
}

View File

@@ -26,6 +26,7 @@ import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.unfold.SysUIUnfoldComponent
import com.android.systemui.unfold.config.UnfoldTransitionConfig
@@ -60,6 +61,8 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
private lateinit var progressProvider: ScopedUnfoldTransitionProgressProvider
@Mock
private lateinit var configurationController: ConfigurationController
@Mock
private lateinit var userSwitcherController: StatusBarUserSwitcherController
private lateinit var view: PhoneStatusBarView
private lateinit var controller: PhoneStatusBarViewController
@@ -187,6 +190,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
return PhoneStatusBarViewController.Factory(
Optional.of(sysuiUnfoldComponent),
Optional.of(progressProvider),
userSwitcherController,
configurationController
).create(view, touchEventHandler).also {
it.init()