Merge branch 'pr14'

resolved all confilcts
This commit is contained in:
jmir1 2021-05-18 14:46:35 +02:00
commit f99b6572bc
17 changed files with 42 additions and 408 deletions

View file

@ -78,7 +78,8 @@ class AnimelibUpdateNotifier(private val context: Context) {
context.notificationManager.notify(
Notifications.ID_LIBRARY_PROGRESS,
progressNotificationBuilder
.setContentTitle(title)
.setContentTitle(title.chop(40))
.setContentText("($current/$total)")
.setProgress(total, current, false)
.build()
)

View file

@ -8,7 +8,6 @@ object BackupConst {
const val EXTRA_URI = "$ID.$NAME.EXTRA_URI"
const val EXTRA_FLAGS = "$ID.$NAME.EXTRA_FLAGS"
const val EXTRA_MODE = "$ID.$NAME.EXTRA_MODE"
const val EXTRA_TYPE = "$ID.$NAME.EXTRA_TYPE"
const val BACKUP_TYPE_LEGACY = 0
const val BACKUP_TYPE_FULL = 1

View file

@ -10,7 +10,6 @@ import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.data.backup.full.FullBackupManager
import eu.kanade.tachiyomi.data.backup.legacy.LegacyBackupManager
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.system.acquireWakeLock
import eu.kanade.tachiyomi.util.system.isServiceRunning
@ -52,12 +51,11 @@ class BackupCreateService : Service() {
* @param uri path of Uri
* @param flags determines what to backup
*/
fun start(context: Context, uri: Uri, flags: Int, type: Int) {
fun start(context: Context, uri: Uri, flags: Int) {
if (!isRunning(context)) {
val intent = Intent(context, BackupCreateService::class.java).apply {
putExtra(BackupConst.EXTRA_URI, uri)
putExtra(BackupConst.EXTRA_FLAGS, flags)
putExtra(BackupConst.EXTRA_TYPE, type)
}
ContextCompat.startForegroundService(context, intent)
}
@ -107,15 +105,9 @@ class BackupCreateService : Service() {
try {
val uri = intent.getParcelableExtra<Uri>(BackupConst.EXTRA_URI)
val backupFlags = intent.getIntExtra(BackupConst.EXTRA_FLAGS, 0)
val backupType = intent.getIntExtra(BackupConst.EXTRA_TYPE, BackupConst.BACKUP_TYPE_LEGACY)
val backupManager = when (backupType) {
BackupConst.BACKUP_TYPE_FULL -> FullBackupManager(this)
else -> LegacyBackupManager(this)
}
val backupFileUri = backupManager.createBackup(uri, backupFlags, false)?.toUri()
val backupFileUri = FullBackupManager(this).createBackup(uri, backupFlags, false)?.toUri()
val unifile = UniFile.fromUri(this, backupFileUri)
notifier.showBackupComplete(unifile, backupType == BackupConst.BACKUP_TYPE_LEGACY)
notifier.showBackupComplete(unifile)
} catch (e: Exception) {
notifier.showBackupError(e.message)
}

View file

@ -8,7 +8,6 @@ import androidx.work.WorkManager
import androidx.work.Worker
import androidx.work.WorkerParameters
import eu.kanade.tachiyomi.data.backup.full.FullBackupManager
import eu.kanade.tachiyomi.data.backup.legacy.LegacyBackupManager
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@ -23,9 +22,6 @@ class BackupCreatorJob(private val context: Context, workerParams: WorkerParamet
val flags = BackupCreateService.BACKUP_ALL
return try {
FullBackupManager(context).createBackup(uri, flags, true)
if (preferences.createLegacyBackup().get()) {
LegacyBackupManager(context).createBackup(uri, flags, true)
}
Result.success()
} catch (e: Exception) {
Result.failure()

View file

@ -60,7 +60,7 @@ class BackupNotifier(private val context: Context) {
}
}
fun showBackupComplete(unifile: UniFile, isLegacyFormat: Boolean) {
fun showBackupComplete(unifile: UniFile) {
context.notificationManager.cancel(Notifications.ID_BACKUP_PROGRESS)
with(completeNotificationBuilder) {
@ -73,7 +73,7 @@ class BackupNotifier(private val context: Context) {
addAction(
R.drawable.ic_share_24dp,
context.getString(R.string.action_share),
NotificationReceiver.shareBackupPendingBroadcast(context, unifile.uri, isLegacyFormat, Notifications.ID_BACKUP_COMPLETE)
NotificationReceiver.shareBackupPendingBroadcast(context, unifile.uri, Notifications.ID_BACKUP_COMPLETE)
)
show(Notifications.ID_BACKUP_COMPLETE)

View file

@ -5,37 +5,11 @@ import android.net.Uri
import com.github.salomonbrys.kotson.fromJson
import com.github.salomonbrys.kotson.registerTypeAdapter
import com.github.salomonbrys.kotson.registerTypeHierarchyAdapter
import com.github.salomonbrys.kotson.set
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonArray
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.data.backup.AbstractBackupManager
import eu.kanade.tachiyomi.data.backup.BackupCreateService.Companion.BACKUP_ANIMECATEGORY
import eu.kanade.tachiyomi.data.backup.BackupCreateService.Companion.BACKUP_ANIMECATEGORY_MASK
import eu.kanade.tachiyomi.data.backup.BackupCreateService.Companion.BACKUP_CATEGORY
import eu.kanade.tachiyomi.data.backup.BackupCreateService.Companion.BACKUP_CATEGORY_MASK
import eu.kanade.tachiyomi.data.backup.BackupCreateService.Companion.BACKUP_CHAPTER
import eu.kanade.tachiyomi.data.backup.BackupCreateService.Companion.BACKUP_CHAPTER_MASK
import eu.kanade.tachiyomi.data.backup.BackupCreateService.Companion.BACKUP_EPISODE
import eu.kanade.tachiyomi.data.backup.BackupCreateService.Companion.BACKUP_EPISODE_MASK
import eu.kanade.tachiyomi.data.backup.BackupCreateService.Companion.BACKUP_HISTORY
import eu.kanade.tachiyomi.data.backup.BackupCreateService.Companion.BACKUP_HISTORY_MASK
import eu.kanade.tachiyomi.data.backup.BackupCreateService.Companion.BACKUP_TRACK
import eu.kanade.tachiyomi.data.backup.BackupCreateService.Companion.BACKUP_TRACK_MASK
import eu.kanade.tachiyomi.data.backup.legacy.models.Backup
import eu.kanade.tachiyomi.data.backup.legacy.models.Backup.ANIMECATEGORIES
import eu.kanade.tachiyomi.data.backup.legacy.models.Backup.ANIMEEXTENSIONS
import eu.kanade.tachiyomi.data.backup.legacy.models.Backup.CATEGORIES
import eu.kanade.tachiyomi.data.backup.legacy.models.Backup.CHAPTERS
import eu.kanade.tachiyomi.data.backup.legacy.models.Backup.CURRENT_VERSION
import eu.kanade.tachiyomi.data.backup.legacy.models.Backup.EPISODES
import eu.kanade.tachiyomi.data.backup.legacy.models.Backup.EXTENSIONS
import eu.kanade.tachiyomi.data.backup.legacy.models.Backup.HISTORY
import eu.kanade.tachiyomi.data.backup.legacy.models.Backup.MANGA
import eu.kanade.tachiyomi.data.backup.legacy.models.Backup.TRACK
import eu.kanade.tachiyomi.data.backup.legacy.models.DHistory
import eu.kanade.tachiyomi.data.backup.legacy.serializer.CategoryTypeAdapter
import eu.kanade.tachiyomi.data.backup.legacy.serializer.ChapterTypeAdapter
@ -55,12 +29,9 @@ import eu.kanade.tachiyomi.data.database.models.TrackImpl
import eu.kanade.tachiyomi.data.database.models.toAnimeInfo
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
import eu.kanade.tachiyomi.source.AnimeSource
import eu.kanade.tachiyomi.source.LocalAnimeSource
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.model.toSAnime
import eu.kanade.tachiyomi.source.model.toSManga
import timber.log.Timber
import kotlin.math.max
class LegacyBackupManager(context: Context, version: Int = CURRENT_VERSION) : AbstractBackupManager(context) {
@ -82,268 +53,8 @@ class LegacyBackupManager(context: Context, version: Int = CURRENT_VERSION) : Ab
* @param uri path of Uri
* @param isJob backup called from job
*/
override fun createBackup(uri: Uri, flags: Int, isJob: Boolean): String? {
// Create root object
val root = JsonObject()
// Create manga array
val mangaEntries = JsonArray()
// Create anime array
val animeEntries = JsonArray()
// Create category array
val categoryEntries = JsonArray()
// Create animecategory array
val animecategoryEntries = JsonArray()
// Create extension ID/name mapping
val extensionEntries = JsonArray()
// Create animeextension ID/name mapping
val animeextensionEntries = JsonArray()
// Add value's to root
root[Backup.VERSION] = CURRENT_VERSION
root[Backup.MANGAS] = mangaEntries
root[CATEGORIES] = categoryEntries
root[EXTENSIONS] = extensionEntries
root[Backup.ANIMES] = animeEntries
root[ANIMECATEGORIES] = animecategoryEntries
root[ANIMEEXTENSIONS] = animeextensionEntries
databaseHelper.inTransaction {
val mangas = getFavoriteManga()
val extensions: MutableSet<String> = mutableSetOf()
// Backup library manga and its dependencies
mangas.forEach { manga ->
mangaEntries.add(backupMangaObject(manga, flags))
// Maintain set of extensions/sources used (excludes local source)
if (manga.source != LocalSource.ID) {
sourceManager.get(manga.source)?.let {
extensions.add("${manga.source}:${it.name}")
}
}
}
// Backup categories
if ((flags and BACKUP_CATEGORY_MASK) == BACKUP_CATEGORY) {
backupCategories(categoryEntries)
}
// Backup extension ID/name mapping
backupExtensionInfo(extensionEntries, extensions)
val animes = getFavoriteAnime()
val animeextensions: MutableSet<String> = mutableSetOf()
// Backup library anime and its dependencies
animes.forEach { anime ->
animeEntries.add(backupAnimeObject(anime, flags))
// Maintain set of extensions/sources used (excludes local source)
if (anime.source != LocalAnimeSource.ID) {
sourceManager.get(anime.source)?.let {
extensions.add("${anime.source}:${it.name}")
}
}
}
// Backup categories
if ((flags and BACKUP_ANIMECATEGORY_MASK) == BACKUP_ANIMECATEGORY) {
backupCategories(animecategoryEntries)
}
// Backup extension ID/name mapping
backupExtensionInfo(animeextensionEntries, animeextensions)
}
try {
val file: UniFile = (
if (isJob) {
// Get dir of file and create
var dir = UniFile.fromUri(context, uri)
dir = dir.createDirectory("automatic")
// Delete older backups
val numberOfBackups = numberOfBackups()
val backupRegex = Regex("""tachiyomi_\d+-\d+-\d+_\d+-\d+.json""")
dir.listFiles { _, filename -> backupRegex.matches(filename) }
.orEmpty()
.sortedByDescending { it.name }
.drop(numberOfBackups - 1)
.forEach { it.delete() }
// Create new file to place backup
dir.createFile(Backup.getDefaultFilename())
} else {
UniFile.fromUri(context, uri)
}
)
?: throw Exception("Couldn't create backup file")
file.openOutputStream().bufferedWriter().use {
parser.toJson(root, it)
}
return file.uri.toString()
} catch (e: Exception) {
Timber.e(e)
throw e
}
}
private fun backupExtensionInfo(root: JsonArray, extensions: Set<String>) {
extensions.sorted().forEach {
root.add(it)
}
}
/**
* Backup the categories of library
*
* @param root root of categories json
*/
internal fun backupCategories(root: JsonArray) {
val categories = databaseHelper.getCategories().executeAsBlocking()
categories.forEach { root.add(parser.toJsonTree(it)) }
}
/**
* Backup the categories of library
*
* @param root root of categories json
*/
internal fun backupAnimeCategories(root: JsonArray) {
val categories = animedatabaseHelper.getCategories().executeAsBlocking()
categories.forEach { root.add(parser.toJsonTree(it)) }
}
/**
* Convert a manga to Json
*
* @param manga manga that gets converted
* @return [JsonElement] containing manga information
*/
internal fun backupMangaObject(manga: Manga, options: Int): JsonElement {
// Entry for this manga
val entry = JsonObject()
// Backup manga fields
entry[MANGA] = parser.toJsonTree(manga)
// Check if user wants chapter information in backup
if (options and BACKUP_CHAPTER_MASK == BACKUP_CHAPTER) {
// Backup all the chapters
val chapters = databaseHelper.getChapters(manga).executeAsBlocking()
if (chapters.isNotEmpty()) {
val chaptersJson = parser.toJsonTree(chapters)
if (chaptersJson.asJsonArray.size() > 0) {
entry[CHAPTERS] = chaptersJson
}
}
}
// Check if user wants category information in backup
if (options and BACKUP_CATEGORY_MASK == BACKUP_CATEGORY) {
// Backup categories for this manga
val categoriesForManga = databaseHelper.getCategoriesForManga(manga).executeAsBlocking()
if (categoriesForManga.isNotEmpty()) {
val categoriesNames = categoriesForManga.map { it.name }
entry[CATEGORIES] = parser.toJsonTree(categoriesNames)
}
}
// Check if user wants track information in backup
if (options and BACKUP_TRACK_MASK == BACKUP_TRACK) {
val tracks = databaseHelper.getTracks(manga).executeAsBlocking()
if (tracks.isNotEmpty()) {
entry[TRACK] = parser.toJsonTree(tracks)
}
}
// Check if user wants history information in backup
if (options and BACKUP_HISTORY_MASK == BACKUP_HISTORY) {
val historyForManga = databaseHelper.getHistoryByMangaId(manga.id!!).executeAsBlocking()
if (historyForManga.isNotEmpty()) {
val historyData = historyForManga.mapNotNull { history ->
val url = databaseHelper.getChapter(history.chapter_id).executeAsBlocking()?.url
url?.let { DHistory(url, history.last_read) }
}
val historyJson = parser.toJsonTree(historyData)
if (historyJson.asJsonArray.size() > 0) {
entry[HISTORY] = historyJson
}
}
}
return entry
}
/**
* Convert a anime to Json
*
* @param anime anime that gets converted
* @return [JsonElement] containing anime information
*/
internal fun backupAnimeObject(anime: Anime, options: Int): JsonElement {
// Entry for this anime
val entry = JsonObject()
// Backup anime fields
entry[MANGA] = parser.toJsonTree(anime)
// Check if user wants episode information in backup
if (options and BACKUP_EPISODE_MASK == BACKUP_EPISODE) {
// Backup all the episodes
val episodes = animedatabaseHelper.getEpisodes(anime).executeAsBlocking()
if (episodes.isNotEmpty()) {
val episodesJson = parser.toJsonTree(episodes)
if (episodesJson.asJsonArray.size() > 0) {
entry[EPISODES] = episodesJson
}
}
}
// Check if user wants category information in backup
if (options and BACKUP_CATEGORY_MASK == BACKUP_CATEGORY) {
// Backup categories for this anime
val categoriesForAnime = animedatabaseHelper.getCategoriesForAnime(anime).executeAsBlocking()
if (categoriesForAnime.isNotEmpty()) {
val categoriesNames = categoriesForAnime.map { it.name }
entry[CATEGORIES] = parser.toJsonTree(categoriesNames)
}
}
// Check if user wants track information in backup
if (options and BACKUP_TRACK_MASK == BACKUP_TRACK) {
val tracks = animedatabaseHelper.getTracks(anime).executeAsBlocking()
if (tracks.isNotEmpty()) {
entry[TRACK] = parser.toJsonTree(tracks)
}
}
// Check if user wants history information in backup
if (options and BACKUP_HISTORY_MASK == BACKUP_HISTORY) {
val historyForAnime = animedatabaseHelper.getHistoryByAnimeId(anime.id!!).executeAsBlocking()
if (historyForAnime.isNotEmpty()) {
val historyData = historyForAnime.mapNotNull { history ->
val url = animedatabaseHelper.getEpisode(history.episode_id).executeAsBlocking()?.url
url?.let { DHistory(url, history.last_seen) }
}
val historyJson = parser.toJsonTree(historyData)
if (historyJson.asJsonArray.size() > 0) {
entry[HISTORY] = historyJson
}
}
}
return entry
}
override fun createBackup(uri: Uri, flags: Int, isJob: Boolean) =
throw IllegalStateException("Legacy backup creation is not supported")
fun restoreMangaNoFetch(manga: Manga, dbManga: Manga) {
manga.id = dbManga.id

View file

@ -78,7 +78,8 @@ class LibraryUpdateNotifier(private val context: Context) {
context.notificationManager.notify(
Notifications.ID_LIBRARY_PROGRESS,
progressNotificationBuilder
.setContentTitle(title)
.setContentTitle(title.chop(40))
.setContentText("($current/$total)")
.setProgress(total, current, false)
.build()
)

View file

@ -86,7 +86,7 @@ class NotificationReceiver : BroadcastReceiver() {
shareFile(
context,
intent.getParcelableExtra(EXTRA_URI),
if (intent.getBooleanExtra(EXTRA_IS_LEGACY_BACKUP, false)) "application/json" else "application/x-protobuf+gzip",
"application/x-protobuf+gzip",
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1)
)
ACTION_CANCEL_RESTORE -> cancelRestore(
@ -305,7 +305,6 @@ class NotificationReceiver : BroadcastReceiver() {
private const val EXTRA_MANGA_ID = "$ID.$NAME.EXTRA_MANGA_ID"
private const val EXTRA_CHAPTER_ID = "$ID.$NAME.EXTRA_CHAPTER_ID"
private const val EXTRA_CHAPTER_URL = "$ID.$NAME.EXTRA_CHAPTER_URL"
private const val EXTRA_IS_LEGACY_BACKUP = "$ID.$NAME.EXTRA_IS_LEGACY_BACKUP"
/**
* Returns a [PendingIntent] that resumes the download of a chapter
@ -583,11 +582,10 @@ class NotificationReceiver : BroadcastReceiver() {
* @param notificationId id of notification
* @return [PendingIntent]
*/
internal fun shareBackupPendingBroadcast(context: Context, uri: Uri, isLegacyFormat: Boolean, notificationId: Int): PendingIntent {
internal fun shareBackupPendingBroadcast(context: Context, uri: Uri, notificationId: Int): PendingIntent {
val intent = Intent(context, NotificationReceiver::class.java).apply {
action = ACTION_SHARE_BACKUP
putExtra(EXTRA_URI, uri)
putExtra(EXTRA_IS_LEGACY_BACKUP, isLegacyFormat)
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
}
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)

View file

@ -15,8 +15,6 @@ object PreferenceKeys {
const val hideBottomBar = "pref_hide_bottom_bar_on_scroll"
const val showNavUpdates = "pref_nav_show_updates"
const val enableTransitions = "pref_enable_transitions_key"
const val doubleTapAnimationSpeed = "pref_double_tap_anim_speed"
@ -248,8 +246,6 @@ object PreferenceKeys {
const val incognitoMode = "incognito_mode"
const val createLegacyBackup = "create_legacy_backup"
fun trackUsername(syncId: Int) = "pref_mangasync_username_$syncId"
fun trackPassword(syncId: Int) = "pref_mangasync_password_$syncId"

View file

@ -66,8 +66,6 @@ class PreferencesHelper(val context: Context) {
fun hideBottomBar() = flowPrefs.getBoolean(Keys.hideBottomBar, true)
fun showNavUpdates() = prefs.getBoolean(Keys.showNavUpdates, true)
fun useBiometricLock() = flowPrefs.getBoolean(Keys.useBiometricLock, false)
fun lockAppAfter() = flowPrefs.getInt(Keys.lockAppAfter, 0)
@ -353,8 +351,6 @@ class PreferencesHelper(val context: Context) {
fun incognitoMode() = flowPrefs.getBoolean(Keys.incognitoMode, false)
fun createLegacyBackup() = flowPrefs.getBoolean(Keys.createLegacyBackup, true)
fun setChapterSettingsDefault(manga: Manga) {
prefs.edit {
putInt(Keys.defaultChapterFilterByRead, manga.readFilter)

View file

@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.databinding.AnimeExtensionCardItemBinding
import eu.kanade.tachiyomi.extension.model.AnimeExtension
import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.extension.model.InstallStep
import eu.kanade.tachiyomi.util.system.LocaleHelper
import uy.kohesive.injekt.Injekt
@ -36,8 +37,8 @@ class AnimeExtensionHolder(view: View, val adapter: AnimeExtensionAdapter) :
binding.lang.text = LocaleHelper.getSourceDisplayName(extension.lang, itemView.context)
binding.warning.text = when {
extension is AnimeExtension.Untrusted -> itemView.context.getString(R.string.ext_untrusted)
extension is AnimeExtension.Installed && extension.isObsolete -> itemView.context.getString(R.string.ext_obsolete)
extension is AnimeExtension.Installed && extension.isUnofficial -> itemView.context.getString(R.string.ext_unofficial)
extension is AnimeExtension.Installed && extension.isObsolete -> itemView.context.getString(R.string.ext_obsolete)
extension.isNsfw && shouldLabelNsfw -> itemView.context.getString(R.string.ext_nsfw_short)
else -> ""
}.toUpperCase()

View file

@ -36,8 +36,8 @@ class ExtensionHolder(view: View, val adapter: ExtensionAdapter) :
binding.lang.text = LocaleHelper.getSourceDisplayName(extension.lang, itemView.context)
binding.warning.text = when {
extension is Extension.Untrusted -> itemView.context.getString(R.string.ext_untrusted)
extension is Extension.Installed && extension.isObsolete -> itemView.context.getString(R.string.ext_obsolete)
extension is Extension.Installed && extension.isUnofficial -> itemView.context.getString(R.string.ext_unofficial)
extension is Extension.Installed && extension.isObsolete -> itemView.context.getString(R.string.ext_obsolete)
extension.isNsfw && shouldLabelNsfw -> itemView.context.getString(R.string.ext_nsfw_short)
else -> ""
}.toUpperCase()

View file

@ -5,7 +5,6 @@ import android.content.Intent
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.view.Menu
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
@ -493,7 +492,6 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() {
if (visible) {
if (collapse) {
bottomNavAnimator?.expand()
updateNavMenu(it.menu)
}
bottomViewNavigationBehavior?.slideUp(it)
} else {
@ -509,14 +507,9 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() {
private fun showSideNav(visible: Boolean) {
binding.sideNav?.let {
it.isVisible = visible
updateNavMenu(it.menu)
}
}
private fun updateNavMenu(menu: Menu) {
menu.findItem(R.id.nav_updates).isVisible = preferences.showNavUpdates()
}
/**
* Used to manually offset a view within the activity's child views that might be cut off due to
* the collapsing AppBarLayout.

View file

@ -4,7 +4,6 @@ import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.app.Activity
import android.app.Dialog
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
@ -24,7 +23,6 @@ import eu.kanade.tachiyomi.data.backup.BackupRestoreService
import eu.kanade.tachiyomi.data.backup.full.FullBackupRestoreValidator
import eu.kanade.tachiyomi.data.backup.full.models.BackupFull
import eu.kanade.tachiyomi.data.backup.legacy.LegacyBackupRestoreValidator
import eu.kanade.tachiyomi.data.backup.legacy.models.Backup
import eu.kanade.tachiyomi.data.preference.asImmediateFlow
import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.ui.base.controller.requestPermissionsSafe
@ -36,7 +34,6 @@ import eu.kanade.tachiyomi.util.preference.onClick
import eu.kanade.tachiyomi.util.preference.preference
import eu.kanade.tachiyomi.util.preference.preferenceCategory
import eu.kanade.tachiyomi.util.preference.summaryRes
import eu.kanade.tachiyomi.util.preference.switchPreference
import eu.kanade.tachiyomi.util.preference.titleRes
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.flow.launchIn
@ -66,14 +63,15 @@ class SettingsBackupController : SettingsController() {
titleRes = R.string.pref_create_backup
summaryRes = R.string.pref_create_backup_summ
onClick { backup(context, BackupConst.BACKUP_TYPE_FULL) }
onClick {
if (!BackupCreateService.isRunning(context)) {
val ctrl = CreateBackupDialog()
ctrl.targetController = this@SettingsBackupController
ctrl.showDialog(router)
} else {
context.toast(R.string.backup_in_progress)
}
}
preference {
key = "pref_create_legacy_backup"
titleRes = R.string.pref_create_legacy_backup
summaryRes = R.string.pref_create_legacy_backup_summary
onClick { backup(context, BackupConst.BACKUP_TYPE_LEGACY) }
}
preference {
key = "pref_restore_backup"
@ -150,14 +148,6 @@ class SettingsBackupController : SettingsController() {
defaultValue = "1"
summary = "%s"
preferences.backupInterval().asImmediateFlow { isVisible = it > 0 }
.launchIn(viewScope)
}
switchPreference {
key = Keys.createLegacyBackup
titleRes = R.string.pref_backup_auto_create_legacy
defaultValue = true
preferences.backupInterval().asImmediateFlow { isVisible = it > 0 }
.launchIn(viewScope)
}
@ -182,7 +172,7 @@ class SettingsBackupController : SettingsController() {
// Set backup Uri
preferences.backupsDirectory().set(uri.toString())
}
CODE_FULL_BACKUP_CREATE, CODE_LEGACY_BACKUP_CREATE -> {
CODE_BACKUP_CREATE -> {
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
@ -198,7 +188,6 @@ class SettingsBackupController : SettingsController() {
activity,
file.uri,
backupFlags,
if (requestCode == CODE_FULL_BACKUP_CREATE) BackupConst.BACKUP_TYPE_FULL else BackupConst.BACKUP_TYPE_LEGACY
)
}
CODE_BACKUP_RESTORE -> {
@ -227,49 +216,23 @@ class SettingsBackupController : SettingsController() {
}
}
private fun backup(context: Context, type: Int) {
if (!BackupCreateService.isRunning(context)) {
val ctrl = CreateBackupDialog(type)
ctrl.targetController = this@SettingsBackupController
ctrl.showDialog(router)
} else {
context.toast(R.string.backup_in_progress)
}
}
fun createBackup(flags: Int, type: Int) {
fun createBackup(flags: Int) {
backupFlags = flags
val code = when (type) {
BackupConst.BACKUP_TYPE_FULL -> CODE_FULL_BACKUP_CREATE
else -> CODE_LEGACY_BACKUP_CREATE
}
val fileName = when (type) {
BackupConst.BACKUP_TYPE_FULL -> BackupFull.getDefaultFilename()
else -> Backup.getDefaultFilename()
}
try {
// Use Android's built-in file creator
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
.addCategory(Intent.CATEGORY_OPENABLE)
.setType("application/*")
.putExtra(Intent.EXTRA_TITLE, fileName)
.putExtra(Intent.EXTRA_TITLE, BackupFull.getDefaultFilename())
startActivityForResult(intent, code)
startActivityForResult(intent, CODE_BACKUP_CREATE)
} catch (e: ActivityNotFoundException) {
activity?.toast(R.string.file_picker_error)
}
}
class CreateBackupDialog(bundle: Bundle? = null) : DialogController(bundle) {
constructor(type: Int) : this(
bundleOf(
KEY_TYPE to type
)
)
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
val type = args.getInt(KEY_TYPE)
val activity = activity!!
val options = arrayOf(
R.string.manga,
@ -298,15 +261,11 @@ class SettingsBackupController : SettingsController() {
}
}
(targetController as? SettingsBackupController)?.createBackup(flags, type)
(targetController as? SettingsBackupController)?.createBackup(flags)
}
.positiveButton(R.string.action_create)
.negativeButton(android.R.string.cancel)
}
private companion object {
const val KEY_TYPE = "CreateBackupDialog.type"
}
}
class RestoreBackupDialog(bundle: Bundle? = null) : DialogController(bundle) {
@ -364,9 +323,8 @@ class SettingsBackupController : SettingsController() {
}
private companion object {
const val CODE_LEGACY_BACKUP_CREATE = 501
const val CODE_BACKUP_DIR = 503
const val CODE_FULL_BACKUP_CREATE = 504
const val CODE_BACKUP_CREATE = 504
const val CODE_BACKUP_RESTORE = 505
}
}

View file

@ -11,6 +11,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.animation.doOnEnd
import androidx.core.view.updatePadding
import androidx.preference.PreferenceController
import androidx.preference.PreferenceGroup
@ -98,6 +99,7 @@ abstract class SettingsController : PreferenceController() {
abstract fun setupPreferenceScreen(screen: PreferenceScreen): PreferenceScreen
private fun animatePreferenceHighlight(view: View) {
val origBackground = view.background
ValueAnimator
.ofObject(ArgbEvaluator(), Color.TRANSPARENT, view.context.getResourceColor(R.attr.rippleColor))
.apply {
@ -106,6 +108,10 @@ abstract class SettingsController : PreferenceController() {
addUpdateListener { animator -> view.setBackgroundColor(animator.animatedValue as Int) }
reverse()
}
.doOnEnd {
// Restore original ripple
view.background = origBackground
}
}
open fun getTitle(): String? {

View file

@ -45,6 +45,11 @@ class SettingsGeneralController : SettingsController() {
titleRes = R.string.pref_confirm_exit
defaultValue = false
}
switchPreference {
key = Keys.hideBottomBar
titleRes = R.string.pref_hide_bottom_bar_on_scroll
defaultValue = true
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
preference {
@ -261,20 +266,5 @@ class SettingsGeneralController : SettingsController() {
summary = "%s"
}
}
preferenceCategory {
titleRes = R.string.pref_category_navigation
switchPreference {
key = Keys.hideBottomBar
titleRes = R.string.pref_hide_bottom_bar_on_scroll
defaultValue = true
}
switchPreference {
key = Keys.showNavUpdates
titleRes = R.string.label_recent_updates
defaultValue = true
}
}
}
}

View file

@ -156,7 +156,6 @@
<!-- General section -->
<string name="pref_category_theme">Theme</string>
<string name="pref_category_locale">Locale</string>
<string name="pref_category_navigation">Navigation</string>
<string name="pref_theme_mode">Dark mode</string>
<string name="theme_system">Follow system</string>
<string name="theme_light">Off</string>
@ -399,9 +398,6 @@
<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_summ">Restore library from backup file</string>
<string name="pref_create_legacy_backup">Create legacy backup</string>
<string name="pref_create_legacy_backup_summary">Can be used in older versions of Tachiyomi</string>
<string name="pref_backup_auto_create_legacy">Also create legacy backup</string>
<string name="pref_backup_directory">Backup location</string>
<string name="pref_backup_service_category">Automatic backups</string>
<string name="pref_backup_interval">Backup frequency</string>