Merge changes I8d766f4a,I3f5e212f,Ia78513a0 into tm-qpr-dev

* changes:
  Update status bar user chip view spec
  Replace StatusBarUserSwitcherController with UserInteractor
  Move showUserSwitcher to UserInteractor
This commit is contained in:
Evan Laird
2022-11-17 15:16:16 +00:00
committed by Android (Google) Code Review
31 changed files with 730 additions and 617 deletions

View File

@@ -34,30 +34,13 @@
android:paddingTop="@dimen/status_bar_padding_top"
android:layout_alignParentEnd="true"
android:gravity="center_vertical|end" >
<com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer
<include
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>
android:layout_marginEnd="@dimen/status_bar_user_chip_end_margin"
layout="@layout/status_bar_user_chip_container" />
<FrameLayout android:id="@+id/system_icons_container"
android:layout_width="wrap_content"

View File

@@ -136,31 +136,12 @@
android:gravity="center_vertical|end"
android:clipChildren="false">
<com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer
<include
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>
android:layout_marginEnd="@dimen/status_bar_user_chip_end_margin"
layout="@layout/status_bar_user_chip_container" />
<include layout="@layout/system_icons" />
</com.android.keyguard.AlphaOptimizedLinearLayout>

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/user_switcher_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:layout_marginEnd="@dimen/status_bar_user_chip_end_margin"
android:background="@drawable/status_bar_user_chip_bg"
android:visibility="visible" >
<ImageView android:id="@+id/current_user_avatar"
android:layout_width="@dimen/status_bar_user_chip_avatar_size"
android:layout_height="@dimen/status_bar_user_chip_avatar_size"
android:layout_margin="4dp"
android:scaleType="centerInside" />
<TextView android:id="@+id/current_user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingEnd="8dp"
android:textAppearance="@style/TextAppearance.StatusBar.UserChip"
/>
</com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer>

View File

@@ -1404,6 +1404,11 @@
<dimen name="ongoing_call_chip_icon_text_padding">4dp</dimen>
<dimen name="ongoing_call_chip_corner_radius">28dp</dimen>
<!-- Status bar user chip -->
<dimen name="status_bar_user_chip_avatar_size">16dp</dimen>
<dimen name="status_bar_user_chip_end_margin">12dp</dimen>
<dimen name="status_bar_user_chip_text_size">12sp</dimen>
<!-- Internet panel related dimensions -->
<dimen name="internet_dialog_list_max_height">662dp</dimen>
<!-- The height of the WiFi network in Internet panel. -->

View File

@@ -23,6 +23,12 @@
<item name="android:textColor">@color/status_bar_clock_color</item>
</style>
<style name="TextAppearance.StatusBar.UserChip" parent="@*android:style/TextAppearance.StatusBar.Icon">
<item name="android:textSize">@dimen/status_bar_user_chip_text_size</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
<item name="android:textColor">@color/status_bar_clock_color</item>
</style>
<style name="TextAppearance.StatusBar.Expanded" parent="@*android:style/TextAppearance.StatusBar">
<item name="android:textColor">?android:attr/textColorTertiary</item>
</style>

View File

@@ -21,10 +21,7 @@ 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;
@@ -50,10 +47,4 @@ public abstract class KeyguardStatusBarViewModule {
static StatusBarUserSwitcherContainer getUserSwitcherContainer(KeyguardStatusBarView view) {
return view.findViewById(R.id.user_switcher_container);
}
/** */
@Binds
@KeyguardStatusBarViewScope
abstract StatusBarUserSwitcherController bindStatusBarUserSwitcherController(
StatusBarUserSwitcherControllerImpl controller);
}

View File

@@ -32,8 +32,6 @@ import com.android.systemui.animation.Expandable
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.globalactions.GlobalActionsDialogLite
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.FgsManagerController
@@ -42,10 +40,9 @@ import com.android.systemui.qs.footer.data.model.UserSwitcherStatusModel
import com.android.systemui.qs.footer.data.repository.ForegroundServicesRepository
import com.android.systemui.qs.footer.data.repository.UserSwitcherRepository
import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig
import com.android.systemui.qs.user.UserSwitchDialogController
import com.android.systemui.security.data.repository.SecurityRepository
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.user.UserSwitcherActivity
import com.android.systemui.user.domain.interactor.UserInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
@@ -100,13 +97,12 @@ class FooterActionsInteractorImpl
@Inject
constructor(
private val activityStarter: ActivityStarter,
private val featureFlags: FeatureFlags,
private val metricsLogger: MetricsLogger,
private val uiEventLogger: UiEventLogger,
private val deviceProvisionedController: DeviceProvisionedController,
private val qsSecurityFooterUtils: QSSecurityFooterUtils,
private val fgsManagerController: FgsManagerController,
private val userSwitchDialogController: UserSwitchDialogController,
private val userInteractor: UserInteractor,
securityRepository: SecurityRepository,
foregroundServicesRepository: ForegroundServicesRepository,
userSwitcherRepository: UserSwitcherRepository,
@@ -182,22 +178,6 @@ constructor(
}
override fun showUserSwitcher(context: Context, expandable: Expandable) {
if (!featureFlags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)) {
userSwitchDialogController.showDialog(context, expandable)
return
}
val intent =
Intent(context, UserSwitcherActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
}
activityStarter.startActivity(
intent,
true /* dismissShade */,
expandable.activityLaunchController(),
true /* showOverlockscreenwhenlocked */,
UserHandle.SYSTEM,
)
userInteractor.showUserSwitcher(context, expandable)
}
}

View File

@@ -48,6 +48,9 @@ import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
import com.android.systemui.user.ui.binder.StatusBarUserChipViewBinder;
import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -70,7 +73,7 @@ public class KeyguardStatusBarView extends RelativeLayout {
private ImageView mMultiUserAvatar;
private BatteryMeterView mBatteryView;
private StatusIconContainer mStatusIconContainer;
private ViewGroup mUserSwitcherContainer;
private StatusBarUserSwitcherContainer mUserSwitcherContainer;
private boolean mKeyguardUserSwitcherEnabled;
private boolean mKeyguardUserAvatarEnabled;
@@ -121,8 +124,12 @@ public class KeyguardStatusBarView extends RelativeLayout {
loadDimens();
}
public ViewGroup getUserSwitcherContainer() {
return mUserSwitcherContainer;
/**
* Should only be called from {@link KeyguardStatusBarViewController}
* @param viewModel view model for the status bar user chip
*/
void init(StatusBarUserChipViewModel viewModel) {
StatusBarUserChipViewBinder.bind(mUserSwitcherContainer, viewModel);
}
@Override
@@ -304,10 +311,7 @@ public class KeyguardStatusBarView extends RelativeLayout {
lp = (LayoutParams) mStatusIconArea.getLayoutParams();
lp.removeRule(RelativeLayout.RIGHT_OF);
lp.width = LayoutParams.WRAP_CONTENT;
LinearLayout.LayoutParams llp =
(LinearLayout.LayoutParams) mSystemIconsContainer.getLayoutParams();
llp.setMarginStart(getResources().getDimensionPixelSize(
lp.setMarginStart(getResources().getDimensionPixelSize(
R.dimen.system_icons_super_container_margin_start));
return true;
}
@@ -339,10 +343,7 @@ public class KeyguardStatusBarView extends RelativeLayout {
lp = (LayoutParams) mStatusIconArea.getLayoutParams();
lp.addRule(RelativeLayout.RIGHT_OF, R.id.cutout_space_view);
lp.width = LayoutParams.MATCH_PARENT;
LinearLayout.LayoutParams llp =
(LinearLayout.LayoutParams) mSystemIconsContainer.getLayoutParams();
llp.setMarginStart(0);
lp.setMarginStart(0);
return true;
}

View File

@@ -59,13 +59,11 @@ import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.fragment.StatusBarIconBlocklistKt;
import com.android.systemui.statusbar.phone.fragment.StatusBarSystemEventAnimator;
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;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.settings.SecureSettings;
@@ -110,9 +108,7 @@ 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 StatusBarUserChipViewModel mStatusBarUserChipViewModel;
private final SecureSettings mSecureSettings;
private final CommandQueue mCommandQueue;
private final Executor mMainExecutor;
@@ -276,9 +272,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
SysuiStatusBarStateController statusBarStateController,
StatusBarContentInsetsProvider statusBarContentInsetsProvider,
UserManager userManager,
StatusBarUserSwitcherFeatureController featureController,
StatusBarUserSwitcherController userSwitcherController,
StatusBarUserInfoTracker statusBarUserInfoTracker,
StatusBarUserChipViewModel userChipViewModel,
SecureSettings secureSettings,
CommandQueue commandQueue,
@Main Executor mainExecutor,
@@ -301,9 +295,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
mStatusBarStateController = statusBarStateController;
mInsetsProvider = statusBarContentInsetsProvider;
mUserManager = userManager;
mFeatureController = featureController;
mUserSwitcherController = userSwitcherController;
mStatusBarUserInfoTracker = statusBarUserInfoTracker;
mStatusBarUserChipViewModel = userChipViewModel;
mSecureSettings = secureSettings;
mCommandQueue = commandQueue;
mMainExecutor = mainExecutor;
@@ -328,8 +320,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
R.dimen.header_notifications_collide_distance);
mView.setKeyguardUserAvatarEnabled(
!mFeatureController.isStatusBarUserSwitcherFeatureEnabled());
mFeatureController.addCallback(enabled -> mView.setKeyguardUserAvatarEnabled(!enabled));
!mStatusBarUserChipViewModel.getChipEnabled());
mSystemEventAnimator = new StatusBarSystemEventAnimator(mView, r);
mDisableStateTracker = new DisableStateTracker(
@@ -344,11 +335,11 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
super.onInit();
mCarrierTextController.init();
mBatteryMeterViewController.init();
mUserSwitcherController.init();
}
@Override
protected void onViewAttached() {
mView.init(mStatusBarUserChipViewModel);
mConfigurationController.addCallback(mConfigurationListener);
mAnimationScheduler.addCallback(mAnimationCallback);
mUserInfoController.addCallback(mOnUserInfoChangedListener);
@@ -394,9 +385,6 @@ 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

@@ -38,6 +38,9 @@ import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
import com.android.systemui.user.ui.binder.StatusBarUserChipViewBinder;
import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel;
import com.android.systemui.util.leak.RotationUtils;
import java.util.Objects;
@@ -73,6 +76,11 @@ public class PhoneStatusBarView extends FrameLayout {
mTouchEventHandler = handler;
}
void init(StatusBarUserChipViewModel viewModel) {
StatusBarUserSwitcherContainer container = findViewById(R.id.user_switcher_container);
StatusBarUserChipViewBinder.bind(container, viewModel);
}
@Override
public void onFinishInflate() {
super.onFinishInflate();

View File

@@ -23,11 +23,11 @@ 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.user.ui.viewmodel.StatusBarUserChipViewModel
import com.android.systemui.util.ViewController
import com.android.systemui.util.kotlin.getOrNull
import com.android.systemui.util.view.ViewUtil
@@ -40,7 +40,7 @@ class PhoneStatusBarViewController private constructor(
view: PhoneStatusBarView,
@Named(UNFOLD_STATUS_BAR) private val progressProvider: ScopedUnfoldTransitionProgressProvider?,
private val moveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?,
private val userSwitcherController: StatusBarUserSwitcherController,
private val userChipViewModel: StatusBarUserChipViewModel,
private val viewUtil: ViewUtil,
touchEventHandler: PhoneStatusBarView.TouchEventHandler,
private val configurationController: ConfigurationController
@@ -91,10 +91,10 @@ class PhoneStatusBarViewController private constructor(
init {
mView.setTouchEventHandler(touchEventHandler)
mView.init(userChipViewModel)
}
override fun onInit() {
userSwitcherController.init()
}
fun setImportantForAccessibility(mode: Int) {
@@ -156,9 +156,9 @@ class PhoneStatusBarViewController private constructor(
private val unfoldComponent: Optional<SysUIUnfoldComponent>,
@Named(UNFOLD_STATUS_BAR)
private val progressProvider: Optional<ScopedUnfoldTransitionProgressProvider>,
private val userSwitcherController: StatusBarUserSwitcherController,
private val userChipViewModel: StatusBarUserChipViewModel,
private val viewUtil: ViewUtil,
private val configurationController: ConfigurationController
private val configurationController: ConfigurationController,
) {
fun create(
view: PhoneStatusBarView,
@@ -168,7 +168,7 @@ class PhoneStatusBarViewController private constructor(
view,
progressProvider.getOrNull(),
unfoldComponent.getOrNull()?.getStatusBarMoveFromCenterAnimationController(),
userSwitcherController,
userChipViewModel,
viewUtil,
touchEventHandler,
configurationController

View File

@@ -29,8 +29,6 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.StatusBarBoundsProvider;
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;
@@ -39,7 +37,6 @@ import java.util.Set;
import javax.inject.Named;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.Multibinds;
@@ -125,12 +122,6 @@ public interface StatusBarFragmentModule {
return view.findViewById(R.id.user_switcher_container);
}
/** */
@Binds
@StatusBarFragmentScope
StatusBarUserSwitcherController bindStatusBarUserSwitcherController(
StatusBarUserSwitcherControllerImpl controller);
/** */
@Provides
@StatusBarFragmentScope

View File

@@ -1,134 +0,0 @@
/*
* 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.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.statusbar.policy.UserInfoController
import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener
import java.io.PrintWriter
import java.util.concurrent.Executor
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,
private val dumpManager: DumpManager,
@Main private val mainExecutor: Executor,
@Background private val backgroundExecutor: Executor
) : CallbackController<CurrentUserChipInfoUpdatedListener>, Dumpable {
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 {
dumpManager.registerDumpable(TAG, this)
}
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)
}
/**
* Force a check to [UserManager.isUserSwitcherEnabled], and update listeners if the value has
* changed
*/
fun checkEnabled() {
backgroundExecutor.execute {
// Check on a background thread to avoid main thread Binder calls
val wasEnabled = userSwitcherEnabled
userSwitcherEnabled = userManager.isUserSwitcherEnabled
if (wasEnabled != userSwitcherEnabled) {
mainExecutor.execute {
notifyListenersSettingChanged()
}
}
}
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println(" userSwitcherEnabled=$userSwitcherEnabled")
pw.println(" listening=$listening")
}
}
interface CurrentUserChipInfoUpdatedListener {
fun onCurrentUserChipInfoUpdated()
fun onStatusBarUserSwitcherSettingChanged(enabled: Boolean) {}
}
private const val TAG = "StatusBarUserInfoTracker"

View File

@@ -1,112 +0,0 @@
/*
* 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.Intent
import android.os.UserHandle
import android.view.View
import com.android.systemui.animation.Expandable
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.user.UserSwitchDialogController
import com.android.systemui.user.UserSwitcherActivity
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,
private val featureFlags: FeatureFlags,
private val activityStarter: ActivityStarter,
private val falsingManager: FalsingManager
) : 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()
}
}
public override fun onViewAttached() {
tracker.addCallback(listener)
featureController.addCallback(featureFlagListener)
mView.setOnClickListener { view: View ->
if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
return@setOnClickListener
}
if (featureFlags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)) {
val intent = Intent(context, UserSwitcherActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
activityStarter.startActivity(intent, true /* dismissShade */,
null /* ActivityLaunchAnimator.Controller */,
true /* showOverlockscreenwhenlocked */, UserHandle.SYSTEM)
} else {
userSwitcherDialogController.showDialog(view.context, Expandable.fromView(view))
}
}
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

@@ -1,63 +0,0 @@
/*
* 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

@@ -23,6 +23,7 @@ import android.os.UserHandle
import android.os.UserManager
import android.provider.Settings
import androidx.annotation.VisibleForTesting
import com.android.systemui.R
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
@@ -79,6 +80,9 @@ interface UserRepository {
/** Whether we've scheduled the creation of a guest user. */
val isGuestUserCreationScheduled: AtomicBoolean
/** Whether to enable the status bar user chip (which launches the user switcher) */
val isStatusBarUserChipEnabled: Boolean
/** The user of the secondary service. */
var secondaryUserId: Int
@@ -127,6 +131,9 @@ constructor(
override val isGuestUserCreationScheduled = AtomicBoolean()
override val isStatusBarUserChipEnabled: Boolean =
appContext.resources.getBoolean(R.bool.flag_user_switcher_chip)
override var secondaryUserId: Int = UserHandle.USER_NULL
override var isRefreshUsersPaused: Boolean = false

View File

@@ -34,15 +34,19 @@ import android.util.Log
import com.android.internal.util.UserIcons
import com.android.systemui.R
import com.android.systemui.SystemUISecondaryUserService
import com.android.systemui.animation.Expandable
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.shared.model.Text
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.user.UserSwitchDialogController
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
import com.android.systemui.user.UserSwitcherActivity
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.user.data.source.UserRecord
@@ -81,6 +85,7 @@ constructor(
private val repository: UserRepository,
private val activityStarter: ActivityStarter,
private val keyguardInteractor: KeyguardInteractor,
private val featureFlags: FeatureFlags,
private val manager: UserManager,
@Application private val applicationScope: CoroutineScope,
telephonyInteractor: TelephonyInteractor,
@@ -256,6 +261,9 @@ constructor(
/** Whether the guest user is currently being reset. */
val isGuestUserResetting: Boolean = guestUserInteractor.isGuestUserResetting
/** Whether to enable the user chip in the status bar */
val isStatusBarUserChipEnabled: Boolean = repository.isStatusBarUserChipEnabled
private val _dialogShowRequests = MutableStateFlow<ShowDialogRequestModel?>(null)
val dialogShowRequests: Flow<ShowDialogRequestModel?> = _dialogShowRequests.asStateFlow()
@@ -468,6 +476,26 @@ constructor(
}
}
fun showUserSwitcher(context: Context, expandable: Expandable) {
if (!featureFlags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)) {
showDialog(ShowDialogRequestModel.ShowUserSwitcherDialog)
return
}
val intent =
Intent(context, UserSwitcherActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
}
activityStarter.startActivity(
intent,
true /* dismissShade */,
expandable.activityLaunchController(),
true /* showOverlockscreenwhenlocked */,
UserHandle.SYSTEM,
)
}
private fun showDialog(request: ShowDialogRequestModel) {
_dialogShowRequests.value = request
}

View File

@@ -43,4 +43,7 @@ sealed class ShowDialogRequestModel(
val onExitGuestUser: (guestId: Int, targetId: Int, forceRemoveGuest: Boolean) -> Unit,
override val dialogShower: UserSwitchDialogController.DialogShower?,
) : ShowDialogRequestModel(dialogShower)
/** Show the user switcher dialog */
object ShowUserSwitcherDialog : ShowDialogRequestModel()
}

View File

@@ -0,0 +1,65 @@
/*
* 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.user.ui.binder
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.animation.Expandable
import com.android.systemui.common.ui.binder.TextViewBinder
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer
import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
@OptIn(InternalCoroutinesApi::class)
object StatusBarUserChipViewBinder {
/** Binds the status bar user chip view model to the given view */
@JvmStatic
fun bind(
view: StatusBarUserSwitcherContainer,
viewModel: StatusBarUserChipViewModel,
) {
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
viewModel.isChipVisible.collect { isVisible -> view.isVisible = isVisible }
}
launch {
viewModel.userName.collect { name -> TextViewBinder.bind(view.text, name) }
}
launch {
viewModel.userAvatar.collect { avatar -> view.avatar.setImageDrawable(avatar) }
}
bindButton(view, viewModel)
}
}
}
private fun bindButton(
view: StatusBarUserSwitcherContainer,
viewModel: StatusBarUserChipViewModel,
) {
view.setOnClickListener { viewModel.onClick(Expandable.fromView(view)) }
}
}

View File

@@ -0,0 +1,68 @@
package com.android.systemui.user.ui.dialog
import android.content.Context
import android.content.Intent
import android.provider.Settings
import android.view.LayoutInflater
import com.android.internal.logging.UiEventLogger
import com.android.systemui.R
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.QSUserSwitcherEvent
import com.android.systemui.qs.tiles.UserDetailView
import com.android.systemui.statusbar.phone.SystemUIDialog
/**
* Extracted from the old UserSwitchDialogController. This is the dialog version of the full-screen
* user switcher. See config_enableFullscreenUserSwitcher
*/
class UserSwitchDialog(
context: Context,
adapter: UserDetailView.Adapter,
uiEventLogger: UiEventLogger,
falsingManager: FalsingManager,
activityStarter: ActivityStarter,
dialogLaunchAnimator: DialogLaunchAnimator,
) : SystemUIDialog(context) {
init {
setShowForAllUsers(true)
setCanceledOnTouchOutside(true)
setTitle(R.string.qs_user_switch_dialog_title)
setPositiveButton(R.string.quick_settings_done) { _, _ ->
uiEventLogger.log(QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE)
}
setNeutralButton(
R.string.quick_settings_more_user_settings,
{ _, _ ->
if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
uiEventLogger.log(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS)
val controller =
dialogLaunchAnimator.createActivityLaunchController(
getButton(BUTTON_NEUTRAL)
)
if (controller == null) {
dismiss()
}
activityStarter.postStartActivityDismissingKeyguard(
USER_SETTINGS_INTENT,
0,
controller
)
}
},
false /* dismissOnClick */
)
val gridFrame =
LayoutInflater.from(this.context).inflate(R.layout.qs_user_dialog_content, null)
setView(gridFrame)
adapter.linkToViewGroup(gridFrame.findViewById(R.id.grid))
}
companion object {
private val USER_SETTINGS_INTENT = Intent(Settings.ACTION_USER_SETTINGS)
}
}

View File

@@ -20,6 +20,7 @@ package com.android.systemui.user.ui.dialog
import android.app.Dialog
import android.content.Context
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.UiEventLogger
import com.android.settingslib.users.UserCreatingDialog
import com.android.systemui.CoreStartable
import com.android.systemui.animation.DialogCuj
@@ -27,11 +28,14 @@ import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.tiles.UserDetailView
import com.android.systemui.user.domain.interactor.UserInteractor
import com.android.systemui.user.domain.model.ShowDialogRequestModel
import dagger.Lazy
import javax.inject.Inject
import javax.inject.Provider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch
@@ -47,6 +51,9 @@ constructor(
private val broadcastSender: Lazy<BroadcastSender>,
private val dialogLaunchAnimator: Lazy<DialogLaunchAnimator>,
private val interactor: Lazy<UserInteractor>,
private val userDetailAdapterProvider: Provider<UserDetailView.Adapter>,
private val eventLogger: Lazy<UiEventLogger>,
private val activityStarter: Lazy<ActivityStarter>,
) : CoreStartable {
private var currentDialog: Dialog? = null
@@ -108,6 +115,21 @@ constructor(
INTERACTION_JANK_EXIT_GUEST_MODE_TAG,
),
)
is ShowDialogRequestModel.ShowUserSwitcherDialog ->
Pair(
UserSwitchDialog(
context = context.get(),
adapter = userDetailAdapterProvider.get(),
uiEventLogger = eventLogger.get(),
falsingManager = falsingManager.get(),
activityStarter = activityStarter.get(),
dialogLaunchAnimator = dialogLaunchAnimator.get(),
),
DialogCuj(
InteractionJankMonitor.CUJ_USER_DIALOG_OPEN,
INTERACTION_JANK_EXIT_GUEST_MODE_TAG,
),
)
}
currentDialog = dialog

View File

@@ -0,0 +1,59 @@
/*
* 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.user.ui.viewmodel
import android.content.Context
import android.graphics.drawable.Drawable
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Text
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.user.domain.interactor.UserInteractor
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.mapLatest
@OptIn(ExperimentalCoroutinesApi::class)
class StatusBarUserChipViewModel
@Inject
constructor(
@Application private val context: Context,
interactor: UserInteractor,
) {
/** Whether the status bar chip ui should be available */
val chipEnabled: Boolean = interactor.isStatusBarUserChipEnabled
/** Whether or not the chip should be showing, based on the number of users */
val isChipVisible: Flow<Boolean> =
if (!chipEnabled) {
flowOf(false)
} else {
interactor.users.mapLatest { users -> users.size > 1 }
}
/** The display name of the current user */
val userName: Flow<Text> = interactor.selectedUser.mapLatest { userModel -> userModel.name }
/** Avatar for the current user */
val userAvatar: Flow<Drawable> =
interactor.selectedUser.mapLatest { userModel -> userModel.image }
/** Action to execute on click. Should launch the user switcher */
val onClick: (Expandable) -> Unit = { interactor.showUserSwitcher(context, it) }
}

View File

@@ -16,10 +16,8 @@
package com.android.systemui.qs.footer.domain.interactor
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.UserHandle
import android.provider.Settings
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
@@ -30,17 +28,13 @@ import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.globalactions.GlobalActionsDialogLite
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.QSSecurityFooterUtils
import com.android.systemui.qs.footer.FooterActionsTestUtils
import com.android.systemui.qs.user.UserSwitchDialogController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.truth.correspondence.FakeUiEvent
import com.android.systemui.truth.correspondence.LogMaker
import com.android.systemui.user.UserSwitcherActivity
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
@@ -156,54 +150,4 @@ class FooterActionsInteractorTest : SysuiTestCase() {
// We only unlock the device.
verify(activityStarter).postQSRunnableDismissingKeyguard(any())
}
@Test
fun showUserSwitcher_fullScreenDisabled() {
val featureFlags = FakeFeatureFlags().apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
val userSwitchDialogController = mock<UserSwitchDialogController>()
val underTest =
utils.footerActionsInteractor(
featureFlags = featureFlags,
userSwitchDialogController = userSwitchDialogController,
)
val expandable = mock<Expandable>()
underTest.showUserSwitcher(context, expandable)
// Dialog is shown.
verify(userSwitchDialogController).showDialog(context, expandable)
}
@Test
fun showUserSwitcher_fullScreenEnabled() {
val featureFlags = FakeFeatureFlags().apply { set(Flags.FULL_SCREEN_USER_SWITCHER, true) }
val activityStarter = mock<ActivityStarter>()
val underTest =
utils.footerActionsInteractor(
featureFlags = featureFlags,
activityStarter = activityStarter,
)
// The clicked expandable.
val expandable = mock<Expandable>()
underTest.showUserSwitcher(context, expandable)
// Dialog is shown.
val intentCaptor = argumentCaptor<Intent>()
verify(activityStarter)
.startActivity(
intentCaptor.capture(),
/* dismissShade= */ eq(true),
/* ActivityLaunchAnimator.Controller= */ nullable(),
/* showOverLockscreenWhenLocked= */ eq(true),
eq(UserHandle.SYSTEM),
)
assertThat(intentCaptor.value.component)
.isEqualTo(
ComponentName(
context,
UserSwitcherActivity::class.java,
)
)
}
}

View File

@@ -56,14 +56,12 @@ import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.CommandQueue;
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;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
@@ -112,16 +110,12 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
private StatusBarContentInsetsProvider mStatusBarContentInsetsProvider;
@Mock
private UserManager mUserManager;
@Mock
private StatusBarUserChipViewModel mStatusBarUserChipViewModel;
@Captor
private ArgumentCaptor<ConfigurationListener> mConfigurationListenerCaptor;
@Captor
private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardCallbackCaptor;
@Mock
private StatusBarUserSwitcherFeatureController mStatusBarUserSwitcherFeatureController;
@Mock
private StatusBarUserSwitcherController mStatusBarUserSwitcherController;
@Mock
private StatusBarUserInfoTracker mStatusBarUserInfoTracker;
@Mock private SecureSettings mSecureSettings;
@Mock private CommandQueue mCommandQueue;
@Mock private KeyguardLogger mLogger;
@@ -169,9 +163,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
mStatusBarStateController,
mStatusBarContentInsetsProvider,
mUserManager,
mStatusBarUserSwitcherFeatureController,
mStatusBarUserSwitcherController,
mStatusBarUserInfoTracker,
mStatusBarUserChipViewModel,
mSecureSettings,
mCommandQueue,
mFakeExecutor,
@@ -479,8 +471,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
@Test
public void testNewUserSwitcherDisablesAvatar_newUiOn() {
// GIVEN the status bar user switcher chip is enabled
when(mStatusBarUserSwitcherFeatureController.isStatusBarUserSwitcherFeatureEnabled())
.thenReturn(true);
when(mStatusBarUserChipViewModel.getChipEnabled()).thenReturn(true);
// WHEN the controller is created
mController = createController();
@@ -492,8 +483,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
@Test
public void testNewUserSwitcherDisablesAvatar_newUiOff() {
// GIVEN the status bar user switcher chip is disabled
when(mStatusBarUserSwitcherFeatureController.isStatusBarUserSwitcherFeatureEnabled())
.thenReturn(false);
when(mStatusBarUserChipViewModel.getChipEnabled()).thenReturn(false);
// WHEN the controller is created
mController = createController();

View File

@@ -27,11 +27,11 @@ import androidx.test.platform.app.InstrumentationRegistry
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.shade.NotificationPanelViewController
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
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel
import com.android.systemui.util.mockito.any
import com.android.systemui.util.view.ViewUtil
import com.google.common.truth.Truth.assertThat
@@ -64,7 +64,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
@Mock
private lateinit var configurationController: ConfigurationController
@Mock
private lateinit var userSwitcherController: StatusBarUserSwitcherController
private lateinit var userChipViewModel: StatusBarUserChipViewModel
@Mock
private lateinit var viewUtil: ViewUtil
@@ -79,14 +79,13 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
`when`(notificationPanelViewController.view).thenReturn(panelView)
`when`(sysuiUnfoldComponent.getStatusBarMoveFromCenterAnimationController())
.thenReturn(moveFromCenterAnimation)
// create the view on main thread as it requires main looper
// create the view and controller on main thread as it requires main looper
InstrumentationRegistry.getInstrumentation().runOnMainSync {
val parent = FrameLayout(mContext) // add parent to keep layout params
view = LayoutInflater.from(mContext)
.inflate(R.layout.status_bar, parent, false) as PhoneStatusBarView
controller = createAndInitController(view)
}
controller = createAndInitController(view)
}
@Test
@@ -106,7 +105,10 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
val view = createViewMock()
val argumentCaptor = ArgumentCaptor.forClass(OnPreDrawListener::class.java)
unfoldConfig.isEnabled = true
controller = createAndInitController(view)
// create the controller on main thread as it requires main looper
InstrumentationRegistry.getInstrumentation().runOnMainSync {
controller = createAndInitController(view)
}
verify(view.viewTreeObserver).addOnPreDrawListener(argumentCaptor.capture())
argumentCaptor.value.onPreDraw()
@@ -126,7 +128,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
return PhoneStatusBarViewController.Factory(
Optional.of(sysuiUnfoldComponent),
Optional.of(progressProvider),
userSwitcherController,
userChipViewModel,
viewUtil,
configurationController
).create(view, touchEventHandler).also {

View File

@@ -1,102 +0,0 @@
/*
* 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.Intent
import android.os.UserHandle
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.user.UserSwitchDialogController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
@SmallTest
class StatusBarUserSwitcherControllerOldImplTest : SysuiTestCase() {
@Mock
private lateinit var tracker: StatusBarUserInfoTracker
@Mock
private lateinit var featureController: StatusBarUserSwitcherFeatureController
@Mock
private lateinit var userSwitcherDialogController: UserSwitchDialogController
@Mock
private lateinit var featureFlags: FeatureFlags
@Mock
private lateinit var activityStarter: ActivityStarter
@Mock
private lateinit var falsingManager: FalsingManager
private lateinit var statusBarUserSwitcherContainer: StatusBarUserSwitcherContainer
private lateinit var controller: StatusBarUserSwitcherControllerImpl
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
statusBarUserSwitcherContainer = StatusBarUserSwitcherContainer(mContext, null)
statusBarUserSwitcherContainer
controller = StatusBarUserSwitcherControllerImpl(
statusBarUserSwitcherContainer,
tracker,
featureController,
userSwitcherDialogController,
featureFlags,
activityStarter,
falsingManager
)
controller.init()
controller.onViewAttached()
}
@Test
fun testFalsingManager() {
statusBarUserSwitcherContainer.callOnClick()
verify(falsingManager).isFalseTap(FalsingManager.LOW_PENALTY)
}
@Test
fun testStartActivity() {
`when`(featureFlags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)).thenReturn(false)
statusBarUserSwitcherContainer.callOnClick()
verify(userSwitcherDialogController).showDialog(any(), any())
`when`(featureFlags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)).thenReturn(true)
statusBarUserSwitcherContainer.callOnClick()
verify(activityStarter).startActivity(any(Intent::class.java),
eq(true) /* dismissShade */,
eq(null) /* animationController */,
eq(true) /* showOverLockscreenWhenLocked */,
eq(UserHandle.SYSTEM))
}
}

View File

@@ -19,6 +19,7 @@ package com.android.systemui.user.domain.interactor
import android.app.ActivityManager
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.Intent
import android.content.pm.UserInfo
import android.graphics.Bitmap
@@ -33,7 +34,10 @@ import com.android.systemui.GuestResetOrExitSessionReceiver
import com.android.systemui.GuestResumeSessionReceiver
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Text
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.ActivityStarter
@@ -41,6 +45,7 @@ import com.android.systemui.qs.user.UserSwitchDialogController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
import com.android.systemui.user.UserSwitcherActivity
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.data.source.UserRecord
@@ -48,9 +53,11 @@ import com.android.systemui.user.domain.model.ShowDialogRequestModel
import com.android.systemui.user.shared.model.UserActionModel
import com.android.systemui.user.shared.model.UserModel
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.kotlinArgumentCaptor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
@@ -90,6 +97,7 @@ class UserInteractorTest : SysuiTestCase() {
private lateinit var userRepository: FakeUserRepository
private lateinit var keyguardRepository: FakeKeyguardRepository
private lateinit var telephonyRepository: FakeTelephonyRepository
private lateinit var featureFlags: FakeFeatureFlags
@Before
fun setUp() {
@@ -104,6 +112,7 @@ class UserInteractorTest : SysuiTestCase() {
SUPERVISED_USER_CREATION_APP_PACKAGE,
)
featureFlags = FakeFeatureFlags()
userRepository = FakeUserRepository()
keyguardRepository = FakeKeyguardRepository()
telephonyRepository = FakeTelephonyRepository()
@@ -147,7 +156,8 @@ class UserInteractorTest : SysuiTestCase() {
uiEventLogger = uiEventLogger,
resumeSessionReceiver = resumeSessionReceiver,
resetOrExitSessionReceiver = resetOrExitSessionReceiver,
)
),
featureFlags = featureFlags,
)
}
@@ -715,6 +725,52 @@ class UserInteractorTest : SysuiTestCase() {
job.cancel()
}
@Test
fun `show user switcher - full screen disabled - shows dialog switcher`() =
runBlocking(IMMEDIATE) {
featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, false)
var dialogRequest: ShowDialogRequestModel? = null
val expandable = mock<Expandable>()
underTest.showUserSwitcher(context, expandable)
val job = underTest.dialogShowRequests.onEach { dialogRequest = it }.launchIn(this)
// Dialog is shown.
assertThat(dialogRequest).isEqualTo(ShowDialogRequestModel.ShowUserSwitcherDialog)
underTest.onDialogShown()
assertThat(dialogRequest).isNull()
job.cancel()
}
@Test
fun `show user switcher - full screen enabled - launches activity`() {
featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
val expandable = mock<Expandable>()
underTest.showUserSwitcher(context, expandable)
// Dialog is shown.
val intentCaptor = argumentCaptor<Intent>()
verify(activityStarter)
.startActivity(
intentCaptor.capture(),
/* dismissShade= */ eq(true),
/* ActivityLaunchAnimator.Controller= */ nullable(),
/* showOverLockscreenWhenLocked= */ eq(true),
eq(UserHandle.SYSTEM),
)
assertThat(intentCaptor.value.component)
.isEqualTo(
ComponentName(
context,
UserSwitcherActivity::class.java,
)
)
}
private fun assertUsers(
models: List<UserModel>?,
count: Int,

View File

@@ -0,0 +1,306 @@
/*
* 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.user.ui.viewmodel
import android.app.ActivityManager
import android.app.admin.DevicePolicyManager
import android.content.pm.UserInfo
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.os.UserManager
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.systemui.GuestResetOrExitSessionReceiver
import com.android.systemui.GuestResumeSessionReceiver
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Text
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.domain.interactor.GuestUserInteractor
import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
import com.android.systemui.user.domain.interactor.UserInteractor
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.yield
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.doAnswer
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(JUnit4::class)
class StatusBarUserChipViewModelTest : SysuiTestCase() {
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var activityManager: ActivityManager
@Mock private lateinit var manager: UserManager
@Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
@Mock private lateinit var uiEventLogger: UiEventLogger
@Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver
@Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
private lateinit var underTest: StatusBarUserChipViewModel
private val userRepository = FakeUserRepository()
private val keyguardRepository = FakeKeyguardRepository()
private val featureFlags = FakeFeatureFlags()
private lateinit var guestUserInteractor: GuestUserInteractor
private lateinit var refreshUsersScheduler: RefreshUsersScheduler
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
doAnswer { invocation ->
val userId = invocation.arguments[0] as Int
when (userId) {
USER_ID_0 -> return@doAnswer USER_IMAGE_0
USER_ID_1 -> return@doAnswer USER_IMAGE_1
USER_ID_2 -> return@doAnswer USER_IMAGE_2
else -> return@doAnswer mock<Bitmap>()
}
}
.`when`(manager)
.getUserIcon(anyInt())
userRepository.isStatusBarUserChipEnabled = true
refreshUsersScheduler =
RefreshUsersScheduler(
applicationScope = testScope.backgroundScope,
mainDispatcher = testDispatcher,
repository = userRepository,
)
guestUserInteractor =
GuestUserInteractor(
applicationContext = context,
applicationScope = testScope.backgroundScope,
mainDispatcher = testDispatcher,
backgroundDispatcher = testDispatcher,
manager = manager,
repository = userRepository,
deviceProvisionedController = deviceProvisionedController,
devicePolicyManager = devicePolicyManager,
refreshUsersScheduler = refreshUsersScheduler,
uiEventLogger = uiEventLogger,
resumeSessionReceiver = resumeSessionReceiver,
resetOrExitSessionReceiver = resetOrExitSessionReceiver,
)
underTest = viewModel()
}
@Test
fun `config is false - chip is disabled`() {
// the enabled bit is set at SystemUI startup, so recreate the view model here
userRepository.isStatusBarUserChipEnabled = false
underTest = viewModel()
assertThat(underTest.chipEnabled).isFalse()
}
@Test
fun `config is true - chip is enabled`() {
// the enabled bit is set at SystemUI startup, so recreate the view model here
userRepository.isStatusBarUserChipEnabled = true
underTest = viewModel()
assertThat(underTest.chipEnabled).isTrue()
}
@Test
fun `should show chip criteria - single user`() =
testScope.runTest {
userRepository.setUserInfos(listOf(USER_0))
userRepository.setSelectedUserInfo(USER_0)
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
val values = mutableListOf<Boolean>()
val job = launch { underTest.isChipVisible.toList(values) }
advanceUntilIdle()
assertThat(values).containsExactly(false)
job.cancel()
}
@Test
fun `should show chip criteria - multiple users`() =
testScope.runTest {
setMultipleUsers()
var latest: Boolean? = null
val job = underTest.isChipVisible.onEach { latest = it }.launchIn(this)
yield()
assertThat(latest).isTrue()
job.cancel()
}
@Test
fun `user chip name - shows selected user info`() =
testScope.runTest {
setMultipleUsers()
var latest: Text? = null
val job = underTest.userName.onEach { latest = it }.launchIn(this)
userRepository.setSelectedUserInfo(USER_0)
assertThat(latest).isEqualTo(USER_NAME_0)
userRepository.setSelectedUserInfo(USER_1)
assertThat(latest).isEqualTo(USER_NAME_1)
userRepository.setSelectedUserInfo(USER_2)
assertThat(latest).isEqualTo(USER_NAME_2)
job.cancel()
}
@Test
fun `user chip avatar - shows selected user info`() =
testScope.runTest {
setMultipleUsers()
// A little hacky. System server passes us bitmaps and we wrap them in the interactor.
// Unwrap them to make sure we're always tracking the current user's bitmap
var latest: Bitmap? = null
val job =
underTest.userAvatar
.onEach {
if (it !is BitmapDrawable) {
latest = null
}
latest = (it as BitmapDrawable).bitmap
}
.launchIn(this)
userRepository.setSelectedUserInfo(USER_0)
assertThat(latest).isEqualTo(USER_IMAGE_0)
userRepository.setSelectedUserInfo(USER_1)
assertThat(latest).isEqualTo(USER_IMAGE_1)
userRepository.setSelectedUserInfo(USER_2)
assertThat(latest).isEqualTo(USER_IMAGE_2)
job.cancel()
}
private fun viewModel(): StatusBarUserChipViewModel {
return StatusBarUserChipViewModel(
context = context,
interactor =
UserInteractor(
applicationContext = context,
repository = userRepository,
activityStarter = activityStarter,
keyguardInteractor =
KeyguardInteractor(
repository = keyguardRepository,
),
featureFlags = featureFlags,
manager = manager,
applicationScope = testScope.backgroundScope,
telephonyInteractor =
TelephonyInteractor(
repository = FakeTelephonyRepository(),
),
broadcastDispatcher = fakeBroadcastDispatcher,
backgroundDispatcher = testDispatcher,
activityManager = activityManager,
refreshUsersScheduler = refreshUsersScheduler,
guestUserInteractor = guestUserInteractor,
)
)
}
private suspend fun setMultipleUsers() {
userRepository.setUserInfos(listOf(USER_0, USER_1, USER_2))
userRepository.setSelectedUserInfo(USER_0)
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
}
companion object {
private const val USER_ID_0 = 0
private val USER_IMAGE_0 = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
private val USER_NAME_0 = Text.Loaded("zero")
private const val USER_ID_1 = 1
private val USER_IMAGE_1 = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
private val USER_NAME_1 = Text.Loaded("one")
private const val USER_ID_2 = 2
private val USER_IMAGE_2 = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
private val USER_NAME_2 = Text.Loaded("two")
private val USER_0 =
UserInfo(
USER_ID_0,
USER_NAME_0.text!!,
/* iconPath */ "",
/* flags */ 0,
/* userType */ UserManager.USER_TYPE_FULL_SYSTEM
)
private val USER_1 =
UserInfo(
USER_ID_1,
USER_NAME_1.text!!,
/* iconPath */ "",
/* flags */ 0,
/* userType */ UserManager.USER_TYPE_FULL_SYSTEM
)
private val USER_2 =
UserInfo(
USER_ID_2,
USER_NAME_2.text!!,
/* iconPath */ "",
/* flags */ 0,
/* userType */ UserManager.USER_TYPE_FULL_SYSTEM
)
}
}

View File

@@ -27,6 +27,7 @@ import com.android.systemui.GuestResetOrExitSessionReceiver
import com.android.systemui.GuestResumeSessionReceiver
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Text
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.ActivityStarter
@@ -147,6 +148,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
KeyguardInteractor(
repository = keyguardRepository,
),
featureFlags = FakeFeatureFlags(),
manager = manager,
applicationScope = injectedScope,
telephonyInteractor =

View File

@@ -28,8 +28,6 @@ import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.globalactions.GlobalActionsDialogLite
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
@@ -43,7 +41,6 @@ import com.android.systemui.qs.footer.data.repository.UserSwitcherRepositoryImpl
import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractor
import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractorImpl
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.qs.user.UserSwitchDialogController
import com.android.systemui.security.data.repository.SecurityRepository
import com.android.systemui.security.data.repository.SecurityRepositoryImpl
import com.android.systemui.settings.FakeUserTracker
@@ -54,6 +51,7 @@ import com.android.systemui.statusbar.policy.FakeUserInfoController
import com.android.systemui.statusbar.policy.SecurityController
import com.android.systemui.statusbar.policy.UserInfoController
import com.android.systemui.statusbar.policy.UserSwitcherController
import com.android.systemui.user.domain.interactor.UserInteractor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.settings.GlobalSettings
@@ -97,13 +95,12 @@ class FooterActionsTestUtils(
/** Create a [FooterActionsInteractor] to be used in tests. */
fun footerActionsInteractor(
activityStarter: ActivityStarter = mock(),
featureFlags: FeatureFlags = FakeFeatureFlags(),
metricsLogger: MetricsLogger = FakeMetricsLogger(),
uiEventLogger: UiEventLogger = UiEventLoggerFake(),
deviceProvisionedController: DeviceProvisionedController = mock(),
qsSecurityFooterUtils: QSSecurityFooterUtils = mock(),
fgsManagerController: FgsManagerController = mock(),
userSwitchDialogController: UserSwitchDialogController = mock(),
userInteractor: UserInteractor = mock(),
securityRepository: SecurityRepository = securityRepository(),
foregroundServicesRepository: ForegroundServicesRepository = foregroundServicesRepository(),
userSwitcherRepository: UserSwitcherRepository = userSwitcherRepository(),
@@ -112,13 +109,12 @@ class FooterActionsTestUtils(
): FooterActionsInteractor {
return FooterActionsInteractorImpl(
activityStarter,
featureFlags,
metricsLogger,
uiEventLogger,
deviceProvisionedController,
qsSecurityFooterUtils,
fgsManagerController,
userSwitchDialogController,
userInteractor,
securityRepository,
foregroundServicesRepository,
userSwitcherRepository,

View File

@@ -49,6 +49,8 @@ class FakeUserRepository : UserRepository {
override val isGuestUserCreationScheduled = AtomicBoolean()
override var isStatusBarUserChipEnabled: Boolean = false
override var secondaryUserId: Int = UserHandle.USER_NULL
override var isRefreshUsersPaused: Boolean = false