mirror of https://github.com/FongMi/TV.git
parent
783b3c03c0
commit
0077794f0f
@ -1,50 +0,0 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
xmlns:tools="http://schemas.android.com/tools" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:background="@drawable/shape_item_activated" |
||||
android:gravity="center" |
||||
android:orientation="vertical" |
||||
android:padding="@dimen/exo_styled_controls_padding"> |
||||
|
||||
<ImageView |
||||
android:id="@+id/action" |
||||
android:layout_width="52dp" |
||||
android:layout_height="52dp" |
||||
android:background="?attr/selectableItemBackground" |
||||
android:scaleType="fitXY" |
||||
android:src="@drawable/ic_play" /> |
||||
|
||||
<LinearLayout |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:orientation="horizontal"> |
||||
|
||||
<TextView |
||||
android:id="@+id/exo_position" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:textColor="@color/white" |
||||
android:textSize="16sp" |
||||
tools:text="00:00:00" /> |
||||
|
||||
<TextView |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginStart="4dp" |
||||
android:layout_marginEnd="4dp" |
||||
android:text="/" |
||||
android:textColor="@color/white" |
||||
android:textSize="16sp" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/exo_duration" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:textColor="@color/white" |
||||
android:textSize="16sp" |
||||
tools:text="00:00:00" /> |
||||
|
||||
</LinearLayout> |
||||
</LinearLayout> |
||||
@ -0,0 +1,112 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
xmlns:tools="http://schemas.android.com/tools" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent"> |
||||
|
||||
<TextView |
||||
android:id="@+id/title" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="top|start" |
||||
android:background="@drawable/shape_title" |
||||
android:letterSpacing="0.02" |
||||
android:padding="16dp" |
||||
android:shadowColor="@color/grey_700" |
||||
android:shadowDx="2" |
||||
android:shadowDy="2" |
||||
android:shadowRadius="1" |
||||
android:singleLine="true" |
||||
android:textColor="@color/white" |
||||
android:textSize="20sp" |
||||
android:visibility="gone" |
||||
tools:text="慶餘年第一季:第一集" |
||||
tools:visibility="visible" /> |
||||
|
||||
<include |
||||
android:id="@+id/progress" |
||||
layout="@layout/view_progress" |
||||
android:visibility="gone" /> |
||||
|
||||
<LinearLayout |
||||
android:id="@+id/error" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="center" |
||||
android:background="@drawable/shape_item_activated" |
||||
android:gravity="center" |
||||
android:orientation="vertical" |
||||
android:padding="16dp" |
||||
android:visibility="gone" |
||||
tools:visibility="visible"> |
||||
|
||||
<ImageView |
||||
android:layout_width="56dp" |
||||
android:layout_height="56dp" |
||||
android:scaleType="fitCenter" |
||||
android:src="@drawable/ic_vod_error" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/text" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginTop="16dp" |
||||
android:textColor="@color/white" |
||||
android:textSize="16sp" |
||||
tools:text="@string/error_play_format" /> |
||||
|
||||
</LinearLayout> |
||||
|
||||
<LinearLayout |
||||
android:id="@+id/center" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="center" |
||||
android:background="@drawable/shape_item_activated" |
||||
android:gravity="center" |
||||
android:orientation="vertical" |
||||
android:padding="16dp" |
||||
android:visibility="gone" |
||||
tools:visibility="visible"> |
||||
|
||||
<ImageView |
||||
android:id="@+id/action" |
||||
android:layout_width="56dp" |
||||
android:layout_height="56dp" |
||||
android:background="?attr/selectableItemBackground" |
||||
android:scaleType="fitCenter" |
||||
android:src="@drawable/ic_play" /> |
||||
|
||||
<LinearLayout |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:orientation="horizontal"> |
||||
|
||||
<TextView |
||||
android:id="@+id/exo_position" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:textColor="@color/white" |
||||
android:textSize="16sp" |
||||
tools:text="00:00:00" /> |
||||
|
||||
<TextView |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginStart="4dp" |
||||
android:layout_marginEnd="4dp" |
||||
android:text="/" |
||||
android:textColor="@color/white" |
||||
android:textSize="16sp" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/exo_duration" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:textColor="@color/white" |
||||
android:textSize="16sp" |
||||
tools:text="00:00:00" /> |
||||
|
||||
</LinearLayout> |
||||
</LinearLayout> |
||||
</FrameLayout> |
||||
@ -0,0 +1,272 @@ |
||||
package com.fongmi.android.tv.ui.custom; |
||||
|
||||
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.TextView; |
||||
|
||||
import androidx.annotation.NonNull; |
||||
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.fongmi.android.tv.player.Players; |
||||
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.R; |
||||
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; |
||||
|
||||
public final class TrackSelectionDialog extends DialogFragment { |
||||
|
||||
public static final ImmutableList<Integer> SUPPORTED_TRACK_TYPES = ImmutableList.of(C.TRACK_TYPE_VIDEO, C.TRACK_TYPE_AUDIO, C.TRACK_TYPE_TEXT); |
||||
private final SparseArray<TrackSelectionViewFragment> tabFragments; |
||||
private final ArrayList<Integer> tabTrackTypes; |
||||
private DialogInterface.OnClickListener onClickListener; |
||||
private DialogInterface.OnDismissListener onDismissListener; |
||||
|
||||
public TrackSelectionDialog() { |
||||
tabFragments = new SparseArray<>(); |
||||
tabTrackTypes = new ArrayList<>(); |
||||
setRetainInstance(true); |
||||
} |
||||
|
||||
/** |
||||
* Returns whether a track selection dialog will have content to display if initialized with the |
||||
* specified {@link Player}. |
||||
*/ |
||||
public static void setVisible(View view) { |
||||
view.setVisibility(willHaveContent(Players.get().exo().getCurrentTracks()) ? View.VISIBLE : View.GONE); |
||||
} |
||||
|
||||
/** |
||||
* 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(player.getCurrentTracks(), player.getTrackSelectionParameters(), true, false, player::setTrackSelectionParameters, onDismissListener); |
||||
} |
||||
|
||||
/** |
||||
* Creates a dialog for given {@link Tracks} and {@link TrackSelectionParameters}. |
||||
* |
||||
* @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(Tracks tracks, TrackSelectionParameters trackSelectionParameters, boolean allowAdaptiveSelections, boolean allowMultipleOverrides, TrackSelectionListener trackSelectionListener, DialogInterface.OnDismissListener onDismissListener) { |
||||
TrackSelectionDialog trackSelectionDialog = new TrackSelectionDialog(); |
||||
trackSelectionDialog.init(tracks, trackSelectionParameters, allowAdaptiveSelections, allowMultipleOverrides, (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<TrackGroup, TrackSelectionOverride> 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, boolean allowAdaptiveSelections, boolean allowMultipleOverrides, DialogInterface.OnClickListener onClickListener, DialogInterface.OnDismissListener onDismissListener) { |
||||
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<Tracks.Group> 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<TrackGroup, TrackSelectionOverride> getOverrides(int trackType) { |
||||
TrackSelectionViewFragment trackView = tabFragments.get(trackType); |
||||
return trackView == null ? Collections.emptyMap() : trackView.overrides; |
||||
} |
||||
|
||||
@NonNull |
||||
@Override |
||||
public Dialog onCreateDialog(Bundle savedInstanceState) { |
||||
Dialog dialog = new AppCompatDialog(getActivity()); |
||||
dialog.getWindow().setDimAmount(0); |
||||
return dialog; |
||||
} |
||||
|
||||
@Override |
||||
public void onDismiss(@NonNull DialogInterface dialog) { |
||||
super.onDismiss(dialog); |
||||
onDismissListener.onDismiss(dialog); |
||||
} |
||||
|
||||
@Override |
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { |
||||
View dialogView = inflater.inflate(com.fongmi.android.tv.R.layout.dialog_tracks, container, false); |
||||
TabLayout tabLayout = dialogView.findViewById(com.fongmi.android.tv.R.id.track_selection_dialog_tab_layout); |
||||
ViewPager viewPager = dialogView.findViewById(com.fongmi.android.tv.R.id.track_selection_dialog_view_pager); |
||||
TextView cancelButton = dialogView.findViewById(com.fongmi.android.tv.R.id.track_selection_dialog_cancel_button); |
||||
TextView okButton = dialogView.findViewById(com.fongmi.android.tv.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; |
||||
} |
||||
|
||||
public interface TrackSelectionListener { |
||||
|
||||
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 { |
||||
|
||||
private List<Tracks.Group> trackGroups; |
||||
private boolean allowAdaptiveSelections; |
||||
private boolean allowMultipleOverrides; |
||||
Map<TrackGroup, TrackSelectionOverride> overrides; |
||||
boolean isDisabled; |
||||
|
||||
public TrackSelectionViewFragment() { |
||||
setRetainInstance(true); |
||||
} |
||||
|
||||
public void init(List<Tracks.Group> trackGroups, boolean isDisabled, Map<TrackGroup, TrackSelectionOverride> 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, null, this); |
||||
return rootView; |
||||
} |
||||
|
||||
@Override |
||||
public void onTrackSelectionChanged(boolean isDisabled, @NonNull Map<TrackGroup, TrackSelectionOverride> overrides) { |
||||
this.isDisabled = isDisabled; |
||||
this.overrides = overrides; |
||||
} |
||||
} |
||||
|
||||
private final class FragmentAdapter extends FragmentPagerAdapter { |
||||
|
||||
public FragmentAdapter(FragmentManager fragmentManager) { |
||||
super(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); |
||||
} |
||||
|
||||
@NonNull |
||||
@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)); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,12 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:shape="rectangle"> |
||||
|
||||
<gradient |
||||
android:angle="270" |
||||
android:centerColor="#4D000000" |
||||
android:endColor="#00000000" |
||||
android:startColor="#99000000" |
||||
android:type="linear" /> |
||||
|
||||
</shape> |
||||
@ -0,0 +1,54 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:orientation="vertical"> |
||||
|
||||
<androidx.viewpager.widget.ViewPager |
||||
android:id="@+id/track_selection_dialog_view_pager" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="200dp"> |
||||
|
||||
<com.google.android.material.tabs.TabLayout |
||||
android:id="@+id/track_selection_dialog_tab_layout" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
app:tabGravity="fill" |
||||
app:tabMode="fixed" /> |
||||
|
||||
</androidx.viewpager.widget.ViewPager> |
||||
|
||||
<LinearLayout |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_margin="16dp" |
||||
android:orientation="horizontal"> |
||||
|
||||
<TextView |
||||
android:id="@+id/track_selection_dialog_ok_button" |
||||
android:layout_width="0dp" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginEnd="16dp" |
||||
android:layout_weight="1" |
||||
android:background="@drawable/selector_text" |
||||
android:focusable="true" |
||||
android:focusableInTouchMode="true" |
||||
android:gravity="center" |
||||
android:text="@android:string/ok" |
||||
android:textColor="@color/white" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/track_selection_dialog_cancel_button" |
||||
android:layout_width="0dp" |
||||
android:layout_height="wrap_content" |
||||
android:layout_weight="1" |
||||
android:background="@drawable/selector_text" |
||||
android:focusable="true" |
||||
android:focusableInTouchMode="true" |
||||
android:gravity="center" |
||||
android:text="@android:string/cancel" |
||||
android:textColor="@color/white" /> |
||||
|
||||
</LinearLayout> |
||||
</LinearLayout> |
||||
@ -1,26 +0,0 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
xmlns:tools="http://schemas.android.com/tools" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:background="@color/black_50" |
||||
android:gravity="center" |
||||
android:orientation="vertical" |
||||
android:padding="@dimen/exo_styled_controls_padding"> |
||||
|
||||
<ImageView |
||||
android:layout_width="64dp" |
||||
android:layout_height="64dp" |
||||
android:scaleType="fitCenter" |
||||
android:src="@drawable/ic_vod_error" /> |
||||
|
||||
<TextView |
||||
android:id="@+id/text" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginTop="16dp" |
||||
android:textColor="@color/white" |
||||
android:textSize="18sp" |
||||
tools:text="@string/error_play_format" /> |
||||
|
||||
</LinearLayout> |
||||
Loading…
Reference in new issue