public class

UiSelector

extends java.lang.Object

 java.lang.Object

↳androidx.test.uiautomator.UiSelector

Gradle dependencies

compile group: 'androidx.test.uiautomator', name: 'uiautomator', version: '2.2.0'

  • groupId: androidx.test.uiautomator
  • artifactId: uiautomator
  • version: 2.2.0

Artifact androidx.test.uiautomator:uiautomator:2.2.0 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.test.uiautomator:uiautomator com.android.support.test.uiautomator:uiautomator

Androidx class mapping:

androidx.test.uiautomator.UiSelector android.support.test.uiautomator.UiSelector

Overview

Specifies the elements in the layout hierarchy for tests to target, filtered by properties such as text value, content-description, class name, and state information. You can also target an element by its location in a layout hierarchy.

Summary

Constructors
publicUiSelector()

Methods
public UiSelectorcheckable(boolean val)

Set the search criteria to match widgets that are checkable.

public UiSelectorchecked(boolean val)

Set the search criteria to match widgets that are currently checked (usually for checkboxes).

public UiSelectorchildSelector(UiSelector selector)

Adds a child UiSelector criteria to this selector.

public UiSelectorclassName(java.lang.Class<java.lang.Object> type)

Set the search criteria to match the class property for a widget (for example, "android.widget.Button").

public UiSelectorclassName(java.lang.String className)

Set the search criteria to match the class property for a widget (for example, "android.widget.Button").

public UiSelectorclassNameMatches(java.lang.String regex)

Set the search criteria to match the class property for a widget, using a regular expression.

public UiSelectorclickable(boolean val)

Set the search criteria to match widgets that are clickable.

protected UiSelectorcloneSelector()

public UiSelectordescription(java.lang.String desc)

Set the search criteria to match the content-description property for a widget.

public UiSelectordescriptionContains(java.lang.String desc)

Set the search criteria to match the content-description property for a widget.

public UiSelectordescriptionMatches(java.lang.String regex)

Set the search criteria to match the content-description property for a widget.

public UiSelectordescriptionStartsWith(java.lang.String desc)

Set the search criteria to match the content-description property for a widget.

public UiSelectorenabled(boolean val)

Set the search criteria to match widgets that are enabled.

public UiSelectorfocusable(boolean val)

Set the search criteria to match widgets that are focusable.

public UiSelectorfocused(boolean val)

Set the search criteria to match widgets that have focus.

public UiSelectorfromParent(UiSelector selector)

Adds a child UiSelector criteria to this selector which is used to start search from the parent widget.

public UiSelectorindex(int index)

Set the search criteria to match the widget by its node index in the layout hierarchy.

public UiSelectorinstance(int instance)

Set the search criteria to match the widget by its instance number.

public UiSelectorlongClickable(boolean val)

Set the search criteria to match widgets that are long-clickable.

public UiSelectorpackageName(java.lang.String name)

Set the search criteria to match the package name of the application that contains the widget.

public UiSelectorpackageNameMatches(java.lang.String regex)

Set the search criteria to match the package name of the application that contains the widget.

public UiSelectorresourceId(java.lang.String id)

Set the search criteria to match the given resource ID.

public UiSelectorresourceIdMatches(java.lang.String regex)

Set the search criteria to match the resource ID of the widget, using a regular expression.

public UiSelectorscrollable(boolean val)

Set the search criteria to match widgets that are scrollable.

public UiSelectorselected(boolean val)

Set the search criteria to match widgets that are currently selected.

public UiSelectortext(java.lang.String text)

Set the search criteria to match the visible text displayed in a widget (for example, the text label to launch an app).

public UiSelectortextContains(java.lang.String text)

Set the search criteria to match the visible text in a widget where the visible text must contain the string in your input argument.

public UiSelectortextMatches(java.lang.String regex)

Set the search criteria to match the visible text displayed in a layout element, using a regular expression.

public UiSelectortextStartsWith(java.lang.String text)

Set the search criteria to match visible text in a widget that is prefixed by the text parameter.

public java.lang.StringtoString()

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait

Constructors

public UiSelector()

Since: API Level 16

Methods

protected UiSelector cloneSelector()

Since: API Level 17

public UiSelector text(java.lang.String text)

Set the search criteria to match the visible text displayed in a widget (for example, the text label to launch an app). The text for the element must match exactly with the string in your input argument. Matching is case-sensitive.

Parameters:

text: Value to match

Returns:

UiSelector with the specified search criteria

Since: API Level 16

public UiSelector textMatches(java.lang.String regex)

Set the search criteria to match the visible text displayed in a layout element, using a regular expression. The text in the widget must match exactly with the string in your input argument.

Parameters:

regex: a regular expression

Returns:

UiSelector with the specified search criteria

Since: API Level 17

public UiSelector textStartsWith(java.lang.String text)

Set the search criteria to match visible text in a widget that is prefixed by the text parameter. The matching is case-insensitive.

Parameters:

text: Value to match

Returns:

UiSelector with the specified search criteria

Since: API Level 16

public UiSelector textContains(java.lang.String text)

Set the search criteria to match the visible text in a widget where the visible text must contain the string in your input argument. The matching is case-sensitive.

Parameters:

text: Value to match

Returns:

UiSelector with the specified search criteria

Since: API Level 16

public UiSelector className(java.lang.String className)

Set the search criteria to match the class property for a widget (for example, "android.widget.Button").

Parameters:

className: Value to match

Returns:

UiSelector with the specified search criteria

Since: API Level 16

public UiSelector classNameMatches(java.lang.String regex)

Set the search criteria to match the class property for a widget, using a regular expression.

Parameters:

regex: a regular expression

Returns:

UiSelector with the specified search criteria

Since: API Level 17

public UiSelector className(java.lang.Class<java.lang.Object> type)

Set the search criteria to match the class property for a widget (for example, "android.widget.Button").

Parameters:

type: type

Returns:

UiSelector with the specified search criteria

Since: API Level 17

public UiSelector description(java.lang.String desc)

Set the search criteria to match the content-description property for a widget. The content-description is typically used by the Android Accessibility framework to provide an audio prompt for the widget when the widget is selected. The content-description for the widget must match exactly with the string in your input argument. Matching is case-sensitive.

Parameters:

desc: Value to match

Returns:

UiSelector with the specified search criteria

Since: API Level 16

public UiSelector descriptionMatches(java.lang.String regex)

Set the search criteria to match the content-description property for a widget. The content-description is typically used by the Android Accessibility framework to provide an audio prompt for the widget when the widget is selected. The content-description for the widget must match exactly with the string in your input argument.

Parameters:

regex: a regular expression

Returns:

UiSelector with the specified search criteria

Since: API Level 17

public UiSelector descriptionStartsWith(java.lang.String desc)

Set the search criteria to match the content-description property for a widget. The content-description is typically used by the Android Accessibility framework to provide an audio prompt for the widget when the widget is selected. The content-description for the widget must start with the string in your input argument. Matching is case-insensitive.

Parameters:

desc: Value to match

Returns:

UiSelector with the specified search criteria

Since: API Level 16

public UiSelector descriptionContains(java.lang.String desc)

Set the search criteria to match the content-description property for a widget. The content-description is typically used by the Android Accessibility framework to provide an audio prompt for the widget when the widget is selected. The content-description for the widget must contain the string in your input argument. Matching is case-insensitive.

Parameters:

desc: Value to match

Returns:

UiSelector with the specified search criteria

Since: API Level 16

public UiSelector resourceId(java.lang.String id)

Set the search criteria to match the given resource ID.

Parameters:

id: Value to match

Returns:

UiSelector with the specified search criteria

Since: API Level 18

public UiSelector resourceIdMatches(java.lang.String regex)

Set the search criteria to match the resource ID of the widget, using a regular expression.

Parameters:

regex: a regular expression

Returns:

UiSelector with the specified search criteria

Since: API Level 18

public UiSelector index(int index)

Set the search criteria to match the widget by its node index in the layout hierarchy. The index value must be 0 or greater. Using the index can be unreliable and should only be used as a last resort for matching. Instead, consider using the UiSelector.instance(int) method.

Parameters:

index: Value to match

Returns:

UiSelector with the specified search criteria

Since: API Level 16

public UiSelector instance(int instance)

Set the search criteria to match the widget by its instance number. The instance value must be 0 or greater, where the first instance is 0. For example, to simulate a user click on the third image that is enabled in a UI screen, you could specify a a search criteria where the instance is 2, the UiSelector.className(String) matches the image widget class, and UiSelector.enabled(boolean) is true. The code would look like this: new UiSelector().className("android.widget.ImageView") .enabled(true).instance(2);

Parameters:

instance: Value to match

Returns:

UiSelector with the specified search criteria

Since: API Level 16

public UiSelector enabled(boolean val)

Set the search criteria to match widgets that are enabled. Typically, using this search criteria alone is not useful. You should also include additional criteria, such as text, content-description, or the class name for a widget. If no other search criteria is specified, and there is more than one matching widget, the first widget in the tree is selected.

Parameters:

val: Value to match

Returns:

UiSelector with the specified search criteria

Since: API Level 16

public UiSelector focused(boolean val)

Set the search criteria to match widgets that have focus. Typically, using this search criteria alone is not useful. You should also include additional criteria, such as text, content-description, or the class name for a widget. If no other search criteria is specified, and there is more than one matching widget, the first widget in the tree is selected.

Parameters:

val: Value to match

Returns:

UiSelector with the specified search criteria

Since: API Level 16

public UiSelector focusable(boolean val)

Set the search criteria to match widgets that are focusable. Typically, using this search criteria alone is not useful. You should also include additional criteria, such as text, content-description, or the class name for a widget. If no other search criteria is specified, and there is more than one matching widget, the first widget in the tree is selected.

Parameters:

val: Value to match

Returns:

UiSelector with the specified search criteria

Since: API Level 16

public UiSelector scrollable(boolean val)

Set the search criteria to match widgets that are scrollable. Typically, using this search criteria alone is not useful. You should also include additional criteria, such as text, content-description, or the class name for a widget. If no other search criteria is specified, and there is more than one matching widget, the first widget in the tree is selected.

Parameters:

val: Value to match

Returns:

UiSelector with the specified search criteria

Since: API Level 16

public UiSelector selected(boolean val)

Set the search criteria to match widgets that are currently selected. Typically, using this search criteria alone is not useful. You should also include additional criteria, such as text, content-description, or the class name for a widget. If no other search criteria is specified, and there is more than one matching widget, the first widget in the tree is selected.

Parameters:

val: Value to match

Returns:

UiSelector with the specified search criteria

Since: API Level 16

public UiSelector checked(boolean val)

Set the search criteria to match widgets that are currently checked (usually for checkboxes). Typically, using this search criteria alone is not useful. You should also include additional criteria, such as text, content-description, or the class name for a widget. If no other search criteria is specified, and there is more than one matching widget, the first widget in the tree is selected.

Parameters:

val: Value to match

Returns:

UiSelector with the specified search criteria

Since: API Level 16

public UiSelector clickable(boolean val)

Set the search criteria to match widgets that are clickable. Typically, using this search criteria alone is not useful. You should also include additional criteria, such as text, content-description, or the class name for a widget. If no other search criteria is specified, and there is more than one matching widget, the first widget in the tree is selected.

Parameters:

val: Value to match

Returns:

UiSelector with the specified search criteria

Since: API Level 16

public UiSelector checkable(boolean val)

Set the search criteria to match widgets that are checkable. Typically, using this search criteria alone is not useful. You should also include additional criteria, such as text, content-description, or the class name for a widget. If no other search criteria is specified, and there is more than one matching widget, the first widget in the tree is selected.

Parameters:

val: Value to match

Returns:

UiSelector with the specified search criteria

Since: API Level 18

public UiSelector longClickable(boolean val)

Set the search criteria to match widgets that are long-clickable. Typically, using this search criteria alone is not useful. You should also include additional criteria, such as text, content-description, or the class name for a widget. If no other search criteria is specified, and there is more than one matching widget, the first widget in the tree is selected.

Parameters:

val: Value to match

Returns:

UiSelector with the specified search criteria

Since: API Level 17

public UiSelector childSelector(UiSelector selector)

Adds a child UiSelector criteria to this selector. Use this selector to narrow the search scope to child widgets under a specific parent widget.

Parameters:

selector:

Returns:

UiSelector with this added search criterion

Since: API Level 16

public UiSelector fromParent(UiSelector selector)

Adds a child UiSelector criteria to this selector which is used to start search from the parent widget. Use this selector to narrow the search scope to sibling widgets as well all child widgets under a parent.

Parameters:

selector:

Returns:

UiSelector with this added search criterion

Since: API Level 16

public UiSelector packageName(java.lang.String name)

Set the search criteria to match the package name of the application that contains the widget.

Parameters:

name: Value to match

Returns:

UiSelector with the specified search criteria

Since: API Level 16

public UiSelector packageNameMatches(java.lang.String regex)

Set the search criteria to match the package name of the application that contains the widget.

Parameters:

regex: a regular expression

Returns:

UiSelector with the specified search criteria

Since: API Level 17

public java.lang.String toString()

Source

/*
 * Copyright (C) 2012 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 androidx.test.uiautomator;

import android.util.SparseArray;
import android.view.accessibility.AccessibilityNodeInfo;

import java.util.regex.Pattern;

/**
 * Specifies the elements in the layout hierarchy for tests to target, filtered
 * by properties such as text value, content-description, class name, and state
 * information. You can also target an element by its location in a layout
 * hierarchy.
 * @since API Level 16
 */
public class UiSelector {
    static final int SELECTOR_NIL = 0;
    static final int SELECTOR_TEXT = 1;
    static final int SELECTOR_START_TEXT = 2;
    static final int SELECTOR_CONTAINS_TEXT = 3;
    static final int SELECTOR_CLASS = 4;
    static final int SELECTOR_DESCRIPTION = 5;
    static final int SELECTOR_START_DESCRIPTION = 6;
    static final int SELECTOR_CONTAINS_DESCRIPTION = 7;
    static final int SELECTOR_INDEX = 8;
    static final int SELECTOR_INSTANCE = 9;
    static final int SELECTOR_ENABLED = 10;
    static final int SELECTOR_FOCUSED = 11;
    static final int SELECTOR_FOCUSABLE = 12;
    static final int SELECTOR_SCROLLABLE = 13;
    static final int SELECTOR_CLICKABLE = 14;
    static final int SELECTOR_CHECKED = 15;
    static final int SELECTOR_SELECTED = 16;
    static final int SELECTOR_ID = 17;
    static final int SELECTOR_PACKAGE_NAME = 18;
    static final int SELECTOR_CHILD = 19;
    static final int SELECTOR_CONTAINER = 20;
    static final int SELECTOR_PATTERN = 21;
    static final int SELECTOR_PARENT = 22;
    static final int SELECTOR_COUNT = 23;
    static final int SELECTOR_LONG_CLICKABLE = 24;
    static final int SELECTOR_TEXT_REGEX = 25;
    static final int SELECTOR_CLASS_REGEX = 26;
    static final int SELECTOR_DESCRIPTION_REGEX = 27;
    static final int SELECTOR_PACKAGE_NAME_REGEX = 28;
    static final int SELECTOR_RESOURCE_ID = 29;
    static final int SELECTOR_CHECKABLE = 30;
    static final int SELECTOR_RESOURCE_ID_REGEX = 31;

    private SparseArray<Object> mSelectorAttributes = new SparseArray<Object>();

    /**
     * @since API Level 16
     */
    public UiSelector() {
    }

    UiSelector(UiSelector selector) {
        mSelectorAttributes = selector.cloneSelector().mSelectorAttributes;
    }

    /**
     * @since API Level 17
     */
    protected UiSelector cloneSelector() {
        UiSelector ret = new UiSelector();
        ret.mSelectorAttributes = mSelectorAttributes.clone();
        if (hasChildSelector())
            ret.mSelectorAttributes.put(SELECTOR_CHILD, new UiSelector(getChildSelector()));
        if (hasParentSelector())
            ret.mSelectorAttributes.put(SELECTOR_PARENT, new UiSelector(getParentSelector()));
        if (hasPatternSelector())
            ret.mSelectorAttributes.put(SELECTOR_PATTERN, new UiSelector(getPatternSelector()));
        return ret;
    }

    static UiSelector patternBuilder(UiSelector selector) {
        if (!selector.hasPatternSelector()) {
            return new UiSelector().patternSelector(selector);
        }
        return selector;
    }

    static UiSelector patternBuilder(UiSelector container, UiSelector pattern) {
        return new UiSelector(
                new UiSelector().containerSelector(container).patternSelector(pattern));
    }

    /**
     * Set the search criteria to match the visible text displayed
     * in a widget (for example, the text label to launch an app).
     *
     * The text for the element must match exactly with the string in your input
     * argument. Matching is case-sensitive.
     *
     * @param text Value to match
     * @return UiSelector with the specified search criteria
     * @since API Level 16
     */
    public UiSelector text(String text) {
        if (text == null) {
            throw new IllegalArgumentException("text cannot be null");
        }
        return buildSelector(SELECTOR_TEXT, text);
    }

    /**
     * Set the search criteria to match the visible text displayed in a layout
     * element, using a regular expression.
     *
     * The text in the widget must match exactly with the string in your
     * input argument.
     *
     * @param regex a regular expression
     * @return UiSelector with the specified search criteria
     * @since API Level 17
     */
    public UiSelector textMatches(String regex) {
        if (regex == null) {
            throw new IllegalArgumentException("regex cannot be null");
        }
        return buildSelector(SELECTOR_TEXT_REGEX, Pattern.compile(regex));
    }

    /**
     * Set the search criteria to match visible text in a widget that is
     * prefixed by the text parameter.
     *
     * The matching is case-insensitive.
     *
     * @param text Value to match
     * @return UiSelector with the specified search criteria
     * @since API Level 16
     */
    public UiSelector textStartsWith(String text) {
        if (text == null) {
            throw new IllegalArgumentException("text cannot be null");
        }
        return buildSelector(SELECTOR_START_TEXT, text);
    }

    /**
     * Set the search criteria to match the visible text in a widget
     * where the visible text must contain the string in your input argument.
     *
     * The matching is case-sensitive.
     *
     * @param text Value to match
     * @return UiSelector with the specified search criteria
     * @since API Level 16
     */
    public UiSelector textContains(String text) {
        if (text == null) {
            throw new IllegalArgumentException("text cannot be null");
        }
        return buildSelector(SELECTOR_CONTAINS_TEXT, text);
    }

    /**
     * Set the search criteria to match the class property
     * for a widget (for example, "android.widget.Button").
     *
     * @param className Value to match
     * @return UiSelector with the specified search criteria
     * @since API Level 16
     */
    public UiSelector className(String className) {
        if (className == null) {
            throw new IllegalArgumentException("className cannot be null");
        }
        return buildSelector(SELECTOR_CLASS, className);
    }

    /**
     * Set the search criteria to match the class property
     * for a widget, using a regular expression.
     *
     * @param regex a regular expression
     * @return UiSelector with the specified search criteria
     * @since API Level 17
     */
    public UiSelector classNameMatches(String regex) {
        if (regex == null) {
            throw new IllegalArgumentException("regex cannot be null");
        }
        return buildSelector(SELECTOR_CLASS_REGEX, Pattern.compile(regex));
    }

    /**
     * Set the search criteria to match the class property
     * for a widget (for example, "android.widget.Button").
     *
     * @param type type
     * @return UiSelector with the specified search criteria
     * @since API Level 17
     */
    public <T> UiSelector className(Class<T> type) {
        if (type == null) {
            throw new IllegalArgumentException("type cannot be null");
        }
        return buildSelector(SELECTOR_CLASS, type.getName());
    }

    /**
     * Set the search criteria to match the content-description
     * property for a widget.
     *
     * The content-description is typically used
     * by the Android Accessibility framework to
     * provide an audio prompt for the widget when
     * the widget is selected. The content-description
     * for the widget must match exactly
     * with the string in your input argument.
     *
     * Matching is case-sensitive.
     *
     * @param desc Value to match
     * @return UiSelector with the specified search criteria
     * @since API Level 16
     */
    public UiSelector description(String desc) {
        if (desc == null) {
            throw new IllegalArgumentException("desc cannot be null");
        }
        return buildSelector(SELECTOR_DESCRIPTION, desc);
    }

    /**
     * Set the search criteria to match the content-description
     * property for a widget.
     *
     * The content-description is typically used
     * by the Android Accessibility framework to
     * provide an audio prompt for the widget when
     * the widget is selected. The content-description
     * for the widget must match exactly
     * with the string in your input argument.
     *
     * @param regex a regular expression
     * @return UiSelector with the specified search criteria
     * @since API Level 17
     */
    public UiSelector descriptionMatches(String regex) {
        if (regex == null) {
            throw new IllegalArgumentException("regex cannot be null");
        }
        return buildSelector(SELECTOR_DESCRIPTION_REGEX, Pattern.compile(regex));
    }

    /**
     * Set the search criteria to match the content-description
     * property for a widget.
     *
     * The content-description is typically used
     * by the Android Accessibility framework to
     * provide an audio prompt for the widget when
     * the widget is selected. The content-description
     * for the widget must start
     * with the string in your input argument.
     *
     * Matching is case-insensitive.
     *
     * @param desc Value to match
     * @return UiSelector with the specified search criteria
     * @since API Level 16
     */
    public UiSelector descriptionStartsWith(String desc) {
        if (desc == null) {
            throw new IllegalArgumentException("desc cannot be null");
        }
        return buildSelector(SELECTOR_START_DESCRIPTION, desc);
    }

    /**
     * Set the search criteria to match the content-description
     * property for a widget.
     *
     * The content-description is typically used
     * by the Android Accessibility framework to
     * provide an audio prompt for the widget when
     * the widget is selected. The content-description
     * for the widget must contain
     * the string in your input argument.
     *
     * Matching is case-insensitive.
     *
     * @param desc Value to match
     * @return UiSelector with the specified search criteria
     * @since API Level 16
     */
    public UiSelector descriptionContains(String desc) {
        if (desc == null) {
            throw new IllegalArgumentException("desc cannot be null");
        }
        return buildSelector(SELECTOR_CONTAINS_DESCRIPTION, desc);
    }

    /**
     * Set the search criteria to match the given resource ID.
     *
     * @param id Value to match
     * @return UiSelector with the specified search criteria
     * @since API Level 18
     */
    public UiSelector resourceId(String id) {
        if (id == null) {
            throw new IllegalArgumentException("id cannot be null");
        }
        return buildSelector(SELECTOR_RESOURCE_ID, id);
    }

    /**
     * Set the search criteria to match the resource ID
     * of the widget, using a regular expression.
     *
     * @param regex a regular expression
     * @return UiSelector with the specified search criteria
     * @since API Level 18
     */
    public UiSelector resourceIdMatches(String regex) {
        if (regex == null) {
            throw new IllegalArgumentException("regex cannot be null");
        }
        return buildSelector(SELECTOR_RESOURCE_ID_REGEX, Pattern.compile(regex));
    }

    /**
     * Set the search criteria to match the widget by its node
     * index in the layout hierarchy.
     *
     * The index value must be 0 or greater.
     *
     * Using the index can be unreliable and should only
     * be used as a last resort for matching. Instead,
     * consider using the {@link #instance(int)} method.
     *
     * @param index Value to match
     * @return UiSelector with the specified search criteria
     * @since API Level 16
     */
    public UiSelector index(final int index) {
        return buildSelector(SELECTOR_INDEX, index);
    }

    /**
     * Set the search criteria to match the
     * widget by its instance number.
     *
     * The instance value must be 0 or greater, where
     * the first instance is 0.
     *
     * For example, to simulate a user click on
     * the third image that is enabled in a UI screen, you
     * could specify a a search criteria where the instance is
     * 2, the {@link #className(String)} matches the image
     * widget class, and {@link #enabled(boolean)} is true.
     * The code would look like this:
     * <code>
     * new UiSelector().className("android.widget.ImageView")
     *    .enabled(true).instance(2);
     * </code>
     *
     * @param instance Value to match
     * @return UiSelector with the specified search criteria
     * @since API Level 16
     */
    public UiSelector instance(final int instance) {
        return buildSelector(SELECTOR_INSTANCE, instance);
    }

    /**
     * Set the search criteria to match widgets that are enabled.
     *
     * Typically, using this search criteria alone is not useful.
     * You should also include additional criteria, such as text,
     * content-description, or the class name for a widget.
     *
     * If no other search criteria is specified, and there is more
     * than one matching widget, the first widget in the tree
     * is selected.
     *
     * @param val Value to match
     * @return UiSelector with the specified search criteria
     * @since API Level 16
     */
    public UiSelector enabled(boolean val) {
        return buildSelector(SELECTOR_ENABLED, val);
    }

    /**
     * Set the search criteria to match widgets that have focus.
     *
     * Typically, using this search criteria alone is not useful.
     * You should also include additional criteria, such as text,
     * content-description, or the class name for a widget.
     *
     * If no other search criteria is specified, and there is more
     * than one matching widget, the first widget in the tree
     * is selected.
     *
     * @param val Value to match
     * @return UiSelector with the specified search criteria
     * @since API Level 16
     */
    public UiSelector focused(boolean val) {
        return buildSelector(SELECTOR_FOCUSED, val);
    }

    /**
     * Set the search criteria to match widgets that are focusable.
     *
     * Typically, using this search criteria alone is not useful.
     * You should also include additional criteria, such as text,
     * content-description, or the class name for a widget.
     *
     * If no other search criteria is specified, and there is more
     * than one matching widget, the first widget in the tree
     * is selected.
     *
     * @param val Value to match
     * @return UiSelector with the specified search criteria
     * @since API Level 16
     */
    public UiSelector focusable(boolean val) {
        return buildSelector(SELECTOR_FOCUSABLE, val);
    }

    /**
     * Set the search criteria to match widgets that are scrollable.
     *
     * Typically, using this search criteria alone is not useful.
     * You should also include additional criteria, such as text,
     * content-description, or the class name for a widget.
     *
     * If no other search criteria is specified, and there is more
     * than one matching widget, the first widget in the tree
     * is selected.
     *
     * @param val Value to match
     * @return UiSelector with the specified search criteria
     * @since API Level 16
     */
    public UiSelector scrollable(boolean val) {
        return buildSelector(SELECTOR_SCROLLABLE, val);
    }

    /**
     * Set the search criteria to match widgets that
     * are currently selected.
     *
     * Typically, using this search criteria alone is not useful.
     * You should also include additional criteria, such as text,
     * content-description, or the class name for a widget.
     *
     * If no other search criteria is specified, and there is more
     * than one matching widget, the first widget in the tree
     * is selected.
     *
     * @param val Value to match
     * @return UiSelector with the specified search criteria
     * @since API Level 16
     */
    public UiSelector selected(boolean val) {
        return buildSelector(SELECTOR_SELECTED, val);
    }

    /**
     * Set the search criteria to match widgets that
     * are currently checked (usually for checkboxes).
     *
     * Typically, using this search criteria alone is not useful.
     * You should also include additional criteria, such as text,
     * content-description, or the class name for a widget.
     *
     * If no other search criteria is specified, and there is more
     * than one matching widget, the first widget in the tree
     * is selected.
     *
     * @param val Value to match
     * @return UiSelector with the specified search criteria
     * @since API Level 16
     */
    public UiSelector checked(boolean val) {
        return buildSelector(SELECTOR_CHECKED, val);
    }

    /**
     * Set the search criteria to match widgets that are clickable.
     *
     * Typically, using this search criteria alone is not useful.
     * You should also include additional criteria, such as text,
     * content-description, or the class name for a widget.
     *
     * If no other search criteria is specified, and there is more
     * than one matching widget, the first widget in the tree
     * is selected.
     *
     * @param val Value to match
     * @return UiSelector with the specified search criteria
     * @since API Level 16
     */
    public UiSelector clickable(boolean val) {
        return buildSelector(SELECTOR_CLICKABLE, val);
    }

    /**
     * Set the search criteria to match widgets that are checkable.
     *
     * Typically, using this search criteria alone is not useful.
     * You should also include additional criteria, such as text,
     * content-description, or the class name for a widget.
     *
     * If no other search criteria is specified, and there is more
     * than one matching widget, the first widget in the tree
     * is selected.
     *
     * @param val Value to match
     * @return UiSelector with the specified search criteria
     * @since API Level 18
     */
    public UiSelector checkable(boolean val) {
        return buildSelector(SELECTOR_CHECKABLE, val);
    }

    /**
     * Set the search criteria to match widgets that are long-clickable.
     *
     * Typically, using this search criteria alone is not useful.
     * You should also include additional criteria, such as text,
     * content-description, or the class name for a widget.
     *
     * If no other search criteria is specified, and there is more
     * than one matching widget, the first widget in the tree
     * is selected.
     *
     * @param val Value to match
     * @return UiSelector with the specified search criteria
     * @since API Level 17
     */
    public UiSelector longClickable(boolean val) {
        return buildSelector(SELECTOR_LONG_CLICKABLE, val);
    }

    /**
     * Adds a child UiSelector criteria to this selector.
     *
     * Use this selector to narrow the search scope to
     * child widgets under a specific parent widget.
     *
     * @param selector
     * @return UiSelector with this added search criterion
     * @since API Level 16
     */
    public UiSelector childSelector(UiSelector selector) {
        if (selector == null) {
            throw new IllegalArgumentException("selector cannot be null");
        }
        return buildSelector(SELECTOR_CHILD, selector);
    }

    private UiSelector patternSelector(UiSelector selector) {
        return buildSelector(SELECTOR_PATTERN, selector);
    }

    private UiSelector containerSelector(UiSelector selector) {
        return buildSelector(SELECTOR_CONTAINER, selector);
    }

    /**
     * Adds a child UiSelector criteria to this selector which is used to
     * start search from the parent widget.
     *
     * Use this selector to narrow the search scope to
     * sibling widgets as well all child widgets under a parent.
     *
     * @param selector
     * @return UiSelector with this added search criterion
     * @since API Level 16
     */
    public UiSelector fromParent(UiSelector selector) {
        if (selector == null) {
            throw new IllegalArgumentException("selector cannot be null");
        }
        return buildSelector(SELECTOR_PARENT, selector);
    }

    /**
     * Set the search criteria to match the package name
     * of the application that contains the widget.
     *
     * @param name Value to match
     * @return UiSelector with the specified search criteria
     * @since API Level 16
     */
    public UiSelector packageName(String name) {
        if (name == null) {
            throw new IllegalArgumentException("name cannot be null");
        }
        return buildSelector(SELECTOR_PACKAGE_NAME, name);
    }

    /**
     * Set the search criteria to match the package name
     * of the application that contains the widget.
     *
     * @param regex a regular expression
     * @return UiSelector with the specified search criteria
     * @since API Level 17
     */
    public UiSelector packageNameMatches(String regex) {
        if (regex == null) {
            throw new IllegalArgumentException("regex cannot be null");
        }
        return buildSelector(SELECTOR_PACKAGE_NAME_REGEX, Pattern.compile(regex));
    }

    /**
     * Building a UiSelector always returns a new UiSelector and never modifies the
     * existing UiSelector being used.
     */
    private UiSelector buildSelector(int selectorId, Object selectorValue) {
        UiSelector selector = new UiSelector(this);
        if (selectorId == SELECTOR_CHILD || selectorId == SELECTOR_PARENT)
            selector.getLastSubSelector().mSelectorAttributes.put(selectorId, selectorValue);
        else
            selector.mSelectorAttributes.put(selectorId, selectorValue);
        return selector;
    }

    /**
     * Selectors may have a hierarchy defined by specifying child nodes to be matched.
     * It is not necessary that every selector have more than one level. A selector
     * can also be a single level referencing only one node. In such cases the return
     * it null.
     *
     * @return a child selector if one exists. Else null if this selector does not
     * reference child node.
     */
    UiSelector getChildSelector() {
        UiSelector selector = (UiSelector)mSelectorAttributes.get(UiSelector.SELECTOR_CHILD, null);
        if (selector != null)
            return new UiSelector(selector);
        return null;
    }

    UiSelector getPatternSelector() {
        UiSelector selector =
                (UiSelector)mSelectorAttributes.get(UiSelector.SELECTOR_PATTERN, null);
        if (selector != null)
            return new UiSelector(selector);
        return null;
    }

    UiSelector getContainerSelector() {
        UiSelector selector =
                (UiSelector)mSelectorAttributes.get(UiSelector.SELECTOR_CONTAINER, null);
        if (selector != null)
            return new UiSelector(selector);
        return null;
    }

    UiSelector getParentSelector() {
        UiSelector selector =
                (UiSelector) mSelectorAttributes.get(UiSelector.SELECTOR_PARENT, null);
        if (selector != null)
            return new UiSelector(selector);
        return null;
    }

    int getInstance() {
        return getInt(UiSelector.SELECTOR_INSTANCE);
    }

    String getString(int criterion) {
        return (String) mSelectorAttributes.get(criterion, null);
    }

    boolean getBoolean(int criterion) {
        return (Boolean) mSelectorAttributes.get(criterion, false);
    }

    int getInt(int criterion) {
        return (Integer) mSelectorAttributes.get(criterion, 0);
    }

    Pattern getPattern(int criterion) {
        return (Pattern) mSelectorAttributes.get(criterion, null);
    }

    boolean isMatchFor(AccessibilityNodeInfo node, int index) {
        int size = mSelectorAttributes.size();
        for(int x = 0; x < size; x++) {
            CharSequence s = null;
            int criterion = mSelectorAttributes.keyAt(x);
            switch(criterion) {
            case UiSelector.SELECTOR_INDEX:
                if (index != this.getInt(criterion))
                    return false;
                break;
            case UiSelector.SELECTOR_CHECKED:
                if (node.isChecked() != getBoolean(criterion)) {
                    return false;
                }
                break;
            case UiSelector.SELECTOR_CLASS:
                s = node.getClassName();
                if (s == null || !s.toString().contentEquals(getString(criterion))) {
                    return false;
                }
                break;
            case UiSelector.SELECTOR_CLASS_REGEX:
                s = node.getClassName();
                if (s == null || !getPattern(criterion).matcher(s).matches()) {
                    return false;
                }
                break;
            case UiSelector.SELECTOR_CLICKABLE:
                if (node.isClickable() != getBoolean(criterion)) {
                    return false;
                }
                break;
            case UiSelector.SELECTOR_CHECKABLE:
                if (node.isCheckable() != getBoolean(criterion)) {
                    return false;
                }
                break;
            case UiSelector.SELECTOR_LONG_CLICKABLE:
                if (node.isLongClickable() != getBoolean(criterion)) {
                    return false;
                }
                break;
            case UiSelector.SELECTOR_CONTAINS_DESCRIPTION:
                s = node.getContentDescription();
                if (s == null || !s.toString().toLowerCase()
                        .contains(getString(criterion).toLowerCase())) {
                    return false;
                }
                break;
            case UiSelector.SELECTOR_START_DESCRIPTION:
                s = node.getContentDescription();
                if (s == null || !s.toString().toLowerCase()
                        .startsWith(getString(criterion).toLowerCase())) {
                    return false;
                }
                break;
            case UiSelector.SELECTOR_DESCRIPTION:
                s = node.getContentDescription();
                if (s == null || !s.toString().contentEquals(getString(criterion))) {
                    return false;
                }
                break;
            case UiSelector.SELECTOR_DESCRIPTION_REGEX:
                s = node.getContentDescription();
                if (s == null || !getPattern(criterion).matcher(s).matches()) {
                    return false;
                }
                break;
            case UiSelector.SELECTOR_CONTAINS_TEXT:
                s = node.getText();
                if (s == null || !s.toString().toLowerCase()
                        .contains(getString(criterion).toLowerCase())) {
                    return false;
                }
                break;
            case UiSelector.SELECTOR_START_TEXT:
                s = node.getText();
                if (s == null || !s.toString().toLowerCase()
                        .startsWith(getString(criterion).toLowerCase())) {
                    return false;
                }
                break;
            case UiSelector.SELECTOR_TEXT:
                s = node.getText();
                if (s == null || !s.toString().contentEquals(getString(criterion))) {
                    return false;
                }
                break;
            case UiSelector.SELECTOR_TEXT_REGEX:
                s = node.getText();
                if (s == null || !getPattern(criterion).matcher(s).matches()) {
                    return false;
                }
                break;
            case UiSelector.SELECTOR_ENABLED:
                if (node.isEnabled() != getBoolean(criterion)) {
                    return false;
                }
                break;
            case UiSelector.SELECTOR_FOCUSABLE:
                if (node.isFocusable() != getBoolean(criterion)) {
                    return false;
                }
                break;
            case UiSelector.SELECTOR_FOCUSED:
                if (node.isFocused() != getBoolean(criterion)) {
                    return false;
                }
                break;
            case UiSelector.SELECTOR_ID:
                break; //TODO: do we need this for AccessibilityNodeInfo.id?
            case UiSelector.SELECTOR_PACKAGE_NAME:
                s = node.getPackageName();
                if (s == null || !s.toString().contentEquals(getString(criterion))) {
                    return false;
                }
                break;
            case UiSelector.SELECTOR_PACKAGE_NAME_REGEX:
                s = node.getPackageName();
                if (s == null || !getPattern(criterion).matcher(s).matches()) {
                    return false;
                }
                break;
            case UiSelector.SELECTOR_SCROLLABLE:
                if (node.isScrollable() != getBoolean(criterion)) {
                    return false;
                }
                break;
            case UiSelector.SELECTOR_SELECTED:
                if (node.isSelected() != getBoolean(criterion)) {
                    return false;
                }
                break;
            case UiSelector.SELECTOR_RESOURCE_ID:
                s = node.getViewIdResourceName();
                if (s == null || !s.toString().contentEquals(getString(criterion))) {
                    return false;
                }
                break;
            case UiSelector.SELECTOR_RESOURCE_ID_REGEX:
                s = node.getViewIdResourceName();
                if (s == null || !getPattern(criterion).matcher(s).matches()) {
                    return false;
                }
                break;
            }
        }
        return matchOrUpdateInstance();
    }

    private boolean matchOrUpdateInstance() {
        int currentSelectorCounter = 0;
        int currentSelectorInstance = 0;

        // matched attributes - now check for matching instance number
        if (mSelectorAttributes.indexOfKey(UiSelector.SELECTOR_INSTANCE) >= 0) {
            currentSelectorInstance =
                    (Integer)mSelectorAttributes.get(UiSelector.SELECTOR_INSTANCE);
        }

        // instance is required. Add count if not already counting
        if (mSelectorAttributes.indexOfKey(UiSelector.SELECTOR_COUNT) >= 0) {
            currentSelectorCounter = (Integer)mSelectorAttributes.get(UiSelector.SELECTOR_COUNT);
        }

        // Verify
        if (currentSelectorInstance == currentSelectorCounter) {
            return true;
        }
        // Update count
        if (currentSelectorInstance > currentSelectorCounter) {
            mSelectorAttributes.put(UiSelector.SELECTOR_COUNT, ++currentSelectorCounter);
        }
        return false;
    }

    /**
     * Leaf selector indicates no more child or parent selectors
     * are declared in the this selector.
     * @return true if is leaf.
     */
    boolean isLeaf() {
        if (mSelectorAttributes.indexOfKey(UiSelector.SELECTOR_CHILD) < 0 &&
                mSelectorAttributes.indexOfKey(UiSelector.SELECTOR_PARENT) < 0) {
            return true;
        }
        return false;
    }

    boolean hasChildSelector() {
        if (mSelectorAttributes.indexOfKey(UiSelector.SELECTOR_CHILD) < 0) {
            return false;
        }
        return true;
    }

    boolean hasPatternSelector() {
        if (mSelectorAttributes.indexOfKey(UiSelector.SELECTOR_PATTERN) < 0) {
            return false;
        }
        return true;
    }

    boolean hasContainerSelector() {
        if (mSelectorAttributes.indexOfKey(UiSelector.SELECTOR_CONTAINER) < 0) {
            return false;
        }
        return true;
    }

    boolean hasParentSelector() {
        if (mSelectorAttributes.indexOfKey(UiSelector.SELECTOR_PARENT) < 0) {
            return false;
        }
        return true;
    }

    /**
     * Returns the deepest selector in the chain of possible sub selectors.
     * A chain of selector is created when either of {@link UiSelector#childSelector(UiSelector)}
     * or {@link UiSelector#fromParent(UiSelector)} are used once or more in the construction of
     * a selector.
     * @return last UiSelector in chain
     */
    private UiSelector getLastSubSelector() {
        if (mSelectorAttributes.indexOfKey(UiSelector.SELECTOR_CHILD) >= 0) {
            UiSelector child = (UiSelector)mSelectorAttributes.get(UiSelector.SELECTOR_CHILD);
            if (child.getLastSubSelector() == null) {
                return child;
            }
            return child.getLastSubSelector();
        } else if (mSelectorAttributes.indexOfKey(UiSelector.SELECTOR_PARENT) >= 0) {
            UiSelector parent = (UiSelector)mSelectorAttributes.get(UiSelector.SELECTOR_PARENT);
            if (parent.getLastSubSelector() == null) {
                return parent;
            }
            return parent.getLastSubSelector();
        }
        return this;
    }

    @Override
    public String toString() {
        return dumpToString(true);
    }

    String dumpToString(boolean all) {
        StringBuilder builder = new StringBuilder();
        builder.append(UiSelector.class.getSimpleName() + "[");
        final int criterionCount = mSelectorAttributes.size();
        for (int i = 0; i < criterionCount; i++) {
            if (i > 0) {
                builder.append(", ");
            }
            final int criterion = mSelectorAttributes.keyAt(i);
            switch (criterion) {
            case SELECTOR_TEXT:
                builder.append("TEXT=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_TEXT_REGEX:
                builder.append("TEXT_REGEX=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_START_TEXT:
                builder.append("START_TEXT=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_CONTAINS_TEXT:
                builder.append("CONTAINS_TEXT=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_CLASS:
                builder.append("CLASS=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_CLASS_REGEX:
                builder.append("CLASS_REGEX=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_DESCRIPTION:
                builder.append("DESCRIPTION=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_DESCRIPTION_REGEX:
                builder.append("DESCRIPTION_REGEX=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_START_DESCRIPTION:
                builder.append("START_DESCRIPTION=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_CONTAINS_DESCRIPTION:
                builder.append("CONTAINS_DESCRIPTION=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_INDEX:
                builder.append("INDEX=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_INSTANCE:
                builder.append("INSTANCE=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_ENABLED:
                builder.append("ENABLED=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_FOCUSED:
                builder.append("FOCUSED=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_FOCUSABLE:
                builder.append("FOCUSABLE=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_SCROLLABLE:
                builder.append("SCROLLABLE=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_CLICKABLE:
                builder.append("CLICKABLE=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_CHECKABLE:
                builder.append("CHECKABLE=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_LONG_CLICKABLE:
                builder.append("LONG_CLICKABLE=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_CHECKED:
                builder.append("CHECKED=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_SELECTED:
                builder.append("SELECTED=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_ID:
                builder.append("ID=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_CHILD:
                if (all)
                    builder.append("CHILD=").append(mSelectorAttributes.valueAt(i));
                else
                    builder.append("CHILD[..]");
                break;
            case SELECTOR_PATTERN:
                if (all)
                    builder.append("PATTERN=").append(mSelectorAttributes.valueAt(i));
                else
                    builder.append("PATTERN[..]");
                break;
            case SELECTOR_CONTAINER:
                if (all)
                    builder.append("CONTAINER=").append(mSelectorAttributes.valueAt(i));
                else
                    builder.append("CONTAINER[..]");
                break;
            case SELECTOR_PARENT:
                if (all)
                    builder.append("PARENT=").append(mSelectorAttributes.valueAt(i));
                else
                    builder.append("PARENT[..]");
                break;
            case SELECTOR_COUNT:
                builder.append("COUNT=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_PACKAGE_NAME:
                builder.append("PACKAGE NAME=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_PACKAGE_NAME_REGEX:
                builder.append("PACKAGE_NAME_REGEX=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_RESOURCE_ID:
                builder.append("RESOURCE_ID=").append(mSelectorAttributes.valueAt(i));
                break;
            case SELECTOR_RESOURCE_ID_REGEX:
                builder.append("RESOURCE_ID_REGEX=").append(mSelectorAttributes.valueAt(i));
                break;
            default:
                builder.append("UNDEFINED="+criterion+" ").append(mSelectorAttributes.valueAt(i));
            }
        }
        builder.append("]");
        return builder.toString();
    }
}