├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── ant.properties
├── build.xml
├── custom_rules.xml
├── libs
├── jackson-annotations-2.2.2.jar
├── jackson-core-2.2.2.jar
├── jackson-databind-2.2.2.jar
├── jsonrpc4j-0.28.jar
├── portlet-api-2.0.jar
└── servlet-api-2.5.jar
├── local.properties
├── project.properties
└── src
└── com
└── github
└── uiautomatorstub
├── AutomatorHttpServer.java
├── AutomatorService.java
├── AutomatorServiceImpl.java
├── ConfiguratorInfo.java
├── DeviceInfo.java
├── Log.java
├── NotImplementedException.java
├── ObjInfo.java
├── Point.java
├── Rect.java
├── Selector.java
├── Stub.java
└── watcher
├── ClickUiObjectWatcher.java
├── PressKeysWatcher.java
└── SelectorWatcher.java
/.gitignore:
--------------------------------------------------------------------------------
1 | .class
2 | bin
3 | .classpath
4 | .project
5 | .idea
6 | uiautomator-stub.iml
7 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "nanohttpd"]
2 | path = nanohttpd
3 | url = https://github.com/xiaocong/nanohttpd.git
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | Copyright (c) 2013 Xiaocong He
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is
9 | furnished to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all
12 | copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
20 | OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # It's moved to [android-uiautomator-server](https://github.com/xiaocong/android-uiautomator-server), which is based on android suppot library.
2 |
3 | # Purpose
4 |
5 | [UIAutomator](http://developer.android.com/tools/testing/testing_ui.html) is a
6 | great tool to perform Android UI testing, but to do it, you have to write java
7 | code, compile it, install the jar, and run. It's a complex steps for all
8 | testers...
9 |
10 | This project is to build a light weight jsonrpc server in Android device, so
11 | that we can just write PC side script to write UIAutomator tests.
12 |
13 | # Build
14 |
15 | - Update `local.properties` file with your android sdk path.
16 | - Set ANDROID_HOME env variable (`.bashrc` or `.bash_profile`).
17 | - Install Ant if you have not.
18 | - Run command:
19 |
20 | $ git submodule init
21 | $ git submodule update
22 | $ ant build # compile
23 | $ ant install # install jar file to device via adb
24 |
25 | # Run the jsonrcp server on Android device
26 |
27 | $ adb shell uiautomator runtest uiautomator-stub.jar bundle.jar -c com.github.uiautomatorstub.Stub
28 | $ adb forward tcp:9008 tcp:9008 # tcp forward
29 |
30 | # How to use
31 |
32 | Next is a python example using jsonrpclib. Before you run it, make sure install jsonrpclib via
33 | `pip install jsonrpclib`.
34 |
35 | ```python
36 | import jsonrpclib
37 | server = jsonrpclib.Server('http://localhost:9008/jsonrpc/0')
38 |
39 | server.wakeUp()
40 | server.pressKey("home")
41 | server.pressKey("back")
42 | ```
43 |
44 | For convenicence, you can intall its python wrapper library [uiautomator](https://github.com/xiaocong/uiautomator).
45 |
46 | # Notes
47 |
48 | The jsonrpc API is still under discussion, so currently only some demo APIs have been implemented.
49 | If you have any idea, please email xiaocong@gmail.com or [submit tickets](https://github.com/xiaocong/uiautomator/issues/new).
50 |
51 | # Dependencies
52 |
53 | - [nanohttpd](https://github.com/NanoHttpd/nanohttpd)
54 | - [jsonrpc4j](https://code.google.com/p/jsonrpc4j/)
55 |
--------------------------------------------------------------------------------
/ant.properties:
--------------------------------------------------------------------------------
1 | source.dir=src;nanohttpd/core/src/main/java
2 |
--------------------------------------------------------------------------------
/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
29 |
30 |
31 |
35 |
36 |
37 |
38 |
39 |
40 |
49 |
50 |
51 |
52 |
56 |
57 |
69 |
70 |
71 |
89 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/custom_rules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
42 |
43 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/libs/jackson-annotations-2.2.2.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaocong/android-uiautomator-jsonrpcserver/d16f938211435631ea25b2247685e8622c3376b6/libs/jackson-annotations-2.2.2.jar
--------------------------------------------------------------------------------
/libs/jackson-core-2.2.2.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaocong/android-uiautomator-jsonrpcserver/d16f938211435631ea25b2247685e8622c3376b6/libs/jackson-core-2.2.2.jar
--------------------------------------------------------------------------------
/libs/jackson-databind-2.2.2.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaocong/android-uiautomator-jsonrpcserver/d16f938211435631ea25b2247685e8622c3376b6/libs/jackson-databind-2.2.2.jar
--------------------------------------------------------------------------------
/libs/jsonrpc4j-0.28.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaocong/android-uiautomator-jsonrpcserver/d16f938211435631ea25b2247685e8622c3376b6/libs/jsonrpc4j-0.28.jar
--------------------------------------------------------------------------------
/libs/portlet-api-2.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaocong/android-uiautomator-jsonrpcserver/d16f938211435631ea25b2247685e8622c3376b6/libs/portlet-api-2.0.jar
--------------------------------------------------------------------------------
/libs/servlet-api-2.5.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaocong/android-uiautomator-jsonrpcserver/d16f938211435631ea25b2247685e8622c3376b6/libs/servlet-api-2.5.jar
--------------------------------------------------------------------------------
/local.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must *NOT* be checked into Version Control Systems,
5 | # as it contains information specific to your local configuration.
6 |
7 | # location of the SDK. This is only used by Ant
8 | # For customization when using a Version Control System, please read the
9 | # header note.
10 | sdk.dir=/opt/android-studio/sdk
11 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-19
15 |
--------------------------------------------------------------------------------
/src/com/github/uiautomatorstub/AutomatorHttpServer.java:
--------------------------------------------------------------------------------
1 | package com.github.uiautomatorstub;
2 |
3 | import java.io.*;
4 | import java.util.HashMap;
5 | import java.util.Map;
6 |
7 | import android.os.Build;
8 | import com.android.uiautomator.core.UiDevice;
9 | import com.googlecode.jsonrpc4j.JsonRpcServer;
10 |
11 | import fi.iki.elonen.NanoHTTPD;
12 |
13 | public class AutomatorHttpServer extends NanoHTTPD {
14 |
15 | public AutomatorHttpServer(int port) {
16 | super(port);
17 | }
18 |
19 | private Map router = new HashMap();
20 |
21 | public void route(String uri, JsonRpcServer rpc) {
22 | router.put(uri, rpc);
23 | }
24 |
25 | @Override
26 | public Response serve(String uri, Method method,
27 | Map headers, Map params,
28 | Map files) {
29 | Log.d(String.format("URI: %s, Method: %s, Header: %s, params, %s, files: %s", uri, method, headers, params, files));
30 |
31 | if ("/stop".equals(uri)) {
32 | stop();
33 | return new Response("Server stopped!!!");
34 | } else if ("/0/screenshot".equals(uri)) {
35 | if (Build.VERSION.SDK_INT < 17)
36 | return new Response(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "API level less than 17.");
37 | File f = new File(AutomatorServiceImpl.STORAGE_PATH, "screenshot.png");
38 | float scale = 1.0f;
39 | if (params.containsKey("scale")) {
40 | try {
41 | scale = Float.parseFloat(params.get("scale"));
42 | } catch (NumberFormatException e) {
43 | }
44 | }
45 | int quality = 100;
46 | if (params.containsKey("quality")) {
47 | try {
48 | quality = Integer.parseInt(params.get("quality"));
49 | } catch (NumberFormatException e) {
50 | }
51 | }
52 | UiDevice.getInstance().takeScreenshot(f, scale, quality);
53 | try {
54 | return new Response(Response.Status.OK, "image/png", new FileInputStream(f));
55 | } catch (FileNotFoundException e) {
56 | Log.e(e.getMessage());
57 | return new Response(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "Internal Server Error!!!");
58 | }
59 | } else if (router.containsKey(uri)) {
60 | JsonRpcServer jsonRpcServer = router.get(uri);
61 | ByteArrayInputStream is = null;
62 | if (params.get("NanoHttpd.QUERY_STRING") != null)
63 | is = new ByteArrayInputStream(params.get("NanoHttpd.QUERY_STRING").getBytes());
64 | else if (files.get("postData") != null)
65 | is = new ByteArrayInputStream(files.get("postData").getBytes());
66 | else
67 | return new Response(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "Invalid http post data!");
68 | ByteArrayOutputStream os = new ByteArrayOutputStream();
69 | try {
70 | jsonRpcServer.handle(is, os);
71 | return new Response(Response.Status.OK, "application/json", new ByteArrayInputStream(os.toByteArray()));
72 | } catch (IOException e) {
73 | return new Response(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "Internal Server Error!!!");
74 | }
75 | } else
76 | return new Response(Response.Status.NOT_FOUND, MIME_PLAINTEXT, "Not Found!!!");
77 | }
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/src/com/github/uiautomatorstub/AutomatorService.java:
--------------------------------------------------------------------------------
1 | package com.github.uiautomatorstub;
2 |
3 | import android.os.RemoteException;
4 |
5 | import com.android.uiautomator.core.UiObjectNotFoundException;
6 | import com.googlecode.jsonrpc4j.JsonRpcErrors;
7 | import com.googlecode.jsonrpc4j.JsonRpcError;
8 |
9 | public interface AutomatorService {
10 | final static int ERROR_CODE_BASE = -32000;
11 | /**
12 | * It's to test if the service is alive.
13 | * @return 'pong'
14 | */
15 | String ping();
16 |
17 | /***************************************************************************
18 | * Below section contains all methods from UiDevice.
19 | ***************************************************************************/
20 |
21 | /**
22 | * Get the device info.
23 | * @return device info.
24 | */
25 | DeviceInfo deviceInfo();
26 |
27 | /**
28 | * Perform a click at arbitrary coordinates specified by the user.
29 | * @param x coordinate
30 | * @param y coordinate
31 | * @return true if the click succeeded else false
32 | */
33 | boolean click(int x, int y);
34 |
35 | /**
36 | * Performs a swipe from one coordinate to another coordinate. You can control the smoothness and speed of the swipe by specifying the number of steps. Each step execution is throttled to 5 milliseconds per step, so for a 100 steps, the swipe will take around 0.5 seconds to complete.
37 | * @param startX X-axis value for the starting coordinate
38 | * @param startY Y-axis value for the starting coordinate
39 | * @param endX X-axis value for the ending coordinate
40 | * @param endY Y-axis value for the ending coordinate
41 | * @param steps is the number of steps for the swipe action
42 | * @return true if swipe is performed, false if the operation fails or the coordinates are invalid
43 | * @throws NotImplementedException
44 | */
45 | @JsonRpcErrors({@JsonRpcError(exception=NotImplementedException.class, code=ERROR_CODE_BASE-3)})
46 | boolean drag(int startX, int startY, int endX, int endY, int steps) throws NotImplementedException;
47 |
48 | /**
49 | * Performs a swipe from one coordinate to another using the number of steps to determine smoothness and speed. Each step execution is throttled to 5ms per step. So for a 100 steps, the swipe will take about 1/2 second to complete.
50 | * @param startX X-axis value for the starting coordinate
51 | * @param startY Y-axis value for the starting coordinate
52 | * @param endX X-axis value for the ending coordinate
53 | * @param endY Y-axis value for the ending coordinate
54 | * @param steps is the number of move steps sent to the system
55 | * @return false if the operation fails or the coordinates are invalid
56 | */
57 | boolean swipe(int startX, int startY, int endX, int endY, int steps);
58 |
59 | /**
60 | * Helper method used for debugging to dump the current window's layout hierarchy. The file root location is /data/local/tmp
61 | * @param compressed use compressed layout hierarchy or not using setCompressedLayoutHeirarchy method. Ignore the parameter in case the API level lt 18.
62 | * @param filename the filename to be stored.
63 | * @return the absolute path name of dumped file.
64 | */
65 | String dumpWindowHierarchy(boolean compressed, String filename);
66 |
67 | /**
68 | * Take a screenshot of current window and store it as PNG The screenshot is adjusted per screen rotation
69 | * @param filename where the PNG should be written to
70 | * @param scale scale the screenshot down if needed; 1.0f for original size
71 | * @param quality quality of the PNG compression; range: 0-100
72 | * @return the file name of the screenshot. null if failed.
73 | * @throws NotImplementedException
74 | */
75 | @JsonRpcErrors({@JsonRpcError(exception=NotImplementedException.class, code=ERROR_CODE_BASE-3)})
76 | String takeScreenshot(String filename, float scale, int quality) throws NotImplementedException;
77 |
78 | /**
79 | * Disables the sensors and freezes the device rotation at its current rotation state, or enable it.
80 | * @param freeze true to freeze the rotation, false to unfreeze the rotation.
81 | * @throws RemoteException
82 | */
83 | @JsonRpcErrors({@JsonRpcError(exception=RemoteException.class, code=ERROR_CODE_BASE-1)})
84 | void freezeRotation(boolean freeze) throws RemoteException; // freeze or unfreeze rotation, see also unfreezeRotation()
85 |
86 | /**
87 | * Simulates orienting the device to the left/right/natural and also freezes rotation by disabling the sensors.
88 | * @param dir Left or l, Right or r, Natural or n, case insensitive
89 | * @throws RemoteException
90 | * @throws NotImplementedException
91 | */
92 | @JsonRpcErrors({@JsonRpcError(exception=RemoteException.class, code=ERROR_CODE_BASE-1), @JsonRpcError(exception=NotImplementedException.class, code=ERROR_CODE_BASE-3)})
93 | void setOrientation(String dir) throws RemoteException, NotImplementedException;
94 |
95 | /**
96 | * Retrieves the text from the last UI traversal event received.
97 | * @return the text from the last UI traversal event received.
98 | */
99 | String getLastTraversedText();
100 |
101 | /**
102 | * Clears the text from the last UI traversal event.
103 | */
104 | void clearLastTraversedText();
105 |
106 | /**
107 | * Opens the notification shade.
108 | * @return true if successful, else return false
109 | * @throws NotImplementedException
110 | */
111 | @JsonRpcErrors({@JsonRpcError(exception=NotImplementedException.class, code=ERROR_CODE_BASE-3)})
112 | boolean openNotification() throws NotImplementedException;
113 |
114 | /**
115 | * Opens the Quick Settings shade.
116 | * @return true if successful, else return false
117 | * @throws NotImplementedException
118 | */
119 | @JsonRpcErrors({@JsonRpcError(exception=NotImplementedException.class, code=ERROR_CODE_BASE-3)})
120 | boolean openQuickSettings() throws NotImplementedException;
121 |
122 | /**
123 | * Checks if a specific registered UiWatcher has triggered. See registerWatcher(String, UiWatcher). If a UiWatcher runs and its checkForCondition() call returned true, then the UiWatcher is considered triggered. This is helpful if a watcher is detecting errors from ANR or crash dialogs and the test needs to know if a UiWatcher has been triggered.
124 | * @param watcherName the name of registered watcher.
125 | * @return true if triggered else false
126 | */
127 | boolean hasWatcherTriggered(String watcherName); // We should implement some watchers to treat some blocking issues, e.g. force close dialog
128 |
129 | /**
130 | * Checks if any registered UiWatcher have triggered.
131 | * @return true if any UiWatcher have triggered else false.
132 | */
133 | boolean hasAnyWatcherTriggered();
134 |
135 | /**
136 | * Register a ClickUiObjectWatcher
137 | * @param name Watcher name
138 | * @param conditions If all UiObject in the conditions match, the watcher should be triggered.
139 | * @param target The target UiObject should be clicked if all conditions match.
140 | */
141 | void registerClickUiObjectWatcher(String name, Selector[] conditions, Selector target);
142 |
143 | /**
144 | * Register a PressKeysWatcher
145 | * @param name Watcher name
146 | * @param conditions If all UiObject in the conditions match, the watcher should be triggered.
147 | * @param keys All keys will be pressed in sequence.
148 | */
149 | void registerPressKeyskWatcher(String name, Selector[] conditions, String[] keys);
150 |
151 | /**
152 | * Removes a previously registered UiWatcher.
153 | * @param name Watcher name
154 | */
155 | void removeWatcher(String name);
156 |
157 | /**
158 | * Resets a UiWatcher that has been triggered. If a UiWatcher runs and its checkForCondition() call returned true, then the UiWatcher is considered triggered.
159 | */
160 | void resetWatcherTriggers();
161 |
162 | /**
163 | * Force to run all watchers.
164 | */
165 | void runWatchers();
166 |
167 | /**
168 | * Get all registered UiWatchers
169 | * @return UiWatcher names
170 | */
171 | String[] getWatchers();
172 |
173 | /**
174 | * Simulates a short press using key name.
175 | * @param key possible key name is home, back, left, right, up, down, center, menu, search, enter, delete(or del), recent(recent apps), volume_up, volume_down, volume_mute, camera, power
176 | * @return true if successful, else return false
177 | * @throws RemoteException
178 | */
179 | @JsonRpcErrors({@JsonRpcError(exception=RemoteException.class, code=ERROR_CODE_BASE-1)})
180 | boolean pressKey(String key) throws RemoteException;
181 |
182 | /**
183 | * Simulates a short press using a key code. See KeyEvent.
184 | * @param keyCode the key code of the event.
185 | * @return true if successful, else return false
186 | */
187 | boolean pressKeyCode(int keyCode);
188 |
189 | /**
190 | * Simulates a short press using a key code. See KeyEvent.
191 | * @param keyCode the key code of the event.
192 | * @param metaState an integer in which each bit set to 1 represents a pressed meta key
193 | * @return true if successful, else return false
194 | */
195 | boolean pressKeyCode(int keyCode, int metaState);
196 |
197 | /**
198 | * This method simulates pressing the power button if the screen is OFF else it does nothing if the screen is already ON. If the screen was OFF and it just got turned ON, this method will insert a 500ms delay to allow the device time to wake up and accept input.
199 | * @throws RemoteException
200 | */
201 | @JsonRpcErrors({@JsonRpcError(exception=RemoteException.class, code=ERROR_CODE_BASE-1)})
202 | void wakeUp() throws RemoteException;
203 |
204 | /**
205 | * This method simply presses the power button if the screen is ON else it does nothing if the screen is already OFF.
206 | * @throws RemoteException
207 | */
208 | @JsonRpcErrors({@JsonRpcError(exception=RemoteException.class, code=ERROR_CODE_BASE-1)})
209 | void sleep() throws RemoteException;
210 |
211 | /**
212 | * Checks the power manager if the screen is ON.
213 | * @return true if the screen is ON else false
214 | * @throws RemoteException
215 | */
216 | @JsonRpcErrors({@JsonRpcError(exception=RemoteException.class, code=ERROR_CODE_BASE-1)})
217 | boolean isScreenOn() throws RemoteException;
218 |
219 | /**
220 | * Waits for the current application to idle.
221 | * @param timeout in milliseconds
222 | */
223 | void waitForIdle(long timeout);
224 |
225 | /**
226 | * Waits for a window content update event to occur. If a package name for the window is specified, but the current window does not have the same package name, the function returns immediately.
227 | * @param packageName the specified window package name (can be null). If null, a window update from any front-end window will end the wait.
228 | * @param timeout the timeout for the wait
229 | * @return true if a window update occurred, false if timeout has elapsed or if the current window does not have the specified package name
230 | */
231 | boolean waitForWindowUpdate (String packageName, long timeout);
232 |
233 | /***************************************************************************
234 | * Below section contains all methods from UiObject.
235 | ***************************************************************************/
236 |
237 | /**
238 | * Clears the existing text contents in an editable field. The UiSelector of this object must reference a UI element that is editable. When you call this method, the method first sets focus at the start edge of the field. The method then simulates a long-press to select the existing text, and deletes the selected text. If a "Select-All" option is displayed, the method will automatically attempt to use it to ensure full text selection. Note that it is possible that not all the text in the field is selected; for example, if the text contains separators such as spaces, slashes, at symbol etc. Also, not all editable fields support the long-press functionality.
239 | * @param obj the selector of the UiObject.
240 | * @throws UiObjectNotFoundException
241 | */
242 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
243 | void clearTextField(Selector obj) throws UiObjectNotFoundException;
244 |
245 | /**
246 | * Reads the text property of the UI element
247 | * @param obj the selector of the UiObject.
248 | * @return text value of the current node represented by this UiObject
249 | * @throws UiObjectNotFoundException
250 | */
251 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
252 | String getText(Selector obj) throws UiObjectNotFoundException;
253 |
254 | /**
255 | * Sets the text in an editable field, after clearing the field's content. The UiSelector selector of this object must reference a UI element that is editable. When you call this method, the method first simulates a click() on editable field to set focus. The method then clears the field's contents and injects your specified text into the field. If you want to capture the original contents of the field, call getText() first. You can then modify the text and use this method to update the field.
256 | * @param obj the selector of the UiObject.
257 | * @param text string to set
258 | * @return true if operation is successful
259 | * @throws UiObjectNotFoundException
260 | */
261 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
262 | boolean setText(Selector obj, String text) throws UiObjectNotFoundException;
263 |
264 | /**
265 | * Performs a click at the center of the visible bounds of the UI element represented by this UiObject.
266 | * @param obj the target ui object.
267 | * @return true id successful else false
268 | * @throws UiObjectNotFoundException
269 | */
270 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
271 | boolean click(Selector obj) throws UiObjectNotFoundException;
272 |
273 | /**
274 | * Clicks the bottom and right corner or top and left corner of the UI element
275 | * @param obj the target ui object.
276 | * @param corner "br"/"bottomright" means BottomRight, "tl"/"topleft" means TopLeft, "center" means Center.
277 | * @return true on success
278 | * @throws UiObjectNotFoundException
279 | */
280 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
281 | boolean click(Selector obj, String corner) throws UiObjectNotFoundException;
282 |
283 | /**
284 | * Performs a click at the center of the visible bounds of the UI element represented by this UiObject and waits for window transitions. This method differ from click() only in that this method waits for a a new window transition as a result of the click. Some examples of a window transition:
285 | * - launching a new activity
286 | * - bringing up a pop-up menu
287 | * - bringing up a dialog
288 | * @param obj the target ui object.
289 | * @param timeout timeout before giving up on waiting for a new window
290 | * @return true if the event was triggered, else false
291 | * @throws UiObjectNotFoundException
292 | */
293 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
294 | boolean clickAndWaitForNewWindow(Selector obj, long timeout) throws UiObjectNotFoundException;
295 |
296 | /**
297 | * Long clicks the center of the visible bounds of the UI element
298 | * @param obj the target ui object.
299 | * @return true if operation was successful
300 | * @throws UiObjectNotFoundException
301 | */
302 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
303 | boolean longClick(Selector obj) throws UiObjectNotFoundException;
304 |
305 | /**
306 | * Long clicks bottom and right corner of the UI element
307 | * @param obj the target ui object.
308 | * @param corner "br"/"bottomright" means BottomRight, "tl"/"topleft" means TopLeft, "center" means Center.
309 | * @return true if operation was successful
310 | * @throws UiObjectNotFoundException
311 | */
312 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
313 | boolean longClick(Selector obj, String corner) throws UiObjectNotFoundException;
314 |
315 | /**
316 | * Drags this object to a destination UiObject. The number of steps specified in your input parameter can influence the drag speed, and varying speeds may impact the results. Consider evaluating different speeds when using this method in your tests.
317 | * @param obj the ui object to be dragged.
318 | * @param destObj the ui object to be dragged to.
319 | * @param steps usually 40 steps. You can increase or decrease the steps to change the speed.
320 | * @return true if successful
321 | * @throws UiObjectNotFoundException
322 | * @throws NotImplementedException
323 | */
324 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2), @JsonRpcError(exception=NotImplementedException.class, code=ERROR_CODE_BASE-3)})
325 | boolean dragTo (Selector obj, Selector destObj, int steps) throws UiObjectNotFoundException, NotImplementedException;
326 |
327 | /**
328 | * Drags this object to arbitrary coordinates. The number of steps specified in your input parameter can influence the drag speed, and varying speeds may impact the results. Consider evaluating different speeds when using this method in your tests.
329 | * @param obj the ui object to be dragged.
330 | * @param destX the X-axis coordinate of destination.
331 | * @param destY the Y-axis coordinate of destination.
332 | * @param steps usually 40 steps. You can increase or decrease the steps to change the speed.
333 | * @return true if successful
334 | * @throws UiObjectNotFoundException
335 | * @throws NotImplementedException
336 | */
337 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2), @JsonRpcError(exception=NotImplementedException.class, code=ERROR_CODE_BASE-3)})
338 | boolean dragTo (Selector obj, int destX, int destY, int steps) throws UiObjectNotFoundException, NotImplementedException;
339 |
340 | /**
341 | * Check if view exists. This methods performs a waitForExists(long) with zero timeout. This basically returns immediately whether the view represented by this UiObject exists or not.
342 | * @param obj the ui object.
343 | * @return true if the view represented by this UiObject does exist
344 | */
345 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
346 | boolean exist(Selector obj);
347 |
348 | /**
349 | * Get the object info.
350 | * @param obj the target ui object.
351 | * @return object info.
352 | * @throws UiObjectNotFoundException
353 | */
354 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
355 | ObjInfo objInfo(Selector obj) throws UiObjectNotFoundException;
356 |
357 | /**
358 | * Get the count of the UiObject instances by the selector
359 | * @param obj the selector of the ui object
360 | * @return the count of instances.
361 | */
362 | int count(Selector obj);
363 |
364 | /**
365 | * Get the info of all instance by the selector.
366 | * @param obj the selector of ui object.
367 | * @return array of object info.
368 | */
369 | ObjInfo[] objInfoOfAllInstances(Selector obj);
370 |
371 | /**
372 | * Generates a two-pointer gesture with arbitrary starting and ending points.
373 | * @param obj the target ui object. ??
374 | * @param startPoint1 start point of pointer 1
375 | * @param startPoint2 start point of pointer 2
376 | * @param endPoint1 end point of pointer 1
377 | * @param endPoint2 end point of pointer 2
378 | * @param steps the number of steps for the gesture. Steps are injected about 5 milliseconds apart, so 100 steps may take around 0.5 seconds to complete.
379 | * @return true if all touch events for this gesture are injected successfully, false otherwise
380 | * @throws UiObjectNotFoundException
381 | */
382 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2), @JsonRpcError(exception=NotImplementedException.class, code=ERROR_CODE_BASE-3)})
383 | boolean gesture(Selector obj, Point startPoint1, Point startPoint2, Point endPoint1, Point endPoint2, int steps) throws UiObjectNotFoundException, NotImplementedException;
384 |
385 | /**
386 | * Performs a two-pointer gesture, where each pointer moves diagonally toward the other, from the edges to the center of this UiObject .
387 | * @param obj the target ui object.
388 | * @param percent percentage of the object's diagonal length for the pinch gesture
389 | * @param steps the number of steps for the gesture. Steps are injected about 5 milliseconds apart, so 100 steps may take around 0.5 seconds to complete.
390 | * @return true if all touch events for this gesture are injected successfully, false otherwise
391 | * @throws UiObjectNotFoundException
392 | * @throws NotImplementedException
393 | */
394 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2), @JsonRpcError(exception=NotImplementedException.class, code=ERROR_CODE_BASE-3)})
395 | boolean pinchIn(Selector obj, int percent, int steps) throws UiObjectNotFoundException, NotImplementedException;
396 |
397 | /**
398 | * Performs a two-pointer gesture, where each pointer moves diagonally opposite across the other, from the center out towards the edges of the this UiObject.
399 | * @param obj the target ui object.
400 | * @param percent percentage of the object's diagonal length for the pinch gesture
401 | * @param steps the number of steps for the gesture. Steps are injected about 5 milliseconds apart, so 100 steps may take around 0.5 seconds to complete.
402 | * @return true if all touch events for this gesture are injected successfully, false otherwise
403 | * @throws UiObjectNotFoundException
404 | * @throws NotImplementedException
405 | */
406 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2), @JsonRpcError(exception=NotImplementedException.class, code=ERROR_CODE_BASE-3)})
407 | boolean pinchOut(Selector obj, int percent, int steps) throws UiObjectNotFoundException, NotImplementedException;
408 |
409 | /**
410 | * Performs the swipe up/down/left/right action on the UiObject
411 | * @param obj the target ui object.
412 | * @param dir "u"/"up", "d"/"down", "l"/"left", "r"/"right"
413 | * @param steps indicates the number of injected move steps into the system. Steps are injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
414 | * @return true of successful
415 | * @throws UiObjectNotFoundException
416 | */
417 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
418 | boolean swipe(Selector obj, String dir, int steps) throws UiObjectNotFoundException;
419 |
420 | /**
421 | * Waits a specified length of time for a view to become visible. This method waits until the view becomes visible on the display, or until the timeout has elapsed. You can use this method in situations where the content that you want to select is not immediately displayed.
422 | * @param obj the target ui object
423 | * @param timeout time to wait (in milliseconds)
424 | * @return true if the view is displayed, else false if timeout elapsed while waiting
425 | */
426 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
427 | boolean waitForExists (Selector obj, long timeout);
428 |
429 | /**
430 | * Waits a specified length of time for a view to become undetectable. This method waits until a view is no longer matchable, or until the timeout has elapsed. A view becomes undetectable when the UiSelector of the object is unable to find a match because the element has either changed its state or is no longer displayed. You can use this method when attempting to wait for some long operation to compete, such as downloading a large file or connecting to a remote server.
431 | * @param obj the target ui object
432 | * @param timeout time to wait (in milliseconds)
433 | * @return true if the element is gone before timeout elapsed, else false if timeout elapsed but a matching element is still found.
434 | */
435 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
436 | boolean waitUntilGone (Selector obj, long timeout);
437 |
438 | /***************************************************************************
439 | * Below section contains all methods from UiScrollable.
440 | ***************************************************************************/
441 |
442 | /**
443 | * Performs a backwards fling action with the default number of fling steps (5). If the swipe direction is set to vertical, then the swipe will be performed from top to bottom. If the swipe direction is set to horizontal, then the swipes will be performed from left to right. Make sure to take into account devices configured with right-to-left languages like Arabic and Hebrew.
444 | * @param obj the selector of the scrollable object
445 | * @param isVertical vertical or horizontal
446 | * @return true if scrolled, and false if can't scroll anymore
447 | * @throws UiObjectNotFoundException
448 | */
449 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
450 | boolean flingBackward(Selector obj, boolean isVertical) throws UiObjectNotFoundException;
451 |
452 | /**
453 | * Performs a forward fling with the default number of fling steps (5). If the swipe direction is set to vertical, then the swipes will be performed from bottom to top. If the swipe direction is set to horizontal, then the swipes will be performed from right to left. Make sure to take into account devices configured with right-to-left languages like Arabic and Hebrew.
454 | * @param obj the selector of the scrollable object
455 | * @param isVertical vertical or horizontal
456 | * @return true if scrolled, and false if can't scroll anymore
457 | * @throws UiObjectNotFoundException
458 | */
459 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
460 | boolean flingForward(Selector obj, boolean isVertical) throws UiObjectNotFoundException;
461 |
462 | /**
463 | * Performs a fling gesture to reach the beginning of a scrollable layout element. The beginning can be at the top-most edge in the case of vertical controls, or the left-most edge for horizontal controls. Make sure to take into account devices configured with right-to-left languages like Arabic and Hebrew.
464 | * @param obj the selector of the scrollable object
465 | * @param isVertical vertical or horizontal
466 | * @param maxSwipes max swipes to achieve beginning.
467 | * @return true on scrolled, else false
468 | * @throws UiObjectNotFoundException
469 | */
470 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
471 | boolean flingToBeginning (Selector obj, boolean isVertical, int maxSwipes) throws UiObjectNotFoundException;
472 |
473 | /**
474 | * Performs a fling gesture to reach the end of a scrollable layout element. The end can be at the bottom-most edge in the case of vertical controls, or the right-most edge for horizontal controls. Make sure to take into account devices configured with right-to-left languages like Arabic and Hebrew.
475 | * @param obj the selector of the scrollable object
476 | * @param isVertical vertical or horizontal
477 | * @param maxSwipes max swipes to achieve end.
478 | * @return true on scrolled, else false
479 | * @throws UiObjectNotFoundException
480 | */
481 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
482 | boolean flingToEnd (Selector obj, boolean isVertical, int maxSwipes) throws UiObjectNotFoundException;
483 |
484 | /**
485 | * Performs a backward scroll. If the swipe direction is set to vertical, then the swipes will be performed from top to bottom. If the swipe direction is set to horizontal, then the swipes will be performed from left to right. Make sure to take into account devices configured with right-to-left languages like Arabic and Hebrew.
486 | * @param obj the selector of the scrollable object
487 | * @param isVertical vertical or horizontal
488 | * @param steps number of steps. Use this to control the speed of the scroll action.
489 | * @return true if scrolled, false if can't scroll anymore
490 | * @throws UiObjectNotFoundException
491 | */
492 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
493 | boolean scrollBackward (Selector obj, boolean isVertical, int steps) throws UiObjectNotFoundException;
494 |
495 | /**
496 | * Performs a forward scroll with the default number of scroll steps (55). If the swipe direction is set to vertical, then the swipes will be performed from bottom to top. If the swipe direction is set to horizontal, then the swipes will be performed from right to left. Make sure to take into account devices configured with right-to-left languages like Arabic and Hebrew.
497 | * @param obj the selector of the scrollable object
498 | * @param isVertical vertical or horizontal
499 | * @param steps number of steps. Use this to control the speed of the scroll action.
500 | * @return true on scrolled, else false
501 | * @throws UiObjectNotFoundException
502 | */
503 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
504 | boolean scrollForward (Selector obj, boolean isVertical, int steps) throws UiObjectNotFoundException;
505 |
506 | /**
507 | * Scrolls to the beginning of a scrollable layout element. The beginning can be at the top-most edge in the case of vertical controls, or the left-most edge for horizontal controls. Make sure to take into account devices configured with right-to-left languages like Arabic and Hebrew.
508 | * @param obj the selector of the scrollable object
509 | * @param isVertical vertical or horizontal
510 | * @param maxSwipes max swipes to be performed.
511 | * @param steps use steps to control the speed, so that it may be a scroll, or fling
512 | * @return true on scrolled else false
513 | * @throws UiObjectNotFoundException
514 | */
515 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
516 | boolean scrollToBeginning (Selector obj, boolean isVertical, int maxSwipes, int steps) throws UiObjectNotFoundException;
517 |
518 | /**
519 | * Scrolls to the end of a scrollable layout element. The end can be at the bottom-most edge in the case of vertical controls, or the right-most edge for horizontal controls. Make sure to take into account devices configured with right-to-left languages like Arabic and Hebrew.
520 | * @param obj the selector of the scrollable object
521 | * @param isVertical vertical or horizontal
522 | * @param maxSwipes max swipes to be performed.
523 | * @param steps use steps to control the speed, so that it may be a scroll, or fling
524 | * @return true on scrolled, else false
525 | * @throws UiObjectNotFoundException
526 | */
527 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
528 | boolean scrollToEnd (Selector obj, boolean isVertical, int maxSwipes, int steps) throws UiObjectNotFoundException;
529 |
530 | /**
531 | * Perform a scroll forward action to move through the scrollable layout element until a visible item that matches the selector is found.
532 | * @param obj the selector of the scrollable object
533 | * @param targetObj the item matches the selector to be found.
534 | * @param isVertical vertical or horizontal
535 | * @return true on scrolled, else false
536 | * @throws UiObjectNotFoundException
537 | */
538 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
539 | boolean scrollTo (Selector obj, Selector targetObj, boolean isVertical) throws UiObjectNotFoundException;
540 |
541 | /***************************************************************************
542 | * Some time we have to use chained selection, e.g.
543 | * new UiCollection(...).getChildByText(...).getChild()....
544 | * So we should have a mechanism to save the previous UiObject.
545 | ***************************************************************************/
546 |
547 | /**
548 | * Searches for child UI element within the constraints of this UiSelector selector. It looks for any child matching the childPattern argument that has a child UI element anywhere within its sub hierarchy that has a text attribute equal to text. The returned UiObject will point at the childPattern instance that matched the search and not at the identifying child element that matched the text attribute.
549 | * @param collection Selector of UiCollection or UiScrollable.
550 | * @param text String of the identifying child contents of of the childPattern
551 | * @param child UiSelector selector of the child pattern to match and return
552 | * @return A string ID represent the returned UiObject.
553 | */
554 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
555 | String childByText(Selector collection, Selector child, String text) throws UiObjectNotFoundException;
556 |
557 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
558 | String childByText(Selector collection, Selector child, String text, boolean allowScrollSearch) throws UiObjectNotFoundException;
559 |
560 | /**
561 | * Searches for child UI element within the constraints of this UiSelector selector. It looks for any child matching the childPattern argument that has a child UI element anywhere within its sub hierarchy that has content-description text. The returned UiObject will point at the childPattern instance that matched the search and not at the identifying child element that matched the content description.
562 | * @param collection Selector of UiCollection or UiScrollable
563 | * @param child UiSelector selector of the child pattern to match and return
564 | * @param text String of the identifying child contents of of the childPattern
565 | * @return A string ID represent the returned UiObject.
566 | */
567 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
568 | String childByDescription(Selector collection, Selector child, String text) throws UiObjectNotFoundException;
569 |
570 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
571 | String childByDescription(Selector collection, Selector child, String text, boolean allowScrollSearch) throws UiObjectNotFoundException;
572 |
573 | /**
574 | * Searches for child UI element within the constraints of this UiSelector. It looks for any child matching the childPattern argument that has a child UI element anywhere within its sub hierarchy that is at the instance specified. The operation is performed only on the visible items and no scrolling is performed in this case.
575 | * @param collection Selector of UiCollection or UiScrollable
576 | * @param child UiSelector selector of the child pattern to match and return
577 | * @param instance int the desired matched instance of this childPattern
578 | * @return A string ID represent the returned UiObject.
579 | */
580 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
581 | String childByInstance(Selector collection, Selector child, int instance) throws UiObjectNotFoundException;
582 |
583 | /**
584 | * Creates a new UiObject for a child view that is under the present UiObject.
585 | * @param obj The ID string represent the parent UiObject.
586 | * @param selector UiSelector selector of the child pattern to match and return
587 | * @return A string ID represent the returned UiObject.
588 | */
589 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
590 | String getChild(String obj, Selector selector) throws UiObjectNotFoundException;
591 |
592 | /**
593 | * Creates a new UiObject for a sibling view or a child of the sibling view, relative to the present UiObject.
594 | * @param obj The ID string represent the source UiObject.
595 | * @param selector for a sibling view or children of the sibling view
596 | * @return A string ID represent the returned UiObject.
597 | */
598 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
599 | String getFromParent(String obj, Selector selector) throws UiObjectNotFoundException;
600 |
601 | /**
602 | * Get a new UiObject from the selector.
603 | * @param selector Selector of the UiObject
604 | * @return A string ID represent the returned UiObject.
605 | * @throws UiObjectNotFoundException
606 | */
607 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
608 | String getUiObject(Selector selector) throws UiObjectNotFoundException;
609 |
610 | /**
611 | * Remove the UiObject from memory.
612 | */
613 | void removeUiObject(String obj);
614 |
615 |
616 | /**
617 | * Get all named UiObjects.
618 | * @return all names
619 | */
620 | String[] getUiObjects();
621 |
622 | /**
623 | * Clears the existing text contents in an editable field. The UiSelector of this object must reference a UI element that is editable. When you call this method, the method first sets focus at the start edge of the field. The method then simulates a long-press to select the existing text, and deletes the selected text. If a "Select-All" option is displayed, the method will automatically attempt to use it to ensure full text selection. Note that it is possible that not all the text in the field is selected; for example, if the text contains separators such as spaces, slashes, at symbol etc. Also, not all editable fields support the long-press functionality.
624 | * @param obj the id of the UiObject.
625 | * @throws UiObjectNotFoundException
626 | */
627 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
628 | void clearTextField(String obj) throws UiObjectNotFoundException;
629 |
630 | /**
631 | * Reads the text property of the UI element
632 | * @param obj the id of the UiObject.
633 | * @return text value of the current node represented by this UiObject
634 | * @throws UiObjectNotFoundException
635 | */
636 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
637 | String getText(String obj) throws UiObjectNotFoundException;
638 |
639 | /**
640 | * Sets the text in an editable field, after clearing the field's content. The UiSelector selector of this object must reference a UI element that is editable. When you call this method, the method first simulates a click() on editable field to set focus. The method then clears the field's contents and injects your specified text into the field. If you want to capture the original contents of the field, call getText() first. You can then modify the text and use this method to update the field.
641 | * @param obj the id of the UiObject.
642 | * @param text string to set
643 | * @return true if operation is successful
644 | * @throws UiObjectNotFoundException
645 | */
646 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
647 | boolean setText(String obj, String text) throws UiObjectNotFoundException;
648 |
649 | /**
650 | * Performs a click at the center of the visible bounds of the UI element represented by this UiObject.
651 | * @param obj the id of target ui object.
652 | * @return true id successful else false
653 | * @throws UiObjectNotFoundException
654 | */
655 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
656 | boolean click(String obj) throws UiObjectNotFoundException;
657 |
658 | /**
659 | * Clicks the bottom and right corner or top and left corner of the UI element
660 | * @param obj the id of target ui object.
661 | * @param corner "br"/"bottomright" means BottomRight, "tl"/"topleft" means TopLeft, "center" means Center.
662 | * @return true on success
663 | * @throws UiObjectNotFoundException
664 | */
665 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
666 | boolean click(String obj, String corner) throws UiObjectNotFoundException;
667 |
668 | /**
669 | * Performs a click at the center of the visible bounds of the UI element represented by this UiObject and waits for window transitions. This method differ from click() only in that this method waits for a a new window transition as a result of the click. Some examples of a window transition:
670 | * - launching a new activity
671 | * - bringing up a pop-up menu
672 | * - bringing up a dialog
673 | * @param obj the id of target ui object.
674 | * @param timeout timeout before giving up on waiting for a new window
675 | * @return true if the event was triggered, else false
676 | * @throws UiObjectNotFoundException
677 | */
678 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
679 | boolean clickAndWaitForNewWindow(String obj, long timeout) throws UiObjectNotFoundException;
680 |
681 | /**
682 | * Long clicks the center of the visible bounds of the UI element
683 | * @param obj the id of target ui object.
684 | * @return true if operation was successful
685 | * @throws UiObjectNotFoundException
686 | */
687 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
688 | boolean longClick(String obj) throws UiObjectNotFoundException;
689 |
690 | /**
691 | * Long clicks bottom and right corner of the UI element
692 | * @param obj the id of target ui object.
693 | * @param corner "br"/"bottomright" means BottomRight, "tl"/"topleft" means TopLeft, "center" means Center.
694 | * @return true if operation was successful
695 | * @throws UiObjectNotFoundException
696 | */
697 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
698 | boolean longClick(String obj, String corner) throws UiObjectNotFoundException;
699 |
700 | /**
701 | * Drags this object to a destination UiObject. The number of steps specified in your input parameter can influence the drag speed, and varying speeds may impact the results. Consider evaluating different speeds when using this method in your tests.
702 | * @param obj the id of ui object to be dragged.
703 | * @param destObj the ui object to be dragged to.
704 | * @param steps usually 40 steps. You can increase or decrease the steps to change the speed.
705 | * @return true if successful
706 | * @throws UiObjectNotFoundException
707 | * @throws NotImplementedException
708 | */
709 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2), @JsonRpcError(exception=NotImplementedException.class, code=ERROR_CODE_BASE-3)})
710 | boolean dragTo (String obj, Selector destObj, int steps) throws UiObjectNotFoundException, NotImplementedException;
711 |
712 | /**
713 | * Drags this object to arbitrary coordinates. The number of steps specified in your input parameter can influence the drag speed, and varying speeds may impact the results. Consider evaluating different speeds when using this method in your tests.
714 | * @param obj the id of ui object to be dragged.
715 | * @param destX the X-axis coordinate of destination.
716 | * @param destY the Y-axis coordinate of destination.
717 | * @param steps usually 40 steps. You can increase or decrease the steps to change the speed.
718 | * @return true if successful
719 | * @throws UiObjectNotFoundException
720 | * @throws NotImplementedException
721 | */
722 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2), @JsonRpcError(exception=NotImplementedException.class, code=ERROR_CODE_BASE-3)})
723 | boolean dragTo (String obj, int destX, int destY, int steps) throws UiObjectNotFoundException, NotImplementedException;
724 |
725 | /**
726 | * Check if view exists. This methods performs a waitForExists(long) with zero timeout. This basically returns immediately whether the view represented by this UiObject exists or not.
727 | * @param obj the id of ui object.
728 | * @return true if the view represented by this UiObject does exist
729 | */
730 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
731 | boolean exist(String obj);
732 |
733 | /**
734 | * Get the object info.
735 | * @param obj the id of target ui object.
736 | * @return object info.
737 | * @throws UiObjectNotFoundException
738 | */
739 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
740 | ObjInfo objInfo(String obj) throws UiObjectNotFoundException;
741 |
742 | /**
743 | * Generates a two-pointer gesture with arbitrary starting and ending points.
744 | * @param obj the id of target ui object. ??
745 | * @param startPoint1 start point of pointer 1
746 | * @param startPoint2 start point of pointer 2
747 | * @param endPoint1 end point of pointer 1
748 | * @param endPoint2 end point of pointer 2
749 | * @param steps the number of steps for the gesture. Steps are injected about 5 milliseconds apart, so 100 steps may take around 0.5 seconds to complete.
750 | * @return true if all touch events for this gesture are injected successfully, false otherwise
751 | * @throws UiObjectNotFoundException
752 | */
753 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2), @JsonRpcError(exception=NotImplementedException.class, code=ERROR_CODE_BASE-3)})
754 | boolean gesture(String obj, Point startPoint1, Point startPoint2, Point endPoint1, Point endPoint2, int steps) throws UiObjectNotFoundException, NotImplementedException;
755 |
756 | /**
757 | * Performs a two-pointer gesture, where each pointer moves diagonally toward the other, from the edges to the center of this UiObject .
758 | * @param obj the id of target ui object.
759 | * @param percent percentage of the object's diagonal length for the pinch gesture
760 | * @param steps the number of steps for the gesture. Steps are injected about 5 milliseconds apart, so 100 steps may take around 0.5 seconds to complete.
761 | * @return true if all touch events for this gesture are injected successfully, false otherwise
762 | * @throws UiObjectNotFoundException
763 | * @throws NotImplementedException
764 | */
765 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2), @JsonRpcError(exception=NotImplementedException.class, code=ERROR_CODE_BASE-3)})
766 | boolean pinchIn(String obj, int percent, int steps) throws UiObjectNotFoundException, NotImplementedException;
767 |
768 | /**
769 | * Performs a two-pointer gesture, where each pointer moves diagonally opposite across the other, from the center out towards the edges of the this UiObject.
770 | * @param obj the id of target ui object.
771 | * @param percent percentage of the object's diagonal length for the pinch gesture
772 | * @param steps the number of steps for the gesture. Steps are injected about 5 milliseconds apart, so 100 steps may take around 0.5 seconds to complete.
773 | * @return true if all touch events for this gesture are injected successfully, false otherwise
774 | * @throws UiObjectNotFoundException
775 | * @throws NotImplementedException
776 | */
777 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2), @JsonRpcError(exception=NotImplementedException.class, code=ERROR_CODE_BASE-3)})
778 | boolean pinchOut(String obj, int percent, int steps) throws UiObjectNotFoundException, NotImplementedException;
779 |
780 | /**
781 | * Performs the swipe up/down/left/right action on the UiObject
782 | * @param obj the id of target ui object.
783 | * @param dir "u"/"up", "d"/"down", "l"/"left", "r"/"right"
784 | * @param steps indicates the number of injected move steps into the system. Steps are injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
785 | * @return true of successful
786 | * @throws UiObjectNotFoundException
787 | */
788 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
789 | boolean swipe(String obj, String dir, int steps) throws UiObjectNotFoundException;
790 |
791 | /**
792 | * Waits a specified length of time for a view to become visible. This method waits until the view becomes visible on the display, or until the timeout has elapsed. You can use this method in situations where the content that you want to select is not immediately displayed.
793 | * @param obj the id of target ui object
794 | * @param timeout time to wait (in milliseconds)
795 | * @return true if the view is displayed, else false if timeout elapsed while waiting
796 | */
797 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
798 | boolean waitForExists (String obj, long timeout) throws UiObjectNotFoundException;
799 |
800 | /**
801 | * Waits a specified length of time for a view to become undetectable. This method waits until a view is no longer matchable, or until the timeout has elapsed. A view becomes undetectable when the UiSelector of the object is unable to find a match because the element has either changed its state or is no longer displayed. You can use this method when attempting to wait for some long operation to compete, such as downloading a large file or connecting to a remote server.
802 | * @param obj the id of target ui object
803 | * @param timeout time to wait (in milliseconds)
804 | * @return true if the element is gone before timeout elapsed, else false if timeout elapsed but a matching element is still found.
805 | */
806 | @JsonRpcErrors({@JsonRpcError(exception=UiObjectNotFoundException.class, code=ERROR_CODE_BASE-2)})
807 | boolean waitUntilGone (String obj, long timeout) throws UiObjectNotFoundException;
808 |
809 | /**
810 | * Get Configurator
811 | * @return Configurator information.
812 | * @throws NotImplementedException
813 | */
814 | @JsonRpcErrors({@JsonRpcError(exception=NotImplementedException.class, code=ERROR_CODE_BASE-3)})
815 | ConfiguratorInfo getConfigurator() throws NotImplementedException;
816 |
817 | /**
818 | * Set Configurator.
819 | * @param info the configurator information to be set.
820 | * @throws NotImplementedException
821 | */
822 | @JsonRpcErrors({@JsonRpcError(exception=NotImplementedException.class, code=ERROR_CODE_BASE-3)})
823 | ConfiguratorInfo setConfigurator(ConfiguratorInfo info) throws NotImplementedException;
824 | }
825 |
--------------------------------------------------------------------------------
/src/com/github/uiautomatorstub/AutomatorServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.github.uiautomatorstub;
2 |
3 | import android.os.Build;
4 | import android.os.Environment;
5 | import android.os.RemoteException;
6 |
7 | import java.io.*;
8 | import java.lang.reflect.InvocationTargetException;
9 | import java.util.*;
10 | import java.util.concurrent.ConcurrentHashMap;
11 |
12 | import android.view.KeyEvent;
13 | import com.android.uiautomator.core.*;
14 | import com.github.uiautomatorstub.watcher.ClickUiObjectWatcher;
15 | import com.github.uiautomatorstub.watcher.PressKeysWatcher;
16 |
17 |
18 | public class AutomatorServiceImpl implements AutomatorService {
19 |
20 | final public static String STORAGE_PATH = "/data/local/tmp/";
21 | private final HashSet watchers = new HashSet();
22 | private final ConcurrentHashMap uiObjects = new ConcurrentHashMap();
23 |
24 | public AutomatorServiceImpl() {
25 | }
26 |
27 | /**
28 | * Walk around to avoid backforward compatibility issue on uiautomator between api level 16/17.
29 | */
30 | static void setAsHorizontalList(UiScrollable obj) {
31 | Class noparams[] = {};
32 | Object nullparmas[] = {};
33 | try {
34 | Class.forName("com.android.uiautomator.core.UiScrollable").getDeclaredMethod("setAsHorizontalList", noparams).invoke(obj, nullparmas);
35 | } catch (NoSuchMethodException e) {
36 | Log.d(e.getMessage());
37 | } catch (ClassNotFoundException e) {
38 | Log.d(e.getMessage());
39 | } catch (InvocationTargetException e) {
40 | Log.d(e.getMessage());
41 | } catch (IllegalAccessException e) {
42 | Log.d(e.getMessage());
43 | }
44 | }
45 |
46 | /**
47 | * Walk around to avoid backforward compatibility issue on uiautomator between api level 16/17.
48 | */
49 | static void setAsVerticalList(UiScrollable obj) {
50 | Class noparams[] = {};
51 | Object nullparmas[] = {};
52 | try {
53 | Class.forName("com.android.uiautomator.core.UiScrollable").getDeclaredMethod("setAsVerticalList", noparams).invoke(obj, nullparmas);
54 | } catch (NoSuchMethodException e) {
55 | Log.d(e.getMessage());
56 | } catch (ClassNotFoundException e) {
57 | Log.d(e.getMessage());
58 | } catch (InvocationTargetException e) {
59 | Log.d(e.getMessage());
60 | } catch (IllegalAccessException e) {
61 | Log.d(e.getMessage());
62 | }
63 | }
64 |
65 | /**
66 | * It's to test if the service is alive.
67 | *
68 | * @return 'pong'
69 | */
70 | @Override
71 | public String ping() {
72 | //new UiObject(new UiSelector()).exists(); // here we call the method just for checking if the UiAutomationService is ok, else it will throw IllegalStateException.
73 | return "pong";
74 | }
75 |
76 | /**
77 | * Get the device info.
78 | *
79 | * @return device info.
80 | */
81 | @Override
82 | public DeviceInfo deviceInfo() {
83 | return DeviceInfo.getDeviceInfo();
84 | }
85 |
86 | /**
87 | * Perform a click at arbitrary coordinates specified by the user.
88 | *
89 | * @param x coordinate
90 | * @param y coordinate
91 | * @return true if the click succeeded else false
92 | */
93 | @Override
94 | public boolean click(int x, int y) {
95 | return UiDevice.getInstance().click(x, y);
96 | }
97 |
98 | /**
99 | * Performs a swipe from one coordinate to another coordinate. You can control the smoothness and speed of the swipe by specifying the number of steps. Each step execution is throttled to 5 milliseconds per step, so for a 100 steps, the swipe will take around 0.5 seconds to complete.
100 | *
101 | * @param startX X-axis value for the starting coordinate
102 | * @param startY Y-axis value for the starting coordinate
103 | * @param endX X-axis value for the ending coordinate
104 | * @param endY Y-axis value for the ending coordinate
105 | * @param steps is the number of steps for the swipe action
106 | * @return true if swipe is performed, false if the operation fails or the coordinates are invalid
107 | * @throws com.github.uiautomatorstub.NotImplementedException
108 | *
109 | */
110 | @Override
111 | public boolean drag(int startX, int startY, int endX, int endY, int steps) throws NotImplementedException {
112 | if (Build.VERSION.SDK_INT < 18)
113 | throw new NotImplementedException();
114 |
115 | return UiDevice.getInstance().drag(startX, startY, endX, endY, steps);
116 | }
117 |
118 | /**
119 | * Performs a swipe from one coordinate to another using the number of steps to determine smoothness and speed. Each step execution is throttled to 5ms per step. So for a 100 steps, the swipe will take about 1/2 second to complete.
120 | *
121 | * @param startX X-axis value for the starting coordinate
122 | * @param startY Y-axis value for the starting coordinate
123 | * @param endX X-axis value for the ending coordinate
124 | * @param endY Y-axis value for the ending coordinate
125 | * @param steps is the number of move steps sent to the system
126 | * @return false if the operation fails or the coordinates are invalid
127 | */
128 | @Override
129 | public boolean swipe(int startX, int startY, int endX, int endY, int steps) {
130 | return UiDevice.getInstance().swipe(startX,startY, endX, endY, steps);
131 | }
132 |
133 | /**
134 | * Helper method used for debugging to dump the current window's layout hierarchy. The file root location is /data/local/tmp
135 | * @param compressed use compressed layout hierarchy or not using setCompressedLayoutHeirarchy method. Ignore the parameter in case the API level lt 18.
136 | * @param filename the filename to be stored.
137 | * @return the absolute path name of dumped file.
138 | */
139 | @Override
140 | public String dumpWindowHierarchy(boolean compressed, String filename) {
141 | if (Build.VERSION.SDK_INT >= 18)
142 | UiDevice.getInstance().setCompressedLayoutHeirarchy(compressed);
143 | File parent = new File(Environment.getDataDirectory(), "local/tmp"); // Environment.getDataDirectory() return /data/local/tmp in android 4.3 but not expected /data
144 | if (!parent.exists())
145 | parent.mkdirs();
146 | boolean return_value = false;
147 | if (filename == null || filename == "") {
148 | filename = "dump.xml";
149 | return_value = true;
150 | }
151 | File dumpFile = new File(parent, filename).getAbsoluteFile();
152 | UiDevice.getInstance().dumpWindowHierarchy(filename);
153 | File f = new File(STORAGE_PATH, filename); // It should be this one, but in Android4.3, it is "/data/local/tmp/local/tmp"......
154 | if (!f.exists()) f = dumpFile;
155 | if (f.exists()) {
156 | if (return_value) {
157 | BufferedReader reader = null;
158 | try {
159 | StringBuilder sb = new StringBuilder();
160 | reader = new BufferedReader(new FileReader(f));
161 | char[] buffer = new char[4096];
162 | int len = 0;
163 | while ((len = reader.read(buffer)) != -1) {
164 | sb.append(new String(buffer, 0, len));
165 | }
166 | reader.close();
167 | reader = null;
168 | return sb.toString();
169 | } catch (IOException e) {
170 | Log.e(e.toString());
171 | } finally {
172 | if (reader != null) {
173 | try {
174 | reader.close();
175 | reader = null;
176 | } catch (IOException e1) {
177 | }
178 | }
179 | }
180 | return null;
181 | } else
182 | return f.getAbsolutePath();
183 | } else
184 | return null;
185 | }
186 |
187 | /**
188 | * Take a screenshot of current window and store it as PNG The screenshot is adjusted per screen rotation
189 | *
190 | * @param filename where the PNG should be written to
191 | * @param scale scale the screenshot down if needed; 1.0f for original size
192 | * @param quality quality of the PNG compression; range: 0-100
193 | * @return the file name of the screenshot. null if failed.
194 | * @throws com.github.uiautomatorstub.NotImplementedException
195 | *
196 | */
197 | @Override
198 | public String takeScreenshot(String filename, float scale, int quality) throws NotImplementedException {
199 | if (Build.VERSION.SDK_INT < 17)
200 | throw new NotImplementedException("takeScreenshot");
201 | File f = new File(STORAGE_PATH, filename);
202 | UiDevice.getInstance().takeScreenshot(f, scale, quality);
203 | if (f.exists())
204 | return f.getAbsolutePath();
205 | return null;
206 | }
207 |
208 | /**
209 | * Disables the sensors and freezes the device rotation at its current rotation state, or enable it.
210 | *
211 | * @param freeze true to freeze the rotation, false to unfreeze the rotation.
212 | * @throws android.os.RemoteException
213 | */
214 | @Override
215 | public void freezeRotation(boolean freeze) throws RemoteException {
216 | if (freeze)
217 | UiDevice.getInstance().freezeRotation();
218 | else
219 | UiDevice.getInstance().unfreezeRotation();
220 | }
221 |
222 | /**
223 | * Simulates orienting the device to the left/right/natural and also freezes rotation by disabling the sensors.
224 | *
225 | * @param dir Left or l, Right or r, Natural or n, case insensitive
226 | * @throws android.os.RemoteException
227 | * @throws com.github.uiautomatorstub.NotImplementedException
228 | *
229 | */
230 | @Override
231 | public void setOrientation(String dir) throws RemoteException, NotImplementedException {
232 | if (Build.VERSION.SDK_INT < 17)
233 | throw new NotImplementedException("setOrientation");
234 | dir = dir.toLowerCase();
235 | if ("left".equals(dir) || "l".equals(dir))
236 | UiDevice.getInstance().setOrientationLeft();
237 | else if ("right".equals(dir) || "r".equals(dir))
238 | UiDevice.getInstance().setOrientationRight();
239 | else if ("natural".equals(dir) || "n".equals(dir))
240 | UiDevice.getInstance().setOrientationNatural();
241 | }
242 |
243 | /**
244 | * Retrieves the text from the last UI traversal event received.
245 | *
246 | * @return the text from the last UI traversal event received.
247 | */
248 | @Override
249 | public String getLastTraversedText() {
250 | return UiDevice.getInstance().getLastTraversedText();
251 | }
252 |
253 | /**
254 | * Clears the text from the last UI traversal event.
255 | */
256 | @Override
257 | public void clearLastTraversedText() {
258 | UiDevice.getInstance().clearLastTraversedText();
259 | }
260 |
261 | /**
262 | * Opens the notification shade.
263 | *
264 | * @return true if successful, else return false
265 | * @throws com.github.uiautomatorstub.NotImplementedException
266 | *
267 | */
268 | @Override
269 | public boolean openNotification() throws NotImplementedException {
270 | if (Build.VERSION.SDK_INT < 18)
271 | throw new NotImplementedException("openNotification");
272 | return UiDevice.getInstance().openNotification();
273 | }
274 |
275 | /**
276 | * Opens the Quick Settings shade.
277 | *
278 | * @return true if successful, else return false
279 | * @throws com.github.uiautomatorstub.NotImplementedException
280 | *
281 | */
282 | @Override
283 | public boolean openQuickSettings() throws NotImplementedException {
284 | if (Build.VERSION.SDK_INT < 18)
285 | throw new NotImplementedException("openQuickSettings");
286 | return UiDevice.getInstance().openQuickSettings();
287 | }
288 |
289 | /**
290 | * Checks if a specific registered UiWatcher has triggered. See registerWatcher(String, UiWatcher). If a UiWatcher runs and its checkForCondition() call returned true, then the UiWatcher is considered triggered. This is helpful if a watcher is detecting errors from ANR or crash dialogs and the test needs to know if a UiWatcher has been triggered.
291 | *
292 | * @param watcherName the name of registered watcher.
293 | * @return true if triggered else false
294 | */
295 | @Override
296 | public boolean hasWatcherTriggered(String watcherName) {
297 | return UiDevice.getInstance().hasWatcherTriggered(watcherName);
298 | }
299 |
300 | /**
301 | * Checks if any registered UiWatcher have triggered.
302 | *
303 | * @return true if any UiWatcher have triggered else false.
304 | */
305 | @Override
306 | public boolean hasAnyWatcherTriggered() {
307 | return UiDevice.getInstance().hasAnyWatcherTriggered();
308 | }
309 |
310 | /**
311 | * Register a ClickUiObjectWatcher
312 | *
313 | * @param name Watcher name
314 | * @param conditions If all UiObject in the conditions match, the watcher should be triggered.
315 | * @param target The target UiObject should be clicked if all conditions match.
316 | */
317 | @Override
318 | public void registerClickUiObjectWatcher(String name, Selector[] conditions, Selector target) {
319 | synchronized (watchers) {
320 | if (watchers.contains(name)) {
321 | UiDevice.getInstance().removeWatcher(name);
322 | watchers.remove(name);
323 | }
324 |
325 | UiSelector[] selectors = new UiSelector[conditions.length];
326 | for (int i = 0; i < conditions.length; i++) {
327 | selectors[i] = conditions[i].toUiSelector();
328 | }
329 | UiDevice.getInstance().registerWatcher(name, new ClickUiObjectWatcher(selectors, target.toUiSelector()));
330 | watchers.add(name);
331 | }
332 | }
333 |
334 | /**
335 | * Register a PressKeysWatcher
336 | *
337 | * @param name Watcher name
338 | * @param conditions If all UiObject in the conditions match, the watcher should be triggered.
339 | * @param keys All keys will be pressed in sequence.
340 | */
341 | @Override
342 | public void registerPressKeyskWatcher(String name, Selector[] conditions, String[] keys) {
343 | synchronized (watchers) {
344 | if (watchers.contains(name)) {
345 | UiDevice.getInstance().removeWatcher(name);
346 | watchers.remove(name);
347 | }
348 |
349 | UiSelector[] selectors = new UiSelector[conditions.length];
350 | for (int i = 0; i < conditions.length; i++) {
351 | selectors[i] = conditions[i].toUiSelector();
352 | }
353 | UiDevice.getInstance().registerWatcher(name, new PressKeysWatcher(selectors, keys));
354 | watchers.add(name);
355 | }
356 | }
357 |
358 | /**
359 | * Removes a previously registered UiWatcher.
360 | *
361 | * @param name Watcher name
362 | */
363 | @Override
364 | public void removeWatcher(String name) {
365 | synchronized (watchers) {
366 | if (watchers.contains(name)) {
367 | UiDevice.getInstance().removeWatcher(name);
368 | watchers.remove(name);
369 | }
370 | }
371 | }
372 |
373 | /**
374 | * Resets a UiWatcher that has been triggered. If a UiWatcher runs and its checkForCondition() call returned true, then the UiWatcher is considered triggered.
375 | */
376 | @Override
377 | public void resetWatcherTriggers() {
378 | UiDevice.getInstance().resetWatcherTriggers();
379 | }
380 |
381 | /**
382 | * Force to run all watchers.
383 | */
384 | @Override
385 | public void runWatchers() {
386 | UiDevice.getInstance().runWatchers();
387 | }
388 |
389 | /**
390 | * Get all registered UiWatchers
391 | *
392 | * @return UiWatcher names
393 | */
394 | @Override
395 | public String[] getWatchers() {
396 | synchronized (watchers) {
397 | return watchers.toArray(new String[watchers.size()]);
398 | }
399 | }
400 |
401 | /**
402 | * Simulates a short press using key name.
403 | *
404 | * @param key possible key name is home, back, left, right, up, down, center, menu, search, enter, delete(or del), recent(recent apps), volume_up, volume_down, volume_mute, camera, power
405 | * @return true if successful, else return false
406 | * @throws android.os.RemoteException
407 | */
408 | @Override
409 | public boolean pressKey(String key) throws RemoteException {
410 | boolean result;
411 | key = key.toLowerCase();
412 | if ("home".equals(key))
413 | result = UiDevice.getInstance().pressHome();
414 | else if ("back".equals(key))
415 | result = UiDevice.getInstance().pressBack();
416 | else if ("left".equals(key))
417 | result = UiDevice.getInstance().pressDPadLeft();
418 | else if ("right".equals(key))
419 | result = UiDevice.getInstance().pressDPadRight();
420 | else if ("up".equals(key))
421 | result = UiDevice.getInstance().pressDPadUp();
422 | else if ("down".equals(key))
423 | result = UiDevice.getInstance().pressDPadDown();
424 | else if ("center".equals(key))
425 | result = UiDevice.getInstance().pressDPadCenter();
426 | else if ("menu".equals(key))
427 | result = UiDevice.getInstance().pressMenu();
428 | else if ("search".equals(key))
429 | result = UiDevice.getInstance().pressSearch();
430 | else if ("enter".equals(key))
431 | result = UiDevice.getInstance().pressEnter();
432 | else if ("delete".equals(key) || "del".equals(key))
433 | result = UiDevice.getInstance().pressDelete();
434 | else if ("recent".equals(key))
435 | result = UiDevice.getInstance().pressRecentApps();
436 | else if ("volume_up".equals(key))
437 | result = UiDevice.getInstance().pressKeyCode(KeyEvent.KEYCODE_VOLUME_UP);
438 | else if ("volume_down".equals(key))
439 | result = UiDevice.getInstance().pressKeyCode(KeyEvent.KEYCODE_VOLUME_DOWN);
440 | else if ("volume_mute".equals(key))
441 | result = UiDevice.getInstance().pressKeyCode(KeyEvent.KEYCODE_VOLUME_MUTE);
442 | else if ("camera".equals(key))
443 | result = UiDevice.getInstance().pressKeyCode(KeyEvent.KEYCODE_CAMERA);
444 | else result = "power".equals(key) && UiDevice.getInstance().pressKeyCode(KeyEvent.KEYCODE_POWER);
445 |
446 | return result;
447 | }
448 |
449 | /**
450 | * Simulates a short press using a key code. See KeyEvent.
451 | *
452 | * @param keyCode the key code of the event.
453 | * @return true if successful, else return false
454 | */
455 | @Override
456 | public boolean pressKeyCode(int keyCode) {
457 | return UiDevice.getInstance().pressKeyCode(keyCode);
458 | }
459 |
460 | /**
461 | * Simulates a short press using a key code. See KeyEvent.
462 | *
463 | * @param keyCode the key code of the event.
464 | * @param metaState an integer in which each bit set to 1 represents a pressed meta key
465 | * @return true if successful, else return false
466 | */
467 | @Override
468 | public boolean pressKeyCode(int keyCode, int metaState) {
469 | return UiDevice.getInstance().pressKeyCode(keyCode, metaState);
470 | }
471 |
472 | /**
473 | * This method simulates pressing the power button if the screen is OFF else it does nothing if the screen is already ON. If the screen was OFF and it just got turned ON, this method will insert a 500ms delay to allow the device time to wake up and accept input.
474 | *
475 | * @throws android.os.RemoteException
476 | */
477 | @Override
478 | public void wakeUp() throws RemoteException {
479 | UiDevice.getInstance().wakeUp();
480 | }
481 |
482 | /**
483 | * This method simply presses the power button if the screen is ON else it does nothing if the screen is already OFF.
484 | *
485 | * @throws android.os.RemoteException
486 | */
487 | @Override
488 | public void sleep() throws RemoteException {
489 | UiDevice.getInstance().sleep();
490 | }
491 |
492 | /**
493 | * Checks the power manager if the screen is ON.
494 | *
495 | * @return true if the screen is ON else false
496 | * @throws android.os.RemoteException
497 | */
498 | @Override
499 | public boolean isScreenOn() throws RemoteException {
500 | return UiDevice.getInstance().isScreenOn();
501 | }
502 |
503 | /**
504 | * Waits for the current application to idle.
505 | *
506 | * @param timeout in milliseconds
507 | */
508 | @Override
509 | public void waitForIdle(long timeout) {
510 | UiDevice.getInstance().waitForIdle(timeout);
511 | }
512 |
513 | /**
514 | * Waits for a window content update event to occur. If a package name for the window is specified, but the current window does not have the same package name, the function returns immediately.
515 | *
516 | * @param packageName the specified window package name (can be null). If null, a window update from any front-end window will end the wait.
517 | * @param timeout the timeout for the wait
518 | * @return true if a window update occurred, false if timeout has elapsed or if the current window does not have the specified package name
519 | */
520 | @Override
521 | public boolean waitForWindowUpdate(String packageName, long timeout) {
522 | return UiDevice.getInstance().waitForWindowUpdate(packageName, timeout);
523 | }
524 |
525 | /**
526 | * Clears the existing text contents in an editable field. The UiSelector of this object must reference a UI element that is editable. When you call this method, the method first sets focus at the start edge of the field. The method then simulates a long-press to select the existing text, and deletes the selected text. If a "Select-All" option is displayed, the method will automatically attempt to use it to ensure full text selection. Note that it is possible that not all the text in the field is selected; for example, if the text contains separators such as spaces, slashes, at symbol etc. Also, not all editable fields support the long-press functionality.
527 | *
528 | * @param obj the selector of the UiObject.
529 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
530 | *
531 | */
532 | @Override
533 | public void clearTextField(Selector obj) throws UiObjectNotFoundException {
534 | new UiObject(obj.toUiSelector()).clearTextField();
535 | }
536 |
537 | /**
538 | * Reads the text property of the UI element
539 | *
540 | * @param obj the selector of the UiObject.
541 | * @return text value of the current node represented by this UiObject
542 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
543 | *
544 | */
545 | @Override
546 | public String getText(Selector obj) throws UiObjectNotFoundException {
547 | return new UiObject(obj.toUiSelector()).getText();
548 | }
549 |
550 | /**
551 | * Sets the text in an editable field, after clearing the field's content. The UiSelector selector of this object must reference a UI element that is editable. When you call this method, the method first simulates a click() on editable field to set focus. The method then clears the field's contents and injects your specified text into the field. If you want to capture the original contents of the field, call getText() first. You can then modify the text and use this method to update the field.
552 | *
553 | * @param obj the selector of the UiObject.
554 | * @param text string to set
555 | * @return true if operation is successful
556 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
557 | *
558 | */
559 | @Override
560 | public boolean setText(Selector obj, String text) throws UiObjectNotFoundException {
561 | return new UiObject(obj.toUiSelector()).setText(text);
562 | }
563 |
564 | /**
565 | * Performs a click at the center of the visible bounds of the UI element represented by this UiObject.
566 | *
567 | * @param obj the target ui object.
568 | * @return true id successful else false
569 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
570 | *
571 | */
572 | @Override
573 | public boolean click(Selector obj) throws UiObjectNotFoundException {
574 | return new UiObject(obj.toUiSelector()).click();
575 | }
576 |
577 | /**
578 | * Clicks the bottom and right corner or top and left corner of the UI element
579 | *
580 | * @param obj the target ui object.
581 | * @param corner "br"/"bottomright" means BottomRight, "tl"/"topleft" means TopLeft, "center" means Center.
582 | * @return true on success
583 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
584 | *
585 | */
586 | @Override
587 | public boolean click(Selector obj, String corner) throws UiObjectNotFoundException {
588 | return click(new UiObject(obj.toUiSelector()), corner);
589 | }
590 |
591 | private boolean click(UiObject obj, String corner) throws UiObjectNotFoundException {
592 | if (corner == null)
593 | corner = "center";
594 | corner = corner.toLowerCase();
595 | if ("br".equals(corner) || "bottomright".equals(corner))
596 | return obj.clickBottomRight();
597 | else if ("tl".equals(corner) || "topleft".equals(corner))
598 | return obj.clickTopLeft();
599 | else if ("c".equals(corner) || "center".equals(corner))
600 | return obj.click();
601 | return false;
602 | }
603 |
604 | /**
605 | * Performs a click at the center of the visible bounds of the UI element represented by this UiObject and waits for window transitions. This method differ from click() only in that this method waits for a a new window transition as a result of the click. Some examples of a window transition:
606 | * - launching a new activity
607 | * - bringing up a pop-up menu
608 | * - bringing up a dialog
609 | *
610 | * @param obj the target ui object.
611 | * @param timeout timeout before giving up on waiting for a new window
612 | * @return true if the event was triggered, else false
613 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
614 | *
615 | */
616 | @Override
617 | public boolean clickAndWaitForNewWindow(Selector obj, long timeout) throws UiObjectNotFoundException {
618 | return new UiObject(obj.toUiSelector()).clickAndWaitForNewWindow(timeout);
619 | }
620 |
621 | /**
622 | * Long clicks the center of the visible bounds of the UI element
623 | *
624 | * @param obj the target ui object.
625 | * @return true if operation was successful
626 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
627 | *
628 | */
629 | @Override
630 | public boolean longClick(Selector obj) throws UiObjectNotFoundException {
631 | return new UiObject(obj.toUiSelector()).longClick();
632 | }
633 |
634 | /**
635 | * Long clicks bottom and right corner of the UI element
636 | *
637 | * @param obj the target ui object.
638 | * @param corner "br"/"bottomright" means BottomRight, "tl"/"topleft" means TopLeft, "center" means Center.
639 | * @return true if operation was successful
640 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
641 | *
642 | */
643 | @Override
644 | public boolean longClick(Selector obj, String corner) throws UiObjectNotFoundException {
645 | return longClick(new UiObject(obj.toUiSelector()), corner);
646 | }
647 |
648 | private boolean longClick(UiObject obj, String corner) throws UiObjectNotFoundException {
649 | if (corner == null)
650 | corner = "center";
651 |
652 | corner = corner.toLowerCase();
653 | if ("br".equals(corner) || "bottomright".equals(corner))
654 | return obj.longClickBottomRight();
655 | else if ("tl".equals(corner) || "topleft".equals(corner))
656 | return obj.longClickTopLeft();
657 | else if ("c".equals(corner) || "center".equals(corner))
658 | return obj.longClick();
659 |
660 | return false;
661 | }
662 |
663 | /**
664 | * Drags this object to a destination UiObject. The number of steps specified in your input parameter can influence the drag speed, and varying speeds may impact the results. Consider evaluating different speeds when using this method in your tests.
665 | *
666 | * @param obj the ui object to be dragged.
667 | * @param destObj the ui object to be dragged to.
668 | * @param steps usually 40 steps. You can increase or decrease the steps to change the speed.
669 | * @return true if successful
670 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
671 | *
672 | * @throws com.github.uiautomatorstub.NotImplementedException
673 | *
674 | */
675 | @Override
676 | public boolean dragTo(Selector obj, Selector destObj, int steps) throws UiObjectNotFoundException, NotImplementedException {
677 | return dragTo(new UiObject(obj.toUiSelector()), destObj, steps);
678 | }
679 |
680 | private boolean dragTo(UiObject obj, Selector destObj, int steps) throws UiObjectNotFoundException, NotImplementedException {
681 | if (Build.VERSION.SDK_INT < 18)
682 | throw new NotImplementedException("dragTo");
683 | return obj.dragTo(new UiObject(destObj.toUiSelector()), steps);
684 | }
685 |
686 | /**
687 | * Drags this object to arbitrary coordinates. The number of steps specified in your input parameter can influence the drag speed, and varying speeds may impact the results. Consider evaluating different speeds when using this method in your tests.
688 | *
689 | * @param obj the ui object to be dragged.
690 | * @param destX the X-axis coordinate of destination.
691 | * @param destY the Y-axis coordinate of destination.
692 | * @param steps usually 40 steps. You can increase or decrease the steps to change the speed.
693 | * @return true if successful
694 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
695 | *
696 | * @throws com.github.uiautomatorstub.NotImplementedException
697 | *
698 | */
699 | @Override
700 | public boolean dragTo(Selector obj, int destX, int destY, int steps) throws UiObjectNotFoundException, NotImplementedException {
701 | return dragTo(new UiObject(obj.toUiSelector()), destX, destY, steps);
702 | }
703 |
704 | private boolean dragTo(UiObject obj, int destX, int destY, int steps) throws UiObjectNotFoundException, NotImplementedException {
705 | if (Build.VERSION.SDK_INT < 18)
706 | throw new NotImplementedException("dragTo");
707 | return obj.dragTo(destX, destY, steps);
708 | }
709 | /**
710 | * Check if view exists. This methods performs a waitForExists(long) with zero timeout. This basically returns immediately whether the view represented by this UiObject exists or not.
711 | *
712 | * @param obj the ui object.
713 | * @return true if the view represented by this UiObject does exist
714 | */
715 | @Override
716 | public boolean exist(Selector obj) {
717 | return new UiObject(obj.toUiSelector()).exists();
718 | }
719 |
720 | /**
721 | * Get the object info.
722 | *
723 | * @param obj the target ui object.
724 | * @return object info.
725 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
726 | *
727 | */
728 | @Override
729 | public ObjInfo objInfo(Selector obj) throws UiObjectNotFoundException {
730 | return ObjInfo.getObjInfo(obj.toUiSelector());
731 | }
732 |
733 | /**
734 | * Get the count of the UiObject instances by the selector
735 | *
736 | * @param obj the selector of the ui object
737 | * @return the count of instances.
738 | */
739 | @Override
740 | public int count(Selector obj) {
741 | if ((obj.getMask() & Selector.MASK_INSTANCE) > 0) {
742 | if (new UiObject(obj.toUiSelector()).exists())
743 | return 1;
744 | else
745 | return 0;
746 | } else {
747 | UiSelector sel = obj.toUiSelector();
748 | if (! new UiObject(sel).exists())
749 | return 0;
750 | int low = 1;
751 | int high = 2;
752 | sel = sel.instance(high - 1);
753 | while (new UiObject(sel).exists()) {
754 | low = high;
755 | high = high * 2;
756 | sel = sel.instance(high - 1);
757 | }
758 | while (high > low + 1) {
759 | int mid = (low + high)/2;
760 | sel = sel.instance(mid - 1);
761 | if (new UiObject(sel).exists())
762 | low = mid;
763 | else
764 | high = mid;
765 | }
766 | return low;
767 | }
768 | }
769 |
770 | /**
771 | * Get the info of all instance by the selector.
772 | *
773 | * @param obj the selector of ui object.
774 | * @return array of object info.
775 | *
776 | */
777 | @Override
778 | public ObjInfo[] objInfoOfAllInstances(Selector obj) {
779 | int total = count(obj);
780 | ObjInfo objs [] = new ObjInfo[total];
781 | if ((obj.getMask() & Selector.MASK_INSTANCE) > 0 && total > 0) {
782 | try {
783 | objs[0] = objInfo(obj);
784 | } catch (UiObjectNotFoundException e) {
785 | }
786 | } else {
787 | UiSelector sel = obj.toUiSelector();
788 | for (int i = 0; i < total; i++) {
789 | try {
790 | objs[i] = ObjInfo.getObjInfo(sel.instance(i));
791 | } catch (UiObjectNotFoundException e) {
792 | }
793 | }
794 | }
795 | return objs;
796 | }
797 |
798 | /**
799 | * Generates a two-pointer gesture with arbitrary starting and ending points.
800 | *
801 | * @param obj the target ui object. ??
802 | * @param startPoint1 start point of pointer 1
803 | * @param startPoint2 start point of pointer 2
804 | * @param endPoint1 end point of pointer 1
805 | * @param endPoint2 end point of pointer 2
806 | * @param steps the number of steps for the gesture. Steps are injected about 5 milliseconds apart, so 100 steps may take around 0.5 seconds to complete.
807 | * @return true if all touch events for this gesture are injected successfully, false otherwise
808 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
809 | *
810 | */
811 | @Override
812 | public boolean gesture(Selector obj, Point startPoint1, Point startPoint2, Point endPoint1, Point endPoint2, int steps) throws UiObjectNotFoundException, NotImplementedException {
813 | return gesture(new UiObject(obj.toUiSelector()),
814 | startPoint1, startPoint2,
815 | endPoint1, endPoint2, steps);
816 | }
817 |
818 | private boolean gesture(UiObject obj, Point startPoint1, Point startPoint2, Point endPoint1, Point endPoint2, int steps) throws UiObjectNotFoundException, NotImplementedException {
819 | if (Build.VERSION.SDK_INT < 18)
820 | throw new NotImplementedException("gesture(performTwoPointerGesture)");
821 | return obj.performTwoPointerGesture(
822 | startPoint1.toPoint(), startPoint2.toPoint(),
823 | endPoint1.toPoint(), endPoint2.toPoint(), steps);
824 | }
825 |
826 | /**
827 | * Performs a two-pointer gesture, where each pointer moves diagonally toward the other, from the edges to the center of this UiObject .
828 | *
829 | * @param obj the target ui object.
830 | * @param percent percentage of the object's diagonal length for the pinch gesture
831 | * @param steps the number of steps for the gesture. Steps are injected about 5 milliseconds apart, so 100 steps may take around 0.5 seconds to complete.
832 | * @return true if all touch events for this gesture are injected successfully, false otherwise
833 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
834 | *
835 | * @throws com.github.uiautomatorstub.NotImplementedException
836 | *
837 | */
838 | @Override
839 | public boolean pinchIn(Selector obj, int percent, int steps) throws UiObjectNotFoundException, NotImplementedException {
840 | return pinchIn(new UiObject(obj.toUiSelector()), percent, steps);
841 | }
842 |
843 | private boolean pinchIn(UiObject obj, int percent, int steps) throws UiObjectNotFoundException, NotImplementedException {
844 | if (Build.VERSION.SDK_INT < 18)
845 | throw new NotImplementedException("pinchIn");
846 | return obj.pinchIn(percent, steps);
847 | }
848 |
849 | /**
850 | * Performs a two-pointer gesture, where each pointer moves diagonally opposite across the other, from the center out towards the edges of the this UiObject.
851 | *
852 | * @param obj the target ui object.
853 | * @param percent percentage of the object's diagonal length for the pinch gesture
854 | * @param steps the number of steps for the gesture. Steps are injected about 5 milliseconds apart, so 100 steps may take around 0.5 seconds to complete.
855 | * @return true if all touch events for this gesture are injected successfully, false otherwise
856 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
857 | *
858 | * @throws com.github.uiautomatorstub.NotImplementedException
859 | *
860 | */
861 | @Override
862 | public boolean pinchOut(Selector obj, int percent, int steps) throws UiObjectNotFoundException, NotImplementedException {
863 | return pinchOut(new UiObject(obj.toUiSelector()), percent, steps);
864 | }
865 |
866 | private boolean pinchOut(UiObject obj, int percent, int steps) throws UiObjectNotFoundException, NotImplementedException {
867 | if (Build.VERSION.SDK_INT < 18)
868 | throw new NotImplementedException("pinchOut");
869 | return obj.pinchOut(percent, steps);
870 | }
871 |
872 | /**
873 | * Performs the swipe up/down/left/right action on the UiObject
874 | *
875 | * @param obj the target ui object.
876 | * @param dir "u"/"up", "d"/"down", "l"/"left", "r"/"right"
877 | * @param steps indicates the number of injected move steps into the system. Steps are injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
878 | * @return true of successful
879 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
880 | *
881 | */
882 | @Override
883 | public boolean swipe(Selector obj, String dir, int steps) throws UiObjectNotFoundException {
884 | return swipe(new UiObject(obj.toUiSelector()), dir, steps);
885 | }
886 |
887 | private boolean swipe(UiObject item, String dir, int steps) throws UiObjectNotFoundException {
888 | dir = dir.toLowerCase();
889 | boolean result = false;
890 | if ("u".equals(dir) || "up".equals(dir))
891 | result = item.swipeUp(steps);
892 | else if ("d".equals(dir) || "down".equals(dir))
893 | result = item.swipeDown(steps);
894 | else if ("l".equals(dir) || "left".equals(dir))
895 | result = item.swipeLeft(steps);
896 | else if ("r".equals(dir) || "right".equals(dir))
897 | result = item.swipeRight(steps);
898 | return result;
899 | }
900 |
901 | /**
902 | * Waits a specified length of time for a view to become visible. This method waits until the view becomes visible on the display, or until the timeout has elapsed. You can use this method in situations where the content that you want to select is not immediately displayed.
903 | *
904 | * @param obj the target ui object
905 | * @param timeout time to wait (in milliseconds)
906 | * @return true if the view is displayed, else false if timeout elapsed while waiting
907 | */
908 | @Override
909 | public boolean waitForExists(Selector obj, long timeout) {
910 | return new UiObject(obj.toUiSelector()).waitForExists(timeout);
911 | }
912 |
913 | /**
914 | * Waits a specified length of time for a view to become undetectable. This method waits until a view is no longer matchable, or until the timeout has elapsed. A view becomes undetectable when the UiSelector of the object is unable to find a match because the element has either changed its state or is no longer displayed. You can use this method when attempting to wait for some long operation to compete, such as downloading a large file or connecting to a remote server.
915 | *
916 | * @param obj the target ui object
917 | * @param timeout time to wait (in milliseconds)
918 | * @return true if the element is gone before timeout elapsed, else false if timeout elapsed but a matching element is still found.
919 | */
920 | @Override
921 | public boolean waitUntilGone(Selector obj, long timeout) {
922 | return new UiObject(obj.toUiSelector()).waitUntilGone(timeout);
923 | }
924 |
925 | /**
926 | * Performs a backwards fling action with the default number of fling steps (5). If the swipe direction is set to vertical, then the swipe will be performed from top to bottom. If the swipe direction is set to horizontal, then the swipes will be performed from left to right. Make sure to take into account devices configured with right-to-left languages like Arabic and Hebrew.
927 | * @param obj the selector of the scrollable object
928 | * @param isVertical vertical or horizontal
929 | * @return true if scrolled, and false if can't scroll anymore
930 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
931 | *
932 | */
933 | @Override
934 | public boolean flingBackward(Selector obj, boolean isVertical) throws UiObjectNotFoundException {
935 | UiScrollable scrollable = new UiScrollable(obj.toUiSelector());
936 | if (isVertical)
937 | setAsVerticalList(scrollable);
938 | else
939 | setAsHorizontalList(scrollable);
940 | return scrollable.flingBackward();
941 | }
942 |
943 | /**
944 | * Performs a forward fling with the default number of fling steps (5). If the swipe direction is set to vertical, then the swipes will be performed from bottom to top. If the swipe direction is set to horizontal, then the swipes will be performed from right to left. Make sure to take into account devices configured with right-to-left languages like Arabic and Hebrew.
945 | * @param obj the selector of the scrollable object
946 | * @param isVertical vertical or horizontal
947 | * @return true if scrolled, and false if can't scroll anymore
948 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
949 | *
950 | */
951 | @Override
952 | public boolean flingForward(Selector obj, boolean isVertical) throws UiObjectNotFoundException {
953 | UiScrollable scrollable = new UiScrollable(obj.toUiSelector());
954 | if (isVertical)
955 | setAsVerticalList(scrollable);
956 | else
957 | setAsHorizontalList(scrollable);
958 | return scrollable.flingForward();
959 | }
960 |
961 | /**
962 | * Performs a fling gesture to reach the beginning of a scrollable layout element. The beginning can be at the top-most edge in the case of vertical controls, or the left-most edge for horizontal controls. Make sure to take into account devices configured with right-to-left languages like Arabic and Hebrew.
963 | *
964 | * @param obj the selector of the scrollable object
965 | * @param isVertical vertical or horizontal
966 | * @param maxSwipes max swipes to achieve beginning.
967 | * @return true on scrolled, else false
968 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
969 | *
970 | */
971 | @Override
972 | public boolean flingToBeginning(Selector obj, boolean isVertical, int maxSwipes) throws UiObjectNotFoundException {
973 | UiScrollable scrollable = new UiScrollable(obj.toUiSelector());
974 | if (isVertical)
975 | setAsVerticalList(scrollable);
976 | else
977 | setAsHorizontalList(scrollable);
978 | return scrollable.flingToBeginning(maxSwipes);
979 | }
980 |
981 | /**
982 | * Performs a fling gesture to reach the end of a scrollable layout element. The end can be at the bottom-most edge in the case of vertical controls, or the right-most edge for horizontal controls. Make sure to take into account devices configured with right-to-left languages like Arabic and Hebrew.
983 | *
984 | * @param obj the selector of the scrollable object
985 | * @param isVertical vertical or horizontal
986 | * @param maxSwipes max swipes to achieve end.
987 | * @return true on scrolled, else false
988 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
989 | *
990 | */
991 | @Override
992 | public boolean flingToEnd(Selector obj, boolean isVertical, int maxSwipes) throws UiObjectNotFoundException {
993 | UiScrollable scrollable = new UiScrollable(obj.toUiSelector());
994 | if (isVertical)
995 | setAsVerticalList(scrollable);
996 | else
997 | setAsHorizontalList(scrollable);
998 | return scrollable.flingToEnd(maxSwipes);
999 | }
1000 |
1001 | /**
1002 | * Performs a backward scroll. If the swipe direction is set to vertical, then the swipes will be performed from top to bottom. If the swipe direction is set to horizontal, then the swipes will be performed from left to right. Make sure to take into account devices configured with right-to-left languages like Arabic and Hebrew.
1003 | *
1004 | * @param obj the selector of the scrollable object
1005 | * @param isVertical vertical or horizontal
1006 | * @param steps number of steps. Use this to control the speed of the scroll action.
1007 | * @return true if scrolled, false if can't scroll anymore
1008 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
1009 | *
1010 | */
1011 | @Override
1012 | public boolean scrollBackward(Selector obj, boolean isVertical, int steps) throws UiObjectNotFoundException {
1013 | UiScrollable scrollable = new UiScrollable(obj.toUiSelector());
1014 | if (isVertical)
1015 | setAsVerticalList(scrollable);
1016 | else
1017 | setAsHorizontalList(scrollable);
1018 | return scrollable.scrollBackward(steps);
1019 | }
1020 |
1021 | /**
1022 | * Performs a forward scroll with the default number of scroll steps (55). If the swipe direction is set to vertical, then the swipes will be performed from bottom to top. If the swipe direction is set to horizontal, then the swipes will be performed from right to left. Make sure to take into account devices configured with right-to-left languages like Arabic and Hebrew.
1023 | *
1024 | * @param obj the selector of the scrollable object
1025 | * @param isVertical vertical or horizontal
1026 | * @param steps number of steps. Use this to control the speed of the scroll action.
1027 | * @return true on scrolled, else false
1028 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
1029 | *
1030 | */
1031 | @Override
1032 | public boolean scrollForward(Selector obj, boolean isVertical, int steps) throws UiObjectNotFoundException {
1033 | UiScrollable scrollable = new UiScrollable(obj.toUiSelector());
1034 | if (isVertical)
1035 | setAsVerticalList(scrollable);
1036 | else
1037 | setAsHorizontalList(scrollable);
1038 | return scrollable.scrollForward(steps);
1039 | }
1040 |
1041 | /**
1042 | * Scrolls to the beginning of a scrollable layout element. The beginning can be at the top-most edge in the case of vertical controls, or the left-most edge for horizontal controls. Make sure to take into account devices configured with right-to-left languages like Arabic and Hebrew.
1043 | *
1044 | * @param obj the selector of the scrollable object
1045 | * @param isVertical vertical or horizontal
1046 | * @param maxSwipes max swipes to be performed.
1047 | * @param steps use steps to control the speed, so that it may be a scroll, or fling
1048 | * @return true on scrolled else false
1049 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
1050 | *
1051 | */
1052 | @Override
1053 | public boolean scrollToBeginning(Selector obj, boolean isVertical, int maxSwipes, int steps) throws UiObjectNotFoundException {
1054 | UiScrollable scrollable = new UiScrollable(obj.toUiSelector());
1055 | if (isVertical)
1056 | setAsVerticalList(scrollable);
1057 | else
1058 | setAsHorizontalList(scrollable);
1059 | return scrollable.scrollToBeginning(maxSwipes, steps);
1060 | }
1061 |
1062 | /**
1063 | * Scrolls to the end of a scrollable layout element. The end can be at the bottom-most edge in the case of vertical controls, or the right-most edge for horizontal controls. Make sure to take into account devices configured with right-to-left languages like Arabic and Hebrew.
1064 | *
1065 | * @param obj the selector of the scrollable object
1066 | * @param isVertical vertical or horizontal
1067 | * @param maxSwipes max swipes to be performed.
1068 | * @param steps use steps to control the speed, so that it may be a scroll, or fling
1069 | * @return true on scrolled, else false
1070 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
1071 | *
1072 | */
1073 | @Override
1074 | public boolean scrollToEnd(Selector obj, boolean isVertical, int maxSwipes, int steps) throws UiObjectNotFoundException {
1075 | UiScrollable scrollable = new UiScrollable(obj.toUiSelector());
1076 | if (isVertical)
1077 | setAsVerticalList(scrollable);
1078 | else
1079 | setAsHorizontalList(scrollable);
1080 | return scrollable.scrollToEnd(maxSwipes, steps);
1081 | }
1082 |
1083 | /**
1084 | * Perform a scroll forward action to move through the scrollable layout element until a visible item that matches the selector is found.
1085 | *
1086 | * @param obj the selector of the scrollable object
1087 | * @param targetObj the item matches the selector to be found.
1088 | * @param isVertical vertical or horizontal
1089 | * @return true on scrolled, else false
1090 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
1091 | *
1092 | */
1093 | @Override
1094 | public boolean scrollTo(Selector obj, Selector targetObj, boolean isVertical) throws UiObjectNotFoundException {
1095 | UiScrollable scrollable = new UiScrollable(obj.toUiSelector());
1096 | if (isVertical)
1097 | setAsVerticalList(scrollable);
1098 | else
1099 | setAsHorizontalList(scrollable);
1100 | return scrollable.scrollIntoView(targetObj.toUiSelector());
1101 | }
1102 |
1103 |
1104 | /**
1105 | * Name an UiObject and cache it.
1106 | * @param obj UiObject
1107 | * @return the name of the UiObject
1108 | */
1109 | private String addUiObject(UiObject obj) {
1110 | String key = UUID.randomUUID().toString();
1111 | uiObjects.put(key, obj);
1112 | // schedule the clear timer.
1113 | Timer clearTimer = new Timer();
1114 | clearTimer.schedule(new ClearUiObjectTimerTask(key), 60000);
1115 | return key;
1116 | }
1117 |
1118 | class ClearUiObjectTimerTask extends TimerTask {
1119 | String name;
1120 |
1121 | public ClearUiObjectTimerTask(String name) {
1122 | this.name = name;
1123 | }
1124 |
1125 | public void run() {
1126 | uiObjects.remove(name);
1127 | }
1128 | }
1129 | /**
1130 | * Searches for child UI element within the constraints of this UiSelector selector. It looks for any child matching the childPattern argument that has a child UI element anywhere within its sub hierarchy that has a text attribute equal to text. The returned UiObject will point at the childPattern instance that matched the search and not at the identifying child element that matched the text attribute.
1131 | *
1132 | * @param collection Selector of UiCollection or UiScrollable.
1133 | * @param text String of the identifying child contents of of the childPattern
1134 | * @param child UiSelector selector of the child pattern to match and return
1135 | * @return A string ID represent the returned UiObject.
1136 | */
1137 | @Override
1138 | public String childByText(Selector collection, Selector child, String text) throws UiObjectNotFoundException{
1139 | UiObject obj;
1140 | if (exist(collection) && objInfo(collection).isScrollable()) {
1141 | obj = new UiScrollable(collection.toUiSelector()).getChildByText(child.toUiSelector(), text);
1142 | } else {
1143 | obj = new UiCollection(collection.toUiSelector()).getChildByText(child.toUiSelector(), text);
1144 | }
1145 | return addUiObject(obj);
1146 | }
1147 |
1148 | @Override
1149 | public String childByText(Selector collection, Selector child, String text, boolean allowScrollSearch) throws UiObjectNotFoundException {
1150 | UiObject obj = new UiScrollable(collection.toUiSelector()).getChildByText(child.toUiSelector(), text, allowScrollSearch);
1151 | return addUiObject(obj);
1152 | }
1153 |
1154 | /**
1155 | * Searches for child UI element within the constraints of this UiSelector selector. It looks for any child matching the childPattern argument that has a child UI element anywhere within its sub hierarchy that has content-description text. The returned UiObject will point at the childPattern instance that matched the search and not at the identifying child element that matched the content description.
1156 | *
1157 | * @param collection Selector of UiCollection or UiScrollable
1158 | * @param child UiSelector selector of the child pattern to match and return
1159 | * @param text String of the identifying child contents of of the childPattern
1160 | * @return A string ID represent the returned UiObject.
1161 | */
1162 | @Override
1163 | public String childByDescription(Selector collection, Selector child, String text) throws UiObjectNotFoundException {
1164 | UiObject obj;
1165 | if (exist(collection) && objInfo(collection).isScrollable()) {
1166 | obj = new UiScrollable(collection.toUiSelector()).getChildByDescription(child.toUiSelector(), text);
1167 | } else {
1168 | obj = new UiCollection(collection.toUiSelector()).getChildByDescription(child.toUiSelector(), text);
1169 | }
1170 | return addUiObject(obj);
1171 | }
1172 |
1173 | @Override
1174 | public String childByDescription(Selector collection, Selector child, String text, boolean allowScrollSearch) throws UiObjectNotFoundException {
1175 | UiObject obj = new UiScrollable(collection.toUiSelector()).getChildByDescription(child.toUiSelector(), text, allowScrollSearch);
1176 | return addUiObject(obj);
1177 | }
1178 |
1179 | /**
1180 | * Searches for child UI element within the constraints of this UiSelector. It looks for any child matching the childPattern argument that has a child UI element anywhere within its sub hierarchy that is at the instance specified. The operation is performed only on the visible items and no scrolling is performed in this case.
1181 | *
1182 | * @param collection Selector of UiCollection or UiScrollable
1183 | * @param child UiSelector selector of the child pattern to match and return
1184 | * @param instance int the desired matched instance of this childPattern
1185 | * @return A string ID represent the returned UiObject.
1186 | */
1187 | @Override
1188 | public String childByInstance(Selector collection, Selector child, int instance) throws UiObjectNotFoundException {
1189 | UiObject obj;
1190 | if (exist(collection) && objInfo(collection).isScrollable()) {
1191 | obj = new UiScrollable(collection.toUiSelector()).getChildByInstance(child.toUiSelector(), instance);
1192 | } else {
1193 | obj = new UiCollection(collection.toUiSelector()).getChildByInstance(child.toUiSelector(), instance);
1194 | }
1195 | return addUiObject(obj);
1196 | }
1197 |
1198 | /**
1199 | * Creates a new UiObject for a child view that is under the present UiObject.
1200 | *
1201 | * @param obj The ID string represent the parent UiObject.
1202 | * @param selector UiSelector selector of the child pattern to match and return
1203 | * @return A string ID represent the returned UiObject.
1204 | */
1205 | @Override
1206 | public String getChild(String obj, Selector selector) throws UiObjectNotFoundException {
1207 | UiObject ui = uiObjects.get(obj);
1208 | if (ui != null) {
1209 | return addUiObject(ui.getChild(selector.toUiSelector()));
1210 | }
1211 | return null;
1212 | }
1213 |
1214 | /**
1215 | * Creates a new UiObject for a sibling view or a child of the sibling view, relative to the present UiObject.
1216 | *
1217 | * @param obj The ID string represent the source UiObject.
1218 | * @param selector for a sibling view or children of the sibling view
1219 | * @return A string ID represent the returned UiObject.
1220 | */
1221 | @Override
1222 | public String getFromParent(String obj, Selector selector) throws UiObjectNotFoundException {
1223 | UiObject ui = uiObjects.get(obj);
1224 | if (ui != null) {
1225 | return addUiObject(ui.getFromParent(selector.toUiSelector()));
1226 | }
1227 | return null;
1228 | }
1229 |
1230 | /**
1231 | * Get a new UiObject from the selector.
1232 | *
1233 | * @param selector Selector of the UiObject
1234 | * @return A string ID represent the returned UiObject.
1235 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
1236 | *
1237 | */
1238 | @Override
1239 | public String getUiObject(Selector selector) throws UiObjectNotFoundException {
1240 | return addUiObject(new UiObject(selector.toUiSelector()));
1241 | }
1242 |
1243 | /**
1244 | * Remove the UiObject from memory.
1245 | */
1246 | @Override
1247 | public void removeUiObject(String obj) {
1248 | uiObjects.remove(obj);
1249 | }
1250 |
1251 | /**
1252 | * Get all named UiObjects.
1253 | *
1254 | * @return all names
1255 | */
1256 | @Override
1257 | public String[] getUiObjects() {
1258 | Set strings = uiObjects.keySet();
1259 | return strings.toArray(new String[strings.size()]);
1260 | }
1261 |
1262 | private UiObject getUiObject(String name) throws UiObjectNotFoundException{
1263 | if (uiObjects.containsKey(name)) {
1264 | return uiObjects.get(name);
1265 | } else {
1266 | throw new UiObjectNotFoundException("UiObject " + name + " not found!");
1267 | }
1268 | }
1269 |
1270 | /**
1271 | * Clears the existing text contents in an editable field. The UiSelector of this object must reference a UI element that is editable. When you call this method, the method first sets focus at the start edge of the field. The method then simulates a long-press to select the existing text, and deletes the selected text. If a "Select-All" option is displayed, the method will automatically attempt to use it to ensure full text selection. Note that it is possible that not all the text in the field is selected; for example, if the text contains separators such as spaces, slashes, at symbol etc. Also, not all editable fields support the long-press functionality.
1272 | *
1273 | * @param obj the id of the UiObject.
1274 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
1275 | *
1276 | */
1277 | @Override
1278 | public void clearTextField(String obj) throws UiObjectNotFoundException {
1279 | getUiObject(obj).clearTextField();
1280 | }
1281 |
1282 | /**
1283 | * Reads the text property of the UI element
1284 | *
1285 | * @param obj the id of the UiObject.
1286 | * @return text value of the current node represented by this UiObject
1287 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
1288 | *
1289 | */
1290 | @Override
1291 | public String getText(String obj) throws UiObjectNotFoundException {
1292 | return getUiObject(obj).getText();
1293 | }
1294 |
1295 | /**
1296 | * Sets the text in an editable field, after clearing the field's content. The UiSelector selector of this object must reference a UI element that is editable. When you call this method, the method first simulates a click() on editable field to set focus. The method then clears the field's contents and injects your specified text into the field. If you want to capture the original contents of the field, call getText() first. You can then modify the text and use this method to update the field.
1297 | *
1298 | * @param obj the id of the UiObject.
1299 | * @param text string to set
1300 | * @return true if operation is successful
1301 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
1302 | *
1303 | */
1304 | @Override
1305 | public boolean setText(String obj, String text) throws UiObjectNotFoundException {
1306 | return getUiObject(obj).setText(text);
1307 | }
1308 |
1309 | /**
1310 | * Performs a click at the center of the visible bounds of the UI element represented by this UiObject.
1311 | *
1312 | * @param obj the id of target ui object.
1313 | * @return true id successful else false
1314 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
1315 | *
1316 | */
1317 | @Override
1318 | public boolean click(String obj) throws UiObjectNotFoundException {
1319 | return getUiObject(obj).click();
1320 | }
1321 |
1322 | /**
1323 | * Clicks the bottom and right corner or top and left corner of the UI element
1324 | *
1325 | * @param obj the id of target ui object.
1326 | * @param corner "br"/"bottomright" means BottomRight, "tl"/"topleft" means TopLeft, "center" means Center.
1327 | * @return true on success
1328 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
1329 | *
1330 | */
1331 | @Override
1332 | public boolean click(String obj, String corner) throws UiObjectNotFoundException {
1333 | return click(getUiObject(obj), corner);
1334 | }
1335 |
1336 | /**
1337 | * Performs a click at the center of the visible bounds of the UI element represented by this UiObject and waits for window transitions. This method differ from click() only in that this method waits for a a new window transition as a result of the click. Some examples of a window transition:
1338 | * - launching a new activity
1339 | * - bringing up a pop-up menu
1340 | * - bringing up a dialog
1341 | *
1342 | * @param obj the id of target ui object.
1343 | * @param timeout timeout before giving up on waiting for a new window
1344 | * @return true if the event was triggered, else false
1345 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
1346 | *
1347 | */
1348 | @Override
1349 | public boolean clickAndWaitForNewWindow(String obj, long timeout) throws UiObjectNotFoundException {
1350 | return getUiObject(obj).clickAndWaitForNewWindow(timeout);
1351 | }
1352 |
1353 | /**
1354 | * Long clicks the center of the visible bounds of the UI element
1355 | *
1356 | * @param obj the id of target ui object.
1357 | * @return true if operation was successful
1358 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
1359 | *
1360 | */
1361 | @Override
1362 | public boolean longClick(String obj) throws UiObjectNotFoundException {
1363 | return getUiObject(obj).longClick();
1364 | }
1365 |
1366 | /**
1367 | * Long clicks bottom and right corner of the UI element
1368 | *
1369 | * @param obj the id of target ui object.
1370 | * @param corner "br"/"bottomright" means BottomRight, "tl"/"topleft" means TopLeft, "center" means Center.
1371 | * @return true if operation was successful
1372 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
1373 | *
1374 | */
1375 | @Override
1376 | public boolean longClick(String obj, String corner) throws UiObjectNotFoundException {
1377 | return longClick(getUiObject(obj), corner);
1378 | }
1379 |
1380 | /**
1381 | * Drags this object to a destination UiObject. The number of steps specified in your input parameter can influence the drag speed, and varying speeds may impact the results. Consider evaluating different speeds when using this method in your tests.
1382 | *
1383 | * @param obj the id of ui object to be dragged.
1384 | * @param destObj the ui object to be dragged to.
1385 | * @param steps usually 40 steps. You can increase or decrease the steps to change the speed.
1386 | * @return true if successful
1387 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
1388 | *
1389 | * @throws com.github.uiautomatorstub.NotImplementedException
1390 | *
1391 | */
1392 | @Override
1393 | public boolean dragTo(String obj, Selector destObj, int steps) throws UiObjectNotFoundException, NotImplementedException {
1394 | return dragTo(getUiObject(obj), destObj, steps);
1395 | }
1396 |
1397 | /**
1398 | * Drags this object to arbitrary coordinates. The number of steps specified in your input parameter can influence the drag speed, and varying speeds may impact the results. Consider evaluating different speeds when using this method in your tests.
1399 | *
1400 | * @param obj the id of ui object to be dragged.
1401 | * @param destX the X-axis coordinate of destination.
1402 | * @param destY the Y-axis coordinate of destination.
1403 | * @param steps usually 40 steps. You can increase or decrease the steps to change the speed.
1404 | * @return true if successful
1405 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
1406 | *
1407 | * @throws com.github.uiautomatorstub.NotImplementedException
1408 | *
1409 | */
1410 | @Override
1411 | public boolean dragTo(String obj, int destX, int destY, int steps) throws UiObjectNotFoundException, NotImplementedException {
1412 | return dragTo(getUiObject(obj), destX, destY, steps);
1413 | }
1414 |
1415 | /**
1416 | * Check if view exists. This methods performs a waitForExists(long) with zero timeout. This basically returns immediately whether the view represented by this UiObject exists or not.
1417 | *
1418 | * @param obj the id of ui object.
1419 | * @return true if the view represented by this UiObject does exist
1420 | */
1421 | @Override
1422 | public boolean exist(String obj) {
1423 | try {
1424 | return getUiObject(obj).exists();
1425 | } catch (UiObjectNotFoundException e) {
1426 | return false;
1427 | }
1428 | }
1429 |
1430 | /**
1431 | * Get the object info.
1432 | *
1433 | * @param obj the id of target ui object.
1434 | * @return object info.
1435 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
1436 | *
1437 | */
1438 | @Override
1439 | public ObjInfo objInfo(String obj) throws UiObjectNotFoundException {
1440 | return ObjInfo.getObjInfo(getUiObject(obj));
1441 | }
1442 |
1443 | /**
1444 | * Generates a two-pointer gesture with arbitrary starting and ending points.
1445 | *
1446 | * @param obj the id of target ui object. ??
1447 | * @param startPoint1 start point of pointer 1
1448 | * @param startPoint2 start point of pointer 2
1449 | * @param endPoint1 end point of pointer 1
1450 | * @param endPoint2 end point of pointer 2
1451 | * @param steps the number of steps for the gesture. Steps are injected about 5 milliseconds apart, so 100 steps may take around 0.5 seconds to complete.
1452 | * @return true if all touch events for this gesture are injected successfully, false otherwise
1453 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
1454 | *
1455 | */
1456 | @Override
1457 | public boolean gesture(String obj, Point startPoint1, Point startPoint2, Point endPoint1, Point endPoint2, int steps) throws UiObjectNotFoundException, NotImplementedException {
1458 | return gesture(getUiObject(obj), startPoint1, startPoint2, endPoint1, endPoint2, steps);
1459 | }
1460 |
1461 | /**
1462 | * Performs a two-pointer gesture, where each pointer moves diagonally toward the other, from the edges to the center of this UiObject .
1463 | *
1464 | * @param obj the id of target ui object.
1465 | * @param percent percentage of the object's diagonal length for the pinch gesture
1466 | * @param steps the number of steps for the gesture. Steps are injected about 5 milliseconds apart, so 100 steps may take around 0.5 seconds to complete.
1467 | * @return true if all touch events for this gesture are injected successfully, false otherwise
1468 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
1469 | *
1470 | * @throws com.github.uiautomatorstub.NotImplementedException
1471 | *
1472 | */
1473 | @Override
1474 | public boolean pinchIn(String obj, int percent, int steps) throws UiObjectNotFoundException, NotImplementedException {
1475 | return pinchIn(getUiObject(obj), percent, steps);
1476 | }
1477 |
1478 | /**
1479 | * Performs a two-pointer gesture, where each pointer moves diagonally opposite across the other, from the center out towards the edges of the this UiObject.
1480 | *
1481 | * @param obj the id of target ui object.
1482 | * @param percent percentage of the object's diagonal length for the pinch gesture
1483 | * @param steps the number of steps for the gesture. Steps are injected about 5 milliseconds apart, so 100 steps may take around 0.5 seconds to complete.
1484 | * @return true if all touch events for this gesture are injected successfully, false otherwise
1485 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
1486 | *
1487 | * @throws com.github.uiautomatorstub.NotImplementedException
1488 | *
1489 | */
1490 | @Override
1491 | public boolean pinchOut(String obj, int percent, int steps) throws UiObjectNotFoundException, NotImplementedException {
1492 | return pinchOut(getUiObject(obj), percent, steps);
1493 | }
1494 |
1495 | /**
1496 | * Performs the swipe up/down/left/right action on the UiObject
1497 | *
1498 | * @param obj the id of target ui object.
1499 | * @param dir "u"/"up", "d"/"down", "l"/"left", "r"/"right"
1500 | * @param steps indicates the number of injected move steps into the system. Steps are injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
1501 | * @return true of successful
1502 | * @throws com.android.uiautomator.core.UiObjectNotFoundException
1503 | *
1504 | */
1505 | @Override
1506 | public boolean swipe(String obj, String dir, int steps) throws UiObjectNotFoundException {
1507 | return swipe(getUiObject(obj), dir, steps);
1508 | }
1509 |
1510 | /**
1511 | * Waits a specified length of time for a view to become visible. This method waits until the view becomes visible on the display, or until the timeout has elapsed. You can use this method in situations where the content that you want to select is not immediately displayed.
1512 | *
1513 | * @param obj the id of target ui object
1514 | * @param timeout time to wait (in milliseconds)
1515 | * @return true if the view is displayed, else false if timeout elapsed while waiting
1516 | */
1517 | @Override
1518 | public boolean waitForExists(String obj, long timeout) throws UiObjectNotFoundException {
1519 | return getUiObject(obj).waitForExists(timeout);
1520 | }
1521 |
1522 | /**
1523 | * Waits a specified length of time for a view to become undetectable. This method waits until a view is no longer matchable, or until the timeout has elapsed. A view becomes undetectable when the UiSelector of the object is unable to find a match because the element has either changed its state or is no longer displayed. You can use this method when attempting to wait for some long operation to compete, such as downloading a large file or connecting to a remote server.
1524 | *
1525 | * @param obj the id of target ui object
1526 | * @param timeout time to wait (in milliseconds)
1527 | * @return true if the element is gone before timeout elapsed, else false if timeout elapsed but a matching element is still found.
1528 | */
1529 | @Override
1530 | public boolean waitUntilGone(String obj, long timeout) throws UiObjectNotFoundException {
1531 | return getUiObject(obj).waitUntilGone(timeout);
1532 | }
1533 |
1534 | /**
1535 | * Get Configurator
1536 | *
1537 | * @return Configurator information.
1538 | * @throws com.github.uiautomatorstub.NotImplementedException
1539 | */
1540 | @Override
1541 | public ConfiguratorInfo getConfigurator() throws NotImplementedException {
1542 | if (Build.VERSION.SDK_INT < 18)
1543 | throw new NotImplementedException();
1544 |
1545 | return new ConfiguratorInfo();
1546 | }
1547 |
1548 | /**
1549 | * Set Configurator.
1550 | *
1551 | * @param info the configurator information to be set.
1552 | * @throws com.github.uiautomatorstub.NotImplementedException
1553 | */
1554 | @Override
1555 | public ConfiguratorInfo setConfigurator(ConfiguratorInfo info) throws NotImplementedException {
1556 | if (Build.VERSION.SDK_INT < 18)
1557 | throw new NotImplementedException();
1558 |
1559 | ConfiguratorInfo.setConfigurator(info);
1560 | return new ConfiguratorInfo();
1561 | }
1562 | }
1563 |
--------------------------------------------------------------------------------
/src/com/github/uiautomatorstub/ConfiguratorInfo.java:
--------------------------------------------------------------------------------
1 | package com.github.uiautomatorstub;
2 |
3 | import java.lang.reflect.InvocationTargetException;
4 |
5 | /**
6 | * Created by b036 on 12/26/13.
7 | */
8 | public class ConfiguratorInfo {
9 |
10 | public ConfiguratorInfo() {
11 | try {
12 | Class clz = Class.forName("com.android.uiautomator.core.Configurator");
13 | Object conf = clz.getMethod("getInstance").invoke(null);
14 | this._actionAcknowledgmentTimeout = (Long)clz.getMethod("getActionAcknowledgmentTimeout").invoke(conf);
15 | this._keyInjectionDelay = (Long)clz.getMethod("getKeyInjectionDelay").invoke(conf);
16 | this._scrollAcknowledgmentTimeout = (Long)clz.getMethod("getScrollAcknowledgmentTimeout").invoke(conf);
17 | this._waitForIdleTimeout = (Long)clz.getMethod("getWaitForIdleTimeout").invoke(conf);
18 | this._waitForSelectorTimeout = (Long)clz.getMethod("getWaitForSelectorTimeout").invoke(conf);
19 | } catch (IllegalAccessException e) {
20 | e.printStackTrace();
21 | } catch (InvocationTargetException e) {
22 | e.printStackTrace();
23 | } catch (NoSuchMethodException e) {
24 | e.printStackTrace();
25 | } catch (ClassNotFoundException e) {
26 | e.printStackTrace();
27 | }
28 | }
29 |
30 | public long getActionAcknowledgmentTimeout() {
31 | return _actionAcknowledgmentTimeout;
32 | }
33 |
34 | public void setActionAcknowledgmentTimeout(long _actionAcknowledgmentTimeout) {
35 | this._actionAcknowledgmentTimeout = _actionAcknowledgmentTimeout;
36 | }
37 |
38 | public long getKeyInjectionDelay() {
39 | return _keyInjectionDelay;
40 | }
41 |
42 | public void setKeyInjectionDelay(long _keyInjectionDelay) {
43 | this._keyInjectionDelay = _keyInjectionDelay;
44 | }
45 |
46 | public long getScrollAcknowledgmentTimeout() {
47 | return _scrollAcknowledgmentTimeout;
48 | }
49 |
50 | public void setScrollAcknowledgmentTimeout(long _scrollAcknowledgmentTimeout) {
51 | this._scrollAcknowledgmentTimeout = _scrollAcknowledgmentTimeout;
52 | }
53 |
54 | public long getWaitForIdleTimeout() {
55 | return _waitForIdleTimeout;
56 | }
57 |
58 | public void setWaitForIdleTimeout(long _waitForIdleTimeout) {
59 | this._waitForIdleTimeout = _waitForIdleTimeout;
60 | }
61 |
62 | public long getWaitForSelectorTimeout() {
63 | return _waitForSelectorTimeout;
64 | }
65 |
66 | public void setWaitForSelectorTimeout(long _waitForSelectorTimeout) {
67 | this._waitForSelectorTimeout = _waitForSelectorTimeout;
68 | }
69 |
70 | public static void setConfigurator(ConfiguratorInfo info) {
71 | try {
72 | Class clz = Class.forName("com.android.uiautomator.core.Configurator");
73 | Object conf = clz.getMethod("getInstance").invoke(null);
74 | clz.getMethod("setActionAcknowledgmentTimeout", Long.TYPE).invoke(conf, info.getActionAcknowledgmentTimeout());
75 | clz.getMethod("setKeyInjectionDelay", Long.TYPE).invoke(conf, info.getKeyInjectionDelay());
76 | clz.getMethod("setScrollAcknowledgmentTimeout", Long.TYPE).invoke(conf, info.getScrollAcknowledgmentTimeout());
77 | clz.getMethod("setWaitForIdleTimeout", Long.TYPE).invoke(conf, info.getWaitForIdleTimeout());
78 | clz.getMethod("setWaitForSelectorTimeout", Long.TYPE).invoke(conf, info.getWaitForSelectorTimeout());
79 | } catch (IllegalAccessException e) {
80 | Log.d(e.getMessage());
81 | } catch (InvocationTargetException e) {
82 | Log.d(e.getMessage());
83 | } catch (NoSuchMethodException e) {
84 | Log.d(e.getMessage());
85 | } catch (ClassNotFoundException e) {
86 | Log.d(e.getMessage());
87 | }
88 | }
89 |
90 | private long _actionAcknowledgmentTimeout;
91 | private long _keyInjectionDelay;
92 | private long _scrollAcknowledgmentTimeout;
93 | private long _waitForIdleTimeout;
94 | private long _waitForSelectorTimeout;
95 | }
96 |
--------------------------------------------------------------------------------
/src/com/github/uiautomatorstub/DeviceInfo.java:
--------------------------------------------------------------------------------
1 | package com.github.uiautomatorstub;
2 |
3 | import android.os.RemoteException;
4 |
5 | import com.android.uiautomator.core.UiDevice;
6 |
7 | public class DeviceInfo {
8 | private String _currentPackageName;
9 | private int _displayWidth;
10 | private int _displayHeight;
11 | private int _displayRotation;
12 | private int _displaySizeDpX;
13 | private int _displaySizeDpY;
14 | private String _productName;
15 | private boolean _naturalOrientation;
16 |
17 | private int _sdkInt;
18 |
19 | public final static DeviceInfo getDeviceInfo() {
20 | return new DeviceInfo();
21 | }
22 |
23 | private DeviceInfo() {
24 | this._sdkInt = android.os.Build.VERSION.SDK_INT;
25 |
26 | UiDevice ud = UiDevice.getInstance();
27 | this._currentPackageName = ud.getCurrentPackageName();
28 | this._displayWidth = ud.getDisplayWidth();
29 | this._displayHeight = ud.getDisplayHeight();
30 | if (_sdkInt >= 17) {
31 | this._displayRotation = ud.getDisplayRotation();
32 | this._productName = ud.getProductName();
33 | this._naturalOrientation = ud.isNaturalOrientation();
34 | }
35 | if (_sdkInt >= 18) {
36 | this._displaySizeDpX = ud.getDisplaySizeDp().x;
37 | this._displaySizeDpY = ud.getDisplaySizeDp().y;
38 | }
39 | }
40 |
41 | public String getCurrentPackageName() {
42 | return _currentPackageName;
43 | }
44 |
45 | public void setCurrentPackageName(String currentPackageName) {
46 | this._currentPackageName = currentPackageName;
47 | }
48 |
49 | public int getDisplayWidth() {
50 | return _displayWidth;
51 | }
52 |
53 | public void setDisplayWidth(int displayWidth) {
54 | this._displayWidth = displayWidth;
55 | }
56 |
57 | public int getDisplayHeight() {
58 | return _displayHeight;
59 | }
60 |
61 | public void setDisplayHeight(int displayHeight) {
62 | this._displayHeight = displayHeight;
63 | }
64 |
65 | public int getDisplayRotation() {
66 | return _displayRotation;
67 | }
68 |
69 | public void setDisplayRotation(int displayRotation) {
70 | this._displayRotation = displayRotation;
71 | }
72 |
73 | public int getDisplaySizeDpX() {
74 | return _displaySizeDpX;
75 | }
76 |
77 | public void setDisplaySizeDpX(int displaySizeDpX) {
78 | this._displaySizeDpX = displaySizeDpX;
79 | }
80 |
81 | public int getDisplaySizeDpY() {
82 | return _displaySizeDpY;
83 | }
84 |
85 | public void setDisplaySizeDpY(int displaySizeDpY) {
86 | this._displaySizeDpY = displaySizeDpY;
87 | }
88 |
89 | public String getProductName() {
90 | return _productName;
91 | }
92 |
93 | public void setProductName(String productName) {
94 | this._productName = productName;
95 | }
96 |
97 | public boolean isNaturalOrientation() {
98 | return _naturalOrientation;
99 | }
100 |
101 | public void setNaturalOrientation(boolean naturalOrientation) {
102 | this._naturalOrientation = naturalOrientation;
103 | }
104 |
105 | public int getSdkInt() {
106 | return _sdkInt;
107 | }
108 |
109 | public void setSdkInt(int sdkInt) {
110 | this._sdkInt = sdkInt;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/com/github/uiautomatorstub/Log.java:
--------------------------------------------------------------------------------
1 | package com.github.uiautomatorstub;
2 |
3 | public class Log {
4 | public static final String TAG = "UIAutomatorStub";
5 |
6 | public static void d(String msg) {
7 | android.util.Log.d(TAG, msg);
8 | }
9 |
10 | public static void i(String msg) {
11 | android.util.Log.i(TAG, msg);
12 | }
13 |
14 | public static void e(String msg) {
15 | android.util.Log.e(TAG, msg);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/com/github/uiautomatorstub/NotImplementedException.java:
--------------------------------------------------------------------------------
1 | package com.github.uiautomatorstub;
2 |
3 | import android.os.Build;
4 |
5 | /**
6 | * Created with IntelliJ IDEA.
7 | * User: b036
8 | * Date: 8/13/13
9 | * Time: 10:45 AM
10 | * To change this template use File | Settings | File Templates.
11 | */
12 | public class NotImplementedException extends Exception {
13 | public NotImplementedException() {
14 | super("This method is not yet implemented in API level " + Build.VERSION.SDK_INT + ".");
15 | }
16 |
17 | public NotImplementedException(String method) {
18 | super(method + " is not yet implemented in API level " + Build.VERSION.SDK_INT + ".");
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/com/github/uiautomatorstub/ObjInfo.java:
--------------------------------------------------------------------------------
1 | package com.github.uiautomatorstub;
2 |
3 | import com.android.uiautomator.core.UiObject;
4 | import com.android.uiautomator.core.UiObjectNotFoundException;
5 | import com.android.uiautomator.core.UiSelector;
6 |
7 | public class ObjInfo {
8 |
9 | public static final ObjInfo getObjInfo(UiObject obj) throws UiObjectNotFoundException {
10 | return new ObjInfo(obj);
11 | }
12 |
13 | public static final ObjInfo getObjInfo(UiSelector selector) throws UiObjectNotFoundException {
14 | return new ObjInfo(new UiObject(selector));
15 | }
16 |
17 | private ObjInfo(UiObject obj) throws UiObjectNotFoundException {
18 | this._bounds = Rect.from(obj.getBounds());
19 | this._checkable = obj.isCheckable();
20 | this._checked = obj.isChecked();
21 | this._childCount = obj.getChildCount();
22 | this._clickable = obj.isClickable();
23 | this._contentDescription = obj.getContentDescription();
24 | this._enabled = obj.isEnabled();
25 | this._focusable = obj.isFocusable();
26 | this._focused = obj.isFocused();
27 | this._longClickable = obj.isLongClickable();
28 | this._packageName = obj.getPackageName();
29 | this._scrollable = obj.isScrollable();
30 | this._selected = obj.isSelected();
31 | this._text = obj.getText();
32 | if (android.os.Build.VERSION.SDK_INT >= 17) {
33 | this._visibleBounds = Rect.from(obj.getVisibleBounds());
34 | }
35 | if (android.os.Build.VERSION.SDK_INT >= 18) {
36 | this._className = obj.getClassName();
37 | }
38 | }
39 |
40 | private Rect _bounds;
41 | private Rect _visibleBounds;
42 | private int _childCount;
43 | private String _className;
44 | private String _contentDescription;
45 | private String _packageName;
46 | private String _text;
47 | private boolean _checkable;
48 | private boolean _checked;
49 | private boolean _clickable;
50 | private boolean _enabled;
51 | private boolean _focusable;
52 | private boolean _focused;
53 | private boolean _longClickable;
54 | private boolean _scrollable;
55 | private boolean _selected;
56 |
57 | public Rect getBounds() {
58 | return _bounds;
59 | }
60 |
61 | public void setBounds(Rect bounds) {
62 | this._bounds = bounds;
63 | }
64 |
65 | public Rect getVisibleBounds() {
66 | return _visibleBounds;
67 | }
68 |
69 | public void setVisibleBounds(Rect visibleBounds) {
70 | this._visibleBounds = visibleBounds;
71 | }
72 |
73 | public int getChildCount() {
74 | return _childCount;
75 | }
76 |
77 | public void setChildCount(int childCount) {
78 | this._childCount = childCount;
79 | }
80 |
81 | public String getClassName() {
82 | return _className;
83 | }
84 |
85 | public void setClassName(String className) {
86 | this._className = className;
87 | }
88 |
89 | public String getContentDescription() {
90 | return _contentDescription;
91 | }
92 |
93 | public void setContentDescription(String contentDescription) {
94 | this._contentDescription = contentDescription;
95 | }
96 |
97 | public String getPackageName() {
98 | return _packageName;
99 | }
100 |
101 | public void setPackageName(String packageName) {
102 | this._packageName = packageName;
103 | }
104 |
105 | public String getText() {
106 | return _text;
107 | }
108 |
109 | public void setText(String text) {
110 | this._text = text;
111 | }
112 |
113 | public boolean isCheckable() {
114 | return _checkable;
115 | }
116 |
117 | public void setCheckable(boolean checkable) {
118 | this._checkable = checkable;
119 | }
120 |
121 | public boolean isChecked() {
122 | return _checked;
123 | }
124 |
125 | public void setChecked(boolean checked) {
126 | this._checked = checked;
127 | }
128 |
129 | public boolean isClickable() {
130 | return _clickable;
131 | }
132 |
133 | public void setClickable(boolean clickable) {
134 | this._clickable = clickable;
135 | }
136 |
137 | public boolean isEnabled() {
138 | return _enabled;
139 | }
140 |
141 | public void setEnabled(boolean enabled) {
142 | this._enabled = enabled;
143 | }
144 |
145 | public boolean isFocusable() {
146 | return _focusable;
147 | }
148 |
149 | public void setFocusable(boolean focusable) {
150 | this._focusable = focusable;
151 | }
152 |
153 | public boolean isFocused() {
154 | return _focused;
155 | }
156 |
157 | public void setFocused(boolean focused) {
158 | this._focused = focused;
159 | }
160 |
161 | public boolean isLongClickable() {
162 | return _longClickable;
163 | }
164 |
165 | public void setLongClickable(boolean longClickable) {
166 | this._longClickable = longClickable;
167 | }
168 |
169 | public boolean isScrollable() {
170 | return _scrollable;
171 | }
172 |
173 | public void setScrollable(boolean scrollable) {
174 | this._scrollable = scrollable;
175 | }
176 |
177 | public boolean isSelected() {
178 | return _selected;
179 | }
180 |
181 | public void setSelected(boolean selected) {
182 | this._selected = selected;
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/src/com/github/uiautomatorstub/Point.java:
--------------------------------------------------------------------------------
1 | package com.github.uiautomatorstub;
2 |
3 | /**
4 | * Created with IntelliJ IDEA.
5 | * User: b036
6 | * Date: 8/13/13
7 | * Time: 10:11 AM
8 | * To change this template use File | Settings | File Templates.
9 | */
10 | public class Point {
11 | private int _x;
12 | private int _y;
13 |
14 | public int getX() {
15 | return _x;
16 | }
17 |
18 | public void setX(int x) {
19 | this._x = x;
20 | }
21 |
22 | public int getY() {
23 | return _y;
24 | }
25 |
26 | public void setY(int y) {
27 | this._y = y;
28 | }
29 |
30 | public android.graphics.Point toPoint() {
31 | return new android.graphics.Point(_x, _y);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/com/github/uiautomatorstub/Rect.java:
--------------------------------------------------------------------------------
1 | package com.github.uiautomatorstub;
2 |
3 | /**
4 | * Created with IntelliJ IDEA.
5 | * User: b036
6 | * Date: 8/13/13
7 | * Time: 10:13 AM
8 | * To change this template use File | Settings | File Templates.
9 | */
10 | public class Rect {
11 | private int _top;
12 | private int _bottom;
13 | private int _left;
14 | private int _right;
15 |
16 | public static Rect from(android.graphics.Rect r) {
17 | Rect rect = new Rect();
18 | rect._top = r.top;
19 | rect._bottom = r.bottom;
20 | rect._left = r.left;
21 | rect._right = r.right;
22 |
23 | return rect;
24 | }
25 |
26 | public int getTop() {
27 | return _top;
28 | }
29 |
30 | public void setTop(int top) {
31 | this._top = top;
32 | }
33 |
34 | public int getBottom() {
35 | return _bottom;
36 | }
37 |
38 | public void setBottom(int bottom) {
39 | this._bottom = bottom;
40 | }
41 |
42 | public int getLeft() {
43 | return _left;
44 | }
45 |
46 | public void setLeft(int left) {
47 | this._left = left;
48 | }
49 |
50 | public int getRight() {
51 | return _right;
52 | }
53 |
54 | public void setRight(int right) {
55 | this._right = right;
56 | }
57 |
58 | public android.graphics.Rect toRect() {
59 | return new android.graphics.Rect(_left, _top, _right, _bottom);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/com/github/uiautomatorstub/Selector.java:
--------------------------------------------------------------------------------
1 | package com.github.uiautomatorstub;
2 |
3 | import com.android.uiautomator.core.UiSelector;
4 |
5 | public class Selector {
6 | private String _text;
7 | private String _textContains;
8 | private String _textMatches;
9 | private String _textStartsWith;
10 | private String _className;
11 | private String _classNameMatches;
12 | private String _description;
13 | private String _descriptionContains;
14 | private String _descriptionMatches;
15 | private String _descriptionStartsWith;
16 | private boolean _checkable;
17 | private boolean _checked;
18 | private boolean _clickable;
19 | private boolean _longClickable;
20 | private boolean _scrollable;
21 | private boolean _enabled;
22 | private boolean _focusable;
23 | private boolean _focused;
24 | private boolean _selected;
25 | private String _packageName;
26 | private String _packageNameMatches;
27 | private String _resourceId;
28 | private String _resourceIdMatches;
29 | private int _index;
30 | private int _instance;
31 | private Selector[] _childOrSiblingSelector = new Selector[]{};
32 | private String[] _childOrSibling = new String[]{};
33 |
34 | private long _mask;
35 |
36 | public static final long MASK_TEXT = 0x01;
37 | public static final long MASK_TEXTCONTAINS = 0x02;
38 | public static final long MASK_TEXTMATCHES = 0x04;
39 | public static final long MASK_TEXTSTARTSWITH = 0x08;
40 | public static final long MASK_CLASSNAME = 0x10;
41 | public static final long MASK_CLASSNAMEMATCHES = 0x20;
42 | public static final long MASK_DESCRIPTION = 0x40;
43 | public static final long MASK_DESCRIPTIONCONTAINS = 0x80;
44 | public static final long MASK_DESCRIPTIONMATCHES = 0x0100;
45 | public static final long MASK_DESCRIPTIONSTARTSWITH = 0x0200;
46 | public static final long MASK_CHECKABLE = 0x0400;
47 | public static final long MASK_CHECKED = 0x0800;
48 | public static final long MASK_CLICKABLE = 0x1000;
49 | public static final long MASK_LONGCLICKABLE = 0x2000;
50 | public static final long MASK_SCROLLABLE = 0x4000;
51 | public static final long MASK_ENABLED = 0x8000;
52 | public static final long MASK_FOCUSABLE = 0x010000;
53 | public static final long MASK_FOCUSED = 0x020000;
54 | public static final long MASK_SELECTED = 0x040000;
55 | public static final long MASK_PACKAGENAME = 0x080000;
56 | public static final long MASK_PACKAGENAMEMATCHES = 0x100000;
57 | public static final long MASK_RESOURCEID = 0x200000;
58 | public static final long MASK_RESOURCEIDMATCHES = 0x400000;
59 | public static final long MASK_INDEX = 0x800000;
60 | public static final long MASK_INSTANCE = 0x01000000;
61 |
62 | public UiSelector toUiSelector() {
63 | UiSelector s = new UiSelector();
64 | if ((getMask() & Selector.MASK_CHECKABLE) > 0 && android.os.Build.VERSION.SDK_INT >= 18)
65 | s = s.checkable(this.isCheckable());
66 | if ((getMask() & Selector.MASK_CHECKED) > 0)
67 | s = s.checked(isChecked());
68 | if ((getMask() & Selector.MASK_CLASSNAME) > 0)
69 | s = s.className(getClassName()); // API level 16 should support it.... wrong in Android Java Doc
70 | if ((getMask() & Selector.MASK_CLASSNAMEMATCHES) > 0 && android.os.Build.VERSION.SDK_INT >= 17)
71 | s = s.classNameMatches(getClassNameMatches());
72 | if ((getMask() & Selector.MASK_CLICKABLE) > 0)
73 | s = s.clickable(isClickable());
74 | if ((getMask() & Selector.MASK_DESCRIPTION) > 0)
75 | s = s.description(getDescription());
76 | if ((getMask() & Selector.MASK_DESCRIPTIONCONTAINS) > 0)
77 | s = s.descriptionContains(getDescriptionContains());
78 | if ((getMask() & Selector.MASK_DESCRIPTIONMATCHES) > 0 && android.os.Build.VERSION.SDK_INT >= 17)
79 | s = s.descriptionMatches(getDescriptionMatches());
80 | if ((getMask() & Selector.MASK_DESCRIPTIONSTARTSWITH) > 0)
81 | s = s.descriptionStartsWith(getDescriptionStartsWith());
82 | if ((getMask() & Selector.MASK_ENABLED) > 0)
83 | s = s.enabled(isEnabled());
84 | if ((getMask() & Selector.MASK_FOCUSABLE) > 0)
85 | s = s.focusable(isFocusable());
86 | if ((getMask() & Selector.MASK_FOCUSED) > 0)
87 | s = s.focused(isFocused());
88 | if ((getMask() & Selector.MASK_INDEX) > 0)
89 | s = s.index(getIndex());
90 | if ((getMask() & Selector.MASK_INSTANCE) > 0)
91 | s = s .instance(getInstance());
92 | if ((getMask() & Selector.MASK_LONGCLICKABLE) > 0 && android.os.Build.VERSION.SDK_INT >= 17)
93 | s = s.longClickable(isLongClickable());
94 | if ((getMask() & Selector.MASK_PACKAGENAME) > 0)
95 | s = s.packageName(getPackageName());
96 | if ((getMask() & Selector.MASK_PACKAGENAMEMATCHES) > 0 && android.os.Build.VERSION.SDK_INT >= 17)
97 | s = s.packageNameMatches(getPackageNameMatches());
98 | if ((getMask() & Selector.MASK_RESOURCEID) > 0 && android.os.Build.VERSION.SDK_INT >= 18)
99 | s = s.resourceId(getResourceId());
100 | if ((getMask() & Selector.MASK_RESOURCEIDMATCHES) > 0 && android.os.Build.VERSION.SDK_INT >= 18)
101 | s = s.resourceIdMatches(getResourceIdMatches());
102 | if ((getMask() & Selector.MASK_SCROLLABLE) > 0)
103 | s = s.scrollable(isScrollable());
104 | if ((getMask() & Selector.MASK_SELECTED) > 0)
105 | s = s.selected(isSelected());
106 | if ((getMask() & Selector.MASK_TEXT) > 0)
107 | s = s.text(getText());
108 | if ((getMask() & Selector.MASK_TEXTCONTAINS) > 0)
109 | s = s.textContains(getTextContains());
110 | if ((getMask() & Selector.MASK_TEXTSTARTSWITH) > 0)
111 | s = s.textStartsWith(getTextStartsWith());
112 | if ((getMask() & Selector.MASK_TEXTMATCHES) > 0 && android.os.Build.VERSION.SDK_INT >= 17)
113 | s = s.textMatches(getTextMatches());
114 |
115 | for (int i = 0; i < this.getChildOrSibling().length && i < this.getChildOrSiblingSelector().length; i++) {
116 | if (this.getChildOrSibling()[i].toLowerCase().equals("child"))
117 | s = s.childSelector(getChildOrSiblingSelector()[i].toUiSelector());
118 | else if (this.getChildOrSibling()[i].toLowerCase().equals("sibling"))
119 | s = s.fromParent((getChildOrSiblingSelector()[i].toUiSelector()));
120 | }
121 |
122 | return s;
123 | }
124 |
125 | public String getText() {
126 | return _text;
127 | }
128 |
129 | public void setText(String text) {
130 | this._text = text;
131 | }
132 |
133 | public String getClassName() {
134 | return _className;
135 | }
136 |
137 | public void setClassName(String className) {
138 | this._className = className;
139 | }
140 |
141 | public String getDescription() {
142 | return _description;
143 | }
144 |
145 | public void setDescription(String description) {
146 | this._description = description;
147 | }
148 |
149 | public String getTextContains() {
150 | return _textContains;
151 | }
152 |
153 | public void setTextContains(String _textContains) {
154 | this._textContains = _textContains;
155 | }
156 |
157 | public String getTextMatches() {
158 | return _textMatches;
159 | }
160 |
161 | public void setTextMatches(String _textMatches) {
162 | this._textMatches = _textMatches;
163 | }
164 |
165 | public String getTextStartsWith() {
166 | return _textStartsWith;
167 | }
168 |
169 | public void setTextStartsWith(String _textStartsWith) {
170 | this._textStartsWith = _textStartsWith;
171 | }
172 |
173 | public String getClassNameMatches() {
174 | return _classNameMatches;
175 | }
176 |
177 | public void setClassNameMatches(String _classNameMatches) {
178 | this._classNameMatches = _classNameMatches;
179 | }
180 |
181 | public String getDescriptionContains() {
182 | return _descriptionContains;
183 | }
184 |
185 | public void setDescriptionContains(String _descriptionContains) {
186 | this._descriptionContains = _descriptionContains;
187 | }
188 |
189 | public String getDescriptionMatches() {
190 | return _descriptionMatches;
191 | }
192 |
193 | public void setDescriptionMatches(String _descriptionMatches) {
194 | this._descriptionMatches = _descriptionMatches;
195 | }
196 |
197 | public String getDescriptionStartsWith() {
198 | return _descriptionStartsWith;
199 | }
200 |
201 | public void setDescriptionStartsWith(String _descriptionStartsWith) {
202 | this._descriptionStartsWith = _descriptionStartsWith;
203 | }
204 |
205 | public boolean isCheckable() {
206 | return _checkable;
207 | }
208 |
209 | public void setCheckable(boolean _checkable) {
210 | this._checkable = _checkable;
211 | }
212 |
213 | public boolean isChecked() {
214 | return _checked;
215 | }
216 |
217 | public void setChecked(boolean _checked) {
218 | this._checked = _checked;
219 | }
220 |
221 | public boolean isClickable() {
222 | return _clickable;
223 | }
224 |
225 | public void setClickable(boolean _clickable) {
226 | this._clickable = _clickable;
227 | }
228 |
229 | public boolean isScrollable() {
230 | return _scrollable;
231 | }
232 |
233 | public void setScrollable(boolean _scrollable) {
234 | this._scrollable = _scrollable;
235 | }
236 |
237 | public boolean isLongClickable() {
238 | return _longClickable;
239 | }
240 |
241 | public void setLongClickable(boolean _longClickable) {
242 | this._longClickable = _longClickable;
243 | }
244 |
245 | public boolean isEnabled() {
246 | return _enabled;
247 | }
248 |
249 | public void setEnabled(boolean _enabled) {
250 | this._enabled = _enabled;
251 | }
252 |
253 | public boolean isFocusable() {
254 | return _focusable;
255 | }
256 |
257 | public void setFocusable(boolean _focusable) {
258 | this._focusable = _focusable;
259 | }
260 |
261 | public boolean isFocused() {
262 | return _focused;
263 | }
264 |
265 | public void setFocused(boolean _focused) {
266 | this._focused = _focused;
267 | }
268 |
269 | public boolean isSelected() {
270 | return _selected;
271 | }
272 |
273 | public void setSelected(boolean _selected) {
274 | this._selected = _selected;
275 | }
276 |
277 | public String getPackageName() {
278 | return _packageName;
279 | }
280 |
281 | public void setPackageName(String _packageName) {
282 | this._packageName = _packageName;
283 | }
284 |
285 | public String getPackageNameMatches() {
286 | return _packageNameMatches;
287 | }
288 |
289 | public void setPackageNameMatches(String _packageNameMatches) {
290 | this._packageNameMatches = _packageNameMatches;
291 | }
292 |
293 | public String getResourceId() {
294 | return _resourceId;
295 | }
296 |
297 | public void setResourceId(String _resourceId) {
298 | this._resourceId = _resourceId;
299 | }
300 |
301 | public String getResourceIdMatches() {
302 | return _resourceIdMatches;
303 | }
304 |
305 | public void setResourceIdMatches(String _resourceIdMatches) {
306 | this._resourceIdMatches = _resourceIdMatches;
307 | }
308 |
309 | public int getIndex() {
310 | return _index;
311 | }
312 |
313 | public void setIndex(int _index) {
314 | this._index = _index;
315 | }
316 |
317 | public int getInstance() {
318 | return _instance;
319 | }
320 |
321 | public void setInstance(int _instance) {
322 | this._instance = _instance;
323 | }
324 |
325 | public long getMask() {
326 | return _mask;
327 | }
328 |
329 | public void setMask(long _mask) {
330 | this._mask = _mask;
331 | }
332 |
333 | public Selector[] getChildOrSiblingSelector() {
334 | return _childOrSiblingSelector;
335 | }
336 |
337 | public void setChildOrSiblingSelector(Selector[] _childOrSiblingSelector) {
338 | this._childOrSiblingSelector = _childOrSiblingSelector;
339 | }
340 |
341 | public String[] getChildOrSibling() {
342 | return _childOrSibling;
343 | }
344 |
345 | public void setChildOrSibling(String[] _childOrSibling) {
346 | this._childOrSibling = _childOrSibling;
347 | }
348 | }
349 |
--------------------------------------------------------------------------------
/src/com/github/uiautomatorstub/Stub.java:
--------------------------------------------------------------------------------
1 | package com.github.uiautomatorstub;
2 |
3 | import android.test.FlakyTest;
4 | import android.test.suitebuilder.annotation.LargeTest;
5 |
6 | import com.android.uiautomator.testrunner.UiAutomatorTestCase;
7 | import com.fasterxml.jackson.databind.ObjectMapper;
8 | import com.googlecode.jsonrpc4j.JsonRpcServer;
9 |
10 | /**
11 | * A working example of a ui automator test.
12 | *
13 | * @author SNI
14 | */
15 | public class Stub extends UiAutomatorTestCase {
16 |
17 | private static final int TEST_TOLERANCE = 3;
18 | private static final int PORT = 9008;
19 | private AutomatorHttpServer server = null;
20 |
21 | @Override
22 | protected void setUp() throws Exception {
23 | super.setUp();
24 | server = new AutomatorHttpServer(PORT);
25 | server.route("/jsonrpc/0", new JsonRpcServer(new ObjectMapper(),
26 | new AutomatorServiceImpl(), AutomatorService.class));
27 | server.start();
28 | }
29 |
30 | @Override
31 | protected void tearDown() throws Exception {
32 | server.stop();
33 | server = null;
34 | super.tearDown();
35 | }
36 |
37 | @LargeTest
38 | @FlakyTest(tolerance = TEST_TOLERANCE)
39 | public void testUIAutomatorStub() throws InterruptedException {
40 | while (server.isAlive())
41 | Thread.sleep(100);
42 | }
43 | }
--------------------------------------------------------------------------------
/src/com/github/uiautomatorstub/watcher/ClickUiObjectWatcher.java:
--------------------------------------------------------------------------------
1 | package com.github.uiautomatorstub.watcher;
2 |
3 | import com.android.uiautomator.core.UiObject;
4 | import com.android.uiautomator.core.UiObjectNotFoundException;
5 | import com.android.uiautomator.core.UiSelector;
6 | import com.github.uiautomatorstub.Log;
7 |
8 | /**
9 | * Created with IntelliJ IDEA.
10 | * User: b036
11 | * Date: 8/21/13
12 | * Time: 4:18 PM
13 | * To change this template use File | Settings | File Templates.
14 | */
15 | public class ClickUiObjectWatcher extends SelectorWatcher {
16 |
17 | private UiSelector target = null;
18 |
19 | public ClickUiObjectWatcher(UiSelector[] conditions, UiSelector target) {
20 | super(conditions);
21 | this.target = target;
22 | }
23 |
24 | @Override
25 | public void action() {
26 | Log.d("ClickUiObjectWatcher triggered!");
27 | if (target != null) {
28 | try {
29 | new UiObject(target).click();
30 | } catch (UiObjectNotFoundException e) {
31 | Log.d(e.getMessage());
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/com/github/uiautomatorstub/watcher/PressKeysWatcher.java:
--------------------------------------------------------------------------------
1 | package com.github.uiautomatorstub.watcher;
2 |
3 | import android.os.RemoteException;
4 | import android.view.KeyEvent;
5 | import com.android.uiautomator.core.UiDevice;
6 | import com.android.uiautomator.core.UiSelector;
7 | import com.github.uiautomatorstub.Log;
8 |
9 | /**
10 | * Created with IntelliJ IDEA.
11 | * User: b036
12 | * Date: 8/21/13
13 | * Time: 4:24 PM
14 | * To change this template use File | Settings | File Templates.
15 | */
16 | public class PressKeysWatcher extends SelectorWatcher{
17 | private String[] keys = new String[]{};
18 |
19 | public PressKeysWatcher(UiSelector[] conditions, String[] keys) {
20 | super(conditions);
21 | this.keys = keys;
22 | }
23 |
24 | @Override
25 | public void action() {
26 | Log.d("PressKeysWatcher triggered!");
27 | for (String key: keys) {
28 | key = key.toLowerCase();
29 | if ("home".equals(key))
30 | UiDevice.getInstance().pressHome();
31 | else if ("back".equals(key))
32 | UiDevice.getInstance().pressBack();
33 | else if ("left".equals(key))
34 | UiDevice.getInstance().pressDPadLeft();
35 | else if ("right".equals(key))
36 | UiDevice.getInstance().pressDPadRight();
37 | else if ("up".equals(key))
38 | UiDevice.getInstance().pressDPadUp();
39 | else if ("down".equals(key))
40 | UiDevice.getInstance().pressDPadDown();
41 | else if ("center".equals(key))
42 | UiDevice.getInstance().pressDPadCenter();
43 | else if ("menu".equals(key))
44 | UiDevice.getInstance().pressMenu();
45 | else if ("search".equals(key))
46 | UiDevice.getInstance().pressSearch();
47 | else if ("enter".equals(key))
48 | UiDevice.getInstance().pressEnter();
49 | else if ("delete".equals(key) || "del".equals(key))
50 | UiDevice.getInstance().pressDelete();
51 | else if ("recent".equals(key))
52 | try {
53 | UiDevice.getInstance().pressRecentApps();
54 | } catch (RemoteException e) {
55 | Log.d(e.getMessage());
56 | }
57 | else if ("volume_up".equals(key))
58 | UiDevice.getInstance().pressKeyCode(KeyEvent.KEYCODE_VOLUME_UP);
59 | else if ("volume_down".equals(key))
60 | UiDevice.getInstance().pressKeyCode(KeyEvent.KEYCODE_VOLUME_DOWN);
61 | else if ("volume_mute".equals(key))
62 | UiDevice.getInstance().pressKeyCode(KeyEvent.KEYCODE_VOLUME_MUTE);
63 | else if ("camera".equals(key))
64 | UiDevice.getInstance().pressKeyCode(KeyEvent.KEYCODE_CAMERA);
65 | else if ("power".equals(key))
66 | UiDevice.getInstance().pressKeyCode(KeyEvent.KEYCODE_POWER);
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/com/github/uiautomatorstub/watcher/SelectorWatcher.java:
--------------------------------------------------------------------------------
1 | package com.github.uiautomatorstub.watcher;
2 |
3 | import com.android.uiautomator.core.UiObject;
4 | import com.android.uiautomator.core.UiSelector;
5 | import com.android.uiautomator.core.UiWatcher;
6 |
7 | /**
8 | * Created with IntelliJ IDEA.
9 | * User: b036
10 | * Date: 8/21/13
11 | * Time: 1:57 PM
12 | * To change this template use File | Settings | File Templates.
13 | */
14 | public abstract class SelectorWatcher implements UiWatcher {
15 | private UiSelector[] conditions = null;
16 |
17 | public SelectorWatcher(UiSelector[] conditions) {
18 | this.conditions = conditions;
19 | }
20 |
21 | @Override
22 | public boolean checkForCondition() {
23 | for (UiSelector s : conditions) {
24 | UiObject obj = new UiObject(s);
25 | if (!obj.exists()) return false;
26 | }
27 | action();
28 | return true;
29 | }
30 |
31 | public abstract void action();
32 | }
33 |
--------------------------------------------------------------------------------