feat(phone): use custom track selection dialog for default player

Use media3 track type instead of mpv track type
Simplify track selection dialog
This commit is contained in:
Jarne Demeulemeester 2024-01-01 12:23:53 +01:00
parent db5eab1ab2
commit 55427036b2
No known key found for this signature in database
GPG key ID: 1E5C6AFBD622E9F5
5 changed files with 49 additions and 128 deletions

View file

@ -27,18 +27,15 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import androidx.media3.common.C import androidx.media3.common.C
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.AspectRatioFrameLayout import androidx.media3.ui.AspectRatioFrameLayout
import androidx.media3.ui.DefaultTimeBar import androidx.media3.ui.DefaultTimeBar
import androidx.media3.ui.PlayerView import androidx.media3.ui.PlayerView
import androidx.media3.ui.TrackSelectionDialogBuilder
import androidx.navigation.navArgs import androidx.navigation.navArgs
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import dev.jdtech.jellyfin.databinding.ActivityPlayerBinding import dev.jdtech.jellyfin.databinding.ActivityPlayerBinding
import dev.jdtech.jellyfin.dialogs.SpeedSelectionDialogFragment import dev.jdtech.jellyfin.dialogs.SpeedSelectionDialogFragment
import dev.jdtech.jellyfin.dialogs.TrackSelectionDialogFragment import dev.jdtech.jellyfin.dialogs.TrackSelectionDialogFragment
import dev.jdtech.jellyfin.mpv.MPVPlayer import dev.jdtech.jellyfin.mpv.MPVPlayer
import dev.jdtech.jellyfin.mpv.TrackType
import dev.jdtech.jellyfin.utils.PlayerGestureHelper import dev.jdtech.jellyfin.utils.PlayerGestureHelper
import dev.jdtech.jellyfin.utils.PreviewScrubListener import dev.jdtech.jellyfin.utils.PreviewScrubListener
import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel
@ -46,7 +43,6 @@ import dev.jdtech.jellyfin.viewmodels.PlayerEvents
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import dev.jdtech.jellyfin.player.video.R as PlayerVideoR
var isControlsLocked: Boolean = false var isControlsLocked: Boolean = false
@ -201,38 +197,10 @@ class PlayerActivity : BasePlayerActivity() {
} }
audioButton.setOnClickListener { audioButton.setOnClickListener {
when (viewModel.player) { TrackSelectionDialogFragment(C.TRACK_TYPE_AUDIO, viewModel).show(
is MPVPlayer -> { supportFragmentManager,
TrackSelectionDialogFragment(TrackType.AUDIO, viewModel).show( "trackselectiondialog",
supportFragmentManager, )
"trackselectiondialog",
)
}
is ExoPlayer -> {
val mappedTrackInfo =
viewModel.trackSelector.currentMappedTrackInfo ?: return@setOnClickListener
var audioRenderer: Int? = null
for (i in 0 until mappedTrackInfo.rendererCount) {
if (isRendererType(mappedTrackInfo, i, C.TRACK_TYPE_AUDIO)) {
audioRenderer = i
}
}
if (audioRenderer == null) return@setOnClickListener
val trackSelectionDialogBuilder = TrackSelectionDialogBuilder(
this,
resources.getString(PlayerVideoR.string.select_audio_track),
viewModel.player,
C.TRACK_TYPE_AUDIO,
)
trackSelectionDialogBuilder.setShowDisableOption(true)
val trackSelectionDialog = trackSelectionDialogBuilder.build()
trackSelectionDialog.show()
}
}
} }
val exoPlayerControlView = findViewById<FrameLayout>(R.id.player_controls) val exoPlayerControlView = findViewById<FrameLayout>(R.id.player_controls)
@ -253,38 +221,10 @@ class PlayerActivity : BasePlayerActivity() {
} }
subtitleButton.setOnClickListener { subtitleButton.setOnClickListener {
when (viewModel.player) { TrackSelectionDialogFragment(C.TRACK_TYPE_TEXT, viewModel).show(
is MPVPlayer -> { supportFragmentManager,
TrackSelectionDialogFragment(TrackType.SUBTITLE, viewModel).show( "trackselectiondialog",
supportFragmentManager, )
"trackselectiondialog",
)
}
is ExoPlayer -> {
val mappedTrackInfo =
viewModel.trackSelector.currentMappedTrackInfo ?: return@setOnClickListener
var subtitleRenderer: Int? = null
for (i in 0 until mappedTrackInfo.rendererCount) {
if (isRendererType(mappedTrackInfo, i, C.TRACK_TYPE_TEXT)) {
subtitleRenderer = i
}
}
if (subtitleRenderer == null) return@setOnClickListener
val trackSelectionDialogBuilder = TrackSelectionDialogBuilder(
this,
resources.getString(PlayerVideoR.string.select_subtile_track),
viewModel.player,
C.TRACK_TYPE_TEXT,
)
trackSelectionDialogBuilder.setShowDisableOption(true)
val trackSelectionDialog = trackSelectionDialogBuilder.build()
trackSelectionDialog.show()
}
}
} }
speedButton.setOnClickListener { speedButton.setOnClickListener {

View file

@ -124,7 +124,7 @@ fun PlayerScreen(
val trackType = result.value.trackType val trackType = result.value.trackType
val index = result.value.index val index = result.value.index
if (result.value.index == -1) { if (index == -1) {
viewModel.player.trackSelectionParameters = viewModel.player.trackSelectionParameters viewModel.player.trackSelectionParameters = viewModel.player.trackSelectionParameters
.buildUpon() .buildUpon()
.clearOverridesOfType(trackType) .clearOverridesOfType(trackType)

View file

@ -6,56 +6,36 @@ import androidx.fragment.app.DialogFragment
import androidx.media3.common.C import androidx.media3.common.C
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dev.jdtech.jellyfin.getTrackNames import dev.jdtech.jellyfin.getTrackNames
import dev.jdtech.jellyfin.mpv.TrackType
import dev.jdtech.jellyfin.player.video.R import dev.jdtech.jellyfin.player.video.R
import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel
import java.lang.IllegalStateException import java.lang.IllegalStateException
class TrackSelectionDialogFragment( class TrackSelectionDialogFragment(
private val type: TrackType, private val type: @C.TrackType Int,
private val viewModel: PlayerActivityViewModel, private val viewModel: PlayerActivityViewModel,
) : DialogFragment() { ) : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
when (type) { val titleResource = when (type) {
TrackType.AUDIO -> { C.TRACK_TYPE_AUDIO -> R.string.select_audio_track
return activity?.let { activity -> C.TRACK_TYPE_TEXT -> R.string.select_subtile_track
val builder = MaterialAlertDialogBuilder(activity) else -> throw IllegalStateException("TrackType must be AUDIO or TEXT")
val tracksGroups = viewModel.player.currentTracks.groups.filter { it.type == C.TRACK_TYPE_AUDIO }
builder.setTitle(getString(R.string.select_audio_track))
.setSingleChoiceItems(
arrayOf(getString(R.string.none)) + tracksGroups.getTrackNames(), // Add "None" at the top of the list
tracksGroups.indexOfFirst { it.isSelected } + 1, // Add 1 to the index to account for the "None" item
) { dialog, which ->
viewModel.switchToTrack(
TrackType.AUDIO,
which - 1, // Minus 1 to get the correct group without the "None" item. "None" becomes -1
)
dialog.dismiss()
}
builder.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
TrackType.SUBTITLE -> {
return activity?.let { activity ->
val builder = MaterialAlertDialogBuilder(activity)
val tracksGroups = viewModel.player.currentTracks.groups.filter { it.type == C.TRACK_TYPE_TEXT }
builder.setTitle(getString(R.string.select_subtile_track))
.setSingleChoiceItems(
arrayOf(getString(R.string.none)) + tracksGroups.getTrackNames(),
tracksGroups.indexOfFirst { it.isSelected } + 1,
) { dialog, which ->
viewModel.switchToTrack(
TrackType.SUBTITLE,
which - 1,
)
dialog.dismiss()
}
builder.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
else -> {
throw IllegalStateException("TrackType must be AUDIO or SUBTITLE")
}
} }
val tracksGroups = viewModel.player.currentTracks.groups.filter { it.type == type && it.isSupported }
return activity?.let { activity ->
val builder = MaterialAlertDialogBuilder(activity)
builder
.setTitle(getString(titleResource))
.setSingleChoiceItems(
arrayOf(getString(R.string.none)) + tracksGroups.getTrackNames(), // Add "None" at the top of the list
tracksGroups.indexOfFirst { it.isSelected } + 1, // Add 1 to the index to account for the "None" item
) { dialog, which ->
viewModel.switchToTrack(
type,
which - 1, // Minus 1 to get the correct group without the "None" item. "None" becomes -1
)
dialog.dismiss()
}
builder.create()
} ?: throw IllegalStateException("Activity cannot be null")
} }
} }

View file

@ -354,7 +354,7 @@ class MPVPlayer(
* @param id Id to select or [C.INDEX_UNSET] to disable [TrackType] * @param id Id to select or [C.INDEX_UNSET] to disable [TrackType]
* @return true if the track is or was already selected * @return true if the track is or was already selected
*/ */
fun selectTrack( private fun selectTrack(
trackType: TrackType, trackType: TrackType,
id: String, id: String,
) { ) {

View file

@ -11,6 +11,7 @@ import androidx.media3.common.C
import androidx.media3.common.MediaItem import androidx.media3.common.MediaItem
import androidx.media3.common.MediaMetadata import androidx.media3.common.MediaMetadata
import androidx.media3.common.Player import androidx.media3.common.Player
import androidx.media3.common.TrackSelectionOverride
import androidx.media3.common.TrackSelectionParameters import androidx.media3.common.TrackSelectionParameters
import androidx.media3.exoplayer.DefaultRenderersFactory import androidx.media3.exoplayer.DefaultRenderersFactory
import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.ExoPlayer
@ -20,7 +21,6 @@ import dev.jdtech.jellyfin.AppPreferences
import dev.jdtech.jellyfin.models.Intro import dev.jdtech.jellyfin.models.Intro
import dev.jdtech.jellyfin.models.PlayerItem import dev.jdtech.jellyfin.models.PlayerItem
import dev.jdtech.jellyfin.mpv.MPVPlayer import dev.jdtech.jellyfin.mpv.MPVPlayer
import dev.jdtech.jellyfin.mpv.TrackType
import dev.jdtech.jellyfin.player.video.R import dev.jdtech.jellyfin.player.video.R
import dev.jdtech.jellyfin.repository.JellyfinRepository import dev.jdtech.jellyfin.repository.JellyfinRepository
import dev.jdtech.jellyfin.utils.bif.BifData import dev.jdtech.jellyfin.utils.bif.BifData
@ -319,21 +319,22 @@ constructor(
releasePlayer() releasePlayer()
} }
fun switchToTrack(trackType: TrackType, index: Int) { fun switchToTrack(trackType: @C.TrackType Int, index: Int) {
if (player is MPVPlayer) { // Index -1 equals disable track
// Index -1 equals disable track if (index == -1) {
if (index == -1) { player.trackSelectionParameters = player.trackSelectionParameters
player.selectTrack(trackType, id = "no") .buildUpon()
return .clearOverridesOfType(trackType)
} .setTrackTypeDisabled(trackType, true)
.build()
// Get track to select based on index } else {
val tracksGroup = player.currentTracks.groups.filter { TrackType.fromMedia3TrackType(it.type) == trackType }[index] player.trackSelectionParameters = player.trackSelectionParameters
val format = tracksGroup.mediaTrackGroup.getFormat(0) .buildUpon()
if (format.id == null) { .setOverrideForType(
return TrackSelectionOverride(player.currentTracks.groups.filter { it.type == trackType && it.isSupported }[index].mediaTrackGroup, 0),
} )
player.selectTrack(trackType, id = format.id!!) .setTrackTypeDisabled(trackType, false)
.build()
} }
} }