mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 08:08:55 +01:00 
			
		
		
		
	App state banner tweaks (#8746)
* Move download indexing notification to this banner group * Animate state changes
This commit is contained in:
		@@ -1,28 +1,45 @@
 | 
			
		||||
package eu.kanade.presentation.components
 | 
			
		||||
 | 
			
		||||
import androidx.annotation.StringRes
 | 
			
		||||
import androidx.compose.animation.AnimatedVisibility
 | 
			
		||||
import androidx.compose.animation.expandVertically
 | 
			
		||||
import androidx.compose.animation.shrinkVertically
 | 
			
		||||
import androidx.compose.foundation.background
 | 
			
		||||
import androidx.compose.foundation.layout.Column
 | 
			
		||||
import androidx.compose.foundation.layout.Arrangement
 | 
			
		||||
import androidx.compose.foundation.layout.Row
 | 
			
		||||
import androidx.compose.foundation.layout.Spacer
 | 
			
		||||
import androidx.compose.foundation.layout.WindowInsets
 | 
			
		||||
import androidx.compose.foundation.layout.WindowInsetsSides
 | 
			
		||||
import androidx.compose.foundation.layout.fillMaxWidth
 | 
			
		||||
import androidx.compose.foundation.layout.only
 | 
			
		||||
import androidx.compose.foundation.layout.padding
 | 
			
		||||
import androidx.compose.foundation.layout.systemBars
 | 
			
		||||
import androidx.compose.foundation.layout.requiredSize
 | 
			
		||||
import androidx.compose.foundation.layout.statusBars
 | 
			
		||||
import androidx.compose.foundation.layout.width
 | 
			
		||||
import androidx.compose.foundation.layout.windowInsetsPadding
 | 
			
		||||
import androidx.compose.material3.CircularProgressIndicator
 | 
			
		||||
import androidx.compose.material3.MaterialTheme
 | 
			
		||||
import androidx.compose.material3.Text
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.getValue
 | 
			
		||||
import androidx.compose.runtime.mutableStateOf
 | 
			
		||||
import androidx.compose.runtime.remember
 | 
			
		||||
import androidx.compose.runtime.setValue
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.layout.SubcomposeLayout
 | 
			
		||||
import androidx.compose.ui.platform.LocalDensity
 | 
			
		||||
import androidx.compose.ui.res.stringResource
 | 
			
		||||
import androidx.compose.ui.text.style.TextAlign
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import androidx.compose.ui.util.fastForEach
 | 
			
		||||
import androidx.compose.ui.util.fastMap
 | 
			
		||||
import androidx.compose.ui.util.fastMaxBy
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
 | 
			
		||||
val DownloadedOnlyBannerBackgroundColor
 | 
			
		||||
    @Composable get() = MaterialTheme.colorScheme.tertiary
 | 
			
		||||
val IncognitoModeBannerBackgroundColor
 | 
			
		||||
    @Composable get() = MaterialTheme.colorScheme.primary
 | 
			
		||||
val IndexingBannerBackgroundColor
 | 
			
		||||
    @Composable get() = MaterialTheme.colorScheme.secondary
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun WarningBanner(
 | 
			
		||||
@@ -45,23 +62,64 @@ fun WarningBanner(
 | 
			
		||||
fun AppStateBanners(
 | 
			
		||||
    downloadedOnlyMode: Boolean,
 | 
			
		||||
    incognitoMode: Boolean,
 | 
			
		||||
    indexing: Boolean,
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
) {
 | 
			
		||||
    val insets = WindowInsets.systemBars.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
 | 
			
		||||
    Column(modifier = modifier) {
 | 
			
		||||
        if (downloadedOnlyMode) {
 | 
			
		||||
            DownloadedOnlyModeBanner(
 | 
			
		||||
                modifier = Modifier.windowInsetsPadding(insets),
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        if (incognitoMode) {
 | 
			
		||||
            IncognitoModeBanner(
 | 
			
		||||
                modifier = if (!downloadedOnlyMode) {
 | 
			
		||||
                    Modifier.windowInsetsPadding(insets)
 | 
			
		||||
                } else {
 | 
			
		||||
                    Modifier
 | 
			
		||||
                },
 | 
			
		||||
            )
 | 
			
		||||
    val density = LocalDensity.current
 | 
			
		||||
    val mainInsets = WindowInsets.statusBars
 | 
			
		||||
    val mainInsetsTop = mainInsets.getTop(density)
 | 
			
		||||
    SubcomposeLayout(modifier = modifier) { constraints ->
 | 
			
		||||
        val indexingPlaceable = subcompose(0) {
 | 
			
		||||
            AnimatedVisibility(
 | 
			
		||||
                visible = indexing,
 | 
			
		||||
                enter = expandVertically(),
 | 
			
		||||
                exit = shrinkVertically(),
 | 
			
		||||
            ) {
 | 
			
		||||
                IndexingDownloadBanner(
 | 
			
		||||
                    modifier = Modifier.windowInsetsPadding(mainInsets),
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }.fastMap { it.measure(constraints) }
 | 
			
		||||
        val indexingHeight = indexingPlaceable.fastMaxBy { it.height }?.height ?: 0
 | 
			
		||||
 | 
			
		||||
        val downloadedOnlyPlaceable = subcompose(1) {
 | 
			
		||||
            AnimatedVisibility(
 | 
			
		||||
                visible = downloadedOnlyMode,
 | 
			
		||||
                enter = expandVertically(),
 | 
			
		||||
                exit = shrinkVertically(),
 | 
			
		||||
            ) {
 | 
			
		||||
                val top = (mainInsetsTop - indexingHeight).coerceAtLeast(0)
 | 
			
		||||
                DownloadedOnlyModeBanner(
 | 
			
		||||
                    modifier = Modifier.windowInsetsPadding(WindowInsets(top = top)),
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }.fastMap { it.measure(constraints) }
 | 
			
		||||
        val downloadedOnlyHeight = downloadedOnlyPlaceable.fastMaxBy { it.height }?.height ?: 0
 | 
			
		||||
 | 
			
		||||
        val incognitoPlaceable = subcompose(2) {
 | 
			
		||||
            AnimatedVisibility(
 | 
			
		||||
                visible = incognitoMode,
 | 
			
		||||
                enter = expandVertically(),
 | 
			
		||||
                exit = shrinkVertically(),
 | 
			
		||||
            ) {
 | 
			
		||||
                val top = (mainInsetsTop - indexingHeight - downloadedOnlyHeight).coerceAtLeast(0)
 | 
			
		||||
                IncognitoModeBanner(
 | 
			
		||||
                    modifier = Modifier.windowInsetsPadding(WindowInsets(top = top)),
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }.fastMap { it.measure(constraints) }
 | 
			
		||||
        val incognitoHeight = incognitoPlaceable.fastMaxBy { it.height }?.height ?: 0
 | 
			
		||||
 | 
			
		||||
        layout(constraints.maxWidth, indexingHeight + downloadedOnlyHeight + incognitoHeight) {
 | 
			
		||||
            indexingPlaceable.fastForEach {
 | 
			
		||||
                it.place(0, 0)
 | 
			
		||||
            }
 | 
			
		||||
            downloadedOnlyPlaceable.fastForEach {
 | 
			
		||||
                it.place(0, indexingHeight)
 | 
			
		||||
            }
 | 
			
		||||
            incognitoPlaceable.fastForEach {
 | 
			
		||||
                it.place(0, indexingHeight + downloadedOnlyHeight)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -95,3 +153,35 @@ private fun IncognitoModeBanner(modifier: Modifier = Modifier) {
 | 
			
		||||
        style = MaterialTheme.typography.labelMedium,
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
private fun IndexingDownloadBanner(modifier: Modifier = Modifier) {
 | 
			
		||||
    val density = LocalDensity.current
 | 
			
		||||
    Row(
 | 
			
		||||
        modifier = Modifier
 | 
			
		||||
            .background(color = IndexingBannerBackgroundColor)
 | 
			
		||||
            .fillMaxWidth()
 | 
			
		||||
            .padding(8.dp)
 | 
			
		||||
            .then(modifier),
 | 
			
		||||
        horizontalArrangement = Arrangement.Center,
 | 
			
		||||
    ) {
 | 
			
		||||
        var textHeight by remember { mutableStateOf(0.dp) }
 | 
			
		||||
        CircularProgressIndicator(
 | 
			
		||||
            modifier = Modifier.requiredSize(textHeight),
 | 
			
		||||
            color = MaterialTheme.colorScheme.onSecondary,
 | 
			
		||||
            strokeWidth = textHeight / 8,
 | 
			
		||||
        )
 | 
			
		||||
        Spacer(modifier = Modifier.width(8.dp))
 | 
			
		||||
        Text(
 | 
			
		||||
            text = stringResource(R.string.download_notifier_cache_renewal),
 | 
			
		||||
            color = MaterialTheme.colorScheme.onSecondary,
 | 
			
		||||
            textAlign = TextAlign.Center,
 | 
			
		||||
            style = MaterialTheme.typography.labelMedium,
 | 
			
		||||
            onTextLayout = {
 | 
			
		||||
                with(density) {
 | 
			
		||||
                    textHeight = it.size.height.toDp()
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,11 +20,14 @@ import kotlinx.coroutines.awaitAll
 | 
			
		||||
import kotlinx.coroutines.channels.Channel
 | 
			
		||||
import kotlinx.coroutines.delay
 | 
			
		||||
import kotlinx.coroutines.flow.SharingStarted
 | 
			
		||||
import kotlinx.coroutines.flow.distinctUntilChanged
 | 
			
		||||
import kotlinx.coroutines.flow.launchIn
 | 
			
		||||
import kotlinx.coroutines.flow.map
 | 
			
		||||
import kotlinx.coroutines.flow.onEach
 | 
			
		||||
import kotlinx.coroutines.flow.onStart
 | 
			
		||||
import kotlinx.coroutines.flow.receiveAsFlow
 | 
			
		||||
import kotlinx.coroutines.flow.shareIn
 | 
			
		||||
import kotlinx.coroutines.flow.stateIn
 | 
			
		||||
import kotlinx.coroutines.withTimeout
 | 
			
		||||
import uy.kohesive.injekt.Injekt
 | 
			
		||||
import uy.kohesive.injekt.api.get
 | 
			
		||||
@@ -53,8 +56,6 @@ class DownloadCache(
 | 
			
		||||
        .onStart { emit(Unit) }
 | 
			
		||||
        .shareIn(scope, SharingStarted.Eagerly, 1)
 | 
			
		||||
 | 
			
		||||
    private val notifier by lazy { DownloadNotifier(context) }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The interval after which this cache should be invalidated. 1 hour shouldn't cause major
 | 
			
		||||
     * issues, as the cache is only used for UI feedback.
 | 
			
		||||
@@ -66,6 +67,10 @@ class DownloadCache(
 | 
			
		||||
     */
 | 
			
		||||
    private var lastRenew = 0L
 | 
			
		||||
    private var renewalJob: Job? = null
 | 
			
		||||
    val isRenewing = changes
 | 
			
		||||
        .map { renewalJob?.isActive ?: false }
 | 
			
		||||
        .distinctUntilChanged()
 | 
			
		||||
        .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 | 
			
		||||
 | 
			
		||||
    private var rootDownloadsDir = RootDirectory(getDirectoryFromPreference())
 | 
			
		||||
 | 
			
		||||
@@ -260,8 +265,6 @@ class DownloadCache(
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        renewalJob = scope.launchIO {
 | 
			
		||||
            notifier.onCacheProgress()
 | 
			
		||||
 | 
			
		||||
            var sources = getSources()
 | 
			
		||||
 | 
			
		||||
            // Try to wait until extensions and sources have loaded
 | 
			
		||||
@@ -320,7 +323,6 @@ class DownloadCache(
 | 
			
		||||
            lastRenew = System.currentTimeMillis()
 | 
			
		||||
            notifyChanges()
 | 
			
		||||
        }
 | 
			
		||||
        renewalJob?.invokeOnCompletion { notifier.dismissCacheProgress() }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getSources(): List<Source> {
 | 
			
		||||
 
 | 
			
		||||
@@ -39,17 +39,6 @@ internal class DownloadNotifier(private val context: Context) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private val cacheNotificationBuilder by lazy {
 | 
			
		||||
        context.notificationBuilder(Notifications.CHANNEL_DOWNLOADER_CACHE) {
 | 
			
		||||
            setSmallIcon(R.drawable.ic_tachi)
 | 
			
		||||
            setContentTitle(context.getString(R.string.download_notifier_cache_renewal))
 | 
			
		||||
            setProgress(100, 100, true)
 | 
			
		||||
            setOngoing(true)
 | 
			
		||||
            setAutoCancel(false)
 | 
			
		||||
            setOnlyAlertOnce(true)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Status of download. Used for correct notification icon.
 | 
			
		||||
     */
 | 
			
		||||
@@ -223,14 +212,4 @@ internal class DownloadNotifier(private val context: Context) {
 | 
			
		||||
        errorThrown = true
 | 
			
		||||
        isDownloading = false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun onCacheProgress() {
 | 
			
		||||
        with(cacheNotificationBuilder) {
 | 
			
		||||
            show(Notifications.ID_DOWNLOAD_CACHE)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun dismissCacheProgress() {
 | 
			
		||||
        context.notificationManager.cancel(Notifications.ID_DOWNLOAD_CACHE)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -40,8 +40,6 @@ object Notifications {
 | 
			
		||||
    const val ID_DOWNLOAD_CHAPTER_PROGRESS = -201
 | 
			
		||||
    const val CHANNEL_DOWNLOADER_ERROR = "downloader_error_channel"
 | 
			
		||||
    const val ID_DOWNLOAD_CHAPTER_ERROR = -202
 | 
			
		||||
    const val CHANNEL_DOWNLOADER_CACHE = "downloader_cache_renewal"
 | 
			
		||||
    const val ID_DOWNLOAD_CACHE = -204
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Notification channel and ids used by the library updater.
 | 
			
		||||
@@ -91,6 +89,7 @@ object Notifications {
 | 
			
		||||
        "library_channel",
 | 
			
		||||
        "library_progress_channel",
 | 
			
		||||
        "updates_ext_channel",
 | 
			
		||||
        "downloader_cache_renewal",
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -155,12 +154,6 @@ object Notifications {
 | 
			
		||||
                    setGroup(GROUP_DOWNLOADER)
 | 
			
		||||
                    setShowBadge(false)
 | 
			
		||||
                },
 | 
			
		||||
                buildNotificationChannel(CHANNEL_DOWNLOADER_CACHE, IMPORTANCE_LOW) {
 | 
			
		||||
                    setName(context.getString(R.string.channel_downloader_cache))
 | 
			
		||||
                    setGroup(GROUP_DOWNLOADER)
 | 
			
		||||
                    setShowBadge(false)
 | 
			
		||||
                    setSound(null, null)
 | 
			
		||||
                },
 | 
			
		||||
                buildNotificationChannel(CHANNEL_BACKUP_RESTORE_PROGRESS, IMPORTANCE_LOW) {
 | 
			
		||||
                    setName(context.getString(R.string.channel_progress))
 | 
			
		||||
                    setGroup(GROUP_BACKUP_RESTORE)
 | 
			
		||||
 
 | 
			
		||||
@@ -12,10 +12,13 @@ import android.widget.Toast
 | 
			
		||||
import androidx.activity.compose.BackHandler
 | 
			
		||||
import androidx.compose.foundation.isSystemInDarkTheme
 | 
			
		||||
import androidx.compose.foundation.layout.Box
 | 
			
		||||
import androidx.compose.foundation.layout.Column
 | 
			
		||||
import androidx.compose.foundation.layout.WindowInsets
 | 
			
		||||
import androidx.compose.foundation.layout.WindowInsetsSides
 | 
			
		||||
import androidx.compose.foundation.layout.consumeWindowInsets
 | 
			
		||||
import androidx.compose.foundation.layout.statusBars
 | 
			
		||||
import androidx.compose.foundation.layout.navigationBars
 | 
			
		||||
import androidx.compose.foundation.layout.only
 | 
			
		||||
import androidx.compose.foundation.layout.padding
 | 
			
		||||
import androidx.compose.foundation.layout.windowInsetsPadding
 | 
			
		||||
import androidx.compose.material3.AlertDialog
 | 
			
		||||
import androidx.compose.material3.MaterialTheme
 | 
			
		||||
import androidx.compose.material3.Text
 | 
			
		||||
@@ -23,6 +26,7 @@ import androidx.compose.material3.TextButton
 | 
			
		||||
import androidx.compose.material3.surfaceColorAtElevation
 | 
			
		||||
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
 | 
			
		||||
@@ -54,6 +58,8 @@ import eu.kanade.domain.ui.UiPreferences
 | 
			
		||||
import eu.kanade.presentation.components.AppStateBanners
 | 
			
		||||
import eu.kanade.presentation.components.DownloadedOnlyBannerBackgroundColor
 | 
			
		||||
import eu.kanade.presentation.components.IncognitoModeBannerBackgroundColor
 | 
			
		||||
import eu.kanade.presentation.components.IndexingBannerBackgroundColor
 | 
			
		||||
import eu.kanade.presentation.components.Scaffold
 | 
			
		||||
import eu.kanade.presentation.util.AssistContentScreen
 | 
			
		||||
import eu.kanade.presentation.util.DefaultNavigatorScreenTransition
 | 
			
		||||
import eu.kanade.presentation.util.collectAsState
 | 
			
		||||
@@ -61,6 +67,7 @@ import eu.kanade.tachiyomi.BuildConfig
 | 
			
		||||
import eu.kanade.tachiyomi.Migrations
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
 | 
			
		||||
import eu.kanade.tachiyomi.data.download.DownloadCache
 | 
			
		||||
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
 | 
			
		||||
import eu.kanade.tachiyomi.data.updater.AppUpdateChecker
 | 
			
		||||
import eu.kanade.tachiyomi.data.updater.AppUpdateResult
 | 
			
		||||
@@ -101,6 +108,7 @@ class MainActivity : BaseActivity() {
 | 
			
		||||
    private val preferences: BasePreferences by injectLazy()
 | 
			
		||||
 | 
			
		||||
    private val chapterCache: ChapterCache by injectLazy()
 | 
			
		||||
    private val downloadCache: DownloadCache by injectLazy()
 | 
			
		||||
 | 
			
		||||
    // To be checked by splash screen. If true then splash screen will be removed.
 | 
			
		||||
    var ready = false
 | 
			
		||||
@@ -153,94 +161,102 @@ class MainActivity : BaseActivity() {
 | 
			
		||||
        setComposeContent {
 | 
			
		||||
            val incognito by preferences.incognitoMode().collectAsState()
 | 
			
		||||
            val downloadOnly by preferences.downloadedOnly().collectAsState()
 | 
			
		||||
            Column {
 | 
			
		||||
                AppStateBanners(
 | 
			
		||||
                    downloadedOnlyMode = downloadOnly,
 | 
			
		||||
                    incognitoMode = incognito,
 | 
			
		||||
            val indexing by downloadCache.isRenewing.collectAsState()
 | 
			
		||||
 | 
			
		||||
            // Set statusbar color considering the top app state banner
 | 
			
		||||
            val systemUiController = rememberSystemUiController()
 | 
			
		||||
            val isSystemInDarkTheme = isSystemInDarkTheme()
 | 
			
		||||
            val statusBarBackgroundColor = when {
 | 
			
		||||
                indexing -> IndexingBannerBackgroundColor
 | 
			
		||||
                downloadOnly -> DownloadedOnlyBannerBackgroundColor
 | 
			
		||||
                incognito -> IncognitoModeBannerBackgroundColor
 | 
			
		||||
                else -> MaterialTheme.colorScheme.surface
 | 
			
		||||
            }
 | 
			
		||||
            LaunchedEffect(systemUiController, statusBarBackgroundColor) {
 | 
			
		||||
                systemUiController.setStatusBarColor(
 | 
			
		||||
                    color = ComposeColor.Transparent,
 | 
			
		||||
                    darkIcons = statusBarBackgroundColor.luminance() > 0.5,
 | 
			
		||||
                    transformColorForLightContent = { ComposeColor.Black },
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
                // Set statusbar color
 | 
			
		||||
                val systemUiController = rememberSystemUiController()
 | 
			
		||||
                val isSystemInDarkTheme = isSystemInDarkTheme()
 | 
			
		||||
                val statusBarBackgroundColor = when {
 | 
			
		||||
                    downloadOnly -> DownloadedOnlyBannerBackgroundColor
 | 
			
		||||
                    incognito -> IncognitoModeBannerBackgroundColor
 | 
			
		||||
                    else -> MaterialTheme.colorScheme.background
 | 
			
		||||
                }
 | 
			
		||||
                LaunchedEffect(systemUiController, statusBarBackgroundColor) {
 | 
			
		||||
                    systemUiController.setStatusBarColor(
 | 
			
		||||
                        color = ComposeColor.Transparent,
 | 
			
		||||
                        darkIcons = statusBarBackgroundColor.luminance() > 0.5,
 | 
			
		||||
                        transformColorForLightContent = { ComposeColor.Black },
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Set navigation bar color
 | 
			
		||||
                val context = LocalContext.current
 | 
			
		||||
                val navbarScrimColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp)
 | 
			
		||||
                LaunchedEffect(systemUiController, isSystemInDarkTheme, navbarScrimColor) {
 | 
			
		||||
                    systemUiController.setNavigationBarColor(
 | 
			
		||||
                        color = if (context.isNavigationBarNeedsScrim()) {
 | 
			
		||||
                            navbarScrimColor.copy(alpha = 0.7f)
 | 
			
		||||
                        } else {
 | 
			
		||||
                            ComposeColor.Transparent
 | 
			
		||||
                        },
 | 
			
		||||
                        darkIcons = !isSystemInDarkTheme,
 | 
			
		||||
                        navigationBarContrastEnforced = false,
 | 
			
		||||
                        transformColorForLightContent = { ComposeColor.Black },
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Navigator(
 | 
			
		||||
                    screen = HomeScreen,
 | 
			
		||||
                    disposeBehavior = NavigatorDisposeBehavior(disposeNestedNavigators = false, disposeSteps = true),
 | 
			
		||||
                ) { navigator ->
 | 
			
		||||
                    if (navigator.size == 1) {
 | 
			
		||||
                        ConfirmExit()
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    LaunchedEffect(navigator) {
 | 
			
		||||
                        this@MainActivity.navigator = navigator
 | 
			
		||||
 | 
			
		||||
                        if (savedInstanceState == null) {
 | 
			
		||||
                            // Set start screen
 | 
			
		||||
                            handleIntentAction(intent)
 | 
			
		||||
 | 
			
		||||
                            // Reset Incognito Mode on relaunch
 | 
			
		||||
                            preferences.incognitoMode().set(false)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // Consume insets already used by app state banners
 | 
			
		||||
                    val boxModifier = if (incognito || downloadOnly) {
 | 
			
		||||
                        Modifier.consumeWindowInsets(WindowInsets.statusBars)
 | 
			
		||||
            // Set navigation bar color
 | 
			
		||||
            val context = LocalContext.current
 | 
			
		||||
            val navbarScrimColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp)
 | 
			
		||||
            LaunchedEffect(systemUiController, isSystemInDarkTheme, navbarScrimColor) {
 | 
			
		||||
                systemUiController.setNavigationBarColor(
 | 
			
		||||
                    color = if (context.isNavigationBarNeedsScrim()) {
 | 
			
		||||
                        navbarScrimColor.copy(alpha = 0.7f)
 | 
			
		||||
                    } else {
 | 
			
		||||
                        Modifier
 | 
			
		||||
                        ComposeColor.Transparent
 | 
			
		||||
                    },
 | 
			
		||||
                    darkIcons = !isSystemInDarkTheme,
 | 
			
		||||
                    navigationBarContrastEnforced = false,
 | 
			
		||||
                    transformColorForLightContent = { ComposeColor.Black },
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Navigator(
 | 
			
		||||
                screen = HomeScreen,
 | 
			
		||||
                disposeBehavior = NavigatorDisposeBehavior(disposeNestedNavigators = false, disposeSteps = true),
 | 
			
		||||
            ) { navigator ->
 | 
			
		||||
                if (navigator.size == 1) {
 | 
			
		||||
                    ConfirmExit()
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                LaunchedEffect(navigator) {
 | 
			
		||||
                    this@MainActivity.navigator = navigator
 | 
			
		||||
 | 
			
		||||
                    if (savedInstanceState == null) {
 | 
			
		||||
                        // Set start screen
 | 
			
		||||
                        handleIntentAction(intent)
 | 
			
		||||
 | 
			
		||||
                        // Reset Incognito Mode on relaunch
 | 
			
		||||
                        preferences.incognitoMode().set(false)
 | 
			
		||||
                    }
 | 
			
		||||
                    Box(modifier = boxModifier) {
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                val scaffoldInsets = WindowInsets.navigationBars.only(WindowInsetsSides.Horizontal)
 | 
			
		||||
                Scaffold(
 | 
			
		||||
                    topBar = {
 | 
			
		||||
                        AppStateBanners(
 | 
			
		||||
                            downloadedOnlyMode = downloadOnly,
 | 
			
		||||
                            incognitoMode = incognito,
 | 
			
		||||
                            indexing = indexing,
 | 
			
		||||
                            modifier = Modifier.windowInsetsPadding(scaffoldInsets),
 | 
			
		||||
                        )
 | 
			
		||||
                    },
 | 
			
		||||
                    contentWindowInsets = scaffoldInsets,
 | 
			
		||||
                ) { contentPadding ->
 | 
			
		||||
                    // Consume insets already used by app state banners
 | 
			
		||||
                    Box(
 | 
			
		||||
                        modifier = Modifier
 | 
			
		||||
                            .padding(contentPadding)
 | 
			
		||||
                            .consumeWindowInsets(contentPadding),
 | 
			
		||||
                    ) {
 | 
			
		||||
                        // Shows current screen
 | 
			
		||||
                        DefaultNavigatorScreenTransition(navigator = navigator)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                    // Pop source-related screens when incognito mode is turned off
 | 
			
		||||
                    LaunchedEffect(Unit) {
 | 
			
		||||
                        preferences.incognitoMode().changes()
 | 
			
		||||
                            .drop(1)
 | 
			
		||||
                            .onEach {
 | 
			
		||||
                                if (!it) {
 | 
			
		||||
                                    val currentScreen = navigator.lastItem
 | 
			
		||||
                                    if (currentScreen is BrowseSourceScreen ||
 | 
			
		||||
                                        (currentScreen is MangaScreen && currentScreen.fromSource)
 | 
			
		||||
                                    ) {
 | 
			
		||||
                                        navigator.popUntilRoot()
 | 
			
		||||
                                    }
 | 
			
		||||
                // Pop source-related screens when incognito mode is turned off
 | 
			
		||||
                LaunchedEffect(Unit) {
 | 
			
		||||
                    preferences.incognitoMode().changes()
 | 
			
		||||
                        .drop(1)
 | 
			
		||||
                        .onEach {
 | 
			
		||||
                            if (!it) {
 | 
			
		||||
                                val currentScreen = navigator.lastItem
 | 
			
		||||
                                if (currentScreen is BrowseSourceScreen ||
 | 
			
		||||
                                    (currentScreen is MangaScreen && currentScreen.fromSource)
 | 
			
		||||
                                ) {
 | 
			
		||||
                                    navigator.popUntilRoot()
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            .launchIn(this)
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    CheckForUpdate()
 | 
			
		||||
                        }
 | 
			
		||||
                        .launchIn(this)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                CheckForUpdate()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var showChangelog by remember { mutableStateOf(didMigration && !BuildConfig.DEBUG) }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user