From f8966bf324562a8017d164161ccbadc150a23ccc Mon Sep 17 00:00:00 2001 From: Michael McGeagh Date: Wed, 28 Sep 2016 18:05:04 +0100 Subject: [PATCH] GooglePlayBooks: Code tidy to conform. Additional parameter added for when the search name differs to the library name. Defaults changed due to Hamlet dissapearing from the store. Added cornercase of accidentially triggering a card popup New version includes a new dialogue and name for search. Added support for this If device has more than one account associated, a new popup appears. Handle that with an optional parameter listing specific account to select, if ommitted the first account in the list is selected --- wlauto/workloads/googleplaybooks/__init__.py | 42 +- .../com.arm.wlauto.uiauto.googleplaybooks.jar | Bin 16296 -> 16656 bytes .../com/arm/wlauto/uiauto/UiAutomation.java | 394 ++++++++++-------- 3 files changed, 256 insertions(+), 180 deletions(-) diff --git a/wlauto/workloads/googleplaybooks/__init__.py b/wlauto/workloads/googleplaybooks/__init__.py index 5d26ed87..1f3d50d8 100755 --- a/wlauto/workloads/googleplaybooks/__init__.py +++ b/wlauto/workloads/googleplaybooks/__init__.py @@ -14,7 +14,6 @@ # from wlauto import AndroidUxPerfWorkload, Parameter -from wlauto.exceptions import DeviceError class Googleplaybooks(AndroidUxPerfWorkload): @@ -27,7 +26,7 @@ class Googleplaybooks(AndroidUxPerfWorkload): package + '/com.android.vending/com.google.android.finsky.activities.MainActivity', package + '/com.google.android.apps.books.app.ReadingActivity', package + '/com.google.android.apps.books.app.TableOfContentsActivityLight'] - description = """ + description = ''' A workload to perform standard productivity tasks with googleplaybooks. This workload performs various tasks, such as searching for a book title online, browsing through a book, adding and removing notes, word searching, @@ -48,32 +47,57 @@ class Googleplaybooks(AndroidUxPerfWorkload): 12. Switches page styles from 'Day' to 'Night' to 'Sepia' and back to 'Day' 13. Uses the 'About this book' facility on the currently selected book - NOTE: This workload requires a network connection (ideally, wifi) to run - and a Google account to be setup on the device. - """ + NOTE: This workload requires a network connection (ideally, wifi) to run, + a Google account to be setup on the device, and payment details for the account. + Free books require payment details to have been setup otherwise it fails. + Tip: Install the 'Google Opinion Rewards' app to bypass the need to enter valid + card/bank detail. + ''' parameters = [ - Parameter('search_book_title', kind=str, mandatory=False, default="Hamlet", + Parameter('search_book_title', kind=str, default='Nikola Tesla: Imagination and the Man That Invented the 20th Century', description=""" The book title to search for within Google Play Books archive. The book must either be already in the account's library, or free to purchase. """), - Parameter('select_chapter_page_number', kind=int, mandatory=False, default=22, + Parameter('library_book_title', kind=str, default='Nikola Tesla', + description=""" + The book title to search for within My Library. + The Library name can differ (usually shorter) to the Store name. + If left blank, the ``search_book_title`` will be used. + """), + Parameter('select_chapter_page_number', kind=int, default=4, description=""" The Page Number to search for within a selected book's Chapter list. Note: Accepts integers only. """), - Parameter('search_word', kind=str, mandatory=False, default='the', + Parameter('search_word', kind=str, default='the', description=""" The word to search for within a selected book. Note: Accepts single words only. """), + Parameter('account', kind=str, mandatory=False, + description=""" + If you are running this workload on a device which has more than one + Google account setup, then this parameter is used to select which account + to select when prompted. + The account requires the book to have already been purchased or payment details + already associated with the account. + If omitted, the first account in the list will be selected if prompted. + """), ] + # This workload relies on the internet so check that there is a working + # internet connection requires_network = True def validate(self): super(Googleplaybooks, self).validate() - self.uiauto_params['book_title'] = self.search_book_title.replace(" ", "0space0") + self.uiauto_params['search_book_title'] = self.search_book_title.replace(' ', '0space0') + # If library_book_title is blank, set it to the same as search_book_title + if not self.library_book_title: # pylint: disable=access-member-before-definition + self.library_book_title = self.search_book_title # pylint: disable=attribute-defined-outside-init + self.uiauto_params['library_book_title'] = self.library_book_title.replace(' ', '0space0') self.uiauto_params['chapter_page_number'] = self.select_chapter_page_number self.uiauto_params['search_word'] = self.search_word + self.uiauto_params['account'] = self.account diff --git a/wlauto/workloads/googleplaybooks/com.arm.wlauto.uiauto.googleplaybooks.jar b/wlauto/workloads/googleplaybooks/com.arm.wlauto.uiauto.googleplaybooks.jar index fd9abfbea664a5f0a20fbf9b04d50d9afb5e2a97..9166516c88121f5a105630bad4fd943c19db2c22 100644 GIT binary patch delta 16468 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 delta 16105 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 diff --git a/wlauto/workloads/googleplaybooks/uiauto/src/com/arm/wlauto/uiauto/UiAutomation.java b/wlauto/workloads/googleplaybooks/uiauto/src/com/arm/wlauto/uiauto/UiAutomation.java index 31236433..0303cd3e 100755 --- a/wlauto/workloads/googleplaybooks/uiauto/src/com/arm/wlauto/uiauto/UiAutomation.java +++ b/wlauto/workloads/googleplaybooks/uiauto/src/com/arm/wlauto/uiauto/UiAutomation.java @@ -26,13 +26,18 @@ import com.android.uiautomator.core.UiScrollable; import com.arm.wlauto.uiauto.UxPerfUiAutomation; +import static com.arm.wlauto.uiauto.BaseUiAutomation.FindByCriteria.BY_ID; +import static com.arm.wlauto.uiauto.BaseUiAutomation.FindByCriteria.BY_TEXT; +import static com.arm.wlauto.uiauto.BaseUiAutomation.FindByCriteria.BY_DESC; + import java.util.concurrent.TimeUnit; import java.util.LinkedHashMap; import java.util.Iterator; -import android.util.Log; import java.util.Map; import java.util.Map.Entry; +import android.util.Log; + public class UiAutomation extends UxPerfUiAutomation { protected Bundle parameters; @@ -43,25 +48,31 @@ public class UiAutomation extends UxPerfUiAutomation { private long viewTimeout = TimeUnit.SECONDS.toMillis(viewTimeoutSecs); public void runUiAutomation() throws Exception { + // Override superclass value this.uiAutoTimeout = TimeUnit.SECONDS.toMillis(8); parameters = getParams(); packageName = parameters.getString("package"); packageID = packageName + ":id/"; - String bookTitle = parameters.getString("book_title").replace("0space0", " "); + String searchBookTitle = parameters.getString("search_book_title").replace("0space0", " "); + String libraryBookTitle = parameters.getString("library_book_title").replace("0space0", " "); String chapterPageNumber = parameters.getString("chapter_page_number"); String searchWord = parameters.getString("search_word"); String noteText = "This is a test note"; + String account = parameters.getString("account"); setScreenOrientation(ScreenOrientation.NATURAL); + + chooseAccount(account); clearFirstRunDialogues(); + dismissSendBooksAsGiftsDialog(); dismissSync(); - searchForBook(bookTitle); + searchForBook(searchBookTitle); addToLibrary(); openMyLibrary(); - openBook(bookTitle); + openBook(libraryBookTitle); UiWatcher pageSyncPopUpWatcher = createPopUpWatcher(); registerWatcher("pageSyncPopUp", pageSyncPopUpWatcher); @@ -77,45 +88,38 @@ public class UiAutomation extends UxPerfUiAutomation { removeWatcher("pageSyncPop"); pressBack(); + unsetScreenOrientation(); } - // Creates a watcher for when a pop up warning appears when pages are out - // of sync across multiple devices. - private UiWatcher createPopUpWatcher() throws Exception { - UiWatcher pageSyncPopUpWatcher = new UiWatcher() { - - @Override - public boolean checkForCondition() { - UiObject popUpDialogue = - new UiObject(new UiSelector().resourceId("android:id/message") - .textStartsWith("You're on page")); - - // Don't sync and stay on the current page - if (popUpDialogue.exists()) { - try { - UiObject stayOnPage = new UiObject(new UiSelector() - .className("android.widget.Button") - .text("Yes")); - stayOnPage.click(); - } catch (UiObjectNotFoundException e) { - e.printStackTrace(); - } - return popUpDialogue.waitUntilGone(viewTimeout); + // If the device has more than one account setup, a prompt appears + // In this case, select the first account in the list, unless `account` + // has been specified as a parameter, otherwise select `account`. + private void chooseAccount(String account) throws Exception { + UiObject accountPopup = + new UiObject(new UiSelector().textContains("Choose an account") + .className("android.widget.TextView")); + if (accountPopup.exists()) { + if ("None".equals(account)) { + // If no account has been specified, pick the first entry in the list + UiObject list = + new UiObject(new UiSelector().className("android.widget.ListView")); + UiObject first = list.getChild(new UiSelector().index(0)); + if (!first.exists()) { + // Some devices are not zero indexed. If 0 doesnt exist, pick 1 + first = list.getChild(new UiSelector().index(1)); } - return false; + first.click(); + } else { + // Account specified, select that + clickUiObject(BY_TEXT, account, "android.widget.CheckedTextView"); } - }; - - return pageSyncPopUpWatcher; - } - - private void dismissSync() throws Exception { - UiObject keepSyncOff = - new UiObject(new UiSelector().textContains("Keep sync off") - .className("android.widget.Button")); - if (keepSyncOff.exists()) { - keepSyncOff.click(); + // Click OK to proceed + UiObject ok = + new UiObject(new UiSelector().textContains("OK") + .className("android.widget.Button") + .enabled(true)); + ok.clickAndWaitForNewWindow(); } } @@ -125,7 +129,6 @@ public class UiAutomation extends UxPerfUiAutomation { private void clearFirstRunDialogues() throws Exception { UiObject startButton = new UiObject(new UiSelector().resourceId(packageID + "start_button")); - // First try and skip the sample book selection if (startButton.exists()) { startButton.click(); @@ -133,7 +136,6 @@ public class UiAutomation extends UxPerfUiAutomation { UiObject endButton = new UiObject(new UiSelector().resourceId(packageID + "end_button")); - // Click next button if it exists if (endButton.exists()) { endButton.click(); @@ -148,49 +150,65 @@ public class UiAutomation extends UxPerfUiAutomation { } } + private void dismissSendBooksAsGiftsDialog() throws Exception { + UiObject gotIt = + new UiObject(new UiSelector().textContains("GOT IT!")); + if (gotIt.exists()) { + gotIt.click(); + } + } + + private void dismissSync() throws Exception { + UiObject keepSyncOff = + new UiObject(new UiSelector().textContains("Keep sync off") + .className("android.widget.Button")); + if (keepSyncOff.exists()) { + keepSyncOff.click(); + } + } + // Searches for a "free" or "purchased" book title in Google play private void searchForBook(final String bookTitle) throws Exception { UiObject search = new UiObject(new UiSelector().resourceId(packageID + "menu_search")); + if (!search.exists()) { + search = + new UiObject(new UiSelector().resourceId(packageID + "search_box_active_text_view")); + } search.click(); - UiObject searchText = new UiObject(new UiSelector().textContains("Search") - .className("android.widget.EditText")); + UiObject searchText = + new UiObject(new UiSelector().textContains("Search") + .className("android.widget.EditText")); searchText.setText(bookTitle); pressEnter(); UiObject resultList = new UiObject(new UiSelector().resourceId("com.android.vending:id/search_results_list")); - if (!resultList.waitForExists(viewTimeout)) { throw new UiObjectNotFoundException("Could not find \"search results list view\"."); } - String desc = String.format("Book: " + bookTitle); - // Create a selector so that we can search for siblings of the desired // book that contains a "free" or "purchased" book identifier - UiSelector bookSelector = new UiSelector().description(desc).className("android.widget.TextView"); - UiObject label = - new UiObject(new UiSelector().fromParent(bookSelector) + new UiObject(new UiSelector().fromParent(new UiSelector() + .description(String.format("Book: " + bookTitle)) + .className("android.widget.TextView")) .resourceId("com.android.vending:id/li_label") .descriptionMatches("^(Purchased|Free)$")); - UiScrollable searchResultsList = - new UiScrollable(new UiSelector().resourceId("com.android.vending:id/search_results_list")); - final int maxSearchTime = 30; int searchTime = maxSearchTime; while (!label.exists()) { - if (searchTime <= 0) { - throw new UiObjectNotFoundException( - "Exceeded maximum search time (" + maxSearchTime + " seconds) to find book \"" + bookTitle + "\""); - } else { + if (searchTime > 0) { uiDeviceSwipeDown(100); sleep(1); searchTime--; + } else { + throw new UiObjectNotFoundException( + "Exceeded maximum search time (" + maxSearchTime + " seconds) to find book \"" + bookTitle + "\""); } } @@ -200,18 +218,22 @@ public class UiAutomation extends UxPerfUiAutomation { } private void addToLibrary() throws Exception { - UiObject add = new UiObject(new UiSelector().textContains("ADD TO LIBRARY") - .className("android.widget.Button")); + UiObject add = + new UiObject(new UiSelector().textContains("ADD TO LIBRARY") + .className("android.widget.Button")); if (add.exists()) { - add.click(); // add to My Library and opens book by default + // add to My Library and opens book by default + add.click(); + clickUiObject(BY_TEXT, "BUY", "android.widget.Button", true); } else { - UiObject read = getUiObjectByText("READ", "android.widget.Button"); - read.click(); // opens book + // opens book + clickUiObject(BY_TEXT, "READ", "android.widget.Button"); } waitForPage(); - UiObject navigationButton = new UiObject(new UiSelector().description("Navigate up")); + UiObject navigationButton = + new UiObject(new UiSelector().description("Navigate up")); // Return to main app window pressBack(); @@ -227,15 +249,14 @@ public class UiAutomation extends UxPerfUiAutomation { private void openMyLibrary() throws Exception { String testTag = "open_library"; ActionLogger logger = new ActionLogger(testTag, parameters); + logger.start(); - - UiObject openDrawer = getUiObjectByDescription("Show navigation drawer"); - openDrawer.click(); - + clickUiObject(BY_DESC, "Show navigation drawer"); // To correctly find the UiObject we need to specify the index also here UiObject myLibrary = new UiObject(new UiSelector().className("android.widget.TextView") - .text("My library").index(3)); + .text("My library") + .index(3)); myLibrary.clickAndWaitForNewWindow(uiAutoTimeout); logger.stop(); } @@ -244,31 +265,24 @@ public class UiAutomation extends UxPerfUiAutomation { String testTag = "open_book"; ActionLogger logger = new ActionLogger(testTag, parameters); - UiScrollable cardsGrid = - new UiScrollable(new UiSelector().resourceId(packageID + "cards_grid")); + long maxWaitTimeSeconds = 120; + long maxWaitTime = TimeUnit.SECONDS.toMillis(maxWaitTimeSeconds); - UiSelector bookSelector = new UiSelector().text(bookTitle).className("android.widget.TextView"); + UiSelector bookSelector = + new UiSelector().text(bookTitle) + .className("android.widget.TextView"); UiObject book = new UiObject(bookSelector); - // Check that books are sorted by time added to library. This way we // can assume any newly downloaded books will be visible on the first // screen. - UiObject menuSort = - getUiObjectByResourceId(packageID + "menu_sort", "android.widget.TextView"); - menuSort.click(); - - UiObject sortByRecent = getUiObjectByText("Recent", "android.widget.TextView"); - sortByRecent.click(); - + clickUiObject(BY_ID, packageID + "menu_sort", "android.widget.TextView"); + clickUiObject(BY_TEXT, "Recent", "android.widget.TextView"); // When the book is first added to library it may not appear in // cardsGrid until it has been fully downloaded. Wait for fully // downloaded books UiObject downloadComplete = - new UiObject(new UiSelector().fromParent(bookSelector).description("100% downloaded")); - - long maxWaitTimeSeconds = 120; - long maxWaitTime = TimeUnit.SECONDS.toMillis(maxWaitTimeSeconds); - + new UiObject(new UiSelector().fromParent(bookSelector) + .description("100% downloaded")); if (!downloadComplete.waitForExists(maxWaitTime)) { throw new UiObjectNotFoundException( "Exceeded maximum wait time (" + maxWaitTimeSeconds + " seconds) to download book \"" + bookTitle + "\""); @@ -280,6 +294,50 @@ public class UiAutomation extends UxPerfUiAutomation { logger.stop(); } + // Creates a watcher for when a pop up warning appears when pages are out + // of sync across multiple devices. + private UiWatcher createPopUpWatcher() throws Exception { + UiWatcher pageSyncPopUpWatcher = new UiWatcher() { + @Override + public boolean checkForCondition() { + UiObject popUpDialogue = + new UiObject(new UiSelector().textStartsWith("You're on page") + .resourceId("android:id/message")); + // Don't sync and stay on the current page + if (popUpDialogue.exists()) { + try { + UiObject stayOnPage = + new UiObject(new UiSelector().text("Yes") + .className("android.widget.Button")); + stayOnPage.click(); + } catch (UiObjectNotFoundException e) { + e.printStackTrace(); + } + return popUpDialogue.waitUntilGone(viewTimeout); + } + return false; + } + }; + return pageSyncPopUpWatcher; + } + + private void selectChapter(final String chapterPageNumber) throws Exception { + getDropdownMenu(); + + UiObject contents = getUiObjectByResourceId(packageID + "menu_reader_toc"); + contents.clickAndWaitForNewWindow(uiAutoTimeout); + UiObject toChapterView = getUiObjectByResourceId(packageID + "toc_list_view", + "android.widget.ExpandableListView"); + // Navigate to top of chapter view + searchPage(toChapterView, "1", Direction.UP, 10); + // Search for chapter page number + UiObject page = searchPage(toChapterView, chapterPageNumber, Direction.DOWN, 10); + // Go to the page + page.clickAndWaitForNewWindow(viewTimeout); + + waitForPage(); + } + private void gesturesTest() throws Exception { String testTag = "gesture"; @@ -324,72 +382,28 @@ public class UiAutomation extends UxPerfUiAutomation { waitForPage(); } - private UiObject searchPage(final UiObject view, final String pagenum, final Direction updown, - final int attempts) throws Exception { - if (attempts <= 0) { - throw new UiObjectNotFoundException("Could not find \"page number\" after several attempts."); - } - - String search = String.format("page " + pagenum); - UiObject page = new UiObject(new UiSelector().description(search) - .className("android.widget.TextView")); - if (!page.exists()) { - // Scroll up by swiping down - if (updown == Direction.UP) { - view.swipeDown(200); - // Default case is to scroll down (swipe up) - } else { - view.swipeUp(200); - } - searchPage(view, pagenum, updown, attempts - 1); - } - return page; - } - - private void selectChapter(final String chapterPageNumber) throws Exception { - getDropdownMenu(); - - UiObject contents = getUiObjectByResourceId(packageID + "menu_reader_toc"); - contents.clickAndWaitForNewWindow(uiAutoTimeout); - - UiObject toChapterView = getUiObjectByResourceId(packageID + "toc_list_view", - "android.widget.ExpandableListView"); - - // Navigate to top of chapter view - searchPage(toChapterView, "1", Direction.UP, 10); - - UiObject page = searchPage(toChapterView, chapterPageNumber, Direction.DOWN, 10); - - page.clickAndWaitForNewWindow(viewTimeout); - - waitForPage(); - } - private void addNote(final String text) throws Exception { String testTag = "note_add"; ActionLogger logger = new ActionLogger(testTag, parameters); hideDropDownMenu(); + UiObject clickable = new UiObject(new UiSelector().longClickable(true)); + logger.start(); - UiObject clickable = new UiObject(new UiSelector().longClickable(true)); uiObjectPerformLongClick(clickable, 100); - UiObject addNoteButton = new UiObject( - new UiSelector().resourceId(packageID + "add_note_button")); + UiObject addNoteButton = + new UiObject(new UiSelector().resourceId(packageID + "add_note_button")); addNoteButton.click(); UiObject noteEditText = getUiObjectByResourceId(packageID + "note_edit_text", "android.widget.EditText"); noteEditText.setText(text); - UiObject noteMenuButton = getUiObjectByResourceId(packageID + "note_menu_button", - "android.widget.ImageButton"); - noteMenuButton.click(); - - UiObject saveButton = getUiObjectByText("Save", "android.widget.TextView"); - saveButton.click(); + clickUiObject(BY_ID, packageID + "note_menu_button", "android.widget.ImageButton"); + clickUiObject(BY_TEXT, "Save", "android.widget.TextView"); logger.stop(); @@ -399,17 +413,18 @@ public class UiAutomation extends UxPerfUiAutomation { private void removeNote() throws Exception { String testTag = "note_remove"; ActionLogger logger = new ActionLogger(testTag, parameters); - logger.start(); UiObject clickable = new UiObject(new UiSelector().longClickable(true)); + + logger.start(); + uiObjectPerformLongClick(clickable, 100); - UiObject removeButton = new UiObject( - new UiSelector().resourceId(packageID + "remove_highlight_button")); + UiObject removeButton = + new UiObject(new UiSelector().resourceId(packageID + "remove_highlight_button")); removeButton.click(); - UiObject confirmRemove = getUiObjectByText("Remove", "android.widget.Button"); - confirmRemove.click(); + clickUiObject(BY_TEXT, "Remove", "android.widget.Button"); logger.stop(); @@ -420,26 +435,25 @@ public class UiAutomation extends UxPerfUiAutomation { String testTag = "search_word"; ActionLogger logger = new ActionLogger(testTag, parameters); + // Allow extra time for search queries involing high freqency words + final long searchTimeout = TimeUnit.SECONDS.toMillis(20); + getDropdownMenu(); - UiObject search = new UiObject( - new UiSelector().resourceId(packageID + "menu_search")); + UiObject search = + new UiObject(new UiSelector().resourceId(packageID + "menu_search")); search.click(); - UiObject searchText = new UiObject( - new UiSelector().resourceId(packageID + "search_src_text")); + UiObject searchText = + new UiObject(new UiSelector().resourceId(packageID + "search_src_text")); logger.start(); searchText.setText(text); pressEnter(); - UiObject resultList = new UiObject( - new UiSelector().resourceId(packageID + "search_results_list")); - - // Allow extra time for search queries involing high freqency words - final long searchTimeout = TimeUnit.SECONDS.toMillis(20); - + UiObject resultList = + new UiObject(new UiSelector().resourceId(packageID + "search_results_list")); if (!resultList.waitForExists(searchTimeout)) { throw new UiObjectNotFoundException("Could not find \"search results list view\"."); } @@ -447,7 +461,6 @@ public class UiAutomation extends UxPerfUiAutomation { UiObject searchWeb = new UiObject(new UiSelector().text("Search web") .className("android.widget.TextView")); - if (!searchWeb.waitForExists(searchTimeout)) { throw new UiObjectNotFoundException("Could not find \"Search web view\"."); } @@ -461,27 +474,27 @@ public class UiAutomation extends UxPerfUiAutomation { String testTag = "style"; getDropdownMenu(); - UiObject readerSettings = getUiObjectByResourceId(packageID + "menu_reader_settings", - "android.widget.TextView"); - readerSettings.click(); + + clickUiObject(BY_ID, packageID + "menu_reader_settings", "android.widget.TextView"); // Check for lighting option button on newer versions UiObject lightingOptionsButton = new UiObject(new UiSelector().resourceId(packageID + "lighting_options_button")); - if (lightingOptionsButton.exists()) { lightingOptionsButton.click(); } String[] styles = {"Night", "Sepia", "Day"}; - for (String style : styles) { try { ActionLogger logger = new ActionLogger(testTag + "_" + style, parameters); - UiObject pageStyle = new UiObject(new UiSelector().description(style)); + UiObject pageStyle = + new UiObject(new UiSelector().description(style)); + logger.start(); pageStyle.clickAndWaitForNewWindow(viewTimeout); logger.stop(); + } catch (UiObjectNotFoundException e) { // On some devices the lighting options menu disappears // between clicks. Searching for the menu again would affect @@ -501,8 +514,7 @@ public class UiAutomation extends UxPerfUiAutomation { getDropdownMenu(); - UiObject moreOptions = getUiObjectByDescription("More options", "android.widget.ImageView"); - moreOptions.click(); + clickUiObject(BY_DESC, "More options", "android.widget.ImageView"); UiObject bookInfo = getUiObjectByText("About this book", "android.widget.TextView"); @@ -519,15 +531,51 @@ public class UiAutomation extends UxPerfUiAutomation { pressBack(); } + // Helper for waiting on a page between actions + private UiObject waitForPage() throws Exception { + UiObject activityReader = + new UiObject(new UiSelector().resourceId(packageID + "activity_reader") + .childSelector(new UiSelector() + .focusable(true))); + // On some devices the object in the view hierarchy is found before it + // becomes visible on the screen. Therefore add pause instead. + sleep(3); + + if (!activityReader.waitForExists(viewTimeout)) { + throw new UiObjectNotFoundException("Could not find \"activity reader view\"."); + } + + return activityReader; + } + // Helper for accessing the drop down menu private void getDropdownMenu() throws Exception { UiObject actionBar = new UiObject(new UiSelector().resourceId(packageID + "action_bar")); - if (!actionBar.exists()) { tapDisplayCentre(); sleep(1); // Allow previous views to settle } + + UiObject card = + new UiObject(new UiSelector().resourceId(packageID + "cards") + .className("android.view.ViewGroup")); + if (card.exists()) { + // On rare occasions tapping a certain word that appears in the centre + // of the display will bring up a card to describe the word. + // (Such as a place will bring a map of its location) + // In this situation, tap centre to go back, and try again + // at a different set of coordinates + int x = (int)(getDisplayCentreWidth() * 0.8); + int y = (int)(getDisplayCentreHeight() * 0.8); + while (card.exists()) { + tapDisplay(x, y); + sleep(1); + } + + tapDisplay(x, y); + sleep(1); // Allow previous views to settle + } if (!actionBar.exists()) { throw new UiObjectNotFoundException("Could not find \"action bar\"."); @@ -537,7 +585,6 @@ public class UiAutomation extends UxPerfUiAutomation { private void hideDropDownMenu() throws Exception { UiObject actionBar = new UiObject(new UiSelector().resourceId(packageID + "action_bar")); - if (actionBar.exists()) { tapDisplayCentre(); sleep(1); // Allow previous views to settle @@ -548,20 +595,25 @@ public class UiAutomation extends UxPerfUiAutomation { } } - // Helper for waiting on a page between actions - private UiObject waitForPage() throws Exception { - UiObject activityReader = - new UiObject(new UiSelector().resourceId(packageID + "activity_reader") - .childSelector(new UiSelector().focusable(true))); - - // On some devices the object in the view hierarchy is found before it - // becomes visible on the screen. Therefore add pause instead. - sleep(3); - - if (!activityReader.waitForExists(viewTimeout)) { - throw new UiObjectNotFoundException("Could not find \"activity reader view\"."); + private UiObject searchPage(final UiObject view, final String pagenum, final Direction updown, + final int attempts) throws Exception { + if (attempts <= 0) { + throw new UiObjectNotFoundException("Could not find \"page number\" after several attempts."); } - return activityReader; + UiObject page = + new UiObject(new UiSelector().description(String.format("page " + pagenum)) + .className("android.widget.TextView")); + if (!page.exists()) { + // Scroll up by swiping down + if (updown == Direction.UP) { + view.swipeDown(200); + // Default case is to scroll down (swipe up) + } else { + view.swipeUp(200); + } + page = searchPage(view, pagenum, updown, attempts - 1); + } + return page; } }