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
|
* Ensure commands are always executed in the correct order even if some
|
||||||
require asynchronous resolution
|
require asynchronous resolution
|
||||||
([#85](https://github.com/androidx/media/issues/85)).
|
([#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)
|
### 1.0.0-beta02 (2022-07-15)
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ android {
|
|||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
api project(modulePrefix + 'lib-common')
|
api project(modulePrefix + 'lib-common')
|
||||||
|
compileOnly 'com.google.errorprone:error_prone_annotations:' + errorProneVersion
|
||||||
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
|
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
|
||||||
compileOnly 'org.checkerframework:checker-compat-qual:' + checkerframeworkCompatVersion
|
compileOnly 'org.checkerframework:checker-compat-qual:' + checkerframeworkCompatVersion
|
||||||
implementation 'androidx.collection:collection:' + androidxCollectionVersion
|
implementation 'androidx.collection:collection:' + androidxCollectionVersion
|
||||||
|
@ -36,7 +36,9 @@ import android.net.Uri;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import androidx.annotation.DrawableRes;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
import androidx.core.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
import androidx.core.graphics.drawable.IconCompat;
|
import androidx.core.graphics.drawable.IconCompat;
|
||||||
import androidx.media.app.NotificationCompat.MediaStyle;
|
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.FutureCallback;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
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_previous}</b> - The previous icon.
|
||||||
* <li><b>{@code media3_notification_seek_to_next}</b> - The next icon.
|
* <li><b>{@code media3_notification_seek_to_next}</b> - The next icon.
|
||||||
* <li><b>{@code media3_notification_small_icon}</b> - The {@link
|
* <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>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public class DefaultMediaNotificationProvider implements MediaNotification.Provider {
|
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
|
* An extras key that can be used to define the index of a {@link CommandButton} in {@linkplain
|
||||||
* Notification.MediaStyle#setShowActionsInCompactView(int...) compact view}.
|
* 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 =
|
public static final String COMMAND_KEY_COMPACT_VIEW_INDEX =
|
||||||
"androidx.media3.session.command.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 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 Context context;
|
||||||
|
private final int notificationId;
|
||||||
|
private final String channelId;
|
||||||
|
@StringRes private final int channelNameResourceId;
|
||||||
private final NotificationManager notificationManager;
|
private final NotificationManager notificationManager;
|
||||||
private final BitmapLoader bitmapLoader;
|
private final BitmapLoader bitmapLoader;
|
||||||
// Cache the last loaded bitmap to avoid reloading the bitmap again, particularly useful when
|
// 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 final Handler mainHandler;
|
||||||
|
|
||||||
private OnBitmapLoadedFutureCallback pendingOnBitmapLoadedFutureCallback;
|
private OnBitmapLoadedFutureCallback pendingOnBitmapLoadedFutureCallback;
|
||||||
|
@DrawableRes private int smallIconResourceId;
|
||||||
|
|
||||||
/** Creates an instance that uses a {@link SimpleBitmapLoader} for loading artwork images. */
|
private DefaultMediaNotificationProvider(Builder builder) {
|
||||||
public DefaultMediaNotificationProvider(Context context) {
|
this.context = builder.context;
|
||||||
this(context, new SimpleBitmapLoader());
|
this.notificationId = builder.notificationId;
|
||||||
}
|
this.channelId = builder.channelId;
|
||||||
|
this.channelNameResourceId = builder.channelNameResourceId;
|
||||||
/** Creates an instance that uses the {@code bitmapLoader} for loading artwork images. */
|
this.bitmapLoader = builder.bitmapLoader;
|
||||||
public DefaultMediaNotificationProvider(Context context, BitmapLoader bitmapLoader) {
|
|
||||||
this.context = context.getApplicationContext();
|
|
||||||
this.bitmapLoader = bitmapLoader;
|
|
||||||
lastLoadedBitmapInfo = new LoadedBitmapInfo();
|
|
||||||
mainHandler = new Handler(Looper.getMainLooper());
|
|
||||||
notificationManager =
|
notificationManager =
|
||||||
checkStateNotNull(
|
checkStateNotNull(
|
||||||
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE));
|
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE));
|
||||||
|
lastLoadedBitmapInfo = new LoadedBitmapInfo();
|
||||||
|
mainHandler = new Handler(Looper.getMainLooper());
|
||||||
pendingOnBitmapLoadedFutureCallback = new OnBitmapLoadedFutureCallback(bitmap -> {});
|
pendingOnBitmapLoadedFutureCallback = new OnBitmapLoadedFutureCallback(bitmap -> {});
|
||||||
|
smallIconResourceId = R.drawable.media3_notification_small_icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MediaNotification.Provider implementation
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final MediaNotification createNotification(
|
public final MediaNotification createNotification(
|
||||||
MediaSession mediaSession,
|
MediaSession mediaSession,
|
||||||
@ -143,8 +265,7 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
|
|||||||
ensureNotificationChannel();
|
ensureNotificationChannel();
|
||||||
|
|
||||||
Player player = mediaSession.getPlayer();
|
Player player = mediaSession.getPlayer();
|
||||||
NotificationCompat.Builder builder =
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId);
|
||||||
new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID);
|
|
||||||
|
|
||||||
MediaStyle mediaStyle = new MediaStyle();
|
MediaStyle mediaStyle = new MediaStyle();
|
||||||
int[] compactViewIndices =
|
int[] compactViewIndices =
|
||||||
@ -173,7 +294,7 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
|
|||||||
bitmap -> {
|
bitmap -> {
|
||||||
builder.setLargeIcon(bitmap);
|
builder.setLargeIcon(bitmap);
|
||||||
onNotificationChangedCallback.onNotificationChanged(
|
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
|
// This callback must be executed on the next looper iteration, after this method has
|
||||||
// returned a media notification.
|
// returned a media notification.
|
||||||
@ -200,12 +321,12 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
|
|||||||
.setDeleteIntent(
|
.setDeleteIntent(
|
||||||
actionFactory.createMediaActionPendingIntent(mediaSession, COMMAND_STOP))
|
actionFactory.createMediaActionPendingIntent(mediaSession, COMMAND_STOP))
|
||||||
.setOnlyAlertOnce(true)
|
.setOnlyAlertOnce(true)
|
||||||
.setSmallIcon(R.drawable.media3_notification_small_icon)
|
.setSmallIcon(smallIconResourceId)
|
||||||
.setStyle(mediaStyle)
|
.setStyle(mediaStyle)
|
||||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||||
.setOngoing(false)
|
.setOngoing(false)
|
||||||
.build();
|
.build();
|
||||||
return new MediaNotification(NOTIFICATION_ID, notification);
|
return new MediaNotification(notificationId, notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -214,6 +335,18 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
|
|||||||
return false;
|
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
|
* Returns the ordered list of {@linkplain CommandButton command buttons} to be used to build the
|
||||||
* notification.
|
* notification.
|
||||||
@ -367,13 +500,14 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void ensureNotificationChannel() {
|
private void ensureNotificationChannel() {
|
||||||
if (Util.SDK_INT < 26
|
if (Util.SDK_INT < 26 || notificationManager.getNotificationChannel(channelId) != null) {
|
||||||
|| notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID) != null) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
NotificationChannel channel =
|
NotificationChannel channel =
|
||||||
new NotificationChannel(
|
new NotificationChannel(
|
||||||
NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW);
|
channelId,
|
||||||
|
context.getString(channelNameResourceId),
|
||||||
|
NotificationManager.IMPORTANCE_LOW);
|
||||||
notificationManager.createNotificationChannel(channel);
|
notificationManager.createNotificationChannel(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,7 +433,8 @@ public abstract class MediaSessionService extends Service {
|
|||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
if (mediaNotificationManager == null) {
|
if (mediaNotificationManager == null) {
|
||||||
if (mediaNotificationProvider == null) {
|
if (mediaNotificationProvider == null) {
|
||||||
mediaNotificationProvider = new DefaultMediaNotificationProvider(getApplicationContext());
|
mediaNotificationProvider =
|
||||||
|
new DefaultMediaNotificationProvider.Builder(getApplicationContext()).build();
|
||||||
}
|
}
|
||||||
mediaNotificationManager =
|
mediaNotificationManager =
|
||||||
new MediaNotificationManager(
|
new MediaNotificationManager(
|
||||||
|
@ -24,10 +24,14 @@ import static org.mockito.Mockito.verify;
|
|||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.app.NotificationChannel;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
|
import androidx.media3.common.MediaMetadata;
|
||||||
import androidx.media3.common.Player;
|
import androidx.media3.common.Player;
|
||||||
import androidx.media3.common.Player.Commands;
|
import androidx.media3.common.Player.Commands;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
@ -40,6 +44,8 @@ import org.mockito.ArgumentCaptor;
|
|||||||
import org.mockito.InOrder;
|
import org.mockito.InOrder;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.robolectric.Robolectric;
|
import org.robolectric.Robolectric;
|
||||||
|
import org.robolectric.Shadows;
|
||||||
|
import org.robolectric.shadows.ShadowNotificationManager;
|
||||||
|
|
||||||
/** Tests for {@link DefaultMediaNotificationProvider}. */
|
/** Tests for {@link DefaultMediaNotificationProvider}. */
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@ -48,7 +54,8 @@ public class DefaultMediaNotificationProviderTest {
|
|||||||
@Test
|
@Test
|
||||||
public void getMediaButtons_playWhenReadyTrueOrFalse_correctPlayPauseResources() {
|
public void getMediaButtons_playWhenReadyTrueOrFalse_correctPlayPauseResources() {
|
||||||
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||||
new DefaultMediaNotificationProvider(ApplicationProvider.getApplicationContext());
|
new DefaultMediaNotificationProvider.Builder(ApplicationProvider.getApplicationContext())
|
||||||
|
.build();
|
||||||
Commands commands = new Commands.Builder().addAllCommands().build();
|
Commands commands = new Commands.Builder().addAllCommands().build();
|
||||||
|
|
||||||
List<CommandButton> mediaButtonsWhenPlaying =
|
List<CommandButton> mediaButtonsWhenPlaying =
|
||||||
@ -73,7 +80,8 @@ public class DefaultMediaNotificationProviderTest {
|
|||||||
@Test
|
@Test
|
||||||
public void getMediaButtons_allCommandsAvailable_createsPauseSkipNextSkipPreviousButtons() {
|
public void getMediaButtons_allCommandsAvailable_createsPauseSkipNextSkipPreviousButtons() {
|
||||||
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||||
new DefaultMediaNotificationProvider(ApplicationProvider.getApplicationContext());
|
new DefaultMediaNotificationProvider.Builder(ApplicationProvider.getApplicationContext())
|
||||||
|
.build();
|
||||||
Commands commands = new Commands.Builder().addAllCommands().build();
|
Commands commands = new Commands.Builder().addAllCommands().build();
|
||||||
SessionCommand customSessionCommand = new SessionCommand("", Bundle.EMPTY);
|
SessionCommand customSessionCommand = new SessionCommand("", Bundle.EMPTY);
|
||||||
CommandButton customCommandButton =
|
CommandButton customCommandButton =
|
||||||
@ -98,7 +106,8 @@ public class DefaultMediaNotificationProviderTest {
|
|||||||
@Test
|
@Test
|
||||||
public void getMediaButtons_noPlayerCommandsAvailable_onlyCustomLayoutButtons() {
|
public void getMediaButtons_noPlayerCommandsAvailable_onlyCustomLayoutButtons() {
|
||||||
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||||
new DefaultMediaNotificationProvider(ApplicationProvider.getApplicationContext());
|
new DefaultMediaNotificationProvider.Builder(ApplicationProvider.getApplicationContext())
|
||||||
|
.build();
|
||||||
Commands commands = new Commands.Builder().build();
|
Commands commands = new Commands.Builder().build();
|
||||||
SessionCommand customSessionCommand = new SessionCommand("action1", Bundle.EMPTY);
|
SessionCommand customSessionCommand = new SessionCommand("action1", Bundle.EMPTY);
|
||||||
CommandButton customCommandButton =
|
CommandButton customCommandButton =
|
||||||
@ -118,7 +127,8 @@ public class DefaultMediaNotificationProviderTest {
|
|||||||
@Test
|
@Test
|
||||||
public void addNotificationActions_customCompactViewDeclarations_correctCompactViewIndices() {
|
public void addNotificationActions_customCompactViewDeclarations_correctCompactViewIndices() {
|
||||||
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||||
new DefaultMediaNotificationProvider(ApplicationProvider.getApplicationContext());
|
new DefaultMediaNotificationProvider.Builder(ApplicationProvider.getApplicationContext())
|
||||||
|
.build();
|
||||||
NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class);
|
NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class);
|
||||||
MediaNotification.ActionFactory mockActionFactory = mock(MediaNotification.ActionFactory.class);
|
MediaNotification.ActionFactory mockActionFactory = mock(MediaNotification.ActionFactory.class);
|
||||||
MediaSession mockMediaSession = mock(MediaSession.class);
|
MediaSession mockMediaSession = mock(MediaSession.class);
|
||||||
@ -185,7 +195,8 @@ public class DefaultMediaNotificationProviderTest {
|
|||||||
@Test
|
@Test
|
||||||
public void addNotificationActions_playPauseCommandNoCustomDeclaration_playPauseInCompactView() {
|
public void addNotificationActions_playPauseCommandNoCustomDeclaration_playPauseInCompactView() {
|
||||||
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||||
new DefaultMediaNotificationProvider(ApplicationProvider.getApplicationContext());
|
new DefaultMediaNotificationProvider.Builder(ApplicationProvider.getApplicationContext())
|
||||||
|
.build();
|
||||||
NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class);
|
NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class);
|
||||||
MediaNotification.ActionFactory mockActionFactory = mock(MediaNotification.ActionFactory.class);
|
MediaNotification.ActionFactory mockActionFactory = mock(MediaNotification.ActionFactory.class);
|
||||||
MediaSession mockMediaSession = mock(MediaSession.class);
|
MediaSession mockMediaSession = mock(MediaSession.class);
|
||||||
@ -230,7 +241,8 @@ public class DefaultMediaNotificationProviderTest {
|
|||||||
public void
|
public void
|
||||||
addNotificationActions_noPlayPauseCommandNoCustomDeclaration_emptyCompactViewIndices() {
|
addNotificationActions_noPlayPauseCommandNoCustomDeclaration_emptyCompactViewIndices() {
|
||||||
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||||
new DefaultMediaNotificationProvider(ApplicationProvider.getApplicationContext());
|
new DefaultMediaNotificationProvider.Builder(ApplicationProvider.getApplicationContext())
|
||||||
|
.build();
|
||||||
NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class);
|
NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class);
|
||||||
MediaNotification.ActionFactory mockActionFactory = mock(MediaNotification.ActionFactory.class);
|
MediaNotification.ActionFactory mockActionFactory = mock(MediaNotification.ActionFactory.class);
|
||||||
MediaSession mockMediaSession = mock(MediaSession.class);
|
MediaSession mockMediaSession = mock(MediaSession.class);
|
||||||
@ -259,7 +271,8 @@ public class DefaultMediaNotificationProviderTest {
|
|||||||
@Test
|
@Test
|
||||||
public void addNotificationActions_outOfBoundsCompactViewIndices_ignored() {
|
public void addNotificationActions_outOfBoundsCompactViewIndices_ignored() {
|
||||||
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||||
new DefaultMediaNotificationProvider(ApplicationProvider.getApplicationContext());
|
new DefaultMediaNotificationProvider.Builder(ApplicationProvider.getApplicationContext())
|
||||||
|
.build();
|
||||||
NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class);
|
NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class);
|
||||||
MediaNotification.ActionFactory mockActionFactory = mock(MediaNotification.ActionFactory.class);
|
MediaNotification.ActionFactory mockActionFactory = mock(MediaNotification.ActionFactory.class);
|
||||||
MediaSession mockMediaSession = mock(MediaSession.class);
|
MediaSession mockMediaSession = mock(MediaSession.class);
|
||||||
@ -304,7 +317,8 @@ public class DefaultMediaNotificationProviderTest {
|
|||||||
@Test
|
@Test
|
||||||
public void addNotificationActions_unsetLeadingArrayFields_cropped() {
|
public void addNotificationActions_unsetLeadingArrayFields_cropped() {
|
||||||
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||||
new DefaultMediaNotificationProvider(ApplicationProvider.getApplicationContext());
|
new DefaultMediaNotificationProvider.Builder(ApplicationProvider.getApplicationContext())
|
||||||
|
.build();
|
||||||
NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class);
|
NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class);
|
||||||
MediaNotification.ActionFactory mockActionFactory = mock(MediaNotification.ActionFactory.class);
|
MediaNotification.ActionFactory mockActionFactory = mock(MediaNotification.ActionFactory.class);
|
||||||
MediaSession mockMediaSession = mock(MediaSession.class);
|
MediaSession mockMediaSession = mock(MediaSession.class);
|
||||||
@ -337,7 +351,8 @@ public class DefaultMediaNotificationProviderTest {
|
|||||||
@Test
|
@Test
|
||||||
public void addNotificationActions_correctNotificationActionAttributes() {
|
public void addNotificationActions_correctNotificationActionAttributes() {
|
||||||
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||||
new DefaultMediaNotificationProvider(ApplicationProvider.getApplicationContext());
|
new DefaultMediaNotificationProvider.Builder(ApplicationProvider.getApplicationContext())
|
||||||
|
.build();
|
||||||
NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class);
|
NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class);
|
||||||
DefaultActionFactory defaultActionFactory =
|
DefaultActionFactory defaultActionFactory =
|
||||||
new DefaultActionFactory(Robolectric.setupService(TestService.class));
|
new DefaultActionFactory(Robolectric.setupService(TestService.class));
|
||||||
@ -374,6 +389,96 @@ public class DefaultMediaNotificationProviderTest {
|
|||||||
assertThat(actions.get(0).getExtras().size()).isEqualTo(0);
|
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. */
|
/** A test service for unit tests. */
|
||||||
private static final class TestService extends MediaLibraryService {
|
private static final class TestService extends MediaLibraryService {
|
||||||
@Nullable
|
@Nullable
|
||||||
|
Loading…
x
Reference in New Issue
Block a user