[6/n] Letterbox Education: add window manager and connect to controller.

Bug: 207010227
Test: atest WMShellUnitTests:CompatUIWindowManagerTest
Test: atest WMShellUnitTests:CompatUIControllerTest
Change-Id: I1f85245180ffa9b3abe409a0f18bf923bea3a442
This commit is contained in:
tomnatan
2022-01-19 13:50:29 +00:00
parent dc7a4d59cc
commit 2d6097698d
8 changed files with 588 additions and 274 deletions

View File

@@ -40,6 +40,7 @@ import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -74,8 +75,22 @@ public class CompatUIController implements OnDisplaysChangedListener,
private final SparseArray<PerDisplayOnInsetsChangedListener> mOnInsetsChangedListeners =
new SparseArray<>(0);
/** The showing UIs by task id. */
private final SparseArray<CompatUIWindowManager> mActiveLayouts = new SparseArray<>(0);
/**
* The active Compat Control UI layouts by task id.
*
* <p>An active layout is a layout that is eligible to be shown for the associated task but
* isn't necessarily shown at a given time.
*/
private final SparseArray<CompatUIWindowManager> mActiveCompatLayouts = new SparseArray<>(0);
/**
* The active Letterbox Education layout if there is one (there can be at most one active).
*
* <p>An active layout is a layout that is eligible to be shown for the associated task but
* isn't necessarily shown at a given time.
*/
@Nullable
private LetterboxEduWindowManager mActiveLetterboxEduLayout;
/** Avoid creating display context frequently for non-default display. */
private final SparseArray<WeakReference<Context>> mDisplayContextCache = new SparseArray<>(0);
@@ -135,14 +150,12 @@ public class CompatUIController implements OnDisplaysChangedListener,
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
if (taskInfo.configuration == null || taskListener == null) {
// Null token means the current foreground activity is not in compatibility mode.
removeLayout(taskInfo.taskId);
} else if (mActiveLayouts.contains(taskInfo.taskId)) {
// UI already exists, update the UI layout.
updateLayout(taskInfo, taskListener);
} else {
// Create a new compat UI.
createLayout(taskInfo, taskListener);
removeLayouts(taskInfo.taskId);
return;
}
createOrUpdateCompatLayout(taskInfo, taskListener);
createOrUpdateLetterboxEduLayout(taskInfo, taskListener);
}
@Override
@@ -159,7 +172,7 @@ public class CompatUIController implements OnDisplaysChangedListener,
final List<Integer> toRemoveTaskIds = new ArrayList<>();
forAllLayoutsOnDisplay(displayId, layout -> toRemoveTaskIds.add(layout.getTaskId()));
for (int i = toRemoveTaskIds.size() - 1; i >= 0; i--) {
removeLayout(toRemoveTaskIds.get(i));
removeLayouts(toRemoveTaskIds.get(i));
}
}
@@ -218,26 +231,39 @@ public class CompatUIController implements OnDisplaysChangedListener,
return mDisplaysWithIme.contains(displayId);
}
private void createLayout(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) {
final Context context = getOrCreateDisplayContext(taskInfo.displayId);
if (context == null) {
Log.e(TAG, "Cannot get context for display " + taskInfo.displayId);
private void createOrUpdateCompatLayout(TaskInfo taskInfo,
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.
mActiveCompatLayouts.remove(taskInfo.taskId);
}
return;
}
final CompatUIWindowManager compatUIWindowManager =
createLayout(context, taskInfo, taskListener);
mActiveLayouts.put(taskInfo.taskId, compatUIWindowManager);
compatUIWindowManager.createLayout(showOnDisplay(taskInfo.displayId), taskInfo);
// Create a new UI layout.
final Context context = getOrCreateDisplayContext(taskInfo.displayId);
if (context == null) {
return;
}
layout = createCompatUiWindowManager(context, taskInfo, taskListener);
if (layout.createLayout(showOnDisplay(taskInfo.displayId))) {
// The new layout is eligible to be shown, add it the active layouts.
mActiveCompatLayouts.put(taskInfo.taskId, layout);
}
}
@VisibleForTesting
CompatUIWindowManager createLayout(Context context, TaskInfo taskInfo,
CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
ShellTaskOrganizer.TaskListener taskListener) {
final CompatUIWindowManager compatUIWindowManager = new CompatUIWindowManager(context,
taskInfo.configuration, mSyncQueue, mCallback, taskInfo.taskId, taskListener,
taskInfo, mSyncQueue, mCallback, taskListener,
mDisplayController.getDisplayLayout(taskInfo.displayId), mHasShownSizeCompatHint,
mHasShownCameraCompatHint);
// TODO(b/218304113): updates values only if hints are actually shown to the user.
// Only show hints for the first time.
if (taskInfo.topActivityInSizeCompat) {
mHasShownSizeCompatHint = true;
@@ -248,19 +274,53 @@ public class CompatUIController implements OnDisplaysChangedListener,
return compatUIWindowManager;
}
private void updateLayout(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) {
final CompatUIWindowManager layout = mActiveLayouts.get(taskInfo.taskId);
if (layout == null) {
private void createOrUpdateLetterboxEduLayout(TaskInfo taskInfo,
ShellTaskOrganizer.TaskListener taskListener) {
if (mActiveLetterboxEduLayout != null
&& mActiveLetterboxEduLayout.getTaskId() == taskInfo.taskId) {
// UI already exists, update the UI layout.
if (!mActiveLetterboxEduLayout.updateCompatInfo(taskInfo, taskListener,
showOnDisplay(mActiveLetterboxEduLayout.getDisplayId()))) {
// The layout is no longer eligible to be shown, clear active layout.
mActiveLetterboxEduLayout = null;
}
return;
}
layout.updateCompatInfo(taskInfo, taskListener, showOnDisplay(layout.getDisplayId()));
// Create a new UI layout.
final Context context = getOrCreateDisplayContext(taskInfo.displayId);
if (context == null) {
return;
}
LetterboxEduWindowManager newLayout = new LetterboxEduWindowManager(context, taskInfo,
mSyncQueue, taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId),
this::onLetterboxEduDismissed);
if (newLayout.createLayout(showOnDisplay(taskInfo.displayId))) {
// The new layout is eligible to be shown, make it the active layout.
if (mActiveLetterboxEduLayout != null) {
// Release the previous layout since at most one can be active.
// Since letterbox education is only shown once to the user, releasing the previous
// layout is only a precaution.
mActiveLetterboxEduLayout.release();
}
mActiveLetterboxEduLayout = newLayout;
}
}
private void removeLayout(int taskId) {
final CompatUIWindowManager layout = mActiveLayouts.get(taskId);
private void onLetterboxEduDismissed() {
mActiveLetterboxEduLayout = null;
}
private void removeLayouts(int taskId) {
final CompatUIWindowManager layout = mActiveCompatLayouts.get(taskId);
if (layout != null) {
layout.release();
mActiveLayouts.remove(taskId);
mActiveCompatLayouts.remove(taskId);
}
if (mActiveLetterboxEduLayout != null && mActiveLetterboxEduLayout.getTaskId() == taskId) {
mActiveLetterboxEduLayout.release();
mActiveLetterboxEduLayout = null;
}
}
@@ -278,28 +338,34 @@ public class CompatUIController implements OnDisplaysChangedListener,
if (display != null) {
context = mContext.createDisplayContext(display);
mDisplayContextCache.put(displayId, new WeakReference<>(context));
} else {
Log.e(TAG, "Cannot get context for display " + displayId);
}
}
return context;
}
private void forAllLayoutsOnDisplay(int displayId, Consumer<CompatUIWindowManager> callback) {
private void forAllLayoutsOnDisplay(int displayId,
Consumer<CompatUIWindowManagerAbstract> callback) {
forAllLayouts(layout -> layout.getDisplayId() == displayId, callback);
}
private void forAllLayouts(Consumer<CompatUIWindowManager> callback) {
private void forAllLayouts(Consumer<CompatUIWindowManagerAbstract> callback) {
forAllLayouts(layout -> true, callback);
}
private void forAllLayouts(Predicate<CompatUIWindowManager> condition,
Consumer<CompatUIWindowManager> callback) {
for (int i = 0; i < mActiveLayouts.size(); i++) {
final int taskId = mActiveLayouts.keyAt(i);
final CompatUIWindowManager layout = mActiveLayouts.get(taskId);
private void forAllLayouts(Predicate<CompatUIWindowManagerAbstract> condition,
Consumer<CompatUIWindowManagerAbstract> callback) {
for (int i = 0; i < mActiveCompatLayouts.size(); i++) {
final int taskId = mActiveCompatLayouts.keyAt(i);
final CompatUIWindowManager layout = mActiveCompatLayouts.get(taskId);
if (layout != null && condition.test(layout)) {
callback.accept(layout);
}
}
if (mActiveLetterboxEduLayout != null && condition.test(mActiveLetterboxEduLayout)) {
callback.accept(mActiveLetterboxEduLayout);
}
}
/**

View File

@@ -25,7 +25,6 @@ import android.annotation.Nullable;
import android.app.TaskInfo;
import android.app.TaskInfo.CameraCompatControlState;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.util.Log;
import android.view.LayoutInflater;
@@ -36,6 +35,8 @@ 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.compatui.CompatUIController.CompatUICallback;
import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager;
/**
* Window manager for the Size Compat restart button and Camera Compat control.
@@ -43,18 +44,19 @@ import com.android.wm.shell.common.SyncTransactionQueue;
class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
/**
* The Compat UI should be the topmost child of the Task in case there can be more than one
* child.
* The Compat UI should be below the Letterbox Education.
*/
private static final int Z_ORDER = Integer.MAX_VALUE;
private static final int Z_ORDER = LetterboxEduWindowManager.Z_ORDER - 1;
private final CompatUIController.CompatUICallback mCallback;
private final CompatUICallback mCallback;
// Remember the last reported states in case visibility changes due to keyguard or IME updates.
@VisibleForTesting
boolean mHasSizeCompat;
@VisibleForTesting
@CameraCompatControlState
private int mCameraCompatControlState = CAMERA_COMPAT_CONTROL_HIDDEN;
int mCameraCompatControlState = CAMERA_COMPAT_CONTROL_HIDDEN;
@VisibleForTesting
boolean mShouldShowSizeCompatHint;
@@ -65,12 +67,14 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
@VisibleForTesting
CompatUILayout mLayout;
CompatUIWindowManager(Context context, Configuration taskConfig,
SyncTransactionQueue syncQueue, CompatUIController.CompatUICallback callback,
int taskId, ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
CompatUIWindowManager(Context context, TaskInfo taskInfo,
SyncTransactionQueue syncQueue, CompatUICallback callback,
ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
boolean hasShownSizeCompatHint, boolean hasShownCameraCompatHint) {
super(context, taskConfig, syncQueue, taskId, taskListener, displayLayout);
super(context, taskInfo, syncQueue, taskListener, displayLayout);
mCallback = callback;
mHasSizeCompat = taskInfo.topActivityInSizeCompat;
mCameraCompatControlState = taskInfo.cameraCompatControlState;
mShouldShowSizeCompatHint = !hasShownSizeCompatHint;
mShouldShowCameraCompatHint = !hasShownCameraCompatHint;
}
@@ -80,7 +84,6 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
return Z_ORDER;
}
@Override
protected @Nullable View getLayout() {
return mLayout;
@@ -96,16 +99,6 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
return mHasSizeCompat || shouldShowCameraControl();
}
/**
* Updates the internal state with respect to {@code taskInfo} and calls {@link
* #createLayout(boolean)}.
*/
void createLayout(boolean canShow, TaskInfo taskInfo) {
mHasSizeCompat = taskInfo.topActivityInSizeCompat;
mCameraCompatControlState = taskInfo.cameraCompatControlState;
createLayout(canShow);
}
@Override
protected View createLayout() {
mLayout = inflateLayout();
@@ -127,19 +120,23 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
}
@Override
public void updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener,
public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener,
boolean canShow) {
final boolean prevHasSizeCompat = mHasSizeCompat;
final int prevCameraCompatControlState = mCameraCompatControlState;
mHasSizeCompat = taskInfo.topActivityInSizeCompat;
mCameraCompatControlState = taskInfo.cameraCompatControlState;
super.updateCompatInfo(taskInfo, taskListener, canShow);
if (!super.updateCompatInfo(taskInfo, taskListener, canShow)) {
return false;
}
if (prevHasSizeCompat != mHasSizeCompat
|| prevCameraCompatControlState != mCameraCompatControlState) {
updateVisibilityOfViews();
}
return true;
}
/** Called when the restart button is clicked. */

View File

@@ -49,7 +49,7 @@ import com.android.wm.shell.common.SyncTransactionQueue;
*
* <p>Holds view hierarchy of a root surface and helps to inflate and manage layout.
*/
abstract class CompatUIWindowManagerAbstract extends WindowlessWindowManager {
public abstract class CompatUIWindowManagerAbstract extends WindowlessWindowManager {
protected final SyncTransactionQueue mSyncQueue;
protected final int mDisplayId;
@@ -75,15 +75,15 @@ abstract class CompatUIWindowManagerAbstract extends WindowlessWindowManager {
@Nullable
protected SurfaceControl mLeash;
protected CompatUIWindowManagerAbstract(Context context, Configuration taskConfig,
SyncTransactionQueue syncQueue, int taskId,
ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout) {
super(taskConfig, null /* rootSurface */, null /* hostInputToken */);
protected CompatUIWindowManagerAbstract(Context context, TaskInfo taskInfo,
SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener,
DisplayLayout displayLayout) {
super(taskInfo.configuration, null /* rootSurface */, null /* hostInputToken */);
mContext = context;
mSyncQueue = syncQueue;
mTaskConfig = taskConfig;
mTaskConfig = taskInfo.configuration;
mDisplayId = mContext.getDisplayId();
mTaskId = taskId;
mTaskId = taskInfo.taskId;
mTaskListener = taskListener;
mDisplayLayout = displayLayout;
mStableBounds = new Rect();
@@ -105,12 +105,18 @@ abstract class CompatUIWindowManagerAbstract extends WindowlessWindowManager {
* Inflates and inits the layout of this window manager on to the root surface if both {@code
* canShow} and {@link #eligibleToShowLayout} are true.
*
* <p>Doesn't do anything if layout is not eligible to be shown.
*
* @param canShow whether the layout is allowed to be shown by the parent controller.
* @return whether the layout is eligible to be shown.
*/
void createLayout(boolean canShow) {
if (!canShow || !eligibleToShowLayout() || getLayout() != null) {
// Wait until layout should be visible.
return;
protected boolean createLayout(boolean canShow) {
if (!eligibleToShowLayout()) {
return false;
}
if (!canShow || getLayout() != null) {
// Wait until layout should be visible, or layout was already created.
return true;
}
if (mViewHost != null) {
@@ -123,6 +129,8 @@ abstract class CompatUIWindowManagerAbstract extends WindowlessWindowManager {
mViewHost.setView(createLayout(), getWindowLayoutParams());
updateSurfacePosition();
return true;
}
/** Inflates and inits the layout of this window manager. */
@@ -174,9 +182,12 @@ abstract class CompatUIWindowManagerAbstract extends WindowlessWindowManager {
/**
* Called when compat info changed.
*
* <p>The window manager is released if the layout is no longer eligible to be shown.
*
* @param canShow whether the layout is allowed to be shown by the parent controller.
* @return whether the layout is eligible to be shown.
*/
void updateCompatInfo(TaskInfo taskInfo,
protected boolean updateCompatInfo(TaskInfo taskInfo,
ShellTaskOrganizer.TaskListener taskListener, boolean canShow) {
final Configuration prevTaskConfig = mTaskConfig;
final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
@@ -186,12 +197,16 @@ abstract class CompatUIWindowManagerAbstract extends WindowlessWindowManager {
// Update configuration.
setConfiguration(mTaskConfig);
if (!eligibleToShowLayout()) {
release();
return false;
}
View layout = getLayout();
if (layout == null || prevTaskListener != taskListener) {
// TaskListener changed, recreate the layout for new surface parent.
release();
createLayout(canShow);
return;
return createLayout(canShow);
}
boolean boundsUpdated = !mTaskConfig.windowConfiguration.getBounds().equals(
@@ -207,6 +222,8 @@ abstract class CompatUIWindowManagerAbstract extends WindowlessWindowManager {
// Update layout for RTL.
layout.setLayoutDirection(mTaskConfig.getLayoutDirection());
}
return true;
}
@@ -248,16 +265,16 @@ abstract class CompatUIWindowManagerAbstract extends WindowlessWindowManager {
mTaskListener.attachChildSurfaceToTask(mTaskId, b);
}
int getDisplayId() {
public int getDisplayId() {
return mDisplayId;
}
int getTaskId() {
public int getTaskId() {
return mTaskId;
}
/** Releases the surface control and tears down the view hierarchy. */
void release() {
public void release() {
// Hiding before releasing to avoid flickering when transitioning to the Home screen.
View layout = getLayout();
if (layout != null) {
@@ -278,7 +295,7 @@ abstract class CompatUIWindowManagerAbstract extends WindowlessWindowManager {
}
/** Re-layouts the view host and updates the surface position. */
void relayout() {
public void relayout() {
if (mViewHost == null) {
return;
}
@@ -334,7 +351,7 @@ abstract class CompatUIWindowManagerAbstract extends WindowlessWindowManager {
}
/** Gets the layout params. */
private WindowManager.LayoutParams getWindowLayoutParams() {
protected WindowManager.LayoutParams getWindowLayoutParams() {
View layout = getLayout();
if (layout == null) {
return new WindowManager.LayoutParams();
@@ -345,7 +362,7 @@ abstract class CompatUIWindowManagerAbstract extends WindowlessWindowManager {
}
/** Gets the layout params given the width and height of the layout. */
private WindowManager.LayoutParams getWindowLayoutParams(int width, int height) {
protected WindowManager.LayoutParams getWindowLayoutParams(int width, int height) {
final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
// Cannot be wrap_content as this determines the actual window size
width, height,

View File

@@ -35,6 +35,8 @@ class LetterboxEduDialogLayout extends FrameLayout {
// 204 is simply 255 * 0.8.
private static final int BACKGROUND_DIM_ALPHA = 204;
private LetterboxEduWindowManager mWindowManager;
public LetterboxEduDialogLayout(Context context) {
this(context, null);
}
@@ -52,6 +54,17 @@ class LetterboxEduDialogLayout extends FrameLayout {
super(context, attrs, defStyleAttr, defStyleRes);
}
void inject(LetterboxEduWindowManager windowManager) {
mWindowManager = windowManager;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
// Need to relayout after visibility changes since they affect size.
mWindowManager.relayout();
}
/**
* Register a callback for the dismiss button and background dim.
*

View File

@@ -0,0 +1,150 @@
/*
* 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.letterboxedu;
import android.annotation.Nullable;
import android.app.TaskInfo;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Rect;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
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.compatui.CompatUIWindowManagerAbstract;
/**
* Window manager for the Letterbox Education.
*/
// TODO(b/215316431): Add tests
public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
/**
* The Letterbox Education should be the topmost child of the Task in case there can be more
* than one child.
*/
public static final int Z_ORDER = Integer.MAX_VALUE;
/**
* The name of the {@link SharedPreferences} that holds which user has seen the Letterbox
* Education for specific packages and which user has seen the full dialog for any package.
*/
private static final String HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME =
"has_seen_letterbox_education";
/**
* The {@link SharedPreferences} instance for {@link #HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME}.
*/
private final SharedPreferences mSharedPreferences;
// Remember the last reported state in case visibility changes due to keyguard or IME updates.
private boolean mEligibleForLetterboxEducation;
@Nullable
private LetterboxEduDialogLayout mLayout;
private final Runnable mOnDismissCallback;
public LetterboxEduWindowManager(Context context, TaskInfo taskInfo,
SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener,
DisplayLayout displayLayout, Runnable onDismissCallback) {
super(context, taskInfo, syncQueue, taskListener, displayLayout);
mOnDismissCallback = onDismissCallback;
mEligibleForLetterboxEducation = taskInfo.topActivityEligibleForLetterboxEducation;
mSharedPreferences = mContext.getSharedPreferences(HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME,
Context.MODE_PRIVATE);
}
@Override
protected int getZOrder() {
return Z_ORDER;
}
@Override
protected @Nullable View getLayout() {
return mLayout;
}
@Override
protected void removeLayout() {
mLayout = null;
}
@Override
protected boolean eligibleToShowLayout() {
// If the layout isn't null then it was previously showing, and we shouldn't check if the
// user has seen the letterbox education before.
return mEligibleForLetterboxEducation && (mLayout != null
|| !getHasSeenLetterboxEducation());
}
@Override
protected View createLayout() {
setSeenLetterboxEducation();
mLayout = inflateLayout();
mLayout.inject(this);
mLayout.setDismissOnClickListener(this::onDismiss);
return mLayout;
}
private LetterboxEduDialogLayout inflateLayout() {
return (LetterboxEduDialogLayout) LayoutInflater.from(mContext).inflate(
R.layout.letterbox_education_dialog_layout, null);
}
private void onDismiss() {
release();
mOnDismissCallback.run();
}
@Override
public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener,
boolean canShow) {
mEligibleForLetterboxEducation = taskInfo.topActivityEligibleForLetterboxEducation;
return super.updateCompatInfo(taskInfo, taskListener, canShow);
}
@Override
protected void updateSurfacePosition(Rect taskBounds, Rect stableBounds) {
updateSurfacePosition(/* positionX= */ taskBounds.left, /* positionY= */ taskBounds.top);
}
@Override
protected WindowManager.LayoutParams getWindowLayoutParams() {
final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
return getWindowLayoutParams(/* width= */ taskBounds.right - taskBounds.left,
/* height= */ taskBounds.bottom - taskBounds.top);
}
private boolean getHasSeenLetterboxEducation() {
return mSharedPreferences.getBoolean(getPrefKey(), /* default= */ false);
}
private void setSeenLetterboxEducation() {
mSharedPreferences.edit().putBoolean(getPrefKey(), true).apply();
}
private String getPrefKey() {
return String.valueOf(mContext.getUserId());
}
}

View File

@@ -16,15 +16,14 @@
package com.android.wm.shell.compatui;
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
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 static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
@@ -94,10 +93,12 @@ public class CompatUIControllerTest extends ShellTestCase {
doReturn(mMockDisplayLayout).when(mMockDisplayController).getDisplayLayout(anyInt());
doReturn(DISPLAY_ID).when(mMockLayout).getDisplayId();
doReturn(TASK_ID).when(mMockLayout).getTaskId();
doReturn(true).when(mMockLayout).createLayout(anyBoolean());
doReturn(true).when(mMockLayout).updateCompatInfo(any(), any(), anyBoolean());
mController = new CompatUIController(mContext, mMockDisplayController,
mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor) {
@Override
CompatUIWindowManager createLayout(Context context, TaskInfo taskInfo,
CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
ShellTaskOrganizer.TaskListener taskListener) {
return mMockLayout;
}
@@ -113,45 +114,78 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testOnCompatInfoChanged() {
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */,
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_HIDDEN);
// Verify that the compat controls are added with non-null size compat info.
// Verify that the compat controls are added with non-null task listener.
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mController).createLayout(any(), eq(taskInfo), eq(mMockTaskListener));
verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
// Verify that the compat controls are updated with non-null new size compat info.
taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */,
// Verify that the compat controls are updated with new size compat info.
clearInvocations(mMockLayout);
clearInvocations(mController);
taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, true /* canShow */);
// Verify that compat controls are removed with null compat info.
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
false /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN),
null /* taskListener */);
verify(mMockLayout).release();
verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */ true);
// Verify that compat controls are removed with null task listener.
clearInvocations(mMockLayout);
clearInvocations(mController);
// Verify that compat controls are removed with no size compat and dismissed camera state.
taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID,
true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mController).createLayout(any(), eq(taskInfo), eq(mMockTaskListener));
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
false /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_DISMISSED),
null /* taskListener */);
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN),
/* taskListener= */ null);
verify(mMockLayout).release();
}
@Test
public void testOnCompatInfoChanged_createLayoutReturnsFalse() {
doReturn(false).when(mMockLayout).createLayout(anyBoolean());
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_HIDDEN);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
// Verify that the layout is created again.
clearInvocations(mMockLayout);
clearInvocations(mController);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mMockLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
}
@Test
public void testOnCompatInfoChanged_updateCompatInfoReturnsFalse() {
doReturn(false).when(mMockLayout).updateCompatInfo(any(), any(), anyBoolean());
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_HIDDEN);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
clearInvocations(mMockLayout);
clearInvocations(mController);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */ true);
// Verify that the layout is created again.
clearInvocations(mMockLayout);
clearInvocations(mController);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mMockLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
}
@Test
public void testOnDisplayAdded() {
mController.onDisplayAdded(DISPLAY_ID);
@@ -165,7 +199,7 @@ public class CompatUIControllerTest extends ShellTestCase {
public void testOnDisplayRemoved() {
mController.onDisplayAdded(DISPLAY_ID);
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN),
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN),
mMockTaskListener);
mController.onDisplayRemoved(DISPLAY_ID + 1);
@@ -183,7 +217,7 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testOnDisplayConfigurationChanged() {
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
mController.onDisplayConfigurationChanged(DISPLAY_ID + 1, new Configuration());
@@ -198,7 +232,7 @@ public class CompatUIControllerTest extends ShellTestCase {
public void testInsetsChanged() {
mController.onDisplayAdded(DISPLAY_ID);
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
InsetsState insetsState = new InsetsState();
InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
insetsSource.setFrame(0, 0, 1000, 1000);
@@ -219,22 +253,22 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testChangeButtonVisibilityOnImeShowHide() {
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
// Verify that the restart button is hidden after IME is showing.
mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ true);
verify(mMockLayout).updateVisibility(false);
// Verify button remains hidden while IME is showing.
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */,
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_HIDDEN);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, false /* canShow */);
verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */ false);
// Verify button is shown after IME is hidden.
mController.onImeVisibilityChanged(DISPLAY_ID, false /* isShowing */);
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
verify(mMockLayout).updateVisibility(true);
}
@@ -242,7 +276,7 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testChangeButtonVisibilityOnKeyguardOccludedChanged() {
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
// Verify that the restart button is hidden after keyguard becomes occluded.
mController.onKeyguardOccludedChanged(true);
@@ -250,11 +284,11 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockLayout).updateVisibility(false);
// Verify button remains hidden while keyguard is occluded.
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */,
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_HIDDEN);
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, false /* canShow */);
verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */ false);
// Verify button is shown after keyguard becomes not occluded.
mController.onKeyguardOccludedChanged(false);
@@ -265,9 +299,9 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testButtonRemainsHiddenOnKeyguardOccludedFalseWhenImeIsShowing() {
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ true);
mController.onKeyguardOccludedChanged(true);
verify(mMockLayout, times(2)).updateVisibility(false);
@@ -280,7 +314,7 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockLayout).updateVisibility(false);
// Verify button is shown after IME is not showing.
mController.onImeVisibilityChanged(DISPLAY_ID, false /* isShowing */);
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
verify(mMockLayout).updateVisibility(true);
}
@@ -288,9 +322,9 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testButtonRemainsHiddenOnImeHideWhenKeyguardIsOccluded() {
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ true);
mController.onKeyguardOccludedChanged(true);
verify(mMockLayout, times(2)).updateVisibility(false);
@@ -298,7 +332,7 @@ public class CompatUIControllerTest extends ShellTestCase {
clearInvocations(mMockLayout);
// Verify button remains hidden after IME is hidden since keyguard is occluded.
mController.onImeVisibilityChanged(DISPLAY_ID, false /* isShowing */);
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
verify(mMockLayout).updateVisibility(false);

View File

@@ -30,7 +30,6 @@ import static org.mockito.Mockito.verify;
import android.app.ActivityManager;
import android.app.TaskInfo;
import android.app.TaskInfo.CameraCompatControlState;
import android.content.res.Configuration;
import android.testing.AndroidTestingRunner;
import android.view.LayoutInflater;
import android.view.SurfaceControlViewHost;
@@ -69,29 +68,31 @@ public class CompatUILayoutTest extends ShellTestCase {
@Mock private SurfaceControlViewHost mViewHost;
private CompatUIWindowManager mWindowManager;
private CompatUILayout mCompatUILayout;
private CompatUILayout mLayout;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mWindowManager = new CompatUIWindowManager(mContext, new Configuration(),
mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(),
false /* hasShownSizeCompatHint */, false /* hasShownCameraCompatHint */);
mWindowManager = new CompatUIWindowManager(mContext,
createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN),
mSyncTransactionQueue, mCallback, mTaskListener,
new DisplayLayout(), /* hasShownSizeCompatHint= */ false,
/* hasShownCameraCompatHint= */ false);
mCompatUILayout = (CompatUILayout)
mLayout = (CompatUILayout)
LayoutInflater.from(mContext).inflate(R.layout.compat_ui_layout, null);
mCompatUILayout.inject(mWindowManager);
mLayout.inject(mWindowManager);
spyOn(mWindowManager);
spyOn(mCompatUILayout);
spyOn(mLayout);
doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
doReturn(mCompatUILayout).when(mWindowManager).inflateLayout();
doReturn(mLayout).when(mWindowManager).inflateLayout();
}
@Test
public void testOnClickForRestartButton() {
final ImageButton button = mCompatUILayout.findViewById(R.id.size_compat_restart_button);
final ImageButton button = mLayout.findViewById(R.id.size_compat_restart_button);
button.performClick();
verify(mWindowManager).onRestartButtonClicked();
@@ -102,7 +103,7 @@ public class CompatUILayoutTest extends ShellTestCase {
public void testOnLongClickForRestartButton() {
doNothing().when(mWindowManager).onRestartButtonLongClicked();
final ImageButton button = mCompatUILayout.findViewById(R.id.size_compat_restart_button);
final ImageButton button = mLayout.findViewById(R.id.size_compat_restart_button);
button.performLongClick();
verify(mWindowManager).onRestartButtonLongClicked();
@@ -110,20 +111,20 @@ public class CompatUILayoutTest extends ShellTestCase {
@Test
public void testOnClickForSizeCompatHint() {
mWindowManager.createLayout(true /* show */, createTaskInfo(true /* hasSizeCompat */,
CAMERA_COMPAT_CONTROL_HIDDEN));
final LinearLayout sizeCompatHint = mCompatUILayout.findViewById(R.id.size_compat_hint);
mWindowManager.mHasSizeCompat = true;
mWindowManager.createLayout(/* canShow= */ true);
final LinearLayout sizeCompatHint = mLayout.findViewById(R.id.size_compat_hint);
sizeCompatHint.performClick();
verify(mCompatUILayout).setSizeCompatHintVisibility(/* show= */ false);
verify(mLayout).setSizeCompatHintVisibility(/* show= */ false);
}
@Test
public void testUpdateCameraTreatmentButton_treatmentAppliedByDefault() {
mWindowManager.createLayout(true /* show */, createTaskInfo(true /* hasSizeCompat */,
CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED));
mWindowManager.mCameraCompatControlState = CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
mWindowManager.createLayout(/* canShow= */ true);
final ImageButton button =
mCompatUILayout.findViewById(R.id.camera_compat_treatment_button);
mLayout.findViewById(R.id.camera_compat_treatment_button);
button.performClick();
verify(mWindowManager).onCameraTreatmentButtonClicked();
@@ -138,10 +139,10 @@ public class CompatUILayoutTest extends ShellTestCase {
@Test
public void testUpdateCameraTreatmentButton_treatmentSuggestedByDefault() {
mWindowManager.createLayout(true /* show */, createTaskInfo(true /* hasSizeCompat */,
CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
mWindowManager.mCameraCompatControlState = CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
mWindowManager.createLayout(/* canShow= */ true);
final ImageButton button =
mCompatUILayout.findViewById(R.id.camera_compat_treatment_button);
mLayout.findViewById(R.id.camera_compat_treatment_button);
button.performClick();
verify(mWindowManager).onCameraTreatmentButtonClicked();
@@ -156,24 +157,24 @@ public class CompatUILayoutTest extends ShellTestCase {
@Test
public void testOnCameraDismissButtonClicked() {
mWindowManager.createLayout(true /* show */, createTaskInfo(true /* hasSizeCompat */,
CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
mWindowManager.mCameraCompatControlState = CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
mWindowManager.createLayout(/* canShow= */ true);
final ImageButton button =
mCompatUILayout.findViewById(R.id.camera_compat_dismiss_button);
mLayout.findViewById(R.id.camera_compat_dismiss_button);
button.performClick();
verify(mWindowManager).onCameraDismissButtonClicked();
verify(mCallback).onCameraControlStateUpdated(
TASK_ID, CAMERA_COMPAT_CONTROL_DISMISSED);
verify(mCompatUILayout).setCameraControlVisibility(/* show */ false);
verify(mLayout).setCameraControlVisibility(/* show */ false);
}
@Test
public void testOnLongClickForCameraTreatementButton() {
public void testOnLongClickForCameraTreatmentButton() {
doNothing().when(mWindowManager).onCameraButtonLongClicked();
final ImageButton button =
mCompatUILayout.findViewById(R.id.camera_compat_treatment_button);
mLayout.findViewById(R.id.camera_compat_treatment_button);
button.performLongClick();
verify(mWindowManager).onCameraButtonLongClicked();
@@ -183,7 +184,7 @@ public class CompatUILayoutTest extends ShellTestCase {
public void testOnLongClickForCameraDismissButton() {
doNothing().when(mWindowManager).onCameraButtonLongClicked();
final ImageButton button = mCompatUILayout.findViewById(R.id.camera_compat_dismiss_button);
final ImageButton button = mLayout.findViewById(R.id.camera_compat_dismiss_button);
button.performLongClick();
verify(mWindowManager).onCameraButtonLongClicked();
@@ -191,17 +192,18 @@ public class CompatUILayoutTest extends ShellTestCase {
@Test
public void testOnClickForCameraCompatHint() {
mWindowManager.createLayout(true /* show */, createTaskInfo(false /* hasSizeCompat */,
CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
final LinearLayout hint = mCompatUILayout.findViewById(R.id.camera_compat_hint);
mWindowManager.mCameraCompatControlState = CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
mWindowManager.createLayout(/* canShow= */ true);
final LinearLayout hint = mLayout.findViewById(R.id.camera_compat_hint);
hint.performClick();
verify(mCompatUILayout).setCameraCompatHintVisibility(/* show= */ false);
verify(mLayout).setCameraCompatHintVisibility(/* show= */ false);
}
private static TaskInfo createTaskInfo(boolean hasSizeCompat,
@CameraCompatControlState int cameraCompatControlState) {
ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
taskInfo.taskId = TASK_ID;
taskInfo.topActivityInSizeCompat = hasSizeCompat;
taskInfo.cameraCompatControlState = cameraCompatControlState;
return taskInfo;

View File

@@ -26,18 +26,16 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
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.view.DisplayInfo;
@@ -75,7 +73,7 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
@Mock private SyncTransactionQueue mSyncTransactionQueue;
@Mock private CompatUIController.CompatUICallback mCallback;
@Mock private ShellTaskOrganizer.TaskListener mTaskListener;
@Mock private CompatUILayout mCompatUILayout;
@Mock private CompatUILayout mLayout;
@Mock private SurfaceControlViewHost mViewHost;
private CompatUIWindowManager mWindowManager;
@@ -84,47 +82,97 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
public void setUp() {
MockitoAnnotations.initMocks(this);
mWindowManager = new CompatUIWindowManager(mContext, new Configuration(),
mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(),
false /* hasShownSizeCompatHint */, false /* hasShownSizeCompatHint */);
mWindowManager = new CompatUIWindowManager(mContext,
createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN),
mSyncTransactionQueue, mCallback, mTaskListener,
new DisplayLayout(), /* hasShownSizeCompatHint= */ false,
/* hasShownCameraCompatHint= */ false);
spyOn(mWindowManager);
doReturn(mCompatUILayout).when(mWindowManager).inflateLayout();
doReturn(mLayout).when(mWindowManager).inflateLayout();
doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
}
@Test
public void testCreateSizeCompatButton() {
// Not create layout if show is false.
mWindowManager.createLayout(false /* canShow */, createTaskInfo(true /* hasSizeCompat */,
CAMERA_COMPAT_CONTROL_HIDDEN));
// Doesn't create layout if show is false.
mWindowManager.mHasSizeCompat = true;
assertTrue(mWindowManager.createLayout(/* canShow= */ false));
verify(mWindowManager, never()).inflateLayout();
// Not create hint popup.
// Doesn't create hint popup.
mWindowManager.mShouldShowSizeCompatHint = false;
mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
CAMERA_COMPAT_CONTROL_HIDDEN));
assertTrue(mWindowManager.createLayout(/* canShow= */ true));
verify(mWindowManager).inflateLayout();
verify(mCompatUILayout, never()).setSizeCompatHintVisibility(true /* show */);
verify(mLayout).setRestartButtonVisibility(/* show= */ true);
verify(mLayout, never()).setSizeCompatHintVisibility(/* show= */ true);
// Create hint popup.
// Creates hint popup.
clearInvocations(mWindowManager);
clearInvocations(mLayout);
mWindowManager.release();
mWindowManager.mShouldShowSizeCompatHint = true;
mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
CAMERA_COMPAT_CONTROL_HIDDEN));
assertTrue(mWindowManager.createLayout(/* canShow= */ true));
verify(mWindowManager, times(2)).inflateLayout();
assertNotNull(mCompatUILayout);
verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */);
verify(mWindowManager).inflateLayout();
assertNotNull(mLayout);
verify(mLayout).setRestartButtonVisibility(/* show= */ true);
verify(mLayout).setSizeCompatHintVisibility(/* show= */ true);
assertFalse(mWindowManager.mShouldShowSizeCompatHint);
// Returns false and doesn't create layout if has Size Compat is false.
clearInvocations(mWindowManager);
mWindowManager.release();
mWindowManager.mHasSizeCompat = false;
assertFalse(mWindowManager.createLayout(/* canShow= */ true));
verify(mWindowManager, never()).inflateLayout();
}
@Test
public void testCreateCameraCompatControl() {
// Doesn't create layout if show is false.
mWindowManager.mCameraCompatControlState = CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
assertTrue(mWindowManager.createLayout(/* canShow= */ false));
verify(mWindowManager, never()).inflateLayout();
// Doesn't create hint popup.
mWindowManager.mShouldShowCameraCompatHint = false;
assertTrue(mWindowManager.createLayout(/* canShow= */ true));
verify(mWindowManager).inflateLayout();
verify(mLayout).setCameraControlVisibility(/* show= */ true);
verify(mLayout, never()).setCameraCompatHintVisibility(/* show= */ true);
// Creates hint popup.
clearInvocations(mWindowManager);
clearInvocations(mLayout);
mWindowManager.release();
mWindowManager.mShouldShowCameraCompatHint = true;
assertTrue(mWindowManager.createLayout(/* canShow= */ true));
verify(mWindowManager).inflateLayout();
assertNotNull(mLayout);
verify(mLayout).setCameraControlVisibility(/* show= */ true);
verify(mLayout).setCameraCompatHintVisibility(/* show= */ true);
assertFalse(mWindowManager.mShouldShowCameraCompatHint);
// Returns false and doesn't create layout if Camera Compat state is hidden
clearInvocations(mWindowManager);
mWindowManager.release();
mWindowManager.mCameraCompatControlState = CAMERA_COMPAT_CONTROL_HIDDEN;
assertFalse(mWindowManager.createLayout(/* canShow= */ true));
verify(mWindowManager, never()).inflateLayout();
}
@Test
public void testRelease() {
mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
CAMERA_COMPAT_CONTROL_HIDDEN));
mWindowManager.mHasSizeCompat = true;
mWindowManager.createLayout(/* canShow= */ true);
verify(mWindowManager).inflateLayout();
@@ -135,12 +183,13 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
@Test
public void testUpdateCompatInfo() {
TaskInfo taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
mWindowManager.createLayout(true /* canShow */, taskInfo);
mWindowManager.mHasSizeCompat = true;
mWindowManager.createLayout(/* canShow= */ true);
// No diff
clearInvocations(mWindowManager);
mWindowManager.updateCompatInfo(taskInfo, mTaskListener, true /* canShow */);
TaskInfo taskInfo = createTaskInfo(/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN);
assertTrue(mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true));
verify(mWindowManager, never()).updateSurfacePosition();
verify(mWindowManager, never()).release();
@@ -150,84 +199,98 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
clearInvocations(mWindowManager);
final ShellTaskOrganizer.TaskListener newTaskListener = mock(
ShellTaskOrganizer.TaskListener.class);
mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
assertTrue(mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true));
verify(mWindowManager).release();
verify(mWindowManager).createLayout(true);
// Change in Size Compat to false, hides restart button.
clearInvocations(mWindowManager);
taskInfo = createTaskInfo(false /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
verify(mCompatUILayout).setRestartButtonVisibility(/* show */ false);
// Change in Size Compat to true, shows restart button.
clearInvocations(mWindowManager);
clearInvocations(mCompatUILayout);
taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
verify(mCompatUILayout).setRestartButtonVisibility(/* show */ true);
verify(mWindowManager).createLayout(/* canShow= */ true);
// Change Camera Compat state, show a control.
clearInvocations(mWindowManager);
clearInvocations(mCompatUILayout);
taskInfo = createTaskInfo(true /* hasSizeCompat */,
clearInvocations(mLayout);
taskInfo = createTaskInfo(/* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
assertTrue(mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true));
verify(mCompatUILayout).setCameraControlVisibility(/* show */ true);
verify(mCompatUILayout).updateCameraTreatmentButton(
verify(mLayout).setCameraControlVisibility(/* show= */ true);
verify(mLayout).updateCameraTreatmentButton(
CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
// Change Camera Compat state, update a control.
clearInvocations(mWindowManager);
clearInvocations(mCompatUILayout);
taskInfo = createTaskInfo(true /* hasSizeCompat */,
clearInvocations(mLayout);
taskInfo = createTaskInfo(/* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
assertTrue(mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true));
verify(mCompatUILayout).setCameraControlVisibility(/* show */ true);
verify(mCompatUILayout).updateCameraTreatmentButton(
verify(mLayout).setCameraControlVisibility(/* show= */ true);
verify(mLayout).updateCameraTreatmentButton(
CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
// Change Camera Compat state to hidden, hide a control.
// Change has Size Compat to false, hides restart button.
clearInvocations(mWindowManager);
clearInvocations(mCompatUILayout);
taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
clearInvocations(mLayout);
taskInfo = createTaskInfo(/* hasSizeCompat= */ false,
CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
assertTrue(mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true));
verify(mCompatUILayout).setCameraControlVisibility(/* show */ false);
verify(mLayout).setRestartButtonVisibility(/* show= */ false);
// Change has Size Compat to true, shows restart button.
clearInvocations(mWindowManager);
clearInvocations(mLayout);
taskInfo = createTaskInfo(/* hasSizeCompat= */ true,
CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
assertTrue(mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true));
verify(mLayout).setRestartButtonVisibility(/* show= */ true);
// Change Camera Compat state to dismissed, hide a control.
clearInvocations(mWindowManager);
clearInvocations(mLayout);
taskInfo = createTaskInfo(/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_DISMISSED);
assertTrue(mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true));
verify(mLayout).setCameraControlVisibility(/* show= */ false);
// Change task bounds, update position.
clearInvocations(mWindowManager);
taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
clearInvocations(mLayout);
taskInfo = createTaskInfo(/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN);
taskInfo.configuration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000));
mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
assertTrue(mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true));
verify(mWindowManager).updateSurfacePosition();
// Change has Size Compat to false, release layout.
clearInvocations(mWindowManager);
clearInvocations(mLayout);
taskInfo = createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN);
assertFalse(
mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true));
verify(mWindowManager).release();
}
@Test
public void testUpdateCompatInfoLayoutNotInflatedYet() {
TaskInfo taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
mWindowManager.createLayout(false /* canShow */, taskInfo);
mWindowManager.mHasSizeCompat = true;
mWindowManager.createLayout(/* canShow= */ false);
verify(mWindowManager, never()).inflateLayout();
// Change topActivityInSizeCompat to false and pass canShow true, layout shouldn't be
// inflated
clearInvocations(mWindowManager);
taskInfo = createTaskInfo(false /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
mWindowManager.updateCompatInfo(taskInfo, mTaskListener, true /* canShow */);
TaskInfo taskInfo = createTaskInfo(/* hasSizeCompat= */ false,
CAMERA_COMPAT_CONTROL_HIDDEN);
mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true);
verify(mWindowManager, never()).inflateLayout();
// Change topActivityInSizeCompat to true and pass canShow true, layout should be inflated.
clearInvocations(mWindowManager);
taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
mWindowManager.updateCompatInfo(taskInfo, mTaskListener, true /* canShow */);
taskInfo = createTaskInfo(/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN);
mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true);
verify(mWindowManager).inflateLayout();
}
@@ -278,24 +341,24 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
// Create button if it is not created.
mWindowManager.mLayout = null;
mWindowManager.mHasSizeCompat = true;
mWindowManager.updateVisibility(true /* canShow */);
mWindowManager.updateVisibility(/* canShow= */ true);
verify(mWindowManager).createLayout(true /* canShow */);
verify(mWindowManager).createLayout(/* canShow= */ true);
// Hide button.
clearInvocations(mWindowManager);
doReturn(View.VISIBLE).when(mCompatUILayout).getVisibility();
mWindowManager.updateVisibility(false /* canShow */);
doReturn(View.VISIBLE).when(mLayout).getVisibility();
mWindowManager.updateVisibility(/* canShow= */ false);
verify(mWindowManager, never()).createLayout(anyBoolean(), any());
verify(mCompatUILayout).setVisibility(View.GONE);
verify(mWindowManager, never()).createLayout(anyBoolean());
verify(mLayout).setVisibility(View.GONE);
// Show button.
doReturn(View.GONE).when(mCompatUILayout).getVisibility();
mWindowManager.updateVisibility(true /* canShow */);
doReturn(View.GONE).when(mLayout).getVisibility();
mWindowManager.updateVisibility(/* canShow= */ true);
verify(mWindowManager, never()).createLayout(anyBoolean(), any());
verify(mCompatUILayout).setVisibility(View.VISIBLE);
verify(mWindowManager, never()).createLayout(anyBoolean());
verify(mLayout).setVisibility(View.VISIBLE);
}
@Test
@@ -308,32 +371,32 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
@Test
public void testOnCameraDismissButtonClicked() {
mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
clearInvocations(mCompatUILayout);
mWindowManager.mCameraCompatControlState = CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
mWindowManager.createLayout(/* canShow= */ true);
clearInvocations(mLayout);
mWindowManager.onCameraDismissButtonClicked();
verify(mCallback).onCameraControlStateUpdated(TASK_ID, CAMERA_COMPAT_CONTROL_DISMISSED);
verify(mCompatUILayout).setCameraControlVisibility(/* show= */ false);
verify(mLayout).setCameraControlVisibility(/* show= */ false);
}
@Test
public void testOnCameraTreatmentButtonClicked() {
mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
clearInvocations(mCompatUILayout);
mWindowManager.mCameraCompatControlState = CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
mWindowManager.createLayout(/* canShow= */ true);
clearInvocations(mLayout);
mWindowManager.onCameraTreatmentButtonClicked();
verify(mCallback).onCameraControlStateUpdated(
TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
verify(mCompatUILayout).updateCameraTreatmentButton(
verify(mLayout).updateCameraTreatmentButton(
CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
mWindowManager.onCameraTreatmentButtonClicked();
verify(mCallback).onCameraControlStateUpdated(
TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
verify(mCompatUILayout).updateCameraTreatmentButton(
verify(mLayout).updateCameraTreatmentButton(
CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
}
@@ -347,65 +410,37 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
@Test
public void testOnRestartButtonLongClicked_showHint() {
// Not create hint popup.
mWindowManager.mHasSizeCompat = true;
mWindowManager.mShouldShowSizeCompatHint = false;
mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
CAMERA_COMPAT_CONTROL_HIDDEN));
mWindowManager.createLayout(/* canShow= */ true);
verify(mWindowManager).inflateLayout();
verify(mCompatUILayout, never()).setSizeCompatHintVisibility(true /* show */);
verify(mLayout, never()).setSizeCompatHintVisibility(/* show= */ true);
mWindowManager.onRestartButtonLongClicked();
verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */);
verify(mLayout).setSizeCompatHintVisibility(/* show= */ true);
}
@Test
public void testOnCamerControlLongClicked_showHint() {
// Not create hint popup.
mWindowManager.mCameraCompatControlState = CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
mWindowManager.mShouldShowCameraCompatHint = false;
mWindowManager.createLayout(true /* canShow */, createTaskInfo(false /* hasSizeCompat */,
CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
mWindowManager.createLayout(/* canShow= */ true);
verify(mWindowManager).inflateLayout();
verify(mCompatUILayout, never()).setCameraCompatHintVisibility(true /* show */);
verify(mLayout, never()).setCameraCompatHintVisibility(/* show= */ true);
mWindowManager.onCameraButtonLongClicked();
verify(mCompatUILayout).setCameraCompatHintVisibility(true /* show */);
}
@Test
public void testCreateCameraCompatControl() {
// Not create layout if show is false.
mWindowManager.createLayout(false /* canShow */, createTaskInfo(false /* hasSizeCompat */,
CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
verify(mWindowManager, never()).inflateLayout();
// Not create hint popup.
mWindowManager.mShouldShowCameraCompatHint = false;
mWindowManager.createLayout(true /* canShow */, createTaskInfo(false /* hasSizeCompat */,
CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
verify(mWindowManager).inflateLayout();
verify(mCompatUILayout, never()).setCameraCompatHintVisibility(true /* show */);
verify(mCompatUILayout).setCameraControlVisibility(true /* show */);
// Create hint popup.
mWindowManager.release();
mWindowManager.mShouldShowCameraCompatHint = true;
mWindowManager.createLayout(true /* canShow */, createTaskInfo(false /* hasSizeCompat */,
CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
verify(mWindowManager, times(2)).inflateLayout();
assertNotNull(mCompatUILayout);
verify(mCompatUILayout, times(2)).setCameraControlVisibility(true /* show */);
assertFalse(mWindowManager.mShouldShowCameraCompatHint);
verify(mLayout).setCameraCompatHintVisibility(/* show= */ true);
}
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;
return taskInfo;