Merge "Stop assuming the existence of displays when tasks appear" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
54300c5851
@@ -95,6 +95,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
|
|||||||
mDragResizeCallback = dragResizeCallback;
|
mDragResizeCallback = dragResizeCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
void relayout(ActivityManager.RunningTaskInfo taskInfo) {
|
void relayout(ActivityManager.RunningTaskInfo taskInfo) {
|
||||||
final int shadowRadiusDp = taskInfo.isFocused
|
final int shadowRadiusDp = taskInfo.isFocused
|
||||||
? DECOR_SHADOW_FOCUSED_THICKNESS_IN_DIP : DECOR_SHADOW_UNFOCUSED_THICKNESS_IN_DIP;
|
? DECOR_SHADOW_FOCUSED_THICKNESS_IN_DIP : DECOR_SHADOW_UNFOCUSED_THICKNESS_IN_DIP;
|
||||||
@@ -119,8 +120,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (mResult.mRootView == null) {
|
if (mResult.mRootView == null) {
|
||||||
// This means the task is hidden. Nothing is set up in this case including the
|
// This means something blocks the window decor from showing, e.g. the task is hidden.
|
||||||
// decoration surface.
|
// Nothing is set up in this case including the decoration surface.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (oldRootView != mResult.mRootView) {
|
if (oldRootView != mResult.mRootView) {
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ import android.window.WindowContainerTransaction;
|
|||||||
import com.android.wm.shell.ShellTaskOrganizer;
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
import com.android.wm.shell.common.DisplayController;
|
import com.android.wm.shell.common.DisplayController;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages a container surface and a windowless window to show window decoration. Responsible to
|
* Manages a container surface and a windowless window to show window decoration. Responsible to
|
||||||
* update window decoration window state and layout parameters on task info changes and so that
|
* update window decoration window state and layout parameters on task info changes and so that
|
||||||
@@ -53,7 +55,8 @@ import com.android.wm.shell.common.DisplayController;
|
|||||||
*
|
*
|
||||||
* @param <T> The type of the root view
|
* @param <T> The type of the root view
|
||||||
*/
|
*/
|
||||||
public class WindowDecoration<T extends View & TaskFocusStateConsumer> implements AutoCloseable {
|
public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
|
||||||
|
implements AutoCloseable {
|
||||||
private static final int[] CAPTION_INSETS_TYPES = { InsetsState.ITYPE_CAPTION_BAR };
|
private static final int[] CAPTION_INSETS_TYPES = { InsetsState.ITYPE_CAPTION_BAR };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,6 +65,20 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement
|
|||||||
final Context mContext;
|
final Context mContext;
|
||||||
final DisplayController mDisplayController;
|
final DisplayController mDisplayController;
|
||||||
final ShellTaskOrganizer mTaskOrganizer;
|
final ShellTaskOrganizer mTaskOrganizer;
|
||||||
|
final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier;
|
||||||
|
final SurfaceControlViewHostFactory mSurfaceControlViewHostFactory;
|
||||||
|
private final DisplayController.OnDisplaysChangedListener mOnDisplaysChangedListener =
|
||||||
|
new DisplayController.OnDisplaysChangedListener() {
|
||||||
|
@Override
|
||||||
|
public void onDisplayAdded(int displayId) {
|
||||||
|
if (mTaskInfo.displayId != displayId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mDisplayController.removeDisplayWindowListener(this);
|
||||||
|
relayout(mTaskInfo);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
RunningTaskInfo mTaskInfo;
|
RunningTaskInfo mTaskInfo;
|
||||||
final SurfaceControl mTaskSurface;
|
final SurfaceControl mTaskSurface;
|
||||||
@@ -71,7 +88,7 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement
|
|||||||
SurfaceControl mDecorationContainerSurface;
|
SurfaceControl mDecorationContainerSurface;
|
||||||
SurfaceControl mTaskBackgroundSurface;
|
SurfaceControl mTaskBackgroundSurface;
|
||||||
|
|
||||||
private CaptionWindowManager mCaptionWindowManager;
|
private final CaptionWindowManager mCaptionWindowManager;
|
||||||
private SurfaceControlViewHost mViewHost;
|
private SurfaceControlViewHost mViewHost;
|
||||||
|
|
||||||
private final Rect mCaptionInsetsRect = new Rect();
|
private final Rect mCaptionInsetsRect = new Rect();
|
||||||
@@ -84,11 +101,25 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement
|
|||||||
ShellTaskOrganizer taskOrganizer,
|
ShellTaskOrganizer taskOrganizer,
|
||||||
RunningTaskInfo taskInfo,
|
RunningTaskInfo taskInfo,
|
||||||
SurfaceControl taskSurface) {
|
SurfaceControl taskSurface) {
|
||||||
|
this(context, displayController, taskOrganizer, taskInfo, taskSurface,
|
||||||
|
SurfaceControl.Builder::new, new SurfaceControlViewHostFactory() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowDecoration(
|
||||||
|
Context context,
|
||||||
|
DisplayController displayController,
|
||||||
|
ShellTaskOrganizer taskOrganizer,
|
||||||
|
RunningTaskInfo taskInfo,
|
||||||
|
SurfaceControl taskSurface,
|
||||||
|
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
|
||||||
|
SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mDisplayController = displayController;
|
mDisplayController = displayController;
|
||||||
mTaskOrganizer = taskOrganizer;
|
mTaskOrganizer = taskOrganizer;
|
||||||
mTaskInfo = taskInfo;
|
mTaskInfo = taskInfo;
|
||||||
mTaskSurface = taskSurface;
|
mTaskSurface = taskSurface;
|
||||||
|
mSurfaceControlBuilderSupplier = surfaceControlBuilderSupplier;
|
||||||
|
mSurfaceControlViewHostFactory = surfaceControlViewHostFactory;
|
||||||
|
|
||||||
mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
|
mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
|
||||||
mDecorWindowContext = mContext.createConfigurationContext(mTaskInfo.getConfiguration());
|
mDecorWindowContext = mContext.createConfigurationContext(mTaskInfo.getConfiguration());
|
||||||
@@ -99,6 +130,15 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement
|
|||||||
new CaptionWindowManager(mTaskInfo.getConfiguration(), mTaskSurface);
|
new CaptionWindowManager(mTaskInfo.getConfiguration(), mTaskSurface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by {@link WindowDecoration} to trigger a new relayout because the requirements for a
|
||||||
|
* relayout weren't satisfied are satisfied now.
|
||||||
|
*
|
||||||
|
* @param taskInfo The previous {@link RunningTaskInfo} passed into {@link #relayout} or the
|
||||||
|
* constructor.
|
||||||
|
*/
|
||||||
|
abstract void relayout(RunningTaskInfo taskInfo);
|
||||||
|
|
||||||
void relayout(RunningTaskInfo taskInfo, int layoutResId, T rootView, float captionHeightDp,
|
void relayout(RunningTaskInfo taskInfo, int layoutResId, T rootView, float captionHeightDp,
|
||||||
Rect outsetsDp, float shadowRadiusDp, SurfaceControl.Transaction t,
|
Rect outsetsDp, float shadowRadiusDp, SurfaceControl.Transaction t,
|
||||||
WindowContainerTransaction wct, RelayoutResult<T> outResult) {
|
WindowContainerTransaction wct, RelayoutResult<T> outResult) {
|
||||||
@@ -110,7 +150,7 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!mTaskInfo.isVisible) {
|
if (!mTaskInfo.isVisible) {
|
||||||
close();
|
releaseViews();
|
||||||
t.hide(mTaskSurface);
|
t.hide(mTaskSurface);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -123,10 +163,14 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement
|
|||||||
rootView = null; // Clear it just in case we use it accidentally
|
rootView = null; // Clear it just in case we use it accidentally
|
||||||
final Configuration taskConfig = mTaskInfo.getConfiguration();
|
final Configuration taskConfig = mTaskInfo.getConfiguration();
|
||||||
if (oldTaskConfig.densityDpi != taskConfig.densityDpi
|
if (oldTaskConfig.densityDpi != taskConfig.densityDpi
|
||||||
|
|| mDisplay == null
|
||||||
|| mDisplay.getDisplayId() != mTaskInfo.displayId) {
|
|| mDisplay.getDisplayId() != mTaskInfo.displayId) {
|
||||||
close();
|
releaseViews();
|
||||||
|
|
||||||
mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
|
if (!obtainDisplayOrRegisterListener()) {
|
||||||
|
outResult.mRootView = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
mDecorWindowContext = mContext.createConfigurationContext(taskConfig);
|
mDecorWindowContext = mContext.createConfigurationContext(taskConfig);
|
||||||
if (layoutResId != 0) {
|
if (layoutResId != 0) {
|
||||||
outResult.mRootView =
|
outResult.mRootView =
|
||||||
@@ -141,7 +185,7 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement
|
|||||||
|
|
||||||
// DecorationContainerSurface
|
// DecorationContainerSurface
|
||||||
if (mDecorationContainerSurface == null) {
|
if (mDecorationContainerSurface == null) {
|
||||||
final SurfaceControl.Builder builder = new SurfaceControl.Builder();
|
final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
|
||||||
mDecorationContainerSurface = builder
|
mDecorationContainerSurface = builder
|
||||||
.setName("Decor container of Task=" + mTaskInfo.taskId)
|
.setName("Decor container of Task=" + mTaskInfo.taskId)
|
||||||
.setContainerLayer()
|
.setContainerLayer()
|
||||||
@@ -168,7 +212,7 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement
|
|||||||
|
|
||||||
// TaskBackgroundSurface
|
// TaskBackgroundSurface
|
||||||
if (mTaskBackgroundSurface == null) {
|
if (mTaskBackgroundSurface == null) {
|
||||||
final SurfaceControl.Builder builder = new SurfaceControl.Builder();
|
final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
|
||||||
mTaskBackgroundSurface = builder
|
mTaskBackgroundSurface = builder
|
||||||
.setName("Background of Task=" + mTaskInfo.taskId)
|
.setName("Background of Task=" + mTaskInfo.taskId)
|
||||||
.setEffectLayer()
|
.setEffectLayer()
|
||||||
@@ -195,7 +239,7 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement
|
|||||||
lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
|
lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
|
||||||
lp.setTrustedOverlay();
|
lp.setTrustedOverlay();
|
||||||
if (mViewHost == null) {
|
if (mViewHost == null) {
|
||||||
mViewHost = new SurfaceControlViewHost(mDecorWindowContext, mDisplay,
|
mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay,
|
||||||
mCaptionWindowManager, true);
|
mCaptionWindowManager, true);
|
||||||
mViewHost.setView(outResult.mRootView, lp);
|
mViewHost.setView(outResult.mRootView, lp);
|
||||||
} else {
|
} else {
|
||||||
@@ -225,8 +269,22 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement
|
|||||||
.show(mTaskSurface);
|
.show(mTaskSurface);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void close() {
|
* Obtains the {@link Display} instance for the display ID in {@link #mTaskInfo} if it exists or
|
||||||
|
* registers {@link #mOnDisplaysChangedListener} if it doesn't.
|
||||||
|
*
|
||||||
|
* @return {@code true} if the {@link Display} instance exists; or {@code false} otherwise
|
||||||
|
*/
|
||||||
|
private boolean obtainDisplayOrRegisterListener() {
|
||||||
|
mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
|
||||||
|
if (mDisplay == null) {
|
||||||
|
mDisplayController.addDisplayWindowListener(mOnDisplaysChangedListener);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void releaseViews() {
|
||||||
if (mViewHost != null) {
|
if (mViewHost != null) {
|
||||||
mViewHost.release();
|
mViewHost.release();
|
||||||
mViewHost = null;
|
mViewHost = null;
|
||||||
@@ -243,6 +301,12 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
mDisplayController.removeDisplayWindowListener(mOnDisplaysChangedListener);
|
||||||
|
releaseViews();
|
||||||
|
}
|
||||||
|
|
||||||
static class RelayoutResult<T extends View & TaskFocusStateConsumer> {
|
static class RelayoutResult<T extends View & TaskFocusStateConsumer> {
|
||||||
int mWidth;
|
int mWidth;
|
||||||
int mHeight;
|
int mHeight;
|
||||||
@@ -267,4 +331,11 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement
|
|||||||
super.setConfiguration(configuration);
|
super.setConfiguration(configuration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SurfaceControlViewHostFactory {
|
||||||
|
default SurfaceControlViewHost create(
|
||||||
|
Context c, Display d, WindowlessWindowManager wmm, boolean useSfChoreographer) {
|
||||||
|
return new SurfaceControlViewHost(c, d, wmm, useSfChoreographer);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.RETURNS_SELF;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
import android.view.SurfaceControl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to provide mocks for {@link SurfaceControl.Builder} and
|
||||||
|
* {@link SurfaceControl.Transaction} with method chaining support.
|
||||||
|
*/
|
||||||
|
public class MockSurfaceControlHelper {
|
||||||
|
private MockSurfaceControlHelper() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a mock {@link SurfaceControl.Builder} that supports method chaining and return the
|
||||||
|
* given {@link SurfaceControl} when calling {@link SurfaceControl.Builder#build()}.
|
||||||
|
*
|
||||||
|
* @param mockSurfaceControl the first {@link SurfaceControl} to return
|
||||||
|
* @param mockSurfaceControls following {@link SurfaceControl} to return
|
||||||
|
* @return the mock of {@link SurfaceControl.Builder}
|
||||||
|
*/
|
||||||
|
public static SurfaceControl.Builder createMockSurfaceControlBuilder(
|
||||||
|
SurfaceControl mockSurfaceControl, SurfaceControl... mockSurfaceControls) {
|
||||||
|
final SurfaceControl.Builder mockBuilder = mock(SurfaceControl.Builder.class, RETURNS_SELF);
|
||||||
|
doReturn(mockSurfaceControl, (Object[]) mockSurfaceControls)
|
||||||
|
.when(mockBuilder)
|
||||||
|
.build();
|
||||||
|
return mockBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a mock {@link SurfaceControl.Transaction} that supports method chaining.
|
||||||
|
* @return the mock of {@link SurfaceControl.Transaction}
|
||||||
|
*/
|
||||||
|
public static SurfaceControl.Transaction createMockSurfaceControlTransaction() {
|
||||||
|
return mock(SurfaceControl.Transaction.class, RETURNS_SELF);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,8 +25,10 @@ import static org.mockito.Mockito.mock;
|
|||||||
|
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.app.WindowConfiguration;
|
import android.app.WindowConfiguration;
|
||||||
|
import android.graphics.Point;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
import android.view.Display;
|
||||||
import android.window.IWindowContainerToken;
|
import android.window.IWindowContainerToken;
|
||||||
import android.window.WindowContainerToken;
|
import android.window.WindowContainerToken;
|
||||||
|
|
||||||
@@ -38,6 +40,10 @@ public final class TestRunningTaskInfoBuilder {
|
|||||||
private int mParentTaskId = INVALID_TASK_ID;
|
private int mParentTaskId = INVALID_TASK_ID;
|
||||||
private @WindowConfiguration.ActivityType int mActivityType = ACTIVITY_TYPE_STANDARD;
|
private @WindowConfiguration.ActivityType int mActivityType = ACTIVITY_TYPE_STANDARD;
|
||||||
private @WindowConfiguration.WindowingMode int mWindowingMode = WINDOWING_MODE_UNDEFINED;
|
private @WindowConfiguration.WindowingMode int mWindowingMode = WINDOWING_MODE_UNDEFINED;
|
||||||
|
private int mDisplayId = Display.DEFAULT_DISPLAY;
|
||||||
|
private ActivityManager.TaskDescription.Builder mTaskDescriptionBuilder = null;
|
||||||
|
private final Point mPositionInParent = new Point();
|
||||||
|
private boolean mIsVisible = false;
|
||||||
|
|
||||||
public static WindowContainerToken createMockWCToken() {
|
public static WindowContainerToken createMockWCToken() {
|
||||||
final IWindowContainerToken itoken = mock(IWindowContainerToken.class);
|
final IWindowContainerToken itoken = mock(IWindowContainerToken.class);
|
||||||
@@ -68,17 +74,42 @@ public final class TestRunningTaskInfoBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TestRunningTaskInfoBuilder setDisplayId(int displayId) {
|
||||||
|
mDisplayId = displayId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestRunningTaskInfoBuilder setTaskDescriptionBuilder(
|
||||||
|
ActivityManager.TaskDescription.Builder builder) {
|
||||||
|
mTaskDescriptionBuilder = builder;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestRunningTaskInfoBuilder setPositionInParent(int x, int y) {
|
||||||
|
mPositionInParent.set(x, y);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestRunningTaskInfoBuilder setVisible(boolean isVisible) {
|
||||||
|
mIsVisible = isVisible;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public ActivityManager.RunningTaskInfo build() {
|
public ActivityManager.RunningTaskInfo build() {
|
||||||
final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
|
final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
|
||||||
info.parentTaskId = INVALID_TASK_ID;
|
|
||||||
info.taskId = sNextTaskId++;
|
info.taskId = sNextTaskId++;
|
||||||
info.parentTaskId = mParentTaskId;
|
info.parentTaskId = mParentTaskId;
|
||||||
|
info.displayId = mDisplayId;
|
||||||
info.configuration.windowConfiguration.setBounds(mBounds);
|
info.configuration.windowConfiguration.setBounds(mBounds);
|
||||||
info.configuration.windowConfiguration.setActivityType(mActivityType);
|
info.configuration.windowConfiguration.setActivityType(mActivityType);
|
||||||
info.configuration.windowConfiguration.setWindowingMode(mWindowingMode);
|
info.configuration.windowConfiguration.setWindowingMode(mWindowingMode);
|
||||||
info.token = mToken;
|
info.token = mToken;
|
||||||
info.isResizeable = true;
|
info.isResizeable = true;
|
||||||
info.supportsMultiWindow = true;
|
info.supportsMultiWindow = true;
|
||||||
|
info.taskDescription =
|
||||||
|
mTaskDescriptionBuilder != null ? mTaskDescriptionBuilder.build() : null;
|
||||||
|
info.positionInParent = mPositionInParent;
|
||||||
|
info.isVisible = mIsVisible;
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import static android.view.Surface.ROTATION_0;
|
|||||||
import static android.view.Surface.ROTATION_270;
|
import static android.view.Surface.ROTATION_270;
|
||||||
import static android.view.Surface.ROTATION_90;
|
import static android.view.Surface.ROTATION_90;
|
||||||
|
|
||||||
|
import static com.android.wm.shell.MockSurfaceControlHelper.createMockSurfaceControlTransaction;
|
||||||
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
|
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
|
||||||
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
|
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
|
||||||
|
|
||||||
@@ -37,6 +38,7 @@ import android.view.SurfaceControl;
|
|||||||
|
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
|
import com.android.wm.shell.MockSurfaceControlHelper;
|
||||||
import com.android.wm.shell.ShellTestCase;
|
import com.android.wm.shell.ShellTestCase;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -103,7 +105,8 @@ public class PipAnimationControllerTest extends ShellTestCase {
|
|||||||
final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
|
final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
|
||||||
.getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null,
|
.getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null,
|
||||||
TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
|
TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
|
||||||
oldAnimator.setSurfaceControlTransactionFactory(PipDummySurfaceControlTx::new);
|
oldAnimator.setSurfaceControlTransactionFactory(
|
||||||
|
MockSurfaceControlHelper::createMockSurfaceControlTransaction);
|
||||||
oldAnimator.start();
|
oldAnimator.start();
|
||||||
|
|
||||||
final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
|
final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
|
||||||
@@ -133,7 +136,7 @@ public class PipAnimationControllerTest extends ShellTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void pipTransitionAnimator_rotatedEndValue() {
|
public void pipTransitionAnimator_rotatedEndValue() {
|
||||||
final PipDummySurfaceControlTx tx = new PipDummySurfaceControlTx();
|
final SurfaceControl.Transaction tx = createMockSurfaceControlTransaction();
|
||||||
final Rect startBounds = new Rect(200, 700, 400, 800);
|
final Rect startBounds = new Rect(200, 700, 400, 800);
|
||||||
final Rect endBounds = new Rect(0, 0, 500, 1000);
|
final Rect endBounds = new Rect(0, 0, 500, 1000);
|
||||||
// Fullscreen to PiP.
|
// Fullscreen to PiP.
|
||||||
@@ -183,7 +186,8 @@ public class PipAnimationControllerTest extends ShellTestCase {
|
|||||||
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
|
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
|
||||||
.getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null,
|
.getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null,
|
||||||
TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
|
TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
|
||||||
animator.setSurfaceControlTransactionFactory(PipDummySurfaceControlTx::new);
|
animator.setSurfaceControlTransactionFactory(
|
||||||
|
MockSurfaceControlHelper::createMockSurfaceControlTransaction);
|
||||||
|
|
||||||
animator.setPipAnimationCallback(mPipAnimationCallback);
|
animator.setPipAnimationCallback(mPipAnimationCallback);
|
||||||
|
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.pip;
|
|
||||||
|
|
||||||
import android.graphics.Matrix;
|
|
||||||
import android.view.SurfaceControl;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A dummy {@link SurfaceControl.Transaction} class for testing purpose and supports
|
|
||||||
* method chaining.
|
|
||||||
*/
|
|
||||||
public class PipDummySurfaceControlTx extends SurfaceControl.Transaction {
|
|
||||||
@Override
|
|
||||||
public SurfaceControl.Transaction setAlpha(SurfaceControl leash, float alpha) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SurfaceControl.Transaction setPosition(SurfaceControl leash, float x, float y) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SurfaceControl.Transaction setWindowCrop(SurfaceControl leash, int w, int h) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SurfaceControl.Transaction setCornerRadius(SurfaceControl leash, float radius) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SurfaceControl.Transaction setShadowRadius(SurfaceControl leash, float radius) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SurfaceControl.Transaction setMatrix(SurfaceControl leash, Matrix matrix,
|
|
||||||
float[] float9) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SurfaceControl.Transaction setFrameTimelineVsync(long frameTimelineVsyncId) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void apply() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -44,6 +44,7 @@ import android.util.Size;
|
|||||||
import android.view.DisplayInfo;
|
import android.view.DisplayInfo;
|
||||||
import android.window.WindowContainerToken;
|
import android.window.WindowContainerToken;
|
||||||
|
|
||||||
|
import com.android.wm.shell.MockSurfaceControlHelper;
|
||||||
import com.android.wm.shell.ShellTaskOrganizer;
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
import com.android.wm.shell.ShellTestCase;
|
import com.android.wm.shell.ShellTestCase;
|
||||||
import com.android.wm.shell.TestShellExecutor;
|
import com.android.wm.shell.TestShellExecutor;
|
||||||
@@ -246,7 +247,8 @@ public class PipTaskOrganizerTest extends ShellTestCase {
|
|||||||
mPipBoundsState.setDisplayLayout(new DisplayLayout(info,
|
mPipBoundsState.setDisplayLayout(new DisplayLayout(info,
|
||||||
mContext.getResources(), true, true));
|
mContext.getResources(), true, true));
|
||||||
mSpiedPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
|
mSpiedPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
|
||||||
mSpiedPipTaskOrganizer.setSurfaceControlTransactionFactory(PipDummySurfaceControlTx::new);
|
mSpiedPipTaskOrganizer.setSurfaceControlTransactionFactory(
|
||||||
|
MockSurfaceControlHelper::createMockSurfaceControlTransaction);
|
||||||
doNothing().when(mSpiedPipTaskOrganizer).enterPipWithAlphaAnimation(any(), anyLong());
|
doNothing().when(mSpiedPipTaskOrganizer).enterPipWithAlphaAnimation(any(), anyLong());
|
||||||
doNothing().when(mSpiedPipTaskOrganizer).scheduleAnimateResizePip(any(), anyInt(), any());
|
doNothing().when(mSpiedPipTaskOrganizer).scheduleAnimateResizePip(any(), anyInt(), any());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
* 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.windowdecor;
|
||||||
|
|
||||||
|
import static com.android.wm.shell.MockSurfaceControlHelper.createMockSurfaceControlBuilder;
|
||||||
|
import static com.android.wm.shell.MockSurfaceControlHelper.createMockSurfaceControlTransaction;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.any;
|
||||||
|
import static org.mockito.Mockito.anyBoolean;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.eq;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.same;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import android.app.ActivityManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.testing.AndroidTestingRunner;
|
||||||
|
import android.view.Display;
|
||||||
|
import android.view.SurfaceControl;
|
||||||
|
import android.view.SurfaceControlViewHost;
|
||||||
|
import android.view.View;
|
||||||
|
import android.window.WindowContainerTransaction;
|
||||||
|
|
||||||
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
|
import com.android.wm.shell.ShellTestCase;
|
||||||
|
import com.android.wm.shell.TestRunningTaskInfoBuilder;
|
||||||
|
import com.android.wm.shell.common.DisplayController;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link WindowDecoration}.
|
||||||
|
*
|
||||||
|
* Build/Install/Run:
|
||||||
|
* atest WMShellUnitTests:WindowDecorationTests
|
||||||
|
*/
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner.class)
|
||||||
|
public class WindowDecorationTests extends ShellTestCase {
|
||||||
|
private static final int CAPTION_HEIGHT_DP = 32;
|
||||||
|
private static final int SHADOW_RADIUS_DP = 5;
|
||||||
|
|
||||||
|
private final Rect mOutsetsDp = new Rect();
|
||||||
|
private final WindowDecoration.RelayoutResult<TestView> mRelayoutResult =
|
||||||
|
new WindowDecoration.RelayoutResult<>();
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private DisplayController mMockDisplayController;
|
||||||
|
@Mock
|
||||||
|
private ShellTaskOrganizer mMockShellTaskOrganizer;
|
||||||
|
@Mock
|
||||||
|
private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory;
|
||||||
|
@Mock
|
||||||
|
private SurfaceControlViewHost mMockSurfaceControlViewHost;
|
||||||
|
@Mock
|
||||||
|
private TestView mMockView;
|
||||||
|
@Mock
|
||||||
|
private WindowContainerTransaction mMockWindowContainerTransaction;
|
||||||
|
|
||||||
|
private SurfaceControl.Builder mMockSurfaceControlBuilder;
|
||||||
|
private SurfaceControl.Transaction mMockSurfaceControlTransaction;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mMockSurfaceControlBuilder = createMockSurfaceControlBuilder(mock(SurfaceControl.class));
|
||||||
|
mMockSurfaceControlTransaction = createMockSurfaceControlTransaction();
|
||||||
|
|
||||||
|
doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory)
|
||||||
|
.create(any(), any(), any(), anyBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNotCrashWhenDisplayAppearsAfterTask() {
|
||||||
|
doReturn(mock(Display.class)).when(mMockDisplayController)
|
||||||
|
.getDisplay(Display.DEFAULT_DISPLAY);
|
||||||
|
|
||||||
|
final int displayId = Display.DEFAULT_DISPLAY + 1;
|
||||||
|
final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
|
||||||
|
new ActivityManager.TaskDescription.Builder()
|
||||||
|
.setBackgroundColor(Color.BLACK);
|
||||||
|
final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
|
||||||
|
.setDisplayId(displayId)
|
||||||
|
.setTaskDescriptionBuilder(taskDescriptionBuilder)
|
||||||
|
.setVisible(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final TestWindowDecoration windowDecor =
|
||||||
|
createWindowDecoration(taskInfo, new SurfaceControl());
|
||||||
|
windowDecor.relayout(taskInfo);
|
||||||
|
|
||||||
|
// It shouldn't show the window decoration when it can't obtain the display instance.
|
||||||
|
assertThat(mRelayoutResult.mRootView).isNull();
|
||||||
|
|
||||||
|
final ArgumentCaptor<DisplayController.OnDisplaysChangedListener> listenerArgumentCaptor =
|
||||||
|
ArgumentCaptor.forClass(DisplayController.OnDisplaysChangedListener.class);
|
||||||
|
verify(mMockDisplayController).addDisplayWindowListener(listenerArgumentCaptor.capture());
|
||||||
|
final DisplayController.OnDisplaysChangedListener listener =
|
||||||
|
listenerArgumentCaptor.getValue();
|
||||||
|
|
||||||
|
// Adding an irrelevant display shouldn't change the result.
|
||||||
|
listener.onDisplayAdded(Display.DEFAULT_DISPLAY);
|
||||||
|
assertThat(mRelayoutResult.mRootView).isNull();
|
||||||
|
|
||||||
|
final Display mockDisplay = mock(Display.class);
|
||||||
|
doReturn(mockDisplay).when(mMockDisplayController).getDisplay(displayId);
|
||||||
|
|
||||||
|
listener.onDisplayAdded(displayId);
|
||||||
|
|
||||||
|
// The listener should be removed when the display shows up.
|
||||||
|
verify(mMockDisplayController).removeDisplayWindowListener(same(listener));
|
||||||
|
|
||||||
|
assertThat(mRelayoutResult.mRootView).isSameInstanceAs(mMockView);
|
||||||
|
verify(mMockSurfaceControlViewHostFactory)
|
||||||
|
.create(any(), eq(mockDisplay), any(), anyBoolean());
|
||||||
|
verify(mMockSurfaceControlViewHost).setView(same(mMockView), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
private TestWindowDecoration createWindowDecoration(
|
||||||
|
ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) {
|
||||||
|
return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer,
|
||||||
|
taskInfo, testSurface, () -> mMockSurfaceControlBuilder,
|
||||||
|
mMockSurfaceControlViewHostFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TestView extends View implements TaskFocusStateConsumer {
|
||||||
|
private TestView(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTaskFocusState(boolean focused) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestWindowDecoration extends WindowDecoration<TestView> {
|
||||||
|
TestWindowDecoration(Context context, DisplayController displayController,
|
||||||
|
ShellTaskOrganizer taskOrganizer, ActivityManager.RunningTaskInfo taskInfo,
|
||||||
|
SurfaceControl taskSurface,
|
||||||
|
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
|
||||||
|
SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
|
||||||
|
super(context, displayController, taskOrganizer, taskInfo, taskSurface,
|
||||||
|
surfaceControlBuilderSupplier, surfaceControlViewHostFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void relayout(ActivityManager.RunningTaskInfo taskInfo) {
|
||||||
|
relayout(null /* taskInfo */, 0 /* layoutResId */, mMockView, CAPTION_HEIGHT_DP,
|
||||||
|
mOutsetsDp, SHADOW_RADIUS_DP, mMockSurfaceControlTransaction,
|
||||||
|
mMockWindowContainerTransaction, mRelayoutResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user