summaryrefslogtreecommitdiff
path: root/src/com/android/uiautomator/core/UiDevice.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/uiautomator/core/UiDevice.java')
-rw-r--r--src/com/android/uiautomator/core/UiDevice.java856
1 files changed, 856 insertions, 0 deletions
diff --git a/src/com/android/uiautomator/core/UiDevice.java b/src/com/android/uiautomator/core/UiDevice.java
new file mode 100644
index 0000000..2a51109
--- /dev/null
+++ b/src/com/android/uiautomator/core/UiDevice.java
@@ -0,0 +1,856 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * 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.android.uiautomator.core;
+
+import android.app.UiAutomation;
+import android.app.UiAutomation.AccessibilityEventFilter;
+import android.graphics.Point;
+import android.os.Build;
+import android.os.Environment;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Display;
+import android.view.KeyEvent;
+import android.view.Surface;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * UiDevice provides access to state information about the device.
+ * You can also use this class to simulate user actions on the device,
+ * such as pressing the d-pad or pressing the Home and Menu buttons.
+ * @since API Level 16
+ */
+public class UiDevice {
+ private static final String LOG_TAG = UiDevice.class.getSimpleName();
+
+ // Sometimes HOME and BACK key presses will generate no events if already on
+ // home page or there is nothing to go back to, Set low timeouts.
+ private static final long KEY_PRESS_EVENT_TIMEOUT = 1 * 1000;
+
+ // store for registered UiWatchers
+ private final HashMap<String, UiWatcher> mWatchers = new HashMap<String, UiWatcher>();
+ private final List<String> mWatchersTriggers = new ArrayList<String>();
+
+ // remember if we're executing in the context of a UiWatcher
+ private boolean mInWatcherContext = false;
+
+ // provides access the {@link QueryController} and {@link InteractionController}
+ private InstrumentationUiAutomatorBridge mUiAutomationBridge;
+
+ // reference to self
+ private static UiDevice sDevice;
+
+ private UiDevice() {
+ /* hide constructor */
+ }
+
+ /**
+ * @hide
+ */
+ public void initialize(InstrumentationUiAutomatorBridge uiAutomatorBridge) {
+ mUiAutomationBridge = uiAutomatorBridge;
+ }
+
+ boolean isInWatcherContext() {
+ return mInWatcherContext;
+ }
+
+ /**
+ * Provides access the {@link QueryController} and {@link InteractionController}
+ * @return {@link ShellUiAutomatorBridge}
+ */
+ InstrumentationUiAutomatorBridge getAutomatorBridge() {
+ if (mUiAutomationBridge == null) {
+ throw new RuntimeException("UiDevice not initialized");
+ }
+ return mUiAutomationBridge;
+ }
+
+ /**
+ * Enables or disables layout hierarchy compression.
+ *
+ * If compression is enabled, the layout hierarchy derived from the Acessibility
+ * framework will only contain nodes that are important for uiautomator
+ * testing. Any unnecessary surrounding layout nodes that make viewing
+ * and searching the hierarchy inefficient are removed.
+ *
+ * @param compressed true to enable compression; else, false to disable
+ * @since API Level 18
+ */
+ public void setCompressedLayoutHeirarchy(boolean compressed) {
+ getAutomatorBridge().setCompressedLayoutHierarchy(compressed);
+ }
+
+ /**
+ * Retrieves a singleton instance of UiDevice
+ *
+ * @return UiDevice instance
+ * @since API Level 16
+ */
+ public static UiDevice getInstance() {
+ if (sDevice == null) {
+ sDevice = new UiDevice();
+ }
+ return sDevice;
+ }
+
+ /**
+ * Returns the display size in dp (device-independent pixel)
+ *
+ * The returned display size is adjusted per screen rotation. Also this will return the actual
+ * size of the screen, rather than adjusted per system decorations (like status bar).
+ *
+ * @return a Point containing the display size in dp
+ */
+ public Point getDisplaySizeDp() {
+ Tracer.trace();
+ Display display = getAutomatorBridge().getDefaultDisplay();
+ Point p = new Point();
+ display.getRealSize(p);
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getRealMetrics(metrics);
+ float dpx = p.x / metrics.density;
+ float dpy = p.y / metrics.density;
+ p.x = Math.round(dpx);
+ p.y = Math.round(dpy);
+ return p;
+ }
+
+ /**
+ * Retrieves the product name of the device.
+ *
+ * This method provides information on what type of device the test is running on. This value is
+ * the same as returned by invoking #adb shell getprop ro.product.name.
+ *
+ * @return product name of the device
+ * @since API Level 17
+ */
+ public String getProductName() {
+ Tracer.trace();
+ return Build.PRODUCT;
+ }
+
+ /**
+ * Retrieves the text from the last UI traversal event received.
+ *
+ * You can use this method to read the contents in a WebView container
+ * because the accessibility framework fires events
+ * as each text is highlighted. You can write a test to perform
+ * directional arrow presses to focus on different elements inside a WebView,
+ * and call this method to get the text from each traversed element.
+ * If you are testing a view container that can return a reference to a
+ * Document Object Model (DOM) object, your test should use the view's
+ * DOM instead.
+ *
+ * @return text of the last traversal event, else return an empty string
+ * @since API Level 16
+ */
+ public String getLastTraversedText() {
+ Tracer.trace();
+ return getAutomatorBridge().getQueryController().getLastTraversedText();
+ }
+
+ /**
+ * Clears the text from the last UI traversal event.
+ * See {@link #getLastTraversedText()}.
+ * @since API Level 16
+ */
+ public void clearLastTraversedText() {
+ Tracer.trace();
+ getAutomatorBridge().getQueryController().clearLastTraversedText();
+ }
+
+ /**
+ * Simulates a short press on the MENU button.
+ * @return true if successful, else return false
+ * @since API Level 16
+ */
+ public boolean pressMenu() {
+ Tracer.trace();
+ waitForIdle();
+ return getAutomatorBridge().getInteractionController().sendKeyAndWaitForEvent(
+ KeyEvent.KEYCODE_MENU, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
+ KEY_PRESS_EVENT_TIMEOUT);
+ }
+
+ /**
+ * Simulates a short press on the BACK button.
+ * @return true if successful, else return false
+ * @since API Level 16
+ */
+ public boolean pressBack() {
+ Tracer.trace();
+ waitForIdle();
+ return getAutomatorBridge().getInteractionController().sendKeyAndWaitForEvent(
+ KeyEvent.KEYCODE_BACK, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
+ KEY_PRESS_EVENT_TIMEOUT);
+ }
+
+ /**
+ * Simulates a short press on the HOME button.
+ * @return true if successful, else return false
+ * @since API Level 16
+ */
+ public boolean pressHome() {
+ Tracer.trace();
+ waitForIdle();
+ return getAutomatorBridge().getInteractionController().sendKeyAndWaitForEvent(
+ KeyEvent.KEYCODE_HOME, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
+ KEY_PRESS_EVENT_TIMEOUT);
+ }
+
+ /**
+ * Simulates a short press on the SEARCH button.
+ * @return true if successful, else return false
+ * @since API Level 16
+ */
+ public boolean pressSearch() {
+ Tracer.trace();
+ return pressKeyCode(KeyEvent.KEYCODE_SEARCH);
+ }
+
+ /**
+ * Simulates a short press on the CENTER button.
+ * @return true if successful, else return false
+ * @since API Level 16
+ */
+ public boolean pressDPadCenter() {
+ Tracer.trace();
+ return pressKeyCode(KeyEvent.KEYCODE_DPAD_CENTER);
+ }
+
+ /**
+ * Simulates a short press on the DOWN button.
+ * @return true if successful, else return false
+ * @since API Level 16
+ */
+ public boolean pressDPadDown() {
+ Tracer.trace();
+ return pressKeyCode(KeyEvent.KEYCODE_DPAD_DOWN);
+ }
+
+ /**
+ * Simulates a short press on the UP button.
+ * @return true if successful, else return false
+ * @since API Level 16
+ */
+ public boolean pressDPadUp() {
+ Tracer.trace();
+ return pressKeyCode(KeyEvent.KEYCODE_DPAD_UP);
+ }
+
+ /**
+ * Simulates a short press on the LEFT button.
+ * @return true if successful, else return false
+ * @since API Level 16
+ */
+ public boolean pressDPadLeft() {
+ Tracer.trace();
+ return pressKeyCode(KeyEvent.KEYCODE_DPAD_LEFT);
+ }
+
+ /**
+ * Simulates a short press on the RIGHT button.
+ * @return true if successful, else return false
+ * @since API Level 16
+ */
+ public boolean pressDPadRight() {
+ Tracer.trace();
+ return pressKeyCode(KeyEvent.KEYCODE_DPAD_RIGHT);
+ }
+
+ /**
+ * Simulates a short press on the DELETE key.
+ * @return true if successful, else return false
+ * @since API Level 16
+ */
+ public boolean pressDelete() {
+ Tracer.trace();
+ return pressKeyCode(KeyEvent.KEYCODE_DEL);
+ }
+
+ /**
+ * Simulates a short press on the ENTER key.
+ * @return true if successful, else return false
+ * @since API Level 16
+ */
+ public boolean pressEnter() {
+ Tracer.trace();
+ return pressKeyCode(KeyEvent.KEYCODE_ENTER);
+ }
+
+ /**
+ * Simulates a short press using a key code.
+ *
+ * See {@link KeyEvent}
+ * @return true if successful, else return false
+ * @since API Level 16
+ */
+ public boolean pressKeyCode(int keyCode) {
+ Tracer.trace(keyCode);
+ waitForIdle();
+ return getAutomatorBridge().getInteractionController().sendKey(keyCode, 0);
+ }
+
+ /**
+ * Simulates a short press using a key code.
+ *
+ * See {@link KeyEvent}.
+ * @param keyCode the key code of the event.
+ * @param metaState an integer in which each bit set to 1 represents a pressed meta key
+ * @return true if successful, else return false
+ * @since API Level 16
+ */
+ public boolean pressKeyCode(int keyCode, int metaState) {
+ Tracer.trace(keyCode, metaState);
+ waitForIdle();
+ return getAutomatorBridge().getInteractionController().sendKey(keyCode, metaState);
+ }
+
+ /**
+ * Simulates a short press on the Recent Apps button.
+ *
+ * @return true if successful, else return false
+ * @throws RemoteException
+ * @since API Level 16
+ */
+ public boolean pressRecentApps() throws RemoteException {
+ Tracer.trace();
+ waitForIdle();
+ return getAutomatorBridge().getInteractionController().toggleRecentApps();
+ }
+
+ /**
+ * Opens the notification shade.
+ *
+ * @return true if successful, else return false
+ * @since API Level 18
+ */
+ public boolean openNotification() {
+ Tracer.trace();
+ waitForIdle();
+ return getAutomatorBridge().getInteractionController().openNotification();
+ }
+
+ /**
+ * Opens the Quick Settings shade.
+ *
+ * @return true if successful, else return false
+ * @since API Level 18
+ */
+ public boolean openQuickSettings() {
+ Tracer.trace();
+ waitForIdle();
+ return getAutomatorBridge().getInteractionController().openQuickSettings();
+ }
+
+ /**
+ * Gets the width of the display, in pixels. The width and height details
+ * are reported based on the current orientation of the display.
+ * @return width in pixels or zero on failure
+ * @since API Level 16
+ */
+ public int getDisplayWidth() {
+ Tracer.trace();
+ Display display = getAutomatorBridge().getDefaultDisplay();
+ Point p = new Point();
+ display.getSize(p);
+ return p.x;
+ }
+
+ /**
+ * Gets the height of the display, in pixels. The size is adjusted based
+ * on the current orientation of the display.
+ * @return height in pixels or zero on failure
+ * @since API Level 16
+ */
+ public int getDisplayHeight() {
+ Tracer.trace();
+ Display display = getAutomatorBridge().getDefaultDisplay();
+ Point p = new Point();
+ display.getSize(p);
+ return p.y;
+ }
+
+ /**
+ * Perform a click at arbitrary coordinates specified by the user
+ *
+ * @param x coordinate
+ * @param y coordinate
+ * @return true if the click succeeded else false
+ * @since API Level 16
+ */
+ public boolean click(int x, int y) {
+ Tracer.trace(x, y);
+ if (x >= getDisplayWidth() || y >= getDisplayHeight()) {
+ return (false);
+ }
+ return getAutomatorBridge().getInteractionController().clickNoSync(x, y);
+ }
+
+ /**
+ * Performs a swipe from one coordinate to another using the number of steps
+ * to determine smoothness and speed. Each step execution is throttled to 5ms
+ * per step. So for a 100 steps, the swipe will take about 1/2 second to complete.
+ *
+ * @param startX
+ * @param startY
+ * @param endX
+ * @param endY
+ * @param steps is the number of move steps sent to the system
+ * @return false if the operation fails or the coordinates are invalid
+ * @since API Level 16
+ */
+ public boolean swipe(int startX, int startY, int endX, int endY, int steps) {
+ Tracer.trace(startX, startY, endX, endY, steps);
+ return getAutomatorBridge().getInteractionController()
+ .swipe(startX, startY, endX, endY, steps);
+ }
+
+ /**
+ * Performs a swipe from one coordinate to another coordinate. You can control
+ * the smoothness and speed of the swipe by specifying the number of steps.
+ * Each step execution is throttled to 5 milliseconds per step, so for a 100
+ * steps, the swipe will take around 0.5 seconds to complete.
+ *
+ * @param startX X-axis value for the starting coordinate
+ * @param startY Y-axis value for the starting coordinate
+ * @param endX X-axis value for the ending coordinate
+ * @param endY Y-axis value for the ending coordinate
+ * @param steps is the number of steps for the swipe action
+ * @return true if swipe is performed, false if the operation fails
+ * or the coordinates are invalid
+ * @since API Level 18
+ */
+ public boolean drag(int startX, int startY, int endX, int endY, int steps) {
+ Tracer.trace(startX, startY, endX, endY, steps);
+ return getAutomatorBridge().getInteractionController()
+ .swipe(startX, startY, endX, endY, steps, true);
+ }
+
+ /**
+ * Performs a swipe between points in the Point array. Each step execution is throttled
+ * to 5ms per step. So for a 100 steps, the swipe will take about 1/2 second to complete
+ *
+ * @param segments is Point array containing at least one Point object
+ * @param segmentSteps steps to inject between two Points
+ * @return true on success
+ * @since API Level 16
+ */
+ public boolean swipe(Point[] segments, int segmentSteps) {
+ Tracer.trace(segments, segmentSteps);
+ return getAutomatorBridge().getInteractionController().swipe(segments, segmentSteps);
+ }
+
+ /**
+ * Waits for the current application to idle.
+ * Default wait timeout is 10 seconds
+ * @since API Level 16
+ */
+ public void waitForIdle() {
+ Tracer.trace();
+ waitForIdle(Configurator.getInstance().getWaitForIdleTimeout());
+ }
+
+ /**
+ * Waits for the current application to idle.
+ * @param timeout in milliseconds
+ * @since API Level 16
+ */
+ public void waitForIdle(long timeout) {
+ Tracer.trace(timeout);
+ getAutomatorBridge().waitForIdle(timeout);
+ }
+
+ /**
+ * Retrieves the last activity to report accessibility events.
+ * @deprecated The results returned should be considered unreliable
+ * @return String name of activity
+ * @since API Level 16
+ */
+ @Deprecated
+ public String getCurrentActivityName() {
+ Tracer.trace();
+ return getAutomatorBridge().getQueryController().getCurrentActivityName();
+ }
+
+ /**
+ * Retrieves the name of the last package to report accessibility events.
+ * @return String name of package
+ * @since API Level 16
+ */
+ public String getCurrentPackageName() {
+ Tracer.trace();
+ return getAutomatorBridge().getQueryController().getCurrentPackageName();
+ }
+
+ /**
+ * Registers a {@link UiWatcher} to run automatically when the testing framework is unable to
+ * find a match using a {@link UiSelector}. See {@link #runWatchers()}
+ *
+ * @param name to register the UiWatcher
+ * @param watcher {@link UiWatcher}
+ * @since API Level 16
+ */
+ public void registerWatcher(String name, UiWatcher watcher) {
+ Tracer.trace(name, watcher);
+ if (mInWatcherContext) {
+ throw new IllegalStateException("Cannot register new watcher from within another");
+ }
+ mWatchers.put(name, watcher);
+ }
+
+ /**
+ * Removes a previously registered {@link UiWatcher}.
+ *
+ * See {@link #registerWatcher(String, UiWatcher)}
+ * @param name used to register the UiWatcher
+ * @since API Level 16
+ */
+ public void removeWatcher(String name) {
+ Tracer.trace(name);
+ if (mInWatcherContext) {
+ throw new IllegalStateException("Cannot remove a watcher from within another");
+ }
+ mWatchers.remove(name);
+ }
+
+ /**
+ * This method forces all registered watchers to run.
+ * See {@link #registerWatcher(String, UiWatcher)}
+ * @since API Level 16
+ */
+ public void runWatchers() {
+ Tracer.trace();
+ if (mInWatcherContext) {
+ return;
+ }
+
+ for (String watcherName : mWatchers.keySet()) {
+ UiWatcher watcher = mWatchers.get(watcherName);
+ if (watcher != null) {
+ try {
+ mInWatcherContext = true;
+ if (watcher.checkForCondition()) {
+ setWatcherTriggered(watcherName);
+ }
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Exceuting watcher: " + watcherName, e);
+ } finally {
+ mInWatcherContext = false;
+ }
+ }
+ }
+ }
+
+ /**
+ * Resets a {@link UiWatcher} that has been triggered.
+ * If a UiWatcher runs and its {@link UiWatcher#checkForCondition()} call
+ * returned <code>true</code>, then the UiWatcher is considered triggered.
+ * See {@link #registerWatcher(String, UiWatcher)}
+ * @since API Level 16
+ */
+ public void resetWatcherTriggers() {
+ Tracer.trace();
+ mWatchersTriggers.clear();
+ }
+
+ /**
+ * Checks if a specific registered {@link UiWatcher} has triggered.
+ * See {@link #registerWatcher(String, UiWatcher)}. If a UiWatcher runs and its
+ * {@link UiWatcher#checkForCondition()} call returned <code>true</code>, then
+ * the UiWatcher is considered triggered. This is helpful if a watcher is detecting errors
+ * from ANR or crash dialogs and the test needs to know if a UiWatcher has been triggered.
+ *
+ * @param watcherName
+ * @return true if triggered else false
+ * @since API Level 16
+ */
+ public boolean hasWatcherTriggered(String watcherName) {
+ Tracer.trace(watcherName);
+ return mWatchersTriggers.contains(watcherName);
+ }
+
+ /**
+ * Checks if any registered {@link UiWatcher} have triggered.
+ *
+ * See {@link #registerWatcher(String, UiWatcher)}
+ * See {@link #hasWatcherTriggered(String)}
+ * @since API Level 16
+ */
+ public boolean hasAnyWatcherTriggered() {
+ Tracer.trace();
+ return mWatchersTriggers.size() > 0;
+ }
+
+ /**
+ * Used internally by this class to set a {@link UiWatcher} state as triggered.
+ * @param watcherName
+ */
+ private void setWatcherTriggered(String watcherName) {
+ Tracer.trace(watcherName);
+ if (!hasWatcherTriggered(watcherName)) {
+ mWatchersTriggers.add(watcherName);
+ }
+ }
+
+ /**
+ * Check if the device is in its natural orientation. This is determined by checking if the
+ * orientation is at 0 or 180 degrees.
+ * @return true if it is in natural orientation
+ * @since API Level 17
+ */
+ public boolean isNaturalOrientation() {
+ Tracer.trace();
+ waitForIdle();
+ int ret = getAutomatorBridge().getRotation();
+ return ret == UiAutomation.ROTATION_FREEZE_0 ||
+ ret == UiAutomation.ROTATION_FREEZE_180;
+ }
+
+ /**
+ * Returns the current rotation of the display, as defined in {@link Surface}
+ * @since API Level 17
+ */
+ public int getDisplayRotation() {
+ Tracer.trace();
+ waitForIdle();
+ return getAutomatorBridge().getRotation();
+ }
+
+ /**
+ * Disables the sensors and freezes the device rotation at its
+ * current rotation state.
+ * @throws RemoteException
+ * @since API Level 16
+ */
+ public void freezeRotation() throws RemoteException {
+ Tracer.trace();
+ getAutomatorBridge().getInteractionController().freezeRotation();
+ }
+
+ /**
+ * Re-enables the sensors and un-freezes the device rotation allowing its contents
+ * to rotate with the device physical rotation. During a test execution, it is best to
+ * keep the device frozen in a specific orientation until the test case execution has completed.
+ * @throws RemoteException
+ */
+ public void unfreezeRotation() throws RemoteException {
+ Tracer.trace();
+ getAutomatorBridge().getInteractionController().unfreezeRotation();
+ }
+
+ /**
+ * Simulates orienting the device to the left and also freezes rotation
+ * by disabling the sensors.
+ *
+ * If you want to un-freeze the rotation and re-enable the sensors
+ * see {@link #unfreezeRotation()}.
+ * @throws RemoteException
+ * @since API Level 17
+ */
+ public void setOrientationLeft() throws RemoteException {
+ Tracer.trace();
+ getAutomatorBridge().getInteractionController().setRotationLeft();
+ waitForIdle(); // we don't need to check for idle on entry for this. We'll sync on exit
+ }
+
+ /**
+ * Simulates orienting the device to the right and also freezes rotation
+ * by disabling the sensors.
+ *
+ * If you want to un-freeze the rotation and re-enable the sensors
+ * see {@link #unfreezeRotation()}.
+ * @throws RemoteException
+ * @since API Level 17
+ */
+ public void setOrientationRight() throws RemoteException {
+ Tracer.trace();
+ getAutomatorBridge().getInteractionController().setRotationRight();
+ waitForIdle(); // we don't need to check for idle on entry for this. We'll sync on exit
+ }
+
+ /**
+ * Simulates orienting the device into its natural orientation and also freezes rotation
+ * by disabling the sensors.
+ *
+ * If you want to un-freeze the rotation and re-enable the sensors
+ * see {@link #unfreezeRotation()}.
+ * @throws RemoteException
+ * @since API Level 17
+ */
+ public void setOrientationNatural() throws RemoteException {
+ Tracer.trace();
+ getAutomatorBridge().getInteractionController().setRotationNatural();
+ waitForIdle(); // we don't need to check for idle on entry for this. We'll sync on exit
+ }
+
+ /**
+ * This method simulates pressing the power button if the screen is OFF else
+ * it does nothing if the screen is already ON.
+ *
+ * If the screen was OFF and it just got turned ON, this method will insert a 500ms delay
+ * to allow the device time to wake up and accept input.
+ * @throws RemoteException
+ * @since API Level 16
+ */
+ public void wakeUp() throws RemoteException {
+ Tracer.trace();
+ if(getAutomatorBridge().getInteractionController().wakeDevice()) {
+ // sync delay to allow the window manager to start accepting input
+ // after the device is awakened.
+ SystemClock.sleep(500);
+ }
+ }
+
+ /**
+ * Checks the power manager if the screen is ON.
+ *
+ * @return true if the screen is ON else false
+ * @throws RemoteException
+ * @since API Level 16
+ */
+ public boolean isScreenOn() throws RemoteException {
+ Tracer.trace();
+ return getAutomatorBridge().getInteractionController().isScreenOn();
+ }
+
+ /**
+ * This method simply presses the power button if the screen is ON else
+ * it does nothing if the screen is already OFF.
+ *
+ * @throws RemoteException
+ * @since API Level 16
+ */
+ public void sleep() throws RemoteException {
+ Tracer.trace();
+ getAutomatorBridge().getInteractionController().sleepDevice();
+ }
+
+ /**
+ * Helper method used for debugging to dump the current window's layout hierarchy.
+ * Relative file paths are stored the application's internal private storage location.
+ *
+ * @param fileName
+ * @since API Level 16
+ */
+ public void dumpWindowHierarchy(String fileName) {
+ Tracer.trace(fileName);
+ AccessibilityNodeInfo root =
+ getAutomatorBridge().getQueryController().getAccessibilityRootNode();
+ if(root != null) {
+ Display display = getAutomatorBridge().getDefaultDisplay();
+ Point size = new Point();
+ display.getSize(size);
+ File dumpFile = new File(fileName);
+ if (!dumpFile.isAbsolute()) {
+ dumpFile = getAutomatorBridge().getContext().getFileStreamPath(fileName);
+ }
+ AccessibilityNodeInfoDumper.dumpWindowToFile(root, dumpFile, display.getRotation(),
+ size.x, size.y);
+ Log.d(LOG_TAG, String.format("Saved window hierarchy to %s",
+ dumpFile.getAbsolutePath()));
+ }
+ }
+
+ /**
+ * Waits for a window content update event to occur.
+ *
+ * If a package name for the window is specified, but the current window
+ * does not have the same package name, the function returns immediately.
+ *
+ * @param packageName the specified window package name (can be <code>null</code>).
+ * If <code>null</code>, a window update from any front-end window will end the wait
+ * @param timeout the timeout for the wait
+ *
+ * @return true if a window update occurred, false if timeout has elapsed or if the current
+ * window does not have the specified package name
+ * @since API Level 16
+ */
+ public boolean waitForWindowUpdate(final String packageName, long timeout) {
+ Tracer.trace(packageName, timeout);
+ if (packageName != null) {
+ if (!packageName.equals(getCurrentPackageName())) {
+ return false;
+ }
+ }
+ Runnable emptyRunnable = new Runnable() {
+ @Override
+ public void run() {
+ }
+ };
+ AccessibilityEventFilter checkWindowUpdate = new AccessibilityEventFilter() {
+ @Override
+ public boolean accept(AccessibilityEvent t) {
+ if (t.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {
+ return packageName == null || packageName.equals(t.getPackageName());
+ }
+ return false;
+ }
+ };
+ try {
+ getAutomatorBridge().executeCommandAndWaitForAccessibilityEvent(
+ emptyRunnable, checkWindowUpdate, timeout);
+ } catch (TimeoutException e) {
+ return false;
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "waitForWindowUpdate: general exception from bridge", e);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Take a screenshot of current window and store it as PNG
+ *
+ * Default scale of 1.0f (original size) and 90% quality is used
+ * The screenshot is adjusted per screen rotation
+ *
+ * @param storePath where the PNG should be written to
+ * @return true if screen shot is created successfully, false otherwise
+ * @since API Level 17
+ */
+ public boolean takeScreenshot(File storePath) {
+ Tracer.trace(storePath);
+ return takeScreenshot(storePath, 1.0f, 90);
+ }
+
+ /**
+ * Take a screenshot of current window and store it as PNG
+ *
+ * The screenshot is adjusted per screen rotation
+ *
+ * @param storePath where the PNG should be written to
+ * @param scale scale the screenshot down if needed; 1.0f for original size
+ * @param quality quality of the PNG compression; range: 0-100
+ * @return true if screen shot is created successfully, false otherwise
+ * @since API Level 17
+ */
+ public boolean takeScreenshot(File storePath, float scale, int quality) {
+ Tracer.trace(storePath, scale, quality);
+ return getAutomatorBridge().takeScreenshot(storePath, quality);
+ }
+}