Compare commits

...

15 Commits

Author SHA1 Message Date
sdaqo
b79a45619a
Merge 3107b1dd12 into 9580a00aa6 2024-11-05 18:25:07 +01:00
Mend Renovate
9580a00aa6
Update dependency androidx.compose:compose-bom to v2024.10.01 (#1424) 2024-11-05 22:04:10 +06:00
Mend Renovate
cb2b0464d0
Update dependency androidx.core:core-ktx to v1.15.0 (#1417) 2024-11-05 20:51:16 +06:00
Mend Renovate
ef7992f912
Update dependency com.android.tools.build:gradle to v8.7.2 (#1428) 2024-11-05 20:48:47 +06:00
Mend Renovate
261bbef997
Update softprops/action-gh-release action to v2.0.9 (#1425) 2024-11-05 20:48:15 +06:00
Mend Renovate
a5349a881b
Update dependency io.coil-kt.coil3:coil-bom to v3.0.0 (#1444) 2024-11-05 20:47:47 +06:00
Mend Renovate
2ca2cec02b
Update xml.serialization.version to v0.90.3 (#1446) 2024-11-05 20:46:14 +06:00
sdaqo
3107b1dd12
address change requests
address change requests
2024-11-02 00:17:32 +01:00
sdaqo
b47b3bfdd5
Merge branch 'main' into main 2024-10-30 14:02:54 +01:00
sdaqo
fb4feb50c8
remove not used variable 2024-05-05 21:21:06 +02:00
sdaqo
614de13c8d
Merge remote-tracking branch 'upstream/main' 2024-05-05 21:17:29 +02:00
sdaqo
3377e0e585
remove incognito sources when extension is uninstalled 2024-01-21 15:06:39 +01:00
sdaqo
ba0a49ef1b
migrate incognito sources when extension is updated 2024-01-21 15:06:29 +01:00
sdaqo
637d046e60
Merge branch 'mihonapp:main' into main 2024-01-21 13:38:19 +01:00
sdaqo
748ed867c5
add per Extension Incognito Mode 2024-01-20 13:04:52 +01:00
18 changed files with 152 additions and 20 deletions

View File

@ -95,7 +95,7 @@ jobs:
- name: Create Release
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'mihonapp/mihon'
uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8
uses: softprops/action-gh-release@e7a8f85e1c67a31e6ed99a94b41bd0b71bbee6b8 # v2.0.9
with:
tag_name: ${{ env.VERSION_TAG }}
name: Mihon ${{ env.VERSION_TAG }}

View File

@ -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.ToggleIncognitoSource
import eu.kanade.domain.source.interactor.ToggleLanguage
import eu.kanade.domain.source.interactor.ToggleSource
import eu.kanade.domain.source.interactor.ToggleSourcePin
@ -177,9 +179,11 @@ class DomainModule : InjektModule {
addFactory { GetRemoteManga(get()) }
addFactory { GetSourcesWithFavoriteCount(get(), get()) }
addFactory { GetSourcesWithNonLibraryManga(get()) }
addFactory { GetIncognitoState(get(), get()) }
addFactory { SetMigrateSorting(get()) }
addFactory { ToggleLanguage(get()) }
addFactory { ToggleSource(get()) }
addFactory { ToggleIncognitoSource(get()) }
addFactory { ToggleSourcePin(get()) }
addFactory { TrustExtension(get(), get()) }

View File

@ -0,0 +1,15 @@
package eu.kanade.domain.source.interactor
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.source.service.SourcePreferences
class GetIncognitoState(
private val basePreferences: BasePreferences,
private val sourcePreferences: SourcePreferences,
) {
fun isEnabled(sourceId: Long): Boolean {
val globalIncognito = basePreferences.incognitoMode().get()
val sourceIncognito = sourceId.toString() in sourcePreferences.incognitoSources().get()
return globalIncognito || sourceIncognito
}
}

View File

@ -0,0 +1,15 @@
package eu.kanade.domain.source.interactor
import eu.kanade.domain.source.service.SourcePreferences
import tachiyomi.core.common.preference.getAndSet
class ToggleIncognitoSource(
private val preferences: SourcePreferences,
) {
fun await(sourceIds: List<Long>, enable: Boolean) {
val transformedSourceIds = sourceIds.map { it.toString() }
preferences.incognitoSources().getAndSet { incognitoed ->
if (enable) incognitoed.plus(transformedSourceIds) else incognitoed.minus(transformedSourceIds)
}
}
}

View File

@ -8,17 +8,17 @@ class ToggleSource(
private val preferences: SourcePreferences,
) {
fun await(source: Source, enable: Boolean = isEnabled(source.id)) {
await(source.id, enable)
fun awaitDisable(source: Source, enable: Boolean = isEnabled(source.id)) {
awaitDisable(source.id, enable)
}
fun await(sourceId: Long, enable: Boolean = isEnabled(sourceId)) {
fun awaitDisable(sourceId: Long, enable: Boolean = isEnabled(sourceId)) {
preferences.disabledSources().getAndSet { disabled ->
if (enable) disabled.minus("$sourceId") else disabled.plus("$sourceId")
}
}
fun await(sourceIds: List<Long>, enable: Boolean) {
fun awaitDisable(sourceIds: List<Long>, enable: Boolean) {
val transformedSourceIds = sourceIds.map { it.toString() }
preferences.disabledSources().getAndSet { disabled ->
if (enable) disabled.minus(transformedSourceIds) else disabled.plus(transformedSourceIds)

View File

@ -22,6 +22,8 @@ class SourcePreferences(
fun disabledSources() = preferenceStore.getStringSet("hidden_catalogues", emptySet())
fun incognitoSources() = preferenceStore.getStringSet("incognito_catalogues", emptySet())
fun pinnedSources() = preferenceStore.getStringSet("pinned_catalogues", emptySet())
fun lastUsedSource() = preferenceStore.getLong(

View File

@ -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
@ -66,12 +69,14 @@ import tachiyomi.presentation.core.screens.EmptyScreen
fun ExtensionDetailsScreen(
navigateUp: () -> Unit,
state: ExtensionDetailsScreenModel.State,
extIncognitoMode: Boolean,
onClickSourcePreferences: (sourceId: Long) -> Unit,
onClickEnableAll: () -> Unit,
onClickDisableAll: () -> Unit,
onClickClearCookies: () -> Unit,
onClickUninstall: () -> Unit,
onClickSource: (sourceId: Long) -> Unit,
onExtIncognitoChange: (Boolean) -> Unit
) {
val uriHandler = LocalUriHandler.current
val url = remember(state.extension) {
@ -140,9 +145,11 @@ fun ExtensionDetailsScreen(
contentPadding = paddingValues,
extension = state.extension,
sources = state.sources,
extIncognitoMode = extIncognitoMode,
onClickSourcePreferences = onClickSourcePreferences,
onClickUninstall = onClickUninstall,
onClickSource = onClickSource,
onExtIncognitoChange = onExtIncognitoChange,
)
}
}
@ -152,9 +159,11 @@ private fun ExtensionDetails(
contentPadding: PaddingValues,
extension: Extension.Installed,
sources: ImmutableList<ExtensionSourceItem>,
extIncognitoMode: Boolean,
onClickSourcePreferences: (sourceId: Long) -> Unit,
onClickUninstall: () -> Unit,
onClickSource: (sourceId: Long) -> Unit,
onExtIncognitoChange: (Boolean) -> Unit
) {
val context = LocalContext.current
var showNsfwWarning by remember { mutableStateOf(false) }
@ -171,6 +180,7 @@ private fun ExtensionDetails(
item {
DetailsHeader(
extension = extension,
extIncognitoMode = extIncognitoMode,
onClickUninstall = onClickUninstall,
onClickAppInfo = {
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
@ -182,6 +192,7 @@ private fun ExtensionDetails(
onClickAgeRating = {
showNsfwWarning = true
},
onExtIncognitoChange = onExtIncognitoChange,
)
}
@ -209,9 +220,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
@ -317,7 +330,7 @@ private fun DetailsHeader(
start = MaterialTheme.padding.medium,
end = MaterialTheme.padding.medium,
top = MaterialTheme.padding.small,
bottom = MaterialTheme.padding.medium,
bottom = MaterialTheme.padding.small,
),
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.medium),
) {
@ -341,6 +354,35 @@ private fun DetailsHeader(
}
}
Row(
modifier = Modifier.padding(
start = MaterialTheme.padding.small,
end = MaterialTheme.padding.small,
top = MaterialTheme.padding.extraSmall,
bottom = MaterialTheme.padding.medium,
),
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.medium),
) {
TextPreferenceWidget(
modifier = Modifier,
title = stringResource(MR.strings.pref_incognito_mode),
subtitle = stringResource(MR.strings.pref_incognito_mode_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()
}
}

View File

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.extension
import android.content.Context
import android.graphics.drawable.Drawable
import eu.kanade.domain.extension.interactor.TrustExtension
import eu.kanade.domain.source.interactor.ToggleIncognitoSource
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.extension.api.ExtensionApi
import eu.kanade.tachiyomi.extension.api.ExtensionUpdateNotifier
@ -42,6 +43,8 @@ import java.util.Locale
class ExtensionManager(
private val context: Context,
private val preferences: SourcePreferences = Injekt.get(),
private val sourcePreferences: SourcePreferences = Injekt.get(),
private val toggleIncognitoSource: ToggleIncognitoSource = Injekt.get(),
private val trustExtension: TrustExtension = Injekt.get(),
) {
@ -292,6 +295,19 @@ class ExtensionManager(
* @param extension The extension to be registered.
*/
private fun registerUpdatedExtension(extension: Extension.Installed) {
val oldExtension = installedExtensionMapFlow.value[extension.pkgName]
if (oldExtension != null) {
// If a extension has incognito mode enabled we need to consider that sources change and update them
if (oldExtension.sources.first().id.toString() in sourcePreferences.incognitoSources().get()) {
oldExtension.sources
.map { it.id }
.let { toggleIncognitoSource.await(it, false) }
extension.sources
.map { it.id }
.let { toggleIncognitoSource.await(it, true) }
}
}
installedExtensionMapFlow.value += extension
}
@ -302,6 +318,11 @@ class ExtensionManager(
* @param pkgName The package name of the uninstalled application.
*/
private fun unregisterExtension(pkgName: String) {
val installedExtension = installedExtensionMapFlow.value[pkgName]
installedExtension?.sources?.map { it.id }?.let {
toggleIncognitoSource.await(it, false)
}
installedExtensionMapFlow.value -= pkgName
untrustedExtensionMapFlow.value -= pkgName
}

View File

@ -4,6 +4,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator
@ -22,6 +25,8 @@ data class ExtensionDetailsScreen(
val context = LocalContext.current
val screenModel = rememberScreenModel { ExtensionDetailsScreenModel(pkgName = pkgName, context = context) }
val state by screenModel.state.collectAsState()
var isIncognitoMode by remember { mutableStateOf(screenModel.isIncognito()) }
if (state.isLoading) {
LoadingScreen()
@ -33,12 +38,17 @@ data class ExtensionDetailsScreen(
ExtensionDetailsScreen(
navigateUp = navigator::pop,
state = state,
extIncognitoMode = isIncognitoMode,
onClickSourcePreferences = { navigator.push(SourcePreferencesScreen(it)) },
onClickEnableAll = { screenModel.toggleSources(true) },
onClickDisableAll = { screenModel.toggleSources(false) },
onClickClearCookies = screenModel::clearCookies,
onClickUninstall = screenModel::uninstallExtension,
onClickSource = screenModel::toggleSource,
onExtIncognitoChange = { newState ->
screenModel.toggleIncognito(newState)
isIncognitoMode = screenModel.isIncognito()
}
)
LaunchedEffect(Unit) {

View File

@ -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.ToggleIncognitoSource
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
@ -36,6 +38,8 @@ class ExtensionDetailsScreenModel(
private val extensionManager: ExtensionManager = Injekt.get(),
private val getExtensionSources: GetExtensionSources = Injekt.get(),
private val toggleSource: ToggleSource = Injekt.get(),
private val toggleIncognitoSource: ToggleIncognitoSource = Injekt.get(),
private val preferences: SourcePreferences = Injekt.get(),
) : StateScreenModel<ExtensionDetailsScreenModel.State>(State()) {
private val _events: Channel<ExtensionDetailsEvent> = Channel()
@ -109,13 +113,25 @@ class ExtensionDetailsScreenModel(
}
fun toggleSource(sourceId: Long) {
toggleSource.await(sourceId)
toggleSource.awaitDisable(sourceId)
}
fun toggleSources(enable: Boolean) {
state.value.extension?.sources
?.map { it.id }
?.let { toggleSource.await(it, enable) }
?.let { toggleSource.awaitDisable(it, enable) }
}
fun toggleIncognito(enable: Boolean) {
state.value.extension?.sources
?.map { it.id }
?.let { toggleIncognitoSource.await(it, enable) }
}
fun isIncognito(): Boolean {
return state.value.extension?.sources
?.first()
?.id.toString() in preferences.incognitoSources().get()
}
@Immutable

View File

@ -51,7 +51,7 @@ class SourcesFilterScreenModel(
}
fun toggleSource(source: Source) {
toggleSource.await(source)
toggleSource.awaitDisable(source)
}
fun toggleLanguage(language: String) {

View File

@ -83,7 +83,7 @@ class SourcesScreenModel(
}
fun toggleSource(source: Source) {
toggleSource.await(source)
toggleSource.awaitDisable(source)
}
fun togglePin(source: Source) {

View File

@ -14,9 +14,9 @@ import androidx.paging.map
import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
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.GetIncognitoState
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.domain.track.interactor.AddTracks
import eu.kanade.presentation.util.ioCoroutineScope
@ -59,7 +59,6 @@ class BrowseSourceScreenModel(
listingQuery: String?,
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(),
@ -71,7 +70,8 @@ class BrowseSourceScreenModel(
private val networkToLocalManga: NetworkToLocalManga = Injekt.get(),
private val updateManga: UpdateManga = Injekt.get(),
private val addTracks: AddTracks = Injekt.get(),
) : StateScreenModel<BrowseSourceScreenModel.State>(State(Listing.valueOf(listingQuery))) {
private val getIncognitoState: GetIncognitoState = Injekt.get(),
) : StateScreenModel<BrowseSourceScreenModel.State>(State(Listing.valueOf(listingQuery))) {
var displayMode by sourcePreferences.sourceDisplayMode().asState(screenModelScope)
@ -96,7 +96,7 @@ class BrowseSourceScreenModel(
}
}
if (!basePreferences.incognitoMode().get()) {
if (!getIncognitoState.isEnabled(source.id)) {
sourcePreferences.lastUsedSource().set(source.id)
}
}

View File

@ -12,6 +12,8 @@ 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.source.service.SourcePreferences
import eu.kanade.domain.track.interactor.TrackChapter
import eu.kanade.domain.track.service.TrackPreferences
import eu.kanade.tachiyomi.data.database.models.toDomainChapter
@ -91,6 +93,7 @@ class ReaderViewModel @JvmOverloads constructor(
val readerPreferences: ReaderPreferences = Injekt.get(),
private val basePreferences: BasePreferences = Injekt.get(),
private val downloadPreferences: DownloadPreferences = Injekt.get(),
private val sourcePreferences: SourcePreferences = Injekt.get(),
private val trackPreferences: TrackPreferences = Injekt.get(),
private val trackChapter: TrackChapter = Injekt.get(),
private val getManga: GetManga = Injekt.get(),
@ -99,6 +102,7 @@ class ReaderViewModel @JvmOverloads constructor(
private val upsertHistory: UpsertHistory = Injekt.get(),
private val updateChapter: UpdateChapter = Injekt.get(),
private val setMangaViewerFlags: SetMangaViewerFlags = Injekt.get(),
private val getIncognitoState: GetIncognitoState = Injekt.get()
) : ViewModel() {
private val mutableState = MutableStateFlow(State())
@ -216,7 +220,10 @@ class ReaderViewModel @JvmOverloads constructor(
.map(::ReaderChapter)
}
private val incognitoMode = preferences.incognitoMode().get()
// Use lazy here as manga can be null
private val incognitoMode: Boolean by lazy {
getIncognitoState.isEnabled(manga!!.source)
}
private val downloadAheadAmount = downloadPreferences.autoDownloadWhileReading().get()
init {

View File

@ -1,5 +1,5 @@
[versions]
agp_version = "8.7.1"
agp_version = "8.7.2"
lifecycle_version = "2.8.7"
paging_version = "3.3.2"
interpolator_version = "1.0.0"
@ -11,7 +11,7 @@ annotation = "androidx.annotation:annotation:1.9.1"
appcompat = "androidx.appcompat:appcompat:1.7.0"
biometricktx = "androidx.biometric:biometric-ktx:1.2.0-alpha05"
constraintlayout = "androidx.constraintlayout:constraintlayout:2.2.0"
corektx = "androidx.core:core-ktx:1.13.1"
corektx = "androidx.core:core-ktx:1.15.0"
splashscreen = "androidx.core:core-splashscreen:1.0.1"
recyclerview = "androidx.recyclerview:recyclerview:1.3.2"
viewpager = "androidx.viewpager:viewpager:1.1.0-beta01"

View File

@ -1,5 +1,5 @@
[versions]
compose-bom = "2024.10.00"
compose-bom = "2024.10.01"
[libraries]
activity = "androidx.activity:activity-compose:1.9.3"

View File

@ -1,7 +1,7 @@
[versions]
kotlin_version = "2.0.21"
serialization_version = "1.7.3"
xml_serialization_version = "0.90.2"
xml_serialization_version = "0.90.3"
[libraries]
reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin_version" }

View File

@ -42,7 +42,7 @@ preferencektx = "androidx.preference:preference-ktx:1.2.1"
injekt = "com.github.mihonapp:injekt:91edab2317"
coil-bom = { module = "io.coil-kt.coil3:coil-bom", version = "3.0.0-rc02" }
coil-bom = { module = "io.coil-kt.coil3:coil-bom", version = "3.0.0" }
coil-core = { module = "io.coil-kt.coil3:coil" }
coil-gif = { module = "io.coil-kt.coil3:coil-gif" }
coil-compose = { module = "io.coil-kt.coil3:coil-compose" }