mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Compare commits
3 Commits
5f940af3df
...
df8763ae0d
Author | SHA1 | Date | |
---|---|---|---|
![]() |
df8763ae0d | ||
![]() |
9ca8540f85 | ||
![]() |
45bcf3ff92 |
4
.github/ISSUE_TEMPLATE/bug.yml
vendored
4
.github/ISSUE_TEMPLATE/bug.yml
vendored
@ -19,6 +19,7 @@ body:
|
|||||||
options:
|
options:
|
||||||
- Media3 main branch
|
- Media3 main branch
|
||||||
- Media3 pre-release (alpha, beta or RC not in this list)
|
- Media3 pre-release (alpha, beta or RC not in this list)
|
||||||
|
- Media3 1.6.1
|
||||||
- Media3 1.6.0
|
- Media3 1.6.0
|
||||||
- Media3 1.5.1
|
- Media3 1.5.1
|
||||||
- Media3 1.5.0
|
- Media3 1.5.0
|
||||||
@ -44,9 +45,6 @@ body:
|
|||||||
- ExoPlayer 2.16.0
|
- ExoPlayer 2.16.0
|
||||||
- ExoPlayer 2.15.1
|
- ExoPlayer 2.15.1
|
||||||
- ExoPlayer 2.15.0
|
- ExoPlayer 2.15.0
|
||||||
- ExoPlayer 2.14.2
|
|
||||||
- ExoPlayer 2.14.1
|
|
||||||
- ExoPlayer 2.14.0
|
|
||||||
- ExoPlayer dev-v2 branch
|
- ExoPlayer dev-v2 branch
|
||||||
- Older (unsupported)
|
- Older (unsupported)
|
||||||
validations:
|
validations:
|
||||||
|
@ -73,6 +73,11 @@
|
|||||||
player doesn't have `COMMAND_GET_TIMELINE` available while
|
player doesn't have `COMMAND_GET_TIMELINE` available while
|
||||||
`COMMAND_GET_CURRENT_MEDIA_ITEM` is available and the wrapped player is
|
`COMMAND_GET_CURRENT_MEDIA_ITEM` is available and the wrapped player is
|
||||||
empty ([#2320](https://github.com/androidx/media/issues/2320)).
|
empty ([#2320](https://github.com/androidx/media/issues/2320)).
|
||||||
|
* Fix a bug where calling
|
||||||
|
`MediaSessionService.setMediaNotificationProvider` is silently ignored
|
||||||
|
after other interactions with the service like
|
||||||
|
`setForegroundServiceTimeoutMs`
|
||||||
|
([#2305](https://github.com/androidx/media/issues/2305)).
|
||||||
* UI:
|
* UI:
|
||||||
* Enable `PlayerSurface` to work with `ExoPlayer.setVideoEffects` and
|
* Enable `PlayerSurface` to work with `ExoPlayer.setVideoEffects` and
|
||||||
`CompositionPlayer`.
|
`CompositionPlayer`.
|
||||||
|
@ -12,8 +12,8 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
project.ext {
|
project.ext {
|
||||||
releaseVersion = '1.6.0'
|
releaseVersion = '1.6.1'
|
||||||
releaseVersionCode = 1_006_000_3_00
|
releaseVersionCode = 1_006_001_3_00
|
||||||
minSdkVersion = 21
|
minSdkVersion = 21
|
||||||
// See https://developer.android.com/training/cars/media/automotive-os#automotive-module
|
// See https://developer.android.com/training/cars/media/automotive-os#automotive-module
|
||||||
automotiveMinSdkVersion = 28
|
automotiveMinSdkVersion = 28
|
||||||
|
@ -29,11 +29,11 @@ public final class MediaLibraryInfo {
|
|||||||
|
|
||||||
/** The version of the library expressed as a string, for example "1.2.3" or "1.2.0-beta01". */
|
/** The version of the library expressed as a string, for example "1.2.3" or "1.2.0-beta01". */
|
||||||
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa.
|
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa.
|
||||||
public static final String VERSION = "1.6.0";
|
public static final String VERSION = "1.6.1";
|
||||||
|
|
||||||
/** The version of the library expressed as {@code TAG + "/" + VERSION}. */
|
/** The version of the library expressed as {@code TAG + "/" + VERSION}. */
|
||||||
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
|
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
|
||||||
public static final String VERSION_SLASHY = "AndroidXMedia3/1.6.0";
|
public static final String VERSION_SLASHY = "AndroidXMedia3/1.6.1";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The version of the library expressed as an integer, for example 1002003300.
|
* The version of the library expressed as an integer, for example 1002003300.
|
||||||
@ -47,7 +47,7 @@ public final class MediaLibraryInfo {
|
|||||||
* (123-045-006-3-00).
|
* (123-045-006-3-00).
|
||||||
*/
|
*/
|
||||||
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
|
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
|
||||||
public static final int VERSION_INT = 1_006_000_3_00;
|
public static final int VERSION_INT = 1_006_001_3_00;
|
||||||
|
|
||||||
/** Whether the library was compiled with {@link Assertions} checks enabled. */
|
/** Whether the library was compiled with {@link Assertions} checks enabled. */
|
||||||
public static final boolean ASSERTIONS_ENABLED = true;
|
public static final boolean ASSERTIONS_ENABLED = true;
|
||||||
|
@ -60,7 +60,7 @@ import java.util.concurrent.TimeoutException;
|
|||||||
private static final int MSG_USER_ENGAGED_TIMEOUT = 1;
|
private static final int MSG_USER_ENGAGED_TIMEOUT = 1;
|
||||||
|
|
||||||
private final MediaSessionService mediaSessionService;
|
private final MediaSessionService mediaSessionService;
|
||||||
private final MediaNotification.Provider mediaNotificationProvider;
|
|
||||||
private final MediaNotification.ActionFactory actionFactory;
|
private final MediaNotification.ActionFactory actionFactory;
|
||||||
private final NotificationManagerCompat notificationManagerCompat;
|
private final NotificationManagerCompat notificationManagerCompat;
|
||||||
private final Handler mainHandler;
|
private final Handler mainHandler;
|
||||||
@ -68,6 +68,7 @@ import java.util.concurrent.TimeoutException;
|
|||||||
private final Intent startSelfIntent;
|
private final Intent startSelfIntent;
|
||||||
private final Map<MediaSession, ControllerInfo> controllerMap;
|
private final Map<MediaSession, ControllerInfo> controllerMap;
|
||||||
|
|
||||||
|
private MediaNotification.Provider mediaNotificationProvider;
|
||||||
private int totalNotificationCount;
|
private int totalNotificationCount;
|
||||||
@Nullable private MediaNotification mediaNotification;
|
@Nullable private MediaNotification mediaNotification;
|
||||||
private boolean startedInForeground;
|
private boolean startedInForeground;
|
||||||
@ -146,6 +147,15 @@ import java.util.concurrent.TimeoutException;
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the media notification provider.
|
||||||
|
*
|
||||||
|
* @param mediaNotificationProvider The {@link MediaNotification.Provider}.
|
||||||
|
*/
|
||||||
|
public void setMediaNotificationProvider(MediaNotification.Provider mediaNotificationProvider) {
|
||||||
|
this.mediaNotificationProvider = mediaNotificationProvider;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the notification.
|
* Updates the notification.
|
||||||
*
|
*
|
||||||
|
@ -169,23 +169,13 @@ public abstract class MediaSessionService extends Service {
|
|||||||
|
|
||||||
private final Object lock;
|
private final Object lock;
|
||||||
private final Handler mainHandler;
|
private final Handler mainHandler;
|
||||||
|
@Nullable private MediaSessionServiceStub stub;
|
||||||
|
private @MonotonicNonNull MediaNotificationManager mediaNotificationManager;
|
||||||
|
private @MonotonicNonNull DefaultActionFactory actionFactory;
|
||||||
|
|
||||||
@GuardedBy("lock")
|
@GuardedBy("lock")
|
||||||
private final Map<String, MediaSession> sessions;
|
private final Map<String, MediaSession> sessions;
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
@Nullable
|
|
||||||
private MediaSessionServiceStub stub;
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private @MonotonicNonNull MediaNotificationManager mediaNotificationManager;
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private MediaNotification.@MonotonicNonNull Provider mediaNotificationProvider;
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private @MonotonicNonNull DefaultActionFactory actionFactory;
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
@GuardedBy("lock")
|
||||||
@Nullable
|
@Nullable
|
||||||
private Listener listener;
|
private Listener listener;
|
||||||
@ -211,9 +201,7 @@ public abstract class MediaSessionService extends Service {
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
synchronized (lock) {
|
stub = new MediaSessionServiceStub(this);
|
||||||
stub = new MediaSessionServiceStub(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -277,11 +265,10 @@ public abstract class MediaSessionService extends Service {
|
|||||||
if (old == null) {
|
if (old == null) {
|
||||||
// Session has returned for the first time. Register callbacks.
|
// Session has returned for the first time. Register callbacks.
|
||||||
// TODO(b/191644474): Check whether the session is registered to multiple services.
|
// TODO(b/191644474): Check whether the session is registered to multiple services.
|
||||||
MediaNotificationManager notificationManager = getMediaNotificationManager();
|
|
||||||
postOrRun(
|
postOrRun(
|
||||||
mainHandler,
|
mainHandler,
|
||||||
() -> {
|
() -> {
|
||||||
notificationManager.addSession(session);
|
getMediaNotificationManager().addSession(session);
|
||||||
session.setListener(new MediaSessionListener());
|
session.setListener(new MediaSessionListener());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -303,11 +290,10 @@ public abstract class MediaSessionService extends Service {
|
|||||||
checkArgument(sessions.containsKey(session.getId()), "session not found");
|
checkArgument(sessions.containsKey(session.getId()), "session not found");
|
||||||
sessions.remove(session.getId());
|
sessions.remove(session.getId());
|
||||||
}
|
}
|
||||||
MediaNotificationManager notificationManager = getMediaNotificationManager();
|
|
||||||
postOrRun(
|
postOrRun(
|
||||||
mainHandler,
|
mainHandler,
|
||||||
() -> {
|
() -> {
|
||||||
notificationManager.removeSession(session);
|
getMediaNotificationManager().removeSession(session);
|
||||||
session.clearListener();
|
session.clearListener();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -489,6 +475,8 @@ public abstract class MediaSessionService extends Service {
|
|||||||
* <p>The default and maximum value is {@link #DEFAULT_FOREGROUND_SERVICE_TIMEOUT_MS}. If a larger
|
* <p>The default and maximum value is {@link #DEFAULT_FOREGROUND_SERVICE_TIMEOUT_MS}. If a larger
|
||||||
* value is provided, it will be clamped down to {@link #DEFAULT_FOREGROUND_SERVICE_TIMEOUT_MS}.
|
* value is provided, it will be clamped down to {@link #DEFAULT_FOREGROUND_SERVICE_TIMEOUT_MS}.
|
||||||
*
|
*
|
||||||
|
* <p>This method must be called on the main thread.
|
||||||
|
*
|
||||||
* @param foregroundServiceTimeoutMs The timeout in milliseconds.
|
* @param foregroundServiceTimeoutMs The timeout in milliseconds.
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@ -512,6 +500,8 @@ public abstract class MediaSessionService extends Service {
|
|||||||
* {@linkplain #setForegroundServiceTimeoutMs foreground service timeout} after they paused,
|
* {@linkplain #setForegroundServiceTimeoutMs foreground service timeout} after they paused,
|
||||||
* stopped, failed or ended. Use {@link #pauseAllPlayersAndStopSelf()} to pause all ongoing
|
* stopped, failed or ended. Use {@link #pauseAllPlayersAndStopSelf()} to pause all ongoing
|
||||||
* playbacks immediately and terminate the service.
|
* playbacks immediately and terminate the service.
|
||||||
|
*
|
||||||
|
* <p>This method must be called on the main thread.
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public boolean isPlaybackOngoing() {
|
public boolean isPlaybackOngoing() {
|
||||||
@ -524,6 +514,8 @@ public abstract class MediaSessionService extends Service {
|
|||||||
*
|
*
|
||||||
* <p>This terminates the service lifecycle and triggers {@link #onDestroy()} that an app can
|
* <p>This terminates the service lifecycle and triggers {@link #onDestroy()} that an app can
|
||||||
* override to release the sessions and other resources.
|
* override to release the sessions and other resources.
|
||||||
|
*
|
||||||
|
* <p>This method must be called on the main thread.
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public void pauseAllPlayersAndStopSelf() {
|
public void pauseAllPlayersAndStopSelf() {
|
||||||
@ -583,11 +575,9 @@ public abstract class MediaSessionService extends Service {
|
|||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
synchronized (lock) {
|
if (stub != null) {
|
||||||
if (stub != null) {
|
stub.release();
|
||||||
stub.release();
|
stub = null;
|
||||||
stub = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -637,23 +627,22 @@ public abstract class MediaSessionService extends Service {
|
|||||||
/**
|
/**
|
||||||
* Sets the {@link MediaNotification.Provider} to customize notifications.
|
* Sets the {@link MediaNotification.Provider} to customize notifications.
|
||||||
*
|
*
|
||||||
* <p>This should be called before {@link #onCreate()} returns.
|
|
||||||
*
|
|
||||||
* <p>This method can be called from any thread.
|
* <p>This method can be called from any thread.
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
protected final void setMediaNotificationProvider(
|
protected final void setMediaNotificationProvider(
|
||||||
MediaNotification.Provider mediaNotificationProvider) {
|
MediaNotification.Provider mediaNotificationProvider) {
|
||||||
checkNotNull(mediaNotificationProvider);
|
checkNotNull(mediaNotificationProvider);
|
||||||
synchronized (lock) {
|
Util.postOrRun(
|
||||||
this.mediaNotificationProvider = mediaNotificationProvider;
|
mainHandler,
|
||||||
}
|
() ->
|
||||||
|
getMediaNotificationManager(
|
||||||
|
/* initialMediaNotificationProvider= */ mediaNotificationProvider)
|
||||||
|
.setMediaNotificationProvider(mediaNotificationProvider));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ IBinder getServiceBinder() {
|
/* package */ IBinder getServiceBinder() {
|
||||||
synchronized (lock) {
|
return checkStateNotNull(stub).asBinder();
|
||||||
return checkStateNotNull(stub).asBinder();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -679,28 +668,31 @@ public abstract class MediaSessionService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private MediaNotificationManager getMediaNotificationManager() {
|
private MediaNotificationManager getMediaNotificationManager() {
|
||||||
synchronized (lock) {
|
return getMediaNotificationManager(/* initialMediaNotificationProvider= */ null);
|
||||||
if (mediaNotificationManager == null) {
|
}
|
||||||
if (mediaNotificationProvider == null) {
|
|
||||||
checkStateNotNull(getBaseContext(), "Accessing service context before onCreate()");
|
private MediaNotificationManager getMediaNotificationManager(
|
||||||
mediaNotificationProvider =
|
@Nullable MediaNotification.Provider initialMediaNotificationProvider) {
|
||||||
new DefaultMediaNotificationProvider.Builder(getApplicationContext()).build();
|
if (mediaNotificationManager == null) {
|
||||||
}
|
if (initialMediaNotificationProvider == null) {
|
||||||
mediaNotificationManager =
|
checkStateNotNull(getBaseContext(), "Accessing service context before onCreate()");
|
||||||
new MediaNotificationManager(
|
initialMediaNotificationProvider =
|
||||||
/* mediaSessionService= */ this, mediaNotificationProvider, getActionFactory());
|
new DefaultMediaNotificationProvider.Builder(getApplicationContext()).build();
|
||||||
}
|
}
|
||||||
return mediaNotificationManager;
|
mediaNotificationManager =
|
||||||
|
new MediaNotificationManager(
|
||||||
|
/* mediaSessionService= */ this,
|
||||||
|
initialMediaNotificationProvider,
|
||||||
|
getActionFactory());
|
||||||
}
|
}
|
||||||
|
return mediaNotificationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
private DefaultActionFactory getActionFactory() {
|
private DefaultActionFactory getActionFactory() {
|
||||||
synchronized (lock) {
|
if (actionFactory == null) {
|
||||||
if (actionFactory == null) {
|
actionFactory = new DefaultActionFactory(/* service= */ this);
|
||||||
actionFactory = new DefaultActionFactory(/* service= */ this);
|
|
||||||
}
|
|
||||||
return actionFactory;
|
|
||||||
}
|
}
|
||||||
|
return actionFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -504,6 +504,36 @@ public class MediaSessionServiceTest {
|
|||||||
serviceController.destroy();
|
serviceController.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setMediaNotificationProvider_afterSetForegroundServiceTimeoutMs_usesCustomProvider()
|
||||||
|
throws TimeoutException {
|
||||||
|
Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
ExoPlayer player = new TestExoPlayerBuilder(context).build();
|
||||||
|
MediaSession session = new MediaSession.Builder(context, player).build();
|
||||||
|
ServiceController<TestService> serviceController = Robolectric.buildService(TestService.class);
|
||||||
|
TestService service = serviceController.create().get();
|
||||||
|
|
||||||
|
service.setForegroundServiceTimeoutMs(100);
|
||||||
|
service.setMediaNotificationProvider(
|
||||||
|
new DefaultMediaNotificationProvider(
|
||||||
|
service,
|
||||||
|
/* notificationIdProvider= */ mediaSession -> 2000,
|
||||||
|
DefaultMediaNotificationProvider.DEFAULT_CHANNEL_ID,
|
||||||
|
DefaultMediaNotificationProvider.DEFAULT_CHANNEL_NAME_RESOURCE_ID));
|
||||||
|
service.addSession(session);
|
||||||
|
// Start a player to trigger notification creation.
|
||||||
|
player.setMediaItem(MediaItem.fromUri("asset:///media/mp4/sample.mp4"));
|
||||||
|
player.prepare();
|
||||||
|
player.play();
|
||||||
|
runMainLooperUntil(() -> notificationManager.getActiveNotifications().length == 1);
|
||||||
|
|
||||||
|
assertThat(getStatusBarNotification(/* notificationId= */ 2000)).isNotNull();
|
||||||
|
|
||||||
|
session.release();
|
||||||
|
player.release();
|
||||||
|
serviceController.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void onStartCommand_mediaButtonEvent_pausedByMediaNotificationController()
|
public void onStartCommand_mediaButtonEvent_pausedByMediaNotificationController()
|
||||||
throws InterruptedException {
|
throws InterruptedException {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user