blob: 7d414f4910d316e64fce7a0614af619ea2af4d11 [file] [log] [blame]
/*
* Copyright (C) 2020 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.quickstep.util;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.ArrayMap;
import android.util.Pair;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.LauncherModelHelper;
import com.android.launcher3.util.ReflectionHelpers;
import com.android.launcher3.util.RotationUtils;
import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.util.window.CachedDisplayInfo;
import com.android.launcher3.util.window.WindowManagerProxy;
import com.android.quickstep.FallbackActivityInterface;
import com.android.quickstep.SystemUiProxy;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TaskViewSimulatorTest {
@Test
public void taskProperlyScaled_portrait_noRotation_sameInsets1() {
new TaskMatrixVerifier()
.withLauncherSize(1200, 2450)
.withInsets(new Rect(0, 80, 0, 120))
.verifyNoTransforms();
}
@Test
public void taskProperlyScaled_portrait_noRotation_sameInsets2() {
new TaskMatrixVerifier()
.withLauncherSize(1200, 2450)
.withInsets(new Rect(55, 80, 55, 120))
.verifyNoTransforms();
}
@Test
public void taskProperlyScaled_landscape_noRotation_sameInsets1() {
new TaskMatrixVerifier()
.withLauncherSize(2450, 1250)
.withInsets(new Rect(0, 80, 0, 40))
.verifyNoTransforms();
}
@Test
public void taskProperlyScaled_landscape_noRotation_sameInsets2() {
new TaskMatrixVerifier()
.withLauncherSize(2450, 1250)
.withInsets(new Rect(0, 80, 120, 0))
.verifyNoTransforms();
}
@Test
public void taskProperlyScaled_landscape_noRotation_sameInsets3() {
new TaskMatrixVerifier()
.withLauncherSize(2450, 1250)
.withInsets(new Rect(55, 80, 55, 120))
.verifyNoTransforms();
}
@Test
public void taskProperlyScaled_landscape_rotated() {
new TaskMatrixVerifier()
.withLauncherSize(1200, 2450)
.withInsets(new Rect(0, 80, 0, 120))
.withAppBounds(
new Rect(0, 0, 2450, 1200),
new Rect(0, 80, 0, 120),
Surface.ROTATION_90)
.verifyNoTransforms();
}
private static class TaskMatrixVerifier extends TransformParams {
private Point mDisplaySize = new Point();
private Rect mDisplayInsets = new Rect();
private Rect mAppBounds = new Rect();
private Rect mLauncherInsets = new Rect();
private Rect mAppInsets;
private int mAppRotation = -1;
private DeviceProfile mDeviceProfile;
TaskMatrixVerifier withLauncherSize(int width, int height) {
mDisplaySize.set(width, height);
if (mAppBounds.isEmpty()) {
mAppBounds.set(0, 0, width, height);
}
return this;
}
TaskMatrixVerifier withInsets(Rect insets) {
mDisplayInsets.set(insets);
mLauncherInsets.set(insets);
return this;
}
TaskMatrixVerifier withAppBounds(Rect bounds, Rect insets, int appRotation) {
mAppBounds.set(bounds);
mAppInsets = insets;
mAppRotation = appRotation;
return this;
}
void verifyNoTransforms() {
LauncherModelHelper helper = new LauncherModelHelper();
try {
helper.sandboxContext.allow(SystemUiProxy.INSTANCE);
int rotation = mDisplaySize.x > mDisplaySize.y
? Surface.ROTATION_90 : Surface.ROTATION_0;
CachedDisplayInfo cdi =
new CachedDisplayInfo("test-display", mDisplaySize, rotation , new Rect());
WindowBounds wm = new WindowBounds(
new Rect(0, 0, mDisplaySize.x, mDisplaySize.y),
mDisplayInsets);
WindowBounds[] allBounds = new WindowBounds[4];
for (int i = 0; i < 4; i++) {
Rect boundsR = new Rect(wm.bounds);
Rect insetsR = new Rect(wm.insets);
RotationUtils.rotateRect(insetsR, RotationUtils.deltaRotation(rotation, i));
RotationUtils.rotateRect(boundsR, RotationUtils.deltaRotation(rotation, i));
boundsR.set(0, 0, Math.abs(boundsR.width()), Math.abs(boundsR.height()));
allBounds[i] = new WindowBounds(boundsR, insetsR);
}
WindowManagerProxy wmProxy = mock(WindowManagerProxy.class);
doReturn(cdi).when(wmProxy).getDisplayInfo(any(), any());
doReturn(wm).when(wmProxy).getRealBounds(any(), any(), any());
ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> perDisplayBoundsCache =
new ArrayMap<>();
perDisplayBoundsCache.put(cdi.id, Pair.create(cdi.normalize(), allBounds));
DisplayController.Info mockInfo = new Info(
helper.sandboxContext, mock(Display.class), wmProxy, perDisplayBoundsCache);
DisplayController controller =
DisplayController.INSTANCE.get(helper.sandboxContext);
controller.close();
ReflectionHelpers.setField(controller, "mInfo", mockInfo);
mDeviceProfile = InvariantDeviceProfile.INSTANCE.get(helper.sandboxContext)
.getBestMatch(mAppBounds.width(), mAppBounds.height(), rotation);
mDeviceProfile.updateInsets(mLauncherInsets);
TaskViewSimulator tvs = new TaskViewSimulator(helper.sandboxContext,
FallbackActivityInterface.INSTANCE);
tvs.setDp(mDeviceProfile);
int launcherRotation = mockInfo.rotation;
if (mAppRotation < 0) {
mAppRotation = launcherRotation;
}
tvs.getOrientationState().update(launcherRotation, mAppRotation);
if (mAppInsets == null) {
mAppInsets = new Rect(mLauncherInsets);
}
tvs.setPreviewBounds(mAppBounds, mAppInsets);
tvs.fullScreenProgress.value = 1;
tvs.recentsViewScale.value = tvs.getFullScreenScale();
tvs.apply(this);
} finally {
helper.destroy();
}
}
@Override
public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null);
proxy.onBuildTargetParams(builder, mock(RemoteAnimationTargetCompat.class), this);
return new SurfaceParams[] {builder.build()};
}
@Override
public void applySurfaceParams(SurfaceParams[] params) {
// Verify that the task position remains the same
RectF newAppBounds = new RectF(mAppBounds);
params[0].matrix.mapRect(newAppBounds);
Assert.assertThat(newAppBounds, new AlmostSame(mAppBounds));
System.err.println("Bounds mapped: " + mAppBounds + " => " + newAppBounds);
}
}
private static class AlmostSame extends TypeSafeMatcher<RectF> {
// Allow .1% error margin to account for float to int conversions
private final float mErrorFactor = .001f;
private final Rect mExpected;
AlmostSame(Rect expected) {
mExpected = expected;
}
@Override
protected boolean matchesSafely(RectF item) {
float errorWidth = mErrorFactor * mExpected.width();
float errorHeight = mErrorFactor * mExpected.height();
return Math.abs(item.left - mExpected.left) < errorWidth
&& Math.abs(item.top - mExpected.top) < errorHeight
&& Math.abs(item.right - mExpected.right) < errorWidth
&& Math.abs(item.bottom - mExpected.bottom) < errorHeight;
}
@Override
public void describeTo(Description description) {
description.appendValue(mExpected);
}
}
}