Skip to content

Commit 469d948

Browse files
committed
android: add xposed check and email form
too many emails with absolutely no content
1 parent f5d9276 commit 469d948

16 files changed

Lines changed: 689 additions & 442 deletions

File tree

.github/workflows/ci-android.yml

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,16 +84,35 @@ jobs:
8484
needs: build
8585
permissions:
8686
contents: write
87+
8788
steps:
8889
- uses: actions/checkout@v4
90+
91+
- uses: actions/download-artifact@v4
92+
with:
93+
name: apk-release
94+
path: artifacts/apk-release
95+
96+
- uses: actions/download-artifact@v4
97+
with:
98+
name: apk-debug
99+
path: artifacts/apk-debug
100+
89101
- uses: actions/download-artifact@v4
90102
with:
91-
name: release-artifacts
92-
path: artifacts
103+
name: root-module-release
104+
path: artifacts/root-module-release
105+
106+
- uses: actions/download-artifact@v4
107+
with:
108+
name: root-module-debug
109+
path: artifacts/root-module-debug
110+
93111
- id: prev
94112
run: |
95113
TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
96114
echo "tag=$TAG" >> $GITHUB_OUTPUT
115+
97116
- id: changelog
98117
run: |
99118
if [ -z "${{ steps.prev.outputs.tag }}" ]; then
@@ -104,13 +123,15 @@ jobs:
104123
echo "notes<<EOF" >> $GITHUB_OUTPUT
105124
echo "$NOTES" >> $GITHUB_OUTPUT
106125
echo "EOF" >> $GITHUB_OUTPUT
126+
107127
- id: tag
108128
run: echo "tag=nightly-${{ needs.build.outputs.short_sha }}" >> $GITHUB_OUTPUT
129+
109130
- env:
110131
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
111132
run: |
112133
gh release create "${{ steps.tag.outputs.tag }}" \
113-
artifacts/* \
134+
artifacts/**/* \
114135
-t "Nightly ${{ needs.build.outputs.short_sha }}" \
115136
--notes "${{ steps.changelog.outputs.notes }}" \
116-
--prerelease
137+
--prerelease

android/app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import java.util.Properties
22

3-
val appVersionName = "0.2.3"
3+
val appVersionName = "0.2.4"
44

55
plugins {
66
alias(libs.plugins.android.application)
@@ -30,7 +30,7 @@ android {
3030
applicationId = "me.kavishdevar.librepods"
3131
minSdk = 33
3232
targetSdk = 37
33-
versionCode = 38
33+
versionCode = 40
3434
versionName = appVersionName
3535
}
3636
buildTypes {

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

Lines changed: 115 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ import androidx.compose.foundation.layout.padding
6565
import androidx.compose.foundation.layout.size
6666
import androidx.compose.foundation.rememberScrollState
6767
import androidx.compose.foundation.shape.RoundedCornerShape
68+
import androidx.compose.foundation.text.input.rememberTextFieldState
6869
import androidx.compose.foundation.verticalScroll
6970
import androidx.compose.material.icons.Icons
7071
import androidx.compose.material.icons.filled.Notifications
@@ -86,11 +87,13 @@ import androidx.compose.ui.Alignment
8687
import androidx.compose.ui.Modifier
8788
import androidx.compose.ui.draw.clip
8889
import androidx.compose.ui.draw.scale
90+
import androidx.compose.ui.focus.FocusRequester
8991
import androidx.compose.ui.geometry.Offset
9092
import androidx.compose.ui.graphics.Color
9193
import androidx.compose.ui.graphics.drawscope.rotate
9294
import androidx.compose.ui.graphics.vector.ImageVector
9395
import androidx.compose.ui.platform.LocalContext
96+
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
9497
import androidx.compose.ui.platform.LocalWindowInfo
9598
import androidx.compose.ui.res.stringResource
9699
import androidx.compose.ui.res.vectorResource
@@ -120,9 +123,12 @@ import me.kavishdevar.librepods.data.AirPodsNotifications
120123
import me.kavishdevar.librepods.data.ControlCommandRepository
121124
import me.kavishdevar.librepods.presentation.components.ConfirmationDialog
122125
import me.kavishdevar.librepods.presentation.components.DeviceInfoCard
123-
import me.kavishdevar.librepods.presentation.components.PlayBypassSheet
126+
import me.kavishdevar.librepods.presentation.components.SelectItem
127+
import me.kavishdevar.librepods.presentation.components.StyledBottomSheet
124128
import me.kavishdevar.librepods.presentation.components.StyledButton
125129
import me.kavishdevar.librepods.presentation.components.StyledIconButton
130+
import me.kavishdevar.librepods.presentation.components.StyledInputField
131+
import me.kavishdevar.librepods.presentation.components.StyledSelectList
126132
import me.kavishdevar.librepods.presentation.screens.AccessibilitySettingsScreen
127133
import me.kavishdevar.librepods.presentation.screens.AdaptiveStrengthScreen
128134
import me.kavishdevar.librepods.presentation.screens.AirPodsSettingsScreen
@@ -146,6 +152,7 @@ import me.kavishdevar.librepods.presentation.viewmodel.AirPodsViewModel
146152
import me.kavishdevar.librepods.presentation.viewmodel.AppSettingsViewModel
147153
import me.kavishdevar.librepods.presentation.viewmodel.PurchaseViewModel
148154
import me.kavishdevar.librepods.services.AirPodsService
155+
import me.kavishdevar.librepods.utils.XposedState
149156
import me.kavishdevar.librepods.utils.isSupported
150157
import kotlin.io.encoding.ExperimentalEncodingApi
151158

@@ -157,7 +164,7 @@ lateinit var connectionStatusReceiver: BroadcastReceiver
157164
class MainActivity : ComponentActivity() {
158165
companion object {
159166
init {
160-
if (BuildConfig.FLAVOR == "xposed") {
167+
if (XposedState.isAvailable && XposedState.bluetoothScopeEnabled) {
161168
System.loadLibrary("l2c_fcr_hook")
162169
}
163170
}
@@ -329,23 +336,118 @@ fun Main() {
329336
)
330337

331338
if (BuildConfig.PLAY_BUILD) {
332-
PlayBypassSheet(
339+
StyledBottomSheet(
333340
visible = showPlayBypassVisible.value,
334341
onDismiss = {
335342
showPlayBypassVisible.value = false
336343
showDialog.value = true
337344
},
338-
onConfirm = {
339-
showPlayBypassVisible.value = false
340-
sharedPreferences.edit {
341-
putBoolean("bypass_device_check.v2", true)
342-
val intent = Intent(context, MainActivity::class.java)
343-
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
344-
context.startActivity(intent)
345-
}
346-
},
347345
backdrop = backdrop
348-
)
346+
) { innerBackdrop, _ ->
347+
val contentColor = if (isDarkTheme) Color.White else Color.Black
348+
349+
var acknowledged by remember { mutableStateOf(false) }
350+
val inputState = rememberTextFieldState("")
351+
352+
val isValid = acknowledged && inputState.text.trim() == "OK"
353+
354+
val sfPro = FontFamily(Font(R.font.sf_pro))
355+
356+
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
357+
Text(
358+
text = stringResource(R.string.bypass_compatibility_check),
359+
style = TextStyle(
360+
fontFamily = sfPro,
361+
fontWeight = FontWeight.SemiBold,
362+
fontSize = 18.sp,
363+
color = contentColor
364+
),
365+
modifier = Modifier.padding(horizontal = 12.dp)
366+
)
367+
368+
Text(
369+
text = stringResource(R.string.compatibility_play_dialog_confirmation),
370+
style = TextStyle(
371+
fontFamily = sfPro,
372+
fontWeight = FontWeight.Medium,
373+
fontSize = 14.sp,
374+
color = contentColor
375+
),
376+
modifier = Modifier.padding(horizontal = 12.dp)
377+
)
378+
379+
StyledSelectList(
380+
items = listOf(
381+
SelectItem(
382+
name = stringResource(R.string.read_compatibility_requirements),
383+
selected = acknowledged,
384+
onClick = { acknowledged = !acknowledged }
385+
)
386+
)
387+
)
388+
389+
val focusRequester = remember { FocusRequester() }
390+
val keyboardController = LocalSoftwareKeyboardController.current
391+
392+
LaunchedEffect(Unit) {
393+
focusRequester.requestFocus()
394+
keyboardController?.show()
395+
}
396+
397+
StyledInputField(
398+
inputState = inputState,
399+
focusRequester = focusRequester,
400+
placeholder = stringResource(R.string.type_ok_to_continue, "OK")
401+
)
402+
403+
Row(
404+
modifier = Modifier.fillMaxWidth(),
405+
horizontalArrangement = Arrangement.spacedBy(24.dp)
406+
) {
407+
StyledButton(
408+
onClick = { showPlayBypassVisible.value = false },
409+
backdrop = innerBackdrop,
410+
modifier = Modifier.weight(1f),
411+
) {
412+
Text(
413+
text = stringResource(R.string.no),
414+
style = TextStyle(
415+
fontFamily = sfPro,
416+
fontWeight = FontWeight.Medium,
417+
fontSize = 14.sp,
418+
color = contentColor
419+
)
420+
)
421+
}
422+
StyledButton(
423+
onClick = {
424+
showPlayBypassVisible.value = false
425+
sharedPreferences.edit {
426+
putBoolean("bypass_device_check.v2", true)
427+
val intent = Intent(context, MainActivity::class.java)
428+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
429+
context.startActivity(intent)
430+
}
431+
},
432+
backdrop = innerBackdrop,
433+
isInteractive = isValid,
434+
modifier = Modifier.weight(1f),
435+
enabled = isValid,
436+
surfaceColor = if (isDarkTheme) Color(0xFF0091FF) else Color(0xFF0088FF)
437+
) {
438+
Text(
439+
text = stringResource(R.string.proceed),
440+
style = TextStyle(
441+
fontFamily = sfPro,
442+
fontWeight = FontWeight.Medium,
443+
fontSize = 14.sp,
444+
color = if (isValid) contentColor else contentColor.copy(alpha = 0.4f)
445+
)
446+
)
447+
}
448+
}
449+
}
450+
}
349451
}
350452

351453
return

android/app/src/main/java/me/kavishdevar/librepods/presentation/components/DeviceInfoCard.kt

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import android.os.Build
44
import androidx.compose.foundation.background
55
import androidx.compose.foundation.isSystemInDarkTheme
66
import androidx.compose.foundation.layout.Arrangement
7+
import androidx.compose.foundation.layout.Box
78
import androidx.compose.foundation.layout.Column
89
import androidx.compose.foundation.layout.Row
910
import androidx.compose.foundation.layout.fillMaxWidth
@@ -27,6 +28,7 @@ import androidx.compose.ui.text.font.FontWeight
2728
import androidx.compose.ui.unit.dp
2829
import androidx.compose.ui.unit.sp
2930
import me.kavishdevar.librepods.R
31+
import me.kavishdevar.librepods.utils.XposedState
3032

3133
@Composable
3234
fun DeviceInfoCard() {
@@ -41,14 +43,20 @@ fun DeviceInfoCard() {
4143
Column (
4244
verticalArrangement = Arrangement.spacedBy(8.dp)
4345
) {
44-
Text(
45-
text = stringResource(R.string.device_info), style = TextStyle(
46-
fontSize = 14.sp,
47-
fontWeight = FontWeight.Bold,
48-
color = textColor.copy(alpha = 0.6f),
49-
fontFamily = FontFamily(Font(R.font.sf_pro))
50-
), modifier = Modifier.padding(start = 16.dp)
51-
)
46+
Box(
47+
modifier = Modifier
48+
.background(if (isDarkTheme) Color.Black else Color(0xFFF2F2F7))
49+
.padding(start = 16.dp, bottom = 2.dp, top = 24.dp, end = 4.dp)
50+
) {
51+
Text(
52+
text = stringResource(R.string.device_info), style = TextStyle(
53+
fontSize = 14.sp,
54+
fontWeight = FontWeight.Bold,
55+
color = textColor.copy(alpha = 0.6f),
56+
fontFamily = FontFamily(Font(R.font.sf_pro))
57+
)
58+
)
59+
}
5260
Column(
5361
modifier = Modifier
5462
.clip(RoundedCornerShape(28.dp))
@@ -166,6 +174,62 @@ fun DeviceInfoCard() {
166174
)
167175
)
168176
}
177+
HorizontalDivider(
178+
thickness = 1.dp,
179+
color = Color(0x40888888),
180+
modifier = Modifier.padding(horizontal = 12.dp)
181+
)
182+
Row(
183+
modifier = Modifier
184+
.fillMaxWidth()
185+
.padding(16.dp),
186+
horizontalArrangement = Arrangement.SpaceBetween,
187+
) {
188+
Text(
189+
text = stringResource(R.string.xposed_available), style = TextStyle(
190+
fontSize = 16.sp,
191+
color = textColor,
192+
fontFamily = FontFamily(Font(R.font.sf_pro))
193+
)
194+
)
195+
Text(
196+
text = if (XposedState.isAvailable) stringResource(R.string.yes) else stringResource(R.string.no), style = TextStyle(
197+
fontSize = 16.sp,
198+
color = if (isDarkTheme) Color.White.copy(alpha = 0.6f) else Color.Black.copy(
199+
alpha = 0.8f
200+
),
201+
fontFamily = FontFamily(Font(R.font.sf_pro))
202+
)
203+
)
204+
}
205+
HorizontalDivider(
206+
thickness = 1.dp,
207+
color = Color(0x40888888),
208+
modifier = Modifier.padding(horizontal = 12.dp)
209+
)
210+
Row(
211+
modifier = Modifier
212+
.fillMaxWidth()
213+
.padding(16.dp),
214+
horizontalArrangement = Arrangement.SpaceBetween,
215+
) {
216+
Text(
217+
text = stringResource(R.string.app_enabled_in_xposed), style = TextStyle(
218+
fontSize = 16.sp,
219+
color = textColor,
220+
fontFamily = FontFamily(Font(R.font.sf_pro))
221+
)
222+
)
223+
Text(
224+
text = if (XposedState.bluetoothScopeEnabled) stringResource(R.string.yes) else stringResource(R.string.no), style = TextStyle(
225+
fontSize = 16.sp,
226+
color = if (isDarkTheme) Color.White.copy(alpha = 0.6f) else Color.Black.copy(
227+
alpha = 0.8f
228+
),
229+
fontFamily = FontFamily(Font(R.font.sf_pro))
230+
)
231+
)
232+
}
169233
}
170234
}
171235
}

0 commit comments

Comments
 (0)