3636import android .os .Bundle ;
3737import android .os .Handler ;
3838import android .os .Looper ;
39+ import androidx .annotation .DrawableRes ;
3940import androidx .annotation .Nullable ;
41+ import androidx .annotation .StringRes ;
4042import androidx .core .app .NotificationCompat ;
4143import androidx .core .graphics .drawable .IconCompat ;
4244import androidx .media .app .NotificationCompat .MediaStyle ;
5153import com .google .common .util .concurrent .FutureCallback ;
5254import com .google .common .util .concurrent .Futures ;
5355import com .google .common .util .concurrent .ListenableFuture ;
56+ import com .google .errorprone .annotations .CanIgnoreReturnValue ;
5457import java .util .ArrayList ;
5558import java .util .Arrays ;
5659import java .util .List ;
8992 * <li><b>{@code media3_notification_seek_to_previous}</b> - The previous icon.
9093 * <li><b>{@code media3_notification_seek_to_next}</b> - The next icon.
9194 * <li><b>{@code media3_notification_small_icon}</b> - The {@link
92- * NotificationCompat.Builder#setSmallIcon(int) small icon}.
95+ * NotificationCompat.Builder#setSmallIcon(int) small icon}. A different icon can be set with
96+ * {@link #setSmallIcon(int)}.
97+ * </ul>
98+ *
99+ * <h2>String resources</h2>
100+ *
101+ * String resources used can be overridden by resources with the same names defined the application.
102+ * These are:
103+ *
104+ * <ul>
105+ * <li><b>{@code media3_controls_play_description}</b> - The description of the play icon.
106+ * <li><b>{@code media3_controls_pause_description}</b> - The description of the pause icon.
107+ * <li><b>{@code media3_controls_seek_to_previous_description}</b> - The description of the
108+ * previous icon.
109+ * <li><b>{@code media3_controls_seek_to_next_description}</b> - The description of the next icon.
110+ * <li><b>{@code default_notification_channel_name}</b> The name of the {@link
111+ * NotificationChannel} on which created notifications are posted. A different string resource
112+ * can be set when constructing the provider with {@link
113+ * DefaultMediaNotificationProvider.Builder#setChannelName(int)}.
93114 * </ul>
94115 */
95116@ UnstableApi
96117public class DefaultMediaNotificationProvider implements MediaNotification .Provider {
97118
119+ /** A builder for {@link DefaultMediaNotificationProvider} instances. */
120+ public static final class Builder {
121+ private final Context context ;
122+ private int notificationId ;
123+ private String channelId ;
124+ @ StringRes private int channelNameResourceId ;
125+ private BitmapLoader bitmapLoader ;
126+ private boolean built ;
127+
128+ /**
129+ * Creates a builder.
130+ *
131+ * @param context Any {@link Context}.
132+ */
133+ public Builder (Context context ) {
134+ this .context = context ;
135+ notificationId = DEFAULT_NOTIFICATION_ID ;
136+ channelId = DEFAULT_CHANNEL_ID ;
137+ channelNameResourceId = DEFAULT_CHANNEL_NAME_RESOURCE_ID ;
138+ bitmapLoader = new SimpleBitmapLoader ();
139+ }
140+
141+ /**
142+ * Sets the {@link MediaNotification#notificationId} used for the created notifications. By
143+ * default this is set to {@link #DEFAULT_NOTIFICATION_ID}.
144+ *
145+ * @param notificationId The notification ID.
146+ * @return This builder.
147+ */
148+ @ CanIgnoreReturnValue
149+ public Builder setNotificationId (int notificationId ) {
150+ this .notificationId = notificationId ;
151+ return this ;
152+ }
153+
154+ /**
155+ * Sets the ID of the {@link NotificationChannel} on which created notifications are posted on.
156+ * By default this is set to {@link #DEFAULT_CHANNEL_ID}.
157+ *
158+ * @param channelId The channel ID.
159+ * @return This builder.
160+ */
161+ @ CanIgnoreReturnValue
162+ public Builder setChannelId (String channelId ) {
163+ this .channelId = channelId ;
164+ return this ;
165+ }
166+
167+ /**
168+ * Sets the name of the {@link NotificationChannel} on which created notifications are posted
169+ * on. By default this is set to {@link #DEFAULT_CHANNEL_NAME_RESOURCE_ID}.
170+ *
171+ * @param channelNameResourceId The string resource ID with the channel name.
172+ * @return This builder.
173+ */
174+ @ CanIgnoreReturnValue
175+ public Builder setChannelName (@ StringRes int channelNameResourceId ) {
176+ this .channelNameResourceId = channelNameResourceId ;
177+ return this ;
178+ }
179+
180+ /**
181+ * Sets the {@link BitmapLoader} used load artwork. By default, a {@link SimpleBitmapLoader}
182+ * will be used.
183+ *
184+ * @param bitmapLoader The bitmap loader.
185+ * @return This builder.
186+ */
187+ @ CanIgnoreReturnValue
188+ public Builder setBitmapLoader (BitmapLoader bitmapLoader ) {
189+ this .bitmapLoader = bitmapLoader ;
190+ return this ;
191+ }
192+
193+ /**
194+ * Builds the {@link DefaultMediaNotificationProvider}. The method can be called at most once.
195+ */
196+ public DefaultMediaNotificationProvider build () {
197+ checkState (!built );
198+ DefaultMediaNotificationProvider provider = new DefaultMediaNotificationProvider (this );
199+ built = true ;
200+ return provider ;
201+ }
202+ }
203+
98204 /**
99205 * An extras key that can be used to define the index of a {@link CommandButton} in {@linkplain
100206 * Notification.MediaStyle#setShowActionsInCompactView(int...) compact view}.
101207 */
102208 public static final String COMMAND_KEY_COMPACT_VIEW_INDEX =
103209 "androidx.media3.session.command.COMPACT_VIEW_INDEX" ;
104210
211+ /** The default ID used for the {@link MediaNotification#notificationId}. */
212+ public static final int DEFAULT_NOTIFICATION_ID = 1001 ;
213+ /**
214+ * The default ID used for the {@link NotificationChannel} on which created notifications are
215+ * posted on.
216+ */
217+ public static final String DEFAULT_CHANNEL_ID = "default_channel_id" ;
218+ /**
219+ * The default name used for the {@link NotificationChannel} on which created notifications are
220+ * posted on.
221+ */
222+ @ StringRes
223+ public static final int DEFAULT_CHANNEL_NAME_RESOURCE_ID =
224+ R .string .default_notification_channel_name ;
225+
105226 private static final String TAG = "NotificationProvider" ;
106- private static final int NOTIFICATION_ID = 1001 ;
107- private static final String NOTIFICATION_CHANNEL_ID = "default_channel_id" ;
108- private static final String NOTIFICATION_CHANNEL_NAME = "Now playing" ;
109227
110228 private final Context context ;
229+ private final int notificationId ;
230+ private final String channelId ;
231+ @ StringRes private final int channelNameResourceId ;
111232 private final NotificationManager notificationManager ;
112233 private final BitmapLoader bitmapLoader ;
113234 // Cache the last loaded bitmap to avoid reloading the bitmap again, particularly useful when
@@ -116,24 +237,25 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
116237 private final Handler mainHandler ;
117238
118239 private OnBitmapLoadedFutureCallback pendingOnBitmapLoadedFutureCallback ;
119-
120- /** Creates an instance that uses a {@link SimpleBitmapLoader} for loading artwork images. */
121- public DefaultMediaNotificationProvider (Context context ) {
122- this (context , new SimpleBitmapLoader ());
123- }
124-
125- /** Creates an instance that uses the {@code bitmapLoader} for loading artwork images. */
126- public DefaultMediaNotificationProvider (Context context , BitmapLoader bitmapLoader ) {
127- this .context = context .getApplicationContext ();
128- this .bitmapLoader = bitmapLoader ;
129- lastLoadedBitmapInfo = new LoadedBitmapInfo ();
130- mainHandler = new Handler (Looper .getMainLooper ());
240+ @ DrawableRes private int smallIconResourceId ;
241+
242+ private DefaultMediaNotificationProvider (Builder builder ) {
243+ this .context = builder .context ;
244+ this .notificationId = builder .notificationId ;
245+ this .channelId = builder .channelId ;
246+ this .channelNameResourceId = builder .channelNameResourceId ;
247+ this .bitmapLoader = builder .bitmapLoader ;
131248 notificationManager =
132249 checkStateNotNull (
133250 (NotificationManager ) context .getSystemService (Context .NOTIFICATION_SERVICE ));
251+ lastLoadedBitmapInfo = new LoadedBitmapInfo ();
252+ mainHandler = new Handler (Looper .getMainLooper ());
134253 pendingOnBitmapLoadedFutureCallback = new OnBitmapLoadedFutureCallback (bitmap -> {});
254+ smallIconResourceId = R .drawable .media3_notification_small_icon ;
135255 }
136256
257+ // MediaNotification.Provider implementation
258+
137259 @ Override
138260 public final MediaNotification createNotification (
139261 MediaSession mediaSession ,
@@ -143,8 +265,7 @@ public final MediaNotification createNotification(
143265 ensureNotificationChannel ();
144266
145267 Player player = mediaSession .getPlayer ();
146- NotificationCompat .Builder builder =
147- new NotificationCompat .Builder (context , NOTIFICATION_CHANNEL_ID );
268+ NotificationCompat .Builder builder = new NotificationCompat .Builder (context , channelId );
148269
149270 MediaStyle mediaStyle = new MediaStyle ();
150271 int [] compactViewIndices =
@@ -173,7 +294,7 @@ public final MediaNotification createNotification(
173294 bitmap -> {
174295 builder .setLargeIcon (bitmap );
175296 onNotificationChangedCallback .onNotificationChanged (
176- new MediaNotification (NOTIFICATION_ID , builder .build ()));
297+ new MediaNotification (notificationId , builder .build ()));
177298 }),
178299 // This callback must be executed on the next looper iteration, after this method has
179300 // returned a media notification.
@@ -200,12 +321,12 @@ public final MediaNotification createNotification(
200321 .setDeleteIntent (
201322 actionFactory .createMediaActionPendingIntent (mediaSession , COMMAND_STOP ))
202323 .setOnlyAlertOnce (true )
203- .setSmallIcon (R . drawable . media3_notification_small_icon )
324+ .setSmallIcon (smallIconResourceId )
204325 .setStyle (mediaStyle )
205326 .setVisibility (NotificationCompat .VISIBILITY_PUBLIC )
206327 .setOngoing (false )
207328 .build ();
208- return new MediaNotification (NOTIFICATION_ID , notification );
329+ return new MediaNotification (notificationId , notification );
209330 }
210331
211332 @ Override
@@ -214,6 +335,18 @@ public final boolean handleCustomCommand(MediaSession session, String action, Bu
214335 return false ;
215336 }
216337
338+ // Other methods
339+
340+ /**
341+ * Sets the small icon of the notification which is also shown in the system status bar.
342+ *
343+ * @see NotificationCompat.Builder#setSmallIcon(int)
344+ * @param smallIconResourceId The resource id of the small icon.
345+ */
346+ public final void setSmallIcon (@ DrawableRes int smallIconResourceId ) {
347+ this .smallIconResourceId = smallIconResourceId ;
348+ }
349+
217350 /**
218351 * Returns the ordered list of {@linkplain CommandButton command buttons} to be used to build the
219352 * notification.
@@ -367,13 +500,14 @@ protected int[] addNotificationActions(
367500 }
368501
369502 private void ensureNotificationChannel () {
370- if (Util .SDK_INT < 26
371- || notificationManager .getNotificationChannel (NOTIFICATION_CHANNEL_ID ) != null ) {
503+ if (Util .SDK_INT < 26 || notificationManager .getNotificationChannel (channelId ) != null ) {
372504 return ;
373505 }
374506 NotificationChannel channel =
375507 new NotificationChannel (
376- NOTIFICATION_CHANNEL_ID , NOTIFICATION_CHANNEL_NAME , NotificationManager .IMPORTANCE_LOW );
508+ channelId ,
509+ context .getString (channelNameResourceId ),
510+ NotificationManager .IMPORTANCE_LOW );
377511 notificationManager .createNotificationChannel (channel );
378512 }
379513
0 commit comments