mirror of
https://github.com/kavishdevar/librepods.git
synced 2026-02-02 16:19:10 +00:00
android: little more liquid glass
This commit is contained in:
@@ -1,47 +1,39 @@
|
||||
package me.kavishdevar.librepods.composables
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredWidthIn
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.pointer.PointerEventType
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.Font
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import dev.chrisbanes.haze.HazeState
|
||||
import dev.chrisbanes.haze.hazeEffect
|
||||
import dev.chrisbanes.haze.materials.CupertinoMaterials
|
||||
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
||||
import com.kyant.backdrop.Backdrop
|
||||
import com.kyant.backdrop.drawBackdrop
|
||||
import com.kyant.backdrop.effects.blur
|
||||
import com.kyant.backdrop.effects.colorFilter
|
||||
import com.kyant.backdrop.effects.refraction
|
||||
import me.kavishdevar.librepods.R
|
||||
|
||||
@ExperimentalHazeMaterialsApi
|
||||
@Composable
|
||||
fun ConfirmationDialog(
|
||||
showDialog: MutableState<Boolean>,
|
||||
@@ -51,133 +43,100 @@ fun ConfirmationDialog(
|
||||
dismissText: String = "Cancel",
|
||||
onConfirm: () -> Unit,
|
||||
onDismiss: () -> Unit = { showDialog.value = false },
|
||||
hazeState: HazeState,
|
||||
backdrop: Backdrop,
|
||||
) {
|
||||
val isDarkTheme = isSystemInDarkTheme()
|
||||
val textColor = if (isDarkTheme) Color.White else Color.Black
|
||||
val accentColor = if (isDarkTheme) Color(0xFF007AFF) else Color(0xFF3C6DF5)
|
||||
if (showDialog.value) {
|
||||
Dialog(onDismissRequest = { showDialog.value = false }) {
|
||||
val isLightTheme = !isSystemInDarkTheme()
|
||||
val contentColor = if (isLightTheme) Color.Black else Color.White
|
||||
val accentColor = if (isLightTheme) Color(0xFF0088FF) else Color(0xFF0091FF)
|
||||
val containerColor = if (isLightTheme) Color(0xFFFAFAFA).copy(0.6f) else Color(0xFF121212).copy(0.4f)
|
||||
val dimColor = if (isLightTheme) Color(0xFF29293A).copy(0.23f) else Color(0xFF121212).copy(0.56f)
|
||||
|
||||
Box(
|
||||
Modifier
|
||||
.background(dimColor)
|
||||
.fillMaxSize()
|
||||
.clickable(onClick = onDismiss)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
Modifier
|
||||
.align(Alignment.Center)
|
||||
.drawBackdrop(
|
||||
backdrop,
|
||||
{ RoundedCornerShape(48f.dp) },
|
||||
// highlight = { Highlight { HighlightStyle.Solid } },
|
||||
onDrawSurface = { drawRect(containerColor) }
|
||||
) {
|
||||
colorFilter(
|
||||
brightness = if (isLightTheme) 0.2f else 0.1f,
|
||||
saturation = 1.5f
|
||||
)
|
||||
blur(if (isLightTheme) 16f.dp.toPx() else 8f.dp.toPx())
|
||||
refraction(24f.dp.toPx(), 48f.dp.toPx(), true)
|
||||
}
|
||||
.fillMaxWidth(0.75f)
|
||||
.requiredWidthIn(min = 200.dp, max = 360.dp)
|
||||
.background(Color.Transparent, RoundedCornerShape(14.dp))
|
||||
.clip(RoundedCornerShape(14.dp))
|
||||
.hazeEffect(
|
||||
hazeState,
|
||||
style = CupertinoMaterials.regular(
|
||||
containerColor = if (isDarkTheme) Color(0xFF1C1C1E).copy(alpha = 0.95f) else Color.White.copy(alpha = 0.95f)
|
||||
)
|
||||
)
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
androidx.compose.foundation.layout.Spacer(modifier = Modifier.height(24.dp))
|
||||
Column(horizontalAlignment = Alignment.Start) {
|
||||
Spacer(modifier = Modifier.height(28.dp))
|
||||
Text(
|
||||
title,
|
||||
style = TextStyle(
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = textColor,
|
||||
color = contentColor,
|
||||
fontFamily = FontFamily(Font(R.font.sf_pro))
|
||||
),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(horizontal = 16.dp)
|
||||
)
|
||||
androidx.compose.foundation.layout.Spacer(modifier = Modifier.height(12.dp))
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(
|
||||
message,
|
||||
style = TextStyle(
|
||||
fontSize = 14.sp,
|
||||
color = textColor.copy(alpha = 0.8f),
|
||||
color = contentColor.copy(alpha = 0.8f),
|
||||
fontFamily = FontFamily(Font(R.font.sf_pro))
|
||||
),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(horizontal = 16.dp)
|
||||
)
|
||||
androidx.compose.foundation.layout.Spacer(modifier = Modifier.height(16.dp))
|
||||
HorizontalDivider(
|
||||
thickness = 1.dp,
|
||||
color = Color(0x40888888),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
var leftPressed by remember { mutableStateOf(false) }
|
||||
var rightPressed by remember { mutableStateOf(false) }
|
||||
val pressedColor = if (isDarkTheme) Color(0x40888888) else Color(0x40D9D9D9)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(48.dp)
|
||||
.pointerInput(Unit) {
|
||||
awaitPointerEventScope {
|
||||
while (true) {
|
||||
val event = awaitPointerEvent()
|
||||
val position = event.changes.first().position
|
||||
val width = size.width.toFloat()
|
||||
val height = size.height.toFloat()
|
||||
val isWithinBounds = position.y >= 0 && position.y <= height
|
||||
val isLeft = position.x < width / 2
|
||||
event.changes.first().consume()
|
||||
when (event.type) {
|
||||
PointerEventType.Press -> {
|
||||
if (isWithinBounds) {
|
||||
leftPressed = isLeft
|
||||
rightPressed = !isLeft
|
||||
} else {
|
||||
leftPressed = false
|
||||
rightPressed = false
|
||||
}
|
||||
}
|
||||
PointerEventType.Move -> {
|
||||
if (isWithinBounds) {
|
||||
leftPressed = isLeft
|
||||
rightPressed = !isLeft
|
||||
} else {
|
||||
leftPressed = false
|
||||
rightPressed = false
|
||||
}
|
||||
}
|
||||
PointerEventType.Release -> {
|
||||
if (isWithinBounds) {
|
||||
if (leftPressed) {
|
||||
onDismiss()
|
||||
} else if (rightPressed) {
|
||||
onConfirm()
|
||||
}
|
||||
}
|
||||
leftPressed = false
|
||||
rightPressed = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
Modifier
|
||||
.padding(24.dp, 12.dp, 24.dp, 24.dp)
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
Modifier
|
||||
.clip(RoundedCornerShape(50.dp))
|
||||
.background(containerColor.copy(0.2f))
|
||||
.clickable(onClick = onDismiss)
|
||||
.height(48.dp)
|
||||
.weight(1f)
|
||||
.fillMaxHeight()
|
||||
.background(if (leftPressed) pressedColor else Color.Transparent),
|
||||
.padding(horizontal = 16.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(dismissText, color = accentColor)
|
||||
Text(
|
||||
dismissText,
|
||||
style = TextStyle(contentColor, 16.sp)
|
||||
)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(1.dp)
|
||||
.fillMaxHeight()
|
||||
.background(Color(0x40888888))
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
Modifier
|
||||
.clip(RoundedCornerShape(50.dp))
|
||||
.background(accentColor)
|
||||
.clickable(onClick = onConfirm)
|
||||
.height(48.dp)
|
||||
.weight(1f)
|
||||
.fillMaxHeight()
|
||||
.background(if (rightPressed) pressedColor else Color.Transparent),
|
||||
.padding(horizontal = 16.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(confirmText, color = accentColor)
|
||||
Text(
|
||||
confirmText,
|
||||
style = TextStyle(Color.White, 16.sp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,16 +133,18 @@ fun StyledSlider(
|
||||
val density = LocalDensity.current
|
||||
|
||||
val content = @Composable {
|
||||
Box(Modifier.fillMaxWidth()) {
|
||||
Box(
|
||||
Modifier.fillMaxWidth(if (startIcon == null && endIcon == null) 0.95f else 1f)
|
||||
) {
|
||||
Box(Modifier
|
||||
.backdrop(sliderBackdrop)
|
||||
.fillMaxWidth()) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(1f)
|
||||
.padding(vertical = 8.dp),
|
||||
.padding(vertical = 12.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
if (label != null) {
|
||||
Text(
|
||||
@@ -152,13 +154,14 @@ fun StyledSlider(
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = labelTextColor,
|
||||
fontFamily = FontFamily(Font(R.font.sf_pro))
|
||||
)
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
if (startLabel != null || endLabel != null) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
@@ -183,7 +186,10 @@ fun StyledSlider(
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 4.dp)
|
||||
.then(if (startIcon == null && endIcon == null) Modifier.padding(horizontal = 12.dp) else Modifier),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(0.dp)
|
||||
) {
|
||||
@@ -191,7 +197,7 @@ fun StyledSlider(
|
||||
Text(
|
||||
text = startIcon,
|
||||
style = TextStyle(
|
||||
fontSize = 16.sp,
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = labelTextColor,
|
||||
fontFamily = FontFamily(Font(R.font.sf_pro))
|
||||
@@ -240,7 +246,7 @@ fun StyledSlider(
|
||||
Text(
|
||||
text = endIcon,
|
||||
style = TextStyle(
|
||||
fontSize = 16.sp,
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = labelTextColor,
|
||||
fontFamily = FontFamily(Font(R.font.sf_pro))
|
||||
@@ -260,10 +266,10 @@ fun StyledSlider(
|
||||
Modifier
|
||||
.graphicsLayer {
|
||||
val startOffset =
|
||||
if (startIcon != null) startIconWidthState.floatValue + with(density) { 24.dp.toPx() } else 0f
|
||||
if (startIcon != null) startIconWidthState.floatValue + with(density) { 24.dp.toPx() } else with(density) { 8.dp.toPx() }
|
||||
translationX =
|
||||
startOffset + fraction * trackWidthState.floatValue - size.width / 2f
|
||||
translationY = trackPositionState.floatValue / 2f
|
||||
translationY = if (startLabel != null || endLabel != null) trackPositionState.floatValue + with(density) { 22.dp.toPx() } + size.height / 2f else trackPositionState.floatValue + with(density) { 4.dp.toPx() }
|
||||
}
|
||||
.draggable(
|
||||
rememberDraggableState { delta ->
|
||||
@@ -372,13 +378,13 @@ private fun snapIfClose(value: Float, points: List<Float>, threshold: Float = 0.
|
||||
return if (kotlin.math.abs(nearest - value) <= threshold) nearest else value
|
||||
}
|
||||
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
fun StyledSliderPreview() {
|
||||
val a = remember { mutableFloatStateOf(1f) }
|
||||
Box(
|
||||
Modifier
|
||||
.background(if (isSystemInDarkTheme()) Color(0xFF121212) else Color(0xFFF0F0F0))
|
||||
.background(if (isSystemInDarkTheme()) Color(0xFF000000) else Color(0xFFF0F0F0))
|
||||
.padding(16.dp)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
@@ -393,8 +399,8 @@ fun StyledSliderPreview() {
|
||||
},
|
||||
valueRange = 0f..2f,
|
||||
independent = true,
|
||||
startIcon = "A",
|
||||
endIcon = "B"
|
||||
startLabel = "A",
|
||||
endLabel = "B"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,8 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavController
|
||||
import com.kyant.backdrop.backdrop
|
||||
import com.kyant.backdrop.rememberBackdrop
|
||||
import dev.chrisbanes.haze.HazeEffectScope
|
||||
import dev.chrisbanes.haze.HazeState
|
||||
import dev.chrisbanes.haze.hazeEffect
|
||||
@@ -78,7 +80,6 @@ import dev.chrisbanes.haze.materials.CupertinoMaterials
|
||||
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import me.kavishdevar.librepods.R
|
||||
import me.kavishdevar.librepods.composables.ConfirmationDialog
|
||||
@@ -90,8 +91,6 @@ import me.kavishdevar.librepods.utils.parseTransparencySettingsResponse
|
||||
import me.kavishdevar.librepods.utils.sendTransparencySettings
|
||||
import kotlin.io.encoding.ExperimentalEncodingApi
|
||||
|
||||
private var debounceJob: Job? = null
|
||||
private var phoneMediaDebounceJob: Job? = null
|
||||
private const val TAG = "AccessibilitySettings"
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
@@ -109,6 +108,7 @@ fun HearingAidScreen(navController: NavController) {
|
||||
val aacpManager = remember { ServiceManager.getService()?.aacpManager }
|
||||
|
||||
val showDialog = remember { mutableStateOf(false) }
|
||||
val backdrop = rememberBackdrop()
|
||||
|
||||
val hearingAidEnabled = remember {
|
||||
val aidStatus = aacpManager?.controlCommandStatusList?.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID }
|
||||
@@ -164,7 +164,9 @@ fun HearingAidScreen(navController: NavController) {
|
||||
)
|
||||
)
|
||||
},
|
||||
snackbarHost = { SnackbarHost(snackbarHostState) }
|
||||
snackbarHost = { SnackbarHost(snackbarHostState) },
|
||||
modifier = Modifier
|
||||
.backdrop(backdrop)
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@@ -210,7 +212,7 @@ fun HearingAidScreen(navController: NavController) {
|
||||
} else {
|
||||
aacpManager?.sendControlCommand(AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID.value, byteArrayOf(0x01, 0x02))
|
||||
aacpManager?.sendControlCommand(AACPManager.Companion.ControlCommandIdentifiers.HEARING_ASSIST_CONFIG.value, 0x02.toByte())
|
||||
hearingAidEnabled.value = value
|
||||
hearingAidEnabled.value = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,6 +452,6 @@ fun HearingAidScreen(navController: NavController) {
|
||||
}
|
||||
}
|
||||
},
|
||||
hazeState = hazeState
|
||||
backdrop = backdrop
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user