Capture Toast or Notification using Accessibility Service
I did a sample application to Capture Toast/ Notification raised by another/ same application via Accessibility Service.
Here our Service will capture the Toast/ Notification and send Broadcast with text (added into Toast/ NOtification) and source package.
Add Permission to manifest
<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
Add Service to manifest
<service android:name=".MyAccessibilityService" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> </service>
And Here is Our Service
import java.util.List; import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; import android.annotation.TargetApi; import android.app.Notification; import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.Parcelable; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.text.TextUtils; import android.util.Log; import android.view.accessibility.AccessibilityEvent; /** * This service class catches Toast or Notification of applications * * @author pankaj */ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) public class MyAccessibilityService extends AccessibilityService { private final AccessibilityServiceInfo info = new AccessibilityServiceInfo(); private static final String TAG = "MyAccessibilityService"; @Override public void onAccessibilityEvent(AccessibilityEvent event) { final int eventType = event.getEventType(); if (eventType == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) { final String sourcePackageName = (String)event.getPackageName(); Parcelable parcelable = event.getParcelableData(); if (parcelable instanceof Notification) { // Statusbar Notification //Notification notification = (Notification) parcelable; //Log.e(TAG, "Notification -> notification.tickerText :: " + notification.tickerText); List<CharSequence> messages = event.getText(); if (messages.size() > 0) { final String notificationMsg = (String) messages.get(0); Log.v(TAG, "Captured notification message [" + notificationMsg + "] for source [" + sourcePackageName + "]"); Log.v(TAG, "Broadcasting for " + Constants.ACTION_CATCH_NOTIFICATION); try { Intent mIntent = new Intent(Constants.ACTION_CATCH_NOTIFICATION); mIntent.putExtra(Constants.EXTRA_PACKAGE, sourcePackageName); mIntent.putExtra(Constants.EXTRA_MESSAGE, notificationMsg); MyAccessibilityService.this.getApplicationContext().sendBroadcast(mIntent); } catch (Exception e) { Log.e(TAG, e.toString()); } } else { Log.e(TAG, "Notification Message is empty. Can not broadcast"); } } else { // Something else, e.g. a Toast message // Read message and broadcast List<CharSequence> messages = event.getText(); if (messages.size() > 0) { final String toastMsg = (String) messages.get(0); Log.v(TAG, "Captured message [" + toastMsg + "] for source [" + sourcePackageName + "]"); Log.v(TAG, "Broadcasting for " + Constants.ACTION_CATCH_TOAST); try { Intent mIntent = new Intent(Constants.ACTION_CATCH_TOAST); mIntent.putExtra(Constants.EXTRA_PACKAGE, sourcePackageName); mIntent.putExtra(Constants.EXTRA_MESSAGE, toastMsg); MyAccessibilityService.this.getApplicationContext().sendBroadcast(mIntent); } catch (Exception e) { Log.v(TAG, e.toString()); } } else { Log.e(TAG, "Message is empty. Can not broadcast"); } } } else { Log.v(TAG, "Got un-handled Event"); } } @Override public void onInterrupt() { } @Override public void onServiceConnected() { // Set the type of events that this service wants to listen to. //Others won't be passed to this service. info.eventTypes = AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED; // If you only want this service to work with specific applications, set their // package names here. Otherwise, when the service is activated, it will listen // to events from all applications. //info.packageNames = new String[] //{"com.appone.totest.accessibility", "com.apptwo.totest.accessibility"}; // Set the type of feedback your service will provide. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { info.feedbackType = AccessibilityServiceInfo.FEEDBACK_ALL_MASK; } else { info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC; } // Default services are invoked only if no package-specific ones are present // for the type of AccessibilityEvent generated. This service *is* // application-specific, so the flag isn't necessary. If this was a // general-purpose service, it would be worth considering setting the // DEFAULT flag. // info.flags = AccessibilityServiceInfo.DEFAULT; info.notificationTimeout = 100; this.setServiceInfo(info); } public static final class Constants { public static final String EXTRA_MESSAGE = "extra_message"; public static final String EXTRA_PACKAGE = "extra_package"; public static final String ACTION_CATCH_TOAST = "com.mytest.accessibility.CATCH_TOAST"; public static final String ACTION_CATCH_NOTIFICATION = "com.mytest.accessibility.CATCH_NOTIFICATION"; } /** * Check if Accessibility Service is enabled. * * @param mContext * @return <code>true</code> if Accessibility Service is ON, otherwise <code>false</code> */ public static boolean isAccessibilitySettingsOn(Context mContext) { int accessibilityEnabled = 0; final String service = "com.mytest.accessibility/com.mytest.accessibility.MyAccessibilityService"; boolean accessibilityFound = false; try { accessibilityEnabled = Settings.Secure.getInt( mContext.getApplicationContext().getContentResolver(), android.provider.Settings.Secure.ACCESSIBILITY_ENABLED); Log.v(TAG, "accessibilityEnabled = " + accessibilityEnabled); } catch (SettingNotFoundException e) { Log.e(TAG, "Error finding setting, default accessibility to not found: " + e.getMessage()); } TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':'); if (accessibilityEnabled == 1) { Log.v(TAG, "***ACCESSIBILIY IS ENABLED*** -----------------"); String settingValue = Settings.Secure.getString( mContext.getApplicationContext().getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); if (settingValue != null) { TextUtils.SimpleStringSplitter splitter = mStringColonSplitter; splitter.setString(settingValue); while (splitter.hasNext()) { String accessabilityService = splitter.next(); Log.v(TAG, "-------------- > accessabilityService :: " + accessabilityService); if (accessabilityService.equalsIgnoreCase(service)) { Log.v(TAG, "We've found the correct setting - accessibility is switched on!"); return true; } } } } else { Log.v(TAG, "***ACCESSIBILIY IS DISABLED***"); } return accessibilityFound; } }
Now we will write a receiver to listen Broadcast
I used an Activity and register a Receiver. You can do another ways also.
import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.util.Log; import com.mytest.accessibility.MyAccessibilityService.Constants; public class ToastOrNotificationTestActivity extends Activity { private static final String TAG = "ToastOrNotificationTestActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final IntentFilter mIntentFilter = new IntentFilter(Constants.ACTION_CATCH_NOTIFICATION); mIntentFilter.addAction(Constants.ACTION_CATCH_TOAST); registerReceiver(toastOrNotificationCatcherReceiver, mIntentFilter); Log.v(TAG, "Receiver registered."); } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(toastOrNotificationCatcherReceiver); } private final BroadcastReceiver toastOrNotificationCatcherReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Log.v(TAG, "Received message"); Log.v(TAG, "intent.getAction() :: " + intent.getAction()); Log.v(TAG, "intent.getStringExtra(Constants.EXTRA_PACKAGE) :: " + intent.getStringExtra(Constants.EXTRA_PACKAGE)); Log.v(TAG, "intent.getStringExtra(Constants.EXTRA_MESSAGE) :: " + intent.getStringExtra(Constants.EXTRA_MESSAGE)); } }; }
We did from code. Run the application and goto
home -> Settings -> accessibility and on the MyAccessibilityService. After this step our app is ready to capture Toast/ Notification
raised by given (or all) application.
One more thing, you can create Service into one more way. That is using xml and meta tag into manifest. Hope I will work on that and update.
Hi, can you compile this to an APK for download? I am trying to locate the app which is posting toast messages on my phone.
Sure.. share your email-id will send you the apk.
Hi pankajchu can you send me the apk too? dellai.davide@gmail.com thanks
Can you send me the apk too? nguyenducanh37k14@gmail.com thanks pankajchu
would you like to share full source code with us?? i mean zip file of your project?
I am trying this code but i got the following error how can i fix
java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.example.myaccessibilityservice/com.example.myaccessibilityservice.MyAccessibilityService}: java.lang.ClassCastException: com.example.myaccessibilityservice.MyAccessibilityService cannot be cast to android.app.Activity
Kindly send your comments to jeeva2k10@gmail.com
Can u send the apk file to jeeva2k10@gmail.com
can you share the apk on sshlok0@gmail.com
I wrote an app, that capture (and save) every toast: https://play.google.com/store/apps/details?id=org.mars3142.android.toaster
hi, the receiver is registered but other messages are not shown
how to verify whether the toast is being captured or not ??? pls help
Is there any way to unregister the broadcast receiver only when a particular message is received inside OnReceive method? I don’t know how can I force the broadcast to stop listening.
You can not unregister static BroadcastReceivers. But yes you can have a flag at application level, at check into onReceive to do your task or not. And another way is described here http://stackoverflow.com/questions/6529276/android-how-to-unregister-a-receiver-created-in-the-manifest
i always get
Log.e(TAG, “Notification Message is empty. Can not broadcast”);
i check all code
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
System.out.println(“onAccessibilityEvent”);
if (event.getEventType() == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) {
final String sourcePackageName = (String) event.getPackageName();
Parcelable parcelable = event.getParcelableData();
if (parcelable instanceof Notification) {
// Statusbar Notification
Notification notification = (Notification) parcelable;
Log.e(TAG, “Notification -> notification.tickerText :: ” + event.getText());
List messages = event.getText();
if (messages.size() > 0) {
final String notificationMsg = (String) messages.get(0);
Log.v(TAG, “Captured notification message [” + notificationMsg + “] for source [” + sourcePackageName + “]”);
Log.v(TAG, “Broadcasting for ” + Constants.ACTION_CATCH_NOTIFICATION);
try {
Intent mIntent = new Intent(Constants.ACTION_CATCH_NOTIFICATION);
mIntent.putExtra(Constants.EXTRA_PACKAGE, sourcePackageName);
mIntent.putExtra(Constants.EXTRA_MESSAGE, notificationMsg);
NotiService.this.getApplicationContext().sendBroadcast(mIntent);
} catch (Exception e) {
Log.e(TAG, e.toString());
}
} else {
Log.e(TAG, “Notification Message is empty. Can not broadcast”);
}
}
}
}
I’m doing same and on the accessibility service but i’m not getting any toast or log. how can i listen whats app notification through my application any buddy guide me . thanks in advance.