Ver Fonte

Remove built-in official extension repo support

arkon há 1 ano atrás
pai
commit
bf737cf95c

+ 1 - 2
app/src/main/java/eu/kanade/domain/source/interactor/CreateSourceRepo.kt

@@ -7,7 +7,7 @@ class CreateSourceRepo(private val preferences: SourcePreferences) {
 
     fun await(name: String): Result {
         // Do not allow invalid formats
-        if (!name.matches(repoRegex) || name.startsWith(OFFICIAL_REPO_BASE_URL)) {
+        if (!name.matches(repoRegex)) {
             return Result.InvalidUrl
         }
 
@@ -22,5 +22,4 @@ class CreateSourceRepo(private val preferences: SourcePreferences) {
     }
 }
 
-const val OFFICIAL_REPO_BASE_URL = "https://raw.githubusercontent.com/tachiyomiorg/tachiyomi-extensions/repo"
 private val repoRegex = """^https://.*/index\.min\.json$""".toRegex()

+ 22 - 36
app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt

@@ -16,7 +16,7 @@ import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.lazy.items
 import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.History
+import androidx.compose.material.icons.automirrored.outlined.Launch
 import androidx.compose.material.icons.outlined.Settings
 import androidx.compose.material3.AlertDialog
 import androidx.compose.material3.Button
@@ -67,13 +67,23 @@ fun ExtensionDetailsScreen(
     navigateUp: () -> Unit,
     state: ExtensionDetailsScreenModel.State,
     onClickSourcePreferences: (sourceId: Long) -> Unit,
-    onClickWhatsNew: () -> Unit,
     onClickEnableAll: () -> Unit,
     onClickDisableAll: () -> Unit,
     onClickClearCookies: () -> Unit,
     onClickUninstall: () -> Unit,
     onClickSource: (sourceId: Long) -> Unit,
 ) {
+    val uriHandler = LocalUriHandler.current
+    val url = remember(state.extension) {
+        val regex = """https://raw.githubusercontent.com/(.+?)/(.+?)/.+""".toRegex()
+        regex.find(state.extension?.repoUrl.orEmpty())
+            ?.let {
+                val (user, repo) = it.destructured
+                "https://github.com/$user/$repo"
+            }
+            ?: state.extension?.repoUrl
+    }
+
     Scaffold(
         topBar = { scrollBehavior ->
             AppBar(
@@ -83,12 +93,14 @@ fun ExtensionDetailsScreen(
                     AppBarActions(
                         actions = persistentListOf<AppBar.AppBarAction>().builder()
                             .apply {
-                                if (state.extension?.isUnofficial == false) {
+                                if (url != null) {
                                     add(
                                         AppBar.Action(
-                                            title = stringResource(MR.strings.whats_new),
-                                            icon = Icons.Outlined.History,
-                                            onClick = onClickWhatsNew,
+                                            title = stringResource(MR.strings.action_open_repo),
+                                            icon = Icons.AutoMirrored.Outlined.Launch,
+                                            onClick = {
+                                                uriHandler.openUri(url)
+                                            },
                                         ),
                                     )
                                 }
@@ -150,36 +162,10 @@ private fun ExtensionDetails(
     ScrollbarLazyColumn(
         contentPadding = contentPadding,
     ) {
-        when {
-            extension.isFromExternalRepo ->
-                item {
-                    val uriHandler = LocalUriHandler.current
-                    val url = remember(extension) {
-                        val regex = """https://raw.githubusercontent.com/(.+?)/(.+?)/.+""".toRegex()
-                        regex.find(extension.repoUrl.orEmpty())
-                            ?.let {
-                                val (user, repo) = it.destructured
-                                "https://github.com/$user/$repo"
-                            }
-                            ?: extension.repoUrl
-                    }
-
-                    WarningBanner(
-                        MR.strings.repo_extension_message,
-                        modifier = Modifier.clickable {
-                            url ?: return@clickable
-                            uriHandler.openUri(url)
-                        },
-                    )
-                }
-            extension.isUnofficial ->
-                item {
-                    WarningBanner(MR.strings.unofficial_extension_message)
-                }
-            extension.isObsolete ->
-                item {
-                    WarningBanner(MR.strings.obsolete_extension_message)
-                }
+        if (extension.isObsolete) {
+            item {
+                WarningBanner(MR.strings.obsolete_extension_message)
+            }
         }
 
         item {

+ 0 - 1
app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt

@@ -342,7 +342,6 @@ private fun ExtensionItemContent(
 
                 val warning = when {
                     extension is Extension.Untrusted -> MR.strings.ext_untrusted
-                    extension is Extension.Installed && extension.isUnofficial -> MR.strings.ext_unofficial
                     extension is Extension.Installed && extension.isObsolete -> MR.strings.ext_obsolete
                     extension.isNsfw -> MR.strings.ext_nsfw_short
                     else -> null

+ 2 - 1
app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt

@@ -26,6 +26,7 @@ import eu.kanade.presentation.browse.components.BaseSourceItem
 import eu.kanade.presentation.browse.components.SourceIcon
 import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrateSourceScreenModel
 import eu.kanade.tachiyomi.util.system.copyToClipboard
+import kotlinx.collections.immutable.ImmutableList
 import tachiyomi.domain.source.model.Source
 import tachiyomi.i18n.MR
 import tachiyomi.presentation.core.components.Badge
@@ -75,7 +76,7 @@ fun MigrateSourceScreen(
 
 @Composable
 private fun MigrateSourceList(
-    list: List<Pair<Source, Long>>,
+    list: ImmutableList<Pair<Source, Long>>,
     contentPadding: PaddingValues,
     onClickItem: (Source) -> Unit,
     onLongClickItem: (Source) -> Unit,

+ 3 - 5
app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt

@@ -178,7 +178,7 @@ class ExtensionManager(
             val pkgName = installedExt.pkgName
             val availableExt = availableExtensions.find { it.pkgName == pkgName }
 
-            if (!installedExt.isUnofficial && availableExt == null && !installedExt.isObsolete) {
+            if (availableExt == null && !installedExt.isObsolete) {
                 mutInstalledExtensions[index] = installedExt.copy(isObsolete = true)
                 changed = true
             } else if (availableExt != null) {
@@ -187,13 +187,11 @@ class ExtensionManager(
                 if (installedExt.hasUpdate != hasUpdate) {
                     mutInstalledExtensions[index] = installedExt.copy(
                         hasUpdate = hasUpdate,
-                        isFromExternalRepo = availableExt.isFromExternalRepo,
                         repoUrl = availableExt.repoUrl,
                     )
                     changed = true
-                } else if (availableExt.isFromExternalRepo) {
+                } else {
                     mutInstalledExtensions[index] = installedExt.copy(
-                        isFromExternalRepo = true,
                         repoUrl = availableExt.repoUrl,
                     )
                     changed = true
@@ -363,7 +361,7 @@ class ExtensionManager(
 
     private fun Extension.Installed.updateExists(availableExtension: Extension.Available? = null): Boolean {
         val availableExt = availableExtension ?: _availableExtensionsFlow.value.find { it.pkgName == pkgName }
-        if (isUnofficial || availableExt == null) return false
+            ?: return false
 
         return (availableExt.versionCode > versionCode || availableExt.libVersion > libVersion)
     }

+ 6 - 17
app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt

@@ -1,7 +1,6 @@
 package eu.kanade.tachiyomi.extension.api
 
 import android.content.Context
-import eu.kanade.domain.source.interactor.OFFICIAL_REPO_BASE_URL
 import eu.kanade.domain.source.service.SourcePreferences
 import eu.kanade.tachiyomi.extension.ExtensionManager
 import eu.kanade.tachiyomi.extension.model.Extension
@@ -36,14 +35,11 @@ internal class ExtensionApi {
 
     suspend fun findExtensions(): List<Extension.Available> {
         return withIOContext {
-            val extensions = buildList {
-                addAll(getExtensions(OFFICIAL_REPO_BASE_URL, true))
-                sourcePreferences.extensionRepos().get().map { addAll(getExtensions(it, false)) }
-            }
+            val extensions = sourcePreferences.extensionRepos().get().flatMap { getExtensions(it) }
 
             // Sanity check - a small number of extensions probably means something broke
             // with the repo generator
-            if (extensions.size < 50) {
+            if (extensions.isEmpty()) {
                 throw Exception()
             }
 
@@ -51,10 +47,7 @@ internal class ExtensionApi {
         }
     }
 
-    private suspend fun getExtensions(
-        repoBaseUrl: String,
-        isOfficialRepo: Boolean,
-    ): List<Extension.Available> {
+    private suspend fun getExtensions(repoBaseUrl: String): List<Extension.Available> {
         return try {
             val response = networkService.client
                 .newCall(GET("$repoBaseUrl/index.min.json"))
@@ -63,7 +56,7 @@ internal class ExtensionApi {
             with(json) {
                 response
                     .parseAs<List<ExtensionJsonObject>>()
-                    .toExtensions(repoBaseUrl, isRepoSource = !isOfficialRepo)
+                    .toExtensions(repoBaseUrl)
             }
         } catch (e: Throwable) {
             logcat(LogPriority.ERROR, e) { "Failed to get extensions from $repoBaseUrl" }
@@ -98,7 +91,7 @@ internal class ExtensionApi {
             val availableExt = extensions.find { it.pkgName == pkgName } ?: continue
             val hasUpdatedVer = availableExt.versionCode > installedExt.versionCode
             val hasUpdatedLib = availableExt.libVersion > installedExt.libVersion
-            val hasUpdate = installedExt.isUnofficial.not() && (hasUpdatedVer || hasUpdatedLib)
+            val hasUpdate = hasUpdatedVer || hasUpdatedLib
             if (hasUpdate) {
                 extensionsWithUpdate.add(installedExt)
             }
@@ -111,10 +104,7 @@ internal class ExtensionApi {
         return extensionsWithUpdate
     }
 
-    private fun List<ExtensionJsonObject>.toExtensions(
-        repoUrl: String,
-        isRepoSource: Boolean,
-    ): List<Extension.Available> {
+    private fun List<ExtensionJsonObject>.toExtensions(repoUrl: String): List<Extension.Available> {
         return this
             .filter {
                 val libVersion = it.extractLibVersion()
@@ -133,7 +123,6 @@ internal class ExtensionApi {
                     apkName = it.apk,
                     iconUrl = "$repoUrl/icon/${it.pkg}.png",
                     repoUrl = repoUrl,
-                    isFromExternalRepo = isRepoSource,
                 )
             }
     }

+ 0 - 3
app/src/main/java/eu/kanade/tachiyomi/extension/model/Extension.kt

@@ -27,10 +27,8 @@ sealed class Extension {
         val icon: Drawable?,
         val hasUpdate: Boolean = false,
         val isObsolete: Boolean = false,
-        val isUnofficial: Boolean = false,
         val isShared: Boolean,
         val repoUrl: String? = null,
-        val isFromExternalRepo: Boolean = false,
     ) : Extension()
 
     data class Available(
@@ -45,7 +43,6 @@ sealed class Extension {
         val apkName: String,
         val iconUrl: String,
         val repoUrl: String,
-        val isFromExternalRepo: Boolean,
     ) : Extension() {
 
         data class Source(

+ 1 - 17
app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt

@@ -59,9 +59,6 @@ internal object ExtensionLoader {
         PackageManager.GET_SIGNATURES or
         (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) PackageManager.GET_SIGNING_CERTIFICATES else 0)
 
-    // inorichi's key
-    private const val officialSignature = "7ce04da7773d41b489f4693a366c36bcd0a11fc39b547168553c285bd7348e23"
-
     private const val PRIVATE_EXTENSION_EXTENSION = "ext"
 
     private fun getPrivateExtensionDir(context: Context) = File(context.filesDir, "exts")
@@ -255,7 +252,7 @@ internal object ExtensionLoader {
         if (signatures.isNullOrEmpty()) {
             logcat(LogPriority.WARN) { "Package $pkgName isn't signed" }
             return LoadResult.Error
-        } else if (!isTrusted(pkgInfo, signatures)) {
+        } else if (!trustExtension.isTrusted(pkgInfo, signatures.last())) {
             val extension = Extension.Untrusted(
                 extName,
                 pkgName,
@@ -323,7 +320,6 @@ internal object ExtensionLoader {
             isNsfw = isNsfw,
             sources = sources,
             pkgFactory = appInfo.metaData.getString(METADATA_SOURCE_FACTORY),
-            isUnofficial = !isOfficiallySigned(signatures),
             icon = appInfo.loadIcon(pkgManager),
             isShared = extensionInfo.isShared,
         )
@@ -383,18 +379,6 @@ internal object ExtensionLoader {
             ?.toList()
     }
 
-    private fun isTrusted(pkgInfo: PackageInfo, signatures: List<String>): Boolean {
-        if (officialSignature in signatures) {
-            return true
-        }
-
-        return trustExtension.isTrusted(pkgInfo, signatures.last())
-    }
-
-    private fun isOfficiallySigned(signatures: List<String>): Boolean {
-        return signatures.all { it == officialSignature }
-    }
-
     /**
      * On Android 13+ the ApplicationInfo generated by getPackageArchiveInfo doesn't
      * have sourceDir which breaks assets loading (used for getting icon here).

+ 0 - 3
app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreen.kt

@@ -5,7 +5,6 @@ import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalUriHandler
 import cafe.adriel.voyager.core.model.rememberScreenModel
 import cafe.adriel.voyager.navigator.LocalNavigator
 import cafe.adriel.voyager.navigator.currentOrThrow
@@ -30,13 +29,11 @@ data class ExtensionDetailsScreen(
         }
 
         val navigator = LocalNavigator.currentOrThrow
-        val uriHandler = LocalUriHandler.current
 
         ExtensionDetailsScreen(
             navigateUp = navigator::pop,
             state = state,
             onClickSourcePreferences = { navigator.push(SourcePreferencesScreen(it)) },
-            onClickWhatsNew = { uriHandler.openUri(screenModel.getChangelogUrl()) },
             onClickEnableAll = { screenModel.toggleSources(true) },
             onClickDisableAll = { screenModel.toggleSources(false) },
             onClickClearCookies = screenModel::clearCookies,

+ 0 - 29
app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreenModel.kt

@@ -29,9 +29,6 @@ import tachiyomi.core.util.system.logcat
 import uy.kohesive.injekt.Injekt
 import uy.kohesive.injekt.api.get
 
-private const val URL_EXTENSION_COMMITS =
-    "https://github.com/tachiyomiorg/tachiyomi-extensions/commits/master"
-
 class ExtensionDetailsScreenModel(
     pkgName: String,
     context: Context,
@@ -86,16 +83,6 @@ class ExtensionDetailsScreenModel(
         }
     }
 
-    fun getChangelogUrl(): String {
-        val extension = state.value.extension ?: return ""
-
-        val pkgName = extension.pkgName.substringAfter("eu.kanade.tachiyomi.extension.")
-        val pkgFactory = extension.pkgFactory
-
-        // Falling back on GitHub commit history because there is no explicit changelog in extension
-        return createUrl(URL_EXTENSION_COMMITS, pkgName, pkgFactory)
-    }
-
     fun clearCookies() {
         val extension = state.value.extension ?: return
 
@@ -131,22 +118,6 @@ class ExtensionDetailsScreenModel(
             ?.let { toggleSource.await(it, enable) }
     }
 
-    private fun createUrl(
-        url: String,
-        pkgName: String,
-        pkgFactory: String?,
-        path: String = "",
-    ): String {
-        return if (!pkgFactory.isNullOrEmpty()) {
-            when (path.isEmpty()) {
-                true -> "$url/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/$pkgFactory"
-                else -> "$url/multisrc/overrides/$pkgFactory/" + (pkgName.split(".").lastOrNull() ?: "") + path
-            }
-        } else {
-            url + "/src/" + pkgName.replace(".", "/") + path
-        }
-    }
-
     @Immutable
     data class State(
         val extension: Extension.Installed? = null,

+ 2 - 2
app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt

@@ -56,12 +56,12 @@ class CrashLogUtil(
                 val availableExtension = availableExtensions[it.pkgName]
                 val hasUpdate = (availableExtension?.versionCode ?: 0) > it.versionCode
 
-                if (!hasUpdate && !it.isObsolete && !it.isUnofficial) return@mapNotNull null
+                if (!hasUpdate && !it.isObsolete) return@mapNotNull null
 
                 """
                     - ${it.name}
                       Installed: ${it.versionName} / Available: ${availableExtension?.versionName ?: "?"}
-                      Obsolete: ${it.isObsolete} / Unofficial: ${it.isUnofficial}
+                      Obsolete: ${it.isObsolete}
                 """.trimIndent()
             }
 

+ 3 - 3
app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt

@@ -43,8 +43,6 @@ fun Long.toDateKey(): Date {
     return Date.from(instant.truncatedTo(ChronoUnit.DAYS))
 }
 
-private const val MILLISECONDS_IN_DAY = 86_400_000L
-
 fun Date.toRelativeString(
     context: Context,
     relative: Boolean = true,
@@ -69,6 +67,8 @@ fun Date.toRelativeString(
     }
 }
 
+private const val MILLISECONDS_IN_DAY = 86_400_000L
+
 private val Date.timeWithOffset: Long
     get() {
         return Calendar.getInstance().run {
@@ -78,6 +78,6 @@ private val Date.timeWithOffset: Long
         }
     }
 
-fun Long.floorNearest(to: Long): Long {
+private fun Long.floorNearest(to: Long): Long {
     return this.floorDiv(to) * to
 }

+ 8 - 6
data/src/main/java/tachiyomi/data/source/SourceRepositoryImpl.kt

@@ -5,6 +5,7 @@ import eu.kanade.tachiyomi.source.Source
 import eu.kanade.tachiyomi.source.model.FilterList
 import eu.kanade.tachiyomi.source.online.HttpSource
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.map
 import tachiyomi.data.DatabaseHandler
 import tachiyomi.domain.source.model.SourceWithCount
@@ -38,18 +39,19 @@ class SourceRepositoryImpl(
     }
 
     override fun getSourcesWithFavoriteCount(): Flow<List<Pair<DomainSource, Long>>> {
-        val sourceIdWithFavoriteCount =
-            handler.subscribeToList { mangasQueries.getSourceIdWithFavoriteCount() }
-        return sourceIdWithFavoriteCount.map { sourceIdsWithCount ->
-            sourceIdsWithCount
-                .map { (sourceId, count) ->
+        return combine(
+            handler.subscribeToList { mangasQueries.getSourceIdWithFavoriteCount() },
+            sourceManager.catalogueSources
+        ) { sourceIdWithFavoriteCount, _ -> sourceIdWithFavoriteCount }
+            .map {
+                it.map { (sourceId, count) ->
                     val source = sourceManager.getOrStub(sourceId)
                     val domainSource = mapSourceToDomainSource(source).copy(
                         isStub = source is StubSource,
                     )
                     domainSource to count
                 }
-        }
+            }
     }
 
     override fun getSourcesWithNonLibraryManga(): Flow<List<SourceWithCount>> {

+ 2 - 4
i18n/src/commonMain/resources/MR/base/strings.xml

@@ -311,14 +311,12 @@
     <string name="ext_installing">Installing</string>
     <string name="ext_installed">Installed</string>
     <string name="ext_trust">Trust</string>
-    <string name="ext_unofficial">Unofficial</string>
     <string name="ext_untrusted">Untrusted</string>
     <string name="ext_uninstall">Uninstall</string>
     <string name="ext_app_info">App info</string>
     <string name="untrusted_extension">Untrusted extension</string>
-    <string name="untrusted_extension_message">This extension was signed by any unknown author and wasn\'t loaded.\n\nMalicious extensions can read any stored login credentials or execute arbitrary code.\n\nBy trusting this extension\'s certificate, you accept these risks.</string>
+    <string name="untrusted_extension_message">Malicious extensions can read any stored login credentials or execute arbitrary code.\n\nBy trusting this extension, you accept these risks.</string>
     <string name="obsolete_extension_message">This extension is no longer available. It may not function properly and can cause issues with the app. Uninstalling it is recommended.</string>
-    <string name="unofficial_extension_message">This extension is not from the official repo.</string>
     <string name="extension_api_error">Failed to fetch available extensions</string>
     <string name="ext_info_version">Version</string>
     <string name="ext_info_language">Language</string>
@@ -346,7 +344,7 @@
     <string name="action_delete_repo">Delete repo</string>
     <string name="invalid_repo_name">Invalid repo URL</string>
     <string name="delete_repo_confirmation">Do you wish to delete the repo \"%s\"?</string>
-    <string name="repo_extension_message">This extension is from an external repo. Tap to view the repo.</string>
+    <string name="action_open_repo">Open source repo</string>
 
       <!-- Reader section -->
     <string name="pref_fullscreen">Fullscreen</string>