diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 857eaa6a..5ccef5c2 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -38,6 +38,7 @@ import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings import com.lagradost.cloudstream3.APIHolder.initAll import com.lagradost.cloudstream3.APIHolder.updateHasTrailers +import com.lagradost.cloudstream3.AcraApplication.Companion.context import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.CommonActivity.loadThemes @@ -58,6 +59,7 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStri import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringRepo import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringResumeWatching import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringSearch +import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.githubApi import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.inAppAuths import com.lagradost.cloudstream3.ui.APIRepository import com.lagradost.cloudstream3.ui.WatchType @@ -84,6 +86,8 @@ import com.lagradost.cloudstream3.utils.AppUtils.loadRepository import com.lagradost.cloudstream3.utils.AppUtils.loadResult import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus +import com.lagradost.cloudstream3.utils.BackupUtils.backupGithub +import com.lagradost.cloudstream3.utils.BackupUtils.restorePromptGithub import com.lagradost.cloudstream3.utils.BackupUtils.setUpBackup import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.DataStore.getKey @@ -443,6 +447,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { //private var mCastSession: CastSession? = null lateinit var mSessionManager: SessionManager private val mSessionManagerListener: SessionManagerListener by lazy { SessionManagerListenerImpl() } + private val accountsLoginLock = Mutex() private inner class SessionManagerListenerImpl : SessionManagerListener { override fun onSessionStarting(session: Session) { @@ -503,6 +508,10 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { } catch (e: Exception) { logError(e) } + val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) + if (githubApi.getLatestLoginData() != null && settingsManager.getBoolean(getString(R.string.automatic_cloud_backups), true)) { + this@MainActivity.backupGithub() + } } @@ -640,36 +649,52 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { } } - lateinit var viewModel: ResultViewModel2 +lateinit var viewModel: ResultViewModel2 - override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? { - viewModel = - ViewModelProvider(this)[ResultViewModel2::class.java] +override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? { + viewModel = + ViewModelProvider(this)[ResultViewModel2::class.java] - return super.onCreateView(name, context, attrs) - } + return super.onCreateView(name, context, attrs) +} - private fun hidePreviewPopupDialog() { - viewModel.clear() - bottomPreviewPopup.dismissSafe(this) - } +private fun hidePreviewPopupDialog() { + viewModel.clear() + bottomPreviewPopup.dismissSafe(this) +} - var bottomPreviewPopup: BottomSheetDialog? = null - private fun showPreviewPopupDialog(): BottomSheetDialog { - val ret = (bottomPreviewPopup ?: run { - val builder = - BottomSheetDialog(this) - builder.setContentView(R.layout.bottom_resultview_preview) - builder.setOnDismissListener { - bottomPreviewPopup = null - viewModel.clear() +var bottomPreviewPopup: BottomSheetDialog? = null +private fun showPreviewPopupDialog(): BottomSheetDialog { + val ret = (bottomPreviewPopup ?: run { + val builder = + BottomSheetDialog(this) + builder.setContentView(R.layout.bottom_resultview_preview) + builder.setOnDismissListener { + bottomPreviewPopup = null + viewModel.clear() + } + builder.setCanceledOnTouchOutside(true) + builder.show() + builder + }) + bottomPreviewPopup = ret + return ret +} + + override fun onStart() { + val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) + super.onStart() + ioSafe { + accountsLoginLock.withLock { + if (githubApi.getLatestLoginData() != null && settingsManager.getBoolean( + getString(R.string.automatic_cloud_backups), + true + ) + ) { + context?.restorePromptGithub() + } } - builder.setCanceledOnTouchOutside(true) - builder.show() - builder - }) - bottomPreviewPopup = ret - return ret + } } override fun onCreate(savedInstanceState: Bundle?) { @@ -845,15 +870,17 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { // init accounts ioSafe { - for (api in accountManagers) { - api.init() - } + accountsLoginLock.withLock{ + for (api in accountManagers) { + api.init() + } - inAppAuths.amap { api -> - try { - api.initialize() - } catch (e: Exception) { - logError(e) + inAppAuths.amap { api -> + try { + api.initialize() + } catch (e: Exception) { + logError(e) + } } } } @@ -1078,4 +1105,4 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { // } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt index f09bf8fe..7825ea77 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt @@ -11,6 +11,7 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI { val malApi = MALApi(0) val aniListApi = AniListApi(0) val openSubtitlesApi = OpenSubtitlesApi(0) + val githubApi = GithubApi(0) val indexSubtitlesApi = IndexSubtitleApi() val addic7ed = Addic7ed() @@ -23,7 +24,7 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI { // this needs init with context and can be accessed in settings val accountManagers get() = listOf( - malApi, aniListApi, openSubtitlesApi, //nginxApi + malApi, aniListApi, openSubtitlesApi, githubApi //nginxApi ) // used for active syncing diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/GithubApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/GithubApi.kt new file mode 100644 index 00000000..06137a57 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/GithubApi.kt @@ -0,0 +1,166 @@ +package com.lagradost.cloudstream3.syncproviders.providers + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.AcraApplication.Companion.context +import com.lagradost.cloudstream3.AcraApplication.Companion.getKey +import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey +import com.lagradost.cloudstream3.AcraApplication.Companion.setKey +import com.lagradost.cloudstream3.mvvm.logError +import com.lagradost.cloudstream3.syncproviders.AuthAPI +import com.lagradost.cloudstream3.syncproviders.InAppAuthAPI +import com.lagradost.cloudstream3.syncproviders.InAppAuthAPIManager +import com.lagradost.cloudstream3.utils.AppUtils.toJson +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import com.lagradost.cloudstream3.utils.BackupUtils.getBackup +import com.lagradost.cloudstream3.utils.BackupUtils.restorePromptGithub +import com.lagradost.cloudstream3.utils.Coroutines.ioSafe +import com.lagradost.nicehttp.RequestBodyTypes +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody.Companion.toRequestBody + + +class GithubApi(index: Int) : InAppAuthAPIManager(index){ + override val idPrefix = "Github" + override val name = "Github" + override val icon = R.drawable.ic_github_logo + override val requiresPassword = true + override val createAccountUrl = "https://github.com/settings/tokens/new?description=Cloudstream+Backup&scopes=gist" + + data class GithubOAuthEntity( + var gistId: String, + var token: String, + var userName: String, + var userAvatar: String, + ) + + companion object { + const val GITHUB_USER_KEY: String = "github_user" // user data like profile + var currentSession: GithubOAuthEntity? = null + } + + private fun getAuthKey(): GithubOAuthEntity? { + return getKey(accountId, GITHUB_USER_KEY) + } + + data class GistsElements ( + @JsonProperty("id") val gistId:String, + @JsonProperty("files") val files: Map, + @JsonProperty("owner") val owner: OwnerData + ) + data class OwnerData( + @JsonProperty("login") val userName: String, + @JsonProperty("avatar_url") val userAvatar : String + ) + data class File ( + @JsonProperty("content") val dataRaw: String? + ) + + data class GistRequestBody( + @JsonProperty("description") val description: String, + @JsonProperty("public") val public : Boolean, + @JsonProperty("files") val files: FilesGist? + ) + data class FilesGist( + @JsonProperty("Cloudstream_Backup_data.txt") val description: ContentFilesGist?, + ) + data class ContentFilesGist( + @JsonProperty("content") val description: String?, + ) + + private suspend fun initLogin(githubToken: String): Boolean{ + val response = app.get("https://api.github.com/gists", + headers= mapOf( + Pair("Accept" , "application/vnd.github+json"), + Pair("Authorization", "token $githubToken"), + ) + ) + + if (!response.isSuccessful) { return false } + + val repo = tryParseJson>(response.text)?.filter { + it.files.keys.first() == "Cloudstream_Backup_data.txt" + } + + if (repo?.isEmpty() == true){ + val backupData = context?.getBackup() + val gitResponse = app.post("https://api.github.com/gists", + headers= mapOf( + Pair("Accept" , "application/vnd.github+json"), + Pair("Authorization", "token $githubToken"), + ), + requestBody = GistRequestBody("Cloudstream private backup gist", false, FilesGist(ContentFilesGist(backupData?.toJson()))).toJson().toRequestBody( + RequestBodyTypes.JSON.toMediaTypeOrNull())) + + if (!gitResponse.isSuccessful) {return false} + tryParseJson(gitResponse.text).let { + setKey(accountId, GITHUB_USER_KEY, GithubOAuthEntity( + token = githubToken, + gistId = it?.gistId?: run { + return false + }, + userName = it.owner.userName, + userAvatar = it.owner.userAvatar + )) + } + return true + } + else{ + repo?.first().let { + setKey(accountId, GITHUB_USER_KEY, GithubOAuthEntity( + token = githubToken, + gistId = it?.gistId?: run { + return false + }, + userName = it.owner.userName, + userAvatar = it.owner.userAvatar + )) + ioSafe { + context?.restorePromptGithub() + } + return true + } + } + + } + override suspend fun login(data: InAppAuthAPI.LoginData): Boolean { + switchToNewAccount() + val githubToken = data.password ?: throw IllegalArgumentException ("Requires Password") + try { + if (initLogin(githubToken)) { + registerAccount() + return true + } + } catch (e: Exception) { + logError(e) + } + switchToOldAccount() + return false + } + + override fun getLatestLoginData(): InAppAuthAPI.LoginData? { + val current = getAuthKey() ?: return null + return InAppAuthAPI.LoginData(server = current.gistId, password = current.token, username = current.userName) + } + override suspend fun initialize() { + currentSession = getAuthKey() + val gistId = currentSession?.gistId ?: return + val token = currentSession?.token ?: return + setKey(gistId, token) + } + override fun logOut() { + removeKey(accountId, GITHUB_USER_KEY) + removeAccountKeys() + currentSession = getAuthKey() + } + + override fun loginInfo(): AuthAPI.LoginInfo? { + return getAuthKey()?.let { user -> + AuthAPI.LoginInfo( + profilePicture = user.userAvatar, + name = user.userName, + accountIndex = accountIndex, + ) + } + } +} diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/OpenSubtitlesApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/OpenSubtitlesApi.kt index 3e372c2d..64f5a145 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/OpenSubtitlesApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/OpenSubtitlesApi.kt @@ -126,8 +126,8 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi } override suspend fun login(data: InAppAuthAPI.LoginData): Boolean { - val username = data.username ?: throw ErrorLoadingException("Requires Username") - val password = data.password ?: throw ErrorLoadingException("Requires Password") + val username = data.username ?: throw IllegalArgumentException ("Requires Username") + val password = data.password ?: throw IllegalArgumentException ("Requires Password") switchToNewAccount() try { if (initLogin(username, password)) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt index 8a8f90b4..68298370 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt @@ -93,6 +93,7 @@ class HomeFragment : Fragment() { val configEvent = Event() var currentSpan = 1 val listHomepageItems = mutableListOf() + val reloadStoredDataEvent = Event() private val errorProfilePics = listOf( R.drawable.monke_benene, @@ -477,15 +478,21 @@ class HomeFragment : Fragment() { bookmarksUpdatedEvent += ::bookmarksUpdated afterPluginsLoadedEvent += ::afterPluginsLoaded mainPluginsLoadedEvent += ::afterMainPluginsLoaded + reloadStoredDataEvent += ::reloadStoredEvent } override fun onStop() { bookmarksUpdatedEvent -= ::bookmarksUpdated afterPluginsLoadedEvent -= ::afterPluginsLoaded mainPluginsLoadedEvent -= ::afterMainPluginsLoaded + reloadStoredDataEvent -= ::reloadStoredEvent super.onStop() } + private fun reloadStoredEvent(input: Unit) { + reloadStored() + } + private fun reloadStored() { homeViewModel.loadResumeWatching() val list = EnumSet.noneOf(WatchType::class.java) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt index f9627e46..66260d31 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt @@ -20,6 +20,7 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.openSubtitlesApi +import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.githubApi import com.lagradost.cloudstream3.syncproviders.AuthAPI import com.lagradost.cloudstream3.syncproviders.InAppAuthAPI import com.lagradost.cloudstream3.syncproviders.OAuth2API @@ -227,6 +228,8 @@ class SettingsAccount : PreferenceFragmentCompat() { R.string.mal_key to malApi, R.string.anilist_key to aniListApi, R.string.opensubtitles_key to openSubtitlesApi, + R.string.github_key to githubApi + ) for ((key, api) in syncApis) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt index f9ac3fee..39348748 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt @@ -19,6 +19,7 @@ import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPadd import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar import com.lagradost.cloudstream3.utils.BackupUtils.backup import com.lagradost.cloudstream3.utils.BackupUtils.restorePrompt +import com.lagradost.cloudstream3.utils.BackupUtils.restorePromptGithub import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog @@ -31,6 +32,7 @@ import java.io.BufferedReader import java.io.InputStreamReader import java.io.OutputStream + class SettingsUpdates : PreferenceFragmentCompat() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt index 80e5d64a..892defef 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt @@ -14,14 +14,18 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.module.kotlin.readValue import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.plugins.PLUGINS_KEY import com.lagradost.cloudstream3.plugins.PLUGINS_KEY_LOCAL +import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.githubApi import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_CACHED_LIST import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_SHOULD_UPDATE_LIST import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_TOKEN_KEY import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_UNIXTIME_KEY import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_USER_KEY +import com.lagradost.cloudstream3.syncproviders.providers.GithubApi +import com.lagradost.cloudstream3.syncproviders.providers.GithubApi.Companion.GITHUB_USER_KEY import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_CACHED_LIST import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_REFRESH_TOKEN_KEY import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_SHOULD_UPDATE_LIST @@ -29,6 +33,11 @@ import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_T import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_UNIXTIME_KEY import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_USER_KEY import com.lagradost.cloudstream3.syncproviders.providers.OpenSubtitlesApi.Companion.OPEN_SUBTITLES_USER_KEY +import com.lagradost.cloudstream3.ui.home.HomeFragment +import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import com.lagradost.cloudstream3.utils.AppUtils.toJson + +import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.DataStore.getDefaultSharedPrefs import com.lagradost.cloudstream3.utils.DataStore.getSharedPrefs import com.lagradost.cloudstream3.utils.DataStore.mapper @@ -37,12 +46,14 @@ import com.lagradost.cloudstream3.utils.UIHelper.checkWrite import com.lagradost.cloudstream3.utils.UIHelper.requestRW import com.lagradost.cloudstream3.utils.VideoDownloadManager.getBasePath import com.lagradost.cloudstream3.utils.VideoDownloadManager.isDownloadDir +import com.lagradost.nicehttp.RequestBodyTypes +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody.Companion.toRequestBody import java.io.IOException import java.io.PrintWriter import java.lang.System.currentTimeMillis import java.text.SimpleDateFormat import java.util.* - object BackupUtils { /** @@ -65,14 +76,15 @@ object BackupUtils { // The plugins themselves are not backed up PLUGINS_KEY, PLUGINS_KEY_LOCAL, - + GITHUB_USER_KEY, OPEN_SUBTITLES_USER_KEY, "nginx_user", // Nginx user key + ) /** false if blacklisted key */ private fun String.isTransferable(): Boolean { - return !nonTransferableKeys.contains(this) + return !nonTransferableKeys.contains(this) and !nonTransferableKeys.any { this.endsWith(it) } } private var restoreFileSelector: ActivityResultLauncher>? = null @@ -286,4 +298,46 @@ object BackupUtils { setKeyRaw(it.key, it.value, isEditingAppSettings) } } -} \ No newline at end of file + + + fun FragmentActivity.backupGithub(){ + val backup = this.getBackup().toJson() + + val gistId = githubApi.getLatestLoginData()?.server ?: throw IllegalArgumentException ("Requires Username") + val token = githubApi.getLatestLoginData()?.password ?: throw IllegalArgumentException ("Requires Username") + + ioSafe { + app.patch("https://api.github.com/gists/$gistId", + headers= mapOf( + Pair("Accept" , "application/vnd.github+json"), + Pair("Authorization", "token $token"), + ), + requestBody = GithubApi.GistRequestBody( + "Cloudstream private backup gist", + false, + GithubApi.FilesGist(GithubApi.ContentFilesGist(backup))) + .toJson() + .toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull()) + ) + } + showToast( + this, + R.string.backup_success, + Toast.LENGTH_LONG + ) + } + suspend fun Context.restorePromptGithub() { + val gistId = githubApi.getLatestLoginData()?.server ?: throw IllegalAccessException() + val jsonData = app.get("https://api.github.com/gists/$gistId").text + val dataRaw = + parseJson(jsonData ?: "").files.values.first().dataRaw + ?: throw IllegalAccessException() + val data = parseJson(dataRaw) + restore( + data, + restoreSettings = true, + restoreDataStore = true + ) + HomeFragment.reloadStoredDataEvent.invoke(Unit) + } +} diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index f27f7c13..b0863ae5 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -141,6 +141,8 @@ Sincronizza automaticamente gli episodi guardati Ripristinare i dati da backup + Backup automatico su Github + Backup data File di backup caricato Impossibile ripristinare i dati dal file %s @@ -343,7 +345,7 @@ Nessun ritardo dei sottotitoli @@ -507,4 +509,4 @@ L\'app verrà aggiornata all\'uscita Aggiornamento avviato Plugin scaricato - \ No newline at end of file + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6c10d865..59ad0ebd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -61,6 +61,8 @@ pref_filter_search_quality_key enable_nsfw_on_providers_key enable_skip_op_from_database + automatic_cloud_backups + %d %s | %s %s • %s @@ -222,7 +224,12 @@ Update watch progress Automatically sync your current episode progress Restore data from backup - Back up data + Automatic backup from Github + + Cloud Backup deactivated + Cloud Backup enabled + + Backup data Loaded backup file Failed to restore data from file %s Data stored @@ -428,6 +435,7 @@ anilist_key mal_key opensubtitles_key + github_key nginx_key password123 MyCoolUsername @@ -479,7 +487,7 @@ No subtitle delay diff --git a/app/src/main/res/xml/settings_account.xml b/app/src/main/res/xml/settings_account.xml index d4bae8c4..754f895c 100644 --- a/app/src/main/res/xml/settings_account.xml +++ b/app/src/main/res/xml/settings_account.xml @@ -1,27 +1,30 @@ + xmlns:app="http://schemas.android.com/apk/res-auto"> + android:key="@string/mal_key" + android:icon="@drawable/mal_logo" /> + android:key="@string/anilist_key" + android:icon="@drawable/ic_anilist_icon" /> - - - + android:key="@string/opensubtitles_key" + android:icon="@drawable/open_subtitles_icon" /> + + + + - - - - - - - - + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/settings_updates.xml b/app/src/main/res/xml/settings_updates.xml index ba098c8c..29a7ad02 100644 --- a/app/src/main/res/xml/settings_updates.xml +++ b/app/src/main/res/xml/settings_updates.xml @@ -1,6 +1,34 @@ + + + + + + + + + - - - - + - - \ No newline at end of file +