• 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.WindowInsets.Type.ime;
22 import static android.view.WindowInsets.Type.statusBars;
23 
24 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
25 
26 import static org.junit.Assert.assertEquals;
27 import static org.junit.Assume.assumeFalse;
28 import static org.junit.Assume.assumeTrue;
29 import static org.mockito.ArgumentMatchers.any;
30 import static org.mockito.ArgumentMatchers.argThat;
31 import static org.mockito.ArgumentMatchers.eq;
32 import static org.mockito.Mockito.CALLS_REAL_METHODS;
33 import static org.mockito.Mockito.inOrder;
34 import static org.mockito.Mockito.mock;
35 import static org.mockito.Mockito.withSettings;
36 
37 import android.content.pm.PackageManager;
38 import android.platform.test.annotations.Presubmit;
39 import android.view.WindowInsets;
40 
41 import com.android.cts.mockime.MockIme;
42 
43 import org.junit.Before;
44 import org.junit.Test;
45 import org.mockito.InOrder;
46 
47 /**
48  * Same as {@link WindowInsetsAnimationTests} but IME specific.
49  *
50  * Build/Install/Run:
51  *     atest CtsWindowManagerDeviceTestCases:WindowInsetsAnimationImeTests
52  */
53 @Presubmit
54 @android.server.wm.annotation.Group2
55 public class WindowInsetsAnimationImeTests extends WindowInsetsAnimationTestBase {
56 
57     private static final int KEYBOARD_HEIGHT = 600;
58 
59     @Before
setup()60     public void setup() throws Exception {
61         super.setUp();
62         assumeFalse(
63                 "Automotive is to skip this test until showing and hiding certain insets "
64                         + "simultaneously in a single request is supported",
65                 mInstrumentation.getContext().getPackageManager().hasSystemFeature(
66                         PackageManager.FEATURE_AUTOMOTIVE));
67         assumeTrue("MockIme cannot be used for devices that do not support installable IMEs",
68                 mInstrumentation.getContext().getPackageManager().hasSystemFeature(
69                         PackageManager.FEATURE_INPUT_METHODS));
70     }
71 
initActivity(boolean useFloating)72     private void initActivity(boolean useFloating) {
73         MockImeHelper.createManagedMockImeSession(this, KEYBOARD_HEIGHT, useFloating);
74 
75         // The existing IME will be replaced by MockIME. Wait for the new IME window.
76         mWmState.waitFor("MockIme must be ready.", wms -> {
77             final WindowManagerState.WindowState ime = wms.getInputMethodWindowState();
78             return ime != null && ime.getPackageName().equals(MockIme.class.getPackageName());
79         });
80 
81         mActivity = startActivityInWindowingMode(TestActivity.class, WINDOWING_MODE_FULLSCREEN);
82         mRootView = mActivity.getWindow().getDecorView();
83     }
84 
85     @Test
testImeAnimationCallbacksShowAndHide()86     public void testImeAnimationCallbacksShowAndHide() {
87         initActivity(false /* useFloating */);
88         testShowAndHide();
89     }
90 
91     @Test
testAnimationCallbacks_overlapping_opposite()92     public void testAnimationCallbacks_overlapping_opposite() {
93         initActivity(false /* useFloating */);
94         assumeTrue(hasWindowInsets(mRootView, statusBars()));
95 
96         WindowInsets before = mActivity.mLastWindowInsets;
97 
98         MultiAnimCallback callbackInner = new MultiAnimCallback();
99         MultiAnimCallback callback = mock(MultiAnimCallback.class,
100                 withSettings()
101                         .spiedInstance(callbackInner)
102                         .defaultAnswer(CALLS_REAL_METHODS)
103                         .verboseLogging());
104         mActivity.mView.setWindowInsetsAnimationCallback(callback);
105 
106         getInstrumentation().runOnMainSync(
107                 () -> mRootView.getWindowInsetsController().hide(statusBars()));
108         getInstrumentation().runOnMainSync(
109                 () -> mRootView.getWindowInsetsController().show(ime()));
110 
111         waitForOrFail("Waiting until IME animation starts", () -> callback.imeAnimStarted);
112         waitForOrFail("Waiting until animation done", () -> callback.runningAnims.isEmpty());
113 
114         WindowInsets after = mActivity.mLastWindowInsets;
115 
116         // When system bar and IME are animated together, order of events cannot be predicted
117         // relative to one another: especially the end since animation durations are different.
118         // Use individual inOrder for each.
119         InOrder inOrderBar = inOrder(callback, mActivity.mListener);
120         InOrder inOrderIme = inOrder(callback, mActivity.mListener);
121 
122         inOrderBar.verify(callback).onPrepare(eq(callback.statusBarAnim));
123 
124         inOrderIme.verify(mActivity.mListener).onApplyWindowInsets(any(), argThat(
125                 argument -> NONE.equals(argument.getInsets(statusBars()))
126                         && NONE.equals(argument.getInsets(ime()))));
127 
128         inOrderBar.verify(callback).onStart(eq(callback.statusBarAnim), argThat(
129                 argument -> argument.getLowerBound().equals(NONE)
130                         && argument.getUpperBound().equals(before.getInsets(statusBars()))));
131 
132         inOrderIme.verify(callback).onPrepare(eq(callback.imeAnim));
133         inOrderIme.verify(mActivity.mListener).onApplyWindowInsets(
134                 any(), eq(mActivity.mLastWindowInsets));
135 
136         inOrderIme.verify(callback).onStart(eq(callback.imeAnim), argThat(
137                 argument -> argument.getLowerBound().equals(NONE)
138                         && !argument.getUpperBound().equals(NONE)));
139 
140         inOrderBar.verify(callback).onEnd(eq(callback.statusBarAnim));
141         inOrderIme.verify(callback).onEnd(eq(callback.imeAnim));
142 
143         assertAnimationSteps(callback.statusAnimSteps, false /* showAnimation */);
144         assertAnimationSteps(callback.imeAnimSteps, true /* showAnimation */, ime());
145 
146         assertEquals(before.getInsets(statusBars()),
147                 callback.statusAnimSteps.get(0).insets.getInsets(statusBars()));
148         assertEquals(after.getInsets(statusBars()),
149                 callback.statusAnimSteps.get(callback.statusAnimSteps.size() - 1).insets
150                         .getInsets(statusBars()));
151 
152         assertEquals(before.getInsets(ime()),
153                 callback.imeAnimSteps.get(0).insets.getInsets(ime()));
154         assertEquals(after.getInsets(ime()),
155                 callback.imeAnimSteps.get(callback.imeAnimSteps.size() - 1).insets
156                         .getInsets(ime()));
157     }
158 
159     @Test
testZeroInsetsImeAnimates()160     public void testZeroInsetsImeAnimates() {
161         initActivity(true /* useFloating */);
162         testShowAndHide();
163     }
164 
testShowAndHide()165     private void testShowAndHide() {
166         WindowInsets before = mActivity.mLastWindowInsets;
167         getInstrumentation().runOnMainSync(
168                 () -> mRootView.getWindowInsetsController().show(ime()));
169 
170         waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
171         commonAnimationAssertions(mActivity, before, true /* show */, ime());
172 
173         mActivity.resetAnimationDone();
174 
175         before = mActivity.mLastWindowInsets;
176 
177         getInstrumentation().runOnMainSync(
178                 () -> mRootView.getWindowInsetsController().hide(ime()));
179 
180         waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
181 
182         commonAnimationAssertions(mActivity, before, false /* show */, ime());
183     }
184 }
185