diff --git a/demos/main/src/main/java/androidx/media3/demo/main/DownloadTracker.java b/demos/main/src/main/java/androidx/media3/demo/main/DownloadTracker.java index a27991519c..4ad82652b4 100644 --- a/demos/main/src/main/java/androidx/media3/demo/main/DownloadTracker.java +++ b/demos/main/src/main/java/androidx/media3/demo/main/DownloadTracker.java @@ -31,6 +31,8 @@ import androidx.media3.common.Format; import androidx.media3.common.MediaItem; import androidx.media3.common.TrackGroup; import androidx.media3.common.TrackGroupArray; +import androidx.media3.common.TrackSelectionParameters; +import androidx.media3.common.TracksInfo; import androidx.media3.common.util.Log; import androidx.media3.common.util.Util; import androidx.media3.datasource.HttpDataSource; @@ -46,7 +48,6 @@ import androidx.media3.exoplayer.offline.DownloadIndex; import androidx.media3.exoplayer.offline.DownloadManager; import androidx.media3.exoplayer.offline.DownloadRequest; import androidx.media3.exoplayer.offline.DownloadService; -import androidx.media3.exoplayer.trackselection.DefaultTrackSelector; import androidx.media3.exoplayer.trackselection.MappingTrackSelector.MappedTrackInfo; import java.io.IOException; import java.util.HashMap; @@ -69,7 +70,6 @@ public class DownloadTracker { private final CopyOnWriteArraySet listeners; private final HashMap downloads; private final DownloadIndex downloadIndex; - private final DefaultTrackSelector.Parameters trackSelectorParameters; @Nullable private StartDownloadDialogHelper startDownloadDialogHelper; @@ -82,7 +82,6 @@ public class DownloadTracker { listeners = new CopyOnWriteArraySet<>(); downloads = new HashMap<>(); downloadIndex = downloadManager.getDownloadIndex(); - trackSelectorParameters = DownloadHelper.getDefaultTrackSelectorParameters(context); downloadManager.addListener(new DownloadManagerListener()); loadDownloads(); } @@ -159,7 +158,7 @@ public class DownloadTracker { private final class StartDownloadDialogHelper implements DownloadHelper.Callback, - DialogInterface.OnClickListener, + TrackSelectionDialog.TrackSelectionListener, DialogInterface.OnDismissListener { private final FragmentManager fragmentManager; @@ -167,7 +166,6 @@ public class DownloadTracker { private final MediaItem mediaItem; private TrackSelectionDialog trackSelectionDialog; - private MappedTrackInfo mappedTrackInfo; private WidevineOfflineLicenseFetchTask widevineOfflineLicenseFetchTask; @Nullable private byte[] keySetId; @@ -237,21 +235,13 @@ public class DownloadTracker { Log.e(TAG, logMessage, e); } - // DialogInterface.OnClickListener implementation. + // TrackSelectionListener implementation. @Override - public void onClick(DialogInterface dialog, int which) { + public void onTracksSelected(TrackSelectionParameters trackSelectionParameters) { for (int periodIndex = 0; periodIndex < downloadHelper.getPeriodCount(); periodIndex++) { downloadHelper.clearTrackSelections(periodIndex); - for (int i = 0; i < mappedTrackInfo.getRendererCount(); i++) { - if (!trackSelectionDialog.getIsDisabled(/* rendererIndex= */ i)) { - downloadHelper.addTrackSelectionForSingleRenderer( - periodIndex, - /* rendererIndex= */ i, - trackSelectorParameters, - trackSelectionDialog.getOverrides(/* rendererIndex= */ i)); - } - } + downloadHelper.addTrackSelection(periodIndex, trackSelectionParameters); } DownloadRequest downloadRequest = buildDownloadRequest(); if (downloadRequest.streamKeys.isEmpty()) { @@ -316,21 +306,21 @@ public class DownloadTracker { return; } - mappedTrackInfo = downloadHelper.getMappedTrackInfo(/* periodIndex= */ 0); - if (!TrackSelectionDialog.willHaveContent(mappedTrackInfo)) { + TracksInfo tracksInfo = downloadHelper.getTracksInfo(/* periodIndex= */ 0); + if (!TrackSelectionDialog.willHaveContent(tracksInfo)) { Log.d(TAG, "No dialog content. Downloading entire stream."); startDownload(); downloadHelper.release(); return; } trackSelectionDialog = - TrackSelectionDialog.createForMappedTrackInfoAndParameters( + TrackSelectionDialog.createForTracksInfoAndParameters( /* titleId= */ R.string.exo_download_description, - mappedTrackInfo, - trackSelectorParameters, + tracksInfo, + DownloadHelper.getDefaultTrackSelectorParameters(context), /* allowAdaptiveSelections= */ false, /* allowMultipleOverrides= */ true, - /* onClickListener= */ this, + /* onTracksSelectedListener= */ this, /* onDismissListener= */ this); trackSelectionDialog.show(fragmentManager, /* tag= */ null); } diff --git a/demos/main/src/main/java/androidx/media3/demo/main/PlayerActivity.java b/demos/main/src/main/java/androidx/media3/demo/main/PlayerActivity.java index 365bf5422c..d158059a9f 100644 --- a/demos/main/src/main/java/androidx/media3/demo/main/PlayerActivity.java +++ b/demos/main/src/main/java/androidx/media3/demo/main/PlayerActivity.java @@ -34,6 +34,7 @@ import androidx.media3.common.ErrorMessageProvider; import androidx.media3.common.MediaItem; import androidx.media3.common.PlaybackException; import androidx.media3.common.Player; +import androidx.media3.common.TrackSelectionParameters; import androidx.media3.common.TracksInfo; import androidx.media3.common.util.Util; import androidx.media3.datasource.DataSource; @@ -48,7 +49,6 @@ import androidx.media3.exoplayer.offline.DownloadRequest; import androidx.media3.exoplayer.source.DefaultMediaSourceFactory; import androidx.media3.exoplayer.source.MediaSource; import androidx.media3.exoplayer.source.ads.AdsLoader; -import androidx.media3.exoplayer.trackselection.DefaultTrackSelector; import androidx.media3.exoplayer.util.DebugTextViewHelper; import androidx.media3.exoplayer.util.EventLogger; import androidx.media3.ui.PlayerControlView; @@ -79,8 +79,7 @@ public class PlayerActivity extends AppCompatActivity private Button selectTracksButton; private DataSource.Factory dataSourceFactory; private List mediaItems; - private DefaultTrackSelector trackSelector; - private DefaultTrackSelector.Parameters trackSelectionParameters; + private TrackSelectionParameters trackSelectionParameters; private DebugTextViewHelper debugViewHelper; private TracksInfo lastSeenTracksInfo; private boolean startAutoPlay; @@ -113,9 +112,8 @@ public class PlayerActivity extends AppCompatActivity playerView.requestFocus(); if (savedInstanceState != null) { - // Restore as DefaultTrackSelector.Parameters in case ExoPlayer specific parameters were set. trackSelectionParameters = - DefaultTrackSelector.Parameters.CREATOR.fromBundle( + TrackSelectionParameters.CREATOR.fromBundle( savedInstanceState.getBundle(KEY_TRACK_SELECTION_PARAMETERS)); startAutoPlay = savedInstanceState.getBoolean(KEY_AUTO_PLAY); startItemIndex = savedInstanceState.getInt(KEY_ITEM_INDEX); @@ -127,8 +125,7 @@ public class PlayerActivity extends AppCompatActivity adsLoaderStateBundle); } } else { - trackSelectionParameters = - new DefaultTrackSelector.ParametersBuilder(/* context= */ this).build(); + trackSelectionParameters = new TrackSelectionParameters.Builder(/* context= */ this).build(); clearStartPosition(); } } @@ -237,11 +234,11 @@ public class PlayerActivity extends AppCompatActivity public void onClick(View view) { if (view == selectTracksButton && !isShowingTrackSelectionDialog - && TrackSelectionDialog.willHaveContent(trackSelector)) { + && TrackSelectionDialog.willHaveContent(player)) { isShowingTrackSelectionDialog = true; TrackSelectionDialog trackSelectionDialog = - TrackSelectionDialog.createForTrackSelector( - trackSelector, + TrackSelectionDialog.createForPlayer( + player, /* onDismissListener= */ dismissedDialog -> isShowingTrackSelectionDialog = false); trackSelectionDialog.show(getSupportFragmentManager(), /* tag= */ null); } @@ -277,13 +274,11 @@ public class PlayerActivity extends AppCompatActivity RenderersFactory renderersFactory = DemoUtil.buildRenderersFactory(/* context= */ this, preferExtensionDecoders); - trackSelector = new DefaultTrackSelector(/* context= */ this); lastSeenTracksInfo = TracksInfo.EMPTY; player = new ExoPlayer.Builder(/* context= */ this) .setRenderersFactory(renderersFactory) .setMediaSourceFactory(createMediaSourceFactory()) - .setTrackSelector(trackSelector) .build(); player.setTrackSelectionParameters(trackSelectionParameters); player.addListener(new PlayerEventListener()); @@ -400,10 +395,7 @@ public class PlayerActivity extends AppCompatActivity private void updateTrackSelectorParameters() { if (player != null) { - // Until the demo app is fully migrated to TrackSelectionParameters, rely on ExoPlayer to use - // DefaultTrackSelector by default. - trackSelectionParameters = - (DefaultTrackSelector.Parameters) player.getTrackSelectionParameters(); + trackSelectionParameters = player.getTrackSelectionParameters(); } } @@ -424,8 +416,7 @@ public class PlayerActivity extends AppCompatActivity // User controls private void updateButtonVisibility() { - selectTracksButton.setEnabled( - player != null && TrackSelectionDialog.willHaveContent(trackSelector)); + selectTracksButton.setEnabled(player != null && TrackSelectionDialog.willHaveContent(player)); } private void showControls() { diff --git a/demos/main/src/main/java/androidx/media3/demo/main/TrackSelectionDialog.java b/demos/main/src/main/java/androidx/media3/demo/main/TrackSelectionDialog.java index d4df2a1745..8dbb003104 100644 --- a/demos/main/src/main/java/androidx/media3/demo/main/TrackSelectionDialog.java +++ b/demos/main/src/main/java/androidx/media3/demo/main/TrackSelectionDialog.java @@ -31,21 +31,42 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentPagerAdapter; import androidx.media3.common.C; -import androidx.media3.common.TrackGroupArray; -import androidx.media3.common.util.Assertions; -import androidx.media3.exoplayer.trackselection.DefaultTrackSelector; -import androidx.media3.exoplayer.trackselection.DefaultTrackSelector.SelectionOverride; -import androidx.media3.exoplayer.trackselection.MappingTrackSelector.MappedTrackInfo; +import androidx.media3.common.Player; +import androidx.media3.common.TrackGroup; +import androidx.media3.common.TrackSelectionOverride; +import androidx.media3.common.TrackSelectionParameters; +import androidx.media3.common.TracksInfo; +import androidx.media3.common.TracksInfo.TrackGroupInfo; import androidx.media3.ui.TrackSelectionView; import androidx.viewpager.widget.ViewPager; import com.google.android.material.tabs.TabLayout; +import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** Dialog to select tracks. */ public final class TrackSelectionDialog extends DialogFragment { + /** Called when tracks are selected. */ + public interface TrackSelectionListener { + + /** + * Called when tracks are selected. + * + * @param trackSelectionParameters A {@link TrackSelectionParameters} representing the selected + * tracks. Any manual selections are defined by {@link + * TrackSelectionParameters#disabledTrackTypes} and {@link + * TrackSelectionParameters#overrides}. + */ + void onTracksSelected(TrackSelectionParameters trackSelectionParameters); + } + + public static final ImmutableList SUPPORTED_TRACK_TYPES = + ImmutableList.of(C.TRACK_TYPE_VIDEO, C.TRACK_TYPE_AUDIO, C.TRACK_TYPE_TEXT); + private final SparseArray tabFragments; private final ArrayList tabTrackTypes; @@ -55,20 +76,19 @@ public final class TrackSelectionDialog extends DialogFragment { /** * Returns whether a track selection dialog will have content to display if initialized with the - * specified {@link DefaultTrackSelector} in its current state. + * specified {@link Player}. */ - public static boolean willHaveContent(DefaultTrackSelector trackSelector) { - MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo(); - return mappedTrackInfo != null && willHaveContent(mappedTrackInfo); + public static boolean willHaveContent(Player player) { + return willHaveContent(player.getCurrentTracksInfo()); } /** * Returns whether a track selection dialog will have content to display if initialized with the - * specified {@link MappedTrackInfo}. + * specified {@link TracksInfo}. */ - public static boolean willHaveContent(MappedTrackInfo mappedTrackInfo) { - for (int i = 0; i < mappedTrackInfo.getRendererCount(); i++) { - if (showTabForRenderer(mappedTrackInfo, i)) { + public static boolean willHaveContent(TracksInfo tracksInfo) { + for (TrackGroupInfo trackGroupInfo : tracksInfo.getTrackGroupInfos()) { + if (SUPPORTED_TRACK_TYPES.contains(trackGroupInfo.getTrackType())) { return true; } } @@ -76,78 +96,67 @@ public final class TrackSelectionDialog extends DialogFragment { } /** - * Creates a dialog for a given {@link DefaultTrackSelector}, whose parameters will be - * automatically updated when tracks are selected. + * Creates a dialog for a given {@link Player}, whose parameters will be automatically updated + * when tracks are selected. * - * @param trackSelector The {@link DefaultTrackSelector}. + * @param player The {@link Player}. * @param onDismissListener A {@link DialogInterface.OnDismissListener} to call when the dialog is * dismissed. */ - public static TrackSelectionDialog createForTrackSelector( - DefaultTrackSelector trackSelector, DialogInterface.OnDismissListener onDismissListener) { - MappedTrackInfo mappedTrackInfo = - Assertions.checkNotNull(trackSelector.getCurrentMappedTrackInfo()); - TrackSelectionDialog trackSelectionDialog = new TrackSelectionDialog(); - DefaultTrackSelector.Parameters parameters = trackSelector.getParameters(); - trackSelectionDialog.init( - /* titleId= */ R.string.track_selection_title, - mappedTrackInfo, - /* initialParameters = */ parameters, + public static TrackSelectionDialog createForPlayer( + Player player, DialogInterface.OnDismissListener onDismissListener) { + return createForTracksInfoAndParameters( + R.string.track_selection_title, + player.getCurrentTracksInfo(), + player.getTrackSelectionParameters(), /* allowAdaptiveSelections= */ true, /* allowMultipleOverrides= */ false, - /* onClickListener= */ (dialog, which) -> { - DefaultTrackSelector.ParametersBuilder builder = parameters.buildUpon(); - for (int i = 0; i < mappedTrackInfo.getRendererCount(); i++) { - builder - .clearSelectionOverrides(/* rendererIndex= */ i) - .setRendererDisabled( - /* rendererIndex= */ i, - trackSelectionDialog.getIsDisabled(/* rendererIndex= */ i)); - List overrides = - trackSelectionDialog.getOverrides(/* rendererIndex= */ i); - if (!overrides.isEmpty()) { - builder.setSelectionOverride( - /* rendererIndex= */ i, - mappedTrackInfo.getTrackGroups(/* rendererIndex= */ i), - overrides.get(0)); - } - } - trackSelector.setParameters(builder); - }, + player::setTrackSelectionParameters, onDismissListener); - return trackSelectionDialog; } /** - * Creates a dialog for given {@link MappedTrackInfo} and {@link DefaultTrackSelector.Parameters}. + * Creates a dialog for given {@link TracksInfo} and {@link TrackSelectionParameters}. * * @param titleId The resource id of the dialog title. - * @param mappedTrackInfo The {@link MappedTrackInfo} to display. - * @param initialParameters The {@link DefaultTrackSelector.Parameters} describing the initial - * track selection. + * @param tracksInfo The {@link TracksInfo} describing the tracks to display. + * @param trackSelectionParameters The initial {@link TrackSelectionParameters}. * @param allowAdaptiveSelections Whether adaptive selections (consisting of more than one track) * can be made. * @param allowMultipleOverrides Whether tracks from multiple track groups can be selected. - * @param onClickListener {@link DialogInterface.OnClickListener} called when tracks are selected. + * @param trackSelectionListener Called when tracks are selected. * @param onDismissListener {@link DialogInterface.OnDismissListener} called when the dialog is * dismissed. */ - public static TrackSelectionDialog createForMappedTrackInfoAndParameters( + public static TrackSelectionDialog createForTracksInfoAndParameters( int titleId, - MappedTrackInfo mappedTrackInfo, - DefaultTrackSelector.Parameters initialParameters, + TracksInfo tracksInfo, + TrackSelectionParameters trackSelectionParameters, boolean allowAdaptiveSelections, boolean allowMultipleOverrides, - DialogInterface.OnClickListener onClickListener, + TrackSelectionListener trackSelectionListener, DialogInterface.OnDismissListener onDismissListener) { TrackSelectionDialog trackSelectionDialog = new TrackSelectionDialog(); trackSelectionDialog.init( + tracksInfo, + trackSelectionParameters, titleId, - mappedTrackInfo, - initialParameters, allowAdaptiveSelections, allowMultipleOverrides, - onClickListener, + /* onClickListener= */ (dialog, which) -> { + TrackSelectionParameters.Builder builder = trackSelectionParameters.buildUpon(); + for (int i = 0; i < SUPPORTED_TRACK_TYPES.size(); i++) { + int trackType = SUPPORTED_TRACK_TYPES.get(i); + builder.setTrackTypeDisabled(trackType, trackSelectionDialog.getIsDisabled(trackType)); + builder.clearOverridesOfType(trackType); + Map overrides = + trackSelectionDialog.getOverrides(trackType); + for (TrackSelectionOverride override : overrides.values()) { + builder.addOverride(override); + } + } + trackSelectionListener.onTracksSelected(builder.build()); + }, onDismissListener); return trackSelectionDialog; } @@ -160,9 +169,9 @@ public final class TrackSelectionDialog extends DialogFragment { } private void init( + TracksInfo tracksInfo, + TrackSelectionParameters trackSelectionParameters, int titleId, - MappedTrackInfo mappedTrackInfo, - DefaultTrackSelector.Parameters initialParameters, boolean allowAdaptiveSelections, boolean allowMultipleOverrides, DialogInterface.OnClickListener onClickListener, @@ -170,45 +179,49 @@ public final class TrackSelectionDialog extends DialogFragment { this.titleId = titleId; this.onClickListener = onClickListener; this.onDismissListener = onDismissListener; - for (int i = 0; i < mappedTrackInfo.getRendererCount(); i++) { - if (showTabForRenderer(mappedTrackInfo, i)) { - int trackType = mappedTrackInfo.getRendererType(/* rendererIndex= */ i); - TrackGroupArray trackGroupArray = mappedTrackInfo.getTrackGroups(i); + + for (int i = 0; i < SUPPORTED_TRACK_TYPES.size(); i++) { + @C.TrackType int trackType = SUPPORTED_TRACK_TYPES.get(i); + ArrayList trackGroupInfos = new ArrayList<>(); + for (TrackGroupInfo trackGroupInfo : tracksInfo.getTrackGroupInfos()) { + if (trackGroupInfo.getTrackType() == trackType) { + trackGroupInfos.add(trackGroupInfo); + } + } + if (!trackGroupInfos.isEmpty()) { TrackSelectionViewFragment tabFragment = new TrackSelectionViewFragment(); tabFragment.init( - mappedTrackInfo, - /* rendererIndex= */ i, - initialParameters.getRendererDisabled(/* rendererIndex= */ i), - initialParameters.getSelectionOverride(/* rendererIndex= */ i, trackGroupArray), + trackGroupInfos, + trackSelectionParameters.disabledTrackTypes.contains(trackType), + trackSelectionParameters.overrides, allowAdaptiveSelections, allowMultipleOverrides); - tabFragments.put(i, tabFragment); + tabFragments.put(trackType, tabFragment); tabTrackTypes.add(trackType); } } } /** - * Returns whether a renderer is disabled. + * Returns whether the disabled option is selected for the specified track type. * - * @param rendererIndex Renderer index. - * @return Whether the renderer is disabled. + * @param trackType The track type. + * @return Whether the disabled option is selected for the track type. */ - public boolean getIsDisabled(int rendererIndex) { - TrackSelectionViewFragment rendererView = tabFragments.get(rendererIndex); - return rendererView != null && rendererView.isDisabled; + public boolean getIsDisabled(int trackType) { + TrackSelectionViewFragment trackView = tabFragments.get(trackType); + return trackView != null && trackView.isDisabled; } /** - * Returns the list of selected track selection overrides for the specified renderer. There will - * be at most one override for each track group. + * Returns the selected track overrides for the specified track type. * - * @param rendererIndex Renderer index. - * @return The list of track selection overrides for this renderer. + * @param trackType The track type. + * @return The track overrides for the track type. */ - public List getOverrides(int rendererIndex) { - TrackSelectionViewFragment rendererView = tabFragments.get(rendererIndex); - return rendererView == null ? Collections.emptyList() : rendererView.overrides; + public Map getOverrides(int trackType) { + TrackSelectionViewFragment trackView = tabFragments.get(trackType); + return trackView == null ? Collections.emptyMap() : trackView.overrides; } @Override @@ -248,27 +261,7 @@ public final class TrackSelectionDialog extends DialogFragment { return dialogView; } - private static boolean showTabForRenderer(MappedTrackInfo mappedTrackInfo, int rendererIndex) { - TrackGroupArray trackGroupArray = mappedTrackInfo.getTrackGroups(rendererIndex); - if (trackGroupArray.length == 0) { - return false; - } - int trackType = mappedTrackInfo.getRendererType(rendererIndex); - return isSupportedTrackType(trackType); - } - - private static boolean isSupportedTrackType(int trackType) { - switch (trackType) { - case C.TRACK_TYPE_VIDEO: - case C.TRACK_TYPE_AUDIO: - case C.TRACK_TYPE_TEXT: - return true; - default: - return false; - } - } - - private static String getTrackTypeString(Resources resources, int trackType) { + private static String getTrackTypeString(Resources resources, @C.TrackType int trackType) { switch (trackType) { case C.TRACK_TYPE_VIDEO: return resources.getString(R.string.exo_track_selection_title_video); @@ -289,12 +282,12 @@ public final class TrackSelectionDialog extends DialogFragment { @Override public Fragment getItem(int position) { - return tabFragments.valueAt(position); + return tabFragments.get(tabTrackTypes.get(position)); } @Override public int getCount() { - return tabFragments.size(); + return tabTrackTypes.size(); } @Override @@ -307,13 +300,12 @@ public final class TrackSelectionDialog extends DialogFragment { public static final class TrackSelectionViewFragment extends Fragment implements TrackSelectionView.TrackSelectionListener { - private MappedTrackInfo mappedTrackInfo; - private int rendererIndex; + private List trackGroupInfos; private boolean allowAdaptiveSelections; private boolean allowMultipleOverrides; /* package */ boolean isDisabled; - /* package */ List overrides; + /* package */ Map overrides; public TrackSelectionViewFragment() { // Retain instance across activity re-creation to prevent losing access to init data. @@ -321,21 +313,21 @@ public final class TrackSelectionDialog extends DialogFragment { } public void init( - MappedTrackInfo mappedTrackInfo, - int rendererIndex, - boolean initialIsDisabled, - @Nullable SelectionOverride initialOverride, + List trackGroupInfos, + boolean isDisabled, + Map overrides, boolean allowAdaptiveSelections, boolean allowMultipleOverrides) { - this.mappedTrackInfo = mappedTrackInfo; - this.rendererIndex = rendererIndex; - this.isDisabled = initialIsDisabled; - this.overrides = - initialOverride == null - ? Collections.emptyList() - : Collections.singletonList(initialOverride); + this.trackGroupInfos = trackGroupInfos; + this.isDisabled = isDisabled; this.allowAdaptiveSelections = allowAdaptiveSelections; this.allowMultipleOverrides = allowMultipleOverrides; + // TrackSelectionView does this filtering internally, but we need to do it here as well to + // handle the case where the TrackSelectionView is never created. + this.overrides = + new HashMap<>( + TrackSelectionView.filterOverrides( + overrides, trackGroupInfos, allowMultipleOverrides)); } @Override @@ -351,8 +343,7 @@ public final class TrackSelectionDialog extends DialogFragment { trackSelectionView.setAllowMultipleOverrides(allowMultipleOverrides); trackSelectionView.setAllowAdaptiveSelections(allowAdaptiveSelections); trackSelectionView.init( - mappedTrackInfo, - rendererIndex, + trackGroupInfos, isDisabled, overrides, /* trackFormatComparator= */ null, @@ -361,7 +352,8 @@ public final class TrackSelectionDialog extends DialogFragment { } @Override - public void onTrackSelectionChanged(boolean isDisabled, List overrides) { + public void onTrackSelectionChanged( + boolean isDisabled, Map overrides) { this.isDisabled = isDisabled; this.overrides = overrides; } diff --git a/libraries/ui/build.gradle b/libraries/ui/build.gradle index e1e5229f33..c911f434eb 100644 --- a/libraries/ui/build.gradle +++ b/libraries/ui/build.gradle @@ -16,7 +16,7 @@ apply from: "$gradle.ext.androidxMediaSettingsDir/common_library_config.gradle" android.buildTypes.debug.testCoverageEnabled true dependencies { - implementation project(modulePrefix + 'lib-exoplayer') + implementation project(modulePrefix + 'lib-common') implementation 'androidx.media:media:' + androidxMediaVersion implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion implementation 'androidx.recyclerview:recyclerview:' + androidxRecyclerViewVersion diff --git a/libraries/ui/src/main/java/androidx/media3/ui/TrackSelectionDialogBuilder.java b/libraries/ui/src/main/java/androidx/media3/ui/TrackSelectionDialogBuilder.java index 97427aaa5d..86291df78c 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/TrackSelectionDialogBuilder.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/TrackSelectionDialogBuilder.java @@ -15,8 +15,6 @@ */ package androidx.media3.ui; -import static androidx.media3.common.util.Assertions.checkNotNull; - import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; @@ -25,17 +23,22 @@ import android.view.LayoutInflater; import android.view.View; import androidx.annotation.Nullable; import androidx.annotation.StyleRes; +import androidx.media3.common.C; import androidx.media3.common.Format; -import androidx.media3.common.TrackGroupArray; +import androidx.media3.common.Player; +import androidx.media3.common.TrackGroup; +import androidx.media3.common.TrackSelectionOverride; +import androidx.media3.common.TrackSelectionParameters; +import androidx.media3.common.TracksInfo.TrackGroupInfo; import androidx.media3.common.util.UnstableApi; -import androidx.media3.exoplayer.trackselection.DefaultTrackSelector; -import androidx.media3.exoplayer.trackselection.DefaultTrackSelector.SelectionOverride; -import androidx.media3.exoplayer.trackselection.MappingTrackSelector.MappedTrackInfo; -import androidx.media3.exoplayer.trackselection.TrackSelectionUtil; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import java.lang.reflect.Constructor; +import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Map; /** Builder for a dialog with a {@link TrackSelectionView}. */ @UnstableApi @@ -47,25 +50,24 @@ public final class TrackSelectionDialogBuilder { /** * Called when tracks are selected. * - * @param isDisabled Whether the renderer is disabled. - * @param overrides List of selected track selection overrides for the renderer. + * @param isDisabled Whether the disabled option is selected. + * @param overrides The selected track overrides. */ - void onTracksSelected(boolean isDisabled, List overrides); + void onTracksSelected(boolean isDisabled, Map overrides); } private final Context context; - @StyleRes private int themeResId; private final CharSequence title; - private final MappedTrackInfo mappedTrackInfo; - private final int rendererIndex; + private final List trackGroupInfos; private final DialogCallback callback; + @StyleRes private int themeResId; private boolean allowAdaptiveSelections; private boolean allowMultipleOverrides; private boolean showDisableOption; @Nullable private TrackNameProvider trackNameProvider; private boolean isDisabled; - private List overrides; + private Map overrides; @Nullable private Comparator trackFormatComparator; /** @@ -73,59 +75,53 @@ public final class TrackSelectionDialogBuilder { * * @param context The context of the dialog. * @param title The title of the dialog. - * @param mappedTrackInfo The {@link MappedTrackInfo} containing the track information. - * @param rendererIndex The renderer index in the {@code mappedTrackInfo} for which the track - * selection is shown. + * @param trackGroupInfos The {@link TrackGroupInfo TrackGroupInfos} for the track groups. * @param callback The {@link DialogCallback} invoked when a track selection has been made. */ public TrackSelectionDialogBuilder( Context context, CharSequence title, - MappedTrackInfo mappedTrackInfo, - int rendererIndex, + List trackGroupInfos, DialogCallback callback) { this.context = context; this.title = title; - this.mappedTrackInfo = mappedTrackInfo; - this.rendererIndex = rendererIndex; + this.trackGroupInfos = ImmutableList.copyOf(trackGroupInfos); this.callback = callback; - overrides = Collections.emptyList(); + overrides = Collections.emptyMap(); } /** - * Creates a builder for a track selection dialog which automatically updates a {@link - * DefaultTrackSelector}. + * Creates a builder for a track selection dialog. * * @param context The context of the dialog. * @param title The title of the dialog. - * @param trackSelector A {@link DefaultTrackSelector} whose current selection is used to set up - * the dialog and which is updated when new tracks are selected in the dialog. - * @param rendererIndex The renderer index in the {@code trackSelector} for which the track - * selection is shown. + * @param player The {@link Player} whose tracks should be selected. + * @param trackType The type of tracks to show for selection. */ public TrackSelectionDialogBuilder( - Context context, CharSequence title, DefaultTrackSelector trackSelector, int rendererIndex) { + Context context, CharSequence title, Player player, @C.TrackType int trackType) { this.context = context; this.title = title; - this.mappedTrackInfo = checkNotNull(trackSelector.getCurrentMappedTrackInfo()); - this.rendererIndex = rendererIndex; - - TrackGroupArray rendererTrackGroups = mappedTrackInfo.getTrackGroups(rendererIndex); - DefaultTrackSelector.Parameters selectionParameters = trackSelector.getParameters(); - isDisabled = selectionParameters.getRendererDisabled(rendererIndex); - SelectionOverride override = - selectionParameters.getSelectionOverride(rendererIndex, rendererTrackGroups); - overrides = override == null ? Collections.emptyList() : Collections.singletonList(override); - - this.callback = - (newIsDisabled, newOverrides) -> - trackSelector.setParameters( - TrackSelectionUtil.updateParametersWithOverride( - selectionParameters, - rendererIndex, - rendererTrackGroups, - newIsDisabled, - newOverrides.isEmpty() ? null : newOverrides.get(0))); + List allTrackGroupInfos = player.getCurrentTracksInfo().getTrackGroupInfos(); + trackGroupInfos = new ArrayList<>(); + for (int i = 0; i < allTrackGroupInfos.size(); i++) { + TrackGroupInfo trackGroupInfo = allTrackGroupInfos.get(i); + if (trackGroupInfo.getTrackType() == trackType) { + trackGroupInfos.add(trackGroupInfo); + } + } + overrides = Collections.emptyMap(); + callback = + (isDisabled, overrides) -> { + TrackSelectionParameters.Builder parametersBuilder = + player.getTrackSelectionParameters().buildUpon(); + parametersBuilder.setTrackTypeDisabled(trackType, isDisabled); + parametersBuilder.clearOverridesOfType(trackType); + for (TrackSelectionOverride override : overrides.values()) { + parametersBuilder.addOverride(override); + } + player.setTrackSelectionParameters(parametersBuilder.build()); + }; } /** @@ -151,27 +147,28 @@ public final class TrackSelectionDialogBuilder { } /** - * Sets the initial selection override to show. + * Sets the single initial override. * - * @param override The initial override to show, or null for no override. + * @param override The initial override, or {@code null} for no override. * @return This builder, for convenience. */ - public TrackSelectionDialogBuilder setOverride(@Nullable SelectionOverride override) { + public TrackSelectionDialogBuilder setOverride(@Nullable TrackSelectionOverride override) { return setOverrides( - override == null ? Collections.emptyList() : Collections.singletonList(override)); + override == null ? Collections.emptyMap() : ImmutableMap.of(override.trackGroup, override)); } /** - * Sets the list of initial selection overrides to show. + * Sets the initial track overrides. Any overrides that do not correspond to track groups + * described by {@code trackGroupInfos} that have been given to this instance will be ignored. If + * {@link #setAllowMultipleOverrides(boolean)} hasn't been set to {@code true} then all but one + * override will be ignored. The retained override will be the one whose track group is described + * first in {@code trackGroupInfos}. * - *

Note that only the first override will be used unless {@link - * #setAllowMultipleOverrides(boolean)} is set to {@code true}. - * - * @param overrides The list of initial overrides to show. There must be at most one override for - * each track group. + * @param overrides The initially selected track overrides. * @return This builder, for convenience. */ - public TrackSelectionDialogBuilder setOverrides(List overrides) { + public TrackSelectionDialogBuilder setOverrides( + Map overrides) { this.overrides = overrides; return this; } @@ -301,12 +298,7 @@ public final class TrackSelectionDialogBuilder { selectionView.setTrackNameProvider(trackNameProvider); } selectionView.init( - mappedTrackInfo, - rendererIndex, - isDisabled, - overrides, - trackFormatComparator, - /* listener= */ null); + trackGroupInfos, isDisabled, overrides, trackFormatComparator, /* listener= */ null); return (dialog, which) -> callback.onTracksSelected(selectionView.getIsDisabled(), selectionView.getOverrides()); } diff --git a/libraries/ui/src/main/java/androidx/media3/ui/TrackSelectionView.java b/libraries/ui/src/main/java/androidx/media3/ui/TrackSelectionView.java index 9864ae6f8e..0016d77a20 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/TrackSelectionView.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/TrackSelectionView.java @@ -18,29 +18,25 @@ package androidx.media3.ui; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; -import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; import android.widget.CheckedTextView; import android.widget.LinearLayout; import androidx.annotation.AttrRes; import androidx.annotation.Nullable; -import androidx.media3.common.C; import androidx.media3.common.Format; import androidx.media3.common.TrackGroup; -import androidx.media3.common.TrackGroupArray; +import androidx.media3.common.TrackSelectionOverride; +import androidx.media3.common.TracksInfo.TrackGroupInfo; import androidx.media3.common.util.Assertions; import androidx.media3.common.util.UnstableApi; -import androidx.media3.exoplayer.RendererCapabilities; -import androidx.media3.exoplayer.trackselection.DefaultTrackSelector; -import androidx.media3.exoplayer.trackselection.DefaultTrackSelector.SelectionOverride; -import androidx.media3.exoplayer.trackselection.MappingTrackSelector.MappedTrackInfo; +import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; +import java.util.HashMap; import java.util.List; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -import org.checkerframework.checker.nullness.qual.RequiresNonNull; +import java.util.Map; /** A view for making track selections. */ @UnstableApi @@ -52,10 +48,36 @@ public class TrackSelectionView extends LinearLayout { /** * Called when the selected tracks changed. * - * @param isDisabled Whether the renderer is disabled. - * @param overrides List of selected track selection overrides for the renderer. + * @param isDisabled Whether the disabled option is selected. + * @param overrides The selected track overrides. */ - void onTrackSelectionChanged(boolean isDisabled, List overrides); + void onTrackSelectionChanged( + boolean isDisabled, Map overrides); + } + + /** + * Returns the subset of {@code overrides} that apply to track groups in {@code trackGroupInfos}. + * If {@code allowMultipleOverrides} is {@code} then at most one override is retained, which will + * be the one whose track group is first in {@code trackGroupInfos}. + * + * @param overrides The overrides to filter. + * @param trackGroupInfos The track groups whose overrides should be retained. + * @param allowMultipleOverrides Whether more than one override can be retained. + * @return The filtered overrides. + */ + public static Map filterOverrides( + Map overrides, + List trackGroupInfos, + boolean allowMultipleOverrides) { + HashMap filteredOverrides = new HashMap<>(); + for (int i = 0; i < trackGroupInfos.size(); i++) { + TrackGroupInfo trackGroupInfo = trackGroupInfos.get(i); + @Nullable TrackSelectionOverride override = overrides.get(trackGroupInfo.getTrackGroup()); + if (override != null && (allowMultipleOverrides || filteredOverrides.isEmpty())) { + filteredOverrides.put(override.trackGroup, override); + } + } + return filteredOverrides; } private final int selectableItemBackgroundResourceId; @@ -63,7 +85,8 @@ public class TrackSelectionView extends LinearLayout { private final CheckedTextView disableView; private final CheckedTextView defaultView; private final ComponentListener componentListener; - private final SparseArray overrides; + private final List trackGroupInfos; + private final Map overrides; private boolean allowAdaptiveSelections; private boolean allowMultipleOverrides; @@ -71,9 +94,6 @@ public class TrackSelectionView extends LinearLayout { private TrackNameProvider trackNameProvider; private CheckedTextView[][] trackViews; - private @MonotonicNonNull MappedTrackInfo mappedTrackInfo; - private int rendererIndex; - private TrackGroupArray trackGroups; private boolean isDisabled; @Nullable private Comparator trackInfoComparator; @Nullable private TrackSelectionListener listener; @@ -94,9 +114,6 @@ public class TrackSelectionView extends LinearLayout { Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) { super(context, attrs, defStyleAttr); setOrientation(LinearLayout.VERTICAL); - - overrides = new SparseArray<>(); - // Don't save view hierarchy as it needs to be reinitialized with a call to init. setSaveFromParentEnabled(false); @@ -110,7 +127,8 @@ public class TrackSelectionView extends LinearLayout { inflater = LayoutInflater.from(context); componentListener = new ComponentListener(); trackNameProvider = new DefaultTrackNameProvider(getResources()); - trackGroups = TrackGroupArray.EMPTY; + trackGroupInfos = new ArrayList<>(); + overrides = new HashMap<>(); // View for disabling the renderer. disableView = @@ -155,26 +173,28 @@ public class TrackSelectionView extends LinearLayout { /** * Sets whether tracks from multiple track groups can be selected. This results in multiple {@link - * SelectionOverride SelectionOverrides} to be returned by {@link #getOverrides()}. + * TrackSelectionOverride TrackSelectionOverrides} being returned by {@link #getOverrides()}. * - * @param allowMultipleOverrides Whether multiple track selection overrides can be selected. + * @param allowMultipleOverrides Whether tracks from multiple track groups can be selected. */ public void setAllowMultipleOverrides(boolean allowMultipleOverrides) { if (this.allowMultipleOverrides != allowMultipleOverrides) { this.allowMultipleOverrides = allowMultipleOverrides; if (!allowMultipleOverrides && overrides.size() > 1) { - for (int i = overrides.size() - 1; i > 0; i--) { - overrides.remove(i); - } + // Re-filter the overrides to retain only one of them. + Map filteredOverrides = + filterOverrides(overrides, trackGroupInfos, /* allowMultipleOverrides= */ false); + overrides.clear(); + overrides.putAll(filteredOverrides); } updateViews(); } } /** - * Sets whether an option is available for disabling the renderer. + * Sets whether the disabled option can be selected. * - * @param showDisableOption Whether the disable option is shown. + * @param showDisableOption Whether the disabled option can be selected. */ public void setShowDisableOption(boolean showDisableOption) { disableView.setVisibility(showDisableOption ? View.VISIBLE : View.GONE); @@ -192,57 +212,47 @@ public class TrackSelectionView extends LinearLayout { } /** - * Initialize the view to select tracks for a specified renderer using {@link MappedTrackInfo} and - * a set of {@link DefaultTrackSelector.Parameters}. + * Initialize the view to select tracks from a specified list of track groups. * - * @param mappedTrackInfo The {@link MappedTrackInfo}. - * @param rendererIndex The index of the renderer. - * @param isDisabled Whether the renderer should be initially shown as disabled. - * @param overrides List of initial overrides to be shown for this renderer. There must be at most - * one override for each track group. If {@link #setAllowMultipleOverrides(boolean)} hasn't - * been set to {@code true}, only the first override is used. + * @param trackGroupInfos {@link TrackGroupInfo TrackGroupInfos} for the track groups. + * @param isDisabled Whether the disabled option should be initially selected. + * @param overrides The initially selected track overrides. Any overrides that do not correspond + * to track groups described in {@code trackGroupInfos} will be ignored. If {@link + * #setAllowMultipleOverrides(boolean)} hasn't been set to {@code true} then all but one + * override will be ignored. The retained override will be the one whose track group is + * described first in {@code trackGroupInfos}. * @param trackFormatComparator An optional comparator used to determine the display order of the * tracks within each track group. - * @param listener An optional listener for track selection updates. + * @param listener An optional listener to receive selection updates. */ public void init( - MappedTrackInfo mappedTrackInfo, - int rendererIndex, + List trackGroupInfos, boolean isDisabled, - List overrides, + Map overrides, @Nullable Comparator trackFormatComparator, @Nullable TrackSelectionListener listener) { - this.mappedTrackInfo = mappedTrackInfo; - this.rendererIndex = rendererIndex; this.isDisabled = isDisabled; this.trackInfoComparator = trackFormatComparator == null ? null - : (o1, o2) -> trackFormatComparator.compare(o1.format, o2.format); + : (o1, o2) -> trackFormatComparator.compare(o1.getFormat(), o2.getFormat()); this.listener = listener; - int maxOverrides = allowMultipleOverrides ? overrides.size() : Math.min(overrides.size(), 1); - for (int i = 0; i < maxOverrides; i++) { - SelectionOverride override = overrides.get(i); - this.overrides.put(override.groupIndex, override); - } + + this.trackGroupInfos.clear(); + this.trackGroupInfos.addAll(trackGroupInfos); + this.overrides.clear(); + this.overrides.putAll(filterOverrides(overrides, trackGroupInfos, allowMultipleOverrides)); updateViews(); } - /** Returns whether the renderer is disabled. */ + /** Returns whether the disabled option is selected. */ public boolean getIsDisabled() { return isDisabled; } - /** - * Returns the list of selected track selection overrides. There will be at most one override for - * each track group. - */ - public List getOverrides() { - List overrideList = new ArrayList<>(overrides.size()); - for (int i = 0; i < overrides.size(); i++) { - overrideList.add(overrides.valueAt(i)); - } - return overrideList; + /** Returns the selected track overrides. */ + public Map getOverrides() { + return overrides; } // Private methods. @@ -253,7 +263,7 @@ public class TrackSelectionView extends LinearLayout { removeViewAt(i); } - if (mappedTrackInfo == null) { + if (trackGroupInfos.isEmpty()) { // The view is not initialized. disableView.setEnabled(false); defaultView.setEnabled(false); @@ -262,19 +272,18 @@ public class TrackSelectionView extends LinearLayout { disableView.setEnabled(true); defaultView.setEnabled(true); - trackGroups = mappedTrackInfo.getTrackGroups(rendererIndex); - // Add per-track views. - trackViews = new CheckedTextView[trackGroups.length][]; + trackViews = new CheckedTextView[trackGroupInfos.size()][]; boolean enableMultipleChoiceForMultipleOverrides = shouldEnableMultiGroupSelection(); - for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) { - TrackGroup group = trackGroups.get(groupIndex); - boolean enableMultipleChoiceForAdaptiveSelections = shouldEnableAdaptiveSelection(groupIndex); - trackViews[groupIndex] = new CheckedTextView[group.length]; + for (int trackGroupIndex = 0; trackGroupIndex < trackGroupInfos.size(); trackGroupIndex++) { + TrackGroupInfo trackGroupInfo = trackGroupInfos.get(trackGroupIndex); + boolean enableMultipleChoiceForAdaptiveSelections = + shouldEnableAdaptiveSelection(trackGroupInfo); + trackViews[trackGroupIndex] = new CheckedTextView[trackGroupInfo.length]; - TrackInfo[] trackInfos = new TrackInfo[group.length]; - for (int trackIndex = 0; trackIndex < group.length; trackIndex++) { - trackInfos[trackIndex] = new TrackInfo(groupIndex, trackIndex, group.getFormat(trackIndex)); + TrackInfo[] trackInfos = new TrackInfo[trackGroupInfo.length]; + for (int trackIndex = 0; trackIndex < trackGroupInfo.length; trackIndex++) { + trackInfos[trackIndex] = new TrackInfo(trackGroupInfo, trackIndex); } if (trackInfoComparator != null) { Arrays.sort(trackInfos, trackInfoComparator); @@ -291,17 +300,16 @@ public class TrackSelectionView extends LinearLayout { CheckedTextView trackView = (CheckedTextView) inflater.inflate(trackViewLayoutId, this, false); trackView.setBackgroundResource(selectableItemBackgroundResourceId); - trackView.setText(trackNameProvider.getTrackName(trackInfos[trackIndex].format)); + trackView.setText(trackNameProvider.getTrackName(trackInfos[trackIndex].getFormat())); trackView.setTag(trackInfos[trackIndex]); - if (mappedTrackInfo.getTrackSupport(rendererIndex, groupIndex, trackIndex) - == C.FORMAT_HANDLED) { + if (trackGroupInfo.isTrackSupported(trackIndex)) { trackView.setFocusable(true); trackView.setOnClickListener(componentListener); } else { trackView.setFocusable(false); trackView.setEnabled(false); } - trackViews[groupIndex][trackIndex] = trackView; + trackViews[trackGroupIndex][trackIndex] = trackView; addView(trackView); } } @@ -313,11 +321,12 @@ public class TrackSelectionView extends LinearLayout { disableView.setChecked(isDisabled); defaultView.setChecked(!isDisabled && overrides.size() == 0); for (int i = 0; i < trackViews.length; i++) { - SelectionOverride override = overrides.get(i); + @Nullable + TrackSelectionOverride override = overrides.get(trackGroupInfos.get(i).getTrackGroup()); for (int j = 0; j < trackViews[i].length; j++) { if (override != null) { TrackInfo trackInfo = (TrackInfo) Assertions.checkNotNull(trackViews[i][j].getTag()); - trackViews[i][j].setChecked(override.containsTrack(trackInfo.trackIndex)); + trackViews[i][j].setChecked(override.trackIndices.contains(trackInfo.trackIndex)); } else { trackViews[i][j].setChecked(false); } @@ -352,74 +361,52 @@ public class TrackSelectionView extends LinearLayout { private void onTrackViewClicked(View view) { isDisabled = false; TrackInfo trackInfo = (TrackInfo) Assertions.checkNotNull(view.getTag()); - int groupIndex = trackInfo.groupIndex; + TrackGroup trackGroup = trackInfo.trackGroupInfo.getTrackGroup(); int trackIndex = trackInfo.trackIndex; - SelectionOverride override = overrides.get(groupIndex); - Assertions.checkNotNull(mappedTrackInfo); + @Nullable TrackSelectionOverride override = overrides.get(trackGroup); if (override == null) { // Start new override. if (!allowMultipleOverrides && overrides.size() > 0) { // Removed other overrides if we don't allow multiple overrides. overrides.clear(); } - overrides.put(groupIndex, new SelectionOverride(groupIndex, trackIndex)); + overrides.put( + trackGroup, new TrackSelectionOverride(trackGroup, ImmutableList.of(trackIndex))); } else { // An existing override is being modified. - int overrideLength = override.length; - int[] overrideTracks = override.tracks; + ArrayList trackIndices = new ArrayList<>(override.trackIndices); boolean isCurrentlySelected = ((CheckedTextView) view).isChecked(); - boolean isAdaptiveAllowed = shouldEnableAdaptiveSelection(groupIndex); + boolean isAdaptiveAllowed = shouldEnableAdaptiveSelection(trackInfo.trackGroupInfo); boolean isUsingCheckBox = isAdaptiveAllowed || shouldEnableMultiGroupSelection(); if (isCurrentlySelected && isUsingCheckBox) { // Remove the track from the override. - if (overrideLength == 1) { - // The last track is being removed, so the override becomes empty. - overrides.remove(groupIndex); + trackIndices.remove((Integer) trackIndex); + if (trackIndices.isEmpty()) { + // The last track has been removed, so remove the whole override. + overrides.remove(trackGroup); } else { - int[] tracks = getTracksRemoving(overrideTracks, trackIndex); - overrides.put(groupIndex, new SelectionOverride(groupIndex, tracks)); + overrides.put(trackGroup, new TrackSelectionOverride(trackGroup, trackIndices)); } } else if (!isCurrentlySelected) { if (isAdaptiveAllowed) { // Add new track to adaptive override. - int[] tracks = getTracksAdding(overrideTracks, trackIndex); - overrides.put(groupIndex, new SelectionOverride(groupIndex, tracks)); + trackIndices.add(trackIndex); + overrides.put(trackGroup, new TrackSelectionOverride(trackGroup, trackIndices)); } else { // Replace existing track in override. - overrides.put(groupIndex, new SelectionOverride(groupIndex, trackIndex)); + overrides.put( + trackGroup, new TrackSelectionOverride(trackGroup, ImmutableList.of(trackIndex))); } } } } - @RequiresNonNull("mappedTrackInfo") - private boolean shouldEnableAdaptiveSelection(int groupIndex) { - return allowAdaptiveSelections - && trackGroups.get(groupIndex).length > 1 - && mappedTrackInfo.getAdaptiveSupport( - rendererIndex, groupIndex, /* includeCapabilitiesExceededTracks= */ false) - != RendererCapabilities.ADAPTIVE_NOT_SUPPORTED; + private boolean shouldEnableAdaptiveSelection(TrackGroupInfo trackGroupInfo) { + return allowAdaptiveSelections && trackGroupInfo.isAdaptiveSupported(); } private boolean shouldEnableMultiGroupSelection() { - return allowMultipleOverrides && trackGroups.length > 1; - } - - private static int[] getTracksAdding(int[] tracks, int addedTrack) { - tracks = Arrays.copyOf(tracks, tracks.length + 1); - tracks[tracks.length - 1] = addedTrack; - return tracks; - } - - private static int[] getTracksRemoving(int[] tracks, int removedTrack) { - int[] newTracks = new int[tracks.length - 1]; - int trackCount = 0; - for (int track : tracks) { - if (track != removedTrack) { - newTracks[trackCount++] = track; - } - } - return newTracks; + return allowMultipleOverrides && trackGroupInfos.size() > 1; } // Internal classes. @@ -433,14 +420,16 @@ public class TrackSelectionView extends LinearLayout { } private static final class TrackInfo { - public final int groupIndex; + public final TrackGroupInfo trackGroupInfo; public final int trackIndex; - public final Format format; - public TrackInfo(int groupIndex, int trackIndex, Format format) { - this.groupIndex = groupIndex; + public TrackInfo(TrackGroupInfo trackGroupInfo, int trackIndex) { + this.trackGroupInfo = trackGroupInfo; this.trackIndex = trackIndex; - this.format = format; + } + + public Format getFormat() { + return trackGroupInfo.getTrackFormat(trackIndex); } } }