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