Rewrite popups and bottom sheets to not use popup windows
Popup windows are too unrealiable
This commit is contained in:
parent
cbcd9cbfc7
commit
d7c9936014
@ -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
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user