diff --git a/app/src/main/java/eu/kanade/domain/DomainModule.kt b/app/src/main/java/eu/kanade/domain/DomainModule.kt index d41e7b899..5b81e03a3 100644 --- a/app/src/main/java/eu/kanade/domain/DomainModule.kt +++ b/app/src/main/java/eu/kanade/domain/DomainModule.kt @@ -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 @@ -190,5 +192,7 @@ class DomainModule : InjektModule { addFactory { DeleteExtensionRepo(get()) } addFactory { ReplaceExtensionRepo(get()) } addFactory { UpdateExtensionRepo(get(), get()) } + addFactory { ToggleIncognito(get()) } + addFactory { GetIncognitoState(get(), get(), get()) } } } diff --git a/app/src/main/java/eu/kanade/domain/source/interactor/GetIncognitoState.kt b/app/src/main/java/eu/kanade/domain/source/interactor/GetIncognitoState.kt new file mode 100644 index 000000000..41ab65f30 --- /dev/null +++ b/app/src/main/java/eu/kanade/domain/source/interactor/GetIncognitoState.kt @@ -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 { + 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() + } +} diff --git a/app/src/main/java/eu/kanade/domain/source/interactor/ToggleIncognito.kt b/app/src/main/java/eu/kanade/domain/source/interactor/ToggleIncognito.kt new file mode 100644 index 000000000..ccd13bf6a --- /dev/null +++ b/app/src/main/java/eu/kanade/domain/source/interactor/ToggleIncognito.kt @@ -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) + } + } +} diff --git a/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt b/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt index 79203b5fd..4e0be66cf 100644 --- a/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt +++ b/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt @@ -22,6 +22,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( diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt index eac62ee65..f23b5222a 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt @@ -35,8 +35,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 @@ -48,6 +50,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 @@ -73,6 +76,7 @@ fun ExtensionDetailsScreen( onClickClearCookies: () -> Unit, onClickUninstall: () -> Unit, onClickSource: (sourceId: Long) -> Unit, + onClickIncognito: (Boolean) -> Unit, ) { val uriHandler = LocalUriHandler.current val url = remember(state.extension) { @@ -141,9 +145,11 @@ fun ExtensionDetailsScreen( contentPadding = paddingValues, extension = state.extension, sources = state.sources, + incognitoMode = state.isIncognito, onClickSourcePreferences = onClickSourcePreferences, onClickUninstall = onClickUninstall, onClickSource = onClickSource, + onClickIncognito = onClickIncognito, ) } } @@ -153,9 +159,11 @@ private fun ExtensionDetails( contentPadding: PaddingValues, extension: Extension.Installed, sources: ImmutableList, + 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) } @@ -179,6 +187,7 @@ private fun ExtensionDetails( item { DetailsHeader( extension = extension, + extIncognitoMode = incognitoMode, onClickUninstall = onClickUninstall, onClickAppInfo = { Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { @@ -190,6 +199,7 @@ private fun ExtensionDetails( onClickAgeRating = { showNsfwWarning = true }, + onExtIncognitoChange = onClickIncognito, ) } @@ -217,9 +227,11 @@ private fun ExtensionDetails( @Composable private fun DetailsHeader( extension: Extension, + extIncognitoMode: Boolean, onClickAgeRating: () -> Unit, onClickUninstall: () -> Unit, onClickAppInfo: (() -> Unit)?, + onExtIncognitoChange: (Boolean) -> Unit, ) { val context = LocalContext.current @@ -227,9 +239,8 @@ private fun DetailsHeader( 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, ) @@ -321,12 +332,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( @@ -349,6 +357,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() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt index 6698c7425..2c32883b2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt @@ -89,12 +89,25 @@ 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 { + return installedExtensionsFlow.map { extensions -> + extensions.find { extension -> + extension.sources.any { it.id == sourceId } + } + ?.pkgName + } + } + + fun getAppIconForSource(sourceId: Long): Drawable? { + val pkgName = getExtensionPackage(sourceId) ?: return null + if (pkgName != null) { return iconMap[pkgName] ?: iconMap.getOrPut(pkgName) { ExtensionLoader.getExtensionPackageInfoFromPkgName(context, pkgName)!!.applicationInfo!! diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreen.kt index 4a4a78cde..8cac9c5cc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreen.kt @@ -39,6 +39,7 @@ data class ExtensionDetailsScreen( onClickClearCookies = screenModel::clearCookies, onClickUninstall = screenModel::uninstallExtension, onClickSource = screenModel::toggleSource, + onClickIncognito = screenModel::toggleIncognito, ) LaunchedEffect(Unit) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreenModel.kt index 4266c4730..7997f7262 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreenModel.kt @@ -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 @@ -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 @@ -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(State()) { private val _events: Channel = Channel() @@ -80,6 +85,15 @@ class ExtensionDetailsScreenModel( } } } + launch { + preferences.incognitoExtensions() + .changes() + .map { pkgName in it } + .distinctUntilChanged() + .collectLatest { isIncognito -> + mutableState.update { it.copy(isIncognito = isIncognito) } + } + } } } @@ -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? = null, ) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt index a7e1c28a9..523b23a57 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt @@ -75,7 +75,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, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt index 9a0a85c04..624ecfb59 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt @@ -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 @@ -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(), @@ -105,6 +104,7 @@ open class BrowseSourceScreenModel( private 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(), @@ -149,7 +149,7 @@ open class BrowseSourceScreenModel( } } - if (!basePreferences.incognitoMode().get()) { + if (!getIncognitoState.await(source.id)) { sourcePreferences.lastUsedSource().set(source.id) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 543c0c4a2..b119b06e6 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -54,6 +54,7 @@ import cafe.adriel.voyager.navigator.currentOrThrow import com.google.firebase.analytics.ktx.analytics import com.google.firebase.ktx.Firebase import eu.kanade.domain.base.BasePreferences +import eu.kanade.domain.source.interactor.GetIncognitoState import eu.kanade.presentation.components.AppStateBanners import eu.kanade.presentation.components.DownloadedOnlyBannerBackgroundColor import eu.kanade.presentation.components.IncognitoModeBannerBackgroundColor @@ -122,6 +123,8 @@ class MainActivity : BaseActivity() { private val downloadCache: DownloadCache by injectLazy() private val chapterCache: ChapterCache by injectLazy() + private val getIncognitoState: GetIncognitoState by injectLazy() + // To be checked by splash screen. If true then splash screen will be removed. var ready = false @@ -181,7 +184,7 @@ class MainActivity : BaseActivity() { setComposeContent { val context = LocalContext.current - val incognito by preferences.incognitoMode().collectAsState() + var incognito by remember { mutableStateOf(getIncognitoState.await(null)) } val downloadOnly by preferences.downloadedOnly().collectAsState() val indexing by downloadCache.isInitializing.collectAsState() @@ -231,6 +234,11 @@ class MainActivity : BaseActivity() { // SY <-- } } + LaunchedEffect(navigator.lastItem) { + (navigator.lastItem as? BrowseSourceScreen)?.sourceId + .let(getIncognitoState::subscribe) + .collectLatest { incognito = it } + } val scaffoldInsets = WindowInsets.navigationBars.only(WindowInsetsSides.Horizontal) Scaffold( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt index 08c38292c..54ae3b9ea 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt @@ -14,6 +14,7 @@ import eu.kanade.domain.chapter.model.toDbChapter import eu.kanade.domain.manga.interactor.SetMangaViewerFlags import eu.kanade.domain.manga.model.readerOrientation import eu.kanade.domain.manga.model.readingMode +import eu.kanade.domain.source.interactor.GetIncognitoState import eu.kanade.domain.sync.SyncPreferences import eu.kanade.domain.track.interactor.TrackChapter import eu.kanade.domain.track.service.TrackPreferences @@ -115,7 +116,6 @@ class ReaderViewModel @JvmOverloads constructor( private val downloadProvider: DownloadProvider = Injekt.get(), private val tempFileManager: UniFileTempFileManager = Injekt.get(), private val imageSaver: ImageSaver = Injekt.get(), - preferences: BasePreferences = Injekt.get(), val readerPreferences: ReaderPreferences = Injekt.get(), private val basePreferences: BasePreferences = Injekt.get(), private val downloadPreferences: DownloadPreferences = Injekt.get(), @@ -127,8 +127,9 @@ class ReaderViewModel @JvmOverloads constructor( private val upsertHistory: UpsertHistory = Injekt.get(), private val updateChapter: UpdateChapter = Injekt.get(), private val setMangaViewerFlags: SetMangaViewerFlags = Injekt.get(), - private val syncPreferences: SyncPreferences = Injekt.get(), + private val getIncognitoState: GetIncognitoState = Injekt.get(), // SY --> + private val syncPreferences: SyncPreferences = Injekt.get(), private val uiPreferences: UiPreferences = Injekt.get(), private val getFlatMetadataById: GetFlatMetadataById = Injekt.get(), private val getMergedMangaById: GetMergedMangaById = Injekt.get(), @@ -264,7 +265,7 @@ class ReaderViewModel @JvmOverloads constructor( .map(::ReaderChapter) } - private val incognitoMode = preferences.incognitoMode().get() + private val incognitoMode: Boolean by lazy { getIncognitoState.await(manga?.source) } private val downloadAheadAmount = downloadPreferences.autoDownloadWhileReading().get() init { diff --git a/i18n/src/commonMain/moko-resources/base/strings.xml b/i18n/src/commonMain/moko-resources/base/strings.xml index 05b68d21c..7cee52ce7 100755 --- a/i18n/src/commonMain/moko-resources/base/strings.xml +++ b/i18n/src/commonMain/moko-resources/base/strings.xml @@ -637,6 +637,7 @@ Downloaded only Incognito mode Pauses reading history + Pause reading history for extension Disable incognito mode Filters all entries in your library