1e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu/*
2e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * Copyright (C) 2012 The Android Open Source Project
3e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *
4e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * Licensed under the Apache License, Version 2.0 (the "License");
5e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * you may not use this file except in compliance with the License.
6e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * You may obtain a copy of the License at
7e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *
8e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *      http://www.apache.org/licenses/LICENSE-2.0
9e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *
10e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * Unless required by applicable law or agreed to in writing, software
11e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * distributed under the License is distributed on an "AS IS" BASIS,
12e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * See the License for the specific language governing permissions and
14e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * limitations under the License.
15e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu */
16e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
17e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhupackage com.android.uiautomator.core;
18e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
19e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.content.Context;
209fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhuimport android.graphics.Bitmap;
219fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhuimport android.graphics.Canvas;
229fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhuimport android.graphics.Matrix;
23e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.graphics.Point;
24d80b91ded7ae7e80da1c8466c554e8b03bdfb6b6Jeff Brownimport android.hardware.display.DisplayManagerGlobal;
25d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhuimport android.os.Build;
26e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.os.Environment;
27e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.os.RemoteException;
28e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.os.ServiceManager;
29e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.os.SystemClock;
30d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhuimport android.util.DisplayMetrics;
31e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.util.Log;
32e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.view.Display;
33e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.view.KeyEvent;
34e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.view.Surface;
35e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.view.accessibility.AccessibilityEvent;
36e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.view.accessibility.AccessibilityNodeInfo;
37e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
38e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport com.android.internal.statusbar.IStatusBarService;
39e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport com.android.internal.util.Predicate;
40e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
41e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport java.io.File;
429fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhuimport java.io.FileOutputStream;
439fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhuimport java.io.IOException;
44e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport java.util.ArrayList;
45e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport java.util.HashMap;
46e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport java.util.List;
47e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport java.util.concurrent.TimeoutException;
48e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
49e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu/**
5046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz * UiDevice provides access to state information about the device.
5146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz * You can also use this class to simulate user actions on the device,
5246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz * such as pressing the d-pad or pressing the Home and Menu buttons.
53e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu */
54e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhupublic class UiDevice {
55e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final String LOG_TAG = UiDevice.class.getSimpleName();
56e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
57e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final long DEFAULT_TIMEOUT_MILLIS = 10 * 1000;
58e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
596088c8f9e34e34a4958b1601ed7c1bb34c95da21Adam Momtaz    // Sometimes HOME and BACK key presses will generate no events if already on
606088c8f9e34e34a4958b1601ed7c1bb34c95da21Adam Momtaz    // home page or there is nothing to go back to, Set low timeouts.
616088c8f9e34e34a4958b1601ed7c1bb34c95da21Adam Momtaz    private static final long KEY_PRESS_EVENT_TIMEOUT = 1 * 1000;
626088c8f9e34e34a4958b1601ed7c1bb34c95da21Adam Momtaz
63e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    // store for registered UiWatchers
64e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private final HashMap<String, UiWatcher> mWatchers = new HashMap<String, UiWatcher>();
65e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private final List<String> mWatchersTriggers = new ArrayList<String>();
66e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
67e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    // remember if we're executing in the context of a UiWatcher
68e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private boolean mInWatcherContext = false;
69e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
70e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    // provides access the {@link QueryController} and {@link InteractionController}
71e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private final UiAutomatorBridge mUiAutomationBridge;
72e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
73e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    // reference to self
74e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static UiDevice mDevice;
75e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
76e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private UiDevice() {
77e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mUiAutomationBridge = new UiAutomatorBridge();
78e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mDevice = this;
79e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
80e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
81e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    boolean isInWatcherContext() {
82e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mInWatcherContext;
83e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
84e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
85e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
86e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Provides access the {@link QueryController} and {@link InteractionController}
87e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link UiAutomatorBridge}
88e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
89e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    UiAutomatorBridge getAutomatorBridge() {
90e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge;
91e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
92e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
9346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Retrieves a singleton instance of UiDevice
9446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
95e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return UiDevice instance
96e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
97e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public static UiDevice getInstance() {
98e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (mDevice == null) {
99e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            mDevice = new UiDevice();
100e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
101e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mDevice;
102e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
103e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
104e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
105d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu     * Returns the display size in dp (device-independent pixel)
106d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu     *
107d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu     * The returned display size is adjusted per screen rotation
108d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu     *
10946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return a Point containing the display size in dp
110d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu     */
111d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu    public Point getDisplaySizeDp() {
112e7ef253585665720f73b8b2cefc591ffb3314a68Jeff Brown        Display display = getDefaultDisplay();
113d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu        Point p = new Point();
114d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu        display.getSize(p);
115d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu        DisplayMetrics metrics = new DisplayMetrics();
116d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu        display.getMetrics(metrics);
117d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu        float dpx = p.x / metrics.density;
118d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu        float dpy = p.y / metrics.density;
119d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu        p.x = Math.round(dpx);
120d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu        p.y = Math.round(dpy);
121d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu        return p;
122d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu    }
123d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu
124d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu    /**
12546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Retrieves the product name of the device.
12646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
12746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * This method provides information on what type of device the
12846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * test is running on. If you are trying to test for different types of
12946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * UI screen sizes, your test should use
13046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * {@link UiDevice#getDisplaySizeDp()} instead. This value is the same
13146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * returned by invoking #adb shell getprop ro.product.name.
132d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu     *
13346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return product name of the device
134d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu     */
135d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu    public String getProductName() {
136d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu        return Build.PRODUCT;
137d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu    }
138d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu
139d367349218c5e12feb6f0de732293da45f5e9af3Guang Zhu    /**
14046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Retrieves the text from the last UI traversal event received.
14146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
14246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * You can use this method to read the contents in a WebView container
14346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * because the accessibility framework fires events
14446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * as each text is highlighted. You can write a test to perform
14546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * directional arrow presses to focus on different elements inside a WebView,
14646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * and call this method to get the text from each traversed element.
14746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * If you are testing a view container that can return a reference to a
14846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Document Object Model (DOM) object, your test should use the view's
1493d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * DOM instead.
15046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
15146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return text of the last traversal event, else return an empty string
152e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
153e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getLastTraversedText() {
154e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getQueryController().getLastTraversedText();
155e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
156e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
157e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
15846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Clears the text from the last UI traversal event.
1593d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * See {@link #getLastTraversedText()}.
160e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
161e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void clearLastTraversedText() {
162e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mUiAutomationBridge.getQueryController().clearLastTraversedText();
163e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
164e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
165e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
16646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Simulates a short press on the MENU button.
16746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if successful, else return false
168e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
169e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressMenu() {
17065430f29b4305e4dd8ea42650d40896f34486518Adam Momtaz        waitForIdle();
17165430f29b4305e4dd8ea42650d40896f34486518Adam Momtaz        return mUiAutomationBridge.getInteractionController().sendKeyAndWaitForEvent(
17265430f29b4305e4dd8ea42650d40896f34486518Adam Momtaz                KeyEvent.KEYCODE_MENU, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
17365430f29b4305e4dd8ea42650d40896f34486518Adam Momtaz                KEY_PRESS_EVENT_TIMEOUT);
174e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
175e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
176e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
17746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Simulates a short press on the BACK button.
17846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if successful, else return false
179e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
180e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressBack() {
1816088c8f9e34e34a4958b1601ed7c1bb34c95da21Adam Momtaz        waitForIdle();
1826088c8f9e34e34a4958b1601ed7c1bb34c95da21Adam Momtaz        return mUiAutomationBridge.getInteractionController().sendKeyAndWaitForEvent(
1836088c8f9e34e34a4958b1601ed7c1bb34c95da21Adam Momtaz                KeyEvent.KEYCODE_BACK, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
1846088c8f9e34e34a4958b1601ed7c1bb34c95da21Adam Momtaz                KEY_PRESS_EVENT_TIMEOUT);
185e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
186e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
187e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
18846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Simulates a short press on the HOME button.
18946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if successful, else return false
190e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
191e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressHome() {
1926088c8f9e34e34a4958b1601ed7c1bb34c95da21Adam Momtaz        waitForIdle();
1936088c8f9e34e34a4958b1601ed7c1bb34c95da21Adam Momtaz        return mUiAutomationBridge.getInteractionController().sendKeyAndWaitForEvent(
1946088c8f9e34e34a4958b1601ed7c1bb34c95da21Adam Momtaz                KeyEvent.KEYCODE_HOME, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
1956088c8f9e34e34a4958b1601ed7c1bb34c95da21Adam Momtaz                KEY_PRESS_EVENT_TIMEOUT);
196e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
197e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
198e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
19946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Simulates a short press on the SEARCH button.
20046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if successful, else return false
201e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
202e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressSearch() {
203e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return pressKeyCode(KeyEvent.KEYCODE_SEARCH);
204e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
205e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
206e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
20746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Simulates a short press on the CENTER button.
20846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if successful, else return false
209e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
210e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressDPadCenter() {
211e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return pressKeyCode(KeyEvent.KEYCODE_DPAD_CENTER);
212e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
213e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
214e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
21546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Simulates a short press on the DOWN button.
21646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if successful, else return false
217e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
218e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressDPadDown() {
219e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return pressKeyCode(KeyEvent.KEYCODE_DPAD_DOWN);
220e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
221e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
222e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
22346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Simulates a short press on the UP button.
22446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if successful, else return false
225e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
226e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressDPadUp() {
227e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return pressKeyCode(KeyEvent.KEYCODE_DPAD_UP);
228e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
229e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
230e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
23146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Simulates a short press on the LEFT button.
23246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if successful, else return false
233e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
234e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressDPadLeft() {
235e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return pressKeyCode(KeyEvent.KEYCODE_DPAD_LEFT);
236e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
237e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
238e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
23946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Simulates a short press on the RIGHT button.
24046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if successful, else return false
241e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
242e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressDPadRight() {
243e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return pressKeyCode(KeyEvent.KEYCODE_DPAD_RIGHT);
244e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
245e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
246e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
24746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Simulates a short press on the DELETE key.
24846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if successful, else return false
249e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
250e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressDelete() {
251e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return pressKeyCode(KeyEvent.KEYCODE_DEL);
252e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
253e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
254e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
25546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Simulates a short press on the ENTER key.
25646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if successful, else return false
257e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
258e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressEnter() {
259e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return pressKeyCode(KeyEvent.KEYCODE_ENTER);
260e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
261e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
262e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
26346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Simulates a short press using a key code.
26446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
26546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * See {@link KeyEvent}
26646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if successful, else return false
267e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
268e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressKeyCode(int keyCode) {
269e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        waitForIdle();
270e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getInteractionController().sendKey(keyCode, 0);
271e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
272e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
273e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
27446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Simulates a short press using a key code.
27546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
27646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * See {@link KeyEvent}.
27746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @param keyCode the key code of the event.
27846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @param metaState an integer in which each bit set to 1 represents a pressed meta key
27946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if successful, else return false
280e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
281e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressKeyCode(int keyCode, int metaState) {
282e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        waitForIdle();
283e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getInteractionController().sendKey(keyCode, metaState);
284e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
285e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
286e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
28746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Simulates a short press on the Recent Apps button.
28846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
28946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if successful, else return false
290e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws RemoteException
291e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
292e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressRecentApps() throws RemoteException {
293e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        waitForIdle();
294e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        final IStatusBarService statusBar = IStatusBarService.Stub.asInterface(
295e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
296e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
297e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (statusBar != null) {
298e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            statusBar.toggleRecentApps();
299e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return true;
300e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
301e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return false;
302e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
303e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
304e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
305835cffbc85a560a2454fd417073a127895335122Jeff Brown     * Gets the width of the display, in pixels. The width and height details
306835cffbc85a560a2454fd417073a127895335122Jeff Brown     * are reported based on the current orientation of the display.
307835cffbc85a560a2454fd417073a127895335122Jeff Brown     * @return width in pixels or zero on failure
308835cffbc85a560a2454fd417073a127895335122Jeff Brown     */
309835cffbc85a560a2454fd417073a127895335122Jeff Brown    public int getDisplayWidth() {
310e7ef253585665720f73b8b2cefc591ffb3314a68Jeff Brown        Display display = getDefaultDisplay();
31159b4c820f01d48a41d944239290028411528db25Guang Zhu        Point p = new Point();
31259b4c820f01d48a41d944239290028411528db25Guang Zhu        display.getSize(p);
31359b4c820f01d48a41d944239290028411528db25Guang Zhu        return p.x;
314835cffbc85a560a2454fd417073a127895335122Jeff Brown    }
315835cffbc85a560a2454fd417073a127895335122Jeff Brown
316835cffbc85a560a2454fd417073a127895335122Jeff Brown    /**
3173d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Gets the height of the display, in pixels. The size is adjusted based
3183d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * on the current orientation of the display.
319e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return height in pixels or zero on failure
320e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
321e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public int getDisplayHeight() {
322e7ef253585665720f73b8b2cefc591ffb3314a68Jeff Brown        Display display = getDefaultDisplay();
32359b4c820f01d48a41d944239290028411528db25Guang Zhu        Point p = new Point();
32459b4c820f01d48a41d944239290028411528db25Guang Zhu        display.getSize(p);
32559b4c820f01d48a41d944239290028411528db25Guang Zhu        return p.y;
326e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
327e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
328e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3294ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * Perform a click at arbitrary coordinates specified by the user
3303d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
331e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param x coordinate
332e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param y coordinate
3334ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @return true if the click succeeded else false
334e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
3354ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public boolean click(int x, int y) {
336e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (x >= getDisplayWidth() || y >= getDisplayHeight()) {
337e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return (false);
338e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
3394ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return getAutomatorBridge().getInteractionController().click(x, y);
340e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
341e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
342e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
343e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Performs a swipe from one coordinate to another using the number of steps
344467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * to determine smoothness and speed. Each step execution is throttled to 5ms
345467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * per step. So for a 100 steps, the swipe will take about 1/2 second to complete.
3463d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
347e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param startX
348e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param startY
349e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param endX
350e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param endY
351e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param steps is the number of move steps sent to the system
352e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return false if the operation fails or the coordinates are invalid
353e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
354e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipe(int startX, int startY, int endX, int endY, int steps) {
355e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getInteractionController()
356e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                .scrollSwipe(startX, startY, endX, endY, steps);
357e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
358e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
359e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3603d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Performs a swipe between points in the Point array. Each step execution is throttled
361467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * to 5ms per step. So for a 100 steps, the swipe will take about 1/2 second to complete
3623d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
363e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param segments is Point array containing at least one Point object
364e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param segmentSteps steps to inject between two Points
365e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on success
366e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
367e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipe(Point[] segments, int segmentSteps) {
368e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getInteractionController().swipe(segments, segmentSteps);
369e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
370e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
37146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz    /**
37246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Waits for the current application to idle.
37346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Default wait timeout is 10 seconds
37446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     */
375e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void waitForIdle() {
376e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        waitForIdle(DEFAULT_TIMEOUT_MILLIS);
377e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
378e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
37946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz    /**
38046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Waits for the current application to idle.
38146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @param timeout in milliseconds
38246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     */
383e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void waitForIdle(long time) {
384e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mUiAutomationBridge.waitForIdle(time);
385e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
386e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
387e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
38846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Retrieves the last activity to report accessibility events.
3890bb49719aab950cc522c2c56f7adce750c656012Adam Momtaz     * @deprecated The results returned should be considered unreliable
390e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return String name of activity
391e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
3920bb49719aab950cc522c2c56f7adce750c656012Adam Momtaz    @Deprecated
393e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getCurrentActivityName() {
394e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getQueryController().getCurrentActivityName();
395e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
396e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
397e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
39846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Retrieves the name of the last package to report accessibility events.
399e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return String name of package
400e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
401e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getCurrentPackageName() {
402e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getQueryController().getCurrentPackageName();
403e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
404e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
405e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
40646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Registers a {@link UiWatcher} to run automatically when the testing framework is unable to
40746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * find a match using a {@link UiSelector}. See {@link #runWatchers()}
4083d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
40946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @param name to register the UiWatcher
410e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param watcher {@link UiWatcher}
411e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
412e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void registerWatcher(String name, UiWatcher watcher) {
413e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (mInWatcherContext) {
414e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new IllegalStateException("Cannot register new watcher from within another");
415e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
416e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mWatchers.put(name, watcher);
417e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
418e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
419e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
42046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Removes a previously registered {@link UiWatcher}.
4213d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
42246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * See {@link #registerWatcher(String, UiWatcher)}
42346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @param name used to register the UiWatcher
424e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiAutomationException
425e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
426e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void removeWatcher(String name) {
427e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (mInWatcherContext) {
428e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new IllegalStateException("Cannot remove a watcher from within another");
429e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
430e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mWatchers.remove(name);
431e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
432e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
433e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
43446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * This method forces all registered watchers to run.
43546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * See {@link #registerWatcher(String, UiWatcher)}
436e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
437e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void runWatchers() {
438e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (mInWatcherContext) {
439e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return;
440e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
441e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
442e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        for (String watcherName : mWatchers.keySet()) {
443e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            UiWatcher watcher = mWatchers.get(watcherName);
444e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if (watcher != null) {
445e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                try {
446e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    mInWatcherContext = true;
447e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    if (watcher.checkForCondition()) {
448e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                        setWatcherTriggered(watcherName);
449e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    }
450e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                } catch (Exception e) {
451e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    Log.e(LOG_TAG, "Exceuting watcher: " + watcherName, e);
452e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                } finally {
453e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    mInWatcherContext = false;
454e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                }
455e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
456e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
457e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
458e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
459e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
46046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Resets a {@link UiWatcher} that has been triggered.
46146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * If a UiWatcher runs and its {@link UiWatcher#checkForCondition()} call
46246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * returned <code>true</code>, then the UiWatcher is considered triggered.
46346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * See {@link #registerWatcher(String, UiWatcher)}
464e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
465e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void resetWatcherTriggers() {
466e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mWatchersTriggers.clear();
467e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
468e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
469e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
47046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Checks if a specific registered  {@link UiWatcher} has triggered.
47146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * See {@link #registerWatcher(String, UiWatcher)}. If a UiWatcher runs and its
47246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * {@link UiWatcher#checkForCondition()} call returned <code>true</code>, then
47346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * the UiWatcher is considered triggered. This is helpful if a watcher is detecting errors
4743d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * from ANR or crash dialogs and the test needs to know if a UiWatcher has been triggered.
47546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
47646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @param watcherName
47746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if triggered else false
478e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
479e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean hasWatcherTriggered(String watcherName) {
480e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mWatchersTriggers.contains(watcherName);
481e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
482e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
483e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
48446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Checks if any registered {@link UiWatcher} have triggered.
48546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
48646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * See {@link #registerWatcher(String, UiWatcher)}
48746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * See {@link #hasWatcherTriggered(String)}
488e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
489e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean hasAnyWatcherTriggered() {
490e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mWatchersTriggers.size() > 0;
491e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
492e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
49346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz    /**
49446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Used internally by this class to set a {@link UiWatcher} state as triggered.
49546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @param watcherName
49646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     */
497e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private void setWatcherTriggered(String watcherName) {
498e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (!hasWatcherTriggered(watcherName)) {
499e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            mWatchersTriggers.add(watcherName);
500e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
501e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
502e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
503e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5043d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the device is in its natural orientation. This is determined by checking if the
5053d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * orientation is at 0 or 180 degrees.
506e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is in natural orientation
507e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
5083d50587be8ff021369c90554d814839335b445b0Adam Momtaz    public boolean isNaturalOrientation() {
509e7ef253585665720f73b8b2cefc591ffb3314a68Jeff Brown        Display display = getDefaultDisplay();
5103d50587be8ff021369c90554d814839335b445b0Adam Momtaz        return display.getRotation() == Surface.ROTATION_0 ||
5113d50587be8ff021369c90554d814839335b445b0Adam Momtaz                display.getRotation() == Surface.ROTATION_180;
512e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
513e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
514e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
51559b4c820f01d48a41d944239290028411528db25Guang Zhu     * Returns the current rotation of the display, as defined in {@link Surface}
51659b4c820f01d48a41d944239290028411528db25Guang Zhu     * @return
51759b4c820f01d48a41d944239290028411528db25Guang Zhu     */
51859b4c820f01d48a41d944239290028411528db25Guang Zhu    public int getDisplayRotation() {
51959b4c820f01d48a41d944239290028411528db25Guang Zhu        return getDefaultDisplay().getRotation();
52059b4c820f01d48a41d944239290028411528db25Guang Zhu    }
52159b4c820f01d48a41d944239290028411528db25Guang Zhu
52259b4c820f01d48a41d944239290028411528db25Guang Zhu    /**
523e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Disables the sensors and freezes the device rotation at its
524e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * current rotation state.
525e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws RemoteException
526e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
527e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void freezeRotation() throws RemoteException {
528e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getAutomatorBridge().getInteractionController().freezeRotation();
529e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
530e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
531e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5323d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Re-enables the sensors and un-freezes the device rotation allowing its contents
53346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * to rotate with the device physical rotation. During a test execution, it is best to
53446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * keep the device frozen in a specific orientation until the test case execution has completed.
535e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws RemoteException
536e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
537e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void unfreezeRotation() throws RemoteException {
538e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getAutomatorBridge().getInteractionController().unfreezeRotation();
539e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
540e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
541e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
54246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Simulates orienting the device to the left and also freezes rotation
54346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * by disabling the sensors.
54446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
54546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * If you want to un-freeze the rotation and re-enable the sensors
54646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * see {@link #unfreezeRotation()}.
547e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws RemoteException
548e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
5493d50587be8ff021369c90554d814839335b445b0Adam Momtaz    public void setOrientationLeft() throws RemoteException {
550e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getAutomatorBridge().getInteractionController().setRotationLeft();
551e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
552e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
553e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
55446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Simulates orienting the device to the right and also freezes rotation
55546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * by disabling the sensors.
55646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
55746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * If you want to un-freeze the rotation and re-enable the sensors
55846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * see {@link #unfreezeRotation()}.
559e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws RemoteException
560e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
5613d50587be8ff021369c90554d814839335b445b0Adam Momtaz    public void setOrientationRight() throws RemoteException {
562e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getAutomatorBridge().getInteractionController().setRotationRight();
563e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
564e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
565e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
56646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Simulates orienting the device into its natural orientation and also freezes rotation
56746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * by disabling the sensors.
56846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
56946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * If you want to un-freeze the rotation and re-enable the sensors
57046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * see {@link #unfreezeRotation()}.
571e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws RemoteException
572e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
5733d50587be8ff021369c90554d814839335b445b0Adam Momtaz    public void setOrientationNatural() throws RemoteException {
574e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getAutomatorBridge().getInteractionController().setRotationNatural();
575e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
576e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
577e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
57846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * This method simulates pressing the power button if the screen is OFF else
57946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * it does nothing if the screen is already ON.
58046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
58146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * If the screen was OFF and it just got turned ON, this method will insert a 500ms delay
58246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * to allow the device time to wake up and accept input.
583e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws RemoteException
584e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
585e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void wakeUp() throws RemoteException {
586e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(getAutomatorBridge().getInteractionController().wakeDevice()) {
587e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // sync delay to allow the window manager to start accepting input
588e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // after the device is awakened.
589e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            SystemClock.sleep(500);
590e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
591e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
592e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
593e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
59446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Checks the power manager if the screen is ON.
59546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
596e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if the screen is ON else false
597e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws RemoteException
598e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
599e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isScreenOn() throws RemoteException {
600e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getAutomatorBridge().getInteractionController().isScreenOn();
601e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
602e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
603e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
604e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * This method simply presses the power button if the screen is ON else
605e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * it does nothing if the screen is already OFF.
60646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
607e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws RemoteException
608e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
609e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void sleep() throws RemoteException {
610e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getAutomatorBridge().getInteractionController().sleepDevice();
611e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
612e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
613e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
614e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Helper method used for debugging to dump the current window's layout hierarchy.
615e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * The file root location is /data/local/tmp
6163d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
617e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param fileName
618e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
619e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void dumpWindowHierarchy(String fileName) {
620e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo root =
621e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                getAutomatorBridge().getQueryController().getAccessibilityRootNode();
622e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(root != null) {
623e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            AccessibilityNodeInfoDumper.dumpWindowToFile(
624e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    root, new File(new File(Environment.getDataDirectory(),
625e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                            "local/tmp"), fileName));
626e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
627e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
628e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
629e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
63046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Waits for a window content update event to occur.
631e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
63246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * If a package name for the window is specified, but the current window
63346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * does not have the same package name, the function returns immediately.
634e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
63546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @param packageName the specified window package name (can be <code>null</code>).
63646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *        If <code>null</code>, a window update from any front-end window will end the wait
637e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param timeout the timeout for the wait
638e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
63946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if a window update occurred, false if timeout has elapsed or if the current
64046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *         window does not have the specified package name
641e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
642e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean waitForWindowUpdate(final String packageName, long timeout) {
643e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (packageName != null) {
644e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if (!packageName.equals(getCurrentPackageName())) {
645e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                return false;
646e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
647e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
648e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Runnable emptyRunnable = new Runnable() {
649e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            @Override
650e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            public void run() {
651e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
652e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        };
653e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Predicate<AccessibilityEvent> checkWindowUpdate = new Predicate<AccessibilityEvent>() {
654e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            @Override
655e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            public boolean apply(AccessibilityEvent t) {
656e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                if (t.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {
657e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    return packageName == null || packageName.equals(t.getPackageName());
658e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                }
659e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                return false;
660e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
661e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        };
662e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        try {
663e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            getAutomatorBridge().executeCommandAndWaitForAccessibilityEvent(
664e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    emptyRunnable, checkWindowUpdate, timeout);
665e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        } catch (TimeoutException e) {
666e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false;
667e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        } catch (Exception e) {
668e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            Log.e(LOG_TAG, "waitForWindowUpdate: general exception from bridge", e);
669e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false;
670e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
671e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return true;
672e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
673e7ef253585665720f73b8b2cefc591ffb3314a68Jeff Brown
674e7ef253585665720f73b8b2cefc591ffb3314a68Jeff Brown    private static Display getDefaultDisplay() {
675d80b91ded7ae7e80da1c8466c554e8b03bdfb6b6Jeff Brown        return DisplayManagerGlobal.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY);
676e7ef253585665720f73b8b2cefc591ffb3314a68Jeff Brown    }
6779fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu
6789fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu    /**
6799fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu     * @return the current display rotation in degrees
6809fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu     */
6819fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu    private static float getDegreesForRotation(int value) {
6829fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        switch (value) {
6839fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        case Surface.ROTATION_90:
6849fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            return 360f - 90f;
6859fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        case Surface.ROTATION_180:
6869fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            return 360f - 180f;
6879fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        case Surface.ROTATION_270:
6889fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            return 360f - 270f;
6899fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        }
6909fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        return 0f;
6919fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu    }
6929fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu
6939fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu    /**
6949fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu     * Take a screenshot of current window and store it as PNG
6959fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu     *
6969fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu     * Default scale of 1.0f (original size) and 90% quality is used
697ebc5898540750b5758a21d5f98a2c24f2cf8b75bGuang Zhu     * The screenshot is adjusted per screen rotation
6989fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu     *
6999fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu     * @param storePath where the PNG should be written to
7009fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu     * @return
7019fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu     */
7029fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu    public boolean takeScreenshot(File storePath) {
7039fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        return takeScreenshot(storePath, 1.0f, 90);
7049fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu    }
7059fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu
7069fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu    /**
7079fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu     * Take a screenshot of current window and store it as PNG
7089fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu     *
709ebc5898540750b5758a21d5f98a2c24f2cf8b75bGuang Zhu     * The screenshot is adjusted per screen rotation
7109fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu     *
7119fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu     * @param storePath where the PNG should be written to
7129fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu     * @param scale scale the screenshot down if needed; 1.0f for original size
7139fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu     * @param quality quality of the PNG compression; range: 0-100
7149fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu     * @return
7159fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu     */
7169fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu    public boolean takeScreenshot(File storePath, float scale, int quality) {
7179fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        // This is from com.android.systemui.screenshot.GlobalScreenshot#takeScreenshot
7189fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        // We need to orient the screenshot correctly (and the Surface api seems to take screenshots
7199fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        // only in the natural orientation of the device :!)
7209fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        DisplayMetrics displayMetrics = new DisplayMetrics();
7219fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        Display display = getDefaultDisplay();
7229fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        display.getRealMetrics(displayMetrics);
7239fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        float[] dims = {displayMetrics.widthPixels, displayMetrics.heightPixels};
7249fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        float degrees = getDegreesForRotation(display.getRotation());
7259fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        boolean requiresRotation = (degrees > 0);
7269fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        Matrix matrix = new Matrix();
7279fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        matrix.reset();
7289fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        if (scale != 1.0f) {
7299fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            matrix.setScale(scale, scale);
7309fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        }
7319fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        if (requiresRotation) {
7329fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            // Get the dimensions of the device in its native orientation
7339fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            matrix.preRotate(-degrees);
7349fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        }
7359fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        matrix.mapPoints(dims);
7369fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        dims[0] = Math.abs(dims[0]);
7379fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        dims[1] = Math.abs(dims[1]);
7389fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu
7399fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        // Take the screenshot
7409fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        Bitmap screenShot = Surface.screenshot((int) dims[0], (int) dims[1]);
7419fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        if (screenShot == null) {
7429fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            return false;
7439fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        }
7449fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu
7459fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        if (requiresRotation) {
7469fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            // Rotate the screenshot to the current orientation
7479fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            int width = displayMetrics.widthPixels;
7489fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            int height = displayMetrics.heightPixels;
7499fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            if (scale != 1.0f) {
7509fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu                width = Math.round(scale * width);
7519fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu                height = Math.round(scale * height);
7529fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            }
7539fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            Bitmap ss = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
7549fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            Canvas c = new Canvas(ss);
7559fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            c.translate(ss.getWidth() / 2, ss.getHeight() / 2);
7569fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            c.rotate(degrees);
7579fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            c.translate(-dims[0] / 2, -dims[1] / 2);
7589fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            c.drawBitmap(screenShot, 0, 0, null);
7599fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            c.setBitmap(null);
7609fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            screenShot = ss;
7619fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        }
7629fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu
7639fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        // Optimizations
7649fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        screenShot.setHasAlpha(false);
7659fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu
7669fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        try {
7679fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            FileOutputStream fos = new FileOutputStream(storePath);
7689fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            screenShot.compress(Bitmap.CompressFormat.PNG, quality, fos);
7699fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            fos.flush();
7709fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            fos.close();
7719fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        } catch (IOException ioe) {
7729fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            Log.e(LOG_TAG, "failed to save screen shot to file", ioe);
7739fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            return false;
7749fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        } finally {
7759fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu            screenShot.recycle();
7769fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        }
7779fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu        return true;
7789fe7f8d9e5f83bd13bc2b06c672d3cd93f619be0Guang Zhu    }
779e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu}
780