mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
added custom subtitles
This commit is contained in:
parent
914238597e
commit
4ec287d1e9
4 changed files with 246 additions and 152 deletions
|
@ -29,6 +29,7 @@ import android.view.animation.Animation
|
||||||
import android.view.animation.AnimationUtils
|
import android.view.animation.AnimationUtils
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
import android.widget.Toast.LENGTH_SHORT
|
import android.widget.Toast.LENGTH_SHORT
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.graphics.blue
|
import androidx.core.graphics.blue
|
||||||
import androidx.core.graphics.green
|
import androidx.core.graphics.green
|
||||||
|
@ -47,7 +48,7 @@ import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
import com.google.android.exoplayer2.*
|
import com.google.android.exoplayer2.*
|
||||||
import com.google.android.exoplayer2.C.TIME_UNSET
|
import com.google.android.exoplayer2.C.TIME_UNSET
|
||||||
import com.google.android.exoplayer2.database.StandaloneDatabaseProvider
|
import com.google.android.exoplayer2.database.StandaloneDatabaseProvider
|
||||||
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory
|
import com.google.android.exoplayer2.source.*
|
||||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
|
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
|
||||||
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout
|
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout
|
||||||
import com.google.android.exoplayer2.ui.SubtitleView
|
import com.google.android.exoplayer2.ui.SubtitleView
|
||||||
|
@ -63,6 +64,7 @@ import com.google.android.gms.cast.framework.CastButtonFactory
|
||||||
import com.google.android.gms.cast.framework.CastContext
|
import com.google.android.gms.cast.framework.CastContext
|
||||||
import com.google.android.gms.cast.framework.CastState
|
import com.google.android.gms.cast.framework.CastState
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
|
import com.hippo.unifile.UniFile
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.MainActivity.Companion.canEnterPipMode
|
import com.lagradost.cloudstream3.MainActivity.Companion.canEnterPipMode
|
||||||
import com.lagradost.cloudstream3.MainActivity.Companion.getCastSession
|
import com.lagradost.cloudstream3.MainActivity.Companion.getCastSession
|
||||||
|
@ -100,6 +102,7 @@ import com.lagradost.cloudstream3.utils.VideoDownloadManager.getId
|
||||||
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.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import okhttp3.internal.format
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import javax.net.ssl.HttpsURLConnection
|
import javax.net.ssl.HttpsURLConnection
|
||||||
import javax.net.ssl.SSLContext
|
import javax.net.ssl.SSLContext
|
||||||
|
@ -766,6 +769,32 @@ class PlayerFragment : Fragment() {
|
||||||
safeReleasePlayer()
|
safeReleasePlayer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Open file picker
|
||||||
|
private val subsPathPicker =
|
||||||
|
registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
|
||||||
|
// It lies, it can be null if file manager quits.
|
||||||
|
if (uri == null) return@registerForActivityResult
|
||||||
|
val context = context ?: AcraApplication.context ?: return@registerForActivityResult
|
||||||
|
// RW perms for the path
|
||||||
|
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
|
||||||
|
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||||
|
|
||||||
|
context.contentResolver.takePersistableUriPermission(uri, flags)
|
||||||
|
|
||||||
|
val file = UniFile.fromUri(context, uri)
|
||||||
|
println("Selected URI path: $uri - Full path: ${file.filePath}")
|
||||||
|
// DO NOT REMOVE THE FILE EXTENSION FROM NAME, IT'S NEEDED FOR MIME TYPES
|
||||||
|
val name = file.name ?: uri.toString()
|
||||||
|
|
||||||
|
viewModel.loadSubtitleFile(uri, name, getEpisode()?.id)
|
||||||
|
setPreferredSubLanguage(name)
|
||||||
|
showToast(
|
||||||
|
activity,
|
||||||
|
format(context.getString(R.string.player_loaded_subtitles), name),
|
||||||
|
1000
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private class SettingsContentObserver(handler: Handler?, val activity: Activity) :
|
private class SettingsContentObserver(handler: Handler?, val activity: Activity) :
|
||||||
ContentObserver(handler) {
|
ContentObserver(handler) {
|
||||||
private val audioManager = activity.getSystemService(AUDIO_SERVICE) as? AudioManager
|
private val audioManager = activity.getSystemService(AUDIO_SERVICE) as? AudioManager
|
||||||
|
@ -1231,12 +1260,30 @@ class PlayerFragment : Fragment() {
|
||||||
sourceDialog.findViewById<MaterialButton>(R.id.cancel_btt)!!
|
sourceDialog.findViewById<MaterialButton>(R.id.cancel_btt)!!
|
||||||
val subsSettings = sourceDialog.findViewById<View>(R.id.subs_settings)!!
|
val subsSettings = sourceDialog.findViewById<View>(R.id.subs_settings)!!
|
||||||
|
|
||||||
|
val subtitleLoadButton =
|
||||||
|
sourceDialog.findViewById<MaterialButton>(R.id.load_btt)!!
|
||||||
|
|
||||||
|
subtitleLoadButton.setOnClickListener {
|
||||||
|
// "vtt" -> "text/vtt"
|
||||||
|
// "srt" -> "application/x-subrip"// "text/plain"
|
||||||
|
subsPathPicker.launch(
|
||||||
|
arrayOf(
|
||||||
|
"text/vtt",
|
||||||
|
"application/x-subrip",
|
||||||
|
"text/plain",
|
||||||
|
"text/str",
|
||||||
|
"application/octet-stream"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
subsSettings.setOnClickListener {
|
subsSettings.setOnClickListener {
|
||||||
autoHide()
|
autoHide()
|
||||||
saveArguments()
|
saveArguments()
|
||||||
SubtitlesFragment.push(activity)
|
SubtitlesFragment.push(activity)
|
||||||
sourceDialog.dismissSafe(activity)
|
sourceDialog.dismissSafe(activity)
|
||||||
}
|
}
|
||||||
|
|
||||||
var sourceIndex = 0
|
var sourceIndex = 0
|
||||||
var startSource = 0
|
var startSource = 0
|
||||||
var sources: List<ExtractorLink> = emptyList()
|
var sources: List<ExtractorLink> = emptyList()
|
||||||
|
@ -2116,40 +2163,7 @@ class PlayerFragment : Fragment() {
|
||||||
mediaItemBuilder.setUri(uriPrimary)
|
mediaItemBuilder.setUri(uriPrimary)
|
||||||
}
|
}
|
||||||
|
|
||||||
val subs = context?.getSubs() ?: emptyList()
|
fun getDataSourceFactory(isOnline: Boolean): DataSource.Factory {
|
||||||
val subItems = ArrayList<MediaItem.SubtitleConfiguration>()
|
|
||||||
val subItemsId = ArrayList<String>()
|
|
||||||
|
|
||||||
for (sub in sortSubs(subs)) {
|
|
||||||
val langId =
|
|
||||||
sub.lang.trimEnd() //SubtitleHelper.fromLanguageToTwoLetters(it.lang) ?: it.lang
|
|
||||||
subItemsId.add(langId)
|
|
||||||
subItems.add(
|
|
||||||
MediaItem.SubtitleConfiguration.Builder(Uri.parse(sub.url))
|
|
||||||
.setMimeType(sub.url.toSubtitleMimeType())
|
|
||||||
.setLanguage("_$langId")
|
|
||||||
.setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
activeSubtitles = subItemsId
|
|
||||||
mediaItemBuilder.setSubtitleConfigurations(subItems)
|
|
||||||
|
|
||||||
//might add https://github.com/ed828a/Aihua/blob/1896f46888b5a954b367e83f40b845ce174a2328/app/src/main/java/com/dew/aihua/player/playerUI/VideoPlayer.kt#L287 toggle caps
|
|
||||||
|
|
||||||
val mediaItem = mediaItemBuilder.build()
|
|
||||||
val trackSelector = DefaultTrackSelector(requireContext())
|
|
||||||
// Disable subtitles
|
|
||||||
trackSelector.parameters = DefaultTrackSelector.ParametersBuilder(requireContext())
|
|
||||||
// .setRendererDisabled(C.TRACK_TYPE_VIDEO, true)
|
|
||||||
.setRendererDisabled(C.TRACK_TYPE_TEXT, true)
|
|
||||||
.setDisabledTextTrackSelectionFlags(C.TRACK_TYPE_TEXT)
|
|
||||||
.clearSelectionOverrides()
|
|
||||||
.build()
|
|
||||||
|
|
||||||
fun getDataSourceFactory(): DataSource.Factory {
|
|
||||||
return if (isOnline) {
|
return if (isOnline) {
|
||||||
DefaultHttpDataSource.Factory().apply {
|
DefaultHttpDataSource.Factory().apply {
|
||||||
setUserAgent(USER_AGENT)
|
setUserAgent(USER_AGENT)
|
||||||
|
@ -2174,6 +2188,41 @@ class PlayerFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val subs = context?.getSubs() ?: emptyList()
|
||||||
|
val subItemsId = ArrayList<String>()
|
||||||
|
|
||||||
|
val subSources = sortSubs(subs).map { sub ->
|
||||||
|
// The url can look like .../document/4294 when the name is EnglishSDH.srt
|
||||||
|
val subtitleMimeType =
|
||||||
|
if (sub.url.startsWith("content")) sub.lang.toSubtitleMimeType() else sub.url.toSubtitleMimeType()
|
||||||
|
|
||||||
|
val langId =
|
||||||
|
sub.lang.trimEnd() //SubtitleHelper.fromLanguageToTwoLetters(it.lang) ?: it.lang
|
||||||
|
subItemsId.add(langId)
|
||||||
|
val subConfig = MediaItem.SubtitleConfiguration.Builder(Uri.parse(sub.url))
|
||||||
|
.setMimeType(subtitleMimeType)
|
||||||
|
.setLanguage("_$langId")
|
||||||
|
.setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
|
||||||
|
.build()
|
||||||
|
SingleSampleMediaSource.Factory(getDataSourceFactory(!sub.url.startsWith("content")))
|
||||||
|
.createMediaSource(subConfig, TIME_UNSET)
|
||||||
|
}
|
||||||
|
|
||||||
|
activeSubtitles = subItemsId
|
||||||
|
// mediaItemBuilder.setSubtitleConfigurations(subItems)
|
||||||
|
|
||||||
|
//might add https://github.com/ed828a/Aihua/blob/1896f46888b5a954b367e83f40b845ce174a2328/app/src/main/java/com/dew/aihua/player/playerUI/VideoPlayer.kt#L287 toggle caps
|
||||||
|
|
||||||
|
val mediaItem = mediaItemBuilder.build()
|
||||||
|
val trackSelector = DefaultTrackSelector(requireContext())
|
||||||
|
// Disable subtitles
|
||||||
|
trackSelector.parameters = DefaultTrackSelector.ParametersBuilder(requireContext())
|
||||||
|
// .setRendererDisabled(C.TRACK_TYPE_VIDEO, true)
|
||||||
|
.setRendererDisabled(C.TRACK_TYPE_TEXT, true)
|
||||||
|
.setDisabledTextTrackSelectionFlags(C.TRACK_TYPE_TEXT)
|
||||||
|
.clearSelectionOverrides()
|
||||||
|
.build()
|
||||||
|
|
||||||
normalSafeApiCall {
|
normalSafeApiCall {
|
||||||
val databaseProvider = StandaloneDatabaseProvider(requireContext())
|
val databaseProvider = StandaloneDatabaseProvider(requireContext())
|
||||||
simpleCache = SimpleCache(
|
simpleCache = SimpleCache(
|
||||||
|
@ -2187,18 +2236,23 @@ class PlayerFragment : Fragment() {
|
||||||
|
|
||||||
val cacheFactory = CacheDataSource.Factory().apply {
|
val cacheFactory = CacheDataSource.Factory().apply {
|
||||||
simpleCache?.let { setCache(it) }
|
simpleCache?.let { setCache(it) }
|
||||||
setUpstreamDataSourceFactory(getDataSourceFactory())
|
setUpstreamDataSourceFactory(getDataSourceFactory(isOnline))
|
||||||
}
|
}
|
||||||
|
|
||||||
val exoPlayerBuilder =
|
val exoPlayerBuilder =
|
||||||
ExoPlayer.Builder(requireContext())
|
ExoPlayer.Builder(requireContext())
|
||||||
.setTrackSelector(trackSelector)
|
.setTrackSelector(trackSelector)
|
||||||
|
|
||||||
|
val videoMediaSource =
|
||||||
|
DefaultMediaSourceFactory(cacheFactory).createMediaSource(mediaItem)
|
||||||
|
|
||||||
exoPlayer = exoPlayerBuilder.build().apply {
|
exoPlayer = exoPlayerBuilder.build().apply {
|
||||||
playWhenReady = isPlayerPlaying
|
playWhenReady = isPlayerPlaying
|
||||||
seekTo(currentWindow, playbackPosition)
|
seekTo(currentWindow, playbackPosition)
|
||||||
setMediaSource(
|
setMediaSource(
|
||||||
DefaultMediaSourceFactory(cacheFactory).createMediaSource(mediaItem),
|
MergingMediaSource(
|
||||||
|
videoMediaSource, *subSources.toTypedArray()
|
||||||
|
),
|
||||||
playbackPosition
|
playbackPosition
|
||||||
)
|
)
|
||||||
prepare()
|
prepare()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.lagradost.cloudstream3.ui.result
|
package com.lagradost.cloudstream3.ui.result
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
@ -57,7 +58,8 @@ class ResultViewModel : ViewModel() {
|
||||||
|
|
||||||
private val _resultResponse: MutableLiveData<Resource<Any?>> = MutableLiveData()
|
private val _resultResponse: MutableLiveData<Resource<Any?>> = MutableLiveData()
|
||||||
private val _episodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData()
|
private val _episodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData()
|
||||||
private val episodeById: MutableLiveData<HashMap<Int, Int>> = MutableLiveData() // lookup by ID to get Index
|
private val episodeById: MutableLiveData<HashMap<Int, Int>> =
|
||||||
|
MutableLiveData() // lookup by ID to get Index
|
||||||
|
|
||||||
private val _publicEpisodes: MutableLiveData<Resource<List<ResultEpisode>>> = MutableLiveData()
|
private val _publicEpisodes: MutableLiveData<Resource<List<ResultEpisode>>> = MutableLiveData()
|
||||||
private val _publicEpisodesCount: MutableLiveData<Int> = MutableLiveData() // before the sorting
|
private val _publicEpisodesCount: MutableLiveData<Int> = MutableLiveData() // before the sorting
|
||||||
|
@ -83,7 +85,8 @@ class ResultViewModel : ViewModel() {
|
||||||
private val _dubSubSelections: MutableLiveData<Set<DubStatus>> = MutableLiveData()
|
private val _dubSubSelections: MutableLiveData<Set<DubStatus>> = MutableLiveData()
|
||||||
|
|
||||||
val dubSubEpisodes: LiveData<Map<DubStatus, List<ResultEpisode>>?> get() = _dubSubEpisodes
|
val dubSubEpisodes: LiveData<Map<DubStatus, List<ResultEpisode>>?> get() = _dubSubEpisodes
|
||||||
private val _dubSubEpisodes: MutableLiveData<Map<DubStatus, List<ResultEpisode>>?> = MutableLiveData()
|
private val _dubSubEpisodes: MutableLiveData<Map<DubStatus, List<ResultEpisode>>?> =
|
||||||
|
MutableLiveData()
|
||||||
|
|
||||||
private val _watchStatus: MutableLiveData<WatchType> = MutableLiveData()
|
private val _watchStatus: MutableLiveData<WatchType> = MutableLiveData()
|
||||||
val watchStatus: LiveData<WatchType> get() = _watchStatus
|
val watchStatus: LiveData<WatchType> get() = _watchStatus
|
||||||
|
@ -291,7 +294,14 @@ class ResultViewModel : ViewModel() {
|
||||||
_apiName.postValue(apiName)
|
_apiName.postValue(apiName)
|
||||||
val api = getApiFromNameNull(apiName)
|
val api = getApiFromNameNull(apiName)
|
||||||
if (api == null) {
|
if (api == null) {
|
||||||
_resultResponse.postValue(Resource.Failure(false, null, null, "This provider does not exist"))
|
_resultResponse.postValue(
|
||||||
|
Resource.Failure(
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"This provider does not exist"
|
||||||
|
)
|
||||||
|
)
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
repo = APIRepository(api)
|
repo = APIRepository(api)
|
||||||
|
@ -335,7 +345,8 @@ class ResultViewModel : ViewModel() {
|
||||||
_dubStatus.postValue(dubStatus)
|
_dubStatus.postValue(dubStatus)
|
||||||
|
|
||||||
_dubSubSelections.postValue(d.episodes.keys)
|
_dubSubSelections.postValue(d.episodes.keys)
|
||||||
val fillerEpisodes = if (showFillers) safeApiCall { getFillerEpisodes(d.name) } else null
|
val fillerEpisodes =
|
||||||
|
if (showFillers) safeApiCall { getFillerEpisodes(d.name) } else null
|
||||||
|
|
||||||
var idIndex = 0
|
var idIndex = 0
|
||||||
val res = d.episodes.map { ep ->
|
val res = d.episodes.map { ep ->
|
||||||
|
@ -464,6 +475,20 @@ class ResultViewModel : ViewModel() {
|
||||||
return loadEpisode(episode.id, episode.data, isCasting)
|
return loadEpisode(episode.id, episode.data, isCasting)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun loadSubtitleFile(uri: Uri, name: String, id: Int?) {
|
||||||
|
if (id == null) return
|
||||||
|
val hashMap: HashMap<String, SubtitleFile> = _allEpisodesSubs.value?.get(id) ?: hashMapOf()
|
||||||
|
hashMap[name] = SubtitleFile(
|
||||||
|
name,
|
||||||
|
uri.toString()
|
||||||
|
)
|
||||||
|
_allEpisodesSubs.value.apply {
|
||||||
|
this?.set(id, hashMap)
|
||||||
|
}?.let {
|
||||||
|
_allEpisodesSubs.postValue(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun loadEpisode(
|
private suspend fun loadEpisode(
|
||||||
id: Int,
|
id: Int,
|
||||||
data: String,
|
data: String,
|
||||||
|
|
|
@ -1,137 +1,150 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
android:layout_width="match_parent"
|
||||||
android:orientation="vertical"
|
android:layout_height="match_parent"
|
||||||
android:layout_width="match_parent"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:background="@null"
|
android:background="@null"
|
||||||
android:layout_height="match_parent">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:orientation="horizontal"
|
android:layout_width="match_parent"
|
||||||
android:layout_marginBottom="60dp"
|
android:layout_height="match_parent"
|
||||||
android:layout_width="match_parent"
|
android:layout_marginBottom="60dp"
|
||||||
android:layout_height="match_parent"
|
android:baselineAligned="false"
|
||||||
android:baselineAligned="false">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/sort_sources_holder"
|
android:id="@+id/sort_sources_holder"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:layout_weight="50"
|
||||||
android:layout_weight="50">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_marginTop="10dp"
|
android:layout_width="match_parent"
|
||||||
android:paddingTop="10dp"
|
android:layout_height="wrap_content"
|
||||||
android:paddingBottom="10dp"
|
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/pick_source"
|
||||||
|
android:textColor="?attr/textColor"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:id="@+id/sort_providers"
|
||||||
|
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/sort_subtitles_holder"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="50"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/subs_settings"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:paddingBottom="10dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_rowWeight="1"
|
||||||
|
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:nextFocusRight="@id/sort_providers"
|
||||||
|
|
||||||
|
android:nextFocusDown="@id/sort_subtitles"
|
||||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||||
android:textStyle="bold"
|
android:text="@string/pick_subtitle"
|
||||||
android:text="@string/pick_source"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:textColor="?attr/textColor"
|
android:textColor="?attr/textColor"
|
||||||
android:layout_width="match_parent"
|
android:textSize="20sp"
|
||||||
android:layout_rowWeight="1"
|
android:textStyle="bold" />
|
||||||
android:layout_height="wrap_content">
|
|
||||||
</TextView>
|
|
||||||
<ListView
|
|
||||||
android:nextFocusRight="@id/apply_btt"
|
|
||||||
android:nextFocusLeft="@id/sort_subtitles"
|
|
||||||
|
|
||||||
android:id="@+id/sort_providers"
|
|
||||||
android:background="?attr/primaryBlackBackground"
|
|
||||||
android:requiresFadingEdge="vertical"
|
|
||||||
tools:listitem="@layout/sort_bottom_single_choice"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_rowWeight="1"
|
|
||||||
/>
|
|
||||||
</LinearLayout>
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/sort_subtitles_holder"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_weight="50">
|
|
||||||
<FrameLayout
|
|
||||||
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
|
||||||
android:id="@+id/subs_settings"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:paddingTop="10dp"
|
|
||||||
android:paddingBottom="10dp"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
<TextView
|
|
||||||
android:nextFocusRight="@id/sort_providers"
|
|
||||||
android:nextFocusDown="@id/sort_subtitles"
|
|
||||||
|
|
||||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
|
||||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
|
||||||
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:text="@string/pick_subtitle"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:textColor="?attr/textColor"
|
|
||||||
android:layout_rowWeight="1"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
</TextView>
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:visibility="gone"
|
android:layout_width="25dp"
|
||||||
android:layout_marginTop="0dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="10dp"
|
android:layout_gravity="end|center_vertical"
|
||||||
android:layout_gravity="end|center_vertical"
|
android:layout_marginTop="0dp"
|
||||||
|
android:layout_marginEnd="10dp"
|
||||||
|
|
||||||
android:src="@drawable/ic_outline_settings_24"
|
android:contentDescription="@string/home_change_provider_img_des"
|
||||||
android:layout_width="25dp"
|
android:src="@drawable/ic_outline_settings_24"
|
||||||
android:layout_height="wrap_content"
|
android:visibility="gone" />
|
||||||
android:contentDescription="@string/home_change_provider_img_des">
|
</LinearLayout>
|
||||||
</ImageView>
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
<ListView
|
<ListView
|
||||||
android:nextFocusRight="@id/cancel_btt"
|
android:id="@+id/sort_subtitles"
|
||||||
android:nextFocusLeft="@id/sort_providers"
|
android:layout_width="match_parent"
|
||||||
|
|
||||||
android:id="@+id/sort_subtitles"
|
android:layout_height="match_parent"
|
||||||
android:background="?attr/primaryBlackBackground"
|
android:layout_rowWeight="1"
|
||||||
android:requiresFadingEdge="vertical"
|
android:background="?attr/primaryBlackBackground"
|
||||||
tools:listitem="@layout/sort_bottom_single_choice"
|
android:nextFocusLeft="@id/sort_providers"
|
||||||
android:layout_width="match_parent"
|
android:nextFocusRight="@id/cancel_btt"
|
||||||
android:layout_rowWeight="1"
|
tools:listitem="@layout/sort_bottom_single_choice" />
|
||||||
android:layout_height="match_parent">
|
|
||||||
</ListView>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:orientation="horizontal"
|
android:layout_width="match_parent"
|
||||||
android:layout_gravity="bottom"
|
android:layout_height="60dp"
|
||||||
android:gravity="bottom|end"
|
android:layout_gravity="bottom"
|
||||||
android:layout_marginTop="-60dp"
|
android:layout_marginTop="-60dp"
|
||||||
android:layout_width="match_parent"
|
android:orientation="horizontal">
|
||||||
android:layout_height="60dp">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<!-- Kinda hack because the gravity won't behave correctly-->
|
||||||
android:nextFocusRight="@id/cancel_btt"
|
<LinearLayout
|
||||||
android:nextFocusLeft="@id/cancel_btt"
|
android:layout_weight="1"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical|start">
|
||||||
|
|
||||||
android:id="@+id/apply_btt"
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/load_btt"
|
||||||
|
app:icon="@drawable/ic_baseline_subtitles_24"
|
||||||
style="@style/WhiteButton"
|
style="@style/WhiteButton"
|
||||||
android:layout_gravity="center_vertical|end"
|
|
||||||
android:text="@string/sort_apply"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
/>
|
android:nextFocusLeft="@id/cancel_btt"
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:nextFocusRight="@id/apply_btt"
|
android:nextFocusRight="@id/apply_btt"
|
||||||
android:nextFocusLeft="@id/apply_btt"
|
android:text="@string/player_load_subtitles" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
android:id="@+id/cancel_btt"
|
<com.google.android.material.button.MaterialButton
|
||||||
style="@style/BlackButton"
|
android:id="@+id/apply_btt"
|
||||||
android:layout_gravity="center_vertical|end"
|
style="@style/WhiteButton"
|
||||||
android:text="@string/sort_cancel"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="wrap_content"
|
android:layout_gravity="center_vertical|end"
|
||||||
/>
|
android:nextFocusLeft="@id/cancel_btt"
|
||||||
|
android:nextFocusRight="@id/cancel_btt"
|
||||||
|
android:text="@string/sort_apply" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/cancel_btt"
|
||||||
|
style="@style/BlackButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical|end"
|
||||||
|
android:nextFocusLeft="@id/apply_btt"
|
||||||
|
android:nextFocusRight="@id/apply_btt"
|
||||||
|
android:text="@string/sort_cancel" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
@ -368,4 +368,6 @@
|
||||||
<string name="subtitles_example_text">The quick brown fox jumps over the lazy dog</string>
|
<string name="subtitles_example_text">The quick brown fox jumps over the lazy dog</string>
|
||||||
|
|
||||||
<string name="tab_recommended">Recommended</string>
|
<string name="tab_recommended">Recommended</string>
|
||||||
|
<string name="player_loaded_subtitles">Loaded %s</string>
|
||||||
|
<string name="player_load_subtitles">Load from file</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue