diff options
author | 2023-03-22 18:11:18 -0400 | |
---|---|---|
committer | 2023-03-28 15:30:02 +0000 | |
commit | 7f687ebfe412946a10a73db97f8676a42dbaa511 (patch) | |
tree | 3e0012b5d29001f77d412ec156d9ebecb0e49f85 | |
parent | 8df7edf0e3c0eeea75f9f1a1a000991c073e5ef7 (diff) |
Change Fullscreen UserSwitcher into a dialog
This is done to retain the last-active-app-resumption when switching users.
Also mitigates some issues with the previous activity behavior in "lockTaskMode".
Bug: b/270495254
Fixes: b/274122173
Test: UserInteractorTest.kt
Test: manual - switch user from chip bar to other non-supervised user
Test: manual - switch user to locked-down supervised user. verify that user switcher still appears on locked-down child user and that the user can switch back to the main user.
Test: manual - switch user to guest user, and back
Test: manual - open fullscreen user switcher, turn off screen. verify that user switcher is dismissed.
Test: manual - verify user switcher fullscreen is the expected UI in portrait and landscape.
Change-Id: Icc65e464c4f08817fc3668b2e898dadfc7cea4aa
16 files changed, 205 insertions, 302 deletions
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index aadc14061a61..5491b4cbe666 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -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> diff --git a/packages/SystemUI/docs/user-switching.md b/packages/SystemUI/docs/user-switching.md index b9509eb41c3c..01cba426f782 100644 --- a/packages/SystemUI/docs/user-switching.md +++ b/packages/SystemUI/docs/user-switching.md @@ -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 diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 1da1a294f9ba..27b15c926bbb 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -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"> diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt index 8387c1dd60a5..b394a079fb00 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt @@ -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) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt index f170ac1d9d4e..3a9098ab49d3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt @@ -230,7 +230,7 @@ class FooterActionsViewModel( return } - footerActionsInteractor.showUserSwitcher(context, expandable) + footerActionsInteractor.showUserSwitcher(expandable) } private fun onSettingsButtonClicked(expandable: Expandable) { diff --git a/packages/SystemUI/src/com/android/systemui/user/UserModule.java b/packages/SystemUI/src/com/android/systemui/user/UserModule.java index f7c8bac1b478..b2bf9727b534 100644 --- a/packages/SystemUI/src/com/android/systemui/user/UserModule.java +++ b/packages/SystemUI/src/com/android/systemui/user/UserModule.java @@ -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. * diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt deleted file mode 100644 index 52b7fb63c1a2..000000000000 --- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt +++ /dev/null @@ -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, - ) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherFullscreenDialog.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherFullscreenDialog.kt new file mode 100644 index 000000000000..72786efc416d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherFullscreenDialog.kt @@ -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 + +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt index 02d447976746..667a3ca8f9d2 100644 --- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt @@ -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) { diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/model/ShowDialogRequestModel.kt b/packages/SystemUI/src/com/android/systemui/user/domain/model/ShowDialogRequestModel.kt index 14cc3e783fed..de73cdbb6026 100644 --- a/packages/SystemUI/src/com/android/systemui/user/domain/model/ShowDialogRequestModel.kt +++ b/packages/SystemUI/src/com/android/systemui/user/domain/model/ShowDialogRequestModel.kt @@ -50,4 +50,8 @@ sealed class ShowDialogRequestModel( data class ShowUserSwitcherDialog( override val expandable: Expandable?, ) : ShowDialogRequestModel() + + data class ShowUserSwitcherFullscreenDialog( + override val expandable: Expandable?, + ) : ShowDialogRequestModel() } diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt index e13710786fbb..7236e0fd134a 100644 --- a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt @@ -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 } } - - 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, - ) + 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 } - } else if (!isVisible && popupMenu?.isShowing == true) { - popupMenu?.dismiss() - popupMenu = null } } - } - launch { - viewModel.menu.collect { menuViewModels -> - popupMenuAdapter.setItems(menuViewModels) + launch { + viewModel.menu.collect { menuViewModels -> + popupMenuAdapter.setItems(menuViewModels) + } } - } - launch { - viewModel.maximumUserColumns.collect { maximumColumns -> - flowWidget.setMaxElementsWrap(maximumColumns) + 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, - ) + 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, + ) + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt index 79721b370c21..0930cb8a3d7a 100644 --- a/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/user/ui/dialog/UserSwitcherDialogCoordinator.kt @@ -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 diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt index 3300e8e5b2a5..78edad7c3af2 100644 --- a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModel.kt @@ -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) } } diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt index 37115ad53880..afd72e7ed1be 100644 --- a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt @@ -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 - } - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt index 3ed6cc88826c..e3b8649718c1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt @@ -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) - - 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, - ) - ) - } + 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(expandable) + + 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`() = diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt index e08ebf4a9050..5cd2df99325b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt @@ -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 @@ -326,46 +317,12 @@ class UserSwitcherViewModelTest : SysuiTestCase() { } @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() |