From 21b561f835be65a05b387c49533758b3ae9e7f14 Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Fri, 14 Jul 2023 15:08:23 +0530 Subject: [PATCH 1/6] Create a minimal implementation of NavHost --- app/build.gradle | 3 ++ .../com/bnyro/recorder/ui/Destinations.kt | 9 ++++++ .../com/bnyro/recorder/ui/MainActivity.kt | 12 ++++++-- .../java/com/bnyro/recorder/ui/NavHost.kt | 29 +++++++++++++++++++ .../recorder/ui/screens/RecorderScreen.kt | 4 ++- 5 files changed, 53 insertions(+), 4 deletions(-) create mode 100755 app/src/main/java/com/bnyro/recorder/ui/Destinations.kt create mode 100755 app/src/main/java/com/bnyro/recorder/ui/NavHost.kt diff --git a/app/build.gradle b/app/build.gradle index 8f7d6c55..3426d7bb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -65,6 +65,9 @@ dependencies { implementation 'androidx.compose.material3:material3:1.1.1' implementation 'androidx.compose.material:material-icons-extended:1.4.3' + //Navigation + implementation 'androidx.navigation:navigation-compose:2.6.0' + // Testing testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' diff --git a/app/src/main/java/com/bnyro/recorder/ui/Destinations.kt b/app/src/main/java/com/bnyro/recorder/ui/Destinations.kt new file mode 100755 index 00000000..5364e58a --- /dev/null +++ b/app/src/main/java/com/bnyro/recorder/ui/Destinations.kt @@ -0,0 +1,9 @@ +package com.bnyro.recorder.ui + +interface Destination { + val route: String +} + +object Home : Destination { + override val route = "home" +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/recorder/ui/MainActivity.kt b/app/src/main/java/com/bnyro/recorder/ui/MainActivity.kt index 0ae8b235..691463fc 100644 --- a/app/src/main/java/com/bnyro/recorder/ui/MainActivity.kt +++ b/app/src/main/java/com/bnyro/recorder/ui/MainActivity.kt @@ -10,10 +10,10 @@ import androidx.compose.material3.Surface import androidx.compose.ui.Modifier import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.get +import androidx.navigation.compose.rememberNavController import com.bnyro.recorder.enums.Recorder import com.bnyro.recorder.enums.ThemeMode import com.bnyro.recorder.ui.models.ThemeModel -import com.bnyro.recorder.ui.screens.RecorderView import com.bnyro.recorder.ui.theme.RecordYouTheme class MainActivity : ComponentActivity() { @@ -35,11 +35,17 @@ class MainActivity : ComponentActivity() { else -> mode == ThemeMode.DARK } ) { + val navController = rememberNavController() Surface( - modifier = Modifier.fillMaxSize(), + modifier = Modifier + .fillMaxSize(), color = MaterialTheme.colorScheme.background ) { - RecorderView(initialRecorder) + AppNavHost( + navController = navController, + modifier = Modifier, + initialRecorder = initialRecorder + ) } } } diff --git a/app/src/main/java/com/bnyro/recorder/ui/NavHost.kt b/app/src/main/java/com/bnyro/recorder/ui/NavHost.kt new file mode 100755 index 00000000..b1737d2c --- /dev/null +++ b/app/src/main/java/com/bnyro/recorder/ui/NavHost.kt @@ -0,0 +1,29 @@ +package com.bnyro.recorder.ui + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import com.bnyro.recorder.enums.Recorder +import com.bnyro.recorder.ui.screens.RecorderView + +@Composable +fun AppNavHost( + navController: NavHostController, modifier: Modifier = Modifier, initialRecorder: Recorder +) { + NavHost( + navController = navController, startDestination = Home.route, modifier = modifier + ) { + composable(route = Home.route) { + RecorderView(initialRecorder, onNavigate = { destination -> + navController.navigateTo(destination.route) + }) + } + } +} + +fun NavHostController.navigateTo(route: String) = this.navigate(route) { + launchSingleTop = true + restoreState = true +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt b/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt index 4ebbd729..6e0f9080 100644 --- a/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt +++ b/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt @@ -49,6 +49,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel import com.bnyro.recorder.R import com.bnyro.recorder.enums.Recorder import com.bnyro.recorder.enums.RecorderState +import com.bnyro.recorder.ui.Destination import com.bnyro.recorder.ui.common.ClickableIcon import com.bnyro.recorder.ui.components.AudioVisualizer import com.bnyro.recorder.ui.components.SettingsBottomSheet @@ -56,7 +57,8 @@ import com.bnyro.recorder.ui.models.RecorderModel @Composable fun RecorderView( - initialRecorder: Recorder + initialRecorder: Recorder, + onNavigate: (destination: Destination) -> Unit ) { val recorderModel: RecorderModel = viewModel() val context = LocalContext.current From ab429b0a0f01170329787491db0380912eb17382 Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Fri, 14 Jul 2023 15:25:48 +0530 Subject: [PATCH 2/6] Change settings bottom sheet to a separate screen --- .../com/bnyro/recorder/ui/Destinations.kt | 4 + .../java/com/bnyro/recorder/ui/NavHost.kt | 5 + .../ui/components/SettingsBottomSheet.kt | 283 ------------------ .../recorder/ui/screens/RecorderScreen.kt | 12 +- .../recorder/ui/screens/SettingsScreen.kt | 265 ++++++++++++++++ 5 files changed, 276 insertions(+), 293 deletions(-) delete mode 100644 app/src/main/java/com/bnyro/recorder/ui/components/SettingsBottomSheet.kt create mode 100644 app/src/main/java/com/bnyro/recorder/ui/screens/SettingsScreen.kt diff --git a/app/src/main/java/com/bnyro/recorder/ui/Destinations.kt b/app/src/main/java/com/bnyro/recorder/ui/Destinations.kt index 5364e58a..fe3b76fe 100755 --- a/app/src/main/java/com/bnyro/recorder/ui/Destinations.kt +++ b/app/src/main/java/com/bnyro/recorder/ui/Destinations.kt @@ -6,4 +6,8 @@ interface Destination { object Home : Destination { override val route = "home" +} + +object Settings : Destination { + override val route = "settings" } \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/recorder/ui/NavHost.kt b/app/src/main/java/com/bnyro/recorder/ui/NavHost.kt index b1737d2c..27c7dfa4 100755 --- a/app/src/main/java/com/bnyro/recorder/ui/NavHost.kt +++ b/app/src/main/java/com/bnyro/recorder/ui/NavHost.kt @@ -7,6 +7,7 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import com.bnyro.recorder.enums.Recorder import com.bnyro.recorder.ui.screens.RecorderView +import com.bnyro.recorder.ui.screens.SettingsScreen @Composable fun AppNavHost( @@ -20,6 +21,10 @@ fun AppNavHost( navController.navigateTo(destination.route) }) } + + composable(route = Settings.route) { + SettingsScreen() + } } } diff --git a/app/src/main/java/com/bnyro/recorder/ui/components/SettingsBottomSheet.kt b/app/src/main/java/com/bnyro/recorder/ui/components/SettingsBottomSheet.kt deleted file mode 100644 index 0f8b4218..00000000 --- a/app/src/main/java/com/bnyro/recorder/ui/components/SettingsBottomSheet.kt +++ /dev/null @@ -1,283 +0,0 @@ -package com.bnyro.recorder.ui.components - -import android.net.Uri -import android.os.Build -import androidx.activity.compose.rememberLauncherForActivityResult -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.DarkMode -import androidx.compose.material.icons.filled.Info -import androidx.compose.material3.Button -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.ModalBottomSheet -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.lifecycle.viewmodel.compose.viewModel -import com.bnyro.recorder.R -import com.bnyro.recorder.enums.AudioChannels -import com.bnyro.recorder.enums.AudioDeviceSource -import com.bnyro.recorder.enums.AudioSource -import com.bnyro.recorder.enums.ThemeMode -import com.bnyro.recorder.enums.VideoFormat -import com.bnyro.recorder.obj.AudioFormat -import com.bnyro.recorder.ui.common.CheckboxPref -import com.bnyro.recorder.ui.common.ChipSelector -import com.bnyro.recorder.ui.common.ClickableIcon -import com.bnyro.recorder.ui.common.CustomNumInputPref -import com.bnyro.recorder.ui.common.SelectionDialog -import com.bnyro.recorder.ui.dialogs.AboutDialog -import com.bnyro.recorder.ui.models.ThemeModel -import com.bnyro.recorder.util.PickFolderContract -import com.bnyro.recorder.util.Preferences - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun SettingsBottomSheet( - onDismissRequest: () -> Unit -) { - val themeModel: ThemeModel = viewModel() - - var audioFormat by remember { - mutableStateOf(AudioFormat.getCurrent()) - } - var audioChannels by remember { - mutableStateOf( - AudioChannels.fromInt( - Preferences.prefs.getInt(Preferences.audioChannelsKey, AudioChannels.MONO.value) - ) - ) - } - var audioDeviceSource by remember { - mutableStateOf( - AudioDeviceSource.fromInt( - Preferences.prefs.getInt( - Preferences.audioDeviceSourceKey, - AudioDeviceSource.DEFAULT.value - ) - ) - ) - } - var screenAudioSource by remember { - mutableStateOf( - AudioSource.fromInt(Preferences.prefs.getInt(Preferences.audioSourceKey, 0)) - ) - } - var videoEncoder by remember { - mutableStateOf(VideoFormat.getCurrent()) - } - - val directoryPicker = rememberLauncherForActivityResult(PickFolderContract()) { - it ?: return@rememberLauncherForActivityResult - Preferences.edit { putString(Preferences.targetFolderKey, it.toString()) } - } - var showAbout by remember { - mutableStateOf(false) - } - var showThemePref by remember { - mutableStateOf(false) - } - - val scrollState = rememberScrollState() - - ModalBottomSheet( - onDismissRequest = { - onDismissRequest.invoke() - } - ) { - Box { - Column( - modifier = Modifier - .padding(horizontal = 20.dp) - .padding(bottom = 20.dp) - .height(300.dp) - .verticalScroll(scrollState) - ) { - Text( - text = stringResource(R.string.directory), - fontWeight = FontWeight.Bold, - fontSize = 18.sp - ) - Spacer(modifier = Modifier.height(5.dp)) - Button( - onClick = { - val lastDir = Preferences.prefs.getString(Preferences.targetFolderKey, "") - .takeIf { !it.isNullOrBlank() } - directoryPicker.launch(lastDir?.let { Uri.parse(it) }) - } - ) { - Text(stringResource(R.string.choose_dir)) - } - Spacer(modifier = Modifier.height(10.dp)) - ChipSelector( - title = stringResource(R.string.audio_format), - entries = AudioFormat.formats.map { it.name }, - values = AudioFormat.formats.map { it.format }, - selections = listOf(audioFormat.format) - ) { index, newValue -> - if (newValue) { - audioFormat = AudioFormat.formats[index] - Preferences.edit { putString(Preferences.audioFormatKey, audioFormat.name) } - } - } - Row { - CustomNumInputPref( - key = Preferences.audioSampleRateKey, - title = stringResource(R.string.sample_rate), - defValue = 44_100 - ) - Spacer(modifier = Modifier.width(10.dp)) - CustomNumInputPref( - key = Preferences.audioBitrateKey, - title = stringResource(R.string.bitrate), - defValue = 192_000 - ) - } - val audioDeviceSourceValues = AudioDeviceSource.values().map { it.value } - ChipSelector( - entries = listOf( - R.string.default_audio, - R.string.microphone, - R.string.camcorder, - R.string.unprocessed - ).map { - stringResource(it) - }, - values = audioDeviceSourceValues, - selections = listOf(audioDeviceSource.value) - ) { index, newValue -> - if (newValue) { - audioDeviceSource = AudioDeviceSource.fromInt( - audioDeviceSourceValues[index] - ) - Preferences.edit { - putInt(Preferences.audioDeviceSourceKey, audioDeviceSourceValues[index]) - } - } - } - val audioChannelsValues = AudioChannels.values().map { it.value } - ChipSelector( - entries = listOf(R.string.mono, R.string.stereo).map { - stringResource(it) - }, - values = audioChannelsValues, - selections = listOf(audioChannels.value) - ) { index, newValue -> - if (newValue) { - audioChannels = AudioChannels.fromInt(audioChannelsValues[index]) - Preferences.edit { - putInt(Preferences.audioChannelsKey, audioChannelsValues[index]) - } - } - } - Spacer(modifier = Modifier.height(10.dp)) - val audioValues = AudioSource.values().map { it.value } - ChipSelector( - title = stringResource(R.string.screen_recorder), - entries = listOf(R.string.no_audio, R.string.microphone).map { - stringResource(it) - }, - values = audioValues, - selections = listOf(screenAudioSource.value) - ) { index, newValue -> - if (newValue) { - screenAudioSource = AudioSource.fromInt(audioValues[index]) - Preferences.edit { putInt(Preferences.audioSourceKey, audioValues[index]) } - } - } - ChipSelector( - entries = VideoFormat.codecs.map { it.name }, - values = VideoFormat.codecs.map { it.codec }, - selections = listOf(videoEncoder.codec) - ) { index, newValue -> - if (newValue) { - videoEncoder = VideoFormat.codecs[index] - Preferences.edit { putInt(Preferences.videoCodecKey, videoEncoder.codec) } - } - } - Spacer(modifier = Modifier.height(10.dp)) - CustomNumInputPref( - key = Preferences.videoBitrateKey, - title = stringResource(R.string.bitrate), - defValue = 1_200_000 - ) - Spacer(modifier = Modifier.height(10.dp)) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - CheckboxPref( - prefKey = Preferences.losslessRecorderKey, - title = stringResource(R.string.lossless_audio), - summary = stringResource(R.string.lossless_audio_desc) - ) - } - Spacer(modifier = Modifier.height(10.dp)) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - CheckboxPref( - prefKey = Preferences.showOverlayAnnotationToolKey, - title = stringResource(R.string.screen_recorder_annotation), - summary = stringResource(R.string.screen_recorder_annotation_desc) - ) - } - Spacer(modifier = Modifier.height(10.dp)) - NamingPatternPref() - } - - Column( - modifier = Modifier.align(Alignment.TopEnd) - ) { - AnimatedVisibility(visible = scrollState.value < 50) { - Row { - ClickableIcon( - imageVector = Icons.Default.DarkMode, - contentDescription = stringResource(R.string.theme) - ) { - showThemePref = true - } - ClickableIcon( - imageVector = Icons.Default.Info, - contentDescription = stringResource(R.string.about) - ) { - showAbout = true - } - } - } - } - } - } - - if (showThemePref) { - SelectionDialog( - onDismissRequest = { showThemePref = false }, - title = stringResource(R.string.theme), - entries = listOf(R.string.system, R.string.light, R.string.dark).map { - stringResource(it) - } - ) { - themeModel.themeMode = ThemeMode.values()[it] - Preferences.edit { putString(Preferences.themeModeKey, themeModel.themeMode.name) } - } - } - - if (showAbout) { - AboutDialog { - showAbout = false - } - } -} diff --git a/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt b/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt index 6e0f9080..0e85cd11 100644 --- a/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt +++ b/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt @@ -50,9 +50,9 @@ import com.bnyro.recorder.R import com.bnyro.recorder.enums.Recorder import com.bnyro.recorder.enums.RecorderState import com.bnyro.recorder.ui.Destination +import com.bnyro.recorder.ui.Settings import com.bnyro.recorder.ui.common.ClickableIcon import com.bnyro.recorder.ui.components.AudioVisualizer -import com.bnyro.recorder.ui.components.SettingsBottomSheet import com.bnyro.recorder.ui.models.RecorderModel @Composable @@ -66,9 +66,6 @@ fun RecorderView( context.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager val orientation = LocalConfiguration.current.orientation - var showBottomSheet by remember { - mutableStateOf(false) - } var showPlayerScreen by remember { mutableStateOf(false) } @@ -164,7 +161,7 @@ fun RecorderView( imageVector = Icons.Default.Settings, contentDescription = stringResource(R.string.settings) ) { - showBottomSheet = true + onNavigate(Settings) } Spacer(modifier = Modifier.width(20.dp)) @@ -243,11 +240,6 @@ fun RecorderView( } } - if (showBottomSheet) { - SettingsBottomSheet { - showBottomSheet = false - } - } if (showPlayerScreen) { PlayerScreen(recordScreenMode) { showPlayerScreen = false diff --git a/app/src/main/java/com/bnyro/recorder/ui/screens/SettingsScreen.kt b/app/src/main/java/com/bnyro/recorder/ui/screens/SettingsScreen.kt new file mode 100644 index 00000000..faca46ba --- /dev/null +++ b/app/src/main/java/com/bnyro/recorder/ui/screens/SettingsScreen.kt @@ -0,0 +1,265 @@ +package com.bnyro.recorder.ui.screens + +import android.net.Uri +import android.os.Build +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.DarkMode +import androidx.compose.material.icons.filled.Info +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.lifecycle.viewmodel.compose.viewModel +import com.bnyro.recorder.R +import com.bnyro.recorder.enums.AudioChannels +import com.bnyro.recorder.enums.AudioDeviceSource +import com.bnyro.recorder.enums.AudioSource +import com.bnyro.recorder.enums.ThemeMode +import com.bnyro.recorder.enums.VideoFormat +import com.bnyro.recorder.obj.AudioFormat +import com.bnyro.recorder.ui.common.CheckboxPref +import com.bnyro.recorder.ui.common.ChipSelector +import com.bnyro.recorder.ui.common.ClickableIcon +import com.bnyro.recorder.ui.common.CustomNumInputPref +import com.bnyro.recorder.ui.common.SelectionDialog +import com.bnyro.recorder.ui.components.NamingPatternPref +import com.bnyro.recorder.ui.dialogs.AboutDialog +import com.bnyro.recorder.ui.models.ThemeModel +import com.bnyro.recorder.util.PickFolderContract +import com.bnyro.recorder.util.Preferences + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SettingsScreen() { + val themeModel: ThemeModel = viewModel() + + var audioFormat by remember { + mutableStateOf(AudioFormat.getCurrent()) + } + var audioChannels by remember { + mutableStateOf( + AudioChannels.fromInt( + Preferences.prefs.getInt(Preferences.audioChannelsKey, AudioChannels.MONO.value) + ) + ) + } + var audioDeviceSource by remember { + mutableStateOf( + AudioDeviceSource.fromInt( + Preferences.prefs.getInt( + Preferences.audioDeviceSourceKey, + AudioDeviceSource.DEFAULT.value + ) + ) + ) + } + var screenAudioSource by remember { + mutableStateOf( + AudioSource.fromInt(Preferences.prefs.getInt(Preferences.audioSourceKey, 0)) + ) + } + var videoEncoder by remember { + mutableStateOf(VideoFormat.getCurrent()) + } + + val directoryPicker = rememberLauncherForActivityResult(PickFolderContract()) { + it ?: return@rememberLauncherForActivityResult + Preferences.edit { putString(Preferences.targetFolderKey, it.toString()) } + } + var showAbout by remember { + mutableStateOf(false) + } + var showThemePref by remember { + mutableStateOf(false) + } + + Scaffold(modifier = Modifier.fillMaxSize(), topBar = { + TopAppBar( + title = { Text(stringResource(R.string.settings)) }, + actions = { + ClickableIcon( + imageVector = Icons.Default.DarkMode, + contentDescription = stringResource(R.string.theme) + ) { + showThemePref = true + } + ClickableIcon( + imageVector = Icons.Default.Info, + contentDescription = stringResource(R.string.about) + ) { + showAbout = true + } + }) + }) { paddingValues -> + Column( + modifier = Modifier + .padding(paddingValues) + .padding(horizontal = 16.dp) + ) { + Text( + text = stringResource(R.string.directory), + fontWeight = FontWeight.Bold, + fontSize = 18.sp + ) + Spacer(modifier = Modifier.height(5.dp)) + Button( + onClick = { + val lastDir = Preferences.prefs.getString(Preferences.targetFolderKey, "") + .takeIf { !it.isNullOrBlank() } + directoryPicker.launch(lastDir?.let { Uri.parse(it) }) + } + ) { + Text(stringResource(R.string.choose_dir)) + } + Spacer(modifier = Modifier.height(10.dp)) + ChipSelector( + title = stringResource(R.string.audio_format), + entries = AudioFormat.formats.map { it.name }, + values = AudioFormat.formats.map { it.format }, + selections = listOf(audioFormat.format) + ) { index, newValue -> + if (newValue) { + audioFormat = AudioFormat.formats[index] + Preferences.edit { putString(Preferences.audioFormatKey, audioFormat.name) } + } + } + Row { + CustomNumInputPref( + key = Preferences.audioSampleRateKey, + title = stringResource(R.string.sample_rate), + defValue = 44_100 + ) + Spacer(modifier = Modifier.width(10.dp)) + CustomNumInputPref( + key = Preferences.audioBitrateKey, + title = stringResource(R.string.bitrate), + defValue = 192_000 + ) + } + val audioDeviceSourceValues = AudioDeviceSource.values().map { it.value } + ChipSelector( + entries = listOf( + R.string.default_audio, + R.string.microphone, + R.string.camcorder, + R.string.unprocessed + ).map { + stringResource(it) + }, + values = audioDeviceSourceValues, + selections = listOf(audioDeviceSource.value) + ) { index, newValue -> + if (newValue) { + audioDeviceSource = AudioDeviceSource.fromInt( + audioDeviceSourceValues[index] + ) + Preferences.edit { + putInt(Preferences.audioDeviceSourceKey, audioDeviceSourceValues[index]) + } + } + } + val audioChannelsValues = AudioChannels.values().map { it.value } + ChipSelector( + entries = listOf(R.string.mono, R.string.stereo).map { + stringResource(it) + }, + values = audioChannelsValues, + selections = listOf(audioChannels.value) + ) { index, newValue -> + if (newValue) { + audioChannels = AudioChannels.fromInt(audioChannelsValues[index]) + Preferences.edit { + putInt(Preferences.audioChannelsKey, audioChannelsValues[index]) + } + } + } + Spacer(modifier = Modifier.height(10.dp)) + val audioValues = AudioSource.values().map { it.value } + ChipSelector( + title = stringResource(R.string.screen_recorder), + entries = listOf(R.string.no_audio, R.string.microphone).map { + stringResource(it) + }, + values = audioValues, + selections = listOf(screenAudioSource.value) + ) { index, newValue -> + if (newValue) { + screenAudioSource = AudioSource.fromInt(audioValues[index]) + Preferences.edit { putInt(Preferences.audioSourceKey, audioValues[index]) } + } + } + ChipSelector( + entries = VideoFormat.codecs.map { it.name }, + values = VideoFormat.codecs.map { it.codec }, + selections = listOf(videoEncoder.codec) + ) { index, newValue -> + if (newValue) { + videoEncoder = VideoFormat.codecs[index] + Preferences.edit { putInt(Preferences.videoCodecKey, videoEncoder.codec) } + } + } + Spacer(modifier = Modifier.height(10.dp)) + CustomNumInputPref( + key = Preferences.videoBitrateKey, + title = stringResource(R.string.bitrate), + defValue = 1_200_000 + ) + Spacer(modifier = Modifier.height(10.dp)) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + CheckboxPref( + prefKey = Preferences.losslessRecorderKey, + title = stringResource(R.string.lossless_audio), + summary = stringResource(R.string.lossless_audio_desc) + ) + } + Spacer(modifier = Modifier.height(10.dp)) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + CheckboxPref( + prefKey = Preferences.showOverlayAnnotationToolKey, + title = stringResource(R.string.screen_recorder_annotation), + summary = stringResource(R.string.screen_recorder_annotation_desc) + ) + } + Spacer(modifier = Modifier.height(10.dp)) + NamingPatternPref() + } + } + + if (showThemePref) { + SelectionDialog( + onDismissRequest = { showThemePref = false }, + title = stringResource(R.string.theme), + entries = listOf(R.string.system, R.string.light, R.string.dark).map { + stringResource(it) + } + ) { + themeModel.themeMode = ThemeMode.values()[it] + Preferences.edit { putString(Preferences.themeModeKey, themeModel.themeMode.name) } + } + } + + if (showAbout) { + AboutDialog { + showAbout = false + } + } +} From a10c609847db5235370275856231926387aaff96 Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Fri, 14 Jul 2023 15:40:10 +0530 Subject: [PATCH 3/6] Move player dialog to a separate screen --- .../com/bnyro/recorder/ui/Destinations.kt | 4 + .../java/com/bnyro/recorder/ui/NavHost.kt | 5 + .../bnyro/recorder/ui/screens/PlayerScreen.kt | 145 ++++++++++-------- .../recorder/ui/screens/RecorderScreen.kt | 12 +- 4 files changed, 91 insertions(+), 75 deletions(-) diff --git a/app/src/main/java/com/bnyro/recorder/ui/Destinations.kt b/app/src/main/java/com/bnyro/recorder/ui/Destinations.kt index fe3b76fe..9bb8a308 100755 --- a/app/src/main/java/com/bnyro/recorder/ui/Destinations.kt +++ b/app/src/main/java/com/bnyro/recorder/ui/Destinations.kt @@ -10,4 +10,8 @@ object Home : Destination { object Settings : Destination { override val route = "settings" +} + +object RecordingPlayer : Destination { + override val route = "player" } \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/recorder/ui/NavHost.kt b/app/src/main/java/com/bnyro/recorder/ui/NavHost.kt index 27c7dfa4..0892d5bd 100755 --- a/app/src/main/java/com/bnyro/recorder/ui/NavHost.kt +++ b/app/src/main/java/com/bnyro/recorder/ui/NavHost.kt @@ -6,6 +6,7 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import com.bnyro.recorder.enums.Recorder +import com.bnyro.recorder.ui.screens.PlayerScreen import com.bnyro.recorder.ui.screens.RecorderView import com.bnyro.recorder.ui.screens.SettingsScreen @@ -25,6 +26,10 @@ fun AppNavHost( composable(route = Settings.route) { SettingsScreen() } + + composable(route = RecordingPlayer.route) { + PlayerScreen(showVideoModeInitially = false) + } } } diff --git a/app/src/main/java/com/bnyro/recorder/ui/screens/PlayerScreen.kt b/app/src/main/java/com/bnyro/recorder/ui/screens/PlayerScreen.kt index 11d682c2..96ceb08f 100644 --- a/app/src/main/java/com/bnyro/recorder/ui/screens/PlayerScreen.kt +++ b/app/src/main/java/com/bnyro/recorder/ui/screens/PlayerScreen.kt @@ -2,6 +2,7 @@ package com.bnyro.recorder.ui.screens import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Delete @@ -9,10 +10,16 @@ import androidx.compose.material.icons.filled.Sort import androidx.compose.material3.Checkbox import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.LargeTopAppBar +import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.documentfile.provider.DocumentFile @@ -20,14 +27,13 @@ import androidx.lifecycle.viewmodel.compose.viewModel import com.bnyro.recorder.R import com.bnyro.recorder.enums.SortOrder import com.bnyro.recorder.ui.common.ClickableIcon -import com.bnyro.recorder.ui.common.FullscreenDialog import com.bnyro.recorder.ui.components.PlayerView import com.bnyro.recorder.ui.models.PlayerModel +@OptIn(ExperimentalMaterial3Api::class) @Composable fun PlayerScreen( - showVideoModeInitially: Boolean, - onDismissRequest: () -> Unit + showVideoModeInitially: Boolean ) { var showDeleteDialog by remember { mutableStateOf(false) @@ -40,74 +46,83 @@ fun PlayerScreen( } val playerModel: PlayerModel = viewModel() - FullscreenDialog( - title = stringResource(R.string.recordings), - onDismissRequest = onDismissRequest, - useLargeAppBar = true, - actions = { - Box { - var showDropDown by remember { - mutableStateOf(false) - } - ClickableIcon( - imageVector = Icons.Default.Sort, - contentDescription = stringResource(R.string.sort) - ) { - showDropDown = true - } + val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior( + rememberTopAppBarState() + ) - val sortOptions = listOf( - SortOrder.ALPHABETIC, - SortOrder.ALPHABETIC_REV, - SortOrder.SIZE, - SortOrder.SIZE_REV - ) - val sortOptionNames = listOf( - R.string.alphabetic, - R.string.alphabetic_rev, - R.string.size, - R.string.size_rev - ) - DropdownMenu(showDropDown, { showDropDown = false }) { - sortOptions.forEachIndexed { index, sortOrder -> - DropdownMenuItem( - text = { - Text(stringResource(sortOptionNames[index])) - }, - onClick = { - selectedSortOrder = sortOrder - showDropDown = false + Scaffold( + modifier = Modifier + .fillMaxSize() + .nestedScroll(scrollBehavior.nestedScrollConnection), + topBar = { + LargeTopAppBar( + title = { Text(stringResource(R.string.recordings)) }, + actions = { + Box { + var showDropDown by remember { + mutableStateOf(false) + } + ClickableIcon( + imageVector = Icons.Default.Sort, + contentDescription = stringResource(R.string.sort) + ) { + showDropDown = true + } + + val sortOptions = listOf( + SortOrder.ALPHABETIC, + SortOrder.ALPHABETIC_REV, + SortOrder.SIZE, + SortOrder.SIZE_REV + ) + val sortOptionNames = listOf( + R.string.alphabetic, + R.string.alphabetic_rev, + R.string.size, + R.string.size_rev + ) + DropdownMenu(showDropDown, { showDropDown = false }) { + sortOptions.forEachIndexed { index, sortOrder -> + DropdownMenuItem( + text = { + Text(stringResource(sortOptionNames[index])) + }, + onClick = { + selectedSortOrder = sortOrder + showDropDown = false + } + ) + } + } + } + if (selectedFiles.value.isNotEmpty()) { + val selectedAll = selectedFiles.value.size == playerModel.files.size + Checkbox( + modifier = Modifier.align(Alignment.CenterVertically), + checked = selectedAll, + onCheckedChange = { + if (selectedAll) { + selectedFiles.value = listOf() + } else { + selectedFiles.value = playerModel.files + } } ) } - } - } - if (selectedFiles.value.isNotEmpty()) { - val selectedAll = selectedFiles.value.size == playerModel.files.size - Checkbox( - modifier = Modifier.align(Alignment.CenterVertically), - checked = selectedAll, - onCheckedChange = { - if (selectedAll) { - selectedFiles.value = listOf() - } else { - selectedFiles.value = playerModel.files - } + ClickableIcon( + imageVector = Icons.Default.Delete, + contentDescription = stringResource(R.string.delete_all) + ) { + showDeleteDialog = true } - ) - } - ClickableIcon( - imageVector = Icons.Default.Delete, - contentDescription = stringResource(R.string.delete_all) - ) { - showDeleteDialog = true - } - } - ) { - Column( - modifier = Modifier.padding( - horizontal = 20.dp + }, + scrollBehavior = scrollBehavior ) + }) { paddingValues -> + Column( + modifier = Modifier + .padding(paddingValues) + .padding(horizontal = 16.dp) ) { PlayerView(showVideoModeInitially, showDeleteDialog, selectedSortOrder, selectedFiles) { showDeleteDialog = false diff --git a/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt b/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt index 0e85cd11..3ef36493 100644 --- a/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt +++ b/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt @@ -50,6 +50,7 @@ import com.bnyro.recorder.R import com.bnyro.recorder.enums.Recorder import com.bnyro.recorder.enums.RecorderState import com.bnyro.recorder.ui.Destination +import com.bnyro.recorder.ui.RecordingPlayer import com.bnyro.recorder.ui.Settings import com.bnyro.recorder.ui.common.ClickableIcon import com.bnyro.recorder.ui.components.AudioVisualizer @@ -66,9 +67,6 @@ fun RecorderView( context.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager val orientation = LocalConfiguration.current.orientation - var showPlayerScreen by remember { - mutableStateOf(false) - } var recordScreenMode by remember { mutableStateOf(false) } @@ -219,7 +217,7 @@ fun RecorderView( imageVector = Icons.Default.VideoLibrary, contentDescription = stringResource(R.string.recordings) ) { - showPlayerScreen = true + onNavigate(RecordingPlayer) } } } @@ -239,10 +237,4 @@ fun RecorderView( } } } - - if (showPlayerScreen) { - PlayerScreen(recordScreenMode) { - showPlayerScreen = false - } - } } From 9a11da289c60dbb3f1542c687dc8331715359a48 Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Fri, 14 Jul 2023 16:28:08 +0530 Subject: [PATCH 4/6] Add a two page layout for RecorderScreen with a Bottom Bar --- .../java/com/bnyro/recorder/ui/NavHost.kt | 4 +- .../bnyro/recorder/ui/screens/HomeScreen.kt | 116 ++++++++++++++++++ .../recorder/ui/screens/RecorderScreen.kt | 49 +------- 3 files changed, 120 insertions(+), 49 deletions(-) create mode 100644 app/src/main/java/com/bnyro/recorder/ui/screens/HomeScreen.kt diff --git a/app/src/main/java/com/bnyro/recorder/ui/NavHost.kt b/app/src/main/java/com/bnyro/recorder/ui/NavHost.kt index 0892d5bd..2d5b5684 100755 --- a/app/src/main/java/com/bnyro/recorder/ui/NavHost.kt +++ b/app/src/main/java/com/bnyro/recorder/ui/NavHost.kt @@ -6,8 +6,8 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import com.bnyro.recorder.enums.Recorder +import com.bnyro.recorder.ui.screens.HomeScreen import com.bnyro.recorder.ui.screens.PlayerScreen -import com.bnyro.recorder.ui.screens.RecorderView import com.bnyro.recorder.ui.screens.SettingsScreen @Composable @@ -18,7 +18,7 @@ fun AppNavHost( navController = navController, startDestination = Home.route, modifier = modifier ) { composable(route = Home.route) { - RecorderView(initialRecorder, onNavigate = { destination -> + HomeScreen(initialRecorder, onNavigate = { destination -> navController.navigateTo(destination.route) }) } diff --git a/app/src/main/java/com/bnyro/recorder/ui/screens/HomeScreen.kt b/app/src/main/java/com/bnyro/recorder/ui/screens/HomeScreen.kt new file mode 100644 index 00000000..c8fed4ed --- /dev/null +++ b/app/src/main/java/com/bnyro/recorder/ui/screens/HomeScreen.kt @@ -0,0 +1,116 @@ +package com.bnyro.recorder.ui.screens + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Mic +import androidx.compose.material.icons.filled.Settings +import androidx.compose.material.icons.filled.VideoLibrary +import androidx.compose.material.icons.filled.Videocam +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.NavigationBar +import androidx.compose.material3.NavigationBarItem +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.lifecycle.viewmodel.compose.viewModel +import com.bnyro.recorder.R +import com.bnyro.recorder.enums.Recorder +import com.bnyro.recorder.enums.RecorderState +import com.bnyro.recorder.ui.Destination +import com.bnyro.recorder.ui.RecordingPlayer +import com.bnyro.recorder.ui.Settings +import com.bnyro.recorder.ui.common.ClickableIcon +import com.bnyro.recorder.ui.models.RecorderModel +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) +@Composable +fun HomeScreen( + initialRecorder: Recorder, + onNavigate: (Destination) -> Unit, + recorderModel: RecorderModel = viewModel() +) { + val pagerState = rememberPagerState { 2 } + val scope = rememberCoroutineScope() + Scaffold(modifier = Modifier.fillMaxSize(), topBar = { + TopAppBar(title = { Text(stringResource(R.string.app_name)) }, actions = { + ClickableIcon( + imageVector = Icons.Default.Settings, + contentDescription = stringResource(R.string.settings) + ) { + onNavigate(Settings) + } + ClickableIcon( + imageVector = Icons.Default.VideoLibrary, + contentDescription = stringResource(R.string.recordings) + ) { + onNavigate(RecordingPlayer) + } + }) + }, bottomBar = { + Column { + AnimatedVisibility(recorderModel.recorderState == RecorderState.IDLE) { + NavigationBar { + NavigationBarItem(icon = { + Icon( + imageVector = Icons.Default.Mic, + contentDescription = stringResource( + id = R.string.record_sound + ) + ) + }, + label = { Text(stringResource(R.string.record_sound)) }, + selected = (pagerState.currentPage == 0), + onClick = { + scope.launch { + pagerState.animateScrollToPage( + 0 + ) + } + }) + NavigationBarItem(icon = { + Icon( + imageVector = Icons.Default.Videocam, + contentDescription = stringResource( + id = R.string.record_screen + ) + ) + }, + label = { Text(stringResource(R.string.record_screen)) }, + selected = (pagerState.currentPage == 1), + onClick = { + scope.launch { + pagerState.animateScrollToPage( + 1 + ) + } + }) + + } + } + } + }) { paddingValues -> + Column( + Modifier + .fillMaxSize() + .padding(paddingValues) + ) { + HorizontalPager( + state = pagerState, modifier = Modifier.fillMaxSize() + ) { index -> + RecorderView(initialRecorder = initialRecorder, recordScreenMode = (index == 1)) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt b/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt index 3ef36493..44dc7f62 100644 --- a/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt +++ b/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt @@ -8,7 +8,6 @@ import android.os.Build import android.text.format.DateUtils import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts -import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.Crossfade import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -19,14 +18,10 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ExpandLess -import androidx.compose.material.icons.filled.ExpandMore import androidx.compose.material.icons.filled.Mic import androidx.compose.material.icons.filled.Pause import androidx.compose.material.icons.filled.PlayArrow -import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.Stop -import androidx.compose.material.icons.filled.VideoLibrary import androidx.compose.material.icons.filled.Videocam import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon @@ -35,10 +30,6 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration @@ -49,9 +40,6 @@ import androidx.lifecycle.viewmodel.compose.viewModel import com.bnyro.recorder.R import com.bnyro.recorder.enums.Recorder import com.bnyro.recorder.enums.RecorderState -import com.bnyro.recorder.ui.Destination -import com.bnyro.recorder.ui.RecordingPlayer -import com.bnyro.recorder.ui.Settings import com.bnyro.recorder.ui.common.ClickableIcon import com.bnyro.recorder.ui.components.AudioVisualizer import com.bnyro.recorder.ui.models.RecorderModel @@ -59,7 +47,7 @@ import com.bnyro.recorder.ui.models.RecorderModel @Composable fun RecorderView( initialRecorder: Recorder, - onNavigate: (destination: Destination) -> Unit + recordScreenMode: Boolean ) { val recorderModel: RecorderModel = viewModel() val context = LocalContext.current @@ -67,10 +55,6 @@ fun RecorderView( context.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager val orientation = LocalConfiguration.current.orientation - var recordScreenMode by remember { - mutableStateOf(false) - } - val requestRecording = rememberLauncherForActivityResult( ActivityResultContracts.StartActivityForResult() ) { result -> @@ -92,7 +76,6 @@ fun RecorderView( } Recorder.SCREEN -> { - recordScreenMode = true requestScreenRecording() } @@ -155,14 +138,6 @@ fun RecorderView( Row( verticalAlignment = Alignment.CenterVertically ) { - ClickableIcon( - imageVector = Icons.Default.Settings, - contentDescription = stringResource(R.string.settings) - ) { - onNavigate(Settings) - } - - Spacer(modifier = Modifier.width(20.dp)) FloatingActionButton( onClick = { @@ -189,9 +164,8 @@ fun RecorderView( ) } - Spacer(modifier = Modifier.width(20.dp)) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && recorderModel.recorderState != RecorderState.IDLE) { + Spacer(modifier = Modifier.width(20.dp)) ClickableIcon( imageVector = if (recorderModel.recorderState == RecorderState.PAUSED) { Icons.Default.PlayArrow @@ -212,28 +186,9 @@ fun RecorderView( recorderModel.pauseRecording() } } - } else { - ClickableIcon( - imageVector = Icons.Default.VideoLibrary, - contentDescription = stringResource(R.string.recordings) - ) { - onNavigate(RecordingPlayer) - } } } - Spacer(modifier = Modifier.height(5.dp)) - - AnimatedVisibility(recorderModel.recorderState == RecorderState.IDLE) { - ClickableIcon( - imageVector = if (recordScreenMode) Icons.Default.ExpandMore else Icons.Default.ExpandLess, - contentDescription = stringResource( - if (recordScreenMode) R.string.record_sound else R.string.record_screen - ) - ) { - recordScreenMode = !recordScreenMode - } - } } } } From 97fdda2608eb2d519f1bd7f50034f44fa0a4ef63 Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Fri, 14 Jul 2023 19:44:39 +0530 Subject: [PATCH 5/6] Make the homepage more aesthetic --- .../bnyro/recorder/ui/common/BlobIconBox.kt | 41 ++++++ .../recorder/ui/screens/RecorderScreen.kt | 132 +++++++++++------- app/src/main/res/drawable/blob.xml | 10 ++ app/src/main/res/drawable/ic_mic.xml | 63 +++++++++ .../main/res/drawable/ic_screen_record.xml | 24 ++++ 5 files changed, 219 insertions(+), 51 deletions(-) create mode 100644 app/src/main/java/com/bnyro/recorder/ui/common/BlobIconBox.kt create mode 100755 app/src/main/res/drawable/blob.xml create mode 100644 app/src/main/res/drawable/ic_mic.xml create mode 100644 app/src/main/res/drawable/ic_screen_record.xml diff --git a/app/src/main/java/com/bnyro/recorder/ui/common/BlobIconBox.kt b/app/src/main/java/com/bnyro/recorder/ui/common/BlobIconBox.kt new file mode 100644 index 00000000..f7c04226 --- /dev/null +++ b/app/src/main/java/com/bnyro/recorder/ui/common/BlobIconBox.kt @@ -0,0 +1,41 @@ +package com.bnyro.recorder.ui.common + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import com.bnyro.recorder.R + +@Composable +fun BlobIconBox(@DrawableRes icon: Int) { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .fillMaxWidth() + .height(350.dp) + .alpha(0.3f) + ) { + Image( + modifier = Modifier.size(350.dp), + painter = painterResource(id = R.drawable.blob), + contentDescription = null, + colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.tertiaryContainer) + ) + Image( + modifier = Modifier.size(250.dp), + painter = painterResource(id = icon), + contentDescription = null, + colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onTertiaryContainer) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt b/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt index 44dc7f62..80b898ec 100644 --- a/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt +++ b/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt @@ -9,6 +9,9 @@ import android.text.format.DateUtils import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.animation.Crossfade +import androidx.compose.foundation.background +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -16,15 +19,17 @@ 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.CircleShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Mic import androidx.compose.material.icons.filled.Pause import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material.icons.filled.Stop -import androidx.compose.material.icons.filled.Videocam -import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ElevatedCard 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 @@ -32,14 +37,19 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect 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.LocalConfiguration import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.bnyro.recorder.R import com.bnyro.recorder.enums.Recorder import com.bnyro.recorder.enums.RecorderState +import com.bnyro.recorder.ui.common.BlobIconBox import com.bnyro.recorder.ui.common.ClickableIcon import com.bnyro.recorder.ui.components.AudioVisualizer import com.bnyro.recorder.ui.models.RecorderModel @@ -101,18 +111,8 @@ fun RecorderView( modifier = Modifier.weight(1f), targetState = recorderModel.recordedAmplitudes ) { - when (it.isEmpty()) { - true -> Text( - modifier = Modifier - .padding( - top = if (orientation == Configuration.ORIENTATION_LANDSCAPE) 50.dp else 200.dp - ), - text = stringResource( - if (recordScreenMode) R.string.record_screen else R.string.record_sound - ), - fontSize = MaterialTheme.typography.headlineLarge.fontSize, - fontWeight = MaterialTheme.typography.headlineLarge.fontWeight - ) + when (it.isEmpty() && orientation == Configuration.ORIENTATION_PORTRAIT) { + true -> BlobIconBox(icon = if (recordScreenMode) R.drawable.ic_screen_record else R.drawable.ic_mic) false -> AudioVisualizer( modifier = Modifier .fillMaxSize() @@ -130,7 +130,7 @@ fun RecorderView( recorderModel.recordedTime?.let { Text( text = DateUtils.formatElapsedTime(it), - fontSize = MaterialTheme.typography.titleMedium.fontSize + style = MaterialTheme.typography.displayLarge ) Spacer(modifier = Modifier.height(20.dp)) } @@ -139,51 +139,81 @@ fun RecorderView( verticalAlignment = Alignment.CenterVertically ) { - FloatingActionButton( - onClick = { - when { - recorderModel.recorderState != RecorderState.IDLE -> recorderModel.stopRecording() - recordScreenMode -> requestScreenRecording() - else -> recorderModel.startAudioRecorder(context) - } - } + ElevatedCard( + colors = CardDefaults.elevatedCardColors( + containerColor = if (isSystemInDarkTheme()) Color(0xffee665b) else Color( + 0xffdd6f62 + ), + contentColor = Color.White + ), shape = CircleShape ) { - Icon( - imageVector = when { - recorderModel.recorderState != RecorderState.IDLE -> Icons.Default.Stop - recordScreenMode -> Icons.Default.Videocam - else -> Icons.Default.Mic + val buttonDescription = stringResource( + if (recorderModel.recorderState != RecorderState.IDLE) { + R.string.stop + } else { + R.string.record + } + ) + IconButton( + onClick = { + when { + recorderModel.recorderState != RecorderState.IDLE -> recorderModel.stopRecording() + recordScreenMode -> requestScreenRecording() + else -> recorderModel.startAudioRecorder(context) + } }, - contentDescription = stringResource( - if (recorderModel.recorderState != RecorderState.IDLE) { - R.string.stop - } else { - R.string.record + modifier = Modifier + .padding(16.dp) + .semantics { contentDescription = buttonDescription } + ) { + when { + recorderModel.recorderState != RecorderState.IDLE -> { + Icon( + Icons.Default.Stop, + modifier = Modifier.size(36.dp), + contentDescription = stringResource(R.string.pause) + ) } - ) - ) + + else -> { + Box( + Modifier + .size(36.dp) + .clip(CircleShape) + .background(Color.White) + ) + } + } + } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && recorderModel.recorderState != RecorderState.IDLE) { Spacer(modifier = Modifier.width(20.dp)) - ClickableIcon( - imageVector = if (recorderModel.recorderState == RecorderState.PAUSED) { - Icons.Default.PlayArrow - } else { - Icons.Default.Pause - }, - contentDescription = stringResource( + ElevatedCard( + colors = CardDefaults.elevatedCardColors( + containerColor = MaterialTheme.colorScheme.secondaryContainer, + contentColor = MaterialTheme.colorScheme.onSecondaryContainer + ) + ) { + ClickableIcon( + imageVector = if (recorderModel.recorderState == RecorderState.PAUSED) { + Icons.Default.PlayArrow + } else { + Icons.Default.Pause + }, + contentDescription = stringResource( + if (recorderModel.recorderState == RecorderState.PAUSED) { + R.string.resume + } else { + R.string.pause + } + ) + ) { if (recorderModel.recorderState == RecorderState.PAUSED) { - R.string.resume + recorderModel.resumeRecording() } else { - R.string.pause + recorderModel.pauseRecording() } - ) - ) { - if (recorderModel.recorderState == RecorderState.PAUSED) { - recorderModel.resumeRecording() - } else { - recorderModel.pauseRecording() } } } diff --git a/app/src/main/res/drawable/blob.xml b/app/src/main/res/drawable/blob.xml new file mode 100755 index 00000000..8f68a767 --- /dev/null +++ b/app/src/main/res/drawable/blob.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/drawable/ic_mic.xml b/app/src/main/res/drawable/ic_mic.xml new file mode 100644 index 00000000..696c9c87 --- /dev/null +++ b/app/src/main/res/drawable/ic_mic.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_screen_record.xml b/app/src/main/res/drawable/ic_screen_record.xml new file mode 100644 index 00000000..9e1db3e6 --- /dev/null +++ b/app/src/main/res/drawable/ic_screen_record.xml @@ -0,0 +1,24 @@ + + + + + + From 10ec51088595f34581827bab1e5270b9ea917920 Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Fri, 14 Jul 2023 20:46:51 +0530 Subject: [PATCH 6/6] Code cleanup, use dynamic colors for amoled theme --- .../com/bnyro/recorder/enums/ThemeMode.kt | 4 +- .../java/com/bnyro/recorder/ui/Destination.kt | 7 ++ .../com/bnyro/recorder/ui/Destinations.kt | 17 ----- .../com/bnyro/recorder/ui/MainActivity.kt | 17 ++--- .../java/com/bnyro/recorder/ui/NavHost.kt | 18 +++-- .../bnyro/recorder/ui/common/BlobIconBox.kt | 10 ++- .../bnyro/recorder/ui/models/ThemeModel.kt | 2 +- .../bnyro/recorder/ui/screens/HomeScreen.kt | 68 +++++++++---------- .../bnyro/recorder/ui/screens/PlayerScreen.kt | 6 +- .../recorder/ui/screens/RecorderScreen.kt | 20 +++--- .../recorder/ui/screens/SettingsScreen.kt | 65 ++++++++++-------- .../java/com/bnyro/recorder/ui/theme/Theme.kt | 27 ++++++-- app/src/main/res/values/strings.xml | 1 + 13 files changed, 140 insertions(+), 122 deletions(-) create mode 100755 app/src/main/java/com/bnyro/recorder/ui/Destination.kt delete mode 100755 app/src/main/java/com/bnyro/recorder/ui/Destinations.kt diff --git a/app/src/main/java/com/bnyro/recorder/enums/ThemeMode.kt b/app/src/main/java/com/bnyro/recorder/enums/ThemeMode.kt index 124679d2..89cb0238 100644 --- a/app/src/main/java/com/bnyro/recorder/enums/ThemeMode.kt +++ b/app/src/main/java/com/bnyro/recorder/enums/ThemeMode.kt @@ -5,7 +5,9 @@ import com.bnyro.recorder.util.Preferences enum class ThemeMode { SYSTEM, LIGHT, - DARK; + DARK, + AMOLED, + ; companion object { fun getCurrent() = valueOf(Preferences.getString(Preferences.themeModeKey, SYSTEM.name)) diff --git a/app/src/main/java/com/bnyro/recorder/ui/Destination.kt b/app/src/main/java/com/bnyro/recorder/ui/Destination.kt new file mode 100755 index 00000000..d4408063 --- /dev/null +++ b/app/src/main/java/com/bnyro/recorder/ui/Destination.kt @@ -0,0 +1,7 @@ +package com.bnyro.recorder.ui + +sealed class Destination(val route: String) { + object Home : Destination("home") + object Settings : Destination("settings") + object RecordingPlayer : Destination("player") +} diff --git a/app/src/main/java/com/bnyro/recorder/ui/Destinations.kt b/app/src/main/java/com/bnyro/recorder/ui/Destinations.kt deleted file mode 100755 index 9bb8a308..00000000 --- a/app/src/main/java/com/bnyro/recorder/ui/Destinations.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.bnyro.recorder.ui - -interface Destination { - val route: String -} - -object Home : Destination { - override val route = "home" -} - -object Settings : Destination { - override val route = "settings" -} - -object RecordingPlayer : Destination { - override val route = "player" -} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/recorder/ui/MainActivity.kt b/app/src/main/java/com/bnyro/recorder/ui/MainActivity.kt index 3fd10127..7b641ed5 100644 --- a/app/src/main/java/com/bnyro/recorder/ui/MainActivity.kt +++ b/app/src/main/java/com/bnyro/recorder/ui/MainActivity.kt @@ -3,13 +3,12 @@ package com.bnyro.recorder.ui import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.viewModels import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.ui.Modifier -import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.get import androidx.navigation.compose.rememberNavController import com.bnyro.recorder.enums.RecorderType import com.bnyro.recorder.enums.ThemeMode @@ -20,7 +19,7 @@ class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val themeModel: ThemeModel = ViewModelProvider(this).get() + val themeModel: ThemeModel by viewModels() val initialRecorder = when (intent?.getStringExtra("action")) { "audio" -> RecorderType.AUDIO @@ -30,21 +29,23 @@ class MainActivity : ComponentActivity() { setContent { RecordYouTheme( - when (val mode = themeModel.themeMode) { + when (themeModel.themeMode) { ThemeMode.SYSTEM -> isSystemInDarkTheme() - else -> mode == ThemeMode.DARK - } + ThemeMode.DARK -> true + else -> false + }, + amoledDark = themeModel.themeMode == ThemeMode.AMOLED, ) { val navController = rememberNavController() Surface( modifier = Modifier .fillMaxSize(), - color = MaterialTheme.colorScheme.background + color = MaterialTheme.colorScheme.background, ) { AppNavHost( navController = navController, modifier = Modifier, - initialRecorder = initialRecorder + initialRecorder = initialRecorder, ) } } diff --git a/app/src/main/java/com/bnyro/recorder/ui/NavHost.kt b/app/src/main/java/com/bnyro/recorder/ui/NavHost.kt index 2d5b5684..d64328f2 100755 --- a/app/src/main/java/com/bnyro/recorder/ui/NavHost.kt +++ b/app/src/main/java/com/bnyro/recorder/ui/NavHost.kt @@ -5,29 +5,33 @@ import androidx.compose.ui.Modifier import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable -import com.bnyro.recorder.enums.Recorder +import com.bnyro.recorder.enums.RecorderType import com.bnyro.recorder.ui.screens.HomeScreen import com.bnyro.recorder.ui.screens.PlayerScreen import com.bnyro.recorder.ui.screens.SettingsScreen @Composable fun AppNavHost( - navController: NavHostController, modifier: Modifier = Modifier, initialRecorder: Recorder + navController: NavHostController, + modifier: Modifier = Modifier, + initialRecorder: RecorderType, ) { NavHost( - navController = navController, startDestination = Home.route, modifier = modifier + navController = navController, + startDestination = Destination.Home.route, + modifier = modifier, ) { - composable(route = Home.route) { + composable(route = Destination.Home.route) { HomeScreen(initialRecorder, onNavigate = { destination -> navController.navigateTo(destination.route) }) } - composable(route = Settings.route) { + composable(route = Destination.Settings.route) { SettingsScreen() } - composable(route = RecordingPlayer.route) { + composable(route = Destination.RecordingPlayer.route) { PlayerScreen(showVideoModeInitially = false) } } @@ -36,4 +40,4 @@ fun AppNavHost( fun NavHostController.navigateTo(route: String) = this.navigate(route) { launchSingleTop = true restoreState = true -} \ No newline at end of file +} diff --git a/app/src/main/java/com/bnyro/recorder/ui/common/BlobIconBox.kt b/app/src/main/java/com/bnyro/recorder/ui/common/BlobIconBox.kt index f7c04226..4c08d548 100644 --- a/app/src/main/java/com/bnyro/recorder/ui/common/BlobIconBox.kt +++ b/app/src/main/java/com/bnyro/recorder/ui/common/BlobIconBox.kt @@ -3,8 +3,7 @@ package com.bnyro.recorder.ui.common import androidx.annotation.DrawableRes import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.size import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable @@ -21,21 +20,20 @@ fun BlobIconBox(@DrawableRes icon: Int) { Box( contentAlignment = Alignment.Center, modifier = Modifier - .fillMaxWidth() - .height(350.dp) + .fillMaxSize() .alpha(0.3f) ) { Image( modifier = Modifier.size(350.dp), painter = painterResource(id = R.drawable.blob), contentDescription = null, - colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.tertiaryContainer) + colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.secondaryContainer) ) Image( modifier = Modifier.size(250.dp), painter = painterResource(id = icon), contentDescription = null, - colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onTertiaryContainer) + colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSecondaryContainer) ) } } \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/recorder/ui/models/ThemeModel.kt b/app/src/main/java/com/bnyro/recorder/ui/models/ThemeModel.kt index 2974ee43..ba222103 100644 --- a/app/src/main/java/com/bnyro/recorder/ui/models/ThemeModel.kt +++ b/app/src/main/java/com/bnyro/recorder/ui/models/ThemeModel.kt @@ -8,6 +8,6 @@ import com.bnyro.recorder.enums.ThemeMode class ThemeModel : ViewModel() { var themeMode by mutableStateOf( - ThemeMode.getCurrent() + ThemeMode.getCurrent(), ) } diff --git a/app/src/main/java/com/bnyro/recorder/ui/screens/HomeScreen.kt b/app/src/main/java/com/bnyro/recorder/ui/screens/HomeScreen.kt index c8fed4ed..c675b566 100644 --- a/app/src/main/java/com/bnyro/recorder/ui/screens/HomeScreen.kt +++ b/app/src/main/java/com/bnyro/recorder/ui/screens/HomeScreen.kt @@ -25,11 +25,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.lifecycle.viewmodel.compose.viewModel import com.bnyro.recorder.R -import com.bnyro.recorder.enums.Recorder import com.bnyro.recorder.enums.RecorderState +import com.bnyro.recorder.enums.RecorderType import com.bnyro.recorder.ui.Destination -import com.bnyro.recorder.ui.RecordingPlayer -import com.bnyro.recorder.ui.Settings import com.bnyro.recorder.ui.common.ClickableIcon import com.bnyro.recorder.ui.models.RecorderModel import kotlinx.coroutines.launch @@ -37,9 +35,9 @@ import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) @Composable fun HomeScreen( - initialRecorder: Recorder, + initialRecorder: RecorderType, onNavigate: (Destination) -> Unit, - recorderModel: RecorderModel = viewModel() + recorderModel: RecorderModel = viewModel(), ) { val pagerState = rememberPagerState { 2 } val scope = rememberCoroutineScope() @@ -47,56 +45,55 @@ fun HomeScreen( TopAppBar(title = { Text(stringResource(R.string.app_name)) }, actions = { ClickableIcon( imageVector = Icons.Default.Settings, - contentDescription = stringResource(R.string.settings) + contentDescription = stringResource(R.string.settings), ) { - onNavigate(Settings) + onNavigate(Destination.Settings) } ClickableIcon( imageVector = Icons.Default.VideoLibrary, - contentDescription = stringResource(R.string.recordings) + contentDescription = stringResource(R.string.recordings), ) { - onNavigate(RecordingPlayer) + onNavigate(Destination.RecordingPlayer) } }) }, bottomBar = { Column { AnimatedVisibility(recorderModel.recorderState == RecorderState.IDLE) { NavigationBar { - NavigationBarItem(icon = { - Icon( - imageVector = Icons.Default.Mic, - contentDescription = stringResource( - id = R.string.record_sound + NavigationBarItem( + icon = { + Icon( + imageVector = Icons.Default.Mic, + contentDescription = stringResource( + id = R.string.record_sound, + ), ) - ) - }, + }, label = { Text(stringResource(R.string.record_sound)) }, selected = (pagerState.currentPage == 0), onClick = { scope.launch { - pagerState.animateScrollToPage( - 0 - ) + pagerState.animateScrollToPage(0) } - }) - NavigationBarItem(icon = { - Icon( - imageVector = Icons.Default.Videocam, - contentDescription = stringResource( - id = R.string.record_screen + }, + ) + NavigationBarItem( + icon = { + Icon( + imageVector = Icons.Default.Videocam, + contentDescription = stringResource( + id = R.string.record_screen, + ), ) - ) - }, + }, label = { Text(stringResource(R.string.record_screen)) }, selected = (pagerState.currentPage == 1), onClick = { scope.launch { - pagerState.animateScrollToPage( - 1 - ) + pagerState.animateScrollToPage(1) } - }) - + }, + ) } } } @@ -104,13 +101,14 @@ fun HomeScreen( Column( Modifier .fillMaxSize() - .padding(paddingValues) + .padding(paddingValues), ) { HorizontalPager( - state = pagerState, modifier = Modifier.fillMaxSize() + state = pagerState, + modifier = Modifier.fillMaxSize(), ) { index -> RecorderView(initialRecorder = initialRecorder, recordScreenMode = (index == 1)) } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/bnyro/recorder/ui/screens/PlayerScreen.kt b/app/src/main/java/com/bnyro/recorder/ui/screens/PlayerScreen.kt index 8d8a15de..f537ba97 100644 --- a/app/src/main/java/com/bnyro/recorder/ui/screens/PlayerScreen.kt +++ b/app/src/main/java/com/bnyro/recorder/ui/screens/PlayerScreen.kt @@ -14,18 +14,20 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.LargeTopAppBar import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.viewmodel.compose.viewModel import com.bnyro.recorder.R import com.bnyro.recorder.enums.SortOrder +import com.bnyro.recorder.obj.RecordingItemData import com.bnyro.recorder.ui.common.ClickableIcon -import com.bnyro.recorder.ui.common.FullscreenDialog import com.bnyro.recorder.ui.components.PlayerView import com.bnyro.recorder.ui.models.PlayerModel diff --git a/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt b/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt index 34ca23f5..0f4a221f 100644 --- a/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt +++ b/app/src/main/java/com/bnyro/recorder/ui/screens/RecorderScreen.kt @@ -8,7 +8,6 @@ import android.os.Build import android.text.format.DateUtils import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts -import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.Crossfade import androidx.compose.foundation.background import androidx.compose.foundation.isSystemInDarkTheme @@ -24,12 +23,8 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ExpandLess -import androidx.compose.material.icons.filled.ExpandMore -import androidx.compose.material.icons.filled.Mic import androidx.compose.material.icons.filled.Pause import androidx.compose.material.icons.filled.PlayArrow -import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.Stop import androidx.compose.material3.CardDefaults import androidx.compose.material3.ElevatedCard @@ -52,12 +47,11 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.bnyro.recorder.R -import com.bnyro.recorder.enums.RecorderType import com.bnyro.recorder.enums.RecorderState +import com.bnyro.recorder.enums.RecorderType import com.bnyro.recorder.ui.common.BlobIconBox import com.bnyro.recorder.ui.common.ClickableIcon import com.bnyro.recorder.ui.components.AudioVisualizer -import com.bnyro.recorder.ui.components.SettingsBottomSheet import com.bnyro.recorder.ui.models.RecorderModel @Composable @@ -147,7 +141,7 @@ fun RecorderView( ElevatedCard( colors = CardDefaults.elevatedCardColors( - containerColor = if (isSystemInDarkTheme()) Color(0xffee665b) else Color( + containerColor = if (isSystemInDarkTheme()) Color(0xA8EE665B) else Color( 0xffdd6f62 ), contentColor = Color.White @@ -184,9 +178,15 @@ fun RecorderView( else -> { Box( Modifier - .size(36.dp) + .size(40.dp) + .clip(CircleShape) + .background(Color(0x9FFFFFFF)) + ) + Box( + Modifier + .size(26.dp) .clip(CircleShape) - .background(Color.White) + .background(Color(0x9FFFFFFF)) ) } } diff --git a/app/src/main/java/com/bnyro/recorder/ui/screens/SettingsScreen.kt b/app/src/main/java/com/bnyro/recorder/ui/screens/SettingsScreen.kt index faca46ba..7e27cb79 100644 --- a/app/src/main/java/com/bnyro/recorder/ui/screens/SettingsScreen.kt +++ b/app/src/main/java/com/bnyro/recorder/ui/screens/SettingsScreen.kt @@ -2,6 +2,7 @@ package com.bnyro.recorder.ui.screens import android.net.Uri import android.os.Build +import androidx.activity.ComponentActivity import androidx.activity.compose.rememberLauncherForActivityResult import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -24,6 +25,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp @@ -50,16 +52,15 @@ import com.bnyro.recorder.util.Preferences @OptIn(ExperimentalMaterial3Api::class) @Composable fun SettingsScreen() { - val themeModel: ThemeModel = viewModel() - + val themeModel: ThemeModel = viewModel(LocalContext.current as ComponentActivity) var audioFormat by remember { mutableStateOf(AudioFormat.getCurrent()) } var audioChannels by remember { mutableStateOf( AudioChannels.fromInt( - Preferences.prefs.getInt(Preferences.audioChannelsKey, AudioChannels.MONO.value) - ) + Preferences.prefs.getInt(Preferences.audioChannelsKey, AudioChannels.MONO.value), + ), ) } var audioDeviceSource by remember { @@ -67,14 +68,14 @@ fun SettingsScreen() { AudioDeviceSource.fromInt( Preferences.prefs.getInt( Preferences.audioDeviceSourceKey, - AudioDeviceSource.DEFAULT.value - ) - ) + AudioDeviceSource.DEFAULT.value, + ), + ), ) } var screenAudioSource by remember { mutableStateOf( - AudioSource.fromInt(Preferences.prefs.getInt(Preferences.audioSourceKey, 0)) + AudioSource.fromInt(Preferences.prefs.getInt(Preferences.audioSourceKey, 0)), ) } var videoEncoder by remember { @@ -98,27 +99,28 @@ fun SettingsScreen() { actions = { ClickableIcon( imageVector = Icons.Default.DarkMode, - contentDescription = stringResource(R.string.theme) + contentDescription = stringResource(R.string.theme), ) { showThemePref = true } ClickableIcon( imageVector = Icons.Default.Info, - contentDescription = stringResource(R.string.about) + contentDescription = stringResource(R.string.about), ) { showAbout = true } - }) + }, + ) }) { paddingValues -> Column( modifier = Modifier .padding(paddingValues) - .padding(horizontal = 16.dp) + .padding(horizontal = 16.dp), ) { Text( text = stringResource(R.string.directory), fontWeight = FontWeight.Bold, - fontSize = 18.sp + fontSize = 18.sp, ) Spacer(modifier = Modifier.height(5.dp)) Button( @@ -126,7 +128,7 @@ fun SettingsScreen() { val lastDir = Preferences.prefs.getString(Preferences.targetFolderKey, "") .takeIf { !it.isNullOrBlank() } directoryPicker.launch(lastDir?.let { Uri.parse(it) }) - } + }, ) { Text(stringResource(R.string.choose_dir)) } @@ -135,7 +137,7 @@ fun SettingsScreen() { title = stringResource(R.string.audio_format), entries = AudioFormat.formats.map { it.name }, values = AudioFormat.formats.map { it.format }, - selections = listOf(audioFormat.format) + selections = listOf(audioFormat.format), ) { index, newValue -> if (newValue) { audioFormat = AudioFormat.formats[index] @@ -146,13 +148,13 @@ fun SettingsScreen() { CustomNumInputPref( key = Preferences.audioSampleRateKey, title = stringResource(R.string.sample_rate), - defValue = 44_100 + defValue = 44_100, ) Spacer(modifier = Modifier.width(10.dp)) CustomNumInputPref( key = Preferences.audioBitrateKey, title = stringResource(R.string.bitrate), - defValue = 192_000 + defValue = 192_000, ) } val audioDeviceSourceValues = AudioDeviceSource.values().map { it.value } @@ -161,16 +163,16 @@ fun SettingsScreen() { R.string.default_audio, R.string.microphone, R.string.camcorder, - R.string.unprocessed + R.string.unprocessed, ).map { stringResource(it) }, values = audioDeviceSourceValues, - selections = listOf(audioDeviceSource.value) + selections = listOf(audioDeviceSource.value), ) { index, newValue -> if (newValue) { audioDeviceSource = AudioDeviceSource.fromInt( - audioDeviceSourceValues[index] + audioDeviceSourceValues[index], ) Preferences.edit { putInt(Preferences.audioDeviceSourceKey, audioDeviceSourceValues[index]) @@ -183,7 +185,7 @@ fun SettingsScreen() { stringResource(it) }, values = audioChannelsValues, - selections = listOf(audioChannels.value) + selections = listOf(audioChannels.value), ) { index, newValue -> if (newValue) { audioChannels = AudioChannels.fromInt(audioChannelsValues[index]) @@ -200,7 +202,7 @@ fun SettingsScreen() { stringResource(it) }, values = audioValues, - selections = listOf(screenAudioSource.value) + selections = listOf(screenAudioSource.value), ) { index, newValue -> if (newValue) { screenAudioSource = AudioSource.fromInt(audioValues[index]) @@ -210,7 +212,7 @@ fun SettingsScreen() { ChipSelector( entries = VideoFormat.codecs.map { it.name }, values = VideoFormat.codecs.map { it.codec }, - selections = listOf(videoEncoder.codec) + selections = listOf(videoEncoder.codec), ) { index, newValue -> if (newValue) { videoEncoder = VideoFormat.codecs[index] @@ -221,14 +223,14 @@ fun SettingsScreen() { CustomNumInputPref( key = Preferences.videoBitrateKey, title = stringResource(R.string.bitrate), - defValue = 1_200_000 + defValue = 1_200_000, ) Spacer(modifier = Modifier.height(10.dp)) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { CheckboxPref( prefKey = Preferences.losslessRecorderKey, title = stringResource(R.string.lossless_audio), - summary = stringResource(R.string.lossless_audio_desc) + summary = stringResource(R.string.lossless_audio_desc), ) } Spacer(modifier = Modifier.height(10.dp)) @@ -236,7 +238,7 @@ fun SettingsScreen() { CheckboxPref( prefKey = Preferences.showOverlayAnnotationToolKey, title = stringResource(R.string.screen_recorder_annotation), - summary = stringResource(R.string.screen_recorder_annotation_desc) + summary = stringResource(R.string.screen_recorder_annotation_desc), ) } Spacer(modifier = Modifier.height(10.dp)) @@ -248,12 +250,17 @@ fun SettingsScreen() { SelectionDialog( onDismissRequest = { showThemePref = false }, title = stringResource(R.string.theme), - entries = listOf(R.string.system, R.string.light, R.string.dark).map { + entries = listOf( + R.string.system, + R.string.light, + R.string.dark, + R.string.amoled_dark, + ).map { stringResource(it) - } + }, ) { themeModel.themeMode = ThemeMode.values()[it] - Preferences.edit { putString(Preferences.themeModeKey, themeModel.themeMode.name) } + Preferences.edit { putString(Preferences.themeModeKey, ThemeMode.values()[it].name) } } } diff --git a/app/src/main/java/com/bnyro/recorder/ui/theme/Theme.kt b/app/src/main/java/com/bnyro/recorder/ui/theme/Theme.kt index 77649fd5..a67899e5 100644 --- a/app/src/main/java/com/bnyro/recorder/ui/theme/Theme.kt +++ b/app/src/main/java/com/bnyro/recorder/ui/theme/Theme.kt @@ -10,6 +10,7 @@ import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable import androidx.compose.runtime.SideEffect +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalView @@ -18,13 +19,13 @@ import androidx.core.view.WindowCompat private val DarkColorScheme = darkColorScheme( primary = Purple80, secondary = PurpleGrey80, - tertiary = Pink80 + tertiary = Pink80, ) private val LightColorScheme = lightColorScheme( primary = Purple40, secondary = PurpleGrey40, - tertiary = Pink40 + tertiary = Pink40, /* Other default colors to override background = Color(0xFFFFFBFE), @@ -37,18 +38,32 @@ private val LightColorScheme = lightColorScheme( */ ) +private val AmoledDarkColorScheme = darkColorScheme( + primary = Color(0xFFEE665B), + background = Color(0xFF000000), + onPrimary = Color(0xFFFFFFFF), +) + @Composable fun RecordYouTheme( darkTheme: Boolean = isSystemInDarkTheme(), // Dynamic color is available on Android 12+ dynamicColor: Boolean = true, - content: @Composable () -> Unit + amoledDark: Boolean = false, + content: @Composable () -> Unit, ) { val colorScheme = when { + amoledDark && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + dynamicDarkColorScheme(context).copy(background = Color.Black) + } + + amoledDark -> AmoledDarkColorScheme dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { val context = LocalContext.current if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) } + darkTheme -> DarkColorScheme else -> LightColorScheme } @@ -61,11 +76,11 @@ fun RecordYouTheme( activity.window.statusBarColor = colorScheme.background.toArgb() WindowCompat.getInsetsController( activity.window, - view + view, ).isAppearanceLightStatusBars = !darkTheme WindowCompat.getInsetsController( activity.window, - view + view, ).isAppearanceLightNavigationBars = !darkTheme } } @@ -74,6 +89,6 @@ fun RecordYouTheme( MaterialTheme( colorScheme = colorScheme, typography = Typography, - content = content + content = content, ) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5675c40d..24d6cdd2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -66,4 +66,5 @@ Light Screen Record Annotation Show annotation tool during screen recording + Amoled Dark \ No newline at end of file