Rewrite popups and bottom sheets to not use popup windows

Popup windows are too unrealiable
This commit is contained in:
MM20 2023-09-06 22:39:01 +02:00
parent cbcd9cbfc7
commit d7c9936014
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
14 changed files with 403 additions and 266 deletions

View File

@ -26,6 +26,7 @@ import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.CornerSize
@ -65,6 +66,7 @@ import androidx.compose.ui.window.Popup
import androidx.compose.ui.window.PopupPositionProvider
import androidx.compose.ui.window.PopupProperties
import de.mm20.launcher2.ui.ktx.toPixels
import de.mm20.launcher2.ui.overlays.Overlay
import kotlinx.coroutines.launch
import kotlin.math.min
import kotlin.math.roundToInt
@ -159,20 +161,11 @@ fun BottomSheetDialog(
val positionProvider = remember(insets, density) {
BottomSheetPositionProvider(insets, density)
}
Popup(
popupPositionProvider = positionProvider,
properties = PopupProperties(
dismissOnBackPress = dismissible(),
dismissOnClickOutside = dismissible(),
usePlatformDefaultWidth = false,
focusable = true,
clippingEnabled = false,
),
onDismissRequest = onDismissRequest,
) {
Overlay(zIndex = 9999f) {
BoxWithConstraints(
modifier = Modifier
.fillMaxSize()
.systemBarsPadding()
.imePadding(),
propagateMinConstraints = true,
contentAlignment = Alignment.BottomCenter

View File

@ -55,6 +55,7 @@ import de.mm20.launcher2.ui.locals.LocalPreferDarkContentOverWallpaper
import de.mm20.launcher2.ui.locals.LocalSnackbarHostState
import de.mm20.launcher2.ui.locals.LocalWallpaperColors
import de.mm20.launcher2.ui.locals.LocalWindowSize
import de.mm20.launcher2.ui.overlays.OverlayHost
import de.mm20.launcher2.ui.theme.LauncherTheme
import de.mm20.launcher2.ui.theme.wallpaperColorsAsState
import kotlin.math.pow
@ -160,7 +161,7 @@ abstract class SharedLauncherActivity(
systemUiController.isNavigationBarVisible = !hideNav
}
Box(
OverlayHost(
modifier = Modifier
.fillMaxSize()
.background(if (dimBackground) Color.Black.copy(alpha = 0.30f) else Color.Transparent),

View File

@ -4,6 +4,7 @@ import android.app.PendingIntent
import android.content.Intent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.tween
import androidx.compose.animation.expandIn
import androidx.compose.animation.shrinkOut
@ -58,6 +59,7 @@ import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.lerp
import androidx.compose.ui.unit.roundToIntRect
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
@ -428,7 +430,7 @@ fun AppItem(
@Composable
fun AppItemGridPopup(
app: LauncherApp,
show: Boolean,
show: MutableTransitionState<Boolean>,
animationProgress: Float,
origin: Rect,
onDismiss: () -> Unit
@ -452,8 +454,8 @@ fun AppItemGridPopup(
transformOrigin = TransformOrigin(1f, 0f)
)
.offset(
x = 16.dp * (1 - animationProgress).pow(10),
y = -16.dp * (1 - animationProgress),
x = lerp(16.dp, 0.dp, animationProgress),
y = lerp(-16.dp, 0.dp, animationProgress)
),
app = app,
onBack = onDismiss

View File

@ -3,6 +3,7 @@ package de.mm20.launcher2.ui.launcher.search.calendar
import android.content.Context
import android.text.format.DateUtils
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.tween
import androidx.compose.animation.expandIn
@ -301,7 +302,7 @@ fun CalendarItem(
@Composable
fun CalendarItemGridPopup(
calendar: CalendarEvent,
show: Boolean,
show: MutableTransitionState<Boolean>,
animationProgress: Float,
origin: Rect,
onDismiss: () -> Unit

View File

@ -1,24 +1,30 @@
package de.mm20.launcher2.ui.launcher.search.common.grid
import android.content.ComponentName
import android.util.Log
import androidx.activity.compose.BackHandler
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.absoluteOffset
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.ime
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.layout.union
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
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
@ -27,18 +33,18 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.boundsInWindow
import androidx.compose.ui.layout.layout
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Popup
import androidx.compose.ui.window.PopupProperties
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.search.SavableSearchable
import de.mm20.launcher2.search.Searchable
import de.mm20.launcher2.search.data.AppShortcut
@ -51,7 +57,6 @@ import de.mm20.launcher2.search.data.Wikipedia
import de.mm20.launcher2.ui.component.LauncherCard
import de.mm20.launcher2.ui.component.LocalIconShape
import de.mm20.launcher2.ui.component.ShapedLauncherIcon
import de.mm20.launcher2.ui.ktx.toDp
import de.mm20.launcher2.ui.ktx.toPixels
import de.mm20.launcher2.ui.launcher.search.apps.AppItemGridPopup
import de.mm20.launcher2.ui.launcher.search.calendar.CalendarItemGridPopup
@ -65,9 +70,10 @@ import de.mm20.launcher2.ui.launcher.search.wikipedia.WikipediaItemGridPopup
import de.mm20.launcher2.ui.launcher.transitions.EnterHomeTransitionParams
import de.mm20.launcher2.ui.launcher.transitions.HandleEnterHomeTransition
import de.mm20.launcher2.ui.locals.LocalGridSettings
import de.mm20.launcher2.ui.locals.LocalWindowPosition
import de.mm20.launcher2.ui.locals.LocalWindowSize
import kotlinx.coroutines.delay
import de.mm20.launcher2.ui.overlays.Overlay
import kotlin.math.min
import kotlin.math.pow
@Composable
@ -98,7 +104,8 @@ fun GridItem(
onClick = {
if (!launchOnPress || !viewModel.launch(context, bounds)) {
showPopup = true
}},
}
},
onLongClick = {
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
showPopup = true
@ -106,7 +113,8 @@ fun GridItem(
indication = null,
interactionSource = remember { MutableInteractionSource() },
),
horizontalAlignment = Alignment.CenterHorizontally) {
horizontalAlignment = Alignment.CenterHorizontally
) {
val badge by viewModel.badge.collectAsStateWithLifecycle()
val icon by viewModel.icon.collectAsStateWithLifecycle()
@ -181,136 +189,176 @@ fun GridItem(
@Composable
fun ItemPopup(origin: Rect, searchable: Searchable, onDismissRequest: () -> Unit) {
var show by remember { mutableStateOf(false) }
LaunchedEffect(null) {
show = true
val show = remember {
MutableTransitionState(false).apply {
targetState = true
}
}
LaunchedEffect(show) {
if (!show) {
delay(300L)
val animationProgress = remember { Animatable(0f) }
LaunchedEffect(show.targetState) {
if (!show.targetState) {
animationProgress.animateTo(0f, tween(300))
onDismissRequest()
} else {
animationProgress.animateTo(1f, tween(300))
}
}
BackHandler {
show = false
show.targetState = false
}
val animationProgress by animateFloatAsState(if (show) 1f else 0f, tween(300))
Popup(
properties = PopupProperties(
usePlatformDefaultWidth = false,
dismissOnBackPress = true
),
alignment = Alignment.TopCenter,
onDismissRequest = {
show = false
},
offset = IntOffset(-origin.left.toInt(), 0)
) {
CompositionLocalProvider(LocalWindowPosition provides origin.top) {
Box(
Overlay(zIndex = 1f) {
Box(
modifier = Modifier
.background(MaterialTheme.colorScheme.scrim.copy(alpha = 0.32f * animationProgress.value))
.fillMaxSize()
.systemBarsPadding()
.imePadding()
.padding(horizontal = 16.dp)
.pointerInput(Unit) {
detectTapGestures(onPress = {
show.targetState = false
})
},
) {
LauncherCard(
elevation = 8.dp * animationProgress.value,
backgroundOpacity = 1f,
modifier = Modifier
.fillMaxWidth()
.placeOverlay(
origin.translate(
-16.dp.toPixels(),
-WindowInsets.systemBars.union(WindowInsets.ime).getTop(LocalDensity.current).toFloat()
),
animationProgress.value
)
) {
LauncherCard(
elevation = 8.dp * animationProgress,
backgroundOpacity = 1f,
modifier = Modifier
.padding(horizontal = 16.dp)
.absoluteOffset(
x = ((1 - animationProgress) * origin.left).toDp() - 20.dp * (1 - animationProgress),
when (searchable) {
is LauncherApp -> {
AppItemGridPopup(
app = searchable,
show = show,
animationProgress = animationProgress.value,
origin = origin,
onDismiss = {
show.targetState = false
}
)
.wrapContentSize()
.padding(4.dp)
) {
when (searchable) {
is LauncherApp -> {
AppItemGridPopup(
app = searchable,
show = show,
animationProgress = animationProgress,
origin = origin,
onDismiss = {
show = false
}
)
}
}
is Website -> {
WebsiteItemGridPopup(
website = searchable,
show = show,
animationProgress = animationProgress,
origin = origin,
onDismiss = {
show = false
}
)
}
is Website -> {
WebsiteItemGridPopup(
website = searchable,
show = show,
animationProgress = animationProgress.value,
origin = origin,
onDismiss = {
show.targetState = false
}
)
}
is Wikipedia -> {
WikipediaItemGridPopup(
wikipedia = searchable,
show = show,
animationProgress = animationProgress,
origin = origin,
onDismiss = {
show = false
}
)
}
is Wikipedia -> {
WikipediaItemGridPopup(
wikipedia = searchable,
show = show,
animationProgress = animationProgress.value,
origin = origin,
onDismiss = {
show.targetState = false
}
)
}
is Contact -> {
ContactItemGridPopup(
contact = searchable,
show = show,
animationProgress = animationProgress,
origin = origin,
onDismiss = {
show = false
}
)
}
is Contact -> {
ContactItemGridPopup(
contact = searchable,
show = show,
animationProgress = animationProgress.value,
origin = origin,
onDismiss = {
show.targetState = false
}
)
}
is File -> {
FileItemGridPopup(
file = searchable,
show = show,
animationProgress = animationProgress,
origin = origin,
onDismiss = {
show = false
}
)
}
is File -> {
FileItemGridPopup(
file = searchable,
show = show,
animationProgress = animationProgress.value,
origin = origin,
onDismiss = {
show.targetState = false
}
)
}
is CalendarEvent -> {
CalendarItemGridPopup(
calendar = searchable,
show = show,
animationProgress = animationProgress,
origin = origin,
onDismiss = {
show = false
}
)
}
is CalendarEvent -> {
CalendarItemGridPopup(
calendar = searchable,
show = show,
animationProgress = animationProgress.value,
origin = origin,
onDismiss = {
show.targetState = false
}
)
}
is AppShortcut -> {
ShortcutItemGridPopup(
shortcut = searchable,
show = show,
animationProgress = animationProgress,
origin = origin,
onDismiss = {
show = false
}
)
}
is AppShortcut -> {
ShortcutItemGridPopup(
shortcut = searchable,
show = show,
animationProgress = animationProgress.value,
origin = origin,
onDismiss = {
show.targetState = false
}
)
}
}
}
}
}
}
private fun Modifier.placeOverlay(
origin: Rect,
animationProgress: Float,
): Modifier {
return layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
Log.d(
"MM20",
"Layout: maxWidth: ${constraints.maxWidth}, origin: $origin, placeable: ${placeable.width}"
)
layout(constraints.maxWidth, constraints.maxHeight) {
placeable.placeRelative(
(
lerp(
origin.center.x,
constraints.maxWidth / 2f,
((placeable.width.toFloat() - origin.width) / (constraints.maxWidth.toFloat() - origin.width))
) - placeable.width / 2f).toInt(),
lerp(
origin.top,
(origin.center.y - placeable.height / 2f).coerceIn(
0f,
constraints.maxHeight.toFloat() - placeable.height.toFloat(),
),
animationProgress.pow(2f)
).toInt()
)
}
}
}
private fun lerp(start: Float, stop: Float, fraction: Float): Float {
return start + fraction * (stop - start)
}
private fun lerp(start: Int, stop: Int, fraction: Float): Int {
return start + (fraction * (stop - start)).toInt()
}

View File

@ -3,6 +3,7 @@ package de.mm20.launcher2.ui.launcher.search.contacts
import android.content.Intent
import android.net.Uri
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.animateDp
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
@ -402,7 +403,7 @@ fun ContactItem(
@Composable
fun ContactItemGridPopup(
contact: Contact,
show: Boolean,
show: MutableTransitionState<Boolean>,
animationProgress: Float,
origin: Rect,
onDismiss: () -> Unit

View File

@ -1,6 +1,7 @@
package de.mm20.launcher2.ui.launcher.search.files
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.animateDp
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
@ -322,7 +323,7 @@ fun FileItem(
@Composable
fun FileItemGridPopup(
file: File,
show: Boolean,
show: MutableTransitionState<Boolean>,
animationProgress: Float,
origin: Rect,
onDismiss: () -> Unit

View File

@ -5,6 +5,7 @@ import android.content.Intent
import android.net.Uri
import android.provider.Settings
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.animateDp
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.tween
@ -284,7 +285,7 @@ fun AppShortcutItem(
@Composable
fun ShortcutItemGridPopup(
shortcut: AppShortcut,
show: Boolean,
show: MutableTransitionState<Boolean>,
animationProgress: Float,
origin: Rect,
onDismiss: () -> Unit

View File

@ -1,6 +1,7 @@
package de.mm20.launcher2.ui.launcher.search.website
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.tween
import androidx.compose.animation.expandIn
import androidx.compose.animation.shrinkOut
@ -154,7 +155,7 @@ fun WebsiteItem(
@Composable
fun WebsiteItemGridPopup(
website: Website,
show: Boolean,
show: MutableTransitionState<Boolean>,
animationProgress: Float,
origin: Rect,
onDismiss: () -> Unit

View File

@ -1,6 +1,7 @@
package de.mm20.launcher2.ui.launcher.search.wikipedia
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.tween
import androidx.compose.animation.expandIn
import androidx.compose.animation.shrinkOut
@ -161,7 +162,7 @@ fun WikipediaItem(
@Composable
fun WikipediaItemGridPopup(
wikipedia: Wikipedia,
show: Boolean,
show: MutableTransitionState<Boolean>,
animationProgress: Float,
origin: Rect,
onDismiss: () -> Unit

View File

@ -0,0 +1,18 @@
package de.mm20.launcher2.ui.overlays
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
@Composable
fun Overlay(
zIndex: Float = 0f,
overlay: @Composable () -> Unit
) {
val overlayManager = LocalOverlayManager.current
DisposableEffect(Unit) {
overlayManager.addOverlay(overlay, zIndex)
onDispose {
overlayManager.removeOverlay(overlay)
}
}
}

View File

@ -0,0 +1,39 @@
package de.mm20.launcher2.ui.overlays
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.zIndex
@Composable
fun OverlayHost(
modifier: Modifier = Modifier,
contentAlignment: Alignment = Alignment.TopStart,
content: @Composable BoxScope.() -> Unit,
) {
val overlayManager = remember { OverlayManager() }
CompositionLocalProvider(LocalOverlayManager provides overlayManager) {
Box {
Box(
contentAlignment = contentAlignment,
modifier = modifier.zIndex(0f),
) {
content()
}
for (overlay in overlayManager.overlays) {
Box(modifier = Modifier.zIndex(overlay.zIndex + 1f)) {
overlay()
}
}
}
}
}
val LocalOverlayManager = compositionLocalOf<OverlayManager> {
OverlayManager()
}

View File

@ -0,0 +1,26 @@
package de.mm20.launcher2.ui.overlays
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateListOf
class OverlayManager {
val overlays = mutableStateListOf<Overlay>()
fun addOverlay(overlay: @Composable () -> Unit, zIndex: Float = 0f) {
overlays.add(Overlay(overlay, zIndex))
}
fun removeOverlay(overlay: @Composable () -> Unit) {
overlays.removeAll { overlay == it.overlay }
}
}
data class Overlay(
val overlay: @Composable () -> Unit,
val zIndex: Float = 0f,
) {
@Composable
operator fun invoke() {
overlay()
}
}

View File

@ -21,6 +21,7 @@ import de.mm20.launcher2.ui.base.BaseActivity
import de.mm20.launcher2.ui.base.ProvideSettings
import de.mm20.launcher2.ui.locals.LocalNavController
import de.mm20.launcher2.ui.locals.LocalWallpaperColors
import de.mm20.launcher2.ui.overlays.OverlayHost
import de.mm20.launcher2.ui.settings.about.AboutSettingsScreen
import de.mm20.launcher2.ui.settings.appearance.AppearanceSettingsScreen
import de.mm20.launcher2.ui.settings.backup.BackupSettingsScreen
@ -78,129 +79,132 @@ class SettingsActivity : BaseActivity() {
) {
ProvideSettings {
LauncherTheme {
AnimatedNavHost(
navController = navController,
startDestination = "settings",
exitTransition = { fadeOut(tween(300, 300)) },
enterTransition = { fadeIn(tween(200)) },
popEnterTransition = { fadeIn(tween(0)) },
popExitTransition = { fadeOut(tween(200)) },
) {
composable("settings") {
MainSettingsScreen()
}
composable("settings/appearance") {
AppearanceSettingsScreen()
}
composable("settings/homescreen") {
HomescreenSettingsScreen()
}
composable("settings/icons") {
IconsSettingsScreen()
}
composable("settings/appearance/themes") {
ThemesSettingsScreen()
}
composable(
"settings/appearance/themes/{id}",
arguments = listOf(navArgument("id") {
nullable = false
})) {
val id = it.arguments?.getString("id")?.let {
UUID.fromString(it)
} ?: return@composable
ThemeSettingsScreen(id)
}
composable("settings/appearance/cards") {
CardsSettingsScreen()
}
composable("settings/search") {
SearchSettingsScreen()
}
composable("settings/gestures") {
GestureSettingsScreen()
}
composable("settings/search/unitconverter") {
UnitConverterSettingsScreen()
}
composable("settings/search/wikipedia") {
WikipediaSettingsScreen()
}
composable("settings/search/files") {
FileSearchSettingsScreen()
}
composable("settings/search/searchactions") {
SearchActionsSettingsScreen()
}
composable("settings/search/hiddenitems") {
HiddenItemsSettingsScreen()
}
composable("settings/search/tags") {
TagsSettingsScreen()
}
composable(ROUTE_WEATHER_INTEGRATION) {
WeatherIntegrationSettingsScreen()
}
composable(ROUTE_MEDIA_INTEGRATION) {
MediaIntegrationSettingsScreen()
}
composable("settings/homescreen/clock") {
ClockWidgetSettingsScreen()
}
composable("settings/favorites") {
FavoritesSettingsScreen()
}
composable("settings/integrations") {
IntegrationsSettingsScreen()
}
composable("settings/about") {
AboutSettingsScreen()
}
composable("settings/about/buildinfo") {
BuildInfoSettingsScreen()
}
composable("settings/about/easteregg") {
EasterEggSettingsScreen()
}
composable("settings/debug") {
DebugSettingsScreen()
}
composable("settings/backup") {
BackupSettingsScreen()
}
composable("settings/debug/crashreporter") {
CrashReporterScreen()
}
composable("settings/debug/logs") {
LogScreen()
}
composable(
"settings/debug/crashreporter/report?fileName={fileName}",
arguments = listOf(navArgument("fileName") {
nullable = false
})
OverlayHost {
AnimatedNavHost(
navController = navController,
startDestination = "settings",
exitTransition = { fadeOut(tween(300, 300)) },
enterTransition = { fadeIn(tween(200)) },
popEnterTransition = { fadeIn(tween(0)) },
popExitTransition = { fadeOut(tween(200)) },
) {
val fileName = it.arguments?.getString("fileName")
?.let {
URLDecoder.decode(it, "utf8")
}
CrashReportScreen(fileName!!)
}
composable(
"settings/license?library={libraryName}",
arguments = listOf(navArgument("libraryName") {
nullable = true
})
) {
val libraryName = it.arguments?.getString("libraryName")
val library = remember(libraryName) {
if (libraryName == null) {
AppLicense.get(this@SettingsActivity)
} else {
OpenSourceLicenses.first { it.name == libraryName }
}
composable("settings") {
MainSettingsScreen()
}
composable("settings/appearance") {
AppearanceSettingsScreen()
}
composable("settings/homescreen") {
HomescreenSettingsScreen()
}
composable("settings/icons") {
IconsSettingsScreen()
}
composable("settings/appearance/themes") {
ThemesSettingsScreen()
}
composable(
"settings/appearance/themes/{id}",
arguments = listOf(navArgument("id") {
nullable = false
})
) {
val id = it.arguments?.getString("id")?.let {
UUID.fromString(it)
} ?: return@composable
ThemeSettingsScreen(id)
}
composable("settings/appearance/cards") {
CardsSettingsScreen()
}
composable("settings/search") {
SearchSettingsScreen()
}
composable("settings/gestures") {
GestureSettingsScreen()
}
composable("settings/search/unitconverter") {
UnitConverterSettingsScreen()
}
composable("settings/search/wikipedia") {
WikipediaSettingsScreen()
}
composable("settings/search/files") {
FileSearchSettingsScreen()
}
composable("settings/search/searchactions") {
SearchActionsSettingsScreen()
}
composable("settings/search/hiddenitems") {
HiddenItemsSettingsScreen()
}
composable("settings/search/tags") {
TagsSettingsScreen()
}
composable(ROUTE_WEATHER_INTEGRATION) {
WeatherIntegrationSettingsScreen()
}
composable(ROUTE_MEDIA_INTEGRATION) {
MediaIntegrationSettingsScreen()
}
composable("settings/homescreen/clock") {
ClockWidgetSettingsScreen()
}
composable("settings/favorites") {
FavoritesSettingsScreen()
}
composable("settings/integrations") {
IntegrationsSettingsScreen()
}
composable("settings/about") {
AboutSettingsScreen()
}
composable("settings/about/buildinfo") {
BuildInfoSettingsScreen()
}
composable("settings/about/easteregg") {
EasterEggSettingsScreen()
}
composable("settings/debug") {
DebugSettingsScreen()
}
composable("settings/backup") {
BackupSettingsScreen()
}
composable("settings/debug/crashreporter") {
CrashReporterScreen()
}
composable("settings/debug/logs") {
LogScreen()
}
composable(
"settings/debug/crashreporter/report?fileName={fileName}",
arguments = listOf(navArgument("fileName") {
nullable = false
})
) {
val fileName = it.arguments?.getString("fileName")
?.let {
URLDecoder.decode(it, "utf8")
}
CrashReportScreen(fileName!!)
}
composable(
"settings/license?library={libraryName}",
arguments = listOf(navArgument("libraryName") {
nullable = true
})
) {
val libraryName = it.arguments?.getString("libraryName")
val library = remember(libraryName) {
if (libraryName == null) {
AppLicense.get(this@SettingsActivity)
} else {
OpenSourceLicenses.first { it.name == libraryName }
}
}
LicenseScreen(library)
}
LicenseScreen(library)
}
}
}