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: androidx/media#104 PiperOrigin-RevId: 462111536 (cherry picked from commit 436ff6d86a4b8de5324d3b3b08bb655b75ca6632)
This commit is contained in:
parent
eb823a9ab7
commit
62a2d76d00
@ -19,6 +19,12 @@
|
||||
* Ensure commands are always executed in the correct order even if some
|
||||
require asynchronous resolution
|
||||
([#85](https://github.com/androidx/media/issues/85)).
|
||||
* Add `DefaultNotificationProvider.Builder` to build
|
||||
`DefaultNotificationProvider` instances. The builder can configure the
|
||||
notification ID, the notification channel ID and the notification
|
||||
channel name used by the provider. Also, add method
|
||||
`DefaultNotificationProvider.setSmallIcon(int)` to set the notifications
|
||||
small icon ([#104](https://github.com/androidx/media/issues/104)).
|
||||
|
||||
### 1.0.0-beta02 (2022-07-15)
|
||||
|
||||
|
@ -32,6 +32,7 @@ android {
|
||||
}
|
||||
dependencies {
|
||||
api project(modulePrefix + 'lib-common')
|
||||
compileOnly 'com.google.errorprone:error_prone_annotations:' + errorProneVersion
|
||||
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
|
||||
compileOnly 'org.checkerframework:checker-compat-qual:' + checkerframeworkCompatVersion
|
||||
implementation 'androidx.collection:collection:' + androidxCollectionVersion
|
||||
|
@ -36,7 +36,9 @@ import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
import androidx.media.app.NotificationCompat.MediaStyle;
|
||||
@ -51,6 +53,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@ -89,12 +92,115 @@ import java.util.concurrent.ExecutionException;
|
||||
* <li><b>{@code media3_notification_seek_to_previous}</b> - The previous icon.
|
||||
* <li><b>{@code media3_notification_seek_to_next}</b> - The next icon.
|
||||
* <li><b>{@code media3_notification_small_icon}</b> - The {@link
|
||||
* NotificationCompat.Builder#setSmallIcon(int) small icon}.
|
||||
* NotificationCompat.Builder#setSmallIcon(int) small icon}. A different icon can be set with
|
||||
* {@link #setSmallIcon(int)}.
|
||||
* </ul>
|
||||
*
|
||||
* <h2>String resources</h2>
|
||||
*
|
||||
* String resources used can be overridden by resources with the same names defined the application.
|
||||
* These are:
|
||||
*
|
||||
* <ul>
|
||||
* <li><b>{@code media3_controls_play_description}</b> - The description of the play icon.
|
||||
* <li><b>{@code media3_controls_pause_description}</b> - The description of the pause icon.
|
||||
* <li><b>{@code media3_controls_seek_to_previous_description}</b> - The description of the
|
||||
* previous icon.
|
||||
* <li><b>{@code media3_controls_seek_to_next_description}</b> - The description of the next icon.
|
||||
* <li><b>{@code default_notification_channel_name}</b> The name of the {@link
|
||||
* NotificationChannel} on which created notifications are posted. A different string resource
|
||||
* can be set when constructing the provider with {@link
|
||||
* DefaultMediaNotificationProvider.Builder#setChannelName(int)}.
|
||||
* </ul>
|
||||
*/
|
||||
@UnstableApi
|
||||
public class DefaultMediaNotificationProvider implements MediaNotification.Provider {
|
||||
|
||||
/** A builder for {@link DefaultMediaNotificationProvider} instances. */
|
||||
public static final class Builder {
|
||||
private final Context context;
|
||||
private int notificationId;
|
||||
private String channelId;
|
||||
@StringRes private int channelNameResourceId;
|
||||
private BitmapLoader bitmapLoader;
|
||||
private boolean built;
|
||||
|
||||
/**
|
||||
* Creates a builder.
|
||||
*
|
||||
* @param context Any {@link Context}.
|
||||
*/
|
||||
public Builder(Context context) {
|
||||
this.context = context;
|
||||
notificationId = DEFAULT_NOTIFICATION_ID;
|
||||
channelId = DEFAULT_CHANNEL_ID;
|
||||
channelNameResourceId = DEFAULT_CHANNEL_NAME_RESOURCE_ID;
|
||||
bitmapLoader = new SimpleBitmapLoader();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link MediaNotification#notificationId} used for the created notifications. By
|
||||
* default this is set to {@link #DEFAULT_NOTIFICATION_ID}.
|
||||
*
|
||||
* @param notificationId The notification ID.
|
||||
* @return This builder.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public Builder setNotificationId(int notificationId) {
|
||||
this.notificationId = notificationId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ID of the {@link NotificationChannel} on which created notifications are posted on.
|
||||
* By default this is set to {@link #DEFAULT_CHANNEL_ID}.
|
||||
*
|
||||
* @param channelId The channel ID.
|
||||
* @return This builder.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public Builder setChannelId(String channelId) {
|
||||
this.channelId = channelId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the {@link NotificationChannel} on which created notifications are posted
|
||||
* on. By default this is set to {@link #DEFAULT_CHANNEL_NAME_RESOURCE_ID}.
|
||||
*
|
||||
* @param channelNameResourceId The string resource ID with the channel name.
|
||||
* @return This builder.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public Builder setChannelName(@StringRes int channelNameResourceId) {
|
||||
this.channelNameResourceId = channelNameResourceId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link BitmapLoader} used load artwork. By default, a {@link SimpleBitmapLoader}
|
||||
* will be used.
|
||||
*
|
||||
* @param bitmapLoader The bitmap loader.
|
||||
* @return This builder.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public Builder setBitmapLoader(BitmapLoader bitmapLoader) {
|
||||
this.bitmapLoader = bitmapLoader;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the {@link DefaultMediaNotificationProvider}. The method can be called at most once.
|
||||
*/
|
||||
public DefaultMediaNotificationProvider build() {
|
||||
checkState(!built);
|
||||
DefaultMediaNotificationProvider provider = new DefaultMediaNotificationProvider(this);
|
||||
built = true;
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An extras key that can be used to define the index of a {@link CommandButton} in {@linkplain
|
||||
* Notification.MediaStyle#setShowActionsInCompactView(int...) compact view}.
|
||||
@ -102,12 +208,27 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
|
||||
public static final String COMMAND_KEY_COMPACT_VIEW_INDEX =
|
||||
"androidx.media3.session.command.COMPACT_VIEW_INDEX";
|
||||
|
||||
/** The default ID used for the {@link MediaNotification#notificationId}. */
|
||||
public static final int DEFAULT_NOTIFICATION_ID = 1001;
|
||||
/**
|
||||
* The default ID used for the {@link NotificationChannel} on which created notifications are
|
||||
* posted on.
|
||||
*/
|
||||
public static final String DEFAULT_CHANNEL_ID = "default_channel_id";
|
||||
/**
|
||||
* The default name used for the {@link NotificationChannel} on which created notifications are
|
||||
* posted on.
|
||||
*/
|
||||
@StringRes
|
||||
public static final int DEFAULT_CHANNEL_NAME_RESOURCE_ID =
|
||||
R.string.default_notification_channel_name;
|
||||
|
||||
private static final String TAG = "NotificationProvider";
|
||||
private static final int NOTIFICATION_ID = 1001;
|
||||
private static final String NOTIFICATION_CHANNEL_ID = "default_channel_id";
|
||||
private static final String NOTIFICATION_CHANNEL_NAME = "Now playing";
|
||||
|
||||
private final Context context;
|
||||
private final int notificationId;
|
||||
private final String channelId;
|
||||
@StringRes private final int channelNameResourceId;
|
||||
private final NotificationManager notificationManager;
|
||||
private final BitmapLoader bitmapLoader;
|
||||
// Cache the last loaded bitmap to avoid reloading the bitmap again, particularly useful when
|
||||
@ -116,24 +237,25 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
|
||||
private final Handler mainHandler;
|
||||
|
||||
private OnBitmapLoadedFutureCallback pendingOnBitmapLoadedFutureCallback;
|
||||
@DrawableRes private int smallIconResourceId;
|
||||
|
||||
/** Creates an instance that uses a {@link SimpleBitmapLoader} for loading artwork images. */
|
||||
public DefaultMediaNotificationProvider(Context context) {
|
||||
this(context, new SimpleBitmapLoader());
|
||||
}
|
||||
|
||||
/** Creates an instance that uses the {@code bitmapLoader} for loading artwork images. */
|
||||
public DefaultMediaNotificationProvider(Context context, BitmapLoader bitmapLoader) {
|
||||
this.context = context.getApplicationContext();
|
||||
this.bitmapLoader = bitmapLoader;
|
||||
lastLoadedBitmapInfo = new LoadedBitmapInfo();
|
||||
mainHandler = new Handler(Looper.getMainLooper());
|
||||
private DefaultMediaNotificationProvider(Builder builder) {
|
||||
this.context = builder.context;
|
||||
this.notificationId = builder.notificationId;
|
||||
this.channelId = builder.channelId;
|
||||
this.channelNameResourceId = builder.channelNameResourceId;
|
||||
this.bitmapLoader = builder.bitmapLoader;
|
||||
notificationManager =
|
||||
checkStateNotNull(
|
||||
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE));
|
||||
lastLoadedBitmapInfo = new LoadedBitmapInfo();
|
||||
mainHandler = new Handler(Looper.getMainLooper());
|
||||
pendingOnBitmapLoadedFutureCallback = new OnBitmapLoadedFutureCallback(bitmap -> {});
|
||||
smallIconResourceId = R.drawable.media3_notification_small_icon;
|
||||
}
|
||||
|
||||
// MediaNotification.Provider implementation
|
||||
|
||||
@Override
|
||||
public final MediaNotification createNotification(
|
||||
MediaSession mediaSession,
|
||||
@ -143,8 +265,7 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
|
||||
ensureNotificationChannel();
|
||||
|
||||
Player player = mediaSession.getPlayer();
|
||||
NotificationCompat.Builder builder =
|
||||
new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID);
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId);
|
||||
|
||||
MediaStyle mediaStyle = new MediaStyle();
|
||||
int[] compactViewIndices =
|
||||
@ -173,7 +294,7 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
|
||||
bitmap -> {
|
||||
builder.setLargeIcon(bitmap);
|
||||
onNotificationChangedCallback.onNotificationChanged(
|
||||
new MediaNotification(NOTIFICATION_ID, builder.build()));
|
||||
new MediaNotification(notificationId, builder.build()));
|
||||
}),
|
||||
// This callback must be executed on the next looper iteration, after this method has
|
||||
// returned a media notification.
|
||||
@ -200,12 +321,12 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
|
||||
.setDeleteIntent(
|
||||
actionFactory.createMediaActionPendingIntent(mediaSession, COMMAND_STOP))
|
||||
.setOnlyAlertOnce(true)
|
||||
.setSmallIcon(R.drawable.media3_notification_small_icon)
|
||||
.setSmallIcon(smallIconResourceId)
|
||||
.setStyle(mediaStyle)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setOngoing(false)
|
||||
.build();
|
||||
return new MediaNotification(NOTIFICATION_ID, notification);
|
||||
return new MediaNotification(notificationId, notification);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -214,6 +335,18 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
|
||||
return false;
|
||||
}
|
||||
|
||||
// Other methods
|
||||
|
||||
/**
|
||||
* Sets the small icon of the notification which is also shown in the system status bar.
|
||||
*
|
||||
* @see NotificationCompat.Builder#setSmallIcon(int)
|
||||
* @param smallIconResourceId The resource id of the small icon.
|
||||
*/
|
||||
public final void setSmallIcon(@DrawableRes int smallIconResourceId) {
|
||||
this.smallIconResourceId = smallIconResourceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ordered list of {@linkplain CommandButton command buttons} to be used to build the
|
||||
* notification.
|
||||
@ -367,13 +500,14 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
|
||||
}
|
||||
|
||||
private void ensureNotificationChannel() {
|
||||
if (Util.SDK_INT < 26
|
||||
|| notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID) != null) {
|
||||
if (Util.SDK_INT < 26 || notificationManager.getNotificationChannel(channelId) != null) {
|
||||
return;
|
||||
}
|
||||
NotificationChannel channel =
|
||||
new NotificationChannel(
|
||||
NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW);
|
||||
channelId,
|
||||
context.getString(channelNameResourceId),
|
||||
NotificationManager.IMPORTANCE_LOW);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
|
@ -433,7 +433,8 @@ public abstract class MediaSessionService extends Service {
|
||||
synchronized (lock) {
|
||||
if (mediaNotificationManager == null) {
|
||||
if (mediaNotificationProvider == null) {
|
||||
mediaNotificationProvider = new DefaultMediaNotificationProvider(getApplicationContext());
|
||||
mediaNotificationProvider =
|
||||
new DefaultMediaNotificationProvider.Builder(getApplicationContext()).build();
|
||||
}
|
||||
mediaNotificationManager =
|
||||
new MediaNotificationManager(
|
||||
|
@ -24,10 +24,14 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.media3.common.MediaMetadata;
|
||||
import androidx.media3.common.Player;
|
||||
import androidx.media3.common.Player.Commands;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
@ -40,6 +44,8 @@ import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mockito;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.Shadows;
|
||||
import org.robolectric.shadows.ShadowNotificationManager;
|
||||
|
||||
/** Tests for {@link DefaultMediaNotificationProvider}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@ -48,7 +54,8 @@ public class DefaultMediaNotificationProviderTest {
|
||||
@Test
|
||||
public void getMediaButtons_playWhenReadyTrueOrFalse_correctPlayPauseResources() {
|
||||
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||
new DefaultMediaNotificationProvider(ApplicationProvider.getApplicationContext());
|
||||
new DefaultMediaNotificationProvider.Builder(ApplicationProvider.getApplicationContext())
|
||||
.build();
|
||||
Commands commands = new Commands.Builder().addAllCommands().build();
|
||||
|
||||
List<CommandButton> mediaButtonsWhenPlaying =
|
||||
@ -73,7 +80,8 @@ public class DefaultMediaNotificationProviderTest {
|
||||
@Test
|
||||
public void getMediaButtons_allCommandsAvailable_createsPauseSkipNextSkipPreviousButtons() {
|
||||
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||
new DefaultMediaNotificationProvider(ApplicationProvider.getApplicationContext());
|
||||
new DefaultMediaNotificationProvider.Builder(ApplicationProvider.getApplicationContext())
|
||||
.build();
|
||||
Commands commands = new Commands.Builder().addAllCommands().build();
|
||||
SessionCommand customSessionCommand = new SessionCommand("", Bundle.EMPTY);
|
||||
CommandButton customCommandButton =
|
||||
@ -98,7 +106,8 @@ public class DefaultMediaNotificationProviderTest {
|
||||
@Test
|
||||
public void getMediaButtons_noPlayerCommandsAvailable_onlyCustomLayoutButtons() {
|
||||
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||
new DefaultMediaNotificationProvider(ApplicationProvider.getApplicationContext());
|
||||
new DefaultMediaNotificationProvider.Builder(ApplicationProvider.getApplicationContext())
|
||||
.build();
|
||||
Commands commands = new Commands.Builder().build();
|
||||
SessionCommand customSessionCommand = new SessionCommand("action1", Bundle.EMPTY);
|
||||
CommandButton customCommandButton =
|
||||
@ -118,7 +127,8 @@ public class DefaultMediaNotificationProviderTest {
|
||||
@Test
|
||||
public void addNotificationActions_customCompactViewDeclarations_correctCompactViewIndices() {
|
||||
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||
new DefaultMediaNotificationProvider(ApplicationProvider.getApplicationContext());
|
||||
new DefaultMediaNotificationProvider.Builder(ApplicationProvider.getApplicationContext())
|
||||
.build();
|
||||
NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class);
|
||||
MediaNotification.ActionFactory mockActionFactory = mock(MediaNotification.ActionFactory.class);
|
||||
MediaSession mockMediaSession = mock(MediaSession.class);
|
||||
@ -185,7 +195,8 @@ public class DefaultMediaNotificationProviderTest {
|
||||
@Test
|
||||
public void addNotificationActions_playPauseCommandNoCustomDeclaration_playPauseInCompactView() {
|
||||
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||
new DefaultMediaNotificationProvider(ApplicationProvider.getApplicationContext());
|
||||
new DefaultMediaNotificationProvider.Builder(ApplicationProvider.getApplicationContext())
|
||||
.build();
|
||||
NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class);
|
||||
MediaNotification.ActionFactory mockActionFactory = mock(MediaNotification.ActionFactory.class);
|
||||
MediaSession mockMediaSession = mock(MediaSession.class);
|
||||
@ -230,7 +241,8 @@ public class DefaultMediaNotificationProviderTest {
|
||||
public void
|
||||
addNotificationActions_noPlayPauseCommandNoCustomDeclaration_emptyCompactViewIndices() {
|
||||
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||
new DefaultMediaNotificationProvider(ApplicationProvider.getApplicationContext());
|
||||
new DefaultMediaNotificationProvider.Builder(ApplicationProvider.getApplicationContext())
|
||||
.build();
|
||||
NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class);
|
||||
MediaNotification.ActionFactory mockActionFactory = mock(MediaNotification.ActionFactory.class);
|
||||
MediaSession mockMediaSession = mock(MediaSession.class);
|
||||
@ -259,7 +271,8 @@ public class DefaultMediaNotificationProviderTest {
|
||||
@Test
|
||||
public void addNotificationActions_outOfBoundsCompactViewIndices_ignored() {
|
||||
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||
new DefaultMediaNotificationProvider(ApplicationProvider.getApplicationContext());
|
||||
new DefaultMediaNotificationProvider.Builder(ApplicationProvider.getApplicationContext())
|
||||
.build();
|
||||
NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class);
|
||||
MediaNotification.ActionFactory mockActionFactory = mock(MediaNotification.ActionFactory.class);
|
||||
MediaSession mockMediaSession = mock(MediaSession.class);
|
||||
@ -304,7 +317,8 @@ public class DefaultMediaNotificationProviderTest {
|
||||
@Test
|
||||
public void addNotificationActions_unsetLeadingArrayFields_cropped() {
|
||||
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||
new DefaultMediaNotificationProvider(ApplicationProvider.getApplicationContext());
|
||||
new DefaultMediaNotificationProvider.Builder(ApplicationProvider.getApplicationContext())
|
||||
.build();
|
||||
NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class);
|
||||
MediaNotification.ActionFactory mockActionFactory = mock(MediaNotification.ActionFactory.class);
|
||||
MediaSession mockMediaSession = mock(MediaSession.class);
|
||||
@ -337,7 +351,8 @@ public class DefaultMediaNotificationProviderTest {
|
||||
@Test
|
||||
public void addNotificationActions_correctNotificationActionAttributes() {
|
||||
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||
new DefaultMediaNotificationProvider(ApplicationProvider.getApplicationContext());
|
||||
new DefaultMediaNotificationProvider.Builder(ApplicationProvider.getApplicationContext())
|
||||
.build();
|
||||
NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class);
|
||||
DefaultActionFactory defaultActionFactory =
|
||||
new DefaultActionFactory(Robolectric.setupService(TestService.class));
|
||||
@ -374,6 +389,96 @@ public class DefaultMediaNotificationProviderTest {
|
||||
assertThat(actions.get(0).getExtras().size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void provider_withCustomIds_notificationsUseCustomIds() {
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||
new DefaultMediaNotificationProvider.Builder(context)
|
||||
.setNotificationId(/* notificationId= */ 2)
|
||||
.setChannelId(/* channelId= */ "customChannelId")
|
||||
.setChannelName(/* channelNameResourceId= */ R.string.media3_controls_play_description)
|
||||
.build();
|
||||
MediaSession mockMediaSession = createMockMediaSessionForNotification();
|
||||
DefaultActionFactory defaultActionFactory =
|
||||
new DefaultActionFactory(Robolectric.setupService(TestService.class));
|
||||
|
||||
MediaNotification notification =
|
||||
defaultMediaNotificationProvider.createNotification(
|
||||
mockMediaSession,
|
||||
ImmutableList.of(),
|
||||
defaultActionFactory,
|
||||
mock(MediaNotification.Provider.Callback.class));
|
||||
|
||||
assertThat(notification.notificationId).isEqualTo(2);
|
||||
assertThat(notification.notification.getChannelId()).isEqualTo("customChannelId");
|
||||
ShadowNotificationManager shadowNotificationManager =
|
||||
Shadows.shadowOf(
|
||||
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE));
|
||||
assertHasNotificationChannel(
|
||||
shadowNotificationManager.getNotificationChannels(),
|
||||
/* channelId= */ "customChannelId",
|
||||
/* channelName= */ context.getString(R.string.media3_controls_play_description));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setCustomSmallIcon_notificationUsesCustomSmallIcon() {
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||
new DefaultMediaNotificationProvider.Builder(context).build();
|
||||
DefaultActionFactory defaultActionFactory =
|
||||
new DefaultActionFactory(Robolectric.setupService(TestService.class));
|
||||
MediaSession mockMediaSession = createMockMediaSessionForNotification();
|
||||
|
||||
MediaNotification notification =
|
||||
defaultMediaNotificationProvider.createNotification(
|
||||
mockMediaSession,
|
||||
ImmutableList.of(),
|
||||
defaultActionFactory,
|
||||
mock(MediaNotification.Provider.Callback.class));
|
||||
// Change the small icon.
|
||||
defaultMediaNotificationProvider.setSmallIcon(R.drawable.media3_icon_circular_play);
|
||||
MediaNotification notificationWithSmallIcon =
|
||||
defaultMediaNotificationProvider.createNotification(
|
||||
mockMediaSession,
|
||||
ImmutableList.of(),
|
||||
defaultActionFactory,
|
||||
mock(MediaNotification.Provider.Callback.class));
|
||||
|
||||
assertThat(notification.notification.getSmallIcon().getResId())
|
||||
.isEqualTo(R.drawable.media3_notification_small_icon);
|
||||
assertThat(notificationWithSmallIcon.notification.getSmallIcon().getResId())
|
||||
.isEqualTo(R.drawable.media3_icon_circular_play);
|
||||
}
|
||||
|
||||
private static void assertHasNotificationChannel(
|
||||
List<Object> notificationChannels, String channelId, String channelName) {
|
||||
boolean found = false;
|
||||
for (int i = 0; i < notificationChannels.size(); i++) {
|
||||
NotificationChannel notificationChannel = (NotificationChannel) notificationChannels.get(i);
|
||||
found =
|
||||
notificationChannel.getId().equals(channelId)
|
||||
// NotificationChannel.getName() is CharSequence. Use String#contentEquals instead
|
||||
// because CharSequence.equals() has undefined behavior.
|
||||
&& channelName.contentEquals(notificationChannel.getName());
|
||||
if (found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertThat(found).isTrue();
|
||||
}
|
||||
|
||||
private static MediaSession createMockMediaSessionForNotification() {
|
||||
Player mockPlayer = mock(Player.class);
|
||||
when(mockPlayer.getAvailableCommands()).thenReturn(Commands.EMPTY);
|
||||
when(mockPlayer.getMediaMetadata()).thenReturn(MediaMetadata.EMPTY);
|
||||
MediaSession mockMediaSession = mock(MediaSession.class);
|
||||
when(mockMediaSession.getPlayer()).thenReturn(mockPlayer);
|
||||
MediaSessionImpl mockMediaSessionImpl = mock(MediaSessionImpl.class);
|
||||
when(mockMediaSession.getImpl()).thenReturn(mockMediaSessionImpl);
|
||||
when(mockMediaSessionImpl.getUri()).thenReturn(Uri.parse("http://example.com"));
|
||||
return mockMediaSession;
|
||||
}
|
||||
|
||||
/** A test service for unit tests. */
|
||||
private static final class TestService extends MediaLibraryService {
|
||||
@Nullable
|
||||
|
Loading…
x
Reference in New Issue
Block a user