Как нажать кнопку в настройках, используя AccessibilityService?
Я хочу нажать кнопку в настройках Android с помощью AccessibilityService, как сделал greenify, но не могу найти конкретную кнопку. Помогите мне, пожалуйста.
MyAccessibilityService.java:
public class MyAccessibilityService extends AccessibilityService {
private static final String TAG = MyAccessibilityService.class
.getSimpleName();
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
Log.i(TAG, "ACC::onAccessibilityEvent: " + event.getEventType());
//TYPE_WINDOW_STATE_CHANGED = 32,
if (AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED == event.getEventType()) {
AccessibilityNodeInfo nodeInfo = event.getSource();
Log.i(TAG, "ACC::onAccessibilityEvent: nodeInfo=" + nodeInfo.getText());
List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId("com.android.settings:id/left_button");
for (AccessibilityNodeInfo node : list) {
Log.i(TAG, "ACC::onAccessibilityEvent: " + event.getEventType()
+ " " + node);
}
РЕДАКТИРОВАТЬ:
Только когда тип TYPE_WINDOW_STATE_CHANGED, я мог получить объект nodeInfo.
2 ответа
Откройте Appinfo одного приложения с кнопкой принудительного закрытия, чтобы проверить:
public class MyAccessibilityService extends AccessibilityService {
private static final String TAG = MyAccessibilityService.class
.getSimpleName();
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
Log.i(TAG, "ACC::onAccessibilityEvent: " + event.getEventType());
//TYPE_WINDOW_STATE_CHANGED == 32
if (AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED == event
.getEventType()) {
AccessibilityNodeInfo nodeInfo = event.getSource();
Log.i(TAG, "ACC::onAccessibilityEvent: nodeInfo=" + nodeInfo);
if (nodeInfo == null) {
return;
}
List<AccessibilityNodeInfo> list = nodeInfo
.findAccessibilityNodeInfosByViewId("com.android.settings:id/left_button");
for (AccessibilityNodeInfo node : list) {
Log.i(TAG, "ACC::onAccessibilityEvent: left_button " + node);
node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
list = nodeInfo
.findAccessibilityNodeInfosByViewId("android:id/button1");
for (AccessibilityNodeInfo node : list) {
Log.i(TAG, "ACC::onAccessibilityEvent: button1 " + node);
node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
}
}
@Override
public void onServiceConnected() {
Log.i(TAG, "ACC::onServiceConnected: ");
}
@Override
public void onInterrupt() {
// TODO Auto-generated method stub
}
}
Выбранный ответ работает на API 18 и выше, так как он использует findAccessibilityNodeInfosByViewId, который был добавлен в API 18. В итоге я написал этот класс для поддержки API 17 и ниже.
ResourcesCompat
Класс находит ресурсы, идентифицированные с данным активом, который в нашем случае должен быть активностью настройки Android. Вы можете получить ComponentName действия с настройками, вызвав эту функцию при обработке события доступности в вашей службе доступности.
public static ComponentName getForegroundActivity(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
ComponentName topActivity = taskInfo.get(0).topActivity;
return topActivity;
}
Хорошее место для звонка init(...)
в onAccessibilityEvent
когда вы впервые обрабатываете событие thecr0w, как описано в thecr0w.
public class ResourcesCompat {
private final static String RESOURCE_TYPE = "string";
private String mResourcesPackageName;
private Resources mResources;
/**
* Find the resource file for a specific activity
*
* @param context
* @param settingsPackageName
* @param settingsClassName
*/
public void init(Context context, String settingsPackageName, String settingsClassName) {
try {
mResourcesPackageName = settingsPackageName;
ComponentName settingsComponentName = new ComponentName(settingsPackageName, settingsPackageName + settingsClassName);
mResources = context.getPackageManager().getResourcesForActivity(settingsComponentName);
} catch (PackageManager.NameNotFoundException e) {
}
}
/**
* Return the localised string for the given resource name.
* @param resourceName The name of the resource definition in strings.xml
*/
public String getString(String resourceName) {
int resourceId = getIdentifier(resourceName);
return resourceId > 0 ? mResources.getString(resourceId) : null;
}
/**
* Return a resource identifier for the given resource name.
* @param resourceName The name of the desired resource.
* @return int The associated resource identifier. Returns 0 if no such resource was found. (0 is not a valid resource ID.)
*/
private int getIdentifier(String resourceName) {
return mResources.getIdentifier(resourceName, RESOURCE_TYPE, mResourcesPackageName);
}
}
Некоторым производителям нравится перемещать классы и переименовывать строки по умолчанию (кашель Samsung, кашель Xiomi, кашель). Поэтому убедитесь, что вы охватываете все случаи и обрабатываете ошибки и исключения.
Наконец, найдите свой вид по имени. Вот, id
может быть 'force_stop' например
private List<AccessibilityNodeInfo> findAccessibilityNodeInfosByName(AccessibilityNodeInfo source, String id) {
String nodeText = mResourcesCompat.getString(id);
if (nodeText != null) {
return source.findAccessibilityNodeInfosByText(nodeText);
}
return null;
}
Вот что я использовал:
override fun onAccessibilityEvent(event: AccessibilityEvent) {
val eventPackageName = event.packageName
val className = event.className
val source: AccessibilityNodeInfo? = event.source
val targetAppPackageName=...
val targetViewId=...
val viewsToCheck = rootInActiveWindow?.findAccessibilityNodeInfosByViewId("$targetAppPackageName:id/targetViewId")?.getOrNull(0)
viewsToCheck?.performAction(AccessibilityNodeInfo.ACTION_CLICK)
...