mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
Add audio and video track support (#69)
* Add audio and video track support
This commit is contained in:
parent
7a68d6304f
commit
461f3d75d8
12 changed files with 1283 additions and 809 deletions
|
@ -99,6 +99,10 @@ abstract class AbstractPlayerFragment(
|
||||||
throw NotImplementedError()
|
throw NotImplementedError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun onTracksInfoChanged() {
|
||||||
|
throw NotImplementedError()
|
||||||
|
}
|
||||||
|
|
||||||
open fun exitedPipMode() {
|
open fun exitedPipMode() {
|
||||||
throw NotImplementedError()
|
throw NotImplementedError()
|
||||||
}
|
}
|
||||||
|
@ -369,6 +373,7 @@ abstract class AbstractPlayerFragment(
|
||||||
),
|
),
|
||||||
subtitlesUpdates = ::subtitlesChanged,
|
subtitlesUpdates = ::subtitlesChanged,
|
||||||
embeddedSubtitlesFetched = ::embeddedSubtitlesFetched,
|
embeddedSubtitlesFetched = ::embeddedSubtitlesFetched,
|
||||||
|
onTracksInfoChanged = ::onTracksInfoChanged
|
||||||
)
|
)
|
||||||
|
|
||||||
if (player is CS3IPlayer) {
|
if (player is CS3IPlayer) {
|
||||||
|
|
|
@ -8,6 +8,8 @@ import android.util.Log
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.google.android.exoplayer2.*
|
import com.google.android.exoplayer2.*
|
||||||
|
import com.google.android.exoplayer2.C.TRACK_TYPE_AUDIO
|
||||||
|
import com.google.android.exoplayer2.C.TRACK_TYPE_VIDEO
|
||||||
import com.google.android.exoplayer2.database.StandaloneDatabaseProvider
|
import com.google.android.exoplayer2.database.StandaloneDatabaseProvider
|
||||||
import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource
|
import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource
|
||||||
import com.google.android.exoplayer2.source.*
|
import com.google.android.exoplayer2.source.*
|
||||||
|
@ -24,6 +26,8 @@ import com.google.android.exoplayer2.upstream.cache.SimpleCache
|
||||||
import com.google.android.exoplayer2.util.MimeTypes
|
import com.google.android.exoplayer2.util.MimeTypes
|
||||||
import com.google.android.exoplayer2.video.VideoSize
|
import com.google.android.exoplayer2.video.VideoSize
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
|
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||||
import com.lagradost.cloudstream3.USER_AGENT
|
import com.lagradost.cloudstream3.USER_AGENT
|
||||||
import com.lagradost.cloudstream3.app
|
import com.lagradost.cloudstream3.app
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
|
@ -39,6 +43,7 @@ import javax.net.ssl.SSLContext
|
||||||
import javax.net.ssl.SSLSession
|
import javax.net.ssl.SSLSession
|
||||||
|
|
||||||
const val TAG = "CS3ExoPlayer"
|
const val TAG = "CS3ExoPlayer"
|
||||||
|
const val PREFERRED_AUDIO_LANGUAGE_KEY = "preferred_audio_language"
|
||||||
|
|
||||||
/** Cache */
|
/** Cache */
|
||||||
|
|
||||||
|
@ -108,6 +113,7 @@ class CS3IPlayer : IPlayer {
|
||||||
|
|
||||||
private var playerUpdated: ((Any?) -> Unit)? = null
|
private var playerUpdated: ((Any?) -> Unit)? = null
|
||||||
private var embeddedSubtitlesFetched: ((List<SubtitleData>) -> Unit)? = null
|
private var embeddedSubtitlesFetched: ((List<SubtitleData>) -> Unit)? = null
|
||||||
|
private var onTracksInfoChanged: (() -> Unit)? = null
|
||||||
|
|
||||||
override fun releaseCallbacks() {
|
override fun releaseCallbacks() {
|
||||||
playerUpdated = null
|
playerUpdated = null
|
||||||
|
@ -120,7 +126,7 @@ class CS3IPlayer : IPlayer {
|
||||||
nextEpisode = null
|
nextEpisode = null
|
||||||
prevEpisode = null
|
prevEpisode = null
|
||||||
subtitlesUpdates = null
|
subtitlesUpdates = null
|
||||||
embeddedSubtitlesFetched = null
|
onTracksInfoChanged = null
|
||||||
requestSubtitleUpdate = null
|
requestSubtitleUpdate = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,6 +142,7 @@ class CS3IPlayer : IPlayer {
|
||||||
prevEpisode: (() -> Unit)?,
|
prevEpisode: (() -> Unit)?,
|
||||||
subtitlesUpdates: (() -> Unit)?,
|
subtitlesUpdates: (() -> Unit)?,
|
||||||
embeddedSubtitlesFetched: ((List<SubtitleData>) -> Unit)?,
|
embeddedSubtitlesFetched: ((List<SubtitleData>) -> Unit)?,
|
||||||
|
onTracksInfoChanged: (() -> Unit)?,
|
||||||
) {
|
) {
|
||||||
this.playerUpdated = playerUpdated
|
this.playerUpdated = playerUpdated
|
||||||
this.updateIsPlaying = updateIsPlaying
|
this.updateIsPlaying = updateIsPlaying
|
||||||
|
@ -148,6 +155,7 @@ class CS3IPlayer : IPlayer {
|
||||||
this.prevEpisode = prevEpisode
|
this.prevEpisode = prevEpisode
|
||||||
this.subtitlesUpdates = subtitlesUpdates
|
this.subtitlesUpdates = subtitlesUpdates
|
||||||
this.embeddedSubtitlesFetched = embeddedSubtitlesFetched
|
this.embeddedSubtitlesFetched = embeddedSubtitlesFetched
|
||||||
|
this.onTracksInfoChanged = onTracksInfoChanged
|
||||||
}
|
}
|
||||||
|
|
||||||
// I know, this is not a perfect solution, however it works for fixing subs
|
// I know, this is not a perfect solution, however it works for fixing subs
|
||||||
|
@ -212,6 +220,72 @@ class CS3IPlayer : IPlayer {
|
||||||
|
|
||||||
var currentSubtitles: SubtitleData? = null
|
var currentSubtitles: SubtitleData? = null
|
||||||
|
|
||||||
|
override fun setMaxVideoSize(width: Int, height: Int) {
|
||||||
|
exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters
|
||||||
|
?.buildUpon()
|
||||||
|
?.setMaxVideoSize(width, height)
|
||||||
|
?.build()
|
||||||
|
?: return
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setPreferredAudioTrack(trackLanguage: String?) {
|
||||||
|
preferredAudioTrackLanguage = trackLanguage
|
||||||
|
exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters
|
||||||
|
?.buildUpon()
|
||||||
|
?.setPreferredAudioLanguage(trackLanguage)
|
||||||
|
?.build()
|
||||||
|
?: return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all supported formats in a list
|
||||||
|
* */
|
||||||
|
private fun List<TracksInfo.TrackGroupInfo>.getFormats(): List<Format> {
|
||||||
|
return this.map {
|
||||||
|
(0 until it.trackGroup.length).mapNotNull { i ->
|
||||||
|
if (it.isSupported)
|
||||||
|
it.trackGroup.getFormat(i) // to it.isSelected
|
||||||
|
else null
|
||||||
|
}
|
||||||
|
}.flatten()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Format.toAudioTrack(): AudioTrack {
|
||||||
|
return AudioTrack(
|
||||||
|
this.id,
|
||||||
|
this.label,
|
||||||
|
// isPlaying,
|
||||||
|
this.language
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Format.toVideoTrack(): VideoTrack {
|
||||||
|
return VideoTrack(
|
||||||
|
this.id,
|
||||||
|
this.label,
|
||||||
|
// isPlaying,
|
||||||
|
this.language,
|
||||||
|
this.width,
|
||||||
|
this.height
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getVideoTracks(): CurrentTracks {
|
||||||
|
val allTracks = exoPlayer?.currentTracksInfo?.trackGroupInfos ?: emptyList()
|
||||||
|
val videoTracks = allTracks.filter { it.trackType == TRACK_TYPE_VIDEO }.getFormats()
|
||||||
|
.map { it.toVideoTrack() }
|
||||||
|
val audioTracks = allTracks.filter { it.trackType == TRACK_TYPE_AUDIO }.getFormats()
|
||||||
|
.map { it.toAudioTrack() }
|
||||||
|
|
||||||
|
return CurrentTracks(
|
||||||
|
exoPlayer?.videoFormat?.toVideoTrack(),
|
||||||
|
exoPlayer?.audioFormat?.toAudioTrack(),
|
||||||
|
videoTracks,
|
||||||
|
audioTracks
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return True if the player should be reloaded
|
* @return True if the player should be reloaded
|
||||||
* */
|
* */
|
||||||
|
@ -350,6 +424,20 @@ class CS3IPlayer : IPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
/**
|
||||||
|
* Setting this variable is permanent across app sessions.
|
||||||
|
**/
|
||||||
|
private var preferredAudioTrackLanguage: String? = null
|
||||||
|
get() {
|
||||||
|
return field ?: getKey(PREFERRED_AUDIO_LANGUAGE_KEY, field)?.also {
|
||||||
|
field = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
setKey(PREFERRED_AUDIO_LANGUAGE_KEY, value)
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
private var simpleCache: SimpleCache? = null
|
private var simpleCache: SimpleCache? = null
|
||||||
|
|
||||||
var requestSubtitleUpdate: (() -> Unit)? = null
|
var requestSubtitleUpdate: (() -> Unit)? = null
|
||||||
|
@ -460,13 +548,21 @@ class CS3IPlayer : IPlayer {
|
||||||
return getMediaItemBuilder(mimeType).setUri(url).build()
|
return getMediaItemBuilder(mimeType).setUri(url).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getTrackSelector(context: Context): TrackSelector {
|
private fun getTrackSelector(context: Context, maxVideoHeight: Int?): TrackSelector {
|
||||||
val trackSelector = DefaultTrackSelector(context)
|
val trackSelector = DefaultTrackSelector(context)
|
||||||
trackSelector.parameters = DefaultTrackSelector.ParametersBuilder(context)
|
trackSelector.parameters = DefaultTrackSelector.ParametersBuilder(context)
|
||||||
// .setRendererDisabled(C.TRACK_TYPE_VIDEO, true)
|
// .setRendererDisabled(C.TRACK_TYPE_VIDEO, true)
|
||||||
.setRendererDisabled(C.TRACK_TYPE_TEXT, true)
|
.setRendererDisabled(C.TRACK_TYPE_TEXT, true)
|
||||||
|
// Experimental
|
||||||
|
.setTunnelingEnabled(true)
|
||||||
.setDisabledTextTrackSelectionFlags(C.TRACK_TYPE_TEXT)
|
.setDisabledTextTrackSelectionFlags(C.TRACK_TYPE_TEXT)
|
||||||
.clearSelectionOverrides()
|
// This will not force higher quality videos to fail
|
||||||
|
// but will make the m3u8 pick the correct preferred
|
||||||
|
.setMaxVideoSize(Int.MAX_VALUE, maxVideoHeight ?: Int.MAX_VALUE)
|
||||||
|
.setPreferredAudioLanguage(preferredAudioTrackLanguage)
|
||||||
|
|
||||||
|
// This would also clear preferred audio
|
||||||
|
// .clearSelectionOverrides()
|
||||||
.build()
|
.build()
|
||||||
return trackSelector
|
return trackSelector
|
||||||
}
|
}
|
||||||
|
@ -486,6 +582,11 @@ class CS3IPlayer : IPlayer {
|
||||||
playWhenReady: Boolean = true,
|
playWhenReady: Boolean = true,
|
||||||
cacheFactory: CacheDataSource.Factory? = null,
|
cacheFactory: CacheDataSource.Factory? = null,
|
||||||
trackSelector: TrackSelector? = null,
|
trackSelector: TrackSelector? = null,
|
||||||
|
/**
|
||||||
|
* Sets the m3u8 preferred video quality, will not force stop anything with higher quality.
|
||||||
|
* Does not work if trackSelector is defined.
|
||||||
|
**/
|
||||||
|
maxVideoHeight: Int? = null
|
||||||
): ExoPlayer {
|
): ExoPlayer {
|
||||||
val exoPlayerBuilder =
|
val exoPlayerBuilder =
|
||||||
ExoPlayer.Builder(context)
|
ExoPlayer.Builder(context)
|
||||||
|
@ -508,7 +609,7 @@ class CS3IPlayer : IPlayer {
|
||||||
} else it
|
} else it
|
||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
}
|
}
|
||||||
.setTrackSelector(trackSelector ?: getTrackSelector(context))
|
.setTrackSelector(trackSelector ?: getTrackSelector(context, maxVideoHeight))
|
||||||
.setLoadControl(
|
.setLoadControl(
|
||||||
DefaultLoadControl.Builder()
|
DefaultLoadControl.Builder()
|
||||||
.setTargetBufferBytes(
|
.setTargetBufferBytes(
|
||||||
|
@ -637,6 +738,12 @@ class CS3IPlayer : IPlayer {
|
||||||
cacheFactory: CacheDataSource.Factory? = null
|
cacheFactory: CacheDataSource.Factory? = null
|
||||||
) {
|
) {
|
||||||
Log.i(TAG, "loadExo")
|
Log.i(TAG, "loadExo")
|
||||||
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val maxVideoHeight = settingsManager.getInt(
|
||||||
|
context.getString(com.lagradost.cloudstream3.R.string.quality_pref_key),
|
||||||
|
Int.MAX_VALUE
|
||||||
|
)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
hasUsedFirstRender = false
|
hasUsedFirstRender = false
|
||||||
|
|
||||||
|
@ -653,7 +760,8 @@ class CS3IPlayer : IPlayer {
|
||||||
videoBufferMs = videoBufferMs,
|
videoBufferMs = videoBufferMs,
|
||||||
playWhenReady = isPlaying, // this keep the current state of the player
|
playWhenReady = isPlaying, // this keep the current state of the player
|
||||||
cacheFactory = cacheFactory,
|
cacheFactory = cacheFactory,
|
||||||
subtitleOffset = currentSubtitleOffset
|
subtitleOffset = currentSubtitleOffset,
|
||||||
|
maxVideoHeight = maxVideoHeight
|
||||||
)
|
)
|
||||||
|
|
||||||
requestSubtitleUpdate = ::reloadSubs
|
requestSubtitleUpdate = ::reloadSubs
|
||||||
|
@ -713,6 +821,7 @@ class CS3IPlayer : IPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
embeddedSubtitlesFetched?.invoke(exoPlayerReportedTracks)
|
embeddedSubtitlesFetched?.invoke(exoPlayerReportedTracks)
|
||||||
|
onTracksInfoChanged?.invoke()
|
||||||
subtitlesUpdates?.invoke()
|
subtitlesUpdates?.invoke()
|
||||||
}
|
}
|
||||||
super.onTracksInfoChanged(tracksInfo)
|
super.onTracksInfoChanged(tracksInfo)
|
||||||
|
|
|
@ -176,6 +176,10 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
||||||
throw NotImplementedError()
|
throw NotImplementedError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun showTracksDialogue() {
|
||||||
|
throw NotImplementedError()
|
||||||
|
}
|
||||||
|
|
||||||
open fun openOnlineSubPicker(
|
open fun openOnlineSubPicker(
|
||||||
context: Context,
|
context: Context,
|
||||||
imdbId: Long?,
|
imdbId: Long?,
|
||||||
|
@ -1101,6 +1105,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
||||||
|
|
||||||
// if nothing has loaded these buttons should not be visible
|
// if nothing has loaded these buttons should not be visible
|
||||||
player_skip_episode?.isVisible = false
|
player_skip_episode?.isVisible = false
|
||||||
|
player_tracks_btt?.isVisible = false
|
||||||
player_skip_op?.isVisible = false
|
player_skip_op?.isVisible = false
|
||||||
shadow_overlay?.isVisible = false
|
shadow_overlay?.isVisible = false
|
||||||
|
|
||||||
|
@ -1296,6 +1301,10 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
||||||
showMirrorsDialogue()
|
showMirrorsDialogue()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
player_tracks_btt?.setOnClickListener {
|
||||||
|
showTracksDialogue()
|
||||||
|
}
|
||||||
|
|
||||||
player_intro_play?.setOnClickListener {
|
player_intro_play?.setOnClickListener {
|
||||||
player_intro_play?.isGone = true
|
player_intro_play?.isGone = true
|
||||||
player.handleEvent(CSPlayerEvent.Play)
|
player.handleEvent(CSPlayerEvent.Play)
|
||||||
|
|
|
@ -18,6 +18,11 @@ import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.google.android.exoplayer2.C.TRACK_TYPE_AUDIO
|
||||||
|
import com.google.android.exoplayer2.C.TRACK_TYPE_VIDEO
|
||||||
|
import com.google.android.exoplayer2.Format
|
||||||
|
import com.google.android.exoplayer2.Format.NO_VALUE
|
||||||
|
import com.google.android.exoplayer2.TracksInfo
|
||||||
import com.google.android.exoplayer2.util.MimeTypes
|
import com.google.android.exoplayer2.util.MimeTypes
|
||||||
import com.hippo.unifile.UniFile
|
import com.hippo.unifile.UniFile
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
|
@ -53,6 +58,8 @@ import kotlinx.android.synthetic.main.dialog_online_subtitles.cancel_btt
|
||||||
import kotlinx.android.synthetic.main.fragment_player.*
|
import kotlinx.android.synthetic.main.fragment_player.*
|
||||||
import kotlinx.android.synthetic.main.player_custom_layout.*
|
import kotlinx.android.synthetic.main.player_custom_layout.*
|
||||||
import kotlinx.android.synthetic.main.player_select_source_and_subs.*
|
import kotlinx.android.synthetic.main.player_select_source_and_subs.*
|
||||||
|
import kotlinx.android.synthetic.main.player_select_source_and_subs.subtitles_click_settings
|
||||||
|
import kotlinx.android.synthetic.main.player_select_tracks.*
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
|
|
||||||
class GeneratorPlayer : FullScreenPlayer() {
|
class GeneratorPlayer : FullScreenPlayer() {
|
||||||
|
@ -109,6 +116,11 @@ class GeneratorPlayer : FullScreenPlayer() {
|
||||||
viewModel.addSubtitles(subtitles.toSet())
|
viewModel.addSubtitles(subtitles.toSet())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onTracksInfoChanged() {
|
||||||
|
val tracks = player.getVideoTracks()
|
||||||
|
player_tracks_btt?.isVisible = tracks.allVideoTracks.size > 1 || tracks.allAudioTracks.size > 1
|
||||||
|
}
|
||||||
|
|
||||||
private fun noSubtitles(): Boolean {
|
private fun noSubtitles(): Boolean {
|
||||||
return setSubtitles(null)
|
return setSubtitles(null)
|
||||||
}
|
}
|
||||||
|
@ -477,6 +489,8 @@ class GeneratorPlayer : FullScreenPlayer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
var selectSourceDialog: AlertDialog? = null
|
var selectSourceDialog: AlertDialog? = null
|
||||||
|
// var selectTracksDialog: AlertDialog? = null
|
||||||
|
|
||||||
override fun showMirrorsDialogue() {
|
override fun showMirrorsDialogue() {
|
||||||
try {
|
try {
|
||||||
currentSelectedSubtitles = player.getCurrentPreferredSubtitle()
|
currentSelectedSubtitles = player.getCurrentPreferredSubtitle()
|
||||||
|
@ -668,6 +682,121 @@ class GeneratorPlayer : FullScreenPlayer() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun showTracksDialogue() {
|
||||||
|
try {
|
||||||
|
//println("CURRENT SELECTED :$currentSelectedSubtitles of $currentSubs")
|
||||||
|
context?.let { ctx ->
|
||||||
|
val tracks = player.getVideoTracks()
|
||||||
|
|
||||||
|
val isPlaying = player.getIsPlaying()
|
||||||
|
player.handleEvent(CSPlayerEvent.Pause)
|
||||||
|
|
||||||
|
val currentVideoTracks = tracks.allVideoTracks.sortedBy {
|
||||||
|
it.height?.times(-1)
|
||||||
|
}
|
||||||
|
val currentAudioTracks = tracks.allAudioTracks
|
||||||
|
|
||||||
|
val trackBuilder = AlertDialog.Builder(ctx, R.style.AlertDialogCustomBlack)
|
||||||
|
.setView(R.layout.player_select_tracks)
|
||||||
|
|
||||||
|
val tracksDialog = trackBuilder.create()
|
||||||
|
|
||||||
|
// selectTracksDialog = tracksDialog
|
||||||
|
|
||||||
|
tracksDialog.show()
|
||||||
|
val videosList = tracksDialog.video_tracks_list
|
||||||
|
val audioList = tracksDialog.auto_tracks_list
|
||||||
|
|
||||||
|
tracksDialog.video_tracks_holder.isVisible = currentVideoTracks.size > 1
|
||||||
|
tracksDialog.audio_tracks_holder.isVisible = currentAudioTracks.size > 1
|
||||||
|
|
||||||
|
fun dismiss() {
|
||||||
|
if (isPlaying) {
|
||||||
|
player.handleEvent(CSPlayerEvent.Play)
|
||||||
|
}
|
||||||
|
activity?.hideSystemUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
val videosArrayAdapter =
|
||||||
|
ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice)
|
||||||
|
|
||||||
|
videosArrayAdapter.addAll(currentVideoTracks.mapIndexed { index, format ->
|
||||||
|
format.label
|
||||||
|
?: (if (format.height == NO_VALUE || format.width == NO_VALUE) index else "${format.width}x${format.height}").toString()
|
||||||
|
})
|
||||||
|
|
||||||
|
videosList.choiceMode = AbsListView.CHOICE_MODE_SINGLE
|
||||||
|
videosList.adapter = videosArrayAdapter
|
||||||
|
|
||||||
|
// Sometimes the data is not the same because some data gets resolved at different stages i think
|
||||||
|
var videoIndex = currentVideoTracks.indexOf(tracks.currentVideoTrack).takeIf {
|
||||||
|
it != -1
|
||||||
|
} ?: currentVideoTracks.indexOfFirst {
|
||||||
|
tracks.currentVideoTrack?.id == it.id
|
||||||
|
}
|
||||||
|
|
||||||
|
videosList.setSelection(videoIndex)
|
||||||
|
videosList.setItemChecked(videoIndex, true)
|
||||||
|
|
||||||
|
videosList.setOnItemClickListener { _, _, which, _ ->
|
||||||
|
videoIndex = which
|
||||||
|
videosList.setItemChecked(which, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
tracksDialog.setOnDismissListener {
|
||||||
|
dismiss()
|
||||||
|
// selectTracksDialog = null
|
||||||
|
}
|
||||||
|
|
||||||
|
var audioIndexStart = currentAudioTracks.indexOf(tracks.currentAudioTrack).takeIf {
|
||||||
|
it != -1
|
||||||
|
} ?: currentVideoTracks.indexOfFirst {
|
||||||
|
tracks.currentAudioTrack?.id == it.id
|
||||||
|
}
|
||||||
|
|
||||||
|
val audioArrayAdapter =
|
||||||
|
ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice)
|
||||||
|
// audioArrayAdapter.add(ctx.getString(R.string.no_subtitles))
|
||||||
|
audioArrayAdapter.addAll(currentAudioTracks.mapIndexed { index, format ->
|
||||||
|
format.label ?: format.language?.let { fromTwoLettersToLanguage(it) } ?: index.toString()
|
||||||
|
})
|
||||||
|
|
||||||
|
audioList.adapter = audioArrayAdapter
|
||||||
|
audioList.choiceMode = AbsListView.CHOICE_MODE_SINGLE
|
||||||
|
|
||||||
|
audioList.setSelection(audioIndexStart)
|
||||||
|
audioList.setItemChecked(audioIndexStart, true)
|
||||||
|
|
||||||
|
audioList.setOnItemClickListener { _, _, which, _ ->
|
||||||
|
audioIndexStart = which
|
||||||
|
audioList.setItemChecked(which, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
tracksDialog.cancel_btt?.setOnClickListener {
|
||||||
|
tracksDialog.dismissSafe(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
tracksDialog.apply_btt?.setOnClickListener {
|
||||||
|
player.setPreferredAudioTrack(
|
||||||
|
currentAudioTracks.getOrNull(audioIndexStart)?.language
|
||||||
|
)
|
||||||
|
|
||||||
|
val currentVideo = currentVideoTracks.getOrNull(videoIndex)
|
||||||
|
val width = currentVideo?.width ?: NO_VALUE
|
||||||
|
val height = currentVideo?.height ?: NO_VALUE
|
||||||
|
if (width != NO_VALUE && height != NO_VALUE) {
|
||||||
|
player.setMaxVideoSize(width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
tracksDialog.dismissSafe(activity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun playerError(exception: Exception) {
|
override fun playerError(exception: Exception) {
|
||||||
Log.i(TAG, "playerError = $currentSelectedLink")
|
Log.i(TAG, "playerError = $currentSelectedLink")
|
||||||
super.playerError(exception)
|
super.playerError(exception)
|
||||||
|
|
|
@ -46,6 +46,41 @@ enum class CSPlayerLoading {
|
||||||
//IsDone,
|
//IsDone,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface Track {
|
||||||
|
/**
|
||||||
|
* Unique among the class, used to check which track is used.
|
||||||
|
* VideoTrack and AudioTrack can have the same id
|
||||||
|
**/
|
||||||
|
val id: String?
|
||||||
|
val label: String?
|
||||||
|
// val isCurrentlyPlaying: Boolean
|
||||||
|
val language: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
data class VideoTrack(
|
||||||
|
override val id: String?,
|
||||||
|
override val label: String?,
|
||||||
|
// override val isCurrentlyPlaying: Boolean,
|
||||||
|
override val language: String?,
|
||||||
|
val width: Int?,
|
||||||
|
val height: Int?,
|
||||||
|
) : Track
|
||||||
|
|
||||||
|
data class AudioTrack(
|
||||||
|
override val id: String?,
|
||||||
|
override val label: String?,
|
||||||
|
// override val isCurrentlyPlaying: Boolean,
|
||||||
|
override val language: String?,
|
||||||
|
) : Track
|
||||||
|
|
||||||
|
data class CurrentTracks(
|
||||||
|
val currentVideoTrack: VideoTrack?,
|
||||||
|
val currentAudioTrack: AudioTrack?,
|
||||||
|
val allVideoTracks: List<VideoTrack>,
|
||||||
|
val allAudioTracks: List<AudioTrack>,
|
||||||
|
)
|
||||||
|
|
||||||
class InvalidFileException(msg: String) : Exception(msg)
|
class InvalidFileException(msg: String) : Exception(msg)
|
||||||
|
|
||||||
//http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4
|
//http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4
|
||||||
|
@ -88,7 +123,9 @@ interface IPlayer {
|
||||||
prevEpisode: (() -> Unit)? = null, // this is used by the player to load the previous episode
|
prevEpisode: (() -> Unit)? = null, // this is used by the player to load the previous episode
|
||||||
subtitlesUpdates: (() -> Unit)? = null, // callback from player to inform that subtitles have updated in some way
|
subtitlesUpdates: (() -> Unit)? = null, // callback from player to inform that subtitles have updated in some way
|
||||||
embeddedSubtitlesFetched: ((List<SubtitleData>) -> Unit)? = null, // callback from player to give all embedded subtitles
|
embeddedSubtitlesFetched: ((List<SubtitleData>) -> Unit)? = null, // callback from player to give all embedded subtitles
|
||||||
|
onTracksInfoChanged: (() -> Unit)? = null, // Callback when tracks are changed, used for UI changes
|
||||||
)
|
)
|
||||||
|
|
||||||
fun releaseCallbacks()
|
fun releaseCallbacks()
|
||||||
|
|
||||||
fun updateSubtitleStyle(style: SaveCaptionStyle)
|
fun updateSubtitleStyle(style: SaveCaptionStyle)
|
||||||
|
@ -121,4 +158,12 @@ interface IPlayer {
|
||||||
|
|
||||||
/** Get if player is actually used */
|
/** Get if player is actually used */
|
||||||
fun isActive(): Boolean
|
fun isActive(): Boolean
|
||||||
|
|
||||||
|
fun getVideoTracks(): CurrentTracks
|
||||||
|
|
||||||
|
/** If no parameters are set it'll default to no set size */
|
||||||
|
fun setMaxVideoSize(width: Int = Int.MAX_VALUE, height: Int = Int.MAX_VALUE)
|
||||||
|
|
||||||
|
/** If no trackLanguage is set it'll default to first track */
|
||||||
|
fun setPreferredAudioTrack(trackLanguage: String?)
|
||||||
}
|
}
|
|
@ -75,11 +75,14 @@ open class ResultTrailerPlayer : com.lagradost.cloudstream3.ui.player.FullScreen
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showMirrorsDialogue() {}
|
override fun showMirrorsDialogue() {}
|
||||||
|
override fun showTracksDialogue() {}
|
||||||
|
|
||||||
override fun openOnlineSubPicker(context: Context, imdbId: Long?, dismissCallback: () -> Unit) {}
|
override fun openOnlineSubPicker(context: Context, imdbId: Long?, dismissCallback: () -> Unit) {}
|
||||||
|
|
||||||
override fun subtitlesChanged() {}
|
override fun subtitlesChanged() {}
|
||||||
|
|
||||||
override fun embeddedSubtitlesFetched(subtitles: List<SubtitleData>) {}
|
override fun embeddedSubtitlesFetched(subtitles: List<SubtitleData>) {}
|
||||||
|
override fun onTracksInfoChanged() {}
|
||||||
|
|
||||||
override fun exitedPipMode() {}
|
override fun exitedPipMode() {}
|
||||||
|
|
||||||
|
|
|
@ -116,15 +116,16 @@ class M3u8Helper {
|
||||||
return !url.contains("https://") && !url.contains("http://")
|
return !url.contains("https://") && !url.contains("http://")
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun m3u8Generation(m3u8: M3u8Stream, returnThis: Boolean?): List<M3u8Stream> {
|
suspend fun m3u8Generation(m3u8: M3u8Stream, returnThis: Boolean? = true): List<M3u8Stream> {
|
||||||
|
// return listOf(m3u8)
|
||||||
val list = mutableListOf<M3u8Stream>()
|
val list = mutableListOf<M3u8Stream>()
|
||||||
|
|
||||||
val m3u8Parent = getParentLink(m3u8.streamUrl)
|
val m3u8Parent = getParentLink(m3u8.streamUrl)
|
||||||
val response = app.get(m3u8.streamUrl, headers = m3u8.headers, verify = false).text
|
val response = app.get(m3u8.streamUrl, headers = m3u8.headers, verify = false).text
|
||||||
|
|
||||||
var hasAnyContent = false
|
// var hasAnyContent = false
|
||||||
for (match in QUALITY_REGEX.findAll(response)) {
|
for (match in QUALITY_REGEX.findAll(response)) {
|
||||||
hasAnyContent = true
|
// hasAnyContent = true
|
||||||
var (quality, m3u8Link, m3u8Link2) = match.destructured
|
var (quality, m3u8Link, m3u8Link2) = match.destructured
|
||||||
if (m3u8Link.isEmpty()) m3u8Link = m3u8Link2
|
if (m3u8Link.isEmpty()) m3u8Link = m3u8Link2
|
||||||
if (absoluteExtensionDetermination(m3u8Link) == "m3u8") {
|
if (absoluteExtensionDetermination(m3u8Link) == "m3u8") {
|
||||||
|
@ -141,16 +142,14 @@ class M3u8Helper {
|
||||||
m3u8.headers
|
m3u8.headers
|
||||||
), false
|
), false
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
list += M3u8Stream(
|
list += M3u8Stream(
|
||||||
m3u8Link,
|
m3u8Link,
|
||||||
quality.toIntOrNull(),
|
quality.toIntOrNull(),
|
||||||
m3u8.headers
|
m3u8.headers
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
if (returnThis ?: !hasAnyContent) {
|
if (returnThis != false) {
|
||||||
list += M3u8Stream(
|
list += M3u8Stream(
|
||||||
m3u8.streamUrl,
|
m3u8.streamUrl,
|
||||||
Qualities.Unknown.value,
|
Qualities.Unknown.value,
|
||||||
|
@ -169,7 +168,10 @@ class M3u8Helper {
|
||||||
val errored: Boolean = false
|
val errored: Boolean = false
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun hlsYield(qualities: List<M3u8Stream>, startIndex: Int = 0): Iterator<HlsDownloadData> {
|
suspend fun hlsYield(
|
||||||
|
qualities: List<M3u8Stream>,
|
||||||
|
startIndex: Int = 0
|
||||||
|
): Iterator<HlsDownloadData> {
|
||||||
if (qualities.isEmpty()) return listOf(
|
if (qualities.isEmpty()) return listOf(
|
||||||
HlsDownloadData(
|
HlsDownloadData(
|
||||||
byteArrayOf(),
|
byteArrayOf(),
|
||||||
|
|
|
@ -2,74 +2,74 @@
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/player_background"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="horizontal"
|
|
||||||
android:keepScreenOn="true"
|
|
||||||
android:id="@+id/player_background"
|
|
||||||
app:backgroundTint="@android:color/black"
|
|
||||||
android:background="@android:color/black"
|
android:background="@android:color/black"
|
||||||
|
android:keepScreenOn="true"
|
||||||
|
android:orientation="horizontal"
|
||||||
android:screenOrientation="sensorLandscape"
|
android:screenOrientation="sensorLandscape"
|
||||||
|
app:backgroundTint="@android:color/black"
|
||||||
app:surface_type="texture_view">
|
app:surface_type="texture_view">
|
||||||
<!--
|
<!--
|
||||||
app:fastforward_increment="10000"
|
app:fastforward_increment="10000"
|
||||||
app:rewind_increment="10000"-->
|
app:rewind_increment="10000"-->
|
||||||
<com.google.android.exoplayer2.ui.PlayerView
|
<com.google.android.exoplayer2.ui.PlayerView
|
||||||
android:id="@+id/player_view"
|
android:id="@+id/player_view"
|
||||||
app:show_timeout="0"
|
|
||||||
app:hide_on_touch="false"
|
|
||||||
app:auto_show="true"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
app:auto_show="true"
|
||||||
|
app:controller_layout_id="@layout/player_custom_layout_tv"
|
||||||
|
app:hide_on_touch="false"
|
||||||
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:controller_layout_id="@layout/player_custom_layout_tv" />
|
app:show_timeout="0" />
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
|
android:id="@+id/player_loading_overlay"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@android:color/black"
|
||||||
|
android:backgroundTint="@android:color/black"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:id="@+id/player_loading_overlay"
|
|
||||||
android:background="@android:color/black"
|
|
||||||
android:backgroundTint="@android:color/black">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:nextFocusUp="@id/player_loading_go_back"
|
|
||||||
android:nextFocusLeft="@id/player_loading_go_back"
|
|
||||||
|
|
||||||
tools:visibility="visible"
|
|
||||||
android:visibility="gone"
|
|
||||||
android:layout_marginTop="70dp"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:id="@+id/overlay_loading_skip_button"
|
android:id="@+id/overlay_loading_skip_button"
|
||||||
android:text="@string/skip_loading"
|
|
||||||
android:focusable="true"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusableInTouchMode="true"
|
|
||||||
|
|
||||||
app:icon="@drawable/ic_baseline_skip_next_24"
|
|
||||||
style="@style/VideoButtonTV"
|
style="@style/VideoButtonTV"
|
||||||
android:layout_width="wrap_content" />
|
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginTop="70dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:focusableInTouchMode="true"
|
||||||
|
android:nextFocusLeft="@id/player_loading_go_back"
|
||||||
|
android:nextFocusUp="@id/player_loading_go_back"
|
||||||
|
android:text="@string/skip_loading"
|
||||||
|
|
||||||
|
android:visibility="gone"
|
||||||
|
app:icon="@drawable/ic_baseline_skip_next_24"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
|
android:id="@+id/main_load"
|
||||||
android:layout_width="50dp"
|
android:layout_width="50dp"
|
||||||
android:layout_height="50dp"
|
android:layout_height="50dp"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center" />
|
||||||
android:id="@+id/main_load" />
|
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/video_go_back_holder_holder"
|
android:id="@+id/video_go_back_holder_holder"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="5dp"
|
android:layout_margin="5dp"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="30dp"
|
android:layout_width="30dp"
|
||||||
|
@ -79,53 +79,53 @@
|
||||||
app:tint="@android:color/white" />
|
app:tint="@android:color/white" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:nextFocusRight="@id/overlay_loading_skip_button"
|
|
||||||
android:nextFocusDown="@id/overlay_loading_skip_button"
|
|
||||||
android:focusableInTouchMode="true"
|
|
||||||
|
|
||||||
android:id="@+id/player_loading_go_back"
|
android:id="@+id/player_loading_go_back"
|
||||||
android:layout_width="70dp"
|
android:layout_width="70dp"
|
||||||
android:layout_height="70dp"
|
android:layout_height="70dp"
|
||||||
|
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:focusable="true"
|
android:background="@drawable/video_tap_button_always_white"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:background="@drawable/video_tap_button_always_white" />
|
android:focusable="true"
|
||||||
|
android:focusableInTouchMode="true"
|
||||||
|
android:nextFocusRight="@id/overlay_loading_skip_button"
|
||||||
|
android:nextFocusDown="@id/overlay_loading_skip_button" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:visibility="gone"
|
android:id="@+id/player_torrent_info"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
android:paddingStart="20dp"
|
android:paddingStart="20dp"
|
||||||
android:paddingEnd="20dp"
|
android:paddingEnd="20dp"
|
||||||
|
android:visibility="gone"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
android:id="@+id/player_torrent_info"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
android:id="@+id/video_torrent_progress"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
|
||||||
android:gravity="start"
|
|
||||||
android:layout_marginTop="15dp"
|
android:layout_marginTop="15dp"
|
||||||
android:textStyle="bold"
|
android:gravity="start"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:id="@+id/video_torrent_progress"
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="78% at 18kb/s" />
|
tools:text="78% at 18kb/s" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
android:id="@+id/video_torrent_seeders"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
|
||||||
android:gravity="start"
|
|
||||||
android:layout_marginTop="0dp"
|
android:layout_marginTop="0dp"
|
||||||
|
android:gravity="start"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:id="@+id/video_torrent_seeders"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
tools:text="17 seeders"
|
app:layout_constraintTop_toBottomOf="@+id/player_video_title"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/player_video_title" />
|
tools:text="17 seeders" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -75,15 +75,15 @@
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/player_episode_filler_holder"
|
android:id="@+id/player_episode_filler_holder"
|
||||||
android:layout_margin="20dp"
|
|
||||||
android:layout_gravity="end"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layout_margin="20dp">
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/player_episode_filler"
|
||||||
style="@style/SmallBlackButton"
|
style="@style/SmallBlackButton"
|
||||||
android:text="@string/filler"
|
android:text="@string/filler" />
|
||||||
android:id="@+id/player_episode_filler" />
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
@ -149,9 +149,9 @@
|
||||||
android:layout_width="30dp"
|
android:layout_width="30dp"
|
||||||
android:layout_height="30dp"
|
android:layout_height="30dp"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
|
android:contentDescription="@string/go_back_img_des"
|
||||||
android:src="@drawable/ic_baseline_arrow_back_24"
|
android:src="@drawable/ic_baseline_arrow_back_24"
|
||||||
app:tint="@android:color/white"
|
app:tint="@android:color/white" />
|
||||||
android:contentDescription="@string/go_back_img_des" />
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/player_go_back"
|
android:id="@+id/player_go_back"
|
||||||
|
@ -160,8 +160,8 @@
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:background="@drawable/video_tap_button_always_white"
|
android:background="@drawable/video_tap_button_always_white"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:contentDescription="@string/go_back_img_des"
|
||||||
android:contentDescription="@string/go_back_img_des" />
|
android:focusable="true" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
|
|
||||||
|
@ -169,54 +169,54 @@
|
||||||
|
|
||||||
<!--use for thinner app:trackThickness="3dp" com.google.android.material.progressindicator.CircularProgressIndicator-->
|
<!--use for thinner app:trackThickness="3dp" com.google.android.material.progressindicator.CircularProgressIndicator-->
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:id="@+id/player_buffering"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
android:layout_width="wrap_content"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
android:layout_gravity="center"
|
||||||
|
|
||||||
android:focusable="false"
|
|
||||||
android:clickable="false"
|
android:clickable="false"
|
||||||
|
android:focusable="false"
|
||||||
android:focusableInTouchMode="false"
|
android:focusableInTouchMode="false"
|
||||||
|
|
||||||
android:indeterminate="true"
|
android:indeterminate="true"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible"
|
|
||||||
android:id="@+id/player_buffering"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_width="wrap_content" />
|
|
||||||
|
|
||||||
<!-- This nested layout is necessary because of buffering and clicking-->
|
|
||||||
<FrameLayout
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<!-- This nested layout is necessary because of buffering and clicking-->
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/player_pause_play_holder_holder"
|
||||||
android:layout_width="100dp"
|
android:layout_width="100dp"
|
||||||
android:layout_height="100dp"
|
android:layout_height="100dp"
|
||||||
android:id="@+id/player_pause_play_holder_holder">
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
tools:ignore="uselessParent"
|
|
||||||
android:id="@+id/player_pause_play_holder"
|
android:id="@+id/player_pause_play_holder"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
tools:ignore="uselessParent">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
app:tint="@color/white"
|
|
||||||
android:id="@+id/player_pause_play"
|
android:id="@+id/player_pause_play"
|
||||||
|
android:layout_width="70dp"
|
||||||
|
android:layout_height="70dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:background="@drawable/video_tap_button"
|
||||||
android:nextFocusLeft="@id/exo_rew"
|
android:nextFocusLeft="@id/exo_rew"
|
||||||
|
|
||||||
android:nextFocusRight="@id/exo_ffwd"
|
android:nextFocusRight="@id/exo_ffwd"
|
||||||
|
|
||||||
android:nextFocusUp="@id/player_go_back"
|
android:nextFocusUp="@id/player_go_back"
|
||||||
android:nextFocusDown="@id/player_lock"
|
android:nextFocusDown="@id/player_lock"
|
||||||
|
|
||||||
android:layout_gravity="center"
|
|
||||||
|
|
||||||
android:src="@drawable/netflix_pause"
|
android:src="@drawable/netflix_pause"
|
||||||
android:background="@drawable/video_tap_button"
|
app:tint="@color/white"
|
||||||
|
|
||||||
android:layout_width="70dp"
|
|
||||||
android:layout_height="70dp"
|
|
||||||
tools:ignore="ContentDescription" />
|
tools:ignore="ContentDescription" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
@ -227,8 +227,8 @@
|
||||||
android:layout_height="100dp"
|
android:layout_height="100dp"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layoutDirection="ltr"
|
android:layoutDirection="ltr"
|
||||||
|
android:orientation="horizontal"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
@ -238,12 +238,12 @@
|
||||||
android:id="@+id/player_rew_holder"
|
android:id="@+id/player_rew_holder"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintWidth_percent="0.5"
|
|
||||||
android:layout_gravity="center_vertical|start"
|
android:layout_gravity="center_vertical|start"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toLeftOf="@id/player_ffwd_holder"
|
app:layout_constraintRight_toLeftOf="@id/player_ffwd_holder"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintWidth_percent="0.5">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/exo_rew_text"
|
android:id="@+id/exo_rew_text"
|
||||||
|
@ -272,21 +272,21 @@
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:scaleX="-1"
|
android:scaleX="-1"
|
||||||
android:src="@drawable/netflix_skip_forward"
|
android:src="@drawable/netflix_skip_forward"
|
||||||
app:tint="@color/white"
|
|
||||||
android:tintMode="src_in"
|
android:tintMode="src_in"
|
||||||
|
app:tint="@color/white"
|
||||||
tools:ignore="ContentDescription" />
|
tools:ignore="ContentDescription" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/player_ffwd_holder"
|
android:id="@+id/player_ffwd_holder"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
app:layout_constraintWidth_percent="0.5"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical|end"
|
android:layout_gravity="center_vertical|end"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintLeft_toRightOf="@id/player_rew_holder"
|
app:layout_constraintLeft_toRightOf="@id/player_rew_holder"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintWidth_percent="0.5">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/exo_ffwd_text"
|
android:id="@+id/exo_ffwd_text"
|
||||||
|
@ -312,8 +312,8 @@
|
||||||
android:padding="10dp"
|
android:padding="10dp"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:src="@drawable/netflix_skip_forward"
|
android:src="@drawable/netflix_skip_forward"
|
||||||
app:tint="@color/white"
|
|
||||||
android:tintMode="src_in"
|
android:tintMode="src_in"
|
||||||
|
app:tint="@color/white"
|
||||||
tools:ignore="ContentDescription" />
|
tools:ignore="ContentDescription" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
@ -333,48 +333,48 @@
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@id/exo_prev"
|
android:id="@id/exo_prev"
|
||||||
style="@style/ExoMediaButton.Previous"
|
style="@style/ExoMediaButton.Previous"
|
||||||
app:tint="?attr/colorPrimaryDark"
|
|
||||||
android:tintMode="src_in"
|
android:tintMode="src_in"
|
||||||
|
app:tint="?attr/colorPrimaryDark"
|
||||||
tools:ignore="ContentDescription" />
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@id/exo_repeat_toggle"
|
android:id="@id/exo_repeat_toggle"
|
||||||
style="@style/ExoMediaButton"
|
style="@style/ExoMediaButton"
|
||||||
app:tint="?attr/colorPrimaryDark"
|
|
||||||
android:tintMode="src_in"
|
android:tintMode="src_in"
|
||||||
|
app:tint="?attr/colorPrimaryDark"
|
||||||
tools:ignore="ContentDescription" />
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@id/exo_next"
|
android:id="@id/exo_next"
|
||||||
style="@style/ExoMediaButton.Next"
|
style="@style/ExoMediaButton.Next"
|
||||||
app:tint="?attr/colorPrimaryDark"
|
|
||||||
android:tintMode="src_in"
|
android:tintMode="src_in"
|
||||||
|
app:tint="?attr/colorPrimaryDark"
|
||||||
tools:ignore="ContentDescription" />
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@id/exo_vr"
|
android:id="@id/exo_vr"
|
||||||
style="@style/ExoMediaButton.VR"
|
style="@style/ExoMediaButton.VR"
|
||||||
app:tint="?attr/colorPrimaryDark"
|
|
||||||
android:tintMode="src_in"
|
android:tintMode="src_in"
|
||||||
|
app:tint="?attr/colorPrimaryDark"
|
||||||
tools:ignore="ContentDescription" />
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@id/exo_play"
|
android:id="@id/exo_play"
|
||||||
app:tint="?attr/colorPrimaryDark"
|
android:layout_width="0dp"
|
||||||
android:tintMode="src_in"
|
|
||||||
tools:ignore="ContentDescription"
|
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_width="0dp" />
|
android:tintMode="src_in"
|
||||||
|
app:tint="?attr/colorPrimaryDark"
|
||||||
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@id/exo_pause"
|
android:id="@id/exo_pause"
|
||||||
app:tint="?attr/colorPrimaryDark"
|
android:layout_width="0dp"
|
||||||
android:tintMode="src_in"
|
|
||||||
tools:ignore="ContentDescription"
|
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_width="0dp" />
|
android:tintMode="src_in"
|
||||||
|
app:tint="?attr/colorPrimaryDark"
|
||||||
|
tools:ignore="ContentDescription" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
@ -489,25 +489,37 @@
|
||||||
tools:text="Speed" />
|
tools:text="Speed" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:visibility="gone"
|
|
||||||
tools:visibility="visible"
|
|
||||||
android:id="@+id/player_subtitle_offset_btt"
|
android:id="@+id/player_subtitle_offset_btt"
|
||||||
style="@style/VideoButton"
|
style="@style/VideoButton"
|
||||||
android:nextFocusLeft="@id/player_speed_btt"
|
android:nextFocusLeft="@id/player_speed_btt"
|
||||||
|
|
||||||
android:nextFocusRight="@id/player_sources_btt"
|
android:nextFocusRight="@id/player_sources_btt"
|
||||||
|
android:text="@string/subtitle_offset"
|
||||||
|
|
||||||
|
android:visibility="gone"
|
||||||
app:icon="@drawable/ic_outline_subtitles_24"
|
app:icon="@drawable/ic_outline_subtitles_24"
|
||||||
android:text="@string/subtitle_offset" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/player_sources_btt"
|
android:id="@+id/player_sources_btt"
|
||||||
style="@style/VideoButton"
|
style="@style/VideoButton"
|
||||||
android:nextFocusLeft="@id/player_subtitle_offset_btt"
|
android:layout_height="40dp"
|
||||||
|
|
||||||
android:nextFocusRight="@id/player_skip_op"
|
android:nextFocusLeft="@id/player_subtitle_offset_btt"
|
||||||
|
android:nextFocusRight="@id/player_tracks_btt"
|
||||||
android:text="@string/video_source"
|
android:text="@string/video_source"
|
||||||
app:icon="@drawable/ic_baseline_playlist_play_24" />
|
app:icon="@drawable/ic_baseline_playlist_play_24" />
|
||||||
|
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/player_tracks_btt"
|
||||||
|
style="@style/VideoButton"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
|
||||||
|
android:nextFocusLeft="@id/player_sources_btt"
|
||||||
|
android:nextFocusRight="@id/player_skip_op"
|
||||||
|
android:text="@string/tracks"
|
||||||
|
app:icon="@drawable/ic_baseline_playlist_play_24" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/player_skip_op"
|
android:id="@+id/player_skip_op"
|
||||||
style="@style/VideoButton"
|
style="@style/VideoButton"
|
||||||
|
@ -568,14 +580,14 @@
|
||||||
style="@android:style/Widget.Material.ProgressBar.Horizontal"
|
style="@android:style/Widget.Material.ProgressBar.Horizontal"
|
||||||
android:layout_width="4dp"
|
android:layout_width="4dp"
|
||||||
android:layout_height="150dp"
|
android:layout_height="150dp"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
android:layout_gravity="end|center_vertical"
|
android:layout_gravity="end|center_vertical"
|
||||||
android:layout_marginStart="40dp"
|
android:layout_marginStart="40dp"
|
||||||
android:indeterminate="false"
|
android:indeterminate="false"
|
||||||
android:max="100"
|
android:max="100"
|
||||||
android:progress="100"
|
android:progress="100"
|
||||||
android:progressDrawable="@drawable/progress_drawable_vertical"
|
android:progressDrawable="@drawable/progress_drawable_vertical"
|
||||||
tools:progress="30"
|
tools:progress="30" />
|
||||||
android:layout_centerInParent="true" />
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
|
@ -610,13 +622,13 @@
|
||||||
style="@android:style/Widget.Material.ProgressBar.Horizontal"
|
style="@android:style/Widget.Material.ProgressBar.Horizontal"
|
||||||
android:layout_width="4dp"
|
android:layout_width="4dp"
|
||||||
android:layout_height="150dp"
|
android:layout_height="150dp"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
android:layout_gravity="end|center_vertical"
|
android:layout_gravity="end|center_vertical"
|
||||||
android:layout_marginEnd="40dp"
|
android:layout_marginEnd="40dp"
|
||||||
android:indeterminate="false"
|
android:indeterminate="false"
|
||||||
android:max="100"
|
android:max="100"
|
||||||
android:progress="100"
|
android:progress="100"
|
||||||
android:progressDrawable="@drawable/progress_drawable_vertical"
|
android:progressDrawable="@drawable/progress_drawable_vertical" />
|
||||||
android:layout_centerInParent="true" />
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:screenOrientation="landscape"
|
android:screenOrientation="landscape"
|
||||||
tools:orientation="vertical"
|
android:tag="television"
|
||||||
android:tag="television">
|
tools:orientation="vertical">
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/subtitle_holder"
|
android:id="@+id/subtitle_holder"
|
||||||
|
@ -115,9 +115,9 @@
|
||||||
android:layout_width="30dp"
|
android:layout_width="30dp"
|
||||||
android:layout_height="30dp"
|
android:layout_height="30dp"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
|
android:contentDescription="@string/go_back_img_des"
|
||||||
android:src="@drawable/ic_baseline_arrow_back_24"
|
android:src="@drawable/ic_baseline_arrow_back_24"
|
||||||
app:tint="@android:color/white"
|
app:tint="@android:color/white" />
|
||||||
android:contentDescription="@string/go_back_img_des" />
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/player_go_back"
|
android:id="@+id/player_go_back"
|
||||||
|
@ -323,25 +323,34 @@
|
||||||
tools:text="Speed" />
|
tools:text="Speed" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:visibility="gone"
|
|
||||||
tools:visibility="visible"
|
|
||||||
android:id="@+id/player_subtitle_offset_btt"
|
android:id="@+id/player_subtitle_offset_btt"
|
||||||
style="@style/VideoButtonTV"
|
style="@style/VideoButtonTV"
|
||||||
android:nextFocusLeft="@id/player_speed_btt"
|
android:nextFocusLeft="@id/player_speed_btt"
|
||||||
|
|
||||||
android:nextFocusRight="@id/player_sources_btt"
|
android:nextFocusRight="@id/player_sources_btt"
|
||||||
|
android:text="@string/subtitle_offset"
|
||||||
|
|
||||||
|
android:visibility="gone"
|
||||||
app:icon="@drawable/ic_outline_subtitles_24"
|
app:icon="@drawable/ic_outline_subtitles_24"
|
||||||
android:text="@string/subtitle_offset"/>
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/player_sources_btt"
|
android:id="@+id/player_sources_btt"
|
||||||
style="@style/VideoButtonTV"
|
style="@style/VideoButtonTV"
|
||||||
android:nextFocusLeft="@id/player_subtitle_offset_btt"
|
android:nextFocusLeft="@id/player_subtitle_offset_btt"
|
||||||
android:nextFocusRight="@id/player_skip_op"
|
android:nextFocusRight="@id/player_tracks_btt"
|
||||||
android:nextFocusUp="@id/player_pause_play"
|
android:nextFocusUp="@id/player_pause_play"
|
||||||
android:text="@string/video_source"
|
android:text="@string/video_source"
|
||||||
app:icon="@drawable/ic_baseline_playlist_play_24" />
|
app:icon="@drawable/ic_baseline_playlist_play_24" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/player_tracks_btt"
|
||||||
|
style="@style/VideoButtonTV"
|
||||||
|
android:nextFocusLeft="@id/player_sources_btt"
|
||||||
|
android:nextFocusRight="@id/player_skip_op"
|
||||||
|
android:nextFocusUp="@id/player_pause_play"
|
||||||
|
android:text="@string/tracks"
|
||||||
|
app:icon="@drawable/ic_baseline_playlist_play_24" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/player_skip_op"
|
android:id="@+id/player_skip_op"
|
||||||
style="@style/VideoButtonTV"
|
style="@style/VideoButtonTV"
|
||||||
|
|
148
app/src/main/res/layout/player_select_tracks.xml
Normal file
148
app/src/main/res/layout/player_select_tracks.xml
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
<?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="match_parent"
|
||||||
|
android:background="@null"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginBottom="60dp"
|
||||||
|
android:baselineAligned="false"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/video_tracks_holder"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="50"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_rowWeight="1"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
|
android:text="@string/video_tracks"
|
||||||
|
android:textColor="?attr/textColor"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:requiresFadingEdge="vertical"
|
||||||
|
android:id="@+id/video_tracks_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_rowWeight="1"
|
||||||
|
android:background="?attr/primaryBlackBackground"
|
||||||
|
android:nextFocusLeft="@id/sort_subtitles"
|
||||||
|
android:nextFocusRight="@id/apply_btt"
|
||||||
|
tools:listitem="@layout/sort_bottom_single_choice" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/audio_tracks_holder"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="50"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<!-- android:id="@+id/subs_settings" android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||||
|
-->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
tools:ignore="UseCompoundDrawables">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:foreground="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:id="@+id/subtitles_click_settings"
|
||||||
|
android:layout_rowWeight="1"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||||
|
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||||
|
android:text="@string/audio_tracks"
|
||||||
|
android:textColor="?attr/textColor"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<!-- <TextView-->
|
||||||
|
<!-- android:textSize="15sp"-->
|
||||||
|
<!-- android:id="@+id/subtitles_encoding_format"-->
|
||||||
|
<!-- android:textColor="?attr/textColor"-->
|
||||||
|
<!-- android:layout_gravity="center"-->
|
||||||
|
<!-- android:gravity="center"-->
|
||||||
|
<!-- tools:text="Thai (TIS 620-2533/ISO 8859-11)"-->
|
||||||
|
<!-- android:layout_width="wrap_content"-->
|
||||||
|
<!-- android:layout_height="wrap_content" />-->
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="25dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end|center_vertical"
|
||||||
|
android:layout_marginTop="0dp"
|
||||||
|
android:layout_marginEnd="10dp"
|
||||||
|
|
||||||
|
android:contentDescription="@string/home_change_provider_img_des"
|
||||||
|
android:src="@drawable/ic_outline_settings_24"
|
||||||
|
android:visibility="gone" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:requiresFadingEdge="vertical"
|
||||||
|
android:id="@+id/auto_tracks_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_rowWeight="1"
|
||||||
|
android:background="?attr/primaryBlackBackground"
|
||||||
|
android:nextFocusLeft="@id/sort_providers"
|
||||||
|
android:nextFocusRight="@id/cancel_btt"
|
||||||
|
tools:listfooter="@layout/sort_bottom_footer_add_choice"
|
||||||
|
tools:listitem="@layout/sort_bottom_single_choice" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/apply_btt_holder"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:gravity="bottom|end"
|
||||||
|
android:layout_marginTop="-60dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="60dp">
|
||||||
|
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
style="@style/WhiteButton"
|
||||||
|
android:layout_gravity="center_vertical|end"
|
||||||
|
android:text="@string/sort_apply"
|
||||||
|
android:id="@+id/apply_btt"
|
||||||
|
android:layout_width="wrap_content" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
style="@style/BlackButton"
|
||||||
|
android:layout_gravity="center_vertical|end"
|
||||||
|
android:text="@string/sort_cancel"
|
||||||
|
android:id="@+id/cancel_btt"
|
||||||
|
android:layout_width="wrap_content" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
|
@ -609,5 +609,8 @@
|
||||||
|
|
||||||
<string name="download_all_plugins_from_repo">Download all plugins from this repository?</string>
|
<string name="download_all_plugins_from_repo">Download all plugins from this repository?</string>
|
||||||
<string name="single_plugin_disabled" formatted="true">%s (Disabled)</string>
|
<string name="single_plugin_disabled" formatted="true">%s (Disabled)</string>
|
||||||
|
<string name="tracks">Tracks</string>
|
||||||
|
<string name="audio_tracks">Audio tracks</string>
|
||||||
|
<string name="video_tracks">Video tracks</string>
|
||||||
<string name="apply_on_restart">Apply on Restart</string>
|
<string name="apply_on_restart">Apply on Restart</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue