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