From addb84ee1e44a837f834df17ad5683a91d415676 Mon Sep 17 00:00:00 2001 From: ziro Date: Sat, 13 Jan 2024 19:27:22 +0700 Subject: [PATCH] feat: Final touch before merge * Trust Extension rework * Deep link --- app/src/main/AndroidManifest.xml | 12 +++++- .../yokai/domain/extension/TrustExtension.kt | 29 +++++++++++++++ .../extension/repo/ExtensionRepoController.kt | 11 ++++-- .../extension/repo/ExtensionRepoScreen.kt | 20 ++++------ .../java/eu/kanade/tachiyomi/Migrations.kt | 5 +++ .../data/preference/PreferencesHelper.kt | 3 -- .../java/eu/kanade/tachiyomi/di/AppModule.kt | 3 ++ .../tachiyomi/extension/ExtensionManager.kt | 37 ++++++++----------- .../ui/extension/ExtensionBottomPresenter.kt | 4 +- .../ui/extension/ExtensionBottomSheet.kt | 7 ++-- .../ui/extension/ExtensionTrustDialog.kt | 8 ++-- .../kanade/tachiyomi/ui/main/MainActivity.kt | 9 +++++ 12 files changed, 100 insertions(+), 48 deletions(-) create mode 100644 app/src/main/java/dev/yokai/domain/extension/TrustExtension.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f259297e09..de23067d58 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -60,6 +60,16 @@ + + + + + + + + + + - + \ No newline at end of file diff --git a/app/src/main/java/dev/yokai/domain/extension/TrustExtension.kt b/app/src/main/java/dev/yokai/domain/extension/TrustExtension.kt new file mode 100644 index 0000000000..baee9ef96c --- /dev/null +++ b/app/src/main/java/dev/yokai/domain/extension/TrustExtension.kt @@ -0,0 +1,29 @@ +package dev.yokai.domain.extension + +import android.content.pm.PackageInfo +import androidx.core.content.pm.PackageInfoCompat +import dev.yokai.domain.source.SourcePreferences +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +class TrustExtension( + private val sourcePreferences: SourcePreferences = Injekt.get(), +) { + fun isTrusted(pkgInfo: PackageInfo, signatureHash: String): Boolean { + val key = "${pkgInfo.packageName}:${PackageInfoCompat.getLongVersionCode(pkgInfo)}:$signatureHash" + return key in sourcePreferences.trustedExtensions().get() + } + + fun trust(pkgName: String, versionCode: Long, signatureHash: String) { + sourcePreferences.trustedExtensions().let { exts -> + val removed = exts.get().filterNot { it.startsWith("$pkgName:") }.toMutableSet() + + removed += "$pkgName:$versionCode:$signatureHash" + exts.set(removed) + } + } + + fun revokeAll() { + sourcePreferences.trustedExtensions().delete() + } +} diff --git a/app/src/main/java/dev/yokai/presentation/extension/repo/ExtensionRepoController.kt b/app/src/main/java/dev/yokai/presentation/extension/repo/ExtensionRepoController.kt index 31c18d3018..998ba67043 100644 --- a/app/src/main/java/dev/yokai/presentation/extension/repo/ExtensionRepoController.kt +++ b/app/src/main/java/dev/yokai/presentation/extension/repo/ExtensionRepoController.kt @@ -1,18 +1,23 @@ package dev.yokai.presentation.extension.repo import androidx.compose.runtime.Composable -import androidx.compose.ui.tooling.preview.Preview import eu.kanade.tachiyomi.ui.base.controller.BaseComposeController -class ExtensionRepoController : +class ExtensionRepoController() : BaseComposeController() { - @Preview + private var repoUrl: String? = null + + constructor(repoUrl: String) : this() { + this.repoUrl = repoUrl + } + @Composable override fun ScreenContent() { ExtensionRepoScreen( title = "Extension Repos", onBackPress = router::handleBack, + repoUrl = repoUrl, ) } } diff --git a/app/src/main/java/dev/yokai/presentation/extension/repo/ExtensionRepoScreen.kt b/app/src/main/java/dev/yokai/presentation/extension/repo/ExtensionRepoScreen.kt index 25ffdd472a..30cba930b4 100644 --- a/app/src/main/java/dev/yokai/presentation/extension/repo/ExtensionRepoScreen.kt +++ b/app/src/main/java/dev/yokai/presentation/extension/repo/ExtensionRepoScreen.kt @@ -4,11 +4,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.ExtensionOff -import androidx.compose.material3.FloatingActionButton -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState @@ -31,6 +27,7 @@ fun ExtensionRepoScreen( title: String, onBackPress: () -> Unit, viewModel: ExtensionRepoViewModel = viewModel(), + repoUrl: String? = null, ) { val context = LocalContext.current val repoState = viewModel.repoState.collectAsState() @@ -39,14 +36,6 @@ fun ExtensionRepoScreen( YokaiScaffold( onNavigationIconClicked = onBackPress, title = title, - fab = { - FloatingActionButton( - containerColor = MaterialTheme.colorScheme.secondary, - onClick = { context.toast("Test") }, - ) { - Icon(Icons.Filled.Add, "Add repo") - } - }, appBarType = AppBarType.SMALL, ) { innerPadding -> if (repoState.value is ExtensionRepoState.Loading) return@YokaiScaffold @@ -61,6 +50,7 @@ fun ExtensionRepoScreen( item { ExtensionRepoItem( inputText = inputText, + // TODO: i18n inputHint = "Add new repo", onInputChange = { inputText = it }, onAddClick = { viewModel.addRepo(it) }, @@ -72,6 +62,7 @@ fun ExtensionRepoScreen( EmptyScreen( modifier = Modifier.fillParentMaxSize(), image = Icons.Filled.ExtensionOff, + // TODO: i18n message = "No extension repo found", ) } @@ -82,12 +73,17 @@ fun ExtensionRepoScreen( item { ExtensionRepoItem( repoUrl = repo, + // TODO: Confirmation dialog onDeleteClick = { viewModel.deleteRepo(it) }, ) } } } } + + LaunchedEffect(repoUrl) { + repoUrl?.let { viewModel.addRepo(repoUrl) } + } LaunchedEffect(Unit) { viewModel.event.collectLatest { event -> diff --git a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt b/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt index 17fcf27aeb..a1933bfb8e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt @@ -255,6 +255,11 @@ object Migrations { } catch (_: Exception) { } } + if (oldVersion < 112) { + prefs.edit { + remove("trusted_signatures") + } + } return true } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index d8187b0233..9ec3cb4d69 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -343,9 +343,6 @@ class PreferencesHelper(val context: Context, val preferenceStore: PreferenceSto fun migrateFlags() = preferenceStore.getInt("migrate_flags", Int.MAX_VALUE) - // TODO: SourcePref - fun trustedSignatures() = preferenceStore.getStringSet("trusted_signatures", emptySet()) - // using string instead of set so it is ordered // TODO: SourcePref fun migrationSources() = preferenceStore.getString("migrate_sources", "") diff --git a/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt b/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt index 4f4d42bb8b..a9a774f47f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.di import android.app.Application import androidx.core.content.ContextCompat +import dev.yokai.domain.extension.TrustExtension import eu.kanade.tachiyomi.core.preference.AndroidPreferenceStore import eu.kanade.tachiyomi.core.preference.PreferenceStore import eu.kanade.tachiyomi.data.cache.ChapterCache @@ -61,6 +62,8 @@ class AppModule(val app: Application) : InjektModule { addSingletonFactory { MangaShortcutManager() } + addSingletonFactory { TrustExtension() } + // Asynchronously init expensive components for a faster cold start ContextCompat.getMainExecutor(app).execute { diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt index 9e67d1975f..2f1ed4c6dc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt @@ -4,9 +4,8 @@ import android.content.Context import android.graphics.drawable.Drawable import android.os.Build import android.os.Parcelable -import dev.yokai.domain.source.SourcePreferences +import dev.yokai.domain.extension.TrustExtension import eu.kanade.tachiyomi.data.preference.PreferencesHelper -import eu.kanade.tachiyomi.data.preference.minusAssign import eu.kanade.tachiyomi.data.preference.plusAssign import eu.kanade.tachiyomi.extension.api.ExtensionApi import eu.kanade.tachiyomi.extension.model.Extension @@ -23,7 +22,6 @@ import kotlinx.coroutines.async import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.map import kotlinx.parcelize.Parcelize import timber.log.Timber import uy.kohesive.injekt.Injekt @@ -43,6 +41,7 @@ import java.util.Locale class ExtensionManager( private val context: Context, private val preferences: PreferencesHelper = Injekt.get(), + private val trustExtension: TrustExtension = Injekt.get(), ) { /** @@ -307,34 +306,30 @@ class ExtensionManager( } /** - * Adds the given signature to the list of trusted signatures. It also loads in background the - * extensions that match this signature. + * Adds the given extension to the list of trusted extensions. It also loads in background the + * now trusted extensions. * - * @param signature The signature to whitelist. + * @param pkgName the package name of the extension + * @param versionCode the version code of the extension + * @param signatureHash the signature hash of the extension */ - fun trustSignature(signature: String) { - val untrustedSignatures = untrustedExtensionsFlow.value.map { it.signatureHash }.toSet() - if (signature !in untrustedSignatures) return + fun trust(pkgName: String, versionCode: Long, signatureHash: String) { + val untrustedPkgName = untrustedExtensionsFlow.value.map { it.pkgName }.toSet() + if (pkgName !in untrustedPkgName) return - ExtensionLoader.trustedSignatures += signature - val preference = preferences.trustedSignatures() - preference.set(preference.get() + signature) + trustExtension.trust(pkgName, versionCode, signatureHash) - val nowTrustedExtensions = untrustedExtensionsFlow.value.filter { it.signatureHash == signature } + val nowTrustedExtensions = untrustedExtensionsFlow.value + .filter { it.pkgName == pkgName && it.versionCode == versionCode } _untrustedExtensionsFlow.value -= nowTrustedExtensions - val ctx = context launchNow { nowTrustedExtensions .map { extension -> - async { ExtensionLoader.loadExtensionFromPkgName(ctx, extension.pkgName) } - } - .map { it.await() } - .forEach { result -> - if (result is LoadResult.Success) { - registerNewExtension(result.extension) - } + async { ExtensionLoader.loadExtensionFromPkgName(context, extension.pkgName) }.await() } + .filterIsInstance() + .forEach { registerNewExtension(it.extension) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionBottomPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionBottomPresenter.kt index 17ae561782..6db5441b94 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionBottomPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionBottomPresenter.kt @@ -274,7 +274,7 @@ class ExtensionBottomPresenter : BaseMigrationPresenter() } } - fun trustSignature(signatureHash: String) { - extensionManager.trustSignature(signatureHash) + fun trustExtension(pkgName: String, versionCode: Long, signatureHash: String) { + extensionManager.trust(pkgName, versionCode, signatureHash) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionBottomSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionBottomSheet.kt index 5e578d8ee5..0548a97d73 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionBottomSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionBottomSheet.kt @@ -323,7 +323,7 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At } private fun openTrustDialog(extension: Extension.Untrusted) { - ExtensionTrustDialog(this, extension.signatureHash, extension.pkgName) + ExtensionTrustDialog(this, extension.signatureHash, extension.pkgName, extension.versionCode) .showDialog(controller.router) } @@ -407,9 +407,10 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At extAdapter?.updateItem(updateHeader) } - override fun trustSignature(signatureHash: String) { - presenter.trustSignature(signatureHash) + override fun trustExtension(pkgName: String, versionCode: Long, signatureHash: String) { + presenter.trustExtension(pkgName, versionCode, signatureHash) } + override fun uninstallExtension(pkgName: String) { presenter.uninstallExtension(pkgName) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionTrustDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionTrustDialog.kt index 265c0df66b..a10b3bddc2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionTrustDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionTrustDialog.kt @@ -10,10 +10,11 @@ class ExtensionTrustDialog(bundle: Bundle? = null) : DialogController(bundle) where T : ExtensionTrustDialog.Listener { lateinit var listener: Listener - constructor(target: T, signatureHash: String, pkgName: String) : this( + constructor(target: T, signatureHash: String, pkgName: String, versionCode: Long) : this( Bundle().apply { putString(SIGNATURE_KEY, signatureHash) putString(PKGNAME_KEY, pkgName) + putLong(VERSION_CODE, versionCode) }, ) { listener = target @@ -24,7 +25,7 @@ class ExtensionTrustDialog(bundle: Bundle? = null) : DialogController(bundle) .setTitle(R.string.untrusted_extension) .setMessage(R.string.untrusted_extension_message) .setPositiveButton(R.string.trust) { _, _ -> - listener.trustSignature(args.getString(SIGNATURE_KEY)!!) + listener.trustExtension(args.getString(PKGNAME_KEY)!!, args.getLong(VERSION_CODE), args.getString(SIGNATURE_KEY)!!) } .setNegativeButton(R.string.uninstall) { _, _ -> listener.uninstallExtension(args.getString(PKGNAME_KEY)!!) @@ -34,10 +35,11 @@ class ExtensionTrustDialog(bundle: Bundle? = null) : DialogController(bundle) private companion object { const val SIGNATURE_KEY = "signature_key" const val PKGNAME_KEY = "pkgname_key" + const val VERSION_CODE = "version_code" } interface Listener { - fun trustSignature(signatureHash: String) + fun trustExtension(pkgName: String, versionCode: Long, signatureHash: String) fun uninstallExtension(pkgName: String) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index b60ab1cd05..87fe8c8955 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -70,6 +70,7 @@ import com.google.android.material.snackbar.Snackbar import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback import com.google.common.primitives.Floats.max import com.google.common.primitives.Ints.max +import dev.yokai.presentation.extension.repo.ExtensionRepoController import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.Migrations import eu.kanade.tachiyomi.R @@ -1053,6 +1054,14 @@ open class MainActivity : BaseActivity() { controller?.showSheet() } } + Intent.ACTION_VIEW -> { + if (intent.scheme == "tachiyomi" && intent.data?.host == "add-repo") { + intent.data?.getQueryParameter("url")?.let { repoUrl -> + router.popToRoot() + router.pushController(ExtensionRepoController(repoUrl).withFadeTransaction()) + } + } + } else -> return false } return true