Skip to content

Commit 62a2d76

Browse files
christostsmicrokatz
authored andcommitted
Make DefaultMediaNotificationProvider more configurable
Add a Builder to constructor DefaultMediaNotificationProvider. The Builder can also set the provider's: - notification ID - notification channel ID - notification channel name The change adds an API for apps to set the small icon in notifications. #minor-release Issue: #104 PiperOrigin-RevId: 462111536 (cherry picked from commit 436ff6d)
1 parent eb823a9 commit 62a2d76

5 files changed

Lines changed: 281 additions & 34 deletions

File tree

‎RELEASENOTES.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@
1919
* Ensure commands are always executed in the correct order even if some
2020
require asynchronous resolution
2121
([#85](https://github.com/androidx/media/issues/85)).
22+
* Add `DefaultNotificationProvider.Builder` to build
23+
`DefaultNotificationProvider` instances. The builder can configure the
24+
notification ID, the notification channel ID and the notification
25+
channel name used by the provider. Also, add method
26+
`DefaultNotificationProvider.setSmallIcon(int)` to set the notifications
27+
small icon ([#104](https://github.com/androidx/media/issues/104)).
2228

2329
### 1.0.0-beta02 (2022-07-15)
2430

‎libraries/session/build.gradle‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ android {
3232
}
3333
dependencies {
3434
api project(modulePrefix + 'lib-common')
35+
compileOnly 'com.google.errorprone:error_prone_annotations:' + errorProneVersion
3536
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
3637
compileOnly 'org.checkerframework:checker-compat-qual:' + checkerframeworkCompatVersion
3738
implementation 'androidx.collection:collection:' + androidxCollectionVersion

‎libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java‎

Lines changed: 158 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@
3636
import android.os.Bundle;
3737
import android.os.Handler;
3838
import android.os.Looper;
39+
import androidx.annotation.DrawableRes;
3940
import androidx.annotation.Nullable;
41+
import androidx.annotation.StringRes;
4042
import androidx.core.app.NotificationCompat;
4143
import androidx.core.graphics.drawable.IconCompat;
4244
import androidx.media.app.NotificationCompat.MediaStyle;
@@ -51,6 +53,7 @@
5153
import com.google.common.util.concurrent.FutureCallback;
5254
import com.google.common.util.concurrent.Futures;
5355
import com.google.common.util.concurrent.ListenableFuture;
56+
import com.google.errorprone.annotations.CanIgnoreReturnValue;
5457
import java.util.ArrayList;
5558
import java.util.Arrays;
5659
import java.util.List;
@@ -89,25 +92,143 @@
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
96117
public 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

‎libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,8 @@ private MediaNotificationManager getMediaNotificationManager() {
433433
synchronized (lock) {
434434
if (mediaNotificationManager == null) {
435435
if (mediaNotificationProvider == null) {
436-
mediaNotificationProvider = new DefaultMediaNotificationProvider(getApplicationContext());
436+
mediaNotificationProvider =
437+
new DefaultMediaNotificationProvider.Builder(getApplicationContext()).build();
437438
}
438439
mediaNotificationManager =
439440
new MediaNotificationManager(

0 commit comments

Comments
 (0)