diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeActivity.java index 2c41ebb18..80397d82a 100644 --- a/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeActivity.java +++ b/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeActivity.java @@ -149,7 +149,6 @@ import app.fedilab.android.peertube.client.data.PlaylistData; import app.fedilab.android.peertube.client.data.PluginData; import app.fedilab.android.peertube.client.data.VideoData; import app.fedilab.android.peertube.client.entities.Error; -import app.fedilab.android.peertube.client.entities.File; import app.fedilab.android.peertube.client.entities.MenuItemView; import app.fedilab.android.peertube.client.entities.PlaylistExist; import app.fedilab.android.peertube.client.entities.Report; @@ -159,6 +158,7 @@ import app.fedilab.android.peertube.drawer.MenuAdapter; import app.fedilab.android.peertube.drawer.MenuItemAdapter; import app.fedilab.android.peertube.helper.Helper; import app.fedilab.android.peertube.helper.HelperInstance; +import app.fedilab.android.peertube.helper.TrackSelectionDialog; import app.fedilab.android.peertube.viewmodel.CaptionsVM; import app.fedilab.android.peertube.viewmodel.CommentVM; import app.fedilab.android.peertube.viewmodel.PlaylistsVM; @@ -203,7 +203,7 @@ public class PeertubeActivity extends BasePeertubeActivity implements CommentLis private String currentCaption; private boolean isRemote; private boolean willPlayFromIntent; - + private boolean isShowingTrackSelectionDialog; private Status status; public static void hideKeyboard(Activity activity) { @@ -1765,7 +1765,7 @@ public class PeertubeActivity extends BasePeertubeActivity implements CommentLis List items = new ArrayList<>(); switch (action) { case RESOLUTION: - binding.subMenuTitle.setText(R.string.pickup_resolution); + /*binding.subMenuTitle.setText(R.string.pickup_resolution); int position = 0; for (File file : peertube.getAllFile(PeertubeActivity.this)) { @@ -1790,6 +1790,15 @@ public class PeertubeActivity extends BasePeertubeActivity implements CommentLis position++; } } + }*/ + if (!isShowingTrackSelectionDialog + && TrackSelectionDialog.willHaveContent(player)) { + isShowingTrackSelectionDialog = true; + TrackSelectionDialog trackSelectionDialog = + TrackSelectionDialog.createForPlayer( + player, + /* onDismissListener= */ dismissedDialog -> isShowingTrackSelectionDialog = false); + trackSelectionDialog.show(getSupportFragmentManager(), /* tag= */ null); } break; case SPEED: diff --git a/app/src/main/java/app/fedilab/android/peertube/client/data/VideoData.java b/app/src/main/java/app/fedilab/android/peertube/client/data/VideoData.java index 55d9bb250..1b53731cc 100644 --- a/app/src/main/java/app/fedilab/android/peertube/client/data/VideoData.java +++ b/app/src/main/java/app/fedilab/android/peertube/client/data/VideoData.java @@ -146,7 +146,7 @@ public class VideoData implements Serializable { } public List getAllFile(Context context) { - if (files != null && files.size() > 0) { + if (files != null && files.size() > 0) { //Old support return files; } else if (streamingPlaylists != null) { List files = new ArrayList<>(); @@ -156,9 +156,6 @@ public class VideoData implements Serializable { file.setFileUrl(streamingPlaylists.getPlaylistUrl()); file.setFileDownloadUrl(streamingPlaylists.getPlaylistUrl()); files.add(file); - if (streamingPlaylists.getFiles().size() > 0) { - files.addAll(streamingPlaylists.getFiles()); - } } return files; } diff --git a/app/src/main/java/app/fedilab/android/peertube/helper/TrackSelectionDialog.java b/app/src/main/java/app/fedilab/android/peertube/helper/TrackSelectionDialog.java new file mode 100644 index 000000000..897950679 --- /dev/null +++ b/app/src/main/java/app/fedilab/android/peertube/helper/TrackSelectionDialog.java @@ -0,0 +1,352 @@ +package app.fedilab.android.peertube.helper; + +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.res.Resources; +import android.os.Bundle; +import android.util.SparseArray; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatDialog; +import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; +import androidx.viewpager.widget.ViewPager; + +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.Player; +import com.google.android.exoplayer2.Tracks; +import com.google.android.exoplayer2.source.TrackGroup; +import com.google.android.exoplayer2.trackselection.TrackSelectionOverride; +import com.google.android.exoplayer2.trackselection.TrackSelectionParameters; +import com.google.android.exoplayer2.ui.TrackSelectionView; +import com.google.android.material.tabs.TabLayout; +import com.google.common.collect.ImmutableList; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import app.fedilab.android.R; + +/** + * Dialog to select tracks. + */ +public final class TrackSelectionDialog extends DialogFragment { + + public static final ImmutableList SUPPORTED_TRACK_TYPES = + ImmutableList.of(C.TRACK_TYPE_VIDEO, C.TRACK_TYPE_AUDIO, C.TRACK_TYPE_TEXT); + private final SparseArray tabFragments; + private final ArrayList tabTrackTypes; + private int titleId; + private DialogInterface.OnClickListener onClickListener; + private DialogInterface.OnDismissListener onDismissListener; + + public TrackSelectionDialog() { + tabFragments = new SparseArray<>(); + tabTrackTypes = new ArrayList<>(); + // Retain instance across activity re-creation to prevent losing access to init data. + setRetainInstance(true); + } + + /** + * Returns whether a track selection dialog will have content to display if initialized with the + * specified {@link Player}. + */ + public static boolean willHaveContent(Player player) { + return willHaveContent(player.getCurrentTracks()); + } + + /** + * Returns whether a track selection dialog will have content to display if initialized with the + * specified {@link Tracks}. + */ + public static boolean willHaveContent(Tracks tracks) { + for (Tracks.Group trackGroup : tracks.getGroups()) { + if (SUPPORTED_TRACK_TYPES.contains(trackGroup.getType())) { + return true; + } + } + return false; + } + + /** + * Creates a dialog for a given {@link Player}, whose parameters will be automatically updated + * when tracks are selected. + * + * @param player The {@link Player}. + * @param onDismissListener A {@link DialogInterface.OnDismissListener} to call when the dialog is + * dismissed. + */ + public static TrackSelectionDialog createForPlayer( + Player player, DialogInterface.OnDismissListener onDismissListener) { + return createForTracksAndParameters( + R.string.track_selection_title, + player.getCurrentTracks(), + player.getTrackSelectionParameters(), + /* allowAdaptiveSelections= */ true, + /* allowMultipleOverrides= */ false, + player::setTrackSelectionParameters, + onDismissListener); + } + + /** + * Creates a dialog for given {@link Tracks} and {@link TrackSelectionParameters}. + * + * @param titleId The resource id of the dialog title. + * @param tracks The {@link Tracks} describing the tracks to display. + * @param trackSelectionParameters The initial {@link TrackSelectionParameters}. + * @param allowAdaptiveSelections Whether adaptive selections (consisting of more than one track) + * can be made. + * @param allowMultipleOverrides Whether tracks from multiple track groups can be selected. + * @param trackSelectionListener Called when tracks are selected. + * @param onDismissListener {@link DialogInterface.OnDismissListener} called when the dialog is + * dismissed. + */ + public static TrackSelectionDialog createForTracksAndParameters( + int titleId, + Tracks tracks, + TrackSelectionParameters trackSelectionParameters, + boolean allowAdaptiveSelections, + boolean allowMultipleOverrides, + TrackSelectionListener trackSelectionListener, + DialogInterface.OnDismissListener onDismissListener) { + TrackSelectionDialog trackSelectionDialog = new TrackSelectionDialog(); + trackSelectionDialog.init( + tracks, + trackSelectionParameters, + titleId, + allowAdaptiveSelections, + allowMultipleOverrides, + /* onClickListener= */ (dialog, which) -> { + TrackSelectionParameters.Builder builder = trackSelectionParameters.buildUpon(); + for (int i = 0; i < SUPPORTED_TRACK_TYPES.size(); i++) { + int trackType = SUPPORTED_TRACK_TYPES.get(i); + builder.setTrackTypeDisabled(trackType, trackSelectionDialog.getIsDisabled(trackType)); + builder.clearOverridesOfType(trackType); + Map overrides = + trackSelectionDialog.getOverrides(trackType); + for (TrackSelectionOverride override : overrides.values()) { + builder.addOverride(override); + } + } + trackSelectionListener.onTracksSelected(builder.build()); + }, + onDismissListener); + return trackSelectionDialog; + } + + private static String getTrackTypeString(Resources resources, @C.TrackType int trackType) { + switch (trackType) { + case C.TRACK_TYPE_VIDEO: + return resources.getString(R.string.exo_track_selection_title_video); + case C.TRACK_TYPE_AUDIO: + return resources.getString(R.string.exo_track_selection_title_audio); + case C.TRACK_TYPE_TEXT: + return resources.getString(R.string.exo_track_selection_title_text); + default: + throw new IllegalArgumentException(); + } + } + + private void init( + Tracks tracks, + TrackSelectionParameters trackSelectionParameters, + int titleId, + boolean allowAdaptiveSelections, + boolean allowMultipleOverrides, + DialogInterface.OnClickListener onClickListener, + DialogInterface.OnDismissListener onDismissListener) { + this.titleId = titleId; + this.onClickListener = onClickListener; + this.onDismissListener = onDismissListener; + + for (int i = 0; i < SUPPORTED_TRACK_TYPES.size(); i++) { + @C.TrackType int trackType = SUPPORTED_TRACK_TYPES.get(i); + ArrayList trackGroups = new ArrayList<>(); + for (Tracks.Group trackGroup : tracks.getGroups()) { + if (trackGroup.getType() == trackType) { + trackGroups.add(trackGroup); + } + } + if (!trackGroups.isEmpty()) { + TrackSelectionViewFragment tabFragment = new TrackSelectionViewFragment(); + tabFragment.init( + trackGroups, + trackSelectionParameters.disabledTrackTypes.contains(trackType), + trackSelectionParameters.overrides, + allowAdaptiveSelections, + allowMultipleOverrides); + tabFragments.put(trackType, tabFragment); + tabTrackTypes.add(trackType); + } + } + } + + /** + * Returns whether the disabled option is selected for the specified track type. + * + * @param trackType The track type. + * @return Whether the disabled option is selected for the track type. + */ + public boolean getIsDisabled(int trackType) { + TrackSelectionViewFragment trackView = tabFragments.get(trackType); + return trackView != null && trackView.isDisabled; + } + + /** + * Returns the selected track overrides for the specified track type. + * + * @param trackType The track type. + * @return The track overrides for the track type. + */ + public Map getOverrides(int trackType) { + TrackSelectionViewFragment trackView = tabFragments.get(trackType); + return trackView == null ? Collections.emptyMap() : trackView.overrides; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + // We need to own the view to let tab layout work correctly on all API levels. We can't use + // AlertDialog because it owns the view itself, so we use AppCompatDialog instead, themed using + // the AlertDialog theme overlay with force-enabled title. + AppCompatDialog dialog = + new AppCompatDialog(getActivity()); + dialog.setTitle(titleId); + return dialog; + } + + @Override + public void onDismiss(DialogInterface dialog) { + super.onDismiss(dialog); + onDismissListener.onDismiss(dialog); + } + + @Override + public View onCreateView( + LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View dialogView = inflater.inflate(R.layout.track_selection_dialog, container, false); + TabLayout tabLayout = dialogView.findViewById(R.id.track_selection_dialog_tab_layout); + ViewPager viewPager = dialogView.findViewById(R.id.track_selection_dialog_view_pager); + Button cancelButton = dialogView.findViewById(R.id.track_selection_dialog_cancel_button); + Button okButton = dialogView.findViewById(R.id.track_selection_dialog_ok_button); + viewPager.setAdapter(new FragmentAdapter(getChildFragmentManager())); + tabLayout.setupWithViewPager(viewPager); + tabLayout.setVisibility(tabFragments.size() > 1 ? View.VISIBLE : View.GONE); + cancelButton.setOnClickListener(view -> dismiss()); + okButton.setOnClickListener( + view -> { + onClickListener.onClick(getDialog(), DialogInterface.BUTTON_POSITIVE); + dismiss(); + }); + return dialogView; + } + + /** + * Called when tracks are selected. + */ + public interface TrackSelectionListener { + + /** + * Called when tracks are selected. + * + * @param trackSelectionParameters A {@link TrackSelectionParameters} representing the selected + * tracks. Any manual selections are defined by {@link + * TrackSelectionParameters#disabledTrackTypes} and {@link + * TrackSelectionParameters#overrides}. + */ + void onTracksSelected(TrackSelectionParameters trackSelectionParameters); + } + + /** + * Fragment to show a track selection in tab of the track selection dialog. + */ + public static final class TrackSelectionViewFragment extends Fragment + implements TrackSelectionView.TrackSelectionListener { + + /* package */ boolean isDisabled; + /* package */ Map overrides; + private List trackGroups; + private boolean allowAdaptiveSelections; + private boolean allowMultipleOverrides; + + public TrackSelectionViewFragment() { + // Retain instance across activity re-creation to prevent losing access to init data. + setRetainInstance(true); + } + + public void init( + List trackGroups, + boolean isDisabled, + Map overrides, + boolean allowAdaptiveSelections, + boolean allowMultipleOverrides) { + this.trackGroups = trackGroups; + this.isDisabled = isDisabled; + this.allowAdaptiveSelections = allowAdaptiveSelections; + this.allowMultipleOverrides = allowMultipleOverrides; + // TrackSelectionView does this filtering internally, but we need to do it here as well to + // handle the case where the TrackSelectionView is never created. + this.overrides = + new HashMap<>( + TrackSelectionView.filterOverrides(overrides, trackGroups, allowMultipleOverrides)); + } + + @Override + public View onCreateView( + LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View rootView = + inflater.inflate( + R.layout.exo_track_selection_dialog, container, /* attachToRoot= */ false); + TrackSelectionView trackSelectionView = rootView.findViewById(R.id.exo_track_selection_view); + trackSelectionView.setShowDisableOption(true); + trackSelectionView.setAllowMultipleOverrides(allowMultipleOverrides); + trackSelectionView.setAllowAdaptiveSelections(allowAdaptiveSelections); + trackSelectionView.init( + trackGroups, + isDisabled, + overrides, + /* trackFormatComparator= */ null, + /* listener= */ this); + return rootView; + } + + @Override + public void onTrackSelectionChanged( + boolean isDisabled, Map overrides) { + this.isDisabled = isDisabled; + this.overrides = overrides; + } + } + + private final class FragmentAdapter extends FragmentPagerAdapter { + + public FragmentAdapter(FragmentManager fragmentManager) { + super(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); + } + + @Override + public Fragment getItem(int position) { + return tabFragments.get(tabTrackTypes.get(position)); + } + + @Override + public int getCount() { + return tabTrackTypes.size(); + } + + @Override + public CharSequence getPageTitle(int position) { + return getTrackTypeString(getResources(), tabTrackTypes.get(position)); + } + } +} \ No newline at end of file diff --git a/app/src/main/res/layouts/peertube/layout/track_selection_dialog.xml b/app/src/main/res/layouts/peertube/layout/track_selection_dialog.xml new file mode 100644 index 000000000..ba8e5acf1 --- /dev/null +++ b/app/src/main/res/layouts/peertube/layout/track_selection_dialog.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + +