Option to hide NSFW extensions (closes #1312)

This commit is contained in:
arkon 2020-08-08 16:27:55 -04:00
parent 8bab1d9798
commit abaca6e676
8 changed files with 34 additions and 10 deletions

View File

@ -117,6 +117,8 @@ object PreferenceKeys {
const val automaticExtUpdates = "automatic_ext_updates" const val automaticExtUpdates = "automatic_ext_updates"
const val allowNsfwSources = "allow_nsfw_sources"
const val startScreen = "start_screen" const val startScreen = "start_screen"
const val useBiometricLock = "use_biometric_lock" const val useBiometricLock = "use_biometric_lock"

View File

@ -217,6 +217,8 @@ class PreferencesHelper(val context: Context) {
fun automaticExtUpdates() = flowPrefs.getBoolean(Keys.automaticExtUpdates, true) fun automaticExtUpdates() = flowPrefs.getBoolean(Keys.automaticExtUpdates, true)
fun allowNsfwSources() = prefs.getBoolean(Keys.allowNsfwSources, true)
fun extensionUpdatesCount() = flowPrefs.getInt("ext_updates_count", 0) fun extensionUpdatesCount() = flowPrefs.getInt("ext_updates_count", 0)
fun lastExtCheck() = flowPrefs.getLong("last_ext_check", 0) fun lastExtCheck() = flowPrefs.getLong("last_ext_check", 0)

View File

@ -64,9 +64,10 @@ internal class ExtensionGithubApi {
val versionName = element["version"].string val versionName = element["version"].string
val versionCode = element["code"].int val versionCode = element["code"].int
val lang = element["lang"].string val lang = element["lang"].string
val nsfw = element["nsfw"].int == 1
val icon = "$REPO_URL_PREFIX/icon/${apkName.replace(".apk", ".png")}" val icon = "$REPO_URL_PREFIX/icon/${apkName.replace(".apk", ".png")}"
Extension.Available(name, pkgName, versionName, versionCode, lang, apkName, icon) Extension.Available(name, pkgName, versionName, versionCode, lang, nsfw, apkName, icon)
} }
} }

View File

@ -9,14 +9,16 @@ sealed class Extension {
abstract val versionName: String abstract val versionName: String
abstract val versionCode: Int abstract val versionCode: Int
abstract val lang: String? abstract val lang: String?
abstract val isNsfw: Boolean
data class Installed( data class Installed(
override val name: String, override val name: String,
override val pkgName: String, override val pkgName: String,
override val versionName: String, override val versionName: String,
override val versionCode: Int, override val versionCode: Int,
val sources: List<Source>,
override val lang: String, override val lang: String,
override val isNsfw: Boolean,
val sources: List<Source>,
val hasUpdate: Boolean = false, val hasUpdate: Boolean = false,
val isObsolete: Boolean = false, val isObsolete: Boolean = false,
val isUnofficial: Boolean = false val isUnofficial: Boolean = false
@ -28,6 +30,7 @@ sealed class Extension {
override val versionName: String, override val versionName: String,
override val versionCode: Int, override val versionCode: Int,
override val lang: String, override val lang: String,
override val isNsfw: Boolean,
val apkName: String, val apkName: String,
val iconUrl: String val iconUrl: String
) : Extension() ) : Extension()
@ -38,6 +41,7 @@ sealed class Extension {
override val versionName: String, override val versionName: String,
override val versionCode: Int, override val versionCode: Int,
val signatureHash: String, val signatureHash: String,
override val lang: String? = null override val lang: String? = null,
override val isNsfw: Boolean = false
) : Extension() ) : Extension()
} }

View File

@ -15,8 +15,8 @@ import eu.kanade.tachiyomi.util.lang.Hash
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import timber.log.Timber import timber.log.Timber
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
/** /**
* Class that handles the loading of the extensions installed in the system. * Class that handles the loading of the extensions installed in the system.
@ -24,8 +24,11 @@ import uy.kohesive.injekt.api.get
@SuppressLint("PackageManagerGetSignatures") @SuppressLint("PackageManagerGetSignatures")
internal object ExtensionLoader { internal object ExtensionLoader {
private val preferences: PreferencesHelper by injectLazy()
private const val EXTENSION_FEATURE = "tachiyomi.extension" private const val EXTENSION_FEATURE = "tachiyomi.extension"
private const val METADATA_SOURCE_CLASS = "tachiyomi.extension.class" private const val METADATA_SOURCE_CLASS = "tachiyomi.extension.class"
private const val METADATA_NSFW = "tachiyomi.extension.nsfw"
const val LIB_VERSION_MIN = 1.2 const val LIB_VERSION_MIN = 1.2
const val LIB_VERSION_MAX = 1.2 const val LIB_VERSION_MAX = 1.2
@ -36,8 +39,7 @@ internal object ExtensionLoader {
/** /**
* List of the trusted signatures. * List of the trusted signatures.
*/ */
var trustedSignatures = mutableSetOf<String>() + var trustedSignatures = mutableSetOf<String>() + preferences.trustedSignatures().get() + officialSignature
Injekt.get<PreferencesHelper>().trustedSignatures().get() + officialSignature
/** /**
* Return a list of all the installed extensions initialized concurrently. * Return a list of all the installed extensions initialized concurrently.
@ -125,6 +127,11 @@ internal object ExtensionLoader {
return LoadResult.Untrusted(extension) return LoadResult.Untrusted(extension)
} }
val isNsfw = appInfo.metaData.getInt(METADATA_NSFW) == 1
if (!preferences.allowNsfwSources() && isNsfw) {
return LoadResult.Error("NSFW extension $pkgName not allowed")
}
val classLoader = PathClassLoader(appInfo.sourceDir, null, context.classLoader) val classLoader = PathClassLoader(appInfo.sourceDir, null, context.classLoader)
val sources = appInfo.metaData.getString(METADATA_SOURCE_CLASS)!! val sources = appInfo.metaData.getString(METADATA_SOURCE_CLASS)!!
@ -160,7 +167,7 @@ internal object ExtensionLoader {
} }
val extension = Extension.Installed( val extension = Extension.Installed(
extName, pkgName, versionName, versionCode, sources, lang, extName, pkgName, versionName, versionCode, lang, isNsfw, sources,
isUnofficial = signatureHash != officialSignature isUnofficial = signatureHash != officialSignature
) )
return LoadResult.Success(extension) return LoadResult.Success(extension)

View File

@ -55,20 +55,22 @@ open class ExtensionPresenter(
private fun toItems(tuple: ExtensionTuple): List<ExtensionItem> { private fun toItems(tuple: ExtensionTuple): List<ExtensionItem> {
val context = Injekt.get<Application>() val context = Injekt.get<Application>()
val activeLangs = preferences.enabledLanguages().get() val activeLangs = preferences.enabledLanguages().get()
val allowNsfw = preferences.allowNsfwSources()
val (installed, untrusted, available) = tuple val (installed, untrusted, available) = tuple
val items = mutableListOf<ExtensionItem>() val items = mutableListOf<ExtensionItem>()
val updatesSorted = installed.filter { it.hasUpdate }.sortedBy { it.pkgName } val updatesSorted = installed.filter { it.hasUpdate && (allowNsfw || !it.isNsfw) }.sortedBy { it.pkgName }
val installedSorted = installed.filter { !it.hasUpdate }.sortedWith(compareBy({ !it.isObsolete }, { it.pkgName })) val installedSorted = installed.filter { !it.hasUpdate && (allowNsfw || !it.isNsfw) }.sortedWith(compareBy({ !it.isObsolete }, { it.pkgName }))
val untrustedSorted = untrusted.sortedBy { it.pkgName } val untrustedSorted = untrusted.sortedBy { it.pkgName }
val availableSorted = available val availableSorted = available
// Filter out already installed extensions and disabled languages // Filter out already installed extensions and disabled languages
.filter { avail -> .filter { avail ->
installed.none { it.pkgName == avail.pkgName } && installed.none { it.pkgName == avail.pkgName } &&
untrusted.none { it.pkgName == avail.pkgName } && untrusted.none { it.pkgName == avail.pkgName } &&
(avail.lang in activeLangs || avail.lang == "all") (avail.lang in activeLangs || avail.lang == "all") &&
(allowNsfw || !avail.isNsfw)
} }
.sortedBy { it.pkgName } .sortedBy { it.pkgName }

View File

@ -29,6 +29,11 @@ class SettingsBrowseController : SettingsController() {
true true
} }
} }
switchPreference {
key = Keys.allowNsfwSources
titleRes = R.string.pref_allow_nsfw_sources
defaultValue = true
}
} }
preferenceCategory { preferenceCategory {

View File

@ -330,6 +330,7 @@
<!-- Browse section --> <!-- Browse section -->
<string name="pref_enable_automatic_extension_updates">Check for extension updates</string> <string name="pref_enable_automatic_extension_updates">Check for extension updates</string>
<string name="pref_allow_nsfw_sources">Allow sources with NSFW content</string>
<string name="pref_search_pinned_sources_only">Only include pinned sources</string> <string name="pref_search_pinned_sources_only">Only include pinned sources</string>
<!-- Backup section --> <!-- Backup section -->