From 0fb341ffb9c96558430c54c990387cbe1bb01324 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 22 Oct 2023 14:40:57 +0200 Subject: [PATCH 1/2] feat: Add support for old theme selection --- .../java/app/myzel394/alibi/MainActivity.kt | 23 +++ .../java/app/myzel394/alibi/db/AppSettings.kt | 28 +++- .../java/app/myzel394/alibi/ui/Constants.kt | 2 + .../SettingsScreen/atoms/ThemeSelector.kt | 140 ++++++++++++++++++ .../alibi/ui/screens/SettingsScreen.kt | 7 +- 5 files changed, 191 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/ThemeSelector.kt diff --git a/app/src/main/java/app/myzel394/alibi/MainActivity.kt b/app/src/main/java/app/myzel394/alibi/MainActivity.kt index a97fa1ca5..5b817295f 100644 --- a/app/src/main/java/app/myzel394/alibi/MainActivity.kt +++ b/app/src/main/java/app/myzel394/alibi/MainActivity.kt @@ -6,10 +6,15 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.platform.LocalContext import androidx.core.view.WindowCompat import androidx.datastore.dataStore +import app.myzel394.alibi.db.AppSettings import app.myzel394.alibi.db.AppSettingsSerializer import app.myzel394.alibi.ui.Navigation +import app.myzel394.alibi.ui.SUPPORTS_DARK_MODE_NATIVELY import app.myzel394.alibi.ui.theme.AlibiTheme const val SETTINGS_FILE = "settings.json" @@ -25,6 +30,24 @@ class MainActivity : AppCompatActivity() { WindowCompat.setDecorFitsSystemWindows(window, false) setContent { + val dataStore = LocalContext.current.dataStore + val settings = dataStore + .data + .collectAsState(initial = AppSettings.getDefaultInstance()) + .value + + LaunchedEffect(settings.theme) { + if (!SUPPORTS_DARK_MODE_NATIVELY) { + val currentValue = AppCompatDelegate.getDefaultNightMode() + + if (settings.theme == AppSettings.Theme.LIGHT && currentValue != AppCompatDelegate.MODE_NIGHT_NO) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) + } else if (settings.theme == AppSettings.Theme.DARK && currentValue != AppCompatDelegate.MODE_NIGHT_YES) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) + } + } + } + AlibiTheme { Navigation() } 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 89df1730b..127ff9fb4 100644 --- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt +++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt @@ -16,6 +16,7 @@ data class AppSettings( val audioRecorderSettings: AudioRecorderSettings = AudioRecorderSettings(), val hasSeenOnboarding: Boolean = false, val showAdvancedSettings: Boolean = false, + val theme: Theme = Theme.SYSTEM, ) { fun setShowAdvancedSettings(showAdvancedSettings: Boolean): AppSettings { return copy(showAdvancedSettings = showAdvancedSettings) @@ -29,6 +30,16 @@ data class AppSettings( return copy(hasSeenOnboarding = hasSeenOnboarding) } + fun setTheme(theme: Theme): AppSettings { + return copy(theme = theme) + } + + enum class Theme { + SYSTEM, + LIGHT, + DARK, + } + companion object { fun getDefaultInstance(): AppSettings = AppSettings() } @@ -154,7 +165,7 @@ data class AudioRecorderSettings( else MediaRecorder.OutputFormat.THREE_GPP } - return when(encoder) { + return when (encoder) { MediaRecorder.AudioEncoder.AAC -> MediaRecorder.OutputFormat.AAC_ADTS MediaRecorder.AudioEncoder.AAC_ELD -> MediaRecorder.OutputFormat.AAC_ADTS MediaRecorder.AudioEncoder.AMR_NB -> MediaRecorder.OutputFormat.AMR_NB @@ -167,6 +178,7 @@ data class AudioRecorderSettings( MediaRecorder.OutputFormat.AAC_ADTS } } + MediaRecorder.AudioEncoder.OPUS -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { MediaRecorder.OutputFormat.OGG @@ -174,11 +186,12 @@ data class AudioRecorderSettings( MediaRecorder.OutputFormat.AAC_ADTS } } + else -> MediaRecorder.OutputFormat.DEFAULT } } - fun getMimeType(): String = when(getOutputFormat()) { + fun getMimeType(): String = when (getOutputFormat()) { MediaRecorder.OutputFormat.AAC_ADTS -> "audio/aac" MediaRecorder.OutputFormat.THREE_GPP -> "audio/3gpp" MediaRecorder.OutputFormat.MPEG_4 -> "audio/mp4" @@ -190,7 +203,7 @@ data class AudioRecorderSettings( else -> "audio/3gpp" } - fun getSamplingRate(): Int = samplingRate ?: when(getOutputFormat()) { + fun getSamplingRate(): Int = samplingRate ?: when (getOutputFormat()) { MediaRecorder.OutputFormat.AAC_ADTS -> 96000 MediaRecorder.OutputFormat.THREE_GPP -> 44100 MediaRecorder.OutputFormat.MPEG_4 -> 44100 @@ -202,11 +215,10 @@ data class AudioRecorderSettings( else -> 48000 } - fun getEncoder(): Int = encoder ?: - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) - MediaRecorder.AudioEncoder.AAC - else - MediaRecorder.AudioEncoder.AMR_NB + fun getEncoder(): Int = encoder ?: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + MediaRecorder.AudioEncoder.AAC + else + MediaRecorder.AudioEncoder.AMR_NB fun setIntervalDuration(duration: Long): AudioRecorderSettings { if (duration < 10 * 1000L || duration > 60 * 60 * 1000L) { diff --git a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt index e54353144..903c21262 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/Constants.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/Constants.kt @@ -1,6 +1,8 @@ package app.myzel394.alibi.ui +import android.os.Build import androidx.compose.ui.unit.dp val BIG_PRIMARY_BUTTON_SIZE = 64.dp val MAX_AMPLITUDE = 20000 +val SUPPORTS_DARK_MODE_NATIVELY = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/ThemeSelector.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/ThemeSelector.kt new file mode 100644 index 000000000..8e74bc022 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/ThemeSelector.kt @@ -0,0 +1,140 @@ +package app.myzel394.alibi.ui.components.SettingsScreen.atoms + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +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.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Mic +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import androidx.navigation.navOptions +import app.myzel394.alibi.dataStore +import app.myzel394.alibi.db.AppSettings +import kotlinx.coroutines.launch + +@Composable +fun Preview( + backgroundColor: Color, + primaryColor: Color, + textColor: Color, + onSelect: () -> Unit, +) { + Column( + modifier = Modifier + .width(100.dp) + .height(200.dp) + .clip(shape = RoundedCornerShape(10.dp)) + .border(width = 1.dp, color = textColor, shape = RoundedCornerShape(10.dp)) + .background(backgroundColor) + .clickable { onSelect() }, + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.SpaceBetween, + ) { + Row( + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier + .fillMaxWidth() + .padding(10.dp) + ) { + Box( + modifier = Modifier + .width(30.dp) + .height(10.dp) + .clip(shape = RoundedCornerShape(10.dp)) + .background(primaryColor) + ) + Box( + modifier = Modifier + .width(10.dp) + .height(10.dp) + .clip(shape = RoundedCornerShape(10.dp)) + .background(primaryColor) + ) + } + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + Icon( + Icons.Default.Mic, + contentDescription = null, + tint = primaryColor, + ) + Box( + modifier = Modifier + .width(40.dp) + .height(6.dp) + .clip(shape = RoundedCornerShape(10.dp)) + .background(primaryColor) + ) + Box( + modifier = Modifier + .width(75.dp) + .height(10.dp) + .clip(shape = RoundedCornerShape(10.dp)) + .background(textColor) + ) + } + Box {} + } +} + +@Composable +fun ThemeSelector() { + val scope = rememberCoroutineScope() + + val dataStore = LocalContext.current.dataStore + val settings = dataStore + .data + .collectAsState(initial = AppSettings.getDefaultInstance()) + .value + + Row( + horizontalArrangement = Arrangement.SpaceEvenly, + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Preview( + backgroundColor = Color(0xFFF0F0F0), + primaryColor = Color(0xFFAAAAAA), + textColor = Color(0xFFCCCCCC), + onSelect = { + scope.launch { + dataStore.updateData { + it.setTheme(AppSettings.Theme.LIGHT) + } + } + } + ) + Preview( + backgroundColor = Color(0xFF444444), + primaryColor = Color(0xFF888888), + textColor = Color(0xFF606060), + onSelect = { + scope.launch { + dataStore.updateData { + it.setTheme(AppSettings.Theme.DARK) + } + } + } + ) + } +} \ No newline at end of file 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 a1db37851..13294c5a4 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 @@ -35,6 +35,7 @@ import androidx.navigation.NavController import app.myzel394.alibi.R import app.myzel394.alibi.dataStore import app.myzel394.alibi.db.AppSettings +import app.myzel394.alibi.ui.SUPPORTS_DARK_MODE_NATIVELY import app.myzel394.alibi.ui.components.SettingsScreen.atoms.BitrateTile import app.myzel394.alibi.ui.components.SettingsScreen.atoms.EncoderTile import app.myzel394.alibi.ui.components.SettingsScreen.atoms.ForceExactMaxDurationTile @@ -43,6 +44,7 @@ import app.myzel394.alibi.ui.components.SettingsScreen.atoms.IntervalDurationTil import app.myzel394.alibi.ui.components.SettingsScreen.atoms.MaxDurationTile import app.myzel394.alibi.ui.components.SettingsScreen.atoms.OutputFormatTile import app.myzel394.alibi.ui.components.SettingsScreen.atoms.SamplingRateTile +import app.myzel394.alibi.ui.components.SettingsScreen.atoms.ThemeSelector import app.myzel394.alibi.ui.components.atoms.GlobalSwitch import app.myzel394.alibi.ui.components.atoms.MessageBox import app.myzel394.alibi.ui.components.atoms.MessageType @@ -80,7 +82,7 @@ fun SettingsScreen( }, modifier = Modifier .nestedScroll(scrollBehavior.nestedScrollConnection) - ) {padding -> + ) { padding -> Column( modifier = Modifier .fillMaxSize() @@ -107,6 +109,9 @@ fun SettingsScreen( message = stringResource(R.string.ui_settings_hint_recordingActive_message), ) } + if (!SUPPORTS_DARK_MODE_NATIVELY) { + ThemeSelector() + } GlobalSwitch( label = stringResource(R.string.ui_settings_advancedSettings_label), checked = settings.showAdvancedSettings, From c994a47d8a608becc7f370567dfde423a060cc44 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 22 Oct 2023 15:02:28 +0200 Subject: [PATCH 2/2] fix: Improve ThemeSelector --- .../SettingsScreen/atoms/ThemeSelector.kt | 138 +++++++++++------- 1 file changed, 86 insertions(+), 52 deletions(-) diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/ThemeSelector.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/ThemeSelector.kt index 8e74bc022..0d988d8c8 100644 --- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/ThemeSelector.kt +++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/ThemeSelector.kt @@ -7,14 +7,18 @@ 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.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.CheckCircle import androidx.compose.material.icons.filled.Mic import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.rememberCoroutineScope @@ -31,68 +35,90 @@ import kotlinx.coroutines.launch @Composable fun Preview( + modifier: Modifier = Modifier, backgroundColor: Color, primaryColor: Color, textColor: Color, onSelect: () -> Unit, + isSelected: Boolean = false, ) { - Column( - modifier = Modifier - .width(100.dp) - .height(200.dp) - .clip(shape = RoundedCornerShape(10.dp)) - .border(width = 1.dp, color = textColor, shape = RoundedCornerShape(10.dp)) - .background(backgroundColor) - .clickable { onSelect() }, - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.SpaceBetween, + Box( + modifier = modifier, + contentAlignment = Alignment.Center, ) { - Row( - horizontalArrangement = Arrangement.SpaceBetween, - modifier = Modifier - .fillMaxWidth() - .padding(10.dp) - ) { - Box( - modifier = Modifier - .width(30.dp) - .height(10.dp) - .clip(shape = RoundedCornerShape(10.dp)) - .background(primaryColor) - ) - Box( - modifier = Modifier - .width(10.dp) - .height(10.dp) - .clip(shape = RoundedCornerShape(10.dp)) - .background(primaryColor) - ) - } Column( + modifier = Modifier + .width(100.dp) + .height(200.dp) + .clip(shape = RoundedCornerShape(10.dp)) + .border(width = 1.dp, color = textColor, shape = RoundedCornerShape(10.dp)) + .background(backgroundColor) + .clickable { onSelect() }, horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(12.dp), + verticalArrangement = Arrangement.SpaceBetween, ) { - Icon( - Icons.Default.Mic, - contentDescription = null, - tint = primaryColor, - ) - Box( + Row( + horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier - .width(40.dp) - .height(6.dp) - .clip(shape = RoundedCornerShape(10.dp)) - .background(primaryColor) - ) - Box( + .fillMaxWidth() + .padding(10.dp) + ) { + Box( + modifier = Modifier + .width(30.dp) + .height(10.dp) + .clip(shape = RoundedCornerShape(10.dp)) + .background(primaryColor) + ) + Box( + modifier = Modifier + .size(10.dp) + .clip(shape = RoundedCornerShape(10.dp)) + .background(primaryColor) + ) + } + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + Icon( + Icons.Default.Mic, + contentDescription = null, + tint = primaryColor, + ) + Box( + modifier = Modifier + .width(40.dp) + .height(6.dp) + .clip(shape = RoundedCornerShape(10.dp)) + .background(primaryColor) + ) + Box( + modifier = Modifier + .width(75.dp) + .height(10.dp) + .clip(shape = RoundedCornerShape(10.dp)) + .background(textColor) + ) + } + Box {} + } + if (isSelected) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, modifier = Modifier - .width(75.dp) - .height(10.dp) - .clip(shape = RoundedCornerShape(10.dp)) - .background(textColor) - ) + .fillMaxSize(), + ) { + Icon( + Icons.Default.CheckCircle, + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier + .size(30.dp), + ) + } } - Box {} } } @@ -113,6 +139,9 @@ fun ThemeSelector() { .padding(16.dp) ) { Preview( + modifier = Modifier + .fillMaxWidth() + .weight(1f), backgroundColor = Color(0xFFF0F0F0), primaryColor = Color(0xFFAAAAAA), textColor = Color(0xFFCCCCCC), @@ -122,9 +151,13 @@ fun ThemeSelector() { it.setTheme(AppSettings.Theme.LIGHT) } } - } + }, + isSelected = settings.theme == AppSettings.Theme.LIGHT, ) Preview( + modifier = Modifier + .fillMaxWidth() + .weight(1f), backgroundColor = Color(0xFF444444), primaryColor = Color(0xFF888888), textColor = Color(0xFF606060), @@ -134,7 +167,8 @@ fun ThemeSelector() { it.setTheme(AppSettings.Theme.DARK) } } - } + }, + isSelected = settings.theme == AppSettings.Theme.DARK, ) } } \ No newline at end of file