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.support.test.uiautomator.By;
20 import android.support.test.uiautomator.UiDevice;
21 import android.support.test.uiautomator.UiObject;
22 import android.support.test.uiautomator.UiObjectNotFoundException;
23 import android.support.test.uiautomator.UiScrollable;
24 import android.support.test.uiautomator.UiSelector;
25 import android.support.test.uiautomator.Until;
26 import android.util.Log;
27 import static junit.framework.Assert.assertTrue;
28 
29 /**
30  * A helper class for UI-related testing tasks.
31  */
32 final class UiBot {
33 
34     private static final String TAG = "UiBot";
35     private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
36 
37     private final UiDevice mDevice;
38     private final int mTimeout;
39 
UiBot(UiDevice device, int timeout)40     public UiBot(UiDevice device, int timeout) {
41         mDevice = device;
42         mTimeout = timeout;
43     }
44 
45     /**
46      * Opens the system notification and gets a given notification.
47      *
48      * @param text Notificaton's text as displayed by the UI.
49      * @return notification object.
50      */
getNotification(String text)51     public UiObject getNotification(String text) {
52         boolean opened = mDevice.openNotification();
53         Log.v(TAG, "openNotification(): " + opened);
54         boolean gotIt = mDevice.wait(Until.hasObject(By.pkg(SYSTEMUI_PACKAGE)), mTimeout);
55         assertTrue("could not get system ui (" + SYSTEMUI_PACKAGE + ")", gotIt);
56 
57         return getObject(text);
58     }
59 
60     /**
61      * Opens the system notification and clicks a given notification.
62      *
63      * @param text Notificaton's text as displayed by the UI.
64      */
clickOnNotification(String text)65     public void clickOnNotification(String text) {
66         UiObject notification = getNotification(text);
67         click(notification, "bug report notification");
68     }
69 
70     /**
71      * Gets an object that might not yet be available in current UI.
72      *
73      * @param text Object's text as displayed by the UI.
74      */
getObject(String text)75     public UiObject getObject(String text) {
76         boolean gotIt = mDevice.wait(Until.hasObject(By.text(text)), mTimeout);
77         assertTrue("object with text '(" + text + "') not visible yet", gotIt);
78         return getVisibleObject(text);
79     }
80 
81     /**
82      * Gets an object that might not yet be available in current UI.
83      *
84      * @param id Object's fully-qualified resource id (like {@code android:id/button1})
85      */
getObjectById(String id)86     public UiObject getObjectById(String id) {
87         boolean gotIt = mDevice.wait(Until.hasObject(By.res(id)), mTimeout);
88         assertTrue("object with id '(" + id + "') not visible yet", gotIt);
89         return getVisibleObjectById(id);
90     }
91 
92     /**
93      * Gets an object which is guaranteed to be present in the current UI.
94      *
95      * @param text Object's text as displayed by the UI.
96      */
getVisibleObject(String text)97     public UiObject getVisibleObject(String text) {
98         UiObject uiObject = mDevice.findObject(new UiSelector().text(text));
99         assertTrue("could not find object with text '" + text + "'", uiObject.exists());
100         return uiObject;
101     }
102 
103     /**
104      * Gets an object which is guaranteed to be present in the current UI.
105      *
106      * @param text Object's text as displayed by the UI.
107      */
getVisibleObjectById(String id)108     public UiObject getVisibleObjectById(String id) {
109         UiObject uiObject = mDevice.findObject(new UiSelector().resourceId(id));
110         assertTrue("could not find object with id '" + id+ "'", uiObject.exists());
111         return uiObject;
112     }
113 
114 
115     /**
116      * Clicks on a UI element.
117      *
118      * @param uiObject UI element to be clicked.
119      * @param description Elements's description used on logging statements.
120      */
click(UiObject uiObject, String description)121     public void click(UiObject uiObject, String description) {
122         try {
123             boolean clicked = uiObject.click();
124             // TODO: assertion below fails sometimes, even though the click succeeded,
125             // (specially when clicking the "Just Once" button), so it's currently just logged.
126             // assertTrue("could not click on object '" + description + "'", clicked);
127 
128             Log.v(TAG, "onClick for " + description + ": " + clicked);
129         } catch (UiObjectNotFoundException e) {
130             throw new IllegalStateException("exception when clicking on object '" + description
131                     + "'", e);
132         }
133     }
134 
135     /**
136      * Chooses a given activity to handle an Intent, using the "Just Once" button.
137      *
138      * @param name name of the activity as displayed in the UI (typically the value set by
139      *            {@code android:label} in the manifest).
140      */
141     // TODO: UI Automator should provide such logic.
chooseActivity(String name)142     public void chooseActivity(String name) {
143         // First check if the activity is the default option.
144         String shareText = "Share with " + name;
145         Log.v(TAG, "Waiting for ActivityChooser text: '" + shareText + "'");
146         boolean gotIt = mDevice.wait(Until.hasObject(By.text(shareText)), mTimeout);
147         boolean justOnceHack = false;
148 
149         if (gotIt) {
150             Log.v(TAG, "Found activity " + name + ", it's the default action");
151             clickJustOnce();
152         } else {
153             // Since it's not, need to find it in the scrollable list...
154             Log.v(TAG, "Activity " + name + " is not default action");
155             UiScrollable activitiesList = new UiScrollable(new UiSelector().scrollable(true));
156             try {
157                 activitiesList.scrollForward();
158             } catch (UiObjectNotFoundException e) {
159                 // TODO: for some paranormal issue, the first time a test is run the scrollable
160                 // activity list is displayed but calling scrollForwad() (or even isScrollable())
161                 // throws a "UiObjectNotFoundException: UiSelector[SCROLLABLE=true]" exception
162                 justOnceHack = true;
163                 Log.d(TAG, "could not scroll forward", e);
164             }
165             UiObject activity = getVisibleObject(name);
166             // ... then select it.
167             click(activity, name);
168             if (justOnceHack) {
169                 clickJustOnce();
170             }
171         }
172     }
173 
clickJustOnce()174     private void clickJustOnce() {
175         boolean gotIt = mDevice.wait(Until.hasObject(By.res("android", "button_once")), mTimeout);
176         assertTrue("'Just Once' button not visible yet", gotIt);
177 
178         UiObject justOnce = mDevice
179                 .findObject(new UiSelector().resourceId("android:id/button_once"));
180         assertTrue("'Just Once' button not found", justOnce.exists());
181 
182         click(justOnce, "Just Once");
183     }
184 
pressBack()185     public void pressBack() {
186         mDevice.pressBack();
187     }
188 }
189