1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.uiautomator.core;
18 
19 import android.app.UiAutomation;
20 import android.app.UiAutomation.AccessibilityEventFilter;
21 import android.graphics.Point;
22 import android.os.Build;
23 import android.os.Environment;
24 import android.os.RemoteException;
25 import android.os.SystemClock;
26 import android.util.DisplayMetrics;
27 import android.util.Log;
28 import android.view.Display;
29 import android.view.KeyEvent;
30 import android.view.Surface;
31 import android.view.accessibility.AccessibilityEvent;
32 import android.view.accessibility.AccessibilityNodeInfo;
33 
34 import java.io.File;
35 import java.util.ArrayList;
36 import java.util.HashMap;
37 import java.util.List;
38 import java.util.concurrent.TimeoutException;
39 
40 /**
41  * UiDevice provides access to state information about the device.
42  * You can also use this class to simulate user actions on the device,
43  * such as pressing the d-pad or pressing the Home and Menu buttons.
44  * @since API Level 16
45  * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
46  * Android Testing Support Library.
47  */
48 @Deprecated
49 public class UiDevice {
50     private static final String LOG_TAG = UiDevice.class.getSimpleName();
51 
52     // Sometimes HOME and BACK key presses will generate no events if already on
53     // home page or there is nothing to go back to, Set low timeouts.
54     private static final long KEY_PRESS_EVENT_TIMEOUT = 1 * 1000;
55 
56     // store for registered UiWatchers
57     private final HashMap<String, UiWatcher> mWatchers = new HashMap<String, UiWatcher>();
58     private final List<String> mWatchersTriggers = new ArrayList<String>();
59 
60     // remember if we're executing in the context of a UiWatcher
61     private boolean mInWatcherContext = false;
62 
63     // provides access the {@link QueryController} and {@link InteractionController}
64     private UiAutomatorBridge mUiAutomationBridge;
65 
66     // reference to self
67     private static UiDevice sDevice;
68 
UiDevice()69     private UiDevice() {
70         /* hide constructor */
71     }
72 
73     /**
74      * @hide
75      */
initialize(UiAutomatorBridge uiAutomatorBridge)76     public void initialize(UiAutomatorBridge uiAutomatorBridge) {
77         mUiAutomationBridge = uiAutomatorBridge;
78     }
79 
isInWatcherContext()80     boolean isInWatcherContext() {
81         return mInWatcherContext;
82     }
83 
84     /**
85      * Provides access the {@link QueryController} and {@link InteractionController}
86      * @return {@link ShellUiAutomatorBridge}
87      */
getAutomatorBridge()88     UiAutomatorBridge getAutomatorBridge() {
89         if (mUiAutomationBridge == null) {
90             throw new RuntimeException("UiDevice not initialized");
91         }
92         return mUiAutomationBridge;
93     }
94 
95     /**
96      * Enables or disables layout hierarchy compression.
97      *
98      * If compression is enabled, the layout hierarchy derived from the Acessibility
99      * framework will only contain nodes that are important for uiautomator
100      * testing. Any unnecessary surrounding layout nodes that make viewing
101      * and searching the hierarchy inefficient are removed.
102      *
103      * @param compressed true to enable compression; else, false to disable
104      * @since API Level 18
105      */
setCompressedLayoutHeirarchy(boolean compressed)106     public void setCompressedLayoutHeirarchy(boolean compressed) {
107         getAutomatorBridge().setCompressedLayoutHierarchy(compressed);
108     }
109 
110     /**
111      * Retrieves a singleton instance of UiDevice
112      *
113      * @return UiDevice instance
114      * @since API Level 16
115      */
getInstance()116     public static UiDevice getInstance() {
117         if (sDevice == null) {
118             sDevice = new UiDevice();
119         }
120         return sDevice;
121     }
122 
123     /**
124      * Returns the display size in dp (device-independent pixel)
125      *
126      * The returned display size is adjusted per screen rotation. Also this will return the actual
127      * size of the screen, rather than adjusted per system decorations (like status bar).
128      *
129      * @return a Point containing the display size in dp
130      */
getDisplaySizeDp()131     public Point getDisplaySizeDp() {
132         Tracer.trace();
133         Display display = getAutomatorBridge().getDefaultDisplay();
134         Point p = new Point();
135         display.getRealSize(p);
136         DisplayMetrics metrics = new DisplayMetrics();
137         display.getRealMetrics(metrics);
138         float dpx = p.x / metrics.density;
139         float dpy = p.y / metrics.density;
140         p.x = Math.round(dpx);
141         p.y = Math.round(dpy);
142         return p;
143     }
144 
145     /**
146      * Retrieves the product name of the device.
147      *
148      * This method provides information on what type of device the test is running on. This value is
149      * the same as returned by invoking #adb shell getprop ro.product.name.
150      *
151      * @return product name of the device
152      * @since API Level 17
153      */
getProductName()154     public String getProductName() {
155         Tracer.trace();
156         return Build.PRODUCT;
157     }
158 
159     /**
160      * Retrieves the text from the last UI traversal event received.
161      *
162      * You can use this method to read the contents in a WebView container
163      * because the accessibility framework fires events
164      * as each text is highlighted. You can write a test to perform
165      * directional arrow presses to focus on different elements inside a WebView,
166      * and call this method to get the text from each traversed element.
167      * If you are testing a view container that can return a reference to a
168      * Document Object Model (DOM) object, your test should use the view's
169      * DOM instead.
170      *
171      * @return text of the last traversal event, else return an empty string
172      * @since API Level 16
173      */
getLastTraversedText()174     public String getLastTraversedText() {
175         Tracer.trace();
176         return getAutomatorBridge().getQueryController().getLastTraversedText();
177     }
178 
179     /**
180      * Clears the text from the last UI traversal event.
181      * See {@link #getLastTraversedText()}.
182      * @since API Level 16
183      */
clearLastTraversedText()184     public void clearLastTraversedText() {
185         Tracer.trace();
186         getAutomatorBridge().getQueryController().clearLastTraversedText();
187     }
188 
189     /**
190      * Simulates a short press on the MENU button.
191      * @return true if successful, else return false
192      * @since API Level 16
193      */
pressMenu()194     public boolean pressMenu() {
195         Tracer.trace();
196         waitForIdle();
197         return getAutomatorBridge().getInteractionController().sendKeyAndWaitForEvent(
198                 KeyEvent.KEYCODE_MENU, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
199                 KEY_PRESS_EVENT_TIMEOUT);
200     }
201 
202     /**
203      * Simulates a short press on the BACK button.
204      * @return true if successful, else return false
205      * @since API Level 16
206      */
pressBack()207     public boolean pressBack() {
208         Tracer.trace();
209         waitForIdle();
210         return getAutomatorBridge().getInteractionController().sendKeyAndWaitForEvent(
211                 KeyEvent.KEYCODE_BACK, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
212                 KEY_PRESS_EVENT_TIMEOUT);
213     }
214 
215     /**
216      * Simulates a short press on the HOME button.
217      * @return true if successful, else return false
218      * @since API Level 16
219      */
pressHome()220     public boolean pressHome() {
221         Tracer.trace();
222         waitForIdle();
223         return getAutomatorBridge().getInteractionController().sendKeyAndWaitForEvent(
224                 KeyEvent.KEYCODE_HOME, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
225                 KEY_PRESS_EVENT_TIMEOUT);
226     }
227 
228     /**
229      * Simulates a short press on the SEARCH button.
230      * @return true if successful, else return false
231      * @since API Level 16
232      */
pressSearch()233     public boolean pressSearch() {
234         Tracer.trace();
235         return pressKeyCode(KeyEvent.KEYCODE_SEARCH);
236     }
237 
238     /**
239      * Simulates a short press on the CENTER button.
240      * @return true if successful, else return false
241      * @since API Level 16
242      */
pressDPadCenter()243     public boolean pressDPadCenter() {
244         Tracer.trace();
245         return pressKeyCode(KeyEvent.KEYCODE_DPAD_CENTER);
246     }
247 
248     /**
249      * Simulates a short press on the DOWN button.
250      * @return true if successful, else return false
251      * @since API Level 16
252      */
pressDPadDown()253     public boolean pressDPadDown() {
254         Tracer.trace();
255         return pressKeyCode(KeyEvent.KEYCODE_DPAD_DOWN);
256     }
257 
258     /**
259      * Simulates a short press on the UP button.
260      * @return true if successful, else return false
261      * @since API Level 16
262      */
pressDPadUp()263     public boolean pressDPadUp() {
264         Tracer.trace();
265         return pressKeyCode(KeyEvent.KEYCODE_DPAD_UP);
266     }
267 
268     /**
269      * Simulates a short press on the LEFT button.
270      * @return true if successful, else return false
271      * @since API Level 16
272      */
pressDPadLeft()273     public boolean pressDPadLeft() {
274         Tracer.trace();
275         return pressKeyCode(KeyEvent.KEYCODE_DPAD_LEFT);
276     }
277 
278     /**
279      * Simulates a short press on the RIGHT button.
280      * @return true if successful, else return false
281      * @since API Level 16
282      */
pressDPadRight()283     public boolean pressDPadRight() {
284         Tracer.trace();
285         return pressKeyCode(KeyEvent.KEYCODE_DPAD_RIGHT);
286     }
287 
288     /**
289      * Simulates a short press on the DELETE key.
290      * @return true if successful, else return false
291      * @since API Level 16
292      */
pressDelete()293     public boolean pressDelete() {
294         Tracer.trace();
295         return pressKeyCode(KeyEvent.KEYCODE_DEL);
296     }
297 
298     /**
299      * Simulates a short press on the ENTER key.
300      * @return true if successful, else return false
301      * @since API Level 16
302      */
pressEnter()303     public boolean pressEnter() {
304         Tracer.trace();
305         return pressKeyCode(KeyEvent.KEYCODE_ENTER);
306     }
307 
308     /**
309      * Simulates a short press using a key code.
310      *
311      * See {@link KeyEvent}
312      * @return true if successful, else return false
313      * @since API Level 16
314      */
pressKeyCode(int keyCode)315     public boolean pressKeyCode(int keyCode) {
316         Tracer.trace(keyCode);
317         waitForIdle();
318         return getAutomatorBridge().getInteractionController().sendKey(keyCode, 0);
319     }
320 
321     /**
322      * Simulates a short press using a key code.
323      *
324      * See {@link KeyEvent}.
325      * @param keyCode the key code of the event.
326      * @param metaState an integer in which each bit set to 1 represents a pressed meta key
327      * @return true if successful, else return false
328      * @since API Level 16
329      */
pressKeyCode(int keyCode, int metaState)330     public boolean pressKeyCode(int keyCode, int metaState) {
331         Tracer.trace(keyCode, metaState);
332         waitForIdle();
333         return getAutomatorBridge().getInteractionController().sendKey(keyCode, metaState);
334     }
335 
336     /**
337      * Simulates a short press on the Recent Apps button.
338      *
339      * @return true if successful, else return false
340      * @throws RemoteException
341      * @since API Level 16
342      */
pressRecentApps()343     public boolean pressRecentApps() throws RemoteException {
344         Tracer.trace();
345         waitForIdle();
346         return getAutomatorBridge().getInteractionController().toggleRecentApps();
347     }
348 
349     /**
350      * Opens the notification shade.
351      *
352      * @return true if successful, else return false
353      * @since API Level 18
354      */
openNotification()355     public boolean openNotification() {
356         Tracer.trace();
357         waitForIdle();
358         return  getAutomatorBridge().getInteractionController().openNotification();
359     }
360 
361     /**
362      * Opens the Quick Settings shade.
363      *
364      * @return true if successful, else return false
365      * @since API Level 18
366      */
openQuickSettings()367     public boolean openQuickSettings() {
368         Tracer.trace();
369         waitForIdle();
370         return getAutomatorBridge().getInteractionController().openQuickSettings();
371     }
372 
373     /**
374      * Gets the width of the display, in pixels. The width and height details
375      * are reported based on the current orientation of the display.
376      * @return width in pixels or zero on failure
377      * @since API Level 16
378      */
getDisplayWidth()379     public int getDisplayWidth() {
380         Tracer.trace();
381         Display display = getAutomatorBridge().getDefaultDisplay();
382         Point p = new Point();
383         display.getSize(p);
384         return p.x;
385     }
386 
387     /**
388      * Gets the height of the display, in pixels. The size is adjusted based
389      * on the current orientation of the display.
390      * @return height in pixels or zero on failure
391      * @since API Level 16
392      */
getDisplayHeight()393     public int getDisplayHeight() {
394         Tracer.trace();
395         Display display = getAutomatorBridge().getDefaultDisplay();
396         Point p = new Point();
397         display.getSize(p);
398         return p.y;
399     }
400 
401     /**
402      * Perform a click at arbitrary coordinates specified by the user
403      *
404      * @param x coordinate
405      * @param y coordinate
406      * @return true if the click succeeded else false
407      * @since API Level 16
408      */
click(int x, int y)409     public boolean click(int x, int y) {
410         Tracer.trace(x, y);
411         if (x >= getDisplayWidth() || y >= getDisplayHeight()) {
412             return (false);
413         }
414         return getAutomatorBridge().getInteractionController().clickNoSync(x, y);
415     }
416 
417     /**
418      * Performs a swipe from one coordinate to another using the number of steps
419      * to determine smoothness and speed. Each step execution is throttled to 5ms
420      * per step. So for a 100 steps, the swipe will take about 1/2 second to complete.
421      *
422      * @param startX
423      * @param startY
424      * @param endX
425      * @param endY
426      * @param steps is the number of move steps sent to the system
427      * @return false if the operation fails or the coordinates are invalid
428      * @since API Level 16
429      */
swipe(int startX, int startY, int endX, int endY, int steps)430     public boolean swipe(int startX, int startY, int endX, int endY, int steps) {
431         Tracer.trace(startX, startY, endX, endY, steps);
432         return getAutomatorBridge().getInteractionController()
433                 .swipe(startX, startY, endX, endY, steps);
434     }
435 
436     /**
437      * Performs a swipe from one coordinate to another coordinate. You can control
438      * the smoothness and speed of the swipe by specifying the number of steps.
439      * Each step execution is throttled to 5 milliseconds per step, so for a 100
440      * steps, the swipe will take around 0.5 seconds to complete.
441      *
442      * @param startX X-axis value for the starting coordinate
443      * @param startY Y-axis value for the starting coordinate
444      * @param endX X-axis value for the ending coordinate
445      * @param endY Y-axis value for the ending coordinate
446      * @param steps is the number of steps for the swipe action
447      * @return true if swipe is performed, false if the operation fails
448      * or the coordinates are invalid
449      * @since API Level 18
450      */
drag(int startX, int startY, int endX, int endY, int steps)451     public boolean drag(int startX, int startY, int endX, int endY, int steps) {
452         Tracer.trace(startX, startY, endX, endY, steps);
453         return getAutomatorBridge().getInteractionController()
454                 .swipe(startX, startY, endX, endY, steps, true);
455     }
456 
457     /**
458      * Performs a swipe between points in the Point array. Each step execution is throttled
459      * to 5ms per step. So for a 100 steps, the swipe will take about 1/2 second to complete
460      *
461      * @param segments is Point array containing at least one Point object
462      * @param segmentSteps steps to inject between two Points
463      * @return true on success
464      * @since API Level 16
465      */
swipe(Point[] segments, int segmentSteps)466     public boolean swipe(Point[] segments, int segmentSteps) {
467         Tracer.trace(segments, segmentSteps);
468         return getAutomatorBridge().getInteractionController().swipe(segments, segmentSteps);
469     }
470 
471     /**
472      * Waits for the current application to idle.
473      * Default wait timeout is 10 seconds
474      * @since API Level 16
475      */
waitForIdle()476     public void waitForIdle() {
477         Tracer.trace();
478         waitForIdle(Configurator.getInstance().getWaitForIdleTimeout());
479     }
480 
481     /**
482      * Waits for the current application to idle.
483      * @param timeout in milliseconds
484      * @since API Level 16
485      */
waitForIdle(long timeout)486     public void waitForIdle(long timeout) {
487         Tracer.trace(timeout);
488         getAutomatorBridge().waitForIdle(timeout);
489     }
490 
491     /**
492      * Retrieves the last activity to report accessibility events.
493      * @deprecated The results returned should be considered unreliable
494      * @return String name of activity
495      * @since API Level 16
496      */
497     @Deprecated
getCurrentActivityName()498     public String getCurrentActivityName() {
499         Tracer.trace();
500         return getAutomatorBridge().getQueryController().getCurrentActivityName();
501     }
502 
503     /**
504      * Retrieves the name of the last package to report accessibility events.
505      * @return String name of package
506      * @since API Level 16
507      */
getCurrentPackageName()508     public String getCurrentPackageName() {
509         Tracer.trace();
510         return getAutomatorBridge().getQueryController().getCurrentPackageName();
511     }
512 
513     /**
514      * Registers a {@link UiWatcher} to run automatically when the testing framework is unable to
515      * find a match using a {@link UiSelector}. See {@link #runWatchers()}
516      *
517      * @param name to register the UiWatcher
518      * @param watcher {@link UiWatcher}
519      * @since API Level 16
520      */
registerWatcher(String name, UiWatcher watcher)521     public void registerWatcher(String name, UiWatcher watcher) {
522         Tracer.trace(name, watcher);
523         if (mInWatcherContext) {
524             throw new IllegalStateException("Cannot register new watcher from within another");
525         }
526         mWatchers.put(name, watcher);
527     }
528 
529     /**
530      * Removes a previously registered {@link UiWatcher}.
531      *
532      * See {@link #registerWatcher(String, UiWatcher)}
533      * @param name used to register the UiWatcher
534      * @since API Level 16
535      */
removeWatcher(String name)536     public void removeWatcher(String name) {
537         Tracer.trace(name);
538         if (mInWatcherContext) {
539             throw new IllegalStateException("Cannot remove a watcher from within another");
540         }
541         mWatchers.remove(name);
542     }
543 
544     /**
545      * This method forces all registered watchers to run.
546      * See {@link #registerWatcher(String, UiWatcher)}
547      * @since API Level 16
548      */
runWatchers()549     public void runWatchers() {
550         Tracer.trace();
551         if (mInWatcherContext) {
552             return;
553         }
554 
555         for (String watcherName : mWatchers.keySet()) {
556             UiWatcher watcher = mWatchers.get(watcherName);
557             if (watcher != null) {
558                 try {
559                     mInWatcherContext = true;
560                     if (watcher.checkForCondition()) {
561                         setWatcherTriggered(watcherName);
562                     }
563                 } catch (Exception e) {
564                     Log.e(LOG_TAG, "Exceuting watcher: " + watcherName, e);
565                 } finally {
566                     mInWatcherContext = false;
567                 }
568             }
569         }
570     }
571 
572     /**
573      * Resets a {@link UiWatcher} that has been triggered.
574      * If a UiWatcher runs and its {@link UiWatcher#checkForCondition()} call
575      * returned <code>true</code>, then the UiWatcher is considered triggered.
576      * See {@link #registerWatcher(String, UiWatcher)}
577      * @since API Level 16
578      */
resetWatcherTriggers()579     public void resetWatcherTriggers() {
580         Tracer.trace();
581         mWatchersTriggers.clear();
582     }
583 
584     /**
585      * Checks if a specific registered  {@link UiWatcher} has triggered.
586      * See {@link #registerWatcher(String, UiWatcher)}. If a UiWatcher runs and its
587      * {@link UiWatcher#checkForCondition()} call returned <code>true</code>, then
588      * the UiWatcher is considered triggered. This is helpful if a watcher is detecting errors
589      * from ANR or crash dialogs and the test needs to know if a UiWatcher has been triggered.
590      *
591      * @param watcherName
592      * @return true if triggered else false
593      * @since API Level 16
594      */
hasWatcherTriggered(String watcherName)595     public boolean hasWatcherTriggered(String watcherName) {
596         Tracer.trace(watcherName);
597         return mWatchersTriggers.contains(watcherName);
598     }
599 
600     /**
601      * Checks if any registered {@link UiWatcher} have triggered.
602      *
603      * See {@link #registerWatcher(String, UiWatcher)}
604      * See {@link #hasWatcherTriggered(String)}
605      * @since API Level 16
606      */
hasAnyWatcherTriggered()607     public boolean hasAnyWatcherTriggered() {
608         Tracer.trace();
609         return mWatchersTriggers.size() > 0;
610     }
611 
612     /**
613      * Used internally by this class to set a {@link UiWatcher} state as triggered.
614      * @param watcherName
615      */
setWatcherTriggered(String watcherName)616     private void setWatcherTriggered(String watcherName) {
617         Tracer.trace(watcherName);
618         if (!hasWatcherTriggered(watcherName)) {
619             mWatchersTriggers.add(watcherName);
620         }
621     }
622 
623     /**
624      * Check if the device is in its natural orientation. This is determined by checking if the
625      * orientation is at 0 or 180 degrees.
626      * @return true if it is in natural orientation
627      * @since API Level 17
628      */
isNaturalOrientation()629     public boolean isNaturalOrientation() {
630         Tracer.trace();
631         waitForIdle();
632         int ret = getAutomatorBridge().getRotation();
633         return ret == UiAutomation.ROTATION_FREEZE_0 ||
634                 ret == UiAutomation.ROTATION_FREEZE_180;
635     }
636 
637     /**
638      * Returns the current rotation of the display, as defined in {@link Surface}
639      * @since API Level 17
640      */
getDisplayRotation()641     public int getDisplayRotation() {
642         Tracer.trace();
643         waitForIdle();
644         return getAutomatorBridge().getRotation();
645     }
646 
647     /**
648      * Disables the sensors and freezes the device rotation at its
649      * current rotation state.
650      * @throws RemoteException
651      * @since API Level 16
652      */
freezeRotation()653     public void freezeRotation() throws RemoteException {
654         Tracer.trace();
655         getAutomatorBridge().getInteractionController().freezeRotation();
656     }
657 
658     /**
659      * Re-enables the sensors and un-freezes the device rotation allowing its contents
660      * to rotate with the device physical rotation. During a test execution, it is best to
661      * keep the device frozen in a specific orientation until the test case execution has completed.
662      * @throws RemoteException
663      */
unfreezeRotation()664     public void unfreezeRotation() throws RemoteException {
665         Tracer.trace();
666         getAutomatorBridge().getInteractionController().unfreezeRotation();
667     }
668 
669     /**
670      * Simulates orienting the device to the left and also freezes rotation
671      * by disabling the sensors.
672      *
673      * If you want to un-freeze the rotation and re-enable the sensors
674      * see {@link #unfreezeRotation()}.
675      * @throws RemoteException
676      * @since API Level 17
677      */
setOrientationLeft()678     public void setOrientationLeft() throws RemoteException {
679         Tracer.trace();
680         getAutomatorBridge().getInteractionController().setRotationLeft();
681         waitForIdle(); // we don't need to check for idle on entry for this. We'll sync on exit
682     }
683 
684     /**
685      * Simulates orienting the device to the right and also freezes rotation
686      * by disabling the sensors.
687      *
688      * If you want to un-freeze the rotation and re-enable the sensors
689      * see {@link #unfreezeRotation()}.
690      * @throws RemoteException
691      * @since API Level 17
692      */
setOrientationRight()693     public void setOrientationRight() throws RemoteException {
694         Tracer.trace();
695         getAutomatorBridge().getInteractionController().setRotationRight();
696         waitForIdle(); // we don't need to check for idle on entry for this. We'll sync on exit
697     }
698 
699     /**
700      * Simulates orienting the device into its natural orientation and also freezes rotation
701      * by disabling the sensors.
702      *
703      * If you want to un-freeze the rotation and re-enable the sensors
704      * see {@link #unfreezeRotation()}.
705      * @throws RemoteException
706      * @since API Level 17
707      */
setOrientationNatural()708     public void setOrientationNatural() throws RemoteException {
709         Tracer.trace();
710         getAutomatorBridge().getInteractionController().setRotationNatural();
711         waitForIdle(); // we don't need to check for idle on entry for this. We'll sync on exit
712     }
713 
714     /**
715      * This method simulates pressing the power button if the screen is OFF else
716      * it does nothing if the screen is already ON.
717      *
718      * If the screen was OFF and it just got turned ON, this method will insert a 500ms delay
719      * to allow the device time to wake up and accept input.
720      * @throws RemoteException
721      * @since API Level 16
722      */
wakeUp()723     public void wakeUp() throws RemoteException {
724         Tracer.trace();
725         if(getAutomatorBridge().getInteractionController().wakeDevice()) {
726             // sync delay to allow the window manager to start accepting input
727             // after the device is awakened.
728             SystemClock.sleep(500);
729         }
730     }
731 
732     /**
733      * Checks the power manager if the screen is ON.
734      *
735      * @return true if the screen is ON else false
736      * @throws RemoteException
737      * @since API Level 16
738      */
isScreenOn()739     public boolean isScreenOn() throws RemoteException {
740         Tracer.trace();
741         return getAutomatorBridge().getInteractionController().isScreenOn();
742     }
743 
744     /**
745      * This method simply presses the power button if the screen is ON else
746      * it does nothing if the screen is already OFF.
747      *
748      * @throws RemoteException
749      * @since API Level 16
750      */
sleep()751     public void sleep() throws RemoteException {
752         Tracer.trace();
753         getAutomatorBridge().getInteractionController().sleepDevice();
754     }
755 
756     /**
757      * Helper method used for debugging to dump the current window's layout hierarchy.
758      * The file root location is /data/local/tmp
759      *
760      * @param fileName
761      * @since API Level 16
762      */
dumpWindowHierarchy(String fileName)763     public void dumpWindowHierarchy(String fileName) {
764         Tracer.trace(fileName);
765         AccessibilityNodeInfo root =
766                 getAutomatorBridge().getQueryController().getAccessibilityRootNode();
767         if(root != null) {
768             Display display = getAutomatorBridge().getDefaultDisplay();
769             Point size = new Point();
770             display.getSize(size);
771             AccessibilityNodeInfoDumper.dumpWindowToFile(root,
772                     new File(new File(Environment.getDataDirectory(), "local/tmp"), fileName),
773                     display.getRotation(), size.x, size.y);
774         }
775     }
776 
777     /**
778      * Waits for a window content update event to occur.
779      *
780      * If a package name for the window is specified, but the current window
781      * does not have the same package name, the function returns immediately.
782      *
783      * @param packageName the specified window package name (can be <code>null</code>).
784      *        If <code>null</code>, a window update from any front-end window will end the wait
785      * @param timeout the timeout for the wait
786      *
787      * @return true if a window update occurred, false if timeout has elapsed or if the current
788      *         window does not have the specified package name
789      * @since API Level 16
790      */
waitForWindowUpdate(final String packageName, long timeout)791     public boolean waitForWindowUpdate(final String packageName, long timeout) {
792         Tracer.trace(packageName, timeout);
793         if (packageName != null) {
794             if (!packageName.equals(getCurrentPackageName())) {
795                 return false;
796             }
797         }
798         Runnable emptyRunnable = new Runnable() {
799             @Override
800             public void run() {
801             }
802         };
803         AccessibilityEventFilter checkWindowUpdate = new AccessibilityEventFilter() {
804             @Override
805             public boolean accept(AccessibilityEvent t) {
806                 if (t.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {
807                     return packageName == null || packageName.equals(t.getPackageName());
808                 }
809                 return false;
810             }
811         };
812         try {
813             getAutomatorBridge().executeCommandAndWaitForAccessibilityEvent(
814                     emptyRunnable, checkWindowUpdate, timeout);
815         } catch (TimeoutException e) {
816             return false;
817         } catch (Exception e) {
818             Log.e(LOG_TAG, "waitForWindowUpdate: general exception from bridge", e);
819             return false;
820         }
821         return true;
822     }
823 
824     /**
825      * Take a screenshot of current window and store it as PNG
826      *
827      * Default scale of 1.0f (original size) and 90% quality is used
828      * The screenshot is adjusted per screen rotation
829      *
830      * @param storePath where the PNG should be written to
831      * @return true if screen shot is created successfully, false otherwise
832      * @since API Level 17
833      */
takeScreenshot(File storePath)834     public boolean takeScreenshot(File storePath) {
835         Tracer.trace(storePath);
836         return takeScreenshot(storePath, 1.0f, 90);
837     }
838 
839     /**
840      * Take a screenshot of current window and store it as PNG
841      *
842      * The screenshot is adjusted per screen rotation
843      *
844      * @param storePath where the PNG should be written to
845      * @param scale scale the screenshot down if needed; 1.0f for original size
846      * @param quality quality of the PNG compression; range: 0-100
847      * @return true if screen shot is created successfully, false otherwise
848      * @since API Level 17
849      */
takeScreenshot(File storePath, float scale, int quality)850     public boolean takeScreenshot(File storePath, float scale, int quality) {
851         Tracer.trace(storePath, scale, quality);
852         return getAutomatorBridge().takeScreenshot(storePath, quality);
853     }
854 }
855