mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
feat: Final touch before merge
* Trust Extension rework * Deep link
This commit is contained in:
parent
67f32d400d
commit
addb84ee1e
12 changed files with 100 additions and 48 deletions
|
@ -60,6 +60,16 @@
|
|||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<!-- Deep link to add repos -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:scheme="tachiyomi" />
|
||||
<data android:host="add-repo" />
|
||||
</intent-filter>
|
||||
<meta-data android:name="android.app.searchable" android:resource="@xml/searchable"/>
|
||||
</activity>
|
||||
<activity
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,6 +73,7 @@ fun ExtensionRepoScreen(
|
|||
item {
|
||||
ExtensionRepoItem(
|
||||
repoUrl = repo,
|
||||
// TODO: Confirmation dialog
|
||||
onDeleteClick = { viewModel.deleteRepo(it) },
|
||||
)
|
||||
}
|
||||
|
@ -89,6 +81,10 @@ fun ExtensionRepoScreen(
|
|||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(repoUrl) {
|
||||
repoUrl?.let { viewModel.addRepo(repoUrl) }
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.event.collectLatest { event ->
|
||||
if (event is ExtensionRepoEvent.LocalizedMessage)
|
||||
|
|
|
@ -255,6 +255,11 @@ object Migrations {
|
|||
} catch (_: Exception) {
|
||||
}
|
||||
}
|
||||
if (oldVersion < 112) {
|
||||
prefs.edit {
|
||||
remove("trusted_signatures")
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -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", "")
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<LoadResult.Success>()
|
||||
.forEach { registerNewExtension(it.extension) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -274,7 +274,7 @@ class ExtensionBottomPresenter : BaseMigrationPresenter<ExtensionBottomSheet>()
|
|||
}
|
||||
}
|
||||
|
||||
fun trustSignature(signatureHash: String) {
|
||||
extensionManager.trustSignature(signatureHash)
|
||||
fun trustExtension(pkgName: String, versionCode: Long, signatureHash: String) {
|
||||
extensionManager.trust(pkgName, versionCode, signatureHash)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -10,10 +10,11 @@ class ExtensionTrustDialog<T>(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<T>(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<T>(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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<MainActivityBinding>() {
|
|||
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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue