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
diff --git a/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml b/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml
new file mode 100644
index 0000000..9891156
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml
@@ -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>
diff --git a/packages/SystemUI/res/layout/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml
index 850b0171..e47eed9 100644
--- a/packages/SystemUI/res/layout/keyguard_status_bar.xml
+++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml
@@ -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" />
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 8b244c7..af98019 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -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>
diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml
index 6d5c7d4..4f4bae4 100644
--- a/packages/SystemUI/res/layout/system_icons.xml
+++ b/packages/SystemUI/res/layout/system_icons.xml
@@ -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">
 
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 2a70645..49dd574 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -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>
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java
index fc14b6a..8fc8600 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java
@@ -20,7 +20,11 @@
 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 @@
     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);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index c894b70..d67815f 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -112,8 +112,8 @@
     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
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 2ec5f25..b8e9875 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -40,6 +40,8 @@
 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 @@
     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 @@
         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 @@
     }
 
     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 @@
         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 @@
 
     /** 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 @@
                 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);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index ee97fd6..1df1aff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -47,6 +47,9 @@
 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 @@
     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 @@
             BiometricUnlockController biometricUnlockController,
             SysuiStatusBarStateController statusBarStateController,
             StatusBarContentInsetsProvider statusBarContentInsetsProvider,
-            UserManager userManager
+            UserManager userManager,
+            StatusBarUserSwitcherFeatureController featureController,
+            StatusBarUserSwitcherController userSwitcherController,
+            StatusBarUserInfoTracker statusBarUserInfoTracker
     ) {
         super(view);
         mCarrierTextController = carrierTextController;
@@ -265,6 +274,9 @@
         mStatusBarStateController = statusBarStateController;
         mInsetsProvider = statusBarContentInsetsProvider;
         mUserManager = userManager;
+        mFeatureController = featureController;
+        mUserSwitcherController = userSwitcherController;
+        mStatusBarUserInfoTracker = statusBarUserInfoTracker;
 
         mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
         mKeyguardStateController.addCallback(
@@ -286,6 +298,10 @@
                 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 @@
         super.onInit();
         mCarrierTextController.init();
         mBatteryMeterViewController.init();
+        mUserSwitcherController.init();
     }
 
     @Override
@@ -334,6 +351,9 @@
     /** 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. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index d6bf5f2..224b2e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -21,15 +21,19 @@
 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 @@
     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 @@
         mView.setTouchEventHandler(touchEventHandler)
     }
 
+    override fun onInit() {
+        userSwitcherController.init()
+    }
+
     fun setImportantForAccessibility(mode: Int) {
         mView.importantForAccessibility = mode
     }
@@ -153,6 +162,7 @@
         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 @@
                 view,
                 progressProvider.getOrNull(),
                 unfoldComponent.getOrNull()?.getStatusBarMoveFromCenterAnimationController(),
+                userSwitcherController,
                 touchEventHandler,
                 configurationController
             )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
index dea1b43..e2dc905 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
@@ -26,11 +26,15 @@
 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;
 
@@ -83,6 +87,20 @@
     /** */
     @Provides
     @StatusBarFragmentScope
+    static StatusBarUserSwitcherContainer provideStatusBarUserSwitcherContainer(
+            @RootView PhoneStatusBarView view) {
+        return view.findViewById(R.id.user_switcher_container);
+    }
+
+    /** */
+    @Binds
+    @StatusBarFragmentScope
+    StatusBarUserSwitcherController bindStatusBarUserSwitcherController(
+            StatusBarUserSwitcherControllerImpl controller);
+
+    /** */
+    @Provides
+    @StatusBarFragmentScope
     static PhoneStatusBarViewController providePhoneStatusBarViewController(
             PhoneStatusBarViewController.Factory phoneStatusBarViewControllerFactory,
             @RootView PhoneStatusBarView phoneStatusBarView,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserInfoTracker.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserInfoTracker.kt
new file mode 100644
index 0000000..2dbc19c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserInfoTracker.kt
@@ -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) {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt
new file mode 100644
index 0000000..2c8677d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt
@@ -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)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt
new file mode 100644
index 0000000..a124753
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt
@@ -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"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherFeatureController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherFeatureController.kt
new file mode 100644
index 0000000..7bae9ff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherFeatureController.kt
@@ -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)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index 01e9822e..7de3545 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -47,6 +47,9 @@
 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 @@
     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 @@
                             .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 @@
                 mBiometricUnlockController,
                 mStatusBarStateController,
                 mStatusBarContentInsetsProvider,
-                mUserManager
+                mUserManager,
+                mStatusBarUserSwitcherFeatureController,
+                mStatusBarUserSwitcherController,
+                mStatusBarUserInfoTracker
         );
     }
 
@@ -356,6 +372,32 @@
         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);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index c65a6b6..5891161 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -26,6 +26,7 @@
 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 @@
     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 @@
         return PhoneStatusBarViewController.Factory(
             Optional.of(sysuiUnfoldComponent),
             Optional.of(progressProvider),
+            userSwitcherController,
             configurationController
         ).create(view, touchEventHandler).also {
             it.init()