Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@abhiint16
Last active September 7, 2023 11:10
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save abhiint16/b473e9b1111bd8bda4833c288ae6a1b4 to your computer and use it in GitHub Desktop.
Save abhiint16/b473e9b1111bd8bda4833c288ae6a1b4 to your computer and use it in GitHub Desktop.
package //your own package name here;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.support.annotation.AttrRes;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CheckedTextView;
import android.widget.LinearLayout;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
import com.google.android.exoplayer2.ui.DefaultTrackNameProvider;
import com.google.android.exoplayer2.ui.TrackNameProvider;
import com.google.android.exoplayer2.util.Assertions;
import com.narayana.nlearn.R;
import java.util.Arrays;
public class TrackSelection extends LinearLayout {
private final int selectableItemBackgroundResourceId;
private final LayoutInflater inflater;
private final CheckedTextView disableView;
private final CheckedTextView defaultView;
private final TrackSelection.ComponentListener componentListener;
private boolean allowAdaptiveSelections;
private TrackNameProvider trackNameProvider;
private CheckedTextView[][] trackViews;
private DefaultTrackSelector trackSelector;
private int rendererIndex;
private TrackGroupArray trackGroups;
private boolean isDisabled;
private @Nullable
DefaultTrackSelector.SelectionOverride override;
private String[] resoName = {"Low", "Medium", "High", "HD"};
private String[] resoNameInitial = {"Low", "Med", "High", "HD"};
private String resoInitial = "A";
GetReso getReso;
boolean b;
private Activity activityContext;
private Format format;
interface GetReso {
void getResoText(String resoText);
}
public static Pair<AlertDialog, TrackSelection> getDialog(final Activity activity, CharSequence
title, DefaultTrackSelector trackSelector, int rendererIndex, final GetReso getReso, String trackSelection, Format format) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
// 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);
final TrackSelection selectionView = dialogView.findViewById(R.id.exo_track_selection_init_view);
selectionView.init(trackSelector, rendererIndex, activity, format);
selectionView.resoInitial = trackSelection;
Dialog.OnClickListener okClickListener = (dialog, which) -> {
selectionView.applySelection();
selectionView.getReso = getReso;
selectionView.getReso.getResoText(selectionView.resoInitial);
};
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);
}
public TrackSelection(Context context) {
this(context, null);
}
public TrackSelection(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
@SuppressWarnings("nullness")
public TrackSelection(Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray attributeArray = context.getTheme().obtainStyledAttributes(new int[]{android.R.attr.selectableItemBackground});
selectableItemBackgroundResourceId = attributeArray.getResourceId(0, 0);
attributeArray.recycle();
inflater = LayoutInflater.from(context);
componentListener = new TrackSelection.ComponentListener();
trackNameProvider = new DefaultTrackNameProvider(getResources());
// View for disabling the renderer.
disableView = (CheckedTextView) inflater.inflate(android.R.layout.simple_list_item_single_choice, this, false);
disableView.setBackgroundResource(selectableItemBackgroundResourceId);
disableView.setText(com.google.android.exoplayer2.ui.R.string.exo_track_selection_none);
disableView.setEnabled(false);
disableView.setFocusable(true);
disableView.setOnClickListener(componentListener);
disableView.setVisibility(View.GONE);
addView(disableView);
// Divider view.
addView(inflater.inflate(com.google.android.exoplayer2.ui.R.layout.exo_list_divider, this, false));
// View for clearing the override to allow the selector to use its default selection logic.
defaultView = (CheckedTextView) inflater.inflate(android.R.layout.simple_list_item_single_choice, this, false);
defaultView.setBackgroundResource(selectableItemBackgroundResourceId);
defaultView.setText(com.google.android.exoplayer2.ui.R.string.exo_track_selection_auto);
defaultView.setEnabled(false);
defaultView.setFocusable(true);
defaultView.setTextColor(getResources().getColor(R.color.color_black));
defaultView.setContentDescription("A");
defaultView.setOnClickListener(componentListener);
addView(defaultView);
}
/**
* Sets whether adaptive selections (consisting of more than one track) can be made using this
* selection view.
* <p>
* <p>For the 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.
*/
public void setAllowAdaptiveSelections(boolean allowAdaptiveSelections) {
if (this.allowAdaptiveSelections != allowAdaptiveSelections) {
this.allowAdaptiveSelections = allowAdaptiveSelections;
updateViews();
}
}
/**
* Sets whether an option is available for disabling the renderer.
*
* @param showDisableOption Whether the disable option is shown.
*/
public void setShowDisableOption(boolean showDisableOption) {
disableView.setVisibility(showDisableOption ? View.VISIBLE : View.GONE);
}
/**
* 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.
*/
public void setTrackNameProvider(TrackNameProvider trackNameProvider) {
this.trackNameProvider = Assertions.checkNotNull(trackNameProvider);
updateViews();
}
/**
* Initialize the view to select tracks for a specified renderer using a {@link
* DefaultTrackSelector}.
*
* @param trackSelector The {@link DefaultTrackSelector}.
* @param rendererIndex The index of the renderer.
*/
public void init(DefaultTrackSelector trackSelector, int rendererIndex, Activity activity, Format format) {
this.trackSelector = trackSelector;
this.rendererIndex = rendererIndex;
this.activityContext = activity;
this.format = format;
updateViews();
}
private void updateViews() {
// Remove previous per-track views.
for (int i = getChildCount() - 1; i >= 3; i--) {
removeViewAt(i);
}
MappingTrackSelector.MappedTrackInfo trackInfo =
trackSelector == null ? null : trackSelector.getCurrentMappedTrackInfo();
if (trackSelector == null || trackInfo == null) {
// The view is not initialized.
disableView.setEnabled(false);
defaultView.setEnabled(false);
return;
}
disableView.setEnabled(true);
defaultView.setEnabled(true);
trackGroups = trackInfo.getTrackGroups(rendererIndex);
DefaultTrackSelector.Parameters parameters = trackSelector.getParameters();
isDisabled = parameters.getRendererDisabled(rendererIndex);
override = parameters.getSelectionOverride(rendererIndex, trackGroups);
// Add per-track views.
trackViews = new CheckedTextView[trackGroups.length][];
for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) {
TrackGroup group = trackGroups.get(groupIndex);
boolean enableAdaptiveSelections =
allowAdaptiveSelections
&& trackGroups.get(groupIndex).length > 1
&& trackInfo.getAdaptiveSupport(rendererIndex, groupIndex, false)
!= RendererCapabilities.ADAPTIVE_NOT_SUPPORTED;
trackViews[groupIndex] = new CheckedTextView[group.length];
for (int trackIndex = 0; trackIndex < group.length; trackIndex++) {
if (trackIndex == 0) {
//addView(inflater.inflate(android.R.layout.select_dialog_item, this, false));
}
int trackViewLayoutId =
enableAdaptiveSelections
? android.R.layout.simple_list_item_single_choice
: android.R.layout.simple_list_item_single_choice;
CheckedTextView trackView =
(CheckedTextView) inflater.inflate(trackViewLayoutId, this, false);
trackView.setTextColor(getResources().getColor(R.color.color_black));
trackView.setContentDescription(resoNameInitial[trackIndex] + "");
trackView.setBackgroundResource(selectableItemBackgroundResourceId);
trackView.setText(trackNameProvider.getTrackName(group.getFormat(trackIndex)));
//trackView.setText(resoName[trackIndex] + "");
if (trackInfo.getTrackSupport(rendererIndex, groupIndex, trackIndex)
== RendererCapabilities.FORMAT_HANDLED) {
trackView.setFocusable(true);
trackView.setTag(Pair.create(groupIndex, trackIndex));
trackView.setOnClickListener(componentListener);
} else {
trackView.setFocusable(false);
trackView.setEnabled(false);
}
trackViews[groupIndex][trackIndex] = trackView;
addView(trackView);
}
}
updateViewStates2();
}
private void updateViewStates() {
disableView.setChecked(isDisabled);
defaultView.setChecked(!isDisabled && override == null);
for (int i = 0; i < trackViews.length; i++) {
for (int j = 0; j < trackViews[i].length; j++) {
trackViews[i][j].setChecked(
override != null && override.groupIndex == i && override.containsTrack(j));
}
}
}
private void updateViewStates2() {
disableView.setChecked(isDisabled);
defaultView.setChecked(!isDisabled && override == null);
if (!isDisabled && override == null) {
defaultView.setText("Auto " + "( " + trackNameProvider.getTrackName(format) + " )");
}
for (int i = 0; i < trackViews.length; i++) {
for (int j = 0; j < trackViews[i].length; j++) {
b = trackViews[i][j].getContentDescription().equals(resoInitial);
trackViews[i][j].setChecked(b &&
override != null && override.groupIndex == i && override.containsTrack(j));
}
}
}
private void applySelection() {
DefaultTrackSelector.ParametersBuilder parametersBuilder = trackSelector.buildUponParameters();
parametersBuilder.setRendererDisabled(rendererIndex, isDisabled);
if (override != null) {
parametersBuilder.setSelectionOverride(rendererIndex, trackGroups, override);
} else {
parametersBuilder.clearSelectionOverrides(rendererIndex);
}
trackSelector.setParameters(parametersBuilder);
}
private void onClick(View view) {
if (view == disableView) {
onDisableViewClicked();
} else if (view == defaultView) {
onDefaultViewClicked(view);
} else {
onTrackViewClicked(view);
}
updateViewStates2();
}
private void onDisableViewClicked() {
isDisabled = true;
override = null;
}
private void onDefaultViewClicked(View view) {
resoInitial = view.getContentDescription() + "";
isDisabled = false;
override = null;
}
private void onTrackViewClicked(View view) {
isDisabled = false;
@SuppressWarnings("unchecked")
Pair<Integer, Integer> tag = (Pair<Integer, Integer>) view.getTag();
int groupIndex = tag.first;
int trackIndex = tag.second;
resoInitial = view.getContentDescription() + "";
if (override == null || override.groupIndex != groupIndex || !allowAdaptiveSelections) {
// A new override is being started.
override = new DefaultTrackSelector.SelectionOverride(groupIndex, trackIndex);
} else {
// An existing override is being modified.
int overrideLength = override.length;
int[] overrideTracks = override.tracks;
if (((CheckedTextView) view).isChecked()) {
// Remove the track from the override.
if (overrideLength == 1) {
// The last track is being removed, so the override becomes empty.
override = null;
isDisabled = true;
} else {
int[] tracks = getTracksRemoving(overrideTracks, trackIndex);
override = new DefaultTrackSelector.SelectionOverride(groupIndex, tracks);
}
} else {
int[] tracks = getTracksAdding(overrideTracks, trackIndex);
override = new DefaultTrackSelector.SelectionOverride(groupIndex, tracks);
}
}
}
private static int[] getTracksAdding(int[] tracks, int addedTrack) {
tracks = Arrays.copyOf(tracks, tracks.length + 1);
tracks[tracks.length - 1] = addedTrack;
return tracks;
}
private static int[] getTracksRemoving(int[] tracks, int removedTrack) {
int[] newTracks = new int[tracks.length - 1];
int trackCount = 0;
for (int track : tracks) {
if (track != removedTrack) {
newTracks[trackCount++] = track;
}
}
return newTracks;
}
private class ComponentListener implements OnClickListener {
@Override
public void onClick(View view) {
TrackSelection.this.onClick(view);
}
}
}
@dubariyaankit
Copy link

Hello i don't understand to how to use this class please explain

@abhiint16
Copy link
Author

Hey @dubariyaankit

You will have to add a trackselection button(any view) to your exoplayer view and then in onClickListener of that button you'll use this File as followed in this code snippet, https://gist.github.com/abhiint16/165449a1a7d1a55a8f69d23718c603c2

@dubariyaankit
Copy link

dubariyaankit commented Jun 3, 2019 via email

@abhiint16
Copy link
Author

@dubariyaankit
Copy link

dubariyaankit commented Jun 4, 2019 via email

@abhiint16
Copy link
Author

supported format for exoplayer are given in this link https://exoplayer.dev/supported-formats.html

@masschandan
Copy link

hi Abhishek Kumar,
can u check my error, Exo player video are properly working fine. but when i click on quality button that time i face this issue. and i've added the TrackSelection.java class. just copy from TrackSelectionView.java class and past it in to TrackSelection.java class. and then my CustomLayout.java class just call the TrackSelection.getDialog() method in my CustomLayout.java class.and also implement the interface GetReso in CustomLayout.... can u check my code what is the wrong in my code. it will be better i'll be send u my project file and u can easily check my code. can u send me your mail Id.my mail id : (bhanjachandan102@gmail.com)

Process: com.exoplayernewversion, PID: 3563
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources$Theme android.content.Context.getTheme()' on a null object reference
at android.app.AlertDialog.resolveDialogTheme(AlertDialog.java:222)
at android.app.AlertDialog$Builder.(AlertDialog.java:452)
at com.exoplayernewversion.TrackSelection.getDialog(TrackSelection.java:58)
at com.exoplayernewversion.CustomLayout.onClick(CustomLayout.java:266)
at android.view.View.performClick(View.java:5201)
at android.view.View$PerformClick.run(View.java:21163)
at android.os.Handler.handleCallback(Handler.java:746)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5443)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:728)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)

@langaliamayank
Copy link

                Pair<AlertDialog, NewTrackSelection> dialogPair = NewTrackSelection
                        .getDialog(PlayerActivity.this, "Video", trackSelector, 0, **PlayerActivity.this,**
                                "L", mPlayer.getVideoFormat());

Getting Error GetReso. What to write in it. Please help me

@abhiint16
Copy link
Author

What is the exact error that you're getting?

@nehme77
Copy link

nehme77 commented Jan 15, 2021

Hi abhiint16, i also have the same error GetReso (like langaliamayank), when i put the PlayerActivity
Pair<AlertDialog, NewTrackSelection> dialogPair = NewTrackSelection
.getDialog(PlayerActivity.this, "Video", trackSelector, 0, PlayerActivity.this,
"L", mPlayer.getVideoFormat());

@abhiint16
Copy link
Author

abhiint16 commented Jan 15, 2021 via email

@langaliamayank
Copy link

Pair<AlertDialog, TrackSelection> dialogPair = in.mivaanplayer.TrackSelection
.getDialog(getActivity(), "Video", trackSelector, 0,this,
"L", exoPlayer.getVideoFormat());

class PlayerActivity implement GetReso {
private String[] resoName = {"Low", "Medium", "High", "HD"};

@OverRide
public void getResoText(String resoText) {
resoName = resoText;
?
}
}

Please Abhishek Kumar guide me what should i do? Im facing some issue. in getResoText method what should i write?

@abhiint16
Copy link
Author

You can simply use resoText to feed some TextView to show to the user which resolution has been selected from the dialog.

@override public void getResoText(String resoText) { someTextViewField.setText(resoText); }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment