• 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 
17 package android.server.wm;
18 
19 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
20 import static android.graphics.Insets.NONE;
21 import static android.view.Display.DEFAULT_DISPLAY;
22 import static android.view.WindowInsets.Type.captionBar;
23 import static android.view.WindowInsets.Type.navigationBars;
24 import static android.view.WindowInsets.Type.statusBars;
25 import static android.view.WindowInsets.Type.systemBars;
26 import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE;
27 
28 import static androidx.test.InstrumentationRegistry.getInstrumentation;
29 
30 import static org.junit.Assert.assertEquals;
31 import static org.junit.Assert.assertFalse;
32 import static org.junit.Assume.assumeFalse;
33 import static org.junit.Assume.assumeTrue;
34 import static org.mockito.ArgumentMatchers.any;
35 import static org.mockito.ArgumentMatchers.argThat;
36 import static org.mockito.ArgumentMatchers.eq;
37 import static org.mockito.Mockito.CALLS_REAL_METHODS;
38 import static org.mockito.Mockito.atLeastOnce;
39 import static org.mockito.Mockito.inOrder;
40 import static org.mockito.Mockito.mock;
41 import static org.mockito.Mockito.verify;
42 import static org.mockito.Mockito.verifyZeroInteractions;
43 import static org.mockito.Mockito.withSettings;
44 
45 import android.platform.test.annotations.Presubmit;
46 import android.view.View;
47 import android.view.WindowInsets;
48 import android.view.WindowInsetsAnimation;
49 import android.view.WindowInsetsAnimation.Bounds;
50 import android.view.WindowInsetsAnimation.Callback;
51 
52 import org.junit.Before;
53 import org.junit.Test;
54 import org.mockito.InOrder;
55 
56 import java.util.List;
57 
58 /**
59  * Test whether {@link WindowInsetsAnimation.Callback} are properly dispatched to views.
60  *
61  * Build/Install/Run:
62  *     atest CtsWindowManagerDeviceTestCases:WindowInsetsAnimationTests
63  */
64 @Presubmit
65 @android.server.wm.annotation.Group2
66 public class WindowInsetsAnimationTests extends WindowInsetsAnimationTestBase {
67 
68     @Before
setup()69     public void setup() throws Exception {
70         super.setUp();
71         mActivity = startActivity(TestActivity.class, DEFAULT_DISPLAY, true,
72                 WINDOWING_MODE_FULLSCREEN);
73         mRootView = mActivity.getWindow().getDecorView();
74         assumeTrue(hasWindowInsets(mRootView, systemBars()));
75         assumeFalse(isCar() && remoteInsetsControllerControlsSystemBars());
76     }
77 
78     @Test
testAnimationCallbacksHide()79     public void testAnimationCallbacksHide() {
80         WindowInsets before = mActivity.mLastWindowInsets;
81 
82         getInstrumentation().runOnMainSync(
83                 () -> mRootView.getWindowInsetsController().hide(systemBars()));
84 
85         waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
86 
87         commonAnimationAssertions(mActivity, before, false /* show */,
88                 systemBars() & ~captionBar());
89     }
90 
91     @Test
testAnimationCallbacksShow()92     public void testAnimationCallbacksShow() {
93         getInstrumentation().runOnMainSync(
94                 () -> mRootView.getWindowInsetsController().hide(systemBars()));
95 
96         waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
97         mActivity.mCallback.animationDone = false;
98 
99         WindowInsets before = mActivity.mLastWindowInsets;
100 
101         getInstrumentation().runOnMainSync(
102                 () -> mRootView.getWindowInsetsController().show(systemBars()));
103 
104         waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
105 
106         commonAnimationAssertions(mActivity, before, true /* show */,
107                 systemBars() & ~captionBar());
108     }
109 
110     @Test
testAnimationCallbacks_overlapping()111     public void testAnimationCallbacks_overlapping() {
112         assumeTrue(
113                 "Test requires navBar and statusBar to create overlapping animations.",
114                 hasWindowInsets(mRootView, navigationBars())
115                         && hasWindowInsets(mRootView, statusBars()));
116 
117         WindowInsets before = mActivity.mLastWindowInsets;
118         MultiAnimCallback callbackInner = new MultiAnimCallback();
119         MultiAnimCallback callback = mock(MultiAnimCallback.class,
120                 withSettings()
121                         .spiedInstance(callbackInner)
122                         .defaultAnswer(CALLS_REAL_METHODS)
123                         .verboseLogging());
124         mActivity.mView.setWindowInsetsAnimationCallback(callback);
125         callback.startRunnable = () -> mRootView.postDelayed(
126                 () -> mRootView.getWindowInsetsController().hide(statusBars()), 50);
127 
128         getInstrumentation().runOnMainSync(
129                 () -> mRootView.getWindowInsetsController().hide(navigationBars()));
130 
131         waitForOrFail("Waiting until animation done", () -> callback.animationDone);
132 
133         WindowInsets after = mActivity.mLastWindowInsets;
134 
135         InOrder inOrder = inOrder(callback, mActivity.mListener);
136 
137         inOrder.verify(callback).onPrepare(eq(callback.navBarAnim));
138 
139         inOrder.verify(mActivity.mListener).onApplyWindowInsets(any(), argThat(
140                 argument -> NONE.equals(argument.getInsets(navigationBars()))
141                         && !NONE.equals(argument.getInsets(statusBars()))));
142 
143         inOrder.verify(callback).onStart(eq(callback.navBarAnim), argThat(
144                 argument -> argument.getLowerBound().equals(NONE)
145                         && argument.getUpperBound().equals(before.getInsets(navigationBars()))));
146 
147         inOrder.verify(callback).onPrepare(eq(callback.statusBarAnim));
148         inOrder.verify(mActivity.mListener).onApplyWindowInsets(
149                 any(), eq(mActivity.mLastWindowInsets));
150 
151         inOrder.verify(callback).onStart(eq(callback.statusBarAnim), argThat(
152                 argument -> argument.getLowerBound().equals(NONE)
153                         && argument.getUpperBound().equals(before.getInsets(statusBars()))));
154 
155         inOrder.verify(callback).onEnd(eq(callback.navBarAnim));
156         inOrder.verify(callback).onEnd(eq(callback.statusBarAnim));
157 
158         assertAnimationSteps(callback.navAnimSteps, false /* showAnimation */);
159         assertAnimationSteps(callback.statusAnimSteps, false /* showAnimation */);
160 
161         assertEquals(before.getInsets(navigationBars()),
162                 callback.navAnimSteps.get(0).insets.getInsets(navigationBars()));
163         assertEquals(after.getInsets(navigationBars()),
164                 callback.navAnimSteps.get(callback.navAnimSteps.size() - 1).insets
165                         .getInsets(navigationBars()));
166 
167         assertEquals(before.getInsets(statusBars()),
168                 callback.statusAnimSteps.get(0).insets.getInsets(statusBars()));
169         assertEquals(after.getInsets(statusBars()),
170                 callback.statusAnimSteps.get(callback.statusAnimSteps.size() - 1).insets
171                         .getInsets(statusBars()));
172     }
173 
174     @Test
testAnimationCallbacks_consumedByDecor()175     public void testAnimationCallbacks_consumedByDecor() {
176         getInstrumentation().runOnMainSync(() -> {
177             mActivity.getWindow().setDecorFitsSystemWindows(true);
178             mRootView.getWindowInsetsController().hide(systemBars());
179         });
180 
181         getWmState().waitFor(state -> !state.isWindowVisible("StatusBar"),
182                 "Waiting for status bar to be hidden");
183         assertFalse(getWmState().isWindowVisible("StatusBar"));
184 
185         verifyZeroInteractions(mActivity.mCallback);
186     }
187 
188     @Test
testAnimationCallbacks_childDoesntGetCallback()189     public void testAnimationCallbacks_childDoesntGetCallback() {
190         WindowInsetsAnimation.Callback childCallback = mock(WindowInsetsAnimation.Callback.class);
191 
192         getInstrumentation().runOnMainSync(() -> {
193             mActivity.mChild.setWindowInsetsAnimationCallback(childCallback);
194             mRootView.getWindowInsetsController().hide(systemBars());
195         });
196 
197         waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
198 
199         verifyZeroInteractions(childCallback);
200     }
201 
202     @Test
testAnimationCallbacks_childInsetting()203     public void testAnimationCallbacks_childInsetting() {
204         // test requires navbar.
205         assumeTrue(hasWindowInsets(mRootView, navigationBars()));
206 
207         WindowInsets before = mActivity.mLastWindowInsets;
208         boolean[] done = new boolean[1];
209         WindowInsetsAnimation.Callback childCallback = mock(WindowInsetsAnimation.Callback.class);
210         WindowInsetsAnimation.Callback callback = new Callback(DISPATCH_MODE_CONTINUE_ON_SUBTREE) {
211 
212             @Override
213             public Bounds onStart(WindowInsetsAnimation animation, Bounds bounds) {
214                 return bounds.inset(before.getInsets(navigationBars()));
215             }
216 
217             @Override
218             public WindowInsets onProgress(WindowInsets insets,
219                     List<WindowInsetsAnimation> runningAnimations) {
220                 return insets.inset(insets.getInsets(navigationBars()));
221             }
222 
223             @Override
224             public void onEnd(WindowInsetsAnimation animation) {
225                 done[0] = true;
226             }
227         };
228 
229         getInstrumentation().runOnMainSync(() -> {
230             mActivity.mView.setWindowInsetsAnimationCallback(callback);
231             mActivity.mChild.setWindowInsetsAnimationCallback(childCallback);
232             mRootView.getWindowInsetsController().hide(systemBars());
233         });
234 
235         waitForOrFail("Waiting until animation done", () -> done[0]);
236 
237         if (hasWindowInsets(mRootView, statusBars())) {
238             verify(childCallback).onStart(any(), argThat(
239                     bounds -> bounds.getUpperBound().equals(before.getInsets(statusBars()))));
240         }
241         if (hasWindowInsets(mRootView, navigationBars())) {
242             verify(childCallback, atLeastOnce()).onProgress(argThat(
243                     insets -> NONE.equals(insets.getInsets(navigationBars()))), any());
244         }
245     }
246 
247     @Test
testAnimationCallbacks_withLegacyFlags()248     public void testAnimationCallbacks_withLegacyFlags() {
249         getInstrumentation().runOnMainSync(() -> {
250             mActivity.getWindow().setDecorFitsSystemWindows(true);
251             mRootView.setSystemUiVisibility(
252                     View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
253                             | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
254                             | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
255             mRootView.post(() -> {
256                 mRootView.getWindowInsetsController().hide(systemBars());
257             });
258         });
259 
260         waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
261 
262         mWmState.computeState();
263         assertFalse(mWmState.isWindowVisible("StatusBar"));
264         verify(mActivity.mCallback).onPrepare(any());
265         verify(mActivity.mCallback).onStart(any(), any());
266         verify(mActivity.mCallback, atLeastOnce()).onProgress(any(), any());
267         verify(mActivity.mCallback).onEnd(any());
268     }
269 }
270