diff options
19 files changed, 549 insertions, 257 deletions
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 d7274348bd16..000000000000 --- a/core/res/res/layout/car_user_switching_dialog.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - 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. ---> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:fitsSystemWindows="true" - android:layout_width="wrap_content" - android:layout_height="wrap_content"> - - <ImageView - android:id="@+id/user_loading_avatar" - android:layout_width="@dimen/car_fullscreen_user_pod_image_avatar_width" - android:layout_height="@dimen/car_fullscreen_user_pod_image_avatar_height" - android:layout_centerHorizontal="true"/> - - <TextView android:id="@+id/user_loading" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/car_padding_4" - android:textSize="@dimen/car_body1_size" - android:textColor="@color/car_body1" - android:layout_below="@id/user_loading_avatar" - android:gravity="center"/> - -</RelativeLayout>
\ 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 bd4c48427e0e..2c4f4c89ab81 100644 --- a/core/res/res/values/dimens_car.xml +++ b/core/res/res/values/dimens_car.xml @@ -16,14 +16,7 @@ */ --> <resources> - <dimen name="car_fullscreen_user_pod_icon_text_size">64sp</dimen> - <dimen name="car_fullscreen_user_pod_width">243dp</dimen> - <dimen name="car_fullscreen_user_pod_height">356dp</dimen> - <dimen name="car_fullscreen_user_pod_image_avatar_width">96dp</dimen> - <dimen name="car_fullscreen_user_pod_image_avatar_height">96dp</dimen> <dimen name="car_large_avatar_size">96dp</dimen> - - <!-- Application Bar --> <dimen name="car_app_bar_height">80dp</dimen> <!-- Margin --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 369a3e51df26..19cdbbf83ebf 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3638,13 +3638,6 @@ <java-symbol type="color" name="car_card_dark" /> <java-symbol type="dimen" name="car_body1_size" /> <java-symbol type="dimen" name="car_padding_4" /> - <java-symbol type="dimen" name="car_fullscreen_user_pod_icon_text_size" /> - <java-symbol type="dimen" name="car_fullscreen_user_pod_image_avatar_height" /> - <java-symbol type="dimen" name="car_fullscreen_user_pod_image_avatar_width" /> - <java-symbol type="dimen" name="car_large_avatar_size" /> - <java-symbol type="layout" name="car_user_switching_dialog" /> - <java-symbol type="id" name="user_loading_avatar" /> - <java-symbol type="id" name="user_loading" /> <java-symbol type="style" name="Theme.DeviceDefault.Light.Dialog.Alert.UserSwitchingDialog" /> <java-symbol type="string" name="battery_saver_description_with_learn_more" /> diff --git a/packages/CarSystemUI/AndroidManifest.xml b/packages/CarSystemUI/AndroidManifest.xml index 261b9f508ccd..1dd02919a093 100644 --- a/packages/CarSystemUI/AndroidManifest.xml +++ b/packages/CarSystemUI/AndroidManifest.xml @@ -25,4 +25,6 @@ <uses-permission android:name="android.car.permission.CAR_ENROLL_TRUST"/> <!-- This permission is required to get bluetooth broadcast. --> <uses-permission android:name="android.permission.BLUETOOTH" /> + <!-- This permission is required to check the foreground user id. --> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> </manifest> 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 000000000000..0a294246dfaa --- /dev/null +++ b/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:fitsSystemWindows="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + android:background="@color/car_user_switching_dialog_background_color"> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_gravity="center" + android:gravity="center_horizontal"> + <ImageView + android:id="@+id/user_loading_avatar" + android:layout_width="@dimen/car_fullscreen_user_pod_image_avatar_width" + android:layout_height="@dimen/car_fullscreen_user_pod_image_avatar_height"/> + + <TextView + android:id="@+id/user_loading" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/car_user_switching_dialog_loading_text_margin_top" + android:textSize="@dimen/car_user_switching_dialog_loading_text_font_size" + android:textColor="@color/car_user_switching_dialog_loading_text_color" + android:layout_below="@id/user_loading_avatar"/> + </LinearLayout> +</FrameLayout> diff --git a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml index 35423231bb97..2dc499c160c6 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"/> + <ViewStub android:id="@+id/user_switching_dialog_stub" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout="@layout/car_user_switching_dialog"/> + </FrameLayout>
\ No newline at end of file diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml index 3e44721848a1..0e84d517759a 100644 --- a/packages/CarSystemUI/res/values/colors.xml +++ b/packages/CarSystemUI/res/values/colors.xml @@ -55,4 +55,7 @@ <color name="list_divider_color">@*android:color/car_list_divider_light</color> <color name="car_volume_item_divider_color">@*android:color/car_list_divider</color> <color name="car_volume_item_background_color">@*android:color/car_card_dark</color> + + <color name="car_user_switching_dialog_background_color">@android:color/black</color> + <color name="car_user_switching_dialog_loading_text_color">@*android:color/car_body1</color> </resources> diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml index 67066d7c426f..4bf0fca445d1 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. --> <integer name="config_finalNotificationBackgroundAlpha">100</integer> - <!-- Car System UI's OverlayViewsMediator--> + <!-- Car System UI's OverlayViewsMediator. + Whenever a new class is added, make sure to also add that class to OverlayWindowModule. --> <string-array name="config_carSystemUIOverlayViewsMediators" translatable="false"> <item>@string/config_notificationPanelViewMediator</item> <item>com.android.systemui.car.keyguard.CarKeyguardViewMediator</item> <item>com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator</item> + <item>com.android.systemui.car.userswitcher.UserSwitchTransitionViewMediator</item> </string-array> <!-- diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml index 9014eb15d6cf..ed0b4853994d 100644 --- a/packages/CarSystemUI/res/values/dimens.xml +++ b/packages/CarSystemUI/res/values/dimens.xml @@ -15,6 +15,32 @@ ~ limitations under the License --> <resources> + <!-- Text size for car --> + <dimen name="car_title_size">32sp</dimen> + <dimen name="car_title2_size">32sp</dimen> + <dimen name="car_headline1_size">45sp</dimen> + <dimen name="car_headline2_size">32sp</dimen> + <dimen name="car_headline3_size">24sp</dimen> + <dimen name="car_headline4_size">20sp</dimen> + <dimen name="car_body1_size">32sp</dimen> + <dimen name="car_body2_size">28sp</dimen> + <dimen name="car_body3_size">26sp</dimen> + <dimen name="car_body4_size">24sp</dimen> + <!-- car_body5_size is deprecated --> + <dimen name="car_body5_size">18sp</dimen> + <dimen name="car_label1_size">26sp</dimen> + <dimen name="car_label2_size">64sp</dimen> + <dimen name="car_action1_size">26sp</dimen> + <dimen name="car_action2_size">26sp</dimen> + <!-- Paddings --> + <dimen name="car_padding_0">4dp</dimen> + <dimen name="car_padding_1">8dp</dimen> + <dimen name="car_padding_2">16dp</dimen> + <dimen name="car_padding_3">24dp</dimen> + <dimen name="car_padding_4">32dp</dimen> + <dimen name="car_padding_5">64dp</dimen> + <dimen name="car_padding_6">96dp</dimen> + <!-- Note: status bar height and navigation bar heights are defined in frameworks/base/core package and thus will have no effect if @@ -156,4 +182,10 @@ <dimen name="car_user_switcher_container_height">420dp</dimen> <!-- This must be the negative of car_user_switcher_container_height for the animation. --> <dimen name="car_user_switcher_container_anim_height">-420dp</dimen> + + <!-- dimensions for car user switching dialog --> + <dimen name="car_fullscreen_user_pod_image_avatar_width">96dp</dimen> + <dimen name="car_fullscreen_user_pod_image_avatar_height">96dp</dimen> + <dimen name="car_user_switching_dialog_loading_text_margin_top">@*android:dimen/car_padding_4</dimen> + <dimen name="car_user_switching_dialog_loading_text_font_size">@*android:dimen/car_body1_size</dimen> </resources> diff --git a/packages/CarSystemUI/res/values/strings.xml b/packages/CarSystemUI/res/values/strings.xml index 9fae4b3e2b46..67fd5bb68521 100644 --- a/packages/CarSystemUI/res/values/strings.xml +++ b/packages/CarSystemUI/res/values/strings.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- String to represent lowest setting of an HVAC system [CHAR LIMIT=10]--> <string name="hvac_min_text">Min</string> <!-- String to represent largest setting of an HVAC system [CHAR LIMIT=10]--> @@ -34,4 +34,8 @@ <string name="user_add_user_message_setup">When you add a new user, that person needs to set up their space.</string> <!-- Message to inform user that the newly created user will have permissions to update apps for all other users. [CHAR LIMIT=100] --> <string name="user_add_user_message_update">Any user can update apps for all other users.</string> + <!-- Message to inform user that the new user profile is loading. [CHAR LIMIT=20] --> + <string name="car_loading_profile">Loading</string> + <!-- Message to inform user that the new user profile is loading with additional information on the previous and the next user. [CHAR LIMIT=100] --> + <string name="car_loading_profile_developer_message">Loading user (from <xliff:g id="from_user" example="10">%1$d</xliff:g> to <xliff:g id="to_user" example="12">%2$d</xliff:g>)</string> </resources> 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 346c38ced766..8b399f888eb3 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 000000000000..775ef8152ca2 --- /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 000000000000..aea691443290 --- /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 ac574eda4c9f..3e7b4a2665ee 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 484aa63e8bda..5a16efa3dd9b 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 000000000000..eab381c92d98 --- /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 000000000000..a808e2d40e26 --- /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 0e3480131952..000000000000 --- 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 5b12933f7a40..fac4a1e95827 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) { |