diff --git a/app/build.gradle b/app/build.gradle index d3c9db3f1..b5d105696 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,7 +6,7 @@ plugins { android { namespace 'app.myzel394.alibi' - compileSdk 34 + compileSdk 33 def keystoreProperties = new Properties() def keystorePropertiesFile = rootProject.file('key.properties') @@ -34,7 +34,7 @@ android { multiDexEnabled true applicationId "app.myzel394.alibi" minSdk 24 - targetSdk 34 + targetSdk 33 versionCode 7 versionName "0.3.0" @@ -42,6 +42,13 @@ android { vectorDrawables { useSupportLibrary true } + + configurations.all { + resolutionStrategy { + force("androidx.emoji2:emoji2-views-helper:1.3.0") + force("androidx.emoji2:emoji2:1.3.0") + } + } } signingConfigs { @@ -91,16 +98,16 @@ android { } dependencies { - implementation 'androidx.core:core-ktx:1.12.0' + implementation 'androidx.core:core-ktx:1.10.0' implementation platform('org.jetbrains.kotlin:kotlin-bom:1.8.0') implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2' - implementation 'androidx.activity:activity-compose:1.7.2' + implementation 'androidx.activity:activity-compose:1.0.0' implementation platform('androidx.compose:compose-bom:2022.10.00') implementation 'androidx.compose.ui:ui' implementation 'androidx.compose.ui:ui-graphics' implementation 'androidx.compose.ui:ui-tooling-preview' implementation 'androidx.compose.material3:material3' - implementation "androidx.compose.material:material-icons-extended:1.5.1" + implementation "androidx.compose.material:material-icons-extended:1.5.4" implementation 'androidx.appcompat:appcompat:1.6.1' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' @@ -110,7 +117,7 @@ dependencies { debugImplementation 'androidx.compose.ui:ui-tooling' debugImplementation 'androidx.compose.ui:ui-test-manifest' - implementation "androidx.navigation:navigation-compose:2.7.2" + implementation "androidx.navigation:navigation-compose:2.5.0" implementation 'com.google.dagger:hilt-android:2.46.1' annotationProcessor 'com.google.dagger:hilt-compiler:2.46.1' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8f67b7cd0..32cc19fb9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,6 +13,8 @@ + + @@ -56,6 +59,14 @@ android:name="autoStoreLocales" android:value="true" /> + + + + + + \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/NotificationHelper.kt b/app/src/main/java/app/myzel394/alibi/NotificationHelper.kt index 679cee744..c34c221f0 100644 --- a/app/src/main/java/app/myzel394/alibi/NotificationHelper.kt +++ b/app/src/main/java/app/myzel394/alibi/NotificationHelper.kt @@ -9,18 +9,30 @@ import androidx.annotation.RequiresApi object NotificationHelper { const val RECORDER_CHANNEL_ID = "recorder" const val RECORDER_CHANNEL_NOTIFICATION_ID = 1 + const val BOOT_CHANNEL_ID = "boot" + const val BOOT_CHANNEL_NOTIFICATION_ID = 2 @RequiresApi(Build.VERSION_CODES.O) fun createChannels(context: Context) { - val channel = NotificationChannel( - RECORDER_CHANNEL_ID, - context.resources.getString(R.string.notificationChannels_recorder_name), - android.app.NotificationManager.IMPORTANCE_LOW, - ) - channel.description = context.resources.getString(R.string.notificationChannels_recorder_description) - val notificationManager = context.getSystemService(NotificationManager::class.java) - notificationManager.createNotificationChannel(channel) + notificationManager.createNotificationChannel( + NotificationChannel( + RECORDER_CHANNEL_ID, + context.getString(R.string.notificationChannels_recorder_name), + NotificationManager.IMPORTANCE_LOW, + ).apply { + description = context.getString(R.string.notificationChannels_recorder_description) + } + ) + notificationManager.createNotificationChannel( + NotificationChannel( + BOOT_CHANNEL_ID, + context.getString(R.string.notificationChannels_boot_name), + NotificationManager.IMPORTANCE_LOW, + ).apply { + description = context.getString(R.string.notificationChannels_boot_description) + } + ) } } \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt index 505f1a7b3..328f73841 100644 --- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt +++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt @@ -19,6 +19,7 @@ data class AppSettings( val showAdvancedSettings: Boolean = false, val theme: Theme = Theme.SYSTEM, val lastRecording: RecordingInformation? = null, + val bootBehavior: BootBehavior? = BootBehavior.CONTINUE_RECORDING, ) { fun setShowAdvancedSettings(showAdvancedSettings: Boolean): AppSettings { return copy(showAdvancedSettings = showAdvancedSettings) @@ -44,12 +45,27 @@ data class AppSettings( return copy(lastRecording = lastRecording) } + fun setBootBehavior(bootBehavior: BootBehavior?): AppSettings { + return copy(bootBehavior = bootBehavior) + } + enum class Theme { SYSTEM, LIGHT, DARK, } + enum class BootBehavior { + // Always start recording, no matter if it was interrupted or not + START_RECORDING, + + // Only start recording if it was interrupted + CONTINUE_RECORDING, + + // Show a notification if interrupted + SHOW_NOTIFICATION, + } + fun exportToString(): String { return Json.encodeToString(serializer(), this) } diff --git a/app/src/main/java/app/myzel394/alibi/receivers/BootReceiver.kt b/app/src/main/java/app/myzel394/alibi/receivers/BootReceiver.kt new file mode 100644 index 000000000..c12ef8f5c --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/receivers/BootReceiver.kt @@ -0,0 +1,128 @@ +package app.myzel394.alibi.receivers + +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.BroadcastReceiver +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.ServiceConnection +import android.os.IBinder +import androidx.core.app.NotificationCompat +import androidx.core.content.ContextCompat +import app.myzel394.alibi.MainActivity +import app.myzel394.alibi.NotificationHelper +import app.myzel394.alibi.R +import app.myzel394.alibi.dataStore +import app.myzel394.alibi.db.AppSettings +import app.myzel394.alibi.helpers.AudioRecorderExporter +import app.myzel394.alibi.services.AudioRecorderService +import app.myzel394.alibi.services.RecorderNotificationHelper +import app.myzel394.alibi.services.RecorderService +import app.myzel394.alibi.ui.enums.Screen +import app.myzel394.alibi.ui.models.AudioRecorderModel +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch +import kotlinx.serialization.json.Json + +class BootReceiver : BroadcastReceiver() { + private var job = SupervisorJob() + private var scope = CoroutineScope(Dispatchers.IO + job) + + private fun startRecording(context: Context, settings: AppSettings) { + val connection = object : ServiceConnection { + override fun onServiceConnected(className: ComponentName, service: IBinder) { + ((service as RecorderService.RecorderBinder).getService() as AudioRecorderService).also { recorder -> + recorder.startRecording() + } + } + + override fun onServiceDisconnected(arg0: ComponentName) { + } + } + + println("BootReceiver.startRecording()") + val intent = Intent(context, AudioRecorderService::class.java).apply { + action = "init" + + putExtra( + "startImmediately", + true, + ) + + if (settings.notificationSettings != null) { + putExtra( + "notificationDetails", + Json.encodeToString( + RecorderNotificationHelper.NotificationDetails.serializer(), + RecorderNotificationHelper.NotificationDetails.fromNotificationSettings( + context, + settings.notificationSettings + ) + ), + ) + } + } + ContextCompat.startForegroundService(context, intent) + } + + private fun showNotification(context: Context) { + if (!AudioRecorderExporter.hasRecordingsAvailable(context)) { + // Nothing interrupted, so no notification needs to be shown + return + } + + val notification = NotificationCompat.Builder(context, NotificationHelper.BOOT_CHANNEL_ID) + .setPriority(NotificationCompat.PRIORITY_LOW) + .setCategory(NotificationCompat.CATEGORY_REMINDER) + .setSmallIcon(R.drawable.launcher_monochrome_noopacity) + .setContentIntent( + PendingIntent.getActivity( + context, + 0, + Intent(context, MainActivity::class.java), + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE, + ) + ) + .setOnlyAlertOnce(true) + .setContentTitle(context.getString(R.string.notification_boot_title)) + .setContentText(context.getString(R.string.notification_boot_message)) + .build() + + val notificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + + notificationManager.notify(NotificationHelper.BOOT_CHANNEL_NOTIFICATION_ID, notification) + } + + override fun onReceive(context: Context?, intent: Intent?) { + if (intent?.action != Intent.ACTION_BOOT_COMPLETED || context == null) { + return + } + + println("BootReceiver.onReceive()") + + scope.launch { + context.dataStore.data.collectLatest { settings -> + println("BootBehavior: ${settings.bootBehavior}") + when (settings.bootBehavior) { + AppSettings.BootBehavior.CONTINUE_RECORDING -> { + if (AudioRecorderExporter.hasRecordingsAvailable(context)) { + startRecording(context, settings) + } + } + + AppSettings.BootBehavior.START_RECORDING -> startRecording(context, settings) + AppSettings.BootBehavior.SHOW_NOTIFICATION -> showNotification(context) + null -> { + // Nothing to do + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt index 88d2bca0a..56183f9b6 100644 --- a/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/IntervalRecorderService.kt @@ -1,5 +1,6 @@ package app.myzel394.alibi.services +import android.media.MediaMetadataRetriever import android.media.MediaRecorder import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AudioRecorderSettings @@ -58,10 +59,45 @@ abstract class IntervalRecorderService : ExtraRecorderInformationService() { } } + private fun fetchCounterValue() { + val files = outputFolder.listFiles()?.filter { + val name = it.nameWithoutExtension + + name.toIntOrNull() != null + }?.toList() ?: emptyList() + + counter = files.size + } + + private fun fetchRecordingTime() { + var oldAmount = 0L + + for (file in outputFolder.listFiles() ?: emptyArray()) { + if (file.nameWithoutExtension.toIntOrNull() == null) { + continue + } + + // It's better to at least get an approximate value, than to crash + runCatching { + val amount = MediaMetadataRetriever().run { + setDataSource(file.absolutePath) + extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toIntOrNull() + ?: 0 + } + + oldAmount += amount + } + } + + recordingTime = oldAmount + } + override fun start() { super.start() outputFolder.mkdirs() + fetchCounterValue() + fetchRecordingTime() scope.launch { dataStore.data.collectLatest { preferenceSettings -> diff --git a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt index 02b579794..b3cd09a49 100644 --- a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt @@ -34,7 +34,7 @@ abstract class RecorderService : Service() { var onStateChange: ((RecorderState) -> Unit)? = null var recordingTime = 0L - private set + protected set private lateinit var recordingTimeTimer: ScheduledExecutorService var onRecordingTimeChange: ((Long) -> Unit)? = null var notificationDetails: RecorderNotificationHelper.NotificationDetails? = null @@ -55,6 +55,10 @@ abstract class RecorderService : Service() { it ) } + + if (intent.getBooleanExtra("startImmediately", false)) { + startRecording() + } } "changeState" -> { @@ -142,16 +146,22 @@ abstract class RecorderService : Service() { fun startRecording() { recordingStart = LocalDateTime.now() - ServiceCompat.startForeground( - this, - NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID, - getNotificationHelper().buildStartingNotification(), - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE - } else { - 0 - }, - ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + startForeground( + NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID, + getNotificationHelper().buildStartingNotification(), + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE + } else { + 0 + }, + ) + } else { + startForeground( + NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID, + getNotificationHelper().buildStartingNotification(), + ) + } // Start changeState(RecorderState.RECORDING) diff --git a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt index 985e79d56..f55e4acf2 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt @@ -60,15 +60,6 @@ fun Navigation( } composable( Screen.AudioRecorder.route, - enterTransition = { - when (initialState.destination.route) { - Screen.Welcome.route -> null - else -> scaleIn(initialScale = SCALE_IN) + fadeIn() - } - }, - exitTransition = { - scaleOut(targetScale = SCALE_IN) + fadeOut(tween(durationMillis = 150)) - } ) { AudioRecorderScreen( navController = navController, @@ -77,12 +68,6 @@ fun Navigation( } composable( Screen.Settings.route, - enterTransition = { - scaleIn(initialScale = 1 / SCALE_IN) + fadeIn() - }, - exitTransition = { - scaleOut(targetScale = 1 / SCALE_IN) + fadeOut(tween(durationMillis = 150)) - } ) { SettingsScreen( navController = navController, @@ -91,16 +76,6 @@ fun Navigation( } composable( Screen.CustomRecordingNotifications.route, - enterTransition = { - slideInHorizontally( - initialOffsetX = { it -> it / 2 } - ) + fadeIn() - }, - exitTransition = { - slideOutHorizontally( - targetOffsetX = { it -> it / 2 } - ) + fadeOut(tween(150)) - } ) { CustomRecordingNotificationsScreen( navController = navController, @@ -108,12 +83,6 @@ fun Navigation( } composable( Screen.About.route, - enterTransition = { - scaleIn() - }, - exitTransition = { - scaleOut() + fadeOut(tween(150)) - } ) { AboutScreen( navController = navController, diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/StartRecording.kt b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/StartRecording.kt index 72f2f42e5..4111fca7d 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/StartRecording.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/StartRecording.kt @@ -82,6 +82,10 @@ fun StartRecording( ) } + runCatching { + audioRecorder.stopRecording(context) + } + AudioRecorderExporter.clearAllRecordings(context) audioRecorder.startRecording(context) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/BootBehaviorTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/BootBehaviorTile.kt new file mode 100644 index 000000000..9982f5bb4 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/BootBehaviorTile.kt @@ -0,0 +1,124 @@ +package app.myzel394.alibi.ui.components.SettingsScreen.atoms + +import androidx.appcompat.app.AppCompatDelegate +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.CheckCircle +import androidx.compose.material.icons.filled.Smartphone +import androidx.compose.material.icons.filled.Translate +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.core.os.LocaleListCompat +import app.myzel394.alibi.R +import app.myzel394.alibi.SUPPORTED_LOCALES +import app.myzel394.alibi.dataStore +import app.myzel394.alibi.db.AppSettings +import app.myzel394.alibi.ui.components.atoms.SettingsTile +import app.myzel394.alibi.ui.utils.IconResource +import com.maxkeppeker.sheets.core.models.base.ButtonStyle +import com.maxkeppeker.sheets.core.models.base.Header +import com.maxkeppeker.sheets.core.models.base.IconSource +import com.maxkeppeker.sheets.core.models.base.SelectionButton +import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState +import com.maxkeppeler.sheets.list.ListDialog +import com.maxkeppeler.sheets.list.models.ListOption +import com.maxkeppeler.sheets.list.models.ListSelection +import kotlinx.coroutines.launch +import java.util.Locale + +val BOOT_BEHAVIOR_TITLE_MAP = mapOf( + AppSettings.BootBehavior.SHOW_NOTIFICATION to R.string.ui_settings_bootBehavior_values_SHOW_NOTIFICATION_title, + AppSettings.BootBehavior.START_RECORDING to R.string.ui_settings_bootBehavior_values_START_RECORDING_title, + AppSettings.BootBehavior.CONTINUE_RECORDING to R.string.ui_settings_bootBehavior_values_CONTINUE_RECORDING_title, +) +val BOOT_BEHAVIOR_DESCRIPTION_MAP = mapOf( + AppSettings.BootBehavior.SHOW_NOTIFICATION to R.string.ui_settings_bootBehavior_values_SHOW_NOTIFICATION_description, + AppSettings.BootBehavior.START_RECORDING to R.string.ui_settings_bootBehavior_values_START_RECORDING_description, + AppSettings.BootBehavior.CONTINUE_RECORDING to R.string.ui_settings_bootBehavior_values_CONTINUE_RECORDING_description, +) + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun BootBehaviorTile( + settings: AppSettings, +) { + val scope = rememberCoroutineScope() + val showDialog = rememberUseCaseState() + val dataStore = LocalContext.current.dataStore + + fun updateValue(behavior: AppSettings.BootBehavior?) { + scope.launch { + dataStore.updateData { + it.setBootBehavior( + behavior + ) + } + } + } + + ListDialog( + state = showDialog, + header = Header.Default( + title = stringResource(R.string.ui_settings_bootBehavior_help), + icon = IconSource( + painter = IconResource.fromImageVector(Icons.Default.Smartphone) + .asPainterResource(), + contentDescription = null, + ) + ), + selection = ListSelection.Single( + showRadioButtons = true, + options = AppSettings.BootBehavior.entries.map { + ListOption( + titleText = stringResource( + BOOT_BEHAVIOR_TITLE_MAP[it]!! + ), + subtitleText = stringResource( + BOOT_BEHAVIOR_DESCRIPTION_MAP[it]!! + ), + selected = settings.bootBehavior == it, + ) + }.toList() + listOf( + ListOption( + titleText = stringResource(R.string.ui_settings_bootBehavior_values_nothing_title), + ) + ), + positiveButton = SelectionButton( + icon = IconSource( + painter = IconResource.fromImageVector(Icons.Default.CheckCircle) + .asPainterResource(), + contentDescription = null, + ), + text = stringResource(android.R.string.ok), + type = ButtonStyle.TEXT, + ) + ) { index, _ -> + val behavior = AppSettings.BootBehavior.values().getOrNull(index) + updateValue(behavior) + }, + ) + SettingsTile( + firstModifier = Modifier + .fillMaxHeight() + .clickable { + showDialog.show() + }, + title = stringResource(R.string.ui_settings_bootBehavior_title), + description = stringResource( + BOOT_BEHAVIOR_TITLE_MAP[settings.bootBehavior] ?: R.string.ui_settings_bootBehavior_help + ), + leading = { + Icon( + Icons.Default.Smartphone, + contentDescription = null, + ) + }, + ) +} diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt index 7c9724af8..da3f1d721 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt @@ -125,7 +125,7 @@ class AudioRecorderModel : ViewModel() { } } ContextCompat.startForegroundService(context, intent) - context.bindService(intent, connection, Context.BIND_AUTO_CREATE) + context.bindService(intent, connection, 0) } fun stopRecording(context: Context) { diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorderScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorderScreen.kt index ce3cff6d5..687a29fdc 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorderScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorderScreen.kt @@ -94,6 +94,7 @@ fun AudioRecorderScreen( delay(100) try { + audioRecorder.stopRecording(context) val file = AudioRecorderExporter( audioRecorder.recorderService?.getRecordingInformation() ?: settings.lastRecording diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt index 0a1b4f96d..f28bafa2a 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt @@ -41,6 +41,7 @@ import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.ui.SUPPORTS_DARK_MODE_NATIVELY import app.myzel394.alibi.ui.components.SettingsScreen.atoms.AboutTile import app.myzel394.alibi.ui.components.SettingsScreen.atoms.BitrateTile +import app.myzel394.alibi.ui.components.SettingsScreen.atoms.BootBehaviorTile import app.myzel394.alibi.ui.components.SettingsScreen.atoms.CustomNotificationTile import app.myzel394.alibi.ui.components.SettingsScreen.atoms.DeleteRecordingsImmediatelyTile import app.myzel394.alibi.ui.components.SettingsScreen.atoms.EncoderTile @@ -149,6 +150,7 @@ fun SettingsScreen( InAppLanguagePicker() DeleteRecordingsImmediatelyTile(settings = settings) CustomNotificationTile(navController = navController, settings = settings) + BootBehaviorTile(settings = settings) AboutTile(navController = navController) AnimatedVisibility(visible = settings.showAdvancedSettings) { Column( diff --git a/app/src/main/java/app/myzel394/alibi/ui/utils/available-microphones.kt b/app/src/main/java/app/myzel394/alibi/ui/utils/available-microphones.kt index c36c91bf5..2a435ddd7 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/utils/available-microphones.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/utils/available-microphones.kt @@ -16,10 +16,13 @@ val ALLOWED_MICROPHONE_TYPES = AudioDeviceInfo.TYPE_WIRED_HEADPHONES, AudioDeviceInfo.TYPE_IP, AudioDeviceInfo.TYPE_DOCK, + /* + Currently unavailable due to targetSDK 33 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { AudioDeviceInfo.TYPE_DOCK_ANALOG } else { }, + */ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { AudioDeviceInfo.TYPE_BLE_HEADSET } else { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a1328ef00..945137157 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -109,4 +109,17 @@ Become a GitHub Sponsor Delete Recordings Immediately If enabled, Alibi will immediately delete recordings after you have saved the file. + Boot Behavior + Show a notification + Boot notification + If enabled, you\'ll be informed that your recording was interrupted + Alibi was interrupted + Your device restarted and your recording has been interrupted + Always start recording + Continue recording + Show a notification if recording has been interrupted + Continue recording, if interrupted, otherwise start a new recording + If recording has been interrupted, continue it + Do nothing + What should Alibi do when your phone boots up? \ No newline at end of file