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.widget.*
|
||||
import android.widget.Toast.LENGTH_SHORT
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.graphics.blue
|
||||
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.C.TIME_UNSET
|
||||
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.ui.AspectRatioFrameLayout
|
||||
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.CastState
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.hippo.unifile.UniFile
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.MainActivity.Companion.canEnterPipMode
|
||||
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.player_custom_layout.*
|
||||
import kotlinx.coroutines.*
|
||||
import okhttp3.internal.format
|
||||
import java.io.File
|
||||
import javax.net.ssl.HttpsURLConnection
|
||||
import javax.net.ssl.SSLContext
|
||||
|
@ -766,6 +769,32 @@ class PlayerFragment : Fragment() {
|
|||
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) :
|
||||
ContentObserver(handler) {
|
||||
private val audioManager = activity.getSystemService(AUDIO_SERVICE) as? AudioManager
|
||||
|
@ -1231,12 +1260,30 @@ class PlayerFragment : Fragment() {
|
|||
sourceDialog.findViewById<MaterialButton>(R.id.cancel_btt)!!
|
||||
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 {
|
||||
autoHide()
|
||||
saveArguments()
|
||||
SubtitlesFragment.push(activity)
|
||||
sourceDialog.dismissSafe(activity)
|
||||
}
|
||||
|
||||
var sourceIndex = 0
|
||||
var startSource = 0
|
||||
var sources: List<ExtractorLink> = emptyList()
|
||||
|
@ -2116,40 +2163,7 @@ class PlayerFragment : Fragment() {
|
|||
mediaItemBuilder.setUri(uriPrimary)
|
||||
}
|
||||
|
||||
val subs = context?.getSubs() ?: emptyList()
|
||||
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 {
|
||||
fun getDataSourceFactory(isOnline: Boolean): DataSource.Factory {
|
||||
return if (isOnline) {
|
||||
DefaultHttpDataSource.Factory().apply {
|
||||
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 {
|
||||
val databaseProvider = StandaloneDatabaseProvider(requireContext())
|
||||
simpleCache = SimpleCache(
|
||||
|
@ -2187,18 +2236,23 @@ class PlayerFragment : Fragment() {
|
|||
|
||||
val cacheFactory = CacheDataSource.Factory().apply {
|
||||
simpleCache?.let { setCache(it) }
|
||||
setUpstreamDataSourceFactory(getDataSourceFactory())
|
||||
setUpstreamDataSourceFactory(getDataSourceFactory(isOnline))
|
||||
}
|
||||
|
||||
val exoPlayerBuilder =
|
||||
ExoPlayer.Builder(requireContext())
|
||||
.setTrackSelector(trackSelector)
|
||||
|
||||
val videoMediaSource =
|
||||
DefaultMediaSourceFactory(cacheFactory).createMediaSource(mediaItem)
|
||||
|
||||
exoPlayer = exoPlayerBuilder.build().apply {
|
||||
playWhenReady = isPlayerPlaying
|
||||
seekTo(currentWindow, playbackPosition)
|
||||
setMediaSource(
|
||||
DefaultMediaSourceFactory(cacheFactory).createMediaSource(mediaItem),
|
||||
MergingMediaSource(
|
||||
videoMediaSource, *subSources.toTypedArray()
|
||||
),
|
||||
playbackPosition
|
||||
)
|
||||
prepare()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.lagradost.cloudstream3.ui.result
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
@ -57,7 +58,8 @@ class ResultViewModel : ViewModel() {
|
|||
|
||||
private val _resultResponse: MutableLiveData<Resource<Any?>> = 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 _publicEpisodesCount: MutableLiveData<Int> = MutableLiveData() // before the sorting
|
||||
|
@ -83,7 +85,8 @@ class ResultViewModel : ViewModel() {
|
|||
private val _dubSubSelections: MutableLiveData<Set<DubStatus>> = MutableLiveData()
|
||||
|
||||
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()
|
||||
val watchStatus: LiveData<WatchType> get() = _watchStatus
|
||||
|
@ -291,7 +294,14 @@ class ResultViewModel : ViewModel() {
|
|||
_apiName.postValue(apiName)
|
||||
val api = getApiFromNameNull(apiName)
|
||||
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
|
||||
}
|
||||
repo = APIRepository(api)
|
||||
|
@ -335,7 +345,8 @@ class ResultViewModel : ViewModel() {
|
|||
_dubStatus.postValue(dubStatus)
|
||||
|
||||
_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
|
||||
val res = d.episodes.map { ep ->
|
||||
|
@ -464,6 +475,20 @@ class ResultViewModel : ViewModel() {
|
|||
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(
|
||||
id: Int,
|
||||
data: String,
|
||||
|
|
|
@ -1,137 +1,150 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:background="@null"
|
||||
android:layout_height="match_parent">
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="60dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:baselineAligned="false">
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
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/sort_sources_holder"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_weight="50">
|
||||
<TextView
|
||||
android:layout_marginTop="10dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/pick_source"
|
||||
android:textSize="20sp"
|
||||
android:textColor="?attr/textColor"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_height="wrap_content">
|
||||
</TextView>
|
||||
<ListView
|
||||
android:nextFocusRight="@id/apply_btt"
|
||||
android:nextFocusLeft="@id/sort_subtitles"
|
||||
android:layout_weight="50"
|
||||
android:orientation="vertical">
|
||||
|
||||
android:id="@+id/sort_providers"
|
||||
android:background="?attr/primaryBlackBackground"
|
||||
android:requiresFadingEdge="vertical"
|
||||
tools:listitem="@layout/sort_bottom_single_choice"
|
||||
<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/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: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: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: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>
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<ImageView
|
||||
android:visibility="gone"
|
||||
android:layout_marginTop="0dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
|
||||
android:src="@drawable/ic_outline_settings_24"
|
||||
android:layout_width="25dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/home_change_provider_img_des">
|
||||
</ImageView>
|
||||
</FrameLayout>
|
||||
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:nextFocusRight="@id/cancel_btt"
|
||||
android:nextFocusLeft="@id/sort_providers"
|
||||
|
||||
android:id="@+id/sort_subtitles"
|
||||
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"
|
||||
android:layout_height="match_parent">
|
||||
</ListView>
|
||||
android:background="?attr/primaryBlackBackground"
|
||||
android:nextFocusLeft="@id/sort_providers"
|
||||
android:nextFocusRight="@id/cancel_btt"
|
||||
tools:listitem="@layout/sort_bottom_single_choice" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_gravity="bottom"
|
||||
android:gravity="bottom|end"
|
||||
android:layout_marginTop="-60dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp">
|
||||
android:layout_height="60dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginTop="-60dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<!-- Kinda hack because the gravity won't behave correctly-->
|
||||
<LinearLayout
|
||||
android:layout_weight="1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical|start">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:nextFocusRight="@id/cancel_btt"
|
||||
android:id="@+id/load_btt"
|
||||
app:icon="@drawable/ic_baseline_subtitles_24"
|
||||
style="@style/WhiteButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:nextFocusLeft="@id/cancel_btt"
|
||||
android:nextFocusRight="@id/apply_btt"
|
||||
android:text="@string/player_load_subtitles" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/apply_btt"
|
||||
style="@style/WhiteButton"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:text="@string/sort_apply"
|
||||
android:layout_width="wrap_content"
|
||||
/>
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:nextFocusRight="@id/apply_btt"
|
||||
android:nextFocusLeft="@id/apply_btt"
|
||||
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_gravity="center_vertical|end"
|
||||
android:text="@string/sort_cancel"
|
||||
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>
|
||||
|
|
|
@ -368,4 +368,6 @@
|
|||
<string name="subtitles_example_text">The quick brown fox jumps over the lazy dog</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>
|
||||
|
|
Loading…
Reference in a new issue