package com.lukekorth.deviceautomator; import androidx.test.uiautomator.By; import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.UiObject; import androidx.test.uiautomator.UiSelector; import java.util.regex.Pattern; /** * A clean interface for specifying {@link UiObject}s to interact with. */ public class UiObjectMatcher { private UiSelector mUiSelector; private BySelector mBySelector; public UiObjectMatcher(UiSelector uiSelector, BySelector bySelector) { mUiSelector = uiSelector; mBySelector = bySelector; } /** * Find a view based on the prefixed text in the view. The matching is case-insensitive. * * @param text Prefix to search for. * @return */ public static UiObjectMatcher withTextStartingWith(String text) { return withTextStartingWith(text, null); } /** * Find a view based on the prefixed text in the view. The matching is case-insensitive. * * @param text Prefix to search for. * @param klass Expected class of the view. * @return */ public static UiObjectMatcher withTextStartingWith(String text, Class klass) { UiSelector uiSelector = new UiSelector() .textStartsWith(text); BySelector bySelector = By.textStartsWith(text); if (klass != null) { uiSelector = uiSelector.className(klass); bySelector.clazz(klass); } return new UiObjectMatcher(uiSelector, bySelector); } /** * Find a view based on the text contained within the view. The matching is case-sensitive. * * @param text Text to search for inside a view. * @return */ public static UiObjectMatcher withTextContaining(String text) { return withTextContaining(text, null); } /** * Find a view based on the text contained within the view. The matching is case-sensitive. * * @param text Text to search for inside a view. * @param klass Expected class of the view. * @return */ public static UiObjectMatcher withTextContaining(String text, Class klass) { UiSelector uiSelector = new UiSelector() .textContains(text); BySelector bySelector = By.textContains(text); if (klass != null) { uiSelector = uiSelector.className(klass); bySelector.clazz(klass); } return new UiObjectMatcher(uiSelector, bySelector); } /** * Find a view based on the exact text contained within the view. Matching is case-insensitive. * * @param text Exact text in the view. * @return */ public static UiObjectMatcher withText(String text) { return withText(text, null); } /** * Find a view based on the exact text contained within the view. Matching is case-insensitive. * * @param text Exact text in the view. * @param klass Expected class of the view. * @return */ public static UiObjectMatcher withText(String text, Class klass) { Pattern pattern = Pattern.compile("(?i)" + Pattern.quote(text)); UiSelector uiSelector = new UiSelector() .textMatches(pattern.pattern()); BySelector bySelector = By.text(pattern); if (klass != null) { uiSelector = uiSelector.className(klass); bySelector.clazz(klass); } return new UiObjectMatcher(uiSelector, bySelector); } /** * Find a view based on the content description of the view. 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. * * On {@link android.os.Build.VERSION_CODES#LOLLIPOP} devices and higher than matcher can be * used to match views in {@link android.webkit.WebView}s and browsers. * * @param text Content description of the view. * @return */ public static UiObjectMatcher withContentDescription(String text) { return withContentDescription(text, null); } /** * Find a view based on the content description of the view. 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. * * On {@link android.os.Build.VERSION_CODES#LOLLIPOP} devices and higher than matcher can be * used to match views in {@link android.webkit.WebView}s and browsers. * * @param text Content description of the view. * @param klass Expected class of the view. * @return */ public static UiObjectMatcher withContentDescription(String text, Class klass) { UiSelector uiSelector = new UiSelector() .description(text); BySelector bySelector = By.desc(text); if (klass != null) { uiSelector = uiSelector.className(klass); bySelector.clazz(klass); } return new UiObjectMatcher(uiSelector, bySelector); } /** * Find a view based on the resource id. Resource ids should be the fully qualified id, * ex: com.android.browser:id/url * * @param id The fully qualified id of the view, ex: com.android.browser:id/url * @return */ public static UiObjectMatcher withResourceId(String id) { return withResourceId(id, null); } /** * Find a view based on the resource id. Resource ids should be the fully qualified id, * ex: com.android.browser:id/url * * @param id The fully qualified id of the view, ex: com.android.browser:id/url * @param klass Expected class of the view. * @return */ public static UiObjectMatcher withResourceId(String id, Class klass) { UiSelector uiSelector = new UiSelector() .resourceId(id); BySelector bySelector = By.res(id); if (klass != null) { uiSelector = uiSelector.className(klass); bySelector.clazz(klass); } return new UiObjectMatcher(uiSelector, bySelector); } /** * Find a view based on it's class. * * @param klass The class of the view to find. * @return */ public static UiObjectMatcher withClass(Class klass) { UiSelector uiSelector = new UiSelector() .className(klass); BySelector bySelector = By.clazz(klass); return new UiObjectMatcher(uiSelector, bySelector); } /** * Specify the child matcher for use with {@link UiSelector#childSelector(UiSelector)}. * * Use this selector to narrow the search scope to child widgets under a specific parent widget. * * @param childMatcher The {@link UiObjectMatcher} to use as the child matcher. * @return */ public UiObjectMatcher childMatcher(UiObjectMatcher childMatcher) { mUiSelector = mUiSelector.childSelector(childMatcher.getUiSelector()); return this; } /** * 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. * * @param instance The instance to find. * @return */ public UiObjectMatcher instance(int instance) { mUiSelector = mUiSelector.instance(instance); return this; } public UiSelector getUiSelector() { return mUiSelector; } public UiObject getUiObject(UiDevice device) { return device.findObject(getUiSelector()); } public BySelector getBySelector() { return mBySelector; } }