feat: add remote sync capability - update scheduling logic

This commit is contained in:
Martin Filo 2023-04-05 00:47:22 +02:00
parent d50fbe566c
commit 5ec443916b
15 changed files with 148 additions and 55 deletions

View file

@ -1,36 +1,103 @@
package com.lagradost.cloudstream3.syncproviders package com.lagradost.cloudstream3.syncproviders
import android.content.Context import android.content.Context
import android.content.SharedPreferences
import android.os.Handler
import android.os.Looper
import android.util.Log
import com.lagradost.cloudstream3.mvvm.launchSafe import com.lagradost.cloudstream3.mvvm.launchSafe
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
interface BackupAPI<LOGIN_DATA> { interface BackupAPI<LOGIN_DATA> {
private companion object { companion object {
val DEBOUNCE_TIME_MS = 15.seconds val UPLOAD_THROTTLE = 10.seconds
val DOWNLOAD_THROTTLE = 60.seconds
fun createBackupScheduler() = Scheduler<Pair<String, Boolean>>(
UPLOAD_THROTTLE.inWholeMilliseconds
) { input ->
if (input == null) {
throw IllegalStateException()
}
AccountManager.BackupApis.forEach { it.addToQueue(input.first, input.second) }
}
fun SharedPreferences.attachListener(isSettings: Boolean = true): Pair<SharedPreferences, Scheduler<Pair<String, Boolean>>> {
val scheduler = createBackupScheduler()
registerOnSharedPreferenceChangeListener { _, key ->
scheduler.work(Pair(key, isSettings))
}
return Pair(
this,
scheduler
)
}
} }
fun downloadSyncData() fun downloadSyncData()
fun uploadSyncData() fun uploadSyncData()
fun shouldUpdate(): Boolean fun shouldUpdate(changedKey: String, isSettings: Boolean): Boolean
fun Context.mergeBackup(incomingData: String) fun Context.mergeBackup(incomingData: String)
fun Context.createBackup(loginData: LOGIN_DATA) fun Context.createBackup(loginData: LOGIN_DATA)
var uploadJob: Job? var uploadJob: Job?
fun addToQueue() { fun addToQueue(changedKey: String, isSettings: Boolean) {
if (!shouldUpdate()) { if (!shouldUpdate(changedKey, isSettings)) {
Log.d("SYNC_API", "upload not required, data is same")
return return
} }
if (uploadJob != null && uploadJob!!.isActive) {
Log.d("SYNC_API", "upload is canceled, scheduling new")
uploadJob?.cancel() uploadJob?.cancel()
}
// we should ensure job will before app is closed
uploadJob = CoroutineScope(Dispatchers.IO).launchSafe { uploadJob = CoroutineScope(Dispatchers.IO).launchSafe {
delay(DEBOUNCE_TIME_MS) Log.d("SYNC_API", "upload is running now")
uploadSyncData() uploadSyncData()
} }
} }
class Scheduler<INPUT>(
private val throttleTimeMs: Long,
private val onWork: (INPUT?) -> Unit
) {
private companion object {
var SCHEDULER_ID = 1
}
private val id = SCHEDULER_ID++
private val handler = Handler(Looper.getMainLooper())
private var runnable: Runnable? = null
fun work(input: INPUT? = null) {
Log.d("SYNC_API", "[$id] wants to schedule")
throttle(input)
}
fun stop() {
runnable?.let {
handler.removeCallbacks(it)
runnable = null
}
}
private fun throttle(input: INPUT?) {
stop()
runnable = Runnable {
Log.d("SYNC_API", "[$id] schedule success")
onWork(input)
}
handler.postDelayed(runnable!!, throttleTimeMs)
}
}
} }

View file

@ -2,6 +2,7 @@ package com.lagradost.cloudstream3.syncproviders.providers
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import android.util.Log
import androidx.browser.customtabs.CustomTabsIntent import androidx.browser.customtabs.CustomTabsIntent
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.readValue
@ -19,7 +20,6 @@ import com.google.api.services.drive.model.File
import com.lagradost.cloudstream3.AcraApplication import com.lagradost.cloudstream3.AcraApplication
import com.lagradost.cloudstream3.CommonActivity import com.lagradost.cloudstream3.CommonActivity
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.launchSafe
import com.lagradost.cloudstream3.syncproviders.AuthAPI import com.lagradost.cloudstream3.syncproviders.AuthAPI
import com.lagradost.cloudstream3.syncproviders.BackupAPI import com.lagradost.cloudstream3.syncproviders.BackupAPI
import com.lagradost.cloudstream3.syncproviders.InAppOAuth2API import com.lagradost.cloudstream3.syncproviders.InAppOAuth2API
@ -29,11 +29,9 @@ import com.lagradost.cloudstream3.utils.BackupUtils
import com.lagradost.cloudstream3.utils.BackupUtils.getBackup import com.lagradost.cloudstream3.utils.BackupUtils.getBackup
import com.lagradost.cloudstream3.utils.BackupUtils.restore import com.lagradost.cloudstream3.utils.BackupUtils.restore
import com.lagradost.cloudstream3.utils.DataStore import com.lagradost.cloudstream3.utils.DataStore
import com.lagradost.cloudstream3.utils.DataStore.getSharedPrefs
import com.lagradost.cloudstream3.utils.DataStore.removeKey import com.lagradost.cloudstream3.utils.DataStore.removeKey
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import java.io.InputStream import java.io.InputStream
import java.util.* import java.util.*
@ -68,8 +66,6 @@ class GoogleDriveApi(index: Int) :
var tempAuthFlow: AuthorizationCodeFlow? = null var tempAuthFlow: AuthorizationCodeFlow? = null
var lastBackupJson: String? = null var lastBackupJson: String? = null
var continuousDownloadJob: Job? = null
///////////////////////////////////////// /////////////////////////////////////////
///////////////////////////////////////// /////////////////////////////////////////
// OAuth2API implementation // OAuth2API implementation
@ -104,7 +100,7 @@ class GoogleDriveApi(index: Int) :
) )
storeValue(K.TOKEN, googleTokenResponse) storeValue(K.TOKEN, googleTokenResponse)
startContinuousDownload() runDownloader()
tempAuthFlow = null tempAuthFlow = null
return true return true
@ -118,22 +114,7 @@ class GoogleDriveApi(index: Int) :
return return
} }
startContinuousDownload() runDownloader()
}
private fun startContinuousDownload() {
continuousDownloadJob?.cancel()
continuousDownloadJob = CoroutineScope(Dispatchers.IO).launchSafe {
if (uploadJob?.isActive == true) {
uploadJob!!.invokeOnCompletion {
startContinuousDownload()
}
} else {
downloadSyncData()
delay(1000 * 60)
startContinuousDownload()
}
}
} }
override fun loginInfo(): AuthAPI.LoginInfo? { override fun loginInfo(): AuthAPI.LoginInfo? {
@ -192,13 +173,11 @@ class GoogleDriveApi(index: Int) :
removeKey(it) removeKey(it)
} }
restore( restore(
newData, newData,
restoreSettings = true, restoreSettings = true,
restoreDataStore = true restoreDataStore = true
) )
} }
// 🤮 // 🤮
@ -227,6 +206,7 @@ class GoogleDriveApi(index: Int) :
val drive = getDriveService()!! val drive = getDriveService()!!
val fileName = loginData.fileName val fileName = loginData.fileName
val syncFileId = loginData.syncFileId
val ioFile = java.io.File(AcraApplication.context?.cacheDir, fileName) val ioFile = java.io.File(AcraApplication.context?.cacheDir, fileName)
lastBackupJson = getBackup().toJson() lastBackupJson = getBackup().toJson()
ioFile.writeText(lastBackupJson!!) ioFile.writeText(lastBackupJson!!)
@ -250,8 +230,11 @@ class GoogleDriveApi(index: Int) :
loginData.syncFileId = file.id loginData.syncFileId = file.id
} }
// in case we had to create new file
if (syncFileId != loginData.syncFileId) {
storeValue(K.LOGIN_DATA, loginData) storeValue(K.LOGIN_DATA, loginData)
} }
}
override fun downloadSyncData() { override fun downloadSyncData() {
val ctx = AcraApplication.context ?: return val ctx = AcraApplication.context ?: return
@ -273,11 +256,13 @@ class GoogleDriveApi(index: Int) :
try { try {
val inputStream: InputStream = existingFile.executeMediaAsInputStream() val inputStream: InputStream = existingFile.executeMediaAsInputStream()
val content: String = inputStream.bufferedReader().use { it.readText() } val content: String = inputStream.bufferedReader().use { it.readText() }
Log.d("SYNC_API", "downloadSyncData merging")
ctx.mergeBackup(content) ctx.mergeBackup(content)
return return
} catch (_: Exception) { } catch (_: Exception) {
} }
} else { } else {
Log.d("SYNC_API", "downloadSyncData file not exists")
uploadSyncData() uploadSyncData()
} }
} }
@ -310,12 +295,14 @@ class GoogleDriveApi(index: Int) :
override fun uploadSyncData() { override fun uploadSyncData() {
val ctx = AcraApplication.context ?: return val ctx = AcraApplication.context ?: return
val loginData = getLatestLoginData() ?: return val loginData = getLatestLoginData() ?: return
Log.d("SYNC_API", "uploadSyncData createBackup")
ctx.createBackup(loginData) ctx.createBackup(loginData)
} }
override fun shouldUpdate(): Boolean { override fun shouldUpdate(changedKey: String, isSettings: Boolean): Boolean {
val ctx = AcraApplication.context ?: return false val ctx = AcraApplication.context ?: return false
// would be smarter to check properties, but its called once in UPLOAD_THROTTLE seconds
val newBackup = ctx.getBackup().toJson() val newBackup = ctx.getBackup().toJson()
return lastBackupJson != newBackup return lastBackupJson != newBackup
} }
@ -335,6 +322,25 @@ class GoogleDriveApi(index: Int) :
///////////////////////////////////////// /////////////////////////////////////////
///////////////////////////////////////// /////////////////////////////////////////
// Internal // Internal
private val continuousDownloader = BackupAPI.Scheduler<Unit>(
BackupAPI.DOWNLOAD_THROTTLE.inWholeMilliseconds
) {
if (uploadJob?.isActive == true) {
uploadJob!!.invokeOnCompletion {
Log.d("SYNC_API", "upload is running, reschedule download")
runDownloader()
}
} else {
Log.d("SYNC_API", "downloadSyncData will run")
downloadSyncData()
runDownloader()
}
}
private fun runDownloader() {
continuousDownloader.work()
}
private fun getCredentialsFromStore(): Credential? { private fun getCredentialsFromStore(): Credential? {
val LOGIN_DATA = getLatestLoginData() val LOGIN_DATA = getLatestLoginData()
val TOKEN = getValue<TokenResponse>(K.TOKEN) val TOKEN = getValue<TokenResponse>(K.TOKEN)

View file

@ -28,6 +28,7 @@ import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.mvvm.* import com.lagradost.cloudstream3.mvvm.*
import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.subtitleProviders import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.subtitleProviders
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.attachListener
import com.lagradost.cloudstream3.ui.player.CS3IPlayer.Companion.preferredAudioTrackLanguage import com.lagradost.cloudstream3.ui.player.CS3IPlayer.Companion.preferredAudioTrackLanguage
import com.lagradost.cloudstream3.ui.player.CustomDecoder.Companion.updateForcedEncoding import com.lagradost.cloudstream3.ui.player.CustomDecoder.Companion.updateForcedEncoding
import com.lagradost.cloudstream3.ui.player.PlayerSubtitleHelper.Companion.toSubtitleMimeType import com.lagradost.cloudstream3.ui.player.PlayerSubtitleHelper.Companion.toSubtitleMimeType
@ -664,7 +665,7 @@ class GeneratorPlayer : FullScreenPlayer() {
} }
sourceDialog.subtitles_click_settings?.setOnClickListener { sourceDialog.subtitles_click_settings?.setOnClickListener {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx) val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx).attachListener().first
val prefNames = ctx.resources.getStringArray(R.array.subtitles_encoding_list) val prefNames = ctx.resources.getStringArray(R.array.subtitles_encoding_list)
val prefValues = ctx.resources.getStringArray(R.array.subtitles_encoding_values) val prefValues = ctx.resources.getStringArray(R.array.subtitles_encoding_values)

View file

@ -25,6 +25,7 @@ import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.network.initClient import com.lagradost.cloudstream3.network.initClient
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.attachListener
import com.lagradost.cloudstream3.ui.EasterEggMonke import com.lagradost.cloudstream3.ui.EasterEggMonke
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.setPaddingBottom import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
@ -137,20 +138,26 @@ class SettingsGeneral : PreferenceFragmentCompat() {
// Stores the real URI using download_path_key // Stores the real URI using download_path_key
// Important that the URI is stored instead of filepath due to permissions. // Important that the URI is stored instead of filepath due to permissions.
PreferenceManager.getDefaultSharedPreferences(context) PreferenceManager.getDefaultSharedPreferences(context)
.edit().putString(getString(R.string.download_path_key), uri.toString()).apply() .attachListener().first
.edit()
.putString(getString(R.string.download_path_key), uri.toString())
.apply()
// From URI -> File path // From URI -> File path
// File path here is purely for cosmetic purposes in settings // File path here is purely for cosmetic purposes in settings
(file.filePath ?: uri.toString()).let { (file.filePath ?: uri.toString()).let {
PreferenceManager.getDefaultSharedPreferences(context) PreferenceManager.getDefaultSharedPreferences(context)
.edit().putString(getString(R.string.download_path_pref), it).apply() .attachListener().first
.edit()
.putString(getString(R.string.download_path_pref), it)
.apply()
} }
} }
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
hideKeyboard() hideKeyboard()
setPreferencesFromResource(R.xml.settins_general, rootKey) setPreferencesFromResource(R.xml.settins_general, rootKey)
val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext()) val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext()).attachListener().first
fun getCurrent(): MutableList<CustomSite> { fun getCurrent(): MutableList<CustomSite> {
return getKey<Array<CustomSite>>(USER_PROVIDER_API)?.toMutableList() return getKey<Array<CustomSite>>(USER_PROVIDER_API)?.toMutableList()

View file

@ -7,6 +7,7 @@ import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.attachListener
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getFolderSize import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getFolderSize
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.setPaddingBottom import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
@ -27,7 +28,7 @@ class SettingsPlayer : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
hideKeyboard() hideKeyboard()
setPreferencesFromResource(R.xml.settings_player, rootKey) setPreferencesFromResource(R.xml.settings_player, rootKey)
val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext()) val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext()).attachListener().first
getPref(R.string.video_buffer_length_key)?.setOnPreferenceClickListener { getPref(R.string.video_buffer_length_key)?.setOnPreferenceClickListener {
val prefNames = resources.getStringArray(R.array.video_buffer_length_names) val prefNames = resources.getStringArray(R.array.video_buffer_length_names)

View file

@ -10,6 +10,7 @@ import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.attachListener
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.setPaddingBottom import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
@ -30,7 +31,7 @@ class SettingsProviders : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
hideKeyboard() hideKeyboard()
setPreferencesFromResource(R.xml.settings_providers, rootKey) setPreferencesFromResource(R.xml.settings_providers, rootKey)
val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext()) val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext()).attachListener().first
getPref(R.string.display_sub_key)?.setOnPreferenceClickListener { getPref(R.string.display_sub_key)?.setOnPreferenceClickListener {
activity?.getApiDubstatusSettings()?.let { current -> activity?.getApiDubstatusSettings()?.let { current ->

View file

@ -8,6 +8,7 @@ import androidx.preference.PreferenceManager
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.SearchQuality import com.lagradost.cloudstream3.SearchQuality
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.attachListener
import com.lagradost.cloudstream3.ui.search.SearchResultBuilder import com.lagradost.cloudstream3.ui.search.SearchResultBuilder
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.setPaddingBottom import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
@ -28,7 +29,7 @@ class SettingsUI : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
hideKeyboard() hideKeyboard()
setPreferencesFromResource(R.xml.settins_ui, rootKey) setPreferencesFromResource(R.xml.settins_ui, rootKey)
val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext()) val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext()).attachListener().first
getPref(R.string.poster_ui_key)?.setOnPreferenceClickListener { getPref(R.string.poster_ui_key)?.setOnPreferenceClickListener {
val prefNames = resources.getStringArray(R.array.poster_ui_options) val prefNames = resources.getStringArray(R.array.poster_ui_options)

View file

@ -14,6 +14,7 @@ import androidx.preference.PreferenceManager
import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.attachListener
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.setPaddingBottom import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
@ -126,7 +127,7 @@ class SettingsUpdates : PreferenceFragmentCompat() {
} }
getPref(R.string.apk_installer_key)?.setOnPreferenceClickListener { getPref(R.string.apk_installer_key)?.setOnPreferenceClickListener {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(it.context) val settingsManager = PreferenceManager.getDefaultSharedPreferences(it.context).attachListener().first
val prefNames = resources.getStringArray(R.array.apk_installer_pref) val prefNames = resources.getStringArray(R.array.apk_installer_pref)
val prefValues = resources.getIntArray(R.array.apk_installer_values) val prefValues = resources.getIntArray(R.array.apk_installer_values)

View file

@ -15,6 +15,7 @@ import com.lagradost.cloudstream3.CommonActivity
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.plugins.PluginManager import com.lagradost.cloudstream3.plugins.PluginManager
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.attachListener
import com.lagradost.cloudstream3.ui.settings.appLanguages import com.lagradost.cloudstream3.ui.settings.appLanguages
import com.lagradost.cloudstream3.ui.settings.getCurrentLocale import com.lagradost.cloudstream3.ui.settings.getCurrentLocale
import com.lagradost.cloudstream3.utils.SubtitleHelper import com.lagradost.cloudstream3.utils.SubtitleHelper
@ -42,7 +43,7 @@ class SetupFragmentLanguage : Fragment() {
normalSafeApiCall { normalSafeApiCall {
with(context) { with(context) {
if (this == null) return@normalSafeApiCall if (this == null) return@normalSafeApiCall
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) val settingsManager = PreferenceManager.getDefaultSharedPreferences(this).attachListener().first
val arrayAdapter = val arrayAdapter =
ArrayAdapter<String>(this, R.layout.sort_bottom_single_choice) ArrayAdapter<String>(this, R.layout.sort_bottom_single_choice)

View file

@ -10,6 +10,7 @@ import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController 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.syncproviders.BackupAPI.Companion.attachListener
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import kotlinx.android.synthetic.main.fragment_setup_layout.* import kotlinx.android.synthetic.main.fragment_setup_layout.*
import kotlinx.android.synthetic.main.fragment_setup_media.listview1 import kotlinx.android.synthetic.main.fragment_setup_media.listview1
@ -33,7 +34,7 @@ class SetupFragmentLayout : Fragment() {
with(context) { with(context) {
if (this == null) return if (this == null) return
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) val settingsManager = PreferenceManager.getDefaultSharedPreferences(this).attachListener().first
val prefNames = resources.getStringArray(R.array.app_layout) val prefNames = resources.getStringArray(R.array.app_layout)
val prefValues = resources.getIntArray(R.array.app_layout_values) val prefValues = resources.getIntArray(R.array.app_layout_values)

View file

@ -12,6 +12,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.TvType import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.attachListener
import com.lagradost.cloudstream3.utils.DataStore.removeKey import com.lagradost.cloudstream3.utils.DataStore.removeKey
import com.lagradost.cloudstream3.utils.USER_SELECTED_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
@ -32,7 +33,7 @@ class SetupFragmentMedia : Fragment() {
with(context) { with(context) {
if (this == null) return if (this == null) return
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) val settingsManager = PreferenceManager.getDefaultSharedPreferences(this).attachListener().first
val arrayAdapter = val arrayAdapter =
ArrayAdapter<String>(this, R.layout.sort_bottom_single_choice) ArrayAdapter<String>(this, R.layout.sort_bottom_single_choice)

View file

@ -14,6 +14,7 @@ import com.lagradost.cloudstream3.APIHolder
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
import com.lagradost.cloudstream3.AllLanguagesName import com.lagradost.cloudstream3.AllLanguagesName
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.attachListener
import com.lagradost.cloudstream3.utils.SubtitleHelper import com.lagradost.cloudstream3.utils.SubtitleHelper
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.*
@ -33,7 +34,7 @@ class SetupFragmentProviderLanguage : Fragment() {
with(context) { with(context) {
if (this == null) return if (this == null) return
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) val settingsManager = PreferenceManager.getDefaultSharedPreferences(this).attachListener().first
val arrayAdapter = val arrayAdapter =
ArrayAdapter<String>(this, R.layout.sort_bottom_single_choice) ArrayAdapter<String>(this, R.layout.sort_bottom_single_choice)

View file

@ -27,6 +27,7 @@ import com.lagradost.cloudstream3.CommonActivity.onColorSelectedEvent
import com.lagradost.cloudstream3.CommonActivity.onDialogDismissedEvent import com.lagradost.cloudstream3.CommonActivity.onDialogDismissedEvent
import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.attachListener
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.DataStore.setKey
import com.lagradost.cloudstream3.utils.Event import com.lagradost.cloudstream3.utils.Event
@ -447,6 +448,7 @@ class SubtitlesFragment : Fragment() {
subtitles_filter_sub_lang?.setOnCheckedChangeListener { _, b -> subtitles_filter_sub_lang?.setOnCheckedChangeListener { _, b ->
context?.let { ctx -> context?.let { ctx ->
PreferenceManager.getDefaultSharedPreferences(ctx) PreferenceManager.getDefaultSharedPreferences(ctx)
.attachListener().first
.edit() .edit()
.putBoolean(getString(R.string.filter_sub_lang_key), b) .putBoolean(getString(R.string.filter_sub_lang_key), b)
.apply() .apply()

View file

@ -7,7 +7,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.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.syncproviders.AccountManager import com.lagradost.cloudstream3.syncproviders.BackupAPI
const val DOWNLOAD_HEADER_CACHE = "download_header_cache" const val DOWNLOAD_HEADER_CACHE = "download_header_cache"
@ -25,6 +25,8 @@ object DataStore {
.configure(DeserializationFeature.USE_LONG_FOR_INTS, true) .configure(DeserializationFeature.USE_LONG_FOR_INTS, true)
.build() .build()
private val backupScheduler = BackupAPI.createBackupScheduler()
private fun getPreferences(context: Context): SharedPreferences { private fun getPreferences(context: Context): SharedPreferences {
return context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE) return context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE)
} }
@ -83,8 +85,7 @@ object DataStore {
val editor: SharedPreferences.Editor = prefs.edit() val editor: SharedPreferences.Editor = prefs.edit()
editor.remove(path) editor.remove(path)
editor.apply() editor.apply()
backupScheduler.work(Pair(path, false))
AccountManager.BackupApis.forEach { it.addToQueue() }
} }
} catch (e: Exception) { } catch (e: Exception) {
logError(e) logError(e)
@ -104,8 +105,7 @@ object DataStore {
val editor: SharedPreferences.Editor = getSharedPrefs().edit() val editor: SharedPreferences.Editor = getSharedPrefs().edit()
editor.putString(path, mapper.writeValueAsString(value)) editor.putString(path, mapper.writeValueAsString(value))
editor.apply() editor.apply()
backupScheduler.work(Pair(path, false))
AccountManager.BackupApis.forEach { it.addToQueue() }
} catch (e: Exception) { } catch (e: Exception) {
logError(e) logError(e)
} }
@ -115,6 +115,7 @@ object DataStore {
setKey(getFolderName(folder, path), value) setKey(getFolderName(folder, path), value)
} }
inline fun <reified T : Any> String.toKotlinObject(): T { inline fun <reified T : Any> String.toKotlinObject(): T {
return mapper.readValue(this, T::class.java) return mapper.readValue(this, T::class.java)
} }

View file

@ -23,6 +23,7 @@ import okio.buffer
import okio.sink import okio.sink
import java.io.File import java.io.File
import android.text.TextUtils import android.text.TextUtils
import com.lagradost.cloudstream3.syncproviders.BackupAPI.Companion.attachListener
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
import java.io.BufferedReader import java.io.BufferedReader
import java.io.IOException import java.io.IOException
@ -73,7 +74,7 @@ class InAppUpdater {
private suspend fun Activity.getAppUpdate(): Update { private suspend fun Activity.getAppUpdate(): Update {
return try { return try {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) val settingsManager = PreferenceManager.getDefaultSharedPreferences(this).attachListener().first
if (settingsManager.getBoolean( if (settingsManager.getBoolean(
getString(R.string.prerelease_update_key), getString(R.string.prerelease_update_key),
resources.getBoolean(R.bool.is_prerelease) resources.getBoolean(R.bool.is_prerelease)
@ -254,7 +255,7 @@ class InAppUpdater {
* @param checkAutoUpdate if the update check was launched automatically * @param checkAutoUpdate if the update check was launched automatically
**/ **/
suspend fun Activity.runAutoUpdate(checkAutoUpdate: Boolean = true): Boolean { suspend fun Activity.runAutoUpdate(checkAutoUpdate: Boolean = true): Boolean {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) val settingsManager = PreferenceManager.getDefaultSharedPreferences(this).attachListener().first
if (!checkAutoUpdate || settingsManager.getBoolean( if (!checkAutoUpdate || settingsManager.getBoolean(
getString(R.string.auto_update_key), getString(R.string.auto_update_key),