6

我需要从RemoteViews对象中检索一些文本。我有可能获得 LayoutId,但我不知道如何从中检索文本(TextViewRemoteView通知)。

RemoteView唯一包含setter,但没有getter,所以我想我必须使用LayoutId(不知何故)。

你能帮我解决这个问题吗?谢谢!

/edit:我问这个的原因是因为我有一个AccessibilityService检索通知的。因此,这是检索值的唯一方法。

/edit2:我使用此代码接收通知:

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
    if (event.getEventType() == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) {
        List<CharSequence> notificationList = event.getText();
        for (int i = 0; i < notificationList.size(); i++) {
            Toast.makeText(this.getApplicationContext(), notificationList.get(i), 1).show();
        }
        if (!(parcel instanceof Notification)) {
            return;
        }
        final Notification notification = (Notification) parcel;
        doMoreStuff();

    }
}

通过该notification对象,我可以访问RemoteViews( notification.contentView) 和PendingIntent( notification.contentIntent)。要获取 layoutId,我可以调用contentView.getLayoutId()

4

4 回答 4

9

我在这里提出了一个类似的解决方案,它也使用反射来解决问题,但以更平易近人的方式。这是我的解决方案。在这种情况下,RemoteViews 来自通知,因此如果您已经有权访问 RemoteViews 对象,则可以忽略前三行。页面上的链接对实际发生的情况提供了更详细的解释。我希望这会帮助任何有类似问题的人。

public static List<String> getText(Notification notification)
{
    // We have to extract the information from the view
    RemoteViews        views = notification.bigContentView;
    if (views == null) views = notification.contentView;
    if (views == null) return null;

    // Use reflection to examine the m_actions member of the given RemoteViews object.
    // It's not pretty, but it works.
    List<String> text = new ArrayList<String>();
    try
    {
        Field field = views.getClass().getDeclaredField("mActions");
        field.setAccessible(true);

        @SuppressWarnings("unchecked")
        ArrayList<Parcelable> actions = (ArrayList<Parcelable>) field.get(views);

        // Find the setText() and setTime() reflection actions
        for (Parcelable p : actions)
        {
            Parcel parcel = Parcel.obtain();
            p.writeToParcel(parcel, 0);
            parcel.setDataPosition(0);

            // The tag tells which type of action it is (2 is ReflectionAction, from the source)
            int tag = parcel.readInt();
            if (tag != 2) continue;

            // View ID
            parcel.readInt();

            String methodName = parcel.readString();
            if (methodName == null) continue;

            // Save strings
            else if (methodName.equals("setText"))
            {
                // Parameter type (10 = Character Sequence)
                parcel.readInt();

                // Store the actual string
                String t = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel).toString().trim();
                text.add(t);
            }

            // Save times. Comment this section out if the notification time isn't important
            else if (methodName.equals("setTime"))
            {
                // Parameter type (5 = Long)
                parcel.readInt();

                String t = new SimpleDateFormat("h:mm a").format(new Date(parcel.readLong()));
                text.add(t);
            }

            parcel.recycle();
        }
    }

    // It's not usually good style to do this, but then again, neither is the use of reflection...
    catch (Exception e)
    {
        Log.e("NotificationClassifier", e.toString());
    }

    return text;
}
于 2013-12-02T06:44:21.787 回答
5

取自从 parcelable、contentView 或 contentIntent中提取通知文本

Notification notification = (Notification) event.getParcelableData();
RemoteViews views = notification.contentView;
Class secretClass = views.getClass();

try {
    Map<Integer, String> text = new HashMap<Integer, String>();

    Field outerFields[] = secretClass.getDeclaredFields();
    for (int i = 0; i < outerFields.length; i++) {
        if (!outerFields[i].getName().equals("mActions")) continue;

        outerFields[i].setAccessible(true);

        ArrayList<Object> actions = (ArrayList<Object>) outerFields[i]
        .get(views);
        for (Object action : actions) {
            Field innerFields[] = action.getClass().getDeclaredFields();

            Object value = null;
            Integer type = null;
            Integer viewId = null;
            for (Field field : innerFields) {
                field.setAccessible(true);
                if (field.getName().equals("value")) {
                    value = field.get(action);
                } else if (field.getName().equals("type")) {
                    type = field.getInt(action);
                } else if (field.getName().equals("viewId")) {
                    viewId = field.getInt(action);
                }
            }

            if (type == 9 || type == 10) {
                text.put(viewId, value.toString());
            }
        }

        System.out.println("title is: " + text.get(16908310));
        System.out.println("info is: " + text.get(16909082));
        System.out.println("text is: " + text.get(16908358));
    }
} catch (Exception e) {
    e.printStackTrace();
}
于 2012-06-11T09:17:20.503 回答
0

CommonsWare这个问题中说:

...应用程序小部件是只写的:您可以将数据推送给它们,但您无法读取它们。相反,当您使用新文本更新应用小部件时,您需要将该文本存储在某个地方,也许是在一个文件中。

他的回答似乎是合乎逻辑的。

于 2012-06-10T14:19:54.203 回答
0

如果您的目标是 Android 19+,则可以使用以下代码从 Notification 对象获取标题/文本,而无需使用任何私有 API。

Notification noty = ...;
Bundle extras = noty.extras;
if (extras != null) {
  String title = extras.getString(Notification.EXTRA_TITLE);
  String text = extras.getString(Notification.EXTRA_TEXT);
}
于 2016-08-14T04:11:13.867 回答