7

As I understood this is possible, from here Detecting toast messages But I am unable to catch any event with code snippet 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, e.g. 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>

Seems like this service is simply not started. What I am doing wrong?

Why I am doing this: I am installing many shortcuts on the stock launcher from my app. I am having the problem that theese shortcuts are placed one over another in one cell (even Sleep 500 did not help). So I am finding a way to install them one by another. But how to know when shortcut was successfully installed? I have found only a message that ics launcher shows to user.

Community
  • 1
  • 1
POMATu
  • 3,422
  • 7
  • 30
  • 42

3 Answers3

3

TYPE_NOTIFICATION_STATE_CHANGED generally refers to NotificationManager and icons placed in the status bar. Nonetheless, the below code should help shed some light as to the origin of a Toast message. On Android 4.0.4 ICS a Toast has a class of android.widget.Toast so getClassName should do the trick.

For what its worth, the change seems to have 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.

Tom
  • 6,947
  • 7
  • 46
  • 76
  • Still don't work. Can you please share a project with this code? Maybe I am doing something wrong... – POMATu Jun 23 '12 at 18:25
  • Sorry, that was copy-pasted from an app of mine and I'm not pasting full source. I can assure you that on Android 4.0.4 running Slim ICS on my Verizon Galaxy Nexus that this does work, but it does NOT work on older versions of Android. I'm not sure I would use it in an app considering how unreliable this is. – Tom Jun 23 '12 at 18:52
  • I am using android 4.0.3 on archos 80 g9. Can you please also post your manifest? Shoud I start service with startservice()? – POMATu Jun 23 '12 at 19:17
  • 1
    Absolutely not! You cannot force an Accessibility Service to be turned on. That is your problem. The user must go to System Settings > Accessibility and enable both Accessibility and YOUR Accessibility Service. These are Secure Settings (http://developer.android.com/reference/android/provider/Settings.Secure.html). – Tom Jun 23 '12 at 19:20
  • My app have system priveleges (located in /system/app). Can't I still set that setting programmatically? – POMATu Jun 24 '12 at 07:17
  • In that case you'll have to look into how Accessibility enables its Services. You'll probably need to update system settings and do something else, but I only develop for the SDK. – Tom Jun 24 '12 at 14:12
  • Is it also possible to be notified when the toast got hidden, and also be able to hide it ourselves (via the accessibility service) ? – android developer May 09 '19 at 18:00
0

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

something like this will be the code inside the activity.

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

In your manifest make the intent filter for the activity have the MAIN and LAUNCHER in it so that it will the Activity that is run when the user (or adb) starts your app. The activity will then start your service for you.

EDIT: I assume you saw this note from the post that you linked. And that you aren't trying this on 2.2?

Note: This didn't work for me on Android 2.2 (it doesn't seem to catch Toasts), but it worked on Android 4.0.

FoamyGuy
  • 46,603
  • 18
  • 125
  • 156
  • I am trying on android 4.0.3. Still can't get this working, even with your code. – POMATu Jun 16 '12 at 08:02
  • 1
    This won't work, you cannot start your own Accessibility Service or you will NOT receive any Accessibility Events. – Tom Jun 23 '12 at 19:20
0

First of all you shouldn't be trying to catch the Toast as this is an Asynchronous call that bring up a Toast on the screen and it will stay on the screen depending on the Timing, it is possible to leave the application and still have the toast appear. You should NOT care when the toast is done as this is irrelevant. All a Toast should be used for is JUST for giving the User info about a particular process that is underway/done or just Information. Its not meant for what you're trying to do.

Why don't you just send an internal broadcast into your application and catch it via an Intent Filter and then upon receiving that broadcast, you should start the service.

JoxTraex
  • 13,423
  • 6
  • 32
  • 45
  • I am not an author of app that sends toasts. So I cannot modify it to catch broadcast – POMATu Jun 16 '12 at 07:54
  • I am installing many shortcuts on the stock launcher from my app. I am having the problem that theese shortcuts are placed one over another in one cell (even Sleep 500 did not help). So I am finding a way to install them one by another. But how to know when shortcut was successfully installed? I have found only a message that ics launcher shows to user. – POMATu Jun 16 '12 at 08:05
  • Try reading the android source code to see if there is someway/some broadcast after that particular event has occured. I'm pretty sure there is nothing, but I haven't seen the ICS source code for the launcher shortcuts, chances are its the same as GB, which just takes the intent and puts it on the launcher and nothing else. – JoxTraex Jun 16 '12 at 20:54