mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Allow session activity to be set per controller
PiperOrigin-RevId: 644693533
(cherry picked from commit 856d394c28)
This commit is contained in:
parent
bd5f99cb09
commit
56ae85fa3c
14 changed files with 288 additions and 27 deletions
|
|
@ -1,5 +1,43 @@
|
||||||
# Release notes
|
# Release notes
|
||||||
|
|
||||||
|
### Unreleased changes
|
||||||
|
|
||||||
|
* Common Library:
|
||||||
|
* ExoPlayer:
|
||||||
|
* Transformer:
|
||||||
|
* Track Selection:
|
||||||
|
* Extractors:
|
||||||
|
* Audio:
|
||||||
|
* Video:
|
||||||
|
* Text:
|
||||||
|
* Metadata:
|
||||||
|
* Image:
|
||||||
|
* DRM:
|
||||||
|
* Effect:
|
||||||
|
* Muxers:
|
||||||
|
* IMA extension:
|
||||||
|
* Session:
|
||||||
|
* Allow the session activity to be set per controller to override the
|
||||||
|
global session activity. The session activity can be defined for a
|
||||||
|
controller at connection time by creating a `ConnectionResult` with
|
||||||
|
`AcceptedResultBuilder.setSessionActivivty(PendingIntent)`. Once
|
||||||
|
connected, the session activity can be updated with
|
||||||
|
`MediaSession.setSessionActivity(ControllerInfo, PendingIntent)`.
|
||||||
|
* UI:
|
||||||
|
* Downloads:
|
||||||
|
* OkHttp Extension:
|
||||||
|
* Cronet Extension:
|
||||||
|
* RTMP Extension:
|
||||||
|
* HLS Extension:
|
||||||
|
* Smooth Streaming Extension:
|
||||||
|
* RTSP Extension:
|
||||||
|
* Decoder Extensions (FFmpeg, VP9, AV1, etc.):
|
||||||
|
* MIDI extension:
|
||||||
|
* Leanback extension:
|
||||||
|
* Cast Extension:
|
||||||
|
* Test Utilities:
|
||||||
|
* Remove deprecated symbols:
|
||||||
|
|
||||||
## 1.4
|
## 1.4
|
||||||
|
|
||||||
### 1.4.0-beta01 (2024-06-21)
|
### 1.4.0-beta01 (2024-06-21)
|
||||||
|
|
|
||||||
|
|
@ -759,6 +759,8 @@ public class MediaSession {
|
||||||
* Builder#setSessionActivity(PendingIntent) building the session}.
|
* Builder#setSessionActivity(PendingIntent) building the session}.
|
||||||
*
|
*
|
||||||
* @param activityPendingIntent The pending intent to start the session activity.
|
* @param activityPendingIntent The pending intent to start the session activity.
|
||||||
|
* @throws IllegalArgumentException if the {@link PendingIntent} passed into this method is
|
||||||
|
* {@linkplain PendingIntent#getActivity(Context, int, Intent, int) not an activity}.
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final void setSessionActivity(PendingIntent activityPendingIntent) {
|
public final void setSessionActivity(PendingIntent activityPendingIntent) {
|
||||||
|
|
@ -768,6 +770,30 @@ public class MediaSession {
|
||||||
impl.setSessionActivity(activityPendingIntent);
|
impl.setSessionActivity(activityPendingIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends the session activity to the connected controller.
|
||||||
|
*
|
||||||
|
* <p>This call immediately returns and doesn't wait for a result from the controller.
|
||||||
|
*
|
||||||
|
* <p>Interoperability: This call has no effect when called for a {@linkplain
|
||||||
|
* ControllerInfo#LEGACY_CONTROLLER_VERSION legacy controller}. To set the session activity of the
|
||||||
|
* platform session use {@linkplain #getMediaNotificationControllerInfo() the media notification
|
||||||
|
* controller} as the target controller.
|
||||||
|
*
|
||||||
|
* @param controller The controller to send the session activity to.
|
||||||
|
* @param activityPendingIntent The pending intent to start the session activity.
|
||||||
|
* @throws IllegalArgumentException if the {@link PendingIntent} passed into this method is
|
||||||
|
* {@linkplain PendingIntent#getActivity(Context, int, Intent, int) not an activity}.
|
||||||
|
*/
|
||||||
|
@UnstableApi
|
||||||
|
public final void setSessionActivity(
|
||||||
|
ControllerInfo controller, PendingIntent activityPendingIntent) {
|
||||||
|
if (Util.SDK_INT >= 31) {
|
||||||
|
checkArgument(Api31.isActivity(activityPendingIntent));
|
||||||
|
}
|
||||||
|
impl.setSessionActivity(controller, activityPendingIntent);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
*
|
*
|
||||||
|
|
@ -1025,7 +1051,7 @@ public class MediaSession {
|
||||||
/**
|
/**
|
||||||
* Broadcasts a custom command to all connected controllers.
|
* Broadcasts a custom command to all connected controllers.
|
||||||
*
|
*
|
||||||
* <p>This is a synchronous call and doesn't wait for results from the controllers.
|
* <p>This call immediately returns and doesn't wait for a result from the controller.
|
||||||
*
|
*
|
||||||
* <p>A command is not accepted if it is not a custom command.
|
* <p>A command is not accepted if it is not a custom command.
|
||||||
*
|
*
|
||||||
|
|
@ -1061,7 +1087,7 @@ public class MediaSession {
|
||||||
* <p>The initial extras can be set {@linkplain Builder#setSessionExtras(Bundle) when building the
|
* <p>The initial extras can be set {@linkplain Builder#setSessionExtras(Bundle) when building the
|
||||||
* session}.
|
* session}.
|
||||||
*
|
*
|
||||||
* <p>This is a synchronous call and doesn't wait for results from the controllers.
|
* <p>This call immediately returns and doesn't wait for a result from the controller.
|
||||||
*
|
*
|
||||||
* @param sessionExtras The session extras.
|
* @param sessionExtras The session extras.
|
||||||
*/
|
*/
|
||||||
|
|
@ -1078,7 +1104,7 @@ public class MediaSession {
|
||||||
* ConnectionResult.AcceptedResultBuilder#setSessionExtras(Bundle) building the connection
|
* ConnectionResult.AcceptedResultBuilder#setSessionExtras(Bundle) building the connection
|
||||||
* result}.
|
* result}.
|
||||||
*
|
*
|
||||||
* <p>This is a synchronous call and doesn't wait for results from the controller.
|
* <p>This call immediately returns and doesn't wait for a result from the controller.
|
||||||
*
|
*
|
||||||
* <p>Interoperability: This call has no effect when called for a {@linkplain
|
* <p>Interoperability: This call has no effect when called for a {@linkplain
|
||||||
* ControllerInfo#LEGACY_CONTROLLER_VERSION legacy controller}.
|
* ControllerInfo#LEGACY_CONTROLLER_VERSION legacy controller}.
|
||||||
|
|
@ -1713,6 +1739,7 @@ public class MediaSession {
|
||||||
private Player.Commands availablePlayerCommands = DEFAULT_PLAYER_COMMANDS;
|
private Player.Commands availablePlayerCommands = DEFAULT_PLAYER_COMMANDS;
|
||||||
@Nullable private ImmutableList<CommandButton> customLayout;
|
@Nullable private ImmutableList<CommandButton> customLayout;
|
||||||
@Nullable private Bundle sessionExtras;
|
@Nullable private Bundle sessionExtras;
|
||||||
|
@Nullable private PendingIntent sessionActivity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance.
|
* Creates an instance.
|
||||||
|
|
@ -1788,6 +1815,18 @@ public class MediaSession {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the session activity, overriding the {@linkplain MediaSession#getSessionActivity()
|
||||||
|
* session activity of the session}.
|
||||||
|
*
|
||||||
|
* <p>The default is null to indicate that the session activity of the session should be used.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public AcceptedResultBuilder setSessionActivity(@Nullable PendingIntent sessionActivity) {
|
||||||
|
this.sessionActivity = sessionActivity;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns a new {@link ConnectionResult} instance for accepting a connection. */
|
/** Returns a new {@link ConnectionResult} instance for accepting a connection. */
|
||||||
public ConnectionResult build() {
|
public ConnectionResult build() {
|
||||||
return new ConnectionResult(
|
return new ConnectionResult(
|
||||||
|
|
@ -1795,7 +1834,8 @@ public class MediaSession {
|
||||||
availableSessionCommands,
|
availableSessionCommands,
|
||||||
availablePlayerCommands,
|
availablePlayerCommands,
|
||||||
customLayout,
|
customLayout,
|
||||||
sessionExtras);
|
sessionExtras,
|
||||||
|
sessionActivity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1826,18 +1866,23 @@ public class MediaSession {
|
||||||
/** The session extras. */
|
/** The session extras. */
|
||||||
@UnstableApi @Nullable public final Bundle sessionExtras;
|
@UnstableApi @Nullable public final Bundle sessionExtras;
|
||||||
|
|
||||||
|
/** The session activity. */
|
||||||
|
@UnstableApi @Nullable public final PendingIntent sessionActivity;
|
||||||
|
|
||||||
/** Creates a new instance with the given available session and player commands. */
|
/** Creates a new instance with the given available session and player commands. */
|
||||||
private ConnectionResult(
|
private ConnectionResult(
|
||||||
boolean accepted,
|
boolean accepted,
|
||||||
SessionCommands availableSessionCommands,
|
SessionCommands availableSessionCommands,
|
||||||
Player.Commands availablePlayerCommands,
|
Player.Commands availablePlayerCommands,
|
||||||
@Nullable ImmutableList<CommandButton> customLayout,
|
@Nullable ImmutableList<CommandButton> customLayout,
|
||||||
@Nullable Bundle sessionExtras) {
|
@Nullable Bundle sessionExtras,
|
||||||
|
@Nullable PendingIntent sessionActivity) {
|
||||||
isAccepted = accepted;
|
isAccepted = accepted;
|
||||||
this.availableSessionCommands = availableSessionCommands;
|
this.availableSessionCommands = availableSessionCommands;
|
||||||
this.availablePlayerCommands = availablePlayerCommands;
|
this.availablePlayerCommands = availablePlayerCommands;
|
||||||
this.customLayout = customLayout;
|
this.customLayout = customLayout;
|
||||||
this.sessionExtras = sessionExtras;
|
this.sessionExtras = sessionExtras;
|
||||||
|
this.sessionActivity = sessionActivity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1857,7 +1902,8 @@ public class MediaSession {
|
||||||
availableSessionCommands,
|
availableSessionCommands,
|
||||||
availablePlayerCommands,
|
availablePlayerCommands,
|
||||||
/* customLayout= */ null,
|
/* customLayout= */ null,
|
||||||
/* sessionExtras= */ null);
|
/* sessionExtras= */ null,
|
||||||
|
/* sessionActivity= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a {@link ConnectionResult} to reject a connection. */
|
/** Creates a {@link ConnectionResult} to reject a connection. */
|
||||||
|
|
@ -1867,7 +1913,8 @@ public class MediaSession {
|
||||||
SessionCommands.EMPTY,
|
SessionCommands.EMPTY,
|
||||||
Player.Commands.EMPTY,
|
Player.Commands.EMPTY,
|
||||||
/* customLayout= */ ImmutableList.of(),
|
/* customLayout= */ ImmutableList.of(),
|
||||||
/* sessionExtras= */ Bundle.EMPTY);
|
/* sessionExtras= */ Bundle.EMPTY,
|
||||||
|
/* sessionActivity= */ null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -783,19 +783,23 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
protected void setSessionActivity(PendingIntent sessionActivity) {
|
protected void setSessionActivity(PendingIntent sessionActivity) {
|
||||||
if (Objects.equals(this.sessionActivity, sessionActivity)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.sessionActivity = sessionActivity;
|
this.sessionActivity = sessionActivity;
|
||||||
sessionLegacyStub.getSessionCompat().setSessionActivity(sessionActivity);
|
|
||||||
ImmutableList<ControllerInfo> connectedControllers =
|
ImmutableList<ControllerInfo> connectedControllers =
|
||||||
sessionStub.getConnectedControllersManager().getConnectedControllers();
|
sessionStub.getConnectedControllersManager().getConnectedControllers();
|
||||||
for (int i = 0; i < connectedControllers.size(); i++) {
|
for (int i = 0; i < connectedControllers.size(); i++) {
|
||||||
ControllerInfo controllerInfo = connectedControllers.get(i);
|
setSessionActivity(connectedControllers.get(i), sessionActivity);
|
||||||
if (controllerInfo.getControllerVersion() >= 3) {
|
}
|
||||||
dispatchRemoteControllerTaskWithoutReturn(
|
}
|
||||||
controllerInfo,
|
|
||||||
(controller, seq) -> controller.onSessionActivityChanged(seq, sessionActivity));
|
@UnstableApi
|
||||||
|
protected void setSessionActivity(ControllerInfo controller, PendingIntent sessionActivity) {
|
||||||
|
if (controller.getControllerVersion() >= 3
|
||||||
|
&& sessionStub.getConnectedControllersManager().isConnected(controller)) {
|
||||||
|
dispatchRemoteControllerTaskWithoutReturn(
|
||||||
|
controller, (callback, seq) -> callback.onSessionActivityChanged(seq, sessionActivity));
|
||||||
|
if (isMediaNotificationController(controller)) {
|
||||||
|
dispatchRemoteControllerTaskToLegacyStub(
|
||||||
|
(callback, seq) -> callback.onSessionActivityChanged(seq, sessionActivity));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1117,6 +1117,11 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
||||||
sessionCompat.setPlaybackState(sessionImpl.getPlayerWrapper().createPlaybackStateCompat());
|
sessionCompat.setPlaybackState(sessionImpl.getPlayerWrapper().createPlaybackStateCompat());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSessionActivityChanged(int seq, PendingIntent sessionActivity) {
|
||||||
|
sessionCompat.setSessionActivity(sessionActivity);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(int seq, SessionError sessionError) {
|
public void onError(int seq, SessionError sessionError) {
|
||||||
PlayerWrapper playerWrapper = sessionImpl.getPlayerWrapper();
|
PlayerWrapper playerWrapper = sessionImpl.getPlayerWrapper();
|
||||||
|
|
|
||||||
|
|
@ -526,7 +526,9 @@ import java.util.concurrent.ExecutionException;
|
||||||
MediaLibraryInfo.VERSION_INT,
|
MediaLibraryInfo.VERSION_INT,
|
||||||
MediaSessionStub.VERSION_INT,
|
MediaSessionStub.VERSION_INT,
|
||||||
MediaSessionStub.this,
|
MediaSessionStub.this,
|
||||||
sessionImpl.getSessionActivity(),
|
connectionResult.sessionActivity != null
|
||||||
|
? connectionResult.sessionActivity
|
||||||
|
: sessionImpl.getSessionActivity(),
|
||||||
connectionResult.customLayout != null
|
connectionResult.customLayout != null
|
||||||
? connectionResult.customLayout
|
? connectionResult.customLayout
|
||||||
: sessionImpl.getCustomLayout(),
|
: sessionImpl.getCustomLayout(),
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,9 @@ interface IRemoteMediaController {
|
||||||
// MediaController Methods
|
// MediaController Methods
|
||||||
Bundle getConnectedSessionToken(String controllerId);
|
Bundle getConnectedSessionToken(String controllerId);
|
||||||
Bundle getSessionExtras(String controllerId);
|
Bundle getSessionExtras(String controllerId);
|
||||||
|
Bundle getCustomLayout(String controllerId);
|
||||||
|
Bundle getAvailableCommands(String controllerId);
|
||||||
|
PendingIntent getSessionActivity(String controllerId);
|
||||||
void play(String controllerId);
|
void play(String controllerId);
|
||||||
void pause(String controllerId);
|
void pause(String controllerId);
|
||||||
void setPlayWhenReady(String controllerId, boolean playWhenReady);
|
void setPlayWhenReady(String controllerId, boolean playWhenReady);
|
||||||
|
|
@ -101,8 +104,6 @@ interface IRemoteMediaController {
|
||||||
int page,
|
int page,
|
||||||
int pageSize,
|
int pageSize,
|
||||||
in Bundle libraryParams);
|
in Bundle libraryParams);
|
||||||
Bundle getCustomLayout(String controllerId);
|
|
||||||
Bundle getAvailableCommands(String controllerId);
|
|
||||||
Bundle getItem(String controllerId, String mediaId);
|
Bundle getItem(String controllerId, String mediaId);
|
||||||
Bundle search(String controllerId, String query, in Bundle libraryParams);
|
Bundle search(String controllerId, String query, in Bundle libraryParams);
|
||||||
Bundle getSearchResult(
|
Bundle getSearchResult(
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ interface IRemoteMediaSession {
|
||||||
void setSessionExtras(String sessionId, in Bundle extras);
|
void setSessionExtras(String sessionId, in Bundle extras);
|
||||||
void setSessionExtrasForController(String sessionId, in String controllerKey, in Bundle extras);
|
void setSessionExtrasForController(String sessionId, in String controllerKey, in Bundle extras);
|
||||||
void sendError(String sessionId, String controllerKey, in Bundle SessionError);
|
void sendError(String sessionId, String controllerKey, in Bundle SessionError);
|
||||||
void setSessionActivity(String sessionId, in PendingIntent sessionActivity);
|
void setSessionActivity(String sessionId, String controllerKey, in PendingIntent sessionActivity);
|
||||||
|
|
||||||
// Player Methods
|
// Player Methods
|
||||||
void setPlayWhenReady(String sessionId, boolean playWhenReady, int reason);
|
void setPlayWhenReady(String sessionId, boolean playWhenReady, int reason);
|
||||||
|
|
|
||||||
|
|
@ -1227,7 +1227,8 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setSessionActivity_changedWhenReceivedWithSetter() throws Exception {
|
public void setSessionActivity_forAllControllers_changedWhenReceivedWithSetter()
|
||||||
|
throws Exception {
|
||||||
Intent intent = new Intent(context, SurfaceActivity.class);
|
Intent intent = new Intent(context, SurfaceActivity.class);
|
||||||
PendingIntent sessionActivity =
|
PendingIntent sessionActivity =
|
||||||
PendingIntent.getActivity(
|
PendingIntent.getActivity(
|
||||||
|
|
@ -1243,7 +1244,34 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
|
||||||
controllerCompat.registerCallback(callback, handler);
|
controllerCompat.registerCallback(callback, handler);
|
||||||
assertThat(controllerCompat.getSessionActivity()).isNull();
|
assertThat(controllerCompat.getSessionActivity()).isNull();
|
||||||
|
|
||||||
session.setSessionActivity(sessionActivity);
|
session.setSessionActivity(/* controllerKey= */ null, sessionActivity);
|
||||||
|
// The legacy API has no change listener for the session activity. Changing the state to
|
||||||
|
// trigger a callback.
|
||||||
|
session.getMockPlayer().notifyPlaybackStateChanged(STATE_READY);
|
||||||
|
|
||||||
|
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||||
|
assertThat(controllerCompat.getSessionActivity()).isEqualTo(sessionActivity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setSessionActivity_setToNotificationController_changedWhenReceivedWithSetter()
|
||||||
|
throws Exception {
|
||||||
|
Intent intent = new Intent(context, SurfaceActivity.class);
|
||||||
|
PendingIntent sessionActivity =
|
||||||
|
PendingIntent.getActivity(
|
||||||
|
context, 0, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
MediaControllerCompat.Callback callback =
|
||||||
|
new MediaControllerCompat.Callback() {
|
||||||
|
@Override
|
||||||
|
public void onPlaybackStateChanged(PlaybackStateCompat state) {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
controllerCompat.registerCallback(callback, handler);
|
||||||
|
assertThat(controllerCompat.getSessionActivity()).isNull();
|
||||||
|
|
||||||
|
session.setSessionActivity(NOTIFICATION_CONTROLLER_KEY, sessionActivity);
|
||||||
// The legacy API has no change listener for the session activity. Changing the state to
|
// The legacy API has no change listener for the session activity. Changing the state to
|
||||||
// trigger a callback.
|
// trigger a callback.
|
||||||
session.getMockPlayer().notifyPlaybackStateChanged(STATE_READY);
|
session.getMockPlayer().notifyPlaybackStateChanged(STATE_READY);
|
||||||
|
|
|
||||||
|
|
@ -2535,13 +2535,69 @@ public class MediaControllerListenerTest {
|
||||||
remoteSession.getToken(), /* connectionHints= */ null, listener);
|
remoteSession.getToken(), /* connectionHints= */ null, listener);
|
||||||
assertThat(controller.getSessionActivity()).isNull();
|
assertThat(controller.getSessionActivity()).isNull();
|
||||||
|
|
||||||
remoteSession.setSessionActivity(sessionActivity);
|
remoteSession.setSessionActivity(/* controllerKey= */ null, sessionActivity);
|
||||||
|
|
||||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||||
assertThat(controller.getSessionActivity()).isEqualTo(sessionActivity);
|
assertThat(controller.getSessionActivity()).isEqualTo(sessionActivity);
|
||||||
assertThat(receivedSessionActivities).containsExactly(sessionActivity);
|
assertThat(receivedSessionActivities).containsExactly(sessionActivity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setSessionActivity_forSpecificController_onSessionActivityChangedCalled()
|
||||||
|
throws Exception {
|
||||||
|
Intent intent = new Intent(context, SurfaceActivity.class);
|
||||||
|
PendingIntent sessionActivity =
|
||||||
|
PendingIntent.getActivity(
|
||||||
|
context, 0, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
CountDownLatch latch1 = new CountDownLatch(1);
|
||||||
|
List<PendingIntent> receivedSessionActivities1 = new ArrayList<>();
|
||||||
|
MediaController.Listener listener1 =
|
||||||
|
new MediaController.Listener() {
|
||||||
|
@Override
|
||||||
|
public void onSessionActivityChanged(
|
||||||
|
MediaController controller, PendingIntent sessionActivity) {
|
||||||
|
receivedSessionActivities1.add(sessionActivity);
|
||||||
|
latch1.countDown();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Bundle connectionHints1 = new Bundle();
|
||||||
|
connectionHints1.putString(KEY_CONTROLLER, "ctrl-1");
|
||||||
|
MediaController controller1 =
|
||||||
|
controllerTestRule.createController(remoteSession.getToken(), connectionHints1, listener1);
|
||||||
|
List<PendingIntent> receivedSessionActivities2 = new ArrayList<>();
|
||||||
|
CountDownLatch latch2 = new CountDownLatch(1);
|
||||||
|
MediaController.Listener listener2 =
|
||||||
|
new MediaController.Listener() {
|
||||||
|
@Override
|
||||||
|
public void onSessionActivityChanged(
|
||||||
|
MediaController controller, PendingIntent sessionActivity) {
|
||||||
|
receivedSessionActivities2.add(sessionActivity);
|
||||||
|
latch2.countDown();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Bundle connectionHints2 = new Bundle();
|
||||||
|
connectionHints2.putString(KEY_CONTROLLER, "ctrl-2");
|
||||||
|
MediaController controller2 =
|
||||||
|
controllerTestRule.createController(remoteSession.getToken(), connectionHints2, listener2);
|
||||||
|
assertThat(controller1.getSessionActivity()).isNull();
|
||||||
|
assertThat(controller2.getSessionActivity()).isNull();
|
||||||
|
|
||||||
|
remoteSession.setSessionActivity(/* controllerKey= */ "ctrl-1", sessionActivity);
|
||||||
|
|
||||||
|
assertThat(latch1.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||||
|
assertThat(controller1.getSessionActivity()).isEqualTo(sessionActivity);
|
||||||
|
assertThat(controller2.getSessionActivity()).isNull();
|
||||||
|
assertThat(receivedSessionActivities1).containsExactly(sessionActivity);
|
||||||
|
assertThat(receivedSessionActivities2).isEmpty();
|
||||||
|
|
||||||
|
remoteSession.setSessionActivity(/* controllerKey= */ "ctrl-2", sessionActivity);
|
||||||
|
|
||||||
|
assertThat(latch2.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||||
|
assertThat(controller2.getSessionActivity()).isEqualTo(sessionActivity);
|
||||||
|
assertThat(receivedSessionActivities1).containsExactly(sessionActivity);
|
||||||
|
assertThat(receivedSessionActivities2).containsExactly(sessionActivity);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void onVideoSizeChanged() throws Exception {
|
public void onVideoSizeChanged() throws Exception {
|
||||||
VideoSize defaultVideoSize = MediaTestUtils.createDefaultVideoSize();
|
VideoSize defaultVideoSize = MediaTestUtils.createDefaultVideoSize();
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,9 @@ import static com.google.common.truth.Truth.assertThat;
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
@ -45,6 +47,7 @@ import androidx.media3.session.MediaSession.ConnectionResult.AcceptedResultBuild
|
||||||
import androidx.media3.session.MediaSession.ControllerInfo;
|
import androidx.media3.session.MediaSession.ControllerInfo;
|
||||||
import androidx.media3.test.session.common.HandlerThreadTestRule;
|
import androidx.media3.test.session.common.HandlerThreadTestRule;
|
||||||
import androidx.media3.test.session.common.MainLooperTestRule;
|
import androidx.media3.test.session.common.MainLooperTestRule;
|
||||||
|
import androidx.media3.test.session.common.SurfaceActivity;
|
||||||
import androidx.media3.test.session.common.TestHandler;
|
import androidx.media3.test.session.common.TestHandler;
|
||||||
import androidx.media3.test.session.common.TestUtils;
|
import androidx.media3.test.session.common.TestUtils;
|
||||||
import androidx.media3.test.utils.TestExoPlayerBuilder;
|
import androidx.media3.test.utils.TestExoPlayerBuilder;
|
||||||
|
|
@ -293,6 +296,49 @@ public class MediaSessionCallbackTest {
|
||||||
assertThat(remoteController.getSessionExtras().getString("origin")).isEqualTo("controller");
|
assertThat(remoteController.getSessionExtras().getString("origin")).isEqualTo("controller");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onConnect_connectionResultSessionActivitySet_usesControllerSpecificSessionActivity()
|
||||||
|
throws Exception {
|
||||||
|
Intent intent = new Intent(context, SurfaceActivity.class);
|
||||||
|
PendingIntent controllerSpecificSessionActivity =
|
||||||
|
PendingIntent.getActivity(
|
||||||
|
context, 0, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
MediaSession.Callback callback =
|
||||||
|
new MediaSession.Callback() {
|
||||||
|
@Override
|
||||||
|
public MediaSession.ConnectionResult onConnect(
|
||||||
|
MediaSession session, ControllerInfo controller) {
|
||||||
|
AcceptedResultBuilder connectionResult =
|
||||||
|
new AcceptedResultBuilder(session)
|
||||||
|
.setAvailablePlayerCommands(Player.Commands.EMPTY);
|
||||||
|
if (controller.getConnectionHints().getBoolean("want_session_activity", false)) {
|
||||||
|
connectionResult.setSessionActivity(controllerSpecificSessionActivity);
|
||||||
|
}
|
||||||
|
return connectionResult.build();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
MediaSession session =
|
||||||
|
sessionTestRule.ensureReleaseAfterTest(
|
||||||
|
new MediaSession.Builder(context, player)
|
||||||
|
.setCallback(callback)
|
||||||
|
.setId(
|
||||||
|
"onConnect_connectionResultSessionActivitySet_usesControllerSpecificSessionActivity")
|
||||||
|
.build());
|
||||||
|
Bundle connectionHints = new Bundle();
|
||||||
|
connectionHints.putBoolean("want_session_activity", true);
|
||||||
|
|
||||||
|
RemoteMediaController remoteControllerWithSpecificSessionActivity =
|
||||||
|
remoteControllerTestRule.createRemoteController(
|
||||||
|
session.getToken(), /* waitForConnection= */ true, connectionHints);
|
||||||
|
RemoteMediaController remoteControllerWithoutSpecificSessionActivity =
|
||||||
|
remoteControllerTestRule.createRemoteController(
|
||||||
|
session.getToken(), /* waitForConnection= */ true, /* connectionHints= */ Bundle.EMPTY);
|
||||||
|
|
||||||
|
assertThat(remoteControllerWithSpecificSessionActivity.getSessionActivity())
|
||||||
|
.isEqualTo(controllerSpecificSessionActivity);
|
||||||
|
assertThat(remoteControllerWithoutSpecificSessionActivity.getSessionActivity()).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void onConnect_connectionResultDefault_emptySessionExtras() throws Exception {
|
public void onConnect_connectionResultDefault_emptySessionExtras() throws Exception {
|
||||||
MediaSession session =
|
MediaSession session =
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import static androidx.media3.test.session.common.CommonConstants.KEY_COMMAND_BU
|
||||||
import static androidx.media3.test.session.common.TestUtils.SERVICE_CONNECTION_TIMEOUT_MS;
|
import static androidx.media3.test.session.common.TestUtils.SERVICE_CONNECTION_TIMEOUT_MS;
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
|
@ -854,6 +855,12 @@ public class MediaControllerProviderService extends Service {
|
||||||
return runOnHandler(controller::getAvailableCommands).toBundle();
|
return runOnHandler(controller::getAvailableCommands).toBundle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingIntent getSessionActivity(String controllerId) throws RemoteException {
|
||||||
|
MediaController controller = mediaControllerMap.get(controllerId);
|
||||||
|
return runOnHandler(controller::getSessionActivity);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Bundle getItem(String controllerId, String mediaId) throws RemoteException {
|
public Bundle getItem(String controllerId, String mediaId) throws RemoteException {
|
||||||
MediaBrowser browser = (MediaBrowser) mediaControllerMap.get(controllerId);
|
MediaBrowser browser = (MediaBrowser) mediaControllerMap.get(controllerId);
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,7 @@ import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -607,9 +608,29 @@ public class MediaSessionProviderService extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setSessionActivity(String sessionId, PendingIntent sessionActivity)
|
public void setSessionActivity(
|
||||||
|
String sessionId, @Nullable String controllerKey, PendingIntent sessionActivity)
|
||||||
throws RemoteException {
|
throws RemoteException {
|
||||||
runOnHandler(() -> sessionMap.get(sessionId).setSessionActivity(sessionActivity));
|
MediaSession mediaSession = sessionMap.get(sessionId);
|
||||||
|
if (mediaSession == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (controllerKey == null) {
|
||||||
|
// Set to all controllers by using the global session method.
|
||||||
|
runOnHandler(() -> mediaSession.setSessionActivity(sessionActivity));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<ControllerInfo> connectedControllers = mediaSession.getConnectedControllers();
|
||||||
|
for (int i = 0; i < connectedControllers.size(); i++) {
|
||||||
|
ControllerInfo controllerInfo = connectedControllers.get(i);
|
||||||
|
@Nullable
|
||||||
|
String connectedControllerKey =
|
||||||
|
controllerInfo.getConnectionHints().getString(KEY_CONTROLLER);
|
||||||
|
if (Objects.equals(controllerKey, connectedControllerKey)) {
|
||||||
|
// Set to controller for that the test case has given the provided controllerKey.
|
||||||
|
runOnHandler(() -> mediaSession.setSessionActivity(controllerInfo, sessionActivity));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import static androidx.media3.test.session.common.TestUtils.SERVICE_CONNECTION_T
|
||||||
import static com.google.common.truth.Truth.assertWithMessage;
|
import static com.google.common.truth.Truth.assertWithMessage;
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
|
@ -393,6 +394,10 @@ public class RemoteMediaController {
|
||||||
return Player.Commands.fromBundle(commandsBundle);
|
return Player.Commands.fromBundle(commandsBundle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PendingIntent getSessionActivity() throws RemoteException {
|
||||||
|
return binder.getSessionActivity(controllerId);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Non-public methods
|
// Non-public methods
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
||||||
|
|
@ -209,8 +209,9 @@ public class RemoteMediaSession {
|
||||||
binder.setSessionExtrasForController(sessionId, controllerKey, extras);
|
binder.setSessionExtrasForController(sessionId, controllerKey, extras);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSessionActivity(PendingIntent sessionActivity) throws RemoteException {
|
public void setSessionActivity(String controllerKey, PendingIntent sessionActivity)
|
||||||
binder.setSessionActivity(sessionId, sessionActivity);
|
throws RemoteException {
|
||||||
|
binder.setSessionActivity(sessionId, controllerKey, sessionActivity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendError(@Nullable String controllerKey, SessionError sessionError)
|
public void sendError(@Nullable String controllerKey, SessionError sessionError)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue