blob: 337663e33cd03acc139745bc1b186a6c4d2f3166 [file] [log] [blame]
Jorim Jaggib6030952018-10-23 18:31:52 +02001/*
2 * Copyright (C) 2018 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
17package android.view;
18
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080019import static android.view.InsetsState.TYPE_IME;
20import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
Jorim Jaggib6030952018-10-23 18:31:52 +020021import static android.view.InsetsState.TYPE_TOP_BAR;
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +010022import static android.view.WindowInsets.Type.topBar;
Jorim Jaggi648e5882019-01-24 13:24:02 +010023import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
Tadashi G. Takaoka82eb7a52019-03-26 18:22:01 +090024
25import static org.junit.Assert.assertEquals;
26import static org.junit.Assert.assertFalse;
27import static org.junit.Assert.assertNull;
28import static org.junit.Assert.assertTrue;
Tarandeep Singha6f35612019-01-11 19:50:46 -080029import static org.mockito.ArgumentMatchers.any;
30import static org.mockito.ArgumentMatchers.anyInt;
31import static org.mockito.Mockito.mock;
32import static org.mockito.Mockito.never;
33import static org.mockito.Mockito.verify;
Tadashi G. Takaokab4470f22019-01-15 18:29:15 +090034
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080035import android.content.Context;
36import android.graphics.Insets;
Tarandeep Singh215929b2019-01-11 18:24:37 -080037import android.graphics.Point;
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080038import android.graphics.Rect;
Jorim Jaggib6030952018-10-23 18:31:52 +020039import android.platform.test.annotations.Presubmit;
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080040import android.view.WindowInsets.Type;
41import android.view.WindowManager.BadTokenException;
42import android.view.WindowManager.LayoutParams;
43import android.widget.TextView;
Tadashi G. Takaokab4470f22019-01-15 18:29:15 +090044
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080045import androidx.test.InstrumentationRegistry;
Tadashi G. Takaokab4470f22019-01-15 18:29:15 +090046import androidx.test.runner.AndroidJUnit4;
Jorim Jaggib6030952018-10-23 18:31:52 +020047
48import org.junit.Before;
49import org.junit.Test;
50import org.junit.runner.RunWith;
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +010051import org.mockito.ArgumentCaptor;
52
53import java.util.concurrent.CountDownLatch;
Jorim Jaggib6030952018-10-23 18:31:52 +020054
Tadashi G. Takaoka82eb7a52019-03-26 18:22:01 +090055/**
56 * Tests for {@link InsetsController}.
57 *
58 * <p>Build/Install/Run:
59 * atest FrameworksCoreTests:InsetsControllerTest
60 *
61 * <p>This test class is a part of Window Manager Service tests and specified in
62 * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
63 */
Jorim Jaggib6030952018-10-23 18:31:52 +020064@Presubmit
Jorim Jaggib6030952018-10-23 18:31:52 +020065@RunWith(AndroidJUnit4.class)
66public class InsetsControllerTest {
67
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080068 private InsetsController mController;
Jorim Jaggib6030952018-10-23 18:31:52 +020069 private SurfaceSession mSession = new SurfaceSession();
70 private SurfaceControl mLeash;
71
72 @Before
73 public void setup() {
74 mLeash = new SurfaceControl.Builder(mSession)
75 .setName("testSurface")
76 .build();
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080077 InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
78 Context context = InstrumentationRegistry.getTargetContext();
79 // cannot mock ViewRootImpl since it's final.
80 ViewRootImpl viewRootImpl = new ViewRootImpl(context, context.getDisplay());
81 try {
82 viewRootImpl.setView(new TextView(context), new LayoutParams(), null);
83 } catch (BadTokenException e) {
84 // activity isn't running, we will ignore BadTokenException.
85 }
86 mController = new InsetsController(viewRootImpl);
87 final Rect rect = new Rect(5, 5, 5, 5);
88 mController.calculateInsets(
89 false,
90 false,
91 new DisplayCutout(
92 Insets.of(10, 10, 10, 10), rect, rect, rect, rect),
Jorim Jaggi648e5882019-01-24 13:24:02 +010093 rect, rect, SOFT_INPUT_ADJUST_RESIZE);
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +010094 mController.onFrameChanged(new Rect(0, 0, 100, 100));
95 mController.getState().setDisplayFrame(new Rect(0, 0, 100, 100));
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080096 });
Tarandeep Singh46d59f02019-01-29 18:09:15 -080097 InstrumentationRegistry.getInstrumentation().waitForIdleSync();
Jorim Jaggib6030952018-10-23 18:31:52 +020098 }
99
100 @Test
101 public void testControlsChanged() {
Tarandeep Singh215929b2019-01-11 18:24:37 -0800102 InsetsSourceControl control = new InsetsSourceControl(TYPE_TOP_BAR, mLeash, new Point());
Jorim Jaggib6030952018-10-23 18:31:52 +0200103 mController.onControlsChanged(new InsetsSourceControl[] { control });
104 assertEquals(mLeash,
105 mController.getSourceConsumer(TYPE_TOP_BAR).getControl().getLeash());
106 }
107
108 @Test
109 public void testControlsRevoked() {
Tarandeep Singh215929b2019-01-11 18:24:37 -0800110 InsetsSourceControl control = new InsetsSourceControl(TYPE_TOP_BAR, mLeash, new Point());
Jorim Jaggib6030952018-10-23 18:31:52 +0200111 mController.onControlsChanged(new InsetsSourceControl[] { control });
112 mController.onControlsChanged(new InsetsSourceControl[0]);
113 assertNull(mController.getSourceConsumer(TYPE_TOP_BAR).getControl());
114 }
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800115
116 @Test
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100117 public void testControlsRevoked_duringAnim() {
118 InsetsSourceControl control = new InsetsSourceControl(TYPE_TOP_BAR, mLeash, new Point());
119 mController.onControlsChanged(new InsetsSourceControl[] { control });
120
121 WindowInsetsAnimationControlListener mockListener =
122 mock(WindowInsetsAnimationControlListener.class);
123 mController.controlWindowInsetsAnimation(topBar(), mockListener);
124 verify(mockListener).onReady(any(), anyInt());
125 mController.onControlsChanged(new InsetsSourceControl[0]);
126 verify(mockListener).onCancelled();
127 }
128
129 @Test
Tarandeep Singha6f35612019-01-11 19:50:46 -0800130 public void testFrameDoesntMatchDisplay() {
131 mController.onFrameChanged(new Rect(0, 0, 100, 100));
132 mController.getState().setDisplayFrame(new Rect(0, 0, 200, 200));
133 WindowInsetsAnimationControlListener controlListener =
134 mock(WindowInsetsAnimationControlListener.class);
135 mController.controlWindowInsetsAnimation(0, controlListener);
136 verify(controlListener).onCancelled();
137 verify(controlListener, never()).onReady(any(), anyInt());
138 }
139
140 @Test
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800141 public void testAnimationEndState() {
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800142 InsetsSourceControl[] controls = prepareControls();
143 InsetsSourceControl navBar = controls[0];
144 InsetsSourceControl topBar = controls[1];
145 InsetsSourceControl ime = controls[2];
146
147 InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
Tarandeep Singh92d2dd32019-08-07 14:45:01 -0700148 mController.getSourceConsumer(TYPE_IME).onWindowFocusGained();
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100149 // since there is no focused view, forcefully make IME visible.
150 mController.applyImeVisibility(true /* setVisible */);
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800151 mController.show(Type.all());
152 // quickly jump to final state by cancelling it.
153 mController.cancelExistingAnimation();
154 assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible());
155 assertTrue(mController.getSourceConsumer(topBar.getType()).isVisible());
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100156 assertTrue(mController.getSourceConsumer(ime.getType()).isVisible());
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800157
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100158 mController.applyImeVisibility(false /* setVisible */);
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800159 mController.hide(Type.all());
160 mController.cancelExistingAnimation();
161 assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
162 assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible());
163 assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
Tarandeep Singh92d2dd32019-08-07 14:45:01 -0700164 mController.getSourceConsumer(TYPE_IME).onWindowFocusLost();
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800165 });
166 InstrumentationRegistry.getInstrumentation().waitForIdleSync();
167 }
168
169 @Test
170 public void testApplyImeVisibility() {
171 final InsetsSourceControl ime = new InsetsSourceControl(TYPE_IME, mLeash, new Point());
172
173 InsetsSourceControl[] controls = new InsetsSourceControl[3];
174 controls[0] = ime;
175 mController.onControlsChanged(controls);
176 InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
Tarandeep Singh92d2dd32019-08-07 14:45:01 -0700177 mController.getSourceConsumer(TYPE_IME).onWindowFocusGained();
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800178 mController.applyImeVisibility(true);
179 mController.cancelExistingAnimation();
180 assertTrue(mController.getSourceConsumer(ime.getType()).isVisible());
181 mController.applyImeVisibility(false);
182 mController.cancelExistingAnimation();
183 assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
Tarandeep Singh92d2dd32019-08-07 14:45:01 -0700184 mController.getSourceConsumer(TYPE_IME).onWindowFocusLost();
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800185 });
186 InstrumentationRegistry.getInstrumentation().waitForIdleSync();
187 }
188
189 @Test
190 public void testShowHideSelectively() {
191 InsetsSourceControl[] controls = prepareControls();
192 InsetsSourceControl navBar = controls[0];
193 InsetsSourceControl topBar = controls[1];
194 InsetsSourceControl ime = controls[2];
195
196 InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
197 int types = Type.sideBars() | Type.systemBars();
198 // test show select types.
199 mController.show(types);
200 mController.cancelExistingAnimation();
201 assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible());
202 assertTrue(mController.getSourceConsumer(topBar.getType()).isVisible());
203 assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
204
205 // test hide all
206 mController.hide(types);
207 mController.cancelExistingAnimation();
208 assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
209 assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible());
210 assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
211 });
212 InstrumentationRegistry.getInstrumentation().waitForIdleSync();
213 }
214
215 @Test
216 public void testShowHideSingle() {
217 InsetsSourceControl[] controls = prepareControls();
218 InsetsSourceControl navBar = controls[0];
219 InsetsSourceControl topBar = controls[1];
220 InsetsSourceControl ime = controls[2];
221
222 InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
223 int types = Type.sideBars() | Type.systemBars();
224 // test show select types.
225 mController.show(types);
226 mController.cancelExistingAnimation();
227 assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible());
228 assertTrue(mController.getSourceConsumer(topBar.getType()).isVisible());
229 assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
230
231 // test hide all
232 mController.hide(Type.all());
233 mController.cancelExistingAnimation();
234 assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
235 assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible());
236 assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
237
238 // test single show
239 mController.show(Type.sideBars());
240 mController.cancelExistingAnimation();
241 assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible());
242 assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible());
243 assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
244
245 // test single hide
246 mController.hide(Type.sideBars());
247 assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
248 assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible());
249 assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
250
251 });
252 InstrumentationRegistry.getInstrumentation().waitForIdleSync();
253 }
254
255 @Test
256 public void testShowHideMultiple() {
257 InsetsSourceControl[] controls = prepareControls();
258 InsetsSourceControl navBar = controls[0];
259 InsetsSourceControl topBar = controls[1];
260 InsetsSourceControl ime = controls[2];
261
262 InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
263 // start two animations and see if previous is cancelled and final state is reached.
264 mController.show(Type.sideBars());
265 mController.show(Type.systemBars());
266 mController.cancelExistingAnimation();
267 assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible());
268 assertTrue(mController.getSourceConsumer(topBar.getType()).isVisible());
269 assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
270
271 mController.hide(Type.sideBars());
272 mController.hide(Type.systemBars());
273 mController.cancelExistingAnimation();
274 assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
275 assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible());
276 assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
277
278 int types = Type.sideBars() | Type.systemBars();
279 // show two at a time and hide one by one.
280 mController.show(types);
281 mController.hide(Type.sideBars());
282 mController.cancelExistingAnimation();
283 assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
284 assertTrue(mController.getSourceConsumer(topBar.getType()).isVisible());
285 assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
286
287 mController.hide(Type.systemBars());
288 mController.cancelExistingAnimation();
289 assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
290 assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible());
291 assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
292 });
293 InstrumentationRegistry.getInstrumentation().waitForIdleSync();
294 }
295
296 @Test
297 public void testShowMultipleHideOneByOne() {
298 InsetsSourceControl[] controls = prepareControls();
299 InsetsSourceControl navBar = controls[0];
300 InsetsSourceControl topBar = controls[1];
301 InsetsSourceControl ime = controls[2];
302
303 InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
304 int types = Type.sideBars() | Type.systemBars();
305 // show two at a time and hide one by one.
306 mController.show(types);
307 mController.hide(Type.sideBars());
308 mController.cancelExistingAnimation();
309 assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
310 assertTrue(mController.getSourceConsumer(topBar.getType()).isVisible());
311 assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
312
313 mController.hide(Type.systemBars());
314 mController.cancelExistingAnimation();
315 assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
316 assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible());
317 assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
318 });
319 InstrumentationRegistry.getInstrumentation().waitForIdleSync();
320 }
321
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100322 @Test
323 public void testAnimationEndState_controller() throws Exception {
324 InsetsSourceControl control = new InsetsSourceControl(TYPE_TOP_BAR, mLeash, new Point());
325 mController.onControlsChanged(new InsetsSourceControl[] { control });
326
327 InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
328 WindowInsetsAnimationControlListener mockListener =
329 mock(WindowInsetsAnimationControlListener.class);
330 mController.controlWindowInsetsAnimation(topBar(), mockListener);
331
332 ArgumentCaptor<WindowInsetsAnimationController> controllerCaptor =
333 ArgumentCaptor.forClass(WindowInsetsAnimationController.class);
334 verify(mockListener).onReady(controllerCaptor.capture(), anyInt());
335 controllerCaptor.getValue().finish(0 /* shownTypes */);
336 });
337 waitUntilNextFrame();
338 InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
339 assertFalse(mController.getSourceConsumer(TYPE_TOP_BAR).isVisible());
340 });
341 InstrumentationRegistry.getInstrumentation().waitForIdleSync();
342 }
343
344 private void waitUntilNextFrame() throws Exception {
345 final CountDownLatch latch = new CountDownLatch(1);
346 Choreographer.getMainThreadInstance().postCallback(Choreographer.CALLBACK_COMMIT,
347 latch::countDown, null /* token */);
348 latch.await();
349 }
350
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800351 private InsetsSourceControl[] prepareControls() {
Tarandeep Singh215929b2019-01-11 18:24:37 -0800352 final InsetsSourceControl navBar = new InsetsSourceControl(TYPE_NAVIGATION_BAR, mLeash,
353 new Point());
354 final InsetsSourceControl topBar = new InsetsSourceControl(TYPE_TOP_BAR, mLeash,
355 new Point());
356 final InsetsSourceControl ime = new InsetsSourceControl(TYPE_IME, mLeash, new Point());
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800357
358 InsetsSourceControl[] controls = new InsetsSourceControl[3];
359 controls[0] = navBar;
360 controls[1] = topBar;
361 controls[2] = ime;
362 mController.onControlsChanged(controls);
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800363 return controls;
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800364 }
Jorim Jaggib6030952018-10-23 18:31:52 +0200365}