Added view introspection functionality to Monkey
Change-Id: Ica8439d8c5694dc6ecc5c64d8ce52d16f51cbb25
This commit is contained in:
@@ -48,6 +48,8 @@ import java.util.StringTokenizer;
|
|||||||
*/
|
*/
|
||||||
public class MonkeySourceNetwork implements MonkeyEventSource {
|
public class MonkeySourceNetwork implements MonkeyEventSource {
|
||||||
private static final String TAG = "MonkeyStub";
|
private static final String TAG = "MonkeyStub";
|
||||||
|
/* The version of the monkey network protocol */
|
||||||
|
public static final int MONKEY_NETWORK_VERSION = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ReturnValue from the MonkeyCommand that indicates whether the
|
* ReturnValue from the MonkeyCommand that indicates whether the
|
||||||
@@ -95,7 +97,7 @@ public class MonkeySourceNetwork implements MonkeyEventSource {
|
|||||||
*
|
*
|
||||||
* @param command the command line.
|
* @param command the command line.
|
||||||
* @param queue the command queue.
|
* @param queue the command queue.
|
||||||
* @returs MonkeyCommandReturn indicating what happened.
|
* @return MonkeyCommandReturn indicating what happened.
|
||||||
*/
|
*/
|
||||||
MonkeyCommandReturn translateCommand(List<String> command, CommandQueue queue);
|
MonkeyCommandReturn translateCommand(List<String> command, CommandQueue queue);
|
||||||
}
|
}
|
||||||
@@ -234,7 +236,7 @@ public class MonkeySourceNetwork implements MonkeyEventSource {
|
|||||||
* Get an integer keycode value from a given keyname.
|
* Get an integer keycode value from a given keyname.
|
||||||
*
|
*
|
||||||
* @param keyName the key name to get the code for
|
* @param keyName the key name to get the code for
|
||||||
* @returns the integer keycode value, or -1 on error.
|
* @return the integer keycode value, or -1 on error.
|
||||||
*/
|
*/
|
||||||
private static int getKeyCode(String keyName) {
|
private static int getKeyCode(String keyName) {
|
||||||
int keyCode = -1;
|
int keyCode = -1;
|
||||||
@@ -408,6 +410,11 @@ public class MonkeySourceNetwork implements MonkeyEventSource {
|
|||||||
COMMAND_MAP.put("type", new TypeCommand());
|
COMMAND_MAP.put("type", new TypeCommand());
|
||||||
COMMAND_MAP.put("listvar", new MonkeySourceNetworkVars.ListVarCommand());
|
COMMAND_MAP.put("listvar", new MonkeySourceNetworkVars.ListVarCommand());
|
||||||
COMMAND_MAP.put("getvar", new MonkeySourceNetworkVars.GetVarCommand());
|
COMMAND_MAP.put("getvar", new MonkeySourceNetworkVars.GetVarCommand());
|
||||||
|
COMMAND_MAP.put("listviews", new MonkeySourceNetworkViews.ListViewsCommand());
|
||||||
|
COMMAND_MAP.put("queryview", new MonkeySourceNetworkViews.QueryViewCommand());
|
||||||
|
COMMAND_MAP.put("getrootview", new MonkeySourceNetworkViews.GetRootViewCommand());
|
||||||
|
COMMAND_MAP.put("getviewswithtext",
|
||||||
|
new MonkeySourceNetworkViews.GetViewsWithTextCommand());
|
||||||
}
|
}
|
||||||
|
|
||||||
// QUIT command
|
// QUIT command
|
||||||
@@ -444,7 +451,7 @@ public class MonkeySourceNetwork implements MonkeyEventSource {
|
|||||||
/**
|
/**
|
||||||
* Get the next queued event to excecute.
|
* Get the next queued event to excecute.
|
||||||
*
|
*
|
||||||
* @returns the next event, or null if there aren't any more.
|
* @return the next event, or null if there aren't any more.
|
||||||
*/
|
*/
|
||||||
public MonkeyEvent getNextQueuedEvent() {
|
public MonkeyEvent getNextQueuedEvent() {
|
||||||
return queuedEvents.poll();
|
return queuedEvents.poll();
|
||||||
@@ -477,8 +484,12 @@ public class MonkeySourceNetwork implements MonkeyEventSource {
|
|||||||
*/
|
*/
|
||||||
private void startServer() throws IOException {
|
private void startServer() throws IOException {
|
||||||
clientSocket = serverSocket.accept();
|
clientSocket = serverSocket.accept();
|
||||||
// At this point, we have a client connected. Wake the device
|
// At this point, we have a client connected.
|
||||||
// up in preparation for doing some commands.
|
// Attach the accessibility listeners so that we can start receiving
|
||||||
|
// view events. Do this before wake so we can catch the wake event
|
||||||
|
// if possible.
|
||||||
|
MonkeySourceNetworkViews.setup();
|
||||||
|
// Wake the device up in preparation for doing some commands.
|
||||||
wake();
|
wake();
|
||||||
|
|
||||||
input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
|
input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
|
||||||
@@ -501,7 +512,7 @@ public class MonkeySourceNetwork implements MonkeyEventSource {
|
|||||||
* charaters with their real values.
|
* charaters with their real values.
|
||||||
*
|
*
|
||||||
* @param input the string to do replacement on.
|
* @param input the string to do replacement on.
|
||||||
* @returns the results with the characters replaced.
|
* @return the results with the characters replaced.
|
||||||
*/
|
*/
|
||||||
private static String replaceQuotedChars(String input) {
|
private static String replaceQuotedChars(String input) {
|
||||||
return input.replace("\\\"", "\"");
|
return input.replace("\\\"", "\"");
|
||||||
@@ -559,8 +570,7 @@ public class MonkeySourceNetwork implements MonkeyEventSource {
|
|||||||
if (parts.size() > 0) {
|
if (parts.size() > 0) {
|
||||||
MonkeyCommand command = COMMAND_MAP.get(parts.get(0));
|
MonkeyCommand command = COMMAND_MAP.get(parts.get(0));
|
||||||
if (command != null) {
|
if (command != null) {
|
||||||
MonkeyCommandReturn ret = command.translateCommand(parts,
|
MonkeyCommandReturn ret = command.translateCommand(parts, commandQueue);
|
||||||
commandQueue);
|
|
||||||
if (ret.wasSuccessful()) {
|
if (ret.wasSuccessful()) {
|
||||||
if (ret.hasMessage()) {
|
if (ret.hasMessage()) {
|
||||||
returnOk(ret.getMessage());
|
returnOk(ret.getMessage());
|
||||||
|
|||||||
@@ -159,6 +159,11 @@ public class MonkeySourceNetworkVars {
|
|||||||
return Long.toString(System.currentTimeMillis());
|
return Long.toString(System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
VAR_MAP.put("monkey.version", new VarGetter() {
|
||||||
|
public String get() {
|
||||||
|
return Integer.toString(MonkeySourceNetwork.MONKEY_NETWORK_VERSION);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,614 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011, The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.commands.monkey;
|
||||||
|
|
||||||
|
import static com.android.commands.monkey.MonkeySourceNetwork.EARG;
|
||||||
|
|
||||||
|
import android.accessibilityservice.IAccessibilityServiceConnection;
|
||||||
|
import android.accessibilityservice.IEventListener;
|
||||||
|
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.IPackageManager;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.os.ServiceManager;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.accessibility.AccessibilityNodeInfo;
|
||||||
|
import android.view.accessibility.IAccessibilityManager;
|
||||||
|
import android.view.accessibility.AccessibilityEvent;
|
||||||
|
|
||||||
|
import dalvik.system.DexClassLoader;
|
||||||
|
|
||||||
|
import com.android.commands.monkey.MonkeySourceNetwork.CommandQueue;
|
||||||
|
import com.android.commands.monkey.MonkeySourceNetwork.MonkeyCommand;
|
||||||
|
import com.android.commands.monkey.MonkeySourceNetwork.MonkeyCommandReturn;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class that enables Monkey to perform view introspection when issued Monkey Network
|
||||||
|
* Script commands over the network.
|
||||||
|
*/
|
||||||
|
public class MonkeySourceNetworkViews {
|
||||||
|
private static final String TAG = "MonkeyViews";
|
||||||
|
|
||||||
|
private static volatile AtomicReference<AccessibilityEvent> sLastAccessibilityEvent
|
||||||
|
= new AtomicReference<AccessibilityEvent>();
|
||||||
|
protected static IAccessibilityServiceConnection sConnection;
|
||||||
|
private static IPackageManager sPm =
|
||||||
|
IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
|
||||||
|
private static Map<String, Class<?>> sClassMap = new HashMap<String, Class<?>>();
|
||||||
|
|
||||||
|
private static final String REMOTE_ERROR =
|
||||||
|
"Unable to retrieve application info from PackageManager";
|
||||||
|
private static final String CLASS_NOT_FOUND = "Error retrieving class information";
|
||||||
|
private static final String NO_ACCESSIBILITY_EVENT = "No accessibility event has occured yet";
|
||||||
|
private static final String NO_NODE = "Node with given ID does not exist";
|
||||||
|
private static final String NO_CONNECTION = "Failed to connect to AccessibilityService, "
|
||||||
|
+ "try restarting Monkey";
|
||||||
|
|
||||||
|
private static final Map<String, ViewIntrospectionCommand> COMMAND_MAP =
|
||||||
|
new HashMap<String, ViewIntrospectionCommand>();
|
||||||
|
|
||||||
|
/* Interface for view queries */
|
||||||
|
private static interface ViewIntrospectionCommand {
|
||||||
|
/**
|
||||||
|
* Get the response to the query
|
||||||
|
* @return the response to the query
|
||||||
|
*/
|
||||||
|
public MonkeyCommandReturn query(AccessibilityNodeInfo node, List<String> args);
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
COMMAND_MAP.put("getlocation", new GetLocation());
|
||||||
|
COMMAND_MAP.put("gettext", new GetText());
|
||||||
|
COMMAND_MAP.put("getclass", new GetClass());
|
||||||
|
COMMAND_MAP.put("getchecked", new GetChecked());
|
||||||
|
COMMAND_MAP.put("getenabled", new GetEnabled());
|
||||||
|
COMMAND_MAP.put("getselected", new GetSelected());
|
||||||
|
COMMAND_MAP.put("setselected", new SetSelected());
|
||||||
|
COMMAND_MAP.put("getfocused", new GetFocused());
|
||||||
|
COMMAND_MAP.put("setfocused", new SetFocused());
|
||||||
|
COMMAND_MAP.put("getparent", new GetParent());
|
||||||
|
COMMAND_MAP.put("getchildren", new GetChildren());
|
||||||
|
COMMAND_MAP.put("getaccessibilityids", new GetAccessibilityIds());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This registers our listener with accessibility services, and gives us a connection object */
|
||||||
|
private static IAccessibilityServiceConnection getConnection() throws RemoteException {
|
||||||
|
IEventListener listener = new IEventListener.Stub() {
|
||||||
|
public void setConnection(IAccessibilityServiceConnection connection)
|
||||||
|
throws RemoteException {
|
||||||
|
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
|
||||||
|
info.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
|
||||||
|
info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
|
||||||
|
info.notificationTimeout = 0;
|
||||||
|
info.flags = AccessibilityServiceInfo.DEFAULT;
|
||||||
|
connection.setServiceInfo(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onInterrupt() {}
|
||||||
|
|
||||||
|
public void onAccessibilityEvent(AccessibilityEvent event) {
|
||||||
|
sLastAccessibilityEvent.set(AccessibilityEvent.obtain(event));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface(
|
||||||
|
ServiceManager.getService(Context.ACCESSIBILITY_SERVICE));
|
||||||
|
return manager.registerEventListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the event listener for AccessibilityEvents.
|
||||||
|
* Also sets up a communication connection so we can query the
|
||||||
|
* accessibility service.
|
||||||
|
*/
|
||||||
|
public static void setup() {
|
||||||
|
try {
|
||||||
|
sConnection = getConnection();
|
||||||
|
} catch (RemoteException re) {
|
||||||
|
Log.e(TAG,"Remote Exception encountered when"
|
||||||
|
+ " attempting to connect to Accessibility Service");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ID class for the given package.
|
||||||
|
* This will cause issues if people reload a package with different
|
||||||
|
* resource identifiers, but don't restart the Monkey server.
|
||||||
|
*
|
||||||
|
* @param packageName The package that we want to retrieve the ID class for
|
||||||
|
* @return The ID class for the given package
|
||||||
|
*/
|
||||||
|
private static Class<?> getIdClass(String packageName, String sourceDir)
|
||||||
|
throws RemoteException, ClassNotFoundException {
|
||||||
|
// This kind of reflection is expensive, so let's only do it
|
||||||
|
// if we need to
|
||||||
|
Class<?> klass = sClassMap.get(packageName);
|
||||||
|
if (klass == null) {
|
||||||
|
DexClassLoader classLoader = new DexClassLoader(
|
||||||
|
sourceDir, "/data/local/tmp",
|
||||||
|
null, ClassLoader.getSystemClassLoader());
|
||||||
|
klass = classLoader.loadClass(packageName + ".R$id");
|
||||||
|
sClassMap.put(packageName, klass);
|
||||||
|
}
|
||||||
|
return klass;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getPositionFromNode(AccessibilityNodeInfo node) {
|
||||||
|
Rect nodePosition = new Rect();
|
||||||
|
node.getBoundsInScreen(nodePosition);
|
||||||
|
StringBuilder positions = new StringBuilder();
|
||||||
|
positions.append(nodePosition.left).append(" ").append(nodePosition.top);
|
||||||
|
positions.append(" ").append(nodePosition.right-nodePosition.left).append(" ");
|
||||||
|
positions.append(nodePosition.bottom-nodePosition.top);
|
||||||
|
return positions.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a resource identifier into it's generated integer ID
|
||||||
|
*
|
||||||
|
* @param stringId the string identifier
|
||||||
|
* @return the generated integer identifier.
|
||||||
|
*/
|
||||||
|
private static int getId(String stringId, AccessibilityEvent event)
|
||||||
|
throws MonkeyViewException {
|
||||||
|
try {
|
||||||
|
AccessibilityNodeInfo node = event.getSource();
|
||||||
|
String packageName = node.getPackageName().toString();
|
||||||
|
ApplicationInfo appInfo = sPm.getApplicationInfo(packageName, 0);
|
||||||
|
Class<?> klass;
|
||||||
|
klass = getIdClass(packageName, appInfo.sourceDir);
|
||||||
|
return klass.getField(stringId).getInt(null);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw new MonkeyViewException(REMOTE_ERROR);
|
||||||
|
} catch (ClassNotFoundException e){
|
||||||
|
throw new MonkeyViewException(e.getMessage());
|
||||||
|
} catch (NoSuchFieldException e){
|
||||||
|
throw new MonkeyViewException("No such node with given id");
|
||||||
|
} catch (IllegalAccessException e){
|
||||||
|
throw new MonkeyViewException("Private identifier");
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
// AccessibilityServiceConnection throws a NullPointerException if you hand it
|
||||||
|
// an ID that doesn't exist onscreen
|
||||||
|
throw new MonkeyViewException("No node with given id exists onscreen");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AccessibilityNodeInfo getNodeByAccessibilityIds(
|
||||||
|
String windowString, String viewString) throws RemoteException {
|
||||||
|
int windowId = Integer.parseInt(windowString);
|
||||||
|
int viewId = Integer.parseInt(viewString);
|
||||||
|
return sConnection.findAccessibilityNodeInfoByAccessibilityId(windowId, viewId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AccessibilityNodeInfo getNodeByViewId(String viewId, AccessibilityEvent event)
|
||||||
|
throws RemoteException, MonkeyViewException {
|
||||||
|
int id = getId(viewId, event);
|
||||||
|
return sConnection.findAccessibilityNodeInfoByViewIdInActiveWindow(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command to list all possible view ids for the given application.
|
||||||
|
* This lists all view ids regardless if they are on screen or not.
|
||||||
|
*/
|
||||||
|
public static class ListViewsCommand implements MonkeyCommand {
|
||||||
|
//listviews
|
||||||
|
public MonkeyCommandReturn translateCommand(List<String> command,
|
||||||
|
CommandQueue queue) {
|
||||||
|
AccessibilityEvent lastEvent = sLastAccessibilityEvent.get();
|
||||||
|
if (lastEvent == null) {
|
||||||
|
return new MonkeyCommandReturn(false, NO_ACCESSIBILITY_EVENT);
|
||||||
|
}
|
||||||
|
lastEvent.setSealed(true);
|
||||||
|
AccessibilityNodeInfo node = lastEvent.getSource();
|
||||||
|
/* Occasionally the API will generate an event with no source, which is essentially the
|
||||||
|
* same as it generating no event at all */
|
||||||
|
if (node == null) {
|
||||||
|
return new MonkeyCommandReturn(false, NO_ACCESSIBILITY_EVENT);
|
||||||
|
}
|
||||||
|
String packageName = node.getPackageName().toString();
|
||||||
|
try{
|
||||||
|
Class<?> klass;
|
||||||
|
ApplicationInfo appInfo = sPm.getApplicationInfo(packageName, 0);
|
||||||
|
klass = getIdClass(packageName, appInfo.sourceDir);
|
||||||
|
StringBuilder fieldBuilder = new StringBuilder();
|
||||||
|
Field[] fields = klass.getFields();
|
||||||
|
for (Field field : fields) {
|
||||||
|
fieldBuilder.append(field.getName() + " ");
|
||||||
|
}
|
||||||
|
return new MonkeyCommandReturn(true, fieldBuilder.toString());
|
||||||
|
} catch (RemoteException e){
|
||||||
|
return new MonkeyCommandReturn(false, REMOTE_ERROR);
|
||||||
|
} catch (ClassNotFoundException e){
|
||||||
|
return new MonkeyCommandReturn(false, CLASS_NOT_FOUND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A command that allows for querying of views. It takes an id type, the requisite ids,
|
||||||
|
* and the command for querying the view.
|
||||||
|
*/
|
||||||
|
public static class QueryViewCommand implements MonkeyCommand {
|
||||||
|
//queryview [id type] [id(s)] [command]
|
||||||
|
//queryview viewid button1 gettext
|
||||||
|
//queryview accessibilityids 12 5 getparent
|
||||||
|
public MonkeyCommandReturn translateCommand(List<String> command,
|
||||||
|
CommandQueue queue) {
|
||||||
|
if (command.size() > 2) {
|
||||||
|
if (sConnection == null) {
|
||||||
|
return new MonkeyCommandReturn(false, NO_CONNECTION);
|
||||||
|
}
|
||||||
|
AccessibilityEvent lastEvent = sLastAccessibilityEvent.get();
|
||||||
|
if (lastEvent == null) {
|
||||||
|
return new MonkeyCommandReturn(false, NO_ACCESSIBILITY_EVENT);
|
||||||
|
}
|
||||||
|
lastEvent.setSealed(true);
|
||||||
|
String idType = command.get(1);
|
||||||
|
AccessibilityNodeInfo node;
|
||||||
|
String viewQuery;
|
||||||
|
List<String> args;
|
||||||
|
if ("viewid".equals(idType)) {
|
||||||
|
try {
|
||||||
|
node = getNodeByViewId(command.get(2), lastEvent);
|
||||||
|
viewQuery = command.get(3);
|
||||||
|
args = command.subList(4, command.size());
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
return new MonkeyCommandReturn(false, REMOTE_ERROR);
|
||||||
|
} catch (MonkeyViewException e) {
|
||||||
|
return new MonkeyCommandReturn(false, e.getMessage());
|
||||||
|
}
|
||||||
|
} else if (idType.equals("accessibilityids")) {
|
||||||
|
try {
|
||||||
|
node = getNodeByAccessibilityIds(command.get(2), command.get(3));
|
||||||
|
viewQuery = command.get(4);
|
||||||
|
args = command.subList(5, command.size());
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
return new MonkeyCommandReturn(false, REMOTE_ERROR);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return EARG;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return EARG;
|
||||||
|
}
|
||||||
|
if (node == null) {
|
||||||
|
return new MonkeyCommandReturn(false, NO_NODE);
|
||||||
|
}
|
||||||
|
ViewIntrospectionCommand getter = COMMAND_MAP.get(viewQuery);
|
||||||
|
if (getter != null) {
|
||||||
|
return getter.query(node, args);
|
||||||
|
} else {
|
||||||
|
return EARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return EARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A command that returns the accessibility ids of the root view.
|
||||||
|
*/
|
||||||
|
public static class GetRootViewCommand implements MonkeyCommand {
|
||||||
|
// getrootview
|
||||||
|
public MonkeyCommandReturn translateCommand(List<String> command,
|
||||||
|
CommandQueue queue) {
|
||||||
|
AccessibilityEvent lastEvent = sLastAccessibilityEvent.get();
|
||||||
|
if (lastEvent == null) {
|
||||||
|
return new MonkeyCommandReturn(false, NO_ACCESSIBILITY_EVENT);
|
||||||
|
}
|
||||||
|
lastEvent.setSealed(true);
|
||||||
|
AccessibilityNodeInfo node = lastEvent.getSource();
|
||||||
|
return (new GetAccessibilityIds()).query(node, new ArrayList<String>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A command that returns the accessibility ids of the views that contain the given text.
|
||||||
|
* It takes a string of text and returns the accessibility ids of the nodes that contain the
|
||||||
|
* text as a list of integers separated by spaces.
|
||||||
|
*/
|
||||||
|
public static class GetViewsWithTextCommand implements MonkeyCommand {
|
||||||
|
// getviewswithtext [text]
|
||||||
|
// getviewswithtext "some text here"
|
||||||
|
public MonkeyCommandReturn translateCommand(List<String> command,
|
||||||
|
CommandQueue queue) {
|
||||||
|
if (sConnection == null) {
|
||||||
|
return new MonkeyCommandReturn(false, NO_CONNECTION);
|
||||||
|
}
|
||||||
|
if (command.size() == 2) {
|
||||||
|
StringBuilder logs = new StringBuilder();
|
||||||
|
String text = command.get(1);
|
||||||
|
List<AccessibilityNodeInfo> nodes;
|
||||||
|
try {
|
||||||
|
nodes = sConnection.findAccessibilityNodeInfosByViewTextInActiveWindow(text);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
return new MonkeyCommandReturn(false, REMOTE_ERROR);
|
||||||
|
}
|
||||||
|
if (nodes == null) {
|
||||||
|
return new MonkeyCommandReturn(true, "");
|
||||||
|
}
|
||||||
|
ViewIntrospectionCommand idGetter = new GetAccessibilityIds();
|
||||||
|
List<String> emptyArgs = new ArrayList<String>();
|
||||||
|
StringBuilder ids = new StringBuilder();
|
||||||
|
for (AccessibilityNodeInfo node : nodes) {
|
||||||
|
MonkeyCommandReturn result = idGetter.query(node, emptyArgs);
|
||||||
|
if (!result.wasSuccessful()){
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
ids.append(result.getMessage()).append(" ");
|
||||||
|
}
|
||||||
|
return new MonkeyCommandReturn(true, ids.toString());
|
||||||
|
}
|
||||||
|
return EARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command to retrieve the location of the given node.
|
||||||
|
* Returns the x, y, width and height of the view, separated by spaces.
|
||||||
|
*/
|
||||||
|
public static class GetLocation implements ViewIntrospectionCommand {
|
||||||
|
//queryview [id type] [id] getlocation
|
||||||
|
//queryview viewid button1 getlocation
|
||||||
|
public MonkeyCommandReturn query(AccessibilityNodeInfo node,
|
||||||
|
List<String> args) {
|
||||||
|
if (args.size() == 0) {
|
||||||
|
Rect nodePosition = new Rect();
|
||||||
|
node.getBoundsInScreen(nodePosition);
|
||||||
|
StringBuilder positions = new StringBuilder();
|
||||||
|
positions.append(nodePosition.left).append(" ").append(nodePosition.top);
|
||||||
|
positions.append(" ").append(nodePosition.right-nodePosition.left).append(" ");
|
||||||
|
positions.append(nodePosition.bottom-nodePosition.top);
|
||||||
|
return new MonkeyCommandReturn(true, positions.toString());
|
||||||
|
}
|
||||||
|
return EARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command to retrieve the text of the given node
|
||||||
|
*/
|
||||||
|
public static class GetText implements ViewIntrospectionCommand {
|
||||||
|
//queryview [id type] [id] gettext
|
||||||
|
//queryview viewid button1 gettext
|
||||||
|
public MonkeyCommandReturn query(AccessibilityNodeInfo node,
|
||||||
|
List<String> args) {
|
||||||
|
if (args.size() == 0) {
|
||||||
|
if (node.isPassword()){
|
||||||
|
return new MonkeyCommandReturn(false, "Node contains a password");
|
||||||
|
}
|
||||||
|
return new MonkeyCommandReturn(true, node.getText().toString());
|
||||||
|
}
|
||||||
|
return EARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command to retrieve the class name of the given node
|
||||||
|
*/
|
||||||
|
public static class GetClass implements ViewIntrospectionCommand {
|
||||||
|
//queryview [id type] [id] getclass
|
||||||
|
//queryview viewid button1 getclass
|
||||||
|
public MonkeyCommandReturn query(AccessibilityNodeInfo node,
|
||||||
|
List<String> args) {
|
||||||
|
if (args.size() == 0) {
|
||||||
|
return new MonkeyCommandReturn(true, node.getClassName().toString());
|
||||||
|
}
|
||||||
|
return EARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Command to retrieve the checked status of the given node
|
||||||
|
*/
|
||||||
|
public static class GetChecked implements ViewIntrospectionCommand {
|
||||||
|
//queryview [id type] [id] getchecked
|
||||||
|
//queryview viewid button1 getchecked
|
||||||
|
public MonkeyCommandReturn query(AccessibilityNodeInfo node,
|
||||||
|
List<String> args) {
|
||||||
|
if (args.size() == 0) {
|
||||||
|
return new MonkeyCommandReturn(true, Boolean.toString(node.isChecked()));
|
||||||
|
}
|
||||||
|
return EARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command to retrieve whether the given node is enabled
|
||||||
|
*/
|
||||||
|
public static class GetEnabled implements ViewIntrospectionCommand {
|
||||||
|
//queryview [id type] [id] getenabled
|
||||||
|
//queryview viewid button1 getenabled
|
||||||
|
public MonkeyCommandReturn query(AccessibilityNodeInfo node,
|
||||||
|
List<String> args) {
|
||||||
|
if (args.size() == 0) {
|
||||||
|
return new MonkeyCommandReturn(true, Boolean.toString(node.isEnabled()));
|
||||||
|
}
|
||||||
|
return EARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command to retrieve whether the given node is selected
|
||||||
|
*/
|
||||||
|
public static class GetSelected implements ViewIntrospectionCommand {
|
||||||
|
//queryview [id type] [id] getselected
|
||||||
|
//queryview viewid button1 getselected
|
||||||
|
public MonkeyCommandReturn query(AccessibilityNodeInfo node,
|
||||||
|
List<String> args) {
|
||||||
|
if (args.size() == 0) {
|
||||||
|
return new MonkeyCommandReturn(true, Boolean.toString(node.isSelected()));
|
||||||
|
}
|
||||||
|
return EARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command to set the selected status of the given node. Takes a boolean value as its only
|
||||||
|
* argument.
|
||||||
|
*/
|
||||||
|
public static class SetSelected implements ViewIntrospectionCommand {
|
||||||
|
//queryview [id type] [id] setselected [boolean]
|
||||||
|
//queryview viewid button1 setselected true
|
||||||
|
public MonkeyCommandReturn query(AccessibilityNodeInfo node,
|
||||||
|
List<String> args) {
|
||||||
|
if (args.size() == 1) {
|
||||||
|
boolean actionPerformed;
|
||||||
|
if (Boolean.valueOf(args.get(0))) {
|
||||||
|
actionPerformed = node.performAction(AccessibilityNodeInfo.ACTION_SELECT);
|
||||||
|
} else if (!Boolean.valueOf(args.get(0))) {
|
||||||
|
actionPerformed =
|
||||||
|
node.performAction(AccessibilityNodeInfo.ACTION_CLEAR_SELECTION);
|
||||||
|
} else {
|
||||||
|
return EARG;
|
||||||
|
}
|
||||||
|
return new MonkeyCommandReturn(actionPerformed);
|
||||||
|
}
|
||||||
|
return EARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command to get whether the given node is focused.
|
||||||
|
*/
|
||||||
|
public static class GetFocused implements ViewIntrospectionCommand {
|
||||||
|
//queryview [id type] [id] getfocused
|
||||||
|
//queryview viewid button1 getfocused
|
||||||
|
public MonkeyCommandReturn query(AccessibilityNodeInfo node,
|
||||||
|
List<String> args) {
|
||||||
|
if (args.size() == 0) {
|
||||||
|
return new MonkeyCommandReturn(true, Boolean.toString(node.isFocused()));
|
||||||
|
}
|
||||||
|
return EARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command to set the focus status of the given node. Takes a boolean value
|
||||||
|
* as its only argument.
|
||||||
|
*/
|
||||||
|
public static class SetFocused implements ViewIntrospectionCommand {
|
||||||
|
//queryview [id type] [id] setfocused [boolean]
|
||||||
|
//queryview viewid button1 setfocused false
|
||||||
|
public MonkeyCommandReturn query(AccessibilityNodeInfo node,
|
||||||
|
List<String> args) {
|
||||||
|
node.setSealed(true);
|
||||||
|
if (args.size() == 1) {
|
||||||
|
boolean actionPerformed;
|
||||||
|
if (Boolean.valueOf(args.get(0))) {
|
||||||
|
actionPerformed = node.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
|
||||||
|
} else if (!Boolean.valueOf(args.get(0))) {
|
||||||
|
actionPerformed = node.performAction(AccessibilityNodeInfo.ACTION_CLEAR_FOCUS);
|
||||||
|
} else {
|
||||||
|
return EARG;
|
||||||
|
}
|
||||||
|
return new MonkeyCommandReturn(actionPerformed);
|
||||||
|
}
|
||||||
|
return EARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command to get the accessibility ids of the given node. Returns the accessibility ids as a
|
||||||
|
* space separated pair of integers with window id coming first, followed by the accessibility
|
||||||
|
* view id.
|
||||||
|
*/
|
||||||
|
public static class GetAccessibilityIds implements ViewIntrospectionCommand {
|
||||||
|
//queryview [id type] [id] getaccessibilityids
|
||||||
|
//queryview viewid button1 getaccessibilityids
|
||||||
|
public MonkeyCommandReturn query(AccessibilityNodeInfo node,
|
||||||
|
List<String> args) {
|
||||||
|
if (args.size() == 0) {
|
||||||
|
int viewId;
|
||||||
|
try {
|
||||||
|
Class klass = node.getClass();
|
||||||
|
Field field = klass.getDeclaredField("mAccessibilityViewId");
|
||||||
|
field.setAccessible(true);
|
||||||
|
viewId = ((Integer) field.get(node)).intValue();
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
return new MonkeyCommandReturn(false, NO_NODE);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
return new MonkeyCommandReturn(false, "Access exception");
|
||||||
|
}
|
||||||
|
String ids = node.getWindowId() + " " + viewId;
|
||||||
|
return new MonkeyCommandReturn(true, ids);
|
||||||
|
}
|
||||||
|
return EARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command to get the accessibility ids of the parent of the given node. Returns the
|
||||||
|
* accessibility ids as a space separated pair of integers with window id coming first followed
|
||||||
|
* by the accessibility view id.
|
||||||
|
*/
|
||||||
|
public static class GetParent implements ViewIntrospectionCommand {
|
||||||
|
//queryview [id type] [id] getparent
|
||||||
|
//queryview viewid button1 getparent
|
||||||
|
public MonkeyCommandReturn query(AccessibilityNodeInfo node,
|
||||||
|
List<String> args) {
|
||||||
|
if (args.size() == 0) {
|
||||||
|
AccessibilityNodeInfo parent = node.getParent();
|
||||||
|
if (parent == null) {
|
||||||
|
return new MonkeyCommandReturn(false, "Given node has no parent");
|
||||||
|
}
|
||||||
|
return (new GetAccessibilityIds()).query(parent, new ArrayList<String>());
|
||||||
|
}
|
||||||
|
return EARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command to get the accessibility ids of the children of the given node. Returns the
|
||||||
|
* children's ids as a space separated list of integer pairs. Each of the pairs consists of the
|
||||||
|
* window id, followed by the accessibility id.
|
||||||
|
*/
|
||||||
|
public static class GetChildren implements ViewIntrospectionCommand {
|
||||||
|
//queryview [id type] [id] getchildren
|
||||||
|
//queryview viewid button1 getchildren
|
||||||
|
public MonkeyCommandReturn query(AccessibilityNodeInfo node,
|
||||||
|
List<String> args) {
|
||||||
|
if (args.size() == 0) {
|
||||||
|
ViewIntrospectionCommand idGetter = new GetAccessibilityIds();
|
||||||
|
List<String> emptyArgs = new ArrayList<String>();
|
||||||
|
StringBuilder ids = new StringBuilder();
|
||||||
|
int totalChildren = node.getChildCount();
|
||||||
|
for (int i = 0; i < totalChildren; i++) {
|
||||||
|
MonkeyCommandReturn result = idGetter.query(node.getChild(i), emptyArgs);
|
||||||
|
if (!result.wasSuccessful()) {
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
ids.append(result.getMessage()).append(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new MonkeyCommandReturn(true, ids.toString());
|
||||||
|
}
|
||||||
|
return EARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.android.commands.monkey;
|
||||||
|
|
||||||
|
public class MonkeyViewException extends java.lang.Exception {
|
||||||
|
/**
|
||||||
|
* Constructs a MonkeyViewException object
|
||||||
|
*/
|
||||||
|
public MonkeyViewException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a MonkeyViewException object
|
||||||
|
*
|
||||||
|
* @param message the error message
|
||||||
|
*/
|
||||||
|
public MonkeyViewException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user