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