/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.server.wm; import static org.junit.Assert.assertTrue; import android.app.Instrumentation; import android.app.UiAutomation; import android.os.SystemClock; import androidx.annotation.NonNull; import androidx.test.platform.app.InstrumentationRegistry; import com.android.cts.mockime.Watermark; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; /** * Provides utility methods to test whether test IMEs are visible to the user or not. */ public final class InputMethodVisibilityVerifier { private static final long SCREENSHOT_DELAY = 100; // msec private static final long SCREENSHOT_TIME_SLICE = 500; // msec /** * Not intended to be instantiated. */ private InputMethodVisibilityVerifier() { } @NonNull private static boolean containsWatermark(@NonNull UiAutomation uiAutomation) { return Watermark.detect(uiAutomation.takeScreenshot()); } @NonNull private static boolean notContainsWatermark(@NonNull UiAutomation uiAutomation) { return !Watermark.detect(uiAutomation.takeScreenshot()); } private static boolean waitUntil(long timeout, @NonNull Predicate condition) { final long startTime = SystemClock.elapsedRealtime(); SystemClock.sleep(SCREENSHOT_DELAY); final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); // Wait until the main thread becomes idle. final CountDownLatch latch = new CountDownLatch(1); instrumentation.waitForIdle(latch::countDown); try { if (!latch.await(timeout, TimeUnit.MILLISECONDS)) { return false; } } catch (InterruptedException e) { } final UiAutomation uiAutomation = instrumentation.getUiAutomation(); if (condition.test(uiAutomation)) { return true; } while ((SystemClock.elapsedRealtime() - startTime) < timeout) { SystemClock.sleep(SCREENSHOT_TIME_SLICE); if (condition.test(uiAutomation)) { return true; } } return condition.test(uiAutomation); } /** * Asserts that {@link com.android.cts.mockime.MockIme} is visible to the user. * *

This never succeeds when * {@link com.android.cts.mockime.ImeSettings.Builder#setWatermarkEnabled(boolean)} is * explicitly called with {@code false}.

* * @param timeout timeout in milliseconds. */ public static void expectImeVisible(long timeout) { assertTrue(waitUntil(timeout, InputMethodVisibilityVerifier::containsWatermark)); } /** * Asserts that {@link com.android.cts.mockime.MockIme} is not visible to the user. * *

This always succeeds when * {@link com.android.cts.mockime.ImeSettings.Builder#setWatermarkEnabled(boolean)} is * explicitly called with {@code false}.

* * @param timeout timeout in milliseconds. */ public static void expectImeInvisible(long timeout) { assertTrue(waitUntil(timeout, InputMethodVisibilityVerifier::notContainsWatermark)); } }