Merge pull request #1257 from Bnyro/master

bottom sheet for sorting
This commit is contained in:
Bnyro 2022-09-10 16:57:24 +02:00 committed by GitHub
commit 7f13fd1689
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 304 additions and 300 deletions

View File

@ -0,0 +1,42 @@
package com.github.libretube.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.databinding.BottomSheetItemBinding
import com.github.libretube.obj.BottomSheetItem
class BottomSheetAdapter(
private val items: List<BottomSheetItem>,
private val listener: (index: Int) -> Unit
) : RecyclerView.Adapter<BottomSheetViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BottomSheetViewHolder {
val binding = BottomSheetItemBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return BottomSheetViewHolder(binding)
}
override fun onBindViewHolder(holder: BottomSheetViewHolder, position: Int) {
val item = items[position]
holder.binding.apply {
title.text = if (item.currentValue != null) "${item.title} (${item.currentValue})" else item.title
if (item.drawable != null) drawable.setImageResource(item.drawable) else drawable.visibility = View.GONE
root.setOnClickListener {
listener.invoke(position)
}
}
}
override fun getItemCount(): Int {
return items.size
}
}
class BottomSheetViewHolder(
val binding: BottomSheetItemBinding
) : RecyclerView.ViewHolder(binding.root)

View File

@ -60,7 +60,7 @@ import com.github.libretube.extensions.await
import com.github.libretube.extensions.formatShort
import com.github.libretube.extensions.hideKeyboard
import com.github.libretube.extensions.toID
import com.github.libretube.interfaces.OnlinePlayerOptionsInterface
import com.github.libretube.interfaces.PlayerOptionsInterface
import com.github.libretube.models.PlayerViewModel
import com.github.libretube.obj.ChapterSegment
import com.github.libretube.obj.Segment
@ -399,7 +399,7 @@ class PlayerFragment : BaseFragment() {
}
}
private val onlinePlayerOptionsInterface = object : OnlinePlayerOptionsInterface {
private val onlinePlayerOptionsInterface = object : PlayerOptionsInterface {
override fun onCaptionClicked() {
if (!this@PlayerFragment::streams.isInitialized ||
streams.subtitles == null ||

View File

@ -16,8 +16,9 @@ import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.databinding.FragmentSubscriptionsBinding
import com.github.libretube.extensions.BaseFragment
import com.github.libretube.models.SubscriptionsViewModel
import com.github.libretube.obj.BottomSheetItem
import com.github.libretube.util.PreferenceHelper
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.github.libretube.views.BottomSheet
class SubscriptionsFragment : BaseFragment() {
private lateinit var binding: FragmentSubscriptionsBinding
@ -107,15 +108,20 @@ class SubscriptionsFragment : BaseFragment() {
private fun showSortDialog() {
val sortOptions = resources.getStringArray(R.array.sortOptions)
val sortOptionValues = resources.getStringArray(R.array.sortOptionsValues)
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.sort)
.setItems(sortOptions) { _, index ->
val items = mutableListOf<BottomSheetItem>()
sortOptions.forEach {
items += BottomSheetItem(it)
}
val bottomSheet = BottomSheet().apply {
setItems(items) { index ->
binding.sortTV.text = sortOptions[index]
sortOrder = sortOptionValues[index]
showFeed()
}
.setNegativeButton(R.string.cancel, null)
.show()
}
bottomSheet.show(childFragmentManager, null)
}
private fun showFeed() {

View File

@ -1,7 +0,0 @@
package com.github.libretube.interfaces
interface OnlinePlayerOptionsInterface {
fun onCaptionClicked()
fun onQualityClicked()
}

View File

@ -1,11 +1,7 @@
package com.github.libretube.interfaces
interface PlayerOptionsInterface {
fun onAutoplayClicked()
fun onCaptionClicked()
fun onPlaybackSpeedClicked()
fun onResizeModeClicked()
fun onRepeatModeClicked()
fun onQualityClicked()
}

View File

@ -0,0 +1,7 @@
package com.github.libretube.obj
data class BottomSheetItem(
val title: String,
val drawable: Int? = null,
val currentValue: String? = null
)

View File

@ -0,0 +1,51 @@
package com.github.libretube.views
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import com.github.libretube.adapters.BottomSheetAdapter
import com.github.libretube.databinding.BottomSheetBinding
import com.github.libretube.obj.BottomSheetItem
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
class BottomSheet : BottomSheetDialogFragment() {
private lateinit var items: List<BottomSheetItem>
private lateinit var listener: (index: Int) -> Unit
private lateinit var binding: BottomSheetBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
dialog!!.setOnShowListener { dialog ->
val d = dialog as BottomSheetDialog
val bottomSheetInternal =
d.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)!!
BottomSheetBehavior.from(bottomSheetInternal).state =
BottomSheetBehavior.STATE_EXPANDED
}
binding = BottomSheetBinding.inflate(layoutInflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.optionsRecycler.layoutManager = LinearLayoutManager(requireContext())
binding.optionsRecycler.adapter = BottomSheetAdapter(items, listener)
}
fun setItems(items: List<BottomSheetItem>, listener: (index: Int) -> Unit) {
this.items = items
this.listener = { index ->
listener.invoke(index)
dialog?.dismiss()
}
}
}

View File

@ -18,8 +18,8 @@ import com.github.libretube.databinding.DoubleTapOverlayBinding
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
import com.github.libretube.extensions.setSliderRangeAndValue
import com.github.libretube.interfaces.DoubleTapInterface
import com.github.libretube.interfaces.OnlinePlayerOptionsInterface
import com.github.libretube.interfaces.PlayerOptionsInterface
import com.github.libretube.obj.BottomSheetItem
import com.github.libretube.util.DoubleTapListener
import com.github.libretube.util.PreferenceHelper
import com.google.android.exoplayer2.trackselection.TrackSelector
@ -40,7 +40,7 @@ internal class CustomExoPlayerView(
* Objects from the parent fragment
*/
private var doubleTapListener: DoubleTapInterface? = null
private var onlinePlayerOptionsInterface: OnlinePlayerOptionsInterface? = null
private var playerOptionsInterface: PlayerOptionsInterface? = null
private lateinit var childFragmentManager: FragmentManager
private var trackSelector: TrackSelector? = null
@ -85,12 +85,12 @@ internal class CustomExoPlayerView(
fun initialize(
childFragmentManager: FragmentManager,
playerViewInterface: OnlinePlayerOptionsInterface?,
playerViewInterface: PlayerOptionsInterface?,
doubleTapOverlayBinding: DoubleTapOverlayBinding,
trackSelector: TrackSelector?
) {
this.childFragmentManager = childFragmentManager
this.onlinePlayerOptionsInterface = playerViewInterface
this.playerOptionsInterface = playerViewInterface
this.doubleTapOverlayBinding = doubleTapOverlayBinding
this.trackSelector = trackSelector
@ -99,7 +99,7 @@ internal class CustomExoPlayerView(
enableDoubleTapToSeek()
initializeAdvancedOptions()
initializeAdvancedOptions(context)
// locking the player
binding.lockPlayer.setOnClickListener {
@ -139,48 +139,77 @@ internal class CustomExoPlayerView(
return false
}
private fun initializeAdvancedOptions() {
private fun initializeAdvancedOptions(context: Context) {
binding.toggleOptions.setOnClickListener {
val bottomSheetFragment = PlayerOptionsBottomSheet().apply {
setOnClickListeners(
playerOptionsInterface,
onlinePlayerOptionsInterface
val bottomSheetFragment = BottomSheet().apply {
val items = mutableListOf(
BottomSheetItem(
context.getString(R.string.player_autoplay),
R.drawable.ic_play,
if (autoplayEnabled) {
context.getString(R.string.enabled)
} else {
context.getString(R.string.disabled)
}
),
BottomSheetItem(
context.getString(R.string.repeat_mode),
R.drawable.ic_repeat,
if (player?.repeatMode == RepeatModeUtil.REPEAT_TOGGLE_MODE_NONE) {
context.getString(R.string.repeat_mode_none)
} else {
context.getString(R.string.repeat_mode_current)
}
),
BottomSheetItem(
context.getString(R.string.player_resize_mode),
R.drawable.ic_aspect_ratio,
when (resizeMode) {
AspectRatioFrameLayout.RESIZE_MODE_FIT -> context.getString(R.string.resize_mode_fit)
AspectRatioFrameLayout.RESIZE_MODE_FILL -> context.getString(R.string.resize_mode_fill)
else -> context.getString(R.string.resize_mode_zoom)
}
),
BottomSheetItem(
context.getString(R.string.playback_speed),
R.drawable.ic_speed,
"${player?.playbackParameters?.speed
.toString()
.replace(".0", "")
}x"
)
)
// set the auto play mode
currentAutoplayMode = if (autoplayEnabled) {
context?.getString(R.string.enabled)
} else {
context?.getString(R.string.disabled)
if (playerOptionsInterface != null) {
items.add(
BottomSheetItem(
context.getString(R.string.quality),
R.drawable.ic_hd,
"${player?.videoSize?.height}p"
)
)
items.add(
BottomSheetItem(
context.getString(R.string.captions),
R.drawable.ic_caption,
if (trackSelector != null && trackSelector!!.parameters.preferredTextLanguages.isNotEmpty()) {
trackSelector!!.parameters.preferredTextLanguages[0]
} else {
context.getString(R.string.none)
}
)
)
}
// set the current caption language
currentCaptions =
if (trackSelector != null && trackSelector!!.parameters.preferredTextLanguages.isNotEmpty()) {
trackSelector!!.parameters.preferredTextLanguages[0]
} else {
context?.getString(R.string.none)
setItems(items) { index ->
when (index) {
0 -> onAutoplayClicked()
1 -> onRepeatModeClicked()
2 -> onResizeModeClicked()
3 -> onPlaybackSpeedClicked()
4 -> playerOptionsInterface?.onQualityClicked()
5 -> playerOptionsInterface?.onCaptionClicked()
}
// set the playback speed
currentPlaybackSpeed = "${
player?.playbackParameters?.speed.toString()
.replace(".0", "")
}x"
// set the quality text
val quality = player?.videoSize?.height
if (quality != 0) {
currentQuality = "${quality}p"
}
// set the repeat mode
currentRepeatMode =
if (player?.repeatMode == RepeatModeUtil.REPEAT_TOGGLE_MODE_NONE) {
context?.getString(R.string.repeat_mode_none)
} else {
context?.getString(R.string.repeat_mode_current)
}
// set the aspect ratio mode
currentResizeMode = when (resizeMode) {
AspectRatioFrameLayout.RESIZE_MODE_FIT -> context?.getString(R.string.resize_mode_fit)
AspectRatioFrameLayout.RESIZE_MODE_FILL -> context?.getString(R.string.resize_mode_fill)
else -> context?.getString(R.string.resize_mode_zoom)
}
}
bottomSheetFragment.show(childFragmentManager, null)
@ -282,81 +311,80 @@ internal class CustomExoPlayerView(
}
}
private val playerOptionsInterface = object : PlayerOptionsInterface {
override fun onAutoplayClicked() {
// autoplay options dialog
MaterialAlertDialogBuilder(context)
.setTitle(R.string.player_autoplay)
.setItems(
arrayOf(
context.getString(R.string.enabled),
context.getString(R.string.disabled)
)
) { _, index ->
when (index) {
0 -> autoplayEnabled = true
1 -> autoplayEnabled = false
}
fun onAutoplayClicked() {
// autoplay options dialog
MaterialAlertDialogBuilder(context)
.setTitle(R.string.player_autoplay)
.setItems(
arrayOf(
context.getString(R.string.enabled),
context.getString(R.string.disabled)
)
) { _, index ->
when (index) {
0 -> autoplayEnabled = true
1 -> autoplayEnabled = false
}
.show()
}
}
.show()
}
override fun onPlaybackSpeedClicked() {
val playbackSpeedBinding = DialogSliderBinding.inflate(
LayoutInflater.from(context)
)
playbackSpeedBinding.slider.setSliderRangeAndValue(
PreferenceRanges.playbackSpeed
)
playbackSpeedBinding.slider.value = player?.playbackParameters?.speed ?: 1f
// change playback speed dialog
MaterialAlertDialogBuilder(context)
.setTitle(R.string.change_playback_speed)
.setView(playbackSpeedBinding.root)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.okay) { _, _ ->
player?.setPlaybackSpeed(
playbackSpeedBinding.slider.value
)
}
.show()
}
fun onPlaybackSpeedClicked() {
val playbackSpeedBinding = DialogSliderBinding.inflate(
LayoutInflater.from(context)
)
playbackSpeedBinding.slider.setSliderRangeAndValue(
PreferenceRanges.playbackSpeed
)
playbackSpeedBinding.slider.value = player?.playbackParameters?.speed ?: 1f
// change playback speed dialog
MaterialAlertDialogBuilder(context)
.setTitle(R.string.change_playback_speed)
.setView(playbackSpeedBinding.root)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.okay) { _, _ ->
player?.setPlaybackSpeed(
playbackSpeedBinding.slider.value
)
}
.show()
}
override fun onResizeModeClicked() {
// switching between original aspect ratio (black bars) and zoomed to fill device screen
val aspectRatioModeNames = context.resources?.getStringArray(R.array.resizeMode)
fun onResizeModeClicked() {
// switching between original aspect ratio (black bars) and zoomed to fill device screen
val aspectRatioModeNames = context.resources?.getStringArray(R.array.resizeMode)
val aspectRatioModes = arrayOf(
AspectRatioFrameLayout.RESIZE_MODE_FIT,
AspectRatioFrameLayout.RESIZE_MODE_ZOOM,
AspectRatioFrameLayout.RESIZE_MODE_FILL
)
val aspectRatioModes = arrayOf(
AspectRatioFrameLayout.RESIZE_MODE_FIT,
AspectRatioFrameLayout.RESIZE_MODE_ZOOM,
AspectRatioFrameLayout.RESIZE_MODE_FILL
)
MaterialAlertDialogBuilder(context)
.setTitle(R.string.aspect_ratio)
.setItems(aspectRatioModeNames) { _, index ->
resizeMode = aspectRatioModes[index]
}
.show()
}
MaterialAlertDialogBuilder(context)
.setTitle(R.string.aspect_ratio)
.setItems(aspectRatioModeNames) { _, index ->
resizeMode = aspectRatioModes[index]
}
.show()
}
override fun onRepeatModeClicked() {
val repeatModeNames = arrayOf(
context.getString(R.string.repeat_mode_none),
context.getString(R.string.repeat_mode_current)
)
fun onRepeatModeClicked() {
val repeatModeNames = arrayOf(
context.getString(R.string.repeat_mode_none),
context.getString(R.string.repeat_mode_current)
)
val repeatModes = arrayOf(
RepeatModeUtil.REPEAT_TOGGLE_MODE_ALL,
RepeatModeUtil.REPEAT_TOGGLE_MODE_NONE
)
// repeat mode options dialog
MaterialAlertDialogBuilder(context)
.setTitle(R.string.repeat_mode)
.setItems(repeatModeNames) { _, index ->
player?.repeatMode = repeatModes[index]
}
.show()
}
val repeatModes = arrayOf(
RepeatModeUtil.REPEAT_TOGGLE_MODE_NONE,
RepeatModeUtil.REPEAT_TOGGLE_MODE_ALL
)
// repeat mode options dialog
MaterialAlertDialogBuilder(context)
.setTitle(R.string.repeat_mode)
.setItems(repeatModeNames) { _, index ->
player?.repeatMode = repeatModes[index]
}
.show()
}
}

View File

@ -1,118 +0,0 @@
package com.github.libretube.views
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import com.github.libretube.databinding.BottomSheetBinding
import com.github.libretube.interfaces.OnlinePlayerOptionsInterface
import com.github.libretube.interfaces.PlayerOptionsInterface
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
/**
* Bottom Sheet including all the player options
*/
class PlayerOptionsBottomSheet : BottomSheetDialogFragment() {
lateinit var binding: BottomSheetBinding
private lateinit var playerOptionsInterface: PlayerOptionsInterface
private var onlinePlayerOptionsInterface: OnlinePlayerOptionsInterface? = null
/**
* current values
*/
var currentPlaybackSpeed: String? = null
var currentAutoplayMode: String? = null
var currentRepeatMode: String? = null
var currentQuality: String? = null
var currentResizeMode: String? = null
var currentCaptions: String? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// expand the bottom sheet on creation
dialog!!.setOnShowListener { dialog ->
val d = dialog as BottomSheetDialog
val bottomSheetInternal =
d.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)!!
BottomSheetBehavior.from(bottomSheetInternal).state =
BottomSheetBehavior.STATE_EXPANDED
}
binding = BottomSheetBinding.inflate(layoutInflater, container, false)
return binding.root
}
fun setOnClickListeners(
playerOptionsInterface: PlayerOptionsInterface,
onlinePlayerOptionsInterface: OnlinePlayerOptionsInterface?
) {
this.playerOptionsInterface = playerOptionsInterface
this.onlinePlayerOptionsInterface = onlinePlayerOptionsInterface
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (onlinePlayerOptionsInterface == null) {
binding.captions.visibility = View.GONE
binding.quality.visibility = View.GONE
}
/**
* update the text if a value is selected
*/
binding.autoplay.updateText(currentAutoplayMode)
binding.captions.updateText(currentCaptions)
binding.playbackSpeed.updateText(currentPlaybackSpeed)
binding.quality.updateText(currentQuality)
binding.repeatMode.updateText(currentRepeatMode)
binding.resizeMode.updateText(currentResizeMode)
binding.resizeMode.setOnClickListener {
playerOptionsInterface.onResizeModeClicked()
this.dismiss()
}
binding.quality.setOnClickListener {
onlinePlayerOptionsInterface?.onQualityClicked()
this.dismiss()
}
binding.playbackSpeed.setOnClickListener {
playerOptionsInterface.onPlaybackSpeedClicked()
this.dismiss()
}
binding.captions.setOnClickListener {
onlinePlayerOptionsInterface?.onCaptionClicked()
this.dismiss()
}
binding.autoplay.setOnClickListener {
playerOptionsInterface.onAutoplayClicked()
this.dismiss()
}
binding.repeatMode.setOnClickListener {
playerOptionsInterface.onRepeatModeClicked()
this.dismiss()
}
}
private fun TextView.updateText(currentValue: String?) {
if (currentValue == null) return
this.text = "${this.text} ($currentValue)"
}
}

View File

@ -1,46 +1,34 @@
<?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"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingHorizontal="7dp"
android:paddingVertical="10dp">
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="@+id/quality"
style="@style/BottomSheetItem"
android:text="@string/quality"
app:drawableStartCompat="@drawable/ic_hd" />
<FrameLayout
android:id="@+id/standard_bottom_sheet"
style="@style/Widget.Material3.BottomSheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<TextView
android:id="@+id/playbackSpeed"
style="@style/BottomSheetItem"
android:text="@string/playback_speed"
app:drawableStartCompat="@drawable/ic_speed" />
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/captions"
style="@style/BottomSheetItem"
android:text="@string/captions"
app:drawableStartCompat="@drawable/ic_caption" />
<!-- Drag handle for accessibility -->
<com.google.android.material.bottomsheet.BottomSheetDragHandleView
android:id="@+id/drag_handle"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/autoplay"
style="@style/BottomSheetItem"
android:text="@string/player_autoplay"
app:drawableStartCompat="@drawable/ic_play" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/options_recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/repeatMode"
style="@style/BottomSheetItem"
android:text="@string/repeat_mode"
app:drawableStartCompat="@drawable/ic_repeat" />
</LinearLayout>
<TextView
android:id="@+id/resizeMode"
style="@style/BottomSheetItem"
android:text="@string/player_resize_mode"
app:drawableStartCompat="@drawable/ic_aspect_ratio" />
</FrameLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,24 @@
<?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="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:orientation="horizontal"
android:padding="10dp">
<ImageView
android:id="@+id/drawable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
tools:src="@drawable/ic_download" />
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
tools:text="Option" />
</LinearLayout>

View File

@ -151,19 +151,6 @@
</style>
<style name="BottomSheetItem">
<item name="android:textSize">16sp</item>
<item name="android:drawablePadding">20dp</item>
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:padding">10dp</item>
<item name="android:layout_marginTop">2dp</item>
<item name="android:layout_marginBottom">2dp</item>
<item name="background">?attr/selectableItemBackground</item>
</style>
<style name="ItemRow">
<item name="android:layout_width">match_parent</item>