diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt index 818248439d0ab..9f275afa37656 100644 --- a/packages/SystemUI/ktfmt_includes.txt +++ b/packages/SystemUI/ktfmt_includes.txt @@ -813,7 +813,7 @@ -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManagerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerTest.kt +-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt @@ -828,7 +828,7 @@ -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt +-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/WalletControllerImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index f73c98e4971b2..a0cfd41b999fd 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -22,8 +22,6 @@ import static android.view.WindowInsets.Type.systemBars; import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY; -import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA; -import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA; import static java.lang.Integer.max; @@ -87,8 +85,8 @@ import com.android.systemui.R; import com.android.systemui.animation.Interpolators; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.shared.system.SysUiStatsLog; +import com.android.systemui.statusbar.policy.BaseUserSwitcherAdapter; import com.android.systemui.statusbar.policy.UserSwitcherController; -import com.android.systemui.statusbar.policy.UserSwitcherController.BaseUserAdapter; import com.android.systemui.user.data.source.UserRecord; import com.android.systemui.util.settings.GlobalSettings; @@ -1137,7 +1135,7 @@ public class KeyguardSecurityContainer extends FrameLayout { KeyguardUserSwitcherAnchor anchor = mView.findViewById(R.id.user_switcher_anchor); - BaseUserAdapter adapter = new BaseUserAdapter(mUserSwitcherController) { + BaseUserSwitcherAdapter adapter = new BaseUserSwitcherAdapter(mUserSwitcherController) { @Override public View getView(int position, View convertView, ViewGroup parent) { UserRecord item = getItem(position); @@ -1172,8 +1170,7 @@ public class KeyguardSecurityContainer extends FrameLayout { } textView.setSelected(item == currentUser); view.setEnabled(item.isSwitchToEnabled); - view.setAlpha(view.isEnabled() ? USER_SWITCH_ENABLED_ALPHA : - USER_SWITCH_DISABLED_ALPHA); + UserSwitcherController.setSelectableAlpha(view); return view; } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java index 44feac13198fb..938332d75dd57 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java @@ -104,6 +104,10 @@ public class Flags { public static final UnreleasedFlag MODERN_USER_SWITCHER_ACTIVITY = new UnreleasedFlag(209, true); + /** Whether the new implementation of UserSwitcherController should be used. */ + public static final UnreleasedFlag REFACTORED_USER_SWITCHER_CONTROLLER = + new UnreleasedFlag(210, false); + /***************************************/ // 300 - power menu public static final ReleasedFlag POWER_MENU_LITE = diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java index 0ec4eef1e5516..97476b2d1cdee 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java @@ -16,9 +16,6 @@ package com.android.systemui.qs.tiles; -import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA; -import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA; - import android.content.Context; import android.content.Intent; import android.graphics.drawable.Drawable; @@ -42,6 +39,7 @@ import com.android.systemui.qs.PseudoGridView; import com.android.systemui.qs.QSUserSwitcherEvent; import com.android.systemui.qs.user.UserSwitchDialogController; import com.android.systemui.statusbar.phone.SystemUIDialog; +import com.android.systemui.statusbar.policy.BaseUserSwitcherAdapter; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.user.data.source.UserRecord; @@ -73,7 +71,8 @@ public class UserDetailView extends PseudoGridView { mAdapter.refresh(); } - public static class Adapter extends UserSwitcherController.BaseUserAdapter + /** Provides views for user detail items. */ + public static class Adapter extends BaseUserSwitcherAdapter implements OnClickListener { private final Context mContext; @@ -137,7 +136,7 @@ public class UserDetailView extends PseudoGridView { v.setActivated(item.isCurrent); v.setDisabledByAdmin(mController.isDisabledByAdmin(item)); v.setEnabled(item.isSwitchToEnabled); - v.setAlpha(v.isEnabled() ? USER_SWITCH_ENABLED_ALPHA : USER_SWITCH_DISABLED_ALPHA); + UserSwitcherController.setSelectableAlpha(v); if (item.isCurrent) { mCurrentUserView = v; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java deleted file mode 100644 index 4551807499abe..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar; - -import android.content.Context; -import android.content.DialogInterface; - -import com.android.systemui.R; -import com.android.systemui.statusbar.phone.SystemUIDialog; -import com.android.systemui.statusbar.policy.UserSwitcherController; - -public class UserUtil { - public static void deleteUserWithPrompt(Context context, int userId, - UserSwitcherController userSwitcherController) { - new RemoveUserDialog(context, userId, userSwitcherController).show(); - } - - private final static class RemoveUserDialog extends SystemUIDialog implements - DialogInterface.OnClickListener { - - private final int mUserId; - private final UserSwitcherController mUserSwitcherController; - - public RemoveUserDialog(Context context, int userId, - UserSwitcherController userSwitcherController) { - super(context); - setTitle(R.string.user_remove_user_title); - setMessage(context.getString(R.string.user_remove_user_message)); - setButton(DialogInterface.BUTTON_NEUTRAL, - context.getString(android.R.string.cancel), this); - setButton(DialogInterface.BUTTON_POSITIVE, - context.getString(R.string.user_remove_user_remove), this); - setCanceledOnTouchOutside(false); - mUserId = userId; - mUserSwitcherController = userSwitcherController; - } - - @Override - public void onClick(DialogInterface dialog, int which) { - if (which == BUTTON_NEUTRAL) { - cancel(); - } else { - dismiss(); - mUserSwitcherController.removeUserId(mUserId); - } - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java index 4d6168989691a..00c3e8fac0b4e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java @@ -33,6 +33,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.qs.FooterActionsView; import com.android.systemui.qs.dagger.QSScope; import com.android.systemui.qs.user.UserSwitchDialogController; +import com.android.systemui.statusbar.policy.BaseUserSwitcherAdapter; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.user.UserSwitcherActivity; import com.android.systemui.util.ViewController; @@ -49,7 +50,7 @@ public class MultiUserSwitchController extends ViewController { private final ActivityStarter mActivityStarter; private final FeatureFlags mFeatureFlags; - private UserSwitcherController.BaseUserAdapter mUserListener; + private BaseUserSwitcherAdapter mUserListener; private final View.OnClickListener mOnClickListener = new View.OnClickListener() { @Override @@ -135,7 +136,7 @@ public class MultiUserSwitchController extends ViewController { final UserSwitcherController controller = mUserSwitcherController; if (controller != null) { - mUserListener = new UserSwitcherController.BaseUserAdapter(controller) { + mUserListener = new BaseUserSwitcherAdapter(controller) { @Override public void notifyDataSetChanged() { mView.refreshContentDescription(getCurrentUser()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt new file mode 100644 index 0000000000000..5b2d69564585b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.android.systemui.statusbar.policy + +import android.content.Context +import android.graphics.ColorFilter +import android.graphics.ColorMatrix +import android.graphics.ColorMatrixColorFilter +import android.graphics.drawable.Drawable +import android.os.UserHandle +import android.widget.BaseAdapter +import com.android.systemui.qs.user.UserSwitchDialogController.DialogShower +import com.android.systemui.user.data.source.UserRecord +import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper.getUserRecordName +import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper.getUserSwitcherActionIconResourceId +import java.lang.ref.WeakReference + +/** Provides views for user switcher experiences. */ +abstract class BaseUserSwitcherAdapter +protected constructor( + protected val controller: UserSwitcherController, +) : BaseAdapter() { + + protected open val users: ArrayList + get() = controller.users + + init { + controller.addAdapter(WeakReference(this)) + } + + override fun getCount(): Int { + return if (controller.isKeyguardShowing) { + users.count { !it.isRestricted } + } else { + users.size + } + } + + override fun getItem(position: Int): UserRecord { + return users[position] + } + + override fun getItemId(position: Int): Long { + return position.toLong() + } + + /** + * Notifies that a user item in the UI has been clicked. + * + * If the user switcher is hosted in a dialog, passing a non-null [dialogShower] will allow + * animation to and from the parent dialog. + */ + @JvmOverloads + fun onUserListItemClicked( + record: UserRecord, + dialogShower: DialogShower? = null, + ) { + controller.onUserListItemClicked(record, dialogShower) + } + + open fun getName(context: Context, item: UserRecord): String { + return getName(context, item, false) + } + + /** Returns the name for the given {@link UserRecord}. */ + open fun getName(context: Context, item: UserRecord, isTablet: Boolean): String { + return getUserRecordName( + context = context, + record = item, + isGuestUserAutoCreated = controller.isGuestUserAutoCreated, + isGuestUserResetting = controller.isGuestUserResetting, + isTablet = isTablet, + ) + } + + fun refresh() { + controller.refreshUsers(UserHandle.USER_NULL) + } + + companion object { + @JvmStatic + protected val disabledUserAvatarColorFilter: ColorFilter by lazy { + val matrix = ColorMatrix() + matrix.setSaturation(0f) // 0 - grayscale + ColorMatrixColorFilter(matrix) + } + + @JvmStatic + @JvmOverloads + protected fun getIconDrawable( + context: Context, + item: UserRecord, + isTablet: Boolean = false, + ): Drawable { + val iconRes = + getUserSwitcherActionIconResourceId( + item.isAddUser, + item.isGuest, + item.isAddSupervisedUser, + isTablet, + ) + return checkNotNull(context.getDrawable(iconRes)) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java index 163060814545e..dc73d1f007c6a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java @@ -69,7 +69,7 @@ public class KeyguardQsUserSwitchController extends ViewController private final Context mContext; private Resources mResources; private final UserSwitcherController mUserSwitcherController; - private UserSwitcherController.BaseUserAdapter mAdapter; + private BaseUserSwitcherAdapter mAdapter; private final KeyguardStateController mKeyguardStateController; private final FalsingManager mFalsingManager; protected final SysuiStatusBarStateController mStatusBarStateController; @@ -171,7 +171,7 @@ public class KeyguardQsUserSwitchController extends ViewController mUserAvatarView = mView.findViewById(R.id.kg_multi_user_avatar); mUserAvatarViewWithBackground = mView.findViewById( R.id.kg_multi_user_avatar_with_background); - mAdapter = new UserSwitcherController.BaseUserAdapter(mUserSwitcherController) { + mAdapter = new BaseUserSwitcherAdapter(mUserSwitcherController) { @Override public View getView(int position, View convertView, ViewGroup parent) { return null; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java index e2f5734cb4f91..0995a00533a8c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java @@ -16,9 +16,6 @@ package com.android.systemui.statusbar.policy; -import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA; -import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA; - import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; @@ -232,14 +229,8 @@ public class KeyguardUserSwitcherController extends ViewController - *
  • {@link com.android.internal.R.bool.config_expandLockScreenUserSwitcher}
  • - *
  • {@link UserSwitcherController.SIMPLE_USER_SWITCHER_GLOBAL_SETTING}
  • - * - * - * @return true if the user switcher should be open by default on the lock screen. * @see android.os.UserManager#isUserSwitcherEnabled() */ public boolean isSimpleUserSwitcher() { @@ -436,7 +427,7 @@ public class KeyguardUserSwitcherController extends ViewController + + /** Whether the user switcher experience should use the simple experience. */ + val isSimpleUserSwitcher: Boolean + + /** Require a view for jank detection */ + fun init(view: View) + + /** The [UserRecord] of the current user or `null` when none. */ + val currentUserRecord: UserRecord? + + /** The name of the current user of the device or `null`, when none is selected. */ + val currentUserName: String? + + /** + * Notifies that a user has been selected. + * + * This will trigger the right user journeys to create a guest user, switch users, and/or + * navigate to the correct destination. + * + * If a user with the given ID is not found, this method is a no-op. + * + * @param userId The ID of the user to switch to. + * @param dialogShower An optional [DialogShower] in case we need to show dialogs. + */ + fun onUserSelected(userId: Int, dialogShower: DialogShower?) + + /** Whether it is allowed to add users while the device is locked. */ + val isAddUsersFromLockScreenEnabled: Flow + + /** Whether the guest user is configured to always be present on the device. */ + val isGuestUserAutoCreated: Boolean + + /** Whether the guest user is currently being reset. */ + val isGuestUserResetting: Boolean + + /** Creates and switches to the guest user. */ + fun createAndSwitchToGuestUser(dialogShower: DialogShower?) + + /** Shows the add user dialog. */ + fun showAddUserDialog(dialogShower: DialogShower?) + + /** Starts an activity to add a supervised user to the device. */ + fun startSupervisedUserActivity() + + /** Notifies when the display density or font scale has changed. */ + fun onDensityOrFontScaleChanged() + + /** Registers an adapter to notify when the users change. */ + fun addAdapter(adapter: WeakReference) + + /** Notifies the item for a user has been clicked. */ + fun onUserListItemClicked(record: UserRecord, dialogShower: DialogShower?) + + /** + * Removes guest user and switches to target user. The guest must be the current user and its id + * must be `guestUserId`. + * + * If `targetUserId` is `UserHandle.USER_NULL`, then create a new guest user in the foreground, + * and immediately switch to it. This is used for wiping the current guest and replacing it with + * a new one. + * + * If `targetUserId` is specified, then remove the guest in the background while switching to + * `targetUserId`. + * + * If device is configured with `config_guestUserAutoCreated`, then after guest user is removed, + * a new one is created in the background. This has no effect if `targetUserId` is + * `UserHandle.USER_NULL`. + * + * @param guestUserId id of the guest user to remove + * @param targetUserId id of the user to switch to after guest is removed. If + * `UserHandle.USER_NULL`, then switch immediately to the newly created guest user. + */ + fun removeGuestUser(@UserIdInt guestUserId: Int, @UserIdInt targetUserId: Int) + + /** + * Exits guest user and switches to previous non-guest user. The guest must be the current user. + * + * @param guestUserId user id of the guest user to exit + * @param targetUserId user id of the guest user to exit, set to UserHandle#USER_NULL when + * target user id is not known + * @param forceRemoveGuestOnExit true: remove guest before switching user, false: remove guest + * only if its ephemeral, else keep guest + */ + fun exitGuestUser( + @UserIdInt guestUserId: Int, + @UserIdInt targetUserId: Int, + forceRemoveGuestOnExit: Boolean + ) + + /** + * Guarantee guest is present only if the device is provisioned. Otherwise, create a content + * observer to wait until the device is provisioned, then schedule the guest creation. + */ + fun schedulePostBootGuestCreation() + + /** Whether keyguard is showing. */ + val isKeyguardShowing: Boolean + + /** Returns the [EnforcedAdmin] for the given record, or `null` if there isn't one. */ + fun getEnforcedAdmin(record: UserRecord): EnforcedAdmin? + + /** Returns `true` if the given record is disabled by the admin; `false` otherwise. */ + fun isDisabledByAdmin(record: UserRecord): Boolean + + /** Starts an activity with the given [Intent]. */ + fun startActivity(intent: Intent) + + /** + * Refreshes users from UserManager. + * + * The pictures are only loaded if they have not been loaded yet. + * + * @param forcePictureLoadForId forces the picture of the given user to be reloaded. + */ + fun refreshUsers(forcePictureLoadForId: Int) + + /** Adds a subscriber to when user switches. */ + fun addUserSwitchCallback(callback: UserSwitchCallback) + + /** Removes a previously-added subscriber. */ + fun removeUserSwitchCallback(callback: UserSwitchCallback) + + /** Defines interface for classes that can be called back when the user is switched. */ + fun interface UserSwitchCallback { + /** Notifies that the user has switched. */ + fun onUserSwitched() + } + + companion object { + /** Alpha value to apply to a user view in the user switcher when it's selectable. */ + private const val ENABLED_ALPHA = + LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA + + /** Alpha value to apply to a user view in the user switcher when it's not selectable. */ + private const val DISABLED_ALPHA = + LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_NOT_SELECTABLE_ALPHA + + @JvmStatic + fun setSelectableAlpha(view: View) { + view.alpha = + if (view.isEnabled) { + ENABLED_ALPHA + } else { + DISABLED_ALPHA + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt new file mode 100644 index 0000000000000..12834f68c3b70 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.statusbar.policy + +import android.content.Intent +import android.view.View +import com.android.settingslib.RestrictedLockUtils +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.qs.user.UserSwitchDialogController +import com.android.systemui.user.data.source.UserRecord +import dagger.Lazy +import java.io.PrintWriter +import java.lang.ref.WeakReference +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow + +/** Implementation of [UserSwitcherController]. */ +class UserSwitcherControllerImpl +@Inject +constructor( + private val flags: FeatureFlags, + @Suppress("DEPRECATION") private val oldImpl: Lazy, +) : UserSwitcherController { + + private val isNewImpl: Boolean + get() = flags.isEnabled(Flags.REFACTORED_USER_SWITCHER_CONTROLLER) + private val _oldImpl: UserSwitcherControllerOldImpl + get() = oldImpl.get() + + private fun notYetImplemented(): Nothing { + error("Not yet implemented!") + } + + override val users: ArrayList + get() = + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.users + } + + override val isSimpleUserSwitcher: Boolean + get() = + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.isSimpleUserSwitcher + } + + override fun init(view: View) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.init(view) + } + } + + override val currentUserRecord: UserRecord? + get() = + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.currentUserRecord + } + + override val currentUserName: String? + get() = + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.currentUserName + } + + override fun onUserSelected( + userId: Int, + dialogShower: UserSwitchDialogController.DialogShower? + ) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.onUserSelected(userId, dialogShower) + } + } + + override val isAddUsersFromLockScreenEnabled: Flow + get() = + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.isAddUsersFromLockScreenEnabled + } + + override val isGuestUserAutoCreated: Boolean + get() = + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.isGuestUserAutoCreated + } + + override val isGuestUserResetting: Boolean + get() = + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.isGuestUserResetting + } + + override fun createAndSwitchToGuestUser( + dialogShower: UserSwitchDialogController.DialogShower?, + ) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.createAndSwitchToGuestUser(dialogShower) + } + } + + override fun showAddUserDialog(dialogShower: UserSwitchDialogController.DialogShower?) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.showAddUserDialog(dialogShower) + } + } + + override fun startSupervisedUserActivity() { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.startSupervisedUserActivity() + } + } + + override fun onDensityOrFontScaleChanged() { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.onDensityOrFontScaleChanged() + } + } + + override fun addAdapter(adapter: WeakReference) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.addAdapter(adapter) + } + } + + override fun onUserListItemClicked( + record: UserRecord, + dialogShower: UserSwitchDialogController.DialogShower?, + ) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.onUserListItemClicked(record, dialogShower) + } + } + + override fun removeGuestUser(guestUserId: Int, targetUserId: Int) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.removeGuestUser(guestUserId, targetUserId) + } + } + + override fun exitGuestUser( + guestUserId: Int, + targetUserId: Int, + forceRemoveGuestOnExit: Boolean + ) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.exitGuestUser(guestUserId, targetUserId, forceRemoveGuestOnExit) + } + } + + override fun schedulePostBootGuestCreation() { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.schedulePostBootGuestCreation() + } + } + + override val isKeyguardShowing: Boolean + get() = + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.isKeyguardShowing + } + + override fun getEnforcedAdmin(record: UserRecord): RestrictedLockUtils.EnforcedAdmin? { + return if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.getEnforcedAdmin(record) + } + } + + override fun isDisabledByAdmin(record: UserRecord): Boolean { + return if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.isDisabledByAdmin(record) + } + } + + override fun startActivity(intent: Intent) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.startActivity(intent) + } + } + + override fun refreshUsers(forcePictureLoadForId: Int) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.refreshUsers(forcePictureLoadForId) + } + } + + override fun addUserSwitchCallback(callback: UserSwitcherController.UserSwitchCallback) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.addUserSwitchCallback(callback) + } + } + + override fun removeUserSwitchCallback(callback: UserSwitcherController.UserSwitchCallback) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.removeUserSwitchCallback(callback) + } + } + + override fun dump(pw: PrintWriter, args: Array) { + if (isNewImpl) { + notYetImplemented() + } else { + _oldImpl.dump(pw, args) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImpl.java similarity index 82% rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java rename to packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImpl.java index 1d5b88e7dee01..d365aa6f952d4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImpl.java @@ -11,9 +11,8 @@ * 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 + * limitations under the License. */ - package com.android.systemui.statusbar.policy; import static android.os.UserManager.SWITCHABILITY_STATUS_OK; @@ -34,10 +33,6 @@ import android.content.IntentFilter; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.graphics.Bitmap; -import android.graphics.ColorFilter; -import android.graphics.ColorMatrix; -import android.graphics.ColorMatrixColorFilter; -import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; @@ -51,7 +46,6 @@ import android.util.SparseArray; import android.util.SparseBooleanArray; import android.view.View; import android.view.WindowManagerGlobal; -import android.widget.BaseAdapter; import android.widget.Toast; import androidx.annotation.Nullable; @@ -63,7 +57,6 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.util.LatencyTracker; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.users.UserCreatingDialog; -import com.android.systemui.Dumpable; import com.android.systemui.GuestResetOrExitSessionReceiver; import com.android.systemui.GuestResumeSessionReceiver; import com.android.systemui.R; @@ -86,7 +79,6 @@ import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.telephony.TelephonyListenerManager; import com.android.systemui.user.CreateUserActivity; import com.android.systemui.user.data.source.UserRecord; -import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper; import com.android.systemui.util.settings.GlobalSettings; import com.android.systemui.util.settings.SecureSettings; @@ -106,15 +98,14 @@ import kotlinx.coroutines.flow.MutableStateFlow; import kotlinx.coroutines.flow.StateFlowKt; /** - * Keeps a list of all users on the device for user switching. + * Old implementation. Keeps a list of all users on the device for user switching. + * + * @deprecated This is the old implementation. Please depend on {@link UserSwitcherController} + * instead. */ +@Deprecated @SysUISingleton -public class UserSwitcherController implements Dumpable { - - public static final float USER_SWITCH_ENABLED_ALPHA = - LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA; - public static final float USER_SWITCH_DISABLED_ALPHA = - LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_NOT_SELECTABLE_ALPHA; +public class UserSwitcherControllerOldImpl implements UserSwitcherController { private static final String TAG = "UserSwitcherController"; private static final boolean DEBUG = false; @@ -123,7 +114,7 @@ public class UserSwitcherController implements Dumpable { private static final int PAUSE_REFRESH_USERS_TIMEOUT_MS = 3000; private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; - private static final long MULTI_USER_JOURNEY_TIMEOUT = 20000l; + private static final long MULTI_USER_JOURNEY_TIMEOUT = 20000L; private static final String INTERACTION_JANK_ADD_NEW_USER_TAG = "add_new_user"; private static final String INTERACTION_JANK_EXIT_GUEST_MODE_TAG = "exit_guest_mode"; @@ -132,7 +123,7 @@ public class UserSwitcherController implements Dumpable { protected final UserTracker mUserTracker; protected final UserManager mUserManager; private final ContentObserver mSettingsObserver; - private final ArrayList> mAdapters = new ArrayList<>(); + private final ArrayList> mAdapters = new ArrayList<>(); @VisibleForTesting final GuestResumeSessionReceiver mGuestResumeSessionReceiver; @VisibleForTesting @@ -158,7 +149,6 @@ public class UserSwitcherController implements Dumpable { @VisibleForTesting Dialog mAddUserDialog; private int mLastNonGuestUser = UserHandle.USER_SYSTEM; - private boolean mResumeUserOnGuestLogout = true; private boolean mSimpleUserSwitcher; // When false, there won't be any visual affordance to add a new user from the keyguard even if // the user is unlocked @@ -187,7 +177,8 @@ public class UserSwitcherController implements Dumpable { Collections.synchronizedList(new ArrayList<>()); @Inject - public UserSwitcherController(Context context, + public UserSwitcherControllerOldImpl( + Context context, IActivityManager activityManager, UserManager userManager, UserTracker userTracker, @@ -303,16 +294,10 @@ public class UserSwitcherController implements Dumpable { refreshUsers(UserHandle.USER_NULL); } - /** - * Refreshes users from UserManager. - * - * The pictures are only loaded if they have not been loaded yet. - * - * @param forcePictureLoadForId forces the picture of the given user to be reloaded. - */ + @Override @SuppressWarnings("unchecked") - private void refreshUsers(int forcePictureLoadForId) { - if (DEBUG) Log.d(TAG, "refreshUsers(forcePictureLoadForId=" + forcePictureLoadForId+")"); + public void refreshUsers(int forcePictureLoadForId) { + if (DEBUG) Log.d(TAG, "refreshUsers(forcePictureLoadForId=" + forcePictureLoadForId + ")"); if (forcePictureLoadForId != UserHandle.USER_NULL) { mForcePictureLoadForUserId.put(forcePictureLoadForId, true); } @@ -323,8 +308,8 @@ public class UserSwitcherController implements Dumpable { boolean forceAllUsers = mForcePictureLoadForUserId.get(UserHandle.USER_ALL); SparseArray bitmaps = new SparseArray<>(mUsers.size()); - final int N = mUsers.size(); - for (int i = 0; i < N; i++) { + final int userCount = mUsers.size(); + for (int i = 0; i < userCount; i++) { UserRecord r = mUsers.get(i); if (r == null || r.picture == null || r.info == null || forceAllUsers || mForcePictureLoadForUserId.get(r.info.id)) { @@ -431,38 +416,41 @@ public class UserSwitcherController implements Dumpable { }); } - boolean systemCanCreateUsers() { + private boolean systemCanCreateUsers() { return !mUserManager.hasBaseUserRestriction( UserManager.DISALLOW_ADD_USER, UserHandle.SYSTEM); } - boolean currentUserCanCreateUsers() { + private boolean currentUserCanCreateUsers() { UserInfo currentUser = mUserTracker.getUserInfo(); return currentUser != null && (currentUser.isAdmin() || mUserTracker.getUserId() == UserHandle.USER_SYSTEM) && systemCanCreateUsers(); } - boolean anyoneCanCreateUsers() { + private boolean anyoneCanCreateUsers() { return systemCanCreateUsers() && mAddUsersFromLockScreen.getValue(); } + @VisibleForTesting boolean canCreateGuest(boolean hasExistingGuest) { return mUserSwitcherEnabled && (currentUserCanCreateUsers() || anyoneCanCreateUsers()) && !hasExistingGuest; } + @VisibleForTesting boolean canCreateUser() { return mUserSwitcherEnabled && (currentUserCanCreateUsers() || anyoneCanCreateUsers()) && mUserManager.canAddMoreUsers(UserManager.USER_TYPE_FULL_SECONDARY); } - boolean createIsRestricted() { + private boolean createIsRestricted() { return !mAddUsersFromLockScreen.getValue(); } + @VisibleForTesting boolean canCreateSupervisedUser() { return !TextUtils.isEmpty(mCreateSupervisedUserPackage) && canCreateUser(); } @@ -476,7 +464,7 @@ public class UserSwitcherController implements Dumpable { private void notifyAdapters() { for (int i = mAdapters.size() - 1; i >= 0; i--) { - BaseUserAdapter adapter = mAdapters.get(i).get(); + BaseUserSwitcherAdapter adapter = mAdapters.get(i).get(); if (adapter != null) { adapter.notifyDataSetChanged(); } else { @@ -485,37 +473,20 @@ public class UserSwitcherController implements Dumpable { } } + @Override public boolean isSimpleUserSwitcher() { return mSimpleUserSwitcher; } - public void setResumeUserOnGuestLogout(boolean resume) { - mResumeUserOnGuestLogout = resume; - } - /** * Returns whether the current user is a system user. */ - public boolean isSystemUser() { + @VisibleForTesting + boolean isSystemUser() { return mUserTracker.getUserId() == UserHandle.USER_SYSTEM; } - public void removeUserId(int userId) { - if (userId == UserHandle.USER_SYSTEM) { - Log.w(TAG, "User " + userId + " could not removed."); - return; - } - if (mUserTracker.getUserId() == userId) { - switchToUserId(UserHandle.USER_SYSTEM); - } - if (mUserManager.removeUser(userId)) { - refreshUsers(UserHandle.USER_NULL); - } - } - - /** - * @return UserRecord for the current user - */ + @Override public @Nullable UserRecord getCurrentUserRecord() { for (int i = 0; i < mUsers.size(); ++i) { UserRecord userRecord = mUsers.get(i); @@ -526,17 +497,7 @@ public class UserSwitcherController implements Dumpable { return null; } - /** - * Notifies that a user has been selected. - * - *

    This will trigger the right user journeys to create a guest user, switch users, and/or - * navigate to the correct destination. - * - *

    If a user with the given ID is not found, this method is a no-op. - * - * @param userId The ID of the user to switch to. - * @param dialogShower An optional {@link DialogShower} in case we need to show dialogs. - */ + @Override public void onUserSelected(int userId, @Nullable DialogShower dialogShower) { UserRecord userRecord = mUsers.stream() .filter(x -> x.resolveId() == userId) @@ -549,23 +510,23 @@ public class UserSwitcherController implements Dumpable { onUserListItemClicked(userRecord, dialogShower); } - /** Whether it is allowed to add users while the device is locked. */ - public Flow getAddUsersFromLockScreen() { + @Override + public Flow isAddUsersFromLockScreenEnabled() { return mAddUsersFromLockScreen; } - /** Returns {@code true} if the guest user is configured to always be present on the device. */ + @Override public boolean isGuestUserAutoCreated() { return mGuestUserAutoCreated; } - /** Returns {@code true} if the guest user is currently being reset. */ + @Override public boolean isGuestUserResetting() { return mGuestIsResetting.get(); } - @VisibleForTesting - void onUserListItemClicked(UserRecord record, DialogShower dialogShower) { + @Override + public void onUserListItemClicked(UserRecord record, DialogShower dialogShower) { if (record.isGuest && record.info == null) { createAndSwitchToGuestUser(dialogShower); } else if (record.isAddUser) { @@ -604,7 +565,7 @@ public class UserSwitcherController implements Dumpable { switchToUserId(id); } - protected void switchToUserId(int id) { + private void switchToUserId(int id) { try { if (mView != null) { mInteractionJankMonitor.begin(InteractionJankMonitor.Configuration.Builder @@ -621,7 +582,7 @@ public class UserSwitcherController implements Dumpable { private void showExitGuestDialog(int id, boolean isGuestEphemeral, DialogShower dialogShower) { int newId = UserHandle.USER_SYSTEM; - if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) { + if (mLastNonGuestUser != UserHandle.USER_SYSTEM) { UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser); if (info != null && info.isEnabled() && info.supportsSwitchToByUser()) { newId = info.id; @@ -645,9 +606,7 @@ public class UserSwitcherController implements Dumpable { } } - /** - * Creates and switches to the guest user. - */ + @Override public void createAndSwitchToGuestUser(@Nullable DialogShower dialogShower) { createGuestAsync(guestId -> { // guestId may be USER_NULL if we haven't reloaded the user list yet. @@ -658,9 +617,7 @@ public class UserSwitcherController implements Dumpable { }); } - /** - * Shows the add user dialog. - */ + @Override public void showAddUserDialog(@Nullable DialogShower dialogShower) { if (mAddUserDialog != null && mAddUserDialog.isShowing()) { mAddUserDialog.cancel(); @@ -677,9 +634,7 @@ public class UserSwitcherController implements Dumpable { } } - /** - * Starts an activity to add a supervised user to the device. - */ + @Override public void startSupervisedUserActivity() { final Intent intent = new Intent() .setAction(UserManager.ACTION_CREATE_SUPERVISED_USER) @@ -711,7 +666,7 @@ public class UserSwitcherController implements Dumpable { public void onReceive(Context context, Intent intent) { if (DEBUG) { Log.v(TAG, "Broadcast: a=" + intent.getAction() - + " user=" + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)); + + " user=" + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)); } boolean unpauseRefreshUsers = false; @@ -725,8 +680,8 @@ public class UserSwitcherController implements Dumpable { final int currentId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); final UserInfo userInfo = mUserManager.getUserInfo(currentId); - final int N = mUsers.size(); - for (int i = 0; i < N; i++) { + final int userCount = mUsers.size(); + for (int i = 0; i < userCount; i++) { UserRecord record = mUsers.get(i); if (record.info == null) continue; boolean shouldBeCurrent = record.info.id == currentId; @@ -805,7 +760,7 @@ public class UserSwitcherController implements Dumpable { pw.println("mGuestUserAutoCreated=" + mGuestUserAutoCreated); } - /** Returns the name of the current user of the phone. */ + @Override public String getCurrentUserName() { if (mUsers.isEmpty()) return null; UserRecord item = mUsers.stream().filter(x -> x.isCurrent).findFirst().orElse(null); @@ -814,40 +769,22 @@ public class UserSwitcherController implements Dumpable { return item.info.name; } + @Override public void onDensityOrFontScaleChanged() { refreshUsers(UserHandle.USER_ALL); } - @VisibleForTesting - public void addAdapter(WeakReference adapter) { + @Override + public void addAdapter(WeakReference adapter) { mAdapters.add(adapter); } - @VisibleForTesting + @Override public ArrayList getUsers() { return mUsers; } - /** - * Removes guest user and switches to target user. The guest must be the current user and its id - * must be {@code guestUserId}. - * - *

    If {@code targetUserId} is {@link UserHandle#USER_NULL}, then create a new guest user in - * the foreground, and immediately switch to it. This is used for wiping the current guest and - * replacing it with a new one. - * - *

    If {@code targetUserId} is specified, then remove the guest in the background while - * switching to {@code targetUserId}. - * - *

    If device is configured with {@link - * com.android.internal.R.bool.config_guestUserAutoCreated}, then after guest user is removed, a - * new one is created in the background. This has no effect if {@code targetUserId} is {@link - * UserHandle#USER_NULL}. - * - * @param guestUserId id of the guest user to remove - * @param targetUserId id of the user to switch to after guest is removed. If {@link - * UserHandle#USER_NULL}, then switch immediately to the newly created guest user. - */ + @Override public void removeGuestUser(@UserIdInt int guestUserId, @UserIdInt int targetUserId) { UserInfo currentUser = mUserTracker.getUserInfo(); if (currentUser.id != guestUserId) { @@ -894,18 +831,9 @@ public class UserSwitcherController implements Dumpable { } } - /** - * Exits guest user and switches to previous non-guest user. The guest must be the current - * user. - * - * @param guestUserId user id of the guest user to exit - * @param targetUserId user id of the guest user to exit, set to UserHandle#USER_NULL when - * target user id is not known - * @param forceRemoveGuestOnExit true: remove guest before switching user, - * false: remove guest only if its ephemeral, else keep guest - */ + @Override public void exitGuestUser(@UserIdInt int guestUserId, @UserIdInt int targetUserId, - boolean forceRemoveGuestOnExit) { + boolean forceRemoveGuestOnExit) { UserInfo currentUser = mUserTracker.getUserInfo(); if (currentUser.id != guestUserId) { Log.w(TAG, "User requesting to start a new session (" + guestUserId + ")" @@ -921,7 +849,7 @@ public class UserSwitcherController implements Dumpable { int newUserId = UserHandle.USER_SYSTEM; if (targetUserId == UserHandle.USER_NULL) { // when target user is not specified switch to last non guest user - if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) { + if (mLastNonGuestUser != UserHandle.USER_SYSTEM) { UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser); if (info != null && info.isEnabled() && info.supportsSwitchToByUser()) { newUserId = info.id; @@ -959,10 +887,7 @@ public class UserSwitcherController implements Dumpable { } - /** - * Guarantee guest is present only if the device is provisioned. Otherwise, create a content - * observer to wait until the device is provisioned, then schedule the guest creation. - */ + @Override public void schedulePostBootGuestCreation() { if (isDeviceAllowedToAddGuest()) { guaranteeGuestPresent(); @@ -1014,7 +939,7 @@ public class UserSwitcherController implements Dumpable { * @return The multi-user user ID of the newly created guest user, or * {@link UserHandle#USER_NULL} if the guest couldn't be created. */ - public @UserIdInt int createGuest() { + private @UserIdInt int createGuest() { UserInfo guest; try { guest = mUserManager.createGuest(mContext); @@ -1029,135 +954,27 @@ public class UserSwitcherController implements Dumpable { return guest.id; } - /** - * Require a view for jank detection - */ + @Override public void init(View view) { mView = view; } - @VisibleForTesting - public KeyguardStateController getKeyguardStateController() { - return mKeyguardStateController; + @Override + public boolean isKeyguardShowing() { + return mKeyguardStateController.isShowing(); } - /** - * Returns the {@link EnforcedAdmin} for the given record, or {@code null} if there isn't one. - */ + @Override @Nullable public EnforcedAdmin getEnforcedAdmin(UserRecord record) { return mEnforcedAdminByUserRecord.get(record); } - /** - * Returns {@code true} if the given record is disabled by the admin; {@code false} otherwise. - */ + @Override public boolean isDisabledByAdmin(UserRecord record) { return mDisabledByAdmin.contains(record); } - public static abstract class BaseUserAdapter extends BaseAdapter { - - final UserSwitcherController mController; - private final KeyguardStateController mKeyguardStateController; - - protected BaseUserAdapter(UserSwitcherController controller) { - mController = controller; - mKeyguardStateController = controller.getKeyguardStateController(); - controller.addAdapter(new WeakReference<>(this)); - } - - protected ArrayList getUsers() { - return mController.getUsers(); - } - - public int getUserCount() { - return countUsers(false); - } - - @Override - public int getCount() { - return countUsers(true); - } - - private int countUsers(boolean includeGuest) { - boolean keyguardShowing = mKeyguardStateController.isShowing(); - final int userSize = getUsers().size(); - int count = 0; - for (int i = 0; i < userSize; i++) { - if (getUsers().get(i).isGuest && !includeGuest) { - continue; - } - if (getUsers().get(i).isRestricted && keyguardShowing) { - break; - } - count++; - } - return count; - } - - @Override - public UserRecord getItem(int position) { - return getUsers().get(position); - } - - @Override - public long getItemId(int position) { - return position; - } - - /** - * It handles click events on user list items. - * - * If the user switcher is hosted in a dialog, passing a non-null {@link DialogShower} - * will allow animation to and from the parent dialog. - * - */ - public void onUserListItemClicked(UserRecord record, @Nullable DialogShower dialogShower) { - mController.onUserListItemClicked(record, dialogShower); - } - - public void onUserListItemClicked(UserRecord record) { - onUserListItemClicked(record, null); - } - - public String getName(Context context, UserRecord item) { - return getName(context, item, false); - } - - /** - * Returns the name for the given {@link UserRecord}. - */ - public String getName(Context context, UserRecord item, boolean isTablet) { - return LegacyUserUiHelper.getUserRecordName( - context, - item, - mController.isGuestUserAutoCreated(), - mController.isGuestUserResetting(), - isTablet); - } - - protected static ColorFilter getDisabledUserAvatarColorFilter() { - ColorMatrix matrix = new ColorMatrix(); - matrix.setSaturation(0f); // 0 - grayscale - return new ColorMatrixColorFilter(matrix); - } - - protected static Drawable getIconDrawable(Context context, UserRecord item) { - return getIconDrawable(context, item, false); - } - protected static Drawable getIconDrawable(Context context, UserRecord item, - boolean isTablet) { - int iconRes = LegacyUserUiHelper.getUserSwitcherActionIconResourceId( - item.isAddUser, item.isGuest, item.isAddSupervisedUser, isTablet); - return context.getDrawable(iconRes); - } - - public void refresh() { - mController.refreshUsers(UserHandle.USER_NULL); - } - } - private void checkIfAddUserDisallowedByAdminOnly(UserRecord record) { EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(mContext, UserManager.DISALLOW_ADD_USER, mUserTracker.getUserId()); @@ -1178,20 +995,17 @@ public class UserSwitcherController implements Dumpable { defaultSimpleUserSwitcher, UserHandle.USER_SYSTEM) != 0; } + @Override public void startActivity(Intent intent) { mActivityStarter.startActivity(intent, true); } - /** - * Add a subscriber to when user switches. - */ + @Override public void addUserSwitchCallback(UserSwitchCallback callback) { mUserSwitchCallbacks.add(callback); } - /** - * Remove a subscriber to when user switches. - */ + @Override public void removeUserSwitchCallback(UserSwitchCallback callback) { mUserSwitchCallbacks.remove(callback); } @@ -1218,7 +1032,7 @@ public class UserSwitcherController implements Dumpable { // which // helps making the transition faster. if (!mKeyguardStateController.isShowing()) { - mHandler.post(UserSwitcherController.this::notifyAdapters); + mHandler.post(UserSwitcherControllerOldImpl.this::notifyAdapters); } else { notifyAdapters(); } @@ -1367,13 +1181,4 @@ public class UserSwitcherController implements Dumpable { } } - /** - * Callback to for when this controller receives the intent to switch users. - */ - public interface UserSwitchCallback { - /** - * Called when user has switched. - */ - void onUserSwitched(); - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java index 1b7353923adaf..b1b45b51d8e4c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java @@ -58,6 +58,8 @@ import com.android.systemui.statusbar.policy.SecurityController; import com.android.systemui.statusbar.policy.SecurityControllerImpl; import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; +import com.android.systemui.statusbar.policy.UserSwitcherController; +import com.android.systemui.statusbar.policy.UserSwitcherControllerImpl; import com.android.systemui.statusbar.policy.WalletController; import com.android.systemui.statusbar.policy.WalletControllerImpl; import com.android.systemui.statusbar.policy.ZenModeController; @@ -196,4 +198,8 @@ public interface StatusBarPolicyModule { static DataSaverController provideDataSaverController(NetworkController networkController) { return networkController.getDataSaverController(); } + + /** Binds {@link UserSwitcherController} to its implementation. */ + @Binds + UserSwitcherController bindUserSwitcherController(UserSwitcherControllerImpl impl); } diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt index 5e2dde6be0462..108ab43977e9c 100644 --- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt @@ -53,10 +53,8 @@ import com.android.systemui.flags.Flags import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.FalsingManager.LOW_PENALTY import com.android.systemui.settings.UserTracker +import com.android.systemui.statusbar.policy.BaseUserSwitcherAdapter import com.android.systemui.statusbar.policy.UserSwitcherController -import com.android.systemui.statusbar.policy.UserSwitcherController.BaseUserAdapter -import com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA -import com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA import com.android.systemui.user.data.source.UserRecord import com.android.systemui.user.ui.binder.UserSwitcherViewBinder import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel @@ -66,10 +64,10 @@ import kotlin.math.ceil private const val USER_VIEW = "user_view" -/** - * Support a fullscreen user switcher - */ -open class UserSwitcherActivity @Inject constructor( +/** Support a fullscreen user switcher */ +open class UserSwitcherActivity +@Inject +constructor( private val userSwitcherController: UserSwitcherController, private val broadcastDispatcher: BroadcastDispatcher, private val falsingCollector: FalsingCollector, @@ -86,11 +84,12 @@ open class UserSwitcherActivity @Inject constructor( private lateinit var addButton: View private var addUserRecords = mutableListOf() private val onBackCallback = OnBackInvokedCallback { finish() } - private val userSwitchedCallback: UserTracker.Callback = object : UserTracker.Callback { - override fun onUserChanged(newUser: Int, userContext: Context) { - finish() + private val userSwitchedCallback: UserTracker.Callback = + object : UserTracker.Callback { + override fun onUserChanged(newUser: Int, userContext: Context) { + finish() + } } - } // When the add users options become available, insert another option to manage users private val manageUserRecord = UserRecord( @@ -114,13 +113,14 @@ open class UserSwitcherActivity @Inject constructor( @VisibleForTesting fun createActivity() { setContentView(R.layout.user_switcher_fullscreen) - window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE - or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) + window.decorView.systemUiVisibility = + (View.SYSTEM_UI_FLAG_LAYOUT_STABLE or + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) if (isUsingModernArchitecture()) { Log.d(TAG, "Using modern architecture.") - val viewModel = ViewModelProvider( - this, viewModelFactory.get())[UserSwitcherViewModel::class.java] + val viewModel = + ViewModelProvider(this, viewModelFactory.get())[UserSwitcherViewModel::class.java] UserSwitcherViewBinder.bind( view = requireViewById(R.id.user_switcher_root), viewModel = viewModel, @@ -136,27 +136,23 @@ open class UserSwitcherActivity @Inject constructor( parent = requireViewById(R.id.user_switcher_root) - parent.touchHandler = object : Gefingerpoken { - override fun onTouchEvent(ev: MotionEvent?): Boolean { - falsingCollector.onTouchEvent(ev) - return false + parent.touchHandler = + object : Gefingerpoken { + override fun onTouchEvent(ev: MotionEvent?): Boolean { + falsingCollector.onTouchEvent(ev) + return false + } } - } - requireViewById(R.id.cancel).apply { - setOnClickListener { - _ -> finish() - } - } + requireViewById(R.id.cancel).apply { setOnClickListener { _ -> finish() } } - addButton = requireViewById(R.id.add).apply { - setOnClickListener { - _ -> showPopupMenu() - } - } + addButton = + requireViewById(R.id.add).apply { setOnClickListener { _ -> showPopupMenu() } } onBackInvokedDispatcher.registerOnBackInvokedCallback( - OnBackInvokedDispatcher.PRIORITY_DEFAULT, onBackCallback) + OnBackInvokedDispatcher.PRIORITY_DEFAULT, + onBackCallback + ) userSwitcherController.init(parent) initBroadcastReceiver() @@ -169,25 +165,30 @@ open class UserSwitcherActivity @Inject constructor( val items = mutableListOf() addUserRecords.forEach { items.add(it) } - var popupMenuAdapter = ItemAdapter( - this, - R.layout.user_switcher_fullscreen_popup_item, - layoutInflater, - { item: UserRecord -> adapter.getName(this@UserSwitcherActivity, item, true) }, - { item: UserRecord -> adapter.findUserIcon(item, true).mutate().apply { - setTint(resources.getColor( - R.color.user_switcher_fullscreen_popup_item_tint, - getTheme() - )) - } } - ) + var popupMenuAdapter = + ItemAdapter( + this, + R.layout.user_switcher_fullscreen_popup_item, + layoutInflater, + { item: UserRecord -> adapter.getName(this@UserSwitcherActivity, item, true) }, + { item: UserRecord -> + adapter.findUserIcon(item, true).mutate().apply { + setTint( + resources.getColor( + R.color.user_switcher_fullscreen_popup_item_tint, + getTheme() + ) + ) + } + } + ) popupMenuAdapter.addAll(items) - popupMenu = UserSwitcherPopupMenu(this).apply { - setAnchorView(addButton) - setAdapter(popupMenuAdapter) - setOnItemClickListener { - parent: AdapterView<*>, view: View, pos: Int, id: Long -> + popupMenu = + UserSwitcherPopupMenu(this).apply { + setAnchorView(addButton) + setAdapter(popupMenuAdapter) + setOnItemClickListener { parent: AdapterView<*>, view: View, pos: Int, id: Long -> if (falsingManager.isFalseTap(LOW_PENALTY) || !view.isEnabled()) { return@setOnItemClickListener } @@ -206,10 +207,10 @@ open class UserSwitcherActivity @Inject constructor( if (!item.isAddUser) { this@UserSwitcherActivity.finish() } - } + } - show() - } + show() + } } private fun buildUserViews() { @@ -227,8 +228,8 @@ open class UserSwitcherActivity @Inject constructor( val totalWidth = parent.width val userViewCount = adapter.getTotalUserViews() val maxColumns = getMaxColumns(userViewCount) - val horizontalGap = resources - .getDimensionPixelSize(R.dimen.user_switcher_fullscreen_horizontal_gap) + val horizontalGap = + resources.getDimensionPixelSize(R.dimen.user_switcher_fullscreen_horizontal_gap) val totalWidthOfHorizontalGap = (maxColumns - 1) * horizontalGap val maxWidgetDiameter = (totalWidth - totalWidthOfHorizontalGap) / maxColumns @@ -299,14 +300,15 @@ open class UserSwitcherActivity @Inject constructor( } private fun initBroadcastReceiver() { - broadcastReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - val action = intent.getAction() - if (Intent.ACTION_SCREEN_OFF.equals(action)) { - finish() + broadcastReceiver = + object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val action = intent.getAction() + if (Intent.ACTION_SCREEN_OFF.equals(action)) { + finish() + } } } - } val filter = IntentFilter() filter.addAction(Intent.ACTION_SCREEN_OFF) @@ -322,9 +324,7 @@ open class UserSwitcherActivity @Inject constructor( return flags.isEnabled(Flags.MODERN_USER_SWITCHER_ACTIVITY) } - /** - * Provides views to populate the option menu. - */ + /** Provides views to populate the option menu. */ private class ItemAdapter( val parentContext: Context, val resource: Int, @@ -337,43 +337,27 @@ open class UserSwitcherActivity @Inject constructor( val item = getItem(position) val view = convertView ?: layoutInflater.inflate(resource, parent, false) - view.requireViewById(R.id.icon).apply { - setImageDrawable(iconGetter(item)) - } - view.requireViewById(R.id.text).apply { - setText(textGetter(item)) - } + view.requireViewById(R.id.icon).apply { setImageDrawable(iconGetter(item)) } + view.requireViewById(R.id.text).apply { setText(textGetter(item)) } return view } } - private inner class UserAdapter : BaseUserAdapter(userSwitcherController) { + private inner class UserAdapter : BaseUserSwitcherAdapter(userSwitcherController) { override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { val item = getItem(position) var view = convertView as ViewGroup? if (view == null) { - view = layoutInflater.inflate( - R.layout.user_switcher_fullscreen_item, - parent, - false - ) as ViewGroup - } - (view.getChildAt(0) as ImageView).apply { - setImageDrawable(getDrawable(item)) - } - (view.getChildAt(1) as TextView).apply { - setText(getName(getContext(), item)) + view = + layoutInflater.inflate(R.layout.user_switcher_fullscreen_item, parent, false) + as ViewGroup } + (view.getChildAt(0) as ImageView).apply { setImageDrawable(getDrawable(item)) } + (view.getChildAt(1) as TextView).apply { setText(getName(getContext(), item)) } view.setEnabled(item.isSwitchToEnabled) - view.setAlpha( - if (view.isEnabled()) { - USER_SWITCH_ENABLED_ALPHA - } else { - USER_SWITCH_DISABLED_ALPHA - } - ) + UserSwitcherController.setSelectableAlpha(view) view.setTag(USER_VIEW) return view } @@ -401,23 +385,20 @@ open class UserSwitcherActivity @Inject constructor( } fun getTotalUserViews(): Int { - return users.count { item -> - !doNotRenderUserView(item) - } + return users.count { item -> !doNotRenderUserView(item) } } fun doNotRenderUserView(item: UserRecord): Boolean { - return item.isAddUser || - item.isAddSupervisedUser || - item.isGuest && item.info == null + return item.isAddUser || item.isAddSupervisedUser || item.isGuest && item.info == null } private fun getDrawable(item: UserRecord): Drawable { - var drawable = if (item.isGuest) { - getDrawable(R.drawable.ic_account_circle) - } else { - findUserIcon(item) - } + var drawable = + if (item.isGuest) { + getDrawable(R.drawable.ic_account_circle) + } else { + findUserIcon(item) + } drawable.mutate() if (!item.isCurrent && !item.isSwitchToEnabled) { @@ -429,16 +410,16 @@ open class UserSwitcherActivity @Inject constructor( ) } - val ld = getDrawable(R.drawable.user_switcher_icon_large).mutate() - as LayerDrawable - if (item == userSwitcherController.getCurrentUserRecord()) { + val ld = getDrawable(R.drawable.user_switcher_icon_large).mutate() as LayerDrawable + if (item == userSwitcherController.currentUserRecord) { (ld.findDrawableByLayerId(R.id.ring) as GradientDrawable).apply { - val stroke = resources - .getDimensionPixelSize(R.dimen.user_switcher_icon_selected_width) - val color = Utils.getColorAttrDefaultColor( - this@UserSwitcherActivity, - com.android.internal.R.attr.colorAccentPrimary - ) + val stroke = + resources.getDimensionPixelSize(R.dimen.user_switcher_icon_selected_width) + val color = + Utils.getColorAttrDefaultColor( + this@UserSwitcherActivity, + com.android.internal.R.attr.colorAccentPrimary + ) setStroke(stroke, color) } diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt index 305b5ee920a16..035638800f9cf 100644 --- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt @@ -99,7 +99,7 @@ constructor( override val actions: Flow> = userRecords.map { records -> records.filter { it.isNotUser() }.map { it.toActionModel() } } - override val isActionableWhenLocked: Flow = controller.addUsersFromLockScreen + override val isActionableWhenLocked: Flow = controller.isAddUsersFromLockScreenEnabled override val isGuestUserAutoCreated: Boolean = controller.isGuestUserAutoCreated diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java index 28e99da494966..43f6f1aac0974 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java @@ -116,9 +116,7 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase { ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); when(mUserSwitcherController.getCurrentUserName()).thenReturn("Test User"); - when(mUserSwitcherController.getKeyguardStateController()) - .thenReturn(mKeyguardStateController); - when(mKeyguardStateController.isShowing()).thenReturn(true); + when(mUserSwitcherController.isKeyguardShowing()).thenReturn(true); mScreenWidth = getUiDevice().getDisplayWidth(); mFakeMeasureSpec = View diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt similarity index 98% rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerTest.kt rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt index 37c0f3621b6f1..bf432388ad283 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt @@ -34,14 +34,14 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper @SmallTest -class StatusBarUserSwitcherControllerTest : SysuiTestCase() { +class StatusBarUserSwitcherControllerOldImplTest : SysuiTestCase() { @Mock private lateinit var tracker: StatusBarUserInfoTracker diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt new file mode 100644 index 0000000000000..f3046477f4d16 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.statusbar.policy + +import android.content.pm.UserInfo +import android.graphics.Bitmap +import android.os.UserHandle +import android.view.View +import android.view.ViewGroup +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.qs.user.UserSwitchDialogController +import com.android.systemui.user.data.source.UserRecord +import com.android.systemui.util.mockito.kotlinArgumentCaptor +import com.android.systemui.util.mockito.mock +import com.google.common.truth.Truth.assertThat +import java.lang.ref.WeakReference +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(JUnit4::class) +class BaseUserSwitcherAdapterTest : SysuiTestCase() { + + @Mock private lateinit var controller: UserSwitcherController + + private lateinit var underTest: BaseUserSwitcherAdapter + + private lateinit var users: ArrayList + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + users = + ArrayList( + listOf( + createUserRecord( + id = 0, + picture = mock(), + isSelected = true, + isGuest = false, + ), + createUserRecord( + id = 1, + picture = mock(), + isSelected = false, + isGuest = false, + ), + createUserRecord( + id = UserHandle.USER_NULL, + picture = null, + isSelected = false, + isGuest = true, + ), + ) + ) + + whenever(controller.users).thenAnswer { users } + + underTest = + object : BaseUserSwitcherAdapter(controller) { + override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { + return mock() + } + } + } + + @Test + fun `Adds self to controller in constructor`() { + val captor = kotlinArgumentCaptor>() + verify(controller).addAdapter(captor.capture()) + + assertThat(captor.value.get()).isEqualTo(underTest) + } + + @Test + fun count() { + assertThat(underTest.count).isEqualTo(users.size) + } + + @Test + fun `count - ignores restricted users when device is locked`() { + whenever(controller.isKeyguardShowing).thenReturn(true) + users = + ArrayList( + listOf( + createUserRecord( + id = 0, + picture = mock(), + isSelected = true, + isGuest = false, + isRestricted = false, + ), + createUserRecord( + id = 1, + picture = mock(), + isSelected = false, + isGuest = false, + isRestricted = true, // this one will be ignored. + ), + createUserRecord( + id = UserHandle.USER_NULL, + picture = null, + isSelected = false, + isGuest = true, + ), + ) + ) + assertThat(underTest.count).isEqualTo(users.size - 1) + } + + @Test + fun `count - does not ignore restricted users when device is not locked`() { + whenever(controller.isKeyguardShowing).thenReturn(false) + users = + ArrayList( + listOf( + createUserRecord( + id = 0, + picture = mock(), + isSelected = true, + isGuest = false, + isRestricted = false, + ), + createUserRecord( + id = 1, + picture = mock(), + isSelected = false, + isGuest = false, + isRestricted = true, + ), + createUserRecord( + id = UserHandle.USER_NULL, + picture = null, + isSelected = false, + isGuest = true, + ), + ) + ) + assertThat(underTest.count).isEqualTo(users.size) + } + + @Test + fun getItem() { + assertThat((0 until underTest.count).map { position -> underTest.getItem(position) }) + .isEqualTo(users) + } + + @Test + fun getItemId() { + (0 until underTest.count).map { position -> + assertThat(underTest.getItemId(position)).isEqualTo(position) + } + } + + @Test + fun onUserListItemClicked() { + val userRecord = users[users.size / 2] + val dialogShower: UserSwitchDialogController.DialogShower = mock() + + underTest.onUserListItemClicked(userRecord, dialogShower) + + verify(controller).onUserListItemClicked(userRecord, dialogShower) + } + + @Test + fun `getName - non guest - returns real name`() { + val userRecord = + createUserRecord( + id = 1, + picture = mock(), + ) + + assertThat(underTest.getName(context, userRecord)).isEqualTo(userRecord.info?.name) + } + + @Test + fun `getName - guest and selected - returns exit guest action name`() { + val expected = "Exit guest" + context.orCreateTestableResources.addOverride( + com.android.settingslib.R.string.guest_exit_quick_settings_button, + expected, + ) + + val userRecord = + createUserRecord( + id = 2, + picture = null, + isGuest = true, + isSelected = true, + ) + + assertThat(underTest.getName(context, userRecord)).isEqualTo(expected) + } + + @Test + fun `getName - guest and not selected - returns enter guest action name`() { + val expected = "Guest" + context.orCreateTestableResources.addOverride( + com.android.internal.R.string.guest_name, + expected, + ) + + val userRecord = + createUserRecord( + id = 2, + picture = null, + isGuest = true, + isSelected = false, + ) + + assertThat(underTest.getName(context, userRecord)).isEqualTo("Guest") + } + + @Test + fun refresh() { + underTest.refresh() + + verify(controller).refreshUsers(UserHandle.USER_NULL) + } + + private fun createUserRecord( + id: Int, + picture: Bitmap? = null, + isSelected: Boolean = false, + isGuest: Boolean = false, + isAction: Boolean = false, + isRestricted: Boolean = false, + ): UserRecord { + return UserRecord( + info = + if (isAction) { + null + } else { + UserInfo(id, "name$id", 0) + }, + picture = picture, + isCurrent = isSelected, + isGuest = isGuest, + isRestricted = isRestricted, + ) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt index b4f3987b2f957..b86ca6fc53757 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt @@ -38,9 +38,9 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.Mockito.times import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest @@ -102,8 +102,7 @@ class KeyguardQsUserSwitchControllerTest : SysuiTestCase() { ViewUtils.attachView(view) testableLooper.processAllMessages() - `when`(userSwitcherController.keyguardStateController).thenReturn(keyguardStateController) - `when`(userSwitcherController.keyguardStateController.isShowing).thenReturn(true) + `when`(userSwitcherController.isKeyguardShowing).thenReturn(true) `when`(keyguardStateController.isShowing).thenReturn(true) `when`(keyguardStateController.isKeyguardGoingAway).thenReturn(false) keyguardQsUserSwitchController.init() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImplTest.kt similarity index 99% rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImplTest.kt index 8dcd4bb3b7387..76ecc1c7f36da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImplTest.kt @@ -86,7 +86,7 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest -class UserSwitcherControllerTest : SysuiTestCase() { +class UserSwitcherControllerOldImplTest : SysuiTestCase() { @Mock private lateinit var keyguardStateController: KeyguardStateController @Mock private lateinit var activityManager: IActivityManager @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController @@ -118,7 +118,7 @@ class UserSwitcherControllerTest : SysuiTestCase() { private lateinit var longRunningExecutor: FakeExecutor private lateinit var uiExecutor: FakeExecutor private lateinit var uiEventLogger: UiEventLoggerFake - private lateinit var userSwitcherController: UserSwitcherController + private lateinit var userSwitcherController: UserSwitcherControllerOldImpl private lateinit var picture: Bitmap private val ownerId = UserHandle.USER_SYSTEM private val ownerInfo = UserInfo(ownerId, "Owner", null, @@ -205,7 +205,8 @@ class UserSwitcherControllerTest : SysuiTestCase() { } private fun setupController() { - userSwitcherController = UserSwitcherController( + userSwitcherController = + UserSwitcherControllerOldImpl( mContext, activityManager, userManager, @@ -230,7 +231,8 @@ class UserSwitcherControllerTest : SysuiTestCase() { dumpManager, dialogLaunchAnimator, guestResumeSessionReceiver, - guestResetOrExitSessionReceiver) + guestResetOrExitSessionReceiver + ) userSwitcherController.init(notificationShadeWindowView) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt index 6b466e1ac2d87..6fec343d036cc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt @@ -60,7 +60,7 @@ class UserRepositoryImplTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) - whenever(controller.addUsersFromLockScreen).thenReturn(MutableStateFlow(false)) + whenever(controller.isAddUsersFromLockScreenEnabled).thenReturn(MutableStateFlow(false)) whenever(controller.isGuestUserAutoCreated).thenReturn(false) whenever(controller.isGuestUserResetting).thenReturn(false)