mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-31 07:13:00 +00:00 
			
		
		
		
	Add stream segments to player
This commit is contained in:
		| @@ -27,7 +27,7 @@ public final class FlingBehavior extends AppBarLayout.Behavior { | |||||||
|     private boolean allowScroll = true; |     private boolean allowScroll = true; | ||||||
|     private final Rect globalRect = new Rect(); |     private final Rect globalRect = new Rect(); | ||||||
|     private final List<Integer> skipInterceptionOfElements = Arrays.asList( |     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); |             R.id.playPauseButton, R.id.playPreviousButton, R.id.playNextButton); | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|   | |||||||
| @@ -2283,7 +2283,7 @@ public final class VideoDetailFragment | |||||||
|                         // Re-enable clicks |                         // Re-enable clicks | ||||||
|                         setOverlayElementsClickable(true); |                         setOverlayElementsClickable(true); | ||||||
|                         if (player != null) { |                         if (player != null) { | ||||||
|                             player.closeQueue(); |                             player.closeItemsList(); | ||||||
|                         } |                         } | ||||||
|                         setOverlayLook(appBarLayout, behavior, 0); |                         setOverlayLook(appBarLayout, behavior, 0); | ||||||
|                         break; |                         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 |             // Android TV will handle back button in case controls will be visible | ||||||
|             // (one more additional unneeded click while the player is hidden) |             // (one more additional unneeded click while the player is hidden) | ||||||
|             player.hideControls(0, 0); |             player.hideControls(0, 0); | ||||||
|             player.closeQueue(); |             player.closeItemsList(); | ||||||
|             // Notification shows information about old stream but if a user selects |             // Notification shows information about old stream but if a user selects | ||||||
|             // a stream from backStack it's not actual anymore |             // a stream from backStack it's not actual anymore | ||||||
|             // So we should hide the notification at all. |             // 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.databinding.PlayerPopupCloseOverlayBinding; | ||||||
| import org.schabi.newpipe.extractor.MediaFormat; | import org.schabi.newpipe.extractor.MediaFormat; | ||||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | 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.extractor.stream.VideoStream; | ||||||
| import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; | import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; | ||||||
| import org.schabi.newpipe.fragments.detail.VideoDetailFragment; | 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.local.history.HistoryRecordManager; | ||||||
| import org.schabi.newpipe.player.MainPlayer.PlayerType; | import org.schabi.newpipe.player.MainPlayer.PlayerType; | ||||||
| import org.schabi.newpipe.player.event.PlayerEventListener; | import org.schabi.newpipe.player.event.PlayerEventListener; | ||||||
| @@ -245,6 +247,7 @@ public final class Player implements | |||||||
|  |  | ||||||
|     private PlayQueue playQueue; |     private PlayQueue playQueue; | ||||||
|     private PlayQueueAdapter playQueueAdapter; |     private PlayQueueAdapter playQueueAdapter; | ||||||
|  |     private StreamSegmentAdapter segmentAdapter; | ||||||
|  |  | ||||||
|     @Nullable private MediaSourceManager playQueueManager; |     @Nullable private MediaSourceManager playQueueManager; | ||||||
|  |  | ||||||
| @@ -301,6 +304,7 @@ public final class Player implements | |||||||
|  |  | ||||||
|     // fullscreen player |     // fullscreen player | ||||||
|     private boolean isQueueVisible = false; |     private boolean isQueueVisible = false; | ||||||
|  |     private boolean areSegmentsVisible = false; | ||||||
|     private ItemTouchHelper itemTouchHelper; |     private ItemTouchHelper itemTouchHelper; | ||||||
|  |  | ||||||
|     /*////////////////////////////////////////////////////////////////////////// |     /*////////////////////////////////////////////////////////////////////////// | ||||||
| @@ -454,7 +458,7 @@ public final class Player implements | |||||||
|         binding.channelTextView.setSelected(true); |         binding.channelTextView.setSelected(true); | ||||||
|  |  | ||||||
|         // Prevent hiding of bottom sheet via swipe inside queue |         // Prevent hiding of bottom sheet via swipe inside queue | ||||||
|         binding.playQueue.setNestedScrollingEnabled(false); |         binding.itemsList.setNestedScrollingEnabled(false); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void initPlayer(final boolean playOnReady) { |     private void initPlayer(final boolean playOnReady) { | ||||||
| @@ -505,6 +509,7 @@ public final class Player implements | |||||||
|         binding.getRoot().setOnTouchListener(listener); |         binding.getRoot().setOnTouchListener(listener); | ||||||
|  |  | ||||||
|         binding.queueButton.setOnClickListener(this); |         binding.queueButton.setOnClickListener(this); | ||||||
|  |         binding.segmentsButton.setOnClickListener(this); | ||||||
|         binding.repeatButton.setOnClickListener(this); |         binding.repeatButton.setOnClickListener(this); | ||||||
|         binding.shuffleButton.setOnClickListener(this); |         binding.shuffleButton.setOnClickListener(this); | ||||||
|  |  | ||||||
| @@ -533,7 +538,7 @@ public final class Player implements | |||||||
|                 settingsContentObserver); |                 settingsContentObserver); | ||||||
|         binding.getRoot().addOnLayoutChangeListener(this::onLayoutChange); |         binding.getRoot().addOnLayoutChangeListener(this::onLayoutChange); | ||||||
|  |  | ||||||
|         ViewCompat.setOnApplyWindowInsetsListener(binding.playQueuePanel, (view, windowInsets) -> { |         ViewCompat.setOnApplyWindowInsetsListener(binding.itemsListPanel, (view, windowInsets) -> { | ||||||
|             final DisplayCutoutCompat cutout = windowInsets.getDisplayCutout(); |             final DisplayCutoutCompat cutout = windowInsets.getDisplayCutout(); | ||||||
|             if (cutout != null) { |             if (cutout != null) { | ||||||
|                 view.setPadding(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(), |                 view.setPadding(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(), | ||||||
| @@ -665,11 +670,11 @@ public final class Player implements | |||||||
|                                         playbackSkipSilence, playWhenReady, isMuted); |                                         playbackSkipSilence, playWhenReady, isMuted); | ||||||
|                             }, |                             }, | ||||||
|                             () -> { |                             () -> { | ||||||
|                                     // Completed but not found in history |                                 // Completed but not found in history | ||||||
|                                     initPlayback(newQueue, repeatMode, playbackSpeed, playbackPitch, |                                 initPlayback(newQueue, repeatMode, playbackSpeed, playbackPitch, | ||||||
|                                             playbackSkipSilence, playWhenReady, isMuted); |                                         playbackSkipSilence, playWhenReady, isMuted); | ||||||
|                                 } |                             } | ||||||
|                         )); |                     )); | ||||||
|         } else { |         } else { | ||||||
|             // Good to go... |             // Good to go... | ||||||
|             // In a case of equal PlayQueues we can re-init old one but only when it is disposed |             // 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 { |         } else { | ||||||
|             binding.getRoot().setVisibility(View.VISIBLE); |             binding.getRoot().setVisibility(View.VISIBLE); | ||||||
|             initVideoPlayer(); |             initVideoPlayer(); | ||||||
|             closeQueue(); |             closeItemsList(); | ||||||
|             // Android TV: without it focus will frame the whole player |             // Android TV: without it focus will frame the whole player | ||||||
|             binding.playPauseButton.requestFocus(); |             binding.playPauseButton.requestFocus(); | ||||||
|  |  | ||||||
| @@ -730,6 +735,7 @@ public final class Player implements | |||||||
|             playQueueAdapter.dispose(); |             playQueueAdapter.dispose(); | ||||||
|         } |         } | ||||||
|         playQueueAdapter = new PlayQueueAdapter(context, playQueue); |         playQueueAdapter = new PlayQueueAdapter(context, playQueue); | ||||||
|  |         segmentAdapter = new StreamSegmentAdapter(getStreamSegmentListener()); | ||||||
|  |  | ||||||
|         simpleExoPlayer.setVolume(isMuted ? 0 : 1); |         simpleExoPlayer.setVolume(isMuted ? 0 : 1); | ||||||
|         notifyQueueUpdateToListeners(); |         notifyQueueUpdateToListeners(); | ||||||
| @@ -923,6 +929,7 @@ public final class Player implements | |||||||
|             binding.resizeTextView.setVisibility(View.GONE); |             binding.resizeTextView.setVisibility(View.GONE); | ||||||
|             binding.getRoot().findViewById(R.id.metadataView).setVisibility(View.GONE); |             binding.getRoot().findViewById(R.id.metadataView).setVisibility(View.GONE); | ||||||
|             binding.queueButton.setVisibility(View.GONE); |             binding.queueButton.setVisibility(View.GONE); | ||||||
|  |             binding.segmentsButton.setVisibility(View.GONE); | ||||||
|             binding.moreOptionsButton.setVisibility(View.GONE); |             binding.moreOptionsButton.setVisibility(View.GONE); | ||||||
|             binding.topControls.setOrientation(LinearLayout.HORIZONTAL); |             binding.topControls.setOrientation(LinearLayout.HORIZONTAL); | ||||||
|             binding.primaryControls.getLayoutParams().width |             binding.primaryControls.getLayoutParams().width | ||||||
| @@ -939,7 +946,7 @@ public final class Player implements | |||||||
|             binding.topControls.setClickable(false); |             binding.topControls.setClickable(false); | ||||||
|             binding.topControls.setFocusable(false); |             binding.topControls.setFocusable(false); | ||||||
|             binding.bottomControls.bringToFront(); |             binding.bottomControls.bringToFront(); | ||||||
|             closeQueue(); |             closeItemsList(); | ||||||
|         } else if (videoPlayerSelected()) { |         } else if (videoPlayerSelected()) { | ||||||
|             binding.fullScreenButton.setVisibility(View.GONE); |             binding.fullScreenButton.setVisibility(View.GONE); | ||||||
|             setupScreenRotationButton(); |             setupScreenRotationButton(); | ||||||
| @@ -1123,7 +1130,7 @@ public final class Player implements | |||||||
|                 } |                 } | ||||||
|                 // Close it because when changing orientation from portrait |                 // Close it because when changing orientation from portrait | ||||||
|                 // (in fullscreen mode) the size of queue layout can be larger than the screen size |                 // (in fullscreen mode) the size of queue layout can be larger than the screen size | ||||||
|                 closeQueue(); |                 closeItemsList(); | ||||||
|                 break; |                 break; | ||||||
|             case Intent.ACTION_SCREEN_ON: |             case Intent.ACTION_SCREEN_ON: | ||||||
|                 // Interrupt playback only when screen turns on |                 // Interrupt playback only when screen turns on | ||||||
| @@ -1484,6 +1491,10 @@ public final class Player implements | |||||||
|  |  | ||||||
|         notifyProgressUpdateToListeners(currentProgress, duration, bufferPercent); |         notifyProgressUpdateToListeners(currentProgress, duration, bufferPercent); | ||||||
|  |  | ||||||
|  |         if (areSegmentsVisible) { | ||||||
|  |             segmentAdapter.selectSegmentAt(getNearestStreamSegmentPosition(currentProgress)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         final boolean showThumbnail = prefs.getBoolean( |         final boolean showThumbnail = prefs.getBoolean( | ||||||
|                 context.getString(R.string.show_thumbnail_key), true); |                 context.getString(R.string.show_thumbnail_key), true); | ||||||
|         // setMetadata only updates the metadata when any of the metadata keys are null |         // 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.removeCallbacksAndMessages(null); | ||||||
|         controlsVisibilityHandler.postDelayed(() -> { |         controlsVisibilityHandler.postDelayed(() -> { | ||||||
|                     showHideShadow(false, duration); |             showHideShadow(false, duration); | ||||||
|                     animateView(binding.playbackControlRoot, false, duration, 0, |             animateView(binding.playbackControlRoot, false, duration, 0, | ||||||
|                             this::hideSystemUIIfNeeded); |                     this::hideSystemUIIfNeeded); | ||||||
|                 }, delay); |         }, delay); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void showHideShadow(final boolean show, final long duration) { |     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 showPrev = playQueue.getIndex() != 0; | ||||||
|         final boolean showNext = playQueue.getIndex() + 1 != playQueue.getStreams().size(); |         final boolean showNext = playQueue.getIndex() + 1 != playQueue.getStreams().size(); | ||||||
|         final boolean showQueue = playQueue.getStreams().size() > 1 && !popupPlayerSelected(); |         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.setVisibility(showPrev ? View.VISIBLE : View.INVISIBLE); | ||||||
|         binding.playPreviousButton.setAlpha(showPrev ? 1.0f : 0.0f); |         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.playNextButton.setAlpha(showNext ? 1.0f : 0.0f); | ||||||
|         binding.queueButton.setVisibility(showQueue ? View.VISIBLE : View.GONE); |         binding.queueButton.setVisibility(showQueue ? View.VISIBLE : View.GONE); | ||||||
|         binding.queueButton.setAlpha(showQueue ? 1.0f : 0.0f); |         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() { |     private void showSystemUIPartially() { | ||||||
| @@ -2725,6 +2743,17 @@ public final class Player implements | |||||||
|  |  | ||||||
|         NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false); |         NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false); | ||||||
|         notifyMetadataUpdateToListeners(); |         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() { |     private void maybeUpdateCurrentMetadata() { | ||||||
| @@ -2787,7 +2816,7 @@ public final class Player implements | |||||||
|  |  | ||||||
|  |  | ||||||
|     /*////////////////////////////////////////////////////////////////////////// |     /*////////////////////////////////////////////////////////////////////////// | ||||||
|     // Play queue and streams |     // Play queue, segments and streams | ||||||
|     //////////////////////////////////////////////////////////////////////////*/ |     //////////////////////////////////////////////////////////////////////////*/ | ||||||
|     //region |     //region | ||||||
|  |  | ||||||
| @@ -2835,41 +2864,90 @@ public final class Player implements | |||||||
|  |  | ||||||
|         hideSystemUIIfNeeded(); |         hideSystemUIIfNeeded(); | ||||||
|         buildQueue(); |         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); |         hideControls(0, 0); | ||||||
|         binding.playQueuePanel.requestFocus(); |         binding.itemsListPanel.requestFocus(); | ||||||
|         animateView(binding.playQueuePanel, SLIDE_AND_ALPHA, true, |         animateView(binding.itemsListPanel, SLIDE_AND_ALPHA, true, | ||||||
|                 DEFAULT_CONTROLS_DURATION); |                 DEFAULT_CONTROLS_DURATION); | ||||||
|  |  | ||||||
|         binding.playQueue.scrollToPosition(playQueue.getIndex()); |         binding.itemsList.scrollToPosition(playQueue.getIndex()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void buildQueue() { |     private void buildQueue() { | ||||||
|         binding.playQueue.setAdapter(playQueueAdapter); |         binding.itemsList.setAdapter(playQueueAdapter); | ||||||
|         binding.playQueue.setClickable(true); |         binding.itemsList.setClickable(true); | ||||||
|         binding.playQueue.setLongClickable(true); |         binding.itemsList.setLongClickable(true); | ||||||
|  |  | ||||||
|         binding.playQueue.clearOnScrollListeners(); |         binding.itemsList.clearOnScrollListeners(); | ||||||
|         binding.playQueue.addOnScrollListener(getQueueScrollListener()); |         binding.itemsList.addOnScrollListener(getQueueScrollListener()); | ||||||
|  |  | ||||||
|         itemTouchHelper = new ItemTouchHelper(getItemTouchCallback()); |         itemTouchHelper = new ItemTouchHelper(getItemTouchCallback()); | ||||||
|         itemTouchHelper.attachToRecyclerView(binding.playQueue); |         itemTouchHelper.attachToRecyclerView(binding.itemsList); | ||||||
|  |  | ||||||
|         playQueueAdapter.setSelectedListener(getOnSelectedListener()); |         playQueueAdapter.setSelectedListener(getOnSelectedListener()); | ||||||
|  |  | ||||||
|         binding.playQueueClose.setOnClickListener(view -> closeQueue()); |         binding.itemsListClose.setOnClickListener(view -> closeItemsList()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void closeQueue() { |     private void onSegmentsClicked() { | ||||||
|         if (isQueueVisible) { |         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; |             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, () -> { |                     DEFAULT_CONTROLS_DURATION, 0, () -> { | ||||||
|                         // Even when queueLayout is GONE it receives touch events |                         // Even when queueLayout is GONE it receives touch events | ||||||
|                         // and ruins normal behavior of the app. This line fixes it |                         // and ruins normal behavior of the app. This line fixes it | ||||||
|                         binding.playQueuePanel.setTranslationY( |                         binding.itemsListPanel.setTranslationY( | ||||||
|                                 -binding.playQueuePanel.getHeight() * 5); |                                 -binding.itemsListPanel.getHeight() * 5); | ||||||
|                     }); |                     }); | ||||||
|             binding.playPauseButton.requestFocus(); |             binding.playPauseButton.requestFocus(); | ||||||
|         } |         } | ||||||
| @@ -2882,12 +2960,33 @@ public final class Player implements | |||||||
|                 if (playQueue != null && !playQueue.isComplete()) { |                 if (playQueue != null && !playQueue.isComplete()) { | ||||||
|                     playQueue.fetch(); |                     playQueue.fetch(); | ||||||
|                 } else if (binding != null) { |                 } 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() { |     private ItemTouchHelper.SimpleCallback getItemTouchCallback() { | ||||||
|         return new PlayQueueItemTouchCallback() { |         return new PlayQueueItemTouchCallback() { | ||||||
|             @Override |             @Override | ||||||
| @@ -3313,6 +3412,9 @@ public final class Player implements | |||||||
|         } else if (v.getId() == binding.queueButton.getId()) { |         } else if (v.getId() == binding.queueButton.getId()) { | ||||||
|             onQueueClicked(); |             onQueueClicked(); | ||||||
|             return; |             return; | ||||||
|  |         } else if (v.getId() == binding.segmentsButton.getId()) { | ||||||
|  |             onSegmentsClicked(); | ||||||
|  |             return; | ||||||
|         } else if (v.getId() == binding.repeatButton.getId()) { |         } else if (v.getId() == binding.repeatButton.getId()) { | ||||||
|             onRepeatClicked(); |             onRepeatClicked(); | ||||||
|             return; |             return; | ||||||
| @@ -3610,8 +3712,8 @@ public final class Player implements | |||||||
|             binding.brightnessProgressBar.setMax(maxGestureLength); |             binding.brightnessProgressBar.setMax(maxGestureLength); | ||||||
|  |  | ||||||
|             setInitialGestureValues(); |             setInitialGestureValues(); | ||||||
|             binding.playQueuePanel.getLayoutParams().height |             binding.itemsListPanel.getLayoutParams().height | ||||||
|                     = height - binding.playQueuePanel.getTop(); |                     = height - binding.itemsListPanel.getTop(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -3663,7 +3765,7 @@ public final class Player implements | |||||||
|         if (!isFullscreen) { |         if (!isFullscreen) { | ||||||
|             binding.playbackControlRoot.setPadding(0, 0, 0, 0); |             binding.playbackControlRoot.setPadding(0, 0, 0, 0); | ||||||
|         } |         } | ||||||
|         binding.playQueuePanel.setPadding(0, 0, 0, 0); |         binding.itemsListPanel.setPadding(0, 0, 0, 0); | ||||||
|         notifyQueueUpdateToListeners(); |         notifyQueueUpdateToListeners(); | ||||||
|         notifyMetadataUpdateToListeners(); |         notifyMetadataUpdateToListeners(); | ||||||
|         notifyPlaybackUpdateToListeners(); |         notifyPlaybackUpdateToListeners(); | ||||||
|   | |||||||
| @@ -24,7 +24,7 @@ public class CustomBottomSheetBehavior extends BottomSheetBehavior<FrameLayout> | |||||||
|     private boolean skippingInterception = false; |     private boolean skippingInterception = false; | ||||||
|     private final List<Integer> skipInterceptionOfElements = Arrays.asList( |     private final List<Integer> skipInterceptionOfElements = Arrays.asList( | ||||||
|             R.id.detail_content_root_layout, R.id.relatedStreamsLayout, |             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); |             R.id.playPauseButton, R.id.playPreviousButton, R.id.playNextButton); | ||||||
|  |  | ||||||
|     @Override |     @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:ignore="ContentDescription,RtlHardcoded" | ||||||
|                         tools:visibility="visible" /> |                         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 |                     <androidx.appcompat.widget.AppCompatImageButton | ||||||
|                         android:id="@+id/moreOptionsButton" |                         android:id="@+id/moreOptionsButton" | ||||||
|                         android:layout_width="wrap_content" |                         android:layout_width="wrap_content" | ||||||
| @@ -452,7 +470,7 @@ | |||||||
|     </RelativeLayout> |     </RelativeLayout> | ||||||
|  |  | ||||||
|     <RelativeLayout |     <RelativeLayout | ||||||
|         android:id="@+id/playQueuePanel" |         android:id="@+id/itemsListPanel" | ||||||
|         android:layout_width="380dp" |         android:layout_width="380dp" | ||||||
|         android:layout_height="match_parent" |         android:layout_height="match_parent" | ||||||
|         android:layout_alignParentEnd="true" |         android:layout_alignParentEnd="true" | ||||||
| @@ -461,14 +479,30 @@ | |||||||
|         tools:visibility="visible"> |         tools:visibility="visible"> | ||||||
|  |  | ||||||
|         <RelativeLayout |         <RelativeLayout | ||||||
|             android:id="@+id/playQueueControl" |             android:id="@+id/itemsListControl" | ||||||
|             android:layout_width="match_parent" |             android:layout_width="match_parent" | ||||||
|             android:layout_height="60dp" |             android:layout_height="60dp" | ||||||
|             android:clickable="true" |             android:clickable="true" | ||||||
|             android:focusable="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 |             <androidx.appcompat.widget.AppCompatImageButton | ||||||
|                 android:id="@+id/playQueueClose" |                 android:id="@+id/itemsListClose" | ||||||
|                 android:layout_width="50dp" |                 android:layout_width="50dp" | ||||||
|                 android:layout_height="50dp" |                 android:layout_height="50dp" | ||||||
|                 android:layout_alignParentEnd="true" |                 android:layout_alignParentEnd="true" | ||||||
| @@ -517,10 +551,10 @@ | |||||||
|         </RelativeLayout> |         </RelativeLayout> | ||||||
|  |  | ||||||
|         <androidx.recyclerview.widget.RecyclerView |         <androidx.recyclerview.widget.RecyclerView | ||||||
|             android:id="@+id/playQueue" |             android:id="@+id/itemsList" | ||||||
|             android:layout_width="match_parent" |             android:layout_width="match_parent" | ||||||
|             android:layout_height="match_parent" |             android:layout_height="match_parent" | ||||||
|             android:layout_below="@id/playQueueControl" |             android:layout_below="@id/itemsListControl" | ||||||
|             android:scrollbars="vertical" |             android:scrollbars="vertical" | ||||||
|             app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" |             app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" | ||||||
|             tools:listitem="@layout/play_queue_item" /> |             tools:listitem="@layout/play_queue_item" /> | ||||||
|   | |||||||
							
								
								
									
										64
									
								
								app/src/main/res/layout/item_stream_segment.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								app/src/main/res/layout/item_stream_segment.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -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" |                         app:srcCompat="@drawable/ic_list_white_24dp" | ||||||
|                         tools:ignore="ContentDescription,RtlHardcoded" /> |                         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 |                     <androidx.appcompat.widget.AppCompatImageButton | ||||||
|                         android:id="@+id/moreOptionsButton" |                         android:id="@+id/moreOptionsButton" | ||||||
|                         android:layout_width="wrap_content" |                         android:layout_width="wrap_content" | ||||||
| @@ -450,7 +467,7 @@ | |||||||
|     </RelativeLayout> |     </RelativeLayout> | ||||||
|  |  | ||||||
|     <RelativeLayout |     <RelativeLayout | ||||||
|         android:id="@+id/playQueuePanel" |         android:id="@+id/itemsListPanel" | ||||||
|         android:layout_width="match_parent" |         android:layout_width="match_parent" | ||||||
|         android:layout_height="match_parent" |         android:layout_height="match_parent" | ||||||
|         android:background="?attr/queue_background_color" |         android:background="?attr/queue_background_color" | ||||||
| @@ -458,14 +475,30 @@ | |||||||
|         tools:visibility="visible"> |         tools:visibility="visible"> | ||||||
|  |  | ||||||
|         <RelativeLayout |         <RelativeLayout | ||||||
|             android:id="@+id/playQueueControl" |             android:id="@+id/itemsListControl" | ||||||
|             android:layout_width="match_parent" |             android:layout_width="match_parent" | ||||||
|             android:layout_height="60dp" |             android:layout_height="60dp" | ||||||
|             android:clickable="true" |             android:clickable="true" | ||||||
|             android:focusable="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 |             <androidx.appcompat.widget.AppCompatImageButton | ||||||
|                 android:id="@+id/playQueueClose" |                 android:id="@+id/itemsListClose" | ||||||
|                 android:layout_width="50dp" |                 android:layout_width="50dp" | ||||||
|                 android:layout_height="50dp" |                 android:layout_height="50dp" | ||||||
|                 android:layout_alignParentEnd="true" |                 android:layout_alignParentEnd="true" | ||||||
| @@ -514,10 +547,10 @@ | |||||||
|         </RelativeLayout> |         </RelativeLayout> | ||||||
|  |  | ||||||
|         <androidx.recyclerview.widget.RecyclerView |         <androidx.recyclerview.widget.RecyclerView | ||||||
|             android:id="@+id/playQueue" |             android:id="@+id/itemsList" | ||||||
|             android:layout_width="match_parent" |             android:layout_width="match_parent" | ||||||
|             android:layout_height="match_parent" |             android:layout_height="match_parent" | ||||||
|             android:layout_below="@id/playQueueControl" |             android:layout_below="@id/itemsListControl" | ||||||
|             android:scrollbars="vertical" |             android:scrollbars="vertical" | ||||||
|             app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" |             app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" | ||||||
|             tools:listitem="@layout/play_queue_item" /> |             tools:listitem="@layout/play_queue_item" /> | ||||||
|   | |||||||
| @@ -692,4 +692,5 @@ | |||||||
|     <string name="show_thumbnail_title">Show thumbnail</string> |     <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="show_thumbnail_summary">Use thumbnail for both lock screen background and notifications</string> | ||||||
|     <string name="recent">Recent</string> |     <string name="recent">Recent</string> | ||||||
|  |     <string name="chapters">Chapters</string> | ||||||
| </resources> | </resources> | ||||||
		Reference in New Issue
	
	Block a user
	 vkay94
					vkay94