diff --git a/app/src/main/java/app/myzel394/locationtest/db/AppSettings.kt b/app/src/main/java/app/myzel394/locationtest/db/AppSettings.kt index 7fa9f0e..6664c6d 100644 --- a/app/src/main/java/app/myzel394/locationtest/db/AppSettings.kt +++ b/app/src/main/java/app/myzel394/locationtest/db/AppSettings.kt @@ -10,6 +10,7 @@ import kotlinx.serialization.json.decodeFromStream @Serializable data class AppSettings( val audioRecorderSettings: AudioRecorderSettings = AudioRecorderSettings(), + val hasSeenOnboarding: Boolean = false, val showAdvancedSettings: Boolean = false, ) { fun setShowAdvancedSettings(showAdvancedSettings: Boolean): AppSettings { @@ -20,6 +21,10 @@ data class AppSettings( return copy(audioRecorderSettings = audioRecorderSettings) } + fun setHasSeenOnboarding(hasSeenOnboarding: Boolean): AppSettings { + return copy(hasSeenOnboarding = hasSeenOnboarding) + } + companion object { fun getDefaultInstance(): AppSettings = AppSettings() } diff --git a/app/src/main/java/app/myzel394/locationtest/ui/components/AudioRecorder/atoms/StartRecording.kt b/app/src/main/java/app/myzel394/locationtest/ui/components/AudioRecorder/molecules/StartRecording.kt similarity index 72% rename from app/src/main/java/app/myzel394/locationtest/ui/components/AudioRecorder/atoms/StartRecording.kt rename to app/src/main/java/app/myzel394/locationtest/ui/components/AudioRecorder/molecules/StartRecording.kt index fde0c3d..e1209bb 100644 --- a/app/src/main/java/app/myzel394/locationtest/ui/components/AudioRecorder/atoms/StartRecording.kt +++ b/app/src/main/java/app/myzel394/locationtest/ui/components/AudioRecorder/molecules/StartRecording.kt @@ -1,5 +1,6 @@ -package app.myzel394.locationtest.ui.components.AudioRecorder.atoms +package app.myzel394.locationtest.ui.components.AudioRecorder.molecules +import android.Manifest import android.content.ServiceConnection import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -29,6 +30,8 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import app.myzel394.locationtest.services.RecorderService import app.myzel394.locationtest.ui.BIG_PRIMARY_BUTTON_SIZE +import app.myzel394.locationtest.ui.components.AudioRecorder.atoms.AudioVisualizer +import app.myzel394.locationtest.ui.components.atoms.PermissionRequester import app.myzel394.locationtest.ui.utils.rememberFileSaverDialog import java.time.format.DateTimeFormatter @@ -51,32 +54,45 @@ fun StartRecording( horizontalAlignment = Alignment.CenterHorizontally, ) { Spacer(modifier = Modifier.weight(1f)) - Button( - onClick = { - RecorderService.startService(context, connection) - }, - modifier = Modifier - .semantics { - contentDescription = "Start recording" - } - .size(200.dp) - .clip(shape = CircleShape), - colors = ButtonDefaults.outlinedButtonColors(), - ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - ) { + PermissionRequester( + permission = arrayOf(Manifest.permission.RECORD_AUDIO), + icon = { Icon( Icons.Default.Mic, contentDescription = null, - modifier = Modifier - .size(80.dp), - ) - Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) - Text( - "Start Recording", - style = MaterialTheme.typography.titleSmall, ) + }, + onPermissionAvailable = { + RecorderService.startService(context, connection) + }, + ) { trigger -> + Button( + onClick = { + trigger() + }, + modifier = Modifier + .semantics { + contentDescription = "Start recording" + } + .size(200.dp) + .clip(shape = CircleShape), + colors = ButtonDefaults.outlinedButtonColors(), + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Icon( + Icons.Default.Mic, + contentDescription = null, + modifier = Modifier + .size(80.dp), + ) + Spacer(modifier = Modifier.height(ButtonDefaults.IconSpacing)) + Text( + "Start Recording", + style = MaterialTheme.typography.titleSmall, + ) + } } } if (service?.recordingStart != null) diff --git a/app/src/main/java/app/myzel394/locationtest/ui/components/atoms/PermissionRequester.kt b/app/src/main/java/app/myzel394/locationtest/ui/components/atoms/PermissionRequester.kt new file mode 100644 index 0000000..9145f1b --- /dev/null +++ b/app/src/main/java/app/myzel394/locationtest/ui/components/atoms/PermissionRequester.kt @@ -0,0 +1,96 @@ +package app.myzel394.locationtest.ui.components.atoms + +import android.graphics.drawable.Icon +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Cancel +import androidx.compose.material.icons.filled.OpenInBrowser +import androidx.compose.material.icons.filled.OpenInNew +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalContext +import androidx.core.content.PermissionChecker +import androidx.compose.runtime.getValue +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import app.myzel394.locationtest.ui.utils.PermissionHelper +import app.myzel394.locationtest.ui.utils.openAppSystemSettings +import kotlinx.serialization.json.JsonNull.content + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun PermissionRequester( + permission: Array, + icon: (@Composable () -> Unit)? = null, + onPermissionAvailable: () -> Unit, + content: @Composable (trigger: () -> Unit) -> Unit +) { + val context = LocalContext.current + + var showPermissionDenied by remember { mutableStateOf(false) } + + val callback = { + if (PermissionHelper.checkPermissions(context, permission)) { + onPermissionAvailable() + } else { + showPermissionDenied = true + } + } + + if (showPermissionDenied) { + AlertDialog( + onDismissRequest = { + showPermissionDenied = false + }, + icon = icon, + title = { + Text("Permission denied") + }, + text = { + Text("Please grant the permission to continue. You will be redirected to the app settings to grant the permission there.") + }, + confirmButton = { + Button( + onClick = { + showPermissionDenied = false + context.openAppSystemSettings() + } + ) { + Icon( + Icons.Default.OpenInNew, + contentDescription = null, + modifier = Modifier.size(ButtonDefaults.IconSize), + ) + Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing)) + Text("OK") + } + }, + dismissButton = { + Button( + onClick = { + showPermissionDenied = false + }, + colors = ButtonDefaults.textButtonColors(), + ) { + Icon( + Icons.Default.Cancel, + contentDescription = null, + modifier = Modifier.size(ButtonDefaults.IconSize), + ) + Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing)) + Text("Cancel") + } + } + ) + } + content(callback) +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/locationtest/ui/screens/AudioRecorder.kt b/app/src/main/java/app/myzel394/locationtest/ui/screens/AudioRecorder.kt index af60398..89ccaa2 100644 --- a/app/src/main/java/app/myzel394/locationtest/ui/screens/AudioRecorder.kt +++ b/app/src/main/java/app/myzel394/locationtest/ui/screens/AudioRecorder.kt @@ -1,49 +1,29 @@ package app.myzel394.locationtest.ui.screens -import android.Manifest import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.ServiceConnection -import android.media.MediaPlayer import android.os.IBinder -import androidx.activity.compose.rememberLauncherForActivityResult -import androidx.activity.result.contract.ActivityResultContracts -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Mic import androidx.compose.material.icons.filled.Settings -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalContext import androidx.compose.runtime.* -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.shadow -import androidx.compose.ui.graphics.Outline -import androidx.compose.ui.unit.dp -import androidx.core.content.ContextCompat import androidx.navigation.NavController import app.myzel394.locationtest.services.RecorderService -import app.myzel394.locationtest.ui.components.AudioRecorder.atoms.RecordingStatus -import app.myzel394.locationtest.ui.components.AudioRecorder.atoms.StartRecording +import app.myzel394.locationtest.ui.components.AudioRecorder.molecules.RecordingStatus +import app.myzel394.locationtest.ui.components.AudioRecorder.molecules.StartRecording import app.myzel394.locationtest.ui.enums.Screen import app.myzel394.locationtest.ui.utils.rememberFileSaverDialog diff --git a/app/src/main/java/app/myzel394/locationtest/ui/utils/PermissionHelper.kt b/app/src/main/java/app/myzel394/locationtest/ui/utils/PermissionHelper.kt index 01c0689..f139feb 100644 --- a/app/src/main/java/app/myzel394/locationtest/ui/utils/PermissionHelper.kt +++ b/app/src/main/java/app/myzel394/locationtest/ui/utils/PermissionHelper.kt @@ -2,9 +2,19 @@ package app.myzel394.locationtest.ui.utils import android.app.Activity import android.content.Context +import android.content.Intent import android.content.pm.PackageManager +import android.net.Uri +import android.provider.Settings import androidx.core.app.ActivityCompat +fun Context.openAppSystemSettings() { + startActivity(Intent().apply { + action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS, + data = Uri.fromParts("package", packageName, null) + }) +} + // From @Bnyro object PermissionHelper { fun checkPermissions(context: Context, permissions: Array): Boolean {