Loading packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +10 −9 Original line number Diff line number Diff line Loading @@ -62,7 +62,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.settingslib.utils.ThreadUtils; import com.android.systemui.Dumpable; import com.android.systemui.GuestResetOrExitSessionReceiver; import com.android.systemui.GuestResumeSessionReceiver; Loading Loading @@ -939,8 +938,9 @@ public class UserSwitcherController implements Dumpable { guestCreationProgressDialog.show(); // userManager.createGuest will block the thread so post is needed for the dialog to show ThreadUtils.postOnMainThread(() -> { mBgExecutor.execute(() -> { final int guestId = createGuest(); mUiExecutor.execute(() -> { guestCreationProgressDialog.dismiss(); if (guestId == UserHandle.USER_NULL) { Toast.makeText(mContext, Loading @@ -949,6 +949,7 @@ public class UserSwitcherController implements Dumpable { } callback.accept(guestId); }); }); } /** Loading packages/SystemUI/src/com/android/systemui/user/UserCreator.java→packages/SystemUI/src/com/android/systemui/user/UserCreator.kt +81 −0 Original line number Diff line number Diff line Loading @@ -13,39 +13,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.user package com.android.systemui.user; import android.app.Dialog; import android.content.Context; import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.UserManager; import com.android.internal.util.UserIcons; import com.android.settingslib.users.UserCreatingDialog; import com.android.settingslib.utils.ThreadUtils; import java.util.function.Consumer; import javax.inject.Inject; import android.app.Dialog import android.content.Context import android.content.pm.UserInfo import android.graphics.drawable.Drawable import android.os.UserManager import com.android.internal.util.UserIcons import com.android.settingslib.users.UserCreatingDialog import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import java.util.concurrent.Executor import java.util.function.Consumer import javax.inject.Inject /** * A class to do the user creation process. It shows a progress dialog, and manages the user * creation */ public class UserCreator { private final Context mContext; private final UserManager mUserManager; @Inject public UserCreator(Context context, UserManager userManager) { mContext = context; mUserManager = userManager; } class UserCreator @Inject constructor( private val context: Context, private val userManager: UserManager, @Main private val mainExecutor: Executor, @Background private val bgExecutor: Executor ) { /** * Shows a progress dialog then starts the user creation process on the main thread. * Loading @@ -53,33 +45,37 @@ public class UserCreator { * @param errorCallback is called when userManager.createUser returns null. * (Exceptions are not handled by this class) */ public void createUser(String userName, Drawable userIcon, Consumer<UserInfo> successCallback, Runnable errorCallback) { Dialog userCreationProgressDialog = new UserCreatingDialog(mContext); userCreationProgressDialog.show(); fun createUser( userName: String?, userIcon: Drawable?, successCallback: Consumer<UserInfo?>, errorCallback: Runnable ) { val userCreationProgressDialog: Dialog = UserCreatingDialog(context) userCreationProgressDialog.show() // userManager.createUser will block the thread so post is needed for the dialog to show ThreadUtils.postOnMainThread(() -> { UserInfo user = mUserManager.createUser(userName, UserManager.USER_TYPE_FULL_SECONDARY, 0); bgExecutor.execute { val user = userManager.createUser(userName, UserManager.USER_TYPE_FULL_SECONDARY, 0) mainExecutor.execute main@{ if (user == null) { // Couldn't create user for some reason userCreationProgressDialog.dismiss(); errorCallback.run(); return; userCreationProgressDialog.dismiss() errorCallback.run() return@main } Drawable newUserIcon = userIcon; Resources res = mContext.getResources(); bgExecutor.execute { var newUserIcon = userIcon val res = context.resources if (newUserIcon == null) { newUserIcon = UserIcons.getDefaultUserIcon(res, user.id, false); newUserIcon = UserIcons.getDefaultUserIcon(res, user.id, false) } userManager.setUserIcon( user.id, UserIcons.convertToBitmapAtUserIconSize(res, newUserIcon)) } userCreationProgressDialog.dismiss() successCallback.accept(user) } } mUserManager.setUserIcon( user.id, UserIcons.convertToBitmapAtUserIconSize(res, newUserIcon)); userCreationProgressDialog.dismiss(); successCallback.accept(user); }); } } packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt +25 −0 Original line number Diff line number Diff line Loading @@ -77,6 +77,7 @@ import org.mockito.Mockito.doNothing import org.mockito.Mockito.doReturn import org.mockito.Mockito.eq import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations Loading Loading @@ -269,6 +270,8 @@ class UserSwitcherControllerTest : SysuiTestCase() { `when`(userManager.createGuest(any())).thenReturn(guestInfo) userSwitcherController.onUserListItemClicked(emptyGuestUserRecord, null) bgExecutor.runAllReady() uiExecutor.runAllReady() testableLooper.processAllMessages() verify(interactionJankMonitor).begin(any()) verify(latencyTracker).onActionStart(LatencyTracker.ACTION_USER_SWITCH) Loading @@ -294,6 +297,8 @@ class UserSwitcherControllerTest : SysuiTestCase() { `when`(userManager.createGuest(any())).thenReturn(guestInfo) userSwitcherController.onUserListItemClicked(emptyGuestUserRecord, dialogShower) bgExecutor.runAllReady() uiExecutor.runAllReady() testableLooper.processAllMessages() verify(dialogShower).dismiss() } Loading Loading @@ -584,4 +589,24 @@ class UserSwitcherControllerTest : SysuiTestCase() { broadcastReceiverCaptor.value.onReceive(context, intent) verify(cb).onUserSwitched() } @Test fun onUserItemClicked_guest_runsOnBgThread() { val dialogShower = mock(UserSwitchDialogController.DialogShower::class.java) val guestUserRecord = UserSwitcherController.UserRecord( null, picture, true /* guest */, false /* current */, false /* isAddUser */, false /* isRestricted */, true /* isSwitchToEnabled */, false /* isAddSupervisedUser */) userSwitcherController.onUserListItemClicked(guestUserRecord, dialogShower) assertTrue(bgExecutor.numPending() > 0) verify(userManager, never()).createGuest(context) bgExecutor.runAllReady() verify(userManager).createGuest(context) } } packages/SystemUI/tests/src/com/android/systemui/user/UserCreatorTest.kt 0 → 100644 +73 −0 Original line number Diff line number Diff line package com.android.systemui.user import android.content.pm.UserInfo import android.graphics.Bitmap import android.os.UserManager import android.test.suitebuilder.annotation.SmallTest import android.testing.AndroidTestingRunner import android.testing.TestableLooper import com.android.systemui.SysuiTestCase import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.time.FakeSystemClock import java.util.function.Consumer import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class UserCreatorTest : SysuiTestCase() { companion object { const val USER_NAME = "abc" } @Mock private lateinit var userCreator: UserCreator @Mock private lateinit var userManager: UserManager private lateinit var mainExecutor: FakeExecutor private lateinit var bgExecutor: FakeExecutor private lateinit var user: UserInfo @Before fun setUp() { MockitoAnnotations.initMocks(this) mainExecutor = FakeExecutor(FakeSystemClock()) bgExecutor = FakeExecutor(FakeSystemClock()) userCreator = UserCreator(context, userManager, mainExecutor, bgExecutor) user = Mockito.mock(UserInfo::class.java) Mockito.`when`(userManager.createUser(USER_NAME, UserManager.USER_TYPE_FULL_SECONDARY, 0)) .thenReturn(user) } @Test fun testCreateUser_threadingOrder() { val successCallback = Mockito.mock(Consumer::class.java) val errorCallback = Mockito.mock(Runnable::class.java) userCreator.createUser( USER_NAME, null, successCallback as Consumer<UserInfo?>, errorCallback) verify(userManager, never()).createUser(USER_NAME, UserManager.USER_TYPE_FULL_SECONDARY, 0) bgExecutor.runAllReady() verify(successCallback, never()).accept(user) mainExecutor.runAllReady() verify(userManager, never()).setUserIcon(anyInt(), any(Bitmap::class.java)) bgExecutor.runAllReady() verify(userManager).createUser(USER_NAME, UserManager.USER_TYPE_FULL_SECONDARY, 0) verify(userManager).setUserIcon(anyInt(), any(Bitmap::class.java)) verify(successCallback).accept(user) } } Loading
packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +10 −9 Original line number Diff line number Diff line Loading @@ -62,7 +62,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.settingslib.utils.ThreadUtils; import com.android.systemui.Dumpable; import com.android.systemui.GuestResetOrExitSessionReceiver; import com.android.systemui.GuestResumeSessionReceiver; Loading Loading @@ -939,8 +938,9 @@ public class UserSwitcherController implements Dumpable { guestCreationProgressDialog.show(); // userManager.createGuest will block the thread so post is needed for the dialog to show ThreadUtils.postOnMainThread(() -> { mBgExecutor.execute(() -> { final int guestId = createGuest(); mUiExecutor.execute(() -> { guestCreationProgressDialog.dismiss(); if (guestId == UserHandle.USER_NULL) { Toast.makeText(mContext, Loading @@ -949,6 +949,7 @@ public class UserSwitcherController implements Dumpable { } callback.accept(guestId); }); }); } /** Loading
packages/SystemUI/src/com/android/systemui/user/UserCreator.java→packages/SystemUI/src/com/android/systemui/user/UserCreator.kt +81 −0 Original line number Diff line number Diff line Loading @@ -13,39 +13,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.user package com.android.systemui.user; import android.app.Dialog; import android.content.Context; import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.UserManager; import com.android.internal.util.UserIcons; import com.android.settingslib.users.UserCreatingDialog; import com.android.settingslib.utils.ThreadUtils; import java.util.function.Consumer; import javax.inject.Inject; import android.app.Dialog import android.content.Context import android.content.pm.UserInfo import android.graphics.drawable.Drawable import android.os.UserManager import com.android.internal.util.UserIcons import com.android.settingslib.users.UserCreatingDialog import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import java.util.concurrent.Executor import java.util.function.Consumer import javax.inject.Inject /** * A class to do the user creation process. It shows a progress dialog, and manages the user * creation */ public class UserCreator { private final Context mContext; private final UserManager mUserManager; @Inject public UserCreator(Context context, UserManager userManager) { mContext = context; mUserManager = userManager; } class UserCreator @Inject constructor( private val context: Context, private val userManager: UserManager, @Main private val mainExecutor: Executor, @Background private val bgExecutor: Executor ) { /** * Shows a progress dialog then starts the user creation process on the main thread. * Loading @@ -53,33 +45,37 @@ public class UserCreator { * @param errorCallback is called when userManager.createUser returns null. * (Exceptions are not handled by this class) */ public void createUser(String userName, Drawable userIcon, Consumer<UserInfo> successCallback, Runnable errorCallback) { Dialog userCreationProgressDialog = new UserCreatingDialog(mContext); userCreationProgressDialog.show(); fun createUser( userName: String?, userIcon: Drawable?, successCallback: Consumer<UserInfo?>, errorCallback: Runnable ) { val userCreationProgressDialog: Dialog = UserCreatingDialog(context) userCreationProgressDialog.show() // userManager.createUser will block the thread so post is needed for the dialog to show ThreadUtils.postOnMainThread(() -> { UserInfo user = mUserManager.createUser(userName, UserManager.USER_TYPE_FULL_SECONDARY, 0); bgExecutor.execute { val user = userManager.createUser(userName, UserManager.USER_TYPE_FULL_SECONDARY, 0) mainExecutor.execute main@{ if (user == null) { // Couldn't create user for some reason userCreationProgressDialog.dismiss(); errorCallback.run(); return; userCreationProgressDialog.dismiss() errorCallback.run() return@main } Drawable newUserIcon = userIcon; Resources res = mContext.getResources(); bgExecutor.execute { var newUserIcon = userIcon val res = context.resources if (newUserIcon == null) { newUserIcon = UserIcons.getDefaultUserIcon(res, user.id, false); newUserIcon = UserIcons.getDefaultUserIcon(res, user.id, false) } userManager.setUserIcon( user.id, UserIcons.convertToBitmapAtUserIconSize(res, newUserIcon)) } userCreationProgressDialog.dismiss() successCallback.accept(user) } } mUserManager.setUserIcon( user.id, UserIcons.convertToBitmapAtUserIconSize(res, newUserIcon)); userCreationProgressDialog.dismiss(); successCallback.accept(user); }); } }
packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt +25 −0 Original line number Diff line number Diff line Loading @@ -77,6 +77,7 @@ import org.mockito.Mockito.doNothing import org.mockito.Mockito.doReturn import org.mockito.Mockito.eq import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations Loading Loading @@ -269,6 +270,8 @@ class UserSwitcherControllerTest : SysuiTestCase() { `when`(userManager.createGuest(any())).thenReturn(guestInfo) userSwitcherController.onUserListItemClicked(emptyGuestUserRecord, null) bgExecutor.runAllReady() uiExecutor.runAllReady() testableLooper.processAllMessages() verify(interactionJankMonitor).begin(any()) verify(latencyTracker).onActionStart(LatencyTracker.ACTION_USER_SWITCH) Loading @@ -294,6 +297,8 @@ class UserSwitcherControllerTest : SysuiTestCase() { `when`(userManager.createGuest(any())).thenReturn(guestInfo) userSwitcherController.onUserListItemClicked(emptyGuestUserRecord, dialogShower) bgExecutor.runAllReady() uiExecutor.runAllReady() testableLooper.processAllMessages() verify(dialogShower).dismiss() } Loading Loading @@ -584,4 +589,24 @@ class UserSwitcherControllerTest : SysuiTestCase() { broadcastReceiverCaptor.value.onReceive(context, intent) verify(cb).onUserSwitched() } @Test fun onUserItemClicked_guest_runsOnBgThread() { val dialogShower = mock(UserSwitchDialogController.DialogShower::class.java) val guestUserRecord = UserSwitcherController.UserRecord( null, picture, true /* guest */, false /* current */, false /* isAddUser */, false /* isRestricted */, true /* isSwitchToEnabled */, false /* isAddSupervisedUser */) userSwitcherController.onUserListItemClicked(guestUserRecord, dialogShower) assertTrue(bgExecutor.numPending() > 0) verify(userManager, never()).createGuest(context) bgExecutor.runAllReady() verify(userManager).createGuest(context) } }
packages/SystemUI/tests/src/com/android/systemui/user/UserCreatorTest.kt 0 → 100644 +73 −0 Original line number Diff line number Diff line package com.android.systemui.user import android.content.pm.UserInfo import android.graphics.Bitmap import android.os.UserManager import android.test.suitebuilder.annotation.SmallTest import android.testing.AndroidTestingRunner import android.testing.TestableLooper import com.android.systemui.SysuiTestCase import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.time.FakeSystemClock import java.util.function.Consumer import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class UserCreatorTest : SysuiTestCase() { companion object { const val USER_NAME = "abc" } @Mock private lateinit var userCreator: UserCreator @Mock private lateinit var userManager: UserManager private lateinit var mainExecutor: FakeExecutor private lateinit var bgExecutor: FakeExecutor private lateinit var user: UserInfo @Before fun setUp() { MockitoAnnotations.initMocks(this) mainExecutor = FakeExecutor(FakeSystemClock()) bgExecutor = FakeExecutor(FakeSystemClock()) userCreator = UserCreator(context, userManager, mainExecutor, bgExecutor) user = Mockito.mock(UserInfo::class.java) Mockito.`when`(userManager.createUser(USER_NAME, UserManager.USER_TYPE_FULL_SECONDARY, 0)) .thenReturn(user) } @Test fun testCreateUser_threadingOrder() { val successCallback = Mockito.mock(Consumer::class.java) val errorCallback = Mockito.mock(Runnable::class.java) userCreator.createUser( USER_NAME, null, successCallback as Consumer<UserInfo?>, errorCallback) verify(userManager, never()).createUser(USER_NAME, UserManager.USER_TYPE_FULL_SECONDARY, 0) bgExecutor.runAllReady() verify(successCallback, never()).accept(user) mainExecutor.runAllReady() verify(userManager, never()).setUserIcon(anyInt(), any(Bitmap::class.java)) bgExecutor.runAllReady() verify(userManager).createUser(USER_NAME, UserManager.USER_TYPE_FULL_SECONDARY, 0) verify(userManager).setUserIcon(anyInt(), any(Bitmap::class.java)) verify(successCallback).accept(user) } }