Home > Android > Capture Toast or Notification using Accessibility Service

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.

  1. Samuel
    December 13, 2013 at 2:17 pm

    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.

    • January 29, 2014 at 6:19 am

      Sure.. share your email-id will send you the apk.

  2. March 14, 2014 at 11:48 am

    Hi pankajchu can you send me the apk too? dellai.davide@gmail.com thanks

  3. Nguyen ANh
    February 15, 2015 at 7:53 pm

    Can you send me the apk too? nguyenducanh37k14@gmail.com thanks pankajchu

  4. kelvin
    February 20, 2015 at 9:51 am

    would you like to share full source code with us?? i mean zip file of your project?

  5. jeeva
    June 1, 2015 at 7:03 am

    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

  6. jeeva
    June 1, 2015 at 8:53 am

    Kindly send your comments to jeeva2k10@gmail.com

  7. jeeva
    June 6, 2015 at 10:28 am

    Can u send the apk file to jeeva2k10@gmail.com

  8. sshlok
    August 31, 2015 at 12:19 pm

    can you share the apk on sshlok0@gmail.com

  9. September 15, 2015 at 9:54 am

    I wrote an app, that capture (and save) every toast: https://play.google.com/store/apps/details?id=org.mars3142.android.toaster

  10. madhina
    February 11, 2016 at 9:14 am

    hi, the receiver is registered but other messages are not shown

  11. February 23, 2016 at 10:21 am

    how to verify whether the toast is being captured or not ??? pls help

  12. September 6, 2016 at 10:34 am

    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.

  13. devindra
    January 11, 2017 at 7:32 am

    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”);
    }
    }
    }
    }

  14. namrata
    March 9, 2017 at 11:03 am

    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.

  1. March 15, 2014 at 12:00 pm
  2. May 22, 2015 at 11:29 pm
  3. January 9, 2016 at 12:48 am

Leave a comment