Catch toast event (from any application) and receive messages with toasts

As I understand it, this is possible, from here Toast detection But I can not catch any event with a code fragment from the link.

MyAccessibilityService.java

package com.test.toasts2; import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; import android.app.Notification; import android.os.Parcelable; import android.view.accessibility.AccessibilityEvent; import android.widget.Toast; public class MyAccessibilityService extends AccessibilityService { @Override public void onAccessibilityEvent(AccessibilityEvent event) { System.out.println("event catched"); Toast.makeText(this, "catched " + "!", Toast.LENGTH_SHORT).show(); if(event.getEventType() != AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) return; // event is not a notification String sourcePackageName = (String)event.getPackageName(); Parcelable parcelable = event.getParcelableData(); if(parcelable instanceof Notification){ // Statusbar Notification } else{ // something else, eg a Toast message String log = "Message: "+event.getText().get(0)+" [Source: "+sourcePackageName+"]"; System.out.println(log); // write `log` to file... } } @Override public void onInterrupt() { // TODO Auto-generated method stub } @Override protected void onServiceConnected() { // TODO Auto-generated method stub super.onServiceConnected(); AccessibilityServiceInfo info = new AccessibilityServiceInfo(); info.feedbackType = AccessibilityServiceInfo.DEFAULT; setServiceInfo(info); } } 

AndroidManifest.xml

 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.test.toasts2" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="15" /> <application> <service android:name=".MyAccessibilityService" android:label="label"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> </service> </application> </manifest> 

It seems that this service just does not start. What am I doing wrong?

Why I do this: I install many shortcuts on the launcher from my application. I had a problem that these shortcuts are placed one after another in one cell (even Sleep 500 did not help). Therefore, I find a way to install them one by one. But how do you know when a shortcut was successfully installed? I found only a message that ics launcher shows to the user.

+6
source share
3 answers

TYPE_NOTIFICATION_STATE_CHANGED usually refers to NotificationManager and icons placed on the status bar. However, the code below should help shed light on the origin of the Toast message. On Android 4.0.4, ICS toast has the class android.widget.Toast , so getClassName should do the trick.

What is its value, it seems, a change has been made in Android 4.0.3 to add and use the following method in Toast.TN

 private void trySendAccessibilityEvent() { AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mView.getContext()); if (!accessibilityManager.isEnabled()) { return; } // treat toasts as notifications since they are used to // announce a transient piece of information to the user AccessibilityEvent event = AccessibilityEvent.obtain( AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); event.setClassName(getClass().getName()); event.setPackageName(mView.getContext().getPackageName()); mView.dispatchPopulateAccessibilityEvent(event); accessibilityManager.sendAccessibilityEvent(event); } 

You can see the Toast class in all versions of Android here .

 private final String getEventType(AccessibilityEvent event) { switch (event.getEventType()) { case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED: return "TYPE_NOTIFICATION_STATE_CHANGED"; case AccessibilityEvent.TYPE_VIEW_CLICKED: return "TYPE_VIEW_CLICKED"; case AccessibilityEvent.TYPE_VIEW_FOCUSED: return "TYPE_VIEW_FOCUSED"; case AccessibilityEvent.TYPE_VIEW_LONG_CLICKED: return "TYPE_VIEW_LONG_CLICKED"; case AccessibilityEvent.TYPE_VIEW_SELECTED: return "TYPE_VIEW_SELECTED"; case AccessibilityEvent.TYPE_VIEW_SCROLLED: return "TYPE_VIEW_SCROLLED"; case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: return "TYPE_VIEW_HOVER_EXIT"; case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER: return "TYPE_VIEW_HOVER_ENTER"; case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START: return "TYPE_TOUCH_EXPLORATION_GESTURE_START"; case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END: return "TYPE_TOUCH_EXPLORATION_GESTURE_END"; case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: return "TYPE_WINDOW_STATE_CHANGED"; case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: return "TYPE_WINDOW_CONTENT_CHANGED"; case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: return "TYPE_VIEW_TEXT_SELECTION_CHANGED"; case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: return "TYPE_VIEW_TEXT_CHANGED"; } return "default"; } private final String getEventText(AccessibilityEvent event) { StringBuilder sb = new StringBuilder(); for (CharSequence s : event.getText()) { sb.append(s); sb.append('\n'); } return sb.toString(); } @Override public void onAccessibilityEvent(AccessibilityEvent event) { Log.v(TAG, String.format( "onAccessibilityEvent: [type] %s [class] %s [package] %s [time] %s [fullscreen] %s [text] %s", getEventType(event), event.getClassName(), event.getPackageName(), event.getEventTime(), Boolean.toString( event.isFullScreen()), getEventText(event))); if (android.os.Build.VERSION.SDK_INT >= 14) Log.v(TAG, "Window ID: " + Integer.toString(event.getWindowId()) + "."); } private void setServiceInfo(int feedbackType) { final AccessibilityServiceInfo info = new AccessibilityServiceInfo(); // We are interested in all types of accessibility events. info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK; // We want to provide specific type of feedback. info.feedbackType = feedbackType; // We want to receive events in a certain interval. // info.notificationTimeout = EVENT_NOTIFICATION_TIMEOUT_MILLIS; // We want to receive accessibility events only from certain packages. // info.packageNames = PACKAGE_NAMES; setServiceInfo(info); } private boolean isInfrastructureInitialized = false; @Override public void onServiceConnected() { if (isInfrastructureInitialized) return; // Claim the events with which to listen to. setServiceInfo(AccessibilityServiceInfo.FEEDBACK_ALL_MASK); // We are in an initialized state now. isInfrastructureInitialized = true; } 

Source: personal experience.

+3
source

create an action that will build the intent and use it to start your service.

something like this will be the code inside the action.

 Intent i = new Intent(YourActivity.this, MyAccessibilityService.class); startService(i); 

In your manifests for the intent filter for this action, there are MAIN and LAUNCHER so that it performs an action that runs when the user (or adb) launches your application. Then the action will start your service for you.

EDIT: I assume you saw this note from the post you linked. And that you did not try it on 2.2?

Note. This did not work for me on Android 2.2 (it doesn't seem to catch toasts), but it worked on Android 4.0.

0
source

First of all, you should not try to catch Toast, since this is an asynchronous call that displays Toast on the screen, and it will remain on the screen depending on the time, you can leave the application and still have a toast. You do not have to worry about when the toast is made, as it does not matter. All toasts should be used for JUST to provide the User with information about a specific process that is being performed or is being performed, or simply Information. This does not mean what you are trying to do.

Why don't you just send the internal broadcast to your application and catch it through the Intent Filter, and then after receiving this broadcast, you should start the service.

0
source

Source: https://habr.com/ru/post/918213/


All Articles