android - पार्सलेबल, कंटेंटव्यू या कंटेंटइंटेंट से नोटिफिकेशन टेक्स्ट को निकालें




notifications android-pendingintent (6)

इसलिए मुझे निम्नलिखित कोड के साथ अपनी AccessibilityService काम करने लगी:

@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();
        }
    }
}

नोटिफिकेशन (1) बनाए जाने पर प्रदर्शित पाठ को पढ़ने के लिए यह ठीक काम करता है।

एकमात्र समस्या यह है, मुझे भी (3) के मूल्य की आवश्यकता है जो उपयोगकर्ता द्वारा सूचना पट्टी खोलने पर प्रदर्शित होता है। (2) मेरे लिए महत्वपूर्ण नहीं है, लेकिन यह जानना अच्छा होगा कि इसे कैसे पढ़ा जाए। जैसा कि आप शायद जानते हैं, सभी मूल्य अलग-अलग हो सकते हैं।

तो, मैं बाहर (3) कैसे पढ़ सकता हूं? मुझे संदेह है कि यह असंभव है, लेकिन मेरी notificationList में केवल एक प्रविष्टि है (कम से कम केवल एक टोस्ट दिखाया गया है)।

आपका बहुत बहुत धन्यवाद!

/ edit: मैं अधिसूचना पार्सल के साथ निकाल सकता हूँ

if (!(parcel instanceof Notification)) {
            return;
        }
        final Notification notification = (Notification) parcel;

हालाँकि, मुझे नहीं पता कि notification या notification.contentView से notification का संदेश कैसे निकाला जाए।

कोई विचार?

/ संपादित करें: यह स्पष्ट करने के लिए कि यहां क्या पूछा गया है: मैं कैसे पढ़ सकता हूं (3) ?


Achep की AcDisplay परियोजना ने एक समाधान प्रदान किया है, Extractor.java से कोड की जांच करें


अपने प्रश्न का उत्तर देने के लिए: यह आपके मामले में संभव नहीं लगता है। नीचे मैं समझाता हूं कि क्यों।

"एक्सेसिबिलिटी इवेंट का मुख्य उद्देश्य एक एक्सेसिबिलिटी सर्विस के लिए उपयोगकर्ता को सार्थक प्रतिक्रिया प्रदान करने के लिए पर्याप्त जानकारी को उजागर करना है।" मामलों में, जैसे तुम्हारा:

पहुँच सेवा को अधिक प्रासंगिक जानकारी की आवश्यकता हो सकती है, फिर घटना पे-लोड में से एक। ऐसे मामलों में सेवा ईवेंट स्रोत प्राप्त कर सकती है जो एक AccessibilityNodeInfo (एक दृश्य स्थिति का स्नैपशॉट) है जिसका उपयोग विंडो सामग्री की खोज के लिए किया जा सकता है। ध्यान दें कि किसी ईवेंट के स्रोत तक पहुंचने का विशेषाधिकार, इस प्रकार विंडो सामग्री को स्पष्ट रूप से अनुरोध किया जाना है। ( AccessibilityEvent देखें)

हम आपके Android मेनिफ़ेस्ट फ़ाइल में सेवा के लिए मेटा डेटा सेट करके इस विशेषाधिकार को स्पष्ट रूप से अनुरोध कर सकते हैं:

<meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibilityservice" />

जहाँ आपकी xml फाइल दिख सकती है:

<?xml version="1.0" encoding="utf-8"?>
 <accessibility-service
     android:accessibilityEventTypes="typeNotificationStateChanged"
     android:canRetrieveWindowContent="true"
 />

हम स्पष्ट रूप से एक घटना के स्रोत (खिड़की की सामग्री) तक पहुँचने के लिए प्राइवेटलाइज का अनुरोध करते हैं और हम निर्दिष्ट करते हैं ( accessibilityEventTypes typeNotificationStateChanged का उपयोग करके) यह सेवा उस प्रकार को प्राप्त करती है जो यह सेवा प्राप्त करना चाहती है (आपके मामले में केवल typeNotificationStateChanged )। अधिक विकल्पों के लिए AccessibilityService देखें जिसे आप xml फ़ाइल में सेट कर सकते हैं।

आम तौर पर (नीचे देखें कि इस मामले में क्यों नहीं), यह event.getSource() को कॉल करना संभव है event.getSource() और एक AccessibilityNodeInfo प्राप्त AccessibilityNodeInfo और विंडो सामग्री के माध्यम से ट्रैवर्स करें, क्योंकि "एक्सेस ट्री में दृश्य दृश्य शीर्ष पर पहुंच द्वारा भेजा जाता है"।

हालांकि, ऐसा लगता है कि इसे अभी काम करना संभव है, AccessibilityEvent प्रलेखन में आगे पढ़ना हमें बताता है:

यदि पहुँच सेवा ने विंडो सामग्री को पुनः प्राप्त करने का अनुरोध नहीं किया है तो घटना में उसके स्रोत का संदर्भ नहीं होगा। इसके अलावा TYPE_NOTIFICATION_STATE_CHANGED प्रकार की घटनाओं के लिए स्रोत कभी उपलब्ध नहीं है।

जाहिर है, यह सुरक्षा उद्देश्यों के कारण है ...

नोटिफिकेशन के मैसेज को नोटिफिकेशन या notification.contentView / notification.contentIntent से कैसे निकाला जाए, इस पर हुक करने के लिए। मुझे नहीं लगता कि आप कर सकते हैं।

ContentView एक RemoteView और अधिसूचना के बारे में जानकारी प्राप्त करने के लिए कोई RemoteView प्रदान नहीं करता है।

इसी तरह कंटेंटइंटेंट एक PendingIntent , जो नोटिफिकेशन को क्लिक करने पर लॉन्च किए जाने वाले इरादे के बारे में जानकारी प्राप्त करने के लिए कोई PendingIntent प्रदान नहीं करता है। (यानी आप उदाहरण के लिए इरादे से एक्स्ट्रा कलाकार प्राप्त नहीं कर सकते हैं)।

इसके अलावा, चूंकि आपने इस बारे में कोई जानकारी नहीं दी है कि आप अधिसूचना का विवरण क्यों प्राप्त करना चाहते हैं और आप इसके साथ क्या करना चाहते हैं, मैं वास्तव में इसे हल करने के लिए आपको आपूर्ति नहीं कर सकता।


एक और तरीका है यदि आप प्रतिबिंब का उपयोग नहीं करना चाहते हैं: दूरस्थ दृश्य ऑब्जेक्ट में सूचीबद्ध "क्रिया" की सूची को ट्रेस करने के बजाय, आप उन्हें व्यूग्रुप पर "रीप्ले" कर सकते हैं:

/* Re-create a 'local' view group from the info contained in the remote view */
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ViewGroup localView = (ViewGroup) inflater.inflate(remoteView.getLayoutId(), null);
remoteView.reapply(getApplicationContext(), localView);

ध्यान दें कि रिमोट व्यू.getLayoutId () का उपयोग यह सुनिश्चित करने के लिए करें कि फुलाया गया दृश्य सूचना के किसी एक से मेल खाता है।

उसके बाद, आप कुछ और-या-से-कम मानक टेक्स्टव्यू प्राप्त कर सकते हैं

 TextView tv = (TextView) localView.findViewById(android.R.id.title);
 Log.d("blah", tv.getText());

अपने स्वयं के उद्देश्य के लिए (जो अपने स्वयं के पैकेज द्वारा पोस्ट की गई सूचनाओं की जासूसी करने के लिए है) मैंने स्थानीय दृश्य के तहत पूरे पदानुक्रम को पार करने और सभी मौजूदा TextViews को इकट्ठा करने के लिए चुना।


मैंने पिछले दिनों के कुछ घंटों को बर्बाद कर दिया है, जो आप (और मुझे भी, वैसे) करना चाहते हैं। दो बार RemoteViews के पूरे स्रोत को देखने के बाद, मुझे लगा कि इस कार्य को पूरा करने का एकमात्र तरीका अच्छा पुराना, बदसूरत और हैकी जावा प्रतिबिंब है।

यह रहा:

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

इस कोड ने एंड्रॉइड 4.0.3 के साथ एक नेक्सस एस पर ठीक काम किया। हालाँकि, मैंने परीक्षण नहीं किया कि क्या यह एंड्रॉइड के अन्य संस्करणों पर काम करता है। यह बहुत संभावना है कि कुछ मान, विशेष रूप से viewId बदल गए। मुझे लगता है कि एंड्रॉइड के सभी संस्करणों को हार्ड-कोडिंग के बिना सभी संभव आईडी का समर्थन करने के तरीके होने चाहिए, लेकिन यह एक और सवाल का जवाब है ...);

पुनश्च: जिस मूल्य की आप तलाश कर रहे हैं (आपके मूल प्रश्न में "(3)" के रूप में) "टेक्स्ट" -वल्यू है।


यदि आपने एंड्रॉइड 4.2.2 पर टॉमटेश के समाधान की कोशिश की है, तो आप देखेंगे कि यह काम नहीं करता है। उनके जवाब पर विस्तार, और user1060919 की टिप्पणी ... यहाँ एक उदाहरण है जो 4.2.2 के लिए काम करता है:

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

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

    Field outerField = secretClass.getDeclaredField("mActions");
    outerField.setAccessible(true);
    ArrayList<Object> actions = (ArrayList<Object>) outerField.get(views);

    for (Object action : actions) {
        Field innerFields[] = action.getClass().getDeclaredFields();
        Field innerFieldsSuper[] = action.getClass().getSuperclass().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);
            }
        }
        for (Field field : innerFieldsSuper) {
            field.setAccessible(true);
            if (field.getName().equals("viewId")) {
                viewId = field.getInt(action);
            }
        }

        if (value != null && type != null && viewId != null && (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();
}

विभिन्न अधिसूचना प्रकारों की पहचान करने और डेटा निकालने के लिए, रेमी के उत्तर को जोड़ना, नीचे दिए गए कोड का उपयोग करें।

Resources resources = null;
try {
    PackageManager pkm = getPackageManager();
    resources = pkm.getResourcesForApplication(strPackage);
} catch (Exception ex){
    Log.e(strTag, "Failed to initialize ids: " + ex.getMessage());
}
if (resources == null)
    return;

ICON = resources.getIdentifier("android:id/icon", null, null);
TITLE = resources.getIdentifier("android:id/title", null, null);
BIG_TEXT = resources.getIdentifier("android:id/big_text", null, null);
TEXT = resources.getIdentifier("android:id/text", null, null);
BIG_PIC = resources.getIdentifier("android:id/big_picture", null, null);
EMAIL_0 = resources.getIdentifier("android:id/inbox_text0", null, null);
EMAIL_1 = resources.getIdentifier("android:id/inbox_text1", null, null);
EMAIL_2 = resources.getIdentifier("android:id/inbox_text2", null, null);
EMAIL_3 = resources.getIdentifier("android:id/inbox_text3", null, null);
EMAIL_4 = resources.getIdentifier("android:id/inbox_text4", null, null);
EMAIL_5 = resources.getIdentifier("android:id/inbox_text5", null, null);
EMAIL_6 = resources.getIdentifier("android:id/inbox_text6", null, null);
INBOX_MORE = resources.getIdentifier("android:id/inbox_more", null, null);






parcelable