diff --git a/RELEASENOTES.md b/RELEASENOTES.md index f183fbb31e..de48e5d90e 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -24,6 +24,10 @@ ([#7967](https://github.com/google/ExoPlayer/issues/7967)). * Use TLEN ID3 tag to compute the duration in Mp3Extractor ([#7949](https://github.com/google/ExoPlayer/issues/7949)). +* UI + * Add the option to sort tracks by `Format` in `TrackSelectionView` and + `TrackSelectionDialogBuilder` + ([#7709](https://github.com/google/ExoPlayer/issues/7709)). ### 2.12.0 (2020-09-11) ### diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionDialog.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionDialog.java index 5cf2353f21..d3f9b3880d 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionDialog.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/TrackSelectionDialog.java @@ -354,7 +354,12 @@ public final class TrackSelectionDialog extends DialogFragment { trackSelectionView.setAllowMultipleOverrides(allowMultipleOverrides); trackSelectionView.setAllowAdaptiveSelections(allowAdaptiveSelections); trackSelectionView.init( - mappedTrackInfo, rendererIndex, isDisabled, overrides, /* listener= */ this); + mappedTrackInfo, + rendererIndex, + isDisabled, + overrides, + /* trackFormatComparator= */ null, + /* listener= */ this); return rootView; } diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/TrackSelectionDialogBuilder.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/TrackSelectionDialogBuilder.java index 520b2d7580..be3fb9bc90 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/TrackSelectionDialogBuilder.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/TrackSelectionDialogBuilder.java @@ -25,6 +25,7 @@ import android.view.LayoutInflater; import android.view.View; import androidx.annotation.Nullable; import androidx.annotation.StyleRes; +import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.SelectionOverride; @@ -32,6 +33,7 @@ import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedT import com.google.android.exoplayer2.trackselection.TrackSelectionUtil; import java.lang.reflect.Constructor; import java.util.Collections; +import java.util.Comparator; import java.util.List; /** Builder for a dialog with a {@link TrackSelectionView}. */ @@ -62,6 +64,7 @@ public final class TrackSelectionDialogBuilder { @Nullable private TrackNameProvider trackNameProvider; private boolean isDisabled; private List overrides; + @Nullable private Comparator trackFormatComparator; /** * Creates a builder for a track selection dialog. @@ -208,6 +211,16 @@ public final class TrackSelectionDialogBuilder { return this; } + /** + * Sets a {@link Comparator} used to determine the display order of the tracks within each track + * group. + * + * @param trackFormatComparator The comparator, or {@code null} to use the original order. + */ + public void setTrackFormatComparator(@Nullable Comparator trackFormatComparator) { + this.trackFormatComparator = trackFormatComparator; + } + /** * Sets the {@link TrackNameProvider} used to generate the user visible name of each track and * updates the view with track names queried from the specified provider. @@ -287,7 +300,13 @@ public final class TrackSelectionDialogBuilder { if (trackNameProvider != null) { selectionView.setTrackNameProvider(trackNameProvider); } - selectionView.init(mappedTrackInfo, rendererIndex, isDisabled, overrides, /* listener= */ null); + selectionView.init( + mappedTrackInfo, + rendererIndex, + isDisabled, + overrides, + trackFormatComparator, + /* listener= */ null); return (dialog, which) -> callback.onTracksSelected(selectionView.getIsDisabled(), selectionView.getOverrides()); } diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/TrackSelectionView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/TrackSelectionView.java index b47feb2a71..8a8f3d3c76 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/TrackSelectionView.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/TrackSelectionView.java @@ -18,7 +18,6 @@ package com.google.android.exoplayer2.ui; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; -import android.util.Pair; import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; @@ -26,6 +25,7 @@ import android.widget.CheckedTextView; import android.widget.LinearLayout; import androidx.annotation.AttrRes; import androidx.annotation.Nullable; +import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; @@ -35,6 +35,7 @@ import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedT import com.google.android.exoplayer2.util.Assertions; import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.List; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.RequiresNonNull; @@ -71,6 +72,7 @@ public class TrackSelectionView extends LinearLayout { private int rendererIndex; private TrackGroupArray trackGroups; private boolean isDisabled; + @Nullable private Comparator trackInfoComparator; @Nullable private TrackSelectionListener listener; /** Creates a track selection view. */ @@ -196,6 +198,8 @@ public class TrackSelectionView extends LinearLayout { * @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 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. */ public void init( @@ -203,10 +207,15 @@ public class TrackSelectionView extends LinearLayout { int rendererIndex, boolean isDisabled, List 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); this.listener = listener; int maxOverrides = allowMultipleOverrides ? overrides.size() : Math.min(overrides.size(), 1); for (int i = 0; i < maxOverrides; i++) { @@ -259,7 +268,16 @@ public class TrackSelectionView extends LinearLayout { TrackGroup group = trackGroups.get(groupIndex); boolean enableMultipleChoiceForAdaptiveSelections = shouldEnableAdaptiveSelection(groupIndex); trackViews[groupIndex] = new CheckedTextView[group.length]; + + TrackInfo[] trackInfos = new TrackInfo[group.length]; for (int trackIndex = 0; trackIndex < group.length; trackIndex++) { + trackInfos[trackIndex] = new TrackInfo(groupIndex, trackIndex, group.getFormat(trackIndex)); + } + if (trackInfoComparator != null) { + Arrays.sort(trackInfos, trackInfoComparator); + } + + for (int trackIndex = 0; trackIndex < trackInfos.length; trackIndex++) { if (trackIndex == 0) { addView(inflater.inflate(R.layout.exo_list_divider, this, false)); } @@ -270,11 +288,11 @@ public class TrackSelectionView extends LinearLayout { CheckedTextView trackView = (CheckedTextView) inflater.inflate(trackViewLayoutId, this, false); trackView.setBackgroundResource(selectableItemBackgroundResourceId); - trackView.setText(trackNameProvider.getTrackName(group.getFormat(trackIndex))); + trackView.setText(trackNameProvider.getTrackName(trackInfos[trackIndex].format)); if (mappedTrackInfo.getTrackSupport(rendererIndex, groupIndex, trackIndex) == RendererCapabilities.FORMAT_HANDLED) { trackView.setFocusable(true); - trackView.setTag(Pair.create(groupIndex, trackIndex)); + trackView.setTag(trackInfos[trackIndex]); trackView.setOnClickListener(componentListener); } else { trackView.setFocusable(false); @@ -294,7 +312,12 @@ public class TrackSelectionView extends LinearLayout { for (int i = 0; i < trackViews.length; i++) { SelectionOverride override = overrides.get(i); for (int j = 0; j < trackViews[i].length; j++) { - trackViews[i][j].setChecked(override != null && override.containsTrack(j)); + if (override != null) { + TrackInfo trackInfo = (TrackInfo) Assertions.checkNotNull(trackViews[i][j].getTag()); + trackViews[i][j].setChecked(override.containsTrack(trackInfo.trackIndex)); + } else { + trackViews[i][j].setChecked(false); + } } } } @@ -325,10 +348,9 @@ public class TrackSelectionView extends LinearLayout { private void onTrackViewClicked(View view) { isDisabled = false; - @SuppressWarnings("unchecked") - Pair tag = (Pair) Assertions.checkNotNull(view.getTag()); - int groupIndex = tag.first; - int trackIndex = tag.second; + TrackInfo trackInfo = (TrackInfo) Assertions.checkNotNull(view.getTag()); + int groupIndex = trackInfo.groupIndex; + int trackIndex = trackInfo.trackIndex; SelectionOverride override = overrides.get(groupIndex); Assertions.checkNotNull(mappedTrackInfo); if (override == null) { @@ -406,4 +428,16 @@ public class TrackSelectionView extends LinearLayout { TrackSelectionView.this.onClick(view); } } + + private static final class TrackInfo { + public final int groupIndex; + public final int trackIndex; + public final Format format; + + public TrackInfo(int groupIndex, int trackIndex, Format format) { + this.groupIndex = groupIndex; + this.trackIndex = trackIndex; + this.format = format; + } + } }