Add stream segments to player
This commit is contained in:
parent
8fd48a88be
commit
37aa41afae
|
@ -27,7 +27,7 @@ public final class FlingBehavior extends AppBarLayout.Behavior {
|
|||
private boolean allowScroll = true;
|
||||
private final Rect globalRect = new Rect();
|
||||
private final List<Integer> skipInterceptionOfElements = Arrays.asList(
|
||||
R.id.playQueuePanel, R.id.playbackSeekBar,
|
||||
R.id.itemsListPanel, R.id.playbackSeekBar,
|
||||
R.id.playPauseButton, R.id.playPreviousButton, R.id.playNextButton);
|
||||
|
||||
@Override
|
||||
|
|
|
@ -2283,7 +2283,7 @@ public final class VideoDetailFragment
|
|||
// Re-enable clicks
|
||||
setOverlayElementsClickable(true);
|
||||
if (player != null) {
|
||||
player.closeQueue();
|
||||
player.closeItemsList();
|
||||
}
|
||||
setOverlayLook(appBarLayout, behavior, 0);
|
||||
break;
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
package org.schabi.newpipe.info_list
|
||||
|
||||
import android.util.Log
|
||||
import com.xwray.groupie.GroupAdapter
|
||||
import com.xwray.groupie.GroupieViewHolder
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo
|
||||
import kotlin.math.max
|
||||
|
||||
/**
|
||||
* Custom RecyclerView.Adapter/GroupieAdapter for [StreamSegmentItem] for handling selection state.
|
||||
*/
|
||||
class StreamSegmentAdapter(
|
||||
private val listener: StreamSegmentListener
|
||||
) : GroupAdapter<GroupieViewHolder>() {
|
||||
|
||||
var currentIndex: Int = 0
|
||||
private set
|
||||
|
||||
/**
|
||||
* Returns `true` if the provided [StreamInfo] contains segments, `false` otherwise.
|
||||
*/
|
||||
fun setItems(info: StreamInfo): Boolean {
|
||||
if (info.streamSegments.isNotEmpty()) {
|
||||
clear()
|
||||
addAll(info.streamSegments.map { StreamSegmentItem(it, listener) })
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun selectSegment(segment: StreamSegmentItem) {
|
||||
unSelectCurrentSegment()
|
||||
currentIndex = max(0, getAdapterPosition(segment))
|
||||
segment.isSelected = true
|
||||
segment.notifyChanged(StreamSegmentItem.PAYLOAD_SELECT)
|
||||
}
|
||||
|
||||
fun selectSegmentAt(position: Int) {
|
||||
try {
|
||||
selectSegment(getGroupAtAdapterPosition(position) as StreamSegmentItem)
|
||||
} catch (e: IndexOutOfBoundsException) {
|
||||
// Just to make sure that getGroupAtAdapterPosition doesn't close the app
|
||||
// Shouldn't happen since setItems is always called before select-methods but just in case
|
||||
currentIndex = 0
|
||||
Log.e("StreamSegmentAdapter", "selectSegmentAt: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun unSelectCurrentSegment() {
|
||||
try {
|
||||
val segmentItem = getGroupAtAdapterPosition(currentIndex) as StreamSegmentItem
|
||||
currentIndex = 0
|
||||
segmentItem.isSelected = false
|
||||
segmentItem.notifyChanged(StreamSegmentItem.PAYLOAD_SELECT)
|
||||
} catch (e: IndexOutOfBoundsException) {
|
||||
// Just to make sure that getGroupAtAdapterPosition doesn't close the app
|
||||
// Shouldn't happen since setItems is always called before select-methods but just in case
|
||||
currentIndex = 0
|
||||
Log.e("StreamSegmentAdapter", "unSelectCurrentSegment: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
interface StreamSegmentListener {
|
||||
fun onItemClick(item: StreamSegmentItem, seconds: Int)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package org.schabi.newpipe.info_list
|
||||
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import com.nostra13.universalimageloader.core.ImageLoader
|
||||
import com.xwray.groupie.GroupieViewHolder
|
||||
import com.xwray.groupie.Item
|
||||
import org.schabi.newpipe.R
|
||||
import org.schabi.newpipe.extractor.stream.StreamSegment
|
||||
import org.schabi.newpipe.util.ImageDisplayConstants
|
||||
import org.schabi.newpipe.util.Localization
|
||||
|
||||
class StreamSegmentItem(
|
||||
private val item: StreamSegment,
|
||||
private val onClick: StreamSegmentAdapter.StreamSegmentListener
|
||||
) : Item<GroupieViewHolder>() {
|
||||
|
||||
companion object {
|
||||
const val PAYLOAD_SELECT = 1
|
||||
}
|
||||
|
||||
var isSelected = false
|
||||
|
||||
override fun bind(viewHolder: GroupieViewHolder, position: Int) {
|
||||
item.previewUrl?.let {
|
||||
ImageLoader.getInstance().displayImage(
|
||||
it, viewHolder.root.findViewById<ImageView>(R.id.previewImage),
|
||||
ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS
|
||||
)
|
||||
}
|
||||
viewHolder.root.findViewById<TextView>(R.id.textViewTitle).text = item.title
|
||||
viewHolder.root.findViewById<TextView>(R.id.textViewStartSeconds).text =
|
||||
Localization.getDurationString(item.startTimeSeconds.toLong())
|
||||
viewHolder.root.setOnClickListener { onClick.onItemClick(this, item.startTimeSeconds) }
|
||||
viewHolder.root.isSelected = isSelected
|
||||
}
|
||||
|
||||
override fun bind(viewHolder: GroupieViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||
if (payloads.contains(PAYLOAD_SELECT)) {
|
||||
viewHolder.root.isSelected = isSelected
|
||||
return
|
||||
}
|
||||
super.bind(viewHolder, position, payloads)
|
||||
}
|
||||
|
||||
override fun getLayout() = R.layout.item_stream_segment
|
||||
}
|
|
@ -151,7 +151,7 @@ public final class MainPlayer extends Service {
|
|||
// Android TV will handle back button in case controls will be visible
|
||||
// (one more additional unneeded click while the player is hidden)
|
||||
player.hideControls(0, 0);
|
||||
player.closeQueue();
|
||||
player.closeItemsList();
|
||||
// Notification shows information about old stream but if a user selects
|
||||
// a stream from backStack it's not actual anymore
|
||||
// So we should hide the notification at all.
|
||||
|
|
|
@ -87,9 +87,11 @@ import org.schabi.newpipe.databinding.PlayerBinding;
|
|||
import org.schabi.newpipe.databinding.PlayerPopupCloseOverlayBinding;
|
||||
import org.schabi.newpipe.extractor.MediaFormat;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||
import org.schabi.newpipe.extractor.stream.StreamSegment;
|
||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
|
||||
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
|
||||
import org.schabi.newpipe.info_list.StreamSegmentAdapter;
|
||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||
import org.schabi.newpipe.player.MainPlayer.PlayerType;
|
||||
import org.schabi.newpipe.player.event.PlayerEventListener;
|
||||
|
@ -245,6 +247,7 @@ public final class Player implements
|
|||
|
||||
private PlayQueue playQueue;
|
||||
private PlayQueueAdapter playQueueAdapter;
|
||||
private StreamSegmentAdapter segmentAdapter;
|
||||
|
||||
@Nullable private MediaSourceManager playQueueManager;
|
||||
|
||||
|
@ -301,6 +304,7 @@ public final class Player implements
|
|||
|
||||
// fullscreen player
|
||||
private boolean isQueueVisible = false;
|
||||
private boolean areSegmentsVisible = false;
|
||||
private ItemTouchHelper itemTouchHelper;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -454,7 +458,7 @@ public final class Player implements
|
|||
binding.channelTextView.setSelected(true);
|
||||
|
||||
// Prevent hiding of bottom sheet via swipe inside queue
|
||||
binding.playQueue.setNestedScrollingEnabled(false);
|
||||
binding.itemsList.setNestedScrollingEnabled(false);
|
||||
}
|
||||
|
||||
private void initPlayer(final boolean playOnReady) {
|
||||
|
@ -505,6 +509,7 @@ public final class Player implements
|
|||
binding.getRoot().setOnTouchListener(listener);
|
||||
|
||||
binding.queueButton.setOnClickListener(this);
|
||||
binding.segmentsButton.setOnClickListener(this);
|
||||
binding.repeatButton.setOnClickListener(this);
|
||||
binding.shuffleButton.setOnClickListener(this);
|
||||
|
||||
|
@ -533,7 +538,7 @@ public final class Player implements
|
|||
settingsContentObserver);
|
||||
binding.getRoot().addOnLayoutChangeListener(this::onLayoutChange);
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.playQueuePanel, (view, windowInsets) -> {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.itemsListPanel, (view, windowInsets) -> {
|
||||
final DisplayCutoutCompat cutout = windowInsets.getDisplayCutout();
|
||||
if (cutout != null) {
|
||||
view.setPadding(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(),
|
||||
|
@ -665,11 +670,11 @@ public final class Player implements
|
|||
playbackSkipSilence, playWhenReady, isMuted);
|
||||
},
|
||||
() -> {
|
||||
// Completed but not found in history
|
||||
initPlayback(newQueue, repeatMode, playbackSpeed, playbackPitch,
|
||||
playbackSkipSilence, playWhenReady, isMuted);
|
||||
}
|
||||
));
|
||||
// Completed but not found in history
|
||||
initPlayback(newQueue, repeatMode, playbackSpeed, playbackPitch,
|
||||
playbackSkipSilence, playWhenReady, isMuted);
|
||||
}
|
||||
));
|
||||
} else {
|
||||
// Good to go...
|
||||
// In a case of equal PlayQueues we can re-init old one but only when it is disposed
|
||||
|
@ -697,7 +702,7 @@ public final class Player implements
|
|||
} else {
|
||||
binding.getRoot().setVisibility(View.VISIBLE);
|
||||
initVideoPlayer();
|
||||
closeQueue();
|
||||
closeItemsList();
|
||||
// Android TV: without it focus will frame the whole player
|
||||
binding.playPauseButton.requestFocus();
|
||||
|
||||
|
@ -730,6 +735,7 @@ public final class Player implements
|
|||
playQueueAdapter.dispose();
|
||||
}
|
||||
playQueueAdapter = new PlayQueueAdapter(context, playQueue);
|
||||
segmentAdapter = new StreamSegmentAdapter(getStreamSegmentListener());
|
||||
|
||||
simpleExoPlayer.setVolume(isMuted ? 0 : 1);
|
||||
notifyQueueUpdateToListeners();
|
||||
|
@ -923,6 +929,7 @@ public final class Player implements
|
|||
binding.resizeTextView.setVisibility(View.GONE);
|
||||
binding.getRoot().findViewById(R.id.metadataView).setVisibility(View.GONE);
|
||||
binding.queueButton.setVisibility(View.GONE);
|
||||
binding.segmentsButton.setVisibility(View.GONE);
|
||||
binding.moreOptionsButton.setVisibility(View.GONE);
|
||||
binding.topControls.setOrientation(LinearLayout.HORIZONTAL);
|
||||
binding.primaryControls.getLayoutParams().width
|
||||
|
@ -939,7 +946,7 @@ public final class Player implements
|
|||
binding.topControls.setClickable(false);
|
||||
binding.topControls.setFocusable(false);
|
||||
binding.bottomControls.bringToFront();
|
||||
closeQueue();
|
||||
closeItemsList();
|
||||
} else if (videoPlayerSelected()) {
|
||||
binding.fullScreenButton.setVisibility(View.GONE);
|
||||
setupScreenRotationButton();
|
||||
|
@ -1123,7 +1130,7 @@ public final class Player implements
|
|||
}
|
||||
// Close it because when changing orientation from portrait
|
||||
// (in fullscreen mode) the size of queue layout can be larger than the screen size
|
||||
closeQueue();
|
||||
closeItemsList();
|
||||
break;
|
||||
case Intent.ACTION_SCREEN_ON:
|
||||
// Interrupt playback only when screen turns on
|
||||
|
@ -1484,6 +1491,10 @@ public final class Player implements
|
|||
|
||||
notifyProgressUpdateToListeners(currentProgress, duration, bufferPercent);
|
||||
|
||||
if (areSegmentsVisible) {
|
||||
segmentAdapter.selectSegmentAt(getNearestStreamSegmentPosition(currentProgress));
|
||||
}
|
||||
|
||||
final boolean showThumbnail = prefs.getBoolean(
|
||||
context.getString(R.string.show_thumbnail_key), true);
|
||||
// setMetadata only updates the metadata when any of the metadata keys are null
|
||||
|
@ -1696,10 +1707,10 @@ public final class Player implements
|
|||
|
||||
controlsVisibilityHandler.removeCallbacksAndMessages(null);
|
||||
controlsVisibilityHandler.postDelayed(() -> {
|
||||
showHideShadow(false, duration);
|
||||
animateView(binding.playbackControlRoot, false, duration, 0,
|
||||
this::hideSystemUIIfNeeded);
|
||||
}, delay);
|
||||
showHideShadow(false, duration);
|
||||
animateView(binding.playbackControlRoot, false, duration, 0,
|
||||
this::hideSystemUIIfNeeded);
|
||||
}, delay);
|
||||
}
|
||||
|
||||
private void showHideShadow(final boolean show, final long duration) {
|
||||
|
@ -1715,6 +1726,11 @@ public final class Player implements
|
|||
final boolean showPrev = playQueue.getIndex() != 0;
|
||||
final boolean showNext = playQueue.getIndex() + 1 != playQueue.getStreams().size();
|
||||
final boolean showQueue = playQueue.getStreams().size() > 1 && !popupPlayerSelected();
|
||||
boolean showSegment = false;
|
||||
if (currentMetadata != null) {
|
||||
showSegment = !currentMetadata.getMetadata().getStreamSegments().isEmpty()
|
||||
&& !popupPlayerSelected();
|
||||
}
|
||||
|
||||
binding.playPreviousButton.setVisibility(showPrev ? View.VISIBLE : View.INVISIBLE);
|
||||
binding.playPreviousButton.setAlpha(showPrev ? 1.0f : 0.0f);
|
||||
|
@ -1722,6 +1738,8 @@ public final class Player implements
|
|||
binding.playNextButton.setAlpha(showNext ? 1.0f : 0.0f);
|
||||
binding.queueButton.setVisibility(showQueue ? View.VISIBLE : View.GONE);
|
||||
binding.queueButton.setAlpha(showQueue ? 1.0f : 0.0f);
|
||||
binding.segmentsButton.setVisibility(showSegment ? View.VISIBLE : View.GONE);
|
||||
binding.segmentsButton.setAlpha(showSegment ? 1.0f : 0.0f);
|
||||
}
|
||||
|
||||
private void showSystemUIPartially() {
|
||||
|
@ -2725,6 +2743,17 @@ public final class Player implements
|
|||
|
||||
NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false);
|
||||
notifyMetadataUpdateToListeners();
|
||||
|
||||
if (areSegmentsVisible) {
|
||||
if (segmentAdapter.setItems(info)) {
|
||||
final int adapterPosition = getNearestStreamSegmentPosition(
|
||||
simpleExoPlayer.getCurrentPosition());
|
||||
segmentAdapter.selectSegmentAt(adapterPosition);
|
||||
binding.itemsList.scrollToPosition(adapterPosition);
|
||||
} else {
|
||||
closeItemsList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeUpdateCurrentMetadata() {
|
||||
|
@ -2787,7 +2816,7 @@ public final class Player implements
|
|||
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Play queue and streams
|
||||
// Play queue, segments and streams
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
//region
|
||||
|
||||
|
@ -2835,41 +2864,90 @@ public final class Player implements
|
|||
|
||||
hideSystemUIIfNeeded();
|
||||
buildQueue();
|
||||
//updatePlaybackButtons();//TODO verify this can be removed
|
||||
|
||||
binding.itemsListHeaderTitle.setVisibility(View.GONE);
|
||||
binding.shuffleButton.setVisibility(View.VISIBLE);
|
||||
binding.repeatButton.setVisibility(View.VISIBLE);
|
||||
|
||||
hideControls(0, 0);
|
||||
binding.playQueuePanel.requestFocus();
|
||||
animateView(binding.playQueuePanel, SLIDE_AND_ALPHA, true,
|
||||
binding.itemsListPanel.requestFocus();
|
||||
animateView(binding.itemsListPanel, SLIDE_AND_ALPHA, true,
|
||||
DEFAULT_CONTROLS_DURATION);
|
||||
|
||||
binding.playQueue.scrollToPosition(playQueue.getIndex());
|
||||
binding.itemsList.scrollToPosition(playQueue.getIndex());
|
||||
}
|
||||
|
||||
private void buildQueue() {
|
||||
binding.playQueue.setAdapter(playQueueAdapter);
|
||||
binding.playQueue.setClickable(true);
|
||||
binding.playQueue.setLongClickable(true);
|
||||
binding.itemsList.setAdapter(playQueueAdapter);
|
||||
binding.itemsList.setClickable(true);
|
||||
binding.itemsList.setLongClickable(true);
|
||||
|
||||
binding.playQueue.clearOnScrollListeners();
|
||||
binding.playQueue.addOnScrollListener(getQueueScrollListener());
|
||||
binding.itemsList.clearOnScrollListeners();
|
||||
binding.itemsList.addOnScrollListener(getQueueScrollListener());
|
||||
|
||||
itemTouchHelper = new ItemTouchHelper(getItemTouchCallback());
|
||||
itemTouchHelper.attachToRecyclerView(binding.playQueue);
|
||||
itemTouchHelper.attachToRecyclerView(binding.itemsList);
|
||||
|
||||
playQueueAdapter.setSelectedListener(getOnSelectedListener());
|
||||
|
||||
binding.playQueueClose.setOnClickListener(view -> closeQueue());
|
||||
binding.itemsListClose.setOnClickListener(view -> closeItemsList());
|
||||
}
|
||||
|
||||
public void closeQueue() {
|
||||
if (isQueueVisible) {
|
||||
private void onSegmentsClicked() {
|
||||
areSegmentsVisible = true;
|
||||
|
||||
hideSystemUIIfNeeded();
|
||||
buildSegments();
|
||||
|
||||
binding.itemsListHeaderTitle.setVisibility(View.VISIBLE);
|
||||
binding.shuffleButton.setVisibility(View.GONE);
|
||||
binding.repeatButton.setVisibility(View.GONE);
|
||||
|
||||
hideControls(0, 0);
|
||||
binding.itemsListPanel.requestFocus();
|
||||
animateView(binding.itemsListPanel, SLIDE_AND_ALPHA, true,
|
||||
DEFAULT_CONTROLS_DURATION);
|
||||
|
||||
final int adapterPosition = getNearestStreamSegmentPosition(simpleExoPlayer
|
||||
.getCurrentPosition());
|
||||
segmentAdapter.selectSegmentAt(adapterPosition);
|
||||
binding.itemsList.scrollToPosition(adapterPosition);
|
||||
}
|
||||
|
||||
private void buildSegments() {
|
||||
binding.itemsList.setAdapter(segmentAdapter);
|
||||
binding.itemsList.setClickable(true);
|
||||
binding.itemsList.setLongClickable(false);
|
||||
|
||||
binding.itemsList.clearOnScrollListeners();
|
||||
if (itemTouchHelper != null) {
|
||||
itemTouchHelper.attachToRecyclerView(null);
|
||||
}
|
||||
|
||||
if (currentMetadata != null) {
|
||||
segmentAdapter.setItems(currentMetadata.getMetadata());
|
||||
}
|
||||
|
||||
binding.shuffleButton.setVisibility(View.GONE);
|
||||
binding.repeatButton.setVisibility(View.GONE);
|
||||
binding.itemsListClose.setOnClickListener(view -> closeItemsList());
|
||||
}
|
||||
|
||||
public void closeItemsList() {
|
||||
if (isQueueVisible || areSegmentsVisible) {
|
||||
isQueueVisible = false;
|
||||
animateView(binding.playQueuePanel, SLIDE_AND_ALPHA, false,
|
||||
areSegmentsVisible = false;
|
||||
|
||||
if (itemTouchHelper != null) {
|
||||
itemTouchHelper.attachToRecyclerView(null);
|
||||
}
|
||||
|
||||
animateView(binding.itemsListPanel, SLIDE_AND_ALPHA, false,
|
||||
DEFAULT_CONTROLS_DURATION, 0, () -> {
|
||||
// Even when queueLayout is GONE it receives touch events
|
||||
// and ruins normal behavior of the app. This line fixes it
|
||||
binding.playQueuePanel.setTranslationY(
|
||||
-binding.playQueuePanel.getHeight() * 5);
|
||||
binding.itemsListPanel.setTranslationY(
|
||||
-binding.itemsListPanel.getHeight() * 5);
|
||||
});
|
||||
binding.playPauseButton.requestFocus();
|
||||
}
|
||||
|
@ -2882,12 +2960,33 @@ public final class Player implements
|
|||
if (playQueue != null && !playQueue.isComplete()) {
|
||||
playQueue.fetch();
|
||||
} else if (binding != null) {
|
||||
binding.playQueue.clearOnScrollListeners();
|
||||
binding.itemsList.clearOnScrollListeners();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private StreamSegmentAdapter.StreamSegmentListener getStreamSegmentListener() {
|
||||
return (item, seconds) -> {
|
||||
segmentAdapter.selectSegment(item);
|
||||
seekTo(seconds * 1000);
|
||||
triggerProgressUpdate();
|
||||
};
|
||||
}
|
||||
|
||||
private int getNearestStreamSegmentPosition(final long playbackPosition) {
|
||||
int nearestPosition = 0;
|
||||
final List<StreamSegment> segments = currentMetadata.getMetadata().getStreamSegments();
|
||||
|
||||
for (int i = 0; i < segments.size(); i++) {
|
||||
if (segments.get(i).getStartTimeSeconds() * 1000 > playbackPosition) {
|
||||
break;
|
||||
}
|
||||
nearestPosition++;
|
||||
}
|
||||
return Math.max(0, nearestPosition - 1);
|
||||
}
|
||||
|
||||
private ItemTouchHelper.SimpleCallback getItemTouchCallback() {
|
||||
return new PlayQueueItemTouchCallback() {
|
||||
@Override
|
||||
|
@ -3313,6 +3412,9 @@ public final class Player implements
|
|||
} else if (v.getId() == binding.queueButton.getId()) {
|
||||
onQueueClicked();
|
||||
return;
|
||||
} else if (v.getId() == binding.segmentsButton.getId()) {
|
||||
onSegmentsClicked();
|
||||
return;
|
||||
} else if (v.getId() == binding.repeatButton.getId()) {
|
||||
onRepeatClicked();
|
||||
return;
|
||||
|
@ -3610,8 +3712,8 @@ public final class Player implements
|
|||
binding.brightnessProgressBar.setMax(maxGestureLength);
|
||||
|
||||
setInitialGestureValues();
|
||||
binding.playQueuePanel.getLayoutParams().height
|
||||
= height - binding.playQueuePanel.getTop();
|
||||
binding.itemsListPanel.getLayoutParams().height
|
||||
= height - binding.itemsListPanel.getTop();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3663,7 +3765,7 @@ public final class Player implements
|
|||
if (!isFullscreen) {
|
||||
binding.playbackControlRoot.setPadding(0, 0, 0, 0);
|
||||
}
|
||||
binding.playQueuePanel.setPadding(0, 0, 0, 0);
|
||||
binding.itemsListPanel.setPadding(0, 0, 0, 0);
|
||||
notifyQueueUpdateToListeners();
|
||||
notifyMetadataUpdateToListeners();
|
||||
notifyPlaybackUpdateToListeners();
|
||||
|
|
|
@ -24,7 +24,7 @@ public class CustomBottomSheetBehavior extends BottomSheetBehavior<FrameLayout>
|
|||
private boolean skippingInterception = false;
|
||||
private final List<Integer> skipInterceptionOfElements = Arrays.asList(
|
||||
R.id.detail_content_root_layout, R.id.relatedStreamsLayout,
|
||||
R.id.playQueuePanel, R.id.viewpager, R.id.bottomControls,
|
||||
R.id.itemsListPanel, R.id.viewpager, R.id.bottomControls,
|
||||
R.id.playPauseButton, R.id.playPreviousButton, R.id.playNextButton);
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#FFFFFF"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M2,17h2v0.5L3,17.5v1h1v0.5L2,19v1h3v-4L2,16v1zM3,8h1L4,4L2,4v1h1v3zM2,11h1.8L2,13.1v0.9h3v-1L3.2,13L5,10.9L5,10L2,10v1zM7,5v2h14L21,5L7,5zM7,19h14v-2L7,17v2zM7,13h14v-2L7,11v2z" />
|
||||
</vector>
|
|
@ -191,6 +191,24 @@
|
|||
tools:ignore="ContentDescription,RtlHardcoded"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/segmentsButton"
|
||||
android:layout_width="35dp"
|
||||
android:layout_height="35dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:paddingStart="3dp"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingEnd="3dp"
|
||||
android:paddingBottom="3dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:visibility="gone"
|
||||
app:srcCompat="@drawable/ic_format_list_numbered_white_24"
|
||||
tools:ignore="ContentDescription,RtlHardcoded"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/moreOptionsButton"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -452,7 +470,7 @@
|
|||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/playQueuePanel"
|
||||
android:id="@+id/itemsListPanel"
|
||||
android:layout_width="380dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentEnd="true"
|
||||
|
@ -461,14 +479,30 @@
|
|||
tools:visibility="visible">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/playQueueControl"
|
||||
android:id="@+id/itemsListControl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/itemsListHeaderTitle"
|
||||
style="@style/TextAppearance.AppCompat.Medium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignEnd="@id/itemsListClose"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="56dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:text="@string/chapters"
|
||||
android:textColor="@android:color/white"
|
||||
android:visibility="gone" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/playQueueClose"
|
||||
android:id="@+id/itemsListClose"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
|
@ -517,10 +551,10 @@
|
|||
</RelativeLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/playQueue"
|
||||
android:id="@+id/itemsList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/playQueueControl"
|
||||
android:layout_below="@id/itemsListControl"
|
||||
android:scrollbars="vertical"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/play_queue_item" />
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selector"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="4dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/previewImage"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/play_queue_thumbnail_width"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/dummy_thumbnail"
|
||||
app:layout_constraintDimensionRatio="16:9"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/textContainer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/previewImage"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/previewImage"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:textSize="@dimen/video_item_search_title_text_size"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Lorem ipusum is widely used to create long sample text which is used here too" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewStartSeconds"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textSize="@dimen/video_item_search_upload_date_text_size"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/textViewTitle"
|
||||
tools:text="04:26" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -192,6 +192,23 @@
|
|||
app:srcCompat="@drawable/ic_list_white_24dp"
|
||||
tools:ignore="ContentDescription,RtlHardcoded" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/segmentsButton"
|
||||
android:layout_width="35dp"
|
||||
android:layout_height="35dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:paddingStart="6dp"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingEnd="6dp"
|
||||
android:paddingBottom="3dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:visibility="gone"
|
||||
app:srcCompat="@drawable/ic_format_list_numbered_white_24"
|
||||
tools:ignore="ContentDescription,RtlHardcoded" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/moreOptionsButton"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -450,7 +467,7 @@
|
|||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/playQueuePanel"
|
||||
android:id="@+id/itemsListPanel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/queue_background_color"
|
||||
|
@ -458,14 +475,30 @@
|
|||
tools:visibility="visible">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/playQueueControl"
|
||||
android:id="@+id/itemsListControl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/itemsListHeaderTitle"
|
||||
style="@style/TextAppearance.AppCompat.Medium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignEnd="@id/itemsListClose"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="56dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:text="@string/chapters"
|
||||
android:textColor="@android:color/white"
|
||||
android:visibility="gone" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/playQueueClose"
|
||||
android:id="@+id/itemsListClose"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
|
@ -514,10 +547,10 @@
|
|||
</RelativeLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/playQueue"
|
||||
android:id="@+id/itemsList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/playQueueControl"
|
||||
android:layout_below="@id/itemsListControl"
|
||||
android:scrollbars="vertical"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/play_queue_item" />
|
||||
|
|
|
@ -692,4 +692,5 @@
|
|||
<string name="show_thumbnail_title">Show thumbnail</string>
|
||||
<string name="show_thumbnail_summary">Use thumbnail for both lock screen background and notifications</string>
|
||||
<string name="recent">Recent</string>
|
||||
<string name="chapters">Chapters</string>
|
||||
</resources>
|
Loading…
Reference in New Issue