diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 35b2d41a65..df0978fa75 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -60,6 +60,8 @@ `PlayerNotificationManager` has been fixed. Apps using `DownloadNotificationUtil` should switch to using `DownloadNotificationHelper`. +* Move creation of dialogs for `TrackSelectionView`s to + `TrackSelectionDialogBuilder` and add option to select multiple overrides. ### 2.9.5 ### diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloadTracker.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloadTracker.java index 689e7241f7..50ec5048ce 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloadTracker.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloadTracker.java @@ -24,7 +24,6 @@ import android.net.Uri; import android.os.Handler; import android.os.HandlerThread; import android.support.annotation.Nullable; -import android.util.Pair; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageButton; @@ -45,11 +44,13 @@ import com.google.android.exoplayer2.offline.StreamKey; import com.google.android.exoplayer2.scheduler.Requirements; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; +import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.SelectionOverride; import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo; import com.google.android.exoplayer2.trackselection.TrackSelection; +import com.google.android.exoplayer2.trackselection.TrackSelectionUtil; import com.google.android.exoplayer2.ui.DefaultTrackNameProvider; import com.google.android.exoplayer2.ui.TrackNameProvider; -import com.google.android.exoplayer2.ui.TrackSelectionView; +import com.google.android.exoplayer2.ui.TrackSelectionDialogBuilder; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Util; @@ -229,8 +230,7 @@ public class DownloadTracker implements DownloadManager.Listener { implements DownloadHelper.Callback, DialogInterface.OnClickListener, DialogInterface.OnDismissListener, - View.OnClickListener, - TrackSelectionView.DialogCallback { + View.OnClickListener { private final DownloadHelper downloadHelper; private final String name; @@ -290,29 +290,19 @@ public class DownloadTracker implements DownloadManager.Listener { @Override public void onClick(View v) { Integer rendererIndex = (Integer) v.getTag(); + TrackGroupArray trackGroupArray = mappedTrackInfo.getTrackGroups(rendererIndex); String dialogTitle = getTrackTypeString(mappedTrackInfo.getRendererType(rendererIndex)); - Pair dialogPair = - TrackSelectionView.getDialog( + new TrackSelectionDialogBuilder( dialog.getContext(), dialogTitle, mappedTrackInfo, rendererIndex, - parameters, - /* callback= */ this); - dialogPair.second.setShowDisableOption(true); - dialogPair.second.setAllowAdaptiveSelections(false); - dialogPair.first.show(); - } - - // TrackSelectionView.DialogCallback implementation. - - @Override - public void onTracksSelected(DefaultTrackSelector.Parameters parameters) { - for (int i = 0; i < downloadHelper.getPeriodCount(); i++) { - downloadHelper.replaceTrackSelections(/* periodIndex= */ i, parameters); - } - this.parameters = parameters; - updateSelectionList(); + (isDisabled, overrides) -> updateTracks(rendererIndex, isDisabled, overrides)) + .setShowDisableOption(true) + .setIsDisabled(parameters.getRendererDisabled(rendererIndex)) + .setOverride(parameters.getSelectionOverride(rendererIndex, trackGroupArray)) + .build() + .show(); } // DialogInterface.OnClickListener implementation. @@ -332,6 +322,21 @@ public class DownloadTracker implements DownloadManager.Listener { // Internal methods. + private void updateTracks( + int rendererIndex, boolean isDisabled, List overrides) { + parameters = + TrackSelectionUtil.updateParametersWithOverride( + parameters, + rendererIndex, + mappedTrackInfo.getTrackGroups(rendererIndex), + isDisabled, + overrides.isEmpty() ? null : overrides.get(0)); + for (int i = 0; i < downloadHelper.getPeriodCount(); i++) { + downloadHelper.replaceTrackSelections(/* periodIndex= */ i, parameters); + } + updateSelectionList(); + } + private void updateSelectionList() { selectionList.removeAllViews(); for (int i = 0; i < mappedTrackInfo.getRendererCount(); i++) { diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java index 2d4efd7f3d..5284183a90 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java @@ -16,7 +16,6 @@ package com.google.android.exoplayer2.demo; import android.app.Activity; -import android.app.AlertDialog; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; @@ -68,7 +67,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.ui.DebugTextViewHelper; import com.google.android.exoplayer2.ui.PlayerControlView; import com.google.android.exoplayer2.ui.PlayerView; -import com.google.android.exoplayer2.ui.TrackSelectionView; +import com.google.android.exoplayer2.ui.TrackSelectionDialogBuilder; import com.google.android.exoplayer2.ui.spherical.SphericalSurfaceView; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.HttpDataSource; @@ -307,11 +306,11 @@ public class PlayerActivity extends Activity || (rendererType == C.TRACK_TYPE_AUDIO && mappedTrackInfo.getTypeSupport(C.TRACK_TYPE_VIDEO) == MappedTrackInfo.RENDERER_SUPPORT_NO_TRACKS); - Pair dialogPair = - TrackSelectionView.getDialog(this, title, trackSelector, rendererIndex); - dialogPair.second.setShowDisableOption(true); - dialogPair.second.setAllowAdaptiveSelections(allowAdaptiveSelections); - dialogPair.first.show(); + new TrackSelectionDialogBuilder(/* context= */ this, title, trackSelector, rendererIndex) + .setShowDisableOption(true) + .setAllowAdaptiveSelections(allowAdaptiveSelections) + .build() + .show(); } } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectionUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectionUtil.java index 7800495a62..b94102a0b1 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectionUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectionUtil.java @@ -19,9 +19,11 @@ import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; 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.chunk.MediaChunk; import com.google.android.exoplayer2.source.chunk.MediaChunkIterator; import com.google.android.exoplayer2.source.chunk.MediaChunkListIterator; +import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.SelectionOverride; import com.google.android.exoplayer2.trackselection.TrackSelection.Definition; import com.google.android.exoplayer2.util.Assertions; import java.util.Arrays; @@ -75,6 +77,34 @@ public final class TrackSelectionUtil { return selections; } + /** + * Updates {@link DefaultTrackSelector.Parameters} with an override. + * + * @param parameters The current {@link DefaultTrackSelector.Parameters} to build upon. + * @param rendererIndex The renderer index to update. + * @param trackGroupArray The {@link TrackGroupArray} of the renderer. + * @param isDisabled Whether the renderer should be set disabled. + * @param override An optional override for the renderer. If null, no override will be set and an + * existing override for this renderer will be cleared. + * @return The updated {@link DefaultTrackSelector.Parameters}. + */ + public static DefaultTrackSelector.Parameters updateParametersWithOverride( + DefaultTrackSelector.Parameters parameters, + int rendererIndex, + TrackGroupArray trackGroupArray, + boolean isDisabled, + @Nullable SelectionOverride override) { + DefaultTrackSelector.ParametersBuilder builder = + parameters + .buildUpon() + .clearSelectionOverrides(rendererIndex) + .setRendererDisabled(rendererIndex, isDisabled); + if (override != null) { + builder.setSelectionOverride(rendererIndex, trackGroupArray, override); + } + return builder.build(); + } + /** * Returns average bitrate for chunks in bits per second. Chunks are included in average until * {@code maxDurationMs} or the first unknown length chunk. 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 new file mode 100644 index 0000000000..9c46f8b827 --- /dev/null +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/TrackSelectionDialogBuilder.java @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2.ui; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import com.google.android.exoplayer2.source.TrackGroupArray; +import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; +import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.SelectionOverride; +import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo; +import com.google.android.exoplayer2.trackselection.TrackSelectionUtil; +import com.google.android.exoplayer2.util.Assertions; +import java.util.Collections; +import java.util.List; + +/** Builder for a dialog with a {@link TrackSelectionView}. */ +public final class TrackSelectionDialogBuilder { + + /** Callback which is invoked when a track selection has been made. */ + public interface DialogCallback { + + /** + * Called when tracks are selected. + * + * @param isDisabled Whether the renderer is disabled. + * @param overrides List of selected track selection overrides for the renderer. + */ + void onTracksSelected(boolean isDisabled, List overrides); + } + + private final Context context; + private final CharSequence title; + private final MappedTrackInfo mappedTrackInfo; + private final int rendererIndex; + private final DialogCallback callback; + + private boolean allowAdaptiveSelections; + private boolean allowMultipleOverrides; + private boolean showDisableOption; + @Nullable private TrackNameProvider trackNameProvider; + private boolean isDisabled; + private List overrides; + + /** + * Creates a builder for a track selection dialog. + * + * @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 callback The {@link DialogCallback} invoked when a track selection has been made. + */ + public TrackSelectionDialogBuilder( + Context context, + CharSequence title, + MappedTrackInfo mappedTrackInfo, + int rendererIndex, + DialogCallback callback) { + this.context = context; + this.title = title; + this.mappedTrackInfo = mappedTrackInfo; + this.rendererIndex = rendererIndex; + this.callback = callback; + overrides = Collections.emptyList(); + } + + /** + * Creates a builder for a track selection dialog which automatically updates a {@link + * DefaultTrackSelector}. + * + * @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. + */ + public TrackSelectionDialogBuilder( + Context context, CharSequence title, DefaultTrackSelector trackSelector, int rendererIndex) { + this.context = context; + this.title = title; + this.mappedTrackInfo = Assertions.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))); + } + + /** + * Sets whether the selection is initially shown as disabled. + * + * @param isDisabled Whether the selection is initially shown as disabled. + * @return This builder, for convenience. + */ + public TrackSelectionDialogBuilder setIsDisabled(boolean isDisabled) { + this.isDisabled = isDisabled; + return this; + } + + /** + * Sets the initial selection override to show. + * + * @param override The initial override to show, or null for no override. + * @return This builder, for convenience. + */ + public TrackSelectionDialogBuilder setOverride(@Nullable SelectionOverride override) { + return setOverrides( + override == null ? Collections.emptyList() : Collections.singletonList(override)); + } + + /** + * Sets the list of initial selection overrides to show. + * + *

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. + * @return This builder, for convenience. + */ + public TrackSelectionDialogBuilder setOverrides(List overrides) { + this.overrides = overrides; + return this; + } + + /** + * Sets whether adaptive selections (consisting of more than one track) can be made. + * + *

For the selection view to enable adaptive selection it is necessary both for this feature to + * be enabled, and for the target renderer to support adaptation between the available tracks. + * + * @param allowAdaptiveSelections Whether adaptive selection is enabled. + * @return This builder, for convenience. + */ + public TrackSelectionDialogBuilder setAllowAdaptiveSelections(boolean allowAdaptiveSelections) { + this.allowAdaptiveSelections = allowAdaptiveSelections; + return this; + } + + /** + * Sets whether multiple overrides can be set and selected, i.e. tracks from multiple track groups + * can be selected. + * + * @param allowMultipleOverrides Whether multiple track selection overrides are allowed. + * @return This builder, for convenience. + */ + public TrackSelectionDialogBuilder setAllowMultipleOverrides(boolean allowMultipleOverrides) { + this.allowMultipleOverrides = allowMultipleOverrides; + return this; + } + + /** + * Sets whether an option is available for disabling the renderer. + * + * @param showDisableOption Whether the disable option is shown. + * @return This builder, for convenience. + */ + public TrackSelectionDialogBuilder setShowDisableOption(boolean showDisableOption) { + this.showDisableOption = showDisableOption; + return this; + } + + /** + * 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. + * + * @param trackNameProvider The {@link TrackNameProvider} to use, or null to use the default. + */ + public TrackSelectionDialogBuilder setTrackNameProvider( + @Nullable TrackNameProvider trackNameProvider) { + this.trackNameProvider = trackNameProvider; + return this; + } + + /** Builds the dialog. */ + public AlertDialog build() { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + + // Inflate with the builder's context to ensure the correct style is used. + LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext()); + View dialogView = dialogInflater.inflate(R.layout.exo_track_selection_dialog, /* root= */ null); + + TrackSelectionView selectionView = dialogView.findViewById(R.id.exo_track_selection_view); + selectionView.setAllowMultipleOverrides(allowMultipleOverrides); + selectionView.setAllowAdaptiveSelections(allowAdaptiveSelections); + selectionView.setShowDisableOption(showDisableOption); + if (trackNameProvider != null) { + selectionView.setTrackNameProvider(trackNameProvider); + } + selectionView.init(mappedTrackInfo, rendererIndex, isDisabled, overrides); + Dialog.OnClickListener okClickListener = + (dialog, which) -> + callback.onTracksSelected(selectionView.getIsDisabled(), selectionView.getOverrides()); + + return builder + .setTitle(title) + .setView(dialogView) + .setPositiveButton(android.R.string.ok, okClickListener) + .setNegativeButton(android.R.string.cancel, null) + .create(); + } +} 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 fd78631337..35b7d71b4e 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 @@ -15,14 +15,13 @@ */ package com.google.android.exoplayer2.ui; -import android.app.AlertDialog; -import android.app.Dialog; import android.content.Context; import android.content.res.TypedArray; import android.support.annotation.AttrRes; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.util.Pair; +import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; import android.widget.CheckedTextView; @@ -34,115 +33,49 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.SelectionOverride; import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo; import com.google.android.exoplayer2.util.Assertions; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** A view for making track selections. */ public class TrackSelectionView extends LinearLayout { - /** Callback which is invoked when a track selection has been made. */ - public interface DialogCallback { - - /** - * Called when track are selected. - * - * @param parameters The {@link DefaultTrackSelector.Parameters} for the selected tracks. - */ - void onTracksSelected(DefaultTrackSelector.Parameters parameters); - } - private final int selectableItemBackgroundResourceId; private final LayoutInflater inflater; private final CheckedTextView disableView; private final CheckedTextView defaultView; private final ComponentListener componentListener; + private final SparseArray overrides; private boolean allowAdaptiveSelections; + private boolean allowMultipleOverrides; private TrackNameProvider trackNameProvider; private CheckedTextView[][] trackViews; private @MonotonicNonNull MappedTrackInfo mappedTrackInfo; private int rendererIndex; - private DefaultTrackSelector.Parameters parameters; private TrackGroupArray trackGroups; private boolean isDisabled; - @Nullable private SelectionOverride override; - - /** - * Gets a pair consisting of a dialog and the {@link TrackSelectionView} that will be shown by it. - * - *

The dialog shows the current configuration of the provided {@code TrackSelector} and updates - * the parameters when closing the dialog. - * - * @param context The parent context. - * @param title The dialog's title. - * @param trackSelector The track selector. - * @param rendererIndex The index of the renderer. - * @return The dialog and the {@link TrackSelectionView} that will be shown by it. - */ - public static Pair getDialog( - Context context, CharSequence title, DefaultTrackSelector trackSelector, int rendererIndex) { - return getDialog( - context, - title, - Assertions.checkNotNull(trackSelector.getCurrentMappedTrackInfo()), - rendererIndex, - trackSelector.getParameters(), - trackSelector::setParameters); - } - - /** - * Gets a pair consisting of a dialog and the {@link TrackSelectionView} that will be shown by it. - * - * @param context The parent context. - * @param title The dialog's title. - * @param mappedTrackInfo The {@link MappedTrackInfo}. - * @param rendererIndex The index of the renderer. - * @param parameters The {@link DefaultTrackSelector.Parameters}. - * @param callback The {@link DialogCallback} invoked when the dialog is closed successfully. - * @return The dialog and the {@link TrackSelectionView} that will be shown by it. - */ - public static Pair getDialog( - Context context, - CharSequence title, - MappedTrackInfo mappedTrackInfo, - int rendererIndex, - DefaultTrackSelector.Parameters parameters, - DialogCallback callback) { - AlertDialog.Builder builder = new AlertDialog.Builder(context); - - // Inflate with the builder's context to ensure the correct style is used. - LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext()); - View dialogView = dialogInflater.inflate(R.layout.exo_track_selection_dialog, null); - - TrackSelectionView selectionView = dialogView.findViewById(R.id.exo_track_selection_view); - selectionView.init(mappedTrackInfo, rendererIndex, parameters); - Dialog.OnClickListener okClickListener = - (dialog, which) -> callback.onTracksSelected(selectionView.getSelectionParameters()); - - AlertDialog dialog = - builder - .setTitle(title) - .setView(dialogView) - .setPositiveButton(android.R.string.ok, okClickListener) - .setNegativeButton(android.R.string.cancel, null) - .create(); - return Pair.create(dialog, selectionView); - } + /** Creates a track selection view. */ public TrackSelectionView(Context context) { this(context, null); } + /** Creates a track selection view. */ public TrackSelectionView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } + /** Creates a track selection view. */ @SuppressWarnings("nullness") public TrackSelectionView( Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) { super(context, attrs, defStyleAttr); + overrides = new SparseArray<>(); + TypedArray attributeArray = context .getTheme() @@ -153,7 +86,6 @@ public class TrackSelectionView extends LinearLayout { inflater = LayoutInflater.from(context); componentListener = new ComponentListener(); trackNameProvider = new DefaultTrackNameProvider(getResources()); - parameters = DefaultTrackSelector.Parameters.DEFAULT; trackGroups = TrackGroupArray.EMPTY; // View for disabling the renderer. @@ -197,6 +129,24 @@ 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()}. + * + * @param allowMultipleOverrides Whether multiple track selection overrides 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); + } + } + updateViews(); + } + } + /** * Sets whether an option is available for disabling the renderer. * @@ -223,28 +173,42 @@ public class TrackSelectionView extends LinearLayout { * * @param mappedTrackInfo The {@link MappedTrackInfo}. * @param rendererIndex The index of the renderer. - * @param parameters The {@link DefaultTrackSelector.Parameters}. + * @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. */ public void init( MappedTrackInfo mappedTrackInfo, int rendererIndex, - DefaultTrackSelector.Parameters parameters) { + boolean isDisabled, + List overrides) { this.mappedTrackInfo = mappedTrackInfo; this.rendererIndex = rendererIndex; - this.parameters = parameters; + this.isDisabled = isDisabled; + 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); + } updateViews(); } - /** Returns the {@link DefaultTrackSelector.Parameters} for the current selection. */ - public DefaultTrackSelector.Parameters getSelectionParameters() { - DefaultTrackSelector.ParametersBuilder parametersBuilder = parameters.buildUpon(); - parametersBuilder.setRendererDisabled(rendererIndex, isDisabled); - if (override != null) { - parametersBuilder.setSelectionOverride(rendererIndex, trackGroups, override); - } else { - parametersBuilder.clearSelectionOverrides(rendererIndex); + /** Returns whether the renderer is disabled. */ + 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 parametersBuilder.build(); + return overrideList; } // Private methods. @@ -266,14 +230,19 @@ public class TrackSelectionView extends LinearLayout { trackGroups = mappedTrackInfo.getTrackGroups(rendererIndex); - isDisabled = parameters.getRendererDisabled(rendererIndex); - override = parameters.getSelectionOverride(rendererIndex, trackGroups); - // Add per-track views. + if (trackGroups.length == 1 && trackGroups.get(0).length == 1) { + // Don't add group selection if there is nothing to select from. + trackViews = new CheckedTextView[0][0]; + updateViewStates(); + return; + } trackViews = new CheckedTextView[trackGroups.length][]; + boolean enableMultipleChoiceForMultipleOverrides = + allowMultipleOverrides && trackGroups.length > 1; for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) { TrackGroup group = trackGroups.get(groupIndex); - boolean enableAdaptiveSelections = + boolean enableMultipleChoiceForAdaptiveSelections = allowAdaptiveSelections && trackGroups.get(groupIndex).length > 1 && mappedTrackInfo.getAdaptiveSupport(rendererIndex, groupIndex, false) @@ -284,7 +253,7 @@ public class TrackSelectionView extends LinearLayout { addView(inflater.inflate(R.layout.exo_list_divider, this, false)); } int trackViewLayoutId = - enableAdaptiveSelections + enableMultipleChoiceForAdaptiveSelections || enableMultipleChoiceForMultipleOverrides ? android.R.layout.simple_list_item_multiple_choice : android.R.layout.simple_list_item_single_choice; CheckedTextView trackView = @@ -310,11 +279,11 @@ public class TrackSelectionView extends LinearLayout { private void updateViewStates() { disableView.setChecked(isDisabled); - defaultView.setChecked(!isDisabled && override == null); + defaultView.setChecked(!isDisabled && overrides.size() == 0); 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.groupIndex == i && override.containsTrack(j)); + trackViews[i][j].setChecked(override != null && override.containsTrack(j)); } } } @@ -332,12 +301,12 @@ public class TrackSelectionView extends LinearLayout { private void onDisableViewClicked() { isDisabled = true; - override = null; + overrides.clear(); } private void onDefaultViewClicked() { isDisabled = false; - override = null; + overrides.clear(); } private void onTrackViewClicked(View view) { @@ -346,9 +315,21 @@ public class TrackSelectionView extends LinearLayout { Pair tag = (Pair) view.getTag(); int groupIndex = tag.first; int trackIndex = tag.second; - if (override == null || override.groupIndex != groupIndex || !allowAdaptiveSelections) { - // A new override is being started. - override = new SelectionOverride(groupIndex, trackIndex); + SelectionOverride override = overrides.get(groupIndex); + if (!allowMultipleOverrides && override == null && overrides.size() > 0) { + // A new override is being started and we don't allow multiple overrides. + overrides.clear(); + } + if (override == null || !allowAdaptiveSelections) { + // Update override for current group. + if (((CheckedTextView) view).isChecked()) { + overrides.remove(groupIndex); + if (overrides.size() == 0) { + isDisabled = true; + } + } else { + overrides.put(groupIndex, new SelectionOverride(groupIndex, trackIndex)); + } } else { // An existing override is being modified. int overrideLength = override.length; @@ -357,15 +338,17 @@ public class TrackSelectionView extends LinearLayout { // Remove the track from the override. if (overrideLength == 1) { // The last track is being removed, so the override becomes empty. - override = null; - isDisabled = true; + overrides.remove(groupIndex); + if (overrides.size() == 0) { + isDisabled = true; + } } else { int[] tracks = getTracksRemoving(overrideTracks, trackIndex); - override = new SelectionOverride(groupIndex, tracks); + overrides.put(groupIndex, new SelectionOverride(groupIndex, tracks)); } } else { int[] tracks = getTracksAdding(overrideTracks, trackIndex); - override = new SelectionOverride(groupIndex, tracks); + overrides.put(groupIndex, new SelectionOverride(groupIndex, tracks)); } } }