Include reader tap settings into backups

This commit is contained in:
Koitharu
2025-02-23 18:48:37 +02:00
parent 4cee432a82
commit fc5ad9ff90
15 changed files with 141 additions and 77 deletions

View File

@@ -3,7 +3,6 @@ package org.koitharu.kotatsu.alternatives.domain
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit import kotlinx.coroutines.sync.withPermit
@@ -40,13 +39,16 @@ class AlternativesUseCase @Inject constructor(
searchHelper(manga.title, SearchKind.TITLE)?.manga searchHelper(manga.title, SearchKind.TITLE)?.manga
} }
}.getOrNull() }.getOrNull()
list?.forEach { send(it) } list?.forEach {
launch {
val details = runCatchingCancellable {
mangaRepositoryFactory.create(it.source).getDetails(it)
}.getOrDefault(it)
send(details)
}
}
} }
} }
}.map {
runCatchingCancellable {
mangaRepositoryFactory.create(it.source).getDetails(it)
}.getOrDefault(it)
} }
} }

View File

@@ -16,6 +16,7 @@ class BackupEntry(
CATEGORIES("categories"), CATEGORIES("categories"),
FAVOURITES("favourites"), FAVOURITES("favourites"),
SETTINGS("settings"), SETTINGS("settings"),
SETTINGS_READER_GRID("reader_grid"),
BOOKMARKS("bookmarks"), BOOKMARKS("bookmarks"),
SOURCES("sources"), SOURCES("sources"),
} }

View File

@@ -12,6 +12,7 @@ import org.koitharu.kotatsu.parsers.util.json.asTypedList
import org.koitharu.kotatsu.parsers.util.json.getLongOrDefault import org.koitharu.kotatsu.parsers.util.json.getLongOrDefault
import org.koitharu.kotatsu.parsers.util.json.mapJSON import org.koitharu.kotatsu.parsers.util.json.mapJSON
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import org.koitharu.kotatsu.reader.data.TapGridSettings
import java.util.Date import java.util.Date
import javax.inject.Inject import javax.inject.Inject
@@ -20,6 +21,7 @@ private const val PAGE_SIZE = 10
class BackupRepository @Inject constructor( class BackupRepository @Inject constructor(
private val db: MangaDatabase, private val db: MangaDatabase,
private val settings: AppSettings, private val settings: AppSettings,
private val tapGridSettings: TapGridSettings,
) { ) {
suspend fun dumpHistory(): BackupEntry { suspend fun dumpHistory(): BackupEntry {
@@ -105,6 +107,14 @@ class BackupRepository @Inject constructor(
return entry return entry
} }
fun dumpReaderGridSettings(): BackupEntry {
val entry = BackupEntry(BackupEntry.Name.SETTINGS_READER_GRID, JSONArray())
val settingsDump = tapGridSettings.getAllValues()
val json = JsonSerializer(settingsDump).toJson()
entry.data.put(json)
return entry
}
suspend fun dumpSources(): BackupEntry { suspend fun dumpSources(): BackupEntry {
val entry = BackupEntry(BackupEntry.Name.SOURCES, JSONArray()) val entry = BackupEntry(BackupEntry.Name.SOURCES, JSONArray())
val all = db.getSourcesDao().findAll() val all = db.getSourcesDao().findAll()
@@ -229,4 +239,14 @@ class BackupRepository @Inject constructor(
} }
return result return result
} }
fun restoreReaderGridSettings(entry: BackupEntry): CompositeResult {
val result = CompositeResult()
for (item in entry.data.asTypedList<JSONObject>()) {
result += runCatchingCancellable {
tapGridSettings.upsertAll(JsonDeserializer(item).toMap())
}
}
return result
}
} }

View File

@@ -15,13 +15,13 @@ import androidx.core.os.LocaleListCompat
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import org.json.JSONArray
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.ZoomMode import org.koitharu.kotatsu.core.model.ZoomMode
import org.koitharu.kotatsu.core.network.DoHProvider import org.koitharu.kotatsu.core.network.DoHProvider
import org.koitharu.kotatsu.core.util.ext.connectivityManager import org.koitharu.kotatsu.core.util.ext.connectivityManager
import org.koitharu.kotatsu.core.util.ext.getEnumValue import org.koitharu.kotatsu.core.util.ext.getEnumValue
import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.putAll
import org.koitharu.kotatsu.core.util.ext.putEnumValue import org.koitharu.kotatsu.core.util.ext.putEnumValue
import org.koitharu.kotatsu.core.util.ext.takeIfReadable import org.koitharu.kotatsu.core.util.ext.takeIfReadable
import org.koitharu.kotatsu.core.util.ext.toUriOrNull import org.koitharu.kotatsu.core.util.ext.toUriOrNull
@@ -569,20 +569,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
fun getAllValues(): Map<String, *> = prefs.all fun getAllValues(): Map<String, *> = prefs.all
fun upsertAll(m: Map<String, *>) { fun upsertAll(m: Map<String, *>) = prefs.edit { putAll(m) }
prefs.edit {
m.forEach { e ->
when (val v = e.value) {
is Boolean -> putBoolean(e.key, v)
is Int -> putInt(e.key, v)
is Long -> putLong(e.key, v)
is Float -> putFloat(e.key, v)
is String -> putString(e.key, v)
is JSONArray -> putStringSet(e.key, v.toStringSet())
}
}
}
}
private fun isBackgroundNetworkRestricted(): Boolean { private fun isBackgroundNetworkRestricted(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
@@ -592,15 +579,6 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
} }
} }
private fun JSONArray.toStringSet(): Set<String> {
val len = length()
val result = ArraySet<String>(len)
for (i in 0 until len) {
result.add(getString(i))
}
return result
}
companion object { companion object {
const val TRACK_HISTORY = "history" const val TRACK_HISTORY = "history"

View File

@@ -13,19 +13,16 @@ import android.content.Context.POWER_SERVICE
import android.content.ContextWrapper import android.content.ContextWrapper
import android.content.Intent import android.content.Intent
import android.content.OperationApplicationException import android.content.OperationApplicationException
import android.content.SharedPreferences
import android.content.SyncResult import android.content.SyncResult
import android.content.pm.PackageManager.PERMISSION_GRANTED import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.content.pm.ResolveInfo import android.content.pm.ResolveInfo
import android.database.SQLException import android.database.SQLException
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Color
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.os.Build import android.os.Build
import android.os.PowerManager import android.os.PowerManager
import android.provider.Settings import android.provider.Settings
import android.view.ViewPropertyAnimator import android.view.ViewPropertyAnimator
import android.view.Window
import android.webkit.CookieManager import android.webkit.CookieManager
import android.webkit.WebView import android.webkit.WebView
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
@@ -37,7 +34,6 @@ import androidx.appcompat.app.AppCompatDialog
import androidx.core.app.ActivityOptionsCompat import androidx.core.app.ActivityOptionsCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
import androidx.core.os.LocaleListCompat import androidx.core.os.LocaleListCompat
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
@@ -46,15 +42,8 @@ import androidx.lifecycle.coroutineScope
import androidx.webkit.WebViewCompat import androidx.webkit.WebViewCompat
import androidx.webkit.WebViewFeature import androidx.webkit.WebViewFeature
import androidx.work.CoroutineWorker import androidx.work.CoroutineWorker
import com.google.android.material.elevation.ElevationOverlayProvider
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runInterruptible import kotlinx.coroutines.runInterruptible
import okio.IOException import okio.IOException
@@ -68,7 +57,6 @@ import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserException import org.xmlpull.v1.XmlPullParserException
import java.io.File import java.io.File
import kotlin.math.roundToLong import kotlin.math.roundToLong
import com.google.android.material.R as materialR
val Context.activityManager: ActivityManager? val Context.activityManager: ActivityManager?
get() = getSystemService(ACTIVITY_SERVICE) as? ActivityManager get() = getSystemService(ACTIVITY_SERVICE) as? ActivityManager
@@ -101,25 +89,6 @@ fun <I> ActivityResultLauncher<I>.tryLaunch(
e.printStackTraceDebug() e.printStackTraceDebug()
}.isSuccess }.isSuccess
fun SharedPreferences.observe(): Flow<String?> = callbackFlow {
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
trySendBlocking(key)
}
registerOnSharedPreferenceChangeListener(listener)
awaitClose {
unregisterOnSharedPreferenceChangeListener(listener)
}
}
fun <T> SharedPreferences.observe(key: String, valueProducer: suspend () -> T): Flow<T> = flow {
emit(valueProducer())
observe().collect { upstreamKey ->
if (upstreamKey == key) {
emit(valueProducer())
}
}
}.distinctUntilChanged()
fun Lifecycle.postDelayed(delay: Long, runnable: Runnable) { fun Lifecycle.postDelayed(delay: Long, runnable: Runnable) {
coroutineScope.launch { coroutineScope.launch {
delay(delay) delay(delay)

View File

@@ -1,8 +1,16 @@
package org.koitharu.kotatsu.core.util.ext package org.koitharu.kotatsu.core.util.ext
import android.content.SharedPreferences import android.content.SharedPreferences
import androidx.collection.ArraySet
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.MultiSelectListPreference import androidx.preference.MultiSelectListPreference
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
import org.json.JSONArray
fun ListPreference.setDefaultValueCompat(defaultValue: String) { fun ListPreference.setDefaultValueCompat(defaultValue: String) {
if (value == null) { if (value == null) {
@@ -28,3 +36,44 @@ fun <E : Enum<E>> SharedPreferences.getEnumValue(key: String, defaultValue: E):
fun <E : Enum<E>> SharedPreferences.Editor.putEnumValue(key: String, value: E?) { fun <E : Enum<E>> SharedPreferences.Editor.putEnumValue(key: String, value: E?) {
putString(key, value?.name) putString(key, value?.name)
} }
fun SharedPreferences.observe(): Flow<String?> = callbackFlow {
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
trySendBlocking(key)
}
registerOnSharedPreferenceChangeListener(listener)
awaitClose {
unregisterOnSharedPreferenceChangeListener(listener)
}
}
fun <T> SharedPreferences.observe(key: String, valueProducer: suspend () -> T): Flow<T> = flow {
emit(valueProducer())
observe().collect { upstreamKey ->
if (upstreamKey == key) {
emit(valueProducer())
}
}
}.distinctUntilChanged()
fun SharedPreferences.Editor.putAll(values: Map<String, *>) {
values.forEach { e ->
when (val v = e.value) {
is Boolean -> putBoolean(e.key, v)
is Int -> putInt(e.key, v)
is Long -> putLong(e.key, v)
is Float -> putFloat(e.key, v)
is String -> putString(e.key, v)
is JSONArray -> putStringSet(e.key, v.toStringSet())
}
}
}
private fun JSONArray.toStringSet(): Set<String> {
val len = length()
val result = ArraySet<String>(len)
for (i in 0 until len) {
result.add(getString(i))
}
return result
}

View File

@@ -14,6 +14,9 @@ data class Progress(
val isFull: Boolean val isFull: Boolean
get() = progress == total get() = progress == total
val isIndeterminate: Boolean
get() = total < 0
override fun compareTo(other: Progress): Int = if (total == other.total) { override fun compareTo(other: Progress): Int = if (total == other.total) {
progress.compareTo(other.progress) progress.compareTo(other.progress)
} else { } else {
@@ -44,4 +47,9 @@ data class Progress(
) )
fun percentSting() = (percent * 100f).toInt().toString() fun percentSting() = (percent * 100f).toInt().toString()
companion object {
val INDETERMINATE = Progress(0, -1)
}
} }

View File

@@ -3,16 +3,19 @@ package org.koitharu.kotatsu.reader.data
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import androidx.core.content.edit import androidx.core.content.edit
import dagger.Reusable
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import org.koitharu.kotatsu.core.util.ext.getEnumValue import org.koitharu.kotatsu.core.util.ext.getEnumValue
import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.putAll
import org.koitharu.kotatsu.core.util.ext.putEnumValue import org.koitharu.kotatsu.core.util.ext.putEnumValue
import org.koitharu.kotatsu.reader.domain.TapGridArea import org.koitharu.kotatsu.reader.domain.TapGridArea
import org.koitharu.kotatsu.reader.ui.tapgrid.TapAction import org.koitharu.kotatsu.reader.ui.tapgrid.TapAction
import javax.inject.Inject import javax.inject.Inject
@Reusable
class TapGridSettings @Inject constructor(@ApplicationContext context: Context) { class TapGridSettings @Inject constructor(@ApplicationContext context: Context) {
private val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) private val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
@@ -43,6 +46,10 @@ class TapGridSettings @Inject constructor(@ApplicationContext context: Context)
fun observe() = prefs.observe().flowOn(Dispatchers.IO) fun observe() = prefs.observe().flowOn(Dispatchers.IO)
fun getAllValues(): Map<String, *> = prefs.all
fun upsertAll(m: Map<String, *>) = prefs.edit { putAll(m) }
private fun initPrefs(withDefaultValues: Boolean) { private fun initPrefs(withDefaultValues: Boolean) {
prefs.edit { prefs.edit {
clear() clear()

View File

@@ -15,6 +15,7 @@ import org.koitharu.kotatsu.core.backup.BackupZipOutput
import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.core.db.MangaDatabase
import org.koitharu.kotatsu.core.exceptions.BadBackupFormatException import org.koitharu.kotatsu.core.exceptions.BadBackupFormatException
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.reader.data.TapGridSettings
import java.io.File import java.io.File
import java.io.FileDescriptor import java.io.FileDescriptor
import java.io.FileInputStream import java.io.FileInputStream
@@ -38,7 +39,14 @@ class AppBackupAgent : BackupAgent() {
override fun onFullBackup(data: FullBackupDataOutput) { override fun onFullBackup(data: FullBackupDataOutput) {
super.onFullBackup(data) super.onFullBackup(data)
val file = val file =
createBackupFile(this, BackupRepository(MangaDatabase(applicationContext), AppSettings(applicationContext))) createBackupFile(
this,
BackupRepository(
MangaDatabase(context = applicationContext),
AppSettings(applicationContext),
TapGridSettings(applicationContext),
),
)
try { try {
fullBackupFile(file, data) fullBackupFile(file, data)
} finally { } finally {
@@ -58,7 +66,11 @@ class AppBackupAgent : BackupAgent() {
restoreBackupFile( restoreBackupFile(
data.fileDescriptor, data.fileDescriptor,
size, size,
BackupRepository(MangaDatabase(applicationContext), AppSettings(applicationContext)), BackupRepository(
db = MangaDatabase(applicationContext),
settings = AppSettings(applicationContext),
tapGridSettings = TapGridSettings(applicationContext),
),
) )
destination.delete() destination.delete()
} else { } else {
@@ -76,6 +88,7 @@ class AppBackupAgent : BackupAgent() {
backup.put(repository.dumpBookmarks()) backup.put(repository.dumpBookmarks())
backup.put(repository.dumpSources()) backup.put(repository.dumpSources())
backup.put(repository.dumpSettings()) backup.put(repository.dumpSettings())
backup.put(repository.dumpReaderGridSettings())
backup.finish() backup.finish()
backup.file backup.file
} }
@@ -103,6 +116,7 @@ class AppBackupAgent : BackupAgent() {
backup.getEntry(BackupEntry.Name.BOOKMARKS)?.let { repository.restoreBookmarks(it) } backup.getEntry(BackupEntry.Name.BOOKMARKS)?.let { repository.restoreBookmarks(it) }
backup.getEntry(BackupEntry.Name.SOURCES)?.let { repository.restoreSources(it) } backup.getEntry(BackupEntry.Name.SOURCES)?.let { repository.restoreSources(it) }
backup.getEntry(BackupEntry.Name.SETTINGS)?.let { repository.restoreSettings(it) } backup.getEntry(BackupEntry.Name.SETTINGS)?.let { repository.restoreSettings(it) }
backup.getEntry(BackupEntry.Name.SETTINGS_READER_GRID)?.let { repository.restoreReaderGridSettings(it) }
} }
} finally { } finally {
backup.close() backup.close()

View File

@@ -16,10 +16,10 @@ import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.core.util.ext.observeEvent
import org.koitharu.kotatsu.core.util.ext.tryLaunch import org.koitharu.kotatsu.core.util.ext.tryLaunch
import org.koitharu.kotatsu.core.util.progress.Progress
import org.koitharu.kotatsu.databinding.DialogProgressBinding import org.koitharu.kotatsu.databinding.DialogProgressBinding
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import kotlin.math.roundToInt
@AndroidEntryPoint @AndroidEntryPoint
class BackupDialogFragment : AlertDialogFragment<DialogProgressBinding>() { class BackupDialogFragment : AlertDialogFragment<DialogProgressBinding>() {
@@ -68,13 +68,14 @@ class BackupDialogFragment : AlertDialogFragment<DialogProgressBinding>() {
dismiss() dismiss()
} }
private fun onProgressChanged(value: Float) { private fun onProgressChanged(value: Progress) {
with(requireViewBinding().progressBar) { with(requireViewBinding().progressBar) {
isVisible = true isVisible = true
val wasIndeterminate = isIndeterminate val wasIndeterminate = isIndeterminate
isIndeterminate = value < 0 isIndeterminate = value.isIndeterminate
if (value >= 0) { if (!value.isIndeterminate) {
setProgressCompat((value * max).roundToInt(), !wasIndeterminate) max = value.total
setProgressCompat(value.progress, !wasIndeterminate)
} }
} }
} }

View File

@@ -20,6 +20,7 @@ data class BackupEntryModel(
BackupEntry.Name.CATEGORIES -> R.string.favourites_categories BackupEntry.Name.CATEGORIES -> R.string.favourites_categories
BackupEntry.Name.FAVOURITES -> R.string.favourites BackupEntry.Name.FAVOURITES -> R.string.favourites
BackupEntry.Name.SETTINGS -> R.string.settings BackupEntry.Name.SETTINGS -> R.string.settings
BackupEntry.Name.SETTINGS_READER_GRID -> R.string.reader_actions
BackupEntry.Name.BOOKMARKS -> R.string.bookmarks BackupEntry.Name.BOOKMARKS -> R.string.bookmarks
BackupEntry.Name.SOURCES -> R.string.remote_sources BackupEntry.Name.SOURCES -> R.string.remote_sources
} }

View File

@@ -9,6 +9,7 @@ import org.koitharu.kotatsu.core.backup.BackupZipOutput
import org.koitharu.kotatsu.core.ui.BaseViewModel import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
import org.koitharu.kotatsu.core.util.ext.call import org.koitharu.kotatsu.core.util.ext.call
import org.koitharu.kotatsu.core.util.progress.Progress
import java.io.File import java.io.File
import javax.inject.Inject import javax.inject.Inject
@@ -18,35 +19,37 @@ class BackupViewModel @Inject constructor(
@ApplicationContext context: Context, @ApplicationContext context: Context,
) : BaseViewModel() { ) : BaseViewModel() {
val progress = MutableStateFlow(-1f) val progress = MutableStateFlow(Progress.INDETERMINATE)
val onBackupDone = MutableEventFlow<File>() val onBackupDone = MutableEventFlow<File>()
init { init {
launchLoadingJob { launchLoadingJob {
val file = BackupZipOutput.createTemp(context).use { backup -> val file = BackupZipOutput.createTemp(context).use { backup ->
val step = 1f / 6f progress.value = Progress(0, 7)
backup.put(repository.createIndex()) backup.put(repository.createIndex())
progress.value = 0f
backup.put(repository.dumpHistory()) backup.put(repository.dumpHistory())
progress.value = progress.value.inc()
progress.value += step
backup.put(repository.dumpCategories()) backup.put(repository.dumpCategories())
progress.value = progress.value.inc()
progress.value += step
backup.put(repository.dumpFavourites()) backup.put(repository.dumpFavourites())
progress.value = progress.value.inc()
progress.value += step
backup.put(repository.dumpBookmarks()) backup.put(repository.dumpBookmarks())
progress.value = progress.value.inc()
progress.value += step
backup.put(repository.dumpSources()) backup.put(repository.dumpSources())
progress.value = progress.value.inc()
progress.value += step
backup.put(repository.dumpSettings()) backup.put(repository.dumpSettings())
progress.value = progress.value.inc()
backup.put(repository.dumpReaderGridSettings())
progress.value = progress.value.inc()
backup.finish() backup.finish()
progress.value = 1f
backup.file backup.file
} }
onBackupDone.call(file) onBackupDone.call(file)

View File

@@ -43,6 +43,7 @@ class PeriodicalBackupService : CoroutineIntentService() {
backup.put(repository.dumpBookmarks()) backup.put(repository.dumpBookmarks())
backup.put(repository.dumpSources()) backup.put(repository.dumpSources())
backup.put(repository.dumpSettings()) backup.put(repository.dumpSettings())
backup.put(repository.dumpReaderGridSettings())
backup.finish() backup.finish()
} }
externalBackupStorage.put(output.file) externalBackupStorage.put(output.file)

View File

@@ -164,6 +164,15 @@ class RestoreService : CoroutineIntentService() {
notify() notify()
if (BackupEntry.Name.SETTINGS_READER_GRID in entries) {
input.getEntry(BackupEntry.Name.SETTINGS_READER_GRID)?.let {
result += repository.restoreReaderGridSettings(it)
}
progress++
}
notify()
return result return result
} }

View File

@@ -82,6 +82,7 @@
android:layout_marginEnd="@dimen/margin_normal" android:layout_marginEnd="@dimen/margin_normal"
android:ellipsize="end" android:ellipsize="end"
android:gravity="center_vertical|start" android:gravity="center_vertical|start"
android:maxLines="3"
android:textAppearance="?attr/textAppearanceBodyLarge" android:textAppearance="?attr/textAppearanceBodyLarge"
app:layout_constraintBottom_toBottomOf="@id/guideline" app:layout_constraintBottom_toBottomOf="@id/guideline"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"