Поймать всплывающее сообщение (из любого приложения) и получить всплывающее сообщение

Как я понял, это возможно, отсюда Обнаружение всплывающих сообщений Но я не могу поймать какое-либо событие с кодом фрагмент по ссылке.

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>

Похоже, что эта служба просто не запущена. Что я делаю неправильно?

Почему я это делаю: я устанавливаю множество ярлыков на стандартный лаунчер из своего приложения. У меня такая проблема, что эти ярлыки располагаются друг над другом в одной ячейке (даже Sleep 500 не помог). Поэтому я нахожу способ установить их один за другим. Но как узнать, что ярлык был успешно установлен? Я нашел только сообщение, которое программа запуска ics показывает пользователю.


person POMATu    schedule 15.06.2012    source источник
comment
тебе не хватает разрешения   -  person njzk2    schedule 16.04.2013


Ответы (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);
    }

Вы можете увидеть Toast во всех версиях Android здесь.

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

Источник: личный опыт.

person Tom    schedule 23.06.2012
comment
Все еще не работает. Не могли бы вы поделиться проектом с этим кодом? Может я что-то не так делаю... - person POMATu; 23.06.2012
comment
Извините, это было скопировано из моего приложения, и я не вставляю полный исходный код. Я могу заверить вас, что на Android 4.0.4 с Slim ICS на моем Verizon Galaxy Nexus это работает, но НЕ работает на более старых версиях Android. Я не уверен, что буду использовать его в приложении, учитывая, насколько это ненадежно. - person Tom; 23.06.2012
comment
Я использую андроид 4.0.3 на archos 80 g9. Не могли бы вы также опубликовать свой манифест? Должен ли я запускать службу с помощью startservice()? - person POMATu; 23.06.2012
comment
Точно нет! Вы не можете принудительно включить службу специальных возможностей. Это ваша проблема. Пользователь должен перейти в Системные настройки › Специальные возможности и включить специальные возможности и ВАШУ службу специальных возможностей. Это параметры безопасности (developer.android.com/reference/android/provider). /). - person Tom; 23.06.2012
comment
Мое приложение имеет системные привилегии (находится в /system/app). Могу ли я установить этот параметр программно? - person POMATu; 24.06.2012
comment
В этом случае вам придется изучить, как Accessibility включает свои службы. Вам, вероятно, потребуется обновить системные настройки и сделать что-то еще, но я разрабатываю только для SDK. - person Tom; 24.06.2012
comment
Можно ли также получить уведомление, когда всплывающее уведомление было скрыто, а также иметь возможность скрыть его самостоятельно (через службу специальных возможностей)? - person android developer; 09.05.2019

создайте действие, которое создаст намерение, и используйте его для запуска службы.

что-то вроде этого будет кодом внутри активности.

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

В вашем манифесте сделайте так, чтобы в фильтре намерений для действия были MAIN и LAUNCHER, чтобы он выполнял действие, которое запускается, когда пользователь (или adb) запускает ваше приложение. Затем активность запустит вашу службу для вас.

EDIT: я предполагаю, что вы видели это примечание из поста, на который вы ссылаетесь. И что вы не пробуете это на 2.2?

Примечание. Это не сработало для меня на Android 2.2 (похоже, оно не ловит тосты), но сработало на Android 4.0.

person FoamyGuy    schedule 15.06.2012
comment
Пробую на андроиде 4.0.3. Все еще не могу заставить это работать, даже с вашим кодом. - person POMATu; 16.06.2012
comment
Это не сработает, вы не сможете запустить собственную службу специальных возможностей или НЕ будете получать события специальных возможностей. - person Tom; 23.06.2012

Прежде всего, вы не должны пытаться поймать Toast, так как это асинхронный вызов, который выводит Toast на экран, и он останется на экране в зависимости от времени, можно выйти из приложения и по-прежнему иметь появляются тосты. Вас НЕ должно волновать, когда тост будет сделан, так как это не имеет значения. Все тосты должны использоваться ПРОСТО для предоставления пользователю информации о конкретном процессе, который выполняется/выполняется, или просто информации. Это не предназначено для того, что вы пытаетесь сделать.

Почему бы вам просто не отправить внутреннюю трансляцию в свое приложение и не перехватить ее через фильтр намерений, а затем, получив эту трансляцию, вы должны запустить службу.

person JoxTraex    schedule 15.06.2012
comment
Я не автор приложения, которое отправляет тосты. Поэтому я не могу изменить его, чтобы поймать трансляцию - person POMATu; 16.06.2012
comment
Я устанавливаю много ярлыков на стандартный лаунчер из своего приложения. У меня такая проблема, что эти ярлыки располагаются друг над другом в одной ячейке (даже Sleep 500 не помог). Поэтому я нахожу способ установить их один за другим. Но как узнать, что ярлык был успешно установлен? Я нашел только сообщение, которое программа запуска ics показывает пользователю. - person POMATu; 16.06.2012
comment
Попробуйте прочитать исходный код Android, чтобы узнать, есть ли какая-либо трансляция после того, как произошло это конкретное событие. Я почти уверен, что ничего нет, но я не видел исходный код ICS для ярлыков запуска, скорее всего, он такой же, как GB, который просто берет намерение и помещает его в панель запуска и ничего больше. - person JoxTraex; 17.06.2012