How can I programmatically start an AccessibilityService? - android

I'm using an accessibility-service to check which application I'm using for-example I am using WhatsApp and I am trying to get its package name and compare it with my database if the package exists in my database then I'm closing the app so that I can't use this app, I just working on my idea. It's been days since I got this problem and I can't find the solutions to my problems can anyone help me. Thank you
The Problems that I am facing are listed below:
Accessibility service is automatically closing when I try to close my app. If I don't close the app it's working fine. For-example If I close my main application the service will turn off. Is there any way to stop that?
When I found the package name in my database what I'm doing is taking the user on the main screen "Home Page". That is not a good way to do it, can someone help me with this one. I need to properly block that app.
Is there any way to close the application which I am trying to block from recent apps?
My code is listed below:
Accessibility service class
override fun onServiceConnected() {
super.onServiceConnected()
val info = serviceInfo
info.eventTypes = AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
info.flags = AccessibilityServiceInfo.DEFAULT
info.flags = AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
info.flags = AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS
info.flags = AccessibilityServiceInfo.FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY
info.flags = AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS
info.feedbackType = AccessibilityServiceInfo.FEEDBACK_ALL_MASK
info.notificationTimeout = 100
info.packageNames = null
serviceInfo = info
}
override fun onAccessibilityEvent(event: AccessibilityEvent) {
if(AccessibilityEvent.eventTypeToString(event.eventType).contains("WINDOW")){
val nodeInfo = event.source
dfs(nodeInfo)
}
if (event.packageName != null) {
getAllBlockedApps(packageName = event.packageName.toString())
return
}
}
Block Application Code
val startMain = Intent(Intent.ACTION_MAIN)
startMain.addCategory(Intent.CATEGORY_HOME)
startMain.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startMain.flags = Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
startActivity(startMain)

Related

On Android, how to check the phone screen is casting?

I need to check if the Android phone my app runs on is using casting which is enabled outside of my app.
It seems CastSession or SessionManager can provide the session related to my app which is not helpful for me.
For example, I can start casting with an app called xx which will cast or mirror the entire screen of my phone. Now, I need to notify when I open my app that the phone's screen is casting/mirroring so I can prevent showing specific content on my app.
I checked it with the code below:
val isCastingEnabledLiveData = MutableLiveData<Boolean>()
fun isCastingEnabled(context: Context): Boolean {
val mediaRouter = MediaRouter.getInstance(context)
if (mediaRouter.routes.size <= 1) {
isCastingEnabledLiveData.value = false
return
}
val selector = MediaRouteSelector.Builder()
.addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)
.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
.build()
mediaRouter.addCallback(selector, object : MediaRouter.Callback() {
override fun onRouteChanged(router: MediaRouter?, route: MediaRouter.RouteInfo?) {
super.onRouteChanged(router, route)
isCastingEnabledLiveData.value = if (route != mediaRouter.defaultRoute) {
route?.connectionState != MediaRouter.RouteInfo.CONNECTION_STATE_DISCONNECTED
} else false
}
})
}
you can check whether the phone screen is casting or not by using the MediaRouter class.
Here is an example of how you could check if the phone screen is casting:
MediaRouter mediaRouter = (MediaRouter)
getSystemService(Context.MEDIA_ROUTER_SERVICE);
MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute();
if(route.isDefault()){
// Screen is not casting
} else {
// Screen is casting
}
This code uses the getSelectedRoute() method of the MediaRouter class to get the currently selected route. If the returned RouteInfo object is the default route, then the screen is not casting, otherwise it is.
Please note that this code uses the getSystemService(Context.MEDIA_ROUTER_SERVICE) method to get an instance of the MediaRouter class, so it should be called from an Activity or Service context.
Additionally, you could also use MediaRouter.Callback and MediaRouter.addCallback to set a callback to monitor the state of the casting, so you could get the updates on the casting state change as well.

Get package name of apps running in foreground (Kotlin)

I have been struggling with an issue where I want to access the package name of apps
running in foreground and not being the same app as mine. But as far as I am concerned
it is not possible as long as I have to give a local Context parametrized into the method
which returns exactly that.
Therefore I wanted to ask if there is a way of doing exactly this but not only on my App itself but globally?
I tried doing this from another StackOverflow thread:
fun getCurrentForegroundRunningApp(context: Context): String {
val am = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val appProcesses = am.runningAppProcesses
for (appProcessInfo in appProcesses) {
Log.i("For all:: ",""+appProcessInfo.processName)
if (appProcessInfo.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
return appProcessInfo.processName
} else {
return "There is no app in the foreground"
}
}
return "No App in the foreground"
}
I expected for example that it returns how I am running com.etc.somepackage that isn't what
the app package my app is called is. It only returns such value on my app but not on any other.

JetpackCompose how to createChooser and listen for result

I want to show a system dialog to user to select from available applications for sharing text from my app. I can do this by using createChooser function from Intent class. But i also want to listen for the system dialog result, so that i can disable/enable my share button to prevent creating multiple system dialogs overlapping each other.
To do this i need to know whenever the dialog is dismissed or an app option is selected by the user. So i need the result of the chooser Dialog i have created.
I was able to get the selected app, but was not able to listen the dismiss event for the system dialog because Intent.ACTION_CLOSE_SYSTEM_DIALOGS event is deprecated for third party applications. So is there any other way on how to know when the system dialog is closed?
Thanks in advance.
I was able to listen the result using rememberLauncherForActivityResult Composable function by combining it with ActivityResultContracts.StartActivityForResult abstract class. you can see the usage example i have implemented below. Please share your opinions/corrections or alternatives for my problem.
var shareEnabled by remember { mutableStateOf(true) }
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
// you can use the ActivityResult(it) here
shareEnabled = true
}
Button(
onClick = {
shareEnabled = false
launcher.launch(getShareText().shareExternal())
},
enabled = shareEnabled
)
shareExternal is an extension function that creates and returns the chooser Intent;
fun String.shareExternal(): Intent {
val dataToShare = this
val sendIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, dataToShare)
type = "text/plain"
}
return Intent.createChooser(sendIntent, null)
}

Accessibility Event occurs only once after enabling the Accessibility Service

I've just started coding my app which uses Accessibility Service. I'll explain my problem in detail.
Below is my onServiceConnected method of MyAccessibilityService class
protected void onServiceConnected() {
super.onServiceConnected();
AccessibilityServiceInfo info = getServiceInfo();
info.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.WINDOWS_CHANGE_ADDED;
info.packageNames = new String[]
{THIRD_PARTY_APP_PACKAGE};
info.notificationTimeout = 100;
this.setServiceInfo(info);
}
The app is detecting events in onAccessibilityEvent() method
public void onAccessibilityEvent(AccessibilityEvent event) {
AccessibilityNodeInfo source = event.getSource();
if (source == null) {
return;
}
Toast.makeText(this, "Event Occured", Toast.LENGTH_SHORT).show();
}
Now when I open the third party app, I'm getting the Toast "Event occured". Now I close the app and when I open it again, the method is not called and I don't get any Toast. To make it working again, I have to disable the accessibility service of my app in my phone's Settings and again enable it.
I know I'm missing something and my only question is what should be the additional part of code or what modifications I need in order to detect the event every time I open the third party app?
Have you tried getting rid of the notification timeout? You probably don't need it, and it isn't the best-tested API.

AccessibilityService setServiceInfo method's changes don't seem to take effect outside onServiceConnected

Quoting the docs for setServiceInfo
"You can call this method any time but the info will be picked up after the system has bound to this service and when this method is called thereafter."
Accessibility has been enabled for my app and I have set the serviceInfo inside the onServiceConnected method and I am receiving events for those apps and all is well.
Now ,the problem is I'm trying to modify the package list by calling setServiceInfo outside the onServiceConnected method, but the changes are not taking effect( i.e i'm still receiving events from the packages in the package list specified earlier which are absent in the modified package list).
#Override
protected void onServiceConnected() {
AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo();
accessibilityServiceInfo.packageNames = new String[]{"packageA","packageB"};
accessibilityServiceInfo.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
| AccessibilityEvent.TYPE_VIEW_SCROLLED | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
| AccessibilityEvent.TYPE_VIEW_FOCUSED | AccessibilityEvent.TYPE_VIEW_SELECTED
| AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
accessibilityServiceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
accessibilityServiceInfo.notificationTimeout = 100;
accessibilityServiceInfo.flags = (AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS | accessibilityServiceInfo.flags);
setServiceInfo(accessibilityServiceInfo);
}
#Override
public void onAccessibilityEvent(final AccessibilityEvent event) {
AccessibilityServiceInfo serviceInfo = getServiceInfo();
serviceInfo.packageNames = new String[]{"packageA"};
setServiceInfo(serviceInfo);
}
accessibility_service_config.xml
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:canRetrieveWindowContent="true"
android:description="#string/accessibility_service_description"
/>
So, the question is how do I add/remove packages inside the Accessibility Service depending on the Accessibility events I receive later?
You can't exclude package names by adding them to that list.
serviceInfo.packageNames = null
Get events for all packages.
serviceInfo.packageNames = {"some.package"}
Get events ONLY for some.package. This list is not exclusions, it's a list of things to only include.
Now, if you could keep a list of all packages that you want to get events for, you could do this. Setting service info dynamically is just dandy! But, TBH, you're making this way too difficult and not using this API the way its intended. If you want to exclude only a few things, it is very ill advised to save a list of every other package name in the world, to get their events. It seems to me like you need your own logic filter package names. I would try this instead:
#Override
public void onAccessibilityEvent(AccessibilityEvent e) {
//Do the filtering yourself.
ArrayList<String> includedPackageNames = youlist;
if (!includedPackageNames.contains(e.getPackageName())) return;
//Do the rest of your stuff here.
}
EDIT:
You have some issues with your service info stuff. When you do this line:
#Override
protected void onServiceConnected() {
AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo(); //This one!
...
}
You overwrite all of the information in your service_config.xml file as well as any default values that you DID NOT set in there. You should let service_config.xml handle your static initialization and only make dynamic changes by modifying the fetched serviceInfo object. OR, at the very least, fetch the one that has been built from your service config.
Change that line to this:
//Get the object that has been initialized with settings from your service_config.xml file
AccessibilityServiceInfo accessibilityServiceInfo = getServiceInfo();
OR, better yet, delete your entire onServiceConnected function, and do it all statically in your service_config.xml file.
Encountered the same issue recently. After looking into source code (SDK 8.1), it seems the API description gives the wrong expectation that set packageNames to some app list then set it null would make the service receive all events again.
The reason is in two methods below. In #1 when you it calls setServiceInfo(info) where the info.mPackageNames is null, here it just skips it and never set service.mPackageNames to null. In #2, when mPackageNames already includes the apps you added before. So later mPackageNames.isEmpty() will always be false.
http://androidxref.com/8.1.0_r33/xref/frameworks/base/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java#mPackageNames
1
line 2786 - 2788
if (packageNames != null) {
mPackageNames.addAll(Arrays.asList(packageNames));
}
2
1428 Set<String> packageNames = service.mPackageNames;
1429 String packageName = (event.getPackageName() != null)
1430 ? event.getPackageName().toString() : null;
1431
1432 return (packageNames.isEmpty() || packageNames.contains(packageName));

Categories

Resources