dev: adding sorting for TrackSelectionDialog and TrackSelectionDialogBuilder

This commit is contained in:
Yoni Obadia 2020-08-24 15:13:21 +02:00
parent 99d245f7a6
commit 181676d950
5 changed files with 114 additions and 17 deletions

View file

@ -243,7 +243,7 @@ public class DownloadTracker {
/* allowAdaptiveSelections =*/ false, /* allowAdaptiveSelections =*/ false,
/* allowMultipleOverrides= */ true, /* allowMultipleOverrides= */ true,
/* onClickListener= */ this, /* onClickListener= */ this,
/* onDismissListener= */ this); /* onDismissListener= */ this, null);
trackSelectionDialog.show(fragmentManager, /* tag= */ null); trackSelectionDialog.show(fragmentManager, /* tag= */ null);
} }

View file

@ -34,6 +34,7 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.PlaybackPreparer; import com.google.android.exoplayer2.PlaybackPreparer;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
@ -64,6 +65,8 @@ import java.net.CookieManager;
import java.net.CookiePolicy; import java.net.CookiePolicy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List; import java.util.List;
/** An activity that plays media using {@link SimpleExoPlayer}. */ /** An activity that plays media using {@link SimpleExoPlayer}. */
@ -242,15 +245,30 @@ public class PlayerActivity extends AppCompatActivity
if (view == selectTracksButton if (view == selectTracksButton
&& !isShowingTrackSelectionDialog && !isShowingTrackSelectionDialog
&& TrackSelectionDialog.willHaveContent(trackSelector)) { && TrackSelectionDialog.willHaveContent(trackSelector)) {
HashMap<Integer, Comparator<Format>> comparatorHashMap = new HashMap<>();
comparatorHashMap.put(getRendererTypeIndex(C.TRACK_TYPE_AUDIO), (o1, o2) -> o1.bitrate - o2.bitrate);
comparatorHashMap.put(getRendererTypeIndex(C.TRACK_TYPE_VIDEO), (o1, o2) -> o2.bitrate - o1.bitrate);
isShowingTrackSelectionDialog = true; isShowingTrackSelectionDialog = true;
TrackSelectionDialog trackSelectionDialog = TrackSelectionDialog trackSelectionDialog =
TrackSelectionDialog.createForTrackSelector( TrackSelectionDialog.createForTrackSelector(
trackSelector, trackSelector,
/* onDismissListener= */ dismissedDialog -> isShowingTrackSelectionDialog = false); /* onDismissListener= */ dismissedDialog -> isShowingTrackSelectionDialog = false, comparatorHashMap);
trackSelectionDialog.show(getSupportFragmentManager(), /* tag= */ null); trackSelectionDialog.show(getSupportFragmentManager(), /* tag= */ null);
} }
} }
private Integer getRendererTypeIndex(int trackType) {
MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo();
for(int i = 0; i < mappedTrackInfo.getRendererCount(); i++) {
TrackGroupArray trackGroupArray = mappedTrackInfo.getTrackGroups(i);
if(trackGroupArray.length == 0) return null;
if(trackType == mappedTrackInfo.getRendererType(i)) {
return i;
}
}
return null;
}
// PlaybackPreparer implementation // PlaybackPreparer implementation
@Override @Override

View file

@ -33,6 +33,7 @@ import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter; import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager; import androidx.viewpager.widget.ViewPager;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.SelectionOverride; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.SelectionOverride;
@ -42,6 +43,8 @@ import com.google.android.exoplayer2.util.Assertions;
import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayout;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List; import java.util.List;
/** Dialog to select tracks. */ /** Dialog to select tracks. */
@ -53,6 +56,7 @@ public final class TrackSelectionDialog extends DialogFragment {
private int titleId; private int titleId;
private DialogInterface.OnClickListener onClickListener; private DialogInterface.OnClickListener onClickListener;
private DialogInterface.OnDismissListener onDismissListener; private DialogInterface.OnDismissListener onDismissListener;
private HashMap<Integer, Comparator<Format>> comparators = new HashMap<>();
/** /**
* Returns whether a track selection dialog will have content to display if initialized with the * Returns whether a track selection dialog will have content to display if initialized with the
@ -85,7 +89,8 @@ public final class TrackSelectionDialog extends DialogFragment {
* dismissed. * dismissed.
*/ */
public static TrackSelectionDialog createForTrackSelector( public static TrackSelectionDialog createForTrackSelector(
DefaultTrackSelector trackSelector, DialogInterface.OnDismissListener onDismissListener) { DefaultTrackSelector trackSelector, DialogInterface.OnDismissListener onDismissListener,
HashMap<Integer, Comparator<Format>> comparators) {
MappedTrackInfo mappedTrackInfo = MappedTrackInfo mappedTrackInfo =
Assertions.checkNotNull(trackSelector.getCurrentMappedTrackInfo()); Assertions.checkNotNull(trackSelector.getCurrentMappedTrackInfo());
TrackSelectionDialog trackSelectionDialog = new TrackSelectionDialog(); TrackSelectionDialog trackSelectionDialog = new TrackSelectionDialog();
@ -115,7 +120,8 @@ public final class TrackSelectionDialog extends DialogFragment {
} }
trackSelector.setParameters(builder); trackSelector.setParameters(builder);
}, },
onDismissListener); onDismissListener,
comparators);
return trackSelectionDialog; return trackSelectionDialog;
} }
@ -140,7 +146,8 @@ public final class TrackSelectionDialog extends DialogFragment {
boolean allowAdaptiveSelections, boolean allowAdaptiveSelections,
boolean allowMultipleOverrides, boolean allowMultipleOverrides,
DialogInterface.OnClickListener onClickListener, DialogInterface.OnClickListener onClickListener,
DialogInterface.OnDismissListener onDismissListener) { DialogInterface.OnDismissListener onDismissListener,
@Nullable HashMap<Integer, Comparator<Format>> comparators) {
TrackSelectionDialog trackSelectionDialog = new TrackSelectionDialog(); TrackSelectionDialog trackSelectionDialog = new TrackSelectionDialog();
trackSelectionDialog.init( trackSelectionDialog.init(
titleId, titleId,
@ -149,7 +156,8 @@ public final class TrackSelectionDialog extends DialogFragment {
allowAdaptiveSelections, allowAdaptiveSelections,
allowMultipleOverrides, allowMultipleOverrides,
onClickListener, onClickListener,
onDismissListener); onDismissListener,
comparators);
return trackSelectionDialog; return trackSelectionDialog;
} }
@ -167,10 +175,12 @@ public final class TrackSelectionDialog extends DialogFragment {
boolean allowAdaptiveSelections, boolean allowAdaptiveSelections,
boolean allowMultipleOverrides, boolean allowMultipleOverrides,
DialogInterface.OnClickListener onClickListener, DialogInterface.OnClickListener onClickListener,
DialogInterface.OnDismissListener onDismissListener) { DialogInterface.OnDismissListener onDismissListener,
HashMap<Integer, Comparator<Format>> comparators) {
this.titleId = titleId; this.titleId = titleId;
this.onClickListener = onClickListener; this.onClickListener = onClickListener;
this.onDismissListener = onDismissListener; this.onDismissListener = onDismissListener;
this.comparators = comparators;
for (int i = 0; i < mappedTrackInfo.getRendererCount(); i++) { for (int i = 0; i < mappedTrackInfo.getRendererCount(); i++) {
if (showTabForRenderer(mappedTrackInfo, i)) { if (showTabForRenderer(mappedTrackInfo, i)) {
int trackType = mappedTrackInfo.getRendererType(/* rendererIndex= */ i); int trackType = mappedTrackInfo.getRendererType(/* rendererIndex= */ i);
@ -182,7 +192,9 @@ public final class TrackSelectionDialog extends DialogFragment {
initialParameters.getRendererDisabled(/* rendererIndex= */ i), initialParameters.getRendererDisabled(/* rendererIndex= */ i),
initialParameters.getSelectionOverride(/* rendererIndex= */ i, trackGroupArray), initialParameters.getSelectionOverride(/* rendererIndex= */ i, trackGroupArray),
allowAdaptiveSelections, allowAdaptiveSelections,
allowMultipleOverrides); allowMultipleOverrides,
this.comparators.get(i) != null ? comparators.get(i) : null
);
tabFragments.put(i, tabFragment); tabFragments.put(i, tabFragment);
tabTrackTypes.add(trackType); tabTrackTypes.add(trackType);
} }
@ -314,6 +326,7 @@ public final class TrackSelectionDialog extends DialogFragment {
private int rendererIndex; private int rendererIndex;
private boolean allowAdaptiveSelections; private boolean allowAdaptiveSelections;
private boolean allowMultipleOverrides; private boolean allowMultipleOverrides;
private Comparator<Format> comparator;
/* package */ boolean isDisabled; /* package */ boolean isDisabled;
/* package */ List<SelectionOverride> overrides; /* package */ List<SelectionOverride> overrides;
@ -329,10 +342,12 @@ public final class TrackSelectionDialog extends DialogFragment {
boolean initialIsDisabled, boolean initialIsDisabled,
@Nullable SelectionOverride initialOverride, @Nullable SelectionOverride initialOverride,
boolean allowAdaptiveSelections, boolean allowAdaptiveSelections,
boolean allowMultipleOverrides) { boolean allowMultipleOverrides,
@Nullable Comparator<Format> comparator) {
this.mappedTrackInfo = mappedTrackInfo; this.mappedTrackInfo = mappedTrackInfo;
this.rendererIndex = rendererIndex; this.rendererIndex = rendererIndex;
this.isDisabled = initialIsDisabled; this.isDisabled = initialIsDisabled;
this.comparator = comparator;
this.overrides = this.overrides =
initialOverride == null initialOverride == null
? Collections.emptyList() ? Collections.emptyList()
@ -354,7 +369,7 @@ public final class TrackSelectionDialog extends DialogFragment {
trackSelectionView.setAllowMultipleOverrides(allowMultipleOverrides); trackSelectionView.setAllowMultipleOverrides(allowMultipleOverrides);
trackSelectionView.setAllowAdaptiveSelections(allowAdaptiveSelections); trackSelectionView.setAllowAdaptiveSelections(allowAdaptiveSelections);
trackSelectionView.init( trackSelectionView.init(
mappedTrackInfo, rendererIndex, isDisabled, overrides, /* listener= */ this); mappedTrackInfo, rendererIndex, isDisabled, overrides, /* listener= */ this, comparator);
return rootView; return rootView;
} }

View file

@ -24,6 +24,7 @@ import android.content.DialogInterface;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.SelectionOverride; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.SelectionOverride;
@ -31,6 +32,7 @@ import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedT
import com.google.android.exoplayer2.trackselection.TrackSelectionUtil; import com.google.android.exoplayer2.trackselection.TrackSelectionUtil;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.List; import java.util.List;
/** Builder for a dialog with a {@link TrackSelectionView}. */ /** Builder for a dialog with a {@link TrackSelectionView}. */
@ -60,6 +62,7 @@ public final class TrackSelectionDialogBuilder {
@Nullable private TrackNameProvider trackNameProvider; @Nullable private TrackNameProvider trackNameProvider;
private boolean isDisabled; private boolean isDisabled;
private List<SelectionOverride> overrides; private List<SelectionOverride> overrides;
private Comparator<Format> comparator;
/** /**
* Creates a builder for a track selection dialog. * Creates a builder for a track selection dialog.
@ -195,6 +198,12 @@ public final class TrackSelectionDialogBuilder {
return this; return this;
} }
public void setComparator(Comparator<Format> comparator) {
if(this.comparator != comparator) {
this.comparator = comparator;
}
}
/** /**
* Sets the {@link TrackNameProvider} used to generate the user visible name of each track and * 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. * updates the view with track names queried from the specified provider.
@ -274,7 +283,7 @@ public final class TrackSelectionDialogBuilder {
if (trackNameProvider != null) { if (trackNameProvider != null) {
selectionView.setTrackNameProvider(trackNameProvider); selectionView.setTrackNameProvider(trackNameProvider);
} }
selectionView.init(mappedTrackInfo, rendererIndex, isDisabled, overrides, /* listener= */ null); selectionView.init(mappedTrackInfo, rendererIndex, isDisabled, overrides, /* listener= */ null, comparator);
return (dialog, which) -> return (dialog, which) ->
callback.onTracksSelected(selectionView.getIsDisabled(), selectionView.getOverrides()); callback.onTracksSelected(selectionView.getIsDisabled(), selectionView.getOverrides());
} }

View file

@ -26,6 +26,7 @@ import android.widget.CheckedTextView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import androidx.annotation.AttrRes; import androidx.annotation.AttrRes;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
@ -35,6 +36,7 @@ import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedT
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator;
import java.util.List; import java.util.List;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull; import org.checkerframework.checker.nullness.qual.RequiresNonNull;
@ -64,6 +66,9 @@ public class TrackSelectionView extends LinearLayout {
private boolean allowAdaptiveSelections; private boolean allowAdaptiveSelections;
private boolean allowMultipleOverrides; private boolean allowMultipleOverrides;
@Nullable private Comparator<Format> comparator;
private TrackGroupArray sortedTrackGroups;
private TrackNameProvider trackNameProvider; private TrackNameProvider trackNameProvider;
private CheckedTextView[][] trackViews; private CheckedTextView[][] trackViews;
@ -203,11 +208,13 @@ public class TrackSelectionView extends LinearLayout {
int rendererIndex, int rendererIndex,
boolean isDisabled, boolean isDisabled,
List<SelectionOverride> overrides, List<SelectionOverride> overrides,
@Nullable TrackSelectionListener listener) { @Nullable TrackSelectionListener listener,
@Nullable Comparator<Format> comparator) {
this.mappedTrackInfo = mappedTrackInfo; this.mappedTrackInfo = mappedTrackInfo;
this.rendererIndex = rendererIndex; this.rendererIndex = rendererIndex;
this.isDisabled = isDisabled; this.isDisabled = isDisabled;
this.listener = listener; this.listener = listener;
this.comparator = comparator;
int maxOverrides = allowMultipleOverrides ? overrides.size() : Math.min(overrides.size(), 1); int maxOverrides = allowMultipleOverrides ? overrides.size() : Math.min(overrides.size(), 1);
for (int i = 0; i < maxOverrides; i++) { for (int i = 0; i < maxOverrides; i++) {
SelectionOverride override = overrides.get(i); SelectionOverride override = overrides.get(i);
@ -251,12 +258,13 @@ public class TrackSelectionView extends LinearLayout {
defaultView.setEnabled(true); defaultView.setEnabled(true);
trackGroups = mappedTrackInfo.getTrackGroups(rendererIndex); trackGroups = mappedTrackInfo.getTrackGroups(rendererIndex);
sortedTrackGroups = initSortedTrackGroups(trackGroups);
// Add per-track views. // Add per-track views.
trackViews = new CheckedTextView[trackGroups.length][]; trackViews = new CheckedTextView[sortedTrackGroups.length][];
boolean enableMultipleChoiceForMultipleOverrides = shouldEnableMultiGroupSelection(); boolean enableMultipleChoiceForMultipleOverrides = shouldEnableMultiGroupSelection();
for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) { for (int groupIndex = 0; groupIndex < sortedTrackGroups.length; groupIndex++) {
TrackGroup group = trackGroups.get(groupIndex); TrackGroup group = sortedTrackGroups.get(groupIndex);
boolean enableMultipleChoiceForAdaptiveSelections = shouldEnableAdaptiveSelection(groupIndex); boolean enableMultipleChoiceForAdaptiveSelections = shouldEnableAdaptiveSelection(groupIndex);
trackViews[groupIndex] = new CheckedTextView[group.length]; trackViews[groupIndex] = new CheckedTextView[group.length];
for (int trackIndex = 0; trackIndex < group.length; trackIndex++) { for (int trackIndex = 0; trackIndex < group.length; trackIndex++) {
@ -294,7 +302,12 @@ public class TrackSelectionView extends LinearLayout {
for (int i = 0; i < trackViews.length; i++) { for (int i = 0; i < trackViews.length; i++) {
SelectionOverride override = overrides.get(i); SelectionOverride override = overrides.get(i);
for (int j = 0; j < trackViews[i].length; j++) { for (int j = 0; j < trackViews[i].length; j++) {
trackViews[i][j].setChecked(override != null && override.containsTrack(j)); if(override != null) {
int sortedIndex = getSortedIndexFromInitialTrackGroup(override.groupIndex, j);
trackViews[i][j].setChecked(override.containsTrack(sortedIndex));
} else {
trackViews[i][j].setChecked(false);
}
} }
} }
} }
@ -328,7 +341,7 @@ public class TrackSelectionView extends LinearLayout {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Pair<Integer, Integer> tag = (Pair<Integer, Integer>) Assertions.checkNotNull(view.getTag()); Pair<Integer, Integer> tag = (Pair<Integer, Integer>) Assertions.checkNotNull(view.getTag());
int groupIndex = tag.first; int groupIndex = tag.first;
int trackIndex = tag.second; int trackIndex = getSortedIndexFromInitialTrackGroup(tag.first, tag.second);
SelectionOverride override = overrides.get(groupIndex); SelectionOverride override = overrides.get(groupIndex);
Assertions.checkNotNull(mappedTrackInfo); Assertions.checkNotNull(mappedTrackInfo);
if (override == null) { if (override == null) {
@ -367,6 +380,48 @@ public class TrackSelectionView extends LinearLayout {
} }
} }
private TrackGroupArray initSortedTrackGroups(TrackGroupArray trackGroups) {
TrackGroupArray trackGroupArray = trackGroups;
if(comparator != null) {
TrackGroupArray trackGroupsArray = mappedTrackInfo.getTrackGroups(rendererIndex);
for (int groupIndex = 0; groupIndex < trackGroupsArray.length; groupIndex++) {
TrackGroup group = trackGroupsArray.get(groupIndex);
Format[] listFormats = new Format[group.length];
for (int formatIndex = 0; formatIndex < group.length; formatIndex++) {
listFormats[formatIndex] = group.getFormat(formatIndex);
}
Arrays.sort(listFormats, comparator);
trackGroupArray = new TrackGroupArray(new TrackGroup(listFormats));
}
}
return trackGroupArray;
}
/**
* The correspondence between trackGroup and sortedTrackGroup indexes.
* initial array (only quality for this example) : [480,720,256,1080]
* asc sorted array (only quality for this example) : [256,480,720,1080]
* initial array index for 480 is 0, but for sorted array index is 1.
* Initial array index is @param trackIndex, and the @return result is sorted array index
* @param groupIndex which TrackGroup you want to browse into
* @param trackIndex which index of the initial array
* @return index of the sorted array that correspond to the same element in the initial array
*/
private int getSortedIndexFromInitialTrackGroup(int groupIndex, int trackIndex) {
int sortedTrackIndex = trackIndex;
if(sortedTrackGroups != trackGroups) {
Format selectedFormat = sortedTrackGroups.get(rendererIndex).getFormat(trackIndex);
int trackHash = selectedFormat.hashCode();
for (int formatIndex = 0; formatIndex < trackGroups.get(groupIndex).length; formatIndex++) {
if(trackGroups.get(groupIndex).getFormat(formatIndex).hashCode() == trackHash) {
sortedTrackIndex = formatIndex;
break;
}
}
}
return sortedTrackIndex;
}
@RequiresNonNull("mappedTrackInfo") @RequiresNonNull("mappedTrackInfo")
private boolean shouldEnableAdaptiveSelection(int groupIndex) { private boolean shouldEnableAdaptiveSelection(int groupIndex) {
return allowAdaptiveSelections return allowAdaptiveSelections