public class

BySelector

extends java.lang.Object

 java.lang.Object

↳androidx.test.uiautomator.BySelector

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.BySelector android.support.test.uiautomator.BySelector

Overview

A BySelector specifies criteria for matching UI elements during a call to UiDevice.findObject(BySelector).

Summary

Methods
public BySelectorcheckable(boolean isCheckable)

Sets the search criteria to match elements that are checkable or not checkable.

public BySelectorchecked(boolean isChecked)

Sets the search criteria to match elements that are checked or unchecked.

public BySelectorclazz(java.lang.Class clazz)

Sets the class name criteria for matching.

public BySelectorclazz(java.util.regex.Pattern className)

Sets the class name criteria for matching.

public BySelectorclazz(java.lang.String className)

Sets the class name criteria for matching.

public BySelectorclazz(java.lang.String packageName, java.lang.String className)

Sets the class name criteria for matching.

public BySelectorclickable(boolean isClickable)

Sets the search criteria to match elements that are clickable or not clickable.

public BySelectordepth(int exactDepth)

Sets the search criteria to match elements that are at a certain depth.

public BySelectordepth(int min, int max)

Sets the search criteria to match elements that are in a range of depths.

public BySelectordesc(java.util.regex.Pattern contentDescription)

Sets the content description criteria for matching.

public BySelectordesc(java.lang.String contentDescription)

Sets the content description criteria for matching.

public BySelectordescContains(java.lang.String substring)

Sets the content description criteria for matching.

public BySelectordescEndsWith(java.lang.String substring)

Sets the content description criteria for matching.

public BySelectordescStartsWith(java.lang.String substring)

Sets the content description criteria for matching.

public BySelectorenabled(boolean isEnabled)

Sets the search criteria to match elements that are enabled or disabled.

public BySelectorfocusable(boolean isFocusable)

Sets the search criteria to match elements that are focusable or not focusable.

public BySelectorfocused(boolean isFocused)

Sets the search criteria to match elements that are focused or unfocused.

public BySelectorhasChild(BySelector childSelector)

Adds a child selector criteria for matching.

public BySelectorhasDescendant(BySelector descendantSelector)

Adds a descendant selector criteria for matching.

public BySelectorhasDescendant(BySelector descendantSelector, int maxDepth)

Adds a descendant selector criteria for matching.

public BySelectorlongClickable(boolean isLongClickable)

Sets the search criteria to match elements that are long clickable or not long clickable.

public BySelectormaxDepth(int max)

Sets the search criteria to match elements that are no more than a certain depth.

public BySelectorminDepth(int min)

Sets the search criteria to match elements that are at least a certain depth.

public BySelectorpkg(java.util.regex.Pattern applicationPackage)

Sets the package name criteria for matching.

public BySelectorpkg(java.lang.String applicationPackage)

Sets the application package name criteria for matching.

public BySelectorres(java.util.regex.Pattern resourceName)

Sets the resource name criteria for matching.

public BySelectorres(java.lang.String resourceName)

Sets the resource name criteria for matching.

public BySelectorres(java.lang.String resourcePackage, java.lang.String resourceId)

Sets the resource name criteria for matching.

public BySelectorscrollable(boolean isScrollable)

Sets the search criteria to match elements that are scrollable or not scrollable.

public BySelectorselected(boolean isSelected)

Sets the search criteria to match elements that are selected or not selected.

public BySelectortext(java.util.regex.Pattern textValue)

Sets the text value criteria for matching.

public BySelectortext(java.lang.String textValue)

Sets the text value criteria for matching.

public BySelectortextContains(java.lang.String substring)

Sets the text value criteria for matching.

public BySelectortextEndsWith(java.lang.String substring)

Sets the text value criteria for matching.

public BySelectortextStartsWith(java.lang.String substring)

Sets the text value criteria for matching.

public java.lang.StringtoString()

Returns a java.lang.String representation of this BySelector.

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

Methods

public BySelector clazz(java.lang.String className)

Sets the class name criteria for matching. A UI element will be considered a match if its class name exactly matches the className parameter and all other criteria for this selector are met. If className starts with a period, it is assumed to be in the android.widget package.

Parameters:

className: The full class name value to match.

Returns:

A reference to this BySelector.

public BySelector clazz(java.lang.String packageName, java.lang.String className)

Sets the class name criteria for matching. A UI element will be considered a match if its package and class name exactly match the packageName and className parameters and all other criteria for this selector are met.

Parameters:

packageName: The package value to match.
className: The class name value to match.

Returns:

A reference to this BySelector.

public BySelector clazz(java.lang.Class clazz)

Sets the class name criteria for matching. A UI element will be considered a match if its class name matches clazz and all other criteria for this selector are met.

Parameters:

clazz: The class to match.

Returns:

A reference to this BySelector

public BySelector clazz(java.util.regex.Pattern className)

Sets the class name criteria for matching. A UI element will be considered a match if its full class name matches the className java.util.regex.Pattern and all other criteria for this selector are met.

Parameters:

className: The java.util.regex.Pattern to be used for matching.

Returns:

A reference to this BySelector.

public BySelector desc(java.lang.String contentDescription)

Sets the content description criteria for matching. A UI element will be considered a match if its content description exactly matches the contentDescription parameter and all other criteria for this selector are met.

Parameters:

contentDescription: The exact value to match.

Returns:

A reference to this BySelector.

public BySelector descContains(java.lang.String substring)

Sets the content description criteria for matching. A UI element will be considered a match if its content description contains the substring parameter and all other criteria for this selector are met.

Parameters:

substring: The substring to match.

Returns:

A reference to this BySelector.

public BySelector descStartsWith(java.lang.String substring)

Sets the content description criteria for matching. A UI element will be considered a match if its content description starts with the substring parameter and all other criteria for this selector are met.

Parameters:

substring: The substring to match.

Returns:

A reference to this BySelector.

public BySelector descEndsWith(java.lang.String substring)

Sets the content description criteria for matching. A UI element will be considered a match if its content description ends with the substring parameter and all other criteria for this selector are met.

Parameters:

substring: The substring to match.

Returns:

A reference to this BySelector.

public BySelector desc(java.util.regex.Pattern contentDescription)

Sets the content description criteria for matching. A UI element will be considered a match if its content description matches the contentDescription java.util.regex.Pattern and all other criteria for this selector are met.

Parameters:

contentDescription: The java.util.regex.Pattern to be used for matching.

Returns:

A reference to this BySelector.

public BySelector pkg(java.lang.String applicationPackage)

Sets the application package name criteria for matching. A UI element will be considered a match if its application package name exactly matches the applicationPackage parameter and all other criteria for this selector are met.

Parameters:

applicationPackage: The exact value to match.

Returns:

A reference to this BySelector.

public BySelector pkg(java.util.regex.Pattern applicationPackage)

Sets the package name criteria for matching. A UI element will be considered a match if its application package name matches the applicationPackage java.util.regex.Pattern and all other criteria for this selector are met.

Parameters:

applicationPackage: The java.util.regex.Pattern to be used for matching.

Returns:

A reference to this BySelector.

public BySelector res(java.lang.String resourceName)

Sets the resource name criteria for matching. A UI element will be considered a match if its resource name exactly matches the resourceName parameter and all other criteria for this selector are met.

Parameters:

resourceName: The exact value to match.

Returns:

A reference to this BySelector.

public BySelector res(java.lang.String resourcePackage, java.lang.String resourceId)

Sets the resource name criteria for matching. A UI element will be considered a match if its resource package and resource id exactly match the resourcePackage and resourceId parameters and all other criteria for this selector are met.

Parameters:

resourcePackage: The resource package value to match.
resourceId: The resouce-id value to match.

Returns:

A reference to this BySelector.

public BySelector res(java.util.regex.Pattern resourceName)

Sets the resource name criteria for matching. A UI element will be considered a match if its resource name matches the resourceName java.util.regex.Pattern and all other criteria for this selector are met.

Parameters:

resourceName: The java.util.regex.Pattern to be used for matching.

Returns:

A reference to this BySelector.

public BySelector text(java.lang.String textValue)

Sets the text value criteria for matching. A UI element will be considered a match if its text value exactly matches the textValue parameter and all other criteria for this selector are met.

Parameters:

textValue: The exact value to match.

Returns:

A reference to this BySelector.

public BySelector textContains(java.lang.String substring)

Sets the text value criteria for matching. A UI element will be considered a match if its text value contains the substring parameter and all other criteria for this selector are met.

Parameters:

substring: The substring to match.

Returns:

A reference to this BySelector.

public BySelector textStartsWith(java.lang.String substring)

Sets the text value criteria for matching. A UI element will be considered a match if its text value starts with the substring parameter and all other criteria for this selector are met.

Parameters:

substring: The substring to match.

Returns:

A reference to this BySelector.

public BySelector textEndsWith(java.lang.String substring)

Sets the text value criteria for matching. A UI element will be considered a match if its text value ends with the substring parameter and all other criteria for this selector are met.

Parameters:

substring: The substring to match.

Returns:

A reference to this BySelector.

public BySelector text(java.util.regex.Pattern textValue)

Sets the text value criteria for matching. A UI element will be considered a match if its text value matches the textValue java.util.regex.Pattern and all other criteria for this selector are met.

Parameters:

textValue: The java.util.regex.Pattern to be used for matching.

Returns:

A reference to this BySelector.

public BySelector checkable(boolean isCheckable)

Sets the search criteria to match elements that are checkable or not checkable.

Parameters:

isCheckable: Whether to match elements that are checkable or elements that are not checkable.

Returns:

A reference to this BySelector.

public BySelector checked(boolean isChecked)

Sets the search criteria to match elements that are checked or unchecked.

Parameters:

isChecked: Whether to match elements that are checked or elements that are unchecked.

Returns:

A reference to this BySelector.

public BySelector clickable(boolean isClickable)

Sets the search criteria to match elements that are clickable or not clickable.

Parameters:

isClickable: Whether to match elements that are clickable or elements that are not clickable.

Returns:

A reference to this BySelector.

public BySelector enabled(boolean isEnabled)

Sets the search criteria to match elements that are enabled or disabled.

Parameters:

isEnabled: Whether to match elements that are enabled or elements that are disabled.

Returns:

A reference to this BySelector.

public BySelector focusable(boolean isFocusable)

Sets the search criteria to match elements that are focusable or not focusable.

Parameters:

isFocusable: Whether to match elements that are focusable or elements that are not focusable.

Returns:

A reference to this BySelector.

public BySelector focused(boolean isFocused)

Sets the search criteria to match elements that are focused or unfocused.

Parameters:

isFocused: Whether to match elements that are focused or elements that are unfocused.

Returns:

A reference to this BySelector.

public BySelector longClickable(boolean isLongClickable)

Sets the search criteria to match elements that are long clickable or not long clickable.

Parameters:

isLongClickable: Whether to match elements that are long clickable or elements that are not long clickable.

Returns:

A reference to this BySelector.

public BySelector scrollable(boolean isScrollable)

Sets the search criteria to match elements that are scrollable or not scrollable.

Parameters:

isScrollable: Whether to match elements that are scrollable or elements that are not scrollable.

Returns:

A reference to this BySelector.

public BySelector selected(boolean isSelected)

Sets the search criteria to match elements that are selected or not selected.

Parameters:

isSelected: Whether to match elements that are selected or elements that are not selected.

Returns:

A reference to this BySelector.

public BySelector depth(int exactDepth)

Sets the search criteria to match elements that are at a certain depth.

public BySelector depth(int min, int max)

Sets the search criteria to match elements that are in a range of depths.

public BySelector minDepth(int min)

Sets the search criteria to match elements that are at least a certain depth.

public BySelector maxDepth(int max)

Sets the search criteria to match elements that are no more than a certain depth.

public BySelector hasChild(BySelector childSelector)

Adds a child selector criteria for matching. A UI element will be considered a match if it has a child element (direct descendant) which matches the childSelector and all other criteria for this selector are met. If specified more than once, matches must be found for all childSelectors.

Parameters:

childSelector: The selector used to find a matching child element.

Returns:

A reference to this BySelector.

public BySelector hasDescendant(BySelector descendantSelector)

Adds a descendant selector criteria for matching. A UI element will be considered a match if it has a descendant element which matches the descendantSelector and all other criteria for this selector are met. If specified more than once, matches must be found for all descendantSelectors.

Parameters:

descendantSelector: The selector used to find a matching descendant element.

Returns:

A reference to this BySelector.

public BySelector hasDescendant(BySelector descendantSelector, int maxDepth)

Adds a descendant selector criteria for matching. A UI element will be considered a match if it has a descendant element which matches the descendantSelector and all other criteria for this selector are met. If specified more than once, matches must be found for all descendantSelectors.

Parameters:

descendantSelector: The selector used to find a matching descendant element.
maxDepth: The maximum depth under the element to search the descendant.

Returns:

A reference to this BySelector.

public java.lang.String toString()

Returns a java.lang.String representation of this BySelector. The format is "BySelector [<KEY>='<VALUE> ... ]". Each criteria is listed as a key-value pair where the key is the name of the criteria expressed in all caps (e.g. CLAZZ, RES, etc).

Source

/*
 * Copyright (C) 2014 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.view.accessibility.AccessibilityNodeInfo;

import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * A {@link BySelector} specifies criteria for matching UI elements during a call to
 * {@link UiDevice#findObject(BySelector)}.
 */
public class BySelector {

    // Regex patterns for String criteria
    Pattern mClazz;
    Pattern mDesc;
    Pattern mPkg;
    Pattern mRes;
    Pattern mText;

    // Boolean criteria
    Boolean mChecked;
    Boolean mCheckable;
    Boolean mClickable;
    Boolean mEnabled;
    Boolean mFocused;
    Boolean mFocusable;
    Boolean mLongClickable;
    Boolean mScrollable;
    Boolean mSelected;

    // Depth restrictions
    Integer mMinDepth;
    Integer mMaxDepth;

    // Child selectors
    List<BySelector> mChildSelectors = new LinkedList<BySelector>();


    /** Clients should not instanciate this class directly. Use the {@link By} factory class instead. */
    BySelector() { }

    /**
     * Constructs a new {@link BySelector} and copies the criteria from {@code original}.
     *
     * @param original The {@link BySelector} to copy.
     */
    BySelector(BySelector original) {
        mClazz = original.mClazz;
        mDesc  = original.mDesc;
        mPkg   = original.mPkg;
        mRes   = original.mRes;
        mText  = original.mText;

        mChecked       = original.mChecked;
        mCheckable     = original.mCheckable;
        mClickable     = original.mClickable;
        mEnabled       = original.mEnabled;
        mFocused       = original.mFocused;
        mFocusable     = original.mFocusable;
        mLongClickable = original.mLongClickable;
        mScrollable    = original.mScrollable;
        mSelected      = original.mSelected;

        for (BySelector childSelector : original.mChildSelectors) {
            mChildSelectors.add(new BySelector(childSelector));
        }
    }

    /**
     * Sets the class name criteria for matching. A UI element will be considered a match if its
     * class name exactly matches the {@code className} parameter and all other criteria for
     * this selector are met. If {@code className} starts with a period, it is assumed to be in the
     * {@link android.widget} package.
     *
     * @param className The full class name value to match.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector clazz(String className) {
        checkNotNull(className, "className cannot be null");

        // If className starts with a period, assume the package is 'android.widget'
        if (className.charAt(0) == '.') {
            return clazz("android.widget", className.substring(1));
        } else {
            return clazz(Pattern.compile(Pattern.quote(className)));
        }
    }

    /**
     * Sets the class name criteria for matching. A UI element will be considered a match if its
     * package and class name exactly match the {@code packageName} and {@code className} parameters
     * and all other criteria for this selector are met.
     *
     * @param packageName The package value to match.
     * @param className The class name value to match.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector clazz(String packageName, String className) {
        checkNotNull(packageName, "packageName cannot be null");
        checkNotNull(className, "className cannot be null");

        return clazz(Pattern.compile(Pattern.quote(
                String.format("%s.%s", packageName, className))));
    }

    /**
     * Sets the class name criteria for matching. A UI element will be considered a match if its
     * class name matches {@code clazz} and all other criteria for this selector are met.
     *
     * @param clazz The class to match.
     * @return A reference to this {@link BySelector}
     */
    public BySelector clazz(Class clazz) {
        checkNotNull(clazz, "clazz cannot be null");

        return clazz(Pattern.compile(Pattern.quote(clazz.getName())));
    }

    /**
     * Sets the class name criteria for matching. A UI element will be considered a match if its
     * full class name matches the {@code className} {@link Pattern} and all other criteria for this
     * selector are met.
     *
     * @param className The {@link Pattern} to be used for matching.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector clazz(Pattern className) {
        checkNotNull(className, "className cannot be null");

        if (mClazz != null) {
            throw new IllegalStateException("Class selector is already defined");
        }
        mClazz = className;
        return this;
    }

    /**
     * Sets the content description criteria for matching. A UI element will be considered a match
     * if its content description exactly matches the {@code contentDescription} parameter and all
     * other criteria for this selector are met.
     *
     * @param contentDescription The exact value to match.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector desc(String contentDescription) {
        checkNotNull(contentDescription, "contentDescription cannot be null");

        return desc(Pattern.compile(Pattern.quote(contentDescription)));
    }

    /**
     * Sets the content description criteria for matching. A UI element will be considered a match
     * if its content description contains the {@code substring} parameter and all other criteria
     * for this selector are met.
     *
     * @param substring The substring to match.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector descContains(String substring) {
        checkNotNull(substring, "substring cannot be null");

        return desc(Pattern.compile(String.format("^.*%s.*$", Pattern.quote(substring))));
    }

    /**
     * Sets the content description criteria for matching. A UI element will be considered a match
     * if its content description starts with the {@code substring} parameter and all other criteria
     * for this selector are met.
     *
     * @param substring The substring to match.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector descStartsWith(String substring) {
        checkNotNull(substring, "substring cannot be null");

        return desc(Pattern.compile(String.format("^%s.*$", Pattern.quote(substring))));
    }

    /**
     * Sets the content description criteria for matching. A UI element will be considered a match
     * if its content description ends with the {@code substring} parameter and all other criteria
     * for this selector are met.
     *
     * @param substring The substring to match.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector descEndsWith(String substring) {
        checkNotNull(substring, "substring cannot be null");

        return desc(Pattern.compile(String.format("^.*%s$", Pattern.quote(substring))));
    }

    /**
     * Sets the content description criteria for matching. A UI element will be considered a match
     * if its content description matches the {@code contentDescription} {@link Pattern} and all
     * other criteria for this selector are met.
     *
     * @param contentDescription The {@link Pattern} to be used for matching.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector desc(Pattern contentDescription) {
        checkNotNull(contentDescription, "contentDescription cannot be null");

        if (mDesc != null) {
            throw new IllegalStateException("Description selector is already defined");
        }
        mDesc = contentDescription;
        return this;
    }

    /**
     * Sets the application package name criteria for matching. A UI element will be considered a
     * match if its application package name exactly matches the {@code applicationPackage}
     * parameter and all other criteria for this selector are met.
     *
     * @param applicationPackage The exact value to match.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector pkg(String applicationPackage) {
        checkNotNull(applicationPackage, "applicationPackage cannot be null");

        return pkg(Pattern.compile(Pattern.quote(applicationPackage)));
    }

    /**
     * Sets the package name criteria for matching. A UI element will be considered a match if its
     * application package name matches the {@code applicationPackage} {@link Pattern} and all other
     * criteria for this selector are met.
     *
     * @param applicationPackage The {@link Pattern} to be used for matching.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector pkg(Pattern applicationPackage) {
        checkNotNull(applicationPackage, "applicationPackage cannot be null");

        if (mPkg != null) {
            throw new IllegalStateException("Package selector is already defined");
        }
        mPkg = applicationPackage;
        return this;
    }

    /**
     * Sets the resource name criteria for matching. A UI element will be considered a match if its
     * resource name exactly matches the {@code resourceName} parameter and all other criteria for
     * this selector are met.
     *
     * @param resourceName The exact value to match.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector res(String resourceName) {
        checkNotNull(resourceName, "resourceName cannot be null");

        return res(Pattern.compile(Pattern.quote(resourceName)));
    }

    /**
     * Sets the resource name criteria for matching. A UI element will be considered a match if its
     * resource package and resource id exactly match the {@code resourcePackage} and
     * {@code resourceId} parameters and all other criteria for this selector are met.
     *
     * @param resourcePackage The resource package value to match.
     * @param resourceId The resouce-id value to match.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector res(String resourcePackage, String resourceId) {
        checkNotNull(resourcePackage, "resourcePackage cannot be null");
        checkNotNull(resourceId, "resourceId cannot be null");

        return res(Pattern.compile(Pattern.quote(
                String.format("%s:id/%s", resourcePackage, resourceId))));
    }

    /**
     * Sets the resource name criteria for matching. A UI element will be considered a match if its
     * resource name matches the {@code resourceName} {@link Pattern} and all other criteria for
     * this selector are met.
     *
     * @param resourceName The {@link Pattern} to be used for matching.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector res(Pattern resourceName) {
        checkNotNull(resourceName, "resourceName cannot be null");

        if (mRes != null) {
            throw new IllegalStateException("Resource name selector is already defined");
        }
        mRes = resourceName;
        return this;
    }

    /**
     * Sets the text value criteria for matching. A UI element will be considered a match if its
     * text value exactly matches the {@code textValue} parameter and all other criteria for this
     * selector are met.
     *
     * @param textValue The exact value to match.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector text(String textValue) {
        checkNotNull(textValue, "textValue cannot be null");

        return text(Pattern.compile(Pattern.quote(textValue)));
    }

    /**
     * Sets the text value criteria for matching. A UI element will be considered a match if its
     * text value contains the {@code substring} parameter and all other criteria for this selector
     * are met.
     *
     * @param substring The substring to match.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector textContains(String substring) {
        checkNotNull(substring, "substring cannot be null");

        return text(Pattern.compile(String.format("^.*%s.*$", Pattern.quote(substring))));
    }

    /**
     * Sets the text value criteria for matching. A UI element will be considered a match if its
     * text value starts with the {@code substring} parameter and all other criteria for this
     * selector are met.
     *
     * @param substring The substring to match.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector textStartsWith(String substring) {
        checkNotNull(substring, "substring cannot be null");

        return text(Pattern.compile(String.format("^%s.*$", Pattern.quote(substring))));
    }

    /**
     * Sets the text value criteria for matching. A UI element will be considered a match if its
     * text value ends with the {@code substring} parameter and all other criteria for this selector
     * are met.
     *
     * @param substring The substring to match.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector textEndsWith(String substring) {
        checkNotNull(substring, "substring cannot be null");

        return text(Pattern.compile(String.format("^.*%s$", Pattern.quote(substring))));
    }

    /** Sets the text value criteria for matching. A UI element will be considered a match if its
     * text value matches the {@code textValue} {@link Pattern} and all other criteria for this
     * selector are met.
     *
     * @param textValue The {@link Pattern} to be used for matching.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector text(Pattern textValue) {
        checkNotNull(textValue, "textValue cannot be null");

        if (mText != null) {
            throw new IllegalStateException("Text selector is already defined");
        }
        mText = textValue;
        return this;
    }


    /**
     * Sets the search criteria to match elements that are checkable or not checkable.
     *
     * @param isCheckable Whether to match elements that are checkable or elements that are not
     * checkable.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector checkable(boolean isCheckable) {
        if (mCheckable != null) {
            throw new IllegalStateException("Checkable selector is already defined");
        }
        mCheckable = isCheckable;
        return this;
    }

    /**
     * Sets the search criteria to match elements that are checked or unchecked.
     *
     * @param isChecked Whether to match elements that are checked or elements that are unchecked.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector checked(boolean isChecked) {
        if (mChecked != null) {
            throw new IllegalStateException("Checked selector is already defined");
        }
        mChecked = isChecked;
        return this;
    }

    /**
     * Sets the search criteria to match elements that are clickable or not clickable.
     *
     * @param isClickable Whether to match elements that are clickable or elements that are not
     * clickable.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector clickable(boolean isClickable) {
        if (mClickable != null) {
            throw new IllegalStateException("Clickable selector is already defined");
        }
        mClickable = isClickable;
        return this;
    }
    /**
     * Sets the search criteria to match elements that are enabled or disabled.
     *
     * @param isEnabled Whether to match elements that are enabled or elements that are disabled.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector enabled(boolean isEnabled) {
        if (mEnabled != null) {
            throw new IllegalStateException("Enabled selector is already defined");
        }
        mEnabled = isEnabled;
        return this;
    }

    /**
     * Sets the search criteria to match elements that are focusable or not focusable.
     *
     * @param isFocusable Whether to match elements that are focusable or elements that are not
     * focusable.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector focusable(boolean isFocusable) {
        if (mFocusable != null) {
            throw new IllegalStateException("Focusable selector is already defined");
        }
        mFocusable = isFocusable;
        return this;
    }

    /**
     * Sets the search criteria to match elements that are focused or unfocused.
     *
     * @param isFocused Whether to match elements that are focused or elements that are unfocused.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector focused(boolean isFocused) {
        if (mFocused != null) {
            throw new IllegalStateException("Focused selector is already defined");
        }
        mFocused = isFocused;
        return this;
    }

    /**
     * Sets the search criteria to match elements that are long clickable or not long clickable.
     *
     * @param isLongClickable Whether to match elements that are long clickable or elements that are
     * not long clickable.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector longClickable(boolean isLongClickable) {
        if (mLongClickable != null) {
            throw new IllegalStateException("Long Clickable selector is already defined");
        }
        mLongClickable = isLongClickable;
        return this;
    }

    /**
     * Sets the search criteria to match elements that are scrollable or not scrollable.
     *
     * @param isScrollable Whether to match elements that are scrollable or elements that are not
     * scrollable.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector scrollable(boolean isScrollable) {
        if (mScrollable != null) {
            throw new IllegalStateException("Scrollable selector is already defined");
        }
        mScrollable = isScrollable;
        return this;
    }

    /**
     * Sets the search criteria to match elements that are selected or not selected.
     *
     * @param isSelected Whether to match elements that are selected or elements that are not
     * selected.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector selected(boolean isSelected) {
        if (mSelected != null) {
            throw new IllegalStateException("Selected selector is already defined");
        }
        mSelected = isSelected;
        return this;
    }

    /** Sets the search criteria to match elements that are at a certain depth. */
    public BySelector depth(int exactDepth) {
        return depth(exactDepth, exactDepth);
    }

    /** Sets the search criteria to match elements that are in a range of depths. */
    public BySelector depth(int min, int max) {
        if (min < 0) {
            throw new IllegalArgumentException("min cannot be negative");
        }
        if (max < 0) {
            throw new IllegalArgumentException("max cannot be negative");
        }
        if (mMinDepth != null) {
            throw new IllegalStateException("Minimum Depth selector is already defined");
        }
        if (mMaxDepth != null) {
            throw new IllegalStateException("Maximum Depth selector is already defined");
        }
        mMinDepth = min;
        mMaxDepth = max;
        return this;
    }

    /** Sets the search criteria to match elements that are at least a certain depth. */
    public BySelector minDepth(int min) {
        if (min < 0) {
            throw new IllegalArgumentException("min cannot be negative");
        }
        if (mMinDepth != null) {
            throw new IllegalStateException("Depth selector is already defined");
        }
        mMinDepth = min;
        return this;
    }

    /** Sets the search criteria to match elements that are no more than a certain depth. */
    public BySelector maxDepth(int max) {
        if (max < 0) {
            throw new IllegalArgumentException("max cannot be negative");
        }
        if (mMaxDepth != null) {
            throw new IllegalStateException("Depth selector is already defined");
        }
        mMaxDepth = max;
        return this;
    }

    /**
     * Adds a child selector criteria for matching. A UI element will be considered a match if it
     * has a child element (direct descendant) which matches the {@code childSelector} and all
     * other criteria for this selector are met. If specified more than once, matches must be found
     * for all {@code childSelector}s.
     *
     * @param childSelector The selector used to find a matching child element.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector hasChild(BySelector childSelector) {
        checkNotNull(childSelector, "childSelector cannot be null");

        return hasDescendant(childSelector, 1);
    }

    /**
     * Adds a descendant selector criteria for matching. A UI element will be considered a match if
     * it has a descendant element which matches the {@code descendantSelector} and all other
     * criteria for this selector are met. If specified more than once, matches must be found for
     * all {@code descendantSelector}s.
     *
     * @param descendantSelector The selector used to find a matching descendant element.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector hasDescendant(BySelector descendantSelector) {
        checkNotNull(descendantSelector, "descendantSelector cannot be null");

        mChildSelectors.add(descendantSelector);
        return this;
    }

    /**
     * Adds a descendant selector criteria for matching. A UI element will be considered a match if
     * it has a descendant element which matches the {@code descendantSelector} and all other
     * criteria for this selector are met. If specified more than once, matches must be found for
     * all {@code descendantSelector}s.
     *
     * @param descendantSelector The selector used to find a matching descendant element.
     * @param maxDepth The maximum depth under the element to search the descendant.
     * @return A reference to this {@link BySelector}.
     */
    public BySelector hasDescendant(BySelector descendantSelector, int maxDepth) {
        checkNotNull(descendantSelector, "descendantSelector cannot be null");

        descendantSelector.mMaxDepth = maxDepth;
        mChildSelectors.add(descendantSelector);
        return this;
    }

    /**
     * Returns a {@link String} representation of this {@link BySelector}. The format is
     * "BySelector [&lt;KEY&gt;='&lt;VALUE&gt; ... ]". Each criteria is listed as a key-value pair
     * where the key is the name of the criteria expressed in all caps (e.g. CLAZZ, RES, etc).
     */
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder("BySelector [");
        if (mClazz != null) {
            builder.append("CLASS='").append(mClazz).append("', ");
        }
        if (mDesc != null) {
            builder.append("DESC='").append(mDesc).append("', ");
        }
        if (mPkg != null) {
            builder.append("PKG='").append(mPkg).append("', ");
        }
        if (mRes != null) {
            builder.append("RES='").append(mRes).append("', ");
        }
        if (mText != null) {
            builder.append("TEXT='").append(mText).append("', ");
        }
        if (mChecked != null) {
            builder.append("CHECKED='").append(mChecked).append("', ");
        }
        if (mCheckable != null) {
            builder.append("CHECKABLE='").append(mCheckable).append("', ");
        }
        if (mClickable != null) {
            builder.append("CLICKABLE='").append(mClickable).append("', ");
        }
        if (mEnabled != null) {
            builder.append("ENABLED='").append(mEnabled).append("', ");
        }
        if (mFocused != null) {
            builder.append("FOCUSED='").append(mFocused).append("', ");
        }
        if (mFocusable != null) {
            builder.append("FOCUSABLE='").append(mFocusable).append("', ");
        }
        if (mLongClickable != null) {
            builder.append("LONGCLICKABLE='").append(mLongClickable).append("', ");
        }
        if (mScrollable != null) {
            builder.append("SCROLLABLE='").append(mScrollable).append("', ");
        }
        if (mSelected != null) {
            builder.append("SELECTED='").append(mSelected).append("', ");
        }
        for (BySelector childSelector : mChildSelectors) {
            builder.append("CHILD='").append(childSelector.toString().substring(11)).append("', ");
        }
        builder.setLength(builder.length() - 2);
        builder.append("]");
        return builder.toString();
    }

    private static <T> T checkNotNull(T value, String message) {
        if (value == null) {
            throw new NullPointerException(message);
        }
        return value;
    }
}