• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.quickstep.util;
17 
18 import static org.mockito.ArgumentMatchers.any;
19 import static org.mockito.Mockito.doReturn;
20 import static org.mockito.Mockito.mock;
21 import static org.mockito.Mockito.when;
22 
23 import android.content.Context;
24 import android.content.res.Configuration;
25 import android.graphics.Point;
26 import android.graphics.Rect;
27 import android.graphics.RectF;
28 import android.util.ArrayMap;
29 import android.util.DisplayMetrics;
30 import android.view.RemoteAnimationTarget;
31 import android.view.Surface;
32 
33 import androidx.test.ext.junit.runners.AndroidJUnit4;
34 import androidx.test.filters.SmallTest;
35 
36 import com.android.launcher3.DeviceProfile;
37 import com.android.launcher3.InvariantDeviceProfile;
38 import com.android.launcher3.dagger.LauncherAppComponent;
39 import com.android.launcher3.dagger.LauncherAppSingleton;
40 import com.android.launcher3.util.AllModulesMinusWMProxy;
41 import com.android.launcher3.util.DisplayController;
42 import com.android.launcher3.util.DisplayController.Info;
43 import com.android.launcher3.util.LauncherModelHelper;
44 import com.android.launcher3.util.NavigationMode;
45 import com.android.launcher3.util.RotationUtils;
46 import com.android.launcher3.util.WindowBounds;
47 import com.android.launcher3.util.window.CachedDisplayInfo;
48 import com.android.launcher3.util.window.WindowManagerProxy;
49 import com.android.quickstep.FallbackActivityInterface;
50 import com.android.quickstep.util.SurfaceTransaction.MockProperties;
51 
52 import dagger.BindsInstance;
53 import dagger.Component;
54 
55 import org.hamcrest.Description;
56 import org.hamcrest.TypeSafeMatcher;
57 import org.junit.Assert;
58 import org.junit.Test;
59 import org.junit.runner.RunWith;
60 
61 import java.util.ArrayList;
62 import java.util.List;
63 
64 @SmallTest
65 @RunWith(AndroidJUnit4.class)
66 public class TaskViewSimulatorTest {
67 
68     @Test
taskProperlyScaled_portrait_noRotation_sameInsets1()69     public void taskProperlyScaled_portrait_noRotation_sameInsets1() {
70         new TaskMatrixVerifier()
71                 .withLauncherSize(1200, 2450)
72                 .withDensityDpi(420)
73                 .withInsets(new Rect(0, 80, 0, 120))
74                 .verifyNoTransforms();
75     }
76 
77     @Test
taskProperlyScaled_portrait_noRotation_sameInsets2()78     public void taskProperlyScaled_portrait_noRotation_sameInsets2() {
79         new TaskMatrixVerifier()
80                 .withLauncherSize(1200, 2450)
81                 .withDensityDpi(420)
82                 .withInsets(new Rect(55, 80, 55, 120))
83                 .verifyNoTransforms();
84     }
85 
86     @Test
taskProperlyScaled_landscape_noRotation_sameInsets1()87     public void taskProperlyScaled_landscape_noRotation_sameInsets1() {
88         new TaskMatrixVerifier()
89                 .withLauncherSize(2450, 1250)
90                 .withDensityDpi(420)
91                 .withInsets(new Rect(0, 80, 0, 40))
92                 .verifyNoTransforms();
93     }
94 
95     @Test
taskProperlyScaled_landscape_noRotation_sameInsets2()96     public void taskProperlyScaled_landscape_noRotation_sameInsets2() {
97         new TaskMatrixVerifier()
98                 .withLauncherSize(2450, 1250)
99                 .withDensityDpi(420)
100                 .withInsets(new Rect(0, 80, 120, 0))
101                 .verifyNoTransforms();
102     }
103 
104     @Test
taskProperlyScaled_landscape_noRotation_sameInsets3()105     public void taskProperlyScaled_landscape_noRotation_sameInsets3() {
106         new TaskMatrixVerifier()
107                 .withLauncherSize(2450, 1250)
108                 .withDensityDpi(420)
109                 .withInsets(new Rect(55, 80, 55, 120))
110                 .verifyNoTransforms();
111     }
112 
113     @Test
taskProperlyScaled_landscape_rotated()114     public void taskProperlyScaled_landscape_rotated() {
115         new TaskMatrixVerifier()
116                 .withLauncherSize(1200, 2450)
117                 .withDensityDpi(420)
118                 .withInsets(new Rect(0, 80, 0, 120))
119                 .withAppBounds(
120                         new Rect(0, 0, 2450, 1200),
121                         new Rect(0, 80, 0, 120),
122                         Surface.ROTATION_90)
123                 .verifyNoTransforms();
124     }
125 
126     private static class TaskMatrixVerifier extends TransformParams {
127 
128         private Point mDisplaySize = new Point();
129         private int mDensityDpi = DisplayMetrics.DENSITY_DEFAULT;
130         private Rect mDisplayInsets = new Rect();
131         private Rect mAppBounds = new Rect();
132         private Rect mLauncherInsets = new Rect();
133 
134         private Rect mAppInsets;
135 
136         private int mAppRotation = -1;
137         private DeviceProfile mDeviceProfile;
138 
withLauncherSize(int width, int height)139         TaskMatrixVerifier withLauncherSize(int width, int height) {
140             mDisplaySize.set(width, height);
141             if (mAppBounds.isEmpty()) {
142                 mAppBounds.set(0, 0, width, height);
143             }
144             return this;
145         }
146 
withDensityDpi(int densityDpi)147         TaskMatrixVerifier withDensityDpi(int densityDpi) {
148             mDensityDpi = densityDpi;
149             return this;
150         }
151 
withInsets(Rect insets)152         TaskMatrixVerifier withInsets(Rect insets) {
153             mDisplayInsets.set(insets);
154             mLauncherInsets.set(insets);
155             return this;
156         }
157 
withAppBounds(Rect bounds, Rect insets, int appRotation)158         TaskMatrixVerifier withAppBounds(Rect bounds, Rect insets, int appRotation) {
159             mAppBounds.set(bounds);
160             mAppInsets = insets;
161             mAppRotation = appRotation;
162             return this;
163         }
164 
verifyNoTransforms()165         void verifyNoTransforms() {
166             LauncherModelHelper helper = new LauncherModelHelper();
167             try {
168                 DisplayController mockController = mock(DisplayController.class);
169 
170                 helper.sandboxContext.initDaggerComponent(
171                         DaggerTaskViewSimulatorTest_TaskViewSimulatorTestComponent.builder()
172                                 .bindDisplayController(mockController));
173                 int rotation = mDisplaySize.x > mDisplaySize.y
174                         ? Surface.ROTATION_90 : Surface.ROTATION_0;
175                 CachedDisplayInfo cdi = new CachedDisplayInfo(mDisplaySize, rotation);
176                 WindowBounds wm = new WindowBounds(
177                         new Rect(0, 0, mDisplaySize.x, mDisplaySize.y),
178                         mDisplayInsets);
179                 List<WindowBounds> allBounds = new ArrayList<>(4);
180                 for (int i = 0; i < 4; i++) {
181                     Rect boundsR = new Rect(wm.bounds);
182                     Rect insetsR = new Rect(wm.insets);
183 
184                     RotationUtils.rotateRect(insetsR, RotationUtils.deltaRotation(rotation, i));
185                     RotationUtils.rotateRect(boundsR, RotationUtils.deltaRotation(rotation, i));
186                     boundsR.set(0, 0, Math.abs(boundsR.width()), Math.abs(boundsR.height()));
187                     allBounds.add(new WindowBounds(boundsR, insetsR));
188                 }
189 
190                 WindowManagerProxy wmProxy = mock(WindowManagerProxy.class);
191                 doReturn(cdi).when(wmProxy).getDisplayInfo(any());
192                 doReturn(wm).when(wmProxy).getRealBounds(any(), any());
193                 doReturn(NavigationMode.NO_BUTTON).when(wmProxy).getNavigationMode(any());
194 
195                 ArrayMap<CachedDisplayInfo, List<WindowBounds>> perDisplayBoundsCache =
196                         new ArrayMap<>();
197                 perDisplayBoundsCache.put(cdi.normalize(wmProxy), allBounds);
198 
199                 Configuration configuration = new Configuration();
200                 configuration.densityDpi = mDensityDpi;
201                 Context configurationContext = helper.sandboxContext.createConfigurationContext(
202                         configuration);
203 
204                 DisplayController.Info info = new Info(
205                         configurationContext, wmProxy, perDisplayBoundsCache);
206                 when(mockController.getInfo()).thenReturn(info);
207 
208                 mDeviceProfile = InvariantDeviceProfile.INSTANCE.get(helper.sandboxContext)
209                         .getBestMatch(mAppBounds.width(), mAppBounds.height(), rotation);
210                 mDeviceProfile.updateInsets(mLauncherInsets);
211 
212                 TaskViewSimulator tvs = new TaskViewSimulator(helper.sandboxContext,
213                         FallbackActivityInterface.INSTANCE, false, 0);
214                 tvs.setDp(mDeviceProfile);
215 
216                 int launcherRotation = info.rotation;
217                 if (mAppRotation < 0) {
218                     mAppRotation = launcherRotation;
219                 }
220 
221                 tvs.getOrientationState().update(launcherRotation, mAppRotation);
222                 if (mAppInsets == null) {
223                     mAppInsets = new Rect(mLauncherInsets);
224                 }
225                 tvs.setPreviewBounds(mAppBounds, mAppInsets);
226 
227                 tvs.fullScreenProgress.value = 1;
228                 tvs.recentsViewScale.value = tvs.getFullScreenScale();
229                 tvs.apply(this);
230             } finally {
231                 helper.destroy();
232             }
233         }
234 
235         @Override
createSurfaceParams(BuilderProxy proxy)236         public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) {
237             RecordingSurfaceTransaction transaction = new RecordingSurfaceTransaction();
238             proxy.onBuildTargetParams(
239                     transaction.mockProperties, mock(RemoteAnimationTarget.class), this);
240             return transaction;
241         }
242 
243         @Override
applySurfaceParams(SurfaceTransaction params)244         public void applySurfaceParams(SurfaceTransaction params) {
245             Assert.assertTrue(params instanceof RecordingSurfaceTransaction);
246             MockProperties p = ((RecordingSurfaceTransaction) params).mockProperties;
247 
248             // Verify that the task position remains the same
249             RectF newAppBounds = new RectF(mAppBounds);
250             p.matrix.mapRect(newAppBounds);
251             Assert.assertThat(newAppBounds, new AlmostSame(mAppBounds));
252 
253             System.err.println("Bounds mapped: " + mAppBounds + " => " + newAppBounds);
254         }
255     }
256 
257     private static class AlmostSame extends TypeSafeMatcher<RectF>  {
258 
259         // Allow .1% error margin to account for float to int conversions
260         private final float mErrorFactor = .001f;
261         private final Rect mExpected;
262 
AlmostSame(Rect expected)263         AlmostSame(Rect expected) {
264             mExpected = expected;
265         }
266 
267         @Override
matchesSafely(RectF item)268         protected boolean matchesSafely(RectF item) {
269             float errorWidth = mErrorFactor * mExpected.width();
270             float errorHeight = mErrorFactor * mExpected.height();
271             return Math.abs(item.left - mExpected.left) < errorWidth
272                     && Math.abs(item.top - mExpected.top) < errorHeight
273                     && Math.abs(item.right - mExpected.right) < errorWidth
274                     && Math.abs(item.bottom - mExpected.bottom) < errorHeight;
275         }
276 
277         @Override
describeTo(Description description)278         public void describeTo(Description description) {
279             description.appendValue(mExpected);
280         }
281     }
282 
283     @LauncherAppSingleton
284     @Component(modules = {AllModulesMinusWMProxy.class})
285     interface TaskViewSimulatorTestComponent extends LauncherAppComponent {
286 
287         @Component.Builder
288         interface Builder extends LauncherAppComponent.Builder {
289 
290             @BindsInstance
bindDisplayController(DisplayController controller)291             Builder bindDisplayController(DisplayController controller);
292 
build()293             TaskViewSimulatorTestComponent build();
294         }
295     }
296 }
297