From abb53d7ce707476e5ab22fa990070bc8d7b2179f Mon Sep 17 00:00:00 2001 From: tonihei Date: Mon, 11 Feb 2019 10:01:34 +0000 Subject: [PATCH] Support multiple overrides in TrackSelectionView and factor out Builder. Supporting multiple overrides allows to select tracks from multiple groups, if enabled. As more options are added, the creation of the dialog is moved to a separate builder class. PiperOrigin-RevId: 233366282 --- RELEASENOTES.md | 2 + .../exoplayer2/demo/DownloadTracker.java | 49 ++-- .../exoplayer2/demo/PlayerActivity.java | 13 +- .../trackselection/TrackSelectionUtil.java | 30 +++ .../ui/TrackSelectionDialogBuilder.java | 234 ++++++++++++++++++ .../exoplayer2/ui/TrackSelectionView.java | 197 +++++++-------- 6 files changed, 389 insertions(+), 136 deletions(-) create mode 100644 library/ui/src/main/java/com/google/android/exoplayer2/ui/TrackSelectionDialogBuilder.java 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)); } } }