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
821615ce
authored
May 26, 2022
by
bachinger
Committed by
Marc Baechinger
May 30, 2022
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Avoid usage of MediaController in MediaNotification.Provider
PiperOrigin-RevId: 451155897
parent
3d2b3358
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
78 additions
and
69 deletions
libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java
libraries/session/src/main/java/androidx/media3/session/MediaNotification.java
libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java
libraries/session/src/main/java/androidx/media3/session/MediaSession.java
libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java
View file @
821615ce
...
@@ -51,7 +51,6 @@ import com.google.common.collect.ImmutableList;
...
@@ -51,7 +51,6 @@ 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.common.util.concurrent.MoreExecutors
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.List
;
import
java.util.List
;
...
@@ -126,16 +125,17 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
...
@@ -126,16 +125,17 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
@Override
@Override
public
final
MediaNotification
createNotification
(
public
final
MediaNotification
createNotification
(
Media
Controller
mediaController
,
Media
Session
mediaSession
,
ImmutableList
<
CommandButton
>
customLayout
,
ImmutableList
<
CommandButton
>
customLayout
,
MediaNotification
.
ActionFactory
actionFactory
,
MediaNotification
.
ActionFactory
actionFactory
,
Callback
onNotificationChangedCallback
)
{
Callback
onNotificationChangedCallback
)
{
ensureNotificationChannel
();
ensureNotificationChannel
();
Player
player
=
mediaSession
.
getPlayer
();
NotificationCompat
.
Builder
builder
=
NotificationCompat
.
Builder
builder
=
new
NotificationCompat
.
Builder
(
context
,
NOTIFICATION_CHANNEL_ID
);
new
NotificationCompat
.
Builder
(
context
,
NOTIFICATION_CHANNEL_ID
);
// Set metadata info in the notification.
// Set metadata info in the notification.
MediaMetadata
metadata
=
mediaControll
er
.
getMediaMetadata
();
MediaMetadata
metadata
=
play
er
.
getMediaMetadata
();
builder
.
setContentTitle
(
metadata
.
title
).
setContentText
(
metadata
.
artist
);
builder
.
setContentTitle
(
metadata
.
title
).
setContentText
(
metadata
.
artist
);
@Nullable
ListenableFuture
<
Bitmap
>
bitmapFuture
=
loadArtworkBitmap
(
metadata
);
@Nullable
ListenableFuture
<
Bitmap
>
bitmapFuture
=
loadArtworkBitmap
(
metadata
);
...
@@ -164,19 +164,16 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
...
@@ -164,19 +164,16 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
MediaStyle
mediaStyle
=
new
MediaStyle
();
MediaStyle
mediaStyle
=
new
MediaStyle
();
int
[]
compactViewIndices
=
int
[]
compactViewIndices
=
addNotificationActions
(
addNotificationActions
(
getMediaButtons
(
getMediaButtons
(
player
.
getAvailableCommands
(),
customLayout
,
player
.
getPlayWhenReady
()),
mediaController
.
getAvailableCommands
(),
customLayout
,
mediaController
.
getPlayWhenReady
()),
builder
,
builder
,
actionFactory
);
actionFactory
);
mediaStyle
.
setShowActionsInCompactView
(
compactViewIndices
);
mediaStyle
.
setShowActionsInCompactView
(
compactViewIndices
);
if
(
mediaControll
er
.
isCommandAvailable
(
COMMAND_STOP
)
||
Util
.
SDK_INT
<
21
)
{
if
(
play
er
.
isCommandAvailable
(
COMMAND_STOP
)
||
Util
.
SDK_INT
<
21
)
{
// We must include a cancel intent for pre-L devices.
// We must include a cancel intent for pre-L devices.
mediaStyle
.
setCancelButtonIntent
(
actionFactory
.
createMediaActionPendingIntent
(
COMMAND_STOP
));
mediaStyle
.
setCancelButtonIntent
(
actionFactory
.
createMediaActionPendingIntent
(
COMMAND_STOP
));
}
}
long
playbackStartTimeMs
=
getPlaybackStartTimeEpochMs
(
mediaControll
er
);
long
playbackStartTimeMs
=
getPlaybackStartTimeEpochMs
(
play
er
);
boolean
displayElapsedTimeWithChronometer
=
playbackStartTimeMs
!=
C
.
TIME_UNSET
;
boolean
displayElapsedTimeWithChronometer
=
playbackStartTimeMs
!=
C
.
TIME_UNSET
;
builder
builder
.
setWhen
(
playbackStartTimeMs
)
.
setWhen
(
playbackStartTimeMs
)
...
@@ -185,7 +182,7 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
...
@@ -185,7 +182,7 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
Notification
notification
=
Notification
notification
=
builder
builder
.
setContentIntent
(
media
Controller
.
getSessionActivity
())
.
setContentIntent
(
media
Session
.
getSessionActivity
())
.
setDeleteIntent
(
actionFactory
.
createMediaActionPendingIntent
(
COMMAND_STOP
))
.
setDeleteIntent
(
actionFactory
.
createMediaActionPendingIntent
(
COMMAND_STOP
))
.
setOnlyAlertOnce
(
true
)
.
setOnlyAlertOnce
(
true
)
.
setSmallIcon
(
getSmallIconResId
(
context
))
.
setSmallIcon
(
getSmallIconResId
(
context
))
...
@@ -197,34 +194,9 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
...
@@ -197,34 +194,9 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
}
}
@Override
@Override
public
final
void
handleCustomCommand
(
public
final
boolean
handleCustomCommand
(
MediaSession
session
,
String
action
,
Bundle
extras
)
{
MediaController
mediaController
,
String
action
,
Bundle
extras
)
{
// Make the custom action being delegated to the session as a custom session command.
@Nullable
SessionCommand
customCommand
=
null
;
return
false
;
for
(
SessionCommand
command
:
mediaController
.
getAvailableSessionCommands
().
commands
)
{
if
(
command
.
commandCode
==
SessionCommand
.
COMMAND_CODE_CUSTOM
&&
command
.
customAction
.
equals
(
action
))
{
customCommand
=
command
;
break
;
}
}
if
(
customCommand
!=
null
)
{
ListenableFuture
<
SessionResult
>
future
=
mediaController
.
sendCustomCommand
(
customCommand
,
extras
);
Futures
.
addCallback
(
future
,
new
FutureCallback
<
SessionResult
>()
{
@Override
public
void
onSuccess
(
SessionResult
result
)
{
// Do nothing.
}
@Override
public
void
onFailure
(
Throwable
t
)
{
Log
.
w
(
TAG
,
"custom command "
+
action
+
" produced an error: "
+
t
.
getMessage
(),
t
);
}
},
MoreExecutors
.
directExecutor
());
}
}
}
/**
/**
...
@@ -422,14 +394,14 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
...
@@ -422,14 +394,14 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
}
}
}
}
private
static
long
getPlaybackStartTimeEpochMs
(
MediaController
controll
er
)
{
private
static
long
getPlaybackStartTimeEpochMs
(
Player
play
er
)
{
// Changing "showWhen" causes notification flicker if SDK_INT < 21.
// Changing "showWhen" causes notification flicker if SDK_INT < 21.
if
(
Util
.
SDK_INT
>=
21
if
(
Util
.
SDK_INT
>=
21
&&
controll
er
.
isPlaying
()
&&
play
er
.
isPlaying
()
&&
!
controll
er
.
isPlayingAd
()
&&
!
play
er
.
isPlayingAd
()
&&
!
controll
er
.
isCurrentMediaItemDynamic
()
&&
!
play
er
.
isCurrentMediaItemDynamic
()
&&
controll
er
.
getPlaybackParameters
().
speed
==
1
f
)
{
&&
play
er
.
getPlaybackParameters
().
speed
==
1
f
)
{
return
System
.
currentTimeMillis
()
-
controll
er
.
getContentPosition
();
return
System
.
currentTimeMillis
()
-
play
er
.
getContentPosition
();
}
else
{
}
else
{
return
C
.
TIME_UNSET
;
return
C
.
TIME_UNSET
;
}
}
...
...
libraries/session/src/main/java/androidx/media3/session/MediaNotification.java
View file @
821615ce
...
@@ -74,7 +74,7 @@ public final class MediaNotification {
...
@@ -74,7 +74,7 @@ public final class MediaNotification {
* <p>The returned {@link NotificationCompat.Action} will have a {@link PendingIntent} with the
* <p>The returned {@link NotificationCompat.Action} will have a {@link PendingIntent} with the
* extras from {@link SessionCommand#customExtras}. Accordingly the {@linkplain
* extras from {@link SessionCommand#customExtras}. Accordingly the {@linkplain
* SessionCommand#customExtras command's extras} will be passed to {@link
* SessionCommand#customExtras command's extras} will be passed to {@link
* Provider#handleCustomCommand(Media
Controller
, String, Bundle)} when the action is executed.
* Provider#handleCustomCommand(Media
Session
, String, Bundle)} when the action is executed.
*
*
* @param customCommandButton A {@linkplain CommandButton custom command button}.
* @param customCommandButton A {@linkplain CommandButton custom command button}.
* @see MediaNotification.Provider#handleCustomCommand
* @see MediaNotification.Provider#handleCustomCommand
...
@@ -116,8 +116,8 @@ public final class MediaNotification {
...
@@ -116,8 +116,8 @@ public final class MediaNotification {
/**
/**
* Creates a new {@link MediaNotification}.
* Creates a new {@link MediaNotification}.
*
*
* @param
mediaController The controller of the
session.
* @param
session The media
session.
* @param actionFactory The {@link ActionFactory} for creating notification {@link
plain
* @param actionFactory The {@link ActionFactory} for creating notification {@link
* NotificationCompat.Action actions}.
* NotificationCompat.Action actions}.
* @param customLayout The custom layout {@linkplain MediaSession#setCustomLayout(List) set by
* @param customLayout The custom layout {@linkplain MediaSession#setCustomLayout(List) set by
* the session}.
* the session}.
...
@@ -126,7 +126,7 @@ public final class MediaNotification {
...
@@ -126,7 +126,7 @@ public final class MediaNotification {
* been loaded asynchronously.
* been loaded asynchronously.
*/
*/
MediaNotification
createNotification
(
MediaNotification
createNotification
(
Media
Controller
mediaController
,
Media
Session
session
,
ImmutableList
<
CommandButton
>
customLayout
,
ImmutableList
<
CommandButton
>
customLayout
,
ActionFactory
actionFactory
,
ActionFactory
actionFactory
,
Callback
onNotificationChangedCallback
);
Callback
onNotificationChangedCallback
);
...
@@ -134,13 +134,15 @@ public final class MediaNotification {
...
@@ -134,13 +134,15 @@ public final class MediaNotification {
/**
/**
* Handles a notification's custom command.
* Handles a notification's custom command.
*
*
* @param
mediaController The controller of the
session.
* @param
session The media
session.
* @param action The custom command action.
* @param action The custom command action.
* @param extras A bundle {@linkplain SessionCommand#customExtras set in the custom command},
* @param extras A bundle {@linkplain SessionCommand#customExtras set in the custom command},
* otherwise {@link Bundle#EMPTY}.
* otherwise {@link Bundle#EMPTY}.
* @return {@code false} if the action should be delivered to the session as a custom command or
* {@code true} if the action has been handled completely by the provider.
* @see ActionFactory#createCustomAction
* @see ActionFactory#createCustomAction
*/
*/
void
handleCustomCommand
(
MediaController
mediaController
,
String
action
,
Bundle
extras
);
boolean
handleCustomCommand
(
MediaSession
session
,
String
action
,
Bundle
extras
);
}
}
/** The notification id. */
/** The notification id. */
...
...
libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java
View file @
821615ce
...
@@ -32,8 +32,10 @@ import androidx.media3.common.Player;
...
@@ -32,8 +32,10 @@ import androidx.media3.common.Player;
import
androidx.media3.common.util.Log
;
import
androidx.media3.common.util.Log
;
import
androidx.media3.common.util.Util
;
import
androidx.media3.common.util.Util
;
import
com.google.common.collect.ImmutableList
;
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.Futures
;
import
com.google.common.util.concurrent.ListenableFuture
;
import
com.google.common.util.concurrent.ListenableFuture
;
import
com.google.common.util.concurrent.MoreExecutors
;
import
java.util.HashMap
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map
;
...
@@ -122,28 +124,44 @@ import java.util.concurrent.TimeoutException;
...
@@ -122,28 +124,44 @@ import java.util.concurrent.TimeoutException;
return
;
return
;
}
}
try
{
try
{
MediaController
mediaController
=
controllerFuture
.
get
(
0
,
TimeUnit
.
MILLISECONDS
);
MediaController
mediaController
=
checkStateNotNull
(
Futures
.
getDone
(
controllerFuture
));
mediaNotificationProvider
.
handleCustomCommand
(
mediaController
,
action
,
extras
);
if
(!
mediaNotificationProvider
.
handleCustomCommand
(
session
,
action
,
extras
))
{
}
catch
(
InterruptedException
|
ExecutionException
|
TimeoutException
e
)
{
@Nullable
SessionCommand
customCommand
=
null
;
// We should never reach this.
for
(
SessionCommand
command
:
mediaController
.
getAvailableSessionCommands
().
commands
)
{
throw
new
IllegalStateException
(
e
);
if
(
command
.
commandCode
==
SessionCommand
.
COMMAND_CODE_CUSTOM
}
&&
command
.
customAction
.
equals
(
action
))
{
customCommand
=
command
;
break
;
}
}
if
(
customCommand
!=
null
&&
mediaController
.
getAvailableSessionCommands
().
contains
(
customCommand
))
{
ListenableFuture
<
SessionResult
>
future
=
mediaController
.
sendCustomCommand
(
customCommand
,
Bundle
.
EMPTY
);
Futures
.
addCallback
(
future
,
new
FutureCallback
<
SessionResult
>()
{
@Override
public
void
onSuccess
(
SessionResult
result
)
{
// Do nothing.
}
}
public
void
updateNotification
(
MediaSession
session
)
{
@Override
@Nullable
ListenableFuture
<
MediaController
>
controllerFuture
=
controllerMap
.
get
(
session
);
public
void
onFailure
(
Throwable
t
)
{
if
(
controllerFuture
==
null
)
{
Log
.
w
(
return
;
TAG
,
"custom command "
+
action
+
" produced an error: "
+
t
.
getMessage
(),
t
);
}
},
MoreExecutors
.
directExecutor
());
}
}
}
MediaController
mediaController
;
try
{
mediaController
=
checkStateNotNull
(
Futures
.
getDone
(
controllerFuture
));
}
catch
(
ExecutionException
e
)
{
}
catch
(
ExecutionException
e
)
{
// We should never reach this
point
.
// We should never reach this.
throw
new
IllegalStateException
(
e
);
throw
new
IllegalStateException
(
e
);
}
}
}
public
void
updateNotification
(
MediaSession
session
)
{
if
(!
mediaSessionService
.
isSessionAdded
(
session
)
||
!
canStartPlayback
(
session
.
getPlayer
()))
{
if
(!
mediaSessionService
.
isSessionAdded
(
session
)
||
!
canStartPlayback
(
session
.
getPlayer
()))
{
maybeStopForegroundService
(
/* removeNotifications= */
true
);
maybeStopForegroundService
(
/* removeNotifications= */
true
);
return
;
return
;
...
@@ -157,10 +175,7 @@ import java.util.concurrent.TimeoutException;
...
@@ -157,10 +175,7 @@ import java.util.concurrent.TimeoutException;
MediaNotification
mediaNotification
=
MediaNotification
mediaNotification
=
this
.
mediaNotificationProvider
.
createNotification
(
this
.
mediaNotificationProvider
.
createNotification
(
mediaController
,
session
,
checkStateNotNull
(
customLayoutMap
.
get
(
session
)),
actionFactory
,
callback
);
checkStateNotNull
(
customLayoutMap
.
get
(
session
)),
actionFactory
,
callback
);
updateNotificationInternal
(
session
,
mediaNotification
);
updateNotificationInternal
(
session
,
mediaNotification
);
}
}
...
...
libraries/session/src/main/java/androidx/media3/session/MediaSession.java
View file @
821615ce
...
@@ -35,6 +35,7 @@ import android.view.KeyEvent;
...
@@ -35,6 +35,7 @@ import android.view.KeyEvent;
import
androidx.annotation.GuardedBy
;
import
androidx.annotation.GuardedBy
;
import
androidx.annotation.Nullable
;
import
androidx.annotation.Nullable
;
import
androidx.annotation.VisibleForTesting
;
import
androidx.annotation.VisibleForTesting
;
import
androidx.core.app.NotificationCompat
;
import
androidx.media.MediaSessionManager.RemoteUserInfo
;
import
androidx.media.MediaSessionManager.RemoteUserInfo
;
import
androidx.media3.common.AudioAttributes
;
import
androidx.media3.common.AudioAttributes
;
import
androidx.media3.common.DeviceInfo
;
import
androidx.media3.common.DeviceInfo
;
...
@@ -247,6 +248,14 @@ public class MediaSession {
...
@@ -247,6 +248,14 @@ public class MediaSession {
* Sets a {@link PendingIntent} to launch an {@link android.app.Activity} for the {@link
* Sets a {@link PendingIntent} to launch an {@link android.app.Activity} for the {@link
* MediaSession}. This can be used as a quick link to an ongoing media screen.
* MediaSession}. This can be used as a quick link to an ongoing media screen.
*
*
* <p>A client can use this pending intent to start an activity belonging to this session. When
* this pending intent is for instance included in the notification {@linkplain
* NotificationCompat.Builder#setContentIntent(PendingIntent) as the content intent}, tapping
* the notification will open this activity.
*
* <p>See <a href="https://developer.android.com/training/notify-user/navigation">'Start an
* Activity from a Notification'</a> also.
*
* @param pendingIntent The pending intent.
* @param pendingIntent The pending intent.
* @return The builder to allow chaining.
* @return The builder to allow chaining.
*/
*/
...
@@ -515,6 +524,17 @@ public class MediaSession {
...
@@ -515,6 +524,17 @@ public class MediaSession {
}
}
/**
/**
* Returns the {@link PendingIntent} to launch {@linkplain
* Builder#setSessionActivity(PendingIntent) the session activity} or null if not set.
*
* @return The {@link PendingIntent} to launch an activity belonging to the session.
*/
@Nullable
public
PendingIntent
getSessionActivity
()
{
return
impl
.
getSessionActivity
();
}
/**
* Sets the underlying {@link Player} for this session to dispatch incoming events to.
* Sets the underlying {@link Player} for this session to dispatch incoming events to.
*
*
* @param player A player that handles actual media playback in your app.
* @param player A player that handles actual media playback in your app.
...
...
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