Merge "Use abstractions to allow FullscreenUserSwitcher take advantage of SystemUIOverlayWindow." into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
995e2de829
@@ -16,6 +16,7 @@
|
|||||||
-->
|
-->
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/fullscreen_user_switcher"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:fitsSystemWindows="true">
|
android:fitsSystemWindows="true">
|
||||||
@@ -24,22 +25,26 @@
|
|||||||
android:id="@+id/container"
|
android:id="@+id/container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<include
|
<!-- TODO(b/150302361): Status bar is commented out since a top inset is being added which causes it to be displayed below the top of the screen. -->
|
||||||
layout="@layout/car_status_bar_header"
|
<!-- <include
|
||||||
android:layout_alignParentTop="true"
|
layout="@layout/car_status_bar_header"
|
||||||
android:theme="@android:style/Theme"/>
|
android:layout_alignParentTop="true"
|
||||||
|
android:theme="@android:style/Theme"/>-->
|
||||||
|
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
<com.android.systemui.statusbar.car.UserGridRecyclerView
|
<com.android.systemui.car.userswitcher.UserGridRecyclerView
|
||||||
android:id="@+id/user_grid"
|
android:id="@+id/user_grid"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"/>
|
||||||
android:layout_marginTop="@dimen/car_user_switcher_margin_top"/>
|
<!-- TODO(b/150302361): Re-add marginTop once status bar has been added back. -->
|
||||||
|
<!-- android:layout_marginTop="@dimen/car_user_switcher_margin_top"/>-->
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/car_user_switcher_container_height">
|
android:layout_height="@dimen/car_user_switcher_container_height">
|
||||||
|
|
||||||
<com.android.systemui.statusbar.car.UserGridRecyclerView
|
<com.android.systemui.car.userswitcher.UserGridRecyclerView
|
||||||
android:id="@+id/user_grid"
|
android:id="@+id/user_grid"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"/>
|
android:layout_height="match_parent"/>
|
||||||
|
|||||||
@@ -58,6 +58,11 @@
|
|||||||
to a constant alpha percent value using the initial alpha. -->
|
to a constant alpha percent value using the initial alpha. -->
|
||||||
<integer name="config_finalNotificationBackgroundAlpha">100</integer>
|
<integer name="config_finalNotificationBackgroundAlpha">100</integer>
|
||||||
|
|
||||||
|
<!-- Car System UI's OverlayViewsMediator-->
|
||||||
|
<string-array name="config_carSystemUIOverlayViewsMediators" translatable="false">
|
||||||
|
<item>com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
<!-- SystemUI Services: The classes of the stuff to start. -->
|
<!-- SystemUI Services: The classes of the stuff to start. -->
|
||||||
<string-array name="config_systemUIServiceComponents" translatable="false">
|
<string-array name="config_systemUIServiceComponents" translatable="false">
|
||||||
<item>com.android.systemui.util.NotificationChannels</item>
|
<item>com.android.systemui.util.NotificationChannels</item>
|
||||||
@@ -85,5 +90,6 @@
|
|||||||
<item>com.android.systemui.navigationbar.car.CarNavigationBar</item>
|
<item>com.android.systemui.navigationbar.car.CarNavigationBar</item>
|
||||||
<item>com.android.systemui.toast.ToastUI</item>
|
<item>com.android.systemui.toast.ToastUI</item>
|
||||||
<item>com.android.systemui.voicerecognition.car.ConnectedDeviceVoiceRecognitionNotifier</item>
|
<item>com.android.systemui.voicerecognition.car.ConnectedDeviceVoiceRecognitionNotifier</item>
|
||||||
|
<item>com.android.systemui.window.SystemUIOverlayWindowManager</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ import com.android.systemui.toast.ToastUI;
|
|||||||
import com.android.systemui.util.leak.GarbageMonitor;
|
import com.android.systemui.util.leak.GarbageMonitor;
|
||||||
import com.android.systemui.voicerecognition.car.ConnectedDeviceVoiceRecognitionNotifier;
|
import com.android.systemui.voicerecognition.car.ConnectedDeviceVoiceRecognitionNotifier;
|
||||||
import com.android.systemui.volume.VolumeUI;
|
import com.android.systemui.volume.VolumeUI;
|
||||||
|
import com.android.systemui.window.OverlayWindowModule;
|
||||||
|
import com.android.systemui.window.SystemUIOverlayWindowManager;
|
||||||
|
|
||||||
import dagger.Binds;
|
import dagger.Binds;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
@@ -47,7 +49,7 @@ import dagger.multibindings.IntoMap;
|
|||||||
|
|
||||||
/** Binder for car specific {@link SystemUI} modules. */
|
/** Binder for car specific {@link SystemUI} modules. */
|
||||||
@Module(includes = {RecentsModule.class, CarStatusBarModule.class, NotificationsModule.class,
|
@Module(includes = {RecentsModule.class, CarStatusBarModule.class, NotificationsModule.class,
|
||||||
BubbleModule.class, KeyguardModule.class})
|
BubbleModule.class, KeyguardModule.class, OverlayWindowModule.class})
|
||||||
public abstract class CarSystemUIBinder {
|
public abstract class CarSystemUIBinder {
|
||||||
/** Inject into AuthController. */
|
/** Inject into AuthController. */
|
||||||
@Binds
|
@Binds
|
||||||
@@ -182,4 +184,10 @@ public abstract class CarSystemUIBinder {
|
|||||||
@ClassKey(ConnectedDeviceVoiceRecognitionNotifier.class)
|
@ClassKey(ConnectedDeviceVoiceRecognitionNotifier.class)
|
||||||
public abstract SystemUI bindConnectedDeviceVoiceRecognitionNotifier(
|
public abstract SystemUI bindConnectedDeviceVoiceRecognitionNotifier(
|
||||||
ConnectedDeviceVoiceRecognitionNotifier sysui);
|
ConnectedDeviceVoiceRecognitionNotifier sysui);
|
||||||
|
|
||||||
|
/** Inject into SystemUIOverlayWindowManager. */
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@ClassKey(SystemUIOverlayWindowManager.class)
|
||||||
|
public abstract SystemUI bindSystemUIPrimaryWindowManager(SystemUIOverlayWindowManager sysui);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2019 The Android Open Source Project
|
* Copyright (C) 2020 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.android.systemui.statusbar.car;
|
package com.android.systemui.car.userswitcher;
|
||||||
|
|
||||||
import android.app.admin.DevicePolicyManager;
|
import android.app.admin.DevicePolicyManager;
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
@@ -56,8 +56,8 @@ class CarTrustAgentUnlockDialogHelper extends BroadcastReceiver{
|
|||||||
private final UserManager mUserManager;
|
private final UserManager mUserManager;
|
||||||
private final WindowManager.LayoutParams mParams;
|
private final WindowManager.LayoutParams mParams;
|
||||||
/**
|
/**
|
||||||
* Not using Dialog because context passed from {@link FullscreenUserSwitcher} is not an
|
* Not using Dialog because context passed from {@link FullscreenUserSwitcherViewMediator}
|
||||||
* activity.
|
* is not an activity.
|
||||||
*/
|
*/
|
||||||
private final View mUnlockDialogLayout;
|
private final View mUnlockDialogLayout;
|
||||||
private final TextView mUnlockingText;
|
private final TextView mUnlockingText;
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* 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.animation.Animator;
|
||||||
|
import android.animation.AnimatorListenerAdapter;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
|
|
||||||
|
import com.android.systemui.R;
|
||||||
|
import com.android.systemui.dagger.qualifiers.Main;
|
||||||
|
import com.android.systemui.window.OverlayViewController;
|
||||||
|
import com.android.systemui.window.OverlayViewGlobalStateController;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for {@link R.layout#car_fullscreen_user_switcher}.
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class FullScreenUserSwitcherViewController extends OverlayViewController {
|
||||||
|
private final Context mContext;
|
||||||
|
private final Resources mResources;
|
||||||
|
private final int mShortAnimationDuration;
|
||||||
|
private UserGridRecyclerView mUserGridView;
|
||||||
|
private UserGridRecyclerView.UserSelectionListener mUserSelectionListener;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public FullScreenUserSwitcherViewController(
|
||||||
|
Context context,
|
||||||
|
@Main Resources resources,
|
||||||
|
OverlayViewGlobalStateController overlayViewGlobalStateController) {
|
||||||
|
super(R.id.fullscreen_user_switcher_stub, overlayViewGlobalStateController);
|
||||||
|
mContext = context;
|
||||||
|
mResources = resources;
|
||||||
|
mShortAnimationDuration = mResources.getInteger(android.R.integer.config_shortAnimTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onFinishInflate() {
|
||||||
|
// Initialize user grid.
|
||||||
|
mUserGridView = getLayout().findViewById(R.id.user_grid);
|
||||||
|
GridLayoutManager layoutManager = new GridLayoutManager(mContext,
|
||||||
|
mResources.getInteger(R.integer.user_fullscreen_switcher_num_col));
|
||||||
|
mUserGridView.setLayoutManager(layoutManager);
|
||||||
|
mUserGridView.buildAdapter();
|
||||||
|
mUserGridView.setUserSelectionListener(mUserSelectionListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void showInternal() {
|
||||||
|
getLayout().setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void hideInternal() {
|
||||||
|
// Switching is about to happen, since it takes time, fade out the switcher gradually.
|
||||||
|
fadeOut();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fadeOut() {
|
||||||
|
mUserGridView.animate()
|
||||||
|
.alpha(0.0f)
|
||||||
|
.setDuration(mShortAnimationDuration)
|
||||||
|
.setListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
getLayout().setVisibility(View.GONE);
|
||||||
|
mUserGridView.setAlpha(1.0f);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate underlying view.
|
||||||
|
*/
|
||||||
|
void invalidate() {
|
||||||
|
if (getLayout() == null) {
|
||||||
|
// layout hasn't been inflated.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getLayout().invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set {@link UserGridRecyclerView.UserSelectionListener}.
|
||||||
|
*/
|
||||||
|
void setUserGridSelectionListener(
|
||||||
|
UserGridRecyclerView.UserSelectionListener userGridSelectionListener) {
|
||||||
|
mUserSelectionListener = userGridSelectionListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} when layout is visible.
|
||||||
|
*/
|
||||||
|
boolean isVisible() {
|
||||||
|
if (getLayout() == null) {
|
||||||
|
// layout hasn't been inflated.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getLayout().getVisibility() == View.VISIBLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,291 @@
|
|||||||
|
/*
|
||||||
|
* 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.car.Car;
|
||||||
|
import android.car.trust.CarTrustAgentEnrollmentManager;
|
||||||
|
import android.car.userlib.CarUserManagerHelper;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.content.pm.UserInfo;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
import android.os.UserManager;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.android.internal.widget.LockPatternUtils;
|
||||||
|
import com.android.systemui.R;
|
||||||
|
import com.android.systemui.car.CarServiceProvider;
|
||||||
|
import com.android.systemui.dagger.qualifiers.Main;
|
||||||
|
import com.android.systemui.keyguard.ScreenLifecycle;
|
||||||
|
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||||
|
import com.android.systemui.statusbar.StatusBarState;
|
||||||
|
import com.android.systemui.statusbar.car.CarStatusBar;
|
||||||
|
import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
|
||||||
|
import com.android.systemui.window.OverlayViewMediator;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages the fullscreen user switcher and it's interactions with the keyguard.
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class FullscreenUserSwitcherViewMediator implements OverlayViewMediator {
|
||||||
|
private static final String TAG = FullscreenUserSwitcherViewMediator.class.getSimpleName();
|
||||||
|
|
||||||
|
private final Context mContext;
|
||||||
|
private final UserManager mUserManager;
|
||||||
|
private final CarServiceProvider mCarServiceProvider;
|
||||||
|
private final CarTrustAgentUnlockDialogHelper mUnlockDialogHelper;
|
||||||
|
private final CarStatusBarKeyguardViewManager mCarStatusBarKeyguardViewManager;
|
||||||
|
private final Handler mMainHandler;
|
||||||
|
private final StatusBarStateController mStatusBarStateController;
|
||||||
|
private final FullScreenUserSwitcherViewController mFullScreenUserSwitcherViewController;
|
||||||
|
private final ScreenLifecycle mScreenLifecycle;
|
||||||
|
private final CarStatusBar mCarStatusBar;
|
||||||
|
private final boolean mIsUserSwitcherEnabled;
|
||||||
|
private final CarUserManagerHelper mCarUserManagerHelper;
|
||||||
|
|
||||||
|
private CarTrustAgentEnrollmentManager mEnrollmentManager;
|
||||||
|
private UserGridRecyclerView.UserRecord mSelectedUser;
|
||||||
|
private final CarTrustAgentUnlockDialogHelper.OnHideListener mOnHideListener =
|
||||||
|
dismissUserSwitcher -> {
|
||||||
|
if (dismissUserSwitcher) {
|
||||||
|
dismissUserSwitcher();
|
||||||
|
} else {
|
||||||
|
// Re-draw the parent view, otherwise the unlock dialog will not be removed
|
||||||
|
// from the screen immediately.
|
||||||
|
invalidateFullscreenUserSwitcherView();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private final BroadcastReceiver mUserUnlockReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||||
|
Log.d(TAG, "user 0 is unlocked, SharedPreference is accessible.");
|
||||||
|
}
|
||||||
|
showDialogForInitialUser();
|
||||||
|
mContext.unregisterReceiver(mUserUnlockReceiver);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public FullscreenUserSwitcherViewMediator(
|
||||||
|
Context context,
|
||||||
|
@Main Resources resources,
|
||||||
|
@Main Handler mainHandler,
|
||||||
|
UserManager userManager,
|
||||||
|
CarServiceProvider carServiceProvider,
|
||||||
|
CarTrustAgentUnlockDialogHelper carTrustAgentUnlockDialogHelper,
|
||||||
|
CarStatusBarKeyguardViewManager carStatusBarKeyguardViewManager,
|
||||||
|
CarStatusBar carStatusBar,
|
||||||
|
StatusBarStateController statusBarStateController,
|
||||||
|
FullScreenUserSwitcherViewController fullScreenUserSwitcherViewController,
|
||||||
|
ScreenLifecycle screenLifecycle) {
|
||||||
|
mContext = context;
|
||||||
|
|
||||||
|
mIsUserSwitcherEnabled = resources.getBoolean(R.bool.config_enableFullscreenUserSwitcher);
|
||||||
|
|
||||||
|
mMainHandler = mainHandler;
|
||||||
|
mUserManager = userManager;
|
||||||
|
|
||||||
|
mCarServiceProvider = carServiceProvider;
|
||||||
|
mCarServiceProvider.addListener(
|
||||||
|
car -> mEnrollmentManager = (CarTrustAgentEnrollmentManager) car.getCarManager(
|
||||||
|
Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE));
|
||||||
|
|
||||||
|
mUnlockDialogHelper = carTrustAgentUnlockDialogHelper;
|
||||||
|
mCarStatusBarKeyguardViewManager = carStatusBarKeyguardViewManager;
|
||||||
|
mCarStatusBar = carStatusBar;
|
||||||
|
mStatusBarStateController = statusBarStateController;
|
||||||
|
mFullScreenUserSwitcherViewController = fullScreenUserSwitcherViewController;
|
||||||
|
mScreenLifecycle = screenLifecycle;
|
||||||
|
|
||||||
|
mCarUserManagerHelper = new CarUserManagerHelper(mContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerListeners() {
|
||||||
|
registerUserSwitcherShowListeners();
|
||||||
|
registerUserSwitcherHideListeners();
|
||||||
|
registerHideKeyguardListeners();
|
||||||
|
|
||||||
|
if (mUserManager.isUserUnlocked(UserHandle.USER_SYSTEM)) {
|
||||||
|
// User0 is unlocked, switched to the initial user
|
||||||
|
showDialogForInitialUser();
|
||||||
|
} else {
|
||||||
|
// listen to USER_UNLOCKED
|
||||||
|
mContext.registerReceiverAsUser(mUserUnlockReceiver,
|
||||||
|
UserHandle.getUserHandleForUid(UserHandle.USER_SYSTEM),
|
||||||
|
new IntentFilter(Intent.ACTION_USER_UNLOCKED),
|
||||||
|
/* broadcastPermission= */ null,
|
||||||
|
/* scheduler= */ null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerUserSwitcherShowListeners() {
|
||||||
|
mCarStatusBarKeyguardViewManager.addOnKeyguardCancelClickedListener(this::show);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerUserSwitcherHideListeners() {
|
||||||
|
mStatusBarStateController.addCallback(new StatusBarStateController.StateListener() {
|
||||||
|
@Override
|
||||||
|
public void onStateChanged(int newState) {
|
||||||
|
if (newState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerHideKeyguardListeners() {
|
||||||
|
mStatusBarStateController.addCallback(new StatusBarStateController.StateListener() {
|
||||||
|
@Override
|
||||||
|
public void onStateChanged(int newState) {
|
||||||
|
if (newState != StatusBarState.FULLSCREEN_USER_SWITCHER) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dismissKeyguardWhenUserSwitcherNotDisplayed(newState);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mScreenLifecycle.addObserver(new ScreenLifecycle.Observer() {
|
||||||
|
@Override
|
||||||
|
public void onScreenTurnedOn() {
|
||||||
|
dismissKeyguardWhenUserSwitcherNotDisplayed(mStatusBarStateController.getState());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mContext.registerReceiver(new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (!intent.getAction().equals(Intent.ACTION_USER_SWITCHED)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to dismiss the keyguard after every user switch.
|
||||||
|
dismissKeyguardWhenUserSwitcherNotDisplayed(mStatusBarStateController.getState());
|
||||||
|
}
|
||||||
|
}, new IntentFilter(Intent.ACTION_USER_SWITCHED));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setupOverlayContentViewControllers() {
|
||||||
|
mFullScreenUserSwitcherViewController.setUserGridSelectionListener(this::onUserSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Every time user clicks on an item in the switcher, if the clicked user has no trusted
|
||||||
|
* device, we hide the switcher, either gradually or immediately.
|
||||||
|
* If the user has trusted device, we show an unlock dialog to notify user the unlock
|
||||||
|
* state.
|
||||||
|
* When the unlock dialog is dismissed by user, we hide the unlock dialog and the switcher.
|
||||||
|
* We dismiss the entire keyguard when we hide the switcher if user clicked on the
|
||||||
|
* foreground user (user we're already logged in as).
|
||||||
|
*/
|
||||||
|
private void onUserSelected(UserGridRecyclerView.UserRecord record) {
|
||||||
|
mSelectedUser = record;
|
||||||
|
if (record.mInfo != null) {
|
||||||
|
if (hasScreenLock(record.mInfo.id) && hasTrustedDevice(record.mInfo.id)) {
|
||||||
|
mUnlockDialogHelper.showUnlockDialog(record.mInfo.id, mOnHideListener);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||||
|
Log.d(TAG, "no trusted device enrolled for uid: " + record.mInfo.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dismissUserSwitcher();
|
||||||
|
}
|
||||||
|
|
||||||
|
// We automatically dismiss keyguard unless user switcher is being shown above the keyguard.
|
||||||
|
private void dismissKeyguardWhenUserSwitcherNotDisplayed(int state) {
|
||||||
|
if (!mIsUserSwitcherEnabled) {
|
||||||
|
return; // Not using the full screen user switcher.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == StatusBarState.FULLSCREEN_USER_SWITCHER
|
||||||
|
&& !mFullScreenUserSwitcherViewController.isVisible()) {
|
||||||
|
// Current execution path continues to set state after this, thus we deffer the
|
||||||
|
// dismissal to the next execution cycle.
|
||||||
|
|
||||||
|
// Dismiss the keyguard if switcher is not visible.
|
||||||
|
// TODO(b/150402329): Remove once keyguard is implemented using Overlay Window
|
||||||
|
// abstractions.
|
||||||
|
mMainHandler.post(mCarStatusBar::dismissKeyguard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasScreenLock(int uid) {
|
||||||
|
LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
|
||||||
|
return lockPatternUtils.getCredentialTypeForUser(uid)
|
||||||
|
!= LockPatternUtils.CREDENTIAL_TYPE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasTrustedDevice(int uid) {
|
||||||
|
if (mEnrollmentManager == null) { // car service not ready, so it cannot be available.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !mEnrollmentManager.getEnrolledDeviceInfoForUser(uid).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dismissUserSwitcher() {
|
||||||
|
if (mSelectedUser == null) {
|
||||||
|
Log.e(TAG, "Request to dismiss user switcher, but no user selected");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mSelectedUser.mType == UserGridRecyclerView.UserRecord.FOREGROUND_USER) {
|
||||||
|
hide();
|
||||||
|
mCarStatusBar.dismissKeyguard();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showDialogForInitialUser() {
|
||||||
|
int initialUser = mCarUserManagerHelper.getInitialUser();
|
||||||
|
UserInfo initialUserInfo = mUserManager.getUserInfo(initialUser);
|
||||||
|
mSelectedUser = new UserGridRecyclerView.UserRecord(initialUserInfo,
|
||||||
|
UserGridRecyclerView.UserRecord.FOREGROUND_USER);
|
||||||
|
|
||||||
|
// If the initial user has screen lock and trusted device, display the unlock dialog on the
|
||||||
|
// keyguard.
|
||||||
|
if (hasScreenLock(initialUser) && hasTrustedDevice(initialUser)) {
|
||||||
|
mUnlockDialogHelper.showUnlockDialogAfterDelay(initialUser,
|
||||||
|
mOnHideListener);
|
||||||
|
} else {
|
||||||
|
// If no trusted device, dismiss the keyguard.
|
||||||
|
dismissUserSwitcher();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invalidateFullscreenUserSwitcherView() {
|
||||||
|
mFullScreenUserSwitcherViewController.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hide() {
|
||||||
|
mFullScreenUserSwitcherViewController.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void show() {
|
||||||
|
mFullScreenUserSwitcherViewController.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2018 The Android Open Source Project
|
* Copyright (C) 2020 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.android.systemui.statusbar.car;
|
package com.android.systemui.car.userswitcher;
|
||||||
|
|
||||||
import static android.content.DialogInterface.BUTTON_NEGATIVE;
|
import static android.content.DialogInterface.BUTTON_NEGATIVE;
|
||||||
import static android.content.DialogInterface.BUTTON_POSITIVE;
|
import static android.content.DialogInterface.BUTTON_POSITIVE;
|
||||||
@@ -45,7 +45,6 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.view.WindowInsets;
|
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -114,8 +113,6 @@ public class UserGridRecyclerView extends RecyclerView {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the adapter that populates the grid layout
|
* Initializes the adapter that populates the grid layout
|
||||||
*
|
|
||||||
* @return the adapter
|
|
||||||
*/
|
*/
|
||||||
public void buildAdapter() {
|
public void buildAdapter() {
|
||||||
List<UserRecord> userRecords = createUserRecords(getUsersForUserGrid());
|
List<UserRecord> userRecords = createUserRecords(getUsersForUserGrid());
|
||||||
@@ -236,10 +233,16 @@ public class UserGridRecyclerView extends RecyclerView {
|
|||||||
mNewUserName = mRes.getString(R.string.car_new_user);
|
mNewUserName = mRes.getString(R.string.car_new_user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears list of user records.
|
||||||
|
*/
|
||||||
public void clearUsers() {
|
public void clearUsers() {
|
||||||
mUsers.clear();
|
mUsers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates list of user records.
|
||||||
|
*/
|
||||||
public void updateUsers(List<UserRecord> users) {
|
public void updateUsers(List<UserRecord> users) {
|
||||||
mUsers = users;
|
mUsers = users;
|
||||||
}
|
}
|
||||||
@@ -483,6 +486,10 @@ public class UserGridRecyclerView extends RecyclerView {
|
|||||||
return mUsers.size();
|
return mUsers.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An extension of {@link RecyclerView.ViewHolder} that also houses the user name and the
|
||||||
|
* user avatar.
|
||||||
|
*/
|
||||||
public class UserAdapterViewHolder extends RecyclerView.ViewHolder {
|
public class UserAdapterViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
|
||||||
public ImageView mUserAvatarImageView;
|
public ImageView mUserAvatarImageView;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2019 The Android Open Source Project
|
* Copyright (C) 2020 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.android.systemui.statusbar.car;
|
package com.android.systemui.car.userswitcher;
|
||||||
|
|
||||||
import android.annotation.UserIdInt;
|
import android.annotation.UserIdInt;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -71,6 +71,30 @@ public class CarNavigationBarController {
|
|||||||
mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar);
|
mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides all navigation bars.
|
||||||
|
*/
|
||||||
|
public void hideBars() {
|
||||||
|
if (mTopView != null) {
|
||||||
|
mTopView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
setBottomWindowVisibility(View.GONE);
|
||||||
|
setLeftWindowVisibility(View.GONE);
|
||||||
|
setRightWindowVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows all navigation bars.
|
||||||
|
*/
|
||||||
|
public void showBars() {
|
||||||
|
if (mTopView != null) {
|
||||||
|
mTopView.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
setBottomWindowVisibility(View.VISIBLE);
|
||||||
|
setLeftWindowVisibility(View.VISIBLE);
|
||||||
|
setRightWindowVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
/** Connect to hvac service. */
|
/** Connect to hvac service. */
|
||||||
public void connectToHvac() {
|
public void connectToHvac() {
|
||||||
mHvacControllerLazy.get().connectToCarService();
|
mHvacControllerLazy.get().connectToCarService();
|
||||||
|
|||||||
@@ -35,9 +35,9 @@ import androidx.annotation.VisibleForTesting;
|
|||||||
import androidx.recyclerview.widget.GridLayoutManager;
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
|
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
|
import com.android.systemui.car.userswitcher.UserGridRecyclerView;
|
||||||
import com.android.systemui.plugins.qs.QS;
|
import com.android.systemui.plugins.qs.QS;
|
||||||
import com.android.systemui.qs.QSFooter;
|
import com.android.systemui.qs.QSFooter;
|
||||||
import com.android.systemui.statusbar.car.UserGridRecyclerView;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|||||||
@@ -67,7 +67,6 @@ import com.android.systemui.bubbles.BubbleController;
|
|||||||
import com.android.systemui.car.CarDeviceProvisionedController;
|
import com.android.systemui.car.CarDeviceProvisionedController;
|
||||||
import com.android.systemui.car.CarDeviceProvisionedListener;
|
import com.android.systemui.car.CarDeviceProvisionedListener;
|
||||||
import com.android.systemui.car.CarServiceProvider;
|
import com.android.systemui.car.CarServiceProvider;
|
||||||
import com.android.systemui.car.SystemUIPrimaryWindowController;
|
|
||||||
import com.android.systemui.classifier.FalsingLog;
|
import com.android.systemui.classifier.FalsingLog;
|
||||||
import com.android.systemui.colorextraction.SysuiColorExtractor;
|
import com.android.systemui.colorextraction.SysuiColorExtractor;
|
||||||
import com.android.systemui.dagger.qualifiers.UiBackground;
|
import com.android.systemui.dagger.qualifiers.UiBackground;
|
||||||
@@ -95,7 +94,6 @@ import com.android.systemui.statusbar.NotificationMediaManager;
|
|||||||
import com.android.systemui.statusbar.NotificationRemoteInputManager;
|
import com.android.systemui.statusbar.NotificationRemoteInputManager;
|
||||||
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
|
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
|
||||||
import com.android.systemui.statusbar.PulseExpansionHandler;
|
import com.android.systemui.statusbar.PulseExpansionHandler;
|
||||||
import com.android.systemui.statusbar.StatusBarState;
|
|
||||||
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
|
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
|
||||||
import com.android.systemui.statusbar.SysuiStatusBarStateController;
|
import com.android.systemui.statusbar.SysuiStatusBarStateController;
|
||||||
import com.android.systemui.statusbar.VibratorHelper;
|
import com.android.systemui.statusbar.VibratorHelper;
|
||||||
@@ -181,14 +179,13 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
|
|||||||
private Drawable mNotificationPanelBackground;
|
private Drawable mNotificationPanelBackground;
|
||||||
|
|
||||||
private final Object mQueueLock = new Object();
|
private final Object mQueueLock = new Object();
|
||||||
private final SystemUIPrimaryWindowController mSystemUIPrimaryWindowController;
|
|
||||||
private final CarNavigationBarController mCarNavigationBarController;
|
private final CarNavigationBarController mCarNavigationBarController;
|
||||||
private final FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder;
|
private final FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder;
|
||||||
private final Lazy<PowerManagerHelper> mPowerManagerHelperLazy;
|
private final Lazy<PowerManagerHelper> mPowerManagerHelperLazy;
|
||||||
private final FullscreenUserSwitcher mFullscreenUserSwitcher;
|
|
||||||
private final ShadeController mShadeController;
|
private final ShadeController mShadeController;
|
||||||
private final CarServiceProvider mCarServiceProvider;
|
private final CarServiceProvider mCarServiceProvider;
|
||||||
private final CarDeviceProvisionedController mCarDeviceProvisionedController;
|
private final CarDeviceProvisionedController mCarDeviceProvisionedController;
|
||||||
|
private final ScreenLifecycle mScreenLifecycle;
|
||||||
|
|
||||||
private boolean mDeviceIsSetUpForUser = true;
|
private boolean mDeviceIsSetUpForUser = true;
|
||||||
private boolean mIsUserSetupInProgress = false;
|
private boolean mIsUserSetupInProgress = false;
|
||||||
@@ -196,7 +193,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
|
|||||||
private FlingAnimationUtils mFlingAnimationUtils;
|
private FlingAnimationUtils mFlingAnimationUtils;
|
||||||
private NotificationDataManager mNotificationDataManager;
|
private NotificationDataManager mNotificationDataManager;
|
||||||
private NotificationClickHandlerFactory mNotificationClickHandlerFactory;
|
private NotificationClickHandlerFactory mNotificationClickHandlerFactory;
|
||||||
private ScreenLifecycle mScreenLifecycle;
|
|
||||||
|
|
||||||
// The container for the notifications.
|
// The container for the notifications.
|
||||||
private CarNotificationView mNotificationView;
|
private CarNotificationView mNotificationView;
|
||||||
@@ -333,8 +329,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
|
|||||||
/* Car Settings injected components. */
|
/* Car Settings injected components. */
|
||||||
CarServiceProvider carServiceProvider,
|
CarServiceProvider carServiceProvider,
|
||||||
Lazy<PowerManagerHelper> powerManagerHelperLazy,
|
Lazy<PowerManagerHelper> powerManagerHelperLazy,
|
||||||
FullscreenUserSwitcher fullscreenUserSwitcher,
|
|
||||||
SystemUIPrimaryWindowController systemUIPrimaryWindowController,
|
|
||||||
CarNavigationBarController carNavigationBarController,
|
CarNavigationBarController carNavigationBarController,
|
||||||
FlingAnimationUtils.Builder flingAnimationUtilsBuilder) {
|
FlingAnimationUtils.Builder flingAnimationUtilsBuilder) {
|
||||||
super(
|
super(
|
||||||
@@ -423,10 +417,9 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
|
|||||||
mShadeController = shadeController;
|
mShadeController = shadeController;
|
||||||
mCarServiceProvider = carServiceProvider;
|
mCarServiceProvider = carServiceProvider;
|
||||||
mPowerManagerHelperLazy = powerManagerHelperLazy;
|
mPowerManagerHelperLazy = powerManagerHelperLazy;
|
||||||
mFullscreenUserSwitcher = fullscreenUserSwitcher;
|
|
||||||
mSystemUIPrimaryWindowController = systemUIPrimaryWindowController;
|
|
||||||
mCarNavigationBarController = carNavigationBarController;
|
mCarNavigationBarController = carNavigationBarController;
|
||||||
mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder;
|
mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder;
|
||||||
|
mScreenLifecycle = screenLifecycle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -434,18 +427,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
|
|||||||
mDeviceIsSetUpForUser = mCarDeviceProvisionedController.isCurrentUserSetup();
|
mDeviceIsSetUpForUser = mCarDeviceProvisionedController.isCurrentUserSetup();
|
||||||
mIsUserSetupInProgress = mCarDeviceProvisionedController.isCurrentUserSetupInProgress();
|
mIsUserSetupInProgress = mCarDeviceProvisionedController.isCurrentUserSetupInProgress();
|
||||||
|
|
||||||
// Need to initialize screen lifecycle before calling super.start - before switcher is
|
|
||||||
// created.
|
|
||||||
mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
|
|
||||||
mScreenLifecycle.addObserver(mScreenObserver);
|
|
||||||
|
|
||||||
// TODO: Remove the setup of user switcher from Car Status Bar.
|
|
||||||
mSystemUIPrimaryWindowController.attach();
|
|
||||||
mFullscreenUserSwitcher.setStatusBar(this);
|
|
||||||
mFullscreenUserSwitcher.setContainer(
|
|
||||||
mSystemUIPrimaryWindowController.getBaseLayout().findViewById(
|
|
||||||
R.id.fullscreen_user_switcher_stub));
|
|
||||||
|
|
||||||
// Notification bar related setup.
|
// Notification bar related setup.
|
||||||
mInitialBackgroundAlpha = (float) mContext.getResources().getInteger(
|
mInitialBackgroundAlpha = (float) mContext.getResources().getInteger(
|
||||||
R.integer.config_initialNotificationBackgroundAlpha) / 100;
|
R.integer.config_initialNotificationBackgroundAlpha) / 100;
|
||||||
@@ -965,49 +946,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setLockscreenUser(int newUserId) {
|
|
||||||
super.setLockscreenUser(newUserId);
|
|
||||||
// Try to dismiss the keyguard after every user switch.
|
|
||||||
dismissKeyguardWhenUserSwitcherNotDisplayed();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStateChanged(int newState) {
|
|
||||||
super.onStateChanged(newState);
|
|
||||||
|
|
||||||
if (newState != StatusBarState.FULLSCREEN_USER_SWITCHER) {
|
|
||||||
mFullscreenUserSwitcher.hide();
|
|
||||||
} else {
|
|
||||||
dismissKeyguardWhenUserSwitcherNotDisplayed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
|
|
||||||
@Override
|
|
||||||
public void onScreenTurnedOn() {
|
|
||||||
dismissKeyguardWhenUserSwitcherNotDisplayed();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// We automatically dismiss keyguard unless user switcher is being shown on the keyguard.
|
|
||||||
private void dismissKeyguardWhenUserSwitcherNotDisplayed() {
|
|
||||||
if (!mUserSwitcherController.useFullscreenUserSwitcher()) {
|
|
||||||
return; // Not using the full screen user switcher.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER
|
|
||||||
&& !mFullscreenUserSwitcher.isVisible()) {
|
|
||||||
// Current execution path continues to set state after this, thus we deffer the
|
|
||||||
// dismissal to the next execution cycle.
|
|
||||||
postDismissKeyguard(); // Dismiss the keyguard if switcher is not visible.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void postDismissKeyguard() {
|
|
||||||
mHandler.post(this::dismissKeyguard);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dismisses the keyguard and shows bouncer if authentication is necessary.
|
* Dismisses the keyguard and shows bouncer if authentication is necessary.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
|
|||||||
import com.android.systemui.statusbar.policy.ConfigurationController;
|
import com.android.systemui.statusbar.policy.ConfigurationController;
|
||||||
import com.android.systemui.statusbar.policy.KeyguardStateController;
|
import com.android.systemui.statusbar.policy.KeyguardStateController;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
@@ -42,7 +45,7 @@ public class CarStatusBarKeyguardViewManager extends StatusBarKeyguardViewManage
|
|||||||
|
|
||||||
protected boolean mShouldHideNavBar;
|
protected boolean mShouldHideNavBar;
|
||||||
private final CarNavigationBarController mCarNavigationBarController;
|
private final CarNavigationBarController mCarNavigationBarController;
|
||||||
private final FullscreenUserSwitcher mFullscreenUserSwitcher;
|
private Set<OnKeyguardCancelClickedListener> mKeygaurdCancelClickedListenerSet;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public CarStatusBarKeyguardViewManager(Context context,
|
public CarStatusBarKeyguardViewManager(Context context,
|
||||||
@@ -56,8 +59,7 @@ public class CarStatusBarKeyguardViewManager extends StatusBarKeyguardViewManage
|
|||||||
NotificationShadeWindowController notificationShadeWindowController,
|
NotificationShadeWindowController notificationShadeWindowController,
|
||||||
KeyguardStateController keyguardStateController,
|
KeyguardStateController keyguardStateController,
|
||||||
NotificationMediaManager notificationMediaManager,
|
NotificationMediaManager notificationMediaManager,
|
||||||
CarNavigationBarController carNavigationBarController,
|
CarNavigationBarController carNavigationBarController) {
|
||||||
FullscreenUserSwitcher fullscreenUserSwitcher) {
|
|
||||||
super(context, callback, lockPatternUtils, sysuiStatusBarStateController,
|
super(context, callback, lockPatternUtils, sysuiStatusBarStateController,
|
||||||
configurationController, keyguardUpdateMonitor, navigationModeController,
|
configurationController, keyguardUpdateMonitor, navigationModeController,
|
||||||
dockManager, notificationShadeWindowController, keyguardStateController,
|
dockManager, notificationShadeWindowController, keyguardStateController,
|
||||||
@@ -65,7 +67,7 @@ public class CarStatusBarKeyguardViewManager extends StatusBarKeyguardViewManage
|
|||||||
mShouldHideNavBar = context.getResources()
|
mShouldHideNavBar = context.getResources()
|
||||||
.getBoolean(R.bool.config_hideNavWhenKeyguardBouncerShown);
|
.getBoolean(R.bool.config_hideNavWhenKeyguardBouncerShown);
|
||||||
mCarNavigationBarController = carNavigationBarController;
|
mCarNavigationBarController = carNavigationBarController;
|
||||||
mFullscreenUserSwitcher = fullscreenUserSwitcher;
|
mKeygaurdCancelClickedListenerSet = new HashSet<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -95,7 +97,7 @@ public class CarStatusBarKeyguardViewManager extends StatusBarKeyguardViewManage
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onCancelClicked() {
|
public void onCancelClicked() {
|
||||||
mFullscreenUserSwitcher.show();
|
mKeygaurdCancelClickedListenerSet.forEach(OnKeyguardCancelClickedListener::onCancelClicked);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,4 +107,31 @@ public class CarStatusBarKeyguardViewManager extends StatusBarKeyguardViewManage
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onDensityOrFontScaleChanged() { }
|
public void onDensityOrFontScaleChanged() { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add listener for keyguard cancel clicked.
|
||||||
|
*/
|
||||||
|
public void addOnKeyguardCancelClickedListener(
|
||||||
|
OnKeyguardCancelClickedListener keyguardCancelClickedListener) {
|
||||||
|
mKeygaurdCancelClickedListenerSet.add(keyguardCancelClickedListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove listener for keyguard cancel clicked.
|
||||||
|
*/
|
||||||
|
public void removeOnKeyguardCancelClickedListener(
|
||||||
|
OnKeyguardCancelClickedListener keyguardCancelClickedListener) {
|
||||||
|
mKeygaurdCancelClickedListenerSet.remove(keyguardCancelClickedListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a callback for keyguard cancel button clicked listeners.
|
||||||
|
*/
|
||||||
|
public interface OnKeyguardCancelClickedListener {
|
||||||
|
/**
|
||||||
|
* Called when keyguard cancel button is clicked.
|
||||||
|
*/
|
||||||
|
void onCancelClicked();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import com.android.systemui.assist.AssistManager;
|
|||||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||||
import com.android.systemui.bubbles.BubbleController;
|
import com.android.systemui.bubbles.BubbleController;
|
||||||
import com.android.systemui.car.CarServiceProvider;
|
import com.android.systemui.car.CarServiceProvider;
|
||||||
import com.android.systemui.car.SystemUIPrimaryWindowController;
|
|
||||||
import com.android.systemui.colorextraction.SysuiColorExtractor;
|
import com.android.systemui.colorextraction.SysuiColorExtractor;
|
||||||
import com.android.systemui.dagger.qualifiers.UiBackground;
|
import com.android.systemui.dagger.qualifiers.UiBackground;
|
||||||
import com.android.systemui.keyguard.DismissCallbackRegistry;
|
import com.android.systemui.keyguard.DismissCallbackRegistry;
|
||||||
@@ -205,8 +204,6 @@ public class CarStatusBarModule {
|
|||||||
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
|
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
|
||||||
CarServiceProvider carServiceProvider,
|
CarServiceProvider carServiceProvider,
|
||||||
Lazy<PowerManagerHelper> powerManagerHelperLazy,
|
Lazy<PowerManagerHelper> powerManagerHelperLazy,
|
||||||
FullscreenUserSwitcher fullscreenUserSwitcher,
|
|
||||||
SystemUIPrimaryWindowController systemUIPrimaryWindowController,
|
|
||||||
CarNavigationBarController carNavigationBarController,
|
CarNavigationBarController carNavigationBarController,
|
||||||
FlingAnimationUtils.Builder flingAnimationUtilsBuilder) {
|
FlingAnimationUtils.Builder flingAnimationUtilsBuilder) {
|
||||||
return new CarStatusBar(
|
return new CarStatusBar(
|
||||||
@@ -288,8 +285,6 @@ public class CarStatusBarModule {
|
|||||||
statusBarTouchableRegionManager,
|
statusBarTouchableRegionManager,
|
||||||
carServiceProvider,
|
carServiceProvider,
|
||||||
powerManagerHelperLazy,
|
powerManagerHelperLazy,
|
||||||
fullscreenUserSwitcher,
|
|
||||||
systemUIPrimaryWindowController,
|
|
||||||
carNavigationBarController,
|
carNavigationBarController,
|
||||||
flingAnimationUtilsBuilder);
|
flingAnimationUtilsBuilder);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,271 +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.systemui.statusbar.car;
|
|
||||||
|
|
||||||
import android.animation.Animator;
|
|
||||||
import android.animation.AnimatorListenerAdapter;
|
|
||||||
import android.car.Car;
|
|
||||||
import android.car.trust.CarTrustAgentEnrollmentManager;
|
|
||||||
import android.car.userlib.CarUserManagerHelper;
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.content.pm.UserInfo;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.os.UserHandle;
|
|
||||||
import android.os.UserManager;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewStub;
|
|
||||||
|
|
||||||
import androidx.recyclerview.widget.GridLayoutManager;
|
|
||||||
|
|
||||||
import com.android.internal.widget.LockPatternUtils;
|
|
||||||
import com.android.systemui.R;
|
|
||||||
import com.android.systemui.car.CarServiceProvider;
|
|
||||||
import com.android.systemui.car.SystemUIPrimaryWindowController;
|
|
||||||
import com.android.systemui.dagger.qualifiers.Main;
|
|
||||||
import com.android.systemui.statusbar.car.CarTrustAgentUnlockDialogHelper.OnHideListener;
|
|
||||||
import com.android.systemui.statusbar.car.UserGridRecyclerView.UserRecord;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manages the fullscreen user switcher.
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
public class FullscreenUserSwitcher {
|
|
||||||
private static final String TAG = FullscreenUserSwitcher.class.getSimpleName();
|
|
||||||
private final Context mContext;
|
|
||||||
private final Resources mResources;
|
|
||||||
private final UserManager mUserManager;
|
|
||||||
private final CarServiceProvider mCarServiceProvider;
|
|
||||||
private final CarTrustAgentUnlockDialogHelper mUnlockDialogHelper;
|
|
||||||
private final SystemUIPrimaryWindowController mSystemUIPrimaryWindowController;
|
|
||||||
private CarStatusBar mCarStatusBar;
|
|
||||||
private final int mShortAnimDuration;
|
|
||||||
|
|
||||||
private View mParent;
|
|
||||||
private UserGridRecyclerView mUserGridView;
|
|
||||||
private CarTrustAgentEnrollmentManager mEnrollmentManager;
|
|
||||||
private UserGridRecyclerView.UserRecord mSelectedUser;
|
|
||||||
private CarUserManagerHelper mCarUserManagerHelper;
|
|
||||||
private final BroadcastReceiver mUserUnlockReceiver = new BroadcastReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
|
||||||
Log.d(TAG, "user 0 is unlocked, SharedPreference is accessible.");
|
|
||||||
}
|
|
||||||
showDialogForInitialUser();
|
|
||||||
mContext.unregisterReceiver(mUserUnlockReceiver);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public FullscreenUserSwitcher(
|
|
||||||
Context context,
|
|
||||||
@Main Resources resources,
|
|
||||||
UserManager userManager,
|
|
||||||
CarServiceProvider carServiceProvider,
|
|
||||||
CarTrustAgentUnlockDialogHelper carTrustAgentUnlockDialogHelper,
|
|
||||||
SystemUIPrimaryWindowController systemUIPrimaryWindowController) {
|
|
||||||
mContext = context;
|
|
||||||
mResources = resources;
|
|
||||||
mUserManager = userManager;
|
|
||||||
mCarServiceProvider = carServiceProvider;
|
|
||||||
mUnlockDialogHelper = carTrustAgentUnlockDialogHelper;
|
|
||||||
mSystemUIPrimaryWindowController = systemUIPrimaryWindowController;
|
|
||||||
|
|
||||||
mShortAnimDuration = mResources.getInteger(android.R.integer.config_shortAnimTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sets the status bar which gives an entry point to dismiss the keyguard. */
|
|
||||||
// TODO: Remove this in favor of a keyguard controller.
|
|
||||||
public void setStatusBar(CarStatusBar statusBar) {
|
|
||||||
mCarStatusBar = statusBar;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns {@code true} if the user switcher already has a parent view. */
|
|
||||||
public boolean isAttached() {
|
|
||||||
return mParent != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sets the {@link ViewStub} to show the user switcher. */
|
|
||||||
public void setContainer(ViewStub containerStub) {
|
|
||||||
if (isAttached()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mParent = containerStub.inflate();
|
|
||||||
|
|
||||||
View container = mParent.findViewById(R.id.container);
|
|
||||||
|
|
||||||
// Initialize user grid.
|
|
||||||
mUserGridView = container.findViewById(R.id.user_grid);
|
|
||||||
GridLayoutManager layoutManager = new GridLayoutManager(mContext,
|
|
||||||
mResources.getInteger(R.integer.user_fullscreen_switcher_num_col));
|
|
||||||
mUserGridView.setLayoutManager(layoutManager);
|
|
||||||
mUserGridView.buildAdapter();
|
|
||||||
mUserGridView.setUserSelectionListener(this::onUserSelected);
|
|
||||||
mCarUserManagerHelper = new CarUserManagerHelper(mContext);
|
|
||||||
mCarServiceProvider.addListener(
|
|
||||||
car -> mEnrollmentManager = (CarTrustAgentEnrollmentManager) car.getCarManager(
|
|
||||||
Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE));
|
|
||||||
|
|
||||||
IntentFilter filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
|
|
||||||
if (mUserManager.isUserUnlocked(UserHandle.USER_SYSTEM)) {
|
|
||||||
// User0 is unlocked, switched to the initial user
|
|
||||||
showDialogForInitialUser();
|
|
||||||
} else {
|
|
||||||
// listen to USER_UNLOCKED
|
|
||||||
mContext.registerReceiverAsUser(mUserUnlockReceiver,
|
|
||||||
UserHandle.getUserHandleForUid(UserHandle.USER_SYSTEM),
|
|
||||||
filter,
|
|
||||||
/* broadcastPermission= */ null,
|
|
||||||
/* scheduler */ null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showDialogForInitialUser() {
|
|
||||||
int initialUser = mCarUserManagerHelper.getInitialUser();
|
|
||||||
UserInfo initialUserInfo = mUserManager.getUserInfo(initialUser);
|
|
||||||
mSelectedUser = new UserRecord(initialUserInfo, UserRecord.FOREGROUND_USER);
|
|
||||||
|
|
||||||
// If the initial user has screen lock and trusted device, display the unlock dialog on the
|
|
||||||
// keyguard.
|
|
||||||
if (hasScreenLock(initialUser) && hasTrustedDevice(initialUser)) {
|
|
||||||
mUnlockDialogHelper.showUnlockDialogAfterDelay(initialUser,
|
|
||||||
mOnHideListener);
|
|
||||||
} else {
|
|
||||||
// If no trusted device, dismiss the keyguard.
|
|
||||||
dismissUserSwitcher();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Makes user grid visible.
|
|
||||||
*/
|
|
||||||
public void show() {
|
|
||||||
if (!isAttached()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mParent.setVisibility(View.VISIBLE);
|
|
||||||
mSystemUIPrimaryWindowController.setWindowExpanded(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hides the user grid.
|
|
||||||
*/
|
|
||||||
public void hide() {
|
|
||||||
if (!isAttached()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mParent.setVisibility(View.INVISIBLE);
|
|
||||||
mSystemUIPrimaryWindowController.setWindowExpanded(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {@code true} if user grid is visible, {@code false} otherwise.
|
|
||||||
*/
|
|
||||||
public boolean isVisible() {
|
|
||||||
if (!isAttached()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return mParent.getVisibility() == View.VISIBLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Every time user clicks on an item in the switcher, if the clicked user has no trusted device,
|
|
||||||
* we hide the switcher, either gradually or immediately.
|
|
||||||
*
|
|
||||||
* If the user has trusted device, we show an unlock dialog to notify user the unlock state.
|
|
||||||
* When the unlock dialog is dismissed by user, we hide the unlock dialog and the switcher.
|
|
||||||
*
|
|
||||||
* We dismiss the entire keyguard when we hide the switcher if user clicked on the foreground
|
|
||||||
* user (user we're already logged in as).
|
|
||||||
*/
|
|
||||||
private void onUserSelected(UserGridRecyclerView.UserRecord record) {
|
|
||||||
mSelectedUser = record;
|
|
||||||
if (record.mInfo != null) {
|
|
||||||
if (hasScreenLock(record.mInfo.id) && hasTrustedDevice(record.mInfo.id)) {
|
|
||||||
mUnlockDialogHelper.showUnlockDialog(record.mInfo.id, mOnHideListener);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
|
||||||
Log.d(TAG, "no trusted device enrolled for uid: " + record.mInfo.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dismissUserSwitcher();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void dismissUserSwitcher() {
|
|
||||||
if (mSelectedUser == null) {
|
|
||||||
Log.e(TAG, "Request to dismiss user switcher, but no user selected");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (mSelectedUser.mType == UserRecord.FOREGROUND_USER) {
|
|
||||||
hide();
|
|
||||||
mCarStatusBar.dismissKeyguard();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Switching is about to happen, since it takes time, fade out the switcher gradually.
|
|
||||||
fadeOut();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fadeOut() {
|
|
||||||
mUserGridView.animate()
|
|
||||||
.alpha(0.0f)
|
|
||||||
.setDuration(mShortAnimDuration)
|
|
||||||
.setListener(new AnimatorListenerAdapter() {
|
|
||||||
@Override
|
|
||||||
public void onAnimationEnd(Animator animation) {
|
|
||||||
hide();
|
|
||||||
mUserGridView.setAlpha(1.0f);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasScreenLock(int uid) {
|
|
||||||
LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
|
|
||||||
return lockPatternUtils.getCredentialTypeForUser(uid)
|
|
||||||
!= LockPatternUtils.CREDENTIAL_TYPE_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasTrustedDevice(int uid) {
|
|
||||||
if (mEnrollmentManager == null) { // car service not ready, so it cannot be available.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return !mEnrollmentManager.getEnrolledDeviceInfoForUser(uid).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
private OnHideListener mOnHideListener = new OnHideListener() {
|
|
||||||
@Override
|
|
||||||
public void onHide(boolean dismissUserSwitcher) {
|
|
||||||
if (dismissUserSwitcher) {
|
|
||||||
dismissUserSwitcher();
|
|
||||||
} else {
|
|
||||||
// Re-draw the parent view, otherwise the unlock dialog will not be removed from
|
|
||||||
// the screen immediately.
|
|
||||||
mParent.invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* 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.window;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.ViewStub;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Owns a {@link View} that is present in SystemUIOverlayWindow.
|
||||||
|
*/
|
||||||
|
public class OverlayViewController {
|
||||||
|
private final int mStubId;
|
||||||
|
private final OverlayViewGlobalStateController mOverlayViewGlobalStateController;
|
||||||
|
|
||||||
|
private View mLayout;
|
||||||
|
|
||||||
|
public OverlayViewController(int stubId,
|
||||||
|
OverlayViewGlobalStateController overlayViewGlobalStateController) {
|
||||||
|
mLayout = null;
|
||||||
|
mStubId = stubId;
|
||||||
|
mOverlayViewGlobalStateController = overlayViewGlobalStateController;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows content of {@link OverlayViewController}.
|
||||||
|
*
|
||||||
|
* Should be used to show view externally and in particular by {@link OverlayViewMediator}.
|
||||||
|
*/
|
||||||
|
public final void start() {
|
||||||
|
mOverlayViewGlobalStateController.showView(/* viewController= */ this, this::show);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides content of {@link OverlayViewController}.
|
||||||
|
*
|
||||||
|
* Should be used to hide view externally and in particular by {@link OverlayViewMediator}.
|
||||||
|
*/
|
||||||
|
public final void stop() {
|
||||||
|
mOverlayViewGlobalStateController.hideView(/* viewController= */ this, this::hide);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inflate layout owned by controller.
|
||||||
|
*/
|
||||||
|
public final void inflate(ViewGroup baseLayout) {
|
||||||
|
ViewStub viewStub = baseLayout.findViewById(mStubId);
|
||||||
|
mLayout = viewStub.inflate();
|
||||||
|
onFinishInflate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called once inflate finishes.
|
||||||
|
*/
|
||||||
|
protected void onFinishInflate() {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns [@code true} if layout owned by controller has been inflated.
|
||||||
|
*/
|
||||||
|
public final boolean isInflated() {
|
||||||
|
return mLayout != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void show() {
|
||||||
|
if (mLayout == null) {
|
||||||
|
// layout must be inflated before show() is called.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses should override this method to implement reveal animations and implement logic
|
||||||
|
* specific to when the layout owned by the controller is shown.
|
||||||
|
*
|
||||||
|
* Should only be overridden by Superclass but not called by any {@link OverlayViewMediator}.
|
||||||
|
*/
|
||||||
|
protected void showInternal() {
|
||||||
|
mLayout.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hide() {
|
||||||
|
if (mLayout == null) {
|
||||||
|
// layout must be inflated before hide() is called.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hideInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses should override this method to implement conceal animations and implement logic
|
||||||
|
* specific to when the layout owned by the controller is hidden.
|
||||||
|
*
|
||||||
|
* Should only be overridden by Superclass but not called by any {@link OverlayViewMediator}.
|
||||||
|
*/
|
||||||
|
protected void hideInternal() {
|
||||||
|
mLayout.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides access to layout owned by controller.
|
||||||
|
*/
|
||||||
|
protected final View getLayout() {
|
||||||
|
return mLayout;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* 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.window;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
|
import com.android.systemui.navigationbar.car.CarNavigationBarController;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This controller is responsible for the following:
|
||||||
|
* <p><ul>
|
||||||
|
* <li>Holds the global state for SystemUIOverlayWindow.
|
||||||
|
* <li>Allows {@link SystemUIOverlayWindowManager} to register {@link OverlayViewMediator}(s).
|
||||||
|
* <li>Enables {@link OverlayViewController)(s) to reveal/conceal themselves while respecting the
|
||||||
|
* global state of SystemUIOverlayWindow.
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class OverlayViewGlobalStateController {
|
||||||
|
private static final String TAG = OverlayViewGlobalStateController.class.getSimpleName();
|
||||||
|
private final SystemUIOverlayWindowController mSystemUIOverlayWindowController;
|
||||||
|
private final CarNavigationBarController mCarNavigationBarController;
|
||||||
|
@VisibleForTesting
|
||||||
|
Set<String> mShownSet;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public OverlayViewGlobalStateController(
|
||||||
|
CarNavigationBarController carNavigationBarController,
|
||||||
|
SystemUIOverlayWindowController systemUIOverlayWindowController) {
|
||||||
|
mSystemUIOverlayWindowController = systemUIOverlayWindowController;
|
||||||
|
mSystemUIOverlayWindowController.attach();
|
||||||
|
mCarNavigationBarController = carNavigationBarController;
|
||||||
|
mShownSet = new HashSet<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register {@link OverlayViewMediator} to use in SystemUIOverlayWindow.
|
||||||
|
*/
|
||||||
|
public void registerMediator(OverlayViewMediator overlayViewMediator) {
|
||||||
|
Log.d(TAG, "Registering content mediator: " + overlayViewMediator.getClass().getName());
|
||||||
|
|
||||||
|
overlayViewMediator.registerListeners();
|
||||||
|
overlayViewMediator.setupOverlayContentViewControllers();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show content in Overlay Window.
|
||||||
|
*/
|
||||||
|
public void showView(OverlayViewController viewController, Runnable show) {
|
||||||
|
if (mShownSet.isEmpty()) {
|
||||||
|
mCarNavigationBarController.hideBars();
|
||||||
|
mSystemUIOverlayWindowController.setWindowExpanded(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!viewController.isInflated()) {
|
||||||
|
viewController.inflate(mSystemUIOverlayWindowController.getBaseLayout());
|
||||||
|
}
|
||||||
|
|
||||||
|
show.run();
|
||||||
|
mShownSet.add(viewController.getClass().getName());
|
||||||
|
|
||||||
|
Log.d(TAG, "Content shown: " + viewController.getClass().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide content in Overlay Window.
|
||||||
|
*/
|
||||||
|
public void hideView(OverlayViewController viewController, Runnable hide) {
|
||||||
|
if (!viewController.isInflated()) {
|
||||||
|
Log.d(TAG, "Content cannot be hidden since it isn't inflated: "
|
||||||
|
+ viewController.getClass().getName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!mShownSet.contains(viewController.getClass().getName())) {
|
||||||
|
Log.d(TAG, "Content cannot be hidden since it isn't shown: "
|
||||||
|
+ viewController.getClass().getName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hide.run();
|
||||||
|
mShownSet.remove(viewController.getClass().getName());
|
||||||
|
|
||||||
|
if (mShownSet.isEmpty()) {
|
||||||
|
mCarNavigationBarController.showBars();
|
||||||
|
mSystemUIOverlayWindowController.setWindowExpanded(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "Content hidden: " + viewController.getClass().getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* 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.window;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controls when to show and hide {@link OverlayViewController}(s).
|
||||||
|
*/
|
||||||
|
public interface OverlayViewMediator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register listeners that could use ContentVisibilityAdjuster to show/hide content.
|
||||||
|
*/
|
||||||
|
void registerListeners();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows for post-inflation callbacks and listeners to be set inside required {@link
|
||||||
|
* OverlayViewController}(s).
|
||||||
|
*/
|
||||||
|
void setupOverlayContentViewControllers();
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* 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.window;
|
||||||
|
|
||||||
|
import com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator;
|
||||||
|
|
||||||
|
import dagger.Binds;
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.multibindings.ClassKey;
|
||||||
|
import dagger.multibindings.IntoMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dagger injection module for {@link SystemUIOverlayWindowManager}
|
||||||
|
*/
|
||||||
|
@Module
|
||||||
|
public abstract class OverlayWindowModule {
|
||||||
|
/** Inject into FullscreenUserSwitcherViewsMediator. */
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@ClassKey(FullscreenUserSwitcherViewMediator.class)
|
||||||
|
public abstract OverlayViewMediator bindFullscreenUserSwitcherViewsMediator(
|
||||||
|
FullscreenUserSwitcherViewMediator overlayViewsMediator);
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.android.systemui.car;
|
package com.android.systemui.window;
|
||||||
|
|
||||||
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
|
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ import javax.inject.Singleton;
|
|||||||
* this window for the notification panel.
|
* this window for the notification panel.
|
||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
public class SystemUIPrimaryWindowController implements
|
public class SystemUIOverlayWindowController implements
|
||||||
ConfigurationController.ConfigurationListener {
|
ConfigurationController.ConfigurationListener {
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
@@ -57,7 +57,7 @@ public class SystemUIPrimaryWindowController implements
|
|||||||
private boolean mIsAttached = false;
|
private boolean mIsAttached = false;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public SystemUIPrimaryWindowController(
|
public SystemUIOverlayWindowController(
|
||||||
Context context,
|
Context context,
|
||||||
@Main Resources resources,
|
@Main Resources resources,
|
||||||
WindowManager windowManager,
|
WindowManager windowManager,
|
||||||
@@ -77,7 +77,7 @@ public class SystemUIPrimaryWindowController implements
|
|||||||
|
|
||||||
mLpChanged = new WindowManager.LayoutParams();
|
mLpChanged = new WindowManager.LayoutParams();
|
||||||
mBaseLayout = (ViewGroup) LayoutInflater.from(context)
|
mBaseLayout = (ViewGroup) LayoutInflater.from(context)
|
||||||
.inflate(R.layout.sysui_primary_window, /* root= */ null, false);
|
.inflate(R.layout.sysui_overlay_window, /* root= */ null, false);
|
||||||
|
|
||||||
configurationController.addCallback(this);
|
configurationController.addCallback(this);
|
||||||
}
|
}
|
||||||
@@ -115,7 +115,7 @@ public class SystemUIPrimaryWindowController implements
|
|||||||
mLp.gravity = Gravity.TOP;
|
mLp.gravity = Gravity.TOP;
|
||||||
mLp.setFitInsetsTypes(/* types= */ 0);
|
mLp.setFitInsetsTypes(/* types= */ 0);
|
||||||
mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
|
mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
|
||||||
mLp.setTitle("SystemUIPrimaryWindow");
|
mLp.setTitle("SystemUIOverlayWindow");
|
||||||
mLp.packageName = mContext.getPackageName();
|
mLp.packageName = mContext.getPackageName();
|
||||||
mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
|
mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
|
||||||
|
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* 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.window;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.android.systemui.R;
|
||||||
|
import com.android.systemui.SystemUI;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Provider;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers {@link OverlayViewMediator}(s) and synchronizes their calls to hide/show {@link
|
||||||
|
* OverlayViewController}(s) to allow for the correct visibility of system bars.
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class SystemUIOverlayWindowManager extends SystemUI {
|
||||||
|
private static final String TAG = "SystemUIOverlayWindowManager";
|
||||||
|
private final Map<Class<?>, Provider<OverlayViewMediator>>
|
||||||
|
mContentMediatorCreators;
|
||||||
|
private final OverlayViewGlobalStateController mOverlayViewGlobalStateController;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public SystemUIOverlayWindowManager(
|
||||||
|
Context context,
|
||||||
|
Map<Class<?>, Provider<OverlayViewMediator>> contentMediatorCreators,
|
||||||
|
OverlayViewGlobalStateController overlayViewGlobalStateController) {
|
||||||
|
super(context);
|
||||||
|
mContentMediatorCreators = contentMediatorCreators;
|
||||||
|
mOverlayViewGlobalStateController = overlayViewGlobalStateController;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
String[] names = mContext.getResources().getStringArray(
|
||||||
|
R.array.config_carSystemUIOverlayViewsMediators);
|
||||||
|
startServices(names);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startServices(String[] services) {
|
||||||
|
for (String clsName : services) {
|
||||||
|
try {
|
||||||
|
OverlayViewMediator obj = resolveContentMediator(clsName);
|
||||||
|
if (obj == null) {
|
||||||
|
Constructor constructor = Class.forName(clsName).getConstructor(Context.class);
|
||||||
|
obj = (OverlayViewMediator) constructor.newInstance(this);
|
||||||
|
}
|
||||||
|
mOverlayViewGlobalStateController.registerMediator(obj);
|
||||||
|
} catch (ClassNotFoundException
|
||||||
|
| NoSuchMethodException
|
||||||
|
| IllegalAccessException
|
||||||
|
| InstantiationException
|
||||||
|
| InvocationTargetException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private OverlayViewMediator resolveContentMediator(String className) {
|
||||||
|
return resolve(className, mContentMediatorCreators);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> T resolve(String className, Map<Class<?>, Provider<T>> creators) {
|
||||||
|
try {
|
||||||
|
Class<?> clazz = Class.forName(className);
|
||||||
|
Provider<T> provider = creators.get(clazz);
|
||||||
|
return provider == null ? null : provider.get();
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/overlay_view_controller_test">
|
||||||
|
</LinearLayout>
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- Fullscreen views in sysui should be listed here in increasing Z order. -->
|
||||||
|
<FrameLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ViewStub android:id="@+id/overlay_view_controller_stub"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout="@layout/overlay_view_controller_stub"/>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
* 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.window;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import android.testing.AndroidTestingRunner;
|
||||||
|
import android.testing.TestableLooper;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
|
import com.android.systemui.SysuiTestCase;
|
||||||
|
import com.android.systemui.tests.R;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Captor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
@RunWith(AndroidTestingRunner.class)
|
||||||
|
@TestableLooper.RunWithLooper
|
||||||
|
@SmallTest
|
||||||
|
public class OverlayViewControllerTest extends SysuiTestCase {
|
||||||
|
private MockOverlayViewController mOverlayViewController;
|
||||||
|
private ViewGroup mBaseLayout;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private OverlayViewGlobalStateController mOverlayViewGlobalStateController;
|
||||||
|
|
||||||
|
@Captor
|
||||||
|
private ArgumentCaptor<Runnable> mRunnableArgumentCaptor;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
MockitoAnnotations.initMocks(/* testClass= */ this);
|
||||||
|
|
||||||
|
mOverlayViewController = new MockOverlayViewController(R.id.overlay_view_controller_stub,
|
||||||
|
mOverlayViewGlobalStateController);
|
||||||
|
|
||||||
|
mBaseLayout = (ViewGroup) LayoutInflater.from(mContext).inflate(
|
||||||
|
R.layout.overlay_view_controller_test, /* root= */ null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void inflate_layoutInitialized() {
|
||||||
|
mOverlayViewController.inflate(mBaseLayout);
|
||||||
|
|
||||||
|
assertThat(mOverlayViewController.getLayout().getId()).isEqualTo(
|
||||||
|
R.id.overlay_view_controller_test);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void inflate_onFinishInflateCalled() {
|
||||||
|
mOverlayViewController.inflate(mBaseLayout);
|
||||||
|
|
||||||
|
assertThat(mOverlayViewController.mOnFinishInflateCalled).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void start_viewInflated_viewShown() {
|
||||||
|
mOverlayViewController.inflate(mBaseLayout);
|
||||||
|
|
||||||
|
mOverlayViewController.start();
|
||||||
|
|
||||||
|
verify(mOverlayViewGlobalStateController).showView(eq(mOverlayViewController),
|
||||||
|
mRunnableArgumentCaptor.capture());
|
||||||
|
|
||||||
|
mRunnableArgumentCaptor.getValue().run();
|
||||||
|
|
||||||
|
assertThat(mOverlayViewController.mShowInternalCalled).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void stop_viewInflated_viewHidden() {
|
||||||
|
mOverlayViewController.inflate(mBaseLayout);
|
||||||
|
|
||||||
|
mOverlayViewController.stop();
|
||||||
|
|
||||||
|
verify(mOverlayViewGlobalStateController).hideView(eq(mOverlayViewController),
|
||||||
|
mRunnableArgumentCaptor.capture());
|
||||||
|
|
||||||
|
mRunnableArgumentCaptor.getValue().run();
|
||||||
|
|
||||||
|
assertThat(mOverlayViewController.mHideInternalCalled).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void start_viewNotInflated_viewNotShown() {
|
||||||
|
mOverlayViewController.start();
|
||||||
|
|
||||||
|
verify(mOverlayViewGlobalStateController).showView(eq(mOverlayViewController),
|
||||||
|
mRunnableArgumentCaptor.capture());
|
||||||
|
|
||||||
|
mRunnableArgumentCaptor.getValue().run();
|
||||||
|
|
||||||
|
assertThat(mOverlayViewController.mShowInternalCalled).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void stop_viewNotInflated_viewNotHidden() {
|
||||||
|
mOverlayViewController.stop();
|
||||||
|
|
||||||
|
verify(mOverlayViewGlobalStateController).hideView(eq(mOverlayViewController),
|
||||||
|
mRunnableArgumentCaptor.capture());
|
||||||
|
|
||||||
|
mRunnableArgumentCaptor.getValue().run();
|
||||||
|
|
||||||
|
assertThat(mOverlayViewController.mHideInternalCalled).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MockOverlayViewController extends OverlayViewController {
|
||||||
|
boolean mOnFinishInflateCalled = false;
|
||||||
|
boolean mShowInternalCalled = false;
|
||||||
|
boolean mHideInternalCalled = false;
|
||||||
|
|
||||||
|
MockOverlayViewController(int stubId,
|
||||||
|
OverlayViewGlobalStateController overlayViewGlobalStateController) {
|
||||||
|
super(stubId, overlayViewGlobalStateController);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onFinishInflate() {
|
||||||
|
mOnFinishInflateCalled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void showInternal() {
|
||||||
|
mShowInternalCalled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void hideInternal() {
|
||||||
|
mHideInternalCalled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,250 @@
|
|||||||
|
/*
|
||||||
|
* 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.window;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.testing.AndroidTestingRunner;
|
||||||
|
import android.testing.TestableLooper;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
|
import com.android.systemui.SysuiTestCase;
|
||||||
|
import com.android.systemui.navigationbar.car.CarNavigationBarController;
|
||||||
|
|
||||||
|
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 OverlayViewGlobalStateControllerTest extends SysuiTestCase {
|
||||||
|
private static final String MOCK_OVERLAY_VIEW_CONTROLLER_NAME = "OverlayViewController";
|
||||||
|
|
||||||
|
private OverlayViewGlobalStateController mOverlayViewGlobalStateController;
|
||||||
|
private ViewGroup mBaseLayout;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private CarNavigationBarController mCarNavigationBarController;
|
||||||
|
@Mock
|
||||||
|
private SystemUIOverlayWindowController mSystemUIOverlayWindowController;
|
||||||
|
@Mock
|
||||||
|
private OverlayViewMediator mOverlayViewMediator;
|
||||||
|
@Mock
|
||||||
|
private OverlayViewController mOverlayViewController;
|
||||||
|
@Mock
|
||||||
|
private Runnable mRunnable;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
MockitoAnnotations.initMocks(/* testClass= */ this);
|
||||||
|
|
||||||
|
mOverlayViewGlobalStateController = new OverlayViewGlobalStateController(
|
||||||
|
mCarNavigationBarController, mSystemUIOverlayWindowController);
|
||||||
|
|
||||||
|
verify(mSystemUIOverlayWindowController).attach();
|
||||||
|
|
||||||
|
mBaseLayout = new FrameLayout(mContext);
|
||||||
|
|
||||||
|
when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void registerMediator_overlayViewMediatorListenersRegistered() {
|
||||||
|
mOverlayViewGlobalStateController.registerMediator(mOverlayViewMediator);
|
||||||
|
|
||||||
|
verify(mOverlayViewMediator).registerListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void registerMediator_overlayViewMediatorViewControllerSetup() {
|
||||||
|
mOverlayViewGlobalStateController.registerMediator(mOverlayViewMediator);
|
||||||
|
|
||||||
|
verify(mOverlayViewMediator).setupOverlayContentViewControllers();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void showView_nothingAlreadyShown_navigationBarsHidden() {
|
||||||
|
mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
|
||||||
|
|
||||||
|
verify(mCarNavigationBarController).hideBars();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void showView_nothingAlreadyShown_windowIsExpanded() {
|
||||||
|
mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
|
||||||
|
|
||||||
|
verify(mSystemUIOverlayWindowController).setWindowExpanded(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void showView_somethingAlreadyShown_navigationBarsHidden() {
|
||||||
|
mOverlayViewGlobalStateController.mShownSet.add(MOCK_OVERLAY_VIEW_CONTROLLER_NAME);
|
||||||
|
|
||||||
|
mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
|
||||||
|
|
||||||
|
verify(mCarNavigationBarController, never()).hideBars();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void showView_somethingAlreadyShown_windowIsExpanded() {
|
||||||
|
mOverlayViewGlobalStateController.mShownSet.add(MOCK_OVERLAY_VIEW_CONTROLLER_NAME);
|
||||||
|
|
||||||
|
mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
|
||||||
|
|
||||||
|
verify(mSystemUIOverlayWindowController, never()).setWindowExpanded(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void showView_viewControllerNotInflated_inflateViewController() {
|
||||||
|
when(mOverlayViewController.isInflated()).thenReturn(false);
|
||||||
|
|
||||||
|
mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
|
||||||
|
|
||||||
|
verify(mOverlayViewController).inflate(mBaseLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void showView_viewControllerInflated_inflateViewControllerNotCalled() {
|
||||||
|
when(mOverlayViewController.isInflated()).thenReturn(true);
|
||||||
|
|
||||||
|
mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
|
||||||
|
|
||||||
|
verify(mOverlayViewController, never()).inflate(mBaseLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void showView_showRunnableCalled() {
|
||||||
|
mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
|
||||||
|
|
||||||
|
verify(mRunnable).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void showView_overlayViewControllerAddedToShownSet() {
|
||||||
|
mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
|
||||||
|
|
||||||
|
assertThat(mOverlayViewGlobalStateController.mShownSet.contains(
|
||||||
|
mOverlayViewController.getClass().getName())).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hideView_viewControllerNotInflated_hideRunnableNotCalled() {
|
||||||
|
when(mOverlayViewController.isInflated()).thenReturn(false);
|
||||||
|
|
||||||
|
mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
|
||||||
|
|
||||||
|
verify(mRunnable, never()).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hideView_nothingShown_hideRunnableNotCalled() {
|
||||||
|
when(mOverlayViewController.isInflated()).thenReturn(true);
|
||||||
|
mOverlayViewGlobalStateController.mShownSet.clear();
|
||||||
|
|
||||||
|
mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
|
||||||
|
|
||||||
|
verify(mRunnable, never()).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hideView_viewControllerNotShown_hideRunnableNotCalled() {
|
||||||
|
when(mOverlayViewController.isInflated()).thenReturn(true);
|
||||||
|
mOverlayViewGlobalStateController.mShownSet.add(MOCK_OVERLAY_VIEW_CONTROLLER_NAME);
|
||||||
|
|
||||||
|
mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
|
||||||
|
|
||||||
|
verify(mRunnable, never()).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hideView_viewControllerShown_hideRunnableCalled() {
|
||||||
|
when(mOverlayViewController.isInflated()).thenReturn(true);
|
||||||
|
mOverlayViewGlobalStateController.mShownSet.add(
|
||||||
|
mOverlayViewController.getClass().getName());
|
||||||
|
|
||||||
|
mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
|
||||||
|
|
||||||
|
verify(mRunnable).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hideView_viewControllerOnlyShown_nothingShown() {
|
||||||
|
when(mOverlayViewController.isInflated()).thenReturn(true);
|
||||||
|
mOverlayViewGlobalStateController.mShownSet.add(
|
||||||
|
mOverlayViewController.getClass().getName());
|
||||||
|
|
||||||
|
mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
|
||||||
|
|
||||||
|
assertThat(mOverlayViewGlobalStateController.mShownSet.isEmpty()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hideView_viewControllerNotOnlyShown_navigationBarNotShown() {
|
||||||
|
when(mOverlayViewController.isInflated()).thenReturn(true);
|
||||||
|
mOverlayViewGlobalStateController.mShownSet.add(
|
||||||
|
mOverlayViewController.getClass().getName());
|
||||||
|
mOverlayViewGlobalStateController.mShownSet.add(MOCK_OVERLAY_VIEW_CONTROLLER_NAME);
|
||||||
|
|
||||||
|
mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
|
||||||
|
|
||||||
|
verify(mCarNavigationBarController, never()).showBars();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hideView_viewControllerNotOnlyShown_windowNotCollapsed() {
|
||||||
|
when(mOverlayViewController.isInflated()).thenReturn(true);
|
||||||
|
mOverlayViewGlobalStateController.mShownSet.add(
|
||||||
|
mOverlayViewController.getClass().getName());
|
||||||
|
mOverlayViewGlobalStateController.mShownSet.add(MOCK_OVERLAY_VIEW_CONTROLLER_NAME);
|
||||||
|
|
||||||
|
mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
|
||||||
|
|
||||||
|
verify(mSystemUIOverlayWindowController, never()).setWindowExpanded(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hideView_viewControllerOnlyShown_navigationBarShown() {
|
||||||
|
when(mOverlayViewController.isInflated()).thenReturn(true);
|
||||||
|
mOverlayViewGlobalStateController.mShownSet.add(
|
||||||
|
mOverlayViewController.getClass().getName());
|
||||||
|
|
||||||
|
mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
|
||||||
|
|
||||||
|
verify(mCarNavigationBarController).showBars();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hideView_viewControllerOnlyShown_windowCollapsed() {
|
||||||
|
when(mOverlayViewController.isInflated()).thenReturn(true);
|
||||||
|
mOverlayViewGlobalStateController.mShownSet.add(
|
||||||
|
mOverlayViewController.getClass().getName());
|
||||||
|
|
||||||
|
mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
|
||||||
|
|
||||||
|
verify(mSystemUIOverlayWindowController).setWindowExpanded(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user