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 android.app.Instrumentation; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.pm.PackageManager; 23 import android.net.Uri; 24 25 import androidx.test.core.app.ApplicationProvider; 26 import androidx.test.platform.app.InstrumentationRegistry; 27 import androidx.test.uiautomator.By; 28 import androidx.test.uiautomator.UiDevice; 29 import androidx.test.uiautomator.UiObject; 30 import androidx.test.uiautomator.UiObjectNotFoundException; 31 import androidx.test.uiautomator.UiScrollable; 32 import androidx.test.uiautomator.UiSelector; 33 import androidx.test.uiautomator.Until; 34 35 import com.android.adservices.LogUtil; 36 import com.android.compatibility.common.util.ShellUtils; 37 38 import java.io.File; 39 import java.text.SimpleDateFormat; 40 import java.time.Instant; 41 import java.util.Date; 42 import java.util.Locale; 43 44 /** Util class for APK tests. */ 45 public class ApkTestUtil { 46 47 public static final String ADEXTSERVICES_PACKAGE_NAME = "com.google.android.ext.adservices.api"; 48 private static final String PRIVACY_SANDBOX_UI = "android.adservices.ui.SETTINGS"; 49 private static final int WINDOW_LAUNCH_TIMEOUT = 1000; 50 private static final int SCROLL_TIMEOUT = 500; 51 /** 52 * Check whether the device is supported. Adservices doesn't support non-phone device. 53 * 54 * @return if the device is supported. 55 */ isDeviceSupported()56 public static boolean isDeviceSupported() { 57 final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); 58 PackageManager pm = inst.getContext().getPackageManager(); 59 return !pm.hasSystemFeature(PackageManager.FEATURE_WATCH) 60 && !pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) 61 && !pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK); 62 } 63 getConsentSwitch(UiDevice device)64 public static UiObject getConsentSwitch(UiDevice device) throws UiObjectNotFoundException { 65 UiObject consentSwitch = 66 device.findObject(new UiSelector().className("android.widget.Switch")); 67 68 // Swipe the screen by the width of the toggle so it's not blocked by the nav bar on AOSP 69 // devices. 70 device.swipe( 71 consentSwitch.getVisibleBounds().centerX(), 72 500, 73 consentSwitch.getVisibleBounds().centerX(), 74 0, 75 1000); 76 77 return consentSwitch; 78 } 79 /** Returns the UiObject corresponding to a resource ID. */ getElement(UiDevice device, int resId)80 public static UiObject getElement(UiDevice device, int resId) { 81 UiObject obj = device.findObject(new UiSelector().text(getString(resId))); 82 if (!obj.exists()) { 83 obj = device.findObject(new UiSelector().text(getString(resId).toUpperCase())); 84 } 85 return obj; 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 93 /** Click the top left of the UiObject corresponding to a resource ID after scrolling. */ scrollToAndClick(UiDevice device, int resId)94 public static void scrollToAndClick(UiDevice device, int resId) 95 throws UiObjectNotFoundException { 96 UiObject obj = scrollTo(device, resId); 97 // objects may be partially hidden by the status bar and nav bars. 98 obj.clickTopLeft(); 99 } 100 gentleSwipe(UiDevice device)101 public static void gentleSwipe(UiDevice device) throws UiObjectNotFoundException { 102 UiScrollable scrollView = 103 new UiScrollable( 104 new UiSelector().scrollable(true).className("android.widget.ScrollView")); 105 106 scrollView.scrollForward(100); 107 } 108 109 /** Returns the UiObject corresponding to a resource ID after scrolling. */ scrollTo(UiDevice device, int resId)110 public static UiObject scrollTo(UiDevice device, int resId) throws UiObjectNotFoundException { 111 UiScrollable scrollView = 112 new UiScrollable( 113 new UiSelector().scrollable(true).className("android.widget.ScrollView")); 114 115 UiObject obj = device.findObject(new UiSelector().text(getString(resId))); 116 scrollView.scrollIntoView(obj); 117 try { 118 Thread.sleep(SCROLL_TIMEOUT); 119 } catch (InterruptedException e) { 120 LogUtil.e("InterruptedException:", e.getMessage()); 121 } 122 return obj; 123 } 124 125 /** Returns the string corresponding to a resource ID and index. */ getElement(UiDevice device, int resId, int index)126 public static UiObject getElement(UiDevice device, int resId, int index) { 127 UiObject obj = device.findObject(new UiSelector().text(getString(resId)).instance(index)); 128 if (!obj.exists()) { 129 obj = 130 device.findObject( 131 new UiSelector().text(getString(resId).toUpperCase()).instance(index)); 132 } 133 return obj; 134 } 135 136 /** Returns the UiObject corresponding to a resource ID. */ getPageElement(UiDevice device, int resId)137 public static UiObject getPageElement(UiDevice device, int resId) { 138 return device.findObject(new UiSelector().text(getString(resId))); 139 } 140 141 /** Launch Privacy Sandbox Setting View. */ launchSettingView(Context context, UiDevice device, int launchTimeout)142 public static void launchSettingView(Context context, UiDevice device, int launchTimeout) { 143 // Launch the setting view. 144 Intent intent = new Intent(PRIVACY_SANDBOX_UI); 145 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 146 context.startActivity(intent); 147 148 // Wait for the view to appear 149 device.wait(Until.hasObject(By.pkg(PRIVACY_SANDBOX_UI).depth(0)), launchTimeout); 150 } 151 152 /** Returns the package name of the default browser of the device. */ getDefaultBrowserPkgName(UiDevice device, Context context)153 public static String getDefaultBrowserPkgName(UiDevice device, Context context) { 154 Intent browserIntent = new Intent(Intent.ACTION_VIEW); 155 browserIntent.setData(Uri.parse("https://www.google.com")); 156 browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 157 context.startActivity(browserIntent); 158 device.waitForWindowUpdate(null, WINDOW_LAUNCH_TIMEOUT); 159 return device.getCurrentPackageName(); 160 } 161 162 /** Kills the default browser of the device after test. */ killDefaultBrowserPkgName(UiDevice device, Context context)163 public static void killDefaultBrowserPkgName(UiDevice device, Context context) { 164 ShellUtils.runShellCommand("am force-stop " + getDefaultBrowserPkgName(device, context)); 165 } 166 167 /** Takes the screenshot at the end of each test for debugging. */ takeScreenshot(UiDevice device, String methodName)168 public static void takeScreenshot(UiDevice device, String methodName) { 169 try { 170 String timeStamp = 171 new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US) 172 .format(Date.from(Instant.now())); 173 174 File screenshotFile = new File("/sdcard/Pictures/" + methodName + timeStamp + ".png"); 175 device.takeScreenshot(screenshotFile); 176 } catch (RuntimeException e) { 177 LogUtil.e("Failed to take screenshot: " + e.getMessage()); 178 } 179 } 180 } 181