public final class

TrackSelectionDialogBuilder

extends java.lang.Object

 java.lang.Object

↳androidx.media3.ui.TrackSelectionDialogBuilder

Gradle dependencies

compile group: 'androidx.media3', name: 'media3-ui', version: '1.5.0-alpha01'

  • groupId: androidx.media3
  • artifactId: media3-ui
  • version: 1.5.0-alpha01

Artifact androidx.media3:media3-ui:1.5.0-alpha01 it located at Google repository (https://maven.google.com/)

Overview

Builder for a dialog with a TrackSelectionView.

Summary

Constructors
publicTrackSelectionDialogBuilder(Context context, java.lang.CharSequence title, java.util.List<Tracks.Group> trackGroups, TrackSelectionDialogBuilder.DialogCallback callback)

Creates a builder for a track selection dialog.

publicTrackSelectionDialogBuilder(Context context, java.lang.CharSequence title, Player player, int trackType)

Creates a builder for a track selection dialog.

Methods
public Dialogbuild()

Builds the dialog.

public TrackSelectionDialogBuildersetAllowAdaptiveSelections(boolean allowAdaptiveSelections)

Sets whether adaptive selections (consisting of more than one track) can be made.

public TrackSelectionDialogBuildersetAllowMultipleOverrides(boolean allowMultipleOverrides)

Sets whether multiple overrides can be set and selected, i.e.

public TrackSelectionDialogBuildersetIsDisabled(boolean isDisabled)

Sets whether the selection is initially shown as disabled.

public TrackSelectionDialogBuildersetOverride(TrackSelectionOverride override)

Sets the single initial override.

public TrackSelectionDialogBuildersetOverrides(java.util.Map<TrackGroup, TrackSelectionOverride> overrides)

Sets the initial track overrides.

public TrackSelectionDialogBuildersetShowDisableOption(boolean showDisableOption)

Sets whether an option is available for disabling the renderer.

public TrackSelectionDialogBuildersetTheme(int themeResId)

Sets the resource ID of the theme used to inflate this dialog.

public voidsetTrackFormatComparator(java.util.Comparator<Format> trackFormatComparator)

Sets a java.util.Comparator used to determine the display order of the tracks within each track group.

public TrackSelectionDialogBuildersetTrackNameProvider(TrackNameProvider trackNameProvider)

Sets the TrackNameProvider used to generate the user visible name of each track and updates the view with track names queried from the specified provider.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public TrackSelectionDialogBuilder(Context context, java.lang.CharSequence title, java.util.List<Tracks.Group> trackGroups, TrackSelectionDialogBuilder.DialogCallback callback)

Creates a builder for a track selection dialog.

Parameters:

context: The context of the dialog.
title: The title of the dialog.
trackGroups: The .
callback: The TrackSelectionDialogBuilder.DialogCallback invoked when a track selection has been made.

public TrackSelectionDialogBuilder(Context context, java.lang.CharSequence title, Player player, int trackType)

Creates a builder for a track selection dialog.

Parameters:

context: The context of the dialog.
title: The title of the dialog.
player: The Player whose tracks should be selected.
trackType: The type of tracks to show for selection.

Methods

public TrackSelectionDialogBuilder setTheme(int themeResId)

Sets the resource ID of the theme used to inflate this dialog.

Parameters:

themeResId: The resource ID of the theme.

Returns:

This builder, for convenience.

public TrackSelectionDialogBuilder setIsDisabled(boolean isDisabled)

Sets whether the selection is initially shown as disabled.

Parameters:

isDisabled: Whether the selection is initially shown as disabled.

Returns:

This builder, for convenience.

Sets the single initial override.

Parameters:

override: The initial override, or null for no override.

Returns:

This builder, for convenience.

public TrackSelectionDialogBuilder setOverrides(java.util.Map<TrackGroup, TrackSelectionOverride> overrides)

Sets the initial track overrides. Any overrides that do not correspond to track groups that were passed to the constructor will be ignored. If TrackSelectionDialogBuilder.setAllowMultipleOverrides(boolean) hasn't been set to true then all but one override will be ignored. The retained override will be the one whose track group was first in the list of track groups passed to the constructor.

Parameters:

overrides: The initially selected track overrides.

Returns:

This builder, for convenience.

public TrackSelectionDialogBuilder setAllowAdaptiveSelections(boolean allowAdaptiveSelections)

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.

Parameters:

allowAdaptiveSelections: Whether adaptive selection is enabled.

Returns:

This builder, for convenience.

public TrackSelectionDialogBuilder setAllowMultipleOverrides(boolean allowMultipleOverrides)

Sets whether multiple overrides can be set and selected, i.e. tracks from multiple track groups can be selected.

Parameters:

allowMultipleOverrides: Whether multiple track selection overrides are allowed.

Returns:

This builder, for convenience.

public TrackSelectionDialogBuilder setShowDisableOption(boolean showDisableOption)

Sets whether an option is available for disabling the renderer.

Parameters:

showDisableOption: Whether the disable option is shown.

Returns:

This builder, for convenience.

public void setTrackFormatComparator(java.util.Comparator<Format> trackFormatComparator)

Sets a java.util.Comparator used to determine the display order of the tracks within each track group.

Parameters:

trackFormatComparator: The comparator, or null to use the original order.

public TrackSelectionDialogBuilder setTrackNameProvider(TrackNameProvider trackNameProvider)

Sets the TrackNameProvider used to generate the user visible name of each track and updates the view with track names queried from the specified provider.

Parameters:

trackNameProvider: The TrackNameProvider to use, or null to use the default.

public Dialog build()

Builds the dialog.

Source

/*
 * 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 androidx.media3.ui;

import static androidx.media3.common.Player.COMMAND_GET_TRACKS;
import static androidx.media3.common.Player.COMMAND_SET_TRACK_SELECTION_PARAMETERS;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.LayoutInflater;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.annotation.StyleRes;
import androidx.media3.common.C;
import androidx.media3.common.Format;
import androidx.media3.common.Player;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackSelectionOverride;
import androidx.media3.common.TrackSelectionParameters;
import androidx.media3.common.Tracks;
import androidx.media3.common.util.UnstableApi;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

/** Builder for a dialog with a {@link TrackSelectionView}. */
@UnstableApi
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 disabled option is selected.
     * @param overrides The selected track overrides.
     */
    void onTracksSelected(boolean isDisabled, Map<TrackGroup, TrackSelectionOverride> overrides);
  }

  private final Context context;
  private final CharSequence title;
  private final List<Tracks.Group> trackGroups;
  private final DialogCallback callback;

  @StyleRes private int themeResId;
  private boolean allowAdaptiveSelections;
  private boolean allowMultipleOverrides;
  private boolean showDisableOption;
  @Nullable private TrackNameProvider trackNameProvider;
  private boolean isDisabled;
  private ImmutableMap<TrackGroup, TrackSelectionOverride> overrides;
  @Nullable private Comparator<Format> trackFormatComparator;

  /**
   * Creates a builder for a track selection dialog.
   *
   * @param context The context of the dialog.
   * @param title The title of the dialog.
   * @param trackGroups The {@link Tracks.Group track groups}.
   * @param callback The {@link DialogCallback} invoked when a track selection has been made.
   */
  public TrackSelectionDialogBuilder(
      Context context,
      CharSequence title,
      List<Tracks.Group> trackGroups,
      DialogCallback callback) {
    this.context = context;
    this.title = title;
    this.trackGroups = ImmutableList.copyOf(trackGroups);
    this.callback = callback;
    overrides = ImmutableMap.of();
  }

  /**
   * Creates a builder for a track selection dialog.
   *
   * @param context The context of the dialog.
   * @param title The title of the dialog.
   * @param player The {@link Player} whose tracks should be selected.
   * @param trackType The type of tracks to show for selection.
   */
  public TrackSelectionDialogBuilder(
      Context context, CharSequence title, Player player, @C.TrackType int trackType) {
    this.context = context;
    this.title = title;
    Tracks tracks =
        player.isCommandAvailable(COMMAND_GET_TRACKS) ? player.getCurrentTracks() : Tracks.EMPTY;
    List<Tracks.Group> allTrackGroups = tracks.getGroups();
    trackGroups = new ArrayList<>();
    for (int i = 0; i < allTrackGroups.size(); i++) {
      Tracks.Group trackGroup = allTrackGroups.get(i);
      if (trackGroup.getType() == trackType) {
        trackGroups.add(trackGroup);
      }
    }
    overrides = player.getTrackSelectionParameters().overrides;
    callback =
        (isDisabled, overrides) -> {
          if (!player.isCommandAvailable(COMMAND_SET_TRACK_SELECTION_PARAMETERS)) {
            return;
          }
          TrackSelectionParameters.Builder parametersBuilder =
              player.getTrackSelectionParameters().buildUpon();
          parametersBuilder.setTrackTypeDisabled(trackType, isDisabled);
          parametersBuilder.clearOverridesOfType(trackType);
          for (TrackSelectionOverride override : overrides.values()) {
            parametersBuilder.addOverride(override);
          }
          player.setTrackSelectionParameters(parametersBuilder.build());
        };
  }

  /**
   * Sets the resource ID of the theme used to inflate this dialog.
   *
   * @param themeResId The resource ID of the theme.
   * @return This builder, for convenience.
   */
  public TrackSelectionDialogBuilder setTheme(@StyleRes int themeResId) {
    this.themeResId = themeResId;
    return this;
  }

  /**
   * 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 single initial override.
   *
   * @param override The initial override, or {@code null} for no override.
   * @return This builder, for convenience.
   */
  public TrackSelectionDialogBuilder setOverride(@Nullable TrackSelectionOverride override) {
    return setOverrides(
        override == null
            ? Collections.emptyMap()
            : ImmutableMap.of(override.mediaTrackGroup, override));
  }

  /**
   * Sets the initial track overrides. Any overrides that do not correspond to track groups that
   * were passed to the constructor will be ignored. If {@link #setAllowMultipleOverrides(boolean)}
   * hasn't been set to {@code true} then all but one override will be ignored. The retained
   * override will be the one whose track group was first in the list of track groups passed to the
   * constructor.
   *
   * @param overrides The initially selected track overrides.
   * @return This builder, for convenience.
   */
  public TrackSelectionDialogBuilder setOverrides(
      Map<TrackGroup, TrackSelectionOverride> overrides) {
    this.overrides = ImmutableMap.copyOf(overrides);
    return this;
  }

  /**
   * Sets whether adaptive selections (consisting of more than one track) can be made.
   *
   * <p>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 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<Format> 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.
   *
   * @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 Dialog build() {
    @Nullable Dialog dialog = buildForAndroidX();
    return dialog == null ? buildForPlatform() : dialog;
  }

  private Dialog buildForPlatform() {
    AlertDialog.Builder builder = new AlertDialog.Builder(context, themeResId);

    // Inflate with the builder's context to ensure the correct style is used.
    LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
    // TODO: b/301602565 - See if there's a reasonable non-null root view group we could use here.
    @SuppressWarnings("InflateParams")
    View dialogView = dialogInflater.inflate(R.layout.exo_track_selection_dialog, /* root= */ null);
    Dialog.OnClickListener okClickListener = setUpDialogView(dialogView);

    return builder
        .setTitle(title)
        .setView(dialogView)
        .setPositiveButton(android.R.string.ok, okClickListener)
        .setNegativeButton(android.R.string.cancel, null)
        .create();
  }

  // Reflection calls can't verify null safety of return values or parameters.
  @SuppressWarnings("nullness:argument")
  @Nullable
  private Dialog buildForAndroidX() {
    try {
      // This method uses reflection to avoid a dependency on AndroidX appcompat that adds 800KB to
      // the APK size even with shrinking. See https://issuetracker.google.com/161514204.
      Class<?> builderClazz = Class.forName("androidx.appcompat.app.AlertDialog$Builder");
      Constructor<?> builderConstructor = builderClazz.getConstructor(Context.class, int.class);
      Object builder = builderConstructor.newInstance(context, themeResId);

      // Inflate with the builder's context to ensure the correct style is used.
      Context builderContext = (Context) builderClazz.getMethod("getContext").invoke(builder);
      LayoutInflater dialogInflater = LayoutInflater.from(builderContext);
      // TODO: b/301602565 - See if there's a reasonable non-null root view group we could use here.
      @SuppressWarnings("InflateParams")
      View dialogView =
          dialogInflater.inflate(R.layout.exo_track_selection_dialog, /* root= */ null);
      Dialog.OnClickListener okClickListener = setUpDialogView(dialogView);

      builderClazz.getMethod("setTitle", CharSequence.class).invoke(builder, title);
      builderClazz.getMethod("setView", View.class).invoke(builder, dialogView);
      builderClazz
          .getMethod("setPositiveButton", int.class, DialogInterface.OnClickListener.class)
          .invoke(builder, android.R.string.ok, okClickListener);
      builderClazz
          .getMethod("setNegativeButton", int.class, DialogInterface.OnClickListener.class)
          .invoke(builder, android.R.string.cancel, null);
      return (Dialog) builderClazz.getMethod("create").invoke(builder);
    } catch (ClassNotFoundException e) {
      // Expected if the AndroidX compat library is not available.
      return null;
    } catch (Exception e) {
      throw new IllegalStateException(e);
    }
  }

  private Dialog.OnClickListener setUpDialogView(View dialogView) {
    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(
        trackGroups, isDisabled, overrides, trackFormatComparator, /* listener= */ null);
    return (dialog, which) ->
        callback.onTracksSelected(selectionView.getIsDisabled(), selectionView.getOverrides());
  }
}