抓住toast事件(来自任何应用程序)并获取toast消息

时间:2012-06-15 19:19:48

标签: android android-4.0-ice-cream-sandwich toast accessibility-api

据我所知,这是可能的,从这里开始 Detecting toast messages 但我无法通过链接捕获任何带有代码片段的事件。

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>

似乎这项服务根本没有启动。我做错了什么?

为什么我这样做: 我在我的应用程序的库存启动器上安装了许多快捷方式。我遇到的问题是,theese快捷方式在一个单元格中一个接一个地放置(即使Sleep 500没有帮助)。所以我找到了一种方法来逐个安装它们。但是如何知道何时成功安装了快捷方式?我发现只有ics启动器向用户显示的消息。

3 个答案:

答案 0 :(得分:3)

TYPE_NOTIFICATION_STATE_CHANGED通常是指状态栏中放置的NotificationManager和图标。尽管如此,下面的代码应该有助于阐明Toast消息的来源。在Android 4.0.4 ICS上,Toast的类为android.widget.Toast,因此getClassName应该可以解决问题。

为了它的价值,似乎在Android 4.0.3中进行了更改,以便在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);
    }

您可以在所有版本的Android Toast中看到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;
}

来源:个人经历

答案 1 :(得分:0)

创建一个活动,该活动将构建一个意图并使用它来启动您的服务。

这样的事情将是活动中的代码。

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

在清单中,活动的intent过滤器中包含MAIN和LAUNCHER,以便在用户(或adb)启动应用程序时运行Activity。然后,该活动将为您启动服务。

编辑:我假设您在帖子中看到了此链接。并且你不是在2.2上尝试这个吗?

  

注意:这在Android 2.2上对我不起作用(它似乎没有捕获Toasts),但它适用于Android 4.0。

答案 2 :(得分:0)

首先你不应该试图抓住Toast,因为这是一个异步调用,它会在屏幕上显示Toast,并且它会根据Timing保留在屏幕上,可以离开应用程序仍然有吐司出现。当吐司完成时你不应该在乎,因为这是无关紧要的。所有Toast应该用于 JUST ,以提供有关正在进行/已完成的特定进程或仅信息的用户信息。它不适合你想要做的事情。

为什么不直接向您的应用程序发送内部广播并通过Intent Filter捕获它,然后在收到该广播时,您应该启动该服务。