15e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay/*
25e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay * Copyright (C) 2015 The Android Open Source Project
35e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay *
45e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay * Licensed under the Apache License, Version 2.0 (the "License");
55e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay * you may not use this file except in compliance with the License.
65e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay * You may obtain a copy of the License at
75e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay *
85e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay *      http://www.apache.org/licenses/LICENSE-2.0
95e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay *
105e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay * Unless required by applicable law or agreed to in writing, software
115e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay * distributed under the License is distributed on an "AS IS" BASIS,
125e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay * See the License for the specific language governing permissions and
145e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay * limitations under the License.
155e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay */
165e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
175e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKaypackage com.android.documentsui.bots;
185e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
195e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKayimport static junit.framework.Assert.assertEquals;
205e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKayimport static junit.framework.Assert.assertNotNull;
215e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKayimport static junit.framework.Assert.assertTrue;
22743bc1befcf88af802a686c35a4c4afc961f616eSteve McKayimport static junit.framework.Assert.fail;
2328bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan
24fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Linimport android.app.UiAutomation;
255e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKayimport android.content.Context;
261273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Linimport android.graphics.Point;
2728bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tanimport android.graphics.Rect;
28fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Linimport android.os.SystemClock;
295e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKayimport android.support.test.uiautomator.By;
305e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKayimport android.support.test.uiautomator.BySelector;
315e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKayimport android.support.test.uiautomator.Configurator;
325e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKayimport android.support.test.uiautomator.UiDevice;
335e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKayimport android.support.test.uiautomator.UiObject;
345e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKayimport android.support.test.uiautomator.UiObject2;
355e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKayimport android.support.test.uiautomator.UiObjectNotFoundException;
365e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKayimport android.support.test.uiautomator.UiSelector;
375e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKayimport android.support.test.uiautomator.Until;
38fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Linimport android.view.InputDevice;
39515e51391722649479b6bac357fd363ea17de1f7Ben Linimport android.view.KeyEvent;
405e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKayimport android.view.MotionEvent;
4128bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tanimport android.view.View;
422f22afa82f4d3598a20ad3fa79fd11fd0c859e02Ben Linimport android.widget.ImageView;
435e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
445e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKayimport java.util.ArrayList;
455e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKayimport java.util.Arrays;
465e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKayimport java.util.List;
475e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKayimport java.util.regex.Pattern;
485e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
495e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay/**
505e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay * A test helper class that provides support for controlling directory list
515e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay * and making assertions against the state of it.
525e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay */
5385ec0d676297724f211213c7cb188839f1d3601bSteve McKaypublic class DirectoryListBot extends Bots.BaseBot {
5428bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan    private static final String DIR_CONTAINER_ID = "com.android.documentsui:id/container_directory";
555e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    private static final String DIR_LIST_ID = "com.android.documentsui:id/dir_list";
565e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
575e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    private static final BySelector SNACK_DELETE =
585e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay            By.desc(Pattern.compile("^Deleting [0-9]+ file.+"));
59fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin    private UiAutomation mAutomation;
605e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
61fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin    public DirectoryListBot(
62fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin            UiDevice device, UiAutomation automation, Context context, int timeout) {
635e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        super(device, context, timeout);
64fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin        mAutomation = automation;
655e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    }
665e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
675e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    public void assertDocumentsCount(int count) throws UiObjectNotFoundException {
685e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        UiObject docsList = findDocumentsList();
695e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        assertEquals(count, docsList.getChildCount());
705e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    }
715e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
725e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    public void assertDocumentsPresent(String... labels) throws UiObjectNotFoundException {
735e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        List<String> absent = new ArrayList<>();
745e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        for (String label : labels) {
755e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay            if (!findDocument(label).exists()) {
765e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay                absent.add(label);
775e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay            }
785e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        }
795e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        if (!absent.isEmpty()) {
80743bc1befcf88af802a686c35a4c4afc961f616eSteve McKay            fail("Expected documents " + Arrays.asList(labels)
815e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay                    + ", but missing " + absent);
825e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        }
835e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    }
845e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
855e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    public void assertDocumentsAbsent(String... labels) throws UiObjectNotFoundException {
865e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        List<String> found = new ArrayList<>();
875e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        for (String label : labels) {
885e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay            if (findDocument(label).exists()) {
895e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay                found.add(label);
905e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay            }
915e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        }
925e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        if (!found.isEmpty()) {
93743bc1befcf88af802a686c35a4c4afc961f616eSteve McKay            fail("Expected documents not present" + Arrays.asList(labels)
945e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay                    + ", but present " + found);
955e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        }
965e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    }
975e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
985e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    public void assertDocumentsCountOnList(boolean exists, int count) throws UiObjectNotFoundException {
995e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        UiObject docsList = findDocumentsList();
1005e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        assertEquals(exists, docsList.exists());
1015e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        if(docsList.exists()) {
1025e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay            assertEquals(count, docsList.getChildCount());
1035e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        }
1045e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    }
1055e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
10628bf1424fa8b38b61b0d55763dd03cb8f7685064Ben Lin    public void assertHeaderMessageText(String message) throws UiObjectNotFoundException {
10728bf1424fa8b38b61b0d55763dd03cb8f7685064Ben Lin        UiObject messageTextView = findHeaderMessageTextView();
1085e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        assertTrue(messageTextView.exists());
1095e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
1105e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        String msg = String.valueOf(message);
111f085da0311b87dc1d06975f3fdf48f26a37a2d14Ben Lin        assertEquals(msg, messageTextView.getText());
11228bf1424fa8b38b61b0d55763dd03cb8f7685064Ben Lin    }
11328bf1424fa8b38b61b0d55763dd03cb8f7685064Ben Lin
11428bf1424fa8b38b61b0d55763dd03cb8f7685064Ben Lin    /**
11528bf1424fa8b38b61b0d55763dd03cb8f7685064Ben Lin     * Checks against placeholder text. Placeholder can be Empty page, No results page, or the
11628bf1424fa8b38b61b0d55763dd03cb8f7685064Ben Lin     * "Hourglass" page (ie. something-went-wrong page).
11728bf1424fa8b38b61b0d55763dd03cb8f7685064Ben Lin     */
11828bf1424fa8b38b61b0d55763dd03cb8f7685064Ben Lin    public void assertPlaceholderMessageText(String message) throws UiObjectNotFoundException {
11928bf1424fa8b38b61b0d55763dd03cb8f7685064Ben Lin        UiObject messageTextView = findPlaceholderMessageTextView();
12028bf1424fa8b38b61b0d55763dd03cb8f7685064Ben Lin        assertTrue(messageTextView.exists());
12128bf1424fa8b38b61b0d55763dd03cb8f7685064Ben Lin
12228bf1424fa8b38b61b0d55763dd03cb8f7685064Ben Lin        String msg = String.valueOf(message);
12328bf1424fa8b38b61b0d55763dd03cb8f7685064Ben Lin        assertEquals(msg, messageTextView.getText());
12428bf1424fa8b38b61b0d55763dd03cb8f7685064Ben Lin
12528bf1424fa8b38b61b0d55763dd03cb8f7685064Ben Lin    }
1265e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
12728bf1424fa8b38b61b0d55763dd03cb8f7685064Ben Lin    private UiObject findHeaderMessageTextView() {
12828bf1424fa8b38b61b0d55763dd03cb8f7685064Ben Lin        return findObject(
12928bf1424fa8b38b61b0d55763dd03cb8f7685064Ben Lin                DIR_CONTAINER_ID,
13028bf1424fa8b38b61b0d55763dd03cb8f7685064Ben Lin                "com.android.documentsui:id/message_textview");
1315e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    }
1325e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
13328bf1424fa8b38b61b0d55763dd03cb8f7685064Ben Lin    private UiObject findPlaceholderMessageTextView() {
1345e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        return findObject(
13528bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan                DIR_CONTAINER_ID,
1365e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay                "com.android.documentsui:id/message");
1375e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    }
1385e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
1395e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    public void assertSnackbar(int id) {
1405e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        assertNotNull(getSnackbar(mContext.getString(id)));
1415e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    }
1425e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
1435e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    public void openDocument(String label) throws UiObjectNotFoundException {
1445e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        int toolType = Configurator.getInstance().getToolType();
1455e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_FINGER);
1465e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        UiObject doc = findDocument(label);
1475e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        doc.click();
1485e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        Configurator.getInstance().setToolType(toolType);
1495e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    }
1505e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
1515e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    public void clickDocument(String label) throws UiObjectNotFoundException {
1525e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        findDocument(label).click();
1535e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    }
1545e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
155e5edf047145c916ac5b4696ddd7f14f5c2dbaf40Ben Lin    /**
156e5edf047145c916ac5b4696ddd7f14f5c2dbaf40Ben Lin     * @param label The filename of the document
157e5edf047145c916ac5b4696ddd7f14f5c2dbaf40Ben Lin     * @param number Which nth document it is. The number corresponding to "n selected"
158e5edf047145c916ac5b4696ddd7f14f5c2dbaf40Ben Lin     */
159e5edf047145c916ac5b4696ddd7f14f5c2dbaf40Ben Lin    public void selectDocument(String label, int number) throws UiObjectNotFoundException {
16085ec0d676297724f211213c7cb188839f1d3601bSteve McKay        waitForDocument(label);
1612f22afa82f4d3598a20ad3fa79fd11fd0c859e02Ben Lin        UiObject2 selectionHotspot = findSelectionHotspot(label);
1622f22afa82f4d3598a20ad3fa79fd11fd0c859e02Ben Lin        selectionHotspot.click();
163e5edf047145c916ac5b4696ddd7f14f5c2dbaf40Ben Lin
164e5edf047145c916ac5b4696ddd7f14f5c2dbaf40Ben Lin        // wait until selection is fully done to avoid future click being registered as double
165e5edf047145c916ac5b4696ddd7f14f5c2dbaf40Ben Lin        // clicking
166e5edf047145c916ac5b4696ddd7f14f5c2dbaf40Ben Lin        assertSelection(number);
1672f22afa82f4d3598a20ad3fa79fd11fd0c859e02Ben Lin    }
1682f22afa82f4d3598a20ad3fa79fd11fd0c859e02Ben Lin
1692f22afa82f4d3598a20ad3fa79fd11fd0c859e02Ben Lin    public UiObject2 findSelectionHotspot(String label) {
1702f22afa82f4d3598a20ad3fa79fd11fd0c859e02Ben Lin        final BySelector list = By.res(DIR_LIST_ID);
1712f22afa82f4d3598a20ad3fa79fd11fd0c859e02Ben Lin
1722f22afa82f4d3598a20ad3fa79fd11fd0c859e02Ben Lin        BySelector selector = By.hasChild(By.text(label));
1732f22afa82f4d3598a20ad3fa79fd11fd0c859e02Ben Lin        UiObject2 parent = mDevice.findObject(list).findObject(selector);
1742f22afa82f4d3598a20ad3fa79fd11fd0c859e02Ben Lin        if (parent.getClassName().equals("android.widget.LinearLayout")) {
1752f22afa82f4d3598a20ad3fa79fd11fd0c859e02Ben Lin            // For list mode, the parent of the textView does not contain the selector icon, but the
1762f22afa82f4d3598a20ad3fa79fd11fd0c859e02Ben Lin            // grandparent of the textView does
1772f22afa82f4d3598a20ad3fa79fd11fd0c859e02Ben Lin            // Gotta go one more level up
1782f22afa82f4d3598a20ad3fa79fd11fd0c859e02Ben Lin            selector = By.hasDescendant(By.text(label).depth(2));
1792f22afa82f4d3598a20ad3fa79fd11fd0c859e02Ben Lin            parent = mDevice.findObject(list).findObject(selector);
1802f22afa82f4d3598a20ad3fa79fd11fd0c859e02Ben Lin        }
1812f22afa82f4d3598a20ad3fa79fd11fd0c859e02Ben Lin        return parent.findObject(By.clazz(ImageView.class));
1825e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    }
1835e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
184515e51391722649479b6bac357fd363ea17de1f7Ben Lin    public void copyFilesToClipboard(String...labels) throws UiObjectNotFoundException {
185515e51391722649479b6bac357fd363ea17de1f7Ben Lin        for (String label: labels) {
186515e51391722649479b6bac357fd363ea17de1f7Ben Lin            clickDocument(label);
187515e51391722649479b6bac357fd363ea17de1f7Ben Lin        }
188515e51391722649479b6bac357fd363ea17de1f7Ben Lin        mDevice.pressKeyCode(KeyEvent.KEYCODE_C, KeyEvent.META_CTRL_ON);
189515e51391722649479b6bac357fd363ea17de1f7Ben Lin    }
190515e51391722649479b6bac357fd363ea17de1f7Ben Lin
191515e51391722649479b6bac357fd363ea17de1f7Ben Lin    public void pasteFilesFromClipboard() {
192515e51391722649479b6bac357fd363ea17de1f7Ben Lin        mDevice.pressKeyCode(KeyEvent.KEYCODE_V, KeyEvent.META_CTRL_ON);
193515e51391722649479b6bac357fd363ea17de1f7Ben Lin    }
194515e51391722649479b6bac357fd363ea17de1f7Ben Lin
1955e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    public UiObject2 getSnackbar(String message) {
1965e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        return mDevice.wait(Until.findObject(By.text(message)), mTimeout);
1975e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    }
1985e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
1995e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    public void waitForDeleteSnackbar() {
2005e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        mDevice.wait(Until.findObject(SNACK_DELETE), mTimeout);
2015e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    }
2025e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
2035e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    public void waitForDeleteSnackbarGone() {
2045e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        // wait a little longer for snackbar to go away, as it disappears after a timeout.
2055e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        mDevice.wait(Until.gone(SNACK_DELETE), mTimeout * 2);
2065e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    }
2075e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
2085e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    public void waitForDocument(String label) throws UiObjectNotFoundException {
2095e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        findDocument(label).waitForExists(mTimeout);
2105e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    }
2115e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
2125e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    public UiObject findDocument(String label) throws UiObjectNotFoundException {
2135e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        final UiSelector docList = new UiSelector().resourceId(
21428bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan                DIR_CONTAINER_ID).childSelector(
2155e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay                        new UiSelector().resourceId(DIR_LIST_ID));
2165e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
2175e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        // Wait for the first list item to appear
2185e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        new UiObject(docList.childSelector(new UiSelector())).waitForExists(mTimeout);
2195e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
2205e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        // new UiScrollable(docList).scrollIntoView(new UiSelector().text(label));
2215e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        return mDevice.findObject(docList.childSelector(new UiSelector().text(label)));
2225e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    }
2235e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
2245e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    public boolean hasDocuments(String... labels) throws UiObjectNotFoundException {
2255e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        for (String label : labels) {
2265e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay            if (!findDocument(label).exists()) {
2275e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay                return false;
2285e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay            }
2295e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        }
2305e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        return true;
2315e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    }
2325e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
23326819d7d31185b15db93e614766d7b717b98e97fBen Lin    public void assertFirstDocumentHasFocus() throws UiObjectNotFoundException {
23426819d7d31185b15db93e614766d7b717b98e97fBen Lin        final UiSelector docList = new UiSelector().resourceId(
23528bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan                DIR_CONTAINER_ID).childSelector(
23626819d7d31185b15db93e614766d7b717b98e97fBen Lin                        new UiSelector().resourceId(DIR_LIST_ID));
23726819d7d31185b15db93e614766d7b717b98e97fBen Lin
23826819d7d31185b15db93e614766d7b717b98e97fBen Lin        // Wait for the first list item to appear
23926819d7d31185b15db93e614766d7b717b98e97fBen Lin        UiObject doc = new UiObject(docList.childSelector(new UiSelector()));
24026819d7d31185b15db93e614766d7b717b98e97fBen Lin        doc.waitForExists(mTimeout);
24126819d7d31185b15db93e614766d7b717b98e97fBen Lin
24226819d7d31185b15db93e614766d7b717b98e97fBen Lin        assertTrue(doc.isFocused());
24326819d7d31185b15db93e614766d7b717b98e97fBen Lin    }
24426819d7d31185b15db93e614766d7b717b98e97fBen Lin
245811777ecf0bc23cb698dad6d9e29a5956a6f54ecTomasz Mikolajewski    public UiObject findDocumentsList() {
2465e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        return findObject(
24728bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan                DIR_CONTAINER_ID,
2485e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay                DIR_LIST_ID);
2495e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    }
2505e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay
2515e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    public void assertHasFocus() {
2525e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay        assertHasFocus(DIR_LIST_ID);
2535e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay    }
25428bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan
2551273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin    public void assertSelection(int numSelected) {
2561273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin        String assertSelectionText = numSelected + " selected";
2571273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin        UiObject2 selectionText = mDevice.wait(
2581273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin                Until.findObject(By.text(assertSelectionText)), mTimeout);
2591273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin        assertTrue(selectionText != null);
2601273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin    }
2611273913f4ac0a272150be6c6bd7b0c6760e531b8Ben Lin
26228bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan    public void assertOrder(String[] dirs, String[] files) throws UiObjectNotFoundException {
26328bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan        for (int i = 0; i < dirs.length - 1; ++i) {
26428bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan            assertOrder(dirs[i], dirs[i + 1]);
26528bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan        }
26628bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan
26728bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan        if (dirs.length > 0 && files.length > 0) {
26828bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan            assertOrder(dirs[dirs.length - 1], files[0]);
26928bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan        }
27028bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan
27128bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan        for (int i = 0; i < files.length - 1; ++i) {
27228bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan            assertOrder(files[i], files[i + 1]);
27328bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan        }
27428bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan    }
27528bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan
276fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin    public void rightClickDocument(String label) throws UiObjectNotFoundException {
277fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin        Rect startCoord = findDocument(label).getBounds();
278fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin        rightClickDocument(new Point(startCoord.centerX(), startCoord.centerY()));
279fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin    }
280fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin
281fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin    public void rightClickDocument(Point point) throws UiObjectNotFoundException {
282fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin        //TODO: Use Espresso instead of doing the events mock ourselves
283fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin        MotionEvent motionDown = getTestMotionEvent(
284fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                MotionEvent.ACTION_DOWN,
285fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                MotionEvent.BUTTON_SECONDARY,
286fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                MotionEvent.TOOL_TYPE_MOUSE,
287fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                InputDevice.SOURCE_MOUSE,
288fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                point.x,
289fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                point.y);
290fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin        mAutomation.injectInputEvent(motionDown, true);
291fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin        SystemClock.sleep(100);
292fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin
293fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin        MotionEvent motionUp = getTestMotionEvent(
294fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                MotionEvent.ACTION_UP,
295fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                MotionEvent.BUTTON_SECONDARY,
296fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                MotionEvent.TOOL_TYPE_MOUSE,
297fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                InputDevice.SOURCE_MOUSE,
298fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                point.x,
299fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                point.y);
300fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin
301fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin        mAutomation.injectInputEvent(motionUp, true);
302fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin    }
303fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin
304fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin    private MotionEvent getTestMotionEvent(
305fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin            int action, int buttonState, int toolType, int source, int x, int y) {
306fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin        long eventTime = SystemClock.uptimeMillis();
307fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin
308fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin        MotionEvent.PointerProperties[] pp = {new MotionEvent.PointerProperties()};
309fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin        pp[0].clear();
310fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin        pp[0].id = 0;
311fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin        pp[0].toolType = toolType;
312fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin
313fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin        MotionEvent.PointerCoords[] pointerCoords = {new MotionEvent.PointerCoords()};
314fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin        pointerCoords[0].clear();
315fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin        pointerCoords[0].x = x;
316fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin        pointerCoords[0].y = y;
317fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin        pointerCoords[0].pressure = 0;
318fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin        pointerCoords[0].size = 1;
319fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin
320fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin        return MotionEvent.obtain(
321fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                eventTime,
322fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                eventTime,
323fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                action,
324fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                1,
325fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                pp,
326fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                pointerCoords,
327fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                0,
328fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                buttonState,
329fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                1f,
330fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                1f,
331fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                0,
332fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                0,
333fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                source,
334fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin                0);
335fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin    }
336fa1e1360999a7ca6d78eb1a2fd560196fe0b4dbbBen Lin
33728bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan    private void assertOrder(String first, String second) throws UiObjectNotFoundException {
33828bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan
33928bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan        final UiObject firstObj = findDocument(first);
34028bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan        final UiObject secondObj = findDocument(second);
34128bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan
34228bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan        final int layoutDirection = mContext.getResources().getConfiguration().getLayoutDirection();
34328bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan        final Rect firstBound = firstObj.getVisibleBounds();
34428bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan        final Rect secondBound = secondObj.getVisibleBounds();
34528bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan        if (layoutDirection == View.LAYOUT_DIRECTION_LTR) {
34628bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan            assertTrue(
34728bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan                    "\"" + first + "\" is not located above or to the left of \"" + second
34828bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan                            + "\" in LTR",
34928bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan                    firstBound.bottom < secondBound.top || firstBound.right < secondBound.left);
35028bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan        } else {
35128bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan            assertTrue(
35228bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan                    "\"" + first + "\" is not located above or to the right of \"" + second +
35328bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan                            "\" in RTL",
35428bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan                    firstBound.bottom < secondBound.top || firstBound.left > secondBound.right);
35528bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan        }
35628bbd8defa2ad532d9ff1bc4d8a9ae01b574a067Garfield, Tan    }
3575e1acc9f71ca4c2a07ba64ec9c050b20303993e2Steve McKay}
358