1e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme/*
2e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * Copyright (C) 2015 The Android Open Source Project
3e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme *
4e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * Licensed under the Apache License, Version 2.0 (the "License");
5e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * you may not use this file except in compliance with the License.
6e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * You may obtain a copy of the License at
7e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme *
8e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme *      http://www.apache.org/licenses/LICENSE-2.0
9e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme *
10e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * Unless required by applicable law or agreed to in writing, software
11e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * distributed under the License is distributed on an "AS IS" BASIS,
12e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * See the License for the specific language governing permissions and
14e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * limitations under the License.
15e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme */
16e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme
17e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemepackage com.android.shell;
18e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme
19a86a3012ef689af659261b209da86f2e24ac21ecFelipe Lemeimport android.app.Instrumentation;
20a86a3012ef689af659261b209da86f2e24ac21ecFelipe Lemeimport android.app.StatusBarManager;
21e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemeimport android.support.test.uiautomator.By;
22e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemeimport android.support.test.uiautomator.UiDevice;
23e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemeimport android.support.test.uiautomator.UiObject;
24e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemeimport android.support.test.uiautomator.UiObjectNotFoundException;
25e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemeimport android.support.test.uiautomator.UiSelector;
26e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemeimport android.support.test.uiautomator.Until;
27e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemeimport android.util.Log;
28a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme
29a86a3012ef689af659261b209da86f2e24ac21ecFelipe Lemeimport static junit.framework.Assert.assertFalse;
30e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemeimport static junit.framework.Assert.assertTrue;
31e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme
32e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme/**
33e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * A helper class for UI-related testing tasks.
34e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme */
35e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemefinal class UiBot {
36e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme
37e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme    private static final String TAG = "UiBot";
383fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme    private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
39e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme
40a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme    private final Instrumentation mInstrumentation;
41e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme    private final UiDevice mDevice;
42e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme    private final int mTimeout;
43e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme
44a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme    public UiBot(Instrumentation instrumentation, int timeout) {
45a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme        mInstrumentation = instrumentation;
46a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme        mDevice = UiDevice.getInstance(instrumentation);
47e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme        mTimeout = timeout;
48e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme    }
49e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme
50e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme    /**
5169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme     * Opens the system notification and gets a given notification.
52e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme     *
53e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme     * @param text Notificaton's text as displayed by the UI.
5469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme     * @return notification object.
55e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme     */
5669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme    public UiObject getNotification(String text) {
57e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme        boolean opened = mDevice.openNotification();
58e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme        Log.v(TAG, "openNotification(): " + opened);
593fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme        boolean gotIt = mDevice.wait(Until.hasObject(By.pkg(SYSTEMUI_PACKAGE)), mTimeout);
603fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme        assertTrue("could not get system ui (" + SYSTEMUI_PACKAGE + ")", gotIt);
61e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme
6269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme        return getObject(text);
6369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme    }
64e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme
651ac5441f572c7866a8c5040ae16723fe604960e8Felipe Leme    public void collapseStatusBar() throws Exception {
66a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme        // TODO: mDevice should provide such method..
67a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme        StatusBarManager sbm =
68a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme                (StatusBarManager) mInstrumentation.getContext().getSystemService("statusbar");
69a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme        sbm.collapsePanels();
70a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme    }
71a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme
7269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme    /**
7369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme     * Opens the system notification and clicks a given notification.
7469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme     *
7569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme     * @param text Notificaton's text as displayed by the UI.
7669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme     */
7769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme    public void clickOnNotification(String text) {
7869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme        UiObject notification = getNotification(text);
79e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme        click(notification, "bug report notification");
80e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme    }
81e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme
82e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme    /**
8369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme     * Gets an object that might not yet be available in current UI.
8469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme     *
8569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme     * @param text Object's text as displayed by the UI.
8669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme     */
8769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme    public UiObject getObject(String text) {
8869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme        boolean gotIt = mDevice.wait(Until.hasObject(By.text(text)), mTimeout);
8969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme        assertTrue("object with text '(" + text + "') not visible yet", gotIt);
9069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme        return getVisibleObject(text);
9169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme    }
9269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme
9369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme    /**
94bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme     * Gets an object that might not yet be available in current UI.
95bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme     *
96bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme     * @param id Object's fully-qualified resource id (like {@code android:id/button1})
97bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme     */
98bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme    public UiObject getObjectById(String id) {
99bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme        boolean gotIt = mDevice.wait(Until.hasObject(By.res(id)), mTimeout);
100bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme        assertTrue("object with id '(" + id + "') not visible yet", gotIt);
101bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme        return getVisibleObjectById(id);
102bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme    }
103bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme
104bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme    /**
10569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme     * Gets an object which is guaranteed to be present in the current UI.
106e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme     *
107e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme     * @param text Object's text as displayed by the UI.
108e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme     */
109e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme    public UiObject getVisibleObject(String text) {
110e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme        UiObject uiObject = mDevice.findObject(new UiSelector().text(text));
11169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme        assertTrue("could not find object with text '" + text + "'", uiObject.exists());
112e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme        return uiObject;
113e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme    }
114e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme
115e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme    /**
116bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme     * Gets an object which is guaranteed to be present in the current UI.
117bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme     *
118bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme     * @param text Object's text as displayed by the UI.
119bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme     */
120bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme    public UiObject getVisibleObjectById(String id) {
121bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme        UiObject uiObject = mDevice.findObject(new UiSelector().resourceId(id));
122bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme        assertTrue("could not find object with id '" + id+ "'", uiObject.exists());
123bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme        return uiObject;
124bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme    }
125bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme
126a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme    /**
127a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme     * Asserts an object is not visible.
128a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme     */
129a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme    public void assertNotVisibleById(String id) {
130a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme        // TODO: not working when the bugreport dialog is shown, it hangs until the dialog is
131a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme        // dismissed and hence always work.
132a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme        boolean hasIt = mDevice.hasObject(By.res(id));
133a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme        assertFalse("should not have found object with id '" + id+ "'", hasIt);
134a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme    }
135a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme
136bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme
137bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme    /**
138e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme     * Clicks on a UI element.
139e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme     *
140e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme     * @param uiObject UI element to be clicked.
141e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme     * @param description Elements's description used on logging statements.
142e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme     */
143e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme    public void click(UiObject uiObject, String description) {
144e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme        try {
145e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme            boolean clicked = uiObject.click();
146e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme            // TODO: assertion below fails sometimes, even though the click succeeded,
147e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme            // (specially when clicking the "Just Once" button), so it's currently just logged.
148e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme            // assertTrue("could not click on object '" + description + "'", clicked);
149e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme
150e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme            Log.v(TAG, "onClick for " + description + ": " + clicked);
151e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme        } catch (UiObjectNotFoundException e) {
152e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme            throw new IllegalStateException("exception when clicking on object '" + description
153e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme                    + "'", e);
154e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme        }
155e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme    }
156e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme
157e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme    /**
15891699af8c6dae2a3ca703abc410fc00c0ab5de59Felipe Leme     * Chooses a given activity to handle an Intent.
159e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme     *
160e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme     * @param name name of the activity as displayed in the UI (typically the value set by
161e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme     *            {@code android:label} in the manifest).
162e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme     */
163e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme    public void chooseActivity(String name) {
16491699af8c6dae2a3ca703abc410fc00c0ab5de59Felipe Leme        // It uses an intent chooser now, so just getting the activity by text is enough...
165ca5002f6bde51c0dc80281a821650ab28f36b39cFelipe Leme        UiObject activity = getObject(name);
16691699af8c6dae2a3ca703abc410fc00c0ab5de59Felipe Leme        click(activity, name);
1673fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme    }
1683fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme
169bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme    public void pressBack() {
170bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme        mDevice.pressBack();
171bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme    }
17291699af8c6dae2a3ca703abc410fc00c0ab5de59Felipe Leme
17391699af8c6dae2a3ca703abc410fc00c0ab5de59Felipe Leme    public void turnScreenOn() throws Exception {
17491699af8c6dae2a3ca703abc410fc00c0ab5de59Felipe Leme        mDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP");
17591699af8c6dae2a3ca703abc410fc00c0ab5de59Felipe Leme        mDevice.executeShellCommand("wm dismiss-keyguard");
17691699af8c6dae2a3ca703abc410fc00c0ab5de59Felipe Leme    }
17791699af8c6dae2a3ca703abc410fc00c0ab5de59Felipe Leme
178e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme}
179