Compare commits

...

9 Commits

Author SHA1 Message Date
Kavish Devar 53679cc902 android: bump version
just doing this because play looks "version code up, let's update!" even if it's not installed from Play, and I can't make another rc1.
2026-09-01 00:13:05 +05:30
Kavish Devar b4cacb89f1 android: adjust pre-first connect text style in m3e 2026-08-27 15:50:48 +05:30
Kavish Devar cf6d226fca android: add demo to demo airpods name 2026-08-27 15:44:10 +05:30
Kavish Devar 4c7a3cb203 android: don't force connectionSuccessful to true in demo state 2026-08-27 15:41:02 +05:30
Kavish Devar 1783bb742e android: remove bypass when going back to 'not supported' page in onboarding 2026-08-27 15:29:43 +05:30
Kavish Devar 3cc4d92d62 android: bump version 2026-08-27 15:26:19 +05:30
Kavish Devar 95ecc0eb3d android: show bypassed message on settings page before first connect 2026-08-27 15:26:10 +05:30
Kavish Devar a7537f2361 android: make bypass compatibility check button scroll to next onboarding page 2026-08-27 15:24:52 +05:30
Kavish Devar 21babd819f android: hide reconnect button in m3e when no successful aacp connection 2026-08-27 15:16:28 +05:30
5 changed files with 109 additions and 79 deletions
@@ -736,96 +736,116 @@ fun AirPodsSettingsScreen(
val animatedShapeColor by animateColorAsState(if (reconnecting) primaryContainerColor else secondaryContainerColor)
Box(
modifier = Modifier
.size(240.dp)
.background(
MaterialTheme.colorScheme.surfaceBright,
CircleShape
)
.clickable(
interactionSource = null,
indication = ripple(
bounded = false,
radius = 120.dp
),
enabled = !reconnecting,
onClick = {}
)
.pointerInput(Unit) {
detectTapGestures(
onTap = {
if (!reconnecting) {
currentMorphIndex = 1
reconnecting = true
reconnectFromSavedMac()
}
},
onPress = {
if (!reconnecting) {
morphProgress.animateTo(
targetValue = 1f,
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessLow
if (state.connectionSuccessful) {
Box(
modifier = Modifier
.size(240.dp)
.background(
MaterialTheme.colorScheme.surfaceBright,
CircleShape
)
.clickable(
interactionSource = null,
indication = ripple(
bounded = false,
radius = 120.dp
),
enabled = !reconnecting,
onClick = {}
)
.pointerInput(Unit) {
detectTapGestures(
onTap = {
if (!reconnecting) {
currentMorphIndex = 1
reconnecting = true
reconnectFromSavedMac()
}
},
onPress = {
if (!reconnecting) {
morphProgress.animateTo(
targetValue = 1f,
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessLow
)
)
)
tryAwaitRelease()
morphProgress.animateTo(
targetValue = 0f,
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessLow
tryAwaitRelease()
morphProgress.animateTo(
targetValue = 0f,
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessLow
)
)
)
}
}
}
)
}
.drawWithContent {
val activeMorph = morphs[currentMorphIndex]
)
}
.drawWithContent {
val activeMorph = morphs[currentMorphIndex]
val shapePath = activeMorph.toPath(
progress = morphProgress.value,
path = path
)
val shapePath = activeMorph.toPath(
progress = morphProgress.value,
path = path
)
val bounds = shapePath.getBounds()
val bounds = shapePath.getBounds()
val scale = min(size.width/bounds.width, size.height/bounds.height) * 0.8f
val scale = min(
size.width / bounds.width,
size.height / bounds.height
) * 0.8f
scaleMatrix.reset()
scaleMatrix.reset()
scaleMatrix.scale(x = scale, y = scale)
scaleMatrix.scale(x = scale, y = scale)
shapePath.transform(scaleMatrix)
shapePath.transform(scaleMatrix)
shapePath.translate(size.center - shapePath.getBounds().center)
shapePath.translate(size.center - shapePath.getBounds().center)
drawPath(
path = shapePath,
color = animatedShapeColor
)
drawPath(
path = shapePath,
color = animatedShapeColor
)
drawContent()
},
contentAlignment = Alignment.Center
) {
Icon(
imageVector = if (reconnecting) MaterialIcons.bluetooth_searching else MaterialIcons.headset_off,
contentDescription = null,
modifier = Modifier.size(84.dp),
tint = if (reconnecting) MaterialTheme.colorScheme.onPrimaryContainer else MaterialTheme.colorScheme.onSecondaryContainer
drawContent()
},
contentAlignment = Alignment.Center
) {
Icon(
imageVector = if (reconnecting) MaterialIcons.bluetooth_searching else MaterialIcons.headset_off,
contentDescription = null,
modifier = Modifier.size(84.dp),
tint = if (reconnecting) MaterialTheme.colorScheme.onPrimaryContainer else MaterialTheme.colorScheme.onSecondaryContainer
)
}
Spacer(Modifier.height(40.dp))
Text(
text = if (reconnecting) stringResource(R.string.reconnecting) else stringResource(R.string.tap_to_reconnect),
style = MaterialTheme.typography.labelSmallEmphasized,
color = MaterialTheme.colorScheme.primary
)
} else {
Text(
text = stringResource(R.string.airpods_not_connected),
style = MaterialTheme.typography.headlineMedium,
color = MaterialTheme.colorScheme.primary
)
if (sharedPreferences.getBoolean("bypass_device_check.v2", false)) {
Spacer(modifier = Modifier.height(16.dp))
Text(
text = stringResource(R.string.compatibility_check_bypassed),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurface,
textAlign = TextAlign.Center
)
}
}
Spacer(Modifier.height(40.dp))
Text(
text = if (reconnecting) stringResource(R.string.reconnecting) else stringResource(R.string.tap_to_reconnect),
style = MaterialTheme.typography.labelSmallEmphasized,
color = MaterialTheme.colorScheme.primary
)
}
if (!BuildConfig.PLAY_BUILD) {
@@ -47,6 +47,7 @@ import me.kavishdevar.librepods.presentation.theme.LibrePodsTheme
import me.kavishdevar.librepods.utils.XposedState
import me.kavishdevar.librepods.utils.bypassDeviceCheck
import me.kavishdevar.librepods.utils.isSupported
import me.kavishdevar.librepods.utils.removeDeviceCheckBypass
@OptIn(ExperimentalPermissionsApi::class, ExperimentalMaterial3Api::class)
@Composable
@@ -191,6 +192,9 @@ fun OnboardingScreen(
NotSupportedPage(
bypassCompatibilityCheck = {
bypassDeviceCheck(sharedPreferences)
animationScope.launch {
state.animateScrollToItem(3)
}
}
)
}
@@ -199,6 +203,9 @@ fun OnboardingScreen(
onBackward = {
animationScope.launch {
if (state.canScrollBackward) state.animateScrollToItem(if (isSupported) 1 else 2)
if (!isSupported) {
removeDeviceCheckBypass(sharedPreferences)
}
}
},
onForward = onOnboardingComplete
@@ -105,7 +105,7 @@ data class AirPodsUiState(
)
val demoInstance = AirPodsInstance(
name = "AirPods Pro",
name = "[DEMO] AirPods Pro",
model = AirPodsModels.getModelByModelNumber("A3064")!!,
actualModelNumber = "A3064",
serialNumber = "JXF9Q94A40",
@@ -160,8 +160,6 @@ val demoState = AirPodsUiState(
dynamicEndOfCharge = true,
connectionSuccessful = true,
customEq = CustomEq(state = 2, low = 65, mid = 50, high = 70),
controlStates = mapOf(
@@ -42,3 +42,7 @@ fun isSupported(sharedPreferences: SharedPreferences): Boolean {
fun bypassDeviceCheck(sharedPreferences: SharedPreferences) {
sharedPreferences.edit{ putBoolean("bypass_device_check.v2", true) }
}
fun removeDeviceCheckBypass(sharedPreferences: SharedPreferences) {
sharedPreferences.edit{ putBoolean("bypass_device_check.v2", false) }
}
@@ -290,4 +290,5 @@
<string name="permissions">Permissions</string>
<string name="privacy_policy">Privacy Policy</string>
<string name="i_agree">I agree</string>
<string name="compatibility_check_bypassed">Compatibility check bypassed. Are you sure your device is compatible?</string>
</resources>