/* * 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 com.android.utils; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Resources; import android.os.Bundle; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import android.text.TextUtils; import com.android.utils.compat.CompatUtils; import java.lang.reflect.Field; import java.util.Collections; import java.util.LinkedList; import java.util.List; /** * Contains static UI automation helper methods. */ public class AutomationUtils { /** * Returns the internal Android string corresponding to a specific resource name. * * @param context The parent context. * @param resName The short name of the resource, e.g. "ok". * @return An internal string, or {@code null}. */ public static String getInternalString(Context context, String resName) { final Class<?> internalRes = CompatUtils.getClass("com.android.internal.R$string"); final Field resField = CompatUtils.getField(internalRes, resName); final int resId = (Integer) CompatUtils.getFieldValue(null, 0, resField); if (resId <= 0) { return null; } return context.getString(resId); } /** * Returns the string corresponding to a resource name in a specific package. * * @param context The parent context. * @param packageName The application's package name, e.g. "com.android.settings". * @param resName The short name of the resource, e.g. "ok". * @return A package-specific string, or {@code null}. */ public static String getPackageString(Context context, String packageName, String resName) { final PackageManager pm = context.getPackageManager(); final Resources packageRes; try { packageRes = pm.getResourcesForApplication(packageName); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); return null; } final int resId = packageRes.getIdentifier(resName, "string", packageName); if (resId <= 0) { return null; } final String result; try { result = packageRes.getString(resId); } catch (Resources.NotFoundException e) { e.printStackTrace(); return null; } return result; } /** * Attempts to perform an accessibility action on a view that is an * instance of the class {@code className}, the text or content * description {@code text}, and exists under a parent view {@code root}. * * @param root The parent view's node. * @param className The class name to match. * @param text The text or content description to match. * @param action The action to perform. * @param arguments The action arguments. * @return {@code true} if the action was performed. */ public static boolean performActionOnView(AccessibilityNodeInfoCompat root, CharSequence className, String text, int action, Bundle arguments) { final List<AccessibilityNodeInfoCompat> matches = findViewsWithText(root, className, text); if (matches.size() != 1) { return false; } final AccessibilityNodeInfoCompat node = matches.get(0); return PerformActionUtils.performAction(node, action, arguments); } /** * Returns a list of nodes under the {@code root} node that match the * class specified by {@code className} and exactly match the text or * content description specified by {@code text}. */ private static List<AccessibilityNodeInfoCompat> findViewsWithText( AccessibilityNodeInfoCompat root, CharSequence className, String text) { final List<AccessibilityNodeInfoCompat> nodes = root.findAccessibilityNodeInfosByText(text); final List<AccessibilityNodeInfoCompat> results = new LinkedList<>(); for (AccessibilityNodeInfoCompat node : nodes) { if (nodeMatchesFilter(node, className, text)) { results.add(node); } } return Collections.unmodifiableList(results); } /** * Returns whether a node matches the class specified by * {@code className} and exactly match the text or content description * specified by {@code text}. */ private static boolean nodeMatchesFilter(AccessibilityNodeInfoCompat node, CharSequence referenceClassName, String findText) { return ClassLoadingCache.checkInstanceOf(node.getClassName(), referenceClassName) && (TextUtils.equals(findText, node.getText()) || TextUtils.equals(findText, node.getContentDescription())); } }