summaryrefslogtreecommitdiff
path: root/app/src/main/java/sh/lajo/buddy/OnboardingScreens.kt
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/sh/lajo/buddy/OnboardingScreens.kt')
-rw-r--r--app/src/main/java/sh/lajo/buddy/OnboardingScreens.kt128
1 files changed, 109 insertions, 19 deletions
diff --git a/app/src/main/java/sh/lajo/buddy/OnboardingScreens.kt b/app/src/main/java/sh/lajo/buddy/OnboardingScreens.kt
index d1b2378..2a29079 100644
--- a/app/src/main/java/sh/lajo/buddy/OnboardingScreens.kt
+++ b/app/src/main/java/sh/lajo/buddy/OnboardingScreens.kt
@@ -53,9 +53,8 @@ import sh.lajo.buddy.ApiConfig.BASE_URL
import sh.lajo.buddy.HttpClient.JSON
@Serializable
-data class LoginRequest(
- val email: String,
- val password: String
+data class KidLinkRequest(
+ val code: String,
)
@Serializable
@@ -456,12 +455,89 @@ fun OnboardingStep6Screen(
}
@Composable
+fun OnboardingStepMediaScreen(
+ onBack: () -> Unit,
+ onNext: () -> Unit
+) {
+ val context = LocalContext.current
+ var isMediaGranted by remember { mutableStateOf(isMediaPermissionGranted(context)) }
+
+ val mediaPermissionLauncher = rememberLauncherForActivityResult(
+ ActivityResultContracts.RequestPermission()
+ ) { isGranted ->
+ isMediaGranted = isGranted
+ }
+
+ // Use LifecycleObserver to refresh state when returning from settings
+ val lifecycleOwner = LocalLifecycleOwner.current
+ DisposableEffect(lifecycleOwner) {
+ val observer = LifecycleEventObserver { _, event ->
+ if (event == Lifecycle.Event.ON_RESUME) {
+ isMediaGranted = isMediaPermissionGranted(context)
+ }
+ }
+ lifecycleOwner.lifecycle.addObserver(observer)
+ onDispose {
+ lifecycleOwner.lifecycle.removeObserver(observer)
+ }
+ }
+
+ OnboardingScaffold(
+ title = stringResource(R.string.onboarding_step_media_title),
+ body = stringResource(R.string.onboarding_step_media_body),
+ primaryButtonText = stringResource(R.string.onboarding_next),
+ onPrimary = onNext,
+ primaryEnabled = isMediaGranted,
+ showBack = true,
+ onBack = onBack,
+ extraContent = {
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ verticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ Text(
+ text = stringResource(
+ R.string.onboarding_media_permission_status,
+ if (isMediaGranted) stringResource(R.string.onboarding_permission_granted)
+ else stringResource(R.string.onboarding_permission_not_granted)
+ ),
+ style = MaterialTheme.typography.bodyMedium
+ )
+
+ if (!isMediaGranted) {
+ Button(
+ onClick = {
+ val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Manifest.permission.READ_MEDIA_IMAGES
+ } else {
+ Manifest.permission.READ_EXTERNAL_STORAGE
+ }
+ mediaPermissionLauncher.launch(permission)
+ },
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(stringResource(R.string.onboarding_grant_media_permission))
+ }
+ }
+
+ if (!isMediaGranted) {
+ Spacer(Modifier.height(12.dp))
+ Text(
+ stringResource(R.string.onboarding_grant_media_to_continue),
+ style = MaterialTheme.typography.bodySmall,
+ )
+ }
+ }
+ }
+ )
+}
+
+@Composable
fun OnboardingStep7Screen(
onFinish: () -> Unit,
onBack: () -> Unit
) {
- var email by remember { mutableStateOf("") }
- var password by remember { mutableStateOf("") }
+ var code by remember { mutableStateOf("") }
var isLoading by remember { mutableStateOf(false) }
var resultText by remember { mutableStateOf<String?>(null) }
@@ -473,7 +549,7 @@ fun OnboardingStep7Screen(
OnboardingScaffold(
title = stringResource(R.string.onboarding_step7_title),
body = stringResource(R.string.onboarding_step7_body),
- primaryButtonText = if (isLoading) stringResource(R.string.onboarding_logging_in) else stringResource(R.string.onboarding_login),
+ primaryButtonText = if (isLoading) stringResource(R.string.onboarding_linking) else stringResource(R.string.onboarding_link_device),
onPrimary = {
if (isLoading) return@OnboardingScaffold
@@ -485,7 +561,8 @@ fun OnboardingStep7Screen(
try {
val client = HttpClient.client
- val jsonBody = networkJson.encodeToString(LoginRequest(email, password))
+ val normalizedCode = code.trim().uppercase()
+ val jsonBody = networkJson.encodeToString(KidLinkRequest(normalizedCode))
val body = jsonBody.toRequestBody(JSON)
val request = Request.Builder()
@@ -545,17 +622,20 @@ fun OnboardingStep7Screen(
onBack = onBack,
extraContent = {
OutlinedTextField(
- value = email,
- onValueChange = { email = it },
- label = { Text(stringResource(R.string.onboarding_email)) },
- singleLine = true,
- modifier = Modifier.fillMaxWidth(),
- enabled = !isLoading,
- )
- OutlinedTextField(
- value = password,
- onValueChange = { password = it },
- label = { Text(stringResource(R.string.onboarding_password)) },
+ value = code,
+ onValueChange = { input ->
+ val sanitized = input.uppercase().filter { it.isLetterOrDigit() }
+ val withDash = buildString {
+ sanitized.take(6).forEachIndexed { index, c ->
+ if (index == 3) {
+ append('-')
+ }
+ append(c)
+ }
+ }
+ code = withDash
+ },
+ label = { Text(stringResource(R.string.onboarding_link_code)) },
singleLine = true,
modifier = Modifier.fillMaxWidth(),
enabled = !isLoading,
@@ -675,6 +755,16 @@ private fun isContactsPermissionGranted(context: Context): Boolean {
android.content.pm.PackageManager.PERMISSION_GRANTED
}
+private fun isMediaPermissionGranted(context: Context): Boolean {
+ val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Manifest.permission.READ_MEDIA_IMAGES
+ } else {
+ Manifest.permission.READ_EXTERNAL_STORAGE
+ }
+ return context.checkSelfPermission(permission) ==
+ android.content.pm.PackageManager.PERMISSION_GRANTED
+}
+
private fun isVpnActive(context: Context): Boolean {
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
?: return false
@@ -690,4 +780,4 @@ private fun safeStartActivity(context: Context, intent: Intent) {
if (safeIntent.resolveActivity(context.packageManager) != null) {
context.startActivity(safeIntent)
}
-} \ No newline at end of file
+}