1 /* 2 * Copyright (C) 2017 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.autofillservice.cts.activities; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static com.google.common.truth.Truth.assertWithMessage; 21 22 import android.app.Activity; 23 import android.autofillservice.cts.testcore.AutofillTestWatcher; 24 import android.autofillservice.cts.testcore.MyAutofillCallback; 25 import android.autofillservice.cts.testcore.Timeouts; 26 import android.graphics.Bitmap; 27 import android.graphics.Rect; 28 import android.os.Bundle; 29 import android.view.PixelCopy; 30 import android.view.View; 31 import android.view.WindowInsets; 32 import android.view.autofill.AutofillManager; 33 34 import androidx.annotation.NonNull; 35 36 import com.android.compatibility.common.util.RetryableException; 37 import com.android.compatibility.common.util.SynchronousPixelCopy; 38 import com.android.compatibility.common.util.Timeout; 39 40 import java.util.concurrent.CountDownLatch; 41 import java.util.concurrent.TimeUnit; 42 43 /** 44 * Base class for all activities in this test suite 45 */ 46 public abstract class AbstractAutoFillActivity extends Activity { 47 48 private final CountDownLatch mDestroyedLatch = new CountDownLatch(1); 49 protected final String mTag = getClass().getSimpleName(); 50 private MyAutofillCallback mCallback; 51 52 /** 53 * Run an action in the UI thread, and blocks caller until the action is finished. 54 */ syncRunOnUiThread(Runnable action)55 public final void syncRunOnUiThread(Runnable action) { 56 syncRunOnUiThread(action, Timeouts.UI_TIMEOUT.ms()); 57 } 58 59 /** 60 * Run an action in the UI thread, and blocks caller until the action is finished or it times 61 * out. 62 */ syncRunOnUiThread(Runnable action, long timeoutMs)63 public final void syncRunOnUiThread(Runnable action, long timeoutMs) { 64 final CountDownLatch latch = new CountDownLatch(1); 65 runOnUiThread(() -> { 66 action.run(); 67 latch.countDown(); 68 }); 69 try { 70 if (!latch.await(timeoutMs, TimeUnit.MILLISECONDS)) { 71 throw new RetryableException("action on UI thread timed out after %d ms", 72 timeoutMs); 73 } 74 } catch (InterruptedException e) { 75 Thread.currentThread().interrupt(); 76 throw new RuntimeException("Interrupted", e); 77 } 78 } 79 getAutofillManager()80 public AutofillManager getAutofillManager() { 81 return getSystemService(AutofillManager.class); 82 } 83 84 /** 85 * Takes a screenshot from the whole activity. 86 * 87 * <p><b>Note:</b> this screenshot only contains the contents of the activity, it doesn't 88 * include the autofill UIs; if you need to check that, please use 89 * {@link UiBot#takeScreenshot()} instead. 90 */ takeScreenshot()91 public Bitmap takeScreenshot() { 92 return takeScreenshot(findViewById(android.R.id.content).getRootView()); 93 } 94 95 /** 96 * Takes a screenshot from the a view. 97 */ takeScreenshot(View view)98 public Bitmap takeScreenshot(View view) { 99 final Rect srcRect = new Rect(); 100 syncRunOnUiThread(() -> view.getGlobalVisibleRect(srcRect)); 101 final Bitmap dest = Bitmap.createBitmap( 102 srcRect.width(), srcRect.height(), Bitmap.Config.ARGB_8888); 103 104 final SynchronousPixelCopy copy = new SynchronousPixelCopy(); 105 final int copyResult = copy.request(getWindow(), srcRect, dest); 106 assertThat(copyResult).isEqualTo(PixelCopy.SUCCESS); 107 108 return dest; 109 } 110 111 /** 112 * Registers and returns a custom callback for autofill events. 113 * 114 * <p>Note: caller doesn't need to call {@link #unregisterCallback()}, it will be automatically 115 * unregistered on {@link #finish()}. 116 */ registerCallback()117 public MyAutofillCallback registerCallback() { 118 assertWithMessage("already registered").that(mCallback).isNull(); 119 mCallback = new MyAutofillCallback(); 120 getAutofillManager().registerCallback(mCallback); 121 return mCallback; 122 } 123 124 /** 125 * Unregister the callback from the {@link AutofillManager}. 126 * 127 * <p>This method just need to be called when a test case wants to explicitly test the behavior 128 * of the activity when the callback is unregistered. 129 */ unregisterCallback()130 public void unregisterCallback() { 131 assertWithMessage("not registered").that(mCallback).isNotNull(); 132 unregisterNonNullCallback(); 133 } 134 135 /** 136 * Waits until {@link #onDestroy()} is called. 137 */ waitUntilDestroyed(@onNull Timeout timeout)138 public void waitUntilDestroyed(@NonNull Timeout timeout) throws InterruptedException { 139 if (!mDestroyedLatch.await(timeout.ms(), TimeUnit.MILLISECONDS)) { 140 throw new RetryableException(timeout, "activity %s not destroyed", this); 141 } 142 } 143 unregisterNonNullCallback()144 private void unregisterNonNullCallback() { 145 getAutofillManager().unregisterCallback(mCallback); 146 mCallback = null; 147 } 148 149 @Override onCreate(Bundle savedInstanceState)150 protected void onCreate(Bundle savedInstanceState) { 151 super.onCreate(savedInstanceState); 152 AutofillTestWatcher.registerActivity("onCreate()", this); 153 } 154 155 @Override onDestroy()156 protected void onDestroy() { 157 super.onDestroy(); 158 159 // Activitiy is typically unregistered at finish(), but we need to unregister here too 160 // for the cases where it's destroyed due to a config change (like device rotation). 161 AutofillTestWatcher.unregisterActivity("onDestroy()", this); 162 mDestroyedLatch.countDown(); 163 } 164 165 @Override finish()166 public void finish() { 167 finishOnly(); 168 AutofillTestWatcher.unregisterActivity("finish()", this); 169 } 170 171 /** 172 * Finishes the activity, without unregistering it from {@link AutofillTestWatcher}. 173 */ finishOnly()174 public void finishOnly() { 175 if (mCallback != null) { 176 unregisterNonNullCallback(); 177 } 178 super.finish(); 179 } 180 181 /** 182 * Clears focus from input fields. 183 */ clearFocus()184 public void clearFocus() { 185 throw new UnsupportedOperationException("Not implemented by " + getClass()); 186 } 187 188 /** 189 * Get insets of the root window 190 */ getRootWindowInsets()191 public WindowInsets getRootWindowInsets() { 192 return getWindow().getDecorView().getRootWindowInsets(); 193 } 194 } 195