Android: How do you check if a particular AccessibilityService is enabled
I've written an Android app that requires the use of the AccessibilityService
.
I know 开发者_如何学Gohow to check to see if Accessibility is enabled or disabled on the phone, but I cannot work out a way to determine if my app has been specifically enabled within the accessibility menu.
I'm wanting to prompt the user if the AccessibilityService
is not running, but can't find a good way of doing this.
Is there any API methods that I might be missing that would let me know which accessibility services are enabled on the device?
Since API Level 14, it is also possible to obtain the enabled accessibility services through the AccessibilityManager
:
public static boolean isAccessibilityServiceEnabled(Context context, Class<? extends AccessibilityService> service) {
AccessibilityManager am = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
List<AccessibilityServiceInfo> enabledServices = am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
for (AccessibilityServiceInfo enabledService : enabledServices) {
ServiceInfo enabledServiceInfo = enabledService.getResolveInfo().serviceInfo;
if (enabledServiceInfo.packageName.equals(context.getPackageName()) && enabledServiceInfo.name.equals(service.getName()))
return true;
}
return false;
}
Usage:
boolean enabled = isAccessibilityServiceEnabled(context, MyAccessibilityService.class);
I worked this one out myself in the end:
public boolean isAccessibilityEnabled() {
int accessibilityEnabled = 0;
final String LIGHTFLOW_ACCESSIBILITY_SERVICE = "com.example.test/com.example.text.ccessibilityService";
boolean accessibilityFound = false;
try {
accessibilityEnabled = Settings.Secure.getInt(this.getContentResolver(),android.provider.Settings.Secure.ACCESSIBILITY_ENABLED);
Log.d(LOGTAG, "ACCESSIBILITY: " + accessibilityEnabled);
} catch (SettingNotFoundException e) {
Log.d(LOGTAG, "Error finding setting, default accessibility to not found: " + e.getMessage());
}
TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');
if (accessibilityEnabled==1) {
Log.d(LOGTAG, "***ACCESSIBILIY IS ENABLED***: ");
String settingValue = Settings.Secure.getString(getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
Log.d(LOGTAG, "Setting: " + settingValue);
if (settingValue != null) {
TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
splitter.setString(settingValue);
while (splitter.hasNext()) {
String accessabilityService = splitter.next();
Log.d(LOGTAG, "Setting: " + accessabilityService);
if (accessabilityService.equalsIgnoreCase(ACCESSIBILITY_SERVICE_NAME)){
Log.d(LOGTAG, "We've found the correct setting - accessibility is switched on!");
return true;
}
}
}
Log.d(LOGTAG, "***END***");
}
else {
Log.d(LOGTAG, "***ACCESSIBILIY IS DISABLED***");
}
return accessibilityFound;
}
Checking if the service is enabled
/**
* Based on {@link com.android.settingslib.accessibility.AccessibilityUtils#getEnabledServicesFromSettings(Context,int)}
* @see <a href="https://github.com/android/platform_frameworks_base/blob/d48e0d44f6676de6fd54fd8a017332edd6a9f096/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java#L55">AccessibilityUtils</a>
*/
public static boolean isAccessibilityServiceEnabled(Context context, Class<?> accessibilityService) {
ComponentName expectedComponentName = new ComponentName(context, accessibilityService);
String enabledServicesSetting = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (enabledServicesSetting == null)
return false;
TextUtils.SimpleStringSplitter colonSplitter = new TextUtils.SimpleStringSplitter(':');
colonSplitter.setString(enabledServicesSetting);
while (colonSplitter.hasNext()) {
String componentNameString = colonSplitter.next();
ComponentName enabledService = ComponentName.unflattenFromString(componentNameString);
if (enabledService != null && enabledService.equals(expectedComponentName))
return true;
}
return false;
}
Usage:
boolean enabled = isAccessibilityServiceEnabled(context, MyAccessibilityService.class);
Detecting when the service is enabled or disabled
Make a callback:
ContentObserver observer = new ContentObserver() {
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
boolean accessibilityServiceEnabled = isAccessibilityServiceEnabled(context, MyAccessibilityService.class);
//Do something here
}
};
Subscribe:
Uri uri = Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
context.getContentResolver().registerContentObserver(uri, false, observer);
Unsubscribe when you're done:
context.getContentResolver().unregisterContentObserver(observer);
Note that this doesn't work with the getEnabledAccessibilityServiceList()
approach since its values are out-of-sync with the Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
values. That's why I think using Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
is a better one-size-fits-all approach.
Get the ID when your activity service just started. In your Activity service OnSeriviceCeonnected after all the initialize calls. Use this...
AccessibilityServiceInfo serviceInfo = this.getServiceInfo();
String accessibilityId = serviceInfo.getId();
Requires Jelly Bean API
Then you can use Martin's code (isAccessibilityEnabled) to check running services.
Maybe's too late but here's how I check the Accessibility Service's status:
$ adb shell dumpsys accessibility
Result:
ACCESSIBILITY MANAGER (dumpsys accessibility)
User state[attributes:{id=0, currentUser=true, accessibilityEnabled=false, touchExplorationEnabled=false, displayMagnificationEnabled=false}
services:{}]
I was looking for the same solution and decided to do this check in my AccessabilityService. It works for me and maybe will be helpful for somebody else.
class AppConnectorService : AccessibilityService() {
companion object {
val connected = MutableStateFlow(false)
}
override fun onAccessibilityEvent(event: AccessibilityEvent?) {
Timber.d("!!! $event")
}
override fun onInterrupt() {
TODO("Not yet implemented")
}
override fun onServiceConnected() {
super.onServiceConnected()
connected.tryEmit(true)
Timber.d("!!! AccessibilityService is connected!!!!!")
}
override fun onDestroy() {
super.onDestroy()
connected.tryEmit(false)
Timber.d("!!! AccessibilityService is destroyed!!!!")
}
}
And to check and listen for changes just use. In Compose:
val isConnected by AppConnectorService.connected.collectAsState()
or coroutine:
coroutineScope.launch {
AppConnectorService.connected.collect {
}
}
A good solution I've come up with is to run your AccessibilityService in a separate process. You can add an android:process
attribute in the manifest, e.g.
<service
android:name=".ExampleService"
android:process="com.example.service"
...
Now your service will be running in a separate process with the given name. From your app you can call
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
activityManager.runningAppProcesses.any { it.processName == "com.example.service" }
Which will return true
if the service is running and false
otherwise.
IMPORTANT: note that it will show you when your service was started, but when you disable it (meaning, after system unbinds from it) the process can still be alive. So you can simply force it's removal:
override fun onUnbind(intent: Intent?): Boolean {
stopSelf()
return super.onUnbind(intent)
}
override fun onDestroy() {
super.onDestroy()
killProcess(Process.myPid())
}
Then it works perfectly. I see this method more robust than reading values from settings, because it shows the exact thing needed: whether the service is running or not.
AccessibilityManager accessibilityManager = (AccessibilityManager)context.getSystemService(Context.ACCESSIBILITY_SERVICE);
List<AccessibilityServiceInfo> runningservice = accessibilityManager.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
accessibilityManager.addAccessibilityStateChangeListener(new AccessibilityManager.AccessibilityStateChangeListener() {
@Override
public void onAccessibilityStateChanged(boolean b) {
Toast.makeText(MainActivity.this, "permission "+b, Toast.LENGTH_SHORT).show();
}
});
Listner will be called whenever state is changed you can keep track of the boolean to check the permission this is by far the simplest and lightest solution to check permission
this need more times
AccessibilityManager am = (AccessibilityManager) context
.getSystemService(Context.ACCESSIBILITY_SERVICE);
精彩评论