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:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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. */
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 -> {});
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user