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