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 
17 package android.support.v4.app;
18 
19 import static android.support.v4.app.NotificationCompat.DEFAULT_SOUND;
20 import static android.support.v4.app.NotificationCompat.DEFAULT_VIBRATE;
21 import static android.support.v4.app.NotificationCompat.FLAG_GROUP_SUMMARY;
22 import static android.support.v4.app.NotificationCompat.GROUP_ALERT_ALL;
23 import static android.support.v4.app.NotificationCompat.GROUP_ALERT_CHILDREN;
24 import static android.support.v4.app.NotificationCompat.GROUP_ALERT_SUMMARY;
25 
26 import android.app.Notification;
27 import android.app.PendingIntent;
28 import android.app.RemoteInput;
29 import android.content.Context;
30 import android.graphics.Bitmap;
31 import android.os.Bundle;
32 import android.os.Parcelable;
33 import android.support.annotation.RequiresApi;
34 import android.widget.RemoteViews;
35 
36 import java.util.ArrayList;
37 
38 @RequiresApi(20)
39 class NotificationCompatApi20 {
40     public static class Builder implements NotificationBuilderWithBuilderAccessor,
41             NotificationBuilderWithActions {
42         private Notification.Builder b;
43         private Bundle mExtras;
44         private RemoteViews mContentView;
45         private RemoteViews mBigContentView;
46         private int mGroupAlertBehavior;
47 
Builder(Context context, Notification n, CharSequence contentTitle, CharSequence contentText, CharSequence contentInfo, RemoteViews tickerView, int number, PendingIntent contentIntent, PendingIntent fullScreenIntent, Bitmap largeIcon, int progressMax, int progress, boolean progressIndeterminate, boolean showWhen, boolean useChronometer, int priority, CharSequence subText, boolean localOnly, ArrayList<String> people, Bundle extras, String groupKey, boolean groupSummary, String sortKey, RemoteViews contentView, RemoteViews bigContentView, int groupAlertBehavior)48         public Builder(Context context, Notification n,
49                 CharSequence contentTitle, CharSequence contentText, CharSequence contentInfo,
50                 RemoteViews tickerView, int number,
51                 PendingIntent contentIntent, PendingIntent fullScreenIntent, Bitmap largeIcon,
52                 int progressMax, int progress, boolean progressIndeterminate, boolean showWhen,
53                 boolean useChronometer, int priority, CharSequence subText, boolean localOnly,
54                 ArrayList<String> people, Bundle extras, String groupKey, boolean groupSummary,
55                 String sortKey, RemoteViews contentView, RemoteViews bigContentView,
56                 int groupAlertBehavior) {
57             b = new Notification.Builder(context)
58                 .setWhen(n.when)
59                 .setShowWhen(showWhen)
60                 .setSmallIcon(n.icon, n.iconLevel)
61                 .setContent(n.contentView)
62                 .setTicker(n.tickerText, tickerView)
63                 .setSound(n.sound, n.audioStreamType)
64                 .setVibrate(n.vibrate)
65                 .setLights(n.ledARGB, n.ledOnMS, n.ledOffMS)
66                 .setOngoing((n.flags & Notification.FLAG_ONGOING_EVENT) != 0)
67                 .setOnlyAlertOnce((n.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)
68                 .setAutoCancel((n.flags & Notification.FLAG_AUTO_CANCEL) != 0)
69                 .setDefaults(n.defaults)
70                 .setContentTitle(contentTitle)
71                 .setContentText(contentText)
72                 .setSubText(subText)
73                 .setContentInfo(contentInfo)
74                 .setContentIntent(contentIntent)
75                 .setDeleteIntent(n.deleteIntent)
76                 .setFullScreenIntent(fullScreenIntent,
77                         (n.flags & Notification.FLAG_HIGH_PRIORITY) != 0)
78                 .setLargeIcon(largeIcon)
79                 .setNumber(number)
80                 .setUsesChronometer(useChronometer)
81                 .setPriority(priority)
82                 .setProgress(progressMax, progress, progressIndeterminate)
83                 .setLocalOnly(localOnly)
84                 .setGroup(groupKey)
85                 .setGroupSummary(groupSummary)
86                 .setSortKey(sortKey);
87             mExtras = new Bundle();
88             if (extras != null) {
89                 mExtras.putAll(extras);
90             }
91             if (people != null && !people.isEmpty()) {
92                 mExtras.putStringArray(Notification.EXTRA_PEOPLE,
93                         people.toArray(new String[people.size()]));
94             }
95             mContentView = contentView;
96             mBigContentView = bigContentView;
97             mGroupAlertBehavior = groupAlertBehavior;
98         }
99 
100         @Override
addAction(NotificationCompatBase.Action action)101         public void addAction(NotificationCompatBase.Action action) {
102             NotificationCompatApi20.addAction(b, action);
103         }
104 
105         @Override
getBuilder()106         public Notification.Builder getBuilder() {
107             return b;
108         }
109 
110         @Override
build()111         public Notification build() {
112             b.setExtras(mExtras);
113             Notification notification = b.build();
114             if (mContentView != null) {
115                 notification.contentView = mContentView;
116             }
117             if (mBigContentView != null) {
118                 notification.bigContentView = mBigContentView;
119             }
120 
121             if (mGroupAlertBehavior != GROUP_ALERT_ALL) {
122                 // if is summary and only children should alert
123                 if (notification.getGroup() != null
124                         && (notification.flags & FLAG_GROUP_SUMMARY) != 0
125                         && mGroupAlertBehavior == GROUP_ALERT_CHILDREN) {
126                     removeSoundAndVibration(notification);
127                 }
128                 // if is group child and only summary should alert
129                 if (notification.getGroup() != null
130                         && (notification.flags & FLAG_GROUP_SUMMARY) == 0
131                         && mGroupAlertBehavior == GROUP_ALERT_SUMMARY) {
132                     removeSoundAndVibration(notification);
133                 }
134             }
135 
136             return notification;
137         }
138 
removeSoundAndVibration(Notification notification)139         private void removeSoundAndVibration(Notification notification) {
140             notification.sound = null;
141             notification.vibrate = null;
142             notification.defaults &= ~DEFAULT_SOUND;
143             notification.defaults &= ~DEFAULT_VIBRATE;
144         }
145     }
146 
addAction(Notification.Builder b, NotificationCompatBase.Action action)147     public static void addAction(Notification.Builder b, NotificationCompatBase.Action action) {
148         Notification.Action.Builder actionBuilder = new Notification.Action.Builder(
149                 action.getIcon(), action.getTitle(), action.getActionIntent());
150         if (action.getRemoteInputs() != null) {
151             for (RemoteInput remoteInput : RemoteInputCompatApi20.fromCompat(
152                     action.getRemoteInputs())) {
153                 actionBuilder.addRemoteInput(remoteInput);
154             }
155         }
156         Bundle actionExtras;
157         if (action.getExtras() != null) {
158             actionExtras = new Bundle(action.getExtras());
159         } else {
160             actionExtras = new Bundle();
161         }
162         actionExtras.putBoolean(NotificationCompatJellybean.EXTRA_ALLOW_GENERATED_REPLIES,
163                 action.getAllowGeneratedReplies());
164         actionBuilder.addExtras(actionExtras);
165         b.addAction(actionBuilder.build());
166     }
167 
getAction(Notification notif, int actionIndex, NotificationCompatBase.Action.Factory actionFactory, RemoteInputCompatBase.RemoteInput.Factory remoteInputFactory)168     public static NotificationCompatBase.Action getAction(Notification notif,
169             int actionIndex, NotificationCompatBase.Action.Factory actionFactory,
170             RemoteInputCompatBase.RemoteInput.Factory remoteInputFactory) {
171         return getActionCompatFromAction(notif.actions[actionIndex], actionFactory, remoteInputFactory);
172     }
173 
getActionCompatFromAction( Notification.Action action, NotificationCompatBase.Action.Factory actionFactory, RemoteInputCompatBase.RemoteInput.Factory remoteInputFactory)174     private static NotificationCompatBase.Action getActionCompatFromAction(
175             Notification.Action action, NotificationCompatBase.Action.Factory actionFactory,
176             RemoteInputCompatBase.RemoteInput.Factory remoteInputFactory) {
177         RemoteInputCompatBase.RemoteInput[] remoteInputs = RemoteInputCompatApi20.toCompat(
178                 action.getRemoteInputs(), remoteInputFactory);
179         boolean allowGeneratedReplies = action.getExtras().getBoolean(
180                 NotificationCompatJellybean.EXTRA_ALLOW_GENERATED_REPLIES);
181         return actionFactory.build(action.icon, action.title, action.actionIntent,
182                 action.getExtras(), remoteInputs, null, allowGeneratedReplies);
183     }
184 
getActionFromActionCompat( NotificationCompatBase.Action actionCompat)185     private static Notification.Action getActionFromActionCompat(
186             NotificationCompatBase.Action actionCompat) {
187         Notification.Action.Builder actionBuilder = new Notification.Action.Builder(
188                 actionCompat.getIcon(), actionCompat.getTitle(), actionCompat.getActionIntent());
189         Bundle actionExtras;
190         if (actionCompat.getExtras() != null) {
191             actionExtras = new Bundle(actionCompat.getExtras());
192         } else {
193             actionExtras = new Bundle();
194         }
195         actionExtras.putBoolean(NotificationCompatJellybean.EXTRA_ALLOW_GENERATED_REPLIES,
196                 actionCompat.getAllowGeneratedReplies());
197         actionBuilder.addExtras(actionExtras);
198         RemoteInputCompatBase.RemoteInput[] remoteInputCompats = actionCompat.getRemoteInputs();
199         if (remoteInputCompats != null) {
200             RemoteInput[] remoteInputs = RemoteInputCompatApi20.fromCompat(remoteInputCompats);
201             for (RemoteInput remoteInput : remoteInputs) {
202                 actionBuilder.addRemoteInput(remoteInput);
203             }
204         }
205         return actionBuilder.build();
206     }
207 
208     /**
209      * Get a list of notification compat actions by parsing actions stored within a list of
210      * parcelables using the {@link Bundle#getParcelableArrayList} function in the same
211      * manner that framework code would do so. In API20, Using Action parcelable directly
212      * is correct.
213      */
getActionsFromParcelableArrayList( ArrayList<Parcelable> parcelables, NotificationCompatBase.Action.Factory actionFactory, RemoteInputCompatBase.RemoteInput.Factory remoteInputFactory)214     public static NotificationCompatBase.Action[] getActionsFromParcelableArrayList(
215             ArrayList<Parcelable> parcelables,
216             NotificationCompatBase.Action.Factory actionFactory,
217             RemoteInputCompatBase.RemoteInput.Factory remoteInputFactory) {
218         if (parcelables == null) {
219             return null;
220         }
221         NotificationCompatBase.Action[] actions = actionFactory.newArray(parcelables.size());
222         for (int i = 0; i < actions.length; i++) {
223             Notification.Action action = (Notification.Action) parcelables.get(i);
224             actions[i] = getActionCompatFromAction(action, actionFactory, remoteInputFactory);
225         }
226         return actions;
227     }
228 
229     /**
230      * Get an array list of parcelables, suitable for {@link Bundle#putParcelableArrayList},
231      * that matches what framework code would do to store an actions list in this way. In API20,
232      * action parcelables were directly placed as entries in the array list.
233      */
getParcelableArrayListForActions( NotificationCompatBase.Action[] actions)234     public static ArrayList<Parcelable> getParcelableArrayListForActions(
235             NotificationCompatBase.Action[] actions) {
236         if (actions == null) {
237             return null;
238         }
239         ArrayList<Parcelable> parcelables = new ArrayList<Parcelable>(actions.length);
240         for (NotificationCompatBase.Action action : actions) {
241             parcelables.add(getActionFromActionCompat(action));
242         }
243         return parcelables;
244     }
245 }
246