Merge "Change Fullscreen UserSwitcher into a dialog" into tm-qpr-dev

This commit is contained in:
Brad Hinegardner
2023-03-28 21:12:12 +00:00
committed by Android (Google) Code Review
16 changed files with 208 additions and 305 deletions

View File

@@ -893,18 +893,6 @@
android:visibleToInstantApps="true">
</activity>
<activity android:name=".user.UserSwitcherActivity"
android:label="@string/accessibility_multi_user_switch_switcher"
android:theme="@style/Theme.UserSwitcherActivity"
android:excludeFromRecents="true"
android:showWhenLocked="true"
android:showForAllUsers="true"
android:finishOnTaskLaunch="true"
android:lockTaskMode="always"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
android:visibleToInstantApps="true">
</activity>
<receiver android:name=".controls.management.ControlsRequestReceiver"
android:exported="true">
<intent-filter>

View File

@@ -6,7 +6,7 @@ Multiple users and the ability to switch between them is controlled by Settings
### Quick Settings
In the QS footer, an icon becomes available for users to tap on. The view and its onClick actions are handled by [MultiUserSwitchController][2]. Multiple visual implementations are currently in use; one for phones/foldables ([UserSwitchDialogController][6]) and one for tablets ([UserSwitcherActivity][5]).
In the QS footer, an icon becomes available for users to tap on. The view and its onClick actions are handled by [MultiUserSwitchController][2]. Multiple visual implementations are currently in use; one for phones/foldables ([UserSwitchDialogController][6]) and one for tablets ([UserSwitcherFullscreenDialog][5]).
### Bouncer
@@ -29,7 +29,7 @@ All visual implementations should derive their logic and use the adapter specifi
## Visual Components
### [UserSwitcherActivity][5]
### [UserSwitcherFullscreenDialog][5]
A fullscreen user switching activity, supporting add guest/user actions if configured.
@@ -41,5 +41,5 @@ Renders user switching as a dialog over the current surface, and supports add gu
[2]: /frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserController.java
[3]: /frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
[4]: /frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
[5]: /frameworks/base/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
[5]: /frameworks/base/packages/SystemUI/src/com/android/systemui/user/UserSwitcherFullscreenDialog.kt
[6]: /frameworks/base/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt

View File

@@ -836,12 +836,10 @@
<item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item>
</style>
<style name="Theme.UserSwitcherActivity" parent="@android:style/Theme.DeviceDefault.NoActionBar">
<style name="Theme.UserSwitcherFullscreenDialog" parent="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen">
<item name="android:statusBarColor">@color/user_switcher_fullscreen_bg</item>
<item name="android:windowBackground">@color/user_switcher_fullscreen_bg</item>
<item name="android:navigationBarColor">@color/user_switcher_fullscreen_bg</item>
<!-- Setting a placeholder will avoid using the SystemUI icon on the splash screen -->
<item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_blank</item>
</style>
<style name="Theme.CreateUser" parent="@android:style/Theme.DeviceDefault.NoActionBar">

View File

@@ -89,7 +89,7 @@ interface FooterActionsInteractor {
fun showSettings(expandable: Expandable)
/** Show the user switcher. */
fun showUserSwitcher(context: Context, expandable: Expandable)
fun showUserSwitcher(expandable: Expandable)
}
@SysUISingleton
@@ -177,7 +177,7 @@ constructor(
)
}
override fun showUserSwitcher(context: Context, expandable: Expandable) {
userInteractor.showUserSwitcher(context, expandable)
override fun showUserSwitcher(expandable: Expandable) {
userInteractor.showUserSwitcher(expandable)
}
}

View File

@@ -230,7 +230,7 @@ class FooterActionsViewModel(
return
}
footerActionsInteractor.showUserSwitcher(context, expandable)
footerActionsInteractor.showUserSwitcher(expandable)
}
private fun onSettingsButtonClicked(expandable: Expandable) {

View File

@@ -16,7 +16,6 @@
package com.android.systemui.user;
import android.app.Activity;
import android.os.UserHandle;
import com.android.settingslib.users.EditUserInfoController;
@@ -24,11 +23,8 @@ import com.android.systemui.user.data.repository.UserRepositoryModule;
import com.android.systemui.user.domain.interactor.HeadlessSystemUserModeModule;
import com.android.systemui.user.ui.dialog.UserDialogModule;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
/**
* Dagger module for User related classes.
@@ -49,12 +45,6 @@ public abstract class UserModule {
return new EditUserInfoController(FILE_PROVIDER_AUTHORITY);
}
/** Provides UserSwitcherActivity */
@Binds
@IntoMap
@ClassKey(UserSwitcherActivity.class)
public abstract Activity provideUserSwitcherActivity(UserSwitcherActivity activity);
/**
* Provides the {@link UserHandle} for the user associated with this System UI process.
*

View File

@@ -1,57 +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.user
import android.os.Bundle
import android.view.WindowInsets.Type
import android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
import androidx.activity.ComponentActivity
import androidx.lifecycle.ViewModelProvider
import com.android.systemui.R
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.user.ui.binder.UserSwitcherViewBinder
import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
import dagger.Lazy
import javax.inject.Inject
/** Support a fullscreen user switcher */
open class UserSwitcherActivity
@Inject
constructor(
private val falsingCollector: FalsingCollector,
private val viewModelFactory: Lazy<UserSwitcherViewModel.Factory>,
) : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.user_switcher_fullscreen)
window.decorView.windowInsetsController?.let { controller ->
controller.systemBarsBehavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
controller.hide(Type.systemBars())
}
val viewModel =
ViewModelProvider(this, viewModelFactory.get())[UserSwitcherViewModel::class.java]
UserSwitcherViewBinder.bind(
view = requireViewById(R.id.user_switcher_root),
viewModel = viewModel,
lifecycleOwner = this,
layoutInflater = layoutInflater,
falsingCollector = falsingCollector,
onFinish = this::finish,
)
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2023 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
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.WindowInsets
import android.view.WindowInsetsController
import com.android.systemui.R
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.user.ui.binder.UserSwitcherViewBinder
import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
class UserSwitchFullscreenDialog(
context: Context,
private val falsingCollector: FalsingCollector,
private val userSwitcherViewModel: UserSwitcherViewModel,
) : SystemUIDialog(context, R.style.Theme_UserSwitcherFullscreenDialog) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setShowForAllUsers(true)
setCanceledOnTouchOutside(true)
window?.decorView?.windowInsetsController?.let { controller ->
controller.systemBarsBehavior =
WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
controller.hide(WindowInsets.Type.systemBars())
}
val view =
LayoutInflater.from(this.context).inflate(R.layout.user_switcher_fullscreen, null)
setContentView(view)
UserSwitcherViewBinder.bind(
view = requireViewById(R.id.user_switcher_root),
viewModel = userSwitcherViewModel,
layoutInflater = layoutInflater,
falsingCollector = falsingCollector,
onFinish = this::dismiss,
)
}
override fun getWidth(): Int {
val displayMetrics = context.resources.displayMetrics.apply {
context.display.getRealMetrics(this)
}
return displayMetrics.widthPixels
}
override fun getHeight() = MATCH_PARENT
}

View File

@@ -48,7 +48,6 @@ 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
@@ -502,24 +501,12 @@ constructor(
}
}
fun showUserSwitcher(context: Context, expandable: Expandable) {
if (!featureFlags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)) {
fun showUserSwitcher(expandable: Expandable) {
if (featureFlags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)) {
showDialog(ShowDialogRequestModel.ShowUserSwitcherFullscreenDialog(expandable))
} else {
showDialog(ShowDialogRequestModel.ShowUserSwitcherDialog(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,
)
}
private fun showDialog(request: ShowDialogRequestModel) {

View File

@@ -50,4 +50,8 @@ sealed class ShowDialogRequestModel(
data class ShowUserSwitcherDialog(
override val expandable: Expandable?,
) : ShowDialogRequestModel()
data class ShowUserSwitcherFullscreenDialog(
override val expandable: Expandable?,
) : ShowDialogRequestModel()
}

View File

@@ -31,19 +31,18 @@ import android.widget.TextView
import androidx.constraintlayout.helper.widget.Flow as FlowWidget
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.Gefingerpoken
import com.android.systemui.R
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.user.UserSwitcherPopupMenu
import com.android.systemui.user.UserSwitcherRootView
import com.android.systemui.user.shared.model.UserActionModel
import com.android.systemui.user.ui.viewmodel.UserActionViewModel
import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
import com.android.systemui.util.children
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
@@ -56,7 +55,6 @@ object UserSwitcherViewBinder {
fun bind(
view: ViewGroup,
viewModel: UserSwitcherViewModel,
lifecycleOwner: LifecycleOwner,
layoutInflater: LayoutInflater,
falsingCollector: FalsingCollector,
onFinish: () -> Unit,
@@ -79,88 +77,92 @@ object UserSwitcherViewBinder {
addButton.setOnClickListener { viewModel.onOpenMenuButtonClicked() }
cancelButton.setOnClickListener { viewModel.onCancelButtonClicked() }
lifecycleOwner.lifecycleScope.launch {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) {
launch {
viewModel.isFinishRequested
.filter { it }
.collect {
onFinish()
viewModel.onFinished()
}
view.repeatWhenAttached {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
launch {
viewModel.isFinishRequested
.filter { it }
.collect {
//finish requested, we want to dismiss popupmenu at the same time
popupMenu?.dismiss()
onFinish()
viewModel.onFinished()
}
}
}
}
}
lifecycleOwner.lifecycleScope.launch {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
launch { viewModel.isOpenMenuButtonVisible.collect { addButton.isVisible = it } }
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch { viewModel.isOpenMenuButtonVisible.collect { addButton.isVisible = it } }
launch {
viewModel.isMenuVisible.collect { isVisible ->
if (isVisible && popupMenu?.isShowing != true) {
popupMenu?.dismiss()
// Use post to make sure we show the popup menu *after* the activity is
// ready to show one to avoid a WindowManager$BadTokenException.
view.post {
popupMenu =
createAndShowPopupMenu(
context = view.context,
anchorView = addButton,
adapter = popupMenuAdapter,
onDismissed = viewModel::onMenuClosed,
)
}
} else if (!isVisible && popupMenu?.isShowing == true) {
popupMenu?.dismiss()
popupMenu = null
}
}
}
launch {
viewModel.menu.collect { menuViewModels ->
popupMenuAdapter.setItems(menuViewModels)
}
}
launch {
viewModel.maximumUserColumns.collect { maximumColumns ->
flowWidget.setMaxElementsWrap(maximumColumns)
}
}
launch {
viewModel.users.collect { users ->
val viewPool =
gridContainerView.children
.filter { it.tag == USER_VIEW_TAG }
.toMutableList()
viewPool.forEach {
gridContainerView.removeView(it)
flowWidget.removeView(it)
}
users.forEach { userViewModel ->
val userView =
if (viewPool.isNotEmpty()) {
viewPool.removeAt(0)
} else {
val inflatedView =
layoutInflater.inflate(
R.layout.user_switcher_fullscreen_item,
view,
false,
launch {
viewModel.isMenuVisible.collect { isVisible ->
if (isVisible && popupMenu?.isShowing != true) {
popupMenu?.dismiss()
// Use post to make sure we show the popup menu *after* the activity is
// ready to show one to avoid a WindowManager$BadTokenException.
view.post {
popupMenu =
createAndShowPopupMenu(
context = view.context,
anchorView = addButton,
adapter = popupMenuAdapter,
onDismissed = viewModel::onMenuClosed,
)
inflatedView.tag = USER_VIEW_TAG
inflatedView
}
userView.id = View.generateViewId()
gridContainerView.addView(userView)
flowWidget.addView(userView)
UserViewBinder.bind(
view = userView,
viewModel = userViewModel,
)
} else if (!isVisible && popupMenu?.isShowing == true) {
popupMenu?.dismiss()
popupMenu = null
}
}
}
launch {
viewModel.menu.collect { menuViewModels ->
popupMenuAdapter.setItems(menuViewModels)
}
}
launch {
viewModel.maximumUserColumns.collect { maximumColumns ->
flowWidget.setMaxElementsWrap(maximumColumns)
}
}
launch {
viewModel.users.collect { users ->
val viewPool =
gridContainerView.children
.filter { it.tag == USER_VIEW_TAG }
.toMutableList()
viewPool.forEach {
gridContainerView.removeView(it)
flowWidget.removeView(it)
}
users.forEach { userViewModel ->
val userView =
if (viewPool.isNotEmpty()) {
viewPool.removeAt(0)
} else {
val inflatedView =
layoutInflater.inflate(
R.layout.user_switcher_fullscreen_item,
view,
false,
)
inflatedView.tag = USER_VIEW_TAG
inflatedView
}
userView.id = View.generateViewId()
gridContainerView.addView(userView)
flowWidget.addView(userView)
UserViewBinder.bind(
view = userView,
viewModel = userViewModel,
)
}
}
}
}

View File

@@ -26,13 +26,16 @@ import com.android.systemui.CoreStartable
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.classifier.FalsingCollector
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.UserSwitchFullscreenDialog
import com.android.systemui.user.domain.interactor.UserInteractor
import com.android.systemui.user.domain.model.ShowDialogRequestModel
import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
import dagger.Lazy
import javax.inject.Inject
import javax.inject.Provider
@@ -54,6 +57,8 @@ constructor(
private val userDetailAdapterProvider: Provider<UserDetailView.Adapter>,
private val eventLogger: Lazy<UiEventLogger>,
private val activityStarter: Lazy<ActivityStarter>,
private val falsingCollector: Lazy<FalsingCollector>,
private val userSwitcherViewModel: Lazy<UserSwitcherViewModel>,
) : CoreStartable {
private var currentDialog: Dialog? = null
@@ -124,6 +129,15 @@ constructor(
INTERACTION_JANK_EXIT_GUEST_MODE_TAG,
),
)
is ShowDialogRequestModel.ShowUserSwitcherFullscreenDialog ->
Pair(
UserSwitchFullscreenDialog(
context = context.get(),
falsingCollector = falsingCollector.get(),
userSwitcherViewModel = userSwitcherViewModel.get(),
),
null, /* dialogCuj */
)
}
currentDialog = dialog

View File

@@ -55,5 +55,5 @@ constructor(
interactor.selectedUser.mapLatest { userModel -> userModel.image }
/** Action to execute on click. Should launch the user switcher */
val onClick: (Expandable) -> Unit = { interactor.showUserSwitcher(context, it) }
val onClick: (Expandable) -> Unit = { interactor.showUserSwitcher(it) }
}

View File

@@ -17,12 +17,10 @@
package com.android.systemui.user.ui.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.android.systemui.R
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.ui.drawable.CircularDrawable
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.user.domain.interactor.GuestUserInteractor
import com.android.systemui.user.domain.interactor.UserInteractor
import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
@@ -36,12 +34,13 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
/** Models UI state for the user switcher feature. */
@SysUISingleton
class UserSwitcherViewModel
private constructor(
@Inject
constructor(
private val userInteractor: UserInteractor,
private val guestUserInteractor: GuestUserInteractor,
private val powerInteractor: PowerInteractor,
) : ViewModel() {
) {
/** On-device users. */
val users: Flow<List<UserViewModel>> =
@@ -112,34 +111,15 @@ private constructor(
}
}
private fun createFinishRequestedFlow(): Flow<Boolean> {
var mostRecentSelectedUserId: Int? = null
var mostRecentIsInteractive: Boolean? = null
return combine(
// When the user is switched, we should finish.
userInteractor.selectedUser
.map { it.id }
.map {
val selectedUserChanged =
mostRecentSelectedUserId != null && mostRecentSelectedUserId != it
mostRecentSelectedUserId = it
selectedUserChanged
},
// When the screen turns off, we should finish.
powerInteractor.isInteractive.map {
val screenTurnedOff = mostRecentIsInteractive == true && !it
mostRecentIsInteractive = it
screenTurnedOff
},
private fun createFinishRequestedFlow(): Flow<Boolean> =
combine(
// When the cancel button is clicked, we should finish.
hasCancelButtonBeenClicked,
// If an executed action told us to finish, we should finish,
isFinishRequiredDueToExecutedAction,
) { selectedUserChanged, screenTurnedOff, cancelButtonClicked, executedActionFinish ->
selectedUserChanged || screenTurnedOff || cancelButtonClicked || executedActionFinish
) { cancelButtonClicked, executedActionFinish ->
cancelButtonClicked || executedActionFinish
}
}
private fun toViewModel(
model: UserModel,
@@ -210,22 +190,4 @@ private constructor(
{ userInteractor.selectUser(model.id) }
}
}
class Factory
@Inject
constructor(
private val userInteractor: UserInteractor,
private val guestUserInteractor: GuestUserInteractor,
private val powerInteractor: PowerInteractor,
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
@Suppress("UNCHECKED_CAST")
return UserSwitcherViewModel(
userInteractor = userInteractor,
guestUserInteractor = guestUserInteractor,
powerInteractor = powerInteractor,
)
as T
}
}
}

View File

@@ -19,7 +19,6 @@ 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
@@ -49,7 +48,6 @@ import com.android.systemui.statusbar.CommandQueue
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
@@ -57,11 +55,9 @@ 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 junit.framework.Assert.assertNotNull
@@ -800,7 +796,7 @@ class UserInteractorTest : SysuiTestCase() {
fun `show user switcher - full screen disabled - shows dialog switcher`() =
testScope.runTest {
val expandable = mock<Expandable>()
underTest.showUserSwitcher(context, expandable)
underTest.showUserSwitcher(expandable)
val dialogRequest = collectLastValue(underTest.dialogShowRequests)
@@ -813,30 +809,22 @@ class UserInteractorTest : SysuiTestCase() {
}
@Test
fun `show user switcher - full screen enabled - launches activity`() {
featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
fun `show user switcher - full screen enabled - launches full screen dialog`() =
testScope.runTest {
featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
val expandable = mock<Expandable>()
underTest.showUserSwitcher(context, expandable)
val expandable = mock<Expandable>()
underTest.showUserSwitcher(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,
)
)
}
val dialogRequest = collectLastValue(underTest.dialogShowRequests)
// Dialog is shown.
assertThat(dialogRequest())
.isEqualTo(ShowDialogRequestModel.ShowUserSwitcherFullscreenDialog(expandable))
underTest.onDialogShown()
assertThat(dialogRequest()).isNull()
}
@Test
fun `users - secondary user - managed profile is not included`() =

View File

@@ -34,8 +34,6 @@ import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerReposito
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.power.data.repository.FakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
@@ -88,7 +86,6 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
private lateinit var userRepository: FakeUserRepository
private lateinit var keyguardRepository: FakeKeyguardRepository
private lateinit var powerRepository: FakePowerRepository
private lateinit var testDispatcher: TestDispatcher
private lateinit var testScope: TestScope
@@ -116,7 +113,6 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
}
keyguardRepository = FakeKeyguardRepository()
powerRepository = FakePowerRepository()
val refreshUsersScheduler =
RefreshUsersScheduler(
applicationScope = testScope.backgroundScope,
@@ -145,7 +141,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
set(Flags.FACE_AUTH_REFACTOR, true)
}
underTest =
UserSwitcherViewModel.Factory(
UserSwitcherViewModel(
userInteractor =
UserInteractor(
applicationContext = context,
@@ -173,13 +169,8 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
refreshUsersScheduler = refreshUsersScheduler,
guestUserInteractor = guestUserInteractor,
),
powerInteractor =
PowerInteractor(
repository = powerRepository,
),
guestUserInteractor = guestUserInteractor,
)
.create(UserSwitcherViewModel::class.java)
}
@Test
@@ -325,47 +316,13 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
job.cancel()
}
@Test
fun `isFinishRequested - finishes when user is switched`() =
testScope.runTest {
val userInfos = setUsers(count = 2)
val isFinishRequested = mutableListOf<Boolean>()
val job =
launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) }
assertThat(isFinishRequested.last()).isFalse()
userRepository.setSelectedUserInfo(userInfos[1])
assertThat(isFinishRequested.last()).isTrue()
job.cancel()
}
@Test
fun `isFinishRequested - finishes when the screen turns off`() =
testScope.runTest {
setUsers(count = 2)
powerRepository.setInteractive(true)
val isFinishRequested = mutableListOf<Boolean>()
val job =
launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) }
assertThat(isFinishRequested.last()).isFalse()
powerRepository.setInteractive(false)
assertThat(isFinishRequested.last()).isTrue()
job.cancel()
}
@Test
fun `isFinishRequested - finishes when cancel button is clicked`() =
testScope.runTest {
setUsers(count = 2)
powerRepository.setInteractive(true)
val isFinishRequested = mutableListOf<Boolean>()
val job =
launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) }
launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) }
assertThat(isFinishRequested.last()).isFalse()
underTest.onCancelButtonClicked()