Android Open Source - robo-remote Remote Server






From Project

Back to project page robo-remote.

License

The source code is released under:

Copyright (c) 2012, 2013, 2014, Groupon, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provi...

If you think the Android project robo-remote listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
        Copyright (c) 2012, 2013, 2014, Groupon, Inc.
        All rights reserved.//from  w  w  w . ja  va  2 s .com

        Redistribution and use in source and binary forms, with or without
        modification, are permitted provided that the following conditions
        are met:

        Redistributions of source code must retain the above copyright notice,
        this list of conditions and the following disclaimer.

        Redistributions in binary form must reproduce the above copyright
        notice, this list of conditions and the following disclaimer in the
        documentation and/or other materials provided with the distribution.

        Neither the name of GROUPON nor the names of its contributors may be
        used to endorse or promote products derived from this software without
        specific prior written permission.

        THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
        IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
        TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
        PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
        HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
        SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
        TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
        PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
        LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
        NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
        SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.groupon.roboremote.roboremoteservercommon;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

import android.view.View;
import org.json.JSONArray;
import org.json.JSONObject;

public abstract class RemoteServer {
    // get an instantiated class based on predefined keys(ex: solo for Robotium)
    protected abstract Object getInstantiatedClass(String query);

    // Get a view with the specified name(may not be supported by all automation platforms)
    protected abstract View getView(String viewName);

    public void startServer(int port) throws Exception {
        System.out.println("startServer:: Starting HTTP service");
        try
        {
            new RCHttpd(port);
        }
        catch( IOException ioe )
        {
            System.out.println("startServer:: Couldn't start server:\n" + ioe);
            System.exit( -1 );
        }
        System.out.println("startServer:: Listening on port " + port + ". Kill test to stop.\n");
        while(true) {
            Thread.sleep(5000);
        }
    }

    public class RCHttpd extends NanoHTTPD {
        private Object lastResponseObject = null;
        private HashMap<String, Object> storedResponses = new HashMap<String, Object>();

        public RCHttpd(int port) throws IOException {
            super(port, new File("/"));
        }

        public Response serve( String uri, String method, Properties header, Properties parms, Properties files ) {
            String msg = "";

            uri = uri.substring(1);

            if ( method.equalsIgnoreCase( Constants.NANO_POST )) {
                msg = processPost(uri, parms).toString();
            } else if ( method.equalsIgnoreCase( Constants.NANO_GET )) {
                msg = processGet(uri, parms);
            } else {
                // dunno what to do
            }

            return new Response( HTTP_OK, MIME_HTML, msg );
        }


        /**
         * Process the list of passed in operations and return the result
         * @param operations
         * @return
         * @throws Exception
         */
        private JSONObject processOperations(JSONArray operations) throws Exception {
            JSONArray returnValues = new JSONArray();
            JSONObject returnObject = new JSONObject();

            // the idea here is that there may be multiple operations
            // each subsequent operation is called on the return value of the first operation
            Object currentClassObject = null;
            for (int x = 0; x < operations.length(); x++) {
                JSONObject operation = operations.getJSONObject(x);
                System.out.println("processOperations:: Current operation: " + operation);

                Object[] classArgs = new Object[0];

                // see if this has a query.. normally only the 1st op will
                String query = null;
                if (operation.has(Constants.REQUEST_QUERY)) {
                    query = operation.getString(Constants.REQUEST_QUERY);
                } else if(operation.has(Constants.REQUEST_INSTANTIATE)) {
                    query = operation.getString(Constants.REQUEST_INSTANTIATE);

                    // get arguments for class instantiation
                    JSONArray args = new JSONArray();
                    if (operation.has(Constants.REQUEST_ARGUMENTS)) {
                        args = operation.getJSONArray(Constants.REQUEST_ARGUMENTS);
                        classArgs = new Object[args.length()];
                        for (int xx = 0; xx < args.length(); xx++) {
                            classArgs[xx] = args.get(xx);
                        }
                    }
                }

                // restore stored item if this was a stored value
                if (query != null && query.startsWith(Constants.STORED)) {
                    currentClassObject = storedResponses.get(query);
                }

                // need to find a class object to work on if one isn't already defined
                if (currentClassObject == null && query != null) {
                    // let's find a class based on the query
                    // This function is abstract and delegated to the implementing server
                    Object delegatedClassObject = getInstantiatedClass(query);

                    if (delegatedClassObject != null) {
                        currentClassObject = delegatedClassObject;
                    } else {
                        try {
                            Class c = Class.forName(query);
                            try {
                                // try instantiating.. if that doesn't work then it is probably a static class
                                currentClassObject = instantiateClass(c, classArgs);
                            } catch (Exception e) {

                            }

                            // if we still don't have one then assume it is static and assign to the found class
                            if (currentClassObject == null)
                                currentClassObject = c;
                        } catch (Exception e) {
                            // if we get here that means class resolution failed
                            // we'll return an error
                            returnObject.put(Constants.RESULT_OUTCOME, Constants.RESULT_FAILED);
                            returnObject.put(Constants.RESULT_REASON, "Could not resolve class: " + query);
                            return returnObject;
                        }
                    }
                }

                System.out.println("processOperations:: Working class: " + currentClassObject);

                // the op type could be an Constants.REQUEST_OPERATION or a Constants.REQUEST_FIELD or REQUEST_STORE or REQUEST_REMOVE
                // operations are method calls
                // fields are field accessors(returns the value of the field)
                if (operation.has(Constants.REQUEST_OPERATION)) {
                    // now run an arbitrary method based on the class
                    JSONObject op = operation.getJSONObject(Constants.REQUEST_OPERATION);

                    // get method name
                    String method = op.getString(Constants.REQUEST_METHOD_NAME);

                    // Get arguments and convert into an array of Objects
                    JSONArray args = new JSONArray();
                    if (op.has(Constants.REQUEST_ARGUMENTS)) {
                        args = op.getJSONArray(Constants.REQUEST_ARGUMENTS);
                    }

                    Object[] mArgs = new Object[args.length()];
                    for (int xx = 0; xx < args.length(); xx++) {
                        mArgs[xx] = args.get(xx);
                    }

                    ArbitraryItemStruct funcReturn = null;
                    Boolean callFailed = false;

                    try {
                        funcReturn = runArbitraryMethod(currentClassObject, method, mArgs);
                    } catch (Exception e) {
                        // this means something went wrong trying to call the function
                        String msg = e.getMessage();
                        e.printStackTrace();
                        returnObject.put(Constants.RESULT_OUTCOME, Constants.RESULT_FAILED);
                        returnObject.put(Constants.RESULT_REASON, msg);

                        callFailed = true;
                        break;
                    }

                    if (! callFailed) {
                        if (funcReturn.getReturnVal() != null) {
                            currentClassObject = funcReturn.getReturnVal();
                        }

                        returnValues = getReturnValues(funcReturn);
                        returnObject.put(Constants.RESULT_RESULTS, returnValues);
                        returnObject.put(Constants.RESULT_OUTCOME, Constants.RESULT_SUCCESS);
                    }

                    returnObject.put(Constants.RESULT_RESULTS, returnValues);
                } else if (operation.has(Constants.REQUEST_INSTANTIATE)) {
                    // Instantiate a class
                    JSONArray resultArray = new JSONArray();
                    resultArray.put(currentClassObject);
                    returnObject.put(Constants.RESULT_RESULTS, resultArray);
                    returnObject.put(Constants.RESULT_OUTCOME, Constants.RESULT_SUCCESS);
                } else if (operation.has(Constants.REQUEST_FIELD)) {
                    // we actually want a field accessor
                    String fieldName = operation.getString(Constants.REQUEST_FIELD);

                    ArbitraryItemStruct funcReturn = null;
                    Boolean callFailed = false;

                    try {
                        funcReturn = getArbitraryField(currentClassObject, fieldName);
                    } catch (Exception e) {
                        // this means something went wrong trying to call the function
                        String msg = e.getMessage();
                        returnObject.put(Constants.RESULT_OUTCOME, Constants.RESULT_FAILED);
                        returnObject.put(Constants.RESULT_REASON, msg);

                        callFailed = true;
                        break;
                    }

                    if (funcReturn.getReturnVal() != null) {
                        currentClassObject = funcReturn.getReturnVal();
                    }

                    returnValues = getReturnValues(funcReturn);
                    returnObject.put(Constants.RESULT_RESULTS, returnValues);
                    returnObject.put(Constants.RESULT_OUTCOME, Constants.RESULT_SUCCESS);
                } else if (operation.has(Constants.REQUEST_STORE)) {
                    // store the lastResponseObject
                    storedResponses.put(Constants.STORED + operation.getString(Constants.REQUEST_STORE), lastResponseObject);
                } else if (operation.has(Constants.REQUEST_REMOVE)) {
                    // remove the specified stored response
                    storedResponses.remove(operation.getString(Constants.REQUEST_STORE));
                } else if (operation.has(Constants.REQUEST_RETRIEVE)) {
                    // retrieve a stored response
                    currentClassObject = storedResponses.get(Constants.STORED + operation.get(Constants.REQUEST_RETRIEVE));
                }

                // store currentClassObject
                lastResponseObject = currentClassObject;
            }

            return returnObject;
        }

        /**
         * Returns a JSONArray representing the return values of the call
         * @param returnItem
         * @return
         */
        private JSONArray getReturnValues(ArbitraryItemStruct returnItem) {
            JSONArray returnValues = new JSONArray();

            String returnType = returnItem.getReturnType();

            if (returnType.contains(Constants.RETURN_TYPE_LIST) && returnType.contains(Constants.RETURN_TYPE_JAVA_UTIL)) {
                List<Object> funcList = (List<Object>) returnItem.getReturnVal();

                for (Object obj : funcList) {
                    returnValues.put(obj);
                }
            } else if (returnType.toLowerCase().contains(Constants.RETURN_TYPE_BOOLEAN)) {
                Boolean ret = (Boolean)returnItem.getReturnVal();
                returnValues.put(ret);
            } else if (returnType.toLowerCase().contains(Constants.RETURN_TYPE_ARRAY)) {
                // we need to iterate over this until we get an exception
                int x = 0;
                while(true) {
                    try {
                        returnValues.put(java.lang.reflect.Array.get(returnItem.getReturnVal(), x));
                    } catch (Exception e) {
                        break;
                    }
                    x++;
                }

                if (x == 0) {
                    // we didn't put anything in returnValues
                    // we'll just return true
                    returnValues.put(true);
                }
            } else if (! returnType.toLowerCase().contains(Constants.RETURN_TYPE_VOID)) {
                // we'll grab the result as an object and do what we can
                Object tmpObj = returnItem.getReturnVal();
                try {
                    returnValues.put(tmpObj);
                } catch(Exception ee) {
                    returnValues.put(true);
                }
            } else  {
                // this was a void method so there is no return value
            }

            return returnValues;
        }

        private ArbitraryItemStruct getArbitraryField(Object classObject, String fieldName) throws Exception {
            ArbitraryItemStruct fieldResults = new ArbitraryItemStruct();

            Field f = null;
            Object val = null;

            try {
                // if this was not an instantiated class then we actually want to get fields from the base object
                if (classObject.getClass().toString().contains(Constants.CLASS_TYPE_STATIC)) {
                    f = ((Class)classObject).getField(fieldName);
                    val = f.get((Class)classObject);
                } else {
                    f = classObject.getClass().getField(fieldName);
                    val = f.get(classObject);
                }
            } catch (NoSuchFieldException nsfe) {
                throw new Exception("Could not find field");
            }

            String returnType = f.getType().toString();
            fieldResults.setReturnType(returnType);

            fieldResults.setReturnVal(val);
            fieldResults.setField(f);

            return fieldResults;
        }

        /**
         * Private inner class to represent a complex return value from matchAndConvertArguments
         */
        private class MatchAndConvert {
            Boolean convertedArguments = false;
            Object[] arguments = null;
            int matches = 0;
            Boolean matchSucceeded = false;
        }

        /**
         * This method takes an array of Classes that represent parameter types for a method or class instantation
         * And an array of arguments to match up with that constructor/method signature
         * Some arguments may be converted during the process
         * @param paramTypesToMatch
         * @param args
         * @throws Exception
         */
        private MatchAndConvert matchAndConvertArguments(Class<?>[] paramTypesToMatch, Object[] args) throws Exception {
            // return value
            MatchAndConvert matchReturn = new MatchAndConvert();

            // array to contain the original argument types from "args"
            Class[] argTypes = new Class[args.length];
            // array to contain a copy of the "args" array which may be edited
            matchReturn.arguments = new Object[args.length];

            // replicate the args array
            int x = 0;
            for (Object arg: args) {
                argTypes[x] = arg.getClass();
                matchReturn.arguments[x] = args[x];
                x++;
            }

            // go through each param type and try to match things up
            x = 0;
            matchReturn.matches = 0;
            for (Class<?> paramClass: paramTypesToMatch) {
                // now get the known types array that matches the argTypes type
                String currentClsArg = argTypes[x].toString();
                x++;

                // see if this is a stored value
                if ((String.valueOf(args[matchReturn.matches])).startsWith(Constants.STORED)) {
                    // IF there is a stored value and it's type matches the type we are trying to match
                    if (storedResponses.containsKey((String)args[matchReturn.matches]) &&
                            storedResponses.get((String)args[matchReturn.matches]).getClass().toString().startsWith(paramClass.toString())) {
                        matchReturn.arguments[matchReturn.matches] = storedResponses.get((String)args[matchReturn.matches]);
                        matchReturn.matches++;
                        continue;
                    }
                }

                // get just the class name
                currentClsArg = currentClsArg.substring(currentClsArg.lastIndexOf('.') + 1);

                // it's possible that null was passed in.. if so we'll automatically say that it matches but was converted
                if (currentClsArg.startsWith("JSONObject") && matchReturn.arguments[x - 1].toString().compareTo("null") == 0) {
                    matchReturn.arguments[x - 1] = null;
                    matchReturn.convertedArguments = true;
                    matchReturn.matches++;
                    continue;
                }

                // get type equivalents
                // ex: int == Integer
                String[] knownTypes = getTypeEquivalents(currentClsArg);

                // if the knownTypes array is empty.. then we push the current type on for the type comparison
                // we have no better knowledge, but this allows comparison for types with no equivalents
                if (knownTypes == null || knownTypes.length == 0) {
                    knownTypes = new String[1];
                    knownTypes[0] = currentClsArg;
                }

                // see if one of the knownTypes matches the current paramClass
                String paramClassStr = paramClass.toString().substring(paramClass.toString().lastIndexOf(" ") + 1);
                String paramClassStrEnd = paramClass.toString().substring(paramClass.toString().lastIndexOf(".") + 1);
                for (String knowType: knownTypes) {
                    if (knowType.compareTo(paramClassStrEnd) == 0) {
                        matchReturn.matches++;
                        break;
                    }

                    // special case for String to Class or String to View conversion
                    // some functions want a class and we'll treat String and Class as equivalent if the string exists as a class
                    if ((paramClassStr.contains(Constants.ARGUMENT_TYPE_CLASS)
                            || paramClassStr.contains(Constants.ARGUMENT_TYPE_VIEW)
                            || paramClassStr.contains(Constants.ARGUMENT_TYPE_WIDGET)) && knowType.equals(Constants.ARGUMENT_TYPE_STRING)) {

                        // see if there is a class that represents this value
                        try {
                            Class findC = Class.forName((String)args[matchReturn.matches]);
                            matchReturn.arguments[matchReturn.matches] = findC;
                            matchReturn.convertedArguments = true;
                            matchReturn.matches++;
                            break;
                        } catch (Exception e) {

                        }

                        // try to find a view instead
                        // not all frameworks will support this and getView may return null in those cases
                        View viewFinder = getView((String)args[matchReturn.matches]);
                        if (viewFinder != null) {
                            matchReturn.arguments[matchReturn.matches] = viewFinder;
                            matchReturn.convertedArguments = true;
                            matchReturn.matches++;
                            break;
                        }
                    }

                }

                // last ditch effort.. this param type might match our previous process result
                // we also bail if the amount of matches already matches the amount of parameters we have evaluated
                if (lastResponseObject == null || matchReturn.matches == x)
                    continue;

                // If the last response type matches the current method param type then use it
                if (lastResponseObject.getClass().toString().startsWith(paramClass.toString())) {
                    matchReturn.arguments[matchReturn.matches] = lastResponseObject;
                    matchReturn.convertedArguments = true;
                    matchReturn.matches++;
                }
            }

            // set matchSucceeded if matches == # of parameters
            if (matchReturn.matches == paramTypesToMatch.length) {
                matchReturn.matchSucceeded = true;
            }

            return matchReturn;
        }

        /**
         * Instantiate a class based on a found class and list of arguments
         * @param c
         * @return
         */
        private Object instantiateClass(Class c, Object[] args) {
            Object instantiatedClass = null;
            Constructor constructorToInstantiate = null;
            Object[] argsToPass = new Object[args.length];
            try {
                // if there are no args just try the default constructor.. otherwise search for one
                if (args.length == 0) {
                    instantiatedClass = c.newInstance();
                } else {
                    for (Constructor constructor : c.getDeclaredConstructors()) {
                        if (constructor.getParameterTypes().length == args.length) {
                            MatchAndConvert matchedData = matchAndConvertArguments(constructor.getParameterTypes(), args);
                            if (constructorToInstantiate == null ||
                                    (constructorToInstantiate != null && !matchedData.convertedArguments)
                                    ) {
                                // replace args with the temp args array incase we converted any arguments
                                argsToPass = matchedData.arguments;
                                constructorToInstantiate = constructor;
                            }
                        }
                    }

                    if (constructorToInstantiate != null) {
                        instantiatedClass = constructorToInstantiate.newInstance(argsToPass);
                    }
                }
            } catch (Exception ee) {
                System.out.println("instantiateClass: " + ee.getMessage());
            }

            return instantiatedClass;
        }

        /**
         * Run a method on the current class object with the specified arguments list
         * @param classObject
         * @param methodName
         * @param args
         * @return
         * @throws Exception
         */
        private ArbitraryItemStruct runArbitraryMethod(Object classObject, String methodName, Object[] args) throws Exception {
            ArbitraryItemStruct methodResults = new ArbitraryItemStruct();

            // declare an array for the final arg list
            Object[] argsToPass = new Object[args.length];

            // find the method
            Method m = null;
            Method[] methods = classObject.getClass().getMethods();

            // if this was not an instantiated class then we actually want to get methods from the base object
            if (classObject.getClass().toString().contains(Constants.CLASS_TYPE_STATIC)) {
                methods = ((Class)classObject).getMethods();
            }

            // loop through all the methods and try to manually match the signature
            // based on the method name and argument type equivalents
            for (Method method: methods) {
                // try to match up the name, # args and method signature
                if (method.getName().equals(methodName) && method.getParameterTypes().length == args.length) {
                    MatchAndConvert matchedData = matchAndConvertArguments(method.getParameterTypes(), args);

                    if (matchedData.matches == method.getParameterTypes().length) {
                        // the idea here is to find the best match
                        // the ideal match is one where we didn't do any argument conversion
                        // TODO: this still needs to be smarter

                        // for now do this if:
                        // 1. We don't have a match already(m == null)
                        // 2. We have a match, but the newer match didn't require argument conversion
                        if (m == null ||
                                (m != null && !matchedData.convertedArguments)
                                ) {
                            // replace args with the temp args array incase we converted any arguments
                            argsToPass = matchedData.arguments;
                            m = method;
                        }
                    }
                }
            }

            // run the method if one was found
            // otherwise throw an exception
            if (m == null) {
                throw new Exception("Could not find method");
            } else {
                // check return type for the method
                String returnType = m.getReturnType().toString();
                methodResults.setReturnType(returnType);

                if (! returnType.toLowerCase().contains(Constants.RETURN_TYPE_VOID)) {
                    Object retData = m.invoke(classObject, argsToPass);
                    methodResults.setReturnVal(retData);
                } else {
                    m.invoke(classObject, argsToPass);
                }
            }


            return methodResults;
        }

        private JSONObject processPost(String uri, Properties params) {
            JSONObject returnVal = new JSONObject();
            try {
                if (uri.equalsIgnoreCase(Constants.REQUEST_MAP)) {
                    JSONObject request = new JSONObject(params.getProperty(Constants.REQUEST));

                    // see if the request has an "operations" array
                    JSONArray operations = null;
                    if (request.has(Constants.REQUEST_OPERATIONS)) {
                        operations = request.getJSONArray(Constants.REQUEST_OPERATIONS);
                    } else {
                        // let's push the request onto the operations array
                        operations = new JSONArray("[" + request.toString() + "]");
                        //operations.put(request);
                    }

                    returnVal = processOperations(operations);
                } else {
                    // not sure what to do yet
                }
            } catch (Exception e) {
                System.out.println("processPost:: POST failed: " + e.getMessage());
            }

            System.out.println("processPost:: Return value: " + returnVal);
            return returnVal;
        }

        private String processGet(String uri, Properties params) {
            String msg = "";
            JSONObject returnObject = new JSONObject();

            try {
                if (uri.equalsIgnoreCase(Constants.REQUEST_HEARTBEAT)) {
                    returnObject.put(Constants.RESULT_OUTCOME, Constants.RESULT_SUCCESS);
                } else {
                    returnObject.put(Constants.RESULT_OUTCOME, Constants.RESULT_FAILED);
                }
            } catch (Exception e) {
                e.getMessage();
            }

            return returnObject.toString();
        }

        /**
         * Returns an array of "equivalent" object types for a specified type
         * Ex: Integer, int, Long, long, Float, float are all considered to be the same for function matching
         * @param type
         * @return
         * @throws Exception
         */
        private String[] getTypeEquivalents(String type) throws Exception {
            // Build equivalence table
            String[] StringArray = {"String"};
            String[] IntegerArray = {"Integer", "int", "Long", "long", "Float", "float"};
            String[] BooleanArray = {"Boolean", "boolean"};
            HashMap<String, String[]> typeHash = new HashMap<String, String[]>();
            typeHash.put("String", StringArray);
            typeHash.put("Integer", IntegerArray);
            typeHash.put("Boolean", BooleanArray);

            for (String key : typeHash.keySet()) {
                String[] typeArray = typeHash.get(key);
                for (String entry : typeArray) {
                    if (entry.equals(type)) {
                        return typeArray;
                    }
                }
            }

            return null;
        }

        /**
         * Struct to hold return values from executing an operation
         */
        private class ArbitraryItemStruct
        {
            // would use appropriate names
            private Object _returnVal;
            private Object _field;
            private String _returnType;

            public ArbitraryItemStruct()
            {
                _returnVal    = null;
                _returnType = null;
                _field = null;
            }

            public void setReturnVal(Object returnVal) {
                _returnVal = returnVal;
            }

            public void setReturnType(String returnType) {
                _returnType = returnType;
            }

            public void setField(Object field) {
                _field = field;
            }

            public Object getReturnVal()
            {
                return (_returnVal);
            }

            public String getReturnType()
            {
                return (_returnType);
            }

            public Object getField()
            {
                return (_field);
            }
        }
    }
}




Java Source Code List

com.groupon.roboremote.Constants.java
com.groupon.roboremote.example.helloworld.HelloWorld.java
com.groupon.roboremote.example.helloworld.TestActivity.java
com.groupon.roboremote.example.helloworld.WebviewActivity.java
com.groupon.roboremote.example.helloworld.support.TestableWebView.java
com.groupon.roboremote.example.helloworldtestrunner.Runner.java
com.groupon.roboremote.roboremoteclient.Client.java
com.groupon.roboremote.roboremoteclient.Constants.java
com.groupon.roboremote.roboremoteclient.PortSingleton.java
com.groupon.roboremote.roboremoteclient.QueryBuilder.java
com.groupon.roboremote.roboremoteclient.Solo.java
com.groupon.roboremote.roboremoteclient.TestBase.java
com.groupon.roboremote.roboremoteclient.components.Button.java
com.groupon.roboremote.roboremoteclient.components.ListView.java
com.groupon.roboremote.roboremoteclient.components.Screen.java
com.groupon.roboremote.roboremoteclient.components.Text.java
com.groupon.roboremote.roboremoteclient.junit.TestBase.java
com.groupon.roboremote.roboremoteclientcommon.Client.java
com.groupon.roboremote.roboremoteclientcommon.Constants.java
com.groupon.roboremote.roboremoteclientcommon.DebugBridge.java
com.groupon.roboremote.roboremoteclientcommon.Device.java
com.groupon.roboremote.roboremoteclientcommon.LogbackAppender.java
com.groupon.roboremote.roboremoteclientcommon.QueryBuilder.java
com.groupon.roboremote.roboremoteclientcommon.Utils.java
com.groupon.roboremote.roboremoteclientcommon.http.Get.java
com.groupon.roboremote.roboremoteclientcommon.http.Post.java
com.groupon.roboremote.roboremoteclientcommon.logging.EmSingleton.java
com.groupon.roboremote.roboremoteclientcommon.logging.EventManager.java
com.groupon.roboremote.roboremoteclientcommon.logging.LogEvent.java
com.groupon.roboremote.roboremoteclientcommon.logging.LogcatLogger.java
com.groupon.roboremote.roboremoteclientcommon.logging.TestLogger.java
com.groupon.roboremote.roboremoteserver.Commands.java
com.groupon.roboremote.roboremoteserver.Constants.java
com.groupon.roboremote.roboremoteserver.RemoteTestRunner.java
com.groupon.roboremote.roboremoteserver.RemoteTest.java
com.groupon.roboremote.roboremoteserver.RoboRemoteServer.java
com.groupon.roboremote.roboremoteserver.robotium.Solo2.java
com.groupon.roboremote.roboremoteserver.robotium.SoloSingleton.java
com.groupon.roboremote.roboremoteservercommon.Constants.java
com.groupon.roboremote.roboremoteservercommon.NanoHTTPD.java
com.groupon.roboremote.roboremoteservercommon.RemoteServer.java
com.groupon.roboremote.uiautomatorclient.Client.java
com.groupon.roboremote.uiautomatorclient.Constants.java
com.groupon.roboremote.uiautomatorclient.PortSingleton.java
com.groupon.roboremote.uiautomatorclient.QueryBuilder.java
com.groupon.roboremote.uiautomatorclient.TestBase.java
com.groupon.roboremote.uiautomatorclient.components.BaseObject.java
com.groupon.roboremote.uiautomatorclient.components.Notification.java
com.groupon.roboremote.uiautomatorclient.components.UiCollection.java
com.groupon.roboremote.uiautomatorclient.components.UiDevice.java
com.groupon.roboremote.uiautomatorclient.components.UiObject.java
com.groupon.roboremote.uiautomatorclient.components.UiScrollable.java
com.groupon.roboremote.uiautomatorclient.components.UiSelector.java
com.groupon.roboremote.uiautomatorserver.Constants.java
com.groupon.roboremote.uiautomatorserver.RemoteTest.java
com.groupon.roboremote.uiautomatorserver.UiAutomatorServer.java