diff --git a/core/res/res/layout/car_user_switching_dialog.xml b/core/res/res/layout/car_user_switching_dialog.xml deleted file mode 100644 index d7274348bd161..0000000000000 --- a/core/res/res/layout/car_user_switching_dialog.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/core/res/res/values/dimens_car.xml b/core/res/res/values/dimens_car.xml index bd4c48427e0e5..2c4f4c89ab811 100644 --- a/core/res/res/values/dimens_car.xml +++ b/core/res/res/values/dimens_car.xml @@ -16,14 +16,7 @@ */ --> - 64sp - 243dp - 356dp - 96dp - 96dp 96dp - - 80dp diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 369a3e51df264..19cdbbf83ebfe 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3638,13 +3638,6 @@ - - - - - - - diff --git a/packages/CarSystemUI/AndroidManifest.xml b/packages/CarSystemUI/AndroidManifest.xml index 261b9f508ccd6..1dd02919a0931 100644 --- a/packages/CarSystemUI/AndroidManifest.xml +++ b/packages/CarSystemUI/AndroidManifest.xml @@ -25,4 +25,6 @@ + + diff --git a/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml b/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml new file mode 100644 index 0000000000000..0a294246dfaa8 --- /dev/null +++ b/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml @@ -0,0 +1,43 @@ + + + + + + + + + diff --git a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml index 35423231bb977..2dc499c160c6e 100644 --- a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml +++ b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml @@ -39,4 +39,9 @@ android:layout_height="match_parent" android:layout="@layout/car_fullscreen_user_switcher"/> + + \ No newline at end of file diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml index 3e44721848a1f..0e84d517759a3 100644 --- a/packages/CarSystemUI/res/values/colors.xml +++ b/packages/CarSystemUI/res/values/colors.xml @@ -55,4 +55,7 @@ @*android:color/car_list_divider_light @*android:color/car_list_divider @*android:color/car_card_dark + + @android:color/black + @*android:color/car_body1 diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml index 67066d7c426f1..4bf0fca445d10 100644 --- a/packages/CarSystemUI/res/values/config.xml +++ b/packages/CarSystemUI/res/values/config.xml @@ -70,11 +70,13 @@ to a constant alpha percent value using the initial alpha. --> 100 - + @string/config_notificationPanelViewMediator com.android.systemui.car.keyguard.CarKeyguardViewMediator com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator + com.android.systemui.car.userswitcher.UserSwitchTransitionViewMediator + + 32sp + 32sp + 45sp + 32sp + 24sp + 20sp + 32sp + 28sp + 26sp + 24sp + + 18sp + 26sp + 64sp + 26sp + 26sp + + 4dp + 8dp + 16dp + 24dp + 32dp + 64dp + 96dp + -420dp + + + 96dp + 96dp + @*android:dimen/car_padding_4 + @*android:dimen/car_body1_size diff --git a/packages/CarSystemUI/res/values/strings.xml b/packages/CarSystemUI/res/values/strings.xml index 9fae4b3e2b46e..67fd5bb685211 100644 --- a/packages/CarSystemUI/res/values/strings.xml +++ b/packages/CarSystemUI/res/values/strings.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - + Min @@ -34,4 +34,8 @@ When you add a new user, that person needs to set up their space. Any user can update apps for all other users. + + Loading + + Loading user (from %1$d to %2$d) diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java index 346c38ced766a..8b399f888eb32 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java @@ -34,16 +34,19 @@ public class FullscreenUserSwitcherViewMediator implements OverlayViewMediator { private final StatusBarStateController mStatusBarStateController; private final FullScreenUserSwitcherViewController mFullScreenUserSwitcherViewController; private final CarKeyguardViewController mCarKeyguardViewController; + private final UserSwitchTransitionViewController mUserSwitchTransitionViewController; @Inject public FullscreenUserSwitcherViewMediator( StatusBarStateController statusBarStateController, CarKeyguardViewController carKeyguardViewController, + UserSwitchTransitionViewController userSwitchTransitionViewController, FullScreenUserSwitcherViewController fullScreenUserSwitcherViewController) { mStatusBarStateController = statusBarStateController; - mFullScreenUserSwitcherViewController = fullScreenUserSwitcherViewController; mCarKeyguardViewController = carKeyguardViewController; + mUserSwitchTransitionViewController = userSwitchTransitionViewController; + mFullScreenUserSwitcherViewController = fullScreenUserSwitcherViewController; } @Override @@ -74,6 +77,11 @@ public class FullscreenUserSwitcherViewMediator implements OverlayViewMediator { private void onUserSelected(UserGridRecyclerView.UserRecord record) { if (record.mType != UserGridRecyclerView.UserRecord.FOREGROUND_USER) { mCarKeyguardViewController.hideKeyguardToPrepareBouncer(); + // If guest user, we cannot use record.mInfo.id and should listen to the User lifecycle + // event instead. + if (record.mType != UserGridRecyclerView.UserRecord.START_GUEST) { + mUserSwitchTransitionViewController.handleShow(record.mInfo.id); + } } hide(); diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java new file mode 100644 index 0000000000000..775ef8152ca28 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2020 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.car.userswitcher; + +import static android.car.settings.CarSettings.Global.ENABLE_USER_SWITCH_DEVELOPER_MESSAGE; + +import android.annotation.UserIdInt; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.os.Handler; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.Settings; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.internal.annotations.GuardedBy; +import com.android.settingslib.drawable.CircleFramedDrawable; +import com.android.systemui.R; +import com.android.systemui.car.window.OverlayViewController; +import com.android.systemui.car.window.OverlayViewGlobalStateController; +import com.android.systemui.dagger.qualifiers.Main; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Handles showing and hiding UserSwitchTransitionView that is mounted to SystemUiOverlayWindow. + */ +@Singleton +public class UserSwitchTransitionViewController extends OverlayViewController { + private static final String TAG = "UserSwitchTransitionViewController"; + private static final String ENABLE_DEVELOPER_MESSAGE_TRUE = "true"; + + private final Context mContext; + private final Handler mHandler; + private final Resources mResources; + private final UserManager mUserManager; + + @GuardedBy("this") + private boolean mShowing; + private int mPreviousUserId = UserHandle.USER_NULL; + + @Inject + public UserSwitchTransitionViewController( + Context context, + @Main Handler handler, + @Main Resources resources, + UserManager userManager, + OverlayViewGlobalStateController overlayViewGlobalStateController) { + + super(R.id.user_switching_dialog_stub, overlayViewGlobalStateController); + + mContext = context; + mHandler = handler; + mResources = resources; + mUserManager = userManager; + } + + /** + * Makes the user switch transition view appear and draws the content inside of it if a user + * that is different from the previous user is provided and if the dialog is not already + * showing. + */ + void handleShow(@UserIdInt int newUserId) { + if (mPreviousUserId == newUserId || mShowing) return; + mShowing = true; + mHandler.post(() -> { + start(); + populateDialog(mPreviousUserId, newUserId); + // next time a new user is selected, this current new user will be the previous user. + mPreviousUserId = newUserId; + }); + } + + void handleHide() { + if (!mShowing) return; + mShowing = false; + mHandler.post(this::stop); + } + + private void populateDialog(@UserIdInt int previousUserId, @UserIdInt int newUserId) { + drawUserIcon(newUserId); + populateLoadingText(previousUserId, newUserId); + } + + private void drawUserIcon(int newUserId) { + Bitmap bitmap = mUserManager.getUserIcon(newUserId); + if (bitmap != null) { + CircleFramedDrawable drawable = CircleFramedDrawable.getInstance(mContext, bitmap); + ((ImageView) getLayout().findViewById(R.id.user_loading_avatar)) + .setImageDrawable(drawable); + } + } + + private void populateLoadingText(@UserIdInt int previousUserId, @UserIdInt int newUserId) { + TextView msgView = getLayout().findViewById(R.id.user_loading); + + boolean showInfo = ENABLE_DEVELOPER_MESSAGE_TRUE.equals( + Settings.Global.getString(mContext.getContentResolver(), + ENABLE_USER_SWITCH_DEVELOPER_MESSAGE)); + + if (showInfo && mPreviousUserId != UserHandle.USER_NULL) { + msgView.setText( + mResources.getString(R.string.car_loading_profile_developer_message, + previousUserId, newUserId)); + } else { + msgView.setText(mResources.getString(R.string.car_loading_profile)); + } + } +} diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewMediator.java new file mode 100644 index 0000000000000..aea691443290e --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewMediator.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2020 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.car.userswitcher; + +import android.app.ActivityManager; +import android.car.Car; +import android.car.user.CarUserManager; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.car.CarServiceProvider; +import com.android.systemui.car.window.OverlayViewMediator; + +import javax.inject.Inject; + +/** + * Registers listeners that subscribe to events that show or hide CarUserSwitchingDialog that is + * mounted to SystemUiOverlayWindow. + */ +public class UserSwitchTransitionViewMediator implements OverlayViewMediator, + CarUserManager.UserSwitchUiCallback { + private static final String TAG = "UserSwitchTransitionViewMediator"; + + private final CarServiceProvider mCarServiceProvider; + private final UserSwitchTransitionViewController mUserSwitchTransitionViewController; + + @Inject + public UserSwitchTransitionViewMediator( + CarServiceProvider carServiceProvider, + UserSwitchTransitionViewController userSwitchTransitionViewController) { + mCarServiceProvider = carServiceProvider; + mUserSwitchTransitionViewController = userSwitchTransitionViewController; + } + + @Override + public void registerListeners() { + mCarServiceProvider.addListener(car -> { + CarUserManager carUserManager = + (CarUserManager) car.getCarManager(Car.CAR_USER_SERVICE); + + if (carUserManager != null) { + carUserManager.setUserSwitchUiCallback(this); + carUserManager.addListener(Runnable::run, this::handleUserLifecycleEvent); + } else { + Log.e(TAG, "registerListeners: CarUserManager could not be obtained."); + } + }); + } + + @Override + public void setupOverlayContentViewControllers() { + // no-op. + } + + @Override + public void showUserSwitchDialog(int userId) { + mUserSwitchTransitionViewController.handleShow(userId); + } + + @VisibleForTesting + void handleUserLifecycleEvent(CarUserManager.UserLifecycleEvent event) { + if (event.getEventType() == CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STARTING + && ActivityManager.getCurrentUser() == event.getUserId()) { + mUserSwitchTransitionViewController.handleShow(event.getUserId()); + } + + if (event.getEventType() == CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING) { + mUserSwitchTransitionViewController.handleHide(); + } + } +} diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewMediator.java index ac574eda4c9ff..3e7b4a2665ee8 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewMediator.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewMediator.java @@ -23,6 +23,9 @@ public interface OverlayViewMediator { /** * Register listeners that could use ContentVisibilityAdjuster to show/hide content. + * + * Note that we do not unregister listeners because SystemUI components are expected to live + * for the lifecycle of the device. */ void registerListeners(); diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java index 484aa63e8bdab..5a16efa3dd9bd 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java @@ -21,6 +21,7 @@ import com.android.systemui.car.notification.BottomNotificationPanelViewMediator import com.android.systemui.car.notification.NotificationPanelViewMediator; import com.android.systemui.car.notification.TopNotificationPanelViewMediator; import com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator; +import com.android.systemui.car.userswitcher.UserSwitchTransitionViewMediator; import dagger.Binds; import dagger.Module; @@ -67,4 +68,11 @@ public abstract class OverlayWindowModule { @ClassKey(FullscreenUserSwitcherViewMediator.class) public abstract OverlayViewMediator bindFullscreenUserSwitcherViewsMediator( FullscreenUserSwitcherViewMediator overlayViewsMediator); + + /** Injects CarUserSwitchingDialogMediator. */ + @Binds + @IntoMap + @ClassKey(UserSwitchTransitionViewMediator.class) + public abstract OverlayViewMediator bindUserSwitchTransitionViewMediator( + UserSwitchTransitionViewMediator userSwitchTransitionViewMediator); } diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java new file mode 100644 index 0000000000000..eab381c92d98d --- /dev/null +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2020 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.car.userswitcher; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.content.res.Resources; +import android.os.Handler; +import android.os.UserManager; +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.testing.TestableResources; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.car.window.OverlayViewGlobalStateController; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class UserSwitchTransitionViewControllerTest extends SysuiTestCase { + private static final int TEST_USER_1 = 100; + private static final int TEST_USER_2 = 110; + + private TestableUserSwitchTransitionViewController mCarUserSwitchingDialogController; + private TestableResources mTestableResources; + @Mock + private OverlayViewGlobalStateController mOverlayViewGlobalStateController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mTestableResources = mContext.getOrCreateTestableResources(); + mCarUserSwitchingDialogController = new TestableUserSwitchTransitionViewController( + mContext, + Handler.getMain(), + mTestableResources.getResources(), + (UserManager) mContext.getSystemService(Context.USER_SERVICE), + mOverlayViewGlobalStateController + ); + + mCarUserSwitchingDialogController.inflate((ViewGroup) LayoutInflater.from(mContext).inflate( + R.layout.sysui_overlay_window, /* root= */ null)); + } + + @Test + public void onHandleShow_newUserSelected_showsDialog() { + mCarUserSwitchingDialogController.handleShow(/* currentUserId= */ TEST_USER_1); + + verify(mOverlayViewGlobalStateController).showView(eq(mCarUserSwitchingDialogController), + any()); + } + + @Test + public void onHandleShow_alreadyShowing_ignoresRequest() { + mCarUserSwitchingDialogController.handleShow(/* currentUserId= */ TEST_USER_1); + mCarUserSwitchingDialogController.handleShow(/* currentUserId= */ TEST_USER_2); + + // Verify that the request was processed only once. + verify(mOverlayViewGlobalStateController).showView(eq(mCarUserSwitchingDialogController), + any()); + } + + @Test + public void onHandleShow_sameUserSelected_ignoresRequest() { + mCarUserSwitchingDialogController.handleShow(/* currentUserId= */ TEST_USER_1); + mCarUserSwitchingDialogController.handleHide(); + mCarUserSwitchingDialogController.handleShow(/* currentUserId= */ TEST_USER_1); + + // Verify that the request was processed only once. + verify(mOverlayViewGlobalStateController).showView(eq(mCarUserSwitchingDialogController), + any()); + } + + @Test + public void onHide_currentlyShowing_hidesDialog() { + mCarUserSwitchingDialogController.handleShow(/* currentUserId= */ TEST_USER_1); + mCarUserSwitchingDialogController.handleHide(); + + verify(mOverlayViewGlobalStateController).hideView(eq(mCarUserSwitchingDialogController), + any()); + } + + @Test + public void onHide_notShowing_ignoresRequest() { + mCarUserSwitchingDialogController.handleShow(/* currentUserId= */ TEST_USER_1); + mCarUserSwitchingDialogController.handleHide(); + mCarUserSwitchingDialogController.handleHide(); + + // Verify that the request was processed only once. + verify(mOverlayViewGlobalStateController).hideView(eq(mCarUserSwitchingDialogController), + any()); + } + + private final class TestableUserSwitchTransitionViewController extends + UserSwitchTransitionViewController { + + private final Handler mHandler; + + TestableUserSwitchTransitionViewController(Context context, Handler handler, + Resources resources, UserManager userManager, + OverlayViewGlobalStateController overlayViewGlobalStateController) { + super(context, handler, resources, userManager, overlayViewGlobalStateController); + mHandler = handler; + } + + @Override + public void handleShow(int currentUserId) { + super.handleShow(currentUserId); + waitForIdleSync(mHandler); + } + + @Override + public void handleHide() { + super.handleHide(); + waitForIdleSync(mHandler); + } + } +} diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewMediatorTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewMediatorTest.java new file mode 100644 index 0000000000000..a808e2d40e26e --- /dev/null +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewMediatorTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2020 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.car.userswitcher; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.car.user.CarUserManager; +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import com.android.systemui.car.CarServiceProvider; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class UserSwitchTransitionViewMediatorTest { + private static final int TEST_USER = 100; + + private UserSwitchTransitionViewMediator mUserSwitchTransitionViewMediator; + @Mock + private CarServiceProvider mCarServiceProvider; + @Mock + private UserSwitchTransitionViewController mUserSwitchTransitionViewController; + @Mock + private CarUserManager.UserLifecycleEvent mUserLifecycleEvent; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mUserSwitchTransitionViewMediator = new UserSwitchTransitionViewMediator( + mCarServiceProvider, mUserSwitchTransitionViewController); + + } + + @Test + public void onUserLifecycleEvent_userStarting_callsHandleShow() { + when(mUserLifecycleEvent.getEventType()).thenReturn( + CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STARTING); + when(mUserLifecycleEvent.getUserId()).thenReturn(TEST_USER); + mUserSwitchTransitionViewMediator.handleUserLifecycleEvent(mUserLifecycleEvent); + + verify(mUserSwitchTransitionViewController).handleShow(TEST_USER); + } + + @Test + public void onUserLifecycleEvent_userSwitching_callsHandleHide() { + when(mUserLifecycleEvent.getEventType()).thenReturn( + CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING); + mUserSwitchTransitionViewMediator.handleUserLifecycleEvent(mUserLifecycleEvent); + + verify(mUserSwitchTransitionViewController).handleHide(); + } +} diff --git a/services/core/java/com/android/server/am/CarUserSwitchingDialog.java b/services/core/java/com/android/server/am/CarUserSwitchingDialog.java deleted file mode 100644 index 0e34801319522..0000000000000 --- a/services/core/java/com/android/server/am/CarUserSwitchingDialog.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) 2018 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.server.am; - -import android.content.Context; -import android.content.pm.UserInfo; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.ColorFilter; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.PixelFormat; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.drawable.Drawable; -import android.os.UserManager; -import android.provider.Settings; -import android.view.LayoutInflater; -import android.view.View; -import android.view.WindowInsets; -import android.widget.ImageView; -import android.widget.TextView; - -import com.android.internal.R; - - -/** - * Dialog to show when a user switch it about to happen for the car. The intent is to snapshot the - * screen immediately after the dialog shows so that the user is informed that something is - * happening in the background rather than just freeze the screen and not know if the user-switch - * affordance was being handled. - */ -final class CarUserSwitchingDialog extends UserSwitchingDialog { - - private static final String TAG = "ActivityManagerCarUserSwitchingDialog"; - private View mView; - - public CarUserSwitchingDialog(ActivityManagerService service, Context context, UserInfo oldUser, - UserInfo newUser, boolean aboveSystem, String switchingFromSystemUserMessage, - String switchingToSystemUserMessage) { - super(service, context, oldUser, newUser, aboveSystem, switchingFromSystemUserMessage, - switchingToSystemUserMessage); - } - - @Override - void inflateContent() { - // Set up the dialog contents - setCancelable(false); - Resources res = getContext().getResources(); - // Custom view due to alignment and font size requirements - getContext().setTheme(R.style.Theme_DeviceDefault_Light_Dialog_Alert_UserSwitchingDialog); - mView = LayoutInflater.from(getContext()).inflate( - R.layout.car_user_switching_dialog, - null); - - UserManager userManager = - (UserManager) getContext().getSystemService(Context.USER_SERVICE); - Bitmap bitmap = userManager.getUserIcon(mNewUser.id); - if (bitmap != null) { - CircleFramedDrawable drawable = CircleFramedDrawable.getInstance(bitmap, - res.getDimension(R.dimen.car_fullscreen_user_pod_image_avatar_height)); - ((ImageView) mView.findViewById(R.id.user_loading_avatar)) - .setImageDrawable(drawable); - } - - TextView msgView = mView.findViewById(R.id.user_loading); - - // TODO(b/145132885): use constant from CarSettings - boolean showInfo = "true".equals(Settings.Global.getString( - getContext().getContentResolver(), - "android.car.ENABLE_USER_SWITCH_DEVELOPER_MESSAGE")); - - if (showInfo) { - msgView.setText(res.getString(R.string.car_loading_profile) + " user\n(from " - + mOldUser.id + " to " + mNewUser.id + ")"); - } else { - msgView.setText(res.getString(R.string.car_loading_profile)); - } - setView(mView); - } - - @Override - public void show() { - super.show(); - hideNavigationBar(); - } - - private void hideNavigationBar() { - mView.getWindowInsetsController().hide(WindowInsets.Type.navigationBars()); - } - - /** - * Converts the user icon to a circularly clipped one. This is used in the User Picker and - * Settings. - */ - static class CircleFramedDrawable extends Drawable { - - private final Bitmap mBitmap; - private final int mSize; - private final Paint mPaint; - - private float mScale; - private Rect mSrcRect; - private RectF mDstRect; - - public static CircleFramedDrawable getInstance(Bitmap icon, float iconSize) { - CircleFramedDrawable instance = new CircleFramedDrawable(icon, (int) iconSize); - return instance; - } - - public CircleFramedDrawable(Bitmap icon, int size) { - super(); - mSize = size; - - mBitmap = Bitmap.createBitmap(mSize, mSize, Bitmap.Config.ARGB_8888); - final Canvas canvas = new Canvas(mBitmap); - - final int width = icon.getWidth(); - final int height = icon.getHeight(); - final int square = Math.min(width, height); - - final Rect cropRect = new Rect((width - square) / 2, (height - square) / 2, - square, square); - final RectF circleRect = new RectF(0f, 0f, mSize, mSize); - - final Path fillPath = new Path(); - fillPath.addArc(circleRect, 0f, 360f); - - canvas.drawColor(0, PorterDuff.Mode.CLEAR); - - // opaque circle - mPaint = new Paint(); - mPaint.setAntiAlias(true); - mPaint.setColor(Color.BLACK); - mPaint.setStyle(Paint.Style.FILL); - canvas.drawPath(fillPath, mPaint); - - // mask in the icon where the bitmap is opaque - mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); - canvas.drawBitmap(icon, cropRect, circleRect, mPaint); - - // prepare paint for frame drawing - mPaint.setXfermode(null); - - mScale = 1f; - - mSrcRect = new Rect(0, 0, mSize, mSize); - mDstRect = new RectF(0, 0, mSize, mSize); - } - - @Override - public void draw(Canvas canvas) { - final float inside = mScale * mSize; - final float pad = (mSize - inside) / 2f; - - mDstRect.set(pad, pad, mSize - pad, mSize - pad); - canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, null); - } - - @Override - public int getOpacity() { - return PixelFormat.TRANSLUCENT; - } - - @Override - public void setAlpha(int alpha) { - // Needed to implement abstract method. Do nothing. - } - - @Override - public void setColorFilter(ColorFilter colorFilter) { - // Needed to implement abstract method. Do nothing. - } - } -} diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 5b12933f7a406..fac4a1e95827a 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -2736,19 +2736,13 @@ class UserController implements Handler.Callback { void showUserSwitchingDialog(UserInfo fromUser, UserInfo toUser, String switchingFromSystemUserMessage, String switchingToSystemUserMessage) { - Dialog d; if (!mService.mContext.getPackageManager() .hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { - d = new UserSwitchingDialog(mService, mService.mContext, fromUser, toUser, - true /* above system */, switchingFromSystemUserMessage, - switchingToSystemUserMessage); - } else { - d = new CarUserSwitchingDialog(mService, mService.mContext, fromUser, toUser, - true /* above system */, switchingFromSystemUserMessage, - switchingToSystemUserMessage); + final Dialog d = new UserSwitchingDialog(mService, mService.mContext, fromUser, + toUser, true /* above system */, switchingFromSystemUserMessage, + switchingToSystemUserMessage); + d.show(); } - - d.show(); } void reportGlobalUsageEventLocked(int event) {