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"> android:visibleToInstantApps="true">
</activity> </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" <receiver android:name=".controls.management.ControlsRequestReceiver"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>

View File

@@ -6,7 +6,7 @@ Multiple users and the ability to switch between them is controlled by Settings
### Quick 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 ### Bouncer
@@ -29,7 +29,7 @@ All visual implementations should derive their logic and use the adapter specifi
## Visual Components ## Visual Components
### [UserSwitcherActivity][5] ### [UserSwitcherFullscreenDialog][5]
A fullscreen user switching activity, supporting add guest/user actions if configured. 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 [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 [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 [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 [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> <item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item>
</style> </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:statusBarColor">@color/user_switcher_fullscreen_bg</item>
<item name="android:windowBackground">@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> <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>
<style name="Theme.CreateUser" parent="@android:style/Theme.DeviceDefault.NoActionBar"> <style name="Theme.CreateUser" parent="@android:style/Theme.DeviceDefault.NoActionBar">

View File

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

View File

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

View File

@@ -16,7 +16,6 @@
package com.android.systemui.user; package com.android.systemui.user;
import android.app.Activity;
import android.os.UserHandle; import android.os.UserHandle;
import com.android.settingslib.users.EditUserInfoController; 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.domain.interactor.HeadlessSystemUserModeModule;
import com.android.systemui.user.ui.dialog.UserDialogModule; import com.android.systemui.user.ui.dialog.UserDialogModule;
import dagger.Binds;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
/** /**
* Dagger module for User related classes. * Dagger module for User related classes.
@@ -49,12 +45,6 @@ public abstract class UserModule {
return new EditUserInfoController(FILE_PROVIDER_AUTHORITY); 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. * 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.plugins.ActivityStarter
import com.android.systemui.qs.user.UserSwitchDialogController import com.android.systemui.qs.user.UserSwitchDialogController
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor 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.model.UserSwitcherSettingsModel
import com.android.systemui.user.data.repository.UserRepository import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.user.data.source.UserRecord import com.android.systemui.user.data.source.UserRecord
@@ -502,24 +501,12 @@ constructor(
} }
} }
fun showUserSwitcher(context: Context, expandable: Expandable) { fun showUserSwitcher(expandable: Expandable) {
if (!featureFlags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)) { if (featureFlags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)) {
showDialog(ShowDialogRequestModel.ShowUserSwitcherFullscreenDialog(expandable))
} else {
showDialog(ShowDialogRequestModel.ShowUserSwitcherDialog(expandable)) 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) { private fun showDialog(request: ShowDialogRequestModel) {

View File

@@ -50,4 +50,8 @@ sealed class ShowDialogRequestModel(
data class ShowUserSwitcherDialog( data class ShowUserSwitcherDialog(
override val expandable: Expandable?, override val expandable: Expandable?,
) : ShowDialogRequestModel() ) : 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.constraintlayout.helper.widget.Flow as FlowWidget
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.Gefingerpoken import com.android.systemui.Gefingerpoken
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.classifier.FalsingCollector import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.user.UserSwitcherPopupMenu import com.android.systemui.user.UserSwitcherPopupMenu
import com.android.systemui.user.UserSwitcherRootView import com.android.systemui.user.UserSwitcherRootView
import com.android.systemui.user.shared.model.UserActionModel import com.android.systemui.user.shared.model.UserActionModel
import com.android.systemui.user.ui.viewmodel.UserActionViewModel import com.android.systemui.user.ui.viewmodel.UserActionViewModel
import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
import com.android.systemui.util.children import com.android.systemui.util.children
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -56,7 +55,6 @@ object UserSwitcherViewBinder {
fun bind( fun bind(
view: ViewGroup, view: ViewGroup,
viewModel: UserSwitcherViewModel, viewModel: UserSwitcherViewModel,
lifecycleOwner: LifecycleOwner,
layoutInflater: LayoutInflater, layoutInflater: LayoutInflater,
falsingCollector: FalsingCollector, falsingCollector: FalsingCollector,
onFinish: () -> Unit, onFinish: () -> Unit,
@@ -79,12 +77,15 @@ object UserSwitcherViewBinder {
addButton.setOnClickListener { viewModel.onOpenMenuButtonClicked() } addButton.setOnClickListener { viewModel.onOpenMenuButtonClicked() }
cancelButton.setOnClickListener { viewModel.onCancelButtonClicked() } cancelButton.setOnClickListener { viewModel.onCancelButtonClicked() }
lifecycleOwner.lifecycleScope.launch { view.repeatWhenAttached {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.CREATED) { lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
launch { launch {
viewModel.isFinishRequested viewModel.isFinishRequested
.filter { it } .filter { it }
.collect { .collect {
//finish requested, we want to dismiss popupmenu at the same time
popupMenu?.dismiss()
onFinish() onFinish()
viewModel.onFinished() viewModel.onFinished()
} }
@@ -92,8 +93,8 @@ object UserSwitcherViewBinder {
} }
} }
lifecycleOwner.lifecycleScope.launch { lifecycleScope.launch {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { repeatOnLifecycle(Lifecycle.State.STARTED) {
launch { viewModel.isOpenMenuButtonVisible.collect { addButton.isVisible = it } } launch { viewModel.isOpenMenuButtonVisible.collect { addButton.isVisible = it } }
launch { launch {
@@ -167,6 +168,7 @@ object UserSwitcherViewBinder {
} }
} }
} }
}
private fun createAndShowPopupMenu( private fun createAndShowPopupMenu(
context: Context, context: Context,

View File

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

View File

@@ -55,5 +55,5 @@ constructor(
interactor.selectedUser.mapLatest { userModel -> userModel.image } interactor.selectedUser.mapLatest { userModel -> userModel.image }
/** Action to execute on click. Should launch the user switcher */ /** 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 package com.android.systemui.user.ui.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.common.shared.model.Text import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.ui.drawable.CircularDrawable 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.GuestUserInteractor
import com.android.systemui.user.domain.interactor.UserInteractor import com.android.systemui.user.domain.interactor.UserInteractor
import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
@@ -36,12 +34,13 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
/** Models UI state for the user switcher feature. */ /** Models UI state for the user switcher feature. */
@SysUISingleton
class UserSwitcherViewModel class UserSwitcherViewModel
private constructor( @Inject
constructor(
private val userInteractor: UserInteractor, private val userInteractor: UserInteractor,
private val guestUserInteractor: GuestUserInteractor, private val guestUserInteractor: GuestUserInteractor,
private val powerInteractor: PowerInteractor, ) {
) : ViewModel() {
/** On-device users. */ /** On-device users. */
val users: Flow<List<UserViewModel>> = val users: Flow<List<UserViewModel>> =
@@ -112,33 +111,14 @@ private constructor(
} }
} }
private fun createFinishRequestedFlow(): Flow<Boolean> { private fun createFinishRequestedFlow(): Flow<Boolean> =
var mostRecentSelectedUserId: Int? = null combine(
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
},
// When the cancel button is clicked, we should finish. // When the cancel button is clicked, we should finish.
hasCancelButtonBeenClicked, hasCancelButtonBeenClicked,
// If an executed action told us to finish, we should finish, // If an executed action told us to finish, we should finish,
isFinishRequiredDueToExecutedAction, isFinishRequiredDueToExecutedAction,
) { selectedUserChanged, screenTurnedOff, cancelButtonClicked, executedActionFinish -> ) { cancelButtonClicked, executedActionFinish ->
selectedUserChanged || screenTurnedOff || cancelButtonClicked || executedActionFinish cancelButtonClicked || executedActionFinish
}
} }
private fun toViewModel( private fun toViewModel(
@@ -210,22 +190,4 @@ private constructor(
{ userInteractor.selectUser(model.id) } { 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.ActivityManager
import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.Intent import android.content.Intent
import android.content.pm.UserInfo import android.content.pm.UserInfo
import android.graphics.Bitmap 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.statusbar.policy.DeviceProvisionedController
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor 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.model.UserSwitcherSettingsModel
import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.data.source.UserRecord 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.UserActionModel
import com.android.systemui.user.shared.model.UserModel import com.android.systemui.user.shared.model.UserModel
import com.android.systemui.util.mockito.any 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.eq
import com.android.systemui.util.mockito.kotlinArgumentCaptor import com.android.systemui.util.mockito.kotlinArgumentCaptor
import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.whenever import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertNotNull import junit.framework.Assert.assertNotNull
@@ -800,7 +796,7 @@ class UserInteractorTest : SysuiTestCase() {
fun `show user switcher - full screen disabled - shows dialog switcher`() = fun `show user switcher - full screen disabled - shows dialog switcher`() =
testScope.runTest { testScope.runTest {
val expandable = mock<Expandable>() val expandable = mock<Expandable>()
underTest.showUserSwitcher(context, expandable) underTest.showUserSwitcher(expandable)
val dialogRequest = collectLastValue(underTest.dialogShowRequests) val dialogRequest = collectLastValue(underTest.dialogShowRequests)
@@ -813,29 +809,21 @@ class UserInteractorTest : SysuiTestCase() {
} }
@Test @Test
fun `show user switcher - full screen enabled - launches activity`() { fun `show user switcher - full screen enabled - launches full screen dialog`() =
testScope.runTest {
featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true) featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
val expandable = mock<Expandable>() val expandable = mock<Expandable>()
underTest.showUserSwitcher(context, expandable) underTest.showUserSwitcher(expandable)
val dialogRequest = collectLastValue(underTest.dialogShowRequests)
// Dialog is shown. // Dialog is shown.
val intentCaptor = argumentCaptor<Intent>() assertThat(dialogRequest())
verify(activityStarter) .isEqualTo(ShowDialogRequestModel.ShowUserSwitcherFullscreenDialog(expandable))
.startActivity(
intentCaptor.capture(), underTest.onDialogShown()
/* dismissShade= */ eq(true), assertThat(dialogRequest()).isNull()
/* ActivityLaunchAnimator.Controller= */ nullable(),
/* showOverLockscreenWhenLocked= */ eq(true),
eq(UserHandle.SYSTEM),
)
assertThat(intentCaptor.value.component)
.isEqualTo(
ComponentName(
context,
UserSwitcherActivity::class.java,
)
)
} }
@Test @Test

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.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.ActivityStarter 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.CommandQueue
import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
@@ -88,7 +86,6 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
private lateinit var userRepository: FakeUserRepository private lateinit var userRepository: FakeUserRepository
private lateinit var keyguardRepository: FakeKeyguardRepository private lateinit var keyguardRepository: FakeKeyguardRepository
private lateinit var powerRepository: FakePowerRepository
private lateinit var testDispatcher: TestDispatcher private lateinit var testDispatcher: TestDispatcher
private lateinit var testScope: TestScope private lateinit var testScope: TestScope
@@ -116,7 +113,6 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
} }
keyguardRepository = FakeKeyguardRepository() keyguardRepository = FakeKeyguardRepository()
powerRepository = FakePowerRepository()
val refreshUsersScheduler = val refreshUsersScheduler =
RefreshUsersScheduler( RefreshUsersScheduler(
applicationScope = testScope.backgroundScope, applicationScope = testScope.backgroundScope,
@@ -145,7 +141,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
set(Flags.FACE_AUTH_REFACTOR, true) set(Flags.FACE_AUTH_REFACTOR, true)
} }
underTest = underTest =
UserSwitcherViewModel.Factory( UserSwitcherViewModel(
userInteractor = userInteractor =
UserInteractor( UserInteractor(
applicationContext = context, applicationContext = context,
@@ -173,13 +169,8 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
refreshUsersScheduler = refreshUsersScheduler, refreshUsersScheduler = refreshUsersScheduler,
guestUserInteractor = guestUserInteractor, guestUserInteractor = guestUserInteractor,
), ),
powerInteractor =
PowerInteractor(
repository = powerRepository,
),
guestUserInteractor = guestUserInteractor, guestUserInteractor = guestUserInteractor,
) )
.create(UserSwitcherViewModel::class.java)
} }
@Test @Test
@@ -325,44 +316,10 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
job.cancel() 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 @Test
fun `isFinishRequested - finishes when cancel button is clicked`() = fun `isFinishRequested - finishes when cancel button is clicked`() =
testScope.runTest { testScope.runTest {
setUsers(count = 2) setUsers(count = 2)
powerRepository.setInteractive(true)
val isFinishRequested = mutableListOf<Boolean>() val isFinishRequested = mutableListOf<Boolean>()
val job = val job =
launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) } launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) }