mirror of
https://github.com/aniyomiorg/aniyomi.git
synced 2024-11-26 23:18:17 +03:00
feat: allow backing up extension apks
This commit is contained in:
parent
54ec5ac72c
commit
d871a580b3
8 changed files with 111 additions and 5 deletions
|
@ -23,12 +23,12 @@ import androidx.compose.material3.TextButton
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
@ -48,6 +48,7 @@ import eu.kanade.tachiyomi.data.backup.BackupRestoreService
|
|||
import eu.kanade.tachiyomi.data.backup.models.Backup
|
||||
import eu.kanade.tachiyomi.data.preference.FLAG_CATEGORIES
|
||||
import eu.kanade.tachiyomi.data.preference.FLAG_CHAPTERS
|
||||
import eu.kanade.tachiyomi.data.preference.FLAG_EXTENSIONS
|
||||
import eu.kanade.tachiyomi.data.preference.FLAG_EXT_SETTINGS
|
||||
import eu.kanade.tachiyomi.data.preference.FLAG_HISTORY
|
||||
import eu.kanade.tachiyomi.data.preference.FLAG_SETTINGS
|
||||
|
@ -151,9 +152,17 @@ object SettingsBackupScreen : SearchableSettings {
|
|||
BackupConst.BACKUP_HISTORY to R.string.history,
|
||||
BackupConst.BACKUP_PREFS to R.string.settings,
|
||||
BackupConst.BACKUP_EXT_PREFS to R.string.extension_settings,
|
||||
BackupConst.BACKUP_EXTENSIONS to R.string.label_extensions,
|
||||
)
|
||||
}
|
||||
val flags = remember {
|
||||
mutableStateListOf(
|
||||
BackupConst.BACKUP_CATEGORY,
|
||||
BackupConst.BACKUP_CHAPTER,
|
||||
BackupConst.BACKUP_TRACK,
|
||||
BackupConst.BACKUP_HISTORY,
|
||||
)
|
||||
}
|
||||
val flags = remember { choices.keys.toMutableStateList() }
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
title = { Text(text = stringResource(R.string.backup_choice)) },
|
||||
|
@ -164,7 +173,7 @@ object SettingsBackupScreen : SearchableSettings {
|
|||
item {
|
||||
CreateBackupDialogItem(
|
||||
isSelected = true,
|
||||
title = stringResource(R.string.manga),
|
||||
title = stringResource(R.string.entries),
|
||||
)
|
||||
}
|
||||
choices.forEach { (k, v) ->
|
||||
|
@ -410,6 +419,7 @@ object SettingsBackupScreen : SearchableSettings {
|
|||
FLAG_TRACK to stringResource(R.string.track),
|
||||
FLAG_SETTINGS to stringResource(R.string.settings),
|
||||
FLAG_EXT_SETTINGS to stringResource(R.string.extension_settings),
|
||||
FLAG_EXTENSIONS to stringResource(R.string.label_extensions),
|
||||
),
|
||||
onValueChanged = {
|
||||
if (FLAG_SETTINGS in it || FLAG_EXT_SETTINGS in it) {
|
||||
|
|
|
@ -20,5 +20,7 @@ object BackupConst {
|
|||
internal const val BACKUP_PREFS_MASK = 0x10
|
||||
internal const val BACKUP_EXT_PREFS = 0x20
|
||||
internal const val BACKUP_EXT_PREFS_MASK = 0x20
|
||||
internal const val BACKUP_ALL = 0x3F
|
||||
internal const val BACKUP_EXTENSIONS = 0x40
|
||||
internal const val BACKUP_EXTENSIONS_MASK = 0x40
|
||||
internal const val BACKUP_ALL = 0x7F
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.data.backup
|
|||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.hippo.unifile.UniFile
|
||||
|
@ -17,6 +18,8 @@ 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
|
||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CHAPTER_MASK
|
||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_EXTENSIONS
|
||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_EXTENSIONS_MASK
|
||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_EXT_PREFS
|
||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_EXT_PREFS_MASK
|
||||
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_HISTORY
|
||||
|
@ -30,6 +33,7 @@ import eu.kanade.tachiyomi.data.backup.models.BackupAnime
|
|||
import eu.kanade.tachiyomi.data.backup.models.BackupAnimeHistory
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupAnimeSource
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupExtension
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupExtensionPreferences
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupHistory
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupManga
|
||||
|
@ -53,6 +57,8 @@ import eu.kanade.tachiyomi.data.database.models.anime.Episode
|
|||
import eu.kanade.tachiyomi.data.database.models.manga.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.manga.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.manga.MangaTrack
|
||||
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
|
||||
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
|
||||
import eu.kanade.tachiyomi.source.anime.AnimeSourceManager
|
||||
import eu.kanade.tachiyomi.source.anime.getPreferenceKey
|
||||
import eu.kanade.tachiyomi.source.anime.model.copyFrom
|
||||
|
@ -79,6 +85,7 @@ import tachiyomi.domain.history.anime.model.AnimeHistoryUpdate
|
|||
import tachiyomi.domain.history.manga.model.MangaHistoryUpdate
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.util.Date
|
||||
import kotlin.math.max
|
||||
|
@ -130,6 +137,7 @@ class BackupManager(
|
|||
backupAnimeExtensionInfo(databaseAnime),
|
||||
backupPreferences(prefs, flags),
|
||||
backupExtensionPreferences(flags),
|
||||
backupExtensions(flags),
|
||||
)
|
||||
|
||||
var file: UniFile? = null
|
||||
|
@ -408,6 +416,39 @@ class BackupManager(
|
|||
return backupPreferences
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun backupExtensions(flags: Int): List<BackupExtension> {
|
||||
if (flags and BACKUP_EXTENSIONS_MASK != BACKUP_EXTENSIONS) return emptyList()
|
||||
val installedExtensions = mutableListOf<BackupExtension>()
|
||||
Injekt.get<AnimeExtensionManager>().installedExtensionsFlow.value.forEach {
|
||||
val packageName = it.pkgName
|
||||
val apk = File(
|
||||
context.packageManager
|
||||
.getApplicationInfo(
|
||||
packageName,
|
||||
PackageManager.GET_META_DATA,
|
||||
).publicSourceDir,
|
||||
).readBytes()
|
||||
installedExtensions.add(
|
||||
BackupExtension(packageName, apk),
|
||||
)
|
||||
}
|
||||
Injekt.get<MangaExtensionManager>().installedExtensionsFlow.value.forEach {
|
||||
val packageName = it.pkgName
|
||||
val apk = File(
|
||||
context.packageManager
|
||||
.getApplicationInfo(
|
||||
packageName,
|
||||
PackageManager.GET_META_DATA,
|
||||
).publicSourceDir,
|
||||
).readBytes()
|
||||
installedExtensions.add(
|
||||
BackupExtension(packageName, apk),
|
||||
)
|
||||
}
|
||||
return installedExtensions
|
||||
}
|
||||
|
||||
internal suspend fun restoreExistingManga(manga: Manga, dbManga: Mangas) {
|
||||
manga.id = dbManga._id
|
||||
manga.copyFrom(dbManga)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package eu.kanade.tachiyomi.data.backup
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import androidx.preference.PreferenceManager
|
||||
|
@ -9,6 +10,7 @@ import eu.kanade.tachiyomi.data.backup.models.BackupAnime
|
|||
import eu.kanade.tachiyomi.data.backup.models.BackupAnimeHistory
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupAnimeSource
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupExtension
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupExtensionPreferences
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupHistory
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupManga
|
||||
|
@ -27,8 +29,10 @@ import eu.kanade.tachiyomi.data.database.models.manga.Chapter
|
|||
import eu.kanade.tachiyomi.data.database.models.manga.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.manga.MangaTrack
|
||||
import eu.kanade.tachiyomi.util.BackupUtil
|
||||
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
||||
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
|
||||
import kotlinx.coroutines.Job
|
||||
import tachiyomi.core.util.system.logcat
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
|
@ -143,6 +147,10 @@ class BackupRestorer(
|
|||
restoreExtensionPreferences(backup.backupExtensionPreferences)
|
||||
}
|
||||
|
||||
if (backup.backupExtensions.isNotEmpty()) {
|
||||
restoreExtensions(backup.backupExtensions)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -342,6 +350,21 @@ class BackupRestorer(
|
|||
}
|
||||
}
|
||||
|
||||
private fun restoreExtensions(extensions: List<BackupExtension>) {
|
||||
extensions.forEach {
|
||||
if (context.packageManager.getInstalledPackages(0).none { pkg -> pkg.packageName == it.pkgName }) {
|
||||
logcat { it.pkgName }
|
||||
// save apk in files dir and open installer dialog
|
||||
val file = File(context.cacheDir, "${it.pkgName}.apk")
|
||||
file.writeBytes(it.apk)
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
.setDataAndType(file.getUriCompat(context), "application/vnd.android.package-archive")
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to update dialog in [BackupConst]
|
||||
*
|
||||
|
|
|
@ -19,6 +19,7 @@ data class Backup(
|
|||
@ProtoNumber(103) var backupAnimeSources: List<BackupAnimeSource> = emptyList(),
|
||||
@ProtoNumber(104) var backupPreferences: List<BackupPreference> = emptyList(),
|
||||
@ProtoNumber(105) var backupExtensionPreferences: List<BackupExtensionPreferences> = emptyList(),
|
||||
@ProtoNumber(106) var backupExtensions: List<BackupExtension> = emptyList(),
|
||||
) {
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package eu.kanade.tachiyomi.data.backup.models
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoNumber
|
||||
|
||||
@Serializable
|
||||
data class BackupExtension(
|
||||
@ProtoNumber(1) val pkgName: String,
|
||||
@ProtoNumber(2) val apk: ByteArray,
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as BackupExtension
|
||||
|
||||
if (pkgName != other.pkgName) return false
|
||||
if (!apk.contentEquals(other.apk)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = pkgName.hashCode()
|
||||
result = 31 * result + apk.contentHashCode()
|
||||
return result
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ const val FLAG_HISTORY = "4"
|
|||
const val FLAG_TRACK = "8"
|
||||
const val FLAG_SETTINGS = "10"
|
||||
const val FLAG_EXT_SETTINGS = "20"
|
||||
const val FLAG_EXTENSIONS = "40"
|
||||
|
||||
/**
|
||||
* This class stores the values for the preferences in the application.
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<string name="entries">Library entries</string>
|
||||
<string name="chapters_episodes">Chapters and episodes</string>
|
||||
<string name="episodes">Episodes</string>
|
||||
<string name="backup_settings_warning">Warning: Backing up settings will store your track passwords as well, do not share this backup file!</string>
|
||||
<string name="backup_settings_warning">Warning: Backing up settings might store sensitive information like tracker passwords.\nDo not share this backup file!</string>
|
||||
<string name="label_manga_library">Manga</string>
|
||||
<string name="label_anime_library">Anime</string>
|
||||
<string name="label_anime">Anime</string>
|
||||
|
|
Loading…
Reference in a new issue