android: liquidglass, maybe?

the switch and icon button took quite a while. i forgot the order of modifiers matters!
This commit is contained in:
Kavish Devar
2025-09-28 12:27:05 +05:30
parent 8dc7a97c43
commit 08738a1293
33 changed files with 1245 additions and 1992 deletions

Binary file not shown.

View File

@@ -51,22 +51,22 @@ fun AudioSettings(navController: NavController) {
val textColor = if (isDarkTheme) Color.White else Color.Black
Text(
text = stringResource(R.string.audio).uppercase(),
text = stringResource(R.string.audio),
style = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Light,
fontWeight = FontWeight.Bold,
color = textColor.copy(alpha = 0.6f)
),
modifier = Modifier.padding(8.dp, bottom = 2.dp)
modifier = Modifier.padding(16.dp, bottom = 4.dp)
)
val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
Column(
modifier = Modifier
.clip(RoundedCornerShape(14.dp))
.clip(RoundedCornerShape(28.dp))
.fillMaxWidth()
.background(backgroundColor, RoundedCornerShape(14.dp))
.background(backgroundColor, RoundedCornerShape(28.dp))
.padding(top = 2.dp)
) {
@@ -76,11 +76,12 @@ fun AudioSettings(navController: NavController) {
controlCommandIdentifier = AACPManager.Companion.ControlCommandIdentifiers.ADAPTIVE_VOLUME_CONFIG,
independent = false
)
HorizontalDivider(
thickness = 1.5.dp,
thickness = 1.dp,
color = Color(0x40888888),
modifier = Modifier
.padding(start = 12.dp, end = 0.dp)
.padding(horizontal= 12.dp)
)
StyledToggle(
@@ -90,10 +91,10 @@ fun AudioSettings(navController: NavController) {
independent = false
)
HorizontalDivider(
thickness = 1.5.dp,
thickness = 1.dp,
color = Color(0x40888888),
modifier = Modifier
.padding(start = 12.dp, end = 0.dp)
.padding(horizontal= 12.dp)
)
StyledToggle(
@@ -103,10 +104,10 @@ fun AudioSettings(navController: NavController) {
independent = false
)
HorizontalDivider(
thickness = 1.5.dp,
thickness = 1.dp,
color = Color(0x40888888),
modifier = Modifier
.padding(start = 12.dp, end = 0.dp)
.padding(horizontal= 12.dp)
)
NavigationButton(

View File

@@ -27,7 +27,9 @@ import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.height
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -71,7 +73,6 @@ fun BatteryIndicator(
Column(
modifier = Modifier
.padding(12.dp)
.background(backgroundColor), // just for haze to work
horizontalAlignment = Alignment.CenterHorizontally
) {
@@ -85,7 +86,7 @@ fun BatteryIndicator(
color = batteryFillColor,
gapSize = 0.dp,
strokeCap = StrokeCap.Round,
strokeWidth = 2.dp,
strokeWidth = 4.dp,
trackColor = if (isDarkTheme) Color(0xFF0E0E0F) else Color(0xFFE3E3E8)
)
@@ -101,6 +102,8 @@ fun BatteryIndicator(
)
}
Spacer(modifier = Modifier.height(4.dp))
Text(
text = "$prefix $batteryPercentage%",
color = batteryTextColor,

View File

@@ -146,7 +146,7 @@ fun BatteryView(service: AirPodsService, preview: Boolean = false) {
contentDescription = stringResource(R.string.buds),
modifier = Modifier
.fillMaxWidth()
.padding(12.dp)
.padding(8.dp)
)
if (
leftCharging == rightCharging &&
@@ -202,7 +202,7 @@ fun BatteryView(service: AirPodsService, preview: Boolean = false) {
contentDescription = stringResource(R.string.case_alt),
modifier = Modifier
.fillMaxWidth()
.padding(12.dp)
.padding(8.dp)
)
if (caseLevel > 0 || case?.status != BatteryStatus.DISCONNECTED) {
BatteryIndicator(

View File

@@ -32,12 +32,8 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
@@ -57,6 +53,8 @@ import androidx.compose.ui.layout.positionInParent
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
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.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -76,19 +74,19 @@ fun CallControlSettings(hazeState: HazeState) {
val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
Text(
text = stringResource(R.string.call_controls).uppercase(),
text = stringResource(R.string.call_controls),
style = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Light,
fontWeight = FontWeight.Bold,
color = textColor.copy(alpha = 0.6f)
),
modifier = Modifier.padding(8.dp, bottom = 2.dp)
modifier = Modifier.padding(16.dp, bottom = 4.dp)
)
Column(
modifier = Modifier
.fillMaxWidth()
.background(backgroundColor, RoundedCornerShape(14.dp))
.background(backgroundColor, RoundedCornerShape(28.dp))
.padding(top = 2.dp)
) {
val service = ServiceManager.getService()!!
@@ -169,8 +167,8 @@ fun CallControlSettings(hazeState: HazeState) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 12.dp, end = 12.dp)
.height(50.dp),
.padding(horizontal = 16.dp)
.height(58.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
@@ -187,17 +185,17 @@ fun CallControlSettings(hazeState: HazeState) {
)
}
HorizontalDivider(
thickness = 1.5.dp,
thickness = 1.dp,
color = Color(0x40888888),
modifier = Modifier
.padding(start = 12.dp, end = 0.dp)
.padding(horizontal = 12.dp)
)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 12.dp, end = 12.dp)
.height(50.dp)
.padding(horizontal = 16.dp)
.height(58.dp)
.pointerInput(Unit) {
detectTapGestures { offset ->
val now = System.currentTimeMillis()
@@ -276,14 +274,21 @@ fun CallControlSettings(hazeState: HazeState) {
) {
Text(
text = singlePressAction,
fontSize = 16.sp,
color = textColor.copy(alpha = 0.8f)
style = TextStyle(
fontSize = 16.sp,
color = textColor.copy(alpha = 0.8f),
fontFamily = FontFamily(Font(R.font.sf_pro))
)
)
Icon(
Icons.Default.KeyboardArrowDown,
contentDescription = null,
modifier = Modifier.size(18.dp),
tint = textColor.copy(alpha = 0.6f)
Text(
text = "􀆏",
style = TextStyle(
fontSize = 16.sp,
color = textColor.copy(alpha = 0.6f),
fontFamily = FontFamily(Font(R.font.sf_pro))
),
modifier = Modifier
.padding(start = 6.dp)
)
}
@@ -315,17 +320,17 @@ fun CallControlSettings(hazeState: HazeState) {
}
}
HorizontalDivider(
thickness = 1.5.dp,
thickness = 1.dp,
color = Color(0x40888888),
modifier = Modifier
.padding(start = 12.dp, end = 0.dp)
.padding(horizontal = 12.dp)
)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 12.dp, end = 12.dp)
.height(50.dp)
.padding(horizontal = 16.dp)
.height(58.dp)
.pointerInput(Unit) {
detectTapGestures { offset ->
val now = System.currentTimeMillis()
@@ -404,14 +409,21 @@ fun CallControlSettings(hazeState: HazeState) {
) {
Text(
text = doublePressAction,
fontSize = 16.sp,
color = textColor.copy(alpha = 0.8f)
style = TextStyle(
fontSize = 16.sp,
color = textColor.copy(alpha = 0.8f),
fontFamily = FontFamily(Font(R.font.sf_pro))
)
)
Icon(
Icons.Default.KeyboardArrowDown,
contentDescription = null,
modifier = Modifier.size(18.dp),
tint = textColor.copy(alpha = 0.6f)
Text(
text = "􀆏",
style = TextStyle(
fontSize = 16.sp,
color = textColor.copy(alpha = 0.6f),
fontFamily = FontFamily(Font(R.font.sf_pro))
),
modifier = Modifier
.padding(start = 6.dp)
)
}

View File

@@ -74,7 +74,6 @@ import com.kyant.backdrop.effects.blur
import com.kyant.backdrop.effects.colorControls
import com.kyant.backdrop.effects.refraction
import com.kyant.backdrop.highlight.Highlight
import com.kyant.backdrop.highlight.HighlightStyle
import kotlinx.coroutines.launch
import me.kavishdevar.librepods.R
import me.kavishdevar.librepods.utils.inspectDragGestures
@@ -143,7 +142,9 @@ half4 main(float2 coord) {
.drawBackdrop(
backdrop,
{ RoundedCornerShape(48f.dp) },
highlight = { Highlight { HighlightStyle.Solid } },
highlight = {
Highlight.SolidDefault
},
onDrawSurface = { drawRect(containerColor) },
effects = {
colorControls(
@@ -153,7 +154,7 @@ half4 main(float2 coord) {
blur(if (isLightTheme) 16f.dp.toPx() else 8f.dp.toPx())
refraction(24f.dp.toPx(), 48f.dp.toPx(), true)
},
layer = {
layerBlock = {
val width = size.width
val height = size.height
@@ -273,16 +274,6 @@ half4 main(float2 coord) {
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
// Box(
// Modifier
// .clip(RoundedCornerShape(50.dp))
// .background(containerColor.copy(0.2f))
// .clickable(onClick = onDismiss)
// .height(48.dp)
// .weight(1f)
// .padding(horizontal = 16.dp),
// contentAlignment = Alignment.Center
// ) {
StyledButton(
onClick = onDismiss,
backdrop = backdrop,
@@ -295,16 +286,6 @@ half4 main(float2 coord) {
style = TextStyle(contentColor, 16.sp)
)
}
// Box(
// Modifier
// .clip(RoundedCornerShape(50.dp))
// .background(accentColor)
// .clickable(onClick = onConfirm)
// .height(48.dp)
// .weight(1f)
// .padding(horizontal = 16.dp),
// contentAlignment = Alignment.Center
// ) {
StyledButton(
onClick = onConfirm,
backdrop = backdrop,

View File

@@ -20,6 +20,7 @@
package me.kavishdevar.librepods.composables
import android.content.Context.MODE_PRIVATE
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Column
@@ -34,11 +35,9 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import kotlin.io.encoding.ExperimentalEncodingApi
import android.content.Context.MODE_PRIVATE
import me.kavishdevar.librepods.composables.StyledToggle
import me.kavishdevar.librepods.utils.AACPManager
import me.kavishdevar.librepods.R
import me.kavishdevar.librepods.utils.AACPManager
import kotlin.io.encoding.ExperimentalEncodingApi
@Composable
fun ConnectionSettings() {
@@ -48,7 +47,7 @@ fun ConnectionSettings() {
Column(
modifier = Modifier
.fillMaxWidth()
.background(backgroundColor, RoundedCornerShape(14.dp))
.background(backgroundColor, RoundedCornerShape(28.dp))
.padding(top = 2.dp)
) {
StyledToggle(
@@ -59,10 +58,10 @@ fun ConnectionSettings() {
independent = false
)
HorizontalDivider(
thickness = 1.5.dp,
thickness = 1.dp,
color = Color(0x40888888),
modifier = Modifier
.padding(start = 12.dp, end = 0.dp)
.padding(horizontal= 12.dp)
)
StyledToggle(

View File

@@ -32,11 +32,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
@@ -55,6 +51,9 @@ import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInParent
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@@ -75,7 +74,7 @@ fun MicrophoneSettings(hazeState: HazeState) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(backgroundColor, RoundedCornerShape(14.dp))
.background(backgroundColor, RoundedCornerShape(28.dp))
.padding(top = 2.dp)
) {
val service = ServiceManager.getService()!!
@@ -141,8 +140,8 @@ fun MicrophoneSettings(hazeState: HazeState) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 12.dp, end = 12.dp)
.height(55.dp)
.padding(horizontal = 16.dp)
.height(58.dp)
.pointerInput(Unit) {
detectTapGestures { offset ->
val now = System.currentTimeMillis()
@@ -214,8 +213,11 @@ fun MicrophoneSettings(hazeState: HazeState) {
) {
Text(
text = stringResource(R.string.microphone_mode),
fontSize = 16.sp,
color = textColor,
style = TextStyle(
fontSize = 16.sp,
color = textColor,
fontFamily = FontFamily(Font(R.font.sf_pro))
),
modifier = Modifier.padding(bottom = 4.dp)
)
Box(
@@ -228,14 +230,21 @@ fun MicrophoneSettings(hazeState: HazeState) {
) {
Text(
text = selectedMode,
fontSize = 16.sp,
color = textColor.copy(alpha = 0.8f)
style = TextStyle(
fontSize = 16.sp,
color = textColor.copy(alpha = 0.8f),
fontFamily = FontFamily(Font(R.font.sf_pro))
)
)
Icon(
Icons.Default.KeyboardArrowDown,
contentDescription = null,
modifier = Modifier.size(16.dp),
tint = textColor.copy(alpha = 0.6f)
Text(
text = "􀆏",
style = TextStyle(
fontSize = 16.sp,
color = textColor.copy(alpha = 0.6f),
fontFamily = FontFamily(Font(R.font.sf_pro))
),
modifier = Modifier
.padding(start = 6.dp)
)
}

View File

@@ -1,154 +0,0 @@
/*
* LibrePods - AirPods liberated from Apples ecosystem
*
* Copyright (C) 2025 LibrePods contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package me.kavishdevar.librepods.composables
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
import androidx.compose.material3.Icon
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.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
@Composable
fun NameField(
name: String,
value: String,
navController: NavController
) {
var isFocused by remember { mutableStateOf(false) }
val isDarkTheme = isSystemInDarkTheme()
var backgroundColor by remember { mutableStateOf(if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)) }
val animatedBackgroundColor by animateColorAsState(targetValue = backgroundColor, animationSpec = tween(durationMillis = 500))
val textColor = if (isDarkTheme) Color.White else Color.Black
val cursorColor = if (isFocused) {
if (isDarkTheme) Color.White else Color.Black
} else {
Color.Transparent
}
Box (
modifier = Modifier
.background(animatedBackgroundColor, RoundedCornerShape(14.dp))
.pointerInput(Unit) {
detectTapGestures(
onPress = {
backgroundColor = if (isDarkTheme) Color(0x40888888) else Color(0x40D9D9D9)
tryAwaitRelease()
backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
},
onTap = {
navController.navigate("rename")
}
)
}
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.height(55.dp)
.background(
animatedBackgroundColor,
RoundedCornerShape(14.dp)
)
.padding(horizontal = 16.dp, vertical = 8.dp)
) {
Text(
text = name,
style = TextStyle(
fontSize = 16.sp,
color = textColor
)
)
BasicTextField(
value = value,
textStyle = TextStyle(
color = textColor.copy(alpha = 0.75f),
fontSize = 16.sp,
textAlign = TextAlign.End
),
onValueChange = {},
singleLine = true,
enabled = false,
cursorBrush = SolidColor(cursorColor),
modifier = Modifier
.fillMaxWidth()
.padding(start = 8.dp)
.onFocusChanged { focusState ->
isFocused = focusState.isFocused
},
decorationBox = { innerTextField ->
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
) {
innerTextField()
Icon(
imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight,
contentDescription = "Edit name",
tint = textColor.copy(alpha = 0.75f),
modifier = Modifier
.size(32.dp)
)
}
}
)
}
}
}
@Preview
@Composable
fun StyledTextFieldPreview() {
NameField(name = "Name", value = "AirPods Pro", rememberNavController())
}

View File

@@ -1,17 +1,17 @@
/*
* LibrePods - AirPods liberated from Apples ecosystem
*
*
* Copyright (C) 2025 LibrePods contributors
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
@@ -23,75 +23,105 @@ import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.KeyboardArrowRight
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
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.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
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.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import me.kavishdevar.librepods.R
@Composable
fun NavigationButton(to: String, name: String, navController: NavController, onClick: (() -> Unit)? = null, independent: Boolean = true) {
fun NavigationButton(
to: String,
name: String,
navController: NavController, onClick: (() -> Unit)? = null,
independent: Boolean = true,
description: String? = null,
currentState: String? = null
) {
val isDarkTheme = isSystemInDarkTheme()
var backgroundColor by remember { mutableStateOf(if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)) }
val animatedBackgroundColor by animateColorAsState(targetValue = backgroundColor, animationSpec = tween(durationMillis = 500))
Row(
modifier = Modifier
.background(animatedBackgroundColor, RoundedCornerShape(if (independent) 14.dp else 0.dp))
.height(55.dp)
.pointerInput(Unit) {
detectTapGestures(
onPress = {
backgroundColor = if (isDarkTheme) Color(0x40888888) else Color(0x40D9D9D9)
tryAwaitRelease()
backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
},
onTap = {
if (onClick != null) onClick() else navController.navigate(to)
}
Column {
Row(
modifier = Modifier
.background(animatedBackgroundColor, RoundedCornerShape(if (independent) 28.dp else 0.dp))
.height(58.dp)
.pointerInput(Unit) {
detectTapGestures(
onPress = {
backgroundColor = if (isDarkTheme) Color(0x40888888) else Color(0x40D9D9D9)
tryAwaitRelease()
backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
},
onTap = {
if (onClick != null) onClick() else navController.navigate(to)
}
)
}
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = name,
style = TextStyle(
fontSize = 16.sp,
fontFamily = FontFamily(Font(R.font.sf_pro)),
color = if (isDarkTheme) Color.White else Color.Black,
)
)
Spacer(modifier = Modifier.weight(1f))
if (currentState != null) {
Text(
text = currentState,
style = TextStyle(
fontSize = 16.sp,
fontFamily = FontFamily(Font(R.font.sf_pro)),
color = if (isDarkTheme) Color.White.copy(alpha = 0.6f) else Color.Black.copy(alpha = 0.8f),
)
)
}
) {
Text(
text = name,
modifier = Modifier.padding(16.dp),
color = if (isDarkTheme) Color.White else Color.Black
)
Spacer(modifier = Modifier.weight(1f))
IconButton(
onClick = { if (onClick != null) onClick() else navController.navigate(to) },
colors = IconButtonDefaults.iconButtonColors(
containerColor = Color.Transparent,
contentColor = if (isDarkTheme) Color.White else Color.Black
),
modifier = Modifier
.padding(start = 16.dp)
.fillMaxHeight()
) {
@Suppress("DEPRECATION")
Icon(
imageVector = Icons.Default.KeyboardArrowRight,
contentDescription = name
Text(
text = "􀯻",
style = TextStyle(
fontSize = 16.sp,
fontFamily = FontFamily(Font(R.font.sf_pro)),
color = if (isDarkTheme) Color.White.copy(alpha = 0.6f) else Color.Black.copy(alpha = 0.6f)
),
modifier = Modifier
.padding(start = if (currentState != null) 6.dp else 0.dp)
)
}
if (description != null) {
Text(
text = description,
style = TextStyle(
fontSize = 12.sp,
fontWeight = FontWeight.Light,
color = if (isDarkTheme) Color.White.copy(alpha = 0.6f) else Color.Black.copy(alpha = 0.6f),
fontFamily = FontFamily(Font(R.font.sf_pro))
),
modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp)
)
}
}
@@ -101,4 +131,4 @@ fun NavigationButton(to: String, name: String, navController: NavController, onC
@Composable
fun NavigationButtonPreview() {
NavigationButton("to", "Name", NavController(LocalContext.current))
}
}

View File

@@ -181,10 +181,10 @@ fun NoiseControlSettings(
}
Text(
text = stringResource(R.string.noise_control).uppercase(),
text = stringResource(R.string.noise_control),
style = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Light,
fontWeight = FontWeight.Bold,
color = textColor.copy(alpha = 0.6f),
),
modifier = Modifier.padding(8.dp, bottom = 2.dp)
@@ -241,7 +241,7 @@ fun NoiseControlSettings(
modifier = Modifier
.fillMaxWidth()
.height(60.dp)
.background(backgroundColor, RoundedCornerShape(14.dp))
.background(backgroundColor, RoundedCornerShape(28.dp))
) {
Row(
modifier = Modifier.fillMaxWidth()
@@ -334,7 +334,7 @@ fun NoiseControlSettings(
modifier = Modifier
.fillMaxSize()
.padding(3.dp)
.background(selectedBackground, RoundedCornerShape(12.dp))
.background(selectedBackground, RoundedCornerShape(26.dp))
)
}
@@ -400,7 +400,6 @@ fun NoiseControlSettings(
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 4.dp)
.padding(top = 4.dp)
) {
if (offListeningMode.value) {
@@ -408,7 +407,6 @@ fun NoiseControlSettings(
text = stringResource(R.string.off),
style = TextStyle(fontSize = 12.sp, color = textColor),
textAlign = TextAlign.Center,
fontWeight = FontWeight.Bold,
modifier = Modifier.weight(1f)
)
}
@@ -416,21 +414,18 @@ fun NoiseControlSettings(
text = stringResource(R.string.transparency),
style = TextStyle(fontSize = 12.sp, color = textColor),
textAlign = TextAlign.Center,
fontWeight = FontWeight.Bold,
modifier = Modifier.weight(1f)
)
Text(
text = stringResource(R.string.adaptive),
style = TextStyle(fontSize = 12.sp, color = textColor),
textAlign = TextAlign.Center,
fontWeight = FontWeight.Bold,
modifier = Modifier.weight(1f)
)
Text(
text = stringResource(R.string.noise_cancellation),
style = TextStyle(fontSize = 12.sp, color = textColor),
textAlign = TextAlign.Center,
fontWeight = FontWeight.Bold,
modifier = Modifier.weight(1f)
)
}

View File

@@ -19,34 +19,21 @@
package me.kavishdevar.librepods.composables
import android.content.Context
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.tween
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
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.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
@@ -65,12 +52,6 @@ fun PressAndHoldSettings(navController: NavController) {
val isDarkTheme = isSystemInDarkTheme()
val textColor = if (isDarkTheme) Color.White else Color.Black
val dividerColor = Color(0x40888888)
var leftBackgroundColor by remember { mutableStateOf(if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)) }
var rightBackgroundColor by remember { mutableStateOf(if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)) }
val animationSpec = tween<Color>(durationMillis = 500)
val animatedLeftBackgroundColor by animateColorAsState(targetValue = leftBackgroundColor, animationSpec = animationSpec)
val animatedRightBackgroundColor by animateColorAsState(targetValue = rightBackgroundColor, animationSpec = animationSpec)
val context = LocalContext.current
val sharedPreferences = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
@@ -91,14 +72,14 @@ fun PressAndHoldSettings(navController: NavController) {
}
Text(
text = stringResource(R.string.press_and_hold_airpods).uppercase(),
text = stringResource(R.string.press_and_hold_airpods),
style = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Light,
fontWeight = FontWeight.Bold,
color = textColor.copy(alpha = 0.6f),
fontFamily = FontFamily(Font(R.font.sf_pro))
),
modifier = Modifier.padding(8.dp, bottom = 2.dp)
modifier = Modifier.padding(16.dp, bottom = 4.dp)
)
Spacer(modifier = Modifier.height(1.dp))
@@ -106,126 +87,33 @@ fun PressAndHoldSettings(navController: NavController) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF), RoundedCornerShape(14.dp))
.background(if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF), RoundedCornerShape(28.dp))
.clip(RoundedCornerShape(28.dp))
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(50.dp)
.background(animatedLeftBackgroundColor, RoundedCornerShape(topStart = 14.dp, topEnd = 14.dp))
.pointerInput(Unit) {
detectTapGestures(
onPress = {
leftBackgroundColor = dividerColor
tryAwaitRelease()
leftBackgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
},
onTap = {
navController.navigate("long_press/Left")
}
)
},
contentAlignment = Alignment.Center
) {
Row(
modifier = Modifier
.padding(start = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(R.string.left),
style = TextStyle(
fontSize = 16.sp,
color = textColor,
fontFamily = FontFamily(Font(R.font.sf_pro))
),
)
Spacer(modifier = Modifier.weight(1f))
Text(
text = leftActionText,
style = TextStyle(
fontSize = 16.sp,
color = textColor.copy(alpha = 0.6f),
fontFamily = FontFamily(Font(R.font.sf_pro))
),
)
IconButton(
onClick = {
navController.navigate("long_press/Left")
}
) {
Icon(
imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight,
contentDescription = "go",
tint = textColor
)
}
}
}
NavigationButton(
to = "long_press/Left",
name = stringResource(R.string.left),
navController = navController,
independent = false,
currentState = leftActionText,
)
HorizontalDivider(
thickness = 1.5.dp,
thickness = 1.dp,
color = dividerColor,
modifier = Modifier
.padding(start = 16.dp)
.padding(horizontal = 16.dp)
)
NavigationButton(
to = "long_press/Right",
name = stringResource(R.string.right),
navController = navController,
independent = false,
currentState = rightActionText,
)
Box(
modifier = Modifier
.fillMaxWidth()
.height(50.dp)
.background(animatedRightBackgroundColor, RoundedCornerShape(bottomEnd = 14.dp, bottomStart = 14.dp))
.pointerInput(Unit) {
detectTapGestures(
onPress = {
rightBackgroundColor = dividerColor
tryAwaitRelease()
rightBackgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
},
onTap = {
navController.navigate("long_press/Right")
}
)
},
contentAlignment = Alignment.Center
) {
Row(
modifier = Modifier
.padding(start = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(R.string.right),
style = TextStyle(
fontSize = 16.sp,
color = textColor,
fontFamily = FontFamily(Font(R.font.sf_pro))
),
)
Spacer(modifier = Modifier.weight(1f))
Text(
text = rightActionText,
style = TextStyle(
fontSize = 16.sp,
color = textColor.copy(alpha = 0.6f),
fontFamily = FontFamily(Font(R.font.sf_pro))
),
)
IconButton(
onClick = {
navController.navigate("long_press/Right")
}
) {
Icon(
imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight,
contentDescription = "go",
tint = textColor
)
}
}
}
}
}
@Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun PressAndHoldSettingsPreview() {
PressAndHoldSettings(navController = NavController(LocalContext.current))

View File

@@ -74,7 +74,8 @@ fun StyledButton(
isInteractive: Boolean = true,
tint: Color = Color.Unspecified,
surfaceColor: Color = Color.Unspecified,
content: @Composable RowScope.() -> Unit
maxScale: Float = 0.1f,
content: @Composable RowScope.() -> Unit,
) {
val animationScope = rememberCoroutineScope()
val progressAnimation = remember { Animatable(0f) }
@@ -113,7 +114,7 @@ half4 main(float2 coord) {
effects = {
blur(16f.dp.toPx())
},
layer = null,
layerBlock = null,
onDrawSurface = {
if (tint.isSpecified) {
drawRect(tint, blendMode = BlendMode.Hue)
@@ -147,12 +148,11 @@ half4 main(float2 coord) {
blur(2f.dp.toPx())
refraction(12f.dp.toPx(), 24f.dp.toPx())
},
layer = {
layerBlock = {
val width = size.width
val height = size.height
val progress = progressAnimation.value
val maxScale = 0.1f
val scale = lerp(1f, 1f + maxScale, progress)
val maxOffset = size.minDimension

View File

@@ -230,7 +230,7 @@ fun StyledDropdown(
if (index != options.lastIndex) {
HorizontalDivider(
thickness = 1.5.dp,
thickness = 1.dp,
color = Color(0x40888888),
modifier = Modifier.padding(start = 12.dp, end = 0.dp)
)

View File

@@ -24,6 +24,7 @@ import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.VectorConverter
import androidx.compose.animation.core.VisibilityThreshold
import androidx.compose.animation.core.spring
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -59,8 +60,10 @@ import androidx.compose.ui.unit.sp
import androidx.compose.ui.util.fastCoerceAtMost
import androidx.compose.ui.util.fastCoerceIn
import androidx.compose.ui.util.lerp
import com.kyant.backdrop.backdrops.LayerBackdrop
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
import com.kyant.backdrop.drawBackdrop
import com.kyant.backdrop.effects.blur
import com.kyant.backdrop.effects.refractionWithDispersion
import com.kyant.backdrop.highlight.Highlight
import com.kyant.backdrop.shadow.Shadow
@@ -79,6 +82,8 @@ fun StyledIconButton(
icon: String,
darkMode: Boolean,
tint: Color = Color.Unspecified,
backdrop: LayerBackdrop = rememberLayerBackdrop(),
modifier: Modifier = Modifier,
) {
val animationScope = rememberCoroutineScope()
val progressAnimationSpec = spring(0.5f, 300f, 0.001f)
@@ -110,26 +115,23 @@ half4 main(float2 coord) {
null
}
}
val isDarkTheme = isSystemInDarkTheme()
TextButton(
onClick = onClick,
shape = RoundedCornerShape(56.dp),
modifier = Modifier
modifier = modifier
.padding(horizontal = 12.dp)
.drawBackdrop(
backdrop = rememberLayerBackdrop(),
backdrop = backdrop,
shape = { RoundedCornerShape(56.dp) },
highlight = {
val progress = progressAnimation.value
Highlight.AmbientDefault.copy(alpha = progress.coerceIn(0.45f, 1f))
},
highlight = { Highlight.AmbientDefault.copy(alpha = if (isDarkTheme) 1f else 0f) },
shadow = {
Shadow(
radius = 4f.dp,
color = Color.Black.copy(0.08f)
radius = 48f.dp,
color = Color.Black.copy(if (isDarkTheme) 0.08f else 0.4f)
)
},
layer = {
layerBlock = {
val width = size.width
val height = size.height
@@ -182,7 +184,7 @@ half4 main(float2 coord) {
drawLayer(innerShadowLayer)
drawRect(
Color.White.copy(progress.coerceIn(0.15f, 0.35f))
(if (isDarkTheme) Color(0xFFAFAFAF) else Color.White).copy(progress.coerceIn(0.15f, 0.35f))
)
},
onDrawFront = {
@@ -218,6 +220,7 @@ half4 main(float2 coord) {
},
effects = {
refractionWithDispersion(6f.dp.toPx(), size.height / 2f)
blur(24f, TileMode.Decal)
},
)
.pointerInput(animationScope) {

View File

@@ -31,12 +31,14 @@ 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.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -84,6 +86,7 @@ import com.kyant.backdrop.highlight.Highlight
import com.kyant.backdrop.shadow.Shadow
import kotlinx.coroutines.launch
import me.kavishdevar.librepods.R
import kotlin.math.abs
import kotlin.math.roundToInt
@Composable
@@ -136,23 +139,26 @@ fun StyledSlider(
val content = @Composable {
Box(
Modifier.fillMaxWidth(if (startIcon == null && endIcon == null) 0.95f else 1f)
) {
Modifier
.fillMaxWidth(if (startIcon == null && endIcon == null) 0.95f else 1f)
) {
Box(
Modifier
.padding(vertical = 4.dp)
.layerBackdrop(sliderBackdrop)
.fillMaxWidth()) {
.fillMaxWidth()
) {
Column(
modifier = Modifier
.fillMaxWidth(1f)
.padding(vertical = 12.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
if (startLabel != null || endLabel != null) {
Row(
modifier = Modifier
.fillMaxWidth(),
.fillMaxWidth()
.padding(horizontal = 8.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
@@ -174,80 +180,119 @@ fun StyledSlider(
)
)
}
Spacer(modifier = Modifier.height(12.dp))
}
Row(
Column(
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)
.then(if (startIcon == null && endIcon == null) Modifier.padding(horizontal = 8.dp) else Modifier),
) {
if (startIcon != null) {
Text(
text = startIcon,
style = TextStyle(
fontSize = 18.sp,
fontWeight = FontWeight.Normal,
color = accentColor,
fontFamily = FontFamily(Font(R.font.sf_pro))
),
modifier = Modifier
.padding(horizontal = 12.dp)
.onGloballyPositioned {
startIconWidthState.floatValue = it.size.width.toFloat()
}
)
}
Box(
Modifier
.weight(1f)
.onSizeChanged { trackWidthState.floatValue = it.width.toFloat() }
.onGloballyPositioned {
trackPositionState.floatValue =
it.positionInParent().y + it.size.height / 2f
}
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(0.dp)
) {
Box(
Modifier
.clip(RoundedCornerShape(28.dp))
.background(trackColor)
.height(6f.dp)
.fillMaxWidth()
)
Box(
Modifier
.clip(RoundedCornerShape(28.dp))
.background(accentColor)
.height(6f.dp)
.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
val fraction = fraction
val width =
(fraction * constraints.maxWidth).fastRoundToInt()
layout(width, placeable.height) {
placeable.place(0, 0)
if (startIcon != null) {
Text(
text = startIcon,
style = TextStyle(
fontSize = 18.sp,
fontWeight = FontWeight.Normal,
color = accentColor,
fontFamily = FontFamily(Font(R.font.sf_pro))
),
modifier = Modifier
.padding(horizontal = 12.dp)
.onGloballyPositioned {
startIconWidthState.floatValue = it.size.width.toFloat()
}
}
)
}
if (endIcon != null) {
Text(
text = endIcon,
style = TextStyle(
fontSize = 18.sp,
fontWeight = FontWeight.Normal,
color = accentColor,
fontFamily = FontFamily(Font(R.font.sf_pro))
),
modifier = Modifier
.padding(horizontal = 12.dp)
)
}
Box(
Modifier
.weight(1f)
.onSizeChanged { trackWidthState.floatValue = it.width.toFloat() }
.onGloballyPositioned {
endIconWidthState.floatValue = it.size.width.toFloat()
trackPositionState.floatValue =
it.positionInParent().y + it.size.height / 2f
}
)
) {
Box(
Modifier
.clip(RoundedCornerShape(28.dp))
.background(trackColor)
.height(6f.dp)
.fillMaxWidth()
)
Box(
Modifier
.clip(RoundedCornerShape(28.dp))
.background(accentColor)
.height(6f.dp)
.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
val fraction = fraction
val width =
(fraction * constraints.maxWidth).fastRoundToInt()
layout(width, placeable.height) {
placeable.place(0, 0)
}
}
)
}
if (endIcon != null) {
Text(
text = endIcon,
style = TextStyle(
fontSize = 18.sp,
fontWeight = FontWeight.Normal,
color = accentColor,
fontFamily = FontFamily(Font(R.font.sf_pro))
),
modifier = Modifier
.padding(horizontal = 12.dp)
.onGloballyPositioned {
endIconWidthState.floatValue = it.size.width.toFloat()
}
)
}
}
if (snapPoints.isNotEmpty() && startLabel != null && endLabel != null) Spacer(modifier = Modifier.height(4.dp))
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
if (snapPoints.isNotEmpty()) {
val trackWidth = if (startIcon != null && endIcon != null) trackWidthState.floatValue - with(density) { 6.dp.toPx() } * 2 else trackWidthState.floatValue- with(density) { 22.dp.toPx() }
val startOffset =
if (startIcon != null) startIconWidthState.floatValue + with(
density
) { 34.dp.toPx() } else with(density) { 14.dp.toPx() }
Box(
Modifier
.fillMaxWidth()
) {
snapPoints.forEach { point ->
val pointFraction =
((point - valueRange.start) / (valueRange.endInclusive - valueRange.start))
.fastCoerceIn(0f, 1f)
Box(
Modifier
.graphicsLayer {
translationX =
startOffset + pointFraction * trackWidth - 4.dp.toPx()
}
.size(2.dp)
.background(
trackColor,
CircleShape
)
)
}
}
}
}
}
}
@@ -257,10 +302,10 @@ fun StyledSlider(
Modifier
.graphicsLayer {
val startOffset =
if (startIcon != null) startIconWidthState.floatValue + with(density) { 24.dp.toPx() } else with(density) { 8.dp.toPx() }
if (startIcon != null) startIconWidthState.floatValue + with(density) { 24.dp.toPx() } else with(density) { 12.dp.toPx() }
translationX =
startOffset + fraction * trackWidthState.floatValue - size.width / 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() }
translationY = if (startLabel != null || endLabel != null) trackPositionState.floatValue + with(density) { 26.dp.toPx() } + size.height / 2f else trackPositionState.floatValue + with(density) { 8.dp.toPx() }
}
.draggable(
rememberDraggableState { delta ->
@@ -305,7 +350,7 @@ fun StyledSlider(
color = Color.Black.copy(0.05f)
)
},
layer = {
layerBlock = {
val progress = progressAnimation.value
val scale = lerp(1f, 1.5f, progress)
scaleX = scale
@@ -361,20 +406,20 @@ fun StyledSlider(
text = label,
style = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Light,
fontWeight = FontWeight.Bold,
color = labelTextColor.copy(alpha = 0.6f),
fontFamily = FontFamily(Font(R.font.sf_pro))
),
modifier = Modifier.padding(8.dp)
modifier = Modifier.padding(horizontal = 18.dp, vertical = 4.dp)
)
}
Box(
modifier = Modifier
.fillMaxWidth()
.background(backgroundColor, RoundedCornerShape(14.dp))
.background(backgroundColor, RoundedCornerShape(28.dp))
.padding(horizontal = 8.dp, vertical = 0.dp)
.heightIn(min = 55.dp),
.heightIn(min = 58.dp),
contentAlignment = Alignment.Center
) {
content()
@@ -390,7 +435,7 @@ fun StyledSlider(
fontFamily = FontFamily(Font(R.font.sf_pro))
),
modifier = Modifier
.padding(horizontal = 12.dp, vertical = 4.dp)
.padding(horizontal = 18.dp, vertical = 4.dp)
)
}
}
@@ -402,8 +447,8 @@ fun StyledSlider(
}
private fun snapIfClose(value: Float, points: List<Float>, threshold: Float = 0.05f): Float {
val nearest = points.minByOrNull { kotlin.math.abs(it - value) } ?: value
return if (kotlin.math.abs(nearest - value) <= threshold) nearest else value
val nearest = points.minByOrNull { abs(it - value) } ?: value
return if (abs(nearest - value) <= threshold) nearest else value
}
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@@ -426,9 +471,11 @@ fun StyledSliderPreview() {
a.floatValue = it
},
valueRange = 0f..2f,
snapPoints = listOf(0f, 0.5f, 1f, 1.5f, 2f),
snapThreshold = 0.1f,
independent = true,
startLabel = "A",
endLabel = "B"
endLabel = "B",
)
}
}

View File

@@ -48,8 +48,11 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.BlurEffect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.TileMode
import androidx.compose.ui.graphics.drawOutline
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.graphics.drawscope.scale
import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.layer.CompositingStrategy
@@ -68,7 +71,7 @@ import com.kyant.backdrop.drawBackdrop
import com.kyant.backdrop.effects.refractionWithDispersion
import com.kyant.backdrop.highlight.Highlight
import com.kyant.backdrop.shadow.Shadow
import kotlinx.coroutines.delay
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
@Composable
@@ -76,16 +79,17 @@ fun StyledSwitch(
checked: Boolean,
onCheckedChange: (Boolean) -> Unit,
enabled: Boolean = true,
indpendent: Boolean = true,
) {
val isDarkTheme = isSystemInDarkTheme()
val onColor = if (enabled) Color(0xFF34C759) else if (isDarkTheme) Color(0xFF5B5B5E) else Color(0xFFD1D1D6)
val offColor = if (enabled) if (isDarkTheme) Color(0xFF5B5B5E) else Color(0xFFD1D1D6) else if (isDarkTheme) Color(0xFF5B5B5E) else Color(0xFFD1D1D6)
val trackWidth = 70.dp
val trackHeight = 31.dp
val thumbHeight = 27.dp
val thumbWidth = 36.dp
val trackWidth = 64.dp
val trackHeight = 28.dp
val thumbHeight = 24.dp
val thumbWidth = 39.dp
val backdrop = rememberLayerBackdrop()
val switchBackdrop = rememberLayerBackdrop()
@@ -97,18 +101,23 @@ fun StyledSwitch(
val density = LocalDensity.current
val animationScope = rememberCoroutineScope()
val progressAnimationSpec = spring(0.5f, 300f, 0.001f)
val colorAnimationSpec = tween<Color>(300, easing = FastOutSlowInEasing)
val colorAnimationSpec = tween<Color>(200, easing = FastOutSlowInEasing)
val progressAnimation = remember { Animatable(0f) }
val innerShadowLayer = rememberGraphicsLayer().apply {
compositingStrategy = CompositingStrategy.Offscreen
}
val animatedTrackColor = remember { Animatable(if (checked) onColor else offColor) }
LaunchedEffect(checked) {
val targetColor = if (checked) onColor else offColor
animatedTrackColor.animateTo(targetColor, colorAnimationSpec)
val targetFrac = if (checked) 1f else 0f
animatedFraction.animateTo(targetFrac, progressAnimationSpec)
coroutineScope {
launch {
val targetColor = if (checked) onColor else offColor
animatedTrackColor.animateTo(targetColor, colorAnimationSpec)
}
launch {
val targetFrac = if (checked) 1f else 0f
animatedFraction.animateTo(targetFrac, progressAnimationSpec)
}
}
}
Box(
@@ -136,7 +145,7 @@ fun StyledSwitch(
.then(if (enabled) Modifier.draggable(
rememberDraggableState { delta ->
if (trackWidthPx.floatValue > 0f) {
val newFraction = (animatedFraction.value + delta / trackWidthPx.floatValue).fastCoerceIn(0f, 1f)
val newFraction = (animatedFraction.value + delta / trackWidthPx.floatValue).fastCoerceIn(-0.3f, 1.3f)
animationScope.launch {
animatedFraction.snapTo(newFraction)
}
@@ -155,10 +164,12 @@ fun StyledSwitch(
},
onDragStopped = {
animationScope.launch {
progressAnimation.animateTo(0f, progressAnimationSpec)
val snappedFraction = if (animatedFraction.value >= 0.5f) 1f else 0f
animatedFraction.animateTo(snappedFraction, progressAnimationSpec)
onCheckedChange(snappedFraction >= 0.5f)
coroutineScope {
launch { progressAnimation.animateTo(0f, progressAnimationSpec) }
launch { animatedFraction.animateTo(snappedFraction, progressAnimationSpec) }
}
}
}
) else Modifier)
@@ -175,12 +186,27 @@ fun StyledSwitch(
color = Color.Black.copy(0.05f)
)
},
layer = {
layerBlock = {
val progress = progressAnimation.value
val scale = lerp(1f, 2f, progress)
val scale = lerp(1f, 1.6f, progress)
scaleX = scale
scaleY = scale
},
onDrawBackdrop = { drawScope ->
drawIntoCanvas { canvas ->
canvas.save()
canvas.drawRect(0f, 0f, size.width, size.height, Paint().apply {
color = if (indpendent) {
if (isDarkTheme) Color(0xFF000000) else Color(0xFFF2F2F7)
} else {
if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
}
})
scale(0.75f) {
drawScope()
}
}
},
onDrawSurface = {
val progress = progressAnimation.value.fastCoerceIn(0f, 1f)
@@ -224,12 +250,12 @@ fun StyledSwitch(
@Composable
fun StyledSwitchPreview() {
val isDarkTheme = isSystemInDarkTheme()
val backgroundColor = if (isDarkTheme) Color(0xFF000000) else Color(0xFFF2F2F7)
val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFF2F2F7)
Box(
modifier = Modifier
.background(backgroundColor)
.width(100.dp)
.height(100.dp),
.height(400.dp),
contentAlignment = Alignment.Center
) {
val checked = remember { mutableStateOf(true) }
@@ -238,13 +264,14 @@ fun StyledSwitchPreview() {
onCheckedChange = {
checked.value = it
},
enabled = true
enabled = true,
indpendent = false
)
LaunchedEffect(Unit) {
delay(1000)
checked.value = false
delay(1000)
checked.value = true
}
// LaunchedEffect(Unit) {
// delay(1000)
// checked.value = false
// delay(1000)
// checked.value = true
// }
}
}

View File

@@ -75,6 +75,7 @@ fun StyledToggle(
sharedPreferenceKey: String? = null,
sharedPreferences: SharedPreferences? = null,
independent: Boolean = true,
enabled: Boolean = true,
onCheckedChange: ((Boolean) -> Unit)? = null,
) {
val isDarkTheme = isSystemInDarkTheme()
@@ -82,7 +83,9 @@ fun StyledToggle(
var checked by checkedState
var backgroundColor by remember { mutableStateOf(if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)) }
val animatedBackgroundColor by animateColorAsState(targetValue = backgroundColor, animationSpec = tween(durationMillis = 500))
if (sharedPreferenceKey != null && sharedPreferences != null) {
checked = sharedPreferences.getBoolean(sharedPreferenceKey, checked)
}
fun cb() {
if (sharedPreferences != null) {
if (sharedPreferenceKey == null) {
@@ -101,15 +104,16 @@ fun StyledToggle(
text = title,
style = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Light,
fontWeight = FontWeight.Bold,
color = textColor.copy(alpha = 0.6f)
),
modifier = Modifier.padding(8.dp, bottom = 4.dp)
modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 4.dp)
)
}
Box(
modifier = Modifier
.background(animatedBackgroundColor, RoundedCornerShape(14.dp))
.background(animatedBackgroundColor, RoundedCornerShape(28.dp))
.padding(4.dp)
.pointerInput(Unit) {
detectTapGestures(
onPress = {
@@ -120,8 +124,10 @@ fun StyledToggle(
if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
},
onTap = {
checked = !checked
cb()
if (enabled) {
checked = !checked
cb()
}
}
)
}
@@ -145,10 +151,14 @@ fun StyledToggle(
)
StyledSwitch(
checked = checked,
enabled = enabled,
onCheckedChange = {
checked = it
cb()
}
if (enabled) {
checked = it
cb()
}
},
indpendent = true
)
}
}
@@ -156,7 +166,7 @@ fun StyledToggle(
Spacer(modifier = Modifier.height(8.dp))
Box(
modifier = Modifier
.padding(horizontal = 8.dp)
.padding(horizontal = 16.dp)
.background(if (isDarkTheme) Color(0xFF000000) else Color(0xFFF2F2F7))
) {
Text(
@@ -177,10 +187,10 @@ fun StyledToggle(
modifier = Modifier
.fillMaxWidth()
.background(
shape = RoundedCornerShape(14.dp),
shape = RoundedCornerShape(28.dp),
color = if (isPressed.value) Color(0xFFE0E0E0) else Color.Transparent
)
.padding(horizontal = 12.dp, vertical = 12.dp)
.padding(16.dp)
.pointerInput(Unit) {
detectTapGestures(
onPress = {
@@ -194,8 +204,10 @@ fun StyledToggle(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) {
checked = !checked
cb()
if (enabled) {
checked = !checked
cb()
}
},
verticalAlignment = Alignment.CenterVertically
) {
@@ -206,25 +218,35 @@ fun StyledToggle(
) {
Text(
text = label,
fontSize = 16.sp,
color = textColor
style = TextStyle(
fontSize = 16.sp,
fontFamily = FontFamily(Font(R.font.sf_pro)),
fontWeight = FontWeight.Normal,
color = textColor
)
)
Spacer(modifier = Modifier.height(4.dp))
if (description != null) {
Text(
text = description,
fontSize = 12.sp,
color = textColor.copy(0.6f),
lineHeight = 14.sp,
style = TextStyle(
fontSize = 12.sp,
color = textColor.copy(0.6f),
fontFamily = FontFamily(Font(R.font.sf_pro)),
)
)
}
}
StyledSwitch(
checked = checked,
enabled = enabled,
onCheckedChange = {
checked = it
cb()
}
if (enabled) {
checked = it
cb()
}
},
indpendent = false
)
}
}
@@ -237,6 +259,7 @@ fun StyledToggle(
description: String? = null,
controlCommandIdentifier: AACPManager.Companion.ControlCommandIdentifiers,
independent: Boolean = true,
enabled: Boolean = true,
sharedPreferenceKey: String? = null,
sharedPreferences: SharedPreferences? = null,
onCheckedChange: ((Boolean) -> Unit)? = null,
@@ -291,15 +314,16 @@ fun StyledToggle(
text = title,
style = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Light,
fontWeight = FontWeight.Bold,
color = textColor.copy(alpha = 0.6f)
),
modifier = Modifier.padding(8.dp, bottom = 4.dp)
modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 4.dp)
)
}
Box(
modifier = Modifier
.background(animatedBackgroundColor, RoundedCornerShape(14.dp))
.background(animatedBackgroundColor, RoundedCornerShape(28.dp))
.padding(4.dp)
.pointerInput(Unit) {
detectTapGestures(
onPress = {
@@ -310,8 +334,10 @@ fun StyledToggle(
if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
},
onTap = {
checked = !checked
cb()
if (enabled) {
checked = !checked
cb()
}
}
)
}
@@ -335,10 +361,14 @@ fun StyledToggle(
)
StyledSwitch(
checked = checked,
enabled = enabled,
onCheckedChange = {
checked = it
cb()
}
if (enabled) {
checked = it
cb()
}
},
indpendent = true
)
}
}
@@ -346,7 +376,7 @@ fun StyledToggle(
Spacer(modifier = Modifier.height(8.dp))
Box(
modifier = Modifier
.padding(horizontal = 8.dp)
.padding(horizontal = 16.dp)
.background(if (isDarkTheme) Color(0xFF000000) else Color(0xFFF2F2F7))
) {
Text(
@@ -367,10 +397,10 @@ fun StyledToggle(
modifier = Modifier
.fillMaxWidth()
.background(
shape = RoundedCornerShape(14.dp),
shape = RoundedCornerShape(28.dp),
color = if (isPressed.value) Color(0xFFE0E0E0) else Color.Transparent
)
.padding(horizontal = 12.dp, vertical = 12.dp)
.padding(16.dp)
.pointerInput(Unit) {
detectTapGestures(
onPress = {
@@ -384,8 +414,10 @@ fun StyledToggle(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) {
checked = !checked
cb()
if (enabled) {
checked = !checked
cb()
}
},
verticalAlignment = Alignment.CenterVertically
) {
@@ -396,25 +428,35 @@ fun StyledToggle(
) {
Text(
text = label,
fontSize = 16.sp,
color = textColor
style = TextStyle(
fontSize = 16.sp,
fontFamily = FontFamily(Font(R.font.sf_pro)),
fontWeight = FontWeight.Normal,
color = textColor
)
)
Spacer(modifier = Modifier.height(4.dp))
if (description != null) {
Text(
text = description,
fontSize = 12.sp,
color = textColor.copy(0.6f),
lineHeight = 14.sp,
style = TextStyle(
fontSize = 12.sp,
color = textColor.copy(0.6f),
fontFamily = FontFamily(Font(R.font.sf_pro)),
)
)
}
}
StyledSwitch(
checked = checked,
enabled = enabled,
onCheckedChange = {
checked = it
cb()
}
if (enabled) {
checked = it
cb()
}
},
indpendent = false
)
}
}
@@ -427,6 +469,7 @@ fun StyledToggle(
description: String? = null,
attHandle: ATTHandles,
independent: Boolean = true,
enabled: Boolean = true,
sharedPreferenceKey: String? = null,
sharedPreferences: SharedPreferences? = null,
onCheckedChange: ((Boolean) -> Unit)? = null,
@@ -509,15 +552,16 @@ fun StyledToggle(
text = title,
style = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Light,
fontWeight = FontWeight.Bold,
color = textColor.copy(alpha = 0.6f)
),
modifier = Modifier.padding(8.dp, bottom = 4.dp)
modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 4.dp)
)
}
Box(
modifier = Modifier
.background(animatedBackgroundColor, RoundedCornerShape(14.dp))
.background(animatedBackgroundColor, RoundedCornerShape(28.dp))
.padding(4.dp)
.pointerInput(Unit) {
detectTapGestures(
onPress = {
@@ -528,8 +572,10 @@ fun StyledToggle(
if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
},
onTap = {
checked = !checked
cb()
if (enabled) {
checked = !checked
cb()
}
}
)
}
@@ -553,10 +599,14 @@ fun StyledToggle(
)
StyledSwitch(
checked = checked,
enabled = enabled,
onCheckedChange = {
checked = it
cb()
}
if (enabled) {
checked = it
cb()
}
},
indpendent = true
)
}
}
@@ -564,7 +614,7 @@ fun StyledToggle(
Spacer(modifier = Modifier.height(8.dp))
Box(
modifier = Modifier
.padding(horizontal = 8.dp)
.padding(horizontal = 16.dp)
.background(if (isDarkTheme) Color(0xFF000000) else Color(0xFFF2F2F7))
) {
Text(
@@ -585,10 +635,10 @@ fun StyledToggle(
modifier = Modifier
.fillMaxWidth()
.background(
shape = RoundedCornerShape(14.dp),
shape = RoundedCornerShape(28.dp),
color = if (isPressed.value) Color(0xFFE0E0E0) else Color.Transparent
)
.padding(horizontal = 12.dp, vertical = 12.dp)
.padding(16.dp)
.pointerInput(Unit) {
detectTapGestures(
onPress = {
@@ -602,8 +652,10 @@ fun StyledToggle(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) {
checked = !checked
cb()
if (enabled) {
checked = !checked
cb()
}
},
verticalAlignment = Alignment.CenterVertically
) {
@@ -629,10 +681,14 @@ fun StyledToggle(
}
StyledSwitch(
checked = checked,
enabled = enabled,
onCheckedChange = {
checked = it
cb()
}
if (enabled) {
checked = it
cb()
}
},
indpendent = false
)
}
}

View File

@@ -40,20 +40,16 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material3.Checkbox
import androidx.compose.material3.CheckboxDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.Slider
import androidx.compose.material3.SliderDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableLongStateOf
@@ -79,6 +75,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.backdrops.layerBackdrop
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
import dev.chrisbanes.haze.HazeState
import dev.chrisbanes.haze.hazeSource
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
@@ -149,13 +147,16 @@ fun AccessibilitySettingsScreen(navController: NavController) {
}
}
val backdrop = rememberLayerBackdrop()
StyledScaffold(
title = stringResource(R.string.accessibility),
navigationButton = {
StyledIconButton(
onClick = { navController.popBackStack() },
icon = "􀯶",
darkMode = isDarkTheme
darkMode = isDarkTheme,
backdrop = backdrop
)
},
) { spacerHeight, hazeState ->
@@ -163,6 +164,7 @@ fun AccessibilitySettingsScreen(navController: NavController) {
modifier = Modifier
.fillMaxSize()
.hazeSource(hazeState)
.layerBackdrop(backdrop)
.verticalScroll(rememberScrollState())
.padding(horizontal = 16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
@@ -370,7 +372,7 @@ fun AccessibilitySettingsScreen(navController: NavController) {
)
StyledToggle(
title = stringResource(R.string.noise_control).uppercase(),
title = stringResource(R.string.noise_control),
label = stringResource(R.string.noise_cancellation_single_airpod),
description = stringResource(R.string.noise_cancellation_single_airpod_description),
controlCommandIdentifier = AACPManager.Companion.ControlCommandIdentifiers.ONE_BUD_ANC_MODE,
@@ -392,7 +394,7 @@ fun AccessibilitySettingsScreen(navController: NavController) {
}
StyledSlider(
label = stringResource(R.string.tone_volume).uppercase(),
label = stringResource(R.string.tone_volume),
description = stringResource(R.string.tone_volume_description),
mutableFloatState = toneVolumeValue,
onValueChange = {
@@ -405,6 +407,12 @@ fun AccessibilitySettingsScreen(navController: NavController) {
independent = true
)
StyledToggle(
label = stringResource(R.string.volume_control),
description = stringResource(R.string.volume_control_description),
controlCommandIdentifier = AACPManager.Companion.ControlCommandIdentifiers.VOLUME_SWIPE_MODE,
)
DropdownMenuComponent(
label = stringResource(R.string.volume_swipe_speed),
description = stringResource(R.string.volume_swipe_speed_description),
@@ -425,10 +433,10 @@ fun AccessibilitySettingsScreen(navController: NavController) {
if (!hearingAidEnabled.value&& isSdpOffsetAvailable.value) {
Text(
text = stringResource(R.string.apply_eq_to).uppercase(),
text = stringResource(R.string.apply_eq_to),
style = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Light,
fontWeight = FontWeight.Bold,
color = textColor.copy(alpha = 0.6f),
fontFamily = FontFamily(Font(R.font.sf_pro))
),
@@ -437,12 +445,12 @@ fun AccessibilitySettingsScreen(navController: NavController) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(backgroundColor, RoundedCornerShape(14.dp))
.background(backgroundColor, RoundedCornerShape(28.dp))
.padding(vertical = 0.dp)
) {
val darkModeLocal = isSystemInDarkTheme()
val phoneShape = RoundedCornerShape(topStart = 14.dp, topEnd = 14.dp)
val phoneShape = RoundedCornerShape(topStart = 28.dp, topEnd = 28.dp)
var phoneBackgroundColor by remember {
mutableStateOf(
if (darkModeLocal) Color(
@@ -500,11 +508,11 @@ fun AccessibilitySettingsScreen(navController: NavController) {
}
HorizontalDivider(
thickness = 1.5.dp,
thickness = 1.dp,
color = Color(0x40888888)
)
val mediaShape = RoundedCornerShape(bottomStart = 14.dp, bottomEnd = 14.dp)
val mediaShape = RoundedCornerShape(bottomStart = 28.dp, bottomEnd = 28.dp)
var mediaBackgroundColor by remember {
mutableStateOf(
if (darkModeLocal) Color(
@@ -565,7 +573,7 @@ fun AccessibilitySettingsScreen(navController: NavController) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(backgroundColor, RoundedCornerShape(14.dp))
.background(backgroundColor, RoundedCornerShape(28.dp))
.padding(12.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
@@ -686,15 +694,18 @@ private fun DropdownMenuComponent(
)
.background(
if (independent) (if (isSystemInDarkTheme()) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)) else Color.Transparent,
if (independent) RoundedCornerShape(14.dp) else RoundedCornerShape(0.dp)
if (independent) RoundedCornerShape(28.dp) else RoundedCornerShape(0.dp)
)
.clip(if (independent) RoundedCornerShape(14.dp) else RoundedCornerShape(0.dp))
then(
if (independent) Modifier.padding(horizontal = 4.dp) else Modifier
)
.clip(if (independent) RoundedCornerShape(28.dp) else RoundedCornerShape(0.dp))
){
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 12.dp, end = 12.dp)
.height(55.dp)
.height(58.dp)
.pointerInput(Unit) {
detectTapGestures { offset ->
val now = System.currentTimeMillis()
@@ -766,7 +777,7 @@ private fun DropdownMenuComponent(
color = textColor.copy(alpha = 0.6f),
fontFamily = FontFamily(Font(R.font.sf_pro))
),
modifier = Modifier.padding(bottom = 2.dp)
modifier = Modifier.padding(16.dp, top = 0.dp, bottom = 2.dp)
)
}
}
@@ -780,14 +791,21 @@ private fun DropdownMenuComponent(
) {
Text(
text = selectedOption,
fontSize = 16.sp,
color = textColor.copy(alpha = 0.8f)
style = TextStyle(
fontSize = 16.sp,
color = textColor.copy(alpha = 0.8f),
fontFamily = FontFamily(Font(R.font.sf_pro))
)
)
Icon(
Icons.Default.KeyboardArrowDown,
contentDescription = null,
modifier = Modifier.size(18.dp),
tint = textColor.copy(alpha = 0.6f)
Text(
text = "􀆏",
style = TextStyle(
fontSize = 16.sp,
color = textColor.copy(alpha = 0.6f),
fontFamily = FontFamily(Font(R.font.sf_pro))
),
modifier = Modifier
.padding(start = 6.dp)
)
}
@@ -816,7 +834,7 @@ private fun DropdownMenuComponent(
Box(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 8.dp)
.padding(horizontal = 16.dp)
.background(if (isSystemInDarkTheme()) Color(0xFF000000) else Color(0xFFF2F2F7))
){
Text(

View File

@@ -36,6 +36,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.kyant.backdrop.backdrops.layerBackdrop
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -94,6 +96,7 @@ fun AdaptiveStrengthScreen(navController: NavController) {
}
}
val backdrop = rememberLayerBackdrop()
StyledScaffold(
title = stringResource(R.string.customize_adaptive_audio),
@@ -101,19 +104,21 @@ fun AdaptiveStrengthScreen(navController: NavController) {
StyledIconButton(
onClick = { navController.popBackStack() },
icon = "􀯶",
darkMode = isDarkTheme
darkMode = isDarkTheme,
backdrop = backdrop
)
}
) { spacerHeight ->
Column(
modifier = Modifier
.fillMaxSize()
.layerBackdrop(backdrop)
.padding(horizontal = 16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Spacer(modifier = Modifier.height(spacerHeight))
StyledSlider(
label = stringResource(R.string.customize_adaptive_audio).uppercase(),
label = stringResource(R.string.customize_adaptive_audio),
mutableFloatState = sliderValue,
onValueChange = {
sliderValue.floatValue = it

View File

@@ -68,6 +68,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import com.kyant.backdrop.backdrops.layerBackdrop
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
import com.kyant.backdrop.drawBackdrop
import com.kyant.backdrop.highlight.Highlight
@@ -80,7 +81,6 @@ import me.kavishdevar.librepods.composables.BatteryView
import me.kavishdevar.librepods.composables.CallControlSettings
import me.kavishdevar.librepods.composables.ConnectionSettings
import me.kavishdevar.librepods.composables.MicrophoneSettings
import me.kavishdevar.librepods.composables.NameField
import me.kavishdevar.librepods.composables.NavigationButton
import me.kavishdevar.librepods.composables.NoiseControlSettings
import me.kavishdevar.librepods.composables.PressAndHoldSettings
@@ -199,13 +199,15 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService,
}
}
val darkMode = isSystemInDarkTheme()
val backdrop = rememberLayerBackdrop()
StyledScaffold(
title = deviceName.text,
actionButtons = listOf {
StyledIconButton(
onClick = { navController.navigate("app_settings") },
icon = "􀍟",
darkMode = darkMode
darkMode = darkMode,
backdrop = backdrop
)
},
snackbarHostState = snackbarHostState
@@ -216,6 +218,7 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService,
.fillMaxSize()
.hazeSource(hazeState)
.padding(horizontal = 16.dp)
.layerBackdrop(backdrop)
.verticalScroll(rememberScrollState())
) {
Spacer(modifier = Modifier.height(spacerHeight))
@@ -249,28 +252,28 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService,
// Only show name field when not in BLE-only mode
if (!bleOnlyMode) {
NameField(
NavigationButton(
to = "rename",
name = stringResource(R.string.name),
value = deviceName.text,
navController = navController
currentState = deviceName.text,
navController = navController,
independent = true
)
}
if (!bleOnlyMode) {
Spacer(modifier = Modifier.height(32.dp))
NavigationButton(to = "hearing_aid", stringResource(R.string.hearing_aid), navController)
Spacer(modifier = Modifier.height(16.dp))
NoiseControlSettings(service = service)
Spacer(modifier = Modifier.height(16.dp))
PressAndHoldSettings(navController = navController)
Spacer(modifier = Modifier.height(16.dp))
CallControlSettings(hazeState = hazeState)
// camera control goes here, airpods side is done, i just need to figure out how to listen to app open/close events
Spacer(modifier = Modifier.height(16.dp))
PressAndHoldSettings(navController = navController)
Spacer(modifier = Modifier.height(16.dp))
AudioSettings(navController = navController)

View File

@@ -40,14 +40,11 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
import androidx.compose.material.icons.filled.Send
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
@@ -78,6 +75,8 @@ import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import com.kyant.backdrop.backdrops.layerBackdrop
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
import dev.chrisbanes.haze.hazeSource
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import kotlinx.coroutines.delay
@@ -325,14 +324,15 @@ fun DebugScreen(navController: NavController) {
}
val isDarkTheme = isSystemInDarkTheme()
val backdrop = rememberLayerBackdrop()
StyledScaffold(
title = "Debug",
navigationButton = {
StyledIconButton(
onClick = { navController.popBackStack() },
icon = "􀯶",
darkMode = isDarkTheme
darkMode = isDarkTheme,
backdrop = backdrop
)
},
actionButtons = listOf(
@@ -344,6 +344,7 @@ fun DebugScreen(navController: NavController) {
},
icon = "􀈑",
darkMode = isDarkTheme,
backdrop = backdrop
)
}
),
@@ -353,6 +354,7 @@ fun DebugScreen(navController: NavController) {
.fillMaxSize()
.hazeSource(hazeState)
.navigationBarsPadding()
.layerBackdrop(backdrop)
.padding(horizontal = 16.dp)
) {
Spacer(modifier = Modifier.height(spacerHeight))
@@ -391,11 +393,13 @@ fun DebugScreen(navController: NavController) {
)
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
imageVector = if (isSent) Icons.AutoMirrored.Filled.KeyboardArrowLeft else Icons.AutoMirrored.Filled.KeyboardArrowRight,
contentDescription = null,
tint = if (isSent) Color.Green else Color.Red,
modifier = Modifier.size(24.dp)
Text(
text = if (isSent) "􀆉" else "􀆊",
style = TextStyle(
fontSize = 16.sp,
fontFamily = FontFamily(Font(R.font.sf_pro)),
color = if (isSent) Color(0xFF4CD964) else Color(0xFFFF3B30)
),
)
Spacer(modifier = Modifier.width(4.dp))
Column {

View File

@@ -42,10 +42,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -87,12 +84,15 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import com.kyant.backdrop.backdrops.layerBackdrop
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
import dev.chrisbanes.haze.hazeSource
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import me.kavishdevar.librepods.R
import me.kavishdevar.librepods.composables.StyledButton
import me.kavishdevar.librepods.composables.StyledIconButton
import me.kavishdevar.librepods.composables.StyledScaffold
import me.kavishdevar.librepods.composables.StyledToggle
@@ -116,18 +116,19 @@ fun HeadTrackingScreen(navController: NavController) {
}
}
val isDarkTheme = isSystemInDarkTheme()
val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
val textColor = if (isDarkTheme) Color.White else Color.Black
val scrollState = rememberScrollState()
val backdrop = rememberLayerBackdrop()
StyledScaffold (
title = stringResource(R.string.head_tracking),
navigationButton = {
StyledIconButton(
onClick = { navController.popBackStack() },
icon = "􀯶",
darkMode = isDarkTheme
darkMode = isDarkTheme,
backdrop = backdrop
)
},
actionButtons = listOf(
@@ -144,72 +145,95 @@ fun HeadTrackingScreen(navController: NavController) {
}
},
icon = if (isActive) "􀊅" else "􀊃",
darkMode = isDarkTheme
darkMode = isDarkTheme,
backdrop = backdrop
)
}
),
) { spacerHeight, hazeState ->
Column (
val backdrop = rememberLayerBackdrop()
val sharedPreferences = LocalContext.current.getSharedPreferences("settings", Context.MODE_PRIVATE)
var gestureText by remember { mutableStateOf("") }
val coroutineScope = rememberCoroutineScope()
var lastClickTime by remember { mutableLongStateOf(0L) }
var shouldExplode by remember { mutableStateOf(false) }
Column(
modifier = Modifier
.fillMaxSize()
.padding(top = 8.dp)
.padding(horizontal = 16.dp)
.verticalScroll(scrollState)
.hazeSource(state = hazeState)
.layerBackdrop(backdrop)
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = Modifier.height(spacerHeight))
val sharedPreferences =
LocalContext.current.getSharedPreferences("settings", Context.MODE_PRIVATE)
Column (
modifier = Modifier
.fillMaxWidth()
.padding(top = 8.dp)
.padding(horizontal = 16.dp)
.verticalScroll(scrollState)
.hazeSource(state = hazeState)
.layerBackdrop(
backdrop = backdrop
)
) {
Spacer(modifier = Modifier.height(spacerHeight))
StyledToggle(
label = "Head Gestures",
sharedPreferences = sharedPreferences,
sharedPreferenceKey = "head_gestures",
)
var gestureText by remember { mutableStateOf("") }
val coroutineScope = rememberCoroutineScope()
Spacer(modifier = Modifier.height(2.dp))
Text(
stringResource(R.string.head_gestures_details),
style = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Normal,
fontFamily = FontFamily(Font(R.font.sf_pro)),
color = textColor.copy(0.6f)
),
modifier = Modifier.padding(start = 4.dp)
)
StyledToggle(
label = "Head Gestures",
sharedPreferences = sharedPreferences,
sharedPreferenceKey = "head_gestures",
)
Spacer(modifier = Modifier.height(2.dp))
Text(
stringResource(R.string.head_gestures_details),
style = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Normal,
fontFamily = FontFamily(Font(R.font.sf_pro)),
color = textColor.copy(0.6f)
),
modifier = Modifier.padding(start = 4.dp)
)
Spacer(modifier = Modifier.height(16.dp))
Text(
"Head Orientation",
style = TextStyle(
fontSize = 18.sp,
fontWeight = FontWeight.Medium,
fontFamily = FontFamily(Font(R.font.sf_pro)),
color = textColor
),
modifier = Modifier.padding(start = 4.dp, bottom = 8.dp, top = 8.dp)
)
HeadVisualization()
Spacer(modifier = Modifier.height(16.dp))
Text(
"Head Orientation",
style = TextStyle(
fontSize = 18.sp,
fontWeight = FontWeight.Medium,
fontFamily = FontFamily(Font(R.font.sf_pro)),
color = textColor
),
modifier = Modifier.padding(start = 4.dp, bottom = 8.dp, top = 8.dp)
)
HeadVisualization()
Spacer(modifier = Modifier.height(16.dp))
Text(
"Velocity",
style = TextStyle(
fontSize = 18.sp,
fontWeight = FontWeight.Medium,
fontFamily = FontFamily(Font(R.font.sf_pro)),
color = textColor
),
modifier = Modifier.padding(start = 4.dp, bottom = 8.dp, top = 8.dp)
)
AccelerationPlot()
Spacer(modifier = Modifier.height(16.dp))
Text(
"Velocity",
style = TextStyle(
fontSize = 18.sp,
fontWeight = FontWeight.Medium,
fontFamily = FontFamily(Font(R.font.sf_pro)),
color = textColor
),
modifier = Modifier.padding(start = 4.dp, bottom = 8.dp, top = 8.dp)
)
AccelerationPlot()
Spacer(modifier = Modifier.height(16.dp))
Spacer(modifier = Modifier.height(16.dp))
Button (
LaunchedEffect(gestureText) {
if (gestureText.isNotEmpty()) {
lastClickTime = System.currentTimeMillis()
delay(3000)
if (System.currentTimeMillis() - lastClickTime >= 3000) {
shouldExplode = true
}
}
}
}
StyledButton(
onClick = {
gestureText = "Shake your head or nod!"
coroutineScope.launch {
@@ -217,13 +241,9 @@ fun HeadTrackingScreen(navController: NavController) {
gestureText = if (accepted) "\"Yes\" gesture detected." else "\"No\" gesture detected."
}
},
modifier = Modifier
.fillMaxWidth()
.height(55.dp),
colors = ButtonDefaults.buttonColors(
containerColor = backgroundColor
),
shape = RoundedCornerShape(8.dp)
backdrop = backdrop,
modifier = Modifier.fillMaxWidth(0.75f),
maxScale = 0.05f
) {
Text(
"Test Head Gestures",
@@ -235,19 +255,6 @@ fun HeadTrackingScreen(navController: NavController) {
),
)
}
var lastClickTime by remember { mutableLongStateOf(0L) }
var shouldExplode by remember { mutableStateOf(false) }
LaunchedEffect(gestureText) {
if (gestureText.isNotEmpty()) {
lastClickTime = System.currentTimeMillis()
delay(3000)
if (System.currentTimeMillis() - lastClickTime >= 3000) {
shouldExplode = true
}
}
}
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.padding(top = 12.dp, bottom = 24.dp)

View File

@@ -41,6 +41,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.kyant.backdrop.backdrops.layerBackdrop
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
import dev.chrisbanes.haze.HazeState
import dev.chrisbanes.haze.hazeSource
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
@@ -77,14 +79,15 @@ fun HearingAidAdjustmentsScreen(@Suppress("unused") navController: NavController
val attManager = ServiceManager.getService()?.attManager ?: throw IllegalStateException("ATTManager not available")
val aacpManager = remember { ServiceManager.getService()?.aacpManager }
val backdrop = rememberLayerBackdrop()
StyledScaffold(
title = stringResource(R.string.adjustments),
navigationButton = {
StyledIconButton(
onClick = { navController.popBackStack() },
icon = "􀯶",
darkMode = isDarkTheme
darkMode = isDarkTheme,
backdrop = backdrop
)
}
) { spacerHeight ->
@@ -92,6 +95,7 @@ fun HearingAidAdjustmentsScreen(@Suppress("unused") navController: NavController
modifier = Modifier
.hazeSource(hazeState)
.fillMaxSize()
.layerBackdrop(backdrop)
.verticalScroll(verticalScrollState)
.padding(horizontal = 16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
@@ -282,7 +286,7 @@ fun HearingAidAdjustmentsScreen(@Suppress("unused") navController: NavController
}
StyledSlider(
label = stringResource(R.string.amplification).uppercase(),
label = stringResource(R.string.amplification),
valueRange = -1f..1f,
mutableFloatState = amplificationSliderValue,
onValueChange = {
@@ -301,20 +305,20 @@ fun HearingAidAdjustmentsScreen(@Suppress("unused") navController: NavController
)
StyledSlider(
label = stringResource(R.string.balance).uppercase(),
label = stringResource(R.string.balance),
valueRange = -1f..1f,
mutableFloatState = balanceSliderValue,
onValueChange = {
balanceSliderValue.floatValue = it
},
snapPoints = listOf(0f),
snapPoints = listOf(-1f, 0f, 1f),
startLabel = stringResource(R.string.left),
endLabel = stringResource(R.string.right),
independent = true,
)
StyledSlider(
label = stringResource(R.string.tone).uppercase(),
label = stringResource(R.string.tone),
valueRange = -1f..1f,
mutableFloatState = toneSliderValue,
onValueChange = {
@@ -326,7 +330,7 @@ fun HearingAidAdjustmentsScreen(@Suppress("unused") navController: NavController
)
StyledSlider(
label = stringResource(R.string.ambient_noise_reduction).uppercase(),
label = stringResource(R.string.ambient_noise_reduction),
valueRange = 0f..1f,
mutableFloatState = ambientNoiseReductionSliderValue,
onValueChange = {

View File

@@ -20,15 +20,10 @@ package me.kavishdevar.librepods.screens
import android.annotation.SuppressLint
import android.util.Log
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@@ -37,25 +32,18 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
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.pointerInput
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
@@ -74,9 +62,9 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import me.kavishdevar.librepods.R
import me.kavishdevar.librepods.composables.ConfirmationDialog
import me.kavishdevar.librepods.composables.NavigationButton
import me.kavishdevar.librepods.composables.StyledIconButton
import me.kavishdevar.librepods.composables.StyledScaffold
import me.kavishdevar.librepods.composables.StyledSwitch
import me.kavishdevar.librepods.composables.StyledToggle
import me.kavishdevar.librepods.services.ServiceManager
import me.kavishdevar.librepods.utils.AACPManager
@@ -117,7 +105,8 @@ fun HearingAidScreen(navController: NavController) {
StyledIconButton(
onClick = { navController.popBackStack() },
icon = "􀯶",
darkMode = isDarkTheme
darkMode = isDarkTheme,
backdrop = backdrop
)
},
actionButtons = emptyList(),
@@ -129,7 +118,7 @@ fun HearingAidScreen(navController: NavController) {
.hazeSource(hazeState)
.fillMaxSize()
.verticalScroll(verticalScrollState)
.padding(16.dp),
.padding(horizontal = 16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Spacer(modifier = Modifier.height(spacerHeight))
@@ -175,22 +164,22 @@ fun HearingAidScreen(navController: NavController) {
}
fun onAdjustPhoneChange(value: Boolean) {
adjustPhoneEnabled.value = value
// TODO
}
fun onAdjustMediaChange(value: Boolean) {
adjustMediaEnabled.value = value
// TODO
}
Text(
text = stringResource(R.string.hearing_aid).uppercase(),
text = stringResource(R.string.hearing_aid),
style = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Light,
fontWeight = FontWeight.Bold,
color = textColor.copy(alpha = 0.6f),
fontFamily = FontFamily(Font(R.font.sf_pro))
),
modifier = Modifier.padding(8.dp, bottom = 2.dp)
modifier = Modifier.padding(16.dp, bottom = 2.dp)
)
val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
@@ -198,9 +187,9 @@ fun HearingAidScreen(navController: NavController) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(backgroundColor, RoundedCornerShape(14.dp))
.background(backgroundColor, RoundedCornerShape(28.dp))
.clip(
RoundedCornerShape(14.dp)
RoundedCornerShape(28.dp)
)
) {
StyledToggle(
@@ -209,31 +198,17 @@ fun HearingAidScreen(navController: NavController) {
independent = false
)
HorizontalDivider(
thickness = 1.5.dp,
thickness = 1.dp,
color = Color(0x40888888),
modifier = Modifier
.padding(start = 12.dp, end = 0.dp)
.padding(horizontal = 12.dp)
)
NavigationButton(
to = "hearing_aid_adjustments",
name = stringResource(R.string.adjustments),
navController,
independent = false
)
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { navController.navigate("hearing_aid_adjustments") }
.padding(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(R.string.adjustments),
fontSize = 16.sp,
color = textColor
)
Spacer(modifier = Modifier.weight(1f))
Icon(
imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight,
contentDescription = null,
tint = textColor
)
}
}
Text(
text = stringResource(R.string.hearing_aid_description),
@@ -243,12 +218,12 @@ fun HearingAidScreen(navController: NavController) {
color = (if (isSystemInDarkTheme()) Color.White else Color.Black).copy(alpha = 0.6f),
fontFamily = FontFamily(Font(R.font.sf_pro))
),
modifier = Modifier.padding(horizontal = 8.dp)
modifier = Modifier.padding(horizontal = 16.dp)
)
Spacer(modifier = Modifier.height(16.dp))
StyledToggle(
title = stringResource(R.string.media_assist).uppercase(),
title = stringResource(R.string.media_assist),
label = stringResource(R.string.media_assist),
checkedState = mediaAssistEnabled,
independent = true,
@@ -260,99 +235,27 @@ fun HearingAidScreen(navController: NavController) {
Column (
modifier = Modifier
.fillMaxWidth()
.background(backgroundColor, RoundedCornerShape(14.dp))
.background(backgroundColor, RoundedCornerShape(28.dp))
) {
val isDarkThemeLocal = isSystemInDarkTheme()
var backgroundColorAdjustMedia by remember { mutableStateOf(if (isDarkThemeLocal) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)) }
val animatedBackgroundColorAdjustMedia by animateColorAsState(targetValue = backgroundColorAdjustMedia, animationSpec = tween(durationMillis = 500))
Row(
modifier = Modifier
.fillMaxWidth()
.padding(12.dp)
.pointerInput(Unit) {
detectTapGestures(
onPress = {
backgroundColorAdjustMedia = if (isDarkThemeLocal) Color(0x40888888) else Color(0x40D9D9D9)
tryAwaitRelease()
backgroundColorAdjustMedia = if (isDarkThemeLocal) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
},
onTap = {
onAdjustMediaChange(!adjustMediaEnabled.value)
}
)
}
.background(
animatedBackgroundColorAdjustMedia,
RoundedCornerShape(topStart = 14.dp, topEnd = 14.dp)
),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(R.string.adjust_media),
modifier = Modifier.weight(1f),
style = TextStyle(
fontSize = 16.sp,
fontFamily = FontFamily(Font(R.font.sf_pro)),
fontWeight = FontWeight.Normal,
color = textColor
)
)
StyledSwitch(
checked = adjustMediaEnabled.value,
onCheckedChange = {
onAdjustMediaChange(it)
},
)
}
StyledToggle(
label = stringResource(R.string.adjust_media),
checkedState = adjustMediaEnabled,
onCheckedChange = { onAdjustMediaChange(it) },
independent = false
)
HorizontalDivider(
thickness = 1.5.dp,
thickness = 1.dp,
color = Color(0x40888888),
modifier = Modifier
.padding(start = 12.dp, end = 0.dp)
.padding(horizontal = 12.dp)
)
var backgroundColorAdjustPhone by remember { mutableStateOf(if (isDarkThemeLocal) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)) }
val animatedBackgroundColorAdjustPhone by animateColorAsState(targetValue = backgroundColorAdjustPhone, animationSpec = tween(durationMillis = 500))
Row(
modifier = Modifier
.fillMaxWidth()
.padding(12.dp)
.pointerInput(Unit) {
detectTapGestures(
onPress = {
backgroundColorAdjustPhone = if (isDarkThemeLocal) Color(0x40888888) else Color(0x40D9D9D9)
tryAwaitRelease()
backgroundColorAdjustPhone = if (isDarkThemeLocal) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
},
onTap = {
onAdjustPhoneChange(!adjustPhoneEnabled.value)
}
)
}
.background(
animatedBackgroundColorAdjustPhone,
RoundedCornerShape(bottomStart = 14.dp, bottomEnd = 14.dp)
),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(R.string.adjust_calls),
modifier = Modifier.weight(1f),
style = TextStyle(
fontSize = 16.sp,
fontFamily = FontFamily(Font(R.font.sf_pro)),
fontWeight = FontWeight.Normal,
color = textColor
)
)
StyledSwitch(
checked = adjustPhoneEnabled.value,
onCheckedChange = {
onAdjustPhoneChange(it)
},
)
}
StyledToggle(
label = stringResource(R.string.adjust_calls),
checkedState = adjustPhoneEnabled,
onCheckedChange = { onAdjustPhoneChange(it) },
independent = false
)
}
}
}

View File

@@ -73,6 +73,8 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.edit
import androidx.navigation.NavController
import com.kyant.backdrop.backdrops.layerBackdrop
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -151,6 +153,7 @@ fun Onboarding(navController: NavController, activityContext: Context) {
isComplete = true
}
}
val backdrop = rememberLayerBackdrop()
StyledScaffold(
title = "Setting Up",
actionButtons = listOf(
@@ -160,7 +163,8 @@ fun Onboarding(navController: NavController, activityContext: Context) {
showSkipDialog = true
},
icon = "􀊋",
darkMode = isDarkTheme
darkMode = isDarkTheme,
backdrop = backdrop
)
}
)
@@ -168,6 +172,7 @@ fun Onboarding(navController: NavController, activityContext: Context) {
Column(
modifier = Modifier
.fillMaxSize()
.layerBackdrop(backdrop)
.padding(horizontal = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)

View File

@@ -23,6 +23,7 @@ package me.kavishdevar.librepods.screens
import android.content.Context
import android.util.Log
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures
@@ -37,8 +38,6 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Checkbox
import androidx.compose.material3.CheckboxDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
@@ -50,11 +49,11 @@ 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.scale
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
@@ -63,6 +62,8 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.edit
import androidx.navigation.NavController
import com.kyant.backdrop.backdrops.layerBackdrop
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import me.kavishdevar.librepods.R
import me.kavishdevar.librepods.composables.StyledIconButton
@@ -76,20 +77,20 @@ import kotlin.io.encoding.ExperimentalEncodingApi
@Composable
fun RightDivider() {
HorizontalDivider(
thickness = 1.5.dp,
thickness = 1.dp,
color = Color(0x40888888),
modifier = Modifier
.padding(start = 72.dp)
.padding(start = 72.dp, end = 20.dp)
)
}
@Composable
fun RightDividerNoIcon() {
HorizontalDivider(
thickness = 1.5.dp,
thickness = 1.dp,
color = Color(0x40888888),
modifier = Modifier
.padding(start = 20.dp)
.padding(start = 20.dp, end = 20.dp)
)
}
@@ -117,19 +118,22 @@ fun LongPress(navController: NavController, name: String) {
val longPressActionPref = sharedPreferences.getString(prefKey, StemAction.CYCLE_NOISE_CONTROL_MODES.name)
Log.d("PressAndHoldSettingsScreen", "Long press action preference ($prefKey): $longPressActionPref")
var longPressAction by remember { mutableStateOf(StemAction.valueOf(longPressActionPref ?: StemAction.CYCLE_NOISE_CONTROL_MODES.name)) }
val backdrop = rememberLayerBackdrop()
StyledScaffold(
title = name,
navigationButton = {
StyledIconButton(
onClick = { navController.popBackStack() },
icon = "􀯶",
darkMode = isDarkTheme
darkMode = isDarkTheme,
backdrop = backdrop
)
}
) { spacerHeight ->
val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
Column (
modifier = Modifier
.layerBackdrop(backdrop)
.fillMaxSize()
.padding(top = 8.dp)
.padding(horizontal = 16.dp)
@@ -138,11 +142,11 @@ fun LongPress(navController: NavController, name: String) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(backgroundColor, RoundedCornerShape(14.dp)),
.background(backgroundColor, RoundedCornerShape(28.dp)),
horizontalAlignment = Alignment.CenterHorizontally
) {
LongPressActionElement(
name = "Noise Control",
name = stringResource(R.string.noise_control),
selected = longPressAction == StemAction.CYCLE_NOISE_CONTROL_MODES,
onClick = {
longPressAction = StemAction.CYCLE_NOISE_CONTROL_MODES
@@ -153,7 +157,7 @@ fun LongPress(navController: NavController, name: String) {
)
RightDividerNoIcon()
LongPressActionElement(
name = "Digital Assistant",
name = stringResource(R.string.digital_assistant),
selected = longPressAction == StemAction.DIGITAL_ASSISTANT,
onClick = {
longPressAction = StemAction.DIGITAL_ASSISTANT
@@ -165,23 +169,25 @@ fun LongPress(navController: NavController, name: String) {
}
if (longPressAction == StemAction.CYCLE_NOISE_CONTROL_MODES) {
Spacer(modifier = Modifier.height(32.dp))
Text(
text = "NOISE CONTROL",
text = stringResource(R.string.noise_control),
style = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Light,
fontWeight = FontWeight.Bold,
color = textColor.copy(alpha = 0.6f),
),
fontFamily = FontFamily(Font(R.font.sf_pro)),
modifier = Modifier
.padding(top = 32.dp, bottom = 4.dp)
.padding(horizontal = 8.dp)
.padding(horizontal = 18.dp)
)
Spacer(modifier = Modifier.height(8.dp))
Column(
modifier = Modifier
.fillMaxWidth()
.background(backgroundColor, RoundedCornerShape(14.dp)),
.background(backgroundColor, RoundedCornerShape(28.dp)),
horizontalAlignment = Alignment.CenterHorizontally
) {
val offListeningModeValue = ServiceManager.getService()!!.aacpManager.controlCommandStatusList.find {
@@ -189,28 +195,28 @@ fun LongPress(navController: NavController, name: String) {
}?.value?.takeIf { it.isNotEmpty() }?.get(0)
val offListeningMode = offListeningModeValue == 1.toByte()
ListeningModeElement(
name = "Off",
name = stringResource(R.string.off),
enabled = offListeningMode,
resourceId = R.drawable.noise_cancellation,
isFirst = true)
if (offListeningMode) RightDivider()
ListeningModeElement(
name = "Transparency",
name = stringResource(R.string.transparency),
resourceId = R.drawable.transparency,
isFirst = !offListeningMode)
RightDivider()
ListeningModeElement(
name = "Adaptive",
name = stringResource(R.string.adaptive),
resourceId = R.drawable.adaptive)
RightDivider()
ListeningModeElement(
name = "Noise Cancellation",
name = stringResource(R.string.noise_cancellation),
resourceId = R.drawable.noise_cancellation,
isLast = true)
}
Spacer(modifier = Modifier.height(4.dp))
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Press and hold the stem to cycle between the selected noise control modes.",
text = stringResource(R.string.press_and_hold_noise_control_description),
style = TextStyle(
fontSize = 12.sp,
fontWeight = FontWeight.Light,
@@ -218,7 +224,7 @@ fun LongPress(navController: NavController, name: String) {
fontFamily = FontFamily(Font(R.font.sf_pro))
),
modifier = Modifier
.padding(horizontal = 8.dp)
.padding(horizontal = 18.dp)
)
}
}
@@ -329,8 +335,8 @@ fun ListeningModeElement(name: String, enabled: Boolean = true, resourceId: Int,
}
val shape = when {
isFirst -> RoundedCornerShape(topStart = 14.dp, topEnd = 14.dp)
isLast -> RoundedCornerShape(bottomStart = 14.dp, bottomEnd = 14.dp)
isFirst -> RoundedCornerShape(topStart = 28.dp, topEnd = 28.dp)
isLast -> RoundedCornerShape(bottomStart = 28.dp, bottomEnd = 28.dp)
else -> RoundedCornerShape(0.dp)
}
var backgroundColor by remember { mutableStateOf(if (darkMode) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)) }
@@ -352,7 +358,7 @@ fun ListeningModeElement(name: String, enabled: Boolean = true, resourceId: Int,
},
)
}
.padding(horizontal = 16.dp, vertical = 0.dp),
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
@@ -384,24 +390,19 @@ fun ListeningModeElement(name: String, enabled: Boolean = true, resourceId: Int,
fontFamily = FontFamily(Font(R.font.sf_pro)),
)
}
Checkbox(
checked = checked.value,
onCheckedChange = { valueChanged() },
colors = CheckboxDefaults.colors().copy(
checkedCheckmarkColor = Color(0xFF007AFF),
uncheckedCheckmarkColor = Color.Transparent,
checkedBoxColor = Color.Transparent,
uncheckedBoxColor = Color.Transparent,
checkedBorderColor = Color.Transparent,
uncheckedBorderColor = Color.Transparent,
disabledBorderColor = Color.Transparent,
disabledCheckedBoxColor = Color.Transparent,
disabledUncheckedBoxColor = Color.Transparent,
disabledUncheckedBorderColor = Color.Transparent
val floatAnimateState by animateFloatAsState(
targetValue = if (checked.value) 1f else 0f,
animationSpec = tween(durationMillis = 300)
)
Text(
text = "􀆅",
style = TextStyle(
fontSize = 20.sp,
fontFamily = FontFamily(Font(R.font.sf_pro)),
color = Color(0xFF007AFF).copy(alpha = floatAnimateState),
),
modifier = Modifier
.height(24.dp)
.scale(1.5f),
modifier = Modifier.padding(end = 4.dp)
)
}
}
@@ -417,15 +418,15 @@ fun LongPressActionElement(
) {
val darkMode = isSystemInDarkTheme()
val shape = when {
isFirst -> RoundedCornerShape(topStart = 14.dp, topEnd = 14.dp)
isLast -> RoundedCornerShape(bottomStart = 14.dp, bottomEnd = 14.dp)
isFirst -> RoundedCornerShape(topStart = 28.dp, topEnd = 28.dp)
isLast -> RoundedCornerShape(bottomStart = 28.dp, bottomEnd = 28.dp)
else -> RoundedCornerShape(0.dp)
}
var backgroundColor by remember { mutableStateOf(if (darkMode) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)) }
val animatedBackgroundColor by animateColorAsState(targetValue = backgroundColor, animationSpec = tween(durationMillis = 500))
Row(
modifier = Modifier
.height(48.dp)
.height(55.dp)
.background(animatedBackgroundColor, shape)
.pointerInput(Unit) {
detectTapGestures(
@@ -437,7 +438,7 @@ fun LongPressActionElement(
}
)
}
.padding(horizontal = 16.dp, vertical = 0.dp),
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
@@ -449,24 +450,18 @@ fun LongPressActionElement(
.weight(1f)
.padding(start = 4.dp)
)
Checkbox(
checked = selected,
onCheckedChange = { onClick() },
colors = CheckboxDefaults.colors().copy(
checkedCheckmarkColor = Color(0xFF007AFF),
uncheckedCheckmarkColor = Color.Transparent,
checkedBoxColor = Color.Transparent,
uncheckedBoxColor = Color.Transparent,
checkedBorderColor = Color.Transparent,
uncheckedBorderColor = Color.Transparent,
disabledBorderColor = Color.Transparent,
disabledCheckedBoxColor = Color.Transparent,
disabledUncheckedBoxColor = Color.Transparent,
disabledUncheckedBorderColor = Color.Transparent
val floatAnimateState by animateFloatAsState(
targetValue = if (selected) 1f else 0f,
animationSpec = tween(durationMillis = 300)
)
Text(
text = "􀆅",
style = TextStyle(
fontSize = 20.sp,
fontFamily = FontFamily(Font(R.font.sf_pro)),
color = Color(0xFF007AFF).copy(alpha = floatAnimateState)
),
modifier = Modifier
.height(24.dp)
.scale(1.5f),
modifier = Modifier.padding(end = 4.dp)
)
}
}

View File

@@ -32,11 +32,9 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Clear
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
@@ -52,12 +50,16 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextRange
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.input.TextFieldValue
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.edit
import androidx.navigation.NavController
import com.kyant.backdrop.backdrops.layerBackdrop
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import me.kavishdevar.librepods.R
import me.kavishdevar.librepods.composables.StyledIconButton
@@ -81,19 +83,23 @@ fun RenameScreen(navController: NavController) {
name.value = name.value.copy(selection = TextRange(name.value.text.length))
}
val backdrop = rememberLayerBackdrop()
StyledScaffold(
title = stringResource(R.string.name),
navigationButton = {
StyledIconButton(
onClick = { navController.popBackStack() },
icon = "􀯶",
darkMode = isDarkTheme
darkMode = isDarkTheme,
backdrop = backdrop
)
},
) { spacerHeight ->
Column(
modifier = Modifier
.fillMaxSize()
.layerBackdrop(backdrop)
.padding(horizontal = 16.dp)
) {
Spacer(modifier = Modifier.height(spacerHeight))
@@ -105,10 +111,10 @@ fun RenameScreen(navController: NavController) {
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.height(55.dp)
.height(58.dp)
.background(
backgroundColor,
RoundedCornerShape(14.dp)
RoundedCornerShape(28.dp)
)
.padding(horizontal = 16.dp, vertical = 8.dp)
) {
@@ -120,8 +126,9 @@ fun RenameScreen(navController: NavController) {
ServiceManager.getService()?.setName(it.text)
},
textStyle = TextStyle(
color = textColor,
fontSize = 16.sp,
color = textColor,
fontFamily = FontFamily(Font(R.font.sf_pro))
),
singleLine = true,
cursorBrush = SolidColor(cursorColor),
@@ -138,14 +145,15 @@ fun RenameScreen(navController: NavController) {
IconButton(
onClick = {
name.value = TextFieldValue("")
sharedPreferences.edit { putString("name", "") }
ServiceManager.getService()?.setName("")
}
) {
Icon(
Icons.Default.Clear,
contentDescription = "Clear",
tint = if (isDarkTheme) Color.White else Color.Black
Text(
text = "􀁡",
style = TextStyle(
fontSize = 16.sp,
fontFamily = FontFamily(Font(R.font.sf_pro)),
color = if (isDarkTheme) Color.White.copy(alpha = 0.6f) else Color.Black.copy(alpha = 0.6f)
),
)
}
}

View File

@@ -59,6 +59,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.backdrops.layerBackdrop
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
import dev.chrisbanes.haze.hazeSource
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import kotlinx.coroutines.delay
@@ -95,19 +97,23 @@ fun TransparencySettingsScreen(navController: NavController) {
val activeTrackColor = if (isDarkTheme) Color(0xFF007AFF) else Color(0xFF3C6DF5)
val thumbColor = if (isDarkTheme) Color(0xFFFFFFFF) else Color(0xFFFFFFFF)
val backdrop = rememberLayerBackdrop()
StyledScaffold(
title = stringResource(R.string.customize_transparency_mode),
navigationButton = {
StyledIconButton(
onClick = { navController.popBackStack() },
icon = "􀯶",
darkMode = isDarkTheme
darkMode = isDarkTheme,
backdrop = backdrop
)
}
){ spacerHeight, hazeState ->
Column(
modifier = Modifier
.hazeSource(hazeState)
.layerBackdrop(backdrop)
.fillMaxSize()
.verticalScroll(verticalScrollState)
.padding(horizontal = 16.dp),
@@ -154,19 +160,15 @@ fun TransparencySettingsScreen(navController: NavController) {
object : (ByteArray) -> Unit {
override fun invoke(value: ByteArray) {
val parsed = parseTransparencySettingsResponse(value)
if (parsed != null) {
enabled.value = parsed.enabled
amplificationSliderValue.floatValue = parsed.netAmplification
balanceSliderValue.floatValue = parsed.balance
toneSliderValue.floatValue = parsed.leftTone
ambientNoiseReductionSliderValue.floatValue =
parsed.leftAmbientNoiseReduction
conversationBoostEnabled.value = parsed.leftConversationBoost
eq.value = parsed.leftEQ.copyOf()
Log.d(TAG, "Updated transparency settings from notification")
} else {
Log.w(TAG, "Failed to parse transparency settings from notification")
}
enabled.value = parsed.enabled
amplificationSliderValue.floatValue = parsed.netAmplification
balanceSliderValue.floatValue = parsed.balance
toneSliderValue.floatValue = parsed.leftTone
ambientNoiseReductionSliderValue.floatValue =
parsed.leftAmbientNoiseReduction
conversationBoostEnabled.value = parsed.leftConversationBoost
eq.value = parsed.leftEQ.copyOf()
Log.d(TAG, "Updated transparency settings from notification")
}
}
}
@@ -251,12 +253,7 @@ fun TransparencySettingsScreen(navController: NavController) {
try {
val data = attManager.read(ATTHandles.TRANSPARENCY)
parsedSettings = parseTransparencySettingsResponse(data = data)
if (parsedSettings != null) {
Log.d(TAG, "Parsed settings on attempt $attempt")
break
} else {
Log.d(TAG, "Parsing returned null on attempt $attempt")
}
Log.d(TAG, "Parsed settings on attempt $attempt")
} catch (e: Exception) {
Log.w(TAG, "Read attempt $attempt failed: ${e.message}")
}
@@ -297,7 +294,7 @@ fun TransparencySettingsScreen(navController: NavController) {
)
Spacer(modifier = Modifier.height(4.dp))
StyledSlider(
label = stringResource(R.string.amplification).uppercase(),
label = stringResource(R.string.amplification),
valueRange = -1f..1f,
mutableFloatState = amplificationSliderValue,
onValueChange = {
@@ -309,20 +306,20 @@ fun TransparencySettingsScreen(navController: NavController) {
)
StyledSlider(
label = stringResource(R.string.balance).uppercase(),
label = stringResource(R.string.balance),
valueRange = -1f..1f,
mutableFloatState = balanceSliderValue,
onValueChange = {
balanceSliderValue.floatValue = it
},
snapPoints = listOf(0f),
snapPoints = listOf(-1f, 0f, 1f),
startLabel = stringResource(R.string.left),
endLabel = stringResource(R.string.right),
independent = true,
)
StyledSlider(
label = stringResource(R.string.tone).uppercase(),
label = stringResource(R.string.tone),
valueRange = -1f..1f,
mutableFloatState = toneSliderValue,
onValueChange = {
@@ -334,7 +331,7 @@ fun TransparencySettingsScreen(navController: NavController) {
)
StyledSlider(
label = stringResource(R.string.ambient_noise_reduction).uppercase(),
label = stringResource(R.string.ambient_noise_reduction),
valueRange = 0f..1f,
mutableFloatState = ambientNoiseReductionSliderValue,
onValueChange = {
@@ -356,20 +353,20 @@ fun TransparencySettingsScreen(navController: NavController) {
// Only show transparency mode EQ section if SDP offset is available
if (isSdpOffsetAvailable.value) {
Text(
text = stringResource(R.string.equalizer).uppercase(),
text = stringResource(R.string.equalizer),
style = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Light,
fontWeight = FontWeight.Bold,
color = textColor.copy(alpha = 0.6f),
fontFamily = FontFamily(Font(R.font.sf_pro))
),
modifier = Modifier.padding(8.dp, bottom = 2.dp)
modifier = Modifier.padding(16.dp, bottom = 4.dp)
)
Column(
modifier = Modifier
.fillMaxWidth()
.background(backgroundColor, RoundedCornerShape(14.dp))
.background(backgroundColor, RoundedCornerShape(28.dp))
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceBetween

View File

@@ -85,6 +85,8 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.FileProvider
import androidx.navigation.NavController
import com.kyant.backdrop.backdrops.layerBackdrop
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
import dev.chrisbanes.haze.hazeSource
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import kotlinx.coroutines.Dispatchers
@@ -208,6 +210,8 @@ fun TroubleshootingScreen(navController: NavController) {
showBottomSheet = true
}
val backdrop = rememberLayerBackdrop()
Box(
modifier = Modifier.fillMaxSize()
) {
@@ -217,28 +221,30 @@ fun TroubleshootingScreen(navController: NavController) {
StyledIconButton(
onClick = { navController.popBackStack() },
icon = "􀯶",
darkMode = isDarkTheme
darkMode = isDarkTheme,
backdrop = backdrop
)
}
){ spacerHeight, hazeState ->
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(scrollState)
.layerBackdrop(backdrop)
.hazeSource(state = hazeState)
.verticalScroll(scrollState)
.padding(horizontal = 16.dp)
) {
Spacer(modifier = Modifier.height(spacerHeight))
Text(
text = stringResource(R.string.saved_logs).uppercase(),
text = stringResource(R.string.saved_logs),
style = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Light,
fontWeight = FontWeight.Bold,
color = textColor.copy(alpha = 0.6f),
fontFamily = FontFamily(Font(R.font.sf_pro))
),
modifier = Modifier.padding(8.dp, bottom = 2.dp, top = 8.dp)
modifier = Modifier.padding(16.dp, bottom = 4.dp, top = 8.dp)
)
Spacer(modifier = Modifier.height(2.dp))
@@ -249,7 +255,7 @@ fun TroubleshootingScreen(navController: NavController) {
.fillMaxWidth()
.background(
backgroundColor,
RoundedCornerShape(14.dp)
RoundedCornerShape(28.dp)
)
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
@@ -266,7 +272,7 @@ fun TroubleshootingScreen(navController: NavController) {
.fillMaxWidth()
.background(
backgroundColor,
RoundedCornerShape(14.dp)
RoundedCornerShape(28.dp)
)
.padding(horizontal = 16.dp, vertical = 8.dp)
) {
@@ -372,14 +378,14 @@ fun TroubleshootingScreen(navController: NavController) {
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "TROUBLESHOOTING STEPS".uppercase(),
text = "TROUBLESHOOTING STEPS",
style = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Light,
color = textColor.copy(alpha = 0.6f),
fontFamily = FontFamily(Font(R.font.sf_pro))
),
modifier = Modifier.padding(8.dp, bottom = 2.dp, top = 8.dp)
modifier = Modifier.padding(16.dp, bottom = 2.dp, top = 8.dp)
)
Spacer(modifier = Modifier.height(2.dp))
@@ -389,7 +395,7 @@ fun TroubleshootingScreen(navController: NavController) {
.fillMaxWidth()
.background(
backgroundColor,
RoundedCornerShape(14.dp)
RoundedCornerShape(28.dp)
)
.padding(16.dp)
) {

View File

@@ -15,12 +15,13 @@
<string name="case_alt">Case</string>
<string name="test">Test</string>
<string name="name">Name</string>
<string name="noise_control">Noise Control</string>
<string name="noise_control">Listening Mode</string>
<string name="off">Off</string>
<string name="transparency">Transparency</string>
<string name="adaptive">Adaptive</string>
<string name="noise_cancellation">Noise Cancellation</string>
<string name="press_and_hold_airpods">Press and Hold AirPods</string>
<string name="press_and_hold_noise_control_description">Press and hold the stem to cycle between the selected listening modes.</string>
<string name="head_gestures">Head Gestures</string>
<string name="left">Left</string>
<string name="right">Right</string>
@@ -110,7 +111,7 @@
<string name="press_speed">Press Speed</string>
<string name="press_speed_description">Adjust the speed required to press two or three times on your AirPods.</string>
<string name="press_and_hold_duration">Press and Hold Duration</string>
<string name="press_and_hold_duration_description">Adjust the duration required to press and hold on your AirPods</string>
<string name="press_and_hold_duration_description">Adjust the duration required to press and hold on your AirPods.</string>
<string name="volume_swipe_speed">Volume Swipe Speed</string>
<string name="volume_swipe_speed_description">To prevent unintended volume adjustments, select preferred wait time between swipes.</string>
<string name="equalizer">Equalizer</string>
@@ -133,7 +134,7 @@
<string name="ambient_noise_reduction">Ambient Noise Reduction</string>
<string name="conversation_boost">Conversation Boost</string>
<string name="conversation_boost_description">Conversation Boost focuses your AirPods Pro on the person talking in front of you, making it easier to hear in a face-to-face conversation.</string>
<string name="hearing_aid_description">AirPods can use the results of a hearing test to make adjustments that improve the clarity of voices and sounds around you. \n\n Hearing Aid is only intended for people with perceived mild to moderate hearing loss.</string>
<string name="hearing_aid_description">AirPods can use the results of a hearing test to make adjustments that improve the clarity of voices and sounds around you.\n\nHearing Aid is only intended for people with perceived mild to moderate hearing loss.</string>
<string name="media_assist">Media Assist</string>
<string name="media_assist_description">AirPods Pro can use the results of a hearing test to make adjustments that improve the clarity of music, video, and calls.</string>
<string name="adjust_media">Adjust Music and Video</string>
@@ -174,4 +175,5 @@
<string name="must_be_32_hex_chars">Must be exactly 32 hex characters</string>
<string name="error_converting_hex">Error converting hex:</string>
<string name="found_offset_restart_bluetooth">Found offset please restart the Bluetooth process</string>
<string name="digital_assistant">Digital Assistant</string>
</resources>