2018-02-05 22:50:56 +01:00
|
|
|
package eu.kanade.tachiyomi.extension
|
|
|
|
|
|
|
|
|
|
import android.content.Context
|
2020-03-20 06:52:03 +01:00
|
|
|
import android.graphics.drawable.Drawable
|
2020-12-20 23:56:59 +01:00
|
|
|
import androidx.core.content.ContextCompat
|
2024-01-08 23:15:48 +01:00
|
|
|
import eu.kanade.domain.extension.interactor.TrustExtension
|
2022-09-18 16:36:41 +02:00
|
|
|
import eu.kanade.domain.source.service.SourcePreferences
|
2023-12-24 04:40:54 +01:00
|
|
|
import eu.kanade.tachiyomi.R
|
2024-01-06 05:13:16 +01:00
|
|
|
import eu.kanade.tachiyomi.extension.api.ExtensionApi
|
2023-08-03 00:00:06 +02:00
|
|
|
import eu.kanade.tachiyomi.extension.api.ExtensionUpdateNotifier
|
2018-02-05 22:50:56 +01:00
|
|
|
import eu.kanade.tachiyomi.extension.model.Extension
|
|
|
|
|
import eu.kanade.tachiyomi.extension.model.InstallStep
|
|
|
|
|
import eu.kanade.tachiyomi.extension.model.LoadResult
|
|
|
|
|
import eu.kanade.tachiyomi.extension.util.ExtensionInstallReceiver
|
|
|
|
|
import eu.kanade.tachiyomi.extension.util.ExtensionInstaller
|
|
|
|
|
import eu.kanade.tachiyomi.extension.util.ExtensionLoader
|
2020-04-04 04:54:52 +02:00
|
|
|
import eu.kanade.tachiyomi.util.system.toast
|
2021-03-07 06:23:23 +01:00
|
|
|
import exh.log.xLogD
|
2019-04-14 18:48:59 +02:00
|
|
|
import exh.source.BlacklistedSources
|
2021-01-26 06:40:57 +01:00
|
|
|
import exh.source.EH_SOURCE_ID
|
|
|
|
|
import exh.source.EXH_SOURCE_ID
|
|
|
|
|
import exh.source.MERGED_SOURCE_ID
|
2022-10-21 20:42:21 +02:00
|
|
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
|
|
|
|
import kotlinx.coroutines.GlobalScope
|
2019-04-10 03:43:47 +02:00
|
|
|
import kotlinx.coroutines.async
|
2023-05-30 16:25:20 +02:00
|
|
|
import kotlinx.coroutines.flow.Flow
|
2022-07-16 20:44:37 +02:00
|
|
|
import kotlinx.coroutines.flow.MutableStateFlow
|
2022-10-21 20:42:21 +02:00
|
|
|
import kotlinx.coroutines.flow.SharingStarted
|
2022-07-16 20:44:37 +02:00
|
|
|
import kotlinx.coroutines.flow.asStateFlow
|
2023-05-30 16:25:20 +02:00
|
|
|
import kotlinx.coroutines.flow.emptyFlow
|
2022-10-21 20:42:21 +02:00
|
|
|
import kotlinx.coroutines.flow.map
|
|
|
|
|
import kotlinx.coroutines.flow.stateIn
|
2021-11-29 00:29:22 +01:00
|
|
|
import logcat.LogPriority
|
2023-01-28 04:31:12 +01:00
|
|
|
import tachiyomi.core.util.lang.launchNow
|
|
|
|
|
import tachiyomi.core.util.lang.withUIContext
|
|
|
|
|
import tachiyomi.core.util.system.logcat
|
2023-05-03 16:33:05 +02:00
|
|
|
import tachiyomi.domain.source.model.StubSource
|
2023-11-18 19:54:56 +01:00
|
|
|
import tachiyomi.i18n.MR
|
2018-02-05 22:50:56 +01:00
|
|
|
import uy.kohesive.injekt.Injekt
|
|
|
|
|
import uy.kohesive.injekt.api.get
|
2022-08-30 21:37:15 +02:00
|
|
|
import java.util.Locale
|
2018-02-05 22:50:56 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The manager of extensions installed as another apk which extend the available sources. It handles
|
|
|
|
|
* the retrieval of remotely available extensions as well as installing, updating and removing them.
|
|
|
|
|
* To avoid malicious distribution, every extension must be signed and it will only be loaded if its
|
|
|
|
|
* signature is trusted, otherwise the user will be prompted with a warning to trust it before being
|
|
|
|
|
* loaded.
|
|
|
|
|
*/
|
|
|
|
|
class ExtensionManager(
|
2020-02-27 00:03:34 +01:00
|
|
|
private val context: Context,
|
2022-09-18 16:36:41 +02:00
|
|
|
private val preferences: SourcePreferences = Injekt.get(),
|
2024-01-07 19:35:44 +01:00
|
|
|
private val trustExtension: TrustExtension = Injekt.get(),
|
2018-02-05 22:50:56 +01:00
|
|
|
) {
|
|
|
|
|
|
2022-10-21 21:00:41 +02:00
|
|
|
var isInitialized = false
|
|
|
|
|
private set
|
|
|
|
|
|
2018-02-05 22:50:56 +01:00
|
|
|
/**
|
|
|
|
|
* API where all the available extensions can be found.
|
|
|
|
|
*/
|
2024-01-06 05:13:16 +01:00
|
|
|
private val api = ExtensionApi()
|
2018-02-05 22:50:56 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The installer which installs, updates and uninstalls the extensions.
|
|
|
|
|
*/
|
|
|
|
|
private val installer by lazy { ExtensionInstaller(context) }
|
|
|
|
|
|
2020-04-04 03:39:55 +02:00
|
|
|
private val iconMap = mutableMapOf<String, Drawable>()
|
|
|
|
|
|
2022-10-21 20:42:21 +02:00
|
|
|
private val _installedExtensionsFlow = MutableStateFlow(emptyList<Extension.Installed>())
|
|
|
|
|
val installedExtensionsFlow = _installedExtensionsFlow.asStateFlow()
|
2022-07-16 20:44:37 +02:00
|
|
|
|
2022-08-30 21:37:15 +02:00
|
|
|
private var subLanguagesEnabledOnFirstRun = preferences.enabledLanguages().isSet()
|
|
|
|
|
|
2022-04-24 20:35:59 +02:00
|
|
|
fun getAppIconForSource(sourceId: Long): Drawable? {
|
2022-10-21 20:42:21 +02:00
|
|
|
val pkgName = _installedExtensionsFlow.value.find { ext -> ext.sources.any { it.id == sourceId } }?.pkgName
|
2020-04-04 03:39:55 +02:00
|
|
|
if (pkgName != null) {
|
2023-08-05 18:15:52 +02:00
|
|
|
return iconMap[pkgName] ?: iconMap.getOrPut(pkgName) {
|
|
|
|
|
ExtensionLoader.getExtensionPackageInfoFromPkgName(context, pkgName)!!.applicationInfo
|
|
|
|
|
.loadIcon(context.packageManager)
|
|
|
|
|
}
|
2020-04-04 03:39:55 +02:00
|
|
|
}
|
2020-05-22 23:22:24 +02:00
|
|
|
|
2020-06-20 02:50:16 +02:00
|
|
|
// SY -->
|
2022-04-24 20:35:59 +02:00
|
|
|
return when (sourceId) {
|
2020-12-20 23:56:59 +01:00
|
|
|
EH_SOURCE_ID -> ContextCompat.getDrawable(context, R.mipmap.ic_ehentai_source)
|
2023-05-23 01:38:02 +02:00
|
|
|
EXH_SOURCE_ID -> ContextCompat.getDrawable(context, R.mipmap.ic_exhentai_source)
|
2020-12-20 23:56:59 +01:00
|
|
|
MERGED_SOURCE_ID -> ContextCompat.getDrawable(context, R.mipmap.ic_merged_source)
|
2020-05-22 22:16:49 +02:00
|
|
|
else -> null
|
|
|
|
|
}
|
2020-06-20 02:50:16 +02:00
|
|
|
// SY <--
|
2020-03-20 06:52:03 +01:00
|
|
|
}
|
|
|
|
|
|
2022-10-21 20:42:21 +02:00
|
|
|
private val _availableExtensionsFlow = MutableStateFlow(emptyList<Extension.Available>())
|
2022-07-16 21:08:15 +02:00
|
|
|
|
2022-10-21 20:42:21 +02:00
|
|
|
// SY -->
|
|
|
|
|
@OptIn(DelicateCoroutinesApi::class)
|
|
|
|
|
val availableExtensionsFlow = _availableExtensionsFlow
|
|
|
|
|
.map { it.filterNotBlacklisted() }
|
|
|
|
|
.stateIn(GlobalScope, SharingStarted.Eagerly, emptyList())
|
|
|
|
|
// SY <--
|
2022-07-16 21:08:15 +02:00
|
|
|
|
2023-05-03 16:33:05 +02:00
|
|
|
private var availableExtensionsSourcesData: Map<Long, StubSource> = emptyMap()
|
2022-06-14 15:10:40 +02:00
|
|
|
|
|
|
|
|
private fun setupAvailableExtensionsSourcesDataMap(extensions: List<Extension.Available>) {
|
|
|
|
|
if (extensions.isEmpty()) return
|
|
|
|
|
availableExtensionsSourcesData = extensions
|
2023-05-03 16:33:05 +02:00
|
|
|
.flatMap { ext -> ext.sources.map { it.toStubSource() } }
|
2022-06-14 15:10:40 +02:00
|
|
|
.associateBy { it.id }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun getSourceData(id: Long) = availableExtensionsSourcesData[id]
|
|
|
|
|
|
2022-10-21 20:42:21 +02:00
|
|
|
private val _untrustedExtensionsFlow = MutableStateFlow(emptyList<Extension.Untrusted>())
|
|
|
|
|
val untrustedExtensionsFlow = _untrustedExtensionsFlow.asStateFlow()
|
2022-07-16 21:08:15 +02:00
|
|
|
|
|
|
|
|
init {
|
2018-02-05 22:50:56 +01:00
|
|
|
initExtensions()
|
|
|
|
|
ExtensionInstallReceiver(InstallationListener()).register(context)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Loads and registers the installed extensions.
|
|
|
|
|
*/
|
|
|
|
|
private fun initExtensions() {
|
|
|
|
|
val extensions = ExtensionLoader.loadExtensions(context)
|
|
|
|
|
|
2022-10-21 20:42:21 +02:00
|
|
|
_installedExtensionsFlow.value = extensions
|
2020-04-25 20:24:45 +02:00
|
|
|
.filterIsInstance<LoadResult.Success>()
|
|
|
|
|
.map { it.extension }
|
2018-02-05 22:50:56 +01:00
|
|
|
|
2022-10-21 20:42:21 +02:00
|
|
|
_untrustedExtensionsFlow.value = extensions
|
2020-04-25 20:24:45 +02:00
|
|
|
.filterIsInstance<LoadResult.Untrusted>()
|
|
|
|
|
.map { it.extension }
|
2020-06-20 02:50:16 +02:00
|
|
|
// SY -->
|
2020-01-06 17:43:11 +01:00
|
|
|
.filterNotBlacklisted()
|
2020-06-20 02:50:16 +02:00
|
|
|
// SY <--
|
2022-10-21 21:00:41 +02:00
|
|
|
|
|
|
|
|
isInitialized = true
|
2018-02-05 22:50:56 +01:00
|
|
|
}
|
|
|
|
|
|
2019-04-14 18:48:59 +02:00
|
|
|
// EXH -->
|
2020-12-26 20:22:55 +01:00
|
|
|
private fun <T : Extension> Iterable<T>.filterNotBlacklisted(): List<T> {
|
2020-11-30 20:35:31 +01:00
|
|
|
val blacklistEnabled = preferences.enableSourceBlacklist().get()
|
2021-03-12 04:39:46 +01:00
|
|
|
return filterNot { extension ->
|
|
|
|
|
extension.isBlacklisted(blacklistEnabled)
|
|
|
|
|
.also {
|
2021-03-31 20:29:27 +02:00
|
|
|
if (it) this@ExtensionManager.xLogD("Removing blacklisted extension: (name: %s, pkgName: %s)!", extension.name, extension.pkgName)
|
2021-03-12 04:39:46 +01:00
|
|
|
}
|
2019-04-14 18:48:59 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-10 23:20:42 +01:00
|
|
|
private fun Extension.isBlacklisted(blacklistEnabled: Boolean = preferences.enableSourceBlacklist().get()): Boolean {
|
2019-04-14 18:48:59 +02:00
|
|
|
return pkgName in BlacklistedSources.BLACKLISTED_EXTENSIONS && blacklistEnabled
|
|
|
|
|
}
|
|
|
|
|
// EXH <--
|
2018-02-05 22:50:56 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Finds the available extensions in the [api] and updates [availableExtensions].
|
|
|
|
|
*/
|
2022-08-04 21:44:37 +02:00
|
|
|
suspend fun findAvailableExtensions() {
|
|
|
|
|
val extensions: List<Extension.Available> = try {
|
|
|
|
|
api.findExtensions()
|
|
|
|
|
} catch (e: Exception) {
|
|
|
|
|
logcat(LogPriority.ERROR, e)
|
2023-11-18 19:54:56 +01:00
|
|
|
withUIContext { context.toast(MR.strings.extension_api_error) }
|
2022-08-04 21:44:37 +02:00
|
|
|
emptyList()
|
2020-02-03 04:57:15 +01:00
|
|
|
}
|
2022-08-04 21:44:37 +02:00
|
|
|
|
2022-08-30 21:37:15 +02:00
|
|
|
enableAdditionalSubLanguages(extensions)
|
|
|
|
|
|
2022-10-21 20:42:21 +02:00
|
|
|
_availableExtensionsFlow.value = extensions
|
|
|
|
|
updatedInstalledExtensionsStatuses(extensions)
|
|
|
|
|
setupAvailableExtensionsSourcesDataMap(extensions)
|
2018-02-05 22:50:56 +01:00
|
|
|
}
|
|
|
|
|
|
2022-08-30 21:37:15 +02:00
|
|
|
/**
|
|
|
|
|
* Enables the additional sub-languages in the app first run. This addresses
|
|
|
|
|
* the issue where users still need to enable some specific languages even when
|
|
|
|
|
* the device language is inside that major group. As an example, if a user
|
|
|
|
|
* has a zh device language, the app will also enable zh-Hans and zh-Hant.
|
|
|
|
|
*
|
|
|
|
|
* If the user have already changed the enabledLanguages preference value once,
|
|
|
|
|
* the new languages will not be added to respect the user enabled choices.
|
|
|
|
|
*/
|
|
|
|
|
private fun enableAdditionalSubLanguages(extensions: List<Extension.Available>) {
|
|
|
|
|
if (subLanguagesEnabledOnFirstRun || extensions.isEmpty()) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Use the source lang as some aren't present on the extension level.
|
|
|
|
|
val availableLanguages = extensions
|
|
|
|
|
.flatMap(Extension.Available::sources)
|
2023-05-03 16:33:05 +02:00
|
|
|
.distinctBy(Extension.Available.Source::lang)
|
|
|
|
|
.map(Extension.Available.Source::lang)
|
2022-08-30 21:37:15 +02:00
|
|
|
|
|
|
|
|
val deviceLanguage = Locale.getDefault().language
|
2022-09-17 17:48:24 +02:00
|
|
|
val defaultLanguages = preferences.enabledLanguages().defaultValue()
|
2022-08-30 21:37:15 +02:00
|
|
|
val languagesToEnable = availableLanguages.filter {
|
|
|
|
|
it != deviceLanguage && it.startsWith(deviceLanguage)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
preferences.enabledLanguages().set(defaultLanguages + languagesToEnable)
|
|
|
|
|
subLanguagesEnabledOnFirstRun = true
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-05 22:50:56 +01:00
|
|
|
/**
|
|
|
|
|
* Sets the update field of the installed extensions with the given [availableExtensions].
|
|
|
|
|
*
|
|
|
|
|
* @param availableExtensions The list of extensions given by the [api].
|
|
|
|
|
*/
|
2020-01-13 00:27:04 +01:00
|
|
|
private fun updatedInstalledExtensionsStatuses(availableExtensions: List<Extension.Available>) {
|
2020-01-14 03:30:20 +01:00
|
|
|
if (availableExtensions.isEmpty()) {
|
2020-03-21 03:22:39 +01:00
|
|
|
preferences.extensionUpdatesCount().set(0)
|
2020-01-14 03:30:20 +01:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-21 20:42:21 +02:00
|
|
|
val mutInstalledExtensions = _installedExtensionsFlow.value.toMutableList()
|
2018-02-05 22:50:56 +01:00
|
|
|
var changed = false
|
|
|
|
|
|
|
|
|
|
for ((index, installedExt) in mutInstalledExtensions.withIndex()) {
|
|
|
|
|
val pkgName = installedExt.pkgName
|
2020-06-20 02:50:16 +02:00
|
|
|
// SY -->
|
2022-10-21 20:42:21 +02:00
|
|
|
val availableExt = _availableExtensionsFlow.value.find { it.pkgName == pkgName }
|
2020-06-20 02:50:16 +02:00
|
|
|
// SY <--
|
2018-02-05 22:50:56 +01:00
|
|
|
|
2024-01-08 04:22:32 +01:00
|
|
|
if (availableExt == null && !installedExt.isObsolete) {
|
2020-01-14 03:30:20 +01:00
|
|
|
mutInstalledExtensions[index] = installedExt.copy(isObsolete = true)
|
2018-02-05 22:50:56 +01:00
|
|
|
changed = true
|
2020-06-20 02:50:16 +02:00
|
|
|
// SY -->
|
2020-05-30 20:10:35 +02:00
|
|
|
} else if (installedExt.isBlacklisted() && !installedExt.isRedundant) {
|
|
|
|
|
mutInstalledExtensions[index] = installedExt.copy(isRedundant = true)
|
|
|
|
|
changed = true
|
2020-06-20 02:50:16 +02:00
|
|
|
// SY <--
|
2020-01-14 03:30:20 +01:00
|
|
|
} else if (availableExt != null) {
|
2022-10-28 12:37:59 +02:00
|
|
|
val hasUpdate = installedExt.updateExists(availableExt)
|
2022-07-10 16:00:48 +02:00
|
|
|
|
2020-01-13 00:27:04 +01:00
|
|
|
if (installedExt.hasUpdate != hasUpdate) {
|
2024-01-07 22:35:25 +01:00
|
|
|
mutInstalledExtensions[index] = installedExt.copy(
|
|
|
|
|
hasUpdate = hasUpdate,
|
|
|
|
|
repoUrl = availableExt.repoUrl,
|
|
|
|
|
)
|
2022-10-11 20:50:38 +02:00
|
|
|
changed = true
|
2024-01-08 04:22:32 +01:00
|
|
|
} else {
|
2024-01-07 22:35:25 +01:00
|
|
|
mutInstalledExtensions[index] = installedExt.copy(
|
|
|
|
|
repoUrl = availableExt.repoUrl,
|
|
|
|
|
)
|
2020-01-13 00:27:04 +01:00
|
|
|
changed = true
|
|
|
|
|
}
|
2018-02-05 22:50:56 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (changed) {
|
2022-10-21 20:42:21 +02:00
|
|
|
_installedExtensionsFlow.value = mutInstalledExtensions
|
2018-02-05 22:50:56 +01:00
|
|
|
}
|
2020-03-21 03:53:24 +01:00
|
|
|
updatePendingUpdatesCount()
|
2018-02-05 22:50:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2023-08-03 00:00:06 +02:00
|
|
|
* Returns a flow of the installation process for the given extension. It will complete
|
2018-02-05 22:50:56 +01:00
|
|
|
* once the extension is installed or throws an error. The process will be canceled if
|
|
|
|
|
* unsubscribed before its completion.
|
|
|
|
|
*
|
|
|
|
|
* @param extension The extension to be installed.
|
|
|
|
|
*/
|
2023-05-30 16:25:20 +02:00
|
|
|
fun installExtension(extension: Extension.Available): Flow<InstallStep> {
|
2018-02-05 22:50:56 +01:00
|
|
|
return installer.downloadAndInstall(api.getApkUrl(extension), extension)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2023-08-03 00:00:06 +02:00
|
|
|
* Returns a flow of the installation process for the given extension. It will complete
|
2018-02-05 22:50:56 +01:00
|
|
|
* once the extension is updated or throws an error. The process will be canceled if
|
|
|
|
|
* unsubscribed before its completion.
|
|
|
|
|
*
|
|
|
|
|
* @param extension The extension to be updated.
|
|
|
|
|
*/
|
2023-05-30 16:25:20 +02:00
|
|
|
fun updateExtension(extension: Extension.Installed): Flow<InstallStep> {
|
2022-10-21 20:42:21 +02:00
|
|
|
val availableExt = _availableExtensionsFlow.value.find { it.pkgName == extension.pkgName }
|
2023-05-30 16:25:20 +02:00
|
|
|
?: return emptyFlow()
|
2018-02-05 22:50:56 +01:00
|
|
|
return installExtension(availableExt)
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-25 20:31:52 +02:00
|
|
|
fun cancelInstallUpdateExtension(extension: Extension) {
|
|
|
|
|
installer.cancelInstall(extension.pkgName)
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-02 18:10:10 +01:00
|
|
|
/**
|
2021-09-25 20:31:52 +02:00
|
|
|
* Sets to "installing" status of an extension installation.
|
2018-03-02 18:10:10 +01:00
|
|
|
*
|
|
|
|
|
* @param downloadId The id of the download.
|
|
|
|
|
*/
|
2021-09-25 20:31:52 +02:00
|
|
|
fun setInstalling(downloadId: Long) {
|
|
|
|
|
installer.updateInstallStep(downloadId, InstallStep.Installing)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun updateInstallStep(downloadId: Long, step: InstallStep) {
|
|
|
|
|
installer.updateInstallStep(downloadId, step)
|
2018-03-02 18:10:10 +01:00
|
|
|
}
|
|
|
|
|
|
2018-02-05 22:50:56 +01:00
|
|
|
/**
|
|
|
|
|
* Uninstalls the extension that matches the given package name.
|
|
|
|
|
*
|
2023-07-30 16:08:51 +02:00
|
|
|
* @param extension The extension to uninstall.
|
2018-02-05 22:50:56 +01:00
|
|
|
*/
|
2023-07-30 16:08:51 +02:00
|
|
|
fun uninstallExtension(extension: Extension) {
|
|
|
|
|
installer.uninstallApk(extension.pkgName)
|
2018-02-05 22:50:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2024-01-07 19:35:44 +01:00
|
|
|
* Adds the given extension to the list of trusted extensions. It also loads in background the
|
|
|
|
|
* now trusted extensions.
|
2018-02-05 22:50:56 +01:00
|
|
|
*
|
2024-01-07 19:35:44 +01:00
|
|
|
* @param extension the extension to trust
|
2018-02-05 22:50:56 +01:00
|
|
|
*/
|
2024-01-07 19:35:44 +01:00
|
|
|
fun trust(extension: Extension.Untrusted) {
|
|
|
|
|
val untrustedPkgNames = _untrustedExtensionsFlow.value.map { it.pkgName }.toSet()
|
|
|
|
|
if (extension.pkgName !in untrustedPkgNames) return
|
2018-02-05 22:50:56 +01:00
|
|
|
|
2024-01-07 19:35:44 +01:00
|
|
|
trustExtension.trust(extension.pkgName, extension.versionCode, extension.signatureHash)
|
2018-02-05 22:50:56 +01:00
|
|
|
|
2024-01-07 19:35:44 +01:00
|
|
|
val nowTrustedExtensions = _untrustedExtensionsFlow.value
|
|
|
|
|
.filter { it.pkgName == extension.pkgName && it.versionCode == extension.versionCode }
|
2022-10-21 20:42:21 +02:00
|
|
|
_untrustedExtensionsFlow.value -= nowTrustedExtensions
|
2018-02-05 22:50:56 +01:00
|
|
|
|
|
|
|
|
launchNow {
|
|
|
|
|
nowTrustedExtensions
|
2020-04-25 20:24:45 +02:00
|
|
|
.map { extension ->
|
2023-07-30 16:08:51 +02:00
|
|
|
async { ExtensionLoader.loadExtensionFromPkgName(context, extension.pkgName) }.await()
|
2020-04-25 20:24:45 +02:00
|
|
|
}
|
2023-07-30 16:08:51 +02:00
|
|
|
.filterIsInstance<LoadResult.Success>()
|
|
|
|
|
.forEach { registerNewExtension(it.extension) }
|
2018-02-05 22:50:56 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Registers the given extension in this and the source managers.
|
|
|
|
|
*
|
|
|
|
|
* @param extension The extension to be registered.
|
|
|
|
|
*/
|
|
|
|
|
private fun registerNewExtension(extension: Extension.Installed) {
|
2020-06-20 02:50:16 +02:00
|
|
|
// SY -->
|
2020-04-04 20:20:02 +02:00
|
|
|
if (extension.isBlacklisted()) {
|
2021-03-07 06:23:23 +01:00
|
|
|
xLogD("Removing blacklisted extension: (name: String, pkgName: %s)!", extension.name, extension.pkgName)
|
2019-04-14 18:48:59 +02:00
|
|
|
return
|
|
|
|
|
}
|
2020-06-20 02:50:16 +02:00
|
|
|
// SY <--
|
2019-04-14 18:48:59 +02:00
|
|
|
|
2022-10-21 20:42:21 +02:00
|
|
|
_installedExtensionsFlow.value += extension
|
2018-02-05 22:50:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Registers the given updated extension in this and the source managers previously removing
|
|
|
|
|
* the outdated ones.
|
|
|
|
|
*
|
|
|
|
|
* @param extension The extension to be registered.
|
|
|
|
|
*/
|
|
|
|
|
private fun registerUpdatedExtension(extension: Extension.Installed) {
|
2020-06-20 02:50:16 +02:00
|
|
|
// SY -->
|
2020-04-04 20:20:02 +02:00
|
|
|
if (extension.isBlacklisted()) {
|
2021-03-07 06:23:23 +01:00
|
|
|
xLogD("Removing blacklisted extension: (name: %s, pkgName: %s)!", extension.name, extension.pkgName)
|
2019-04-14 18:48:59 +02:00
|
|
|
return
|
|
|
|
|
}
|
2020-06-20 02:50:16 +02:00
|
|
|
// SY <--
|
2019-04-14 18:48:59 +02:00
|
|
|
|
2022-10-21 20:42:21 +02:00
|
|
|
val mutInstalledExtensions = _installedExtensionsFlow.value.toMutableList()
|
2018-02-05 22:50:56 +01:00
|
|
|
val oldExtension = mutInstalledExtensions.find { it.pkgName == extension.pkgName }
|
|
|
|
|
if (oldExtension != null) {
|
|
|
|
|
mutInstalledExtensions -= oldExtension
|
|
|
|
|
}
|
|
|
|
|
mutInstalledExtensions += extension
|
2022-10-21 20:42:21 +02:00
|
|
|
_installedExtensionsFlow.value = mutInstalledExtensions
|
2018-02-05 22:50:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Unregisters the extension in this and the source managers given its package name. Note this
|
|
|
|
|
* method is called for every uninstalled application in the system.
|
|
|
|
|
*
|
|
|
|
|
* @param pkgName The package name of the uninstalled application.
|
|
|
|
|
*/
|
|
|
|
|
private fun unregisterExtension(pkgName: String) {
|
2022-10-21 20:42:21 +02:00
|
|
|
val installedExtension = _installedExtensionsFlow.value.find { it.pkgName == pkgName }
|
2018-02-05 22:50:56 +01:00
|
|
|
if (installedExtension != null) {
|
2022-10-21 20:42:21 +02:00
|
|
|
_installedExtensionsFlow.value -= installedExtension
|
2018-02-05 22:50:56 +01:00
|
|
|
}
|
2022-10-21 20:42:21 +02:00
|
|
|
val untrustedExtension = _untrustedExtensionsFlow.value.find { it.pkgName == pkgName }
|
2018-02-05 22:50:56 +01:00
|
|
|
if (untrustedExtension != null) {
|
2022-10-21 20:42:21 +02:00
|
|
|
_untrustedExtensionsFlow.value -= untrustedExtension
|
2018-02-05 22:50:56 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Listener which receives events of the extensions being installed, updated or removed.
|
|
|
|
|
*/
|
|
|
|
|
private inner class InstallationListener : ExtensionInstallReceiver.Listener {
|
|
|
|
|
|
|
|
|
|
override fun onExtensionInstalled(extension: Extension.Installed) {
|
|
|
|
|
registerNewExtension(extension.withUpdateCheck())
|
2020-03-21 03:53:24 +01:00
|
|
|
updatePendingUpdatesCount()
|
2018-02-05 22:50:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onExtensionUpdated(extension: Extension.Installed) {
|
|
|
|
|
registerUpdatedExtension(extension.withUpdateCheck())
|
2020-03-21 03:53:24 +01:00
|
|
|
updatePendingUpdatesCount()
|
2018-02-05 22:50:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onExtensionUntrusted(extension: Extension.Untrusted) {
|
2022-10-21 20:42:21 +02:00
|
|
|
_untrustedExtensionsFlow.value += extension
|
2018-02-05 22:50:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onPackageUninstalled(pkgName: String) {
|
2023-08-05 18:15:52 +02:00
|
|
|
ExtensionLoader.uninstallPrivateExtension(context, pkgName)
|
2018-02-05 22:50:56 +01:00
|
|
|
unregisterExtension(pkgName)
|
2020-03-21 03:53:24 +01:00
|
|
|
updatePendingUpdatesCount()
|
2018-02-05 22:50:56 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Extension method to set the update field of an installed extension.
|
|
|
|
|
*/
|
|
|
|
|
private fun Extension.Installed.withUpdateCheck(): Extension.Installed {
|
2022-10-28 12:37:59 +02:00
|
|
|
return if (updateExists()) {
|
|
|
|
|
copy(hasUpdate = true)
|
|
|
|
|
} else {
|
|
|
|
|
this
|
2018-02-05 22:50:56 +01:00
|
|
|
}
|
2022-10-28 12:37:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun Extension.Installed.updateExists(availableExtension: Extension.Available? = null): Boolean {
|
|
|
|
|
val availableExt = availableExtension ?: _availableExtensionsFlow.value.find { it.pkgName == pkgName }
|
2024-01-08 04:22:32 +01:00
|
|
|
?: return false
|
2022-10-28 12:37:59 +02:00
|
|
|
|
|
|
|
|
return (availableExt.versionCode > versionCode || availableExt.libVersion > libVersion)
|
2018-02-05 22:50:56 +01:00
|
|
|
}
|
|
|
|
|
|
2020-03-21 03:53:24 +01:00
|
|
|
private fun updatePendingUpdatesCount() {
|
2023-08-03 00:00:06 +02:00
|
|
|
val pendingUpdateCount = _installedExtensionsFlow.value.count { it.hasUpdate }
|
|
|
|
|
preferences.extensionUpdatesCount().set(pendingUpdateCount)
|
|
|
|
|
if (pendingUpdateCount == 0) {
|
|
|
|
|
ExtensionUpdateNotifier(context).dismiss()
|
|
|
|
|
}
|
2020-03-21 03:53:24 +01:00
|
|
|
}
|
2018-02-05 22:50:56 +01:00
|
|
|
}
|