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;
20e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.graphics.Point;
21e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.os.Environment;
22e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.os.RemoteException;
23e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.os.ServiceManager;
24e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.os.SystemClock;
25e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.util.DisplayMetrics;
26e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.util.Log;
27e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.view.Display;
28e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.view.IWindowManager;
29e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.view.KeyEvent;
30e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.view.Surface;
31e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.view.WindowManagerImpl;
32e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.view.accessibility.AccessibilityEvent;
33e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.view.accessibility.AccessibilityNodeInfo;
34e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
35e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport com.android.internal.statusbar.IStatusBarService;
36e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport com.android.internal.util.Predicate;
37e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
38e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport java.io.File;
39e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport java.util.ArrayList;
40e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport java.util.HashMap;
41e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport java.util.List;
42e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport java.util.concurrent.TimeoutException;
43e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
44e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu/**
45e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * UiDevice provides access to device wide states. Also provides methods to simulate
46e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * pressing hardware buttons such as DPad or the soft buttons such as Home and Menu.
47e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu */
48e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhupublic class UiDevice {
49e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final String LOG_TAG = UiDevice.class.getSimpleName();
50e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
51e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final long DEFAULT_TIMEOUT_MILLIS = 10 * 1000;
52e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
53e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    // store for registered UiWatchers
54e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private final HashMap<String, UiWatcher> mWatchers = new HashMap<String, UiWatcher>();
55e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private final List<String> mWatchersTriggers = new ArrayList<String>();
56e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
57e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    // remember if we're executing in the context of a UiWatcher
58e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private boolean mInWatcherContext = false;
59e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
60e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    // provides access the {@link QueryController} and {@link InteractionController}
61e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private final UiAutomatorBridge mUiAutomationBridge;
62e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
63e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    // reference to self
64e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static UiDevice mDevice;
65e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
66e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private Boolean mIsPhone = null;
67e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
68e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private UiDevice() {
69e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mUiAutomationBridge = new UiAutomatorBridge();
70e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mDevice = this;
71e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
72e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
73e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    boolean isInWatcherContext() {
74e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mInWatcherContext;
75e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
76e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
77e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
78e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Provides access the {@link QueryController} and {@link InteractionController}
79e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link UiAutomatorBridge}
80e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
81e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    UiAutomatorBridge getAutomatorBridge() {
82e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge;
83e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
84e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
85e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Allow both the direct creation of a UiDevice and retrieving a existing
86e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * instance of UiDevice. This helps tests and their libraries to have access
87e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * to UiDevice with necessitating having to always pass copies of UiDevice
88e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * instances around.
89e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return UiDevice instance
90e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
91e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public static UiDevice getInstance() {
92e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (mDevice == null) {
93e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            mDevice = new UiDevice();
94e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
95e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mDevice;
96e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
97e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
98e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
99e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * This forces the return value of {@link #isPhone()} to be a specific device type.
100e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * For example, on certain devices the {@link #isPhone} may return true when an application
101e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * is actually behaving as if it is on a tablet. For these types of devices, it would be
102e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * best if the test forces the issue by invoking this method accordingly.
103e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param val true for phone behavior else false for all other
104e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
105e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void setTypeAsPhone(boolean val) {
106e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mIsPhone = val;
107e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
108e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
109e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
110e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Check if the tests are running on a phone screen. This method assumes a
111e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * phone is a device that its natural rotation has a height > width or when
112e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * rotated it has a width > height. This API is deprecated. Use the UI to
113e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * determine the layout. For example if on larger screen devices your app displays
114e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * two ListViews but on a small screen one, then count the ListViews to decide. see
115e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link UiObject#getMatchesCount()}
116e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if the device has a phone else false
117e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
118e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    @Deprecated
119e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isPhone() {
120e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(mIsPhone == null) {
121e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            DisplayMetrics metrics = new DisplayMetrics();
122e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
123e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            display.getMetrics(metrics);
124e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
125e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if(isOrientationNatural()) {
126e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                // we assume a phone has a natural orientation that has height > width
127e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                if(metrics.heightPixels > metrics.widthPixels)
128e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    return true;
129e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            } else {
130e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                // we assume a phone has a rotated orientation that has height < width
131e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                if(metrics.heightPixels < metrics.widthPixels)
132e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    return true;
133e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
134e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
135e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // not a phone
136e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false;
137e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
138e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
139e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mIsPhone;
140e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
141e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
142e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
143e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Check the current device orientation
144e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if in natural orientation
145e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
146e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isOrientationNatural() {
147e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
148e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return display.getRotation() == Surface.ROTATION_0 ||
149e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                display.getRotation() == Surface.ROTATION_180;
150e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
151e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
152e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
153e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Every event received from accessibility may or may not contain text. This
154e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * method returns the text from the last UI traversal event received that had text.
155e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * This is helpful in web views when the test performs down arrow presses to focus
156e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * on different elements inside the web view, the accessibility will fire events
157e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * with the text just highlighted. In effect once can read the contents of a
158e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * web view this way.
159e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return text of the last traversal event else an empty string
160e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
161e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getLastTraversedText() {
162e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getQueryController().getLastTraversedText();
163e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
164e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
165e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
166e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Helper to clear the text saved of the last accessibility UI traversal event that had
167e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * any text in it. See {@link #getLastTraversedText()}.
168e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
169e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void clearLastTraversedText() {
170e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mUiAutomationBridge.getQueryController().clearLastTraversedText();
171e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
172e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
173e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
174e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Helper method to do a short press on MENU button
175e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if successful else false
176e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
177e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressMenu() {
178e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return pressKeyCode(KeyEvent.KEYCODE_MENU);
179e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
180e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
181e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
182e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Helper method to do a short press on BACK button
183e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if successful else false
184e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
185e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressBack() {
186e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return pressKeyCode(KeyEvent.KEYCODE_BACK);
187e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
188e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
189e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
190e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Helper method to do a short press on HOME button
191e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if successful else false
192e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
193e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressHome() {
194e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return pressKeyCode(KeyEvent.KEYCODE_HOME);
195e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
196e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
197e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
198e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Helper method to do a short press on SEARCH button
199e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if successful else false
200e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
201e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressSearch() {
202e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return pressKeyCode(KeyEvent.KEYCODE_SEARCH);
203e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
204e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
205e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
206e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Helper method to do a short press on DOWN button
207e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if successful else false
208e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
209e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressDPadCenter() {
210e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return pressKeyCode(KeyEvent.KEYCODE_DPAD_CENTER);
211e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
212e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
213e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
214e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Helper method to do a short press on DOWN button
215e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if successful else false
216e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
217e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressDPadDown() {
218e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return pressKeyCode(KeyEvent.KEYCODE_DPAD_DOWN);
219e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
220e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
221e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
222e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Helper method to do a short press on UP button
223e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if successful else false
224e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
225e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressDPadUp() {
226e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return pressKeyCode(KeyEvent.KEYCODE_DPAD_UP);
227e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
228e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
229e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
230e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Helper method to do a short press on LEFT button
231e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if successful else false
232e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
233e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressDPadLeft() {
234e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return pressKeyCode(KeyEvent.KEYCODE_DPAD_LEFT);
235e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
236e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
237e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
238e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Helper method to do a short press on RIGTH button
239e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if successful else false
240e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
241e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressDPadRight() {
242e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return pressKeyCode(KeyEvent.KEYCODE_DPAD_RIGHT);
243e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
244e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
245e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
246e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Helper method to do a short press on DELETE
247e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if successful else false
248e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
249e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressDelete() {
250e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return pressKeyCode(KeyEvent.KEYCODE_DEL);
251e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
252e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
253e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
254e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Helper method to do a short press on ENTER
255e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if successful else false
256e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
257e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressEnter() {
258e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return pressKeyCode(KeyEvent.KEYCODE_ENTER);
259e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
260e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
261e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
262e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Helper method to do a short press using a key code. See {@link KeyEvent}
263e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if successful else false
264e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
265e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressKeyCode(int keyCode) {
266e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        waitForIdle();
267e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getInteractionController().sendKey(keyCode, 0);
268e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
269e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
270e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
271e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Helper method to do a short press using a key code. See {@link KeyEvent}
272e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param keyCode See {@link KeyEvent}
273e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param metaState See {@link KeyEvent}
274e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if successful else false
275e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
276e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressKeyCode(int keyCode, int metaState) {
277e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        waitForIdle();
278e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getInteractionController().sendKey(keyCode, metaState);
279e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
280e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
281e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
282e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Gets the raw width of the display, in pixels. The size is adjusted based
283e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * on the current rotation of the display.
284e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return width in pixels or zero on failure
285e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
286e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public int getDisplayWidth() {
287e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        IWindowManager wm = IWindowManager.Stub.asInterface(
288e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                ServiceManager.getService(Context.WINDOW_SERVICE));
289e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Point p = new Point();
290e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        try {
291e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            wm.getDisplaySize(p);
292e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        } catch (RemoteException e) {
293e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return 0;
294e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
295e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return p.x;
296e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
297e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
298e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
299e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Press recent apps soft key
300e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if successful
301e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws RemoteException
302e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
303e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean pressRecentApps() throws RemoteException {
304e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        waitForIdle();
305e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        final IStatusBarService statusBar = IStatusBarService.Stub.asInterface(
306e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
307e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
308e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (statusBar != null) {
309e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            statusBar.toggleRecentApps();
310e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return true;
311e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
312e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return false;
313e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
314e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
315e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
316e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Gets the raw height of the display, in pixels. The size is adjusted based
317e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * on the current rotation of the display.
318e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return height in pixels or zero on failure
319e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
320e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public int getDisplayHeight() {
321e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        IWindowManager wm = IWindowManager.Stub.asInterface(
322e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                ServiceManager.getService(Context.WINDOW_SERVICE));
323e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Point p = new Point();
324e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        try {
325e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            wm.getDisplaySize(p);
326e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        } catch (RemoteException e) {
327e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return 0;
328e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
329e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return p.y;
330e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
331e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
332e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3334ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * Perform a click at arbitrary coordinates specified by the user
334e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param x coordinate
335e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param y coordinate
3364ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @return true if the click succeeded else false
337e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
3384ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public boolean click(int x, int y) {
339e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (x >= getDisplayWidth() || y >= getDisplayHeight()) {
340e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return (false);
341e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
3424ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return getAutomatorBridge().getInteractionController().click(x, y);
343e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
344e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
345e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
346e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Performs a swipe from one coordinate to another using the number of steps
347e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * to determine smoothness and speed. The more steps the slower and smoother
348e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * the swipe will be.
349e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param startX
350e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param startY
351e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param endX
352e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param endY
353e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param steps is the number of move steps sent to the system
354e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return false if the operation fails or the coordinates are invalid
355e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
356e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipe(int startX, int startY, int endX, int endY, int steps) {
357e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getInteractionController()
358e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                .scrollSwipe(startX, startY, endX, endY, steps);
359e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
360e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
361e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
362e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Performs a swipe between points in the Point array.
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
371e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void waitForIdle() {
372e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        waitForIdle(DEFAULT_TIMEOUT_MILLIS);
373e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
374e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
375e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void waitForIdle(long time) {
376e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mUiAutomationBridge.waitForIdle(time);
377e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
378e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
379e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
380e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Last activity to report accessibility events
381e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return String name of activity
382e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
383e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getCurrentActivityName() {
384e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getQueryController().getCurrentActivityName();
385e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
386e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
387e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
388e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Last package to report accessibility events
389e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return String name of package
390e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
391e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getCurrentPackageName() {
392e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getQueryController().getCurrentPackageName();
393e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
394e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
395e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
396e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
397e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Enables the test script to register a condition watcher to be called by
398e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * the automation library. The automation library will invoke
399e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link UiWatcher#checkForCondition} only when a regular API call is in
400e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * retry mode when it is unable to locate its selector yet. Only during this
401e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * time, the watchers are invoked to check if there is something else
402e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * unexpected on the screen that may be causing the delay in detecting the
403e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * required UI object.
404e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param name of watcher
405e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param watcher {@link UiWatcher}
406e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
407e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void registerWatcher(String name, UiWatcher watcher) {
408e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (mInWatcherContext) {
409e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new IllegalStateException("Cannot register new watcher from within another");
410e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
411e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mWatchers.put(name, watcher);
412e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
413e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
414e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
415e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Removes a previously registered {@link #registerWatcher(String, UiWatcher)}.
416e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param name of watcher used when <code>registerWatcher</code> was called.
417e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiAutomationException
418e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
419e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void removeWatcher(String name) {
420e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (mInWatcherContext) {
421e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new IllegalStateException("Cannot remove a watcher from within another");
422e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
423e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mWatchers.remove(name);
424e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
425e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
426e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
427e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Watchers are generally not run unless a certain UI object is not being
428e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * found. This will help improve performance of tests until there is a good
429e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * reason to check for possible exceptions on the display.<b/><b/> However,
430e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * in some cases it may be desirable to force run the watchers. Calling this
431e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * method will execute all registered watchers.
432e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
433e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void runWatchers() {
434e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (mInWatcherContext) {
435e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return;
436e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
437e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
438e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        for (String watcherName : mWatchers.keySet()) {
439e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            UiWatcher watcher = mWatchers.get(watcherName);
440e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if (watcher != null) {
441e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                try {
442e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    mInWatcherContext = true;
443e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    if (watcher.checkForCondition()) {
444e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                        setWatcherTriggered(watcherName);
445e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    }
446e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                } catch (Exception e) {
447e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    Log.e(LOG_TAG, "Exceuting watcher: " + watcherName, e);
448e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                } finally {
449e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    mInWatcherContext = false;
450e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                }
451e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
452e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
453e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
454e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
455e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
456e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * If you have used {@link #registerWatcher(String, UiWatcher)} then this
457e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * method can be used to reset reported UiWatcher triggers.
458e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * A {@link UiWatcher} reports it is triggered by returning true
459e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * from its implementation of {@link UiWatcher#checkForCondition()}
460e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
461e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void resetWatcherTriggers() {
462e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mWatchersTriggers.clear();
463e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
464e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
465e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
466e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * If you have used {@link #registerWatcher(String, UiWatcher)} then this
467e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * method can be used to check if a specific UiWatcher has ever triggered during the
468e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * test. For a {@link UiWatcher} to report it is triggered it needs to return true
469e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * from its implementation of {@link UiWatcher#checkForCondition()}
470e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
471e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean hasWatcherTriggered(String watcherName) {
472e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mWatchersTriggers.contains(watcherName);
473e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
474e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
475e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
476e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * If you have used {@link #registerWatcher(String, UiWatcher)} then this
477e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * method can be used to check if any of those have ever triggered during the
478e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * test. For a {@link UiWatcher} to report it is triggered it needs to return true
479e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * from its implementation of {@link UiWatcher#checkForCondition()}
480e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
481e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean hasAnyWatcherTriggered() {
482e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mWatchersTriggers.size() > 0;
483e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
484e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
485e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private void setWatcherTriggered(String watcherName) {
486e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (!hasWatcherTriggered(watcherName)) {
487e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            mWatchersTriggers.add(watcherName);
488e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
489e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
490e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
491e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
492e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Check if the device is in its natural orientation. This is determined by
493e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * checking whether the orientation is at 0 or 180 degrees.
494e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is in natural orientation
495e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws RemoteException
496e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
497e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isNaturalRotation() throws RemoteException {
498e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getAutomatorBridge().getInteractionController().isNaturalRotation();
499e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
500e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
501e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
502e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Disables the sensors and freezes the device rotation at its
503e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * current rotation state.
504e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws RemoteException
505e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
506e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void freezeRotation() throws RemoteException {
507e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getAutomatorBridge().getInteractionController().freezeRotation();
508e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
509e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
510e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
511e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Re-enables the sensors and un-freezes the device rotation
512e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * allowing its contents to rotate with the device physical rotation.
513e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws RemoteException
514e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
515e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void unfreezeRotation() throws RemoteException {
516e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getAutomatorBridge().getInteractionController().unfreezeRotation();
517e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
518e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
519e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
520e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Rotates left and also freezes rotation in that position by
521e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * disabling the sensors. If you want to un-freeze the rotation
522e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * and re-enable the sensors see {@link #unfreezeRotation()}. Note
523e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * that doing so may cause the screen contents to rotate
524e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * depending on the current physical position of the test device.
525e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws RemoteException
526e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
527e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void setRotationLeft() throws RemoteException {
528e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getAutomatorBridge().getInteractionController().setRotationLeft();
529e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
530e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
531e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
532e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Rotates right and also freezes rotation in that position by
533e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * disabling the sensors. If you want to un-freeze the rotation
534e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * and re-enable the sensors see {@link #unfreezeRotation()}. Note
535e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * that doing so may cause the screen contents to rotate
536e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * depending on the current physical position of the test device.
537e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws RemoteException
538e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
539e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void setRotationRight() throws RemoteException {
540e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getAutomatorBridge().getInteractionController().setRotationRight();
541e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
542e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
543e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
544e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Check if the device is in its natural orientation. This is determined by
545e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * checking whether the orientation is at 0 or 180 degrees.
546e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is in natural orientation
547e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws RemoteException
548e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
549e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void setRotationNatural() throws RemoteException {
550e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getAutomatorBridge().getInteractionController().setRotationNatural();
551e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
552e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
553e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
554e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * This method simply presses the power button if the screen is OFF else
555e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * it does nothing if the screen is already ON. If the screen was OFF and
556e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * it just got turned ON, this method will insert a 500ms delay to allow
557e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * the device time to wake up and accept input.
558e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws RemoteException
559e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
560e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void wakeUp() throws RemoteException {
561e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(getAutomatorBridge().getInteractionController().wakeDevice()) {
562e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // sync delay to allow the window manager to start accepting input
563e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // after the device is awakened.
564e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            SystemClock.sleep(500);
565e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
566e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
567e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
568e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
569e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Checks the power manager if the screen is ON
570e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if the screen is ON else false
571e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws RemoteException
572e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
573e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isScreenOn() throws RemoteException {
574e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getAutomatorBridge().getInteractionController().isScreenOn();
575e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
576e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
577e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
578e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * This method simply presses the power button if the screen is ON else
579e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * it does nothing if the screen is already OFF.
580e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws RemoteException
581e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
582e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void sleep() throws RemoteException {
583e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getAutomatorBridge().getInteractionController().sleepDevice();
584e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
585e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
586e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
587e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Helper method used for debugging to dump the current window's layout hierarchy.
588e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * The file root location is /data/local/tmp
589e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param fileName
590e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
591e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void dumpWindowHierarchy(String fileName) {
592e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo root =
593e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                getAutomatorBridge().getQueryController().getAccessibilityRootNode();
594e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(root != null) {
595e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            AccessibilityNodeInfoDumper.dumpWindowToFile(
596e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    root, new File(new File(Environment.getDataDirectory(),
597e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                            "local/tmp"), fileName));
598e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
599e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
600e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
601e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
602e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
603e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Waits for a window content update event to occur
604e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
605e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * if a package name for window is specified, but current window is not with the same package
606e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * name, the function will return immediately
607e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
608e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param packageName the specified window package name; maybe <code>null</code>, and a window
609e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *                    update from any frontend window will end the wait
610e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param timeout the timeout for the wait
611e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
612e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if a window update occured, false if timeout has reached or current window is
613e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * not the specified package name
614e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
615e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean waitForWindowUpdate(final String packageName, long timeout) {
616e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (packageName != null) {
617e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if (!packageName.equals(getCurrentPackageName())) {
618e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                return false;
619e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
620e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
621e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Runnable emptyRunnable = new Runnable() {
622e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            @Override
623e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            public void run() {
624e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
625e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        };
626e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Predicate<AccessibilityEvent> checkWindowUpdate = new Predicate<AccessibilityEvent>() {
627e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            @Override
628e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            public boolean apply(AccessibilityEvent t) {
629e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                if (t.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {
630e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    return packageName == null || packageName.equals(t.getPackageName());
631e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                }
632e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                return false;
633e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
634e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        };
635e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        try {
636e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            getAutomatorBridge().executeCommandAndWaitForAccessibilityEvent(
637e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    emptyRunnable, checkWindowUpdate, timeout);
638e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        } catch (TimeoutException e) {
639e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false;
640e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        } catch (Exception e) {
641e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            Log.e(LOG_TAG, "waitForWindowUpdate: general exception from bridge", e);
642e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false;
643e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
644e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return true;
645e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
646e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu}
647