package sh.calaba.instrumentationbackend.query.ast; import static sh.calaba.instrumentationbackend.InstrumentationBackend.viewFetcher; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import org.antlr.runtime.tree.CommonTree; import sh.calaba.instrumentationbackend.InstrumentationBackend; import sh.calaba.instrumentationbackend.actions.webview.QueryHelper; import sh.calaba.instrumentationbackend.query.CompletedFuture; import sh.calaba.instrumentationbackend.query.antlr.UIQueryParser; import sh.calaba.org.codehaus.jackson.map.ObjectMapper; import sh.calaba.org.codehaus.jackson.type.TypeReference; import android.content.res.Resources.NotFoundException; import android.view.View; import android.webkit.WebView; public class UIQueryUtils { @SuppressWarnings({ "unchecked", "rawtypes" }) public static List subviews(Object o) { try { Method getChild = o.getClass().getMethod("getChildAt", int.class); getChild.setAccessible(true); Method getChildCount = o.getClass().getMethod("getChildCount"); getChildCount.setAccessible(true); List result = new ArrayList(8); int childCount = (Integer) getChildCount.invoke(o); for (int i=0;i<childCount;i++) { result.add(getChild.invoke(o, i)); } return result; } catch (NoSuchMethodException e) { return Collections.EMPTY_LIST; } catch (IllegalArgumentException e) { return Collections.EMPTY_LIST; } catch (IllegalAccessException e) { return Collections.EMPTY_LIST; } catch (InvocationTargetException e) { return Collections.EMPTY_LIST; } } @SuppressWarnings({ "unchecked", "rawtypes" }) public static List parents(Object o) { try { Method getParent = o.getClass().getMethod("getParent"); getParent.setAccessible(true); List result = new ArrayList(8); try { while (true) { Object parent = getParent.invoke(o); if (parent == null) { return result; } else { result.add(parent); } o = parent; } } catch (IllegalArgumentException e) { return result; } catch (IllegalAccessException e) { return result; } catch (InvocationTargetException e) { return result; } } catch (NoSuchMethodException e) { return Collections.EMPTY_LIST; } } @SuppressWarnings({ "rawtypes" }) public static Method hasProperty(Object o, String propertyName) { Class c = o.getClass(); Method method = methodOrNull(c,propertyName); if (method != null) { return method;} method = methodOrNull(c,"get"+captitalize(propertyName)); if (method != null) { return method;} method = methodOrNull(c,"is"+captitalize(propertyName)); return method; /* for (Method m : methods) { String methodName = m.getName(); if (methodName.equals(propertyName) || methodName.equals("is"+captitalize(propertyName)) || methodName.equals("get"+captitalize(propertyName))) { return m; } } */ } @SuppressWarnings({ "unchecked", "rawtypes" }) private static Method methodOrNull(Class c, String methodName) { try { return c.getMethod(methodName); } catch (NoSuchMethodException e) { return null; } } private static String captitalize(String propertyName) { return propertyName.substring(0,1).toUpperCase() + propertyName.substring(1); } public static Object getProperty(Object receiver, Method m) { try { return m.invoke(receiver); } catch (IllegalArgumentException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } } public static boolean isVisible(Object v) { if (!(v instanceof View)) { return true; } View view = (View) v; if (view.getWidth() == 0 || view.getWidth() == 0) { return false; } return view.isShown() && viewFetcher.isViewSufficientlyShown(view); } public static String getId(View view) { try { return InstrumentationBackend.solo.getCurrentActivity() .getResources().getResourceEntryName(view.getId()); } catch (NotFoundException e) {} return null; } @SuppressWarnings("rawtypes") public static Future evaluateAsyncInMainThread(final Callable callable) throws Exception { final AtomicReference<Future> result = new AtomicReference<Future>(); final AtomicReference<Exception> errorResult = new AtomicReference<Exception>(); InstrumentationBackend.instrumentation.runOnMainSync(new Runnable() { @SuppressWarnings("unchecked") public void run() { try { Object res = callable.call(); if (res instanceof Future) { result.set((Future) res); } else { result.set(new CompletedFuture(res)); } } catch (Exception e) { errorResult.set(e); } } }); if (result.get() == null) { throw errorResult.get(); } return result.get(); } @SuppressWarnings("rawtypes") public static Object evaluateSyncInMainThread(Callable callable) { try { return evaluateAsyncInMainThread(callable).get(10, TimeUnit.SECONDS); } catch (RuntimeException e) { throw e; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } @SuppressWarnings({ "unchecked", "rawtypes" }) public static List<Map<String, Object>> mapWebViewJsonResponse(final String jsonResponse, final WebView webView) { return (List<Map<String, Object>>) evaluateSyncInMainThread(new Callable() { @Override public Object call() throws Exception { List<Map<String, Object>> parsedResult; try { parsedResult = new ObjectMapper().readValue( jsonResponse, new TypeReference<List<HashMap<String, Object>>>() {}); for (Map<String,Object> data : parsedResult) { Map<String,Object> rect = (Map<String, Object>) data.get("rect"); Map <String,Object> updatedRect = QueryHelper.translateRectToScreenCoordinates(webView, rect); data.put("rect", updatedRect); data.put("webView", webView); } return parsedResult; } catch (Exception igored) { try { Map resultAsMap = new ObjectMapper().readValue( jsonResponse, new TypeReference<HashMap>() {}); //This usually happens in case of error //check this case System.out.println(resultAsMap); String errorMsg = (String) resultAsMap.get("error"); System.out.println(errorMsg); return Collections.singletonList(resultAsMap); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } } }); } public static Object parseValue(CommonTree val) { switch (val.getType()) { case UIQueryParser.STRING: { String textWithPings = val.getText(); String text = textWithPings .substring(1, textWithPings.length() - 1); return text; } case UIQueryParser.INT: return Integer.parseInt(val.getText(), 10); case UIQueryParser.BOOL: { String text = val.getText(); return Boolean.parseBoolean(text); } case UIQueryParser.NIL: return null; default: throw new IllegalArgumentException("Unable to parse value type:" + val.getType() + " text " + val.getText()); } } }