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