1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.support.v4.app;
18
19import static android.support.v4.app.NotificationCompat.DEFAULT_SOUND;
20import static android.support.v4.app.NotificationCompat.DEFAULT_VIBRATE;
21import static android.support.v4.app.NotificationCompat.FLAG_GROUP_SUMMARY;
22import static android.support.v4.app.NotificationCompat.GROUP_ALERT_ALL;
23import static android.support.v4.app.NotificationCompat.GROUP_ALERT_CHILDREN;
24import static android.support.v4.app.NotificationCompat.GROUP_ALERT_SUMMARY;
25
26import android.app.Notification;
27import android.app.PendingIntent;
28import android.content.Context;
29import android.graphics.Bitmap;
30import android.os.Bundle;
31import android.os.Parcelable;
32import android.support.annotation.RequiresApi;
33import android.widget.RemoteViews;
34
35import java.util.ArrayList;
36
37@RequiresApi(21)
38class NotificationCompatApi21 {
39
40    public static final String CATEGORY_CALL = Notification.CATEGORY_CALL;
41    public static final String CATEGORY_MESSAGE = Notification.CATEGORY_MESSAGE;
42    public static final String CATEGORY_EMAIL = Notification.CATEGORY_EMAIL;
43    public static final String CATEGORY_EVENT = Notification.CATEGORY_EVENT;
44    public static final String CATEGORY_PROMO = Notification.CATEGORY_PROMO;
45    public static final String CATEGORY_ALARM = Notification.CATEGORY_ALARM;
46    public static final String CATEGORY_PROGRESS = Notification.CATEGORY_PROGRESS;
47    public static final String CATEGORY_SOCIAL = Notification.CATEGORY_SOCIAL;
48    public static final String CATEGORY_ERROR = Notification.CATEGORY_ERROR;
49    public static final String CATEGORY_TRANSPORT = Notification.CATEGORY_TRANSPORT;
50    public static final String CATEGORY_SYSTEM = Notification.CATEGORY_SYSTEM;
51    public static final String CATEGORY_SERVICE = Notification.CATEGORY_SERVICE;
52    public static final String CATEGORY_RECOMMENDATION = Notification.CATEGORY_RECOMMENDATION;
53    public static final String CATEGORY_STATUS = Notification.CATEGORY_STATUS;
54
55    private static final String KEY_AUTHOR = "author";
56    private static final String KEY_TEXT = "text";
57    private static final String KEY_MESSAGES = "messages";
58    private static final String KEY_REMOTE_INPUT = "remote_input";
59    private static final String KEY_ON_REPLY = "on_reply";
60    private static final String KEY_ON_READ = "on_read";
61    private static final String KEY_PARTICIPANTS = "participants";
62    private static final String KEY_TIMESTAMP = "timestamp";
63
64    public static class Builder implements NotificationBuilderWithBuilderAccessor,
65            NotificationBuilderWithActions {
66        private Notification.Builder b;
67        private Bundle mExtras;
68        private RemoteViews mContentView;
69        private RemoteViews mBigContentView;
70        private RemoteViews mHeadsUpContentView;
71        private int mGroupAlertBehavior;
72
73        public Builder(Context context, Notification n,
74                CharSequence contentTitle, CharSequence contentText, CharSequence contentInfo,
75                RemoteViews tickerView, int number,
76                PendingIntent contentIntent, PendingIntent fullScreenIntent, Bitmap largeIcon,
77                int progressMax, int progress, boolean progressIndeterminate, boolean showWhen,
78                boolean useChronometer, int priority, CharSequence subText, boolean localOnly,
79                String category, ArrayList<String> people, Bundle extras, int color,
80                int visibility, Notification publicVersion, String groupKey, boolean groupSummary,
81                String sortKey, RemoteViews contentView, RemoteViews bigContentView,
82                RemoteViews headsUpContentView, int groupAlertBehavior) {
83            b = new Notification.Builder(context)
84                    .setWhen(n.when)
85                    .setShowWhen(showWhen)
86                    .setSmallIcon(n.icon, n.iconLevel)
87                    .setContent(n.contentView)
88                    .setTicker(n.tickerText, tickerView)
89                    .setSound(n.sound, n.audioStreamType)
90                    .setVibrate(n.vibrate)
91                    .setLights(n.ledARGB, n.ledOnMS, n.ledOffMS)
92                    .setOngoing((n.flags & Notification.FLAG_ONGOING_EVENT) != 0)
93                    .setOnlyAlertOnce((n.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)
94                    .setAutoCancel((n.flags & Notification.FLAG_AUTO_CANCEL) != 0)
95                    .setDefaults(n.defaults)
96                    .setContentTitle(contentTitle)
97                    .setContentText(contentText)
98                    .setSubText(subText)
99                    .setContentInfo(contentInfo)
100                    .setContentIntent(contentIntent)
101                    .setDeleteIntent(n.deleteIntent)
102                    .setFullScreenIntent(fullScreenIntent,
103                            (n.flags & Notification.FLAG_HIGH_PRIORITY) != 0)
104                    .setLargeIcon(largeIcon)
105                    .setNumber(number)
106                    .setUsesChronometer(useChronometer)
107                    .setPriority(priority)
108                    .setProgress(progressMax, progress, progressIndeterminate)
109                    .setLocalOnly(localOnly)
110                    .setGroup(groupKey)
111                    .setGroupSummary(groupSummary)
112                    .setSortKey(sortKey)
113                    .setCategory(category)
114                    .setColor(color)
115                    .setVisibility(visibility)
116                    .setPublicVersion(publicVersion);
117            mExtras = new Bundle();
118            if (extras != null) {
119                mExtras.putAll(extras);
120            }
121            for (String person: people) {
122                b.addPerson(person);
123            }
124            mContentView = contentView;
125            mBigContentView = bigContentView;
126            mHeadsUpContentView = headsUpContentView;
127            mGroupAlertBehavior = groupAlertBehavior;
128        }
129
130        @Override
131        public void addAction(NotificationCompatBase.Action action) {
132            NotificationCompatApi20.addAction(b, action);
133        }
134
135        @Override
136        public Notification.Builder getBuilder() {
137            return b;
138        }
139
140        @Override
141        public Notification build() {
142            b.setExtras(mExtras);
143            Notification notification = b.build();
144            if (mContentView != null) {
145                notification.contentView = mContentView;
146            }
147            if (mBigContentView != null) {
148                notification.bigContentView = mBigContentView;
149            }
150            if (mHeadsUpContentView != null) {
151                notification.headsUpContentView = mHeadsUpContentView;
152            }
153
154            if (mGroupAlertBehavior != GROUP_ALERT_ALL) {
155                // if is summary and only children should alert
156                if (notification.getGroup() != null
157                        && (notification.flags & FLAG_GROUP_SUMMARY) != 0
158                        && mGroupAlertBehavior == GROUP_ALERT_CHILDREN) {
159                    removeSoundAndVibration(notification);
160                }
161                // if is group child and only summary should alert
162                if (notification.getGroup() != null
163                        && (notification.flags & FLAG_GROUP_SUMMARY) == 0
164                        && mGroupAlertBehavior == GROUP_ALERT_SUMMARY) {
165                    removeSoundAndVibration(notification);
166                }
167            }
168            return notification;
169        }
170
171        private void removeSoundAndVibration(Notification notification) {
172            notification.sound = null;
173            notification.vibrate = null;
174            notification.defaults &= ~DEFAULT_SOUND;
175            notification.defaults &= ~DEFAULT_VIBRATE;
176        }
177    }
178
179    static Bundle getBundleForUnreadConversation(NotificationCompatBase.UnreadConversation uc) {
180        if (uc == null) {
181            return null;
182        }
183        Bundle b = new Bundle();
184        String author = null;
185        if (uc.getParticipants() != null && uc.getParticipants().length > 1) {
186            author = uc.getParticipants()[0];
187        }
188        Parcelable[] messages = new Parcelable[uc.getMessages().length];
189        for (int i = 0; i < messages.length; i++) {
190            Bundle m = new Bundle();
191            m.putString(KEY_TEXT, uc.getMessages()[i]);
192            m.putString(KEY_AUTHOR, author);
193            messages[i] = m;
194        }
195        b.putParcelableArray(KEY_MESSAGES, messages);
196        RemoteInputCompatBase.RemoteInput remoteInput = uc.getRemoteInput();
197        if (remoteInput != null) {
198            b.putParcelable(KEY_REMOTE_INPUT, fromCompatRemoteInput(remoteInput));
199        }
200        b.putParcelable(KEY_ON_REPLY, uc.getReplyPendingIntent());
201        b.putParcelable(KEY_ON_READ, uc.getReadPendingIntent());
202        b.putStringArray(KEY_PARTICIPANTS, uc.getParticipants());
203        b.putLong(KEY_TIMESTAMP, uc.getLatestTimestamp());
204        return b;
205    }
206
207    static NotificationCompatBase.UnreadConversation getUnreadConversationFromBundle(
208            Bundle b, NotificationCompatBase.UnreadConversation.Factory factory,
209            RemoteInputCompatBase.RemoteInput.Factory remoteInputFactory) {
210        if (b == null) {
211            return null;
212        }
213        Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
214        String[] messages = null;
215        if (parcelableMessages != null) {
216            String[] tmp = new String[parcelableMessages.length];
217            boolean success = true;
218            for (int i = 0; i < tmp.length; i++) {
219                if (!(parcelableMessages[i] instanceof Bundle)) {
220                    success = false;
221                    break;
222                }
223                tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
224                if (tmp[i] == null) {
225                    success = false;
226                    break;
227                }
228            }
229            if (success) {
230                messages = tmp;
231            } else {
232                return null;
233            }
234        }
235
236        PendingIntent onRead = b.getParcelable(KEY_ON_READ);
237        PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
238
239        android.app.RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
240
241        String[] participants = b.getStringArray(KEY_PARTICIPANTS);
242        if (participants == null || participants.length != 1) {
243            return null;
244        }
245
246
247        return factory.build(
248                messages,
249                remoteInput != null ? toCompatRemoteInput(remoteInput, remoteInputFactory) : null,
250                onReply,
251                onRead,
252                participants, b.getLong(KEY_TIMESTAMP));
253    }
254
255    private static android.app.RemoteInput fromCompatRemoteInput(
256            RemoteInputCompatBase.RemoteInput src) {
257        return new android.app.RemoteInput.Builder(src.getResultKey())
258                .setLabel(src.getLabel())
259                .setChoices(src.getChoices())
260                .setAllowFreeFormInput(src.getAllowFreeFormInput())
261                .addExtras(src.getExtras())
262                .build();
263    }
264
265    private static RemoteInputCompatBase.RemoteInput toCompatRemoteInput(
266            android.app.RemoteInput remoteInput,
267            RemoteInputCompatBase.RemoteInput.Factory factory) {
268        return factory.build(remoteInput.getResultKey(),
269                remoteInput.getLabel(),
270                remoteInput.getChoices(),
271                remoteInput.getAllowFreeFormInput(),
272                remoteInput.getExtras(),
273                null /* allowedDataTypes */);
274    }
275}
276