feat: allow backing up extension apks

This commit is contained in:
jmir1 2023-06-06 15:22:30 +02:00
parent 54ec5ac72c
commit d871a580b3
8 changed files with 111 additions and 5 deletions

View file

@ -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) {

View file

@ -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
}

View file

@ -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)

View file

@ -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]
*

View file

@ -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 {

View file

@ -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
}
}

View file

@ -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.

View file

@ -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>