diff --git a/android/app/src/main/java/me/kavishdevar/librepods/screens/AppSettingsScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/screens/AppSettingsScreen.kt
index 67bf3cd..6dbf8cc 100644
--- a/android/app/src/main/java/me/kavishdevar/librepods/screens/AppSettingsScreen.kt
+++ b/android/app/src/main/java/me/kavishdevar/librepods/screens/AppSettingsScreen.kt
@@ -35,8 +35,10 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
+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.automirrored.filled.KeyboardArrowLeft
import androidx.compose.material.icons.filled.Refresh
@@ -76,9 +78,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import me.kavishdevar.librepods.R
-import me.kavishdevar.librepods.composables.IndependentToggle
import me.kavishdevar.librepods.composables.StyledSwitch
-import me.kavishdevar.librepods.services.ServiceManager
import me.kavishdevar.librepods.utils.RadareOffsetFinder
import kotlin.math.roundToInt
@@ -89,9 +89,26 @@ fun AppSettingsScreen(navController: NavController) {
val name = remember { mutableStateOf(sharedPreferences.getString("name", "") ?: "") }
val isDarkTheme = isSystemInDarkTheme()
val context = LocalContext.current
+ val scrollState = rememberScrollState()
var showResetDialog by remember { mutableStateOf(false) }
+ var showPhoneBatteryInWidget by remember {
+ mutableStateOf(sharedPreferences.getBoolean("show_phone_battery_in_widget", true))
+ }
+ var conversationalAwarenessPauseMusicEnabled by remember {
+ mutableStateOf(sharedPreferences.getBoolean("conversational_awareness_pause_music", false))
+ }
+ var relativeConversationalAwarenessVolumeEnabled by remember {
+ mutableStateOf(sharedPreferences.getBoolean("relative_conversational_awareness_volume", true))
+ }
+ var openDialogForControlling by remember {
+ mutableStateOf(sharedPreferences.getString("qs_click_behavior", "dialog") == "dialog")
+ }
+ var disconnectWhenNotWearing by remember {
+ mutableStateOf(sharedPreferences.getBoolean("disconnect_when_not_wearing", false))
+ }
+
Scaffold(
topBar = {
CenterAlignedTopAppBar(
@@ -141,24 +158,88 @@ fun AppSettingsScreen(navController: NavController) {
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
- .padding(horizontal = 12.dp)
+ .padding(horizontal = 16.dp)
+ .verticalScroll(scrollState)
) {
val isDarkTheme = isSystemInDarkTheme()
-
val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
val textColor = if (isDarkTheme) Color.White else Color.Black
- IndependentToggle("Show phone battery in widget", ServiceManager.getService()!!, "setPhoneBatteryInWidget", sharedPreferences)
+ Spacer(modifier = Modifier.height(8.dp))
Text(
- text = stringResource(R.string.conversational_awareness_customization).uppercase(),
+ text = "Widget".uppercase(),
style = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Light,
- color = (if (isSystemInDarkTheme()) Color.White else Color.Black).copy(alpha = 0.6f),
+ color = textColor.copy(alpha = 0.6f),
fontFamily = FontFamily(Font(R.font.sf_pro))
),
- modifier = Modifier.padding(8.dp, bottom = 2.dp)
+ modifier = Modifier.padding(8.dp, bottom = 2.dp, top = 8.dp)
+ )
+
+ Spacer(modifier = Modifier.height(2.dp))
+
+ Column (
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(
+ backgroundColor,
+ RoundedCornerShape(14.dp)
+ )
+ .padding(horizontal = 16.dp, vertical = 4.dp)
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable(
+ indication = null,
+ interactionSource = remember { MutableInteractionSource() }
+ ) {
+ showPhoneBatteryInWidget = !showPhoneBatteryInWidget
+ sharedPreferences.edit().putBoolean("show_phone_battery_in_widget", showPhoneBatteryInWidget).apply()
+ },
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Column(
+ modifier = Modifier
+ .weight(1f)
+ .padding(vertical = 8.dp)
+ .padding(end = 4.dp)
+ ) {
+ Text(
+ text = "Show phone battery in widget",
+ fontSize = 16.sp,
+ color = textColor
+ )
+ Spacer(modifier = Modifier.height(4.dp))
+ Text(
+ text = "Display your phone's battery level in the widget alongside AirPods battery",
+ fontSize = 14.sp,
+ color = textColor.copy(0.6f),
+ lineHeight = 16.sp,
+ )
+ }
+
+ StyledSwitch(
+ checked = showPhoneBatteryInWidget,
+ onCheckedChange = {
+ showPhoneBatteryInWidget = it
+ sharedPreferences.edit().putBoolean("show_phone_battery_in_widget", it).apply()
+ }
+ )
+ }
+ }
+
+ Text(
+ text = "Conversational Awareness".uppercase(),
+ 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 = 24.dp)
)
Spacer(modifier = Modifier.height(2.dp))
@@ -166,44 +247,24 @@ fun AppSettingsScreen(navController: NavController) {
Column (
modifier = Modifier
.fillMaxWidth()
- .height(275.sp.value.dp)
.background(
backgroundColor,
RoundedCornerShape(14.dp)
)
- .padding(horizontal = 16.dp, vertical = 8.dp)
+ .padding(horizontal = 16.dp, vertical = 4.dp)
) {
val sliderValue = remember { mutableFloatStateOf(0f) }
LaunchedEffect(sliderValue) {
if (sharedPreferences.contains("conversational_awareness_volume")) {
- sliderValue.floatValue = sharedPreferences.getInt("conversational_awareness_volume", 0).toFloat()
+ sliderValue.floatValue = sharedPreferences.getInt("conversational_awareness_volume", 43).toFloat()
}
}
- LaunchedEffect(sliderValue.floatValue) {
- sharedPreferences.edit().putInt("conversational_awareness_volume", sliderValue.floatValue.toInt()).apply()
- }
-
- val trackColor = if (isDarkTheme) Color(0xFFB3B3B3) else Color(0xFFD9D9D9)
- val thumbColor = if (isDarkTheme) Color(0xFFFFFFFF) else Color(0xFFFFFFFF)
- val labelTextColor = if (isDarkTheme) Color.White else Color.Black
-
- var conversationalAwarenessPauseMusicEnabled by remember {
- mutableStateOf(
- sharedPreferences.getBoolean("conversational_awareness_pause_music", true)
- )
- }
fun updateConversationalAwarenessPauseMusic(enabled: Boolean) {
conversationalAwarenessPauseMusicEnabled = enabled
sharedPreferences.edit().putBoolean("conversational_awareness_pause_music", enabled).apply()
}
- var relativeConversationalAwarenessVolumeEnabled by remember {
- mutableStateOf(
- sharedPreferences.getBoolean("relative_conversational_awareness_volume", true)
- )
- }
-
fun updateRelativeConversationalAwarenessVolume(enabled: Boolean) {
relativeConversationalAwarenessVolumeEnabled = enabled
sharedPreferences.edit().putBoolean("relative_conversational_awareness_volume", enabled).apply()
@@ -212,11 +273,6 @@ fun AppSettingsScreen(navController: NavController) {
Row(
modifier = Modifier
.fillMaxWidth()
- .height(85.sp.value.dp)
- .background(
- shape = RoundedCornerShape(14.dp),
- color = Color.Transparent
- )
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
@@ -228,6 +284,7 @@ fun AppSettingsScreen(navController: NavController) {
Column(
modifier = Modifier
.weight(1f)
+ .padding(vertical = 8.dp)
.padding(end = 4.dp)
) {
Text(
@@ -255,11 +312,6 @@ fun AppSettingsScreen(navController: NavController) {
Row(
modifier = Modifier
.fillMaxWidth()
- .height(85.sp.value.dp)
- .background(
- shape = RoundedCornerShape(14.dp),
- color = Color.Transparent
- )
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
@@ -271,6 +323,7 @@ fun AppSettingsScreen(navController: NavController) {
Column(
modifier = Modifier
.weight(1f)
+ .padding(vertical = 8.dp)
.padding(end = 4.dp)
) {
Text(
@@ -295,20 +348,31 @@ fun AppSettingsScreen(navController: NavController) {
)
}
+ Text(
+ text = "Conversational Awareness Volume",
+ fontSize = 16.sp,
+ color = textColor,
+ modifier = Modifier.padding(top = 8.dp, bottom = 4.dp)
+ )
+
+ val trackColor = if (isDarkTheme) Color(0xFFB3B3B3) else Color(0xFFD9D9D9)
val activeTrackColor = if (isDarkTheme) Color(0xFF007AFF) else Color(0xFF3C6DF5)
+ val thumbColor = if (isDarkTheme) Color(0xFFFFFFFF) else Color(0xFFFFFFFF)
Slider(
value = sliderValue.floatValue,
onValueChange = {
sliderValue.floatValue = it
+ sharedPreferences.edit().putInt("conversational_awareness_volume", it.toInt()).apply()
},
valueRange = 10f..85f,
onValueChangeFinished = {
sliderValue.floatValue = sliderValue.floatValue.roundToInt().toFloat()
},
modifier = Modifier
- .weight(1f)
- .height(36.dp),
+ .fillMaxWidth()
+ .height(36.dp)
+ .padding(vertical = 4.dp),
colors = SliderDefaults.colors(
thumbColor = thumbColor,
activeTrackColor = activeTrackColor,
@@ -347,7 +411,9 @@ fun AppSettingsScreen(navController: NavController) {
)
Row(
- modifier = Modifier.fillMaxWidth(),
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(bottom = 8.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
@@ -355,7 +421,7 @@ fun AppSettingsScreen(navController: NavController) {
style = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Light,
- color = labelTextColor
+ color = textColor.copy(alpha = 0.7f)
),
modifier = Modifier.padding(start = 4.dp)
)
@@ -364,15 +430,26 @@ fun AppSettingsScreen(navController: NavController) {
style = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Light,
- color = labelTextColor
+ color = textColor.copy(alpha = 0.7f)
),
modifier = Modifier.padding(end = 4.dp)
)
}
}
- Spacer(modifier = Modifier.height(24.dp))
-
+ Text(
+ text = "Quick Settings Tile".uppercase(),
+ 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 = 24.dp)
+ )
+
+ Spacer(modifier = Modifier.height(2.dp))
+
Column(
modifier = Modifier
.fillMaxWidth()
@@ -380,14 +457,8 @@ fun AppSettingsScreen(navController: NavController) {
backgroundColor,
RoundedCornerShape(14.dp)
)
- .padding(horizontal = 16.dp, vertical = 8.dp)
+ .padding(horizontal = 16.dp, vertical = 4.dp)
) {
- var openDialogForControlling by remember {
- mutableStateOf(
- sharedPreferences.getString("qs_click_behavior", "dialog") == "dialog"
- )
- }
-
fun updateQsClickBehavior(enabled: Boolean) {
openDialogForControlling = enabled
sharedPreferences.edit().putString("qs_click_behavior", if (enabled) "dialog" else "cycle").apply()
@@ -407,7 +478,7 @@ fun AppSettingsScreen(navController: NavController) {
Column(
modifier = Modifier
.weight(1f)
- .padding(vertical = 16.dp)
+ .padding(vertical = 8.dp)
.padding(end = 4.dp)
) {
Text(
@@ -435,7 +506,83 @@ fun AppSettingsScreen(navController: NavController) {
}
}
- Spacer(modifier = Modifier.height(24.dp))
+ Text(
+ text = "Ear Detection".uppercase(),
+ 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 = 24.dp)
+ )
+
+ Spacer(modifier = Modifier.height(2.dp))
+
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(
+ backgroundColor,
+ RoundedCornerShape(14.dp)
+ )
+ .padding(horizontal = 16.dp, vertical = 4.dp)
+ ) {
+ fun updateDisconnectWhenNotWearing(enabled: Boolean) {
+ disconnectWhenNotWearing = enabled
+ sharedPreferences.edit().putBoolean("disconnect_when_not_wearing", enabled).apply()
+ }
+
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable(
+ indication = null,
+ interactionSource = remember { MutableInteractionSource() }
+ ) {
+ updateDisconnectWhenNotWearing(!disconnectWhenNotWearing)
+ },
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Column(
+ modifier = Modifier
+ .weight(1f)
+ .padding(vertical = 8.dp)
+ .padding(end = 4.dp)
+ ) {
+ Text(
+ text = "Disconnect AirPods when not wearing",
+ fontSize = 16.sp,
+ color = textColor
+ )
+ Spacer(modifier = Modifier.height(4.dp))
+ Text(
+ text = "You will still be able to control them with the app - this just disconnects the audio.",
+ fontSize = 14.sp,
+ color = textColor.copy(0.6f),
+ lineHeight = 16.sp,
+ )
+ }
+
+ StyledSwitch(
+ checked = disconnectWhenNotWearing,
+ onCheckedChange = {
+ updateDisconnectWhenNotWearing(it)
+ }
+ )
+ }
+ }
+
+ Text(
+ text = "Advanced Options".uppercase(),
+ 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 = 24.dp)
+ )
Button(
onClick = { showResetDialog = true },
@@ -470,6 +617,8 @@ fun AppSettingsScreen(navController: NavController) {
}
}
+ Spacer(modifier = Modifier.height(32.dp))
+
if (showResetDialog) {
AlertDialog(
onDismissRequest = { showResetDialog = false },
diff --git a/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsService.kt b/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsService.kt
index f89f650..7f8e171 100644
--- a/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsService.kt
+++ b/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsService.kt
@@ -166,6 +166,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
var longPressOff: Boolean = false,
var volumeControl: Boolean = true,
var headGestures: Boolean = true,
+ var disconnectWhenNotWearing: Boolean = false,
var adaptiveStrength: Int = 51,
var toneVolume: Int = 75,
var conversationalAwarenessVolume: Int = 43,
@@ -221,6 +222,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
longPressOff = sharedPreferences.getBoolean("long_press_off", false),
volumeControl = sharedPreferences.getBoolean("volume_control", true),
headGestures = sharedPreferences.getBoolean("head_gestures", true),
+ disconnectWhenNotWearing = sharedPreferences.getBoolean("disconnect_when_not_wearing", false),
adaptiveStrength = sharedPreferences.getInt("adaptive_strength", 51),
toneVolume = sharedPreferences.getInt("tone_volume", 75),
conversationalAwarenessVolume = sharedPreferences.getInt("conversational_awareness_volume", 43),
@@ -256,6 +258,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
"long_press_off" -> config.longPressOff = preferences.getBoolean(key, false)
"volume_control" -> config.volumeControl = preferences.getBoolean(key, true)
"head_gestures" -> config.headGestures = preferences.getBoolean(key, true)
+ "disconnect_when_not_wearing" -> config.disconnectWhenNotWearing = preferences.getBoolean(key, false)
"adaptive_strength" -> config.adaptiveStrength = preferences.getInt(key, 51)
"tone_volume" -> config.toneVolume = preferences.getInt(key, 75)
"conversational_awareness_volume" -> config.conversationalAwarenessVolume = preferences.getInt(key, 43)
@@ -1026,6 +1029,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
if (!contains("long_press_off")) editor.putBoolean("long_press_off", false)
if (!contains("volume_control")) editor.putBoolean("volume_control", true)
if (!contains("head_gestures")) editor.putBoolean("head_gestures", true)
+ if (!contains("disconnect_when_not_wearing")) editor.putBoolean("disconnect_when_not_wearing", false)
if (!contains("adaptive_strength")) editor.putInt("adaptive_strength", 51)
if (!contains("tone_volume")) editor.putInt("tone_volume", 75)
@@ -1498,7 +1502,9 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
}
} else if (newInEarData == listOf(false, false)) {
MediaController.sendPause(force = true)
- disconnectAudio(this@AirPodsService, device)
+ if (config.disconnectWhenNotWearing) {
+ disconnectAudio(this@AirPodsService, device)
+ }
}
if (inEarData.contains(false) && newInEarData == listOf(
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index cc67374..06bcc06 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -34,8 +34,7 @@
AirPods not connected
Please connect your AirPods to access settings. If you\'re stuck here, then try reopening the app again after closing it from the recents.\n(DO NOT KILL THE APP!)
Back
- App Settings
- Conversational Awareness
+ Customizations
Relative volume
Reduces to a percentage of the current volume instead of the maximum volume.
Pause Music