mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
Merge branch 'master' of https://github.com/recloudstream/cloudstream
This commit is contained in:
commit
4546370dc4
15 changed files with 367 additions and 267 deletions
|
@ -11,6 +11,9 @@ import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.result.ActivityResult
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.annotation.IdRes
|
import androidx.annotation.IdRes
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
@ -33,6 +36,7 @@ import com.lagradost.cloudstream3.APIHolder.apis
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
|
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
|
||||||
import com.lagradost.cloudstream3.APIHolder.initAll
|
import com.lagradost.cloudstream3.APIHolder.initAll
|
||||||
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
|
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
|
||||||
|
import com.lagradost.cloudstream3.CommonActivity.currentToast
|
||||||
import com.lagradost.cloudstream3.CommonActivity.loadThemes
|
import com.lagradost.cloudstream3.CommonActivity.loadThemes
|
||||||
import com.lagradost.cloudstream3.CommonActivity.onColorSelectedEvent
|
import com.lagradost.cloudstream3.CommonActivity.onColorSelectedEvent
|
||||||
import com.lagradost.cloudstream3.CommonActivity.onDialogDismissedEvent
|
import com.lagradost.cloudstream3.CommonActivity.onDialogDismissedEvent
|
||||||
|
@ -85,7 +89,11 @@ import com.lagradost.cloudstream3.plugins.PluginManager
|
||||||
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringRepo
|
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringRepo
|
||||||
import com.lagradost.cloudstream3.ui.setup.SetupFragmentExtensions
|
import com.lagradost.cloudstream3.ui.setup.SetupFragmentExtensions
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.loadRepository
|
import com.lagradost.cloudstream3.utils.AppUtils.loadRepository
|
||||||
|
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||||
import com.lagradost.cloudstream3.utils.Event
|
import com.lagradost.cloudstream3.utils.Event
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
import java.net.URI
|
||||||
|
|
||||||
|
|
||||||
const val VLC_PACKAGE = "org.videolan.vlc"
|
const val VLC_PACKAGE = "org.videolan.vlc"
|
||||||
|
@ -293,7 +301,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
if (VLC_REQUEST_CODE == requestCode) {
|
if (requestCode == VLC_REQUEST_CODE) {
|
||||||
if (resultCode == RESULT_OK && data != null) {
|
if (resultCode == RESULT_OK && data != null) {
|
||||||
val pos: Long =
|
val pos: Long =
|
||||||
data.getLongExtra(
|
data.getLongExtra(
|
||||||
|
@ -368,7 +376,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (str.contains(appStringRepo)) {
|
} else if (URI(str).scheme == appStringRepo) {
|
||||||
val url = str.replaceFirst(appStringRepo, "https")
|
val url = str.replaceFirst(appStringRepo, "https")
|
||||||
loadRepository(url)
|
loadRepository(url)
|
||||||
} else {
|
} else {
|
||||||
|
@ -444,6 +452,28 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
PluginManager.loadAllLocalPlugins(this@MainActivity)
|
PluginManager.loadAllLocalPlugins(this@MainActivity)
|
||||||
|
|
||||||
|
// Load cloned sites after plugins have been loaded since clones depend on plugins.
|
||||||
|
try {
|
||||||
|
getKey<Array<SettingsGeneral.CustomSite>>(USER_PROVIDER_API)?.let { list ->
|
||||||
|
list.forEach { custom ->
|
||||||
|
allProviders.firstOrNull { it.javaClass.simpleName == custom.parentJavaClass }
|
||||||
|
?.let {
|
||||||
|
allProviders.add(it.javaClass.newInstance().apply {
|
||||||
|
name = custom.name
|
||||||
|
lang = custom.lang
|
||||||
|
mainUrl = custom.url.trimEnd('/')
|
||||||
|
canBeOverridden = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apis = allProviders.distinctBy { it }
|
||||||
|
APIHolder.apiMap = null
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logError(e)
|
||||||
|
}
|
||||||
|
|
||||||
afterPluginsLoadedEvent.invoke(true)
|
afterPluginsLoadedEvent.invoke(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -480,26 +510,6 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
initAll()
|
initAll()
|
||||||
// No duplicates (which can happen by registerMainAPI)
|
// No duplicates (which can happen by registerMainAPI)
|
||||||
apis = allProviders.distinctBy { it }
|
apis = allProviders.distinctBy { it }
|
||||||
|
|
||||||
try {
|
|
||||||
getKey<Array<SettingsGeneral.CustomSite>>(USER_PROVIDER_API)?.let { list ->
|
|
||||||
list.forEach { custom ->
|
|
||||||
allProviders.firstOrNull { it.javaClass.simpleName == custom.parentJavaClass }
|
|
||||||
?.let {
|
|
||||||
allProviders.add(it.javaClass.newInstance().apply {
|
|
||||||
name = custom.name
|
|
||||||
lang = custom.lang
|
|
||||||
mainUrl = custom.url.trimEnd('/')
|
|
||||||
canBeOverridden = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
apis = allProviders.distinctBy { it }
|
|
||||||
APIHolder.apiMap = null
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logError(e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// val navView: BottomNavigationView = findViewById(R.id.nav_view)
|
// val navView: BottomNavigationView = findViewById(R.id.nav_view)
|
||||||
|
@ -677,13 +687,25 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
&& PluginManager.getPluginsLocal().isEmpty()
|
&& PluginManager.getPluginsLocal().isEmpty()
|
||||||
// && PREBUILT_REPOSITORIES.isNotEmpty()
|
// && PREBUILT_REPOSITORIES.isNotEmpty()
|
||||||
) {
|
) {
|
||||||
navController.navigate(R.id.navigation_setup_extensions, SetupFragmentExtensions.newInstance(false))
|
navController.navigate(
|
||||||
|
R.id.navigation_setup_extensions,
|
||||||
|
SetupFragmentExtensions.newInstance(false)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logError(e)
|
logError(e)
|
||||||
} finally {
|
} finally {
|
||||||
setKey(HAS_DONE_SETUP_KEY, true)
|
setKey(HAS_DONE_SETUP_KEY, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used to check current focus for TV
|
||||||
|
// main {
|
||||||
|
// while (true) {
|
||||||
|
// delay(1000)
|
||||||
|
// println("Current focus: $currentFocus")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
val relativePath = (Environment.DIRECTORY_DOWNLOADS) + File.separatorChar
|
val relativePath = (Environment.DIRECTORY_DOWNLOADS) + File.separatorChar
|
||||||
|
|
|
@ -11,8 +11,10 @@ import android.webkit.WebViewClient
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.USER_AGENT
|
import com.lagradost.cloudstream3.USER_AGENT
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringRepo
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.loadRepository
|
import com.lagradost.cloudstream3.utils.AppUtils.loadRepository
|
||||||
import kotlinx.android.synthetic.main.fragment_webview.*
|
import kotlinx.android.synthetic.main.fragment_webview.*
|
||||||
|
import java.net.URI
|
||||||
|
|
||||||
class WebviewFragment : Fragment() {
|
class WebviewFragment : Fragment() {
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
@ -27,10 +29,17 @@ class WebviewFragment : Fragment() {
|
||||||
request: WebResourceRequest?
|
request: WebResourceRequest?
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val requestUrl = request?.url.toString()
|
val requestUrl = request?.url.toString()
|
||||||
if (requestUrl.startsWith("https://cs.repo")) {
|
|
||||||
val realUrl = "https://" + requestUrl.substringAfter("?")
|
val repoUrl = if (requestUrl.startsWith("https://cs.repo")) {
|
||||||
println("Repository url: $realUrl :::: $requestUrl")
|
"https://" + requestUrl.substringAfter("?")
|
||||||
activity?.loadRepository(realUrl)
|
} else if (URI(requestUrl).scheme == appStringRepo) {
|
||||||
|
"https://" + requestUrl.replaceFirst(appStringRepo, "https")
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repoUrl != null) {
|
||||||
|
activity?.loadRepository(repoUrl)
|
||||||
findNavController().popBackStack()
|
findNavController().popBackStack()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,8 +60,7 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper.deleteAllResumeStateIds
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.removeLastWatched
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.removeLastWatched
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultWatchState
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultWatchState
|
||||||
import com.lagradost.cloudstream3.utils.Event
|
import com.lagradost.cloudstream3.utils.Event
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
|
||||||
import com.lagradost.cloudstream3.utils.HOMEPAGE_API
|
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showOptionSelectStringRes
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showOptionSelectStringRes
|
||||||
import com.lagradost.cloudstream3.utils.SubtitleHelper.getFlagFromIso
|
import com.lagradost.cloudstream3.utils.SubtitleHelper.getFlagFromIso
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
||||||
|
@ -455,8 +454,9 @@ class HomeFragment : Fragment() {
|
||||||
homeViewModel.loadStoredData(list)
|
homeViewModel.loadStoredData(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadHomePage(successful: Boolean = true) {
|
private fun loadHomePage(successful: Boolean = false) {
|
||||||
val apiName = context?.getKey<String>(HOMEPAGE_API)
|
val apiName = context?.getKey<String>(USER_SELECTED_HOMEPAGE_API)
|
||||||
|
|
||||||
if (homeViewModel.apiName.value != apiName || apiName == null) {
|
if (homeViewModel.apiName.value != apiName || apiName == null) {
|
||||||
//println("Caught home: " + homeViewModel.apiName.value + " at " + apiName)
|
//println("Caught home: " + homeViewModel.apiName.value + " at " + apiName)
|
||||||
homeViewModel.loadAndCancel(apiName)
|
homeViewModel.loadAndCancel(apiName)
|
||||||
|
@ -512,7 +512,7 @@ class HomeFragment : Fragment() {
|
||||||
|
|
||||||
observe(homeViewModel.apiName) { apiName ->
|
observe(homeViewModel.apiName) { apiName ->
|
||||||
currentApiName = apiName
|
currentApiName = apiName
|
||||||
setKey(HOMEPAGE_API, apiName)
|
// setKey(USER_SELECTED_HOMEPAGE_API, apiName)
|
||||||
home_api_fab?.text = apiName
|
home_api_fab?.text = apiName
|
||||||
home_provider_name?.text = apiName
|
home_provider_name?.text = apiName
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -31,7 +31,7 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper.getBookmarkedData
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getLastWatched
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getLastWatched
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
|
||||||
import com.lagradost.cloudstream3.utils.HOMEPAGE_API
|
import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
|
||||||
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
|
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
|
@ -227,7 +227,8 @@ class HomeViewModel : ViewModel() {
|
||||||
expandable.clear()
|
expandable.clear()
|
||||||
data.value.forEach { home ->
|
data.value.forEach { home ->
|
||||||
home?.items?.forEach { list ->
|
home?.items?.forEach { list ->
|
||||||
val filteredList = context?.filterHomePageListByFilmQuality(list) ?: list
|
val filteredList =
|
||||||
|
context?.filterHomePageListByFilmQuality(list) ?: list
|
||||||
expandable[list.name] =
|
expandable[list.name] =
|
||||||
ExpandableHomepageList(filteredList, 1, home.hasNext)
|
ExpandableHomepageList(filteredList, 1, home.hasNext)
|
||||||
}
|
}
|
||||||
|
@ -244,7 +245,9 @@ class HomeViewModel : ViewModel() {
|
||||||
.toList()
|
.toList()
|
||||||
|
|
||||||
if (currentList.isNotEmpty()) {
|
if (currentList.isNotEmpty()) {
|
||||||
val randomItems = context?.filterSearchResultByFilmQuality(currentList.shuffled()) ?: currentList.shuffled()
|
val randomItems =
|
||||||
|
context?.filterSearchResultByFilmQuality(currentList.shuffled())
|
||||||
|
?: currentList.shuffled()
|
||||||
|
|
||||||
_randomItems.postValue(randomItems)
|
_randomItems.postValue(randomItems)
|
||||||
}
|
}
|
||||||
|
@ -266,18 +269,22 @@ class HomeViewModel : ViewModel() {
|
||||||
|
|
||||||
fun loadAndCancel(preferredApiName: String?) = viewModelScope.launch {
|
fun loadAndCancel(preferredApiName: String?) = viewModelScope.launch {
|
||||||
val api = getApiFromNameNull(preferredApiName)
|
val api = getApiFromNameNull(preferredApiName)
|
||||||
if (preferredApiName == noneApi.name)
|
if (preferredApiName == noneApi.name){
|
||||||
|
setKey(USER_SELECTED_HOMEPAGE_API, noneApi.name)
|
||||||
loadAndCancel(noneApi)
|
loadAndCancel(noneApi)
|
||||||
|
}
|
||||||
else if (preferredApiName == randomApi.name || api == null) {
|
else if (preferredApiName == randomApi.name || api == null) {
|
||||||
val validAPIs = context?.filterProviderByPreferredMedia()
|
val validAPIs = context?.filterProviderByPreferredMedia()
|
||||||
if (validAPIs.isNullOrEmpty()) {
|
if (validAPIs.isNullOrEmpty()) {
|
||||||
|
// Do not set USER_SELECTED_HOMEPAGE_API when there is no plugins loaded
|
||||||
loadAndCancel(noneApi)
|
loadAndCancel(noneApi)
|
||||||
} else {
|
} else {
|
||||||
val apiRandom = validAPIs.random()
|
val apiRandom = validAPIs.random()
|
||||||
loadAndCancel(apiRandom)
|
loadAndCancel(apiRandom)
|
||||||
setKey(HOMEPAGE_API, apiRandom.name)
|
setKey(USER_SELECTED_HOMEPAGE_API, apiRandom.name)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
setKey(USER_SELECTED_HOMEPAGE_API, api.name)
|
||||||
loadAndCancel(api)
|
loadAndCancel(api)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ class CustomDecoder : SubtitleDecoder {
|
||||||
private var overrideEncoding: String? = null
|
private var overrideEncoding: String? = null
|
||||||
var regexSubtitlesToRemoveCaptions = false
|
var regexSubtitlesToRemoveCaptions = false
|
||||||
var regexSubtitlesToRemoveBloat = false
|
var regexSubtitlesToRemoveBloat = false
|
||||||
|
var uppercaseSubtitles = false
|
||||||
val bloatRegex =
|
val bloatRegex =
|
||||||
listOf(
|
listOf(
|
||||||
Regex(
|
Regex(
|
||||||
|
@ -193,6 +194,9 @@ class CustomDecoder : SubtitleDecoder {
|
||||||
bloatRegex.forEach { rgx ->
|
bloatRegex.forEach { rgx ->
|
||||||
str = str.replace(rgx, "\n")
|
str = str.replace(rgx, "\n")
|
||||||
}
|
}
|
||||||
|
if (uppercaseSubtitles) {
|
||||||
|
str = str.uppercase()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
inputBuffer.setSubtitleText(str)
|
inputBuffer.setSubtitleText(str)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import com.google.android.exoplayer2.util.MimeTypes
|
||||||
import com.lagradost.cloudstream3.SubtitleFile
|
import com.lagradost.cloudstream3.SubtitleFile
|
||||||
import com.lagradost.cloudstream3.ui.player.CustomDecoder.Companion.regexSubtitlesToRemoveBloat
|
import com.lagradost.cloudstream3.ui.player.CustomDecoder.Companion.regexSubtitlesToRemoveBloat
|
||||||
import com.lagradost.cloudstream3.ui.player.CustomDecoder.Companion.regexSubtitlesToRemoveCaptions
|
import com.lagradost.cloudstream3.ui.player.CustomDecoder.Companion.regexSubtitlesToRemoveCaptions
|
||||||
|
import com.lagradost.cloudstream3.ui.player.CustomDecoder.Companion.uppercaseSubtitles
|
||||||
import com.lagradost.cloudstream3.ui.subtitles.SaveCaptionStyle
|
import com.lagradost.cloudstream3.ui.subtitles.SaveCaptionStyle
|
||||||
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment.Companion.fromSaveToStyle
|
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment.Companion.fromSaveToStyle
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.toPx
|
import com.lagradost.cloudstream3.utils.UIHelper.toPx
|
||||||
|
@ -87,6 +88,7 @@ class PlayerSubtitleHelper {
|
||||||
|
|
||||||
fun setSubStyle(style: SaveCaptionStyle) {
|
fun setSubStyle(style: SaveCaptionStyle) {
|
||||||
regexSubtitlesToRemoveBloat = style.removeBloat
|
regexSubtitlesToRemoveBloat = style.removeBloat
|
||||||
|
uppercaseSubtitles = style.upperCase
|
||||||
regexSubtitlesToRemoveCaptions = style.removeCaptions
|
regexSubtitlesToRemoveCaptions = style.removeCaptions
|
||||||
subtitleView?.context?.let { ctx ->
|
subtitleView?.context?.let { ctx ->
|
||||||
subStyle = style
|
subStyle = style
|
||||||
|
|
|
@ -16,7 +16,7 @@ import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.ui.APIRepository
|
import com.lagradost.cloudstream3.ui.APIRepository
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
||||||
import com.lagradost.cloudstream3.utils.HOMEPAGE_API
|
import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
|
||||||
|
@ -116,7 +116,7 @@ class SettingsLang : PreferenceFragmentCompat() {
|
||||||
.putInt(getString(R.string.prefer_media_type_key), prefValues[it])
|
.putInt(getString(R.string.prefer_media_type_key), prefValues[it])
|
||||||
.apply()
|
.apply()
|
||||||
|
|
||||||
removeKey(HOMEPAGE_API)
|
removeKey(USER_SELECTED_HOMEPAGE_API)
|
||||||
// (context ?: AcraApplication.context)?.let { ctx -> app.initClient(ctx) }
|
// (context ?: AcraApplication.context)?.let { ctx -> app.initClient(ctx) }
|
||||||
}
|
}
|
||||||
return@setOnPreferenceClickListener true
|
return@setOnPreferenceClickListener true
|
||||||
|
|
|
@ -11,7 +11,7 @@ import androidx.navigation.fragment.findNavController
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.utils.DataStore.removeKey
|
import com.lagradost.cloudstream3.utils.DataStore.removeKey
|
||||||
import com.lagradost.cloudstream3.utils.HOMEPAGE_API
|
import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
||||||
import kotlinx.android.synthetic.main.fragment_setup_media.*
|
import kotlinx.android.synthetic.main.fragment_setup_media.*
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ class SetupFragmentMedia : Fragment() {
|
||||||
.apply()
|
.apply()
|
||||||
|
|
||||||
// Regenerate set homepage
|
// Regenerate set homepage
|
||||||
removeKey(HOMEPAGE_API)
|
removeKey(USER_SELECTED_HOMEPAGE_API)
|
||||||
}
|
}
|
||||||
|
|
||||||
next_btt?.setOnClickListener {
|
next_btt?.setOnClickListener {
|
||||||
|
|
|
@ -38,6 +38,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.hideSystemUI
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.navigate
|
import com.lagradost.cloudstream3.utils.UIHelper.navigate
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
|
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
|
||||||
import kotlinx.android.synthetic.main.subtitle_settings.*
|
import kotlinx.android.synthetic.main.subtitle_settings.*
|
||||||
|
import kotlinx.android.synthetic.main.toast.view.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
const val SUBTITLE_KEY = "subtitle_settings"
|
const val SUBTITLE_KEY = "subtitle_settings"
|
||||||
|
@ -60,6 +61,8 @@ data class SaveCaptionStyle(
|
||||||
@JsonProperty("fixedTextSize") var fixedTextSize: Float?,
|
@JsonProperty("fixedTextSize") var fixedTextSize: Float?,
|
||||||
@JsonProperty("removeCaptions") var removeCaptions: Boolean = false,
|
@JsonProperty("removeCaptions") var removeCaptions: Boolean = false,
|
||||||
@JsonProperty("removeBloat") var removeBloat: Boolean = true,
|
@JsonProperty("removeBloat") var removeBloat: Boolean = true,
|
||||||
|
/** Apply caps lock to the text **/
|
||||||
|
@JsonProperty("upperCase") var upperCase: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
const val DEF_SUBS_ELEVATION = 20
|
const val DEF_SUBS_ELEVATION = 20
|
||||||
|
@ -182,6 +185,19 @@ class SubtitlesFragment : Fragment() {
|
||||||
|
|
||||||
private fun Context.updateState() {
|
private fun Context.updateState() {
|
||||||
subtitle_text?.setStyle(fromSaveToStyle(state))
|
subtitle_text?.setStyle(fromSaveToStyle(state))
|
||||||
|
val text = subtitle_text.context.getString(R.string.subtitles_example_text)
|
||||||
|
val fixedText = if (state.upperCase) text.uppercase() else text
|
||||||
|
subtitle_text?.setCues(
|
||||||
|
listOf(
|
||||||
|
Cue.Builder()
|
||||||
|
.setTextSize(
|
||||||
|
getPixels(TypedValue.COMPLEX_UNIT_SP, 25.0f).toFloat(),
|
||||||
|
Cue.TEXT_SIZE_TYPE_ABSOLUTE
|
||||||
|
)
|
||||||
|
.setText(fixedText)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getColor(id: Int): Int {
|
private fun getColor(id: Int): Int {
|
||||||
|
@ -222,7 +238,6 @@ class SubtitlesFragment : Fragment() {
|
||||||
context?.getExternalFilesDir(null)?.absolutePath.toString() + "/Fonts"
|
context?.getExternalFilesDir(null)?.absolutePath.toString() + "/Fonts"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
context?.fixPaddingStatusbar(subs_root)
|
context?.fixPaddingStatusbar(subs_root)
|
||||||
|
|
||||||
state = getCurrentSavedStyle()
|
state = getCurrentSavedStyle()
|
||||||
|
@ -404,6 +419,12 @@ class SubtitlesFragment : Fragment() {
|
||||||
subtitles_remove_bloat?.setOnCheckedChangeListener { _, b ->
|
subtitles_remove_bloat?.setOnCheckedChangeListener { _, b ->
|
||||||
state.removeBloat = b
|
state.removeBloat = b
|
||||||
}
|
}
|
||||||
|
subtitles_uppercase?.isChecked = state.upperCase
|
||||||
|
subtitles_uppercase?.setOnCheckedChangeListener { _, b ->
|
||||||
|
state.upperCase = b
|
||||||
|
context?.updateState()
|
||||||
|
}
|
||||||
|
|
||||||
subtitles_remove_captions?.isChecked = state.removeCaptions
|
subtitles_remove_captions?.isChecked = state.removeCaptions
|
||||||
subtitles_remove_captions?.setOnCheckedChangeListener { _, b ->
|
subtitles_remove_captions?.setOnCheckedChangeListener { _, b ->
|
||||||
state.removeCaptions = b
|
state.removeCaptions = b
|
||||||
|
@ -418,9 +439,11 @@ class SubtitlesFragment : Fragment() {
|
||||||
|
|
||||||
//Fetch current value from preference
|
//Fetch current value from preference
|
||||||
context?.let { ctx ->
|
context?.let { ctx ->
|
||||||
subtitles_filter_sub_lang?.isChecked = PreferenceManager.getDefaultSharedPreferences(ctx)
|
subtitles_filter_sub_lang?.isChecked =
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(ctx)
|
||||||
.getBoolean(getString(R.string.filter_sub_lang_key), false)
|
.getBoolean(getString(R.string.filter_sub_lang_key), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
subtitles_filter_sub_lang?.setOnCheckedChangeListener { _, b ->
|
subtitles_filter_sub_lang?.setOnCheckedChangeListener { _, b ->
|
||||||
context?.let { ctx ->
|
context?.let { ctx ->
|
||||||
PreferenceManager.getDefaultSharedPreferences(ctx)
|
PreferenceManager.getDefaultSharedPreferences(ctx)
|
||||||
|
@ -553,17 +576,5 @@ class SubtitlesFragment : Fragment() {
|
||||||
it.context.fromSaveToStyle(state)
|
it.context.fromSaveToStyle(state)
|
||||||
activity?.popCurrentPage()
|
activity?.popCurrentPage()
|
||||||
}
|
}
|
||||||
|
|
||||||
subtitle_text.setCues(
|
|
||||||
listOf(
|
|
||||||
Cue.Builder()
|
|
||||||
.setTextSize(
|
|
||||||
getPixels(TypedValue.COMPLEX_UNIT_SP, 25.0f).toFloat(),
|
|
||||||
Cue.TEXT_SIZE_TYPE_ABSOLUTE
|
|
||||||
)
|
|
||||||
.setText(subtitle_text.context.getString(R.string.subtitles_example_text))
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.lagradost.cloudstream3.utils
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.app.Activity.RESULT_CANCELED
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
@ -21,6 +22,7 @@ import android.provider.MediaStore
|
||||||
import android.text.Spanned
|
import android.text.Spanned
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.annotation.WorkerThread
|
import androidx.annotation.WorkerThread
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
@ -41,13 +43,11 @@ import com.google.android.gms.cast.framework.CastState
|
||||||
import com.google.android.gms.common.ConnectionResult
|
import com.google.android.gms.common.ConnectionResult
|
||||||
import com.google.android.gms.common.GoogleApiAvailability
|
import com.google.android.gms.common.GoogleApiAvailability
|
||||||
import com.google.android.gms.common.wrappers.Wrappers
|
import com.google.android.gms.common.wrappers.Wrappers
|
||||||
|
import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.CommonActivity.showToast
|
import com.lagradost.cloudstream3.CommonActivity.showToast
|
||||||
import com.lagradost.cloudstream3.MainActivity.Companion.afterRepositoryLoadedEvent
|
import com.lagradost.cloudstream3.MainActivity.Companion.afterRepositoryLoadedEvent
|
||||||
import com.lagradost.cloudstream3.R
|
|
||||||
import com.lagradost.cloudstream3.SearchResponse
|
|
||||||
import com.lagradost.cloudstream3.isMovieType
|
|
||||||
import com.lagradost.cloudstream3.mapper
|
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
|
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||||
import com.lagradost.cloudstream3.plugins.RepositoryManager
|
import com.lagradost.cloudstream3.plugins.RepositoryManager
|
||||||
import com.lagradost.cloudstream3.ui.WebviewFragment
|
import com.lagradost.cloudstream3.ui.WebviewFragment
|
||||||
import com.lagradost.cloudstream3.ui.result.ResultFragment
|
import com.lagradost.cloudstream3.ui.result.ResultFragment
|
||||||
|
@ -239,6 +239,7 @@ object AppUtils {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Activity.loadRepository(url: String) {
|
fun Activity.loadRepository(url: String) {
|
||||||
ioSafe {
|
ioSafe {
|
||||||
val repo = RepositoryManager.parseRepository(url) ?: return@ioSafe
|
val repo = RepositoryManager.parseRepository(url) ?: return@ioSafe
|
||||||
|
@ -259,6 +260,18 @@ object AppUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Context.hasWebView(): Boolean {
|
||||||
|
return this.packageManager.hasSystemFeature("android.software.webview")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openWebView(fragment: Fragment?, url: String) {
|
||||||
|
if (fragment?.context?.hasWebView() == true)
|
||||||
|
normalSafeApiCall {
|
||||||
|
fragment
|
||||||
|
.findNavController()
|
||||||
|
.navigate(R.id.navigation_webview, WebviewFragment.newInstance(url))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If fallbackWebview is true and a fragment is supplied then it will open a webview with the url if the browser fails.
|
* If fallbackWebview is true and a fragment is supplied then it will open a webview with the url if the browser fails.
|
||||||
|
@ -266,23 +279,34 @@ object AppUtils {
|
||||||
fun Context.openBrowser(
|
fun Context.openBrowser(
|
||||||
url: String,
|
url: String,
|
||||||
fallbackWebview: Boolean = false,
|
fallbackWebview: Boolean = false,
|
||||||
fragment: Fragment? = null
|
fragment: Fragment? = null,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
val intent = Intent(Intent.ACTION_VIEW)
|
val intent = Intent(Intent.ACTION_VIEW)
|
||||||
intent.data = Uri.parse(url)
|
intent.data = Uri.parse(url)
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||||
|
|
||||||
|
// activityResultRegistry is used to fall back to webview if a browser is missing
|
||||||
|
// On older versions the startActivity just crashes, but on newer android versions
|
||||||
|
// You need to check the result to make sure it failed
|
||||||
|
val activityResultRegistry = fragment?.activity?.activityResultRegistry
|
||||||
|
if (activityResultRegistry != null) {
|
||||||
|
activityResultRegistry.register(
|
||||||
|
url,
|
||||||
|
ActivityResultContracts.StartActivityForResult()
|
||||||
|
) { result ->
|
||||||
|
if (result.resultCode == RESULT_CANCELED && fallbackWebview) {
|
||||||
|
openWebView(fragment, url)
|
||||||
|
}
|
||||||
|
}.launch(intent)
|
||||||
|
} else {
|
||||||
ContextCompat.startActivity(this, intent, null)
|
ContextCompat.startActivity(this, intent, null)
|
||||||
|
}
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logError(e)
|
logError(e)
|
||||||
if (fallbackWebview) {
|
if (fallbackWebview) {
|
||||||
try {
|
openWebView(fragment, url)
|
||||||
fragment
|
|
||||||
?.findNavController()
|
|
||||||
?.navigate(R.id.navigation_webview, WebviewFragment.newInstance(url))
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logError(e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ const val DOWNLOAD_HEADER_CACHE = "download_header_cache"
|
||||||
//const val WATCH_HEADER_CACHE = "watch_header_cache"
|
//const val WATCH_HEADER_CACHE = "watch_header_cache"
|
||||||
const val DOWNLOAD_EPISODE_CACHE = "download_episode_cache"
|
const val DOWNLOAD_EPISODE_CACHE = "download_episode_cache"
|
||||||
const val VIDEO_PLAYER_BRIGHTNESS = "video_player_alpha_key"
|
const val VIDEO_PLAYER_BRIGHTNESS = "video_player_alpha_key"
|
||||||
const val HOMEPAGE_API = "home_api_used"
|
const val USER_SELECTED_HOMEPAGE_API = "home_api_used"
|
||||||
const val USER_PROVIDER_API = "user_custom_sites"
|
const val USER_PROVIDER_API = "user_custom_sites"
|
||||||
|
|
||||||
const val PREFERENCES_NAME = "rebuild_preference"
|
const val PREFERENCES_NAME = "rebuild_preference"
|
||||||
|
|
|
@ -163,7 +163,7 @@
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||||
android:id="@+id/add_repo_button"
|
android:id="@+id/add_repo_button"
|
||||||
android:nextFocusUp="@id/list_repositories"
|
android:foreground="@drawable/outline_drawable"
|
||||||
android:nextFocusDown="@id/plugin_storage_appbar"
|
android:nextFocusDown="@id/plugin_storage_appbar"
|
||||||
style="@style/ExtendedFloatingActionButton"
|
style="@style/ExtendedFloatingActionButton"
|
||||||
android:text="@string/add_repository"
|
android:text="@string/add_repository"
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
|
android:nextFocusRight="@id/action_button"
|
||||||
|
android:background="@drawable/outline_drawable"
|
||||||
android:padding="20dp">
|
android:padding="20dp">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
|
@ -86,11 +88,16 @@
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
|
android:padding="10dp"
|
||||||
|
android:background="@drawable/outline_drawable"
|
||||||
|
android:nextFocusLeft="@id/repository_item_root"
|
||||||
|
android:clickable="true"
|
||||||
android:id="@+id/action_button"
|
android:id="@+id/action_button"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical|end"
|
android:layout_gravity="center_vertical|end"
|
||||||
android:layout_marginStart="10dp"
|
android:layout_marginStart="10dp"
|
||||||
tools:src="@drawable/ic_baseline_add_24" />
|
tools:src="@drawable/ic_baseline_add_24"
|
||||||
|
android:focusable="true" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
|
@ -144,43 +144,56 @@
|
||||||
android:text="@string/subs_download_languages" />
|
android:text="@string/subs_download_languages" />
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||||
|
android:id="@+id/subtitles_remove_bloat"
|
||||||
|
style="@style/SettingsItem"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fontFamily="@font/google_sans"
|
||||||
|
android:nextFocusLeft="@id/apply_btt"
|
||||||
|
android:nextFocusRight="@id/cancel_btt"
|
||||||
android:nextFocusUp="@id/subs_download_languages"
|
android:nextFocusUp="@id/subs_download_languages"
|
||||||
android:nextFocusDown="@id/subtitles_remove_captions"
|
android:nextFocusDown="@id/subtitles_remove_captions"
|
||||||
android:nextFocusLeft="@id/apply_btt"
|
|
||||||
android:nextFocusRight="@id/cancel_btt"
|
|
||||||
android:fontFamily="@font/google_sans"
|
|
||||||
style="@style/SettingsItem"
|
|
||||||
app:drawableEndCompat="@null"
|
|
||||||
android:id="@+id/subtitles_remove_bloat"
|
|
||||||
android:text="@string/subtitles_remove_bloat"
|
android:text="@string/subtitles_remove_bloat"
|
||||||
android:layout_width="match_parent"
|
app:drawableEndCompat="@null" />
|
||||||
android:layout_height="match_parent" />
|
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||||
android:nextFocusUp="@id/subtitles_remove_bloat"
|
|
||||||
android:nextFocusDown="@id/apply_btt"
|
|
||||||
android:nextFocusLeft="@id/apply_btt"
|
|
||||||
android:nextFocusRight="@id/cancel_btt"
|
|
||||||
android:fontFamily="@font/google_sans"
|
|
||||||
style="@style/SettingsItem"
|
|
||||||
app:drawableEndCompat="@null"
|
|
||||||
android:id="@+id/subtitles_remove_captions"
|
android:id="@+id/subtitles_remove_captions"
|
||||||
android:text="@string/subtitles_remove_captions"
|
style="@style/SettingsItem"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent"
|
||||||
|
android:fontFamily="@font/google_sans"
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
|
||||||
android:nextFocusUp="@id/subtitles_remove_captions"
|
|
||||||
android:nextFocusDown="@id/apply_btt"
|
|
||||||
android:nextFocusLeft="@id/apply_btt"
|
android:nextFocusLeft="@id/apply_btt"
|
||||||
android:nextFocusRight="@id/cancel_btt"
|
android:nextFocusRight="@id/cancel_btt"
|
||||||
android:fontFamily="@font/google_sans"
|
android:nextFocusUp="@id/subtitles_remove_bloat"
|
||||||
style="@style/SettingsItem"
|
android:nextFocusDown="@id/subtitles_filter_sub_lang"
|
||||||
app:drawableEndCompat="@null"
|
android:text="@string/subtitles_remove_captions"
|
||||||
|
app:drawableEndCompat="@null" />
|
||||||
|
|
||||||
|
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||||
android:id="@+id/subtitles_filter_sub_lang"
|
android:id="@+id/subtitles_filter_sub_lang"
|
||||||
android:text="@string/subtitles_filter_lang"
|
style="@style/SettingsItem"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent"
|
||||||
|
android:fontFamily="@font/google_sans"
|
||||||
|
android:nextFocusLeft="@id/apply_btt"
|
||||||
|
android:nextFocusRight="@id/cancel_btt"
|
||||||
|
android:nextFocusUp="@id/subtitles_remove_captions"
|
||||||
|
android:nextFocusDown="@id/subtitles_uppercase"
|
||||||
|
android:text="@string/subtitles_filter_lang"
|
||||||
|
app:drawableEndCompat="@null" />
|
||||||
|
|
||||||
|
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||||
|
android:id="@+id/subtitles_uppercase"
|
||||||
|
style="@style/SettingsItem"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fontFamily="@font/google_sans"
|
||||||
|
android:nextFocusLeft="@id/apply_btt"
|
||||||
|
android:nextFocusRight="@id/cancel_btt"
|
||||||
|
android:nextFocusUp="@id/subtitles_filter_sub_lang"
|
||||||
|
android:nextFocusDown="@id/apply_btt"
|
||||||
|
android:text="@string/uppercase_all_subtitles"
|
||||||
|
app:drawableEndCompat="@null" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
@ -601,4 +601,5 @@
|
||||||
<string name="blank_repo_message">Add a repository to install site extensions</string>
|
<string name="blank_repo_message">Add a repository to install site extensions</string>
|
||||||
<string name="view_public_repositories_button">View community repositories</string>
|
<string name="view_public_repositories_button">View community repositories</string>
|
||||||
<string name="view_public_repositories_button_short">Public list</string>
|
<string name="view_public_repositories_button_short">Public list</string>
|
||||||
|
<string name="uppercase_all_subtitles">Uppercase all subtitles</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue