Skip to content

Commit 0b578d6

Browse files
committed
android: add more compatibility information, fix FOSS billing, hide upgrade button before first AACP connect
closes #538
1 parent 072b9b4 commit 0b578d6

31 files changed

Lines changed: 1501 additions & 778 deletions

android/app/build.gradle.kts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ android {
2828
applicationId = "me.kavishdevar.librepods"
2929
minSdk = 33
3030
targetSdk = 37
31-
versionCode = 34
31+
versionCode = 36
3232
versionName = "0.2.3"
3333
}
3434
buildTypes {
@@ -50,14 +50,17 @@ android {
5050
debug {
5151
buildConfigField("Boolean", "PLAY_BUILD", "false")
5252
signingConfig = signingConfigs.getByName("release")
53+
versionNameSuffix = "-debug"
5354
}
5455
create("playRelease") {
5556
initWith(getByName("release"))
5657
buildConfigField("Boolean", "PLAY_BUILD", "true")
58+
versionNameSuffix = "-play"
5759
}
5860
create("playDebug") {
5961
initWith(getByName("debug"))
6062
buildConfigField("Boolean", "PLAY_BUILD", "true")
63+
versionNameSuffix = "-youshouldnothavethis"
6164
}
6265
}
6366
compileOptions {
@@ -104,7 +107,6 @@ android {
104107
arguments += "-DIS_XPOSED=ON"
105108
}
106109
}
107-
versionNameSuffix = "-xposed"
108110
}
109111
}
110112
}
@@ -113,6 +115,7 @@ dependencies {
113115
implementation(platform(libs.androidx.compose.bom))
114116
implementation(libs.accompanist.permissions)
115117
implementation(libs.androidx.core.ktx)
118+
implementation(libs.androidx.lifecycle.process)
116119
implementation(libs.androidx.lifecycle.runtime.ktx)
117120
implementation(libs.androidx.activity.compose)
118121
implementation(libs.androidx.ui)

android/app/src/main/AndroidManifest.xml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,18 @@
1414
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
1515
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
1616
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
17-
<!-- <uses-permission-->
18-
<!-- android:name="android.permission.BLUETOOTH_PRIVILEGED"-->
19-
<!-- tools:ignore="ProtectedPermissions" />-->
17+
<uses-permission
18+
android:name="android.permission.BLUETOOTH_PRIVILEGED"
19+
tools:ignore="ProtectedPermissions" />
20+
<uses-permission
21+
android:name="android.permission.MODIFY_PHONE_STATE"
22+
tools:ignore="ProtectedPermissions" />
23+
<uses-permission
24+
android:name="android.permission.LOCAL_MAC_ADDRESS"
25+
tools:ignore="ProtectedPermissions" />
26+
<uses-permission
27+
android:name="android.permission.INTERACT_ACROSS_USERS"
28+
tools:ignore="ProtectedPermissions" />
2029
<uses-permission android:name="android.permission.BLUETOOTH" />
2130
<uses-permission
2231
android:name="android.permission.BLUETOOTH_SCAN"
@@ -27,8 +36,6 @@
2736
<uses-permission android:name="android.permission.ANSWER_PHONE_CALLS" />
2837
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
2938
<!-- <uses-permission android:name="android.permission.INTERNET" />-->
30-
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
31-
tools:ignore="ScopedStorage" />
3239
<!-- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"-->
3340
<!-- android:maxSdkVersion="30" />-->
3441
<!-- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"-->

android/app/src/main/java/me/kavishdevar/librepods/MainActivity.kt

Lines changed: 81 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ import androidx.compose.animation.slideInHorizontally
5252
import androidx.compose.animation.slideOutHorizontally
5353
import androidx.compose.foundation.Canvas
5454
import androidx.compose.foundation.background
55-
import androidx.compose.foundation.gestures.detectTapGestures
5655
import androidx.compose.foundation.isSystemInDarkTheme
5756
import androidx.compose.foundation.layout.Arrangement
5857
import androidx.compose.foundation.layout.Box
@@ -64,7 +63,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
6463
import androidx.compose.foundation.layout.height
6564
import androidx.compose.foundation.layout.padding
6665
import androidx.compose.foundation.layout.size
67-
import androidx.compose.foundation.layout.width
6866
import androidx.compose.foundation.rememberScrollState
6967
import androidx.compose.foundation.shape.RoundedCornerShape
7068
import androidx.compose.foundation.verticalScroll
@@ -81,8 +79,6 @@ import androidx.compose.material3.Text
8179
import androidx.compose.runtime.Composable
8280
import androidx.compose.runtime.LaunchedEffect
8381
import androidx.compose.runtime.getValue
84-
import androidx.compose.runtime.mutableIntStateOf
85-
import androidx.compose.runtime.mutableLongStateOf
8682
import androidx.compose.runtime.mutableStateOf
8783
import androidx.compose.runtime.remember
8884
import androidx.compose.runtime.setValue
@@ -94,8 +90,6 @@ import androidx.compose.ui.geometry.Offset
9490
import androidx.compose.ui.graphics.Color
9591
import androidx.compose.ui.graphics.drawscope.rotate
9692
import androidx.compose.ui.graphics.vector.ImageVector
97-
import androidx.compose.ui.input.pointer.PointerEventPass
98-
import androidx.compose.ui.input.pointer.pointerInput
9993
import androidx.compose.ui.platform.LocalContext
10094
import androidx.compose.ui.platform.LocalWindowInfo
10195
import androidx.compose.ui.res.stringResource
@@ -122,12 +116,11 @@ import com.kyant.backdrop.backdrops.rememberLayerBackdrop
122116
import dev.chrisbanes.haze.hazeSource
123117
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
124118
import dev.chrisbanes.haze.rememberHazeState
125-
import kotlinx.coroutines.delay
126-
import me.kavishdevar.librepods.billing.BillingManager
127-
import me.kavishdevar.librepods.billing.BillingProviderFactory
128119
import me.kavishdevar.librepods.data.AirPodsNotifications
129120
import me.kavishdevar.librepods.data.ControlCommandRepository
130121
import me.kavishdevar.librepods.presentation.components.ConfirmationDialog
122+
import me.kavishdevar.librepods.presentation.components.DeviceInfoCard
123+
import me.kavishdevar.librepods.presentation.components.PlayBypassSheet
131124
import me.kavishdevar.librepods.presentation.components.StyledButton
132125
import me.kavishdevar.librepods.presentation.components.StyledIconButton
133126
import me.kavishdevar.librepods.presentation.screens.AccessibilitySettingsScreen
@@ -148,6 +141,7 @@ import me.kavishdevar.librepods.presentation.screens.TransparencySettingsScreen
148141
import me.kavishdevar.librepods.presentation.screens.TroubleshootingScreen
149142
import me.kavishdevar.librepods.presentation.screens.UpdateHearingTestScreen
150143
import me.kavishdevar.librepods.presentation.screens.VersionScreen
144+
import me.kavishdevar.librepods.presentation.theme.LibrePodsTheme
151145
import me.kavishdevar.librepods.presentation.viewmodel.AirPodsViewModel
152146
import me.kavishdevar.librepods.presentation.viewmodel.AppSettingsViewModel
153147
import me.kavishdevar.librepods.presentation.viewmodel.PurchaseViewModel
@@ -175,7 +169,7 @@ class MainActivity : ComponentActivity() {
175169
enableEdgeToEdge()
176170

177171
setContent {
178-
_root_ide_package_.me.kavishdevar.librepods.presentation.theme.LibrePodsTheme {
172+
LibrePodsTheme {
179173
Main()
180174
}
181175
}
@@ -224,81 +218,72 @@ fun Main() {
224218
val sharedPreferences = context.getSharedPreferences("settings", MODE_PRIVATE)
225219
if (!isSupported(sharedPreferences)) {
226220
val showDialog = remember { mutableStateOf(false) }
227-
221+
val showPlayBypassVisible = remember { mutableStateOf(false) }
228222
val hazeState = rememberHazeState()
223+
val backdrop = rememberLayerBackdrop()
224+
val isDarkTheme = isSystemInDarkTheme()
225+
val textColor = if (isDarkTheme) Color.White else Color.Black
226+
val backgroundColor = if (isSystemInDarkTheme()) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
229227

230228
Box(
231229
modifier = Modifier
232230
.fillMaxSize()
233231
.hazeSource(hazeState)
234-
.background(if (isSystemInDarkTheme()) Color.Black else Color(0xFFF2F2F7)),
232+
.layerBackdrop(backdrop)
233+
.background(if (isDarkTheme) Color.Black else Color(0xFFF2F2F7)),
235234
contentAlignment = Alignment.Center
236235
) {
237-
Box (
238-
modifier = Modifier
239-
.fillMaxSize()
240-
)
241236
Column (
242-
verticalArrangement = Arrangement.spacedBy(8.dp)
237+
modifier = Modifier.padding(horizontal = 16.dp),
238+
verticalArrangement = Arrangement
239+
.spacedBy(16.dp)
243240
) {
244-
Text(
245-
text = stringResource(R.string.not_supported),
246-
style = TextStyle(
247-
fontFamily = FontFamily(Font(R.font.sf_pro)),
248-
fontWeight = FontWeight.SemiBold,
249-
color = if (isSystemInDarkTheme()) Color.White else Color.Black,
250-
fontSize = 20.sp
251-
),
252-
textAlign = TextAlign.Center,
253-
modifier = Modifier.fillMaxWidth()
254-
)
255-
Row (
256-
modifier = Modifier.fillMaxWidth(),
257-
horizontalArrangement = Arrangement.Center
241+
val innerBackdrop = rememberLayerBackdrop()
242+
243+
Column(
244+
modifier = Modifier.layerBackdrop(innerBackdrop),
245+
verticalArrangement = Arrangement
246+
.spacedBy(16.dp)
258247
) {
259248
Text(
260-
text = "Device Info:",
249+
text = stringResource(R.string.not_supported),
261250
style = TextStyle(
262251
fontFamily = FontFamily(Font(R.font.sf_pro)),
263-
fontWeight = FontWeight.Medium,
264-
color = if (isSystemInDarkTheme()) Color.White else Color.Black,
265-
fontSize = 16.sp
266-
),
267-
textAlign = TextAlign.End,
268-
)
269-
Spacer(modifier = Modifier.width(4.dp))
270-
Text(
271-
text =
272-
"MANUFACTURER=${Build.MANUFACTURER}\n" +
273-
"MODEL=${Build.MODEL}\n" +
274-
"BUILD_ID=${Build.ID}\n" +
275-
"SDK_INT_FULL= ${Build.VERSION.SDK_INT_FULL}\n",
276-
style = TextStyle(
277-
fontFamily = FontFamily(Font(R.font.hack)),
278-
fontWeight = FontWeight.Medium,
279-
color = if (isSystemInDarkTheme()) Color.White else Color.Black,
280-
fontSize = 16.sp
252+
fontWeight = FontWeight.SemiBold,
253+
color = textColor,
254+
fontSize = 20.sp,
255+
textAlign = TextAlign.Center
281256
),
282-
textAlign = TextAlign.Start,
257+
modifier = Modifier.fillMaxWidth()
283258
)
259+
260+
DeviceInfoCard()
261+
262+
Box(
263+
modifier = Modifier
264+
.fillMaxWidth()
265+
.background(backgroundColor, RoundedCornerShape(28.dp))
266+
.clip(RoundedCornerShape(28.dp))
267+
) {
268+
Text(
269+
text = stringResource(R.string.check_the_repository_for_more_info),
270+
style = TextStyle(
271+
fontFamily = FontFamily(Font(R.font.sf_pro)),
272+
fontWeight = FontWeight.Medium,
273+
color = if (isSystemInDarkTheme()) Color.White else Color.Black,
274+
fontSize = 16.sp
275+
),
276+
modifier = Modifier
277+
.fillMaxWidth()
278+
.padding(horizontal = 12.dp, vertical = 16.dp)
279+
)
280+
}
284281
}
285-
Text(
286-
text = stringResource(R.string.check_the_repository_for_more_info),
287-
style = TextStyle(
288-
fontFamily = FontFamily(Font(R.font.sf_pro)),
289-
fontWeight = FontWeight.Medium,
290-
color = if (isSystemInDarkTheme()) Color.White else Color.Black,
291-
fontSize = 18.sp
292-
),
293-
textAlign = TextAlign.Center,
294-
modifier = Modifier.fillMaxWidth()
295-
)
296282
StyledButton(
297283
onClick = { showDialog.value = true },
298-
backdrop = rememberLayerBackdrop(),
284+
backdrop = innerBackdrop,
299285
modifier = Modifier
300286
.fillMaxWidth()
301-
.padding(8.dp)
302287
) {
303288
Text(
304289
text = stringResource(R.string.bypass_compatibility_check),
@@ -317,15 +302,19 @@ fun Main() {
317302
showDialog = showDialog,
318303
title = stringResource(R.string.bypass_compatibility_check),
319304
message = stringResource(R.string.bypass_compatiblity_check_confirmation),
320-
confirmText = "Yes",
321-
dismissText = "No",
305+
confirmText = stringResource(R.string.yes),
306+
dismissText = stringResource(R.string.no),
322307
onConfirm = {
323308
showDialog.value = false
324-
sharedPreferences.edit {
325-
putBoolean("bypass_device_check", true)
326-
val intent = Intent(context, MainActivity::class.java)
327-
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
328-
context.startActivity(intent)
309+
if (BuildConfig.PLAY_BUILD) {
310+
showPlayBypassVisible.value = true
311+
} else {
312+
sharedPreferences.edit {
313+
putBoolean("bypass_device_check", true)
314+
val intent = Intent(context, MainActivity::class.java)
315+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
316+
context.startActivity(intent)
317+
}
329318
}
330319
},
331320
onDismiss = {
@@ -334,6 +323,26 @@ fun Main() {
334323
hazeState = hazeState
335324
)
336325

326+
if (BuildConfig.PLAY_BUILD) {
327+
PlayBypassSheet(
328+
visible = showPlayBypassVisible.value,
329+
onDismiss = {
330+
showPlayBypassVisible.value = false
331+
showDialog.value = true
332+
},
333+
onConfirm = {
334+
showPlayBypassVisible.value = false
335+
sharedPreferences.edit {
336+
putBoolean("bypass_device_check", true)
337+
val intent = Intent(context, MainActivity::class.java)
338+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
339+
context.startActivity(intent)
340+
}
341+
},
342+
backdrop = backdrop
343+
)
344+
}
345+
337346
return
338347
}
339348

@@ -347,8 +356,6 @@ fun Main() {
347356
)
348357
}
349358

350-
BillingManager.provider = BillingProviderFactory.create(context)
351-
352359
val bluetoothPermissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
353360
listOf(
354361
"android.permission.BLUETOOTH_CONNECT",
@@ -484,7 +491,7 @@ fun Main() {
484491
if (airPodsViewModel != null) VersionScreen(airPodsViewModel)
485492
}
486493
composable("hearing_protection") {
487-
if (airPodsViewModel != null) HearingProtectionScreen(airPodsViewModel)
494+
if (airPodsViewModel != null) HearingProtectionScreen(airPodsViewModel, navController)
488495
}
489496
composable("purchase_screen") {
490497
val purchaseViewModel: PurchaseViewModel = viewModel()

android/app/src/main/java/me/kavishdevar/librepods/billing/BillingProvider.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,5 @@ interface BillingProvider {
2626
val price: StateFlow<String>
2727
fun purchase(activity: Activity)
2828
fun queryPurchases()
29+
fun restorePurchases()
2930
}

android/app/src/main/java/me/kavishdevar/librepods/billing/FOSSBillingProvider.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ import kotlinx.coroutines.delay
3131
import kotlinx.coroutines.flow.MutableStateFlow
3232
import kotlinx.coroutines.flow.StateFlow
3333
import kotlinx.coroutines.launch
34-
import kotlinx.coroutines.withContext
34+
import me.kavishdevar.librepods.R
3535

3636
class FOSSBillingProvider(context: Context): BillingProvider {
3737
private val _isPremium = MutableStateFlow(false)
3838
override val isPremium: StateFlow<Boolean> = _isPremium
3939

40-
private val _price = MutableStateFlow("Any")
40+
private val _price = MutableStateFlow(context.getString(R.string.name_your_own_price))
4141
override val price: StateFlow<String> = _price
4242

4343
private val sharedPreferences = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
@@ -69,4 +69,9 @@ class FOSSBillingProvider(context: Context): BillingProvider {
6969
_isPremium.value = stored
7070
}
7171
}
72+
73+
override fun restorePurchases() {
74+
_isPremium.value = true
75+
sharedPreferences.edit { putBoolean("foss_upgraded", true) }
76+
}
7277
}

0 commit comments

Comments
 (0)