Merge "[4/n] Implement restart confirmation dialog" into tm-qpr-dev am: 4f0806e6bb

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20764909

Change-Id: I78f7df584daa27ff58209dbe5bbdd1398fc2d4cd
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Massimo Carli
2023-01-24 10:26:19 +00:00
committed by Automerger Merge Worker
11 changed files with 814 additions and 46 deletions

View File

@@ -16,11 +16,12 @@
package com.android.wm.shell.compatui;
import android.annotation.NonNull;
import android.app.TaskInfo;
import android.content.Context;
import android.content.SharedPreferences;
import android.provider.DeviceConfig;
import androidx.annotation.NonNull;
import com.android.wm.shell.R;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.annotations.ShellMainThread;
@@ -34,11 +35,23 @@ import javax.inject.Inject;
@WMSingleton
public class CompatUIConfiguration implements DeviceConfig.OnPropertiesChangedListener {
static final String KEY_ENABLE_LETTERBOX_RESTART_DIALOG = "enable_letterbox_restart_dialog";
private static final String KEY_ENABLE_LETTERBOX_RESTART_DIALOG =
"enable_letterbox_restart_dialog";
static final String KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION =
private static final String KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION =
"enable_letterbox_reachability_education";
/**
* The name of the {@link SharedPreferences} that holds which user has seen the Restart
* confirmation dialog.
*/
private static final String DONT_SHOW_RESTART_DIALOG_PREF_NAME = "dont_show_restart_dialog";
/**
* The {@link SharedPreferences} instance for {@link #DONT_SHOW_RESTART_DIALOG_PREF_NAME}.
*/
private final SharedPreferences mSharedPreferences;
// Whether the extended restart dialog is enabled
private boolean mIsRestartDialogEnabled;
@@ -70,6 +83,8 @@ public class CompatUIConfiguration implements DeviceConfig.OnPropertiesChangedLi
false);
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_APP_COMPAT, mainExecutor,
this);
mSharedPreferences = context.getSharedPreferences(DONT_SHOW_RESTART_DIALOG_PREF_NAME,
Context.MODE_PRIVATE);
}
/**
@@ -102,6 +117,20 @@ public class CompatUIConfiguration implements DeviceConfig.OnPropertiesChangedLi
mIsReachabilityEducationOverrideEnabled = enabled;
}
boolean getDontShowRestartDialogAgain(TaskInfo taskInfo) {
final int userId = taskInfo.userId;
final String packageName = taskInfo.topActivity.getPackageName();
return mSharedPreferences.getBoolean(
getDontShowAgainRestartKey(userId, packageName), /* default= */ false);
}
void setDontShowRestartDialogAgain(TaskInfo taskInfo) {
final int userId = taskInfo.userId;
final String packageName = taskInfo.topActivity.getPackageName();
mSharedPreferences.edit().putBoolean(getDontShowAgainRestartKey(userId, packageName),
true).apply();
}
@Override
public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
// TODO(b/263349751): Update flag and default value to true
@@ -116,4 +145,8 @@ public class CompatUIConfiguration implements DeviceConfig.OnPropertiesChangedLi
KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION, false);
}
}
}
private String getDontShowAgainRestartKey(int userId, String packageName) {
return packageName + "@" + userId;
}
}

View File

@@ -24,6 +24,7 @@ import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import android.view.Display;
import android.view.InsetsSourceControl;
@@ -49,6 +50,7 @@ import com.android.wm.shell.transition.Transitions;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
@@ -90,6 +92,18 @@ public class CompatUIController implements OnDisplaysChangedListener,
*/
private final SparseArray<CompatUIWindowManager> mActiveCompatLayouts = new SparseArray<>(0);
/**
* {@link SparseArray} that maps task ids to {@link RestartDialogWindowManager} that are
* currently visible
*/
private final SparseArray<RestartDialogWindowManager> mTaskIdToRestartDialogWindowManagerMap =
new SparseArray<>(0);
/**
* {@link Set} of task ids for which we need to display a restart confirmation dialog
*/
private Set<Integer> mSetOfTaskIdsShowingRestartDialog = new HashSet<>();
/**
* The active Letterbox Education layout if there is one (there can be at most one active).
*
@@ -111,12 +125,15 @@ public class CompatUIController implements OnDisplaysChangedListener,
private final ShellExecutor mMainExecutor;
private final Lazy<Transitions> mTransitionsLazy;
private final DockStateReader mDockStateReader;
private final CompatUIConfiguration mCompatUIConfiguration;
private CompatUICallback mCallback;
// Only show each hint once automatically in the process life.
private final CompatUIHintsState mCompatUIHintsState;
private final CompatUIShellCommandHandler mCompatUIShellCommandHandler;
// Indicates if the keyguard is currently showing, in which case compat UIs shouldn't
// be shown.
private boolean mKeyguardShowing;
@@ -130,7 +147,9 @@ public class CompatUIController implements OnDisplaysChangedListener,
SyncTransactionQueue syncQueue,
ShellExecutor mainExecutor,
Lazy<Transitions> transitionsLazy,
DockStateReader dockStateReader) {
DockStateReader dockStateReader,
CompatUIConfiguration compatUIConfiguration,
CompatUIShellCommandHandler compatUIShellCommandHandler) {
mContext = context;
mShellController = shellController;
mDisplayController = displayController;
@@ -140,14 +159,17 @@ public class CompatUIController implements OnDisplaysChangedListener,
mMainExecutor = mainExecutor;
mTransitionsLazy = transitionsLazy;
mCompatUIHintsState = new CompatUIHintsState();
shellInit.addInitCallback(this::onInit, this);
mDockStateReader = dockStateReader;
mCompatUIConfiguration = compatUIConfiguration;
mCompatUIShellCommandHandler = compatUIShellCommandHandler;
shellInit.addInitCallback(this::onInit, this);
}
private void onInit() {
mShellController.addKeyguardChangeListener(this);
mDisplayController.addDisplayWindowListener(this);
mImeController.addPositionProcessor(this);
mCompatUIShellCommandHandler.onInit();
}
/** Sets the callback for UI interactions. */
@@ -164,6 +186,9 @@ public class CompatUIController implements OnDisplaysChangedListener,
*/
public void onCompatInfoChanged(TaskInfo taskInfo,
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
if (taskInfo != null && !taskInfo.topActivityInSizeCompat) {
mSetOfTaskIdsShowingRestartDialog.remove(taskInfo.taskId);
}
if (taskInfo.configuration == null || taskListener == null) {
// Null token means the current foreground activity is not in compatibility mode.
removeLayouts(taskInfo.taskId);
@@ -172,6 +197,7 @@ public class CompatUIController implements OnDisplaysChangedListener,
createOrUpdateCompatLayout(taskInfo, taskListener);
createOrUpdateLetterboxEduLayout(taskInfo, taskListener);
createOrUpdateRestartDialogLayout(taskInfo, taskListener);
}
@Override
@@ -278,7 +304,21 @@ public class CompatUIController implements OnDisplaysChangedListener,
ShellTaskOrganizer.TaskListener taskListener) {
return new CompatUIWindowManager(context,
taskInfo, mSyncQueue, mCallback, taskListener,
mDisplayController.getDisplayLayout(taskInfo.displayId), mCompatUIHintsState);
mDisplayController.getDisplayLayout(taskInfo.displayId), mCompatUIHintsState,
mCompatUIConfiguration, this::onRestartButtonClicked);
}
private void onRestartButtonClicked(
Pair<TaskInfo, ShellTaskOrganizer.TaskListener> taskInfoState) {
if (mCompatUIConfiguration.isRestartDialogEnabled()
&& !mCompatUIConfiguration.getDontShowRestartDialogAgain(
taskInfoState.first)) {
// We need to show the dialog
mSetOfTaskIdsShowingRestartDialog.add(taskInfoState.first.taskId);
onCompatInfoChanged(taskInfoState.first, taskInfoState.second);
} else {
mCallback.onSizeCompatRestartButtonClicked(taskInfoState.first.taskId);
}
}
private void createOrUpdateLetterboxEduLayout(TaskInfo taskInfo,
@@ -327,6 +367,60 @@ public class CompatUIController implements OnDisplaysChangedListener,
mActiveLetterboxEduLayout = null;
}
private void createOrUpdateRestartDialogLayout(TaskInfo taskInfo,
ShellTaskOrganizer.TaskListener taskListener) {
RestartDialogWindowManager layout =
mTaskIdToRestartDialogWindowManagerMap.get(taskInfo.taskId);
if (layout != null) {
// TODO(b/266262111) Handle theme change when taskListener changes
if (layout.getTaskListener() != taskListener) {
mSetOfTaskIdsShowingRestartDialog.remove(taskInfo.taskId);
}
layout.setRequestRestartDialog(
mSetOfTaskIdsShowingRestartDialog.contains(taskInfo.taskId));
// UI already exists, update the UI layout.
if (!layout.updateCompatInfo(taskInfo, taskListener,
showOnDisplay(layout.getDisplayId()))) {
// The layout is no longer eligible to be shown, remove from active layouts.
mTaskIdToRestartDialogWindowManagerMap.remove(taskInfo.taskId);
}
return;
}
// Create a new UI layout.
final Context context = getOrCreateDisplayContext(taskInfo.displayId);
if (context == null) {
return;
}
layout = createRestartDialogWindowManager(context, taskInfo, taskListener);
layout.setRequestRestartDialog(
mSetOfTaskIdsShowingRestartDialog.contains(taskInfo.taskId));
if (layout.createLayout(showOnDisplay(taskInfo.displayId))) {
// The new layout is eligible to be shown, add it the active layouts.
mTaskIdToRestartDialogWindowManagerMap.put(taskInfo.taskId, layout);
}
}
@VisibleForTesting
RestartDialogWindowManager createRestartDialogWindowManager(Context context, TaskInfo taskInfo,
ShellTaskOrganizer.TaskListener taskListener) {
return new RestartDialogWindowManager(context, taskInfo, mSyncQueue, taskListener,
mDisplayController.getDisplayLayout(taskInfo.displayId), mTransitionsLazy.get(),
this::onRestartDialogCallback, this::onRestartDialogDismissCallback,
mCompatUIConfiguration);
}
private void onRestartDialogCallback(
Pair<TaskInfo, ShellTaskOrganizer.TaskListener> stateInfo) {
mTaskIdToRestartDialogWindowManagerMap.remove(stateInfo.first.taskId);
mCallback.onSizeCompatRestartButtonClicked(stateInfo.first.taskId);
}
private void onRestartDialogDismissCallback(
Pair<TaskInfo, ShellTaskOrganizer.TaskListener> stateInfo) {
mSetOfTaskIdsShowingRestartDialog.remove(stateInfo.first.taskId);
onCompatInfoChanged(stateInfo.first, stateInfo.second);
}
private void removeLayouts(int taskId) {
final CompatUIWindowManager layout = mActiveCompatLayouts.get(taskId);
if (layout != null) {
@@ -338,6 +432,14 @@ public class CompatUIController implements OnDisplaysChangedListener,
mActiveLetterboxEduLayout.release();
mActiveLetterboxEduLayout = null;
}
final RestartDialogWindowManager restartLayout =
mTaskIdToRestartDialogWindowManagerMap.get(taskId);
if (restartLayout != null) {
restartLayout.release();
mTaskIdToRestartDialogWindowManagerMap.remove(taskId);
mSetOfTaskIdsShowingRestartDialog.remove(taskId);
}
}
private Context getOrCreateDisplayContext(int displayId) {
@@ -382,6 +484,14 @@ public class CompatUIController implements OnDisplaysChangedListener,
if (mActiveLetterboxEduLayout != null && condition.test(mActiveLetterboxEduLayout)) {
callback.accept(mActiveLetterboxEduLayout);
}
for (int i = 0; i < mTaskIdToRestartDialogWindowManagerMap.size(); i++) {
final int taskId = mTaskIdToRestartDialogWindowManagerMap.keyAt(i);
final RestartDialogWindowManager layout =
mTaskIdToRestartDialogWindowManagerMap.get(taskId);
if (layout != null && condition.test(layout)) {
callback.accept(layout);
}
}
}
/** An implementation of {@link OnInsetsChangedListener} for a given display id. */

View File

@@ -21,12 +21,14 @@ import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.TaskInfo;
import android.app.TaskInfo.CameraCompatControlState;
import android.content.Context;
import android.graphics.Rect;
import android.util.Log;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
@@ -38,6 +40,8 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.compatui.CompatUIController.CompatUICallback;
import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager;
import java.util.function.Consumer;
/**
* Window manager for the Size Compat restart button and Camera Compat control.
*/
@@ -50,6 +54,13 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
private final CompatUICallback mCallback;
private final CompatUIConfiguration mCompatUIConfiguration;
private final Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnRestartButtonClicked;
@NonNull
private TaskInfo mTaskInfo;
// Remember the last reported states in case visibility changes due to keyguard or IME updates.
@VisibleForTesting
boolean mHasSizeCompat;
@@ -68,12 +79,16 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
CompatUIWindowManager(Context context, TaskInfo taskInfo,
SyncTransactionQueue syncQueue, CompatUICallback callback,
ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
CompatUIHintsState compatUIHintsState) {
CompatUIHintsState compatUIHintsState, CompatUIConfiguration compatUIConfiguration,
Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onRestartButtonClicked) {
super(context, taskInfo, syncQueue, taskListener, displayLayout);
mTaskInfo = taskInfo;
mCallback = callback;
mHasSizeCompat = taskInfo.topActivityInSizeCompat;
mCameraCompatControlState = taskInfo.cameraCompatControlState;
mCompatUIHintsState = compatUIHintsState;
mCompatUIConfiguration = compatUIConfiguration;
mOnRestartButtonClicked = onRestartButtonClicked;
}
@Override
@@ -119,6 +134,7 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
@Override
public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener,
boolean canShow) {
mTaskInfo = taskInfo;
final boolean prevHasSizeCompat = mHasSizeCompat;
final int prevCameraCompatControlState = mCameraCompatControlState;
mHasSizeCompat = taskInfo.topActivityInSizeCompat;
@@ -138,7 +154,7 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
/** Called when the restart button is clicked. */
void onRestartButtonClicked() {
mCallback.onSizeCompatRestartButtonClicked(mTaskId);
mOnRestartButtonClicked.accept(Pair.create(mTaskInfo, getTaskListener()));
}
/** Called when the camera treatment button is clicked. */

View File

@@ -151,6 +151,7 @@ public abstract class CompatUIWindowManagerAbstract extends WindowlessWindowMana
@Override
public void setConfiguration(Configuration configuration) {
super.setConfiguration(configuration);
// TODO(b/266262111): Investigate loss of theme configuration when switching TaskListener
mContext = mContext.createConfigurationContext(configuration);
}
@@ -169,6 +170,10 @@ public abstract class CompatUIWindowManagerAbstract extends WindowlessWindowMana
initSurface(mLeash);
}
protected ShellTaskOrganizer.TaskListener getTaskListener() {
return mTaskListener;
}
/** Inits the z-order of the surface. */
private void initSurface(SurfaceControl leash) {
final int z = getZOrder();

View File

@@ -0,0 +1,107 @@
/*
* Copyright (C) 2022 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.wm.shell.compatui;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.CheckBox;
import android.widget.TextView;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.android.wm.shell.R;
import java.util.function.Consumer;
/**
* Container for a SCM restart confirmation dialog and background dim.
*/
public class RestartDialogLayout extends ConstraintLayout implements DialogContainerSupplier {
private View mDialogContainer;
private TextView mDialogTitle;
private Drawable mBackgroundDim;
public RestartDialogLayout(Context context) {
this(context, null);
}
public RestartDialogLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RestartDialogLayout(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public RestartDialogLayout(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public View getDialogContainerView() {
return mDialogContainer;
}
TextView getDialogTitle() {
return mDialogTitle;
}
@Override
public Drawable getBackgroundDimDrawable() {
return mBackgroundDim;
}
/**
* Register a callback for the dismiss button and background dim.
*
* @param callback The callback to register or null if all on click listeners should be removed.
*/
void setDismissOnClickListener(@Nullable Runnable callback) {
final OnClickListener listener = callback == null ? null : view -> callback.run();
findViewById(R.id.letterbox_restart_dialog_dismiss_button).setOnClickListener(listener);
}
/**
* Register a callback for the restart button
*
* @param callback The callback to register or null if all on click listeners should be removed.
*/
void setRestartOnClickListener(@Nullable Consumer<Boolean> callback) {
final CheckBox dontShowAgainCheckbox = findViewById(R.id.letterbox_restart_dialog_checkbox);
final OnClickListener listener = callback == null ? null : view -> callback.accept(
dontShowAgainCheckbox.isChecked());
findViewById(R.id.letterbox_restart_dialog_restart_button).setOnClickListener(listener);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mDialogContainer = findViewById(R.id.letterbox_restart_dialog_container);
mDialogTitle = findViewById(R.id.letterbox_restart_dialog_title);
mBackgroundDim = getBackground().mutate();
// Set the alpha of the background dim to 0 for enter animation.
mBackgroundDim.setAlpha(0);
// We add a no-op on-click listener to the dialog container so that clicks on it won't
// propagate to the listener of the layout (which represents the background dim).
mDialogContainer.setOnClickListener(view -> {});
}
}

View File

@@ -0,0 +1,259 @@
/*
* Copyright (C) 2022 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.wm.shell.compatui;
import static android.provider.Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.TaskInfo;
import android.content.Context;
import android.graphics.Rect;
import android.provider.Settings;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.transition.Transitions;
import java.util.function.Consumer;
/**
* Window manager for the Restart Dialog.
*
* TODO(b/263484314): Create abstraction of RestartDialogWindowManager and LetterboxEduWindowManager
*/
class RestartDialogWindowManager extends CompatUIWindowManagerAbstract {
/**
* The restart dialog should be the topmost child of the Task in case there can be more
* than one child.
*/
private static final int Z_ORDER = Integer.MAX_VALUE;
private final DialogAnimationController<RestartDialogLayout> mAnimationController;
private final Transitions mTransitions;
// Remember the last reported state in case visibility changes due to keyguard or IME updates.
private boolean mRequestRestartDialog;
private final Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnDismissCallback;
private final Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnRestartCallback;
private final CompatUIConfiguration mCompatUIConfiguration;
/**
* The vertical margin between the dialog container and the task stable bounds (excluding
* insets).
*/
private final int mDialogVerticalMargin;
@NonNull
private TaskInfo mTaskInfo;
@Nullable
@VisibleForTesting
RestartDialogLayout mLayout;
RestartDialogWindowManager(Context context, TaskInfo taskInfo,
SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener,
DisplayLayout displayLayout, Transitions transitions,
Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onRestartCallback,
Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onDismissCallback,
CompatUIConfiguration compatUIConfiguration) {
this(context, taskInfo, syncQueue, taskListener, displayLayout, transitions,
onRestartCallback, onDismissCallback,
new DialogAnimationController<>(context, "RestartDialogWindowManager"),
compatUIConfiguration);
}
@VisibleForTesting
RestartDialogWindowManager(Context context, TaskInfo taskInfo,
SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener,
DisplayLayout displayLayout, Transitions transitions,
Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onRestartCallback,
Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onDismissCallback,
DialogAnimationController<RestartDialogLayout> animationController,
CompatUIConfiguration compatUIConfiguration) {
super(context, taskInfo, syncQueue, taskListener, displayLayout);
mTaskInfo = taskInfo;
mTransitions = transitions;
mOnDismissCallback = onDismissCallback;
mOnRestartCallback = onRestartCallback;
mAnimationController = animationController;
mDialogVerticalMargin = (int) mContext.getResources().getDimension(
R.dimen.letterbox_restart_dialog_margin);
mCompatUIConfiguration = compatUIConfiguration;
}
@Override
protected int getZOrder() {
return Z_ORDER;
}
@Override
@Nullable
protected View getLayout() {
return mLayout;
}
@Override
protected void removeLayout() {
mLayout = null;
}
@Override
protected boolean eligibleToShowLayout() {
// We don't show this dialog if the user has explicitly selected so clicking on a checkbox.
return mRequestRestartDialog && !isTaskbarEduShowing() && (mLayout != null
|| !mCompatUIConfiguration.getDontShowRestartDialogAgain(mTaskInfo));
}
@Override
protected View createLayout() {
mLayout = inflateLayout();
updateDialogMargins();
// startEnterAnimation will be called immediately if shell-transitions are disabled.
mTransitions.runOnIdle(this::startEnterAnimation);
return mLayout;
}
void setRequestRestartDialog(boolean enabled) {
mRequestRestartDialog = enabled;
}
@Override
public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener,
boolean canShow) {
mTaskInfo = taskInfo;
return super.updateCompatInfo(taskInfo, taskListener, canShow);
}
private void updateDialogMargins() {
if (mLayout == null) {
return;
}
final View dialogContainer = mLayout.getDialogContainerView();
ViewGroup.MarginLayoutParams marginParams =
(ViewGroup.MarginLayoutParams) dialogContainer.getLayoutParams();
final Rect taskBounds = getTaskBounds();
final Rect taskStableBounds = getTaskStableBounds();
marginParams.topMargin = taskStableBounds.top - taskBounds.top + mDialogVerticalMargin;
marginParams.bottomMargin =
taskBounds.bottom - taskStableBounds.bottom + mDialogVerticalMargin;
dialogContainer.setLayoutParams(marginParams);
}
private RestartDialogLayout inflateLayout() {
return (RestartDialogLayout) LayoutInflater.from(mContext).inflate(
R.layout.letterbox_restart_dialog_layout, null);
}
private void startEnterAnimation() {
if (mLayout == null) {
// Dialog has already been released.
return;
}
mAnimationController.startEnterAnimation(mLayout, /* endCallback= */
this::onDialogEnterAnimationEnded);
}
private void onDialogEnterAnimationEnded() {
if (mLayout == null) {
// Dialog has already been released.
return;
}
mLayout.setDismissOnClickListener(this::onDismiss);
mLayout.setRestartOnClickListener(dontShowAgain -> {
if (mLayout != null) {
mLayout.setDismissOnClickListener(null);
mAnimationController.startExitAnimation(mLayout, () -> {
release();
});
}
if (dontShowAgain) {
mCompatUIConfiguration.setDontShowRestartDialogAgain(mTaskInfo);
}
mOnRestartCallback.accept(Pair.create(mTaskInfo, getTaskListener()));
});
// Focus on the dialog title for accessibility.
mLayout.getDialogTitle().sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
}
private void onDismiss() {
if (mLayout == null) {
return;
}
mLayout.setDismissOnClickListener(null);
mAnimationController.startExitAnimation(mLayout, () -> {
release();
mOnDismissCallback.accept(Pair.create(mTaskInfo, getTaskListener()));
});
}
@Override
public void release() {
mAnimationController.cancelAnimation();
super.release();
}
@Override
protected void onParentBoundsChanged() {
if (mLayout == null) {
return;
}
// Both the layout dimensions and dialog margins depend on the parent bounds.
WindowManager.LayoutParams windowLayoutParams = getWindowLayoutParams();
mLayout.setLayoutParams(windowLayoutParams);
updateDialogMargins();
relayout(windowLayoutParams);
}
@Override
protected void updateSurfacePosition() {
// Nothing to do, since the position of the surface is fixed to the top left corner (0,0)
// of the task (parent surface), which is the default position of a surface.
}
@Override
protected WindowManager.LayoutParams getWindowLayoutParams() {
final Rect taskBounds = getTaskBounds();
return getWindowLayoutParams(/* width= */ taskBounds.width(), /* height= */
taskBounds.height());
}
@VisibleForTesting
boolean isTaskbarEduShowing() {
return Settings.Secure.getInt(mContext.getContentResolver(),
LAUNCHER_TASKBAR_EDUCATION_SHOWING, /* def= */ 0) == 1;
}
}

View File

@@ -56,7 +56,9 @@ import com.android.wm.shell.common.annotations.ShellAnimationThread;
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
import com.android.wm.shell.compatui.CompatUIConfiguration;
import com.android.wm.shell.compatui.CompatUIController;
import com.android.wm.shell.compatui.CompatUIShellCommandHandler;
import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.desktopmode.DesktopModeController;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
@@ -196,10 +198,11 @@ public abstract class WMShellBaseModule {
DisplayController displayController, DisplayInsetsController displayInsetsController,
DisplayImeController imeController, SyncTransactionQueue syncQueue,
@ShellMainThread ShellExecutor mainExecutor, Lazy<Transitions> transitionsLazy,
DockStateReader dockStateReader) {
DockStateReader dockStateReader, CompatUIConfiguration compatUIConfiguration,
CompatUIShellCommandHandler compatUIShellCommandHandler) {
return new CompatUIController(context, shellInit, shellController, displayController,
displayInsetsController, imeController, syncQueue, mainExecutor, transitionsLazy,
dockStateReader);
dockStateReader, compatUIConfiguration, compatUIShellCommandHandler);
}
@WMSingleton

View File

@@ -94,7 +94,10 @@ public class CompatUIControllerTest extends ShellTestCase {
private @Mock Lazy<Transitions> mMockTransitionsLazy;
private @Mock CompatUIWindowManager mMockCompatLayout;
private @Mock LetterboxEduWindowManager mMockLetterboxEduLayout;
private @Mock RestartDialogWindowManager mMockRestartDialogLayout;
private @Mock DockStateReader mDockStateReader;
private @Mock CompatUIConfiguration mCompatUIConfiguration;
private @Mock CompatUIShellCommandHandler mCompatUIShellCommandHandler;
@Captor
ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor;
@@ -112,10 +115,17 @@ public class CompatUIControllerTest extends ShellTestCase {
doReturn(TASK_ID).when(mMockLetterboxEduLayout).getTaskId();
doReturn(true).when(mMockLetterboxEduLayout).createLayout(anyBoolean());
doReturn(true).when(mMockLetterboxEduLayout).updateCompatInfo(any(), any(), anyBoolean());
doReturn(DISPLAY_ID).when(mMockRestartDialogLayout).getDisplayId();
doReturn(TASK_ID).when(mMockRestartDialogLayout).getTaskId();
doReturn(true).when(mMockRestartDialogLayout).createLayout(anyBoolean());
doReturn(true).when(mMockRestartDialogLayout).updateCompatInfo(any(), any(), anyBoolean());
mShellInit = spy(new ShellInit(mMockExecutor));
mController = new CompatUIController(mContext, mShellInit, mMockShellController,
mMockDisplayController, mMockDisplayInsetsController, mMockImeController,
mMockSyncQueue, mMockExecutor, mMockTransitionsLazy, mDockStateReader) {
mMockSyncQueue, mMockExecutor, mMockTransitionsLazy, mDockStateReader,
mCompatUIConfiguration, mCompatUIShellCommandHandler) {
@Override
CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
ShellTaskOrganizer.TaskListener taskListener) {
@@ -127,6 +137,12 @@ public class CompatUIControllerTest extends ShellTestCase {
TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) {
return mMockLetterboxEduLayout;
}
@Override
RestartDialogWindowManager createRestartDialogWindowManager(Context context,
TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) {
return mMockRestartDialogLayout;
}
};
mShellInit.init();
spyOn(mController);
@@ -159,6 +175,8 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
eq(mMockTaskListener));
verify(mController).createRestartDialogWindowManager(any(), eq(taskInfo),
eq(mMockTaskListener));
// Verify that the compat controls and letterbox education are updated with new size compat
// info.
@@ -167,10 +185,12 @@ public class CompatUIControllerTest extends ShellTestCase {
CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
true);
verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
true);
verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener,
/* canShow= */ true);
verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener,
/* canShow= */ true);
verify(mMockRestartDialogLayout).updateCompatInfo(taskInfo, mMockTaskListener,
/* canShow= */ true);
// Verify that compat controls and letterbox education are removed with null task listener.
clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mController);
@@ -180,12 +200,14 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockCompatLayout).release();
verify(mMockLetterboxEduLayout).release();
verify(mMockRestartDialogLayout).release();
}
@Test
public void testOnCompatInfoChanged_createLayoutReturnsFalse() {
doReturn(false).when(mMockCompatLayout).createLayout(anyBoolean());
doReturn(false).when(mMockLetterboxEduLayout).createLayout(anyBoolean());
doReturn(false).when(mMockRestartDialogLayout).createLayout(anyBoolean());
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_HIDDEN);
@@ -194,6 +216,8 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
eq(mMockTaskListener));
verify(mController).createRestartDialogWindowManager(any(), eq(taskInfo),
eq(mMockTaskListener));
// Verify that the layout is created again.
clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mController);
@@ -201,15 +225,19 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockCompatLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
verify(mMockLetterboxEduLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
verify(mMockRestartDialogLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
eq(mMockTaskListener));
verify(mController).createRestartDialogWindowManager(any(), eq(taskInfo),
eq(mMockTaskListener));
}
@Test
public void testOnCompatInfoChanged_updateCompatInfoReturnsFalse() {
doReturn(false).when(mMockCompatLayout).updateCompatInfo(any(), any(), anyBoolean());
doReturn(false).when(mMockLetterboxEduLayout).updateCompatInfo(any(), any(), anyBoolean());
doReturn(false).when(mMockRestartDialogLayout).updateCompatInfo(any(), any(), anyBoolean());
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_HIDDEN);
@@ -218,24 +246,33 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
eq(mMockTaskListener));
verify(mController).createRestartDialogWindowManager(any(), eq(taskInfo),
eq(mMockTaskListener));
clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mController);
clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mMockRestartDialogLayout,
mController);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
true);
verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
true);
verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener,
/* canShow= */ true);
verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener,
/* canShow= */ true);
verify(mMockRestartDialogLayout).updateCompatInfo(taskInfo, mMockTaskListener,
/* canShow= */ true);
// Verify that the layout is created again.
clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mController);
clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mMockRestartDialogLayout,
mController);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mMockCompatLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
verify(mMockLetterboxEduLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
verify(mMockRestartDialogLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
eq(mMockTaskListener));
verify(mController).createRestartDialogWindowManager(any(), eq(taskInfo),
eq(mMockTaskListener));
}
@@ -259,6 +296,7 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockCompatLayout, never()).release();
verify(mMockLetterboxEduLayout, never()).release();
verify(mMockRestartDialogLayout, never()).release();
verify(mMockDisplayInsetsController, never()).removeInsetsChangedListener(eq(DISPLAY_ID),
any());
@@ -267,6 +305,7 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockDisplayInsetsController).removeInsetsChangedListener(eq(DISPLAY_ID), any());
verify(mMockCompatLayout).release();
verify(mMockLetterboxEduLayout).release();
verify(mMockRestartDialogLayout).release();
}
@Test
@@ -278,11 +317,13 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockCompatLayout, never()).updateDisplayLayout(any());
verify(mMockLetterboxEduLayout, never()).updateDisplayLayout(any());
verify(mMockRestartDialogLayout, never()).updateDisplayLayout(any());
mController.onDisplayConfigurationChanged(DISPLAY_ID, new Configuration());
verify(mMockCompatLayout).updateDisplayLayout(mMockDisplayLayout);
verify(mMockLetterboxEduLayout).updateDisplayLayout(mMockDisplayLayout);
verify(mMockRestartDialogLayout).updateDisplayLayout(mMockDisplayLayout);
}
@Test
@@ -301,12 +342,14 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockCompatLayout).updateDisplayLayout(mMockDisplayLayout);
verify(mMockLetterboxEduLayout).updateDisplayLayout(mMockDisplayLayout);
verify(mMockRestartDialogLayout).updateDisplayLayout(mMockDisplayLayout);
// No update if the insets state is the same.
clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout);
clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mMockRestartDialogLayout);
mOnInsetsChangedListenerCaptor.getValue().insetsChanged(new InsetsState(insetsState));
verify(mMockCompatLayout, never()).updateDisplayLayout(mMockDisplayLayout);
verify(mMockLetterboxEduLayout, never()).updateDisplayLayout(mMockDisplayLayout);
verify(mMockRestartDialogLayout, never()).updateDisplayLayout(mMockDisplayLayout);
}
@Test
@@ -319,22 +362,26 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockCompatLayout).updateVisibility(false);
verify(mMockLetterboxEduLayout).updateVisibility(false);
verify(mMockRestartDialogLayout).updateVisibility(false);
// Verify button remains hidden while IME is showing.
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_HIDDEN);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
false);
verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
false);
verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener,
/* canShow= */ false);
verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener,
/* canShow= */ false);
verify(mMockRestartDialogLayout).updateCompatInfo(taskInfo, mMockTaskListener,
/* canShow= */ false);
// Verify button is shown after IME is hidden.
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
verify(mMockCompatLayout).updateVisibility(true);
verify(mMockLetterboxEduLayout).updateVisibility(true);
verify(mMockRestartDialogLayout).updateVisibility(true);
}
@Test
@@ -347,22 +394,26 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockCompatLayout).updateVisibility(false);
verify(mMockLetterboxEduLayout).updateVisibility(false);
verify(mMockRestartDialogLayout).updateVisibility(false);
// Verify button remains hidden while keyguard is showing.
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_HIDDEN);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
false);
verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
false);
verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener,
/* canShow= */ false);
verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener,
/* canShow= */ false);
verify(mMockRestartDialogLayout).updateCompatInfo(taskInfo, mMockTaskListener,
/* canShow= */ false);
// Verify button is shown after keyguard becomes not showing.
mController.onKeyguardVisibilityChanged(false, false, false);
verify(mMockCompatLayout).updateVisibility(true);
verify(mMockLetterboxEduLayout).updateVisibility(true);
verify(mMockRestartDialogLayout).updateVisibility(true);
}
@Test
@@ -375,20 +426,23 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockCompatLayout, times(2)).updateVisibility(false);
verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false);
verify(mMockRestartDialogLayout, times(2)).updateVisibility(false);
clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout);
clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mMockRestartDialogLayout);
// Verify button remains hidden after keyguard becomes not showing since IME is showing.
mController.onKeyguardVisibilityChanged(false, false, false);
verify(mMockCompatLayout).updateVisibility(false);
verify(mMockLetterboxEduLayout).updateVisibility(false);
verify(mMockRestartDialogLayout).updateVisibility(false);
// Verify button is shown after IME is not showing.
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
verify(mMockCompatLayout).updateVisibility(true);
verify(mMockLetterboxEduLayout).updateVisibility(true);
verify(mMockRestartDialogLayout).updateVisibility(true);
}
@Test
@@ -401,20 +455,23 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockCompatLayout, times(2)).updateVisibility(false);
verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false);
verify(mMockRestartDialogLayout, times(2)).updateVisibility(false);
clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout);
clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout, mMockRestartDialogLayout);
// Verify button remains hidden after IME is hidden since keyguard is showing.
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
verify(mMockCompatLayout).updateVisibility(false);
verify(mMockLetterboxEduLayout).updateVisibility(false);
verify(mMockRestartDialogLayout).updateVisibility(false);
// Verify button is shown after keyguard becomes not showing.
mController.onKeyguardVisibilityChanged(false, false, false);
verify(mMockCompatLayout).updateVisibility(true);
verify(mMockLetterboxEduLayout).updateVisibility(true);
verify(mMockRestartDialogLayout).updateVisibility(true);
}
private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,

View File

@@ -31,6 +31,7 @@ import android.app.ActivityManager;
import android.app.TaskInfo;
import android.app.TaskInfo.CameraCompatControlState;
import android.testing.AndroidTestingRunner;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.SurfaceControlViewHost;
import android.widget.ImageButton;
@@ -45,12 +46,17 @@ import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState;
import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.function.Consumer;
/**
* Tests for {@link CompatUILayout}.
*
@@ -65,20 +71,22 @@ public class CompatUILayoutTest extends ShellTestCase {
@Mock private SyncTransactionQueue mSyncTransactionQueue;
@Mock private CompatUIController.CompatUICallback mCallback;
@Mock private Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnRestartButtonClicked;
@Mock private ShellTaskOrganizer.TaskListener mTaskListener;
@Mock private SurfaceControlViewHost mViewHost;
@Mock private CompatUIConfiguration mCompatUIConfiguration;
private CompatUIWindowManager mWindowManager;
private CompatUILayout mLayout;
private TaskInfo mTaskInfo;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mWindowManager = new CompatUIWindowManager(mContext,
createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN),
mSyncTransactionQueue, mCallback, mTaskListener,
new DisplayLayout(), new CompatUIHintsState());
mTaskInfo = createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN);
mWindowManager = new CompatUIWindowManager(mContext, mTaskInfo, mSyncTransactionQueue,
mCallback, mTaskListener, new DisplayLayout(), new CompatUIHintsState(),
mCompatUIConfiguration, mOnRestartButtonClicked);
mLayout = (CompatUILayout)
LayoutInflater.from(mContext).inflate(R.layout.compat_ui_layout, null);
@@ -95,8 +103,15 @@ public class CompatUILayoutTest extends ShellTestCase {
final ImageButton button = mLayout.findViewById(R.id.size_compat_restart_button);
button.performClick();
@SuppressWarnings("unchecked")
ArgumentCaptor<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> restartCaptor =
ArgumentCaptor.forClass(Pair.class);
verify(mWindowManager).onRestartButtonClicked();
verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
verify(mOnRestartButtonClicked).accept(restartCaptor.capture());
final Pair<TaskInfo, ShellTaskOrganizer.TaskListener> result = restartCaptor.getValue();
Assert.assertEquals(mTaskInfo, result.first);
Assert.assertEquals(mTaskListener, result.second);
}
@Test

View File

@@ -38,6 +38,7 @@ import android.app.ActivityManager;
import android.app.TaskInfo;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.util.Pair;
import android.view.DisplayInfo;
import android.view.InsetsSource;
import android.view.InsetsState;
@@ -53,12 +54,17 @@ import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState;
import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.function.Consumer;
/**
* Tests for {@link CompatUIWindowManager}.
*
@@ -73,20 +79,22 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
@Mock private SyncTransactionQueue mSyncTransactionQueue;
@Mock private CompatUIController.CompatUICallback mCallback;
@Mock private Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnRestartButtonClicked;
@Mock private ShellTaskOrganizer.TaskListener mTaskListener;
@Mock private CompatUILayout mLayout;
@Mock private SurfaceControlViewHost mViewHost;
@Mock private CompatUIConfiguration mCompatUIConfiguration;
private CompatUIWindowManager mWindowManager;
private TaskInfo mTaskInfo;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mWindowManager = new CompatUIWindowManager(mContext,
createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN),
mSyncTransactionQueue, mCallback, mTaskListener,
new DisplayLayout(), new CompatUIHintsState());
mTaskInfo = createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN);
mWindowManager = new CompatUIWindowManager(mContext, mTaskInfo, mSyncTransactionQueue,
mCallback, mTaskListener, new DisplayLayout(), new CompatUIHintsState(),
mCompatUIConfiguration, mOnRestartButtonClicked);
spyOn(mWindowManager);
doReturn(mLayout).when(mWindowManager).inflateLayout();
@@ -404,7 +412,14 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
public void testOnRestartButtonClicked() {
mWindowManager.onRestartButtonClicked();
verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
@SuppressWarnings("unchecked")
ArgumentCaptor<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> restartCaptor =
ArgumentCaptor.forClass(Pair.class);
verify(mOnRestartButtonClicked).accept(restartCaptor.capture());
final Pair<TaskInfo, ShellTaskOrganizer.TaskListener> result = restartCaptor.getValue();
Assert.assertEquals(mTaskInfo, result.first);
Assert.assertEquals(mTaskListener, result.second);
}
@Test

View File

@@ -0,0 +1,148 @@
/*
* Copyright (C) 2022 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.wm.shell.compatui;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.testing.AndroidTestingRunner;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CheckBox;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.function.Consumer;
/**
* Tests for {@link RestartDialogLayout}.
*
* Build/Install/Run:
* atest WMShellUnitTests:RestartDialogLayoutTest
*/
@RunWith(AndroidTestingRunner.class)
@SmallTest
public class RestartDialogLayoutTest extends ShellTestCase {
@Mock private Runnable mDismissCallback;
@Mock private Consumer<Boolean> mRestartCallback;
private RestartDialogLayout mLayout;
private View mDismissButton;
private View mRestartButton;
private View mDialogContainer;
private CheckBox mDontRepeatCheckBox;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mLayout = (RestartDialogLayout)
LayoutInflater.from(mContext).inflate(R.layout.letterbox_restart_dialog_layout,
null);
mDismissButton = mLayout.findViewById(R.id.letterbox_restart_dialog_dismiss_button);
mRestartButton = mLayout.findViewById(R.id.letterbox_restart_dialog_restart_button);
mDialogContainer = mLayout.findViewById(R.id.letterbox_restart_dialog_container);
mDontRepeatCheckBox = mLayout.findViewById(R.id.letterbox_restart_dialog_checkbox);
mLayout.setDismissOnClickListener(mDismissCallback);
mLayout.setRestartOnClickListener(mRestartCallback);
}
@Test
public void testOnFinishInflate() {
assertEquals(mLayout.getDialogContainerView(),
mLayout.findViewById(R.id.letterbox_restart_dialog_container));
assertEquals(mLayout.getDialogTitle(),
mLayout.findViewById(R.id.letterbox_restart_dialog_title));
assertEquals(mLayout.getBackgroundDimDrawable(), mLayout.getBackground());
assertEquals(mLayout.getBackground().getAlpha(), 0);
}
@Test
public void testOnDismissButtonClicked() {
assertTrue(mDismissButton.performClick());
verify(mDismissCallback).run();
}
@Test
public void testOnRestartButtonClickedWithoutCheckbox() {
mDontRepeatCheckBox.setChecked(false);
assertTrue(mRestartButton.performClick());
verify(mRestartCallback).accept(false);
}
@Test
public void testOnRestartButtonClickedWithCheckbox() {
mDontRepeatCheckBox.setChecked(true);
assertTrue(mRestartButton.performClick());
verify(mRestartCallback).accept(true);
}
@Test
public void testOnBackgroundClickedDoesntDismiss() {
assertFalse(mLayout.performClick());
verify(mDismissCallback, never()).run();
}
@Test
public void testOnDialogContainerClicked() {
assertTrue(mDialogContainer.performClick());
verify(mDismissCallback, never()).run();
verify(mRestartCallback, never()).accept(anyBoolean());
}
@Test
public void testSetDismissOnClickListenerNull() {
mLayout.setDismissOnClickListener(null);
assertFalse(mDismissButton.performClick());
assertFalse(mLayout.performClick());
assertTrue(mDialogContainer.performClick());
verify(mDismissCallback, never()).run();
}
@Test
public void testSetRestartOnClickListenerNull() {
mLayout.setRestartOnClickListener(null);
assertFalse(mRestartButton.performClick());
assertFalse(mLayout.performClick());
assertTrue(mDialogContainer.performClick());
verify(mRestartCallback, never()).accept(anyBoolean());
}
}