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