fix: upstream conflict.

Signed-off-by: KaiserBh <kaiserbh@proton.me>
This commit is contained in:
KaiserBh 2023-11-20 17:57:04 +11:00
commit d180d1de45
No known key found for this signature in database
GPG Key ID: 14D73B142042BBA9
13 changed files with 105 additions and 138 deletions

View File

@ -1,5 +1,6 @@
package eu.kanade.presentation.more.settings.screen package eu.kanade.presentation.more.settings.screen
import android.content.ActivityNotFoundException
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
@ -26,8 +27,11 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.core.net.toUri
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import com.hippo.unifile.UniFile
import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.Preference
import eu.kanade.presentation.more.settings.screen.data.CreateBackupScreen import eu.kanade.presentation.more.settings.screen.data.CreateBackupScreen
import eu.kanade.presentation.more.settings.widget.BasePreferenceWidget import eu.kanade.presentation.more.settings.widget.BasePreferenceWidget
@ -54,6 +58,7 @@ import tachiyomi.core.util.lang.withUIContext
import tachiyomi.core.util.system.logcat import tachiyomi.core.util.system.logcat
import tachiyomi.domain.backup.service.BackupPreferences import tachiyomi.domain.backup.service.BackupPreferences
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.storage.service.StoragePreferences
import tachiyomi.domain.sync.SyncPreferences import tachiyomi.domain.sync.SyncPreferences
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
@ -70,6 +75,7 @@ object SettingsDataScreen : SearchableSettings {
@Composable @Composable
override fun getPreferences(): List<Preference> { override fun getPreferences(): List<Preference> {
val backupPreferences = Injekt.get<BackupPreferences>() val backupPreferences = Injekt.get<BackupPreferences>()
val storagePreferences = Injekt.get<StoragePreferences>()
PermissionRequestHelper.requestStoragePermission() PermissionRequestHelper.requestStoragePermission()
@ -77,11 +83,50 @@ object SettingsDataScreen : SearchableSettings {
val syncService by syncPreferences.syncService().collectAsState() val syncService by syncPreferences.syncService().collectAsState()
return listOf( return listOf(
getStorageLocationPref(storagePreferences = storagePreferences),
Preference.PreferenceItem.InfoPreference(stringResource(MR.strings.pref_storage_location_info)),
getBackupAndRestoreGroup(backupPreferences = backupPreferences), getBackupAndRestoreGroup(backupPreferences = backupPreferences),
getDataGroup(), getDataGroup(),
) + getSyncPreferences(syncPreferences = syncPreferences, syncService = syncService) ) + getSyncPreferences(syncPreferences = syncPreferences, syncService = syncService)
} }
@Composable
private fun getStorageLocationPref(
storagePreferences: StoragePreferences,
): Preference.PreferenceItem.TextPreference {
val context = LocalContext.current
val storageDirPref = storagePreferences.baseStorageDirectory()
val storageDir by storageDirPref.collectAsState()
val pickStorageLocation = rememberLauncherForActivityResult(
contract = ActivityResultContracts.OpenDocumentTree(),
) { uri ->
if (uri != null) {
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
context.contentResolver.takePersistableUriPermission(uri, flags)
val file = UniFile.fromUri(context, uri)
storageDirPref.set(file.uri.toString())
}
}
return Preference.PreferenceItem.TextPreference(
title = stringResource(MR.strings.pref_storage_location),
subtitle = remember(storageDir) {
(UniFile.fromUri(context, storageDir.toUri())?.filePath)
} ?: stringResource(MR.strings.invalid_location, storageDir),
onClick = {
try {
pickStorageLocation.launch(null)
} catch (e: ActivityNotFoundException) {
context.toast(MR.strings.file_picker_error)
}
},
)
}
@Composable @Composable
private fun getBackupAndRestoreGroup(backupPreferences: BackupPreferences): Preference.PreferenceGroup { private fun getBackupAndRestoreGroup(backupPreferences: BackupPreferences): Preference.PreferenceGroup {
val context = LocalContext.current val context = LocalContext.current

View File

@ -1,9 +1,5 @@
package eu.kanade.presentation.more.settings.screen package eu.kanade.presentation.more.settings.screen
import android.content.Intent
import android.os.Environment
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@ -12,10 +8,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.util.fastMap import androidx.compose.ui.util.fastMap
import androidx.core.net.toUri
import com.hippo.unifile.UniFile
import eu.kanade.presentation.category.visualName import eu.kanade.presentation.category.visualName
import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.Preference
import eu.kanade.presentation.more.settings.widget.TriStateListDialog import eu.kanade.presentation.more.settings.widget.TriStateListDialog
@ -29,7 +22,6 @@ import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.io.File
object SettingsDownloadScreen : SearchableSettings { object SettingsDownloadScreen : SearchableSettings {
@ -44,7 +36,6 @@ object SettingsDownloadScreen : SearchableSettings {
val downloadPreferences = remember { Injekt.get<DownloadPreferences>() } val downloadPreferences = remember { Injekt.get<DownloadPreferences>() }
return listOf( return listOf(
getDownloadLocationPreference(downloadPreferences = downloadPreferences),
Preference.PreferenceItem.SwitchPreference( Preference.PreferenceItem.SwitchPreference(
pref = downloadPreferences.downloadOnlyOverWifi(), pref = downloadPreferences.downloadOnlyOverWifi(),
title = stringResource(MR.strings.connected_to_wifi), title = stringResource(MR.strings.connected_to_wifi),
@ -70,67 +61,6 @@ object SettingsDownloadScreen : SearchableSettings {
) )
} }
@Composable
private fun getDownloadLocationPreference(
downloadPreferences: DownloadPreferences,
): Preference.PreferenceItem.ListPreference<String> {
val context = LocalContext.current
val currentDirPref = downloadPreferences.downloadsDirectory()
val currentDir by currentDirPref.collectAsState()
val pickLocation = rememberLauncherForActivityResult(
contract = ActivityResultContracts.OpenDocumentTree(),
) { uri ->
if (uri != null) {
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
context.contentResolver.takePersistableUriPermission(uri, flags)
val file = UniFile.fromUri(context, uri)
currentDirPref.set(file.uri.toString())
}
}
val defaultDirPair = rememberDefaultDownloadDir()
val customDirEntryKey = currentDir.takeIf { it != defaultDirPair.first } ?: "custom"
return Preference.PreferenceItem.ListPreference(
pref = currentDirPref,
title = stringResource(MR.strings.pref_download_directory),
subtitleProvider = { value, _ ->
remember(value) {
UniFile.fromUri(context, value.toUri())?.filePath
} ?: stringResource(MR.strings.invalid_location, value)
},
entries = mapOf(
defaultDirPair,
customDirEntryKey to stringResource(MR.strings.custom_dir),
),
onValueChanged = {
val default = it == defaultDirPair.first
if (!default) {
pickLocation.launch(null)
}
default // Don't update when non-default chosen
},
)
}
@Composable
private fun rememberDefaultDownloadDir(): Pair<String, String> {
val appName = stringResource(MR.strings.app_name)
return remember {
val file = UniFile.fromFile(
File(
"${Environment.getExternalStorageDirectory().absolutePath}${File.separator}$appName",
"downloads",
),
)!!
file.uri.toString() to file.filePath!!
}
}
@Composable @Composable
private fun getDeleteChaptersGroup( private fun getDeleteChaptersGroup(
downloadPreferences: DownloadPreferences, downloadPreferences: DownloadPreferences,

View File

@ -21,6 +21,7 @@ import eu.kanade.tachiyomi.util.system.workManager
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.util.system.logcat import tachiyomi.core.util.system.logcat
import tachiyomi.domain.backup.service.BackupPreferences import tachiyomi.domain.backup.service.BackupPreferences
import tachiyomi.domain.storage.service.StoragePreferences
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.util.Date import java.util.Date
@ -39,8 +40,9 @@ class BackupCreateJob(private val context: Context, workerParams: WorkerParamete
if (isAutoBackup && BackupRestoreJob.isRunning(context)) return Result.retry() if (isAutoBackup && BackupRestoreJob.isRunning(context)) return Result.retry()
val backupPreferences = Injekt.get<BackupPreferences>() val backupPreferences = Injekt.get<BackupPreferences>()
val uri = inputData.getString(LOCATION_URI_KEY)?.toUri() val uri = inputData.getString(LOCATION_URI_KEY)?.toUri()
?: backupPreferences.backupsDirectory().get().toUri() ?: getAutomaticBackupLocation()
val flags = inputData.getInt(BACKUP_FLAGS_KEY, BackupCreateFlags.AutomaticDefaults) val flags = inputData.getInt(BACKUP_FLAGS_KEY, BackupCreateFlags.AutomaticDefaults)
try { try {
@ -73,6 +75,15 @@ class BackupCreateJob(private val context: Context, workerParams: WorkerParamete
) )
} }
private fun getAutomaticBackupLocation(): Uri {
val storagePreferences = Injekt.get<StoragePreferences>()
return storagePreferences.baseStorageDirectory().get().let {
val dir = UniFile.fromUri(context, it.toUri())
.createDirectory(StoragePreferences.BACKUP_DIR)
dir.uri
}
}
companion object { companion object {
fun isManualJobRunning(context: Context): Boolean { fun isManualJobRunning(context: Context): Boolean {
return context.workManager.isRunning(TAG_MANUAL) return context.workManager.isRunning(TAG_MANUAL)

View File

@ -92,8 +92,8 @@ class BackupCreator(
file = ( file = (
if (isAutoBackup) { if (isAutoBackup) {
// Get dir of file and create // Get dir of file and create
var dir = UniFile.fromUri(context, uri) val dir = UniFile.fromUri(context, uri)
dir = dir.createDirectory("automatic") .createDirectory("automatic")
// Delete older backups // Delete older backups
dir.listFiles { _, filename -> Backup.filenameRegex.matches(filename) } dir.listFiles { _, filename -> Backup.filenameRegex.matches(filename) }

View File

@ -44,9 +44,9 @@ import tachiyomi.core.util.lang.launchIO
import tachiyomi.core.util.lang.launchNonCancellable import tachiyomi.core.util.lang.launchNonCancellable
import tachiyomi.core.util.system.logcat import tachiyomi.core.util.system.logcat
import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.download.service.DownloadPreferences
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.source.service.SourceManager
import tachiyomi.domain.storage.service.StoragePreferences
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.io.File import java.io.File
@ -64,7 +64,7 @@ class DownloadCache(
private val provider: DownloadProvider = Injekt.get(), private val provider: DownloadProvider = Injekt.get(),
private val sourceManager: SourceManager = Injekt.get(), private val sourceManager: SourceManager = Injekt.get(),
private val extensionManager: ExtensionManager = Injekt.get(), private val extensionManager: ExtensionManager = Injekt.get(),
private val downloadPreferences: DownloadPreferences = Injekt.get(), private val storagePreferences: StoragePreferences = Injekt.get(),
) { ) {
private val scope = CoroutineScope(Dispatchers.IO) private val scope = CoroutineScope(Dispatchers.IO)
@ -98,7 +98,7 @@ class DownloadCache(
private var rootDownloadsDir = RootDirectory(getDirectoryFromPreference()) private var rootDownloadsDir = RootDirectory(getDirectoryFromPreference())
init { init {
downloadPreferences.downloadsDirectory().changes() storagePreferences.baseStorageDirectory().changes()
.onEach { .onEach {
rootDownloadsDir = RootDirectory(getDirectoryFromPreference()) rootDownloadsDir = RootDirectory(getDirectoryFromPreference())
invalidateCache() invalidateCache()
@ -297,8 +297,8 @@ class DownloadCache(
* Returns the downloads directory from the user's preferences. * Returns the downloads directory from the user's preferences.
*/ */
private fun getDirectoryFromPreference(): UniFile { private fun getDirectoryFromPreference(): UniFile {
val dir = downloadPreferences.downloadsDirectory().get() return UniFile.fromUri(context, storagePreferences.baseStorageDirectory().get().toUri())
return UniFile.fromUri(context, dir.toUri()) .createDirectory(StoragePreferences.DOWNLOADS_DIR)
} }
/** /**

View File

@ -12,8 +12,8 @@ import logcat.LogPriority
import tachiyomi.core.i18n.stringResource import tachiyomi.core.i18n.stringResource
import tachiyomi.core.util.system.logcat import tachiyomi.core.util.system.logcat
import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.download.service.DownloadPreferences
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.storage.service.StoragePreferences
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -26,7 +26,7 @@ import uy.kohesive.injekt.api.get
*/ */
class DownloadProvider( class DownloadProvider(
private val context: Context, private val context: Context,
downloadPreferences: DownloadPreferences = Injekt.get(), private val storagePreferences: StoragePreferences = Injekt.get(),
) { ) {
private val scope = MainScope() private val scope = MainScope()
@ -34,18 +34,23 @@ class DownloadProvider(
/** /**
* The root directory for downloads. * The root directory for downloads.
*/ */
private var downloadsDir = downloadPreferences.downloadsDirectory().get().let { private var downloadsDir = setDownloadsLocation()
val dir = UniFile.fromUri(context, it.toUri())
DiskUtil.createNoMediaFile(dir, context)
dir
}
init { init {
downloadPreferences.downloadsDirectory().changes() storagePreferences.baseStorageDirectory().changes()
.onEach { downloadsDir = UniFile.fromUri(context, it.toUri()) } .onEach { downloadsDir = setDownloadsLocation() }
.launchIn(scope) .launchIn(scope)
} }
private fun setDownloadsLocation(): UniFile {
return storagePreferences.baseStorageDirectory().get().let {
val dir = UniFile.fromUri(context, it.toUri())
.createDirectory(StoragePreferences.DOWNLOADS_DIR)
DiskUtil.createNoMediaFile(dir, context)
dir
}
}
/** /**
* Returns the download directory for a manga. For internal use only. * Returns the download directory for a manga. For internal use only.
* *

View File

@ -11,11 +11,11 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.util.system.isDevFlavor import eu.kanade.tachiyomi.util.system.isDevFlavor
import tachiyomi.core.preference.AndroidPreferenceStore import tachiyomi.core.preference.AndroidPreferenceStore
import tachiyomi.core.preference.PreferenceStore import tachiyomi.core.preference.PreferenceStore
import tachiyomi.core.provider.AndroidBackupFolderProvider import tachiyomi.core.provider.AndroidStorageFolderProvider
import tachiyomi.core.provider.AndroidDownloadFolderProvider
import tachiyomi.domain.backup.service.BackupPreferences import tachiyomi.domain.backup.service.BackupPreferences
import tachiyomi.domain.download.service.DownloadPreferences import tachiyomi.domain.download.service.DownloadPreferences
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.storage.service.StoragePreferences
import tachiyomi.domain.sync.SyncPreferences import tachiyomi.domain.sync.SyncPreferences
import uy.kohesive.injekt.api.InjektModule import uy.kohesive.injekt.api.InjektModule
import uy.kohesive.injekt.api.InjektRegistrar import uy.kohesive.injekt.api.InjektRegistrar
@ -50,20 +50,17 @@ class PreferenceModule(val app: Application) : InjektModule {
TrackPreferences(get()) TrackPreferences(get())
} }
addSingletonFactory { addSingletonFactory {
AndroidDownloadFolderProvider(app) DownloadPreferences(get())
} }
addSingletonFactory { addSingletonFactory {
DownloadPreferences( BackupPreferences(get())
folderProvider = get<AndroidDownloadFolderProvider>(),
preferenceStore = get(),
)
} }
addSingletonFactory { addSingletonFactory {
AndroidBackupFolderProvider(app) AndroidStorageFolderProvider(app)
} }
addSingletonFactory { addSingletonFactory {
BackupPreferences( StoragePreferences(
folderProvider = get<AndroidBackupFolderProvider>(), folderProvider = get<AndroidStorageFolderProvider>(),
preferenceStore = get(), preferenceStore = get(),
) )
} }

View File

@ -1,25 +0,0 @@
package tachiyomi.core.provider
import android.content.Context
import android.os.Environment
import androidx.core.net.toUri
import tachiyomi.core.i18n.stringResource
import tachiyomi.i18n.MR
import java.io.File
class AndroidDownloadFolderProvider(
val context: Context,
) : FolderProvider {
override fun directory(): File {
return File(
Environment.getExternalStorageDirectory().absolutePath + File.separator +
context.stringResource(MR.strings.app_name),
"downloads",
)
}
override fun path(): String {
return directory().toUri().toString()
}
}

View File

@ -7,7 +7,7 @@ import tachiyomi.core.i18n.stringResource
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import java.io.File import java.io.File
class AndroidBackupFolderProvider( class AndroidStorageFolderProvider(
private val context: Context, private val context: Context,
) : FolderProvider { ) : FolderProvider {
@ -15,7 +15,6 @@ class AndroidBackupFolderProvider(
return File( return File(
Environment.getExternalStorageDirectory().absolutePath + File.separator + Environment.getExternalStorageDirectory().absolutePath + File.separator +
context.stringResource(MR.strings.app_name), context.stringResource(MR.strings.app_name),
"backup",
) )
} }

View File

@ -2,15 +2,11 @@ package tachiyomi.domain.backup.service
import tachiyomi.core.preference.Preference import tachiyomi.core.preference.Preference
import tachiyomi.core.preference.PreferenceStore import tachiyomi.core.preference.PreferenceStore
import tachiyomi.core.provider.FolderProvider
class BackupPreferences( class BackupPreferences(
private val folderProvider: FolderProvider,
private val preferenceStore: PreferenceStore, private val preferenceStore: PreferenceStore,
) { ) {
fun backupsDirectory() = preferenceStore.getString("backup_directory", folderProvider.path())
fun backupInterval() = preferenceStore.getInt("backup_interval", 12) fun backupInterval() = preferenceStore.getInt("backup_interval", 12)
fun lastAutoBackupTimestamp() = preferenceStore.getLong(Preference.appStateKey("last_auto_backup_timestamp"), 0L) fun lastAutoBackupTimestamp() = preferenceStore.getLong(Preference.appStateKey("last_auto_backup_timestamp"), 0L)

View File

@ -1,18 +1,11 @@
package tachiyomi.domain.download.service package tachiyomi.domain.download.service
import tachiyomi.core.preference.PreferenceStore import tachiyomi.core.preference.PreferenceStore
import tachiyomi.core.provider.FolderProvider
class DownloadPreferences( class DownloadPreferences(
private val folderProvider: FolderProvider,
private val preferenceStore: PreferenceStore, private val preferenceStore: PreferenceStore,
) { ) {
fun downloadsDirectory() = preferenceStore.getString(
"download_directory",
folderProvider.path(),
)
fun downloadOnlyOverWifi() = preferenceStore.getBoolean( fun downloadOnlyOverWifi() = preferenceStore.getBoolean(
"pref_download_only_over_wifi_key", "pref_download_only_over_wifi_key",
true, true,

View File

@ -0,0 +1,17 @@
package tachiyomi.domain.storage.service
import tachiyomi.core.preference.PreferenceStore
import tachiyomi.core.provider.FolderProvider
class StoragePreferences(
private val folderProvider: FolderProvider,
private val preferenceStore: PreferenceStore,
) {
fun baseStorageDirectory() = preferenceStore.getString("storage_dir", folderProvider.path())
companion object {
const val BACKUP_DIR = "backup"
const val DOWNLOADS_DIR = "downloads"
}
}

View File

@ -431,13 +431,11 @@
<string name="pref_lowest">Lowest</string> <string name="pref_lowest">Lowest</string>
<!-- Downloads section --> <!-- Downloads section -->
<string name="pref_download_directory">Download location</string>
<string name="pref_category_delete_chapters">Delete chapters</string> <string name="pref_category_delete_chapters">Delete chapters</string>
<string name="pref_remove_after_marked_as_read">After manually marked as read</string> <string name="pref_remove_after_marked_as_read">After manually marked as read</string>
<string name="pref_remove_after_read">After reading automatically delete</string> <string name="pref_remove_after_read">After reading automatically delete</string>
<string name="pref_remove_bookmarked_chapters">Allow deleting bookmarked chapters</string> <string name="pref_remove_bookmarked_chapters">Allow deleting bookmarked chapters</string>
<string name="pref_remove_exclude_categories">Excluded categories</string> <string name="pref_remove_exclude_categories">Excluded categories</string>
<string name="custom_dir">Custom location</string>
<string name="invalid_location">Invalid location: %s</string> <string name="invalid_location">Invalid location: %s</string>
<string name="disabled">Disabled</string> <string name="disabled">Disabled</string>
<string name="last_read_chapter">Last read chapter</string> <string name="last_read_chapter">Last read chapter</string>
@ -470,11 +468,12 @@
<string name="pref_hide_in_library_items">Hide entries already in library</string> <string name="pref_hide_in_library_items">Hide entries already in library</string>
<!-- Data and storage section --> <!-- Data and storage section -->
<string name="pref_storage_location">Storage location</string>
<string name="pref_storage_location_info">Used for automatic backups, chapter downloads, and local source.</string>
<string name="pref_create_backup">Create backup</string> <string name="pref_create_backup">Create backup</string>
<string name="pref_create_backup_summ">Can be used to restore current library</string> <string name="pref_create_backup_summ">Can be used to restore current library</string>
<string name="pref_restore_backup">Restore backup</string> <string name="pref_restore_backup">Restore backup</string>
<string name="pref_restore_backup_summ">Restore library from backup file</string> <string name="pref_restore_backup_summ">Restore library from backup file</string>
<string name="pref_backup_directory">Backup location</string>
<string name="pref_backup_interval">Automatic backup frequency</string> <string name="pref_backup_interval">Automatic backup frequency</string>
<string name="action_create">Create</string> <string name="action_create">Create</string>
<string name="backup_created">Backup created</string> <string name="backup_created">Backup created</string>