1 /* 2 * Copyright (C) 2021 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 org.junit.Assert.assertTrue; 20 21 import android.app.Instrumentation; 22 import android.app.UiAutomation; 23 import android.os.SystemClock; 24 25 import androidx.annotation.NonNull; 26 import androidx.test.platform.app.InstrumentationRegistry; 27 28 import com.android.cts.mockime.Watermark; 29 30 import java.util.concurrent.CountDownLatch; 31 import java.util.concurrent.TimeUnit; 32 import java.util.function.Predicate; 33 34 /** 35 * Provides utility methods to test whether test IMEs are visible to the user or not. 36 */ 37 public final class InputMethodVisibilityVerifier { 38 39 private static final long SCREENSHOT_DELAY = 100; // msec 40 private static final long SCREENSHOT_TIME_SLICE = 500; // msec 41 42 /** 43 * Not intended to be instantiated. 44 */ InputMethodVisibilityVerifier()45 private InputMethodVisibilityVerifier() { 46 } 47 48 @NonNull containsWatermark(@onNull UiAutomation uiAutomation)49 private static boolean containsWatermark(@NonNull UiAutomation uiAutomation) { 50 return Watermark.detect(uiAutomation.takeScreenshot()); 51 } 52 53 @NonNull notContainsWatermark(@onNull UiAutomation uiAutomation)54 private static boolean notContainsWatermark(@NonNull UiAutomation uiAutomation) { 55 return !Watermark.detect(uiAutomation.takeScreenshot()); 56 } 57 waitUntil(long timeout, @NonNull Predicate<UiAutomation> condition)58 private static boolean waitUntil(long timeout, @NonNull Predicate<UiAutomation> condition) { 59 final long startTime = SystemClock.elapsedRealtime(); 60 SystemClock.sleep(SCREENSHOT_DELAY); 61 62 final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); 63 64 // Wait until the main thread becomes idle. 65 final CountDownLatch latch = new CountDownLatch(1); 66 instrumentation.waitForIdle(latch::countDown); 67 try { 68 if (!latch.await(timeout, TimeUnit.MILLISECONDS)) { 69 return false; 70 } 71 } catch (InterruptedException e) { 72 } 73 74 final UiAutomation uiAutomation = instrumentation.getUiAutomation(); 75 if (condition.test(uiAutomation)) { 76 return true; 77 } 78 while ((SystemClock.elapsedRealtime() - startTime) < timeout) { 79 SystemClock.sleep(SCREENSHOT_TIME_SLICE); 80 if (condition.test(uiAutomation)) { 81 return true; 82 } 83 } 84 return condition.test(uiAutomation); 85 } 86 87 /** 88 * Asserts that {@link com.android.cts.mockime.MockIme} is visible to the user. 89 * 90 * <p>This never succeeds when 91 * {@link com.android.cts.mockime.ImeSettings.Builder#setWatermarkEnabled(boolean)} is 92 * explicitly called with {@code false}.</p> 93 * 94 * @param timeout timeout in milliseconds. 95 */ expectImeVisible(long timeout)96 public static void expectImeVisible(long timeout) { 97 assertTrue(waitUntil(timeout, InputMethodVisibilityVerifier::containsWatermark)); 98 } 99 100 /** 101 * Asserts that {@link com.android.cts.mockime.MockIme} is not visible to the user. 102 * 103 * <p>This always succeeds when 104 * {@link com.android.cts.mockime.ImeSettings.Builder#setWatermarkEnabled(boolean)} is 105 * explicitly called with {@code false}.</p> 106 * 107 * @param timeout timeout in milliseconds. 108 */ expectImeInvisible(long timeout)109 public static void expectImeInvisible(long timeout) { 110 assertTrue(waitUntil(timeout, InputMethodVisibilityVerifier::notContainsWatermark)); 111 } 112 } 113