1 /* 2 * Copyright (C) 2023 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 com.android.adservices.ui.util; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import android.app.Instrumentation; 22 import android.content.Intent; 23 import android.content.pm.PackageManager; 24 import android.graphics.Point; 25 import android.util.Log; 26 27 import androidx.test.core.app.ApplicationProvider; 28 import androidx.test.platform.app.InstrumentationRegistry; 29 import androidx.test.uiautomator.By; 30 import androidx.test.uiautomator.BySelector; 31 import androidx.test.uiautomator.Direction; 32 import androidx.test.uiautomator.UiDevice; 33 import androidx.test.uiautomator.UiObject; 34 import androidx.test.uiautomator.UiObject2; 35 import androidx.test.uiautomator.UiSelector; 36 import androidx.test.uiautomator.Until; 37 38 import com.android.adservices.LogUtil; 39 import com.android.adservices.shared.testing.common.FileHelper; 40 41 import java.io.File; 42 import java.text.SimpleDateFormat; 43 import java.time.Instant; 44 import java.util.Date; 45 import java.util.List; 46 import java.util.Locale; 47 import java.util.regex.Pattern; 48 49 /** Util class for APK tests. */ 50 public class ApkTestUtil { 51 52 private static final String PRIVACY_SANDBOX_UI = "android.adservices.ui.SETTINGS"; 53 private static final String ANDROID_WIDGET_SCROLLVIEW = "android.widget.ScrollView"; 54 55 private static final String TAG = "ApkTestUtil"; 56 private static final int WINDOW_LAUNCH_TIMEOUT = 2_000; 57 public static final int PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT_MS = 1_000; 58 59 /** 60 * Check whether the device is supported. Adservices doesn't support non-phone device. 61 * 62 * @return if the device is supported. 63 */ isDeviceSupported()64 public static boolean isDeviceSupported() { 65 final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); 66 PackageManager pm = inst.getContext().getPackageManager(); 67 return !pm.hasSystemFeature(PackageManager.FEATURE_WATCH) 68 && !pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) 69 && !pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK); 70 } 71 getConsentSwitch(UiDevice device)72 public static UiObject2 getConsentSwitch(UiDevice device) { 73 UiObject2 consentSwitch = scrollToFindElement(device, By.clazz("android.widget.Switch")); 74 // Swipe the screen by the width of the toggle so it's not blocked by the nav bar on AOSP 75 // devices. 76 if (device.getDisplayHeight() - consentSwitch.getVisibleBounds().centerY() < 100) { 77 device.swipe( 78 consentSwitch.getVisibleBounds().centerX(), 79 500, 80 consentSwitch.getVisibleBounds().centerX(), 81 0, 82 100); 83 } 84 85 return consentSwitch; 86 } 87 88 /** Returns the string corresponding to a resource ID. */ getString(int resourceId)89 public static String getString(int resourceId) { 90 return ApplicationProvider.getApplicationContext().getResources().getString(resourceId); 91 } 92 scrollToAndClick(UiDevice device, int resId)93 public static void scrollToAndClick(UiDevice device, int resId) { 94 UiObject2 obj = scrollTo(device, resId); 95 clickTopLeft(obj); 96 } 97 click(UiDevice device, int resId)98 public static void click(UiDevice device, int resId) { 99 UiObject2 obj = device.findObject(By.text(getString(resId))); 100 // objects may be partially hidden by the status bar and nav bars. 101 clickTopLeft(obj); 102 } 103 clickTopLeft(UiObject2 obj)104 public static void clickTopLeft(UiObject2 obj) { 105 assertThat(obj).isNotNull(); 106 obj.clickAndWait( 107 new Point(obj.getVisibleBounds().top, obj.getVisibleBounds().left), 108 Until.newWindow(), 109 WINDOW_LAUNCH_TIMEOUT); 110 } 111 gentleSwipe(UiDevice device)112 public static void gentleSwipe(UiDevice device) { 113 device.waitForWindowUpdate(null, WINDOW_LAUNCH_TIMEOUT); 114 UiObject2 scrollView = device.wait( 115 Until.findObject(By.scrollable(true).clazz(ANDROID_WIDGET_SCROLLVIEW)), 116 PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT_MS); 117 scrollView.scroll(Direction.DOWN, /* percent */ 0.25F); 118 } 119 scrollTo(UiDevice device, int resId)120 public static UiObject2 scrollTo(UiDevice device, int resId) { 121 String targetStr = getString(resId); 122 return scrollToFindElement( 123 device, By.text(Pattern.compile(targetStr, Pattern.CASE_INSENSITIVE))); 124 } 125 scrollTo(UiDevice device, String regexStr)126 public static UiObject2 scrollTo(UiDevice device, String regexStr) { 127 return scrollToFindElement( 128 device, By.res(Pattern.compile(regexStr, Pattern.CASE_INSENSITIVE))); 129 } 130 scrollToFindElement(UiDevice device, BySelector selector)131 public static UiObject2 scrollToFindElement(UiDevice device, BySelector selector) { 132 device.waitForWindowUpdate(null, WINDOW_LAUNCH_TIMEOUT); 133 UiObject2 scrollView = 134 device.wait( 135 Until.findObject(By.scrollable(true).clazz(ANDROID_WIDGET_SCROLLVIEW)), 136 PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT_MS); 137 if (scrollView == null) { 138 return null; 139 } 140 UiObject2 element = scrollView.scrollUntil(Direction.DOWN, Until.findObject(selector)); 141 142 return element != null 143 ? element 144 : scrollView.scrollUntil(Direction.UP, Until.findObject(selector)); 145 } 146 147 /** Returns the UiObject corresponding to a resource ID. */ getElement(UiDevice device, int resId)148 public static UiObject2 getElement(UiDevice device, int resId) { 149 String targetStr = getString(resId); 150 Log.d( 151 TAG, 152 "Waiting for object using target string " 153 + targetStr 154 + " until a timeout of " 155 + PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT_MS 156 + " ms"); 157 UiObject2 obj = 158 device.wait( 159 Until.findObject(By.text(targetStr)), 160 PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT_MS); 161 if (obj == null) { 162 obj = device.findObject(By.text(targetStr.toUpperCase(Locale.getDefault()))); 163 } 164 return obj; 165 } 166 167 /** Returns the string corresponding to a resource ID and index. */ getElement(UiDevice device, int resId, int index)168 public static UiObject2 getElement(UiDevice device, int resId, int index) { 169 String targetStr = getString(resId); 170 List<UiObject2> objs = 171 device.wait( 172 Until.findObjects(By.text(targetStr)), 173 PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT_MS); 174 if (objs == null) { 175 return device.wait( 176 Until.findObjects(By.text(targetStr.toUpperCase(Locale.getDefault()))), 177 PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT_MS).get(index); 178 } 179 return objs.get(index); 180 } 181 182 /** Returns the UiObject corresponding to a resource ID. */ getPageElement(UiDevice device, int resId)183 public static UiObject getPageElement(UiDevice device, int resId) { 184 return device.findObject(new UiSelector().text(getString(resId))); 185 } 186 187 /** Launch Privacy Sandbox Setting View. */ launchSettingView(UiDevice device, int launchTimeout)188 public static void launchSettingView(UiDevice device, int launchTimeout) { 189 // Launch the setting view. 190 Intent intent = new Intent(PRIVACY_SANDBOX_UI); 191 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 192 ApplicationProvider.getApplicationContext().startActivity(intent); 193 194 // Wait for the view to appear 195 device.wait(Until.hasObject(By.pkg(PRIVACY_SANDBOX_UI).depth(0)), launchTimeout); 196 } 197 198 /** Launch Privacy Sandbox Setting View with UX extra. */ launchSettingViewGivenUx(UiDevice device, int launchTimeout, String ux)199 public static void launchSettingViewGivenUx(UiDevice device, int launchTimeout, String ux) { 200 // Launch the setting view. 201 Intent intent = new Intent(PRIVACY_SANDBOX_UI); 202 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 203 intent.putExtra("ux", ux); 204 205 ApplicationProvider.getApplicationContext().startActivity(intent); 206 207 // Wait for the view to appear 208 device.wait(Until.hasObject(By.pkg(PRIVACY_SANDBOX_UI).depth(0)), launchTimeout); 209 } 210 211 /** Takes the screenshot at the end of each test for debugging. */ takeScreenshot(UiDevice device, String methodName)212 public static void takeScreenshot(UiDevice device, String methodName) { 213 try { 214 String timeStamp = 215 new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US) 216 .format(Date.from(Instant.now())); 217 218 File screenshotFile = 219 new File( 220 FileHelper.getAdServicesTestsOutputDir(), 221 methodName + timeStamp + ".png"); 222 device.takeScreenshot(screenshotFile); 223 } catch (RuntimeException e) { 224 LogUtil.e("Failed to take screenshot: " + e.getMessage()); 225 } 226 } 227 } 228