Merge "[DO NOT MERGE] Animate dialog stack and use in UserSwitcher" into sc-v2-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
4ad4975b28
@@ -16,4 +16,5 @@
|
|||||||
-->
|
-->
|
||||||
<resources>
|
<resources>
|
||||||
<item type="id" name="launch_animation_running"/>
|
<item type="id" name="launch_animation_running"/>
|
||||||
|
<item type="id" name="dialog_content_parent" />
|
||||||
</resources>
|
</resources>
|
||||||
@@ -40,6 +40,7 @@ import android.widget.FrameLayout
|
|||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
private const val TAG = "DialogLaunchAnimator"
|
private const val TAG = "DialogLaunchAnimator"
|
||||||
|
private val DIALOG_CONTENT_PARENT_ID = R.id.dialog_content_parent
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class that allows dialogs to be started in a seamless way from a view that is transforming
|
* A class that allows dialogs to be started in a seamless way from a view that is transforming
|
||||||
@@ -86,10 +87,10 @@ class DialogLaunchAnimator(
|
|||||||
// If the parent of the view we are launching from is the background of some other animated
|
// If the parent of the view we are launching from is the background of some other animated
|
||||||
// dialog, then this means the caller intent is to launch a dialog from another dialog. In
|
// dialog, then this means the caller intent is to launch a dialog from another dialog. In
|
||||||
// this case, we also animate the parent (which is the dialog background).
|
// this case, we also animate the parent (which is the dialog background).
|
||||||
val dialogContentParent = openedDialogs
|
val animatedParent = openedDialogs
|
||||||
.firstOrNull { it.dialogContentParent == view.parent }
|
.firstOrNull { it.dialogContentParent == view.parent }
|
||||||
?.dialogContentParent
|
val parentHostDialog = animatedParent?.hostDialog
|
||||||
val animateFrom = dialogContentParent ?: view
|
val animateFrom = animatedParent?.dialogContentParent ?: view
|
||||||
|
|
||||||
// Make sure we don't run the launch animation from the same view twice at the same time.
|
// Make sure we don't run the launch animation from the same view twice at the same time.
|
||||||
if (animateFrom.getTag(TAG_LAUNCH_ANIMATION_RUNNING) != null) {
|
if (animateFrom.getTag(TAG_LAUNCH_ANIMATION_RUNNING) != null) {
|
||||||
@@ -100,12 +101,18 @@ class DialogLaunchAnimator(
|
|||||||
|
|
||||||
animateFrom.setTag(TAG_LAUNCH_ANIMATION_RUNNING, true)
|
animateFrom.setTag(TAG_LAUNCH_ANIMATION_RUNNING, true)
|
||||||
|
|
||||||
val launchAnimation = AnimatedDialog(
|
val animatedDialog = AnimatedDialog(
|
||||||
context, launchAnimator, hostDialogProvider, animateFrom,
|
context,
|
||||||
onDialogDismissed = { openedDialogs.remove(it) }, originalDialog = dialog,
|
launchAnimator,
|
||||||
animateBackgroundBoundsChange)
|
hostDialogProvider,
|
||||||
val hostDialog = launchAnimation.hostDialog
|
animateFrom,
|
||||||
openedDialogs.add(launchAnimation)
|
onDialogDismissed = { openedDialogs.remove(it) },
|
||||||
|
originalDialog = dialog,
|
||||||
|
animateBackgroundBoundsChange,
|
||||||
|
openedDialogs.firstOrNull { it.hostDialog == parentHostDialog }
|
||||||
|
)
|
||||||
|
val hostDialog = animatedDialog.hostDialog
|
||||||
|
openedDialogs.add(animatedDialog)
|
||||||
|
|
||||||
// If the dialog is dismissed/hidden/shown, then we should actually dismiss/hide/show the
|
// If the dialog is dismissed/hidden/shown, then we should actually dismiss/hide/show the
|
||||||
// host dialog.
|
// host dialog.
|
||||||
@@ -119,15 +126,15 @@ class DialogLaunchAnimator(
|
|||||||
// If AOD is disabled the screen will directly becomes black and we won't see
|
// If AOD is disabled the screen will directly becomes black and we won't see
|
||||||
// the animation anyways.
|
// the animation anyways.
|
||||||
if (reason == DialogListener.DismissReason.DEVICE_LOCKED) {
|
if (reason == DialogListener.DismissReason.DEVICE_LOCKED) {
|
||||||
launchAnimation.exitAnimationDisabled = true
|
animatedDialog.exitAnimationDisabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
hostDialog.dismiss()
|
hostDialog.dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onHide() {
|
override fun onHide() {
|
||||||
if (launchAnimation.ignoreNextCallToHide) {
|
if (animatedDialog.ignoreNextCallToHide) {
|
||||||
launchAnimation.ignoreNextCallToHide = false
|
animatedDialog.ignoreNextCallToHide = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,20 +145,43 @@ class DialogLaunchAnimator(
|
|||||||
hostDialog.show()
|
hostDialog.show()
|
||||||
|
|
||||||
// We don't actually want to show the original dialog, so hide it.
|
// We don't actually want to show the original dialog, so hide it.
|
||||||
launchAnimation.ignoreNextCallToHide = true
|
animatedDialog.ignoreNextCallToHide = true
|
||||||
dialog.hide()
|
dialog.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSizeChanged() {
|
override fun onSizeChanged() {
|
||||||
launchAnimation.onOriginalDialogSizeChanged()
|
animatedDialog.onOriginalDialogSizeChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun prepareForStackDismiss() {
|
||||||
|
animatedDialog.touchSurface = animatedDialog.prepareForStackDismiss()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
launchAnimation.start()
|
animatedDialog.start()
|
||||||
return hostDialog
|
return hostDialog
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Launch [dialog] from a [parentHostDialog] as returned by [showFromView]. This will allow
|
||||||
|
* for dismissing the whole stack.
|
||||||
|
*
|
||||||
|
* This will return a new host dialog, with the same caveat as [showFromView].
|
||||||
|
*
|
||||||
|
* @see DialogListener.prepareForStackDismiss
|
||||||
|
*/
|
||||||
|
fun showFromDialog(
|
||||||
|
dialog: Dialog,
|
||||||
|
parentHostDialog: Dialog,
|
||||||
|
animateBackgroundBoundsChange: Boolean = false
|
||||||
|
): Dialog {
|
||||||
|
val view = parentHostDialog.findViewById<ViewGroup>(DIALOG_CONTENT_PARENT_ID)
|
||||||
|
?.getChildAt(0)
|
||||||
|
?: throw IllegalStateException("No dialog content parent found in host dialog")
|
||||||
|
return showFromView(dialog, view, animateBackgroundBoundsChange)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure that all dialogs currently shown won't animate into their touch surface when
|
* Ensure that all dialogs currently shown won't animate into their touch surface when
|
||||||
* dismissed.
|
* dismissed.
|
||||||
@@ -214,6 +244,12 @@ interface DialogListener {
|
|||||||
/** Called when this dialog show() is called. */
|
/** Called when this dialog show() is called. */
|
||||||
fun onShow()
|
fun onShow()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call before dismissing a stack of dialogs (dialogs launched from dialogs), so the topmost
|
||||||
|
* can animate directly into the original `touchSurface`.
|
||||||
|
*/
|
||||||
|
fun prepareForStackDismiss()
|
||||||
|
|
||||||
/** Called when this dialog size might have changed, e.g. because of configuration changes. */
|
/** Called when this dialog size might have changed, e.g. because of configuration changes. */
|
||||||
fun onSizeChanged()
|
fun onSizeChanged()
|
||||||
}
|
}
|
||||||
@@ -224,7 +260,7 @@ private class AnimatedDialog(
|
|||||||
hostDialogProvider: HostDialogProvider,
|
hostDialogProvider: HostDialogProvider,
|
||||||
|
|
||||||
/** The view that triggered the dialog after being tapped. */
|
/** The view that triggered the dialog after being tapped. */
|
||||||
private val touchSurface: View,
|
var touchSurface: View,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A callback that will be called with this [AnimatedDialog] after the dialog was
|
* A callback that will be called with this [AnimatedDialog] after the dialog was
|
||||||
@@ -236,7 +272,10 @@ private class AnimatedDialog(
|
|||||||
private val originalDialog: Dialog,
|
private val originalDialog: Dialog,
|
||||||
|
|
||||||
/** Whether we should animate the dialog background when its bounds change. */
|
/** Whether we should animate the dialog background when its bounds change. */
|
||||||
private val animateBackgroundBoundsChange: Boolean
|
private val animateBackgroundBoundsChange: Boolean,
|
||||||
|
|
||||||
|
/** Launch animation corresponding to the parent [hostDialog]. */
|
||||||
|
private val parentAnimatedDialog: AnimatedDialog? = null
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* The fullscreen dialog to which we will add the content view [originalDialogView] of
|
* The fullscreen dialog to which we will add the content view [originalDialogView] of
|
||||||
@@ -253,7 +292,9 @@ private class AnimatedDialog(
|
|||||||
* the same size as the original dialog window and to which we will set the original dialog
|
* the same size as the original dialog window and to which we will set the original dialog
|
||||||
* window background.
|
* window background.
|
||||||
*/
|
*/
|
||||||
val dialogContentParent = FrameLayout(context)
|
val dialogContentParent = FrameLayout(context).apply {
|
||||||
|
id = DIALOG_CONTENT_PARENT_ID
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The background color of [originalDialogView], taking into consideration the [originalDialog]
|
* The background color of [originalDialogView], taking into consideration the [originalDialog]
|
||||||
@@ -359,9 +400,7 @@ private class AnimatedDialog(
|
|||||||
// Make the touch surface invisible and make sure that it stays invisible as long as the
|
// Make the touch surface invisible and make sure that it stays invisible as long as the
|
||||||
// dialog is shown or animating.
|
// dialog is shown or animating.
|
||||||
touchSurface.visibility = View.INVISIBLE
|
touchSurface.visibility = View.INVISIBLE
|
||||||
if (touchSurface is LaunchableView) {
|
(touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(true)
|
||||||
touchSurface.setShouldBlockVisibilityChanges(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a pre draw listener to (maybe) start the animation once the touch surface is
|
// Add a pre draw listener to (maybe) start the animation once the touch surface is
|
||||||
// actually invisible.
|
// actually invisible.
|
||||||
@@ -576,9 +615,7 @@ private class AnimatedDialog(
|
|||||||
Log.i(TAG, "Skipping animation of dialog into the touch surface")
|
Log.i(TAG, "Skipping animation of dialog into the touch surface")
|
||||||
|
|
||||||
// Make sure we allow the touch surface to change its visibility again.
|
// Make sure we allow the touch surface to change its visibility again.
|
||||||
if (touchSurface is LaunchableView) {
|
(touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(false)
|
||||||
touchSurface.setShouldBlockVisibilityChanges(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the view is invisible it's probably because of us, so we make it visible again.
|
// If the view is invisible it's probably because of us, so we make it visible again.
|
||||||
if (touchSurface.visibility == View.INVISIBLE) {
|
if (touchSurface.visibility == View.INVISIBLE) {
|
||||||
@@ -598,9 +635,7 @@ private class AnimatedDialog(
|
|||||||
},
|
},
|
||||||
onLaunchAnimationEnd = {
|
onLaunchAnimationEnd = {
|
||||||
// Make sure we allow the touch surface to change its visibility again.
|
// Make sure we allow the touch surface to change its visibility again.
|
||||||
if (touchSurface is LaunchableView) {
|
(touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(false)
|
||||||
touchSurface.setShouldBlockVisibilityChanges(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
touchSurface.visibility = View.VISIBLE
|
touchSurface.visibility = View.VISIBLE
|
||||||
dialogContentParent.visibility = View.INVISIBLE
|
dialogContentParent.visibility = View.INVISIBLE
|
||||||
@@ -796,4 +831,18 @@ private class AnimatedDialog(
|
|||||||
animator.start()
|
animator.start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun prepareForStackDismiss(): View {
|
||||||
|
if (parentAnimatedDialog == null) {
|
||||||
|
return touchSurface
|
||||||
|
}
|
||||||
|
parentAnimatedDialog.exitAnimationDisabled = true
|
||||||
|
parentAnimatedDialog.originalDialog.hide()
|
||||||
|
val view = parentAnimatedDialog.prepareForStackDismiss()
|
||||||
|
parentAnimatedDialog.originalDialog.dismiss()
|
||||||
|
// Make the touch surface invisible, so we end up animating to it when we actually
|
||||||
|
// dismiss the stack
|
||||||
|
view.visibility = View.INVISIBLE
|
||||||
|
return view
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.internal.logging.MetricsLogger;
|
import com.android.internal.logging.MetricsLogger;
|
||||||
import com.android.internal.logging.UiEventLogger;
|
import com.android.internal.logging.UiEventLogger;
|
||||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||||
@@ -37,10 +39,10 @@ import com.android.systemui.R;
|
|||||||
import com.android.systemui.plugins.FalsingManager;
|
import com.android.systemui.plugins.FalsingManager;
|
||||||
import com.android.systemui.qs.PseudoGridView;
|
import com.android.systemui.qs.PseudoGridView;
|
||||||
import com.android.systemui.qs.QSUserSwitcherEvent;
|
import com.android.systemui.qs.QSUserSwitcherEvent;
|
||||||
|
import com.android.systemui.qs.user.UserSwitchDialogController;
|
||||||
|
import com.android.systemui.statusbar.phone.SystemUIDialog;
|
||||||
import com.android.systemui.statusbar.policy.UserSwitcherController;
|
import com.android.systemui.statusbar.policy.UserSwitcherController;
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -77,7 +79,7 @@ public class UserDetailView extends PseudoGridView {
|
|||||||
private View mCurrentUserView;
|
private View mCurrentUserView;
|
||||||
private final UiEventLogger mUiEventLogger;
|
private final UiEventLogger mUiEventLogger;
|
||||||
private final FalsingManager mFalsingManager;
|
private final FalsingManager mFalsingManager;
|
||||||
private Consumer<UserSwitcherController.UserRecord> mClickCallback;
|
private @Nullable UserSwitchDialogController.DialogShower mDialogShower;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public Adapter(Context context, UserSwitcherController controller,
|
public Adapter(Context context, UserSwitcherController controller,
|
||||||
@@ -95,8 +97,17 @@ public class UserDetailView extends PseudoGridView {
|
|||||||
return createUserDetailItemView(convertView, parent, item);
|
return createUserDetailItemView(convertView, parent, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void injectCallback(Consumer<UserSwitcherController.UserRecord> clickCallback) {
|
/**
|
||||||
mClickCallback = clickCallback;
|
* If this adapter is inside a dialog, passing a
|
||||||
|
* {@link UserSwitchDialogController.DialogShower} will help animate to and from the parent
|
||||||
|
* dialog. This will also allow for dismissing the whole stack of dialogs in a single
|
||||||
|
* animation.
|
||||||
|
*
|
||||||
|
* @param shower
|
||||||
|
* @see SystemUIDialog#dismissStack()
|
||||||
|
*/
|
||||||
|
public void injectDialogShower(UserSwitchDialogController.DialogShower shower) {
|
||||||
|
mDialogShower = shower;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserDetailItemView createUserDetailItemView(View convertView, ViewGroup parent,
|
public UserDetailItemView createUserDetailItemView(View convertView, ViewGroup parent,
|
||||||
@@ -172,10 +183,7 @@ public class UserDetailView extends PseudoGridView {
|
|||||||
}
|
}
|
||||||
view.setActivated(true);
|
view.setActivated(true);
|
||||||
}
|
}
|
||||||
onUserListItemClicked(tag);
|
onUserListItemClicked(tag, mDialogShower);
|
||||||
}
|
|
||||||
if (mClickCallback != null) {
|
|
||||||
mClickCallback.accept(tag);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,9 @@
|
|||||||
|
|
||||||
package com.android.systemui.qs.user
|
package com.android.systemui.qs.user
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.DialogInterface
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@@ -84,12 +86,26 @@ class UserSwitchDialogController @VisibleForTesting constructor(
|
|||||||
doneButton.setOnClickListener { dismiss() }
|
doneButton.setOnClickListener { dismiss() }
|
||||||
|
|
||||||
val adapter = userDetailViewAdapterProvider.get()
|
val adapter = userDetailViewAdapterProvider.get()
|
||||||
adapter.injectCallback {
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
adapter.linkToViewGroup(grid)
|
adapter.linkToViewGroup(grid)
|
||||||
|
|
||||||
dialogLaunchAnimator.showFromView(this, view)
|
val hostDialog = dialogLaunchAnimator.showFromView(this, view)
|
||||||
|
adapter.injectDialogShower(DialogShowerImpl(hostDialog, dialogLaunchAnimator))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class DialogShowerImpl(
|
||||||
|
private val hostDialog: Dialog,
|
||||||
|
private val dialogLaunchAnimator: DialogLaunchAnimator
|
||||||
|
) : DialogInterface by hostDialog, DialogShower {
|
||||||
|
override fun showDialog(dialog: Dialog): Dialog {
|
||||||
|
return dialogLaunchAnimator.showFromDialog(
|
||||||
|
dialog,
|
||||||
|
parentHostDialog = hostDialog
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DialogShower : DialogInterface {
|
||||||
|
fun showDialog(dialog: Dialog): Dialog
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -218,6 +218,19 @@ public class SystemUIDialog extends AlertDialog implements ListenableDialog,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dismiss this dialog. If it was launched from another dialog using
|
||||||
|
* {@link com.android.systemui.animation.DialogLaunchAnimator#showFromView} with a
|
||||||
|
* non-{@code null} {@code parentHostDialog} parameter, also dismisses the stack of dialogs,
|
||||||
|
* animating back to the original touchSurface.
|
||||||
|
*/
|
||||||
|
public void dismissStack() {
|
||||||
|
for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) {
|
||||||
|
listener.prepareForStackDismiss();
|
||||||
|
}
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void hide() {
|
public void hide() {
|
||||||
super.hide();
|
super.hide();
|
||||||
|
|||||||
@@ -55,6 +55,8 @@ import android.view.ViewGroup;
|
|||||||
import android.view.WindowManagerGlobal;
|
import android.view.WindowManagerGlobal;
|
||||||
import android.widget.BaseAdapter;
|
import android.widget.BaseAdapter;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.jank.InteractionJankMonitor;
|
import com.android.internal.jank.InteractionJankMonitor;
|
||||||
import com.android.internal.logging.UiEventLogger;
|
import com.android.internal.logging.UiEventLogger;
|
||||||
@@ -77,6 +79,7 @@ import com.android.systemui.plugins.FalsingManager;
|
|||||||
import com.android.systemui.plugins.qs.DetailAdapter;
|
import com.android.systemui.plugins.qs.DetailAdapter;
|
||||||
import com.android.systemui.qs.QSUserSwitcherEvent;
|
import com.android.systemui.qs.QSUserSwitcherEvent;
|
||||||
import com.android.systemui.qs.tiles.UserDetailView;
|
import com.android.systemui.qs.tiles.UserDetailView;
|
||||||
|
import com.android.systemui.qs.user.UserSwitchDialogController.DialogShower;
|
||||||
import com.android.systemui.settings.UserTracker;
|
import com.android.systemui.settings.UserTracker;
|
||||||
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
|
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
|
||||||
import com.android.systemui.statusbar.phone.SystemUIDialog;
|
import com.android.systemui.statusbar.phone.SystemUIDialog;
|
||||||
@@ -460,7 +463,7 @@ public class UserSwitcherController implements Dumpable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void onUserListItemClicked(UserRecord record) {
|
void onUserListItemClicked(UserRecord record, DialogShower dialogShower) {
|
||||||
int id;
|
int id;
|
||||||
if (record.isGuest && record.info == null) {
|
if (record.isGuest && record.info == null) {
|
||||||
// No guest user. Create one.
|
// No guest user. Create one.
|
||||||
@@ -472,7 +475,7 @@ public class UserSwitcherController implements Dumpable {
|
|||||||
mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_ADD);
|
mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_ADD);
|
||||||
id = guestId;
|
id = guestId;
|
||||||
} else if (record.isAddUser) {
|
} else if (record.isAddUser) {
|
||||||
showAddUserDialog();
|
showAddUserDialog(dialogShower);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
id = record.info.id;
|
id = record.info.id;
|
||||||
@@ -481,7 +484,7 @@ public class UserSwitcherController implements Dumpable {
|
|||||||
int currUserId = mUserTracker.getUserId();
|
int currUserId = mUserTracker.getUserId();
|
||||||
if (currUserId == id) {
|
if (currUserId == id) {
|
||||||
if (record.isGuest) {
|
if (record.isGuest) {
|
||||||
showExitGuestDialog(id);
|
showExitGuestDialog(id, dialogShower);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -490,11 +493,15 @@ public class UserSwitcherController implements Dumpable {
|
|||||||
// If switching from guest, we want to bring up the guest exit dialog instead of switching
|
// If switching from guest, we want to bring up the guest exit dialog instead of switching
|
||||||
UserInfo currUserInfo = mUserManager.getUserInfo(currUserId);
|
UserInfo currUserInfo = mUserManager.getUserInfo(currUserId);
|
||||||
if (currUserInfo != null && currUserInfo.isGuest()) {
|
if (currUserInfo != null && currUserInfo.isGuest()) {
|
||||||
showExitGuestDialog(currUserId, record.resolveId());
|
showExitGuestDialog(currUserId, record.resolveId(), dialogShower);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (dialogShower != null) {
|
||||||
|
// If we haven't morphed into another dialog, it means we have just switched users.
|
||||||
|
// Then, dismiss the dialog.
|
||||||
|
dialogShower.dismiss();
|
||||||
|
}
|
||||||
switchToUserId(id);
|
switchToUserId(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -511,7 +518,7 @@ public class UserSwitcherController implements Dumpable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void showExitGuestDialog(int id) {
|
private void showExitGuestDialog(int id, DialogShower dialogShower) {
|
||||||
int newId = UserHandle.USER_SYSTEM;
|
int newId = UserHandle.USER_SYSTEM;
|
||||||
if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) {
|
if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) {
|
||||||
UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser);
|
UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser);
|
||||||
@@ -519,23 +526,31 @@ public class UserSwitcherController implements Dumpable {
|
|||||||
newId = info.id;
|
newId = info.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
showExitGuestDialog(id, newId);
|
showExitGuestDialog(id, newId, dialogShower);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void showExitGuestDialog(int id, int targetId) {
|
private void showExitGuestDialog(int id, int targetId, DialogShower dialogShower) {
|
||||||
if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
|
if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
|
||||||
mExitGuestDialog.cancel();
|
mExitGuestDialog.cancel();
|
||||||
}
|
}
|
||||||
mExitGuestDialog = new ExitGuestDialog(mContext, id, targetId);
|
mExitGuestDialog = new ExitGuestDialog(mContext, id, targetId);
|
||||||
mExitGuestDialog.show();
|
if (dialogShower != null) {
|
||||||
|
dialogShower.showDialog(mExitGuestDialog);
|
||||||
|
} else {
|
||||||
|
mExitGuestDialog.show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showAddUserDialog() {
|
private void showAddUserDialog(DialogShower dialogShower) {
|
||||||
if (mAddUserDialog != null && mAddUserDialog.isShowing()) {
|
if (mAddUserDialog != null && mAddUserDialog.isShowing()) {
|
||||||
mAddUserDialog.cancel();
|
mAddUserDialog.cancel();
|
||||||
}
|
}
|
||||||
mAddUserDialog = new AddUserDialog(mContext);
|
mAddUserDialog = new AddUserDialog(mContext);
|
||||||
mAddUserDialog.show();
|
if (dialogShower != null) {
|
||||||
|
dialogShower.showDialog(mAddUserDialog);
|
||||||
|
} else {
|
||||||
|
mAddUserDialog.show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void listenForCallState() {
|
private void listenForCallState() {
|
||||||
@@ -868,9 +883,17 @@ public class UserSwitcherController implements Dumpable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* It handles click events on user list items.
|
* It handles click events on user list items.
|
||||||
|
*
|
||||||
|
* If the user switcher is hosted in a dialog, passing a non-null {@link DialogShower}
|
||||||
|
* will allow animation to and from the parent dialog.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
public void onUserListItemClicked(UserRecord record, @Nullable DialogShower dialogShower) {
|
||||||
|
mController.onUserListItemClicked(record, dialogShower);
|
||||||
|
}
|
||||||
|
|
||||||
public void onUserListItemClicked(UserRecord record) {
|
public void onUserListItemClicked(UserRecord record) {
|
||||||
mController.onUserListItemClicked(record);
|
onUserListItemClicked(record, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName(Context context, UserRecord item) {
|
public String getName(Context context, UserRecord item) {
|
||||||
@@ -1156,7 +1179,7 @@ public class UserSwitcherController implements Dumpable {
|
|||||||
cancel();
|
cancel();
|
||||||
} else {
|
} else {
|
||||||
mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE);
|
mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE);
|
||||||
dismiss();
|
dismissStack();
|
||||||
removeGuestUser(mGuestId, mTargetId);
|
removeGuestUser(mGuestId, mTargetId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1187,7 +1210,7 @@ public class UserSwitcherController implements Dumpable {
|
|||||||
if (which == BUTTON_NEGATIVE) {
|
if (which == BUTTON_NEGATIVE) {
|
||||||
cancel();
|
cancel();
|
||||||
} else {
|
} else {
|
||||||
dismiss();
|
dismissStack();
|
||||||
if (ActivityManager.isUserAMonkey()) {
|
if (ActivityManager.isUserAMonkey()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import com.android.systemui.animation.DialogListener.DismissReason
|
|||||||
import junit.framework.Assert.assertEquals
|
import junit.framework.Assert.assertEquals
|
||||||
import junit.framework.Assert.assertFalse
|
import junit.framework.Assert.assertFalse
|
||||||
import junit.framework.Assert.assertTrue
|
import junit.framework.Assert.assertTrue
|
||||||
|
import org.junit.After
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
@@ -28,24 +29,22 @@ class DialogLaunchAnimatorTest : SysuiTestCase() {
|
|||||||
private val dialogLaunchAnimator =
|
private val dialogLaunchAnimator =
|
||||||
DialogLaunchAnimator(context, launchAnimator, hostDialogprovider)
|
DialogLaunchAnimator(context, launchAnimator, hostDialogprovider)
|
||||||
|
|
||||||
|
private val attachedViews = mutableSetOf<View>()
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
runOnMainThreadAndWaitForIdleSync {
|
||||||
|
attachedViews.forEach {
|
||||||
|
ViewUtils.detachView(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testShowDialogFromView() {
|
fun testShowDialogFromView() {
|
||||||
// Show the dialog. showFromView() must be called on the main thread with a dialog created
|
// Show the dialog. showFromView() must be called on the main thread with a dialog created
|
||||||
// on the main thread too.
|
// on the main thread too.
|
||||||
val (dialog, hostDialog) = runOnMainThreadAndWaitForIdleSync {
|
val (dialog, hostDialog) = createDialogAndHostDialog()
|
||||||
val touchSurfaceRoot = LinearLayout(context)
|
|
||||||
val touchSurface = View(context)
|
|
||||||
touchSurfaceRoot.addView(touchSurface)
|
|
||||||
|
|
||||||
// We need to attach the root to the window manager otherwise the exit animation will
|
|
||||||
// be skipped
|
|
||||||
ViewUtils.attachView(touchSurfaceRoot)
|
|
||||||
|
|
||||||
val dialog = TestDialog(context)
|
|
||||||
val hostDialog =
|
|
||||||
dialogLaunchAnimator.showFromView(dialog, touchSurface) as TestHostDialog
|
|
||||||
dialog to hostDialog
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only the host dialog is actually showing.
|
// Only the host dialog is actually showing.
|
||||||
assertTrue(hostDialog.isShowing)
|
assertTrue(hostDialog.isShowing)
|
||||||
@@ -100,6 +99,51 @@ class DialogLaunchAnimatorTest : SysuiTestCase() {
|
|||||||
assertTrue(dialog.onStopCalled)
|
assertTrue(dialog.onStopCalled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testStackedDialogsDismissesAll() {
|
||||||
|
val (_, hostDialogFirst) = createDialogAndHostDialog()
|
||||||
|
val (dialogSecond, hostDialogSecond) = createDialogAndHostDialogFromDialog(hostDialogFirst)
|
||||||
|
|
||||||
|
runOnMainThreadAndWaitForIdleSync {
|
||||||
|
dialogLaunchAnimator.disableAllCurrentDialogsExitAnimations()
|
||||||
|
dialogSecond.dismissStack()
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue(hostDialogSecond.wasDismissed)
|
||||||
|
assertTrue(hostDialogFirst.wasDismissed)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createDialogAndHostDialog(): Pair<TestDialog, TestHostDialog> {
|
||||||
|
return runOnMainThreadAndWaitForIdleSync {
|
||||||
|
val touchSurfaceRoot = LinearLayout(context)
|
||||||
|
val touchSurface = View(context)
|
||||||
|
touchSurfaceRoot.addView(touchSurface)
|
||||||
|
|
||||||
|
// We need to attach the root to the window manager otherwise the exit animation will
|
||||||
|
// be skipped
|
||||||
|
ViewUtils.attachView(touchSurfaceRoot)
|
||||||
|
attachedViews.add(touchSurfaceRoot)
|
||||||
|
|
||||||
|
val dialog = TestDialog(context)
|
||||||
|
val hostDialog =
|
||||||
|
dialogLaunchAnimator.showFromView(dialog, touchSurface) as TestHostDialog
|
||||||
|
dialog to hostDialog
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createDialogAndHostDialogFromDialog(
|
||||||
|
hostParent: Dialog
|
||||||
|
): Pair<TestDialog, TestHostDialog> {
|
||||||
|
return runOnMainThreadAndWaitForIdleSync {
|
||||||
|
val dialog = TestDialog(context)
|
||||||
|
val hostDialog = dialogLaunchAnimator.showFromDialog(
|
||||||
|
dialog,
|
||||||
|
hostParent
|
||||||
|
) as TestHostDialog
|
||||||
|
dialog to hostDialog
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun <T : Any> runOnMainThreadAndWaitForIdleSync(f: () -> T): T {
|
private fun <T : Any> runOnMainThreadAndWaitForIdleSync(f: () -> T): T {
|
||||||
lateinit var result: T
|
lateinit var result: T
|
||||||
context.mainExecutor.execute {
|
context.mainExecutor.execute {
|
||||||
@@ -198,6 +242,11 @@ class DialogLaunchAnimatorTest : SysuiTestCase() {
|
|||||||
notifyListeners { onShow() }
|
notifyListeners { onShow() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun dismissStack() {
|
||||||
|
notifyListeners { prepareForStackDismiss() }
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
private fun notifyListeners(notify: DialogListener.() -> Unit) {
|
private fun notifyListeners(notify: DialogListener.() -> Unit) {
|
||||||
for (listener in HashSet(listeners)) {
|
for (listener in HashSet(listeners)) {
|
||||||
listener.notify()
|
listener.notify()
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.systemui.qs.user
|
package com.android.systemui.qs.user
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.testing.AndroidTestingRunner
|
import android.testing.AndroidTestingRunner
|
||||||
@@ -27,7 +28,7 @@ import com.android.systemui.plugins.ActivityStarter
|
|||||||
import com.android.systemui.plugins.FalsingManager
|
import com.android.systemui.plugins.FalsingManager
|
||||||
import com.android.systemui.qs.PseudoGridView
|
import com.android.systemui.qs.PseudoGridView
|
||||||
import com.android.systemui.qs.tiles.UserDetailView
|
import com.android.systemui.qs.tiles.UserDetailView
|
||||||
import com.android.systemui.statusbar.policy.UserSwitcherController
|
import com.android.systemui.util.mockito.any
|
||||||
import com.android.systemui.util.mockito.argumentCaptor
|
import com.android.systemui.util.mockito.argumentCaptor
|
||||||
import com.android.systemui.util.mockito.capture
|
import com.android.systemui.util.mockito.capture
|
||||||
import com.android.systemui.util.mockito.eq
|
import com.android.systemui.util.mockito.eq
|
||||||
@@ -39,15 +40,13 @@ import org.mockito.ArgumentMatcher
|
|||||||
import org.mockito.Captor
|
import org.mockito.Captor
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.Mockito.`when`
|
import org.mockito.Mockito.`when`
|
||||||
import org.mockito.Mockito.any
|
import org.mockito.Mockito.anyBoolean
|
||||||
import org.mockito.Mockito.anyInt
|
import org.mockito.Mockito.anyInt
|
||||||
import org.mockito.Mockito.argThat
|
import org.mockito.Mockito.argThat
|
||||||
import org.mockito.Mockito.inOrder
|
import org.mockito.Mockito.inOrder
|
||||||
import org.mockito.Mockito.mock
|
|
||||||
import org.mockito.Mockito.never
|
import org.mockito.Mockito.never
|
||||||
import org.mockito.Mockito.verify
|
import org.mockito.Mockito.verify
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
import java.util.function.Consumer
|
|
||||||
|
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWith(AndroidTestingRunner::class)
|
@RunWith(AndroidTestingRunner::class)
|
||||||
@@ -71,6 +70,8 @@ class UserSwitchDialogControllerTest : SysuiTestCase() {
|
|||||||
private lateinit var gridView: PseudoGridView
|
private lateinit var gridView: PseudoGridView
|
||||||
@Mock
|
@Mock
|
||||||
private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
|
private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
|
||||||
|
@Mock
|
||||||
|
private lateinit var hostDialog: Dialog
|
||||||
@Captor
|
@Captor
|
||||||
private lateinit var clickCaptor: ArgumentCaptor<View.OnClickListener>
|
private lateinit var clickCaptor: ArgumentCaptor<View.OnClickListener>
|
||||||
|
|
||||||
@@ -85,6 +86,8 @@ class UserSwitchDialogControllerTest : SysuiTestCase() {
|
|||||||
`when`(dialog.grid).thenReturn(gridView)
|
`when`(dialog.grid).thenReturn(gridView)
|
||||||
|
|
||||||
`when`(launchView.context).thenReturn(mContext)
|
`when`(launchView.context).thenReturn(mContext)
|
||||||
|
`when`(dialogLaunchAnimator.showFromView(any(), any(), anyBoolean()))
|
||||||
|
.thenReturn(hostDialog)
|
||||||
|
|
||||||
controller = UserSwitchDialogController(
|
controller = UserSwitchDialogController(
|
||||||
{ userDetailViewAdapter },
|
{ userDetailViewAdapter },
|
||||||
@@ -188,15 +191,15 @@ class UserSwitchDialogControllerTest : SysuiTestCase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun callbackFromDetailView_dismissesDialog() {
|
fun callbackFromDialogShower_dismissesDialog() {
|
||||||
val captor = argumentCaptor<Consumer<UserSwitcherController.UserRecord>>()
|
val captor = argumentCaptor<UserSwitchDialogController.DialogShower>()
|
||||||
|
|
||||||
controller.showDialog(launchView)
|
controller.showDialog(launchView)
|
||||||
verify(userDetailViewAdapter).injectCallback(capture(captor))
|
verify(userDetailViewAdapter).injectDialogShower(capture(captor))
|
||||||
|
|
||||||
captor.value.accept(mock(UserSwitcherController.UserRecord::class.java))
|
captor.value.dismiss()
|
||||||
|
|
||||||
verify(dialog).dismiss()
|
verify(hostDialog).dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
private class IntentMatcher(private val action: String) : ArgumentMatcher<Intent> {
|
private class IntentMatcher(private val action: String) : ArgumentMatcher<Intent> {
|
||||||
|
|||||||
@@ -44,13 +44,16 @@ import com.android.systemui.dump.DumpManager
|
|||||||
import com.android.systemui.plugins.ActivityStarter
|
import com.android.systemui.plugins.ActivityStarter
|
||||||
import com.android.systemui.plugins.FalsingManager
|
import com.android.systemui.plugins.FalsingManager
|
||||||
import com.android.systemui.qs.QSUserSwitcherEvent
|
import com.android.systemui.qs.QSUserSwitcherEvent
|
||||||
|
import com.android.systemui.qs.user.UserSwitchDialogController
|
||||||
import com.android.systemui.settings.UserTracker
|
import com.android.systemui.settings.UserTracker
|
||||||
|
import com.android.systemui.statusbar.phone.NotificationShadeWindowView
|
||||||
import com.android.systemui.telephony.TelephonyListenerManager
|
import com.android.systemui.telephony.TelephonyListenerManager
|
||||||
import com.android.systemui.util.concurrency.FakeExecutor
|
import com.android.systemui.util.concurrency.FakeExecutor
|
||||||
import com.android.systemui.util.settings.SecureSettings
|
import com.android.systemui.util.settings.SecureSettings
|
||||||
import com.android.systemui.util.time.FakeSystemClock
|
import com.android.systemui.util.time.FakeSystemClock
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertNotNull
|
import org.junit.Assert.assertNotNull
|
||||||
|
import org.junit.Assert.assertFalse
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
@@ -85,6 +88,8 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
|||||||
@Mock private lateinit var dumpManager: DumpManager
|
@Mock private lateinit var dumpManager: DumpManager
|
||||||
@Mock private lateinit var interactionJankMonitor: InteractionJankMonitor
|
@Mock private lateinit var interactionJankMonitor: InteractionJankMonitor
|
||||||
@Mock private lateinit var latencyTracker: LatencyTracker
|
@Mock private lateinit var latencyTracker: LatencyTracker
|
||||||
|
@Mock private lateinit var dialogShower: UserSwitchDialogController.DialogShower
|
||||||
|
@Mock private lateinit var notificationShadeWindowView: NotificationShadeWindowView
|
||||||
private lateinit var testableLooper: TestableLooper
|
private lateinit var testableLooper: TestableLooper
|
||||||
private lateinit var uiBgExecutor: FakeExecutor
|
private lateinit var uiBgExecutor: FakeExecutor
|
||||||
private lateinit var uiEventLogger: UiEventLoggerFake
|
private lateinit var uiEventLogger: UiEventLoggerFake
|
||||||
@@ -98,6 +103,8 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
|||||||
private val guestId = 1234
|
private val guestId = 1234
|
||||||
private val guestInfo = UserInfo(guestId, "Guest", null,
|
private val guestInfo = UserInfo(guestId, "Guest", null,
|
||||||
UserInfo.FLAG_FULL or UserInfo.FLAG_GUEST, UserManager.USER_TYPE_FULL_GUEST)
|
UserInfo.FLAG_FULL or UserInfo.FLAG_GUEST, UserManager.USER_TYPE_FULL_GUEST)
|
||||||
|
private val secondaryUser =
|
||||||
|
UserInfo(10, "Secondary", null, 0, UserManager.USER_TYPE_FULL_SECONDARY)
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
@@ -114,6 +121,7 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
|||||||
mock(FingerprintManager::class.java))
|
mock(FingerprintManager::class.java))
|
||||||
|
|
||||||
`when`(userManager.canAddMoreUsers()).thenReturn(true)
|
`when`(userManager.canAddMoreUsers()).thenReturn(true)
|
||||||
|
`when`(notificationShadeWindowView.context).thenReturn(context)
|
||||||
|
|
||||||
userSwitcherController = UserSwitcherController(
|
userSwitcherController = UserSwitcherController(
|
||||||
context,
|
context,
|
||||||
@@ -139,6 +147,26 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
|||||||
userSwitcherController.mPauseRefreshUsers = true
|
userSwitcherController.mPauseRefreshUsers = true
|
||||||
|
|
||||||
picture = UserIcons.convertToBitmap(context.getDrawable(R.drawable.ic_avatar_user))
|
picture = UserIcons.convertToBitmap(context.getDrawable(R.drawable.ic_avatar_user))
|
||||||
|
userSwitcherController.init(notificationShadeWindowView)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSwitchUser_parentDialogDismissed() {
|
||||||
|
val otherUserRecord = UserSwitcherController.UserRecord(
|
||||||
|
secondaryUser,
|
||||||
|
picture,
|
||||||
|
false /* guest */,
|
||||||
|
false /* current */,
|
||||||
|
false /* isAddUser */,
|
||||||
|
false /* isRestricted */,
|
||||||
|
true /* isSwitchToEnabled */)
|
||||||
|
`when`(userTracker.userId).thenReturn(ownerId)
|
||||||
|
`when`(userTracker.userInfo).thenReturn(ownerInfo)
|
||||||
|
|
||||||
|
userSwitcherController.onUserListItemClicked(otherUserRecord, dialogShower)
|
||||||
|
testableLooper.processAllMessages()
|
||||||
|
|
||||||
|
verify(dialogShower).dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -156,7 +184,7 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
|||||||
|
|
||||||
`when`(userManager.createGuest(any(), anyString())).thenReturn(guestInfo)
|
`when`(userManager.createGuest(any(), anyString())).thenReturn(guestInfo)
|
||||||
|
|
||||||
userSwitcherController.onUserListItemClicked(emptyGuestUserRecord)
|
userSwitcherController.onUserListItemClicked(emptyGuestUserRecord, null)
|
||||||
testableLooper.processAllMessages()
|
testableLooper.processAllMessages()
|
||||||
verify(interactionJankMonitor).begin(any())
|
verify(interactionJankMonitor).begin(any())
|
||||||
verify(latencyTracker).onActionStart(LatencyTracker.ACTION_USER_SWITCH)
|
verify(latencyTracker).onActionStart(LatencyTracker.ACTION_USER_SWITCH)
|
||||||
@@ -165,6 +193,26 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
|||||||
assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_ADD.id, uiEventLogger.eventId(0))
|
assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_ADD.id, uiEventLogger.eventId(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAddGuest_parentDialogDismissed() {
|
||||||
|
val emptyGuestUserRecord = UserSwitcherController.UserRecord(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
true /* guest */,
|
||||||
|
false /* current */,
|
||||||
|
false /* isAddUser */,
|
||||||
|
false /* isRestricted */,
|
||||||
|
true /* isSwitchToEnabled */)
|
||||||
|
`when`(userTracker.userId).thenReturn(ownerId)
|
||||||
|
`when`(userTracker.userInfo).thenReturn(ownerInfo)
|
||||||
|
|
||||||
|
`when`(userManager.createGuest(any(), anyString())).thenReturn(guestInfo)
|
||||||
|
|
||||||
|
userSwitcherController.onUserListItemClicked(emptyGuestUserRecord, dialogShower)
|
||||||
|
testableLooper.processAllMessages()
|
||||||
|
verify(dialogShower).dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testRemoveGuest_removeButtonPressed_isLogged() {
|
fun testRemoveGuest_removeButtonPressed_isLogged() {
|
||||||
val currentGuestUserRecord = UserSwitcherController.UserRecord(
|
val currentGuestUserRecord = UserSwitcherController.UserRecord(
|
||||||
@@ -178,7 +226,7 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
|||||||
`when`(userTracker.userId).thenReturn(guestInfo.id)
|
`when`(userTracker.userId).thenReturn(guestInfo.id)
|
||||||
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
||||||
|
|
||||||
userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
|
userSwitcherController.onUserListItemClicked(currentGuestUserRecord, null)
|
||||||
assertNotNull(userSwitcherController.mExitGuestDialog)
|
assertNotNull(userSwitcherController.mExitGuestDialog)
|
||||||
userSwitcherController.mExitGuestDialog
|
userSwitcherController.mExitGuestDialog
|
||||||
.getButton(DialogInterface.BUTTON_POSITIVE).performClick()
|
.getButton(DialogInterface.BUTTON_POSITIVE).performClick()
|
||||||
@@ -187,6 +235,46 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
|||||||
assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE.id, uiEventLogger.eventId(0))
|
assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE.id, uiEventLogger.eventId(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRemoveGuest_removeButtonPressed_dialogDismissed() {
|
||||||
|
val currentGuestUserRecord = UserSwitcherController.UserRecord(
|
||||||
|
guestInfo,
|
||||||
|
picture,
|
||||||
|
true /* guest */,
|
||||||
|
true /* current */,
|
||||||
|
false /* isAddUser */,
|
||||||
|
false /* isRestricted */,
|
||||||
|
true /* isSwitchToEnabled */)
|
||||||
|
`when`(userTracker.userId).thenReturn(guestInfo.id)
|
||||||
|
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
||||||
|
|
||||||
|
userSwitcherController.onUserListItemClicked(currentGuestUserRecord, null)
|
||||||
|
assertNotNull(userSwitcherController.mExitGuestDialog)
|
||||||
|
userSwitcherController.mExitGuestDialog
|
||||||
|
.getButton(DialogInterface.BUTTON_POSITIVE).performClick()
|
||||||
|
testableLooper.processAllMessages()
|
||||||
|
assertFalse(userSwitcherController.mExitGuestDialog.isShowing)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRemoveGuest_dialogShowerUsed() {
|
||||||
|
val currentGuestUserRecord = UserSwitcherController.UserRecord(
|
||||||
|
guestInfo,
|
||||||
|
picture,
|
||||||
|
true /* guest */,
|
||||||
|
true /* current */,
|
||||||
|
false /* isAddUser */,
|
||||||
|
false /* isRestricted */,
|
||||||
|
true /* isSwitchToEnabled */)
|
||||||
|
`when`(userTracker.userId).thenReturn(guestInfo.id)
|
||||||
|
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
||||||
|
|
||||||
|
userSwitcherController.onUserListItemClicked(currentGuestUserRecord, dialogShower)
|
||||||
|
assertNotNull(userSwitcherController.mExitGuestDialog)
|
||||||
|
testableLooper.processAllMessages()
|
||||||
|
verify(dialogShower).showDialog(userSwitcherController.mExitGuestDialog)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testRemoveGuest_cancelButtonPressed_isNotLogged() {
|
fun testRemoveGuest_cancelButtonPressed_isNotLogged() {
|
||||||
val currentGuestUserRecord = UserSwitcherController.UserRecord(
|
val currentGuestUserRecord = UserSwitcherController.UserRecord(
|
||||||
@@ -200,7 +288,7 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
|||||||
`when`(userTracker.userId).thenReturn(guestId)
|
`when`(userTracker.userId).thenReturn(guestId)
|
||||||
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
`when`(userTracker.userInfo).thenReturn(guestInfo)
|
||||||
|
|
||||||
userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
|
userSwitcherController.onUserListItemClicked(currentGuestUserRecord, null)
|
||||||
assertNotNull(userSwitcherController.mExitGuestDialog)
|
assertNotNull(userSwitcherController.mExitGuestDialog)
|
||||||
userSwitcherController.mExitGuestDialog
|
userSwitcherController.mExitGuestDialog
|
||||||
.getButton(DialogInterface.BUTTON_NEGATIVE).performClick()
|
.getButton(DialogInterface.BUTTON_NEGATIVE).performClick()
|
||||||
@@ -226,7 +314,7 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
|||||||
eq(GuestResumeSessionReceiver.SETTING_GUEST_HAS_LOGGED_IN), anyInt(), anyInt()))
|
eq(GuestResumeSessionReceiver.SETTING_GUEST_HAS_LOGGED_IN), anyInt(), anyInt()))
|
||||||
.thenReturn(1)
|
.thenReturn(1)
|
||||||
|
|
||||||
userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
|
userSwitcherController.onUserListItemClicked(currentGuestUserRecord, null)
|
||||||
|
|
||||||
// Simulate a user switch event
|
// Simulate a user switch event
|
||||||
val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, guestId)
|
val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, guestId)
|
||||||
@@ -260,7 +348,7 @@ class UserSwitcherControllerTest : SysuiTestCase() {
|
|||||||
eq(GuestResumeSessionReceiver.SETTING_GUEST_HAS_LOGGED_IN), anyInt(), anyInt()))
|
eq(GuestResumeSessionReceiver.SETTING_GUEST_HAS_LOGGED_IN), anyInt(), anyInt()))
|
||||||
.thenReturn(1)
|
.thenReturn(1)
|
||||||
|
|
||||||
userSwitcherController.onUserListItemClicked(currentGuestUserRecord)
|
userSwitcherController.onUserListItemClicked(currentGuestUserRecord, null)
|
||||||
|
|
||||||
// Simulate a user switch event
|
// Simulate a user switch event
|
||||||
val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, guestId)
|
val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, guestId)
|
||||||
|
|||||||
Reference in New Issue
Block a user