Skip to content

Commit

Permalink
Add option to enable incognito mode per extension (mihonapp/mihon#157)
Browse files Browse the repository at this point in the history
* add per Extension Incognito Mode

* migrate incognito sources when extension is updated

* remove incognito sources when extension is uninstalled

* remove not used variable

* address change requests

address change requests

* Rebase and cleanup code

---------

Co-authored-by: AntsyLich <[email protected]>
(cherry picked from commit c283abefb03f79ce6652492db71cde410f828f78)
  • Loading branch information
sdaqo authored and cuong-tran committed Jan 13, 2025
1 parent 37ac075 commit 19a62fc
Show file tree
Hide file tree
Showing 14 changed files with 146 additions and 20 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co
## [Unreleased]
### Added
- Add option to always decode long strip images with SSIV
- Added option to enable incognito per extension ([@sdaqo](https://github.com/sdaqo), [@AntsyLich](https://github.com/AntsyLich)) ([#157](https://github.com/mihonapp/mihon/pull/157))

## [v0.17.1] - 2024-12-06
### Changed
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/java/eu/kanade/domain/DomainModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import eu.kanade.domain.manga.interactor.SetExcludedScanlators
import eu.kanade.domain.manga.interactor.SetMangaViewerFlags
import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.source.interactor.GetEnabledSources
import eu.kanade.domain.source.interactor.GetIncognitoState
import eu.kanade.domain.source.interactor.GetLanguagesWithSources
import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount
import eu.kanade.domain.source.interactor.SetMigrateSorting
import eu.kanade.domain.source.interactor.ToggleIncognito
import eu.kanade.domain.source.interactor.ToggleLanguage
import eu.kanade.domain.source.interactor.ToggleSource
import eu.kanade.domain.source.interactor.ToggleSourcePin
Expand Down Expand Up @@ -195,5 +197,7 @@ class DomainModule : InjektModule {
addFactory { DeleteExtensionRepo(get()) }
addFactory { ReplaceExtensionRepo(get()) }
addFactory { UpdateExtensionRepo(get(), get()) }
addFactory { ToggleIncognito(get()) }
addFactory { GetIncognitoState(get(), get(), get()) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package eu.kanade.domain.source.interactor

import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.extension.ExtensionManager
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged

class GetIncognitoState(
private val basePreferences: BasePreferences,
private val sourcePreferences: SourcePreferences,
private val extensionManager: ExtensionManager,
) {
fun await(sourceId: Long?): Boolean {
if (basePreferences.incognitoMode().get()) return true
if (sourceId == null) return false
val extensionPackage = extensionManager.getExtensionPackage(sourceId) ?: return false

return extensionPackage in sourcePreferences.incognitoExtensions().get()
}

fun subscribe(sourceId: Long?): Flow<Boolean> {
if (sourceId == null) return basePreferences.incognitoMode().changes()

return combine(
basePreferences.incognitoMode().changes(),
sourcePreferences.incognitoExtensions().changes(),
extensionManager.getExtensionPackageAsFlow(sourceId),
) { incognito, incognitoExtensions, extensionPackage ->
incognito || (extensionPackage in incognitoExtensions)
}
.distinctUntilChanged()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package eu.kanade.domain.source.interactor

import eu.kanade.domain.source.service.SourcePreferences
import tachiyomi.core.common.preference.getAndSet

class ToggleIncognito(
private val preferences: SourcePreferences,
) {
fun await(extensions: String, enable: Boolean) {
preferences.incognitoExtensions().getAndSet {
if (enable) it.plus(extensions) else it.minus(extensions)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class SourcePreferences(

fun disabledSources() = preferenceStore.getStringSet("hidden_catalogues", emptySet())

fun incognitoExtensions() = preferenceStore.getStringSet("incognito_extensions", emptySet())

fun pinnedSources() = preferenceStore.getStringSet("pinned_catalogues", emptySet())

fun lastUsedSource() = preferenceStore.getLong(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
Expand All @@ -49,6 +51,7 @@ import eu.kanade.presentation.components.AppBarActions
import eu.kanade.presentation.components.WarningBanner
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
import eu.kanade.presentation.more.settings.widget.TrailingWidgetBuffer
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.ui.browse.extension.details.ExtensionDetailsScreenModel
Expand Down Expand Up @@ -77,6 +80,7 @@ fun ExtensionDetailsScreen(
onClickClearCookies: () -> Unit,
onClickUninstall: () -> Unit,
onClickSource: (sourceId: Long) -> Unit,
onClickIncognito: (Boolean) -> Unit,
) {
val uriHandler = LocalUriHandler.current
val url = remember(state.extension) {
Expand Down Expand Up @@ -154,9 +158,11 @@ fun ExtensionDetailsScreen(
contentPadding = paddingValues,
extension = state.extension,
sources = state.sources,
incognitoMode = state.isIncognito,
onClickSourcePreferences = onClickSourcePreferences,
onClickUninstall = onClickUninstall,
onClickSource = onClickSource,
onClickIncognito = onClickIncognito,
)
}
}
Expand All @@ -166,9 +172,11 @@ private fun ExtensionDetails(
contentPadding: PaddingValues,
extension: Extension.Installed,
sources: ImmutableList<ExtensionSourceItem>,
incognitoMode: Boolean,
onClickSourcePreferences: (sourceId: Long) -> Unit,
onClickUninstall: () -> Unit,
onClickSource: (sourceId: Long) -> Unit,
onClickIncognito: (Boolean) -> Unit,
) {
val context = LocalContext.current
var showNsfwWarning by remember { mutableStateOf(false) }
Expand All @@ -192,6 +200,7 @@ private fun ExtensionDetails(
item {
DetailsHeader(
extension = extension,
extIncognitoMode = incognitoMode,
onClickUninstall = onClickUninstall,
onClickAppInfo = {
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
Expand All @@ -203,6 +212,7 @@ private fun ExtensionDetails(
onClickAgeRating = {
showNsfwWarning = true
},
onExtIncognitoChange = onClickIncognito,
)
}

Expand Down Expand Up @@ -230,19 +240,20 @@ private fun ExtensionDetails(
@Composable
private fun DetailsHeader(
extension: Extension,
extIncognitoMode: Boolean,
onClickAgeRating: () -> Unit,
onClickUninstall: () -> Unit,
onClickAppInfo: (() -> Unit)?,
onExtIncognitoChange: (Boolean) -> Unit,
) {
val context = LocalContext.current

Column {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = MaterialTheme.padding.medium)
.padding(
start = MaterialTheme.padding.medium,
end = MaterialTheme.padding.medium,
top = MaterialTheme.padding.medium,
bottom = MaterialTheme.padding.small,
)
Expand Down Expand Up @@ -334,12 +345,9 @@ private fun DetailsHeader(
}

Row(
modifier = Modifier.padding(
start = MaterialTheme.padding.medium,
end = MaterialTheme.padding.medium,
top = MaterialTheme.padding.small,
bottom = MaterialTheme.padding.medium,
),
modifier = Modifier
.padding(horizontal = MaterialTheme.padding.medium)
.padding(top = MaterialTheme.padding.small),
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.medium),
) {
OutlinedButton(
Expand All @@ -362,6 +370,24 @@ private fun DetailsHeader(
}
}

TextPreferenceWidget(
modifier = Modifier.padding(horizontal = MaterialTheme.padding.small),
title = stringResource(MR.strings.pref_incognito_mode),
subtitle = stringResource(MR.strings.pref_incognito_mode_extension_summary),
icon = ImageVector.vectorResource(R.drawable.ic_glasses_24dp),
widget = {
Row(
verticalAlignment = Alignment.CenterVertically,
) {
Switch(
checked = extIncognitoMode,
onCheckedChange = onExtIncognitoChange,
modifier = Modifier.padding(start = TrailingWidgetBuffer),
)
}
},
)

HorizontalDivider()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,24 @@ class ExtensionManager(

private var subLanguagesEnabledOnFirstRun = preferences.enabledLanguages().isSet()

fun getAppIconForSource(sourceId: Long): Drawable? {
val pkgName = installedExtensionMapFlow.value.values
.find { ext ->
ext.sources.any { it.id == sourceId }
}
fun getExtensionPackage(sourceId: Long): String? {
return installedExtensionsFlow.value.find { extension ->
extension.sources.any { it.id == sourceId }
}
?.pkgName
}

fun getExtensionPackageAsFlow(sourceId: Long): Flow<String?> {
return installedExtensionsFlow.map { extensions ->
extensions.find { extension ->
extension.sources.any { it.id == sourceId }
}
?.pkgName
}
}

fun getAppIconForSource(sourceId: Long): Drawable? {
val pkgName = getExtensionPackage(sourceId)
if (pkgName != null) {
return iconMap[pkgName] ?: iconMap.getOrPut(pkgName) {
ExtensionLoader.getExtensionPackageInfoFromPkgName(context, pkgName)!!.applicationInfo!!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ data class ExtensionDetailsScreen(
onClickClearCookies = screenModel::clearCookies,
onClickUninstall = screenModel::uninstallExtension,
onClickSource = screenModel::toggleSource,
onClickIncognito = screenModel::toggleIncognito,
)

LaunchedEffect(Unit) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
import eu.kanade.domain.extension.interactor.ExtensionSourceItem
import eu.kanade.domain.extension.interactor.GetExtensionSources
import eu.kanade.domain.source.interactor.ToggleIncognito
import eu.kanade.domain.source.interactor.ToggleSource
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.network.NetworkHelper
Expand All @@ -19,6 +21,7 @@ import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.update
Expand All @@ -36,6 +39,8 @@ class ExtensionDetailsScreenModel(
private val extensionManager: ExtensionManager = Injekt.get(),
private val getExtensionSources: GetExtensionSources = Injekt.get(),
private val toggleSource: ToggleSource = Injekt.get(),
private val toggleIncognito: ToggleIncognito = Injekt.get(),
private val preferences: SourcePreferences = Injekt.get(),
) : StateScreenModel<ExtensionDetailsScreenModel.State>(State()) {

private val _events: Channel<ExtensionDetailsEvent> = Channel()
Expand Down Expand Up @@ -80,6 +85,15 @@ class ExtensionDetailsScreenModel(
}
}
}
launch {
preferences.incognitoExtensions()
.changes()
.map { pkgName in it }
.distinctUntilChanged()
.collectLatest { isIncognito ->
mutableState.update { it.copy(isIncognito = isIncognito) }
}
}
}
}

Expand Down Expand Up @@ -118,9 +132,16 @@ class ExtensionDetailsScreenModel(
?.let { toggleSource.await(it, enable) }
}

fun toggleIncognito(enable: Boolean) {
state.value.extension?.pkgName?.let { packageName ->
toggleIncognito.await(packageName, enable)
}
}

@Immutable
data class State(
val extension: Extension.Installed? = null,
val isIncognito: Boolean = false,
private val _sources: ImmutableList<ExtensionSourceItem>? = null,
) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get

data class BrowseSourceScreen(
private val sourceId: Long,
val sourceId: Long,
private val listingQuery: String?,
// SY -->
private val filtersJson: String? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
import dev.icerock.moko.resources.StringResource
import eu.kanade.core.preference.asState
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.manga.model.toDomainManga
import eu.kanade.domain.source.interactor.GetExhSavedSearch
import eu.kanade.domain.source.interactor.GetIncognitoState
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.domain.track.interactor.AddTracks
import eu.kanade.domain.ui.UiPreferences
Expand Down Expand Up @@ -93,7 +93,6 @@ open class BrowseSourceScreenModel(
// SY <--
private val sourceManager: SourceManager = Injekt.get(),
sourcePreferences: SourcePreferences = Injekt.get(),
basePreferences: BasePreferences = Injekt.get(),
private val libraryPreferences: LibraryPreferences = Injekt.get(),
private val coverCache: CoverCache = Injekt.get(),
private val getRemoteManga: GetRemoteManga = Injekt.get(),
Expand All @@ -105,6 +104,7 @@ open class BrowseSourceScreenModel(
val networkToLocalManga: NetworkToLocalManga = Injekt.get(),
private val updateManga: UpdateManga = Injekt.get(),
private val addTracks: AddTracks = Injekt.get(),
private val getIncognitoState: GetIncognitoState = Injekt.get(),

// SY -->
unsortedPreferences: UnsortedPreferences = Injekt.get(),
Expand Down Expand Up @@ -161,7 +161,7 @@ open class BrowseSourceScreenModel(
}
}.join()

if (!basePreferences.incognitoMode().get()) {
if (!getIncognitoState.await(source.id)) {
sourcePreferences.lastUsedSource().set(source.id)
}

Expand Down
Loading

0 comments on commit 19a62fc

Please sign in to comment.