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
17package com.android.shell;
18
19import android.app.Instrumentation;
20import android.app.StatusBarManager;
21import android.support.test.uiautomator.By;
22import android.support.test.uiautomator.UiDevice;
23import android.support.test.uiautomator.UiObject;
24import android.support.test.uiautomator.UiObjectNotFoundException;
25import android.support.test.uiautomator.UiSelector;
26import android.support.test.uiautomator.Until;
27import android.util.Log;
28
29import static junit.framework.Assert.assertFalse;
30import static junit.framework.Assert.assertTrue;
31
32/**
33 * A helper class for UI-related testing tasks.
34 */
35final class UiBot {
36
37    private static final String TAG = "UiBot";
38    private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
39
40    private final Instrumentation mInstrumentation;
41    private final UiDevice mDevice;
42    private final int mTimeout;
43
44    public UiBot(Instrumentation instrumentation, int timeout) {
45        mInstrumentation = instrumentation;
46        mDevice = UiDevice.getInstance(instrumentation);
47        mTimeout = timeout;
48    }
49
50    /**
51     * Opens the system notification and gets a given notification.
52     *
53     * @param text Notificaton's text as displayed by the UI.
54     * @return notification object.
55     */
56    public UiObject getNotification(String text) {
57        boolean opened = mDevice.openNotification();
58        Log.v(TAG, "openNotification(): " + opened);
59        boolean gotIt = mDevice.wait(Until.hasObject(By.pkg(SYSTEMUI_PACKAGE)), mTimeout);
60        assertTrue("could not get system ui (" + SYSTEMUI_PACKAGE + ")", gotIt);
61
62        return getObject(text);
63    }
64
65    public void collapseStatusBar() throws Exception {
66        // TODO: mDevice should provide such method..
67        StatusBarManager sbm =
68                (StatusBarManager) mInstrumentation.getContext().getSystemService("statusbar");
69        sbm.collapsePanels();
70    }
71
72    /**
73     * Opens the system notification and clicks a given notification.
74     *
75     * @param text Notificaton's text as displayed by the UI.
76     */
77    public void clickOnNotification(String text) {
78        UiObject notification = getNotification(text);
79        click(notification, "bug report notification");
80    }
81
82    /**
83     * Gets an object that might not yet be available in current UI.
84     *
85     * @param text Object's text as displayed by the UI.
86     */
87    public UiObject getObject(String text) {
88        boolean gotIt = mDevice.wait(Until.hasObject(By.text(text)), mTimeout);
89        assertTrue("object with text '(" + text + "') not visible yet", gotIt);
90        return getVisibleObject(text);
91    }
92
93    /**
94     * Gets an object that might not yet be available in current UI.
95     *
96     * @param id Object's fully-qualified resource id (like {@code android:id/button1})
97     */
98    public UiObject getObjectById(String id) {
99        boolean gotIt = mDevice.wait(Until.hasObject(By.res(id)), mTimeout);
100        assertTrue("object with id '(" + id + "') not visible yet", gotIt);
101        return getVisibleObjectById(id);
102    }
103
104    /**
105     * Gets an object which is guaranteed to be present in the current UI.
106     *
107     * @param text Object's text as displayed by the UI.
108     */
109    public UiObject getVisibleObject(String text) {
110        UiObject uiObject = mDevice.findObject(new UiSelector().text(text));
111        assertTrue("could not find object with text '" + text + "'", uiObject.exists());
112        return uiObject;
113    }
114
115    /**
116     * Gets an object which is guaranteed to be present in the current UI.
117     *
118     * @param text Object's text as displayed by the UI.
119     */
120    public UiObject getVisibleObjectById(String id) {
121        UiObject uiObject = mDevice.findObject(new UiSelector().resourceId(id));
122        assertTrue("could not find object with id '" + id+ "'", uiObject.exists());
123        return uiObject;
124    }
125
126    /**
127     * Asserts an object is not visible.
128     */
129    public void assertNotVisibleById(String id) {
130        // TODO: not working when the bugreport dialog is shown, it hangs until the dialog is
131        // dismissed and hence always work.
132        boolean hasIt = mDevice.hasObject(By.res(id));
133        assertFalse("should not have found object with id '" + id+ "'", hasIt);
134    }
135
136
137    /**
138     * Clicks on a UI element.
139     *
140     * @param uiObject UI element to be clicked.
141     * @param description Elements's description used on logging statements.
142     */
143    public void click(UiObject uiObject, String description) {
144        try {
145            boolean clicked = uiObject.click();
146            // TODO: assertion below fails sometimes, even though the click succeeded,
147            // (specially when clicking the "Just Once" button), so it's currently just logged.
148            // assertTrue("could not click on object '" + description + "'", clicked);
149
150            Log.v(TAG, "onClick for " + description + ": " + clicked);
151        } catch (UiObjectNotFoundException e) {
152            throw new IllegalStateException("exception when clicking on object '" + description
153                    + "'", e);
154        }
155    }
156
157    /**
158     * Chooses a given activity to handle an Intent.
159     *
160     * @param name name of the activity as displayed in the UI (typically the value set by
161     *            {@code android:label} in the manifest).
162     */
163    public void chooseActivity(String name) {
164        // It uses an intent chooser now, so just getting the activity by text is enough...
165        UiObject activity = getObject(name);
166        click(activity, name);
167    }
168
169    public void pressBack() {
170        mDevice.pressBack();
171    }
172
173    public void turnScreenOn() throws Exception {
174        mDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP");
175        mDevice.executeShellCommand("wm dismiss-keyguard");
176    }
177
178}
179