Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
SDK
/
exoplayer
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
b440b0fc
authored
Dec 24, 2021
by
jaewan
Committed by
tonihei
Jan 05, 2022
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Take MediaSession for building notification
PiperOrigin-RevId: 418154077
parent
6ee7cdf9
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
158 additions
and
228 deletions
libraries/session/src/main/java/androidx/media3/session/DefaultMediaDescriptionAdapter.java
libraries/session/src/main/java/androidx/media3/session/PlayerNotificationManager.java
libraries/session/src/main/java/androidx/media3/session/DefaultMediaDescriptionAdapter.java
View file @
b440b0fc
...
@@ -15,7 +15,6 @@
...
@@ -15,7 +15,6 @@
*/
*/
package
androidx
.
media3
.
session
;
package
androidx
.
media3
.
session
;
import
android.app.PendingIntent
;
import
android.graphics.Bitmap
;
import
android.graphics.Bitmap
;
import
android.graphics.BitmapFactory
;
import
android.graphics.BitmapFactory
;
import
android.text.TextUtils
;
import
android.text.TextUtils
;
...
@@ -34,20 +33,12 @@ import androidx.media3.session.PlayerNotificationManager.MediaDescriptionAdapter
...
@@ -34,20 +33,12 @@ import androidx.media3.session.PlayerNotificationManager.MediaDescriptionAdapter
@UnstableApi
@UnstableApi
public
final
class
DefaultMediaDescriptionAdapter
implements
MediaDescriptionAdapter
{
public
final
class
DefaultMediaDescriptionAdapter
implements
MediaDescriptionAdapter
{
@Nullable
private
final
PendingIntent
pendingIntent
;
/** Creates a default {@link MediaDescriptionAdapter}. */
public
DefaultMediaDescriptionAdapter
()
{}
/**
* Creates a default {@link MediaDescriptionAdapter}.
*
* @param pendingIntent The {@link PendingIntent} to be returned from {@link
* #createCurrentContentIntent(Player)}, or null if no intent should be fired.
*/
public
DefaultMediaDescriptionAdapter
(
@Nullable
PendingIntent
pendingIntent
)
{
this
.
pendingIntent
=
pendingIntent
;
}
@Override
@Override
public
CharSequence
getCurrentContentTitle
(
Player
player
)
{
public
CharSequence
getCurrentContentTitle
(
MediaSession
session
)
{
Player
player
=
session
.
getPlayer
();
@Nullable
CharSequence
displayTitle
=
player
.
getMediaMetadata
().
displayTitle
;
@Nullable
CharSequence
displayTitle
=
player
.
getMediaMetadata
().
displayTitle
;
if
(!
TextUtils
.
isEmpty
(
displayTitle
))
{
if
(!
TextUtils
.
isEmpty
(
displayTitle
))
{
return
displayTitle
;
return
displayTitle
;
...
@@ -59,13 +50,8 @@ public final class DefaultMediaDescriptionAdapter implements MediaDescriptionAda
...
@@ -59,13 +50,8 @@ public final class DefaultMediaDescriptionAdapter implements MediaDescriptionAda
@Nullable
@Nullable
@Override
@Override
public
PendingIntent
createCurrentContentIntent
(
Player
player
)
{
public
CharSequence
getCurrentContentText
(
MediaSession
session
)
{
return
pendingIntent
;
Player
player
=
session
.
getPlayer
();
}
@Nullable
@Override
public
CharSequence
getCurrentContentText
(
Player
player
)
{
@Nullable
CharSequence
artist
=
player
.
getMediaMetadata
().
artist
;
@Nullable
CharSequence
artist
=
player
.
getMediaMetadata
().
artist
;
if
(!
TextUtils
.
isEmpty
(
artist
))
{
if
(!
TextUtils
.
isEmpty
(
artist
))
{
return
artist
;
return
artist
;
...
@@ -76,7 +62,8 @@ public final class DefaultMediaDescriptionAdapter implements MediaDescriptionAda
...
@@ -76,7 +62,8 @@ public final class DefaultMediaDescriptionAdapter implements MediaDescriptionAda
@Nullable
@Nullable
@Override
@Override
public
Bitmap
getCurrentLargeIcon
(
Player
player
,
BitmapCallback
callback
)
{
public
Bitmap
getCurrentLargeIcon
(
MediaSession
session
,
BitmapCallback
callback
)
{
Player
player
=
session
.
getPlayer
();
@Nullable
byte
[]
data
=
player
.
getMediaMetadata
().
artworkData
;
@Nullable
byte
[]
data
=
player
.
getMediaMetadata
().
artworkData
;
if
(
data
==
null
)
{
if
(
data
==
null
)
{
return
null
;
return
null
;
...
...
libraries/session/src/main/java/androidx/media3/session/PlayerNotificationManager.java
View file @
b440b0fc
...
@@ -31,7 +31,7 @@ import static androidx.media3.common.Player.EVENT_REPEAT_MODE_CHANGED;
...
@@ -31,7 +31,7 @@ import static androidx.media3.common.Player.EVENT_REPEAT_MODE_CHANGED;
import
static
androidx
.
media3
.
common
.
Player
.
EVENT_SHUFFLE_MODE_ENABLED_CHANGED
;
import
static
androidx
.
media3
.
common
.
Player
.
EVENT_SHUFFLE_MODE_ENABLED_CHANGED
;
import
static
androidx
.
media3
.
common
.
Player
.
EVENT_TIMELINE_CHANGED
;
import
static
androidx
.
media3
.
common
.
Player
.
EVENT_TIMELINE_CHANGED
;
import
static
androidx
.
media3
.
common
.
util
.
Assertions
.
checkArgument
;
import
static
androidx
.
media3
.
common
.
util
.
Assertions
.
checkArgument
;
import
static
androidx
.
media3
.
common
.
util
.
Assertions
.
checkState
;
import
static
androidx
.
media3
.
common
.
util
.
Assertions
.
checkState
NotNull
;
import
android.app.Notification
;
import
android.app.Notification
;
import
android.app.NotificationChannel
;
import
android.app.NotificationChannel
;
...
@@ -43,42 +43,43 @@ import android.content.IntentFilter;
...
@@ -43,42 +43,43 @@ import android.content.IntentFilter;
import
android.graphics.Bitmap
;
import
android.graphics.Bitmap
;
import
android.graphics.Color
;
import
android.graphics.Color
;
import
android.net.Uri
;
import
android.net.Uri
;
import
android.os.Bundle
;
import
android.os.Handler
;
import
android.os.Handler
;
import
android.os.Looper
;
import
android.os.Looper
;
import
android.os.Message
;
import
android.os.Message
;
import
android.support.v4.media.session.MediaSessionCompat
;
import
androidx.annotation.DrawableRes
;
import
androidx.annotation.DrawableRes
;
import
androidx.annotation.IntDef
;
import
androidx.annotation.IntDef
;
import
androidx.annotation.IntRange
;
import
androidx.annotation.IntRange
;
import
androidx.annotation.Nullable
;
import
androidx.annotation.Nullable
;
import
androidx.core.app.NotificationCompat
;
import
androidx.core.app.NotificationCompat
;
import
androidx.core.app.NotificationManagerCompat
;
import
androidx.core.app.NotificationManagerCompat
;
import
androidx.
media.app.NotificationCompat.MediaStyle
;
import
androidx.
core.content.ContextCompat
;
import
androidx.media3.common.C
;
import
androidx.media3.common.C
;
import
androidx.media3.common.Player
;
import
androidx.media3.common.Player
;
import
androidx.media3.common.util.Assertions
;
import
androidx.media3.common.util.BundleableUtil
;
import
androidx.media3.common.util.BundleableUtil
;
import
androidx.media3.common.util.Log
;
import
androidx.media3.common.util.Log
;
import
androidx.media3.common.util.NotificationUtil
;
import
androidx.media3.common.util.NotificationUtil
;
import
androidx.media3.common.util.UnstableApi
;
import
androidx.media3.common.util.UnstableApi
;
import
androidx.media3.common.util.Util
;
import
androidx.media3.common.util.Util
;
import
androidx.media3.session.MediaStyleNotificationHelper.MediaStyle
;
import
com.google.common.util.concurrent.ListenableFuture
;
import
java.lang.annotation.Documented
;
import
java.lang.annotation.Documented
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.RetentionPolicy
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.concurrent.ExecutionException
;
import
java.util.concurrent.TimeUnit
;
import
java.util.concurrent.TimeoutException
;
import
org.checkerframework.checker.initialization.qual.Initialized
;
/**
/**
* Starts, updates and cancels a media style notification
reflecting the player state
. The actions
* Starts, updates and cancels a media style notification
for a {@link MediaSession}
. The actions
* included in the notification can be customized along with their drawables, as described below.
* included in the notification can be customized along with their drawables, as described below.
*
*
* <p>The notification is cancelled when {@code null} is passed to {@link #setPlayer(Player)} or
* <p>When notification is no longer required, call {@link #release()} to release resources.
* when the notification is dismissed by the user.
*
* <p>If the player is released it must be removed from the manager by calling {@code
* setPlayer(null)}.
*
*
* <h2>Overriding drawables</h2>
* <h2>Overriding drawables</h2>
*
*
...
@@ -102,7 +103,7 @@ import java.util.Map;
...
@@ -102,7 +103,7 @@ import java.util.Map;
* <p>Unlike the drawables above, the large icon (i.e. the icon passed to {@link
* <p>Unlike the drawables above, the large icon (i.e. the icon passed to {@link
* NotificationCompat.Builder#setLargeIcon(Bitmap)} cannot be overridden in this way. Instead, the
* NotificationCompat.Builder#setLargeIcon(Bitmap)} cannot be overridden in this way. Instead, the
* large icon is obtained from the {@link MediaDescriptionAdapter} passed to {@link
* large icon is obtained from the {@link MediaDescriptionAdapter} passed to {@link
* Builder#
Builder(Context, int, String,
MediaDescriptionAdapter)}.
* Builder#
setMediaDescriptionAdapter(
MediaDescriptionAdapter)}.
*/
*/
@UnstableApi
@UnstableApi
public
class
PlayerNotificationManager
{
public
class
PlayerNotificationManager
{
...
@@ -115,45 +116,34 @@ public class PlayerNotificationManager {
...
@@ -115,45 +116,34 @@ public class PlayerNotificationManager {
*
*
* <p>See {@link NotificationCompat.Builder#setContentTitle(CharSequence)}.
* <p>See {@link NotificationCompat.Builder#setContentTitle(CharSequence)}.
*
*
* @param
player The {@link Player
} for which a notification is being built.
* @param
session The {@link MediaSession
} for which a notification is being built.
* @return The content title for the current media item.
* @return The content title for the current media item.
*/
*/
CharSequence
getCurrentContentTitle
(
Player
player
);
CharSequence
getCurrentContentTitle
(
MediaSession
session
);
/**
* Creates a content intent for the current media item.
*
* <p>See {@link NotificationCompat.Builder#setContentIntent(PendingIntent)}.
*
* @param player The {@link Player} for which a notification is being built.
* @return The content intent for the current media item, or null if no intent should be fired.
*/
@Nullable
PendingIntent
createCurrentContentIntent
(
Player
player
);
/**
/**
* Gets the content text for the current media item.
* Gets the content text for the current media item.
*
*
* <p>See {@link NotificationCompat.Builder#setContentText(CharSequence)}.
* <p>See {@link NotificationCompat.Builder#setContentText(CharSequence)}.
*
*
* @param
player The {@link Player
} for which a notification is being built.
* @param
session The {@link MediaSession
} for which a notification is being built.
* @return The content text for the current media item, or null if no context text should be
* @return The content text for the current media item, or null if no context text should be
* displayed.
* displayed.
*/
*/
@Nullable
@Nullable
CharSequence
getCurrentContentText
(
Player
player
);
CharSequence
getCurrentContentText
(
MediaSession
session
);
/**
/**
* Gets the content sub text for the current media item.
* Gets the content sub text for the current media item.
*
*
* <p>See {@link NotificationCompat.Builder#setSubText(CharSequence)}.
* <p>See {@link NotificationCompat.Builder#setSubText(CharSequence)}.
*
*
* @param
player The {@link Player
} for which a notification is being built.
* @param
session The {@link MediaSession
} for which a notification is being built.
* @return The content subtext for the current media item, or null if no subtext should be
* @return The content subtext for the current media item, or null if no subtext should be
* displayed.
* displayed.
*/
*/
@Nullable
@Nullable
default
CharSequence
getCurrentSubText
(
Player
player
)
{
default
CharSequence
getCurrentSubText
(
MediaSession
session
)
{
return
null
;
return
null
;
}
}
...
@@ -167,52 +157,13 @@ public class PlayerNotificationManager {
...
@@ -167,52 +157,13 @@ public class PlayerNotificationManager {
*
*
* <p>See {@link NotificationCompat.Builder#setLargeIcon(Bitmap)}.
* <p>See {@link NotificationCompat.Builder#setLargeIcon(Bitmap)}.
*
*
* @param
player The {@link Player
} for which a notification is being built.
* @param
session The {@link MediaSession
} for which a notification is being built.
* @param callback A {@link BitmapCallback} to provide a {@link Bitmap} asynchronously.
* @param callback A {@link BitmapCallback} to provide a {@link Bitmap} asynchronously.
* @return The large icon for the current media item, or null if the icon will be returned
* @return The large icon for the current media item, or null if the icon will be returned
* through the {@link BitmapCallback} or if no icon should be displayed.
* through the {@link BitmapCallback} or if no icon should be displayed.
*/
*/
@Nullable
@Nullable
Bitmap
getCurrentLargeIcon
(
Player
player
,
BitmapCallback
callback
);
Bitmap
getCurrentLargeIcon
(
MediaSession
session
,
BitmapCallback
callback
);
}
/** Defines and handles custom actions. */
public
interface
CustomActionReceiver
{
/**
* Gets the actions handled by this receiver.
*
* <p>If multiple {@link PlayerNotificationManager} instances are in use at the same time, the
* {@code instanceId} must be set as an intent extra with key {@link
* PlayerNotificationManager#INTENT_EXTRA_INSTANCE_ID} to avoid sending the action to every
* custom action receiver. It's also necessary to ensure something is different about the
* actions. This may be any of the {@link Intent} attributes considered by {@link
* Intent#filterEquals}, or different request code integers when creating the {@link
* PendingIntent}s with {@link PendingIntent#getBroadcast}. The easiest approach is to use the
* {@code instanceId} as the request code.
*
* @param context The {@link Context}.
* @param instanceId The instance id of the {@link PlayerNotificationManager}.
* @return A map of custom actions.
*/
Map
<
String
,
NotificationCompat
.
Action
>
createCustomActions
(
Context
context
,
int
instanceId
);
/**
* Gets the actions to be included in the notification given the current player state.
*
* @param player The {@link Player} for which a notification is being built.
* @return The actions to be included in the notification.
*/
List
<
String
>
getCustomActions
(
Player
player
);
/**
* Called when a custom action has been received.
*
* @param player The player.
* @param action The action from {@link Intent#getAction()}.
* @param intent The received {@link Intent}.
*/
void
onCustomAction
(
Player
player
,
String
action
,
Intent
intent
);
}
}
/** A listener for changes to the notification. */
/** A listener for changes to the notification. */
...
@@ -248,8 +199,9 @@ public class PlayerNotificationManager {
...
@@ -248,8 +199,9 @@ public class PlayerNotificationManager {
protected
final
int
notificationId
;
protected
final
int
notificationId
;
protected
final
String
channelId
;
protected
final
String
channelId
;
protected
MediaSession
session
;
protected
Bundle
connectionHints
;
@Nullable
protected
NotificationListener
notificationListener
;
@Nullable
protected
NotificationListener
notificationListener
;
@Nullable
protected
CustomActionReceiver
customActionReceiver
;
protected
MediaDescriptionAdapter
mediaDescriptionAdapter
;
protected
MediaDescriptionAdapter
mediaDescriptionAdapter
;
protected
int
channelNameResourceId
;
protected
int
channelNameResourceId
;
protected
int
channelDescriptionResourceId
;
protected
int
channelDescriptionResourceId
;
...
@@ -258,37 +210,41 @@ public class PlayerNotificationManager {
...
@@ -258,37 +210,41 @@ public class PlayerNotificationManager {
@Nullable
protected
String
groupKey
;
@Nullable
protected
String
groupKey
;
/**
/**
* @deprecated Use {@link #Builder(Context, int, String)} instead, then call {@link
* #setMediaDescriptionAdapter(MediaDescriptionAdapter)}.
*/
@Deprecated
public
Builder
(
Context
context
,
int
notificationId
,
String
channelId
,
MediaDescriptionAdapter
mediaDescriptionAdapter
)
{
this
(
context
,
notificationId
,
channelId
);
this
.
mediaDescriptionAdapter
=
mediaDescriptionAdapter
;
}
/**
* Creates an instance.
* Creates an instance.
*
*
* @param context The {@link Context}.
* @param context The {@link Context}.
* @param notificationId The id of the notification to be posted. Must be greater than 0.
* @param notificationId The id of the notification to be posted. Must be greater than 0.
* @param session The session to build notification with.
* @param channelId The id of the notification channel.
* @param channelId The id of the notification channel.
*/
*/
public
Builder
(
Context
context
,
@IntRange
(
from
=
1
)
int
notificationId
,
String
channelId
)
{
public
Builder
(
Context
context
,
MediaSession
session
,
@IntRange
(
from
=
1
)
int
notificationId
,
String
channelId
)
{
checkArgument
(
notificationId
>
0
);
checkArgument
(
notificationId
>
0
);
this
.
context
=
context
;
this
.
context
=
context
;
this
.
session
=
session
;
this
.
notificationId
=
notificationId
;
this
.
notificationId
=
notificationId
;
this
.
channelId
=
channelId
;
this
.
channelId
=
channelId
;
connectionHints
=
Bundle
.
EMPTY
;
channelImportance
=
NotificationUtil
.
IMPORTANCE_LOW
;
channelImportance
=
NotificationUtil
.
IMPORTANCE_LOW
;
mediaDescriptionAdapter
=
new
DefaultMediaDescriptionAdapter
(
/* pendingIntent= */
null
);
mediaDescriptionAdapter
=
new
DefaultMediaDescriptionAdapter
();
smallIconResourceId
=
R
.
drawable
.
media3_notification_small_icon
;
smallIconResourceId
=
R
.
drawable
.
media3_notification_small_icon
;
}
}
/**
/**
* The connection hints for identify {@link MediaController} to deliver commands from the
* notification.
*
* @return This builder
*/
public
Builder
setConnectionHints
(
Bundle
connectionHints
)
{
this
.
connectionHints
=
Assertions
.
checkNotNull
(
connectionHints
);
return
this
;
}
/**
* The name of the channel. If set to a value other than {@code 0}, the channel is automatically
* The name of the channel. If set to a value other than {@code 0}, the channel is automatically
* created when {@link #build()} is called. If the application has already created the
* created when {@link #build()} is called. If the application has already created the
* notification channel, then this method should not be called.
* notification channel, then this method should not be called.
...
@@ -343,18 +299,6 @@ public class PlayerNotificationManager {
...
@@ -343,18 +299,6 @@ public class PlayerNotificationManager {
}
}
/**
/**
* The {@link CustomActionReceiver} to be used.
*
* <p>The default is {@code null}.
*
* @return This builder.
*/
public
Builder
setCustomActionReceiver
(
CustomActionReceiver
customActionReceiver
)
{
this
.
customActionReceiver
=
customActionReceiver
;
return
this
;
}
/**
* The resource id of the small icon of the notification shown in the status bar. See {@link
* The resource id of the small icon of the notification shown in the status bar. See {@link
* NotificationCompat.Builder#setSmallIcon(int)}.
* NotificationCompat.Builder#setSmallIcon(int)}.
*
*
...
@@ -404,11 +348,12 @@ public class PlayerNotificationManager {
...
@@ -404,11 +348,12 @@ public class PlayerNotificationManager {
return
new
PlayerNotificationManager
(
return
new
PlayerNotificationManager
(
context
,
context
,
session
,
connectionHints
,
channelId
,
channelId
,
notificationId
,
notificationId
,
mediaDescriptionAdapter
,
mediaDescriptionAdapter
,
notificationListener
,
notificationListener
,
customActionReceiver
,
smallIconResourceId
,
smallIconResourceId
,
groupKey
);
groupKey
);
}
}
...
@@ -498,17 +443,16 @@ public class PlayerNotificationManager {
...
@@ -498,17 +443,16 @@ public class PlayerNotificationManager {
private
static
int
instanceIdCounter
;
private
static
int
instanceIdCounter
;
private
final
Context
context
;
private
final
Context
context
;
private
final
MediaSession
session
;
private
final
ListenableFuture
<
MediaController
>
controllerFuture
;
private
final
String
channelId
;
private
final
String
channelId
;
private
final
int
notificationId
;
private
final
int
notificationId
;
private
final
MediaDescriptionAdapter
mediaDescriptionAdapter
;
private
final
MediaDescriptionAdapter
mediaDescriptionAdapter
;
@Nullable
private
final
NotificationListener
notificationListener
;
@Nullable
private
final
NotificationListener
notificationListener
;
@Nullable
private
final
CustomActionReceiver
customActionReceiver
;
private
final
Handler
mainHandler
;
private
final
Handler
mainHandler
;
private
final
NotificationManagerCompat
notificationManager
;
private
final
NotificationManagerCompat
notificationManager
;
private
final
IntentFilter
intentFilter
;
private
final
IntentFilter
intentFilter
;
private
final
Player
.
Listener
playerListener
;
private
final
NotificationBroadcastReceiver
notificationBroadcastReceiver
;
private
final
NotificationBroadcastReceiver
notificationBroadcastReceiver
;
private
final
Map
<
String
,
NotificationCompat
.
Action
>
customActions
;
private
final
PendingIntent
dismissPendingIntent
;
private
final
PendingIntent
dismissPendingIntent
;
private
final
int
instanceId
;
private
final
int
instanceId
;
private
final
CommandButton
playButton
;
private
final
CommandButton
playButton
;
...
@@ -519,10 +463,8 @@ public class PlayerNotificationManager {
...
@@ -519,10 +463,8 @@ public class PlayerNotificationManager {
private
final
CommandButton
seekForwardButton
;
private
final
CommandButton
seekForwardButton
;
@Nullable
private
NotificationCompat
.
Builder
builder
;
@Nullable
private
NotificationCompat
.
Builder
builder
;
@Nullable
private
Player
player
;
private
boolean
isNotificationStarted
;
private
boolean
isNotificationStarted
;
private
int
currentNotificationTag
;
private
int
currentNotificationTag
;
@Nullable
private
MediaSessionCompat
.
Token
mediaSessionToken
;
private
int
badgeIconType
;
private
int
badgeIconType
;
private
boolean
colorized
;
private
boolean
colorized
;
private
int
defaults
;
private
int
defaults
;
...
@@ -535,20 +477,21 @@ public class PlayerNotificationManager {
...
@@ -535,20 +477,21 @@ public class PlayerNotificationManager {
protected
PlayerNotificationManager
(
protected
PlayerNotificationManager
(
Context
context
,
Context
context
,
MediaSession
session
,
Bundle
connectionHints
,
String
channelId
,
String
channelId
,
int
notificationId
,
int
notificationId
,
MediaDescriptionAdapter
mediaDescriptionAdapter
,
MediaDescriptionAdapter
mediaDescriptionAdapter
,
@Nullable
NotificationListener
notificationListener
,
@Nullable
NotificationListener
notificationListener
,
@Nullable
CustomActionReceiver
customActionReceiver
,
int
smallIconResourceId
,
int
smallIconResourceId
,
@Nullable
String
groupKey
)
{
@Nullable
String
groupKey
)
{
context
=
context
.
getApplicationContext
();
context
=
context
.
getApplicationContext
();
this
.
context
=
context
;
this
.
context
=
context
;
this
.
session
=
session
;
this
.
channelId
=
channelId
;
this
.
channelId
=
channelId
;
this
.
notificationId
=
notificationId
;
this
.
notificationId
=
notificationId
;
this
.
mediaDescriptionAdapter
=
mediaDescriptionAdapter
;
this
.
mediaDescriptionAdapter
=
mediaDescriptionAdapter
;
this
.
notificationListener
=
notificationListener
;
this
.
notificationListener
=
notificationListener
;
this
.
customActionReceiver
=
customActionReceiver
;
this
.
smallIconResourceId
=
smallIconResourceId
;
this
.
smallIconResourceId
=
smallIconResourceId
;
this
.
groupKey
=
groupKey
;
this
.
groupKey
=
groupKey
;
instanceId
=
instanceIdCounter
++;
instanceId
=
instanceIdCounter
++;
...
@@ -558,8 +501,24 @@ public class PlayerNotificationManager {
...
@@ -558,8 +501,24 @@ public class PlayerNotificationManager {
@SuppressWarnings
(
"nullness:methodref.receiver.bound"
)
@SuppressWarnings
(
"nullness:methodref.receiver.bound"
)
Handler
mainHandler
=
Util
.
createHandler
(
Looper
.
getMainLooper
(),
this
::
handleMessage
);
Handler
mainHandler
=
Util
.
createHandler
(
Looper
.
getMainLooper
(),
this
::
handleMessage
);
this
.
mainHandler
=
mainHandler
;
this
.
mainHandler
=
mainHandler
;
controllerFuture
=
new
MediaController
.
Builder
(
context
,
session
.
getToken
())
.
setApplicationLooper
(
Looper
.
getMainLooper
())
.
setConnectionHints
(
connectionHints
)
.
setListener
(
new
MediaControllerListener
())
.
buildAsync
();
controllerFuture
.
addListener
(
()
->
{
@SuppressWarnings
(
"nullness:assignment"
)
@Initialized
PlayerNotificationManager
thisRef
=
this
;
MediaController
controller
=
thisRef
.
getMediaControllerOrNull
();
if
(
controller
!=
null
)
{
controller
.
addListener
(
new
PlayerListener
());
}
},
ContextCompat
.
getMainExecutor
(
context
));
notificationManager
=
NotificationManagerCompat
.
from
(
context
);
notificationManager
=
NotificationManagerCompat
.
from
(
context
);
playerListener
=
new
PlayerListener
();
notificationBroadcastReceiver
=
new
NotificationBroadcastReceiver
();
notificationBroadcastReceiver
=
new
NotificationBroadcastReceiver
();
intentFilter
=
new
IntentFilter
();
intentFilter
=
new
IntentFilter
();
colorized
=
true
;
colorized
=
true
;
...
@@ -610,56 +569,13 @@ public class PlayerNotificationManager {
...
@@ -610,56 +569,13 @@ public class PlayerNotificationManager {
intentFilter
.
addAction
(
INTENT_ACTION_COMMAND
);
intentFilter
.
addAction
(
INTENT_ACTION_COMMAND
);
intentFilter
.
addAction
(
INTENT_ACTION_DISMISS
);
intentFilter
.
addAction
(
INTENT_ACTION_DISMISS
);
intentFilter
.
addDataScheme
(
INTENT_SCHEME
);
intentFilter
.
addDataScheme
(
INTENT_SCHEME
);
customActions
=
customActionReceiver
!=
null
?
customActionReceiver
.
createCustomActions
(
context
,
instanceId
)
:
Collections
.
emptyMap
();
dismissPendingIntent
=
createBroadcastIntent
(
context
,
INTENT_ACTION_DISMISS
,
instanceId
);
dismissPendingIntent
=
createBroadcastIntent
(
context
,
INTENT_ACTION_DISMISS
,
instanceId
);
}
}
/**
/* Releases all resources, such as internal {@link MediaController}. */
* Sets the {@link Player}.
public
void
release
()
{
*
// This will indirectly call stopNotification(/* dismissedByUser= */ false).
* <p>Setting the player starts a notification immediately unless the player is in {@link
MediaController
.
releaseFuture
(
controllerFuture
);
* Player#STATE_IDLE}, in which case the notification is started as soon as the player transitions
* away from being idle.
*
* <p>If the player is released it must be removed from the manager by calling {@code
* setPlayer(null)}. This will cancel the notification.
*
* @param player The {@link Player} to use, or {@code null} to remove the current player. Only
* players which are accessed on the main thread are supported ({@code
* player.getApplicationLooper() == Looper.getMainLooper()}).
*/
public
final
void
setPlayer
(
@Nullable
Player
player
)
{
checkState
(
Looper
.
myLooper
()
==
Looper
.
getMainLooper
());
checkArgument
(
player
==
null
||
player
.
getApplicationLooper
()
==
Looper
.
getMainLooper
());
if
(
this
.
player
==
player
)
{
return
;
}
if
(
this
.
player
!=
null
)
{
this
.
player
.
removeListener
(
playerListener
);
if
(
player
==
null
)
{
stopNotification
(
/* dismissedByUser= */
false
);
}
}
this
.
player
=
player
;
if
(
player
!=
null
)
{
player
.
addListener
(
playerListener
);
postStartOrUpdateNotification
();
}
}
/**
* Sets the {@link MediaSessionCompat.Token}.
*
* @param token The {@link MediaSessionCompat.Token}.
*/
public
final
void
setMediaSessionToken
(
MediaSessionCompat
.
Token
token
)
{
if
(!
Util
.
areEqual
(
this
.
mediaSessionToken
,
token
))
{
mediaSessionToken
=
token
;
invalidate
();
}
}
}
/**
/**
...
@@ -735,7 +651,7 @@ public class PlayerNotificationManager {
...
@@ -735,7 +651,7 @@ public class PlayerNotificationManager {
*
*
* <p>To set the priority for API levels above 25, you can create your own {@link
* <p>To set the priority for API levels above 25, you can create your own {@link
* NotificationChannel} with a given importance level and pass the id of the channel to {@link
* NotificationChannel} with a given importance level and pass the id of the channel to {@link
* Builder#Builder(Context,
int, String, MediaDescriptionAdapter
)}.
* Builder#Builder(Context,
MediaSession, int, String
)}.
*
*
* @param priority The priority which can be one of {@link NotificationCompat#PRIORITY_DEFAULT},
* @param priority The priority which can be one of {@link NotificationCompat#PRIORITY_DEFAULT},
* {@link NotificationCompat#PRIORITY_MAX}, {@link NotificationCompat#PRIORITY_HIGH}, {@link
* {@link NotificationCompat#PRIORITY_MAX}, {@link NotificationCompat#PRIORITY_HIGH}, {@link
...
@@ -832,9 +748,23 @@ public class PlayerNotificationManager {
...
@@ -832,9 +748,23 @@ public class PlayerNotificationManager {
}
}
}
}
private
void
startOrUpdateNotification
(
Player
player
,
@Nullable
Bitmap
bitmap
)
{
/**
boolean
ongoing
=
getOngoing
(
player
);
* Gets the {@link MediaController} to send command to the session with. Can be {@code null} if
builder
=
createNotification
(
player
,
builder
,
ongoing
,
bitmap
);
* the media controller isn't connected.
*/
@Nullable
public
final
MediaController
getMediaControllerOrNull
()
{
try
{
MediaController
controller
=
controllerFuture
.
get
(
0
,
TimeUnit
.
MILLISECONDS
);
return
controller
.
isConnected
()
?
controller
:
null
;
}
catch
(
ExecutionException
|
InterruptedException
|
TimeoutException
e
)
{
return
null
;
}
}
private
void
startOrUpdateNotification
(
@Nullable
Bitmap
bitmap
)
{
boolean
ongoing
=
getOngoing
();
builder
=
createNotification
(
builder
,
ongoing
,
bitmap
);
if
(
builder
==
null
)
{
if
(
builder
==
null
)
{
stopNotification
(
/* dismissedByUser= */
false
);
stopNotification
(
/* dismissedByUser= */
false
);
return
;
return
;
...
@@ -866,9 +796,8 @@ public class PlayerNotificationManager {
...
@@ -866,9 +796,8 @@ public class PlayerNotificationManager {
}
}
/**
/**
* Creates the notification given the current
player
state.
* Creates the notification given the current
session
state.
*
*
* @param player The player for which state to build a notification.
* @param builder The builder used to build the last notification, or {@code null}. Re-using the
* @param builder The builder used to build the last notification, or {@code null}. Re-using the
* builder when possible can prevent notification flicker when {@code Util#SDK_INT} < 21.
* builder when possible can prevent notification flicker when {@code Util#SDK_INT} < 21.
* @param ongoing Whether the notification should be ongoing.
* @param ongoing Whether the notification should be ongoing.
...
@@ -879,10 +808,8 @@ public class PlayerNotificationManager {
...
@@ -879,10 +808,8 @@ public class PlayerNotificationManager {
*/
*/
@Nullable
@Nullable
protected
NotificationCompat
.
Builder
createNotification
(
protected
NotificationCompat
.
Builder
createNotification
(
Player
player
,
@Nullable
NotificationCompat
.
Builder
builder
,
boolean
ongoing
,
@Nullable
Bitmap
largeIcon
)
{
@Nullable
NotificationCompat
.
Builder
builder
,
Player
player
=
session
.
getPlayer
();
boolean
ongoing
,
@Nullable
Bitmap
largeIcon
)
{
if
(
player
.
getPlaybackState
()
==
Player
.
STATE_IDLE
&&
player
.
getCurrentTimeline
().
isEmpty
())
{
if
(
player
.
getPlaybackState
()
==
Player
.
STATE_IDLE
&&
player
.
getCurrentTimeline
().
isEmpty
())
{
return
null
;
return
null
;
}
}
...
@@ -890,7 +817,7 @@ public class PlayerNotificationManager {
...
@@ -890,7 +817,7 @@ public class PlayerNotificationManager {
if
(
builder
==
null
)
{
if
(
builder
==
null
)
{
builder
=
new
NotificationCompat
.
Builder
(
context
,
channelId
);
builder
=
new
NotificationCompat
.
Builder
(
context
,
channelId
);
}
}
List
<
CommandButton
>
actionButtons
=
getActionButtons
(
player
);
List
<
CommandButton
>
actionButtons
=
getActionButtons
();
for
(
int
i
=
0
;
i
<
actionButtons
.
size
();
i
++)
{
for
(
int
i
=
0
;
i
<
actionButtons
.
size
();
i
++)
{
CommandButton
button
=
actionButtons
.
get
(
i
);
CommandButton
button
=
actionButtons
.
get
(
i
);
NotificationCompat
.
Action
action
=
NotificationCompat
.
Action
action
=
...
@@ -901,12 +828,8 @@ public class PlayerNotificationManager {
...
@@ -901,12 +828,8 @@ public class PlayerNotificationManager {
builder
.
addAction
(
action
);
builder
.
addAction
(
action
);
}
}
MediaStyle
mediaStyle
=
new
MediaStyle
();
MediaStyle
mediaStyle
=
new
MediaStyle
(
session
);
if
(
mediaSessionToken
!=
null
)
{
mediaStyle
.
setShowActionsInCompactView
(
getActionButtonIndicesForCompactView
(
actionButtons
));
mediaStyle
.
setMediaSession
(
mediaSessionToken
);
}
mediaStyle
.
setShowActionsInCompactView
(
getActionButtonIndicesForCompactView
(
actionButtons
,
player
));
// Configure dismiss action prior to API 21 ('x' button).
// Configure dismiss action prior to API 21 ('x' button).
mediaStyle
.
setShowCancelButton
(!
ongoing
);
mediaStyle
.
setShowCancelButton
(!
ongoing
);
mediaStyle
.
setCancelButtonIntent
(
dismissPendingIntent
);
mediaStyle
.
setCancelButtonIntent
(
dismissPendingIntent
);
...
@@ -942,16 +865,19 @@ public class PlayerNotificationManager {
...
@@ -942,16 +865,19 @@ public class PlayerNotificationManager {
}
}
// Set media specific notification properties from MediaDescriptionAdapter.
// Set media specific notification properties from MediaDescriptionAdapter.
builder
.
setContentTitle
(
mediaDescriptionAdapter
.
getCurrentContentTitle
(
player
));
builder
.
setContentTitle
(
mediaDescriptionAdapter
.
getCurrentContentTitle
(
session
));
builder
.
setContentText
(
mediaDescriptionAdapter
.
getCurrentContentText
(
player
));
builder
.
setContentText
(
mediaDescriptionAdapter
.
getCurrentContentText
(
session
));
builder
.
setSubText
(
mediaDescriptionAdapter
.
getCurrentSubText
(
player
));
builder
.
setSubText
(
mediaDescriptionAdapter
.
getCurrentSubText
(
session
));
if
(
largeIcon
==
null
)
{
if
(
largeIcon
==
null
)
{
largeIcon
=
largeIcon
=
mediaDescriptionAdapter
.
getCurrentLargeIcon
(
mediaDescriptionAdapter
.
getCurrentLargeIcon
(
player
,
new
BitmapCallback
(++
currentNotificationTag
));
session
,
new
BitmapCallback
(++
currentNotificationTag
));
}
}
setLargeIcon
(
builder
,
largeIcon
);
setLargeIcon
(
builder
,
largeIcon
);
builder
.
setContentIntent
(
mediaDescriptionAdapter
.
createCurrentContentIntent
(
player
));
MediaController
controller
=
getMediaControllerOrNull
();
if
(
controller
!=
null
)
{
builder
.
setContentIntent
(
controller
.
getSessionActivity
());
}
if
(
groupKey
!=
null
)
{
if
(
groupKey
!=
null
)
{
builder
.
setGroup
(
groupKey
);
builder
.
setGroup
(
groupKey
);
...
@@ -976,8 +902,8 @@ public class PlayerNotificationManager {
...
@@ -976,8 +902,8 @@ public class PlayerNotificationManager {
*
*
* <p>This method can be safely overridden.
* <p>This method can be safely overridden.
*/
*/
// Disclaimer: Custom action support is temporarily removed, but will be added back in next CL.
protected
List
<
CommandButton
>
getActionButtons
()
{
protected
List
<
CommandButton
>
getActionButtons
(
Player
player
)
{
Player
player
=
session
.
getPlayer
();
boolean
enablePrevious
=
player
.
isCommandAvailable
(
COMMAND_SEEK_TO_PREVIOUS
);
boolean
enablePrevious
=
player
.
isCommandAvailable
(
COMMAND_SEEK_TO_PREVIOUS
);
boolean
enableRewind
=
player
.
isCommandAvailable
(
COMMAND_SEEK_BACK
);
boolean
enableRewind
=
player
.
isCommandAvailable
(
COMMAND_SEEK_BACK
);
boolean
enableFastForward
=
player
.
isCommandAvailable
(
COMMAND_SEEK_FORWARD
);
boolean
enableFastForward
=
player
.
isCommandAvailable
(
COMMAND_SEEK_FORWARD
);
...
@@ -990,7 +916,7 @@ public class PlayerNotificationManager {
...
@@ -990,7 +916,7 @@ public class PlayerNotificationManager {
if
(
enableRewind
)
{
if
(
enableRewind
)
{
buttons
.
add
(
seekBackButton
);
buttons
.
add
(
seekBackButton
);
}
}
if
(
shouldShowPauseButton
(
player
))
{
if
(
shouldShowPauseButton
())
{
buttons
.
add
(
pauseButton
);
buttons
.
add
(
pauseButton
);
}
else
{
}
else
{
buttons
.
add
(
playButton
);
buttons
.
add
(
playButton
);
...
@@ -1011,11 +937,9 @@ public class PlayerNotificationManager {
...
@@ -1011,11 +937,9 @@ public class PlayerNotificationManager {
* first parameter.
* first parameter.
*
*
* @param actionButtons The buttons of the actions included in the notification.
* @param actionButtons The buttons of the actions included in the notification.
* @param player The player for which a notification is being built.
*/
*/
@SuppressWarnings
(
"unused"
)
@SuppressWarnings
(
"unused"
)
protected
int
[]
getActionButtonIndicesForCompactView
(
protected
int
[]
getActionButtonIndicesForCompactView
(
List
<
CommandButton
>
actionButtons
)
{
List
<
CommandButton
>
actionButtons
,
Player
player
)
{
int
previousIndex
=
C
.
INDEX_UNSET
;
int
previousIndex
=
C
.
INDEX_UNSET
;
int
nextIndex
=
C
.
INDEX_UNSET
;
int
nextIndex
=
C
.
INDEX_UNSET
;
int
playPauseIndex
=
C
.
INDEX_UNSET
;
int
playPauseIndex
=
C
.
INDEX_UNSET
;
...
@@ -1050,13 +974,15 @@ public class PlayerNotificationManager {
...
@@ -1050,13 +974,15 @@ public class PlayerNotificationManager {
}
}
/** Returns whether the generated notification should be ongoing. */
/** Returns whether the generated notification should be ongoing. */
protected
boolean
getOngoing
(
Player
player
)
{
protected
boolean
getOngoing
()
{
Player
player
=
session
.
getPlayer
();
int
playbackState
=
player
.
getPlaybackState
();
int
playbackState
=
player
.
getPlaybackState
();
return
(
playbackState
==
Player
.
STATE_BUFFERING
||
playbackState
==
Player
.
STATE_READY
)
return
(
playbackState
==
Player
.
STATE_BUFFERING
||
playbackState
==
Player
.
STATE_READY
)
&&
player
.
getPlayWhenReady
();
&&
player
.
getPlayWhenReady
();
}
}
private
boolean
shouldShowPauseButton
(
Player
player
)
{
private
boolean
shouldShowPauseButton
()
{
Player
player
=
session
.
getPlayer
();
return
player
.
getPlaybackState
()
!=
Player
.
STATE_ENDED
return
player
.
getPlaybackState
()
!=
Player
.
STATE_ENDED
&&
player
.
getPlaybackState
()
!=
Player
.
STATE_IDLE
&&
player
.
getPlaybackState
()
!=
Player
.
STATE_IDLE
&&
player
.
getPlayWhenReady
();
&&
player
.
getPlayWhenReady
();
...
@@ -1078,13 +1004,11 @@ public class PlayerNotificationManager {
...
@@ -1078,13 +1004,11 @@ public class PlayerNotificationManager {
private
boolean
handleMessage
(
Message
msg
)
{
private
boolean
handleMessage
(
Message
msg
)
{
switch
(
msg
.
what
)
{
switch
(
msg
.
what
)
{
case
MSG_START_OR_UPDATE_NOTIFICATION:
case
MSG_START_OR_UPDATE_NOTIFICATION:
if
(
player
!=
null
)
{
startOrUpdateNotification
(
/* bitmap= */
null
);
startOrUpdateNotification
(
player
,
/* bitmap= */
null
);
}
break
;
break
;
case
MSG_UPDATE_NOTIFICATION_BITMAP:
case
MSG_UPDATE_NOTIFICATION_BITMAP:
if
(
player
!=
null
&&
isNotificationStarted
&&
currentNotificationTag
==
msg
.
arg1
)
{
if
(
isNotificationStarted
&&
currentNotificationTag
==
msg
.
arg1
)
{
startOrUpdateNotification
(
player
,
(
Bitmap
)
msg
.
obj
);
startOrUpdateNotification
((
Bitmap
)
msg
.
obj
);
}
}
break
;
break
;
default
:
default
:
...
@@ -1143,13 +1067,27 @@ public class PlayerNotificationManager {
...
@@ -1143,13 +1067,27 @@ public class PlayerNotificationManager {
}
}
}
}
private
class
MediaControllerListener
implements
MediaController
.
Listener
{
@Override
public
void
onDisconnected
(
MediaController
controller
)
{
stopNotification
(
/* dismissedByUser= */
false
);
}
@Override
public
void
onAvailableSessionCommandsChanged
(
MediaController
controller
,
SessionCommands
commands
)
{
postStartOrUpdateNotification
();
}
}
private
class
NotificationBroadcastReceiver
extends
BroadcastReceiver
{
private
class
NotificationBroadcastReceiver
extends
BroadcastReceiver
{
@SuppressWarnings
(
"deprecation"
)
@SuppressWarnings
(
"deprecation"
)
@Override
@Override
public
void
onReceive
(
Context
context
,
Intent
intent
)
{
public
void
onReceive
(
Context
context
,
Intent
intent
)
{
Player
player
=
PlayerNotificationManager
.
this
.
player
;
MediaController
controller
=
getMediaControllerOrNull
()
;
if
(
play
er
==
null
if
(
controll
er
==
null
||
!
isNotificationStarted
||
!
isNotificationStarted
||
intent
.
getIntExtra
(
INTENT_EXTRA_INSTANCE_ID
,
instanceId
)
!=
instanceId
)
{
||
intent
.
getIntExtra
(
INTENT_EXTRA_INSTANCE_ID
,
instanceId
)
!=
instanceId
)
{
return
;
return
;
...
@@ -1160,38 +1098,43 @@ public class PlayerNotificationManager {
...
@@ -1160,38 +1098,43 @@ public class PlayerNotificationManager {
int
playerCommand
=
intent
.
getIntExtra
(
INTENT_EXTRA_PLAYER_COMMAND
,
COMMAND_INVALID
);
int
playerCommand
=
intent
.
getIntExtra
(
INTENT_EXTRA_PLAYER_COMMAND
,
COMMAND_INVALID
);
switch
(
playerCommand
)
{
switch
(
playerCommand
)
{
case
COMMAND_PLAY_PAUSE:
case
COMMAND_PLAY_PAUSE:
if
(!
play
er
.
getPlayWhenReady
())
{
if
(!
controll
er
.
getPlayWhenReady
())
{
if
(
player
.
getPlaybackState
()
==
Play
er
.
STATE_IDLE
)
{
if
(
controller
.
getPlaybackState
()
==
controll
er
.
STATE_IDLE
)
{
play
er
.
prepare
();
controll
er
.
prepare
();
}
else
if
(
player
.
getPlaybackState
()
==
Play
er
.
STATE_ENDED
)
{
}
else
if
(
controller
.
getPlaybackState
()
==
controll
er
.
STATE_ENDED
)
{
player
.
seekToDefaultPosition
(
play
er
.
getCurrentWindowIndex
());
controller
.
seekToDefaultPosition
(
controll
er
.
getCurrentWindowIndex
());
}
}
}
else
{
}
else
{
play
er
.
pause
();
controll
er
.
pause
();
}
}
break
;
break
;
case
COMMAND_SEEK_TO_PREVIOUS:
case
COMMAND_SEEK_TO_PREVIOUS:
play
er
.
seekToPrevious
();
controll
er
.
seekToPrevious
();
break
;
break
;
case
COMMAND_SEEK_BACK:
case
COMMAND_SEEK_BACK:
play
er
.
seekBack
();
controll
er
.
seekBack
();
break
;
break
;
case
COMMAND_SEEK_FORWARD:
case
COMMAND_SEEK_FORWARD:
play
er
.
seekForward
();
controll
er
.
seekForward
();
break
;
break
;
case
COMMAND_SEEK_TO_NEXT:
case
COMMAND_SEEK_TO_NEXT:
player
.
seekToNext
();
controller
.
seekToNext
();
break
;
case
COMMAND_INVALID:
SessionCommand
sessionCommand
=
checkStateNotNull
(
BundleableUtil
.
fromNullableBundle
(
SessionCommand
.
CREATOR
,
intent
.
getBundleExtra
(
INTENT_EXTRA_SESSION_COMMAND
)));
ListenableFuture
<
SessionResult
>
unused
=
controller
.
sendCustomCommand
(
sessionCommand
,
/* args= */
Bundle
.
EMPTY
);
break
;
break
;
default
:
default
:
Log
.
w
(
TAG
,
"Unsupported
play
er command, playerCommand="
+
playerCommand
);
Log
.
w
(
TAG
,
"Unsupported
controll
er command, playerCommand="
+
playerCommand
);
break
;
break
;
}
}
}
else
if
(
INTENT_ACTION_DISMISS
.
equals
(
action
))
{
}
else
if
(
INTENT_ACTION_DISMISS
.
equals
(
action
))
{
stopNotification
(
/* dismissedByUser= */
true
);
stopNotification
(
/* dismissedByUser= */
true
);
}
else
if
(
action
!=
null
&&
customActionReceiver
!=
null
&&
customActions
.
containsKey
(
action
))
{
customActionReceiver
.
onCustomAction
(
player
,
action
,
intent
);
}
}
}
}
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment