mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
Backup App and Source settings
Also added PreferenceStore/TrackPreferences and made the track username/password private Updated kotlinSerialization to 1.6.0 Co-Authored-By: jmir1 <43830312+jmir1@users.noreply.github.com> Co-Authored-By: arkon <4098258+arkon@users.noreply.github.com>
This commit is contained in:
parent
cbd6003aba
commit
106f6f52c0
29 changed files with 675 additions and 78 deletions
|
@ -203,7 +203,7 @@ dependencies {
|
|||
implementation(kotlin("reflect", version = AndroidVersions.kotlin))
|
||||
|
||||
// JSON
|
||||
val kotlinSerialization = "1.5.1"
|
||||
val kotlinSerialization = "1.6.0"
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:${kotlinSerialization}")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:${kotlinSerialization}")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-okio:${kotlinSerialization}")
|
||||
|
|
|
@ -7,8 +7,11 @@ import eu.kanade.tachiyomi.data.cache.CoverCache
|
|||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
import eu.kanade.tachiyomi.data.library.CustomMangaManager
|
||||
import eu.kanade.tachiyomi.data.preference.AndroidPreferenceStore
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceStore
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.data.track.TrackPreferences
|
||||
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||
import eu.kanade.tachiyomi.network.JavaScriptEngine
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
|
@ -27,8 +30,14 @@ class AppModule(val app: Application) : InjektModule {
|
|||
override fun InjektRegistrar.registerInjectables() {
|
||||
addSingleton(app)
|
||||
|
||||
addSingletonFactory<PreferenceStore> {
|
||||
AndroidPreferenceStore(app)
|
||||
}
|
||||
|
||||
addSingletonFactory { PreferencesHelper(app) }
|
||||
|
||||
addSingletonFactory { TrackPreferences(get()) }
|
||||
|
||||
addSingletonFactory { DatabaseHelper(app) }
|
||||
|
||||
addSingletonFactory { ChapterCache(app) }
|
||||
|
|
|
@ -5,7 +5,9 @@ import androidx.preference.PreferenceManager
|
|||
import eu.kanade.tachiyomi.data.backup.BackupCreatorJob
|
||||
import eu.kanade.tachiyomi.data.download.DownloadProvider
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
||||
import eu.kanade.tachiyomi.data.preference.Preference
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceKeys
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceStore
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceValues
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.plusAssign
|
||||
|
@ -33,7 +35,11 @@ object Migrations {
|
|||
* @param preferences Preferences of the application.
|
||||
* @return true if a migration is performed, false otherwise.
|
||||
*/
|
||||
fun upgrade(preferences: PreferencesHelper, scope: CoroutineScope): Boolean {
|
||||
fun upgrade(
|
||||
preferences: PreferencesHelper,
|
||||
preferenceStore: PreferenceStore,
|
||||
scope: CoroutineScope,
|
||||
): Boolean {
|
||||
val context = preferences.context
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
prefs.edit {
|
||||
|
@ -220,6 +226,19 @@ object Migrations {
|
|||
LibraryUpdateJob.cancelAllWorks(context)
|
||||
LibraryUpdateJob.setupTask(context)
|
||||
}
|
||||
if (oldVersion < 108) {
|
||||
preferenceStore.getAll()
|
||||
.filter { it.key.startsWith("pref_mangasync_") || it.key.startsWith("track_token_") }
|
||||
.forEach { (key, value) ->
|
||||
if (value is String) {
|
||||
preferenceStore
|
||||
.getString(Preference.privateKey(key))
|
||||
.set(value)
|
||||
|
||||
preferenceStore.getString(key).delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -8,17 +8,22 @@ object BackupConst {
|
|||
const val EXTRA_URI = "$ID.$NAME.EXTRA_URI"
|
||||
|
||||
// Filter options
|
||||
internal const val BACKUP_CATEGORY = 0x1
|
||||
internal const val BACKUP_CATEGORY_MASK = 0x1
|
||||
internal const val BACKUP_CHAPTER = 0x2
|
||||
internal const val BACKUP_CHAPTER_MASK = 0x2
|
||||
internal const val BACKUP_HISTORY = 0x4
|
||||
internal const val BACKUP_HISTORY_MASK = 0x4
|
||||
internal const val BACKUP_TRACK = 0x8
|
||||
internal const val BACKUP_TRACK_MASK = 0x8
|
||||
internal const val BACKUP_CUSTOM_INFO = 0x10
|
||||
internal const val BACKUP_CUSTOM_INFO_MASK = 0x10
|
||||
internal const val BACKUP_READ_MANGA = 0x20
|
||||
internal const val BACKUP_READ_MANGA_MASK = 0x20
|
||||
internal const val BACKUP_ALL = 0x1F
|
||||
const val BACKUP_CATEGORY = 0x1
|
||||
const val BACKUP_CATEGORY_MASK = 0x1
|
||||
const val BACKUP_CHAPTER = 0x2
|
||||
const val BACKUP_CHAPTER_MASK = 0x2
|
||||
const val BACKUP_HISTORY = 0x4
|
||||
const val BACKUP_HISTORY_MASK = 0x4
|
||||
const val BACKUP_TRACK = 0x8
|
||||
const val BACKUP_TRACK_MASK = 0x8
|
||||
const val BACKUP_APP_PREFS = 0x10
|
||||
const val BACKUP_APP_PREFS_MASK = 0x10
|
||||
const val BACKUP_SOURCE_PREFS = 0x20
|
||||
const val BACKUP_SOURCE_PREFS_MASK = 0x20
|
||||
const val BACKUP_CUSTOM_INFO = 0x40
|
||||
const val BACKUP_CUSTOM_INFO_MASK = 0x40
|
||||
const val BACKUP_READ_MANGA = 0x80
|
||||
const val BACKUP_READ_MANGA_MASK = 0x80
|
||||
|
||||
const val BACKUP_ALL = 0x7F
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import android.content.Context
|
|||
import android.net.Uri
|
||||
import com.hippo.unifile.UniFile
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_APP_PREFS
|
||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_APP_PREFS_MASK
|
||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CATEGORY
|
||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CATEGORY_MASK
|
||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CHAPTER
|
||||
|
@ -14,6 +16,8 @@ import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_HISTORY
|
|||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_HISTORY_MASK
|
||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_READ_MANGA
|
||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_READ_MANGA_MASK
|
||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_SOURCE_PREFS
|
||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_SOURCE_PREFS_MASK
|
||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_TRACK
|
||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_TRACK_MASK
|
||||
import eu.kanade.tachiyomi.data.backup.models.Backup
|
||||
|
@ -21,25 +25,41 @@ import eu.kanade.tachiyomi.data.backup.models.BackupCategory
|
|||
import eu.kanade.tachiyomi.data.backup.models.BackupChapter
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupHistory
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupManga
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupPreference
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupSerializer
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupSource
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupSourcePreferences
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupTracking
|
||||
import eu.kanade.tachiyomi.data.backup.models.BooleanPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.models.FloatPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.models.IntPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.models.LongPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.models.StringPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.models.StringSetPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.History
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaCategory
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.preference.Preference
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceStore
|
||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.preferenceKey
|
||||
import eu.kanade.tachiyomi.source.sourcePreferences
|
||||
import kotlinx.serialization.protobuf.ProtoBuf
|
||||
import okio.buffer
|
||||
import okio.gzip
|
||||
import okio.sink
|
||||
import timber.log.Timber
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.FileOutputStream
|
||||
import kotlin.math.max
|
||||
|
||||
class BackupManager(context: Context) : AbstractBackupManager(context) {
|
||||
|
||||
private val preferenceStore: PreferenceStore = Injekt.get()
|
||||
val parser = ProtoBuf
|
||||
|
||||
/**
|
||||
|
@ -64,6 +84,8 @@ class BackupManager(context: Context) : AbstractBackupManager(context) {
|
|||
backupCategories(),
|
||||
emptyList(),
|
||||
backupExtensionInfo(databaseManga),
|
||||
backupAppPreferences(flags),
|
||||
backupSourcePreferences(flags),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -198,6 +220,41 @@ class BackupManager(context: Context) : AbstractBackupManager(context) {
|
|||
return mangaObject
|
||||
}
|
||||
|
||||
private fun backupAppPreferences(flags: Int): List<BackupPreference> {
|
||||
if (flags and BACKUP_APP_PREFS_MASK != BACKUP_APP_PREFS) return emptyList()
|
||||
return preferenceStore.getAll().toBackupPreferences()
|
||||
}
|
||||
|
||||
private fun backupSourcePreferences(flags: Int): List<BackupSourcePreferences> {
|
||||
if (flags and BACKUP_SOURCE_PREFS_MASK != BACKUP_SOURCE_PREFS) return emptyList()
|
||||
return sourceManager.getOnlineSources()
|
||||
.filterIsInstance<ConfigurableSource>()
|
||||
.map {
|
||||
BackupSourcePreferences(
|
||||
it.preferenceKey(),
|
||||
it.sourcePreferences().all.toBackupPreferences(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun Map<String, *>.toBackupPreferences(): List<BackupPreference> {
|
||||
return this.filterKeys { !Preference.isPrivate(it) }
|
||||
.mapNotNull { (key, value) ->
|
||||
when (value) {
|
||||
is Int -> BackupPreference(key, IntPreferenceValue(value))
|
||||
is Long -> BackupPreference(key, LongPreferenceValue(value))
|
||||
is Float -> BackupPreference(key, FloatPreferenceValue(value))
|
||||
is String -> BackupPreference(key, StringPreferenceValue(value))
|
||||
is Boolean -> BackupPreference(key, BooleanPreferenceValue(value))
|
||||
is Set<*> -> (value as? Set<String>)?.let {
|
||||
BackupPreference(key, StringSetPreferenceValue(it))
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun restoreExistingManga(manga: Manga, dbManga: Manga) {
|
||||
manga.id = dbManga.id
|
||||
manga.copyFrom(dbManga)
|
||||
|
|
|
@ -7,24 +7,38 @@ import eu.kanade.tachiyomi.R
|
|||
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupHistory
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupManga
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupPreference
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupSerializer
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupSource
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupSourcePreferences
|
||||
import eu.kanade.tachiyomi.data.backup.models.BooleanPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.models.FloatPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.models.IntPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.models.LongPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.models.StringPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.backup.models.StringSetPreferenceValue
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.library.CustomMangaManager
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
||||
import eu.kanade.tachiyomi.data.preference.AndroidPreferenceStore
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceStore
|
||||
import eu.kanade.tachiyomi.source.sourcePreferences
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.isActive
|
||||
import okio.buffer
|
||||
import okio.gzip
|
||||
import okio.source
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.util.Date
|
||||
|
||||
class BackupRestorer(context: Context, notifier: BackupNotifier) : AbstractBackupRestore<BackupManager>(context, notifier) {
|
||||
|
||||
private val preferenceStore: PreferenceStore = Injekt.get()
|
||||
|
||||
@SuppressLint("Recycle")
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
override suspend fun performRestore(uri: Uri): Boolean {
|
||||
backupManager = BackupManager(context)
|
||||
|
||||
|
@ -44,6 +58,9 @@ class BackupRestorer(context: Context, notifier: BackupNotifier) : AbstractBacku
|
|||
sourceMapping = backupMaps.associate { it.sourceId to it.name }
|
||||
|
||||
return coroutineScope {
|
||||
restoreAppPreferences(backup.backupPreferences)
|
||||
restoreSourcePreferences(backup.backupSourcePreferences)
|
||||
|
||||
// Restore individual manga
|
||||
backup.backupManga.forEach {
|
||||
if (!isActive) {
|
||||
|
@ -147,4 +164,56 @@ class BackupRestorer(context: Context, notifier: BackupNotifier) : AbstractBacku
|
|||
customManga?.id = manga.id!!
|
||||
customManga?.let { customMangaManager.saveMangaInfo(it) }
|
||||
}
|
||||
|
||||
private fun restoreAppPreferences(preferences: List<BackupPreference>) {
|
||||
restorePreferences(preferences, preferenceStore)
|
||||
}
|
||||
|
||||
private fun restoreSourcePreferences(preferences: List<BackupSourcePreferences>) {
|
||||
preferences.forEach {
|
||||
val sourcePrefs = AndroidPreferenceStore(context, sourcePreferences(it.sourceKey))
|
||||
restorePreferences(it.prefs, sourcePrefs)
|
||||
}
|
||||
}
|
||||
|
||||
private fun restorePreferences(
|
||||
toRestore: List<BackupPreference>,
|
||||
preferenceStore: PreferenceStore,
|
||||
) {
|
||||
val prefs = preferenceStore.getAll()
|
||||
toRestore.forEach { (key, value) ->
|
||||
when (value) {
|
||||
is IntPreferenceValue -> {
|
||||
if (prefs[key] is Int?) {
|
||||
preferenceStore.getInt(key).set(value.value)
|
||||
}
|
||||
}
|
||||
is LongPreferenceValue -> {
|
||||
if (prefs[key] is Long?) {
|
||||
preferenceStore.getLong(key).set(value.value)
|
||||
}
|
||||
}
|
||||
is FloatPreferenceValue -> {
|
||||
if (prefs[key] is Float?) {
|
||||
preferenceStore.getFloat(key).set(value.value)
|
||||
}
|
||||
}
|
||||
is StringPreferenceValue -> {
|
||||
if (prefs[key] is String?) {
|
||||
preferenceStore.getString(key).set(value.value)
|
||||
}
|
||||
}
|
||||
is BooleanPreferenceValue -> {
|
||||
if (prefs[key] is Boolean?) {
|
||||
preferenceStore.getBoolean(key).set(value.value)
|
||||
}
|
||||
}
|
||||
is StringSetPreferenceValue -> {
|
||||
if (prefs[key] is Set<*>?) {
|
||||
preferenceStore.getStringSet(key).set(value.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,10 +11,12 @@ import java.util.Locale
|
|||
data class Backup(
|
||||
@ProtoNumber(1) val backupManga: List<BackupManga>,
|
||||
@ProtoNumber(2) var backupCategories: List<BackupCategory> = emptyList(),
|
||||
// Bump by 100 to specify this is a 0.x value
|
||||
@ProtoNumber(100) var backupBrokenSources: List<BrokenBackupSource> = emptyList(),
|
||||
@ProtoNumber(101) var backupSources: List<BackupSource> = emptyList(),
|
||||
@ProtoNumber(104) var backupPreferences: List<BackupPreference> = emptyList(),
|
||||
@ProtoNumber(105) var backupSourcePreferences: List<BackupSourcePreferences> = emptyList(),
|
||||
) {
|
||||
|
||||
companion object {
|
||||
val filenameRegex = """(${BuildConfig.APPLICATION_ID}|tachiyomi)?_\d+-\d+-\d+_\d+-\d+\.(tachibk|proto\.gz)""".toRegex()
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package eu.kanade.tachiyomi.data.backup.models
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoNumber
|
||||
|
||||
@Serializable
|
||||
data class BackupPreference(
|
||||
@ProtoNumber(1) val key: String,
|
||||
@ProtoNumber(2) val value: PreferenceValue,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class BackupSourcePreferences(
|
||||
@ProtoNumber(1) val sourceKey: String,
|
||||
@ProtoNumber(2) val prefs: List<BackupPreference>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
sealed class PreferenceValue
|
||||
|
||||
@Serializable
|
||||
data class IntPreferenceValue(val value: Int) : PreferenceValue()
|
||||
|
||||
@Serializable
|
||||
data class LongPreferenceValue(val value: Long) : PreferenceValue()
|
||||
|
||||
@Serializable
|
||||
data class FloatPreferenceValue(val value: Float) : PreferenceValue()
|
||||
|
||||
@Serializable
|
||||
data class StringPreferenceValue(val value: String) : PreferenceValue()
|
||||
|
||||
@Serializable
|
||||
data class BooleanPreferenceValue(val value: Boolean) : PreferenceValue()
|
||||
|
||||
@Serializable
|
||||
data class StringSetPreferenceValue(val value: Set<String>) : PreferenceValue()
|
|
@ -0,0 +1,194 @@
|
|||
package eu.kanade.tachiyomi.data.preference
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.content.SharedPreferences.Editor
|
||||
import androidx.core.content.edit
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.conflate
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import timber.log.Timber
|
||||
|
||||
sealed class AndroidPreference<T>(
|
||||
private val preferences: SharedPreferences,
|
||||
private val keyFlow: Flow<String?>,
|
||||
private val key: String,
|
||||
private val defaultValue: T,
|
||||
) : Preference<T> {
|
||||
|
||||
abstract fun read(preferences: SharedPreferences, key: String, defaultValue: T): T
|
||||
|
||||
abstract fun write(key: String, value: T): Editor.() -> Unit
|
||||
|
||||
override fun key(): String {
|
||||
return key
|
||||
}
|
||||
|
||||
override fun get(): T {
|
||||
return try {
|
||||
read(preferences, key, defaultValue)
|
||||
} catch (e: ClassCastException) {
|
||||
Timber.d("Invalid value for $key; deleting")
|
||||
delete()
|
||||
defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
override fun set(value: T) {
|
||||
preferences.edit(action = write(key, value))
|
||||
}
|
||||
|
||||
override fun isSet(): Boolean {
|
||||
return preferences.contains(key)
|
||||
}
|
||||
|
||||
override fun delete() {
|
||||
preferences.edit {
|
||||
remove(key)
|
||||
}
|
||||
}
|
||||
|
||||
override fun defaultValue(): T {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
override fun changes(): Flow<T> {
|
||||
return keyFlow
|
||||
.filter { it == key || it == null }
|
||||
.onStart { emit("ignition") }
|
||||
.map { get() }
|
||||
.conflate()
|
||||
}
|
||||
|
||||
override fun stateIn(scope: CoroutineScope): StateFlow<T> {
|
||||
return changes().stateIn(scope, SharingStarted.Eagerly, get())
|
||||
}
|
||||
|
||||
class StringPrimitive(
|
||||
preferences: SharedPreferences,
|
||||
keyFlow: Flow<String?>,
|
||||
key: String,
|
||||
defaultValue: String,
|
||||
) : AndroidPreference<String>(preferences, keyFlow, key, defaultValue) {
|
||||
override fun read(
|
||||
preferences: SharedPreferences,
|
||||
key: String,
|
||||
defaultValue: String,
|
||||
): String {
|
||||
return preferences.getString(key, defaultValue) ?: defaultValue
|
||||
}
|
||||
|
||||
override fun write(key: String, value: String): Editor.() -> Unit = {
|
||||
putString(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
class LongPrimitive(
|
||||
preferences: SharedPreferences,
|
||||
keyFlow: Flow<String?>,
|
||||
key: String,
|
||||
defaultValue: Long,
|
||||
) : AndroidPreference<Long>(preferences, keyFlow, key, defaultValue) {
|
||||
override fun read(preferences: SharedPreferences, key: String, defaultValue: Long): Long {
|
||||
return preferences.getLong(key, defaultValue)
|
||||
}
|
||||
|
||||
override fun write(key: String, value: Long): Editor.() -> Unit = {
|
||||
putLong(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
class IntPrimitive(
|
||||
preferences: SharedPreferences,
|
||||
keyFlow: Flow<String?>,
|
||||
key: String,
|
||||
defaultValue: Int,
|
||||
) : AndroidPreference<Int>(preferences, keyFlow, key, defaultValue) {
|
||||
override fun read(preferences: SharedPreferences, key: String, defaultValue: Int): Int {
|
||||
return preferences.getInt(key, defaultValue)
|
||||
}
|
||||
|
||||
override fun write(key: String, value: Int): Editor.() -> Unit = {
|
||||
putInt(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
class FloatPrimitive(
|
||||
preferences: SharedPreferences,
|
||||
keyFlow: Flow<String?>,
|
||||
key: String,
|
||||
defaultValue: Float,
|
||||
) : AndroidPreference<Float>(preferences, keyFlow, key, defaultValue) {
|
||||
override fun read(preferences: SharedPreferences, key: String, defaultValue: Float): Float {
|
||||
return preferences.getFloat(key, defaultValue)
|
||||
}
|
||||
|
||||
override fun write(key: String, value: Float): Editor.() -> Unit = {
|
||||
putFloat(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
class BooleanPrimitive(
|
||||
preferences: SharedPreferences,
|
||||
keyFlow: Flow<String?>,
|
||||
key: String,
|
||||
defaultValue: Boolean,
|
||||
) : AndroidPreference<Boolean>(preferences, keyFlow, key, defaultValue) {
|
||||
override fun read(
|
||||
preferences: SharedPreferences,
|
||||
key: String,
|
||||
defaultValue: Boolean,
|
||||
): Boolean {
|
||||
return preferences.getBoolean(key, defaultValue)
|
||||
}
|
||||
|
||||
override fun write(key: String, value: Boolean): Editor.() -> Unit = {
|
||||
putBoolean(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
class StringSetPrimitive(
|
||||
preferences: SharedPreferences,
|
||||
keyFlow: Flow<String?>,
|
||||
key: String,
|
||||
defaultValue: Set<String>,
|
||||
) : AndroidPreference<Set<String>>(preferences, keyFlow, key, defaultValue) {
|
||||
override fun read(
|
||||
preferences: SharedPreferences,
|
||||
key: String,
|
||||
defaultValue: Set<String>,
|
||||
): Set<String> {
|
||||
return preferences.getStringSet(key, defaultValue) ?: defaultValue
|
||||
}
|
||||
|
||||
override fun write(key: String, value: Set<String>): Editor.() -> Unit = {
|
||||
putStringSet(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
class Object<T>(
|
||||
preferences: SharedPreferences,
|
||||
keyFlow: Flow<String?>,
|
||||
key: String,
|
||||
defaultValue: T,
|
||||
val serializer: (T) -> String,
|
||||
val deserializer: (String) -> T,
|
||||
) : AndroidPreference<T>(preferences, keyFlow, key, defaultValue) {
|
||||
override fun read(preferences: SharedPreferences, key: String, defaultValue: T): T {
|
||||
return try {
|
||||
preferences.getString(key, null)?.let(deserializer) ?: defaultValue
|
||||
} catch (e: Exception) {
|
||||
defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
override fun write(key: String, value: T): Editor.() -> Unit = {
|
||||
putString(key, serializer(value))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package eu.kanade.tachiyomi.data.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.preference.PreferenceManager
|
||||
import eu.kanade.tachiyomi.data.preference.AndroidPreference.BooleanPrimitive
|
||||
import eu.kanade.tachiyomi.data.preference.AndroidPreference.FloatPrimitive
|
||||
import eu.kanade.tachiyomi.data.preference.AndroidPreference.IntPrimitive
|
||||
import eu.kanade.tachiyomi.data.preference.AndroidPreference.LongPrimitive
|
||||
import eu.kanade.tachiyomi.data.preference.AndroidPreference.Object
|
||||
import eu.kanade.tachiyomi.data.preference.AndroidPreference.StringPrimitive
|
||||
import eu.kanade.tachiyomi.data.preference.AndroidPreference.StringSetPrimitive
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
|
||||
class AndroidPreferenceStore(
|
||||
context: Context,
|
||||
private val sharedPreferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context),
|
||||
) : PreferenceStore {
|
||||
|
||||
private val keyFlow = sharedPreferences.keyFlow
|
||||
|
||||
override fun getString(key: String, defaultValue: String): Preference<String> {
|
||||
return StringPrimitive(sharedPreferences, keyFlow, key, defaultValue)
|
||||
}
|
||||
|
||||
override fun getLong(key: String, defaultValue: Long): Preference<Long> {
|
||||
return LongPrimitive(sharedPreferences, keyFlow, key, defaultValue)
|
||||
}
|
||||
|
||||
override fun getInt(key: String, defaultValue: Int): Preference<Int> {
|
||||
return IntPrimitive(sharedPreferences, keyFlow, key, defaultValue)
|
||||
}
|
||||
|
||||
override fun getFloat(key: String, defaultValue: Float): Preference<Float> {
|
||||
return FloatPrimitive(sharedPreferences, keyFlow, key, defaultValue)
|
||||
}
|
||||
|
||||
override fun getBoolean(key: String, defaultValue: Boolean): Preference<Boolean> {
|
||||
return BooleanPrimitive(sharedPreferences, keyFlow, key, defaultValue)
|
||||
}
|
||||
|
||||
override fun getStringSet(key: String, defaultValue: Set<String>): Preference<Set<String>> {
|
||||
return StringSetPrimitive(sharedPreferences, keyFlow, key, defaultValue)
|
||||
}
|
||||
|
||||
override fun <T> getObject(
|
||||
key: String,
|
||||
defaultValue: T,
|
||||
serializer: (T) -> String,
|
||||
deserializer: (String) -> T,
|
||||
): Preference<T> {
|
||||
return Object(
|
||||
preferences = sharedPreferences,
|
||||
keyFlow = keyFlow,
|
||||
key = key,
|
||||
defaultValue = defaultValue,
|
||||
serializer = serializer,
|
||||
deserializer = deserializer,
|
||||
)
|
||||
}
|
||||
|
||||
override fun getAll(): Map<String, *> {
|
||||
return sharedPreferences.all ?: emptyMap<String, Any>()
|
||||
}
|
||||
}
|
||||
|
||||
private val SharedPreferences.keyFlow
|
||||
get() = callbackFlow {
|
||||
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key: String? ->
|
||||
trySend(
|
||||
key,
|
||||
)
|
||||
}
|
||||
registerOnSharedPreferenceChangeListener(listener)
|
||||
awaitClose {
|
||||
unregisterOnSharedPreferenceChangeListener(listener)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package eu.kanade.tachiyomi.data.preference
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
interface Preference<T> {
|
||||
|
||||
fun key(): String
|
||||
|
||||
fun get(): T
|
||||
|
||||
fun set(value: T)
|
||||
|
||||
fun isSet(): Boolean
|
||||
|
||||
fun delete()
|
||||
|
||||
fun defaultValue(): T
|
||||
|
||||
fun changes(): Flow<T>
|
||||
|
||||
fun stateIn(scope: CoroutineScope): StateFlow<T>
|
||||
|
||||
val isPrivate: Boolean
|
||||
get() = key().startsWith(PRIVATE_PREFIX)
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* A preference that should not be exposed in places like backups.
|
||||
*/
|
||||
fun isPrivate(key: String): Boolean {
|
||||
return key.startsWith(PRIVATE_PREFIX)
|
||||
}
|
||||
|
||||
fun privateKey(key: String): String {
|
||||
return "$PRIVATE_PREFIX$key"
|
||||
}
|
||||
|
||||
private const val PRIVATE_PREFIX = "__PRIVATE_"
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T, R : T> Preference<T>.getAndSet(crossinline block: (T) -> R) = set(
|
||||
block(get()),
|
||||
)
|
||||
|
||||
operator fun <T> Preference<Set<T>>.plusAssign(item: T) {
|
||||
set(get() + item)
|
||||
}
|
||||
|
||||
operator fun <T> Preference<Set<T>>.minusAssign(item: T) {
|
||||
set(get() - item)
|
||||
}
|
||||
|
||||
fun Preference<Boolean>.toggle(): Boolean {
|
||||
set(!get())
|
||||
return get()
|
||||
}
|
|
@ -241,10 +241,4 @@ object PreferenceKeys {
|
|||
const val hideChapterTitles = "hide_chapter_titles"
|
||||
|
||||
const val chaptersDescAsDefault = "chapters_desc_as_default"
|
||||
|
||||
fun trackUsername(syncId: Int) = "pref_mangasync_username_$syncId"
|
||||
|
||||
fun trackPassword(syncId: Int) = "pref_mangasync_password_$syncId"
|
||||
|
||||
fun trackToken(syncId: Int) = "track_token_$syncId"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package eu.kanade.tachiyomi.data.preference
|
||||
|
||||
interface PreferenceStore {
|
||||
|
||||
fun getString(key: String, defaultValue: String = ""): Preference<String>
|
||||
|
||||
fun getLong(key: String, defaultValue: Long = 0): Preference<Long>
|
||||
|
||||
fun getInt(key: String, defaultValue: Int = 0): Preference<Int>
|
||||
|
||||
fun getFloat(key: String, defaultValue: Float = 0f): Preference<Float>
|
||||
|
||||
fun getBoolean(key: String, defaultValue: Boolean = false): Preference<Boolean>
|
||||
|
||||
fun getStringSet(key: String, defaultValue: Set<String> = emptySet()): Preference<Set<String>>
|
||||
|
||||
fun <T> getObject(
|
||||
key: String,
|
||||
defaultValue: T,
|
||||
serializer: (T) -> String,
|
||||
deserializer: (String) -> T,
|
||||
): Preference<T>
|
||||
|
||||
fun getAll(): Map<String, *>
|
||||
}
|
||||
|
||||
inline fun <reified T : Enum<T>> PreferenceStore.getEnum(
|
||||
key: String,
|
||||
defaultValue: T,
|
||||
): Preference<T> {
|
||||
return getObject(
|
||||
key = key,
|
||||
defaultValue = defaultValue,
|
||||
serializer = { it.name },
|
||||
deserializer = {
|
||||
try {
|
||||
enumValueOf(it)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
defaultValue
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
|
@ -11,7 +11,6 @@ import com.google.android.material.color.DynamicColors
|
|||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.data.updater.AppDownloadInstallJob
|
||||
import eu.kanade.tachiyomi.extension.model.InstalledExtensionsOrder
|
||||
import eu.kanade.tachiyomi.extension.util.ExtensionInstaller
|
||||
|
@ -223,19 +222,6 @@ class PreferencesHelper(val context: Context) {
|
|||
|
||||
fun sourceSorting() = flowPrefs.getInt(Keys.sourcesSort, 0)
|
||||
|
||||
fun trackUsername(sync: TrackService) = prefs.getString(Keys.trackUsername(sync.id), "")
|
||||
|
||||
fun trackPassword(sync: TrackService) = prefs.getString(Keys.trackPassword(sync.id), "")
|
||||
|
||||
fun setTrackCredentials(sync: TrackService, username: String, password: String) {
|
||||
prefs.edit()
|
||||
.putString(Keys.trackUsername(sync.id), username)
|
||||
.putString(Keys.trackPassword(sync.id), password)
|
||||
.apply()
|
||||
}
|
||||
|
||||
fun trackToken(sync: TrackService) = flowPrefs.getString(Keys.trackToken(sync.id), "")
|
||||
|
||||
fun anilistScoreType() = flowPrefs.getString("anilist_score_type", "POINT_10")
|
||||
|
||||
fun backupsDirectory() = flowPrefs.getString(Keys.backupDirectory, defaultBackupDir.toString())
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package eu.kanade.tachiyomi.data.track
|
||||
|
||||
import eu.kanade.tachiyomi.data.preference.Preference
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceStore
|
||||
import eu.kanade.tachiyomi.data.track.anilist.Anilist
|
||||
|
||||
class TrackPreferences(
|
||||
private val preferenceStore: PreferenceStore,
|
||||
) {
|
||||
|
||||
fun trackUsername(sync: TrackService) = preferenceStore.getString(trackUsername(sync.id), "")
|
||||
|
||||
fun trackPassword(sync: TrackService) = preferenceStore.getString(trackPassword(sync.id), "")
|
||||
|
||||
fun setCredentials(sync: TrackService, username: String, password: String) {
|
||||
trackUsername(sync).set(username)
|
||||
trackPassword(sync).set(password)
|
||||
}
|
||||
|
||||
fun trackToken(sync: TrackService) = preferenceStore.getString(trackToken(sync.id), "")
|
||||
|
||||
fun anilistScoreType() = preferenceStore.getString("anilist_score_type", Anilist.POINT_10)
|
||||
|
||||
fun autoUpdateTrack() = preferenceStore.getBoolean("pref_auto_update_manga_sync_key", true)
|
||||
|
||||
companion object {
|
||||
fun trackUsername(syncId: Int) = Preference.privateKey("pref_mangasync_username_$syncId")
|
||||
|
||||
private fun trackPassword(syncId: Int) =
|
||||
Preference.privateKey("pref_mangasync_password_$syncId")
|
||||
|
||||
private fun trackToken(syncId: Int) = Preference.privateKey("track_token_$syncId")
|
||||
}
|
||||
}
|
|
@ -5,7 +5,6 @@ import androidx.annotation.DrawableRes
|
|||
import androidx.annotation.StringRes
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import eu.kanade.tachiyomi.util.system.executeOnIO
|
||||
|
@ -14,7 +13,7 @@ import uy.kohesive.injekt.injectLazy
|
|||
|
||||
abstract class TrackService(val id: Int) {
|
||||
|
||||
val preferences: PreferencesHelper by injectLazy()
|
||||
val trackPreferences: TrackPreferences by injectLazy()
|
||||
val networkService: NetworkHelper by injectLazy()
|
||||
val db: DatabaseHelper by injectLazy()
|
||||
open fun canRemoveFromService() = false
|
||||
|
@ -93,19 +92,19 @@ abstract class TrackService(val id: Int) {
|
|||
|
||||
@CallSuper
|
||||
open fun logout() {
|
||||
preferences.setTrackCredentials(this, "", "")
|
||||
trackPreferences.setCredentials(this, "", "")
|
||||
}
|
||||
|
||||
open val isLogged: Boolean
|
||||
get() = getUsername().isNotEmpty() &&
|
||||
getPassword().isNotEmpty()
|
||||
|
||||
fun getUsername() = preferences.trackUsername(this)!!
|
||||
fun getUsername() = trackPreferences.trackUsername(this).get()
|
||||
|
||||
fun getPassword() = preferences.trackPassword(this)!!
|
||||
fun getPassword() = trackPreferences.trackPassword(this).get()
|
||||
|
||||
fun saveCredentials(username: String, password: String) {
|
||||
preferences.setTrackCredentials(this, username, password)
|
||||
trackPreferences.setCredentials(this, username, password)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ class Anilist(private val context: Context, id: Int) : TrackService(id) {
|
|||
|
||||
override val supportsReadingDates: Boolean = true
|
||||
|
||||
private val scorePreference = preferences.anilistScoreType()
|
||||
private val scorePreference = trackPreferences.anilistScoreType()
|
||||
|
||||
init {
|
||||
// If the preference is an int from APIv1, logout user to force using APIv2
|
||||
|
@ -229,17 +229,17 @@ class Anilist(private val context: Context, id: Int) : TrackService(id) {
|
|||
|
||||
override fun logout() {
|
||||
super.logout()
|
||||
preferences.trackToken(this).delete()
|
||||
trackPreferences.trackToken(this).delete()
|
||||
interceptor.setAuth(null)
|
||||
}
|
||||
|
||||
fun saveOAuth(oAuth: OAuth?) {
|
||||
preferences.trackToken(this).set(json.encodeToString(oAuth))
|
||||
trackPreferences.trackToken(this).set(json.encodeToString(oAuth))
|
||||
}
|
||||
|
||||
fun loadOAuth(): OAuth? {
|
||||
return try {
|
||||
json.decodeFromString<OAuth>(preferences.trackToken(this).get())
|
||||
json.decodeFromString<OAuth>(trackPreferences.trackToken(this).get())
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
null
|
||||
|
|
|
@ -129,12 +129,12 @@ class Bangumi(private val context: Context, id: Int) : TrackService(id) {
|
|||
}
|
||||
|
||||
fun saveToken(oauth: OAuth?) {
|
||||
preferences.trackToken(this).set(json.encodeToString(oauth))
|
||||
trackPreferences.trackToken(this).set(json.encodeToString(oauth))
|
||||
}
|
||||
|
||||
fun restoreToken(): OAuth? {
|
||||
return try {
|
||||
json.decodeFromString<OAuth>(preferences.trackToken(this).get())
|
||||
json.decodeFromString<OAuth>(trackPreferences.trackToken(this).get())
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ class Bangumi(private val context: Context, id: Int) : TrackService(id) {
|
|||
|
||||
override fun logout() {
|
||||
super.logout()
|
||||
preferences.trackToken(this).delete()
|
||||
trackPreferences.trackToken(this).delete()
|
||||
interceptor.newAuth(null)
|
||||
}
|
||||
|
||||
|
|
|
@ -154,12 +154,12 @@ class Kitsu(private val context: Context, id: Int) : TrackService(id) {
|
|||
}
|
||||
|
||||
fun saveToken(oauth: OAuth?) {
|
||||
preferences.trackToken(this).set(json.encodeToString(oauth))
|
||||
trackPreferences.trackToken(this).set(json.encodeToString(oauth))
|
||||
}
|
||||
|
||||
fun restoreToken(): OAuth? {
|
||||
return try {
|
||||
json.decodeFromString<OAuth>(preferences.trackToken(this).get())
|
||||
json.decodeFromString<OAuth>(trackPreferences.trackToken(this).get())
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
|
|
|
@ -130,6 +130,6 @@ class MangaUpdates(private val context: Context, id: Int) : TrackService(id) {
|
|||
}
|
||||
|
||||
fun restoreSession(): String? {
|
||||
return preferences.trackPassword(this)
|
||||
return getPassword()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,17 +138,17 @@ class MyAnimeList(private val context: Context, id: Int) : TrackService(id) {
|
|||
|
||||
override fun logout() {
|
||||
super.logout()
|
||||
preferences.trackToken(this).delete()
|
||||
trackPreferences.trackToken(this).delete()
|
||||
interceptor.setAuth(null)
|
||||
}
|
||||
|
||||
fun saveOAuth(oAuth: OAuth?) {
|
||||
preferences.trackToken(this).set(json.encodeToString(oAuth))
|
||||
trackPreferences.trackToken(this).set(json.encodeToString(oAuth))
|
||||
}
|
||||
|
||||
fun loadOAuth(): OAuth? {
|
||||
return try {
|
||||
json.decodeFromString<OAuth>(preferences.trackToken(this).get())
|
||||
json.decodeFromString<OAuth>(trackPreferences.trackToken(this).get())
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
|
|
|
@ -129,12 +129,12 @@ class Shikimori(private val context: Context, id: Int) : TrackService(id) {
|
|||
}
|
||||
|
||||
fun saveToken(oauth: OAuth?) {
|
||||
preferences.trackToken(this).set(json.encodeToString(oauth))
|
||||
trackPreferences.trackToken(this).set(json.encodeToString(oauth))
|
||||
}
|
||||
|
||||
fun restoreToken(): OAuth? {
|
||||
return try {
|
||||
json.decodeFromString<OAuth>(preferences.trackToken(this).get())
|
||||
json.decodeFromString<OAuth>(trackPreferences.trackToken(this).get())
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ class Shikimori(private val context: Context, id: Int) : TrackService(id) {
|
|||
|
||||
override fun logout() {
|
||||
super.logout()
|
||||
preferences.trackToken(this).delete()
|
||||
trackPreferences.trackToken(this).delete()
|
||||
interceptor.newAuth(null)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,3 +23,6 @@ interface ConfigurableSource : Source {
|
|||
// TODO: use getSourcePreferences once all extensions are on ext-lib 1.5
|
||||
fun ConfigurableSource.sourcePreferences(): SharedPreferences =
|
||||
Injekt.get<Application>().getSharedPreferences(preferenceKey(), Context.MODE_PRIVATE)
|
||||
|
||||
fun sourcePreferences(key: String): SharedPreferences =
|
||||
Injekt.get<Application>().getSharedPreferences(key, Context.MODE_PRIVATE)
|
||||
|
|
|
@ -132,6 +132,8 @@ import kotlinx.coroutines.flow.onEach
|
|||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.util.Date
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
@ -541,7 +543,7 @@ open class MainActivity : BaseActivity<MainActivityBinding>() {
|
|||
preferences.incognitoMode().set(false)
|
||||
|
||||
// Show changelog if needed
|
||||
if (Migrations.upgrade(preferences, lifecycleScope)) {
|
||||
if (Migrations.upgrade(preferences, Injekt.get(), lifecycleScope)) {
|
||||
if (!BuildConfig.DEBUG) {
|
||||
content.post {
|
||||
whatsNewSheet().show()
|
||||
|
|
|
@ -200,9 +200,7 @@ class TrackingBottomSheet(private val controller: MangaDetailsController) :
|
|||
return
|
||||
}
|
||||
|
||||
if (track.tracking_url.isBlank()) {
|
||||
activity.toast(R.string.url_not_set_click_again)
|
||||
} else {
|
||||
if (track.tracking_url.isNotBlank()) {
|
||||
activity.openInBrowser(track.tracking_url.toUri())
|
||||
controller.refreshTracker = position
|
||||
}
|
||||
|
|
|
@ -218,6 +218,8 @@ class SettingsBackupController : SettingsController() {
|
|||
R.string.chapters,
|
||||
R.string.tracking,
|
||||
R.string.history,
|
||||
R.string.app_settings,
|
||||
R.string.source_settings,
|
||||
R.string.custom_manga_info,
|
||||
R.string.all_read_manga,
|
||||
)
|
||||
|
@ -244,8 +246,10 @@ class SettingsBackupController : SettingsController() {
|
|||
2 -> flags = flags or BackupConst.BACKUP_CHAPTER
|
||||
3 -> flags = flags or BackupConst.BACKUP_TRACK
|
||||
4 -> flags = flags or BackupConst.BACKUP_HISTORY
|
||||
5 -> flags = flags or BackupConst.BACKUP_CUSTOM_INFO
|
||||
6 -> flags = flags or BackupConst.BACKUP_READ_MANGA
|
||||
5 -> flags = flags or BackupConst.BACKUP_APP_PREFS
|
||||
6 -> flags = flags or BackupConst.BACKUP_SOURCE_PREFS
|
||||
7 -> flags = flags or BackupConst.BACKUP_CUSTOM_INFO
|
||||
8 -> flags = flags or BackupConst.BACKUP_READ_MANGA
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.R
|
|||
import eu.kanade.tachiyomi.data.preference.asImmediateFlowIn
|
||||
import eu.kanade.tachiyomi.data.track.EnhancedTrackService
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.data.track.TrackPreferences
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.data.track.anilist.AnilistApi
|
||||
import eu.kanade.tachiyomi.data.track.bangumi.BangumiApi
|
||||
|
@ -30,6 +31,7 @@ class SettingsTrackingController :
|
|||
TrackLogoutDialog.Listener {
|
||||
|
||||
private val trackManager: TrackManager by injectLazy()
|
||||
val trackPreferences: TrackPreferences by injectLazy()
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
|
||||
titleRes = R.string.tracking
|
||||
|
@ -59,7 +61,7 @@ class SettingsTrackingController :
|
|||
isIconSpaceReserved = true
|
||||
title = context.getString(R.string.update_tracking_scoring_type, context.getString(R.string.anilist))
|
||||
|
||||
preferences.getStringPref(Keys.trackUsername(trackManager.aniList.id))
|
||||
preferences.getStringPref(trackManager.aniList.getUsername())
|
||||
.asImmediateFlowIn(viewScope) {
|
||||
isVisible = it.isNotEmpty()
|
||||
}
|
||||
|
@ -117,7 +119,7 @@ class SettingsTrackingController :
|
|||
): TrackerPreference {
|
||||
return add(
|
||||
TrackerPreference(context).apply {
|
||||
key = Keys.trackUsername(service.id)
|
||||
key = trackPreferences.trackUsername(service).key()
|
||||
title = context.getString(service.nameRes())
|
||||
iconRes = service.getLogo()
|
||||
iconColor = service.getLogoColor()
|
||||
|
@ -125,7 +127,7 @@ class SettingsTrackingController :
|
|||
if (service.isLogged) {
|
||||
if (service is EnhancedTrackService) {
|
||||
service.logout()
|
||||
updatePreference(service.id)
|
||||
updatePreference(service)
|
||||
} else {
|
||||
val dialog = TrackLogoutDialog(service)
|
||||
dialog.targetController = this@SettingsTrackingController
|
||||
|
@ -134,7 +136,7 @@ class SettingsTrackingController :
|
|||
} else {
|
||||
if (service is EnhancedTrackService) {
|
||||
service.loginNoop()
|
||||
updatePreference(service.id)
|
||||
updatePreference(service)
|
||||
} else {
|
||||
login()
|
||||
}
|
||||
|
@ -146,22 +148,22 @@ class SettingsTrackingController :
|
|||
|
||||
override fun onActivityResumed(activity: Activity) {
|
||||
super.onActivityResumed(activity)
|
||||
updatePreference(trackManager.myAnimeList.id)
|
||||
updatePreference(trackManager.aniList.id)
|
||||
updatePreference(trackManager.shikimori.id)
|
||||
updatePreference(trackManager.bangumi.id)
|
||||
updatePreference(trackManager.myAnimeList)
|
||||
updatePreference(trackManager.aniList)
|
||||
updatePreference(trackManager.shikimori)
|
||||
updatePreference(trackManager.bangumi)
|
||||
}
|
||||
|
||||
private fun updatePreference(id: Int) {
|
||||
val pref = findPreference(Keys.trackUsername(id)) as? TrackerPreference
|
||||
private fun updatePreference(service: TrackService) {
|
||||
val pref = findPreference(trackPreferences.trackUsername(service).key()) as? TrackerPreference
|
||||
pref?.notifyChanged()
|
||||
}
|
||||
|
||||
override fun trackLoginDialogClosed(service: TrackService) {
|
||||
updatePreference(service.id)
|
||||
updatePreference(service)
|
||||
}
|
||||
|
||||
override fun trackLogoutDialogClosed(service: TrackService) {
|
||||
updatePreference(service.id)
|
||||
updatePreference(service)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -576,6 +576,8 @@
|
|||
<string name="error_sharing_cover">Error sharing cover</string>
|
||||
<string name="custom_manga_info">Custom manga info</string>
|
||||
<string name="all_read_manga">All read manga</string>
|
||||
<string name="app_settings">App settings</string>
|
||||
<string name="source_settings">Source settings</string>
|
||||
<string name="set_as_default">Set as default</string>
|
||||
<string name="filter_groups">Filter scanlator groups</string>
|
||||
<plurals name="deleted_chapters">
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue