I want to click button in android settings using AccessibilityService like greenify did, but I cannot find the specific button. please help me.
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);
}
EDIT:
Only when type is TYPE_WINDOW_STATE_CHANGED , I could get the nodeInfo object.
Go to Settings > Accessibility. The Global Action Bar Service is installed on your device. The accessibility service requests permission to observe user actions, retrieve window content, and perform gestures on behalf of the user! When using a third party accessibility service, make sure you really trust the source!
Open your Settings app, search for "Accessibility", and tap on the first result. Next, tap one of these three buttons (the button that you see will depend on your device): Installed Services, Downloaded Services, or Downloaded apps. Tap A2U Android. Make sure the A2U Android shortcut is toggled OFF.
If you want an easy-to-use button overlay, turn on the Accessibility Menu on your Android device. This will introduce a floating button that will give you the option to take a screenshot, navigate your screen, control the volume, power off the device, and more.
Open one app's Appinfo with force close button enabled to test:
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
}
}
The selected answer works on API 18 and above since it relays on findAccessibilityNodeInfosByViewId which was added in API 18. I ended up writing this class to support API 17 and below.
ResourcesCompat
class finds the resources identified with the given activty which in our case should be Android's Setting activity. You can get the ComponentName of settings activity by calling this function when handling the accessibility event in you Accessibility Service.
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;
}
A good place to call init(...)
is in onAccessibilityEvent
when you're first handling the TYPE_WINDOW_STATE_CHANGED event as thecr0w described.
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);
}
}
Some manufacturers like to move classes around and rename default strings (cough Samsung cough Xiomi cough) So make sure you cover all cases and handle errors and exceptions.
Finally, find your view by name. here, id
can be 'force_stop' for example
private List<AccessibilityNodeInfo> findAccessibilityNodeInfosByName(AccessibilityNodeInfo source, String id) {
String nodeText = mResourcesCompat.getString(id);
if (nodeText != null) {
return source.findAccessibilityNodeInfosByText(nodeText);
}
return null;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With