Handle uiMode change in CompatUI

When uiMode changes we need to recreate CompatUI components to
allow the update of the resources in case of switch between
Light/Dark more or when the device is docked/undocked.
This allows to cover the use case of a change in theme of
a docked device showing a restart dialog.

Fixes: 279170801
Test: atest WMShellUnitTests:CompatUIWindowManagerTest
      atest WMShellUnitTests:ReachabilityEduWindowManagerTest
      atest WMShellUnitTests:RestartDialogWindowManagerTest

Change-Id: I179df47bdc23f238dee9ffe6b2e579a67921eb1f
This commit is contained in:
Massimo Carli
2023-04-19 17:28:25 +00:00
parent d27cba9d16
commit ebc3e09ca8
9 changed files with 204 additions and 90 deletions

View File

@@ -47,6 +47,8 @@ import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import dagger.Lazy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
@@ -55,8 +57,6 @@ import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
import dagger.Lazy;
/**
* Controller to show/update compat UI components on Tasks based on whether the foreground
* activities are in compatibility mode.
@@ -284,13 +284,18 @@ public class CompatUIController implements OnDisplaysChangedListener,
ShellTaskOrganizer.TaskListener taskListener) {
CompatUIWindowManager layout = mActiveCompatLayouts.get(taskInfo.taskId);
if (layout != null) {
// 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.
if (layout.needsToBeRecreated(taskInfo, taskListener)) {
mActiveCompatLayouts.remove(taskInfo.taskId);
layout.release();
} else {
// 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.
mActiveCompatLayouts.remove(taskInfo.taskId);
}
return;
}
return;
}
// Create a new UI layout.
@@ -433,13 +438,18 @@ public class CompatUIController implements OnDisplaysChangedListener,
private void createOrUpdateReachabilityEduLayout(TaskInfo taskInfo,
ShellTaskOrganizer.TaskListener taskListener) {
if (mActiveReachabilityEduLayout != null) {
// UI already exists, update the UI layout.
if (!mActiveReachabilityEduLayout.updateCompatInfo(taskInfo, taskListener,
showOnDisplay(mActiveReachabilityEduLayout.getDisplayId()))) {
// The layout is no longer eligible to be shown, remove from active layouts.
if (mActiveReachabilityEduLayout.needsToBeRecreated(taskInfo, taskListener)) {
mActiveReachabilityEduLayout.release();
mActiveReachabilityEduLayout = null;
} else {
// UI already exists, update the UI layout.
if (!mActiveReachabilityEduLayout.updateCompatInfo(taskInfo, taskListener,
showOnDisplay(mActiveReachabilityEduLayout.getDisplayId()))) {
// The layout is no longer eligible to be shown, remove from active layouts.
mActiveReachabilityEduLayout = null;
}
return;
}
return;
}
// Create a new UI layout.
final Context context = getOrCreateDisplayContext(taskInfo.displayId);

View File

@@ -22,7 +22,6 @@ import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.TaskInfo;
import android.app.TaskInfo.CameraCompatControlState;
@@ -53,9 +52,6 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
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;
@@ -77,7 +73,6 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
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;
@@ -129,7 +124,6 @@ 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;
@@ -149,7 +143,7 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
/** Called when the restart button is clicked. */
void onRestartButtonClicked() {
mOnRestartButtonClicked.accept(Pair.create(mTaskInfo, getTaskListener()));
mOnRestartButtonClicked.accept(Pair.create(getLastTaskInfo(), getTaskListener()));
}
/** Called when the camera treatment button is clicked. */

View File

@@ -26,6 +26,7 @@ import static com.android.internal.annotations.VisibleForTesting.Visibility.PACK
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.TaskInfo;
import android.content.Context;
@@ -65,6 +66,9 @@ public abstract class CompatUIWindowManagerAbstract extends WindowlessWindowMana
private DisplayLayout mDisplayLayout;
private final Rect mStableBounds;
@NonNull
private TaskInfo mTaskInfo;
/**
* Utility class for adding and releasing a View hierarchy for this {@link
* WindowlessWindowManager} to {@code mLeash}.
@@ -83,6 +87,7 @@ public abstract class CompatUIWindowManagerAbstract extends WindowlessWindowMana
SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener,
DisplayLayout displayLayout) {
super(taskInfo.configuration, null /* rootSurface */, null /* hostInputToken */);
mTaskInfo = taskInfo;
mContext = context;
mSyncQueue = syncQueue;
mTaskConfig = taskInfo.configuration;
@@ -94,6 +99,17 @@ public abstract class CompatUIWindowManagerAbstract extends WindowlessWindowMana
mDisplayLayout.getStableBounds(mStableBounds);
}
/**
* @return {@code true} if the instance of the specific {@link CompatUIWindowManagerAbstract}
* for the current task id needs to be recreated loading the related resources. This happens
* if the user switches between Light/Dark mode, if the device is docked/undocked or if the
* user switches between multi-window mode to fullscreen where the
* {@link ShellTaskOrganizer.TaskListener} implementation is different.
*/
boolean needsToBeRecreated(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) {
return hasUiModeChanged(mTaskInfo, taskInfo) || hasTaskListenerChanged(taskListener);
}
/**
* Returns the z-order of this window which will be passed to the {@link SurfaceControl} once
* {@link #attachToParentSurface} is called.
@@ -195,6 +211,7 @@ public abstract class CompatUIWindowManagerAbstract extends WindowlessWindowMana
@VisibleForTesting(visibility = PROTECTED)
public boolean updateCompatInfo(TaskInfo taskInfo,
ShellTaskOrganizer.TaskListener taskListener, boolean canShow) {
mTaskInfo = taskInfo;
final Configuration prevTaskConfig = mTaskConfig;
final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
mTaskConfig = taskInfo.configuration;
@@ -315,6 +332,11 @@ public abstract class CompatUIWindowManagerAbstract extends WindowlessWindowMana
updateSurfacePosition();
}
@Nullable
protected TaskInfo getLastTaskInfo() {
return mTaskInfo;
}
/**
* Called following a change in the task bounds, display layout stable bounds, or the layout
* direction.
@@ -402,4 +424,12 @@ public abstract class CompatUIWindowManagerAbstract extends WindowlessWindowMana
protected final String getTag() {
return getClass().getSimpleName();
}
protected boolean hasTaskListenerChanged(ShellTaskOrganizer.TaskListener newTaskListener) {
return !mTaskListener.equals(newTaskListener);
}
protected static boolean hasUiModeChanged(TaskInfo currentTaskInfo, TaskInfo newTaskInfo) {
return currentTaskInfo.configuration.uiMode != newTaskInfo.configuration.uiMode;
}
}

View File

@@ -19,7 +19,6 @@ package com.android.wm.shell.compatui;
import static android.provider.Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING;
import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.TaskInfo;
import android.content.Context;
@@ -69,9 +68,6 @@ class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
@VisibleForTesting
LetterboxEduDialogLayout mLayout;
@NonNull
private TaskInfo mTaskInfo;
/**
* The vertical margin between the dialog container and the task stable bounds (excluding
* insets).
@@ -99,7 +95,6 @@ class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
DialogAnimationController<LetterboxEduDialogLayout> animationController,
DockStateReader dockStateReader, CompatUIConfiguration compatUIConfiguration) {
super(context, taskInfo, syncQueue, taskListener, displayLayout);
mTaskInfo = taskInfo;
mTransitions = transitions;
mOnDismissCallback = onDismissCallback;
mAnimationController = animationController;
@@ -197,7 +192,7 @@ class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
mLayout.setDismissOnClickListener(null);
mAnimationController.startExitAnimation(mLayout, () -> {
release();
mOnDismissCallback.accept(Pair.create(mTaskInfo, getTaskListener()));
mOnDismissCallback.accept(Pair.create(getLastTaskInfo(), getTaskListener()));
});
}
@@ -210,7 +205,6 @@ class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
@Override
public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener,
boolean canShow) {
mTaskInfo = taskInfo;
mEligibleForLetterboxEducation = taskInfo.topActivityEligibleForLetterboxEducation;
return super.updateCompatInfo(taskInfo, taskListener, canShow);

View File

@@ -20,7 +20,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.TaskInfo;
import android.content.Context;
@@ -52,9 +51,6 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract {
private final ShellExecutor mMainExecutor;
@NonNull
private TaskInfo mTaskInfo;
private boolean mIsActivityLetterboxed;
private int mLetterboxVerticalPosition;
@@ -86,7 +82,6 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract {
ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
CompatUIConfiguration compatUIConfiguration, ShellExecutor mainExecutor) {
super(context, taskInfo, syncQueue, taskListener, displayLayout);
mTaskInfo = taskInfo;
mIsActivityLetterboxed = taskInfo.isLetterboxDoubleTapEnabled;
mLetterboxVerticalPosition = taskInfo.topActivityLetterboxVerticalPosition;
mLetterboxHorizontalPosition = taskInfo.topActivityLetterboxHorizontalPosition;
@@ -136,7 +131,6 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract {
@Override
public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener,
boolean canShow) {
mTaskInfo = taskInfo;
final boolean prevIsActivityLetterboxed = mIsActivityLetterboxed;
final int prevLetterboxVerticalPosition = mLetterboxVerticalPosition;
final int prevLetterboxHorizontalPosition = mLetterboxHorizontalPosition;
@@ -222,14 +216,14 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract {
if (mLayout == null) {
return;
}
final TaskInfo lastTaskInfo = getLastTaskInfo();
final boolean eligibleForDisplayHorizontalEducation = mForceUpdate
|| !mCompatUIConfiguration.hasSeenHorizontalReachabilityEducation(mTaskInfo)
|| !mCompatUIConfiguration.hasSeenHorizontalReachabilityEducation(lastTaskInfo)
|| (mHasUserDoubleTapped
&& (mLetterboxHorizontalPosition == REACHABILITY_LEFT_OR_UP_POSITION
|| mLetterboxHorizontalPosition == REACHABILITY_RIGHT_OR_BOTTOM_POSITION));
final boolean eligibleForDisplayVerticalEducation = mForceUpdate
|| !mCompatUIConfiguration.hasSeenVerticalReachabilityEducation(mTaskInfo)
|| !mCompatUIConfiguration.hasSeenVerticalReachabilityEducation(lastTaskInfo)
|| (mHasUserDoubleTapped
&& (mLetterboxVerticalPosition == REACHABILITY_LEFT_OR_UP_POSITION
|| mLetterboxVerticalPosition == REACHABILITY_RIGHT_OR_BOTTOM_POSITION));
@@ -241,7 +235,7 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract {
mLayout.handleVisibility(eligibleForDisplayHorizontalEducation,
eligibleForDisplayVerticalEducation,
mLetterboxVerticalPosition, mLetterboxHorizontalPosition, availableWidth,
availableHeight, mCompatUIConfiguration, mTaskInfo);
availableHeight, mCompatUIConfiguration, lastTaskInfo);
if (!mHasLetterboxSizeChanged) {
updateHideTime();
mMainExecutor.executeDelayed(this::hideReachability, DISAPPEAR_DELAY_MS);

View File

@@ -19,7 +19,6 @@ package com.android.wm.shell.compatui;
import static android.provider.Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING;
import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.TaskInfo;
import android.content.Context;
@@ -67,9 +66,6 @@ class RestartDialogWindowManager extends CompatUIWindowManagerAbstract {
*/
private final int mDialogVerticalMargin;
@NonNull
private TaskInfo mTaskInfo;
@Nullable
@VisibleForTesting
RestartDialogLayout mLayout;
@@ -95,7 +91,6 @@ class RestartDialogWindowManager extends CompatUIWindowManagerAbstract {
DialogAnimationController<RestartDialogLayout> animationController,
CompatUIConfiguration compatUIConfiguration) {
super(context, taskInfo, syncQueue, taskListener, displayLayout);
mTaskInfo = taskInfo;
mTransitions = transitions;
mOnDismissCallback = onDismissCallback;
mOnRestartCallback = onRestartCallback;
@@ -125,7 +120,7 @@ class RestartDialogWindowManager extends CompatUIWindowManagerAbstract {
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.shouldShowRestartDialogAgain(mTaskInfo));
|| mCompatUIConfiguration.shouldShowRestartDialogAgain(getLastTaskInfo()));
}
@Override
@@ -143,18 +138,6 @@ class RestartDialogWindowManager extends CompatUIWindowManagerAbstract {
mRequestRestartDialog = enabled;
}
@Override
public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener,
boolean canShow) {
mTaskInfo = taskInfo;
return super.updateCompatInfo(taskInfo, taskListener, canShow);
}
boolean needsToBeRecreated(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) {
return taskInfo.configuration.uiMode != mTaskInfo.configuration.uiMode
|| !getTaskListener().equals(taskListener);
}
private void updateDialogMargins() {
if (mLayout == null) {
return;
@@ -191,6 +174,7 @@ class RestartDialogWindowManager extends CompatUIWindowManagerAbstract {
// Dialog has already been released.
return;
}
final TaskInfo lastTaskInfo = getLastTaskInfo();
mLayout.setDismissOnClickListener(this::onDismiss);
mLayout.setRestartOnClickListener(dontShowAgain -> {
if (mLayout != null) {
@@ -200,9 +184,9 @@ class RestartDialogWindowManager extends CompatUIWindowManagerAbstract {
});
}
if (dontShowAgain) {
mCompatUIConfiguration.setDontShowRestartDialogAgain(mTaskInfo);
mCompatUIConfiguration.setDontShowRestartDialogAgain(lastTaskInfo);
}
mOnRestartCallback.accept(Pair.create(mTaskInfo, getTaskListener()));
mOnRestartCallback.accept(Pair.create(lastTaskInfo, getTaskListener()));
});
// Focus on the dialog title for accessibility.
mLayout.getDialogTitle().sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
@@ -216,7 +200,7 @@ class RestartDialogWindowManager extends CompatUIWindowManagerAbstract {
mLayout.setDismissOnClickListener(null);
mAnimationController.startExitAnimation(mLayout, () -> {
release();
mOnDismissCallback.accept(Pair.create(mTaskInfo, getTaskListener()));
mOnDismissCallback.accept(Pair.create(getLastTaskInfo(), getTaskListener()));
});
}

View File

@@ -37,6 +37,7 @@ import static org.mockito.Mockito.verify;
import android.app.ActivityManager;
import android.app.TaskInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.util.Pair;
@@ -455,12 +456,21 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
verify(mLayout).setCameraCompatHintVisibility(/* show= */ true);
}
@Test
public void testWhenDockedStateHasChanged_needsToBeRecreated() {
ActivityManager.RunningTaskInfo newTaskInfo = new ActivityManager.RunningTaskInfo();
newTaskInfo.configuration.uiMode |= Configuration.UI_MODE_TYPE_DESK;
Assert.assertTrue(mWindowManager.needsToBeRecreated(newTaskInfo, mTaskListener));
}
private static TaskInfo createTaskInfo(boolean hasSizeCompat,
@TaskInfo.CameraCompatControlState int cameraCompatControlState) {
ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
taskInfo.taskId = TASK_ID;
taskInfo.topActivityInSizeCompat = hasSizeCompat;
taskInfo.cameraCompatControlState = cameraCompatControlState;
taskInfo.configuration.uiMode &= ~Configuration.UI_MODE_TYPE_DESK;
return taskInfo;
}
}

View File

@@ -21,6 +21,7 @@ import static org.junit.Assert.assertNull;
import android.app.ActivityManager;
import android.app.TaskInfo;
import android.content.res.Configuration;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
@@ -31,6 +32,8 @@ import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,63 +49,61 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
@SmallTest
public class ReachabilityEduWindowManagerTest extends ShellTestCase {
private static final int USER_ID = 1;
private static final int TASK_ID = 1;
@Mock
private SyncTransactionQueue mSyncTransactionQueue;
@Mock
private ShellTaskOrganizer.TaskListener mTaskListener;
@Mock
private CompatUIController.CompatUICallback mCallback;
@Mock
private CompatUIConfiguration mCompatUIConfiguration;
@Mock
private DisplayLayout mDisplayLayout;
private TestShellExecutor mExecutor;
private TaskInfo mTaskInfo;
private ReachabilityEduWindowManager mWindowManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mExecutor = new TestShellExecutor();
mTaskInfo = new ActivityManager.RunningTaskInfo();
mTaskInfo.configuration.uiMode =
(mTaskInfo.configuration.uiMode & ~Configuration.UI_MODE_NIGHT_MASK)
| Configuration.UI_MODE_NIGHT_NO;
mTaskInfo.configuration.uiMode =
(mTaskInfo.configuration.uiMode & ~Configuration.UI_MODE_TYPE_MASK)
| Configuration.UI_MODE_TYPE_NORMAL;
mWindowManager = createReachabilityEduWindowManager(mTaskInfo);
}
@Test
public void testCreateLayout_notEligible_doesNotCreateLayout() {
final ReachabilityEduWindowManager windowManager = createReachabilityEduWindowManager(
createTaskInfo(/* userId= */ USER_ID, /*isLetterboxDoubleTapEnabled */ false));
assertFalse(mWindowManager.createLayout(/* canShow= */ true));
assertFalse(windowManager.createLayout(/* canShow= */ true));
assertNull(mWindowManager.mLayout);
}
assertNull(windowManager.mLayout);
@Test
public void testWhenDockedStateHasChanged_needsToBeRecreated() {
ActivityManager.RunningTaskInfo newTaskInfo = new ActivityManager.RunningTaskInfo();
newTaskInfo.configuration.uiMode =
(newTaskInfo.configuration.uiMode & ~Configuration.UI_MODE_TYPE_MASK)
| Configuration.UI_MODE_TYPE_DESK;
Assert.assertTrue(mWindowManager.needsToBeRecreated(newTaskInfo, mTaskListener));
}
@Test
public void testWhenDarkLightThemeHasChanged_needsToBeRecreated() {
ActivityManager.RunningTaskInfo newTaskInfo = new ActivityManager.RunningTaskInfo();
mTaskInfo.configuration.uiMode =
(mTaskInfo.configuration.uiMode & ~Configuration.UI_MODE_NIGHT_MASK)
| Configuration.UI_MODE_NIGHT_YES;
Assert.assertTrue(mWindowManager.needsToBeRecreated(newTaskInfo, mTaskListener));
}
private ReachabilityEduWindowManager createReachabilityEduWindowManager(TaskInfo taskInfo) {
return new ReachabilityEduWindowManager(mContext, taskInfo, mSyncTransactionQueue,
mTaskListener, mDisplayLayout, mCompatUIConfiguration, mExecutor);
}
private static TaskInfo createTaskInfo(int userId, boolean isLetterboxDoubleTapEnabled) {
return createTaskInfo(userId, /* isLetterboxDoubleTapEnabled */ isLetterboxDoubleTapEnabled,
/* topActivityLetterboxVerticalPosition */ -1,
/* topActivityLetterboxHorizontalPosition */ -1,
/* topActivityLetterboxWidth */ -1,
/* topActivityLetterboxHeight */ -1);
}
private static TaskInfo createTaskInfo(int userId, boolean isLetterboxDoubleTapEnabled,
int topActivityLetterboxVerticalPosition, int topActivityLetterboxHorizontalPosition,
int topActivityLetterboxWidth, int topActivityLetterboxHeight) {
ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
taskInfo.userId = userId;
taskInfo.taskId = TASK_ID;
taskInfo.isLetterboxDoubleTapEnabled = isLetterboxDoubleTapEnabled;
taskInfo.topActivityLetterboxVerticalPosition = topActivityLetterboxVerticalPosition;
taskInfo.topActivityLetterboxHorizontalPosition = topActivityLetterboxHorizontalPosition;
taskInfo.topActivityLetterboxWidth = topActivityLetterboxWidth;
taskInfo.topActivityLetterboxHeight = topActivityLetterboxHeight;
return taskInfo;
}
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright (C) 2023 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.app.ActivityManager;
import android.app.TaskInfo;
import android.content.res.Configuration;
import android.testing.AndroidTestingRunner;
import android.util.Pair;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.transition.Transitions;
import junit.framework.Assert;
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 RestartDialogWindowManager}.
*
* Build/Install/Run:
* atest WMShellUnitTests:RestartDialogWindowManagerTest
*/
@RunWith(AndroidTestingRunner.class)
@SmallTest
public class RestartDialogWindowManagerTest extends ShellTestCase {
@Mock
private SyncTransactionQueue mSyncTransactionQueue;
@Mock private ShellTaskOrganizer.TaskListener mTaskListener;
@Mock private CompatUIConfiguration mCompatUIConfiguration;
@Mock private Transitions mTransitions;
@Mock private Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnRestartCallback;
@Mock private Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnDismissCallback;
private RestartDialogWindowManager mWindowManager;
private TaskInfo mTaskInfo;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mTaskInfo = new ActivityManager.RunningTaskInfo();
mTaskInfo.configuration.uiMode =
(mTaskInfo.configuration.uiMode & ~Configuration.UI_MODE_NIGHT_MASK)
| Configuration.UI_MODE_NIGHT_NO;
mTaskInfo.configuration.uiMode =
(mTaskInfo.configuration.uiMode & ~Configuration.UI_MODE_TYPE_MASK)
| Configuration.UI_MODE_TYPE_NORMAL;
mWindowManager = new RestartDialogWindowManager(mContext, mTaskInfo, mSyncTransactionQueue,
mTaskListener, new DisplayLayout(), mTransitions, mOnRestartCallback,
mOnDismissCallback, mCompatUIConfiguration);
}
@Test
public void testWhenDockedStateHasChanged_needsToBeRecreated() {
ActivityManager.RunningTaskInfo newTaskInfo = new ActivityManager.RunningTaskInfo();
newTaskInfo.configuration.uiMode =
(newTaskInfo.configuration.uiMode & ~Configuration.UI_MODE_TYPE_MASK)
| Configuration.UI_MODE_TYPE_DESK;
Assert.assertTrue(mWindowManager.needsToBeRecreated(newTaskInfo, mTaskListener));
}
@Test
public void testWhenDarkLightThemeHasChanged_needsToBeRecreated() {
ActivityManager.RunningTaskInfo newTaskInfo = new ActivityManager.RunningTaskInfo();
mTaskInfo.configuration.uiMode =
(mTaskInfo.configuration.uiMode & ~Configuration.UI_MODE_NIGHT_MASK)
| Configuration.UI_MODE_NIGHT_YES;
Assert.assertTrue(mWindowManager.needsToBeRecreated(newTaskInfo, mTaskListener));
}
}