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:
@@ -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>
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user