Implement UserSwitchingDialog in SystemUI mounted to OverlayWindow.
Bug: 145132885 Test: Manual + Unit Tests Change-Id: I9eab89ddd54f9484e9bd79fc867c09ef4a17fe46
This commit is contained in:
@@ -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>
|
||||
@@ -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 -->
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
<!--
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user