mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Add MediaSession.setSessionActivity(PendingIntent)
The session activity is already sent to the controller with the `ConnectionState` when it connects. This change adds the ability to update the activity. This allows an app to change the intent that is used to open an activity for the notification. An app is likely to want to change the session activity just before the session is released. This allows to use a different activity or more importantly the back stack of the activity for while the app is running and when used for the playback resumption notification. PiperOrigin-RevId: 530627102
This commit is contained in:
parent
1105f194ca
commit
5e4421c2ba
13 changed files with 159 additions and 7 deletions
|
|
@ -16,6 +16,7 @@
|
|||
package androidx.media3.session;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.app.PendingIntent;
|
||||
import androidx.media3.session.IMediaSession;
|
||||
|
||||
/**
|
||||
|
|
@ -46,7 +47,8 @@ oneway interface IMediaController {
|
|||
int seq, in Bundle sessionCommandsBundle, in Bundle playerCommandsBundle) = 3009;
|
||||
void onRenderedFirstFrame(int seq) = 3010;
|
||||
void onExtrasChanged(int seq, in Bundle extras) = 3011;
|
||||
// Next Id for MediaController: 3013
|
||||
void onSessionActivityChanged(int seq, in PendingIntent pendingIntent) = 3013;
|
||||
// Next Id for MediaController: 3014
|
||||
|
||||
void onChildrenChanged(
|
||||
int seq, String parentId, int itemCount, in @nullable Bundle libraryParams) = 4000;
|
||||
|
|
|
|||
|
|
@ -396,6 +396,17 @@ public class MediaController implements Player {
|
|||
* @param extras The session extras that have changed.
|
||||
*/
|
||||
default void onExtrasChanged(MediaController controller, Bundle extras) {}
|
||||
|
||||
/**
|
||||
* Called when the {@link PendingIntent} to launch the session activity {@link
|
||||
* MediaSession#setSessionActivity(PendingIntent) has been changed} on the session side.
|
||||
*
|
||||
* @param controller The controller.
|
||||
* @param sessionActivity The pending intent to launch the session activity.
|
||||
*/
|
||||
@UnstableApi
|
||||
default void onSessionActivityChanged(
|
||||
MediaController controller, PendingIntent sessionActivity) {}
|
||||
}
|
||||
|
||||
/* package */ interface ConnectionCallback {
|
||||
|
|
|
|||
|
|
@ -2595,6 +2595,16 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||
.notifyControllerListener(listener -> listener.onExtrasChanged(getInstance(), extras));
|
||||
}
|
||||
|
||||
void onSetSessionActivity(int seq, PendingIntent sessionActivity) {
|
||||
if (!isConnected()) {
|
||||
return;
|
||||
}
|
||||
this.sessionActivity = sessionActivity;
|
||||
getInstance()
|
||||
.notifyControllerListener(
|
||||
listener -> listener.onSessionActivityChanged(getInstance(), sessionActivity));
|
||||
}
|
||||
|
||||
public void onRenderedFirstFrame() {
|
||||
listeners.sendEvent(
|
||||
/* eventFlag= */ Player.EVENT_RENDERED_FIRST_FRAME, Listener::onRenderedFirstFrame);
|
||||
|
|
|
|||
|
|
@ -17,9 +17,11 @@ package androidx.media3.session;
|
|||
|
||||
import static androidx.media3.common.util.Util.postOrRun;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.text.TextUtils;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.Player.Commands;
|
||||
|
|
@ -36,7 +38,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||
private static final String TAG = "MediaControllerStub";
|
||||
|
||||
/** The version of the IMediaController interface. */
|
||||
public static final int VERSION_INT = 2;
|
||||
public static final int VERSION_INT = 3;
|
||||
|
||||
private final WeakReference<MediaControllerImplBase> controller;
|
||||
|
||||
|
|
@ -157,6 +159,13 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||
dispatchControllerTaskOnHandler(controller -> controller.onCustomCommand(seq, command, args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionActivityChanged(int seq, PendingIntent sessionActivity)
|
||||
throws RemoteException {
|
||||
dispatchControllerTaskOnHandler(
|
||||
controller -> controller.onSetSessionActivity(seq, sessionActivity));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPeriodicSessionPositionInfoChanged(int seq, Bundle sessionPositionInfoBundle) {
|
||||
SessionPositionInfo sessionPositionInfo;
|
||||
|
|
|
|||
|
|
@ -599,6 +599,17 @@ public class MediaSession {
|
|||
return impl.getSessionActivity();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the session activity that was set when {@linkplain
|
||||
* Builder#setSessionActivity(PendingIntent) building the session}.
|
||||
*
|
||||
* @param activityPendingIntent The pending intent to start the session activity.
|
||||
*/
|
||||
@UnstableApi
|
||||
public final void setSessionActivity(PendingIntent activityPendingIntent) {
|
||||
impl.setSessionActivity(activityPendingIntent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the underlying {@link Player} for this session to dispatch incoming events to.
|
||||
*
|
||||
|
|
@ -1382,6 +1393,9 @@ public class MediaSession {
|
|||
|
||||
default void setCustomLayout(int seq, List<CommandButton> layout) throws RemoteException {}
|
||||
|
||||
default void onSessionActivityChanged(int seq, PendingIntent sessionActivity)
|
||||
throws RemoteException {}
|
||||
|
||||
default void onSessionExtrasChanged(int seq, Bundle sessionExtras) throws RemoteException {}
|
||||
|
||||
default void sendCustomCommand(int seq, SessionCommand command, Bundle args)
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ import androidx.media3.common.VideoSize;
|
|||
import androidx.media3.common.text.CueGroup;
|
||||
import androidx.media3.common.util.BitmapLoader;
|
||||
import androidx.media3.common.util.Log;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.session.MediaSession.ControllerCb;
|
||||
import androidx.media3.session.MediaSession.ControllerInfo;
|
||||
|
|
@ -80,9 +81,11 @@ import com.google.common.util.concurrent.SettableFuture;
|
|||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import org.checkerframework.checker.initialization.qual.Initialized;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
/* package */ class MediaSessionImpl {
|
||||
|
||||
|
|
@ -119,18 +122,16 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
|||
private final String sessionId;
|
||||
private final SessionToken sessionToken;
|
||||
private final MediaSession instance;
|
||||
@Nullable private final PendingIntent sessionActivity;
|
||||
private final Handler applicationHandler;
|
||||
private final BitmapLoader bitmapLoader;
|
||||
private final Runnable periodicSessionPositionInfoUpdateRunnable;
|
||||
private final Handler mainHandler;
|
||||
|
||||
@Nullable private PlayerListener playerListener;
|
||||
|
||||
@Nullable private MediaSession.Listener mediaSessionListener;
|
||||
|
||||
private PlayerInfo playerInfo;
|
||||
private PlayerWrapper playerWrapper;
|
||||
private @MonotonicNonNull PendingIntent sessionActivity;
|
||||
@Nullable private PlayerListener playerListener;
|
||||
@Nullable private MediaSession.Listener mediaSessionListener;
|
||||
@Nullable private ControllerInfo controllerForCurrentRequest;
|
||||
|
||||
@GuardedBy("lock")
|
||||
|
|
@ -531,6 +532,25 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
|||
return sessionActivity;
|
||||
}
|
||||
|
||||
@UnstableApi
|
||||
protected void setSessionActivity(PendingIntent sessionActivity) {
|
||||
if (Objects.equals(this.sessionActivity, sessionActivity)) {
|
||||
return;
|
||||
}
|
||||
this.sessionActivity = sessionActivity;
|
||||
sessionLegacyStub.getSessionCompat().setSessionActivity(sessionActivity);
|
||||
ImmutableList<ControllerInfo> connectedControllers =
|
||||
sessionStub.getConnectedControllersManager().getConnectedControllers();
|
||||
for (int i = 0; i < connectedControllers.size(); i++) {
|
||||
ControllerInfo controllerInfo = connectedControllers.get(i);
|
||||
if (controllerInfo.getControllerVersion() >= 3) {
|
||||
dispatchRemoteControllerTaskWithoutReturn(
|
||||
controllerInfo,
|
||||
(controller, seq) -> controller.onSessionActivityChanged(seq, sessionActivity));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the service binder from the MediaBrowserServiceCompat. Should be only called by the thread
|
||||
* with a Looper.
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ import static androidx.media3.session.SessionCommand.COMMAND_CODE_LIBRARY_SUBSCR
|
|||
import static androidx.media3.session.SessionCommand.COMMAND_CODE_LIBRARY_UNSUBSCRIBE;
|
||||
import static androidx.media3.session.SessionCommand.COMMAND_CODE_SESSION_SET_RATING;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
|
|
@ -1846,6 +1847,12 @@ import java.util.concurrent.ExecutionException;
|
|||
iController.onSetCustomLayout(sequenceNumber, BundleableUtil.toBundleList(layout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionActivityChanged(int sequenceNumber, PendingIntent sessionActivity)
|
||||
throws RemoteException {
|
||||
iController.onSessionActivityChanged(sequenceNumber, sessionActivity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAvailableCommandsChangedFromSession(
|
||||
int sequenceNumber, SessionCommands sessionCommands, Player.Commands playerCommands)
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ interface IRemoteMediaSession {
|
|||
void setCustomLayout(String sessionId, in List<Bundle> layout);
|
||||
void setSessionExtras(String sessionId, in Bundle extras);
|
||||
void setSessionExtrasForController(String sessionId, in String controllerKey, in Bundle extras);
|
||||
void setSessionActivity(String sessionId, in PendingIntent sessionActivity);
|
||||
|
||||
// Player Methods
|
||||
void setPlayWhenReady(String sessionId, boolean playWhenReady, int reason);
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@ import static androidx.media3.test.session.common.TestUtils.TIMEOUT_MS;
|
|||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.media.MediaDescriptionCompat;
|
||||
|
|
@ -51,6 +53,7 @@ import androidx.media3.common.Timeline;
|
|||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.test.session.common.HandlerThreadTestRule;
|
||||
import androidx.media3.test.session.common.PollingCheck;
|
||||
import androidx.media3.test.session.common.SurfaceActivity;
|
||||
import androidx.media3.test.session.common.TestHandler;
|
||||
import androidx.media3.test.session.common.TestUtils;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
|
@ -980,6 +983,32 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
|
|||
assertThat(TestUtils.equals(receivedSessionExtras.get(1), sessionExtras)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setSessionActivity_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(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 broadcastCustomCommand_cnSessionEventCalled() throws Exception {
|
||||
Bundle commandCallExtras = new Bundle();
|
||||
|
|
|
|||
|
|
@ -34,7 +34,9 @@ import static com.google.common.truth.Truth.assertThat;
|
|||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
|
|
@ -63,6 +65,7 @@ import androidx.media3.common.text.CueGroup;
|
|||
import androidx.media3.session.RemoteMediaSession.RemoteMockPlayer;
|
||||
import androidx.media3.test.session.common.HandlerThreadTestRule;
|
||||
import androidx.media3.test.session.common.MainLooperTestRule;
|
||||
import androidx.media3.test.session.common.SurfaceActivity;
|
||||
import androidx.media3.test.session.common.TestUtils;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
|
@ -2511,6 +2514,35 @@ public class MediaControllerListenerTest {
|
|||
assertThat(TestUtils.equals(receivedSessionExtras.get(0), sessionExtras)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setSessionActivity_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 latch = new CountDownLatch(1);
|
||||
List<PendingIntent> receivedSessionActivities = new ArrayList<>();
|
||||
MediaController.Listener listener =
|
||||
new MediaController.Listener() {
|
||||
@Override
|
||||
public void onSessionActivityChanged(
|
||||
MediaController controller, PendingIntent sessionActivity) {
|
||||
latch.countDown();
|
||||
receivedSessionActivities.add(sessionActivity);
|
||||
}
|
||||
};
|
||||
MediaController controller =
|
||||
controllerTestRule.createController(
|
||||
remoteSession.getToken(), /* connectionHints= */ null, listener);
|
||||
assertThat(controller.getSessionActivity()).isNull();
|
||||
|
||||
remoteSession.setSessionActivity(sessionActivity);
|
||||
|
||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
assertThat(controller.getSessionActivity()).isEqualTo(sessionActivity);
|
||||
assertThat(receivedSessionActivities).containsExactly(sessionActivity);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onVideoSizeChanged() throws Exception {
|
||||
VideoSize testVideoSize =
|
||||
|
|
|
|||
|
|
@ -492,6 +492,12 @@ public class MediaSessionProviderService extends Service {
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSessionActivity(String sessionId, PendingIntent sessionActivity)
|
||||
throws RemoteException {
|
||||
runOnHandler(() -> sessionMap.get(sessionId).setSessionActivity(sessionActivity));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// MockPlayer methods
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ import static androidx.media3.test.session.common.TestUtils.SERVICE_CONNECTION_T
|
|||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
|
@ -207,6 +208,10 @@ public class RemoteMediaSession {
|
|||
binder.setSessionExtrasForController(sessionId, controllerKey, extras);
|
||||
}
|
||||
|
||||
public void setSessionActivity(PendingIntent sessionActivity) throws RemoteException {
|
||||
binder.setSessionActivity(sessionId, sessionActivity);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// RemoteMockPlayer methods
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package androidx.media3.session;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.GuardedBy;
|
||||
import androidx.annotation.Nullable;
|
||||
|
|
@ -61,6 +62,11 @@ public final class TestMediaBrowserListener implements MediaBrowser.Listener {
|
|||
delegate.onExtrasChanged(controller, extras);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionActivityChanged(MediaController controller, PendingIntent sessionActivity) {
|
||||
delegate.onSessionActivityChanged(controller, sessionActivity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAvailableSessionCommandsChanged(
|
||||
MediaController controller, SessionCommands commands) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue