forked from recloudstream/cloudstream
fixed chromecast, fixed tenshi, fixed lang providers
This commit is contained in:
parent
d65ccffe4e
commit
f394469787
13 changed files with 266 additions and 95 deletions
|
@ -31,8 +31,8 @@ android {
|
||||||
applicationId "com.lagradost.cloudstream3"
|
applicationId "com.lagradost.cloudstream3"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 31
|
targetSdkVersion 31
|
||||||
versionCode 26
|
versionCode 27
|
||||||
versionName "1.9.10"
|
versionName "1.9.11"
|
||||||
|
|
||||||
resValue "string", "app_version",
|
resValue "string", "app_version",
|
||||||
"${defaultConfig.versionName}${versionNameSuffix ?: ""}"
|
"${defaultConfig.versionName}${versionNameSuffix ?: ""}"
|
||||||
|
@ -72,9 +72,6 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
maven {
|
|
||||||
url 'https://github.com/psiegman/mvn-repo/raw/master/releases'
|
|
||||||
}
|
|
||||||
maven { url 'https://jitpack.io' }
|
maven { url 'https://jitpack.io' }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,9 +105,9 @@ dependencies {
|
||||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||||
|
|
||||||
// Exoplayer
|
// Exoplayer
|
||||||
implementation 'com.google.android.exoplayer:exoplayer:2.14.2'
|
implementation 'com.google.android.exoplayer:exoplayer:2.15.0'
|
||||||
implementation 'com.google.android.exoplayer:extension-cast:2.14.2'
|
implementation 'com.google.android.exoplayer:extension-cast:2.15.0'
|
||||||
implementation "com.google.android.exoplayer:extension-mediasession:2.14.2"
|
implementation "com.google.android.exoplayer:extension-mediasession:2.15.0"
|
||||||
//implementation "com.google.android.exoplayer:extension-leanback:2.14.0"
|
//implementation "com.google.android.exoplayer:extension-leanback:2.14.0"
|
||||||
|
|
||||||
// Bug reports
|
// Bug reports
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.lagradost.cloudstream3
|
package com.lagradost.cloudstream3
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||||
import com.fasterxml.jackson.databind.json.JsonMapper
|
import com.fasterxml.jackson.databind.json.JsonMapper
|
||||||
|
@ -9,6 +10,8 @@ import com.lagradost.cloudstream3.animeproviders.*
|
||||||
import com.lagradost.cloudstream3.movieproviders.*
|
import com.lagradost.cloudstream3.movieproviders.*
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
import kotlin.collections.HashSet
|
||||||
|
|
||||||
const val USER_AGENT =
|
const val USER_AGENT =
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
|
||||||
|
@ -69,19 +72,30 @@ object APIHolder {
|
||||||
return url.replace(getApiFromName(apiName).mainUrl, "").replace("/", "").hashCode()
|
return url.replace(getApiFromName(apiName).mainUrl, "").replace("/", "").hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Activity.getApiSettings(): HashSet<String> {
|
fun Context.getApiSettings(): HashSet<String> {
|
||||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
|
||||||
val hashSet = HashSet<String>()
|
val hashSet = HashSet<String>()
|
||||||
hashSet.addAll(apis.map { it.name })
|
hashSet.addAll(apis.map { it.name })
|
||||||
|
|
||||||
return settingsManager.getStringSet(
|
val set = settingsManager.getStringSet(
|
||||||
this.getString(R.string.search_providers_list_key),
|
this.getString(R.string.search_providers_list_key),
|
||||||
hashSet
|
hashSet
|
||||||
)?.toHashSet() ?: hashSet
|
)?.toHashSet() ?: hashSet
|
||||||
|
|
||||||
|
val activeLangs = getApiProviderLangSettings()
|
||||||
|
val list = HashSet<String>()
|
||||||
|
for (name in set) {
|
||||||
|
val api = getApiFromNameNull(name) ?: continue
|
||||||
|
if(activeLangs.contains(api.lang) ) {
|
||||||
|
list.add(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(list.isEmpty()) return hashSet
|
||||||
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Activity.getApiDubstatusSettings(): HashSet<DubStatus> {
|
fun Context.getApiDubstatusSettings(): HashSet<DubStatus> {
|
||||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
val hashSet = HashSet<DubStatus>()
|
val hashSet = HashSet<DubStatus>()
|
||||||
hashSet.addAll(DubStatus.values())
|
hashSet.addAll(DubStatus.values())
|
||||||
|
@ -96,7 +110,20 @@ object APIHolder {
|
||||||
return list.filter { names.contains(it) }.map { DubStatus.valueOf(it) }.toHashSet()
|
return list.filter { names.contains(it) }.map { DubStatus.valueOf(it) }.toHashSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Activity.getApiTypeSettings(): HashSet<TvType> {
|
fun Context.getApiProviderLangSettings(): HashSet<String> {
|
||||||
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
val hashSet = HashSet<String>()
|
||||||
|
hashSet.add("en") // def is only en
|
||||||
|
val list = settingsManager.getStringSet(
|
||||||
|
this.getString(R.string.provider_lang_key),
|
||||||
|
hashSet.toMutableSet()
|
||||||
|
)
|
||||||
|
|
||||||
|
if(list.isNullOrEmpty()) return hashSet
|
||||||
|
return list.toHashSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.getApiTypeSettings(): HashSet<TvType> {
|
||||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
val hashSet = HashSet<TvType>()
|
val hashSet = HashSet<TvType>()
|
||||||
hashSet.addAll(TvType.values())
|
hashSet.addAll(TvType.values())
|
||||||
|
@ -120,6 +147,8 @@ abstract class MainAPI {
|
||||||
open val name = "NONE"
|
open val name = "NONE"
|
||||||
open val mainUrl = "NONE"
|
open val mainUrl = "NONE"
|
||||||
|
|
||||||
|
open val lang = "en" // ISO_639_1 check SubtitleHelper
|
||||||
|
|
||||||
/**If link is stored in the "data" string, so links can be instantly loaded*/
|
/**If link is stored in the "data" string, so links can be instantly loaded*/
|
||||||
open val instantLinkLoading = false
|
open val instantLinkLoading = false
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ import androidx.navigation.ui.setupWithNavController
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.transition.ChangeBounds
|
import androidx.transition.ChangeBounds
|
||||||
import androidx.transition.TransitionManager
|
import androidx.transition.TransitionManager
|
||||||
import com.google.android.gms.cast.framework.CastButtonFactory
|
import com.google.android.gms.cast.framework.*
|
||||||
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
|
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
|
||||||
import com.lagradost.cloudstream3.APIHolder.apis
|
import com.lagradost.cloudstream3.APIHolder.apis
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
|
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
|
||||||
|
@ -80,6 +80,53 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
updateLocale() // android fucks me by chaining lang when rotating the phone
|
updateLocale() // android fucks me by chaining lang when rotating the phone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mCastSession: CastSession? = null
|
||||||
|
lateinit var mSessionManager: SessionManager
|
||||||
|
val mSessionManagerListener: SessionManagerListener<Session> by lazy { SessionManagerListenerImpl() }
|
||||||
|
|
||||||
|
private inner class SessionManagerListenerImpl : SessionManagerListener<Session> {
|
||||||
|
override fun onSessionStarting(session: Session) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSessionStarted(session: Session, sessionId: String) {
|
||||||
|
invalidateOptionsMenu()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSessionStartFailed(session: Session, i: Int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSessionEnding(session: Session) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSessionResumed(session: Session, wasSuspended: Boolean) {
|
||||||
|
invalidateOptionsMenu()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSessionResumeFailed(session: Session, i: Int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSessionSuspended(session: Session, i: Int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSessionEnded(session: Session, error: Int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSessionResuming(session: Session, s: String) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
mCastSession = mSessionManager.currentCastSession
|
||||||
|
mSessionManager.addSessionManagerListener(mSessionManagerListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
mSessionManager.removeSessionManagerListener(mSessionManagerListener)
|
||||||
|
mCastSession = null
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
var canEnterPipMode: Boolean = false
|
var canEnterPipMode: Boolean = false
|
||||||
var canShowPipMode: Boolean = false
|
var canShowPipMode: Boolean = false
|
||||||
|
@ -268,6 +315,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
) // THEME IS SET BEFORE VIEW IS CREATED TO APPLY THE THEME TO THE MAIN VIEW
|
) // THEME IS SET BEFORE VIEW IS CREATED TO APPLY THE THEME TO THE MAIN VIEW
|
||||||
updateLocale()
|
updateLocale()
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
mSessionManager = CastContext.getSharedInstance(this).sessionManager
|
||||||
|
|
||||||
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
|
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
|
||||||
|
|
||||||
|
|
|
@ -140,12 +140,16 @@ class TenshiProvider : MainAPI() {
|
||||||
|
|
||||||
@SuppressLint("SimpleDateFormat")
|
@SuppressLint("SimpleDateFormat")
|
||||||
private fun dateParser(dateString: String): String? {
|
private fun dateParser(dateString: String): String? {
|
||||||
val format = SimpleDateFormat("dd 'of' MMM',' yyyy")
|
try {
|
||||||
val newFormat = SimpleDateFormat("dd-MM-yyyy")
|
val format = SimpleDateFormat("dd 'of' MMM',' yyyy")
|
||||||
val data = format.parse(
|
val newFormat = SimpleDateFormat("dd-MM-yyyy")
|
||||||
dateString.replace("th ", " ").replace("st ", " ").replace("nd ", " ").replace("rd ", " ")
|
val data = format.parse(
|
||||||
) ?: return null
|
dateString.replace("th ", " ").replace("st ", " ").replace("nd ", " ").replace("rd ", " ")
|
||||||
return newFormat.format(data)
|
) ?: return null
|
||||||
|
return newFormat.format(data)
|
||||||
|
} catch (e : Exception) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// data class TenshiSearchResponse(
|
// data class TenshiSearchResponse(
|
||||||
|
|
|
@ -11,6 +11,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature
|
||||||
import com.fasterxml.jackson.databind.json.JsonMapper
|
import com.fasterxml.jackson.databind.json.JsonMapper
|
||||||
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||||
import com.google.android.gms.cast.MediaQueueItem
|
import com.google.android.gms.cast.MediaQueueItem
|
||||||
|
import com.google.android.gms.cast.MediaSeekOptions
|
||||||
import com.google.android.gms.cast.MediaStatus.REPEAT_MODE_REPEAT_OFF
|
import com.google.android.gms.cast.MediaStatus.REPEAT_MODE_REPEAT_OFF
|
||||||
import com.google.android.gms.cast.MediaTrack
|
import com.google.android.gms.cast.MediaTrack
|
||||||
import com.google.android.gms.cast.TextTrackStyle
|
import com.google.android.gms.cast.TextTrackStyle
|
||||||
|
@ -33,12 +34,17 @@ import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
class SkipOpController(val view: ImageView) : UIController() {
|
class SkipOpController(val view: ImageView) : UIController() {
|
||||||
init {
|
init {
|
||||||
view.setImageResource(R.drawable.exo_controls_fastforward)
|
view.setImageResource(R.drawable.exo_controls_fastforward)
|
||||||
view.setOnClickListener {
|
view.setOnClickListener {
|
||||||
remoteMediaClient.seek(remoteMediaClient.approximateStreamPosition + 85000)
|
remoteMediaClient?.let {
|
||||||
|
val options = MediaSeekOptions.Builder()
|
||||||
|
.setPosition(it.approximateStreamPosition + 85000)
|
||||||
|
it.seek(options.build())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -192,15 +198,11 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
|
||||||
try { // THIS IS VERY IMPORTANT BECAUSE WE NEVER WANT TO AUTOLOAD THE NEXT EPISODE
|
try { // THIS IS VERY IMPORTANT BECAUSE WE NEVER WANT TO AUTOLOAD THE NEXT EPISODE
|
||||||
val currentIdIndex = remoteMediaClient?.getItemIndex()
|
val currentIdIndex = remoteMediaClient?.getItemIndex()
|
||||||
|
|
||||||
val nextId = remoteMediaClient.mediaQueue.itemIds?.get(currentIdIndex?.plus(1) ?: 0)
|
val nextId = remoteMediaClient?.mediaQueue?.itemIds?.get(currentIdIndex?.plus(1) ?: 0)
|
||||||
|
|
||||||
if (currentIdIndex == null && nextId != null) {
|
if (currentIdIndex == null && nextId != null) {
|
||||||
awaitLinks(
|
awaitLinks(
|
||||||
remoteMediaClient?.queueInsertAndPlayItem(
|
remoteMediaClient?.queueInsertAndPlayItem(
|
||||||
MediaQueueItem.Builder(
|
MediaQueueItem.Builder(mediaItem).build(),
|
||||||
mediaItem
|
|
||||||
)
|
|
||||||
.build(),
|
|
||||||
nextId,
|
nextId,
|
||||||
startAt,
|
startAt,
|
||||||
JSONObject()
|
JSONObject()
|
||||||
|
@ -223,6 +225,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
|
||||||
|
|
||||||
bottomSheetDialog.dismiss()
|
bottomSheetDialog.dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,8 +256,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
|
||||||
|
|
||||||
if (itemCount != null && itemCount - currentIdIndex == 1 && !isLoadingMore) {
|
if (itemCount != null && itemCount - currentIdIndex == 1 && !isLoadingMore) {
|
||||||
isLoadingMore = true
|
isLoadingMore = true
|
||||||
|
thread {
|
||||||
main {
|
|
||||||
val index = meta.currentEpisodeIndex + 1
|
val index = meta.currentEpisodeIndex + 1
|
||||||
val epData = meta.episodes[index]
|
val epData = meta.episodes[index]
|
||||||
val links = ArrayList<ExtractorLink>()
|
val links = ArrayList<ExtractorLink>()
|
||||||
|
@ -280,9 +282,8 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
|
||||||
currentEpisodeIndex = index
|
currentEpisodeIndex = index
|
||||||
)
|
)
|
||||||
|
|
||||||
val done = withContext(Dispatchers.IO) {
|
val done =
|
||||||
JSONObject(mapper.writeValueAsString(jsonCopy))
|
JSONObject(mapper.writeValueAsString(jsonCopy))
|
||||||
}
|
|
||||||
|
|
||||||
val mediaInfo = getMediaInfo(
|
val mediaInfo = getMediaInfo(
|
||||||
epData,
|
epData,
|
||||||
|
@ -304,19 +305,23 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
|
||||||
loadIndex(index + 1)
|
loadIndex(index + 1)
|
||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
activity.runOnUiThread {
|
||||||
|
awaitLinks(
|
||||||
|
|
||||||
awaitLinks(
|
remoteMediaClient?.queueAppendItem(
|
||||||
remoteMediaClient?.queueAppendItem(
|
MediaQueueItem.Builder(mediaInfo).build(),
|
||||||
MediaQueueItem.Builder(mediaInfo).build(),
|
JSONObject()
|
||||||
JSONObject()
|
)
|
||||||
)
|
|
||||||
) {
|
|
||||||
println("FAILED TO LOAD NEXT ITEM")
|
) {
|
||||||
// loadIndex(1)
|
println("FAILED TO LOAD NEXT ITEM")
|
||||||
|
// loadIndex(1)
|
||||||
|
}
|
||||||
|
isLoadingMore = false
|
||||||
}
|
}
|
||||||
|
|
||||||
isLoadingMore = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -340,7 +345,11 @@ class SkipTimeController(val view: ImageView, forwards: Boolean) : UIController(
|
||||||
//view.setImageResource(if (forwards) R.drawable.netflix_skip_forward else R.drawable.netflix_skip_back)
|
//view.setImageResource(if (forwards) R.drawable.netflix_skip_forward else R.drawable.netflix_skip_back)
|
||||||
view.setImageResource(if (forwards) R.drawable.go_forward_30 else R.drawable.go_back_30)
|
view.setImageResource(if (forwards) R.drawable.go_forward_30 else R.drawable.go_back_30)
|
||||||
view.setOnClickListener {
|
view.setOnClickListener {
|
||||||
remoteMediaClient.seek(remoteMediaClient.approximateStreamPosition + time * 1000 * if (forwards) 1 else -1)
|
remoteMediaClient?.let {
|
||||||
|
val options = MediaSeekOptions.Builder()
|
||||||
|
.setPosition(it.approximateStreamPosition + time * 1000 * if (forwards) 1 else -1)
|
||||||
|
it.seek(options.build())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import android.database.ContentObserver
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.Icon
|
import android.graphics.drawable.Icon
|
||||||
import android.media.AudioManager
|
import android.media.AudioManager
|
||||||
|
import android.media.metrics.PlaybackErrorEvent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.*
|
import android.os.*
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
|
@ -46,6 +47,7 @@ import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
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.PlaybackException.ERROR_CODE_AUDIO_TRACK_INIT_FAILED
|
||||||
import com.google.android.exoplayer2.database.ExoDatabaseProvider
|
import com.google.android.exoplayer2.database.ExoDatabaseProvider
|
||||||
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory
|
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory
|
||||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
|
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
|
||||||
|
@ -165,7 +167,6 @@ data class UriData(
|
||||||
|
|
||||||
// YE, I KNOW, THIS COULD BE HANDLED A LOT BETTER
|
// YE, I KNOW, THIS COULD BE HANDLED A LOT BETTER
|
||||||
class PlayerFragment : Fragment() {
|
class PlayerFragment : Fragment() {
|
||||||
|
|
||||||
// ============ TORRENT ============
|
// ============ TORRENT ============
|
||||||
//private var torrentStream: TorrentStream? = null
|
//private var torrentStream: TorrentStream? = null
|
||||||
private var lastTorrentUrl = ""
|
private var lastTorrentUrl = ""
|
||||||
|
@ -752,6 +753,15 @@ class PlayerFragment : Fragment() {
|
||||||
private var volumeObserver: SettingsContentObserver? = null
|
private var volumeObserver: SettingsContentObserver? = null
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
fun String.toSubtitleMimeType(): String {
|
||||||
|
return when {
|
||||||
|
endsWith("vtt", true) -> MimeTypes.TEXT_VTT
|
||||||
|
endsWith("srt", true) -> MimeTypes.APPLICATION_SUBRIP
|
||||||
|
endsWith("xml", true) || endsWith("ttml", true) -> MimeTypes.APPLICATION_TTML
|
||||||
|
else -> MimeTypes.APPLICATION_SUBRIP // TODO get request to see
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun newInstance(data: PlayerData, startPos: Long? = null): Bundle {
|
fun newInstance(data: PlayerData, startPos: Long? = null): Bundle {
|
||||||
return Bundle().apply {
|
return Bundle().apply {
|
||||||
//println(data)
|
//println(data)
|
||||||
|
@ -1050,7 +1060,7 @@ class PlayerFragment : Fragment() {
|
||||||
val epData = getEpisode() ?: return@addCastStateListener
|
val epData = getEpisode() ?: return@addCastStateListener
|
||||||
|
|
||||||
val index = links.indexOf(getCurrentUrl())
|
val index = links.indexOf(getCurrentUrl())
|
||||||
context?.startCast(
|
(activity as MainActivity?)?.mCastSession?.startCast(
|
||||||
apiName,
|
apiName,
|
||||||
currentIsMovie ?: return@addCastStateListener,
|
currentIsMovie ?: return@addCastStateListener,
|
||||||
currentHeaderName,
|
currentHeaderName,
|
||||||
|
@ -1737,15 +1747,6 @@ class PlayerFragment : Fragment() {
|
||||||
|
|
||||||
private val updateProgressAction = Runnable { updateProgressBar() }*/
|
private val updateProgressAction = Runnable { updateProgressBar() }*/
|
||||||
|
|
||||||
private fun String.toSubtitleMimeType(): String {
|
|
||||||
return when {
|
|
||||||
endsWith("vtt", true) -> MimeTypes.TEXT_VTT
|
|
||||||
endsWith("srt", true) -> MimeTypes.APPLICATION_SUBRIP
|
|
||||||
endsWith("xml", true) || endsWith("ttml", true) -> MimeTypes.APPLICATION_TTML
|
|
||||||
else -> MimeTypes.APPLICATION_SUBRIP // TODO get request to see
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var activeSubtitles: List<String> = listOf()
|
var activeSubtitles: List<String> = listOf()
|
||||||
var preferredSubtitles: String = ""
|
var preferredSubtitles: String = ""
|
||||||
|
|
||||||
|
@ -2047,7 +2048,44 @@ class PlayerFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPlayerError(error: ExoPlaybackException) {
|
override fun onPlayerError(error: PlaybackException) {
|
||||||
|
println("CURRENT URL: " + currentUrl?.url)
|
||||||
|
// Lets pray this doesn't spam Toasts :)
|
||||||
|
val msg = error.message ?: ""
|
||||||
|
when (val code = error.errorCode) {
|
||||||
|
PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND, PlaybackException.ERROR_CODE_IO_NO_PERMISSION, PlaybackException.ERROR_CODE_IO_UNSPECIFIED -> {
|
||||||
|
if (currentUrl?.url != "") {
|
||||||
|
showToast(
|
||||||
|
activity,
|
||||||
|
"${getString(R.string.source_error)}\n$code\n$msg",
|
||||||
|
LENGTH_SHORT
|
||||||
|
)
|
||||||
|
tryNextMirror()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PlaybackException.ERROR_CODE_REMOTE_ERROR, PlaybackException.ERROR_CODE_IO_BAD_HTTP_STATUS, PlaybackException.ERROR_CODE_TIMEOUT, PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED, PlaybackException.ERROR_CODE_IO_INVALID_HTTP_CONTENT_TYPE -> {
|
||||||
|
showToast(activity, "${getString(R.string.remote_error)}\n$code\n$msg", LENGTH_SHORT)
|
||||||
|
}
|
||||||
|
PlaybackException.ERROR_CODE_DECODING_FAILED, PlaybackErrorEvent.ERROR_AUDIO_TRACK_INIT_FAILED, PlaybackErrorEvent.ERROR_AUDIO_TRACK_OTHER, PlaybackException.ERROR_CODE_AUDIO_TRACK_WRITE_FAILED, PlaybackException.ERROR_CODE_DECODER_INIT_FAILED, PlaybackException.ERROR_CODE_DECODER_QUERY_FAILED -> {
|
||||||
|
showToast(
|
||||||
|
activity,
|
||||||
|
"${getString(R.string.render_error)}\n$code\n$msg",
|
||||||
|
LENGTH_SHORT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
showToast(
|
||||||
|
activity,
|
||||||
|
"${getString(R.string.unexpected_error)}\n$code\n$msg",
|
||||||
|
LENGTH_SHORT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onPlayerError(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*override fun onPlayerError(error: ExoPlaybackException) {
|
||||||
println("CURRENT URL: " + currentUrl?.url)
|
println("CURRENT URL: " + currentUrl?.url)
|
||||||
// Lets pray this doesn't spam Toasts :)
|
// Lets pray this doesn't spam Toasts :)
|
||||||
when (error.type) {
|
when (error.type) {
|
||||||
|
@ -2079,7 +2117,7 @@ class PlayerFragment : Fragment() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
})
|
})
|
||||||
} catch (e: java.lang.IllegalStateException) {
|
} catch (e: java.lang.IllegalStateException) {
|
||||||
println("Warning: Illegal state exception in PlayerFragment")
|
println("Warning: Illegal state exception in PlayerFragment")
|
||||||
|
|
|
@ -427,7 +427,7 @@ class ResultFragment : Fragment() {
|
||||||
|
|
||||||
fun startChromecast(startIndex: Int) {
|
fun startChromecast(startIndex: Int) {
|
||||||
val eps = currentEpisodes ?: return
|
val eps = currentEpisodes ?: return
|
||||||
context?.startCast(
|
(activity as MainActivity?)?.mCastSession?.startCast(
|
||||||
apiName,
|
apiName,
|
||||||
currentIsMovie ?: return,
|
currentIsMovie ?: return,
|
||||||
currentHeaderName,
|
currentHeaderName,
|
||||||
|
|
|
@ -6,7 +6,10 @@ import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.lagradost.cloudstream3.APIHolder.apis
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
|
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
|
||||||
|
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
|
||||||
|
import com.lagradost.cloudstream3.APIHolder.restrictedApis
|
||||||
import com.lagradost.cloudstream3.DubStatus
|
import com.lagradost.cloudstream3.DubStatus
|
||||||
import com.lagradost.cloudstream3.MainActivity.Companion.setLocale
|
import com.lagradost.cloudstream3.MainActivity.Companion.setLocale
|
||||||
import com.lagradost.cloudstream3.MainActivity.Companion.showToast
|
import com.lagradost.cloudstream3.MainActivity.Companion.showToast
|
||||||
|
@ -19,6 +22,7 @@ import com.lagradost.cloudstream3.utils.Qualities
|
||||||
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
|
||||||
|
import com.lagradost.cloudstream3.utils.SubtitleHelper
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
|
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
|
@ -50,6 +54,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
||||||
val watchQualityPreference = findPreference<Preference>(getString(R.string.quality_pref_key))!!
|
val watchQualityPreference = findPreference<Preference>(getString(R.string.quality_pref_key))!!
|
||||||
val legalPreference = findPreference<Preference>(getString(R.string.legal_notice_key))!!
|
val legalPreference = findPreference<Preference>(getString(R.string.legal_notice_key))!!
|
||||||
val subdubPreference = findPreference<Preference>(getString(R.string.display_sub_key))!!
|
val subdubPreference = findPreference<Preference>(getString(R.string.display_sub_key))!!
|
||||||
|
val providerLangPreference = findPreference<Preference>(getString(R.string.provider_lang_key))!!
|
||||||
|
|
||||||
legalPreference.setOnPreferenceClickListener {
|
legalPreference.setOnPreferenceClickListener {
|
||||||
val builder: AlertDialog.Builder = AlertDialog.Builder(it.context)
|
val builder: AlertDialog.Builder = AlertDialog.Builder(it.context)
|
||||||
|
@ -85,7 +90,39 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return@setOnPreferenceClickListener true
|
||||||
|
}
|
||||||
|
|
||||||
|
providerLangPreference.setOnPreferenceClickListener {
|
||||||
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
|
||||||
|
activity?.getApiProviderLangSettings()?.let { current ->
|
||||||
|
val allLangs = HashSet<String>()
|
||||||
|
for (api in apis) {
|
||||||
|
allLangs.add(api.lang)
|
||||||
|
}
|
||||||
|
for (api in restrictedApis) {
|
||||||
|
allLangs.add(api.lang)
|
||||||
|
}
|
||||||
|
|
||||||
|
val currentList = ArrayList<Int>()
|
||||||
|
for (i in current) {
|
||||||
|
currentList.add(allLangs.indexOf(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
val names = allLangs.mapNotNull { SubtitleHelper.fromTwoLettersToLanguage(it) }
|
||||||
|
|
||||||
|
context?.showMultiDialog(
|
||||||
|
names,
|
||||||
|
currentList,
|
||||||
|
getString(R.string.provider_lang_settings),
|
||||||
|
{}) { selectedList ->
|
||||||
|
settingsManager.edit().putStringSet(
|
||||||
|
this.getString(R.string.provider_lang_key),
|
||||||
|
selectedList.map { names[it] }.toMutableSet()
|
||||||
|
).apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return@setOnPreferenceClickListener true
|
return@setOnPreferenceClickListener true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
package com.lagradost.cloudstream3.utils
|
package com.lagradost.cloudstream3.utils
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||||
import com.fasterxml.jackson.databind.json.JsonMapper
|
import com.fasterxml.jackson.databind.json.JsonMapper
|
||||||
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||||
import com.google.android.exoplayer2.ext.cast.CastPlayer
|
|
||||||
import com.google.android.exoplayer2.util.MimeTypes
|
import com.google.android.exoplayer2.util.MimeTypes
|
||||||
import com.google.android.gms.cast.*
|
import com.google.android.gms.cast.*
|
||||||
import com.google.android.gms.cast.MediaStatus.REPEAT_MODE_REPEAT_OFF
|
import com.google.android.gms.cast.framework.CastSession
|
||||||
import com.google.android.gms.cast.framework.CastContext
|
|
||||||
import com.google.android.gms.cast.framework.media.RemoteMediaClient
|
import com.google.android.gms.cast.framework.media.RemoteMediaClient
|
||||||
import com.google.android.gms.common.api.PendingResult
|
import com.google.android.gms.common.api.PendingResult
|
||||||
import com.google.android.gms.common.images.WebImage
|
import com.google.android.gms.common.images.WebImage
|
||||||
|
@ -59,13 +56,16 @@ object CastHelper {
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
return MediaInfo.Builder(link.url)
|
val builder = MediaInfo.Builder(link.url)
|
||||||
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
|
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
|
||||||
.setContentType(MimeTypes.VIDEO_UNKNOWN)
|
.setContentType(MimeTypes.VIDEO_UNKNOWN)
|
||||||
.setCustomData(data)
|
|
||||||
.setMetadata(movieMetadata)
|
.setMetadata(movieMetadata)
|
||||||
.setMediaTracks(tracks)
|
.setMediaTracks(tracks)
|
||||||
.build()
|
data?.let {
|
||||||
|
builder.setCustomData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun awaitLinks(pending: PendingResult<RemoteMediaClient.MediaChannelResult>?, callback: (Boolean) -> Unit) {
|
fun awaitLinks(pending: PendingResult<RemoteMediaClient.MediaChannelResult>?, callback: (Boolean) -> Unit) {
|
||||||
|
@ -84,8 +84,7 @@ object CastHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun CastSession?.startCast(
|
||||||
fun Context.startCast(
|
|
||||||
apiName: String,
|
apiName: String,
|
||||||
isMovie: Boolean,
|
isMovie: Boolean,
|
||||||
title: String?,
|
title: String?,
|
||||||
|
@ -97,11 +96,10 @@ object CastHelper {
|
||||||
startIndex: Int? = null,
|
startIndex: Int? = null,
|
||||||
startTime: Long? = null,
|
startTime: Long? = null,
|
||||||
) : Boolean {
|
) : Boolean {
|
||||||
|
if(this == null) return false
|
||||||
if (episodes.isEmpty()) return false
|
if (episodes.isEmpty()) return false
|
||||||
if (currentLinks.size <= currentEpisodeIndex) return false
|
if (currentLinks.size <= currentEpisodeIndex) return false
|
||||||
|
|
||||||
val castContext = CastContext.getSharedInstance(this)
|
|
||||||
|
|
||||||
val epData = episodes[currentEpisodeIndex]
|
val epData = episodes[currentEpisodeIndex]
|
||||||
|
|
||||||
val holder =
|
val holder =
|
||||||
|
@ -112,15 +110,8 @@ object CastHelper {
|
||||||
val mediaItem =
|
val mediaItem =
|
||||||
getMediaInfo(epData, holder, index, JSONObject(mapper.writeValueAsString(holder)), subtitles)
|
getMediaInfo(epData, holder, index, JSONObject(mapper.writeValueAsString(holder)), subtitles)
|
||||||
|
|
||||||
val castPlayer = CastPlayer(castContext)
|
|
||||||
|
|
||||||
castPlayer.repeatMode = REPEAT_MODE_REPEAT_OFF
|
|
||||||
|
|
||||||
awaitLinks(
|
awaitLinks(
|
||||||
castPlayer.loadItem(
|
this.remoteMediaClient?.load(MediaLoadRequestData.Builder().setMediaInfo(mediaItem).setCurrentTime(startTime ?: 0L).build() )
|
||||||
MediaQueueItem.Builder(mediaItem).build(),
|
|
||||||
startTime ?: 0,
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
if (currentLinks.size > index + 1)
|
if (currentLinks.size > index + 1)
|
||||||
startCast(
|
startCast(
|
||||||
|
|
|
@ -202,5 +202,5 @@
|
||||||
<string name="resume">Återuppta</string>
|
<string name="resume">Återuppta</string>
|
||||||
<string name="storage_error">Ett nerladdningsfel uppstod, kolla om appen har lagringsbehörigheter</string>
|
<string name="storage_error">Ett nerladdningsfel uppstod, kolla om appen har lagringsbehörigheter</string>
|
||||||
<string name="watch_quality_pref">Föredragen videokvalitet</string>
|
<string name="watch_quality_pref">Föredragen videokvalitet</string>
|
||||||
|
<string name="general">Allmänna Inställningar</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
<string name="swipe_vertical_enabled_key" translatable="false">swipe_vertical_enabled_key</string>
|
<string name="swipe_vertical_enabled_key" translatable="false">swipe_vertical_enabled_key</string>
|
||||||
<string name="display_sub_key" translatable="false">display_sub_key</string>
|
<string name="display_sub_key" translatable="false">display_sub_key</string>
|
||||||
<string name="show_fillers_key" translatable="false">show_fillers_key</string>
|
<string name="show_fillers_key" translatable="false">show_fillers_key</string>
|
||||||
|
<string name="provider_lang_key" translatable="false">provider_lang_key</string>
|
||||||
|
|
||||||
|
|
||||||
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
|
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
|
||||||
<string name="extra_info_format" translatable="false" formatted="true">%d %s | %sMB</string>
|
<string name="extra_info_format" translatable="false" formatted="true">%d %s | %sMB</string>
|
||||||
|
@ -276,4 +278,6 @@
|
||||||
|
|
||||||
It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk.
|
It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk.
|
||||||
</string>
|
</string>
|
||||||
|
<string name="general">General</string>
|
||||||
|
<string name="provider_lang_settings">Provider Languages</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -177,16 +177,19 @@
|
||||||
<item name="android:textColor">?attr/textColor</item>
|
<item name="android:textColor">?attr/textColor</item>
|
||||||
<item name="rippleColor">?attr/textColor</item>
|
<item name="rippleColor">?attr/textColor</item>
|
||||||
</style>
|
</style>
|
||||||
|
<!--@color/white ?attr/colorPrimary-->
|
||||||
|
<!--CHECK ?attr/darkBackground ?attr/colorPrimary-->
|
||||||
<!-- CHROMECAST -->
|
<!-- CHROMECAST -->
|
||||||
<style name="CustomCastExpandedController" parent="CastExpandedController">
|
<style name="CustomCastExpandedController" parent="CastExpandedController">
|
||||||
<item name="castControlButtons">
|
<item name="castControlButtons">
|
||||||
@array/cast_expanded_controller_control_buttons
|
@array/cast_expanded_controller_control_buttons
|
||||||
</item>
|
</item>
|
||||||
<item name="castButtonColor">@null</item>
|
<!-- <item name="castButtonColor">@null</item>
|
||||||
<item name="castSeekBarProgressAndThumbColor">?attr/white</item> <!--@color/white ?attr/colorPrimary-->
|
|
||||||
<item name="castSeekBarSecondaryProgressColor">?attr/darkBackground
|
<item name="castSeekBarSecondaryProgressColor">@color/darkBar
|
||||||
</item> <!--CHECK ?attr/darkBackground ?attr/colorPrimary-->
|
</item>
|
||||||
|
-->
|
||||||
|
<item name="castSeekBarProgressAndThumbColor">?attr/colorPrimary</item>
|
||||||
<item name="castBackground">?attr/colorPrimary</item>
|
<item name="castBackground">?attr/colorPrimary</item>
|
||||||
<item name="castProgressBarColor">?attr/colorPrimary</item>
|
<item name="castProgressBarColor">?attr/colorPrimary</item>
|
||||||
<item name="castPlayButtonDrawable">@drawable/ic_baseline_play_arrow_24</item>
|
<item name="castPlayButtonDrawable">@drawable/ic_baseline_play_arrow_24</item>
|
||||||
|
|
|
@ -68,6 +68,32 @@
|
||||||
app:defaultValue="false"
|
app:defaultValue="false"
|
||||||
/>
|
/>
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
<PreferenceCategory
|
||||||
|
android:key="general"
|
||||||
|
android:title="@string/general"
|
||||||
|
app:isPreferenceVisible="true"
|
||||||
|
>
|
||||||
|
<Preference
|
||||||
|
android:icon="@drawable/ic_baseline_language_24"
|
||||||
|
android:key="@string/provider_lang_key"
|
||||||
|
android:title="@string/provider_lang_settings">
|
||||||
|
</Preference>
|
||||||
|
<Preference
|
||||||
|
android:key="@string/locale_key"
|
||||||
|
android:title="@string/app_language"
|
||||||
|
android:icon="@drawable/ic_baseline_language_24">
|
||||||
|
</Preference>
|
||||||
|
<Preference
|
||||||
|
android:key="@string/display_sub_key"
|
||||||
|
android:title="@string/display_subbed_dubbed_settings"
|
||||||
|
android:icon="@drawable/ic_outline_voice_over_off_24">
|
||||||
|
</Preference>
|
||||||
|
<SwitchPreference
|
||||||
|
android:key="@string/show_fillers_key"
|
||||||
|
android:icon="@drawable/ic_baseline_skip_next_24"
|
||||||
|
android:title="@string/show_fillers_settings"
|
||||||
|
android:defaultValue="true"/>
|
||||||
|
</PreferenceCategory>
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:key="search"
|
android:key="search"
|
||||||
android:title="@string/search"
|
android:title="@string/search"
|
||||||
|
@ -80,11 +106,6 @@
|
||||||
android:summary="@string/advanced_search_des"
|
android:summary="@string/advanced_search_des"
|
||||||
app:defaultValue="true"
|
app:defaultValue="true"
|
||||||
/>
|
/>
|
||||||
<Preference
|
|
||||||
android:key="@string/display_sub_key"
|
|
||||||
android:title="@string/display_subbed_dubbed_settings"
|
|
||||||
android:icon="@drawable/ic_outline_voice_over_off_24">
|
|
||||||
</Preference>
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
|
@ -98,16 +119,6 @@
|
||||||
android:icon="@drawable/ic_baseline_warning_24">
|
android:icon="@drawable/ic_baseline_warning_24">
|
||||||
</Preference>
|
</Preference>
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:key="@string/locale_key"
|
|
||||||
android:title="@string/app_language"
|
|
||||||
android:icon="@drawable/ic_baseline_language_24">
|
|
||||||
</Preference>
|
|
||||||
<SwitchPreference
|
|
||||||
android:key="@string/show_fillers_key"
|
|
||||||
android:icon="@drawable/ic_baseline_skip_next_24"
|
|
||||||
android:title="@string/show_fillers_settings"
|
|
||||||
android:defaultValue="true"/>
|
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:key="acra.disable"
|
android:key="acra.disable"
|
||||||
android:icon="@drawable/ic_baseline_bug_report_24"
|
android:icon="@drawable/ic_baseline_bug_report_24"
|
||||||
|
|
Loading…
Reference in a new issue