58
58
import androidx .media3 .common .util .Util ;
59
59
import androidx .media3 .session .MediaLibraryService .LibraryParams ;
60
60
import com .google .common .base .Objects ;
61
+ import com .google .common .collect .ImmutableList ;
62
+ import com .google .common .primitives .Longs ;
61
63
import com .google .common .util .concurrent .Futures ;
62
64
import com .google .common .util .concurrent .ListenableFuture ;
63
65
import java .util .HashMap ;
@@ -1055,13 +1057,13 @@ default ListenableFuture<SessionResult> onCustomCommand(
1055
1057
1056
1058
/**
1057
1059
* Called when a controller requested to add new {@linkplain MediaItem media items} to the
1058
- * playlist via one of the {@code Player.addMediaItem(s)} or {@code Player.setMediaItem(s)}
1059
- * methods .
1060
+ * playlist via one of the {@code Player.addMediaItem(s)} methods. Unless overriden, {@link
1061
+ * Callback#onSetMediaItems} will direct {@code Player.setMediaItem(s)} to this method as well .
1060
1062
*
1061
- * <p>This callback is also called when an app is using a legacy {@link
1062
- * MediaControllerCompat.TransportControls} to prepare or play media (for instance when browsing
1063
- * the catalogue and then selecting an item for preparation from Android Auto that is using the
1064
- * legacy Media1 library).
1063
+ * <p>In addition, unless {@link Callback#onSetMediaItems} is overridden, this callback is also
1064
+ * called when an app is using a legacy {@link MediaControllerCompat.TransportControls} to
1065
+ * prepare or play media (for instance when browsing the catalogue and then selecting an item
1066
+ * for preparation from Android Auto that is using the legacy Media1 library).
1065
1067
*
1066
1068
* <p>Note that the requested {@linkplain MediaItem media items} don't have a {@link
1067
1069
* MediaItem.LocalConfiguration} (for example, a URI) and need to be updated to make them
@@ -1074,8 +1076,8 @@ default ListenableFuture<SessionResult> onCustomCommand(
1074
1076
* the {@link MediaItem media items} have been resolved, the session will call {@link
1075
1077
* Player#setMediaItems} or {@link Player#addMediaItems} as requested.
1076
1078
*
1077
- * <p>Interoperability: This method will be called in response to the following {@link
1078
- * MediaControllerCompat} methods:
1079
+ * <p>Interoperability: This method will be called, unless {@link Callback#onSetMediaItems} is
1080
+ * overridden, in response to the following {@link MediaControllerCompat} methods:
1079
1081
*
1080
1082
* <ul>
1081
1083
* <li>{@link MediaControllerCompat.TransportControls#prepareFromUri prepareFromUri}
@@ -1103,6 +1105,156 @@ default ListenableFuture<List<MediaItem>> onAddMediaItems(
1103
1105
MediaSession mediaSession , ControllerInfo controller , List <MediaItem > mediaItems ) {
1104
1106
return Futures .immediateFailedFuture (new UnsupportedOperationException ());
1105
1107
}
1108
+
1109
+ /**
1110
+ * Called when a controller requested to set {@linkplain MediaItem media items} to the playlist
1111
+ * via one of the {@code Player.setMediaItem(s)} methods. The default implementation calls
1112
+ * {@link Callback#onAddMediaItems}. Override this method if you want to modify/set the starting
1113
+ * index/position for the {@code Player.setMediaItem(s)} methods.
1114
+ *
1115
+ * <p>This callback is also called when an app is using a legacy {@link
1116
+ * MediaControllerCompat.TransportControls} to prepare or play media (for instance when browsing
1117
+ * the catalogue and then selecting an item for preparation from Android Auto that is using the
1118
+ * legacy Media1 library).
1119
+ *
1120
+ * <p>Note that the requested {@linkplain MediaItem media items} in the
1121
+ * MediaItemsWithStartPosition don't have a {@link MediaItem.LocalConfiguration} (for example, a
1122
+ * URI) and need to be updated to make them playable by the underlying {@link Player}.
1123
+ * Typically, this implementation should be able to identify the correct item by its {@link
1124
+ * MediaItem#mediaId} and/or the {@link MediaItem#requestMetadata}.
1125
+ *
1126
+ * <p>Return a {@link ListenableFuture} with the resolved {@linkplain
1127
+ * MediaItemsWithStartPosition media items and starting index and position}. You can also return
1128
+ * the items directly by using Guava's {@link Futures#immediateFuture(Object)}. Once the {@link
1129
+ * MediaItemsWithStartPosition} has been resolved, the session will call {@link
1130
+ * Player#setMediaItems} as requested. If the resolved {@link
1131
+ * MediaItemsWithStartPosition#startIndex startIndex} is {@link
1132
+ * androidx.media3.common.C#INDEX_UNSET C.INDEX_UNSET} and {@link
1133
+ * MediaItemsWithStartPosition#startPositionMs startPositionMs} is {@link
1134
+ * androidx.media3.common.C#TIME_UNSET C.TIME_UNSET} then the session will call {@link
1135
+ * Player#setMediaItem(MediaItem, boolean)} with {@code resetPosition} set to {@code true}.
1136
+ *
1137
+ * <p>Interoperability: This method will be called in response to the following {@link
1138
+ * MediaControllerCompat} methods:
1139
+ *
1140
+ * <ul>
1141
+ * <li>{@link MediaControllerCompat.TransportControls#prepareFromUri prepareFromUri}
1142
+ * <li>{@link MediaControllerCompat.TransportControls#playFromUri playFromUri}
1143
+ * <li>{@link MediaControllerCompat.TransportControls#prepareFromMediaId prepareFromMediaId}
1144
+ * <li>{@link MediaControllerCompat.TransportControls#playFromMediaId playFromMediaId}
1145
+ * <li>{@link MediaControllerCompat.TransportControls#prepareFromSearch prepareFromSearch}
1146
+ * <li>{@link MediaControllerCompat.TransportControls#playFromSearch playFromSearch}
1147
+ * <li>{@link MediaControllerCompat.TransportControls#addQueueItem addQueueItem}
1148
+ * </ul>
1149
+ *
1150
+ * The values of {@link MediaItem#mediaId}, {@link MediaItem.RequestMetadata#mediaUri}, {@link
1151
+ * MediaItem.RequestMetadata#searchQuery} and {@link MediaItem.RequestMetadata#extras} will be
1152
+ * set to match the legacy method call. The session will call {@link Player#setMediaItems} or
1153
+ * {@link Player#addMediaItems}, followed by {@link Player#prepare()} and {@link Player#play()}
1154
+ * as appropriate once the {@link MediaItem} has been resolved.
1155
+ *
1156
+ * @param mediaSession The session for this event.
1157
+ * @param controller The controller information.
1158
+ * @param mediaItems The list of requested {@linkplain MediaItem media items}.
1159
+ * @param startIndex The start index in the {@link MediaItem} list from which to start playing.
1160
+ * If startIndex is {@link androidx.media3.common.C#INDEX_UNSET C.INDEX_UNSET} and
1161
+ * startPositionMs is {@link androidx.media3.common.C#TIME_UNSET C.TIME_UNSET} then caller
1162
+ * is requesting to set media items with default index and position.
1163
+ * @param startPositionMs The starting position in the media item from where to start playing.
1164
+ * If startIndex is {@link androidx.media3.common.C#INDEX_UNSET C.INDEX_UNSET} and
1165
+ * startPositionMs is {@link androidx.media3.common.C#TIME_UNSET C.TIME_UNSET} then caller
1166
+ * is requesting to set media items with default index and position.
1167
+ * @return A {@link ListenableFuture} with a {@link MediaItemsWithStartPosition} containing a
1168
+ * list of resolved {@linkplain MediaItem media items}, and a starting index and position
1169
+ * that are playable by the underlying {@link Player}. If returned {@link
1170
+ * MediaItemsWithStartPosition#startIndex} is {@link androidx.media3.common.C#INDEX_UNSET
1171
+ * C.INDEX_UNSET} and {@link MediaItemsWithStartPosition#startPositionMs} is {@link
1172
+ * androidx.media3.common.C#TIME_UNSET C.TIME_UNSET}, then {@linkplain
1173
+ * Player#setMediaItems(List, boolean) Player#setMediaItems(List, true)} will be called to
1174
+ * set media items with default index and position.
1175
+ */
1176
+ @ UnstableApi
1177
+ default ListenableFuture <MediaItemsWithStartPosition > onSetMediaItems (
1178
+ MediaSession mediaSession ,
1179
+ ControllerInfo controller ,
1180
+ List <MediaItem > mediaItems ,
1181
+ int startIndex ,
1182
+ long startPositionMs ) {
1183
+ return Util .transformFutureAsync (
1184
+ onAddMediaItems (mediaSession , controller , mediaItems ),
1185
+ (mediaItemList ) ->
1186
+ Futures .immediateFuture (
1187
+ new MediaItemsWithStartPosition (mediaItemList , startIndex , startPositionMs )));
1188
+ }
1189
+ }
1190
+
1191
+ /** Representation of list of media items and where to start playing */
1192
+ @ UnstableApi
1193
+ public static final class MediaItemsWithStartPosition {
1194
+ /** List of {@link MediaItem media items}. */
1195
+ public final ImmutableList <MediaItem > mediaItems ;
1196
+ /**
1197
+ * Index to start playing at in {@link MediaItem} list.
1198
+ *
1199
+ * <p>If startIndex is {@link androidx.media3.common.C#INDEX_UNSET C.INDEX_UNSET} and
1200
+ * startPositionMs is {@link androidx.media3.common.C#TIME_UNSET C.TIME_UNSET} then the
1201
+ * requested start is the default index and position. If only startIndex is {@link
1202
+ * androidx.media3.common.C#INDEX_UNSET C.INDEX_UNSET}, then the requested start is the
1203
+ * {@linkplain Player#getCurrentMediaItemIndex() current index} and {@linkplain
1204
+ * Player#getContentPosition() position}.
1205
+ */
1206
+ public final int startIndex ;
1207
+ /**
1208
+ * Position to start playing from in starting media item.
1209
+ *
1210
+ * <p>If startIndex is {@link androidx.media3.common.C#INDEX_UNSET C.INDEX_UNSET} and
1211
+ * startPositionMs is {@link androidx.media3.common.C#TIME_UNSET C.TIME_UNSET} then the
1212
+ * requested start is the default start index that takes into account whether {@link
1213
+ * Player#getShuffleModeEnabled() shuffling is enabled} and the {@linkplain
1214
+ * Timeline.Window#defaultPositionUs} default position}. If only startIndex is {@link
1215
+ * androidx.media3.common.C#INDEX_UNSET C.INDEX_UNSET}, then the requested start is the
1216
+ * {@linkplain Player#getCurrentMediaItemIndex() current index} and {@linkplain
1217
+ * Player#getContentPosition() position}.
1218
+ */
1219
+ public final long startPositionMs ;
1220
+
1221
+ /**
1222
+ * Create an instance.
1223
+ *
1224
+ * @param mediaItems List of {@link MediaItem media items}.
1225
+ * @param startIndex Index to start playing at in {@link MediaItem} list.
1226
+ * @param startPositionMs Position to start playing from in starting media item.
1227
+ */
1228
+ public MediaItemsWithStartPosition (
1229
+ List <MediaItem > mediaItems , int startIndex , long startPositionMs ) {
1230
+ this .mediaItems = ImmutableList .copyOf (mediaItems );
1231
+ this .startIndex = startIndex ;
1232
+ this .startPositionMs = startPositionMs ;
1233
+ }
1234
+
1235
+ @ Override
1236
+ public boolean equals (@ Nullable Object obj ) {
1237
+ if (this == obj ) {
1238
+ return true ;
1239
+ }
1240
+ if (!(obj instanceof MediaItemsWithStartPosition )) {
1241
+ return false ;
1242
+ }
1243
+
1244
+ MediaItemsWithStartPosition other = (MediaItemsWithStartPosition ) obj ;
1245
+
1246
+ return mediaItems .equals (other .mediaItems )
1247
+ && Util .areEqual (startIndex , other .startIndex )
1248
+ && Util .areEqual (startPositionMs , other .startPositionMs );
1249
+ }
1250
+
1251
+ @ Override
1252
+ public int hashCode () {
1253
+ int result = mediaItems .hashCode ();
1254
+ result = 31 * result + startIndex ;
1255
+ result = 31 * result + Longs .hashCode (startPositionMs );
1256
+ return result ;
1257
+ }
1106
1258
}
1107
1259
1108
1260
/**
0 commit comments