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.preference.cts; 18 19 import static org.junit.Assert.assertNotNull; 20 21 import android.app.Activity; 22 import android.app.Instrumentation; 23 import android.app.UiAutomation; 24 import android.app.UiModeManager; 25 import android.content.Context; 26 import android.content.res.Configuration; 27 import android.graphics.Bitmap; 28 import android.graphics.Rect; 29 import android.util.DisplayMetrics; 30 31 import androidx.test.platform.app.InstrumentationRegistry; 32 import androidx.test.rule.ActivityTestRule; 33 import androidx.test.uiautomator.By; 34 import androidx.test.uiautomator.UiDevice; 35 import androidx.test.uiautomator.UiObject2; 36 import androidx.test.uiautomator.UiObjectNotFoundException; 37 import androidx.test.uiautomator.UiScrollable; 38 import androidx.test.uiautomator.UiSelector; 39 import androidx.test.uiautomator.Until; 40 41 /** 42 * Collection of helper utils for testing preferences. 43 */ 44 public class TestUtils { 45 46 public final UiDevice mDevice; 47 48 private final Context mContext; 49 private final Instrumentation mInstrumentation; 50 private final String mPackageName; 51 private final UiAutomation mAutomation; 52 private int mStatusBarHeight = -1; 53 private int mNavigationBarHeight = -1; 54 private ActivityTestRule<?> mRule; 55 TestUtils(ActivityTestRule<?> rule)56 TestUtils(ActivityTestRule<?> rule) { 57 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 58 mContext = mInstrumentation.getTargetContext(); 59 mPackageName = mContext.getPackageName(); 60 mDevice = UiDevice.getInstance(mInstrumentation); 61 mAutomation = mInstrumentation.getUiAutomation(); 62 mRule = rule; 63 } 64 waitForIdle()65 void waitForIdle() { 66 mDevice.waitForIdle(1000); 67 } 68 takeScreenshot()69 Bitmap takeScreenshot() { 70 // Only take a screenshot once the screen is stable enough. 71 waitForIdle(); 72 73 Bitmap bt = mAutomation.takeScreenshot(mRule.getActivity().getWindow()); 74 assertNotNull("Screenshot must not return null", bt); 75 76 // Crop-out the status bar to avoid flakiness with changing notifications / time. 77 int statusBarHeight = getStatusBarHeight(); 78 79 // Crop-out the navigation bar to avoid flakiness with button animations. 80 int navigationBarHeight = getNavigationBarHeight(); 81 82 // Crop-out the right side for the scrollbar which may or may not be visible. 83 // On wearable devices the scroll bar is a curve and occupies 20% of the right side. 84 int xToCut = isOnWatchUiMode() ? bt.getWidth() / 5 : bt.getWidth() / 20; 85 int yToCut = statusBarHeight; 86 87 if (hasVerticalNavBar()) { 88 xToCut += navigationBarHeight; 89 } else { 90 yToCut += navigationBarHeight; 91 } 92 93 bt = Bitmap.createBitmap( 94 bt, 0, statusBarHeight, bt.getWidth() - xToCut, bt.getHeight() - yToCut); 95 96 return bt; 97 } 98 tapOnViewWithText(String text)99 void tapOnViewWithText(String text) { 100 UiObject2 object2 = getTextObject(text); 101 if (object2 != null) { 102 object2.click(); 103 } else { 104 scrollToAndGetTextObject(text); 105 getTextObject(text).click(); 106 } 107 waitForIdle(); 108 } 109 isTextShown(String text)110 boolean isTextShown(String text) { 111 if (getTextObject(text) != null) { 112 return true; 113 } 114 115 return scrollToAndGetTextObject(text); 116 } 117 isTextHidden(String text)118 boolean isTextHidden(String text) { 119 return getTextObject(text) == null; 120 } 121 isTextFocused(String text)122 boolean isTextFocused(String text) { 123 UiObject2 object = getTextObject(text); 124 return object != null && object.isFocused(); 125 } 126 isOnWatchUiMode()127 boolean isOnWatchUiMode() { 128 UiModeManager uiModeManager = mContext.getSystemService(UiModeManager.class); 129 return uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_WATCH; 130 } 131 getStatusBarHeight()132 private int getStatusBarHeight() { 133 // Cache the result to keep it fast. 134 if (mStatusBarHeight >= 0) { 135 return mStatusBarHeight; 136 } 137 138 int resourceId = mInstrumentation.getTargetContext().getResources() 139 .getIdentifier("status_bar_height", "dimen", "android"); 140 if (resourceId > 0) { 141 mStatusBarHeight = mInstrumentation.getTargetContext().getResources() 142 .getDimensionPixelSize(resourceId); 143 } else { 144 mStatusBarHeight = 0; 145 } 146 return mStatusBarHeight; 147 } 148 getNavigationBarHeight()149 private int getNavigationBarHeight() { 150 // Cache the result to keep it fast. 151 if (mNavigationBarHeight >= 0) { 152 return mNavigationBarHeight; 153 } 154 155 int resourceId = mInstrumentation.getTargetContext().getResources() 156 .getIdentifier("navigation_bar_height", "dimen", "android"); 157 if (resourceId > 0) { 158 mNavigationBarHeight = mInstrumentation.getTargetContext().getResources() 159 .getDimensionPixelSize(resourceId); 160 } else { 161 mNavigationBarHeight = 0; 162 } 163 return mNavigationBarHeight; 164 } 165 hasVerticalNavBar()166 private boolean hasVerticalNavBar() { 167 Rect displayFrame = new Rect(); 168 final Activity activity = mRule.getActivity(); 169 activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(displayFrame); 170 DisplayMetrics dm = new DisplayMetrics(); 171 activity.getDisplay().getRealMetrics(dm); 172 return dm.heightPixels == displayFrame.bottom; 173 } 174 getTextObject(String text)175 private UiObject2 getTextObject(String text) { 176 // Wait for up to 1 second to find the object. Returns null if the object cannot be found. 177 return mDevice.wait(Until.findObject(By.text(text).pkg(mPackageName)), 1000); 178 } 179 scrollToAndGetTextObject(String text)180 private boolean scrollToAndGetTextObject(String text) { 181 UiScrollable scroller = new UiScrollable(new UiSelector().scrollable(true)); 182 try { 183 // Swipe far away from the edges to avoid triggering navigation gestures 184 scroller.setSwipeDeadZonePercentage(0.25); 185 return scroller.scrollTextIntoView(text); 186 } catch (UiObjectNotFoundException e) { 187 throw new AssertionError("View with text '" + text + "' was not found!", e); 188 } 189 } 190 } 191