All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.selendroid.server.model.internal.AbstractNativeElementContext Maven / Gradle / Ivy

There is a newer version: 0.17.0
Show newest version
/*
 * Copyright 2012-2014 eBay Software Foundation and selendroid committers.
 * 
 * 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 io.selendroid.server.model.internal;

import android.app.Activity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.android.internal.util.Predicate;
import io.selendroid.ServerInstrumentation;
import io.selendroid.android.KeySender;
import io.selendroid.android.ViewHierarchyAnalyzer;
import io.selendroid.exceptions.NoSuchElementException;
import io.selendroid.exceptions.SelendroidException;
import io.selendroid.exceptions.UnsupportedOperationException;
import io.selendroid.server.model.AndroidElement;
import io.selendroid.server.model.AndroidNativeElement;
import io.selendroid.server.model.AndroidRElement;
import io.selendroid.server.model.By;
import io.selendroid.server.model.By.ByClass;
import io.selendroid.server.model.By.ById;
import io.selendroid.server.model.By.ByLinkText;
import io.selendroid.server.model.By.ByName;
import io.selendroid.server.model.By.ByTagName;
import io.selendroid.server.model.By.ByXPath;
import io.selendroid.server.model.KnownElements;
import io.selendroid.server.model.SearchContext;
import io.selendroid.util.InstanceOfPredicate;
import io.selendroid.util.ListUtil;
import io.selendroid.util.Preconditions;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;


public abstract class AbstractNativeElementContext
    implements
      SearchContext,
      FindsByTagName,
      FindsByName,
      FindsById,
      FindsByText,
      FindsByXPath,
      FindsByPartialText,
      FindsByClass {
  protected ServerInstrumentation instrumentation;
  protected KeySender keys;
  protected KnownElements knownElements;
  protected ViewHierarchyAnalyzer viewAnalyzer;

  public AbstractNativeElementContext(ServerInstrumentation instrumentation, KeySender keys, KnownElements knownElements) {
    this.instrumentation = Preconditions.checkNotNull(instrumentation);
    this.keys = Preconditions.checkNotNull(keys);
    this.knownElements = Preconditions.checkNotNull(knownElements);
    this.viewAnalyzer = ViewHierarchyAnalyzer.getDefaultInstance();
  }

  AndroidNativeElement newAndroidElement(View view) {
    Preconditions.checkNotNull(view);
    if (knownElements.hasElement(new Long(view.getId()))) {
      AndroidNativeElement element =
          (AndroidNativeElement) knownElements.get(new Long(view.getId()));
      // Caution: this is needed because e.g.
      // in spinner lists the items have by default all the same id
      if (element.getView().equals(view)) {
        return element;
      }
    }

    AndroidNativeElement e = new AndroidNativeElement(view, instrumentation, keys, knownElements);
    knownElements.add(e);
    return e;
  }

  AndroidElement newAndroidElement(int id) {
    if (id < 0) {
      return null;
    }
    String idString = Integer.toString(id);
    if (knownElements.hasElement(idString)) {
      return knownElements.get(idString);
    }
    AndroidElement e = new AndroidRElement(id);
    knownElements.add(e);
    return e;

  }

  public JSONObject getElementTree() throws JSONException {
    View decorView = viewAnalyzer.getRecentDecorView();
    if (decorView == null) {
      throw new SelendroidException("No open windows.");
    }
    AndroidNativeElement rootElement = newAndroidElement(decorView);

    if (decorView instanceof ViewGroup) {
      addChildren((ViewGroup) decorView, rootElement);
    }

    JSONObject root = rootElement.toJson();
    if (root == null) {
      return new JSONObject();
    }
    root.put("activity", instrumentation.getCurrentActivity().getComponentName().toShortString());

    addChildren(root, rootElement);

    return root;
  }

  private void addChildren(JSONObject parent, AndroidElement parentElement) throws JSONException {
    Collection children = parentElement.getChildren();
    if (children == null || children.isEmpty()) {
      return;
    }
    JSONArray childs = new JSONArray();
    for (AndroidElement child : children) {
      if (((AndroidNativeElement) child).getView() != ((AndroidNativeElement) parentElement)
          .getView()) {
        JSONObject jsonChild = ((AndroidNativeElement) child).toJson();
        childs.put(jsonChild);

        addChildren(jsonChild, child);
      }
    }
    parent.put("children", childs);
  }

  private void addChildren(ViewGroup viewGroup, AndroidNativeElement parent) {
    if (viewGroup.getChildCount() == 0) {
      return;
    }
    for (int i = 0; i < viewGroup.getChildCount(); i++) {
      View childView = viewGroup.getChildAt(i);
      AndroidNativeElement child = newAndroidElement(childView);

      parent.addChild(child);
      child.setParent(parent);
      if (childView instanceof ViewGroup) {
        addChildren((ViewGroup) childView, child);
      }
    }
  }

  @Override
  public List findElements(By by) {
    if (by instanceof ById) {
      return findElementsById(by.getElementLocator());
    } else if (by instanceof ByTagName) {
      return findElementsByTagName(by.getElementLocator());
    } else if (by instanceof ByLinkText) {
      return findElementsByText(by.getElementLocator());
    } else if (by instanceof By.ByPartialLinkText) {
      return findElementsByPartialText(by.getElementLocator());
    } else if (by instanceof ByClass) {
      return findElementsByClass(by.getElementLocator());
    } else if (by instanceof ByName) {
      return findElementsByName(by.getElementLocator());
    } else if (by instanceof ByXPath) {
      return findElementsByXPath(by.getElementLocator());
    }

    throw new UnsupportedOperationException(String.format(
        "By locator %s is curently not supported!", by.getClass().getSimpleName()));
  }

  @Override
  public AndroidElement findElement(By by) {
    if (by instanceof ById) {
      return findElementById(by.getElementLocator());
    } else if (by instanceof ByTagName) {
      return findElementByTagName(by.getElementLocator());
    } else if (by instanceof ByLinkText) {
      return findElementByText(by.getElementLocator());
    } else if (by instanceof By.ByPartialLinkText) {
      return findElementByPartialText(by.getElementLocator());
    } else if (by instanceof ByClass) {
      return findElementByClass(by.getElementLocator());
    } else if (by instanceof ByName) {
      return findElementByName(by.getElementLocator());
    } else if (by instanceof ByXPath) {
      return findElementByXPath(by.getElementLocator());
    }
    throw new UnsupportedOperationException(String.format(
        "By locator %s is curently not supported!", by.getClass().getSimpleName()));
  }


  public AndroidElement findElementByXPath(String using) {
    List elements = findElementsByXPath(using);
    if (!elements.isEmpty()) {
      return elements.get(0);
    }
    return null;
  }

  public List findElementsByXPath(String expression) {
    JSONObject root = null;
    try {
      root = getElementTree();
    } catch (JSONException e1) {
      // TODO Auto-generated catch block
      e1.printStackTrace();
    }

    Document xmlDocument = JsonXmlUtil.buildXmlDocument(root);
    XPath xPath = XPathFactory.newInstance().newXPath();

    NodeList nodeList = null;
    try {
      // read a nodelist using xpath
      nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);
    } catch (XPathExpressionException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    List elements = new ArrayList();
    if (nodeList != null && nodeList.getLength() > 0) {
      for (int i = 0; i < nodeList.getLength(); i++) {
        Node node = nodeList.item(i);
        if (node.getAttributes() == null) {
          continue;
        }
        Node namedItem = node.getAttributes().getNamedItem("ref");
        if (namedItem != null) {
          elements.add(knownElements.get(namedItem.getTextContent()));
        }
      }
    }

    return elements;
  }

  @Override
  public AndroidElement findElementById(String using) {
    List elements = findElementsById(using, true);
    if (!elements.isEmpty()) {
      return elements.get(0);
    }
    return null;
  }

  @Override
  public List findElementsById(String using) {
    return findElementsById(using, false);
  }

  private List findElementsById(String using, Boolean findJustOne) {
    List elements = new ArrayList();
    for (View view : viewAnalyzer.getViews(getTopLevelViews())) {
      String id = ViewHierarchyAnalyzer.getNativeId(view);
      if (id.equalsIgnoreCase("id/" + using) && viewAnalyzer.isViewChieldOfCurrentRootView(view)) {
        elements.add(newAndroidElement(view));
        if (findJustOne) return elements;
      }
    }
    if (elements.isEmpty()) {
      // didn't find any in the views, check the current activity
      // haven't seen this happen, just covering my basis / preserving some previous code.
      Activity currentActivity = instrumentation.getCurrentActivity();
      if (currentActivity != null) {
        int intId =
            currentActivity.getResources().getIdentifier(using, "id",
                currentActivity.getPackageName());
        if (intId > 0) {
          View view = currentActivity.findViewById(intId);

          if (viewAnalyzer.isViewChieldOfCurrentRootView(view)) {
            elements.add(newAndroidElement(view));
          }
        }
      }
    }

    return elements;
  }

  @Override
  public AndroidElement findElementByName(String using) {
    List list = findElementsByName(using);

    if (list != null && !list.isEmpty()) {
      return list.get(0);
    }
    return null;
  }

  @Override
  public List findElementsByName(String using) {
    Collection currentViews = viewAnalyzer.getViews(getTopLevelViews());
    Predicate predicate = new ViewContentDescriptionPredicate(using);
    return filterAndTransformElements(currentViews, predicate);
  }

  class ViewContentDescriptionPredicate implements Predicate {
    private String contenDescription = null;

    ViewContentDescriptionPredicate(String contenDescription) {
      this.contenDescription = contenDescription;
    }

    public boolean apply(View view) {
      return contenDescription.equals(view.getContentDescription());
    }
  }

  class ViewTextPredicate implements Predicate {
    private String text = null;

    ViewTextPredicate(String text) {
      this.text = text;
    }

    public boolean apply(View view) {
      if (view instanceof TextView) {
        String textFieldText = String.valueOf(((TextView) view).getText());
        boolean textEqual = text.equals(textFieldText);
        return textEqual;
      }
      return false;
    }
  }

  class ViewPartialTextPredicate implements Predicate {
    private String text = null;

    ViewPartialTextPredicate(String text) {
      this.text = text;
      System.out.println("Finding by partial text: " + text);
    }

    public boolean apply(View view) {
      if (view instanceof TextView) {
        String viewText = ((TextView) view).getText().toString();

        return viewText.indexOf(text) >= 0;
      }
      return false;
    }
  }

  class ViewTagNamePredicate implements Predicate {
    private String tag;

    ViewTagNamePredicate(String tag) {
      this.tag = tag;
    }

    public boolean apply(View view) {
      return view.getClass().getSimpleName().equals(tag);
    }
  }

  @Override
  public AndroidElement findElementByTagName(String using) {
    List elements = findElementsByTagName(using);
    if (elements != null && elements.size() > 0) {
      return elements.get(0);
    }
    return null;
  }

  @Override
  public List findElementsByTagName(String using) {
    Collection currentViews = viewAnalyzer.getViews(getTopLevelViews());
    Predicate predicate = new ViewTagNamePredicate(using);
    return filterAndTransformElements(currentViews, predicate);
  }

  @Override
  public AndroidElement findElementByText(String using) {
    List list = findElementsByText(using);

    if (list != null && !list.isEmpty()) {
      return list.get(0);
    }
    return null;
  }

  @Override
  public List findElementsByText(String using) {
    Collection currentViews = viewAnalyzer.getViews(getTopLevelViews());
    Predicate predicate = new ViewTextPredicate(using);
    return filterAndTransformElements(currentViews, predicate);
  }

  @Override
  public AndroidElement findElementByPartialText(String using) {
    List list = findElementsByPartialText(using);

    if (list != null && !list.isEmpty()) {
      return list.get(0);
    }
    return null;
  }

  @Override
  public List findElementsByPartialText(String using) {
    Collection currentViews = viewAnalyzer.getViews(getTopLevelViews());
    Predicate predicate = new ViewPartialTextPredicate(using);
    return filterAndTransformElements(currentViews, predicate);
  }

  @Override
  public AndroidElement findElementByClass(String using) {
    List list = findElementsByClass(using);

    if (list != null && !list.isEmpty()) {
      return list.get(0);
    }
    return null;
  }

  @Override
  public List findElementsByClass(String using) {
    Collection currentViews = viewAnalyzer.getViews(getTopLevelViews());
    Class viewClass = null;
    try {
      viewClass = Class.forName(using);
    } catch (ClassNotFoundException e) {
      throw new NoSuchElementException("The view class '" + using + "' was not found.");
    }
    return filterAndTransformElements(currentViews, new InstanceOfPredicate(viewClass));
  }

  private List filterAndTransformElements(Collection currentViews,
      Predicate predicate) {
    Collection filteredViews = ListUtil.filter(currentViews, predicate);
    final List filtered = new ArrayList();
    for (Object v : filteredViews) {
      filtered.add(newAndroidElement((View) v));
    }

    return filtered;
  }

  protected abstract View getRootView();

  protected abstract List getTopLevelViews();
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy