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.documentsui.bots;
18
19import static junit.framework.Assert.assertEquals;
20import static junit.framework.Assert.assertFalse;
21import static junit.framework.Assert.assertNotNull;
22import static junit.framework.Assert.assertTrue;
23
24import android.content.Context;
25import android.support.test.uiautomator.By;
26import android.support.test.uiautomator.UiDevice;
27import android.support.test.uiautomator.UiObject;
28import android.support.test.uiautomator.UiObject2;
29import android.support.test.uiautomator.UiObjectNotFoundException;
30import android.support.test.uiautomator.UiSelector;
31import android.support.test.uiautomator.Until;
32
33import com.android.documentsui.R;
34
35import java.util.Iterator;
36import java.util.List;
37
38/**
39 * A test helper class that provides support for controlling DocumentsUI activities
40 * programmatically, and making assertions against the state of the UI.
41 *
42 * <p>Support for working directly with Roots and Directory view can be found
43 * in the respective bots.
44 */
45public class UiBot extends BaseBot {
46    public static final String TARGET_PKG = "com.android.documentsui";
47    private static final String LAUNCHER_PKG = "com.android.launcher";
48
49    public UiBot(UiDevice device, Context context, int timeout) {
50        super(device, context, timeout);
51    }
52
53    public void assertWindowTitle(String expected) {
54        // Turns out the title field on a window does not have
55        // an id associated with it at runtime (which confuses the hell out of me)
56        // In code we address this via "android.R.id.title".
57        UiObject2 o = find(By.text(expected));
58        // It's a bit of a conceit that we then *assert* that the title
59        // is the value that we used to identify the UiObject2.
60        // If the preceeding lookup fails, this'll choke with an NPE.
61        // But given the issue described in the comment above, we're
62        // going to do it anyway. Because we shouldn't be looking up
63        // the uiobject by it's expected content :|
64        assertEquals(expected, o.getText());
65    }
66
67    public void assertMenuEnabled(int id, boolean enabled) {
68        UiObject2 menu= findMenuWithName(mContext.getString(id));
69        assertNotNull(menu);
70        assertEquals(enabled, menu.isEnabled());
71    }
72
73    public void assertSearchTextField(boolean isFocused, String query)
74            throws UiObjectNotFoundException {
75        UiObject textField = findSearchViewTextField();
76        UiObject searchIcon = findSearchViewIcon();
77
78        assertFalse(searchIcon.exists());
79        assertTrue(textField.exists());
80        assertEquals(isFocused, textField.isFocused());
81        if(query != null) {
82            assertEquals(query, textField.getText());
83        }
84    }
85
86    public void assertSearchTextFiledAndIcon(boolean searchTextFieldExists, boolean searchIconExists) {
87        assertEquals(searchTextFieldExists, findSearchViewTextField().exists());
88        assertEquals(searchIconExists, findSearchViewIcon().exists());
89    }
90
91    public void assertInActionMode(boolean inActionMode) {
92        UiObject actionModeBar = findActionModeBar();
93        assertEquals(inActionMode, actionModeBar.exists());
94    }
95
96    public void openSearchView() throws UiObjectNotFoundException {
97        UiObject searchView = findSearchView();
98        searchView.click();
99        assertTrue(searchView.exists());
100    }
101
102    public void setSearchQuery(String query) throws UiObjectNotFoundException {
103        UiObject searchView = findSearchView();
104        assertTrue(searchView.exists());
105        UiObject searchTextField = findSearchViewTextField();
106        searchTextField.setText(query);
107        assertSearchTextField(true, query);
108    }
109
110    public UiObject openOverflowMenu() throws UiObjectNotFoundException {
111        UiObject obj = findMenuMoreOptions();
112        obj.click();
113        mDevice.waitForIdle(mTimeout);
114        return obj;
115    }
116
117    public void setDialogText(String text) throws UiObjectNotFoundException {
118        findDialogEditText().setText(text);
119    }
120
121    void switchViewMode() {
122        UiObject2 mode = menuGridMode();
123        if (mode != null) {
124            mode.click();
125        } else {
126            menuListMode().click();
127        }
128    }
129
130    UiObject2 menuGridMode() {
131        // Note that we're using By.desc rather than By.res, because of b/25285770
132        return find(By.desc("Grid view"));
133    }
134
135        UiObject2 menuListMode() {
136        // Note that we're using By.desc rather than By.res, because of b/25285770
137        return find(By.desc("List view"));
138    }
139
140    public UiObject2 menuDelete() {
141        return find(By.res("com.android.documentsui:id/menu_delete"));
142    }
143
144    public UiObject2 menuShare() {
145        return find(By.res("com.android.documentsui:id/menu_share"));
146    }
147
148    public UiObject2 menuRename() {
149        return findMenuWithName(mContext.getString(R.string.menu_rename));
150    }
151
152    public UiObject2 menuNewFolder() {
153        return findMenuWithName(mContext.getString(R.string.menu_create_dir));
154    }
155
156    UiObject findSearchView() {
157        return findObject("com.android.documentsui:id/menu_search");
158    }
159
160    UiObject findSearchViewTextField() {
161        return findObject("com.android.documentsui:id/menu_search", "android:id/search_src_text");
162    }
163
164    UiObject findSearchViewIcon() {
165        return mContext.getResources().getBoolean(R.bool.full_bar_search_view)
166                ? findObject("com.android.documentsui:id/menu_search")
167                : findObject("com.android.documentsui:id/menu_search", "android:id/search_button");
168    }
169
170    UiObject findActionModeBar() {
171        return findObject("android:id/action_mode_bar");
172    }
173
174    public UiObject findDialogEditText() {
175        return findObject("android:id/content", "android:id/text1");
176    }
177
178    public UiObject findDownloadRetryDialog() {
179        UiSelector selector = new UiSelector().text("Couldn't download");
180        UiObject title = mDevice.findObject(selector);
181        title.waitForExists(mTimeout);
182        return title;
183    }
184
185    public UiObject findDialogOkButton() {
186        UiObject object = findObject("android:id/content", "android:id/button1");
187        object.waitForExists(mTimeout);
188        return object;
189    }
190
191    public UiObject findDialogCancelButton() {
192        UiObject object = findObject("android:id/content", "android:id/button2");
193        object.waitForExists(mTimeout);
194        return object;
195    }
196
197    UiObject findMenuLabelWithName(String label) {
198        UiSelector selector = new UiSelector().text(label);
199        return mDevice.findObject(selector);
200    }
201
202    UiObject2 findMenuWithName(String label) {
203        List<UiObject2> menuItems = mDevice.findObjects(By.clazz("android.widget.LinearLayout"));
204        Iterator<UiObject2> it = menuItems.iterator();
205
206        UiObject2 menuItem = null;
207        while(it.hasNext()) {
208            menuItem = it.next();
209            UiObject2 text = menuItem.findObject(By.text(label));
210            if(text != null) {
211                break;
212            }
213        }
214        return menuItem;
215    }
216
217    UiObject findMenuMoreOptions() {
218        UiSelector selector = new UiSelector().className("android.widget.ImageButton")
219                .descriptionContains("More options");
220        //TODO: use the system string ? android.R.string.action_menu_overflow_description
221        return mDevice.findObject(selector);
222    }
223
224    public void pressKey(int keyCode) {
225        mDevice.pressKeyCode(keyCode);
226    }
227}
228