Split compose screens into multiple smaller sections

This commit is contained in:
Ensar Sarajčić 2023-10-03 09:10:32 +02:00
parent c644def18e
commit 3f901ff290
5 changed files with 281 additions and 206 deletions

View File

@ -22,6 +22,7 @@ import com.simplemobiletools.flashlight.extensions.config
import com.simplemobiletools.flashlight.helpers.SleepTimer import com.simplemobiletools.flashlight.helpers.SleepTimer
import com.simplemobiletools.flashlight.helpers.stopSleepTimerCountDown import com.simplemobiletools.flashlight.helpers.stopSleepTimerCountDown
import com.simplemobiletools.flashlight.screens.BrightDisplayScreen import com.simplemobiletools.flashlight.screens.BrightDisplayScreen
import com.simplemobiletools.flashlight.views.AnimatedSleepTimer
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
@ -63,10 +64,8 @@ class BrightDisplayActivity : ComponentActivity() {
} }
} }
}, },
timerVisible = timerVisible, sleepTimer = {
timerText = timerText, AnimatedSleepTimer(timerText = timerText, timerVisible = timerVisible, onTimerClosePress = ::stopSleepTimer)
onTimerClosePress = {
stopSleepTimer()
} }
) )
} }

View File

@ -40,7 +40,8 @@ import com.simplemobiletools.flashlight.dialogs.SleepTimerCustomDialog
import com.simplemobiletools.flashlight.extensions.config import com.simplemobiletools.flashlight.extensions.config
import com.simplemobiletools.flashlight.extensions.startAboutActivity import com.simplemobiletools.flashlight.extensions.startAboutActivity
import com.simplemobiletools.flashlight.helpers.* import com.simplemobiletools.flashlight.helpers.*
import com.simplemobiletools.flashlight.screens.MainScreen import com.simplemobiletools.flashlight.screens.*
import com.simplemobiletools.flashlight.views.AnimatedSleepTimer
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import java.util.* import java.util.*
import kotlin.system.exitProcess import kotlin.system.exitProcess
@ -61,25 +62,6 @@ class MainActivity : ComponentActivity() {
setContent { setContent {
AppThemeSurface { AppThemeSurface {
val showMoreApps = onEventValue { !resources.getBoolean(R.bool.hide_google_relations) } val showMoreApps = onEventValue { !resources.getBoolean(R.bool.hide_google_relations) }
val timerVisible by viewModel.timerVisible.collectAsStateWithLifecycle()
val timerText by viewModel.timerText.collectAsStateWithLifecycle()
val flashlightActive by viewModel.flashlightOn.collectAsStateWithLifecycle()
val showBrightDisplayButton by preferences.brightDisplayFlow.collectAsStateWithLifecycle(
config.brightDisplay,
minActiveState = Lifecycle.State.CREATED
)
val showSosButton by preferences.sosFlow.collectAsStateWithLifecycle(config.sos, minActiveState = Lifecycle.State.CREATED)
val sosActive by viewModel.sosActive.collectAsStateWithLifecycle()
val showStroboscopeButton by preferences.stroboscopeFlow.collectAsStateWithLifecycle(
config.stroboscope,
minActiveState = Lifecycle.State.CREATED
)
val stroboscopeActive by viewModel.stroboscopeActive.collectAsStateWithLifecycle()
val brightnessBarVisible by viewModel.brightnessBarVisible.collectAsStateWithLifecycle(false)
val brightnessBarValue by viewModel.brightnessBarValue.collectAsStateWithLifecycle()
val stroboscopeBarVisible by viewModel.stroboscopeBarVisible.collectAsStateWithLifecycle(false)
val stroboscopeBarValue by viewModel.stroboscopeBarValue.collectAsStateWithLifecycle()
val sosPermissionLauncher = rememberLauncherForActivityResult( val sosPermissionLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestPermission(), contract = ActivityResultContracts.RequestPermission(),
onResult = { onResult = {
@ -102,34 +84,84 @@ class MainActivity : ComponentActivity() {
) )
MainScreen( MainScreen(
timerText = timerText, flashlightButton = {
timerVisible = timerVisible, val flashlightActive by viewModel.flashlightOn.collectAsStateWithLifecycle()
onTimerClosePress = { stopSleepTimer() },
onFlashlightPress = { viewModel.toggleFlashlight() }, FlashlightButton(
flashlightActive = flashlightActive, onFlashlightPress = { viewModel.toggleFlashlight() },
showBrightDisplayButton = showBrightDisplayButton, flashlightActive = flashlightActive,
onBrightDisplayPress = { )
startActivity(Intent(applicationContext, BrightDisplayActivity::class.java))
}, },
showSosButton = showSosButton, brightDisplayButton = {
sosActive = sosActive, val showBrightDisplayButton by preferences.brightDisplayFlow.collectAsStateWithLifecycle(
onSosButtonPress = { config.brightDisplay,
toggleStroboscope(true, sosPermissionLauncher) minActiveState = Lifecycle.State.CREATED
)
if (showBrightDisplayButton) {
BrightDisplayButton(
onBrightDisplayPress = {
startActivity(Intent(applicationContext, BrightDisplayActivity::class.java))
}
)
}
}, },
showStroboscopeButton = showStroboscopeButton, sosButton = {
stroboscopeActive = stroboscopeActive, val showSosButton by preferences.sosFlow.collectAsStateWithLifecycle(config.sos, minActiveState = Lifecycle.State.CREATED)
onStroboscopeButtonPress = { val sosActive by viewModel.sosActive.collectAsStateWithLifecycle()
toggleStroboscope(false, stroboscopePermissionLauncher)
if (showSosButton) {
SosButton(
sosActive = sosActive,
onSosButtonPress = {
toggleStroboscope(true, sosPermissionLauncher)
},
)
}
}, },
showBrightnessBar = brightnessBarVisible, stroboscopeButton = {
brightnessBarValue = brightnessBarValue, val showStroboscopeButton by preferences.stroboscopeFlow.collectAsStateWithLifecycle(
onBrightnessBarValueChange = { config.stroboscope,
viewModel.updateBrightnessBarValue(it) minActiveState = Lifecycle.State.CREATED
)
val stroboscopeActive by viewModel.stroboscopeActive.collectAsStateWithLifecycle()
if (showStroboscopeButton) {
StroboscopeButton(
stroboscopeActive = stroboscopeActive,
onStroboscopeButtonPress = {
toggleStroboscope(false, stroboscopePermissionLauncher)
},
)
}
}, },
showStroboscopeBar = stroboscopeBarVisible, slidersSection = {
stroboscopeBarValue = stroboscopeBarValue, val brightnessBarVisible by viewModel.brightnessBarVisible.collectAsStateWithLifecycle(false)
onStroboscopeBarValueChange = { val brightnessBarValue by viewModel.brightnessBarValue.collectAsStateWithLifecycle()
viewModel.updateStroboscopeBarValue(it) val stroboscopeBarVisible by viewModel.stroboscopeBarVisible.collectAsStateWithLifecycle(false)
val stroboscopeBarValue by viewModel.stroboscopeBarValue.collectAsStateWithLifecycle()
MainScreenSlidersSection(
showBrightnessBar = brightnessBarVisible,
brightnessBarValue = brightnessBarValue,
onBrightnessBarValueChange = {
viewModel.updateBrightnessBarValue(it)
},
showStroboscopeBar = stroboscopeBarVisible,
stroboscopeBarValue = stroboscopeBarValue,
onStroboscopeBarValueChange = {
viewModel.updateStroboscopeBarValue(it)
},
)
},
sleepTimer = {
val timerVisible by viewModel.timerVisible.collectAsStateWithLifecycle()
val timerText by viewModel.timerText.collectAsStateWithLifecycle()
AnimatedSleepTimer(
timerText = timerText,
timerVisible = timerVisible,
onTimerClosePress = { stopSleepTimer() },
)
}, },
showMoreApps = showMoreApps, showMoreApps = showMoreApps,
openSettings = ::launchSettings, openSettings = ::launchSettings,

View File

@ -1,8 +1,5 @@
package com.simplemobiletools.flashlight.screens package com.simplemobiletools.flashlight.screens
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@ -23,16 +20,14 @@ import com.simplemobiletools.commons.compose.extensions.MyDevices
import com.simplemobiletools.commons.compose.theme.AppThemeSurface import com.simplemobiletools.commons.compose.theme.AppThemeSurface
import com.simplemobiletools.commons.extensions.getContrastColor import com.simplemobiletools.commons.extensions.getContrastColor
import com.simplemobiletools.flashlight.R import com.simplemobiletools.flashlight.R
import com.simplemobiletools.flashlight.views.SleepTimer import com.simplemobiletools.flashlight.views.AnimatedSleepTimer
@Composable @Composable
internal fun BrightDisplayScreen( internal fun BrightDisplayScreen(
backgroundColor: Int, backgroundColor: Int,
contrastColor: Int, contrastColor: Int,
timerText: String,
timerVisible: Boolean,
onChangeColorPress: () -> Unit, onChangeColorPress: () -> Unit,
onTimerClosePress: () -> Unit sleepTimer: @Composable () -> Unit
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
@ -56,18 +51,12 @@ internal fun BrightDisplayScreen(
) )
} }
AnimatedVisibility( Box(
modifier = Modifier modifier = Modifier
.align(Alignment.BottomEnd) .align(Alignment.BottomEnd)
.navigationBarsPadding(), .navigationBarsPadding()
visible = timerVisible && timerText.isNotEmpty(),
enter = fadeIn(),
exit = fadeOut()
) { ) {
SleepTimer( sleepTimer()
timerText = timerText,
onCloseClick = onTimerClosePress
)
} }
} }
} }
@ -79,10 +68,10 @@ private fun BrightDisplayScreenPreview() {
BrightDisplayScreen( BrightDisplayScreen(
backgroundColor = MaterialTheme.colorScheme.background.toArgb(), backgroundColor = MaterialTheme.colorScheme.background.toArgb(),
contrastColor = MaterialTheme.colorScheme.background.toArgb().getContrastColor(), contrastColor = MaterialTheme.colorScheme.background.toArgb().getContrastColor(),
timerText = "00:00", sleepTimer = {
timerVisible = true, AnimatedSleepTimer(timerText = "00:00", timerVisible = true, onTimerClosePress = {})
},
onChangeColorPress = {}, onChangeColorPress = {},
onTimerClosePress = {}
) )
} }
} }

View File

@ -1,8 +1,5 @@
package com.simplemobiletools.flashlight.screens package com.simplemobiletools.flashlight.screens
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
@ -32,31 +29,18 @@ import com.simplemobiletools.commons.compose.settings.scaffold.topAppBarInsets
import com.simplemobiletools.commons.compose.settings.scaffold.topAppBarPaddings import com.simplemobiletools.commons.compose.settings.scaffold.topAppBarPaddings
import com.simplemobiletools.commons.compose.theme.AppThemeSurface import com.simplemobiletools.commons.compose.theme.AppThemeSurface
import com.simplemobiletools.flashlight.R import com.simplemobiletools.flashlight.R
import com.simplemobiletools.flashlight.views.SleepTimer import com.simplemobiletools.flashlight.views.AnimatedSleepTimer
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
@Composable @Composable
internal fun MainScreen( internal fun MainScreen(
timerText: String, flashlightButton: @Composable () -> Unit,
timerVisible: Boolean, brightDisplayButton: @Composable () -> Unit,
onTimerClosePress: () -> Unit, sosButton: @Composable () -> Unit,
flashlightActive: Boolean, stroboscopeButton: @Composable () -> Unit,
onFlashlightPress: () -> Unit, slidersSection: @Composable () -> Unit,
showBrightDisplayButton: Boolean, sleepTimer: @Composable () -> Unit,
onBrightDisplayPress: () -> Unit,
showSosButton: Boolean,
sosActive: Boolean,
onSosButtonPress: () -> Unit,
showStroboscopeButton: Boolean,
stroboscopeActive: Boolean,
onStroboscopeButtonPress: () -> Unit,
showBrightnessBar: Boolean,
brightnessBarValue: Float,
onBrightnessBarValueChange: (Float) -> Unit,
showStroboscopeBar: Boolean,
stroboscopeBarValue: Float,
onStroboscopeBarValueChange: (Float) -> Unit,
showMoreApps: Boolean, showMoreApps: Boolean,
openSettings: () -> Unit, openSettings: () -> Unit,
openAbout: () -> Unit, openAbout: () -> Unit,
@ -90,111 +74,138 @@ internal fun MainScreen(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceEvenly verticalArrangement = Arrangement.SpaceEvenly
) { ) {
Icon( flashlightButton()
modifier = Modifier brightDisplayButton()
.size(dimensionResource(id = R.dimen.main_button_size)) sosButton()
.padding(vertical = dimensionResource(id = R.dimen.normal_margin)) stroboscopeButton()
.clickable( slidersSection()
indication = null,
interactionSource = rememberMutableInteractionSource(),
onClick = onFlashlightPress
),
painter = painterResource(id = R.drawable.ic_flashlight_vector),
contentDescription = stringResource(id = R.string.flashlight_short),
tint = if (flashlightActive) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface
)
if (showBrightDisplayButton) {
Icon(
modifier = Modifier
.size(dimensionResource(id = R.dimen.smaller_button_size))
.padding(vertical = dimensionResource(id = R.dimen.normal_margin))
.clickable(
indication = null,
interactionSource = rememberMutableInteractionSource(),
onClick = onBrightDisplayPress
),
painter = painterResource(id = R.drawable.ic_bright_display_vector),
contentDescription = stringResource(id = R.string.bright_display),
tint = MaterialTheme.colorScheme.onSurface
)
}
if (showSosButton) {
Text(
modifier = Modifier
.padding(vertical = dimensionResource(id = R.dimen.normal_margin))
.clickable(
indication = null,
interactionSource = rememberMutableInteractionSource(),
onClick = onSosButtonPress
),
text = stringResource(id = R.string.sos),
fontSize = TextUnit(dimensionResource(id = R.dimen.sos_text_size).value, TextUnitType.Sp),
fontWeight = FontWeight.Bold,
color = if (sosActive) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface
)
}
if (showStroboscopeButton) {
Icon(
modifier = Modifier
.size(dimensionResource(id = R.dimen.smaller_button_size))
.padding(vertical = dimensionResource(id = R.dimen.normal_margin))
.clickable(
indication = null,
interactionSource = rememberMutableInteractionSource(),
onClick = onStroboscopeButtonPress
),
painter = painterResource(id = R.drawable.ic_stroboscope_vector),
contentDescription = "",
tint = if (stroboscopeActive) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface
)
}
val sliderModifier = Modifier
.padding(dimensionResource(id = R.dimen.activity_margin))
.padding(vertical = dimensionResource(R.dimen.medium_margin))
.padding(bottom = dimensionResource(id = R.dimen.activity_margin))
.size(width = dimensionResource(id = R.dimen.seekbar_width), height = dimensionResource(id = R.dimen.seekbar_height))
if (showBrightnessBar) {
Slider(
modifier = sliderModifier,
value = brightnessBarValue,
onValueChange = onBrightnessBarValueChange
)
}
if (showStroboscopeBar) {
Slider(
modifier = sliderModifier,
value = stroboscopeBarValue,
onValueChange = onStroboscopeBarValueChange
)
}
if (!showBrightnessBar && !showStroboscopeBar) {
Spacer(
modifier = sliderModifier,
)
}
} }
AnimatedVisibility( Box(
modifier = Modifier.align(Alignment.BottomEnd), modifier = Modifier.align(Alignment.BottomEnd),
visible = timerVisible && timerText.isNotEmpty(),
enter = fadeIn(),
exit = fadeOut(),
) { ) {
SleepTimer( sleepTimer()
timerText = timerText,
onCloseClick = onTimerClosePress
)
} }
} }
} }
@Composable
internal fun FlashlightButton(
flashlightActive: Boolean,
onFlashlightPress: () -> Unit,
) {
Icon(
modifier = Modifier
.size(dimensionResource(id = R.dimen.main_button_size))
.padding(vertical = dimensionResource(id = R.dimen.normal_margin))
.clickable(
indication = null,
interactionSource = rememberMutableInteractionSource(),
onClick = onFlashlightPress
),
painter = painterResource(id = R.drawable.ic_flashlight_vector),
contentDescription = stringResource(id = R.string.flashlight_short),
tint = if (flashlightActive) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface
)
}
@Composable
internal fun BrightDisplayButton(
onBrightDisplayPress: () -> Unit,
) {
Icon(
modifier = Modifier
.size(dimensionResource(id = R.dimen.smaller_button_size))
.padding(vertical = dimensionResource(id = R.dimen.normal_margin))
.clickable(
indication = null,
interactionSource = rememberMutableInteractionSource(),
onClick = onBrightDisplayPress
),
painter = painterResource(id = R.drawable.ic_bright_display_vector),
contentDescription = stringResource(id = R.string.bright_display),
tint = MaterialTheme.colorScheme.onSurface
)
}
@Composable
internal fun SosButton(
sosActive: Boolean,
onSosButtonPress: () -> Unit,
) {
Text(
modifier = Modifier
.padding(vertical = dimensionResource(id = R.dimen.normal_margin))
.clickable(
indication = null,
interactionSource = rememberMutableInteractionSource(),
onClick = onSosButtonPress
),
text = stringResource(id = R.string.sos),
fontSize = TextUnit(dimensionResource(id = R.dimen.sos_text_size).value, TextUnitType.Sp),
fontWeight = FontWeight.Bold,
color = if (sosActive) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface
)
}
@Composable
internal fun StroboscopeButton(
stroboscopeActive: Boolean,
onStroboscopeButtonPress: () -> Unit,
) {
Icon(
modifier = Modifier
.size(dimensionResource(id = R.dimen.smaller_button_size))
.padding(vertical = dimensionResource(id = R.dimen.normal_margin))
.clickable(
indication = null,
interactionSource = rememberMutableInteractionSource(),
onClick = onStroboscopeButtonPress
),
painter = painterResource(id = R.drawable.ic_stroboscope_vector),
contentDescription = "",
tint = if (stroboscopeActive) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface
)
}
@Composable
internal fun MainScreenSlidersSection(
showBrightnessBar: Boolean,
brightnessBarValue: Float,
onBrightnessBarValueChange: (Float) -> Unit,
showStroboscopeBar: Boolean,
stroboscopeBarValue: Float,
onStroboscopeBarValueChange: (Float) -> Unit,
) {
val sliderModifier = Modifier
.padding(dimensionResource(id = R.dimen.activity_margin))
.padding(vertical = dimensionResource(R.dimen.medium_margin))
.padding(bottom = dimensionResource(id = R.dimen.activity_margin))
.size(width = dimensionResource(id = R.dimen.seekbar_width), height = dimensionResource(id = R.dimen.seekbar_height))
if (showBrightnessBar) {
Slider(
modifier = sliderModifier,
value = brightnessBarValue,
onValueChange = onBrightnessBarValueChange
)
}
if (showStroboscopeBar) {
Slider(
modifier = sliderModifier,
value = stroboscopeBarValue,
onValueChange = onStroboscopeBarValueChange
)
}
if (!showBrightnessBar && !showStroboscopeBar) {
Spacer(
modifier = sliderModifier,
)
}
}
private fun buildActionMenu( private fun buildActionMenu(
showMoreApps: Boolean, showMoreApps: Boolean,
openSettings: () -> Unit, openSettings: () -> Unit,
@ -218,25 +229,46 @@ private fun buildActionMenu(
internal fun MainScreenPreview() { internal fun MainScreenPreview() {
AppThemeSurface { AppThemeSurface {
MainScreen( MainScreen(
timerText = "00:00", flashlightButton = {
timerVisible = true, FlashlightButton(
onTimerClosePress = {}, onFlashlightPress = {},
onFlashlightPress = {}, flashlightActive = true,
flashlightActive = true, )
showBrightDisplayButton = true, },
onBrightDisplayPress = {}, brightDisplayButton = {
showSosButton = true, BrightDisplayButton(
sosActive = false, onBrightDisplayPress = {}
onSosButtonPress = {}, )
showStroboscopeButton = true, },
stroboscopeActive = false, sosButton = {
onStroboscopeButtonPress = {}, SosButton(
showBrightnessBar = false, sosActive = false,
brightnessBarValue = 0f, onSosButtonPress = {},
onBrightnessBarValueChange = {}, )
showStroboscopeBar = false, },
stroboscopeBarValue = 0f, stroboscopeButton = {
onStroboscopeBarValueChange = {}, StroboscopeButton(
stroboscopeActive = false,
onStroboscopeButtonPress = {},
)
},
slidersSection = {
MainScreenSlidersSection(
showBrightnessBar = false,
brightnessBarValue = 0f,
onBrightnessBarValueChange = {},
showStroboscopeBar = false,
stroboscopeBarValue = 0f,
onStroboscopeBarValueChange = {},
)
},
sleepTimer = {
AnimatedSleepTimer(
timerText = "00:00",
timerVisible = true,
onTimerClosePress = {},
)
},
showMoreApps = true, showMoreApps = true,
openSettings = {}, openSettings = {},
openAbout = {}, openAbout = {},

View File

@ -1,5 +1,8 @@
package com.simplemobiletools.flashlight.views package com.simplemobiletools.flashlight.views
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -61,6 +64,26 @@ internal fun SleepTimer(
} }
} }
@Composable
internal fun AnimatedSleepTimer(
modifier: Modifier = Modifier,
timerText: String,
timerVisible: Boolean,
onTimerClosePress: () -> Unit
) {
AnimatedVisibility(
modifier = modifier,
visible = timerVisible && timerText.isNotEmpty(),
enter = fadeIn(),
exit = fadeOut()
) {
SleepTimer(
timerText = timerText,
onCloseClick = onTimerClosePress
)
}
}
@Composable @Composable
@MyDevices @MyDevices
internal fun SleepTimerPreview() { internal fun SleepTimerPreview() {