Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 20 additions & 14 deletions android/app/src/main/java/me/kavishdevar/librepods/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ import androidx.compose.ui.graphics.drawscope.rotate
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.statusBars
import androidx.compose.ui.platform.LocalWindowInfo
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
Expand All @@ -106,6 +109,7 @@ import androidx.core.net.toUri
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import me.kavishdevar.librepods.utils.popBackStackSafely
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.MultiplePermissionsState
import com.google.accompanist.permissions.isGranted
Expand Down Expand Up @@ -424,6 +428,8 @@ fun Main() {
}
}

val statusBarPadding = WindowInsets.statusBars.asPaddingValues().calculateTopPadding()

AnimatedVisibility(
visible = showBackButton.value,
enter = fadeIn(animationSpec = tween()) + scaleIn(initialScale = 0f, animationSpec = tween()),
Expand All @@ -432,11 +438,11 @@ fun Main() {
.align(Alignment.TopStart)
.padding(
start = 8.dp,
top = (LocalWindowInfo.current.containerSize.width * 0.05f).dp
top = statusBarPadding
)
) {
StyledIconButton(
onClick = { navController.popBackStack() },
onClick = { navController.popBackStackSafely() },
icon = "􀯶",
darkMode = isSystemInDarkTheme(),
backdrop = backButtonBackdrop
Expand Down Expand Up @@ -551,7 +557,7 @@ fun PermissionsScreen(
Spacer(modifier = Modifier.height(16.dp))

Text(
text = "Permission Required",
text = stringResource(R.string.permission_required_title),
style = TextStyle(
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
Expand Down Expand Up @@ -579,8 +585,8 @@ fun PermissionsScreen(
Spacer(modifier = Modifier.height(32.dp))

PermissionCard(
title = "Bluetooth Permissions",
description = "Required to communicate with your AirPods",
title = stringResource(R.string.bluetooth_permissions),
description = stringResource(R.string.bluetooth_permissions_desc),
icon = ImageVector.vectorResource(id = R.drawable.ic_bluetooth),
isGranted = permissionState.permissions.filter {
it.permission.contains("BLUETOOTH")
Expand All @@ -591,8 +597,8 @@ fun PermissionsScreen(
)

PermissionCard(
title = "Notification Permission",
description = "To show battery status",
title = stringResource(R.string.notification_permission),
description = stringResource(R.string.notification_permission_desc),
icon = Icons.Default.Notifications,
isGranted = permissionState.permissions.find {
it.permission == "android.permission.POST_NOTIFICATIONS"
Expand All @@ -603,8 +609,8 @@ fun PermissionsScreen(
)

PermissionCard(
title = "Phone Permissions",
description = "For answering calls with Head Gestures",
title = stringResource(R.string.phone_permissions),
description = stringResource(R.string.phone_permissions_desc),
icon = Icons.Default.Phone,
isGranted = permissionState.permissions.filter {
it.permission.contains("PHONE") || it.permission.contains("CALLS")
Expand All @@ -615,8 +621,8 @@ fun PermissionsScreen(
)

PermissionCard(
title = "Display Over Other Apps",
description = "For popup animations when AirPods connect",
title = stringResource(R.string.display_over_other_apps),
description = stringResource(R.string.display_over_other_apps_desc),
icon = ImageVector.vectorResource(id = R.drawable.ic_layers),
isGranted = canDrawOverlays,
backgroundColor = backgroundColor,
Expand All @@ -637,7 +643,7 @@ fun PermissionsScreen(
shape = RoundedCornerShape(8.dp)
) {
Text(
"Ask for regular permissions",
stringResource(R.string.ask_regular_permissions),
style = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
Expand Down Expand Up @@ -668,7 +674,7 @@ fun PermissionsScreen(
shape = RoundedCornerShape(8.dp)
) {
Text(
if (canDrawOverlays) "Overlay Permission Granted" else "Grant Overlay Permission",
if (canDrawOverlays) stringResource(R.string.overlay_permission_granted) else stringResource(R.string.grant_overlay_permission),
style = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
Expand Down Expand Up @@ -700,7 +706,7 @@ fun PermissionsScreen(
shape = RoundedCornerShape(8.dp)
) {
Text(
"Continue without overlay",
stringResource(R.string.continue_without_overlay),
style = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ import androidx.compose.ui.graphics.graphicsLayer
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.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
Expand Down Expand Up @@ -597,7 +598,7 @@ fun NewControlCenterDialogContent(
Spacer(modifier = Modifier.height(8.dp))

Text(
text = "Conversational\nAwareness",
text = stringResource(R.string.conversational_awareness).replace(" ", "\n"),
color = Color.White,
fontSize = 12.sp,
fontWeight = FontWeight.Medium,
Expand All @@ -613,7 +614,7 @@ fun NewControlCenterDialogContent(
} else {
Spacer(modifier = Modifier.weight(1f))
Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
Text("Loading...", color = textColor)
Text(stringResource(R.string.loading), color = textColor)
}
Spacer(modifier = Modifier.weight(1f))
}
Expand All @@ -629,11 +630,12 @@ private fun getModeIconRes(mode: NoiseControlMode): Int {
}
}

@Composable
private fun getModeLabel(mode: NoiseControlMode): String {
return when (mode) {
NoiseControlMode.OFF -> "Off"
NoiseControlMode.TRANSPARENCY -> "Transparency"
NoiseControlMode.ADAPTIVE -> "Adaptive"
NoiseControlMode.NOISE_CANCELLATION -> "Noise Cancel"
NoiseControlMode.OFF -> stringResource(R.string.off)
NoiseControlMode.TRANSPARENCY -> stringResource(R.string.transparency)
NoiseControlMode.ADAPTIVE -> stringResource(R.string.adaptive)
NoiseControlMode.NOISE_CANCELLATION -> stringResource(R.string.noise_cancel)
}
Comment on lines +633 to 640
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if both noise_cancel and noise_cancellation string resources exist
echo "=== Checking strings.xml for noise_cancel and noise_cancellation ==="
rg -n "noise_cancel" android/app/src/main/res/values/strings.xml
rg -n "noise_cancellation" android/app/src/main/res/values/strings.xml

Repository: kavishdevar/librepods

Length of output: 1071


🏁 Script executed:

#!/bin/bash
# Check the actual usage in AirPodsQSService.kt around line 257
echo "=== AirPodsQSService.kt around line 257 ==="
rg -n "noise_cancellation\|noise_cancel" android/app/src/main/java/me/kavishdevar/librepods/AirPodsQSService.kt
echo ""
# Check if the function in QuickSettingsDialogActivity.kt exists and verify line numbers
echo "=== QuickSettingsDialogActivity.kt - getModeLabel function ==="
rg -n "getModeLabel\|noise_cancel" android/app/src/main/java/me/kavishdevar/librepods/QuickSettingsDialogActivity.kt | head -20

Repository: kavishdevar/librepods

Length of output: 300


🏁 Script executed:

#!/bin/bash
# Find the actual file locations
echo "=== Finding AirPodsQSService.kt ==="
fd "AirPodsQSService.kt" android/

echo ""
echo "=== Finding QuickSettingsDialogActivity.kt ==="
fd "QuickSettingsDialogActivity.kt" android/

Repository: kavishdevar/librepods

Length of output: 310


🏁 Script executed:

#!/bin/bash
# Check usage in AirPodsQSService.kt around line 257
echo "=== AirPodsQSService.kt - searching for noise_cancel/noise_cancellation ==="
rg -n "noise_cancel|noise_cancellation" android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsQSService.kt

echo ""
# Check usage in QuickSettingsDialogActivity.kt 
echo "=== QuickSettingsDialogActivity.kt - getModeLabel function ==="
rg -n "getModeLabel|noise_cancel|noise_cancellation" android/app/src/main/java/me/kavishdevar/librepods/QuickSettingsDialogActivity.kt | head -30

Repository: kavishdevar/librepods

Length of output: 962


Consolidate string resource keys for Noise Cancellation mode.

AirPodsQSService.kt (line 257) uses R.string.noise_cancellation ("Noise Cancellation"), while QuickSettingsDialogActivity.kt (line 639) uses R.string.noise_cancel ("Noise Cancel") for the same NoiseControlMode.NOISE_CANCELLATION. Both resources exist but with different text. Standardize on a single resource key across both files for consistency.

🤖 Prompt for AI Agents
In
android/app/src/main/java/me/kavishdevar/librepods/QuickSettingsDialogActivity.kt
around lines 633 to 640, the NoiseControlMode.NOISE_CANCELLATION case returns
stringResource(R.string.noise_cancel) which is inconsistent with
AirPodsQSService.kt that uses R.string.noise_cancellation; change the
QuickSettingsDialogActivity usage to stringResource(R.string.noise_cancellation)
so both files reference the same resource key (or alternatively update both to a
single chosen key), and run a quick build/strings search to ensure no other
mismatched keys remain.

}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,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.utils.navigateDebounced

@Composable
fun NavigationButton(
Expand Down Expand Up @@ -95,7 +96,7 @@ fun NavigationButton(
backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
},
onTap = {
if (onClick != null) onClick() else navController.navigate(to)
if (onClick != null) onClick() else navController.navigateDebounced(to)
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import androidx.compose.ui.unit.sp
import androidx.core.content.edit
import androidx.core.net.toUri
import androidx.navigation.NavController
import me.kavishdevar.librepods.utils.navigateDebounced
import androidx.navigation.compose.rememberNavController
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
import com.kyant.backdrop.drawBackdrop
Expand Down Expand Up @@ -227,7 +228,7 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService,
actionButtons = listOf(
{scaffoldBackdrop ->
StyledIconButton(
onClick = { navController.navigate("app_settings") },
onClick = { navController.navigateDebounced("app_settings") },
icon = "􀍟",
darkMode = darkMode,
backdrop = scaffoldBackdrop
Expand Down Expand Up @@ -380,7 +381,7 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService,
)
Spacer(Modifier.height(32.dp))
StyledButton(
onClick = { navController.navigate("troubleshooting") },
onClick = { navController.navigateDebounced("troubleshooting") },
backdrop = backdrop,
modifier = Modifier
.fillMaxWidth(0.9f)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.edit
import androidx.navigation.NavController
import me.kavishdevar.librepods.utils.navigateDebounced
import com.kyant.backdrop.backdrops.layerBackdrop
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
import dev.chrisbanes.haze.hazeSource
Expand Down Expand Up @@ -655,7 +656,7 @@ fun AppSettingsScreen(navController: NavController) {
onDismissRequest = { showResetDialog.value = false },
title = {
Text(
"Reset Hook Offset",
stringResource(R.string.reset_hook_offset),
fontFamily = FontFamily(Font(R.font.sf_pro)),
fontWeight = FontWeight.Medium
)
Expand All @@ -678,7 +679,7 @@ fun AppSettingsScreen(navController: NavController) {
Toast.LENGTH_LONG
).show()

navController.navigate("onboarding") {
navController.navigateDebounced("onboarding") {
popUpTo("settings") { inclusive = true }
}
} else {
Expand Down Expand Up @@ -706,7 +707,7 @@ fun AppSettingsScreen(navController: NavController) {
onClick = { showResetDialog.value = false }
) {
Text(
"Cancel",
stringResource(R.string.cancel),
fontFamily = FontFamily(Font(R.font.sf_pro)),
fontWeight = FontWeight.Medium
)
Expand Down Expand Up @@ -761,10 +762,12 @@ fun AppSettingsScreen(navController: NavController) {
confirmButton = {
val successText = stringResource(R.string.irk_set_success)
val errorText = stringResource(R.string.error_converting_hex)
val unknownErrorText = stringResource(R.string.unknown_error)
val hexValidationError = stringResource(R.string.must_be_32_hex_chars)
TextButton(
onClick = {
if (!validateHexInput(irkValue.value)) {
irkError.value = "Must be exactly 32 hex characters"
irkError.value = hexValidationError
return@TextButton
}

Expand All @@ -781,12 +784,12 @@ fun AppSettingsScreen(navController: NavController) {
Toast.makeText(context, successText, Toast.LENGTH_SHORT).show()
showIrkDialog.value = false
} catch (e: Exception) {
irkError.value = errorText + " " + (e.message ?: "Unknown error")
irkError.value = errorText + " " + (e.message ?: unknownErrorText)
}
}
) {
Text(
"Save",
stringResource(R.string.save),
fontFamily = FontFamily(Font(R.font.sf_pro)),
fontWeight = FontWeight.Medium
)
Expand All @@ -797,7 +800,7 @@ fun AppSettingsScreen(navController: NavController) {
onClick = { showIrkDialog.value = false }
) {
Text(
"Cancel",
stringResource(R.string.cancel),
fontFamily = FontFamily(Font(R.font.sf_pro)),
fontWeight = FontWeight.Medium
)
Expand Down Expand Up @@ -852,10 +855,12 @@ fun AppSettingsScreen(navController: NavController) {
confirmButton = {
val successText = stringResource(R.string.encryption_key_set_success)
val errorText = stringResource(R.string.error_converting_hex)
val unknownErrorText = stringResource(R.string.unknown_error)
val hexValidationError = stringResource(R.string.must_be_32_hex_chars)
TextButton(
onClick = {
if (!validateHexInput(encKeyValue.value)) {
encKeyError.value = "Must be exactly 32 hex characters"
encKeyError.value = hexValidationError
return@TextButton
}

Expand All @@ -872,12 +877,12 @@ fun AppSettingsScreen(navController: NavController) {
Toast.makeText(context, successText, Toast.LENGTH_SHORT).show()
showEncKeyDialog.value = false
} catch (e: Exception) {
encKeyError.value = errorText + " " + (e.message ?: "Unknown error")
encKeyError.value = errorText + " " + (e.message ?: unknownErrorText)
}
}
) {
Text(
"Save",
stringResource(R.string.save),
fontFamily = FontFamily(Font(R.font.sf_pro)),
fontWeight = FontWeight.Medium
)
Expand All @@ -888,7 +893,7 @@ fun AppSettingsScreen(navController: NavController) {
onClick = { showEncKeyDialog.value = false }
) {
Text(
"Cancel",
stringResource(R.string.cancel),
fontFamily = FontFamily(Font(R.font.sf_pro)),
fontWeight = FontWeight.Medium
)
Expand Down Expand Up @@ -957,7 +962,7 @@ fun AppSettingsScreen(navController: NavController) {
}
) {
Text(
"Save",
stringResource(R.string.save),
fontFamily = FontFamily(Font(R.font.sf_pro)),
fontWeight = FontWeight.Medium
)
Expand All @@ -968,7 +973,7 @@ fun AppSettingsScreen(navController: NavController) {
onClick = { showCameraDialog.value = false }
) {
Text(
"Cancel",
stringResource(R.string.cancel),
fontFamily = FontFamily(Font(R.font.sf_pro)),
fontWeight = FontWeight.Medium
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,10 @@ fun HearingAidScreen(navController: NavController) {

ConfirmationDialog(
showDialog = showDialog,
title = "Enable Hearing Aid",
message = "Enabling Hearing Aid will disable Headphone Accommodation and Customized Transparency Mode.",
confirmText = "Enable",
dismissText = "Cancel",
title = stringResource(R.string.enable_hearing_aid),
message = stringResource(R.string.enable_hearing_aid_msg),
confirmText = stringResource(R.string.enable),
dismissText = stringResource(R.string.cancel),
onConfirm = {
showDialog.value = false
val enrolled = aacpManager?.controlCommandStatusList?.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID }?.value?.getOrNull(0) == 0x01.toByte()
Expand Down
Loading