1 /* 2 * Copyright (C) 2015 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.shell; 18 19 import android.app.Instrumentation; 20 import android.app.StatusBarManager; 21 import android.os.SystemClock; 22 import android.support.test.uiautomator.By; 23 import android.support.test.uiautomator.UiDevice; 24 import android.support.test.uiautomator.UiObject; 25 import android.support.test.uiautomator.UiObject2; 26 import android.support.test.uiautomator.UiObjectNotFoundException; 27 import android.support.test.uiautomator.UiSelector; 28 import android.support.test.uiautomator.Until; 29 import android.text.format.DateUtils; 30 import android.util.Log; 31 32 import static junit.framework.Assert.assertFalse; 33 import static junit.framework.Assert.assertNotNull; 34 import static junit.framework.Assert.assertTrue; 35 36 import java.util.List; 37 38 /** 39 * A helper class for UI-related testing tasks. 40 */ 41 final class UiBot { 42 43 private static final String TAG = "UiBot"; 44 private static final String SYSTEMUI_PACKAGE = "com.android.systemui"; 45 private static final String ANDROID_PACKAGE = "android"; 46 47 private static final long SHORT_UI_TIMEOUT_MS = (3 * DateUtils.SECOND_IN_MILLIS); 48 49 private final Instrumentation mInstrumentation; 50 private final UiDevice mDevice; 51 private final int mTimeout; 52 UiBot(Instrumentation instrumentation, int timeout)53 public UiBot(Instrumentation instrumentation, int timeout) { 54 mInstrumentation = instrumentation; 55 mDevice = UiDevice.getInstance(instrumentation); 56 mTimeout = timeout; 57 } 58 59 /** 60 * Opens the system notification and gets a UiObject with the text. 61 * 62 * @param text Notification's text as displayed by the UI. 63 * @return notification object. 64 */ getNotification(String text)65 public UiObject getNotification(String text) { 66 boolean opened = mDevice.openNotification(); 67 Log.v(TAG, "openNotification(): " + opened); 68 boolean gotIt = mDevice.wait(Until.hasObject(By.pkg(SYSTEMUI_PACKAGE)), mTimeout); 69 assertTrue("could not get system ui (" + SYSTEMUI_PACKAGE + ")", gotIt); 70 71 return getObject(text); 72 } 73 74 /** 75 * Opens the system notification and gets a notification containing the text. 76 * 77 * @param text Notification's text as displayed by the UI. 78 * @return notification object. 79 */ getNotification2(String text)80 public UiObject2 getNotification2(String text) { 81 boolean opened = mDevice.openNotification(); 82 Log.v(TAG, "openNotification(): " + opened); 83 final UiObject2 notificationScroller = mDevice.wait(Until.findObject( 84 By.res(SYSTEMUI_PACKAGE, "notification_stack_scroller")), mTimeout); 85 assertNotNull("could not get notification stack scroller", notificationScroller); 86 final List<UiObject2> notificationList = notificationScroller.getChildren(); 87 for (UiObject2 notification: notificationList) { 88 final UiObject2 notificationText = notification.findObject(By.textContains(text)); 89 if (notificationText != null) { 90 return notification; 91 } 92 } 93 return null; 94 } 95 96 /** 97 * Expands the notification. 98 * 99 * @param notification The notification object returned by {@link #getNotification2(String)}. 100 */ expandNotification(UiObject2 notification)101 public void expandNotification(UiObject2 notification) { 102 final UiObject2 expandBtn = notification.findObject( 103 By.res(ANDROID_PACKAGE, "expand_button")); 104 if (expandBtn.getContentDescription().equals("Collapse")) { 105 return; 106 } 107 expandBtn.click(); 108 mDevice.waitForIdle(); 109 } 110 collapseStatusBar()111 public void collapseStatusBar() throws Exception { 112 // TODO: mDevice should provide such method.. 113 StatusBarManager sbm = 114 (StatusBarManager) mInstrumentation.getContext().getSystemService("statusbar"); 115 sbm.collapsePanels(); 116 } 117 118 /** 119 * Opens the system notification and clicks a given notification. 120 * 121 * @param text Notificaton's text as displayed by the UI. 122 */ clickOnNotification(String text)123 public void clickOnNotification(String text) { 124 UiObject notification = getNotification(text); 125 click(notification, "bug report notification"); 126 } 127 128 /** 129 * Gets an object that might not yet be available in current UI. 130 * 131 * @param text Object's text as displayed by the UI. 132 */ getObject(String text)133 public UiObject getObject(String text) { 134 boolean gotIt = mDevice.wait(Until.hasObject(By.text(text)), mTimeout); 135 assertTrue("object with text '(" + text + "') not visible yet", gotIt); 136 return getVisibleObject(text); 137 } 138 139 /** 140 * Gets an object that might not yet be available in current UI. 141 * 142 * @param id Object's fully-qualified resource id (like {@code android:id/button1}) 143 */ getObjectById(String id)144 public UiObject getObjectById(String id) { 145 boolean gotIt = mDevice.wait(Until.hasObject(By.res(id)), mTimeout); 146 assertTrue("object with id '(" + id + "') not visible yet", gotIt); 147 return getVisibleObjectById(id); 148 } 149 150 /** 151 * Gets an object which is guaranteed to be present in the current UI. 152 * 153 * @param text Object's text as displayed by the UI. 154 */ getVisibleObject(String text)155 public UiObject getVisibleObject(String text) { 156 UiObject uiObject = mDevice.findObject(new UiSelector().text(text)); 157 assertTrue("could not find object with text '" + text + "'", uiObject.exists()); 158 return uiObject; 159 } 160 161 /** 162 * Gets an object which is guaranteed to be present in the current UI. 163 * 164 * @param text Object's text as displayed by the UI. 165 */ getVisibleObjectById(String id)166 public UiObject getVisibleObjectById(String id) { 167 UiObject uiObject = mDevice.findObject(new UiSelector().resourceId(id)); 168 assertTrue("could not find object with id '" + id+ "'", uiObject.exists()); 169 return uiObject; 170 } 171 172 /** 173 * Asserts an object is not visible. 174 */ assertNotVisibleById(String id)175 public void assertNotVisibleById(String id) { 176 // TODO: not working when the bugreport dialog is shown, it hangs until the dialog is 177 // dismissed and hence always work. 178 boolean hasIt = mDevice.hasObject(By.res(id)); 179 assertFalse("should not have found object with id '" + id+ "'", hasIt); 180 } 181 182 183 /** 184 * Clicks on a UI element. 185 * 186 * @param uiObject UI element to be clicked. 187 * @param description Elements's description used on logging statements. 188 */ click(UiObject uiObject, String description)189 public void click(UiObject uiObject, String description) { 190 try { 191 boolean clicked = uiObject.click(); 192 // TODO: assertion below fails sometimes, even though the click succeeded, 193 // (specially when clicking the "Just Once" button), so it's currently just logged. 194 // assertTrue("could not click on object '" + description + "'", clicked); 195 196 Log.v(TAG, "onClick for " + description + ": " + clicked); 197 } catch (UiObjectNotFoundException e) { 198 throw new IllegalStateException("exception when clicking on object '" + description 199 + "'", e); 200 } 201 } 202 203 /** 204 * Chooses a given activity to handle an Intent. 205 * 206 * @param name name of the activity as displayed in the UI (typically the value set by 207 * {@code android:label} in the manifest). 208 */ chooseActivity(String name)209 public void chooseActivity(String name) { 210 // It uses an intent chooser now, so just getting the activity by text is enough... 211 final String share = mInstrumentation.getContext().getString( 212 com.android.internal.R.string.share); 213 boolean gotIt = mDevice.wait(Until.hasObject(By.text(share)), mTimeout); 214 assertTrue("could not get share activity (" + share + ")", gotIt); 215 swipeUp(); 216 SystemClock.sleep(SHORT_UI_TIMEOUT_MS); 217 UiObject activity = getObject(name); 218 click(activity, name); 219 } 220 pressBack()221 public void pressBack() { 222 mDevice.pressBack(); 223 } 224 turnScreenOn()225 public void turnScreenOn() throws Exception { 226 mDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP"); 227 mDevice.executeShellCommand("wm dismiss-keyguard"); 228 mDevice.waitForIdle(); 229 } 230 swipeUp()231 public void swipeUp() { 232 mDevice.swipe(mDevice.getDisplayWidth() / 2, mDevice.getDisplayHeight() * 3 / 4, 233 mDevice.getDisplayWidth() / 2, 0, 30); 234 } 235 } 236