diff --git a/wlauto/workloads/googleslides/__init__.py b/wlauto/workloads/googleslides/__init__.py new file mode 100755 index 00000000..c70077a7 --- /dev/null +++ b/wlauto/workloads/googleslides/__init__.py @@ -0,0 +1,143 @@ +# Copyright 2014-2016 ARM Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import os +import re + +from wlauto import AndroidUxPerfWorkload, Parameter, File +from wlauto.exceptions import WorkloadError + + +class GoogleSlides(AndroidUxPerfWorkload): + + name = 'googleslides' + description = ''' + A workload to perform standard productivity tasks with Google Slides. The workload carries + out various tasks, such as creating a new presentation, adding text, images, and shapes, + as well as basic editing and playing a slideshow. + This workload should be able to run without a network connection. + + There are two main scenarios: + 1. create test: a presentation is created in-app and some editing done on it, + 2. load test: a pre-existing PowerPoint file is copied onto the device for testing. + + --- create --- + Create a new file in the application and perform basic editing on it. This test also + requires an image file specified by the param ``test_image`` to be copied onto the device. + + Test description: + 1. Start the app and skip the welcome screen. Dismiss the work offline banner if present. + 2. Go to the app settings page and enables PowerPoint compatibility mode. This allows + PowerPoint files to be created inside Google Slides. + 3. Create a new PowerPoint presentation in the app (PPT compatibility mode) with a title + slide and save it to device storage. + 4. Insert another slide and to it insert the pushed image by picking it from the gallery. + 5. Insert the final slide and add a shape to it. Resize and drag the shape to modify it. + 6. Finally, navigate back to the documents list and delete file from the list to remove + it from the device. + + --- load --- + Copy a PowerPoint presentation onto the device to test slide navigation. The PowerPoint + file to be copied is given by ``test_file``. + + Test description: + 1. From the documents list (following the create test), open the specified PowerPoint + by navigating into device storage and wait for it to be loaded. + 2. A navigation test is performed while the file is in editing mode (i.e. not slideshow). + swiping forward to the next slide until ``slide_count`` swipes are performed. + 3. While still in editing mode, the same action is done in the reverse direction back to + the first slide. + 4. Enter presentation mode by selecting to play the slideshow. + 5. Swipe forward to play the slideshow, for a maximum number of ``slide_count`` swipes. + 6. Finally, repeat the previous step in the reverse direction while still in presentation + mode, navigating back to the first slide. + + NOTE: There are known issues with the reliability of this workload on some targets. + It MAY NOT ALWAYS WORK on your device. If you do run into problems, it might help to + set ``do_text_entry`` parameter to ``False``. + + ''' + + package = 'com.google.android.apps.docs.editors.slides' + min_apk_version = '1.6.312.08' + max_apk_version = None # works with latest (1.6.332.13) at time of publishing this + activity = '' + + # Views for FPS instrumentation + view = [ + package + '/com.google.android.apps.docs.quickoffice.filepicker.FilePickerActivity', + package + '/com.google.android.apps.docs.editors.shared.filepicker.FilePickerActivity', + package + '/com.google.android.apps.docs.quickoffice.filepicker.LocalSaveAsActivity', + package + '/com.qo.android.quickpoint.Quickpoint', + package + '/com.google.android.apps.docs.app.DocsPreferencesActivity', + package + '/com.google.android.apps.docs.app.DocListActivity', + package + '/com.google.android.apps.docs.welcome.warmwelcome.TrackingWelcomeActivity', + package + '/com.google.android.apps.docs.app.NewMainProxyActivity', + ] + + parameters = [ + Parameter('test_image', kind=str, mandatory=True, default='uxperf_1600x1200.jpg', + description=''' + An image to be copied onto the device that will be embedded in the + PowerPoint file as part of the test. + '''), + Parameter('test_file', kind=str, default='uxperf_test_doc.pptx', + description=''' + If specified, the workload will copy the PowerPoint file to be used for + testing onto the device. Otherwise, a file will be created inside the app. + '''), + Parameter('slide_count', kind=int, default=5, + description=''' + Number of slides in aforementioned local file. Determines number of + swipe actions when playing slide show. + '''), + Parameter('do_text_entry', kind=bool, default=True, + description=''' + If set to ``True``, will attempt to enter text in the first slide as part + of the test. Currently seems to be problematic on some devices, most + notably Samsung devices. + ''') + ] + + # Created file will be saved with this name + new_doc_name = "WORKLOAD AUTOMATION" + + def __init__(self, device, **kwargs): + super(GoogleSlides, self).__init__(device, **kwargs) + self.run_timeout = 600 + self.deployable_assets += [self.test_image, self.test_file] + + def validate(self): + super(GoogleSlides, self).validate() + self.uiauto_params['workdir_name'] = self.device.path.basename(self.device.working_directory) + self.uiauto_params['test_file'] = self.test_file.replace(' ', '0space0') + self.uiauto_params['slide_count'] = self.slide_count + self.uiauto_params['do_text_entry'] = self.do_text_entry + self.uiauto_params['new_doc_name'] = self.new_doc_name.replace(' ', '0space0') + + def setup(self, context): + super(GoogleSlides, self).setup(context) + # Force re-index of removable storage to pick up pushed image in gallery + self.device.broadcast_media_mounted(self.device.working_directory) + + def teardown(self, context): + super(GoogleSlides, self).teardown(context) + self.device.delete_file(self.device.path.join(self.device.working_directory, self.new_doc_name)) + self.device.broadcast_media_mounted(self.device.working_directory) + + def finalize(self, context): + super(GoogleSlides, self).finalize(context) + self.delete_assets() + self.device.broadcast_media_mounted(self.device.working_directory) diff --git a/wlauto/workloads/googleslides/com.arm.wlauto.uiauto.googleslides.jar b/wlauto/workloads/googleslides/com.arm.wlauto.uiauto.googleslides.jar new file mode 100644 index 00000000..c9fb0d7d Binary files /dev/null and b/wlauto/workloads/googleslides/com.arm.wlauto.uiauto.googleslides.jar differ diff --git a/wlauto/workloads/googleslides/uiauto/build.sh b/wlauto/workloads/googleslides/uiauto/build.sh new file mode 100755 index 00000000..58407ee5 --- /dev/null +++ b/wlauto/workloads/googleslides/uiauto/build.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +# CD into build dir if possible - allows building from any directory +script_path='.' +if `readlink -f $0 &>/dev/null`; then + script_path=`readlink -f $0 2>/dev/null` +fi +script_dir=`dirname $script_path` +cd $script_dir + +# Ensure build.xml exists before starting +if [[ ! -f build.xml ]]; then + echo 'Ant build.xml file not found! Check that you are in the right directory.' + exit 9 +fi + +# Copy base classes from wlauto dist +class_dir=bin/classes/com/arm/wlauto/uiauto +base_classes=`python -c "import os, wlauto; print os.path.join(os.path.dirname(wlauto.__file__), 'common', 'android', '*.class')"` +mkdir -p $class_dir +cp $base_classes $class_dir + +# Build and return appropriate exit code if failed +ant build +exit_code=$? +if [[ $exit_code -ne 0 ]]; then + echo "ERROR: 'ant build' exited with code $exit_code" + exit $exit_code +fi + +# If successful move JAR file to workload folder (remove previous) +package=com.arm.wlauto.uiauto.googleslides.jar +rm -f ../$package +if [[ -f bin/$package ]]; then + cp bin/$package .. +else + echo 'ERROR: UiAutomator JAR could not be found!' + exit 9 +fi diff --git a/wlauto/workloads/googleslides/uiauto/build.xml b/wlauto/workloads/googleslides/uiauto/build.xml new file mode 100644 index 00000000..259eaa6e --- /dev/null +++ b/wlauto/workloads/googleslides/uiauto/build.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wlauto/workloads/googleslides/uiauto/project.properties b/wlauto/workloads/googleslides/uiauto/project.properties new file mode 100644 index 00000000..ce39f2d0 --- /dev/null +++ b/wlauto/workloads/googleslides/uiauto/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-18 diff --git a/wlauto/workloads/googleslides/uiauto/src/com/arm/wlauto/uiauto/UiAutomation.java b/wlauto/workloads/googleslides/uiauto/src/com/arm/wlauto/uiauto/UiAutomation.java new file mode 100755 index 00000000..1b96a68f --- /dev/null +++ b/wlauto/workloads/googleslides/uiauto/src/com/arm/wlauto/uiauto/UiAutomation.java @@ -0,0 +1,417 @@ +/* Copyright 2014-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.arm.wlauto.uiauto.googleslides; + +import java.io.File; +import java.util.LinkedHashMap; +import java.util.Map; + +import android.graphics.Rect; +import android.os.Bundle; +import android.os.SystemClock; + +// Import the uiautomator libraries +import com.android.uiautomator.core.Configurator; +import com.android.uiautomator.core.UiObject; +import com.android.uiautomator.core.UiObjectNotFoundException; +import com.android.uiautomator.core.UiScrollable; +import com.android.uiautomator.core.UiSelector; + +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; + +public class UiAutomation extends UxPerfUiAutomation { + + public static final String ANDROID_WIDGET = "android.widget."; + public static final String CLASS_TEXT_VIEW = ANDROID_WIDGET + "TextView"; + public static final String CLASS_IMAGE_VIEW = ANDROID_WIDGET + "ImageView"; + public static final String CLASS_BUTTON = ANDROID_WIDGET + "Button"; + public static final String CLASS_IMAGE_BUTTON = ANDROID_WIDGET + "ImageButton"; + public static final String CLASS_TABLE_ROW = ANDROID_WIDGET + "TableRow"; + public static final String CLASS_PROGRESS_BAR = ANDROID_WIDGET + "ProgressBar"; + public static final String CLASS_LIST_VIEW = ANDROID_WIDGET + "ListView"; + + public static final int WAIT_TIMEOUT_1SEC = 1000; + public static final int SLIDE_WAIT_TIME_MS = 200; + public static final int DEFAULT_SWIPE_STEPS = 10; + + protected ActionLogger logger; + protected String packageId; + protected Bundle parameters; + protected String newDocumentName; + protected String pushedDocumentName; + protected String workingDirectoryName; + protected int slideCount; + protected boolean doTextEntry; + + public void runUiAutomation() throws Exception { + // Setup + parameters = getParams(); + parseParams(parameters); + setScreenOrientation(ScreenOrientation.NATURAL); + changeAckTimeout(100); + // UI automation begins here + skipWelcomeScreen(); + sleep(1); + dismissWorkOfflineBanner(); + sleep(1); + enablePowerpointCompat(); + sleep(1); + testEditNewSlidesDocument(newDocumentName); + sleep(1); + testSlideshowFromStorage(pushedDocumentName); + // UI automation ends here + unsetScreenOrientation(); + } + + public void parseParams(Bundle parameters) throws Exception { + pushedDocumentName = parameters.getString("test_file").replaceAll("0space0", " "); + newDocumentName = parameters.getString("new_doc_name").replaceAll("0space0", " "); + slideCount = Integer.parseInt(parameters.getString("slide_count")); + packageId = parameters.getString("package") + ":id/"; + workingDirectoryName = parameters.getString("workdir_name"); + doTextEntry = Boolean.parseBoolean(parameters.getString("do_text_entry")); + } + + public void dismissWorkOfflineBanner() throws Exception { + UiObject banner = new UiObject(new UiSelector().textContains("Work offline")); + if (banner.waitForExists(WAIT_TIMEOUT_1SEC)) { + clickUiObject(BY_TEXT, "Got it", CLASS_BUTTON); + } + } + + public void enterTextInSlide(String viewName, String textToEnter) throws Exception { + UiSelector container = new UiSelector().resourceId(packageId + "main_canvas"); + UiObject view = new UiObject(container.childSelector(new UiSelector().descriptionMatches(viewName))); + view.click(); + getUiDevice().pressEnter(); + view.setText(textToEnter); + tapOpenArea(); + // On some devices, keyboard pops up when entering text, and takes a noticeable + // amount of time (few milliseconds) to disappear after clicking Done. + // In these cases, trying to find a view immediately after entering text leads + // to an exception, so a short wait-time is added for stability. + SystemClock.sleep(SLIDE_WAIT_TIME_MS); + } + + public void insertSlide(String slideLayout) throws Exception { + clickUiObject(BY_DESC, "Add slide", true); + clickUiObject(BY_TEXT, slideLayout, true); + } + + public void insertImage() throws Exception { + UiObject insertButton = new UiObject(new UiSelector().descriptionContains("Insert")); + if (insertButton.exists()) { + insertButton.click(); + } else { + clickUiObject(BY_DESC, "More options"); + clickUiObject(BY_TEXT, "Insert"); + } + clickUiObject(BY_TEXT, "Image", true); + clickUiObject(BY_TEXT, "From photos"); + + UiObject imagesFolder = new UiObject(new UiSelector().className(CLASS_TEXT_VIEW).textContains("Images")); + if (!imagesFolder.waitForExists(WAIT_TIMEOUT_1SEC*10)) { + clickUiObject(BY_DESC, "Show roots"); + } + imagesFolder.click(); + + UiObject folderEntry = new UiObject(new UiSelector().textContains(workingDirectoryName)); + UiScrollable list = new UiScrollable(new UiSelector().scrollable(true)); + if (!folderEntry.exists() && list.waitForExists(WAIT_TIMEOUT_1SEC)) { + list.scrollIntoView(folderEntry); + } else { + folderEntry.waitForExists(WAIT_TIMEOUT_1SEC*10); + } + folderEntry.clickAndWaitForNewWindow(); + clickUiObject(BY_ID, "com.android.documentsui:id/date", true); + } + + public void insertShape(String shapeName) throws Exception { + startLogger("shape_insert"); + UiObject insertButton = new UiObject(new UiSelector().descriptionContains("Insert")); + if (insertButton.exists()) { + insertButton.click(); + } else { + clickUiObject(BY_DESC, "More options"); + clickUiObject(BY_TEXT, "Insert"); + } + clickUiObject(BY_TEXT, "Shape"); + clickUiObject(BY_DESC, shapeName); + stopLogger("shape_insert"); + } + + public void modifyShape(String shapeName) throws Exception { + UiObject resizeHandle = new UiObject(new UiSelector().descriptionMatches(".*Bottom[- ]right resize.*")); + Rect bounds = resizeHandle.getVisibleBounds(); + int newX = bounds.left - 40; + int newY = bounds.bottom - 40; + startLogger("shape_resize"); + resizeHandle.dragTo(newX, newY, 40); + stopLogger("shape_resize"); + + UiSelector container = new UiSelector().resourceId(packageId + "main_canvas"); + UiSelector shapeSelector = container.childSelector(new UiSelector().descriptionContains(shapeName)); + startLogger("shape_drag"); + new UiObject(shapeSelector).dragTo(newX, newY, 40); + stopLogger("shape_drag"); + } + + public void openDocument(String docName) throws Exception { + clickUiObject(BY_DESC, "Open presentation"); + clickUiObject(BY_TEXT, "Device storage", true); + clickUiObject(BY_DESC, "Navigate up"); + UiScrollable list = new UiScrollable(new UiSelector().className(CLASS_LIST_VIEW)); + list.scrollIntoView(new UiSelector().textMatches(workingDirectoryName)); + clickUiObject(BY_TEXT, workingDirectoryName); + list.scrollIntoView(new UiSelector().textContains(docName)); + startLogger("document_open"); + clickUiObject(BY_TEXT, docName); + clickUiObject(BY_TEXT, "Open", CLASS_BUTTON, true); + stopLogger("document_open"); + } + + public void newDocument() throws Exception { + startLogger("document_new"); + clickUiObject(BY_DESC, "New presentation"); + clickUiObject(BY_TEXT, "New PowerPoint", true); + stopLogger("document_new"); + waitForProgress(WAIT_TIMEOUT_1SEC * 30); + } + + public void saveDocument(String docName) throws Exception { + UiObject saveActionButton = new UiObject(new UiSelector().resourceId(packageId + "action")); + UiObject unsavedIndicator = new UiObject(new UiSelector().textContains("Not saved")); + startLogger("document_save"); + if (saveActionButton.waitForExists(WAIT_TIMEOUT_1SEC)) { + saveActionButton.click(); + } else if (unsavedIndicator.waitForExists(WAIT_TIMEOUT_1SEC)) { + unsavedIndicator.click(); + } + clickUiObject(BY_TEXT, "Device"); + UiObject save = clickUiObject(BY_TEXT, "Save", CLASS_BUTTON); + if (save.waitForExists(WAIT_TIMEOUT_1SEC)) { + save.click(); + } + stopLogger("document_save"); + + // Overwrite if prompted + // Should not happen under normal circumstances. But ensures test doesn't stop + // if a previous iteration failed prematurely and was unable to delete the file. + // Note that this file isn't removed during workload teardown as deleting it is + // part of the UiAutomator test case. + UiObject overwriteView = new UiObject(new UiSelector().textContains("already exists")); + if (overwriteView.waitForExists(WAIT_TIMEOUT_1SEC)) { + clickUiObject(BY_TEXT, "Overwrite"); + } + } + + public void deleteDocument(String docName) throws Exception { + String filenameRegex = String.format(".*((%s)|([Uu]ntitled presentation)).pptx.*", docName); + UiObject doc = new UiObject(new UiSelector().textMatches(filenameRegex)); + UiObject moreActions = doc.getFromParent(new UiSelector().descriptionContains("More actions")); + startLogger("document_delete"); + moreActions.click(); + + UiObject deleteButton = new UiObject(new UiSelector().textMatches(".*([Dd]elete|[Rr]emove).*")); + if (deleteButton.waitForExists(WAIT_TIMEOUT_1SEC)) { + deleteButton.click(); + } else { + // Delete button not found, try to scroll the view + UiScrollable scrollable = new UiScrollable(new UiSelector().scrollable(true) + .childSelector(new UiSelector().textContains("Rename"))); + if (scrollable.exists()) { + scrollable.scrollIntoView(deleteButton); + } else { + UiObject content = new UiObject(new UiSelector().resourceId(packageId + "content")); + int attemptsLeft = 10; // try a maximum of 10 swipe attempts + while (!deleteButton.exists() && attemptsLeft > 0) { + content.swipeUp(DEFAULT_SWIPE_STEPS); + attemptsLeft--; + } + } + deleteButton.click(); + } + + UiObject okButton = new UiObject(new UiSelector().className(CLASS_BUTTON).textContains("OK")); + if (okButton.waitForExists(WAIT_TIMEOUT_1SEC)) { + okButton.clickAndWaitForNewWindow(); + } else { + clickUiObject(BY_TEXT, "Remove", CLASS_BUTTON, true); + } + stopLogger("document_delete"); + } + + + protected void skipWelcomeScreen() throws Exception { + clickUiObject(BY_TEXT, "Skip", true); + } + + protected void enablePowerpointCompat() throws Exception { + startLogger("enable_pptmode"); + clickUiObject(BY_DESC, "drawer"); + clickUiObject(BY_TEXT, "Settings", true); + clickUiObject(BY_TEXT, "Create PowerPoint"); + getUiDevice().pressBack(); + stopLogger("enable_pptmode"); + } + + protected void testEditNewSlidesDocument(String docName) throws Exception { + // Init + newDocument(); + + // Slide 1 - Text + if (doTextEntry) { + enterTextInSlide(".*[Tt]itle.*", docName); + // Save + saveDocument(docName); + sleep(1); + } + + // Slide 2 - Image + insertSlide("Title only"); + insertImage(); + sleep(1); + + // If text wasn't entered in first slide, save prompt will appear here + if (!doTextEntry) { + // Save + saveDocument(docName); + sleep(1); + } + + // Slide 3 - Shape + insertSlide("Title slide"); + String shapeName = "Rounded rectangle"; + insertShape(shapeName); + modifyShape(shapeName); + getUiDevice().pressBack(); + sleep(1); + + // Tidy up + getUiDevice().pressBack(); + dismissWorkOfflineBanner(); // if it appears on the homescreen + + // Note: Currently disabled because it fails on Samsung devices + // deleteDocument(docName); + } + + protected void testSlideshowFromStorage(String docName) throws Exception { + // Open document + openDocument(docName); + waitForProgress(WAIT_TIMEOUT_1SEC*30); + + // Begin Slide show test + + // Note: Using coordinates slightly offset from the slide edges avoids accidentally + // selecting any shapes or text boxes inside the slides while swiping, which may + // cause the view to switch into edit mode and fail the test + UiObject slideCanvas = new UiObject(new UiSelector().resourceId(packageId + "main_canvas")); + Rect canvasBounds = slideCanvas.getVisibleBounds(); + int leftEdge = canvasBounds.left + 10; + int rightEdge = canvasBounds.right - 10; + int yCoordinate = canvasBounds.top + 5; + int slideIndex = 0; + + // scroll forward in edit mode + startLogger("slideshow_editforward"); + while (++slideIndex < slideCount) { + uiDeviceSwipeHorizontal(rightEdge, leftEdge, yCoordinate, DEFAULT_SWIPE_STEPS); + waitForProgress(WAIT_TIMEOUT_1SEC*5); + } + stopLogger("slideshow_editforward"); + sleep(1); + + // scroll backward in edit mode + startLogger("slideshow_editbackward"); + while (--slideIndex > 0) { + uiDeviceSwipeHorizontal(leftEdge, rightEdge, yCoordinate, DEFAULT_SWIPE_STEPS); + waitForProgress(WAIT_TIMEOUT_1SEC*5); + } + stopLogger("slideshow_editbackward"); + sleep(1); + + // run slideshow + startLogger("slideshow_run"); + clickUiObject(BY_DESC, "Start slideshow", true); + UiObject onDevice = new UiObject(new UiSelector().textContains("this device")); + if (onDevice.waitForExists(WAIT_TIMEOUT_1SEC)) { + onDevice.clickAndWaitForNewWindow(); + waitForProgress(WAIT_TIMEOUT_1SEC*30); + UiObject presentation = new UiObject(new UiSelector().descriptionContains("Presentation Viewer")); + presentation.waitForExists(WAIT_TIMEOUT_1SEC*30); + } + stopLogger("slideshow_run"); + sleep(1); + + // scroll forward in slideshow mode + startLogger("slideshow_playforward"); + while (++slideIndex < slideCount) { + uiDeviceSwipeHorizontal(rightEdge, leftEdge, yCoordinate, DEFAULT_SWIPE_STEPS); + waitForProgress(WAIT_TIMEOUT_1SEC*5); + } + stopLogger("slideshow_playforward"); + sleep(1); + + // scroll backward in slideshow mode + startLogger("slideshow_playbackward"); + while (--slideIndex > 0) { + uiDeviceSwipeHorizontal(leftEdge, rightEdge, yCoordinate, DEFAULT_SWIPE_STEPS); + waitForProgress(WAIT_TIMEOUT_1SEC*5); + } + stopLogger("slideshow_playbackward"); + sleep(1); + + getUiDevice().pressBack(); + getUiDevice().pressBack(); + } + + protected void startLogger(String name) throws Exception { + logger = new ActionLogger(name, parameters); + logger.start(); + } + + protected void stopLogger(String name) throws Exception { + logger.stop(); + } + + protected boolean waitForProgress(int timeout) throws Exception { + UiObject progress = new UiObject(new UiSelector().className(CLASS_PROGRESS_BAR)); + if (progress.waitForExists(WAIT_TIMEOUT_1SEC)) { + return progress.waitUntilGone(timeout); + } else { + return false; + } + } + + private long changeAckTimeout(long newTimeout) { + Configurator config = Configurator.getInstance(); + long oldTimeout = config.getActionAcknowledgmentTimeout(); + config.setActionAcknowledgmentTimeout(newTimeout); + return oldTimeout; + } + + private void tapOpenArea() throws Exception { + UiObject openArea = getUiObjectByResourceId(packageId + "punch_view_pager"); + Rect bounds = openArea.getVisibleBounds(); + // 10px from top of view, 10px from the right edge + tapDisplay(bounds.right - 10, bounds.top + 10); + } + +}