1f021758934b35e3b842c6799344531d7ea2969daChris Wren/*
2f021758934b35e3b842c6799344531d7ea2969daChris Wren * Copyright (C) 2012 The Android Open Source Project
3f021758934b35e3b842c6799344531d7ea2969daChris Wren *
4f021758934b35e3b842c6799344531d7ea2969daChris Wren * Licensed under the Apache License, Version 2.0 (the "License");
5f021758934b35e3b842c6799344531d7ea2969daChris Wren * you may not use this file except in compliance with the License.
6f021758934b35e3b842c6799344531d7ea2969daChris Wren * You may obtain a copy of the License at
7f021758934b35e3b842c6799344531d7ea2969daChris Wren *
8f021758934b35e3b842c6799344531d7ea2969daChris Wren *      http://www.apache.org/licenses/LICENSE-2.0
9f021758934b35e3b842c6799344531d7ea2969daChris Wren *
10f021758934b35e3b842c6799344531d7ea2969daChris Wren * Unless required by applicable law or agreed to in writing, software
11f021758934b35e3b842c6799344531d7ea2969daChris Wren * distributed under the License is distributed on an "AS IS" BASIS,
12f021758934b35e3b842c6799344531d7ea2969daChris Wren * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f021758934b35e3b842c6799344531d7ea2969daChris Wren * See the License for the specific language governing permissions and
14f021758934b35e3b842c6799344531d7ea2969daChris Wren * limitations under the License.
15f021758934b35e3b842c6799344531d7ea2969daChris Wren */
16f021758934b35e3b842c6799344531d7ea2969daChris Wren
17f021758934b35e3b842c6799344531d7ea2969daChris Wrenpackage android.support.v4.app;
18f021758934b35e3b842c6799344531d7ea2969daChris Wren
19f021758934b35e3b842c6799344531d7ea2969daChris Wrenimport android.app.Notification;
20f021758934b35e3b842c6799344531d7ea2969daChris Wrenimport android.app.PendingIntent;
21f021758934b35e3b842c6799344531d7ea2969daChris Wrenimport android.content.Context;
22f021758934b35e3b842c6799344531d7ea2969daChris Wrenimport android.graphics.Bitmap;
23b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazenimport android.os.Bundle;
242bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazenimport android.os.Parcelable;
25a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazenimport android.util.Log;
26300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazenimport android.util.SparseArray;
27f021758934b35e3b842c6799344531d7ea2969daChris Wrenimport android.widget.RemoteViews;
28b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen
29b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazenimport java.lang.reflect.Field;
30f021758934b35e3b842c6799344531d7ea2969daChris Wrenimport java.util.ArrayList;
31300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazenimport java.util.List;
32f021758934b35e3b842c6799344531d7ea2969daChris Wren
33f021758934b35e3b842c6799344531d7ea2969daChris Wrenclass NotificationCompatJellybean {
34a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen    public static final String TAG = "NotificationCompat";
35a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen
36ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    // Extras keys used for Jellybean SDK and above.
37b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen    static final String EXTRA_LOCAL_ONLY = "android.support.localOnly";
38300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen    static final String EXTRA_ACTION_EXTRAS = "android.support.actionExtras";
39ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    static final String EXTRA_REMOTE_INPUTS = "android.support.remoteInputs";
40ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    static final String EXTRA_GROUP_KEY = "android.support.groupKey";
41ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    static final String EXTRA_GROUP_SUMMARY = "android.support.isGroupSummary";
42ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    static final String EXTRA_SORT_KEY = "android.support.sortKey";
43ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    static final String EXTRA_USE_SIDE_CHANNEL = "android.support.useSideChannel";
44300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen
452bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen    // Bundle keys for storing action fields in a bundle
462bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen    private static final String KEY_ICON = "icon";
472bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen    private static final String KEY_TITLE = "title";
482bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen    private static final String KEY_ACTION_INTENT = "actionIntent";
492bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen    private static final String KEY_EXTRAS = "extras";
502bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen    private static final String KEY_REMOTE_INPUTS = "remoteInputs";
512bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen
52a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen    private static final Object sExtrasLock = new Object();
53a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen    private static Field sExtrasField;
54a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen    private static boolean sExtrasFieldAccessFailed;
55b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen
56300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen    private static final Object sActionsLock = new Object();
57300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen    private static Class<?> sActionClass;
58300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen    private static Field sActionsField;
59300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen    private static Field sActionIconField;
60300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen    private static Field sActionTitleField;
61300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen    private static Field sActionIntentField;
62300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen    private static boolean sActionsAccessFailed;
63300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen
64b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen    public static class Builder implements NotificationBuilderWithBuilderAccessor,
65b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen            NotificationBuilderWithActions {
66b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen        private Notification.Builder b;
67ab78e9b2a147c8de7b5cf231b97aad9d8c4f106cGriff Hazen        private final Bundle mExtras;
68300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen        private List<Bundle> mActionExtrasList = new ArrayList<Bundle>();
69b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen
70b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen        public Builder(Context context, Notification n,
71b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                CharSequence contentTitle, CharSequence contentText, CharSequence contentInfo,
72b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                RemoteViews tickerView, int number,
73b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                PendingIntent contentIntent, PendingIntent fullScreenIntent, Bitmap largeIcon,
7443c5718722bab1f836b7c94f2ec0bc19e653037cGriff Hazen                int progressMax, int progress, boolean progressIndeterminate,
75ab78e9b2a147c8de7b5cf231b97aad9d8c4f106cGriff Hazen                boolean useChronometer, int priority, CharSequence subText, boolean localOnly,
76ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                Bundle extras, String groupKey, boolean groupSummary, String sortKey) {
77b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen            b = new Notification.Builder(context)
78b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                .setWhen(n.when)
79b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                .setSmallIcon(n.icon, n.iconLevel)
80b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                .setContent(n.contentView)
81b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                .setTicker(n.tickerText, tickerView)
82b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                .setSound(n.sound, n.audioStreamType)
83b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                .setVibrate(n.vibrate)
84b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                .setLights(n.ledARGB, n.ledOnMS, n.ledOffMS)
85b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                .setOngoing((n.flags & Notification.FLAG_ONGOING_EVENT) != 0)
86b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                .setOnlyAlertOnce((n.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)
87b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                .setAutoCancel((n.flags & Notification.FLAG_AUTO_CANCEL) != 0)
88b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                .setDefaults(n.defaults)
89b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                .setContentTitle(contentTitle)
90b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                .setContentText(contentText)
91b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                .setSubText(subText)
92b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                .setContentInfo(contentInfo)
93b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                .setContentIntent(contentIntent)
94b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                .setDeleteIntent(n.deleteIntent)
95b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                .setFullScreenIntent(fullScreenIntent,
96b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                        (n.flags & Notification.FLAG_HIGH_PRIORITY) != 0)
97b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                .setLargeIcon(largeIcon)
98b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                .setNumber(number)
99b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                .setUsesChronometer(useChronometer)
100b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen                .setPriority(priority)
10143c5718722bab1f836b7c94f2ec0bc19e653037cGriff Hazen                .setProgress(progressMax, progress, progressIndeterminate);
102ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            mExtras = new Bundle();
103ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            if (extras != null) {
104ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                mExtras.putAll(extras);
105ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
106ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            if (localOnly) {
107ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                mExtras.putBoolean(EXTRA_LOCAL_ONLY, true);
108ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
109ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            if (groupKey != null) {
110ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                mExtras.putString(EXTRA_GROUP_KEY, groupKey);
111ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                if (groupSummary) {
112ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    mExtras.putBoolean(EXTRA_GROUP_SUMMARY, true);
113ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                } else {
114ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    mExtras.putBoolean(EXTRA_USE_SIDE_CHANNEL, true);
115ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                }
116ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
117ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            if (sortKey != null) {
118ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                mExtras.putString(EXTRA_SORT_KEY, sortKey);
119ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            }
120b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen        }
121b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen
122b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen        @Override
123ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        public void addAction(NotificationCompatBase.Action action) {
124ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            mActionExtrasList.add(writeActionAndGetExtras(b, action));
125b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen        }
126b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen
127b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen        @Override
128b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen        public Notification.Builder getBuilder() {
129b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen            return b;
130b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen        }
131c55d0160b764bbad4e386556cab3ccac72686b0cGriff Hazen
132b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen        public Notification build() {
133b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen            Notification notif = b.build();
134ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            // Merge in developer provided extras, but let the values already set
135ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            // for keys take precedence.
136ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            Bundle extras = getExtras(notif);
137ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            Bundle mergeBundle = new Bundle(mExtras);
138ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            for (String key : mExtras.keySet()) {
139ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                if (extras.containsKey(key)) {
140ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    mergeBundle.remove(key);
141ab78e9b2a147c8de7b5cf231b97aad9d8c4f106cGriff Hazen                }
142300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen            }
143ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            extras.putAll(mergeBundle);
144300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen            SparseArray<Bundle> actionExtrasMap = buildActionExtrasMap(mActionExtrasList);
145300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen            if (actionExtrasMap != null) {
146300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                // Add the action extras sparse array if any action was added with extras.
147300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                getExtras(notif).putSparseParcelableArray(EXTRA_ACTION_EXTRAS, actionExtrasMap);
148b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen            }
149b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen            return notif;
150b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen        }
151f021758934b35e3b842c6799344531d7ea2969daChris Wren    }
152f021758934b35e3b842c6799344531d7ea2969daChris Wren
153b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen    public static void addBigTextStyle(NotificationBuilderWithBuilderAccessor b,
154b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen            CharSequence bigContentTitle, boolean useSummary,
155f021758934b35e3b842c6799344531d7ea2969daChris Wren            CharSequence summaryText, CharSequence bigText) {
156b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen        Notification.BigTextStyle style = new Notification.BigTextStyle(b.getBuilder())
157f021758934b35e3b842c6799344531d7ea2969daChris Wren            .setBigContentTitle(bigContentTitle)
158f021758934b35e3b842c6799344531d7ea2969daChris Wren            .bigText(bigText);
159f021758934b35e3b842c6799344531d7ea2969daChris Wren        if (useSummary) {
160f021758934b35e3b842c6799344531d7ea2969daChris Wren            style.setSummaryText(summaryText);
161fe3b5bac4901a4bb8cf51c09fe4910b02388818aGriff Hazen        }
162f021758934b35e3b842c6799344531d7ea2969daChris Wren    }
163f021758934b35e3b842c6799344531d7ea2969daChris Wren
164b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen    public static void addBigPictureStyle(NotificationBuilderWithBuilderAccessor b,
165b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen            CharSequence bigContentTitle, boolean useSummary,
16649714d665f839c4804a17eea129092f8b472926dRoman Nurik            CharSequence summaryText, Bitmap bigPicture, Bitmap bigLargeIcon,
16749714d665f839c4804a17eea129092f8b472926dRoman Nurik            boolean bigLargeIconSet) {
168b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen        Notification.BigPictureStyle style = new Notification.BigPictureStyle(b.getBuilder())
169fe3b5bac4901a4bb8cf51c09fe4910b02388818aGriff Hazen            .setBigContentTitle(bigContentTitle)
170fe3b5bac4901a4bb8cf51c09fe4910b02388818aGriff Hazen            .bigPicture(bigPicture);
171fe3b5bac4901a4bb8cf51c09fe4910b02388818aGriff Hazen        if (bigLargeIconSet) {
172fe3b5bac4901a4bb8cf51c09fe4910b02388818aGriff Hazen            style.bigLargeIcon(bigLargeIcon);
173fe3b5bac4901a4bb8cf51c09fe4910b02388818aGriff Hazen        }
174f021758934b35e3b842c6799344531d7ea2969daChris Wren        if (useSummary) {
175f021758934b35e3b842c6799344531d7ea2969daChris Wren            style.setSummaryText(summaryText);
176fe3b5bac4901a4bb8cf51c09fe4910b02388818aGriff Hazen        }
177f021758934b35e3b842c6799344531d7ea2969daChris Wren    }
178f021758934b35e3b842c6799344531d7ea2969daChris Wren
179b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen    public static void addInboxStyle(NotificationBuilderWithBuilderAccessor b,
180b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen            CharSequence bigContentTitle, boolean useSummary,
181f021758934b35e3b842c6799344531d7ea2969daChris Wren            CharSequence summaryText, ArrayList<CharSequence> texts) {
182b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen        Notification.InboxStyle style = new Notification.InboxStyle(b.getBuilder())
183f021758934b35e3b842c6799344531d7ea2969daChris Wren            .setBigContentTitle(bigContentTitle);
184f021758934b35e3b842c6799344531d7ea2969daChris Wren        if (useSummary) {
185f021758934b35e3b842c6799344531d7ea2969daChris Wren            style.setSummaryText(summaryText);
186f021758934b35e3b842c6799344531d7ea2969daChris Wren        }
187f021758934b35e3b842c6799344531d7ea2969daChris Wren        for (CharSequence text: texts) {
188f021758934b35e3b842c6799344531d7ea2969daChris Wren            style.addLine(text);
189f021758934b35e3b842c6799344531d7ea2969daChris Wren        }
190f021758934b35e3b842c6799344531d7ea2969daChris Wren    }
191f021758934b35e3b842c6799344531d7ea2969daChris Wren
192300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen    /** Return an SparseArray for action extras or null if none was needed. */
193300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen    public static SparseArray<Bundle> buildActionExtrasMap(List<Bundle> actionExtrasList) {
194300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen        SparseArray<Bundle> actionExtrasMap = null;
195300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen        for (int i = 0, count = actionExtrasList.size(); i < count; i++) {
196300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen            Bundle actionExtras = actionExtrasList.get(i);
197300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen            if (actionExtras != null) {
198300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                if (actionExtrasMap == null) {
199300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                    actionExtrasMap = new SparseArray<Bundle>();
200300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                }
201300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                actionExtrasMap.put(i, actionExtras);
202300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen            }
203300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen        }
204300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen        return actionExtrasMap;
205300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen    }
206300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen
207b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen    /**
208b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen     * Get the extras Bundle from a notification using reflection. Extras were present in
209b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen     * Jellybean notifications, but the field was private until KitKat.
210b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen     */
211b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen    public static Bundle getExtras(Notification notif) {
212a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen        synchronized (sExtrasLock) {
213a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen            if (sExtrasFieldAccessFailed) {
214a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen                return null;
215b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen            }
216a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen            try {
217a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen                if (sExtrasField == null) {
218a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen                    Field extrasField = Notification.class.getDeclaredField("extras");
219a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen                    if (!Bundle.class.isAssignableFrom(extrasField.getType())) {
220a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen                        Log.e(TAG, "Notification.extras field is not of type Bundle");
221a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen                        sExtrasFieldAccessFailed = true;
222a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen                        return null;
223a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen                    }
224a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen                    extrasField.setAccessible(true);
225a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen                    sExtrasField = extrasField;
226a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen                }
227a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen                Bundle extras = (Bundle) sExtrasField.get(notif);
228a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen                if (extras == null) {
229a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen                    extras = new Bundle();
230a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen                    sExtrasField.set(notif, extras);
231a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen                }
232a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen                return extras;
233a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen            } catch (IllegalAccessException e) {
234a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen                Log.e(TAG, "Unable to access notification extras", e);
235a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen            } catch (NoSuchFieldException e) {
236a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen                Log.e(TAG, "Unable to access notification extras", e);
237b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen            }
238a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen            sExtrasFieldAccessFailed = true;
239a091d8267055595d1bb41abb53c73c61cce1484aGriff Hazen            return null;
240b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen        }
241b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen    }
242b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen
2432bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen    public static NotificationCompatBase.Action readAction(
2442bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen            NotificationCompatBase.Action.Factory factory,
2452bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen            RemoteInputCompatBase.RemoteInput.Factory remoteInputFactory, int icon,
2462bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen            CharSequence title, PendingIntent actionIntent, Bundle extras) {
247ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        RemoteInputCompatBase.RemoteInput[] remoteInputs = null;
248ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        if (extras != null) {
249ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            remoteInputs = RemoteInputCompatJellybean.fromBundleArray(
250ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    BundleUtil.getBundleArrayFromBundle(extras, EXTRA_REMOTE_INPUTS),
251ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    remoteInputFactory);
252ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
253ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        return factory.build(icon, title, actionIntent, extras, remoteInputs);
254ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    }
255ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
256ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    public static Bundle writeActionAndGetExtras(
257ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            Notification.Builder builder, NotificationCompatBase.Action action) {
258ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        builder.addAction(action.getIcon(), action.getTitle(), action.getActionIntent());
259ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        Bundle actionExtras = new Bundle(action.getExtras());
260ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        if (action.getRemoteInputs() != null) {
261ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen            actionExtras.putParcelableArray(EXTRA_REMOTE_INPUTS,
262ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                    RemoteInputCompatJellybean.toBundleArray(action.getRemoteInputs()));
263ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        }
264ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        return actionExtras;
265ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    }
266ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
267300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen    public static int getActionCount(Notification notif) {
268300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen        synchronized (sActionsLock) {
269300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen            Object[] actionObjects = getActionObjectsLocked(notif);
270300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen            return actionObjects != null ? actionObjects.length : 0;
271300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen        }
272300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen    }
273300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen
274ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    public static NotificationCompatBase.Action getAction(Notification notif, int actionIndex,
2752bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen            NotificationCompatBase.Action.Factory factory,
2762bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen            RemoteInputCompatBase.RemoteInput.Factory remoteInputFactory) {
277300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen        synchronized (sActionsLock) {
278300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen            try {
279300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                Object actionObject = getActionObjectsLocked(notif)[actionIndex];
280300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                Bundle actionExtras = null;
281300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                Bundle extras = getExtras(notif);
282300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                if (extras != null) {
283300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                    SparseArray<Bundle> actionExtrasMap = extras.getSparseParcelableArray(
284300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                            EXTRA_ACTION_EXTRAS);
285300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                    if (actionExtrasMap != null) {
286300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                        actionExtras = actionExtrasMap.get(actionIndex);
287300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                    }
288300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                }
289ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                return readAction(factory, remoteInputFactory,
290ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen                        sActionIconField.getInt(actionObject),
291300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                        (CharSequence) sActionTitleField.get(actionObject),
292300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                        (PendingIntent) sActionIntentField.get(actionObject),
293300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                        actionExtras);
294300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen            } catch (IllegalAccessException e) {
295300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                Log.e(TAG, "Unable to access notification actions", e);
296300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                sActionsAccessFailed = true;
297300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen            }
298300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen        }
299ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        return null;
300300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen    }
301300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen
302300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen    private static Object[] getActionObjectsLocked(Notification notif) {
303300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen        synchronized (sActionsLock) {
304300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen            if (!ensureActionReflectionReadyLocked()) {
305300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                return null;
306300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen            }
307300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen            try {
308300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                return (Object[]) sActionsField.get(notif);
309300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen            } catch (IllegalAccessException e) {
310300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                Log.e(TAG, "Unable to access notification actions", e);
311300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                sActionsAccessFailed = true;
312300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                return null;
313300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen            }
314300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen        }
315300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen    }
316300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen
317300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen    private static boolean ensureActionReflectionReadyLocked() {
318300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen        if (sActionsAccessFailed) {
319300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen            return false;
320300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen        }
321300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen        try {
322300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen            if (sActionsField == null) {
323300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                sActionClass = Class.forName("android.app.Notification$Action");
324300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                sActionIconField = sActionClass.getDeclaredField("icon");
325300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                sActionTitleField = sActionClass.getDeclaredField("title");
326300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                sActionIntentField = sActionClass.getDeclaredField("actionIntent");
327300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                sActionsField = Notification.class.getDeclaredField("actions");
328300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen                sActionsField.setAccessible(true);
329300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen            }
330300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen        } catch (ClassNotFoundException e) {
331300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen            Log.e(TAG, "Unable to access notification actions", e);
332300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen            sActionsAccessFailed = true;
333300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen        } catch (NoSuchFieldException e) {
334300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen            Log.e(TAG, "Unable to access notification actions", e);
335300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen            sActionsAccessFailed = true;
336300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen        }
337300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen        return !sActionsAccessFailed;
338300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen    }
339300ad7c234a0ccfc41ae7fdbdcdd57faece2a8e0Griff Hazen
3402bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen    public static NotificationCompatBase.Action[] getActionsFromParcelableArrayList(
3412bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen            ArrayList<Parcelable> parcelables,
3422bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen            NotificationCompatBase.Action.Factory actionFactory,
3432bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen            RemoteInputCompatBase.RemoteInput.Factory remoteInputFactory) {
3442bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen        if (parcelables == null) {
3452bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen            return null;
3462bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen        }
3472bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen        NotificationCompatBase.Action[] actions = actionFactory.newArray(parcelables.size());
3482bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen        for (int i = 0; i < actions.length; i++) {
3492bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen            actions[i] = getActionFromBundle((Bundle) parcelables.get(i),
3502bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen                    actionFactory, remoteInputFactory);
3512bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen        }
3522bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen        return actions;
3532bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen    }
3542bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen
3552bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen    private static NotificationCompatBase.Action getActionFromBundle(Bundle bundle,
3562bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen            NotificationCompatBase.Action.Factory actionFactory,
3572bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen            RemoteInputCompatBase.RemoteInput.Factory remoteInputFactory) {
3582bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen        return actionFactory.build(
3592bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen                bundle.getInt(KEY_ICON),
3602bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen                bundle.getCharSequence(KEY_TITLE),
3612bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen                bundle.<PendingIntent>getParcelable(KEY_ACTION_INTENT),
3622bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen                bundle.getBundle(KEY_EXTRAS),
3632bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen                RemoteInputCompatJellybean.fromBundleArray(
3642bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen                        BundleUtil.getBundleArrayFromBundle(bundle, KEY_REMOTE_INPUTS),
3652bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen                        remoteInputFactory));
3662bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen    }
3672bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen
3682bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen    public static ArrayList<Parcelable> getParcelableArrayListForActions(
3692bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen            NotificationCompatBase.Action[] actions) {
3702bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen        if (actions == null) {
3712bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen            return null;
3722bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen        }
3732bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen        ArrayList<Parcelable> parcelables = new ArrayList<Parcelable>(actions.length);
3742bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen        for (NotificationCompatBase.Action action : actions) {
3752bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen            parcelables.add(getBundleForAction(action));
3762bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen        }
3772bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen        return parcelables;
3782bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen    }
3792bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen
3802bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen    private static Bundle getBundleForAction(NotificationCompatBase.Action action) {
3812bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen        Bundle bundle = new Bundle();
3822bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen        bundle.putInt(KEY_ICON, action.getIcon());
3832bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen        bundle.putCharSequence(KEY_TITLE, action.getTitle());
3842bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen        bundle.putParcelable(KEY_ACTION_INTENT, action.getActionIntent());
3852bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen        bundle.putBundle(KEY_EXTRAS, action.getExtras());
3862bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen        bundle.putParcelableArray(KEY_REMOTE_INPUTS, RemoteInputCompatJellybean.toBundleArray(
3872bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen                action.getRemoteInputs()));
3882bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen        return bundle;
3892bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen    }
3902bb98d48fdaf79a2bbd9d247da81a2bb9834dfc7Griff Hazen
391b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen    public static boolean getLocalOnly(Notification notif) {
392b56de0d1a113c71a2808303009ab4d9708ed6e84Griff Hazen        return getExtras(notif).getBoolean(EXTRA_LOCAL_ONLY);
39362d32dda7a6dd510ba7bbf11bb3edaa314c4948eGriff Hazen    }
394ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
395ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    public static String getGroup(Notification n) {
396ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        return getExtras(n).getString(EXTRA_GROUP_KEY);
397ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    }
398ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
399ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    public static boolean isGroupSummary(Notification n) {
400ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        return getExtras(n).getBoolean(EXTRA_GROUP_SUMMARY);
401ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    }
402ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen
403ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    public static String getSortKey(Notification n) {
404ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen        return getExtras(n).getString(EXTRA_SORT_KEY);
405ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen    }
406f021758934b35e3b842c6799344531d7ea2969daChris Wren}
407