diff --git a/lib/devices/android/android-controller.js b/lib/devices/android/android-controller.js index 3a9324b4054..7a5942e55e3 100644 --- a/lib/devices/android/android-controller.js +++ b/lib/devices/android/android-controller.js @@ -94,13 +94,7 @@ androidController.findElementsFromElement = function (element, strategy, selecto }; var _pathFromDomNode = function (node) { - var path = ""; - _.each(node.attributes, function (attrObj) { - if (attrObj.name === "index") { - path = _pathFromDomNode(node.parentNode) + "/" + attrObj.value; - } - }); - return path; + return node.getAttribute("class") + ':' + node.getAttribute("instance"); }; androidController.findUIElementsByXPath = function (selector, many, cb) { @@ -308,7 +302,7 @@ var _updateSourceXMLNodeNames = function (source) { var newSource; var origDom = new XMLDom.DOMParser().parseFromString(source); var newDom = new XMLDom.DOMImplementation().createDocument(null); - _buildClassNodeFromPlainNode(newDom, newDom, origDom); + _annotateXmlNodes(newDom, newDom, origDom); newSource = new XMLDom.XMLSerializer().serializeToString(newDom); return newSource; }; @@ -329,19 +323,29 @@ var _copyNodeAttributes = function (oldNode, newNode) { }); }; -var _buildClassNodeFromPlainNode = function (newDom, newParent, oldNode) { +// recursively annotate xml nodes. Update tag name to be Android UIElement class name. Add an "instance" identifier which increments for each class separately. +var _annotateXmlNodes = function (newDom, newParent, oldNode, instances) { + if (!instances) { + instances = {}; + } var newNode; var nodeClass = _getNodeClass(oldNode); if (nodeClass) { newNode = newDom.createElement(nodeClass); _copyNodeAttributes(oldNode, newNode); + + // we keep track of the number of instances of each className. We use these to create queries on the bootstrap side. + if (!instances[nodeClass]) { + instances[nodeClass] = 0; + } + newNode.setAttribute('instance', instances[nodeClass]++); } else { newNode = oldNode.cloneNode(false); } newParent.appendChild(newNode); if (oldNode.hasChildNodes()) { _.each(oldNode.childNodes, function (childNode) { - _buildClassNodeFromPlainNode(newDom, newNode, childNode); + _annotateXmlNodes(newDom, newNode, childNode, instances); }); } }; diff --git a/lib/devices/android/bootstrap/src/io/appium/android/bootstrap/handler/Find.java b/lib/devices/android/bootstrap/src/io/appium/android/bootstrap/handler/Find.java index ed8b36c5409..e17a3665d0f 100644 --- a/lib/devices/android/bootstrap/src/io/appium/android/bootstrap/handler/Find.java +++ b/lib/devices/android/bootstrap/src/io/appium/android/bootstrap/handler/Find.java @@ -279,20 +279,16 @@ private JSONObject fetchElement(final UiSelector sel, final String contextId) * @throws ElementNotFoundException * @throws JSONException */ - private JSONObject fetchElementByIndexPath(final String indexPath) + private JSONObject fetchElementByClassAndInstance(final String indexPath) throws ElementNotFoundException, JSONException { - UiSelector sel = new UiSelector().index(0); - Integer curIndex; - List paths = Arrays.asList(indexPath.split("/")); - // throw away the first element since it will be empty, and the second - // element, since it will refer to the root element, which we already have - paths = paths.subList(2, paths.size()); - for (final String index : paths) { - curIndex = Integer.valueOf(index); - // get a new selector which selects the current selector's child at the - // correct index - sel = sel.childSelector(new UiSelector().index(curIndex)); - } + + // path looks like "className:instanceNumber" eg: "android.widget.Button:2" + String[] classInstancePair = indexPath.split(":"); + String androidClass = classInstancePair[0]; + String instance = classInstancePair[1]; + + UiSelector sel = new UiSelector().className(androidClass).instance(Integer.parseInt(instance)); + return fetchElement(sel, ""); } @@ -342,7 +338,7 @@ private AndroidCommandResult findElementsByIndexPaths(final String selector, JSONObject resEl = new JSONObject(); for (final String indexPath : indexPaths) { try { - resEl = fetchElementByIndexPath(indexPath); + resEl = fetchElementByClassAndInstance(indexPath); resArray.put(resEl); } catch (final JSONException e) { return new AndroidCommandResult(WDStatus.UNKNOWN_ERROR, e.getMessage());