diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBackupScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBackupScreen.kt index 2728abc4c..e772df726 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBackupScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBackupScreen.kt @@ -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) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupConst.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupConst.kt index a53d7fb6d..fbd6be54b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupConst.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupConst.kt @@ -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 } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupManager.kt index a88abd013..97ba3f894 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupManager.kt @@ -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 { + if (flags and BACKUP_EXTENSIONS_MASK != BACKUP_EXTENSIONS) return emptyList() + val installedExtensions = mutableListOf() + Injekt.get().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().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) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt index 73137d3df..c0015c9e3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt @@ -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) { + 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] * diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/Backup.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/Backup.kt index f8f768786..48bcc0a39 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/Backup.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/Backup.kt @@ -19,6 +19,7 @@ data class Backup( @ProtoNumber(103) var backupAnimeSources: List = emptyList(), @ProtoNumber(104) var backupPreferences: List = emptyList(), @ProtoNumber(105) var backupExtensionPreferences: List = emptyList(), + @ProtoNumber(106) var backupExtensions: List = emptyList(), ) { companion object { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupExtension.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupExtension.kt new file mode 100644 index 000000000..c0e0c0450 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupExtension.kt @@ -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 + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceValues.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceValues.kt index d7edb2e54..621824b8f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceValues.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceValues.kt @@ -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. diff --git a/i18n/src/main/res/values/strings-aniyomi.xml b/i18n/src/main/res/values/strings-aniyomi.xml index f1effcecf..718b9cabc 100644 --- a/i18n/src/main/res/values/strings-aniyomi.xml +++ b/i18n/src/main/res/values/strings-aniyomi.xml @@ -6,7 +6,7 @@ Library entries Chapters and episodes Episodes - Warning: Backing up settings will store your track passwords as well, do not share this backup file! + Warning: Backing up settings might store sensitive information like tracker passwords.\nDo not share this backup file! Manga Anime Anime