feat(ui): use toolbar instead of navigation bar for navigation
Closes: #111
This commit is contained in:
@@ -21,36 +21,56 @@ import android.content.Intent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.animation.SharedTransitionLayout
|
||||
import androidx.compose.animation.expandHorizontally
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.shrinkHorizontally
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.calculateEndPadding
|
||||
import androidx.compose.foundation.layout.calculateStartPadding
|
||||
import androidx.compose.foundation.layout.displayCutout
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.FloatingToolbarDefaults
|
||||
import androidx.compose.material3.FloatingToolbarDefaults.ScreenOffset
|
||||
import androidx.compose.material3.FloatingToolbarExitDirection
|
||||
import androidx.compose.material3.HorizontalFloatingToolbar
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||
import androidx.compose.material3.MaterialTheme.motionScheme
|
||||
import androidx.compose.material3.NavigationItemIconPosition
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.ShortNavigationBar
|
||||
import androidx.compose.material3.ShortNavigationBarArrangement
|
||||
import androidx.compose.material3.ShortNavigationBarItem
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.ToggleButton
|
||||
import androidx.compose.material3.ToggleButtonDefaults
|
||||
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
|
||||
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.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation3.runtime.entryProvider
|
||||
@@ -82,8 +102,13 @@ fun AppScreen(
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
val motionScheme = motionScheme
|
||||
val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass
|
||||
val systemBarsInsets = WindowInsets.systemBars.asPaddingValues()
|
||||
val cutoutInsets = WindowInsets.displayCutout.asPaddingValues()
|
||||
|
||||
val backStack = rememberNavBackStack(Screen.Timer)
|
||||
val toolbarScrollBehavior = FloatingToolbarDefaults.exitAlwaysScrollBehavior(
|
||||
FloatingToolbarExitDirection.Bottom
|
||||
)
|
||||
|
||||
if (uiState.alarmRinging)
|
||||
AlarmDialog {
|
||||
@@ -99,46 +124,92 @@ fun AppScreen(
|
||||
bottomBar = {
|
||||
AnimatedVisibility(
|
||||
backStack.last() !is Screen.AOD,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut()
|
||||
enter = slideInVertically(motionScheme.slowSpatialSpec()) { it },
|
||||
exit = slideOutVertically(motionScheme.slowSpatialSpec()) { it }
|
||||
) {
|
||||
val wide = remember {
|
||||
windowSizeClass.isWidthAtLeastBreakpoint(
|
||||
WindowSizeClass.WIDTH_DP_MEDIUM_LOWER_BOUND
|
||||
)
|
||||
}
|
||||
ShortNavigationBar(
|
||||
arrangement =
|
||||
if (wide) ShortNavigationBarArrangement.Centered
|
||||
else ShortNavigationBarArrangement.EqualWeight
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
start = cutoutInsets.calculateStartPadding(layoutDirection),
|
||||
end = cutoutInsets.calculateEndPadding(layoutDirection)
|
||||
),
|
||||
Alignment.Center
|
||||
) {
|
||||
mainScreens.forEach {
|
||||
val selected = backStack.last() == it.route
|
||||
ShortNavigationBarItem(
|
||||
selected = selected,
|
||||
onClick = if (it.route != Screen.Timer) { // Ensure the backstack does not accumulate screens
|
||||
{
|
||||
if (backStack.size < 2) backStack.add(it.route)
|
||||
else backStack[1] = it.route
|
||||
HorizontalFloatingToolbar(
|
||||
expanded = true,
|
||||
scrollBehavior = toolbarScrollBehavior,
|
||||
colors = FloatingToolbarDefaults.vibrantFloatingToolbarColors(
|
||||
toolbarContainerColor = colorScheme.primary,
|
||||
toolbarContentColor = colorScheme.onPrimary
|
||||
),
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
top = ScreenOffset,
|
||||
bottom = systemBarsInsets.calculateBottomPadding()
|
||||
+ ScreenOffset
|
||||
)
|
||||
.zIndex(1f)
|
||||
) {
|
||||
mainScreens.forEach { item ->
|
||||
val selected = backStack.last() == item.route
|
||||
ToggleButton(
|
||||
checked = selected,
|
||||
onCheckedChange = if (item.route != Screen.Timer) { // Ensure the backstack does not accumulate screens
|
||||
{
|
||||
if (backStack.size < 2) backStack.add(item.route)
|
||||
else backStack[1] = item.route
|
||||
}
|
||||
} else {
|
||||
{ if (backStack.size > 1) backStack.removeAt(1) }
|
||||
},
|
||||
colors = ToggleButtonDefaults.toggleButtonColors(
|
||||
containerColor = colorScheme.primary,
|
||||
contentColor = colorScheme.onPrimary,
|
||||
checkedContainerColor = colorScheme.primaryContainer,
|
||||
checkedContentColor = colorScheme.onPrimaryContainer
|
||||
),
|
||||
shapes = ToggleButtonDefaults.shapes(
|
||||
CircleShape,
|
||||
CircleShape,
|
||||
CircleShape
|
||||
),
|
||||
modifier = Modifier.height(56.dp)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Crossfade(selected) { selected ->
|
||||
if (selected) Icon(painterResource(item.selectedIcon), null)
|
||||
else Icon(painterResource(item.unselectedIcon), null)
|
||||
}
|
||||
AnimatedVisibility(
|
||||
selected || wide,
|
||||
enter = fadeIn(motionScheme.defaultEffectsSpec()) + expandHorizontally(
|
||||
motionScheme.defaultSpatialSpec()
|
||||
),
|
||||
exit = fadeOut(motionScheme.defaultEffectsSpec()) + shrinkHorizontally(
|
||||
motionScheme.defaultSpatialSpec()
|
||||
),
|
||||
) {
|
||||
Row {
|
||||
Spacer(Modifier.width(ButtonDefaults.MediumIconSpacing))
|
||||
Text(stringResource(item.label))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
{ if (backStack.size > 1) backStack.removeAt(1) }
|
||||
},
|
||||
icon = {
|
||||
Crossfade(selected) { selected ->
|
||||
if (selected) Icon(painterResource(it.selectedIcon), null)
|
||||
else Icon(painterResource(it.unselectedIcon), null)
|
||||
}
|
||||
},
|
||||
iconPosition =
|
||||
if (wide) NavigationItemIconPosition.Start
|
||||
else NavigationItemIconPosition.Top,
|
||||
label = { Text(stringResource(it.label)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = modifier
|
||||
) { contentPadding ->
|
||||
SharedTransitionLayout {
|
||||
NavDisplay(
|
||||
@@ -161,20 +232,12 @@ fun AppScreen(
|
||||
TimerScreen(
|
||||
timerState = uiState,
|
||||
isPlus = isPlus,
|
||||
contentPadding = contentPadding,
|
||||
progress = { progress },
|
||||
onAction = timerViewModel::onAction,
|
||||
modifier = modifier
|
||||
.padding(
|
||||
start = contentPadding.calculateStartPadding(layoutDirection),
|
||||
end = contentPadding.calculateEndPadding(layoutDirection),
|
||||
bottom = contentPadding.calculateBottomPadding()
|
||||
)
|
||||
.then(
|
||||
if (isAODEnabled) Modifier.clickable {
|
||||
if (backStack.size < 2) backStack.add(Screen.AOD)
|
||||
}
|
||||
else Modifier
|
||||
),
|
||||
modifier = if (isAODEnabled) Modifier.clickable {
|
||||
if (backStack.size < 2) backStack.add(Screen.AOD)
|
||||
} else Modifier
|
||||
)
|
||||
}
|
||||
|
||||
@@ -183,36 +246,21 @@ fun AppScreen(
|
||||
timerState = uiState,
|
||||
progress = { progress },
|
||||
setTimerFrequency = setTimerFrequency,
|
||||
modifier = Modifier
|
||||
.then(
|
||||
if (isAODEnabled) Modifier.clickable {
|
||||
if (backStack.size > 1) backStack.removeLastOrNull()
|
||||
}
|
||||
else Modifier
|
||||
)
|
||||
modifier = if (isAODEnabled) Modifier.clickable {
|
||||
if (backStack.size > 1) backStack.removeLastOrNull()
|
||||
} else Modifier
|
||||
)
|
||||
}
|
||||
|
||||
entry<Screen.Settings.Main> {
|
||||
SettingsScreenRoot(
|
||||
setShowPaywall = { showPaywall = it },
|
||||
modifier = modifier.padding(
|
||||
start = contentPadding.calculateStartPadding(layoutDirection),
|
||||
end = contentPadding.calculateEndPadding(layoutDirection),
|
||||
bottom = contentPadding.calculateBottomPadding()
|
||||
)
|
||||
contentPadding = contentPadding
|
||||
)
|
||||
}
|
||||
|
||||
entry<Screen.Stats> {
|
||||
StatsScreenRoot(
|
||||
contentPadding = contentPadding,
|
||||
modifier = modifier.padding(
|
||||
start = contentPadding.calculateStartPadding(layoutDirection),
|
||||
end = contentPadding.calculateEndPadding(layoutDirection),
|
||||
bottom = contentPadding.calculateBottomPadding()
|
||||
)
|
||||
)
|
||||
StatsScreenRoot(contentPadding = contentPadding)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -27,8 +27,10 @@ import androidx.compose.animation.slideOutHorizontally
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.calculateEndPadding
|
||||
import androidx.compose.foundation.layout.calculateStartPadding
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -39,6 +41,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SliderState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
@@ -55,6 +58,7 @@ 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.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
@@ -86,6 +90,7 @@ import org.nsh07.pomodoro.ui.theme.CustomColors.topBarColors
|
||||
@Composable
|
||||
fun SettingsScreenRoot(
|
||||
setShowPaywall: (Boolean) -> Unit,
|
||||
contentPadding: PaddingValues,
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: SettingsViewModel = viewModel(factory = SettingsViewModel.Factory)
|
||||
) {
|
||||
@@ -119,6 +124,7 @@ fun SettingsScreenRoot(
|
||||
serviceRunning = serviceRunning,
|
||||
settingsState = settingsState,
|
||||
backStack = backStack,
|
||||
contentPadding = contentPadding,
|
||||
focusTimeInputFieldState = focusTimeInputFieldState,
|
||||
shortBreakTimeInputFieldState = shortBreakTimeInputFieldState,
|
||||
longBreakTimeInputFieldState = longBreakTimeInputFieldState,
|
||||
@@ -137,6 +143,7 @@ private fun SettingsScreen(
|
||||
serviceRunning: Boolean,
|
||||
settingsState: SettingsState,
|
||||
backStack: SnapshotStateList<Screen.Settings>,
|
||||
contentPadding: PaddingValues,
|
||||
focusTimeInputFieldState: TextFieldState,
|
||||
shortBreakTimeInputFieldState: TextFieldState,
|
||||
longBreakTimeInputFieldState: TextFieldState,
|
||||
@@ -146,6 +153,7 @@ private fun SettingsScreen(
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||
|
||||
val currentLocales =
|
||||
@@ -181,26 +189,36 @@ private fun SettingsScreen(
|
||||
},
|
||||
entryProvider = entryProvider {
|
||||
entry<Screen.Settings.Main> {
|
||||
Column(modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
stringResource(R.string.settings),
|
||||
style = LocalTextStyle.current.copy(
|
||||
fontFamily = robotoFlexTopBar,
|
||||
fontSize = 32.sp,
|
||||
lineHeight = 32.sp
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
stringResource(R.string.settings),
|
||||
style = LocalTextStyle.current.copy(
|
||||
fontFamily = robotoFlexTopBar,
|
||||
fontSize = 32.sp,
|
||||
lineHeight = 32.sp
|
||||
)
|
||||
)
|
||||
)
|
||||
},
|
||||
subtitle = {},
|
||||
colors = topBarColors,
|
||||
titleHorizontalAlignment = Alignment.CenterHorizontally,
|
||||
scrollBehavior = scrollBehavior
|
||||
},
|
||||
subtitle = {},
|
||||
colors = topBarColors,
|
||||
titleHorizontalAlignment = Alignment.CenterHorizontally,
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
},
|
||||
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
) { innerPadding ->
|
||||
val insets = PaddingValues(
|
||||
bottom = contentPadding.calculateBottomPadding(),
|
||||
top = innerPadding.calculateTopPadding(),
|
||||
start = innerPadding.calculateStartPadding(layoutDirection),
|
||||
end = innerPadding.calculateEndPadding(layoutDirection)
|
||||
)
|
||||
|
||||
LazyColumn(
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
contentPadding = insets,
|
||||
modifier = Modifier
|
||||
.background(topBarColors.containerColor)
|
||||
.fillMaxSize()
|
||||
@@ -279,6 +297,7 @@ private fun SettingsScreen(
|
||||
entry<Screen.Settings.Alarm> {
|
||||
AlarmSettings(
|
||||
settingsState = settingsState,
|
||||
contentPadding = contentPadding,
|
||||
onAction = onAction,
|
||||
onBack = backStack::removeLastOrNull,
|
||||
modifier = modifier,
|
||||
@@ -287,6 +306,7 @@ private fun SettingsScreen(
|
||||
entry<Screen.Settings.Appearance> {
|
||||
AppearanceSettings(
|
||||
settingsState = settingsState,
|
||||
contentPadding = contentPadding,
|
||||
isPlus = isPlus,
|
||||
onAction = onAction,
|
||||
setShowPaywall = setShowPaywall,
|
||||
@@ -299,6 +319,7 @@ private fun SettingsScreen(
|
||||
isPlus = isPlus,
|
||||
serviceRunning = serviceRunning,
|
||||
settingsState = settingsState,
|
||||
contentPadding = contentPadding,
|
||||
focusTimeInputFieldState = focusTimeInputFieldState,
|
||||
shortBreakTimeInputFieldState = shortBreakTimeInputFieldState,
|
||||
longBreakTimeInputFieldState = longBreakTimeInputFieldState,
|
||||
|
||||
@@ -28,8 +28,10 @@ import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.calculateEndPadding
|
||||
import androidx.compose.foundation.layout.calculateStartPadding
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -43,6 +45,7 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButtonDefaults
|
||||
import androidx.compose.material3.LargeFlexibleTopAppBar
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.SwitchDefaults
|
||||
import androidx.compose.material3.Text
|
||||
@@ -57,6 +60,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
@@ -80,12 +84,14 @@ import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.topListItemShape
|
||||
@Composable
|
||||
fun AlarmSettings(
|
||||
settingsState: SettingsState,
|
||||
contentPadding: PaddingValues,
|
||||
onAction: (SettingsAction) -> Unit,
|
||||
onBack: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||
val context = LocalContext.current
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
|
||||
var alarmName by remember { mutableStateOf("...") }
|
||||
|
||||
@@ -148,32 +154,42 @@ fun AlarmSettings(
|
||||
)
|
||||
}
|
||||
|
||||
Column(modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) {
|
||||
LargeFlexibleTopAppBar(
|
||||
title = {
|
||||
Text(stringResource(R.string.alarm), fontFamily = robotoFlexTopBar)
|
||||
},
|
||||
subtitle = {
|
||||
Text(stringResource(R.string.settings))
|
||||
},
|
||||
navigationIcon = {
|
||||
FilledTonalIconButton(
|
||||
onClick = onBack,
|
||||
shapes = IconButtonDefaults.shapes(),
|
||||
colors = IconButtonDefaults.filledTonalIconButtonColors(containerColor = listItemColors.containerColor)
|
||||
) {
|
||||
Icon(
|
||||
painterResource(R.drawable.arrow_back),
|
||||
null
|
||||
)
|
||||
}
|
||||
},
|
||||
colors = topBarColors,
|
||||
scrollBehavior = scrollBehavior
|
||||
Scaffold(
|
||||
topBar = {
|
||||
LargeFlexibleTopAppBar(
|
||||
title = {
|
||||
Text(stringResource(R.string.alarm), fontFamily = robotoFlexTopBar)
|
||||
},
|
||||
subtitle = {
|
||||
Text(stringResource(R.string.settings))
|
||||
},
|
||||
navigationIcon = {
|
||||
FilledTonalIconButton(
|
||||
onClick = onBack,
|
||||
shapes = IconButtonDefaults.shapes(),
|
||||
colors = IconButtonDefaults.filledTonalIconButtonColors(containerColor = listItemColors.containerColor)
|
||||
) {
|
||||
Icon(
|
||||
painterResource(R.drawable.arrow_back),
|
||||
null
|
||||
)
|
||||
}
|
||||
},
|
||||
colors = topBarColors,
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
},
|
||||
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
) { innerPadding ->
|
||||
val insets = PaddingValues(
|
||||
bottom = contentPadding.calculateBottomPadding(),
|
||||
top = innerPadding.calculateTopPadding(),
|
||||
start = innerPadding.calculateStartPadding(layoutDirection),
|
||||
end = innerPadding.calculateEndPadding(layoutDirection)
|
||||
)
|
||||
|
||||
LazyColumn(
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
contentPadding = insets,
|
||||
modifier = Modifier
|
||||
.background(topBarColors.containerColor)
|
||||
.fillMaxSize()
|
||||
@@ -248,6 +264,7 @@ fun AlarmSettingsPreview() {
|
||||
val settingsState = SettingsState()
|
||||
AlarmSettings(
|
||||
settingsState = settingsState,
|
||||
contentPadding = PaddingValues(),
|
||||
onAction = {},
|
||||
onBack = {}
|
||||
)
|
||||
|
||||
@@ -19,8 +19,10 @@ package org.nsh07.pomodoro.ui.settingsScreen.screens
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.calculateEndPadding
|
||||
import androidx.compose.foundation.layout.calculateStartPadding
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -33,6 +35,7 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButtonDefaults
|
||||
import androidx.compose.material3.LargeFlexibleTopAppBar
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.SwitchDefaults
|
||||
import androidx.compose.material3.Text
|
||||
@@ -41,6 +44,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
@@ -64,6 +68,7 @@ import org.nsh07.pomodoro.utils.toColor
|
||||
@Composable
|
||||
fun AppearanceSettings(
|
||||
settingsState: SettingsState,
|
||||
contentPadding: PaddingValues,
|
||||
isPlus: Boolean,
|
||||
onAction: (SettingsAction) -> Unit,
|
||||
setShowPaywall: (Boolean) -> Unit,
|
||||
@@ -71,33 +76,44 @@ fun AppearanceSettings(
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
|
||||
Column(modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) {
|
||||
LargeFlexibleTopAppBar(
|
||||
title = {
|
||||
Text(stringResource(R.string.appearance), fontFamily = robotoFlexTopBar)
|
||||
},
|
||||
subtitle = {
|
||||
Text(stringResource(R.string.settings))
|
||||
},
|
||||
navigationIcon = {
|
||||
FilledTonalIconButton(
|
||||
onClick = onBack,
|
||||
shapes = IconButtonDefaults.shapes(),
|
||||
colors = IconButtonDefaults.filledTonalIconButtonColors(containerColor = listItemColors.containerColor)
|
||||
) {
|
||||
Icon(
|
||||
painterResource(R.drawable.arrow_back),
|
||||
null
|
||||
)
|
||||
}
|
||||
},
|
||||
colors = topBarColors,
|
||||
scrollBehavior = scrollBehavior
|
||||
Scaffold(
|
||||
topBar = {
|
||||
LargeFlexibleTopAppBar(
|
||||
title = {
|
||||
Text(stringResource(R.string.appearance), fontFamily = robotoFlexTopBar)
|
||||
},
|
||||
subtitle = {
|
||||
Text(stringResource(R.string.settings))
|
||||
},
|
||||
navigationIcon = {
|
||||
FilledTonalIconButton(
|
||||
onClick = onBack,
|
||||
shapes = IconButtonDefaults.shapes(),
|
||||
colors = IconButtonDefaults.filledTonalIconButtonColors(containerColor = listItemColors.containerColor)
|
||||
) {
|
||||
Icon(
|
||||
painterResource(R.drawable.arrow_back),
|
||||
null
|
||||
)
|
||||
}
|
||||
},
|
||||
colors = topBarColors,
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
},
|
||||
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
) { innerPadding ->
|
||||
val insets = PaddingValues(
|
||||
bottom = contentPadding.calculateBottomPadding(),
|
||||
top = innerPadding.calculateTopPadding(),
|
||||
start = innerPadding.calculateStartPadding(layoutDirection),
|
||||
end = innerPadding.calculateEndPadding(layoutDirection)
|
||||
)
|
||||
|
||||
LazyColumn(
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
contentPadding = insets,
|
||||
modifier = Modifier
|
||||
.background(topBarColors.containerColor)
|
||||
.fillMaxSize()
|
||||
@@ -182,6 +198,7 @@ fun AppearanceSettingsPreview() {
|
||||
TomatoTheme(dynamicColor = false) {
|
||||
AppearanceSettings(
|
||||
settingsState = settingsState,
|
||||
contentPadding = PaddingValues(),
|
||||
isPlus = false,
|
||||
onAction = {},
|
||||
setShowPaywall = {},
|
||||
|
||||
@@ -27,8 +27,11 @@ import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.calculateEndPadding
|
||||
import androidx.compose.foundation.layout.calculateStartPadding
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
@@ -53,6 +56,7 @@ import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||
import androidx.compose.material3.MaterialTheme.typography
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Slider
|
||||
import androidx.compose.material3.SliderState
|
||||
import androidx.compose.material3.Switch
|
||||
@@ -71,6 +75,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
@@ -98,6 +103,7 @@ fun TimerSettings(
|
||||
isPlus: Boolean,
|
||||
serviceRunning: Boolean,
|
||||
settingsState: SettingsState,
|
||||
contentPadding: PaddingValues,
|
||||
focusTimeInputFieldState: TextFieldState,
|
||||
shortBreakTimeInputFieldState: TextFieldState,
|
||||
longBreakTimeInputFieldState: TextFieldState,
|
||||
@@ -109,6 +115,7 @@ fun TimerSettings(
|
||||
) {
|
||||
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||
val context = LocalContext.current
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
val appName = stringResource(R.string.app_name)
|
||||
val notificationManagerService =
|
||||
remember { context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager }
|
||||
@@ -141,32 +148,42 @@ fun TimerSettings(
|
||||
)
|
||||
)
|
||||
|
||||
Column(modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) {
|
||||
LargeFlexibleTopAppBar(
|
||||
title = {
|
||||
Text(stringResource(R.string.timer), fontFamily = robotoFlexTopBar)
|
||||
},
|
||||
subtitle = {
|
||||
Text(stringResource(R.string.settings))
|
||||
},
|
||||
navigationIcon = {
|
||||
FilledTonalIconButton(
|
||||
onClick = onBack,
|
||||
shapes = IconButtonDefaults.shapes(),
|
||||
colors = IconButtonDefaults.filledTonalIconButtonColors(containerColor = listItemColors.containerColor)
|
||||
) {
|
||||
Icon(
|
||||
painterResource(R.drawable.arrow_back),
|
||||
null
|
||||
)
|
||||
}
|
||||
},
|
||||
colors = topBarColors,
|
||||
scrollBehavior = scrollBehavior
|
||||
Scaffold(
|
||||
topBar = {
|
||||
LargeFlexibleTopAppBar(
|
||||
title = {
|
||||
Text(stringResource(R.string.timer), fontFamily = robotoFlexTopBar)
|
||||
},
|
||||
subtitle = {
|
||||
Text(stringResource(R.string.settings))
|
||||
},
|
||||
navigationIcon = {
|
||||
FilledTonalIconButton(
|
||||
onClick = onBack,
|
||||
shapes = IconButtonDefaults.shapes(),
|
||||
colors = IconButtonDefaults.filledTonalIconButtonColors(containerColor = listItemColors.containerColor)
|
||||
) {
|
||||
Icon(
|
||||
painterResource(R.drawable.arrow_back),
|
||||
null
|
||||
)
|
||||
}
|
||||
},
|
||||
colors = topBarColors,
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
},
|
||||
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
) { innerPadding ->
|
||||
val insets = PaddingValues(
|
||||
bottom = contentPadding.calculateBottomPadding(),
|
||||
top = innerPadding.calculateTopPadding(),
|
||||
start = innerPadding.calculateStartPadding(layoutDirection),
|
||||
end = innerPadding.calculateEndPadding(layoutDirection)
|
||||
)
|
||||
|
||||
LazyColumn(
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
contentPadding = insets,
|
||||
modifier = Modifier
|
||||
.background(topBarColors.containerColor)
|
||||
.fillMaxSize()
|
||||
@@ -428,6 +445,7 @@ private fun TimerSettingsPreview() {
|
||||
isPlus = false,
|
||||
serviceRunning = true,
|
||||
settingsState = remember { SettingsState() },
|
||||
contentPadding = PaddingValues(),
|
||||
focusTimeInputFieldState = focusTimeInputFieldState,
|
||||
shortBreakTimeInputFieldState = shortBreakTimeInputFieldState,
|
||||
longBreakTimeInputFieldState = longBreakTimeInputFieldState,
|
||||
|
||||
@@ -27,6 +27,8 @@ import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.calculateEndPadding
|
||||
import androidx.compose.foundation.layout.calculateStartPadding
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -45,6 +47,7 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||
import androidx.compose.material3.MaterialTheme.motionScheme
|
||||
import androidx.compose.material3.MaterialTheme.typography
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
@@ -61,6 +64,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalFontFamilyResolver
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
@@ -129,6 +133,7 @@ fun StatsScreen(
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
|
||||
val hoursFormat = stringResource(R.string.hours_format)
|
||||
val hoursMinutesFormat = stringResource(R.string.hours_and_minutes_format)
|
||||
@@ -160,43 +165,50 @@ fun StatsScreen(
|
||||
val axisTypeface = remember { resolver.resolve(googleFlex400).value as Typeface }
|
||||
val markerTypeface = remember { resolver.resolve(googleFlex600).value as Typeface }
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
) {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
stringResource(R.string.stats),
|
||||
style = LocalTextStyle.current.copy(
|
||||
fontFamily = robotoFlexTopBar,
|
||||
fontSize = 32.sp,
|
||||
lineHeight = 32.sp
|
||||
),
|
||||
modifier = Modifier
|
||||
.padding(top = contentPadding.calculateTopPadding())
|
||||
.padding(vertical = 14.dp)
|
||||
)
|
||||
},
|
||||
actions = if (BuildConfig.DEBUG) {
|
||||
{
|
||||
IconButton(
|
||||
onClick = generateSampleData
|
||||
) {
|
||||
Spacer(Modifier.size(24.dp))
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
stringResource(R.string.stats),
|
||||
style = LocalTextStyle.current.copy(
|
||||
fontFamily = robotoFlexTopBar,
|
||||
fontSize = 32.sp,
|
||||
lineHeight = 32.sp
|
||||
),
|
||||
modifier = Modifier
|
||||
.padding(top = contentPadding.calculateTopPadding())
|
||||
.padding(vertical = 14.dp)
|
||||
)
|
||||
},
|
||||
actions = if (BuildConfig.DEBUG) {
|
||||
{
|
||||
IconButton(
|
||||
onClick = generateSampleData
|
||||
) {
|
||||
Spacer(Modifier.size(24.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
{}
|
||||
},
|
||||
subtitle = {},
|
||||
titleHorizontalAlignment = Alignment.CenterHorizontally,
|
||||
scrollBehavior = scrollBehavior,
|
||||
windowInsets = WindowInsets()
|
||||
} else {
|
||||
{}
|
||||
},
|
||||
subtitle = {},
|
||||
titleHorizontalAlignment = Alignment.CenterHorizontally,
|
||||
scrollBehavior = scrollBehavior,
|
||||
windowInsets = WindowInsets()
|
||||
)
|
||||
},
|
||||
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
) { innerPadding ->
|
||||
val insets = PaddingValues(
|
||||
bottom = contentPadding.calculateBottomPadding(),
|
||||
top = innerPadding.calculateTopPadding(),
|
||||
start = innerPadding.calculateStartPadding(layoutDirection),
|
||||
end = innerPadding.calculateEndPadding(layoutDirection)
|
||||
)
|
||||
|
||||
LazyColumn(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
contentPadding = insets,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
item { Spacer(Modifier) }
|
||||
|
||||
@@ -38,8 +38,11 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.calculateEndPadding
|
||||
import androidx.compose.foundation.layout.calculateStartPadding
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
@@ -47,8 +50,7 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material3.ButtonGroup
|
||||
import androidx.compose.material3.ButtonGroupDefaults
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
@@ -64,6 +66,7 @@ import androidx.compose.material3.MaterialTheme.colorScheme
|
||||
import androidx.compose.material3.MaterialTheme.motionScheme
|
||||
import androidx.compose.material3.MaterialTheme.shapes
|
||||
import androidx.compose.material3.MaterialTheme.typography
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
@@ -84,6 +87,7 @@ import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
@@ -107,12 +111,14 @@ import org.nsh07.pomodoro.ui.timerScreen.viewModel.TimerState
|
||||
fun SharedTransitionScope.TimerScreen(
|
||||
timerState: TimerState,
|
||||
isPlus: Boolean,
|
||||
contentPadding: PaddingValues,
|
||||
progress: () -> Float,
|
||||
onAction: (TimerAction) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val motionScheme = motionScheme
|
||||
val haptic = LocalHapticFeedback.current
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
|
||||
val color by animateColorAsState(
|
||||
if (timerState.timerMode == TimerMode.FOCUS) colorScheme.primary
|
||||
@@ -137,407 +143,424 @@ fun SharedTransitionScope.TimerScreen(
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
||||
|
||||
Column(modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) {
|
||||
TopAppBar(
|
||||
title = {
|
||||
AnimatedContent(
|
||||
if (!timerState.showBrandTitle) timerState.timerMode else TimerMode.BRAND,
|
||||
transitionSpec = {
|
||||
slideInVertically(
|
||||
animationSpec = motionScheme.defaultSpatialSpec(),
|
||||
initialOffsetY = { (-it * 1.25).toInt() }
|
||||
).togetherWith(
|
||||
slideOutVertically(
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
AnimatedContent(
|
||||
if (!timerState.showBrandTitle) timerState.timerMode else TimerMode.BRAND,
|
||||
transitionSpec = {
|
||||
slideInVertically(
|
||||
animationSpec = motionScheme.defaultSpatialSpec(),
|
||||
targetOffsetY = { (it * 1.25).toInt() }
|
||||
initialOffsetY = { (-it * 1.25).toInt() }
|
||||
).togetherWith(
|
||||
slideOutVertically(
|
||||
animationSpec = motionScheme.defaultSpatialSpec(),
|
||||
targetOffsetY = { (it * 1.25).toInt() }
|
||||
)
|
||||
)
|
||||
)
|
||||
},
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier.fillMaxWidth(.9f)
|
||||
) {
|
||||
when (it) {
|
||||
TimerMode.BRAND ->
|
||||
Text(
|
||||
if (!isPlus) stringResource(R.string.app_name)
|
||||
else stringResource(R.string.app_name_plus),
|
||||
},
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier.fillMaxWidth(.9f)
|
||||
) {
|
||||
when (it) {
|
||||
TimerMode.BRAND ->
|
||||
Text(
|
||||
if (!isPlus) stringResource(R.string.app_name)
|
||||
else stringResource(R.string.app_name_plus),
|
||||
style = TextStyle(
|
||||
fontFamily = robotoFlexTopBar,
|
||||
fontSize = 32.sp,
|
||||
lineHeight = 32.sp,
|
||||
color = colorScheme.error
|
||||
),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
TimerMode.FOCUS ->
|
||||
Text(
|
||||
stringResource(R.string.focus),
|
||||
style = TextStyle(
|
||||
fontFamily = robotoFlexTopBar,
|
||||
fontSize = 32.sp,
|
||||
lineHeight = 32.sp,
|
||||
color = colorScheme.primary
|
||||
),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
TimerMode.SHORT_BREAK -> Text(
|
||||
stringResource(R.string.short_break),
|
||||
style = TextStyle(
|
||||
fontFamily = robotoFlexTopBar,
|
||||
fontSize = 32.sp,
|
||||
lineHeight = 32.sp,
|
||||
color = colorScheme.error
|
||||
color = colorScheme.tertiary
|
||||
),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
TimerMode.FOCUS ->
|
||||
Text(
|
||||
stringResource(R.string.focus),
|
||||
TimerMode.LONG_BREAK -> Text(
|
||||
stringResource(R.string.long_break),
|
||||
style = TextStyle(
|
||||
fontFamily = robotoFlexTopBar,
|
||||
fontSize = 32.sp,
|
||||
lineHeight = 32.sp,
|
||||
color = colorScheme.primary
|
||||
color = colorScheme.tertiary
|
||||
),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
TimerMode.SHORT_BREAK -> Text(
|
||||
stringResource(R.string.short_break),
|
||||
style = TextStyle(
|
||||
fontFamily = robotoFlexTopBar,
|
||||
fontSize = 32.sp,
|
||||
lineHeight = 32.sp,
|
||||
color = colorScheme.tertiary
|
||||
),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
TimerMode.LONG_BREAK -> Text(
|
||||
stringResource(R.string.long_break),
|
||||
style = TextStyle(
|
||||
fontFamily = robotoFlexTopBar,
|
||||
fontSize = 32.sp,
|
||||
lineHeight = 32.sp,
|
||||
color = colorScheme.tertiary
|
||||
),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
subtitle = {},
|
||||
titleHorizontalAlignment = CenterHorizontally,
|
||||
scrollBehavior = scrollBehavior
|
||||
},
|
||||
subtitle = {},
|
||||
titleHorizontalAlignment = CenterHorizontally,
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
},
|
||||
modifier = modifier
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
) { innerPadding ->
|
||||
val insets = PaddingValues(
|
||||
bottom = contentPadding.calculateBottomPadding(),
|
||||
top = innerPadding.calculateTopPadding(),
|
||||
start = innerPadding.calculateStartPadding(layoutDirection),
|
||||
end = innerPadding.calculateEndPadding(layoutDirection)
|
||||
)
|
||||
|
||||
Column(
|
||||
LazyColumn(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = CenterHorizontally,
|
||||
contentPadding = insets,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
Column(horizontalAlignment = CenterHorizontally) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
if (timerState.timerMode == TimerMode.FOCUS) {
|
||||
CircularProgressIndicator(
|
||||
progress = progress,
|
||||
modifier = Modifier
|
||||
.sharedBounds(
|
||||
sharedContentState = this@TimerScreen.rememberSharedContentState(
|
||||
"focus progress"
|
||||
),
|
||||
animatedVisibilityScope = LocalNavAnimatedContentScope.current
|
||||
)
|
||||
.widthIn(max = 350.dp)
|
||||
.fillMaxWidth(0.9f)
|
||||
.aspectRatio(1f),
|
||||
color = color,
|
||||
trackColor = colorContainer,
|
||||
strokeWidth = 16.dp,
|
||||
gapSize = 8.dp
|
||||
)
|
||||
} else {
|
||||
CircularWavyProgressIndicator(
|
||||
progress = progress,
|
||||
modifier = Modifier
|
||||
.sharedBounds(
|
||||
sharedContentState = this@TimerScreen.rememberSharedContentState(
|
||||
"break progress"
|
||||
),
|
||||
animatedVisibilityScope = LocalNavAnimatedContentScope.current
|
||||
)
|
||||
.widthIn(max = 350.dp)
|
||||
.fillMaxWidth(0.9f)
|
||||
.aspectRatio(1f),
|
||||
color = color,
|
||||
trackColor = colorContainer,
|
||||
stroke = Stroke(
|
||||
width = with(LocalDensity.current) {
|
||||
16.dp.toPx()
|
||||
},
|
||||
cap = StrokeCap.Round,
|
||||
),
|
||||
trackStroke = Stroke(
|
||||
width = with(LocalDensity.current) {
|
||||
16.dp.toPx()
|
||||
},
|
||||
cap = StrokeCap.Round,
|
||||
),
|
||||
wavelength = 60.dp,
|
||||
gapSize = 8.dp
|
||||
)
|
||||
}
|
||||
var expanded by remember { mutableStateOf(timerState.showBrandTitle) }
|
||||
Column(
|
||||
horizontalAlignment = CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.clip(shapes.largeIncreased)
|
||||
.clickable(onClick = { expanded = !expanded })
|
||||
) {
|
||||
LaunchedEffect(timerState.showBrandTitle) {
|
||||
expanded = timerState.showBrandTitle
|
||||
}
|
||||
Text(
|
||||
text = timerState.timeStr,
|
||||
style = TextStyle(
|
||||
fontFamily = googleFlex600,
|
||||
fontSize = 72.sp,
|
||||
letterSpacing = (-2.6).sp,
|
||||
fontFeatureSettings = "tnum"
|
||||
),
|
||||
textAlign = TextAlign.Center,
|
||||
maxLines = 1,
|
||||
modifier = Modifier.sharedBounds(
|
||||
sharedContentState = this@TimerScreen.rememberSharedContentState("clock"),
|
||||
animatedVisibilityScope = LocalNavAnimatedContentScope.current
|
||||
)
|
||||
)
|
||||
AnimatedVisibility(
|
||||
expanded,
|
||||
enter = fadeIn(motionScheme.defaultEffectsSpec()) +
|
||||
expandVertically(motionScheme.defaultSpatialSpec()),
|
||||
exit = fadeOut(motionScheme.defaultEffectsSpec()) +
|
||||
shrinkVertically(motionScheme.defaultSpatialSpec())
|
||||
) {
|
||||
Text(
|
||||
stringResource(
|
||||
R.string.timer_session_count,
|
||||
timerState.currentFocusCount,
|
||||
timerState.totalFocusCount
|
||||
),
|
||||
fontFamily = googleFlex600,
|
||||
style = typography.titleLarge,
|
||||
color = colorScheme.outline
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
val interactionSources = remember { List(3) { MutableInteractionSource() } }
|
||||
ButtonGroup(
|
||||
overflowIndicator = { state ->
|
||||
ButtonGroupDefaults.OverflowIndicator(
|
||||
state,
|
||||
colors = IconButtonDefaults.filledTonalIconButtonColors(),
|
||||
modifier = Modifier.size(64.dp, 96.dp)
|
||||
)
|
||||
},
|
||||
modifier = Modifier.padding(16.dp)
|
||||
) {
|
||||
customItem(
|
||||
{
|
||||
FilledIconToggleButton(
|
||||
onCheckedChange = { checked ->
|
||||
onAction(TimerAction.ToggleTimer)
|
||||
|
||||
if (checked) haptic.performHapticFeedback(HapticFeedbackType.ToggleOn)
|
||||
else haptic.performHapticFeedback(HapticFeedbackType.ToggleOff)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && checked) {
|
||||
permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||
}
|
||||
},
|
||||
checked = timerState.timerRunning,
|
||||
colors = IconButtonDefaults.filledIconToggleButtonColors(
|
||||
checkedContainerColor = color,
|
||||
checkedContentColor = onColor
|
||||
),
|
||||
shapes = IconButtonDefaults.toggleableShapes(),
|
||||
interactionSource = interactionSources[0],
|
||||
item {
|
||||
Column(horizontalAlignment = CenterHorizontally) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
if (timerState.timerMode == TimerMode.FOCUS) {
|
||||
CircularProgressIndicator(
|
||||
progress = progress,
|
||||
modifier = Modifier
|
||||
.size(width = 128.dp, height = 96.dp)
|
||||
.animateWidth(interactionSources[0])
|
||||
) {
|
||||
if (timerState.timerRunning) {
|
||||
Icon(
|
||||
painterResource(R.drawable.pause_large),
|
||||
contentDescription = stringResource(R.string.pause),
|
||||
modifier = Modifier.size(32.dp)
|
||||
.sharedBounds(
|
||||
sharedContentState = this@TimerScreen.rememberSharedContentState(
|
||||
"focus progress"
|
||||
),
|
||||
animatedVisibilityScope = LocalNavAnimatedContentScope.current
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
painterResource(R.drawable.play_large),
|
||||
contentDescription = stringResource(R.string.play),
|
||||
modifier = Modifier.size(32.dp)
|
||||
.widthIn(max = 350.dp)
|
||||
.fillMaxWidth(0.9f)
|
||||
.aspectRatio(1f),
|
||||
color = color,
|
||||
trackColor = colorContainer,
|
||||
strokeWidth = 16.dp,
|
||||
gapSize = 8.dp
|
||||
)
|
||||
} else {
|
||||
CircularWavyProgressIndicator(
|
||||
progress = progress,
|
||||
modifier = Modifier
|
||||
.sharedBounds(
|
||||
sharedContentState = this@TimerScreen.rememberSharedContentState(
|
||||
"break progress"
|
||||
),
|
||||
animatedVisibilityScope = LocalNavAnimatedContentScope.current
|
||||
)
|
||||
}
|
||||
.widthIn(max = 350.dp)
|
||||
.fillMaxWidth(0.9f)
|
||||
.aspectRatio(1f),
|
||||
color = color,
|
||||
trackColor = colorContainer,
|
||||
stroke = Stroke(
|
||||
width = with(LocalDensity.current) {
|
||||
16.dp.toPx()
|
||||
},
|
||||
cap = StrokeCap.Round,
|
||||
),
|
||||
trackStroke = Stroke(
|
||||
width = with(LocalDensity.current) {
|
||||
16.dp.toPx()
|
||||
},
|
||||
cap = StrokeCap.Round,
|
||||
),
|
||||
wavelength = 60.dp,
|
||||
gapSize = 8.dp
|
||||
)
|
||||
}
|
||||
var expanded by remember { mutableStateOf(timerState.showBrandTitle) }
|
||||
Column(
|
||||
horizontalAlignment = CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.clip(shapes.largeIncreased)
|
||||
.clickable(onClick = { expanded = !expanded })
|
||||
) {
|
||||
LaunchedEffect(timerState.showBrandTitle) {
|
||||
expanded = timerState.showBrandTitle
|
||||
}
|
||||
Text(
|
||||
text = timerState.timeStr,
|
||||
style = TextStyle(
|
||||
fontFamily = googleFlex600,
|
||||
fontSize = 72.sp,
|
||||
letterSpacing = (-2.6).sp,
|
||||
fontFeatureSettings = "tnum"
|
||||
),
|
||||
textAlign = TextAlign.Center,
|
||||
maxLines = 1,
|
||||
modifier = Modifier.sharedBounds(
|
||||
sharedContentState = this@TimerScreen.rememberSharedContentState(
|
||||
"clock"
|
||||
),
|
||||
animatedVisibilityScope = LocalNavAnimatedContentScope.current
|
||||
)
|
||||
)
|
||||
AnimatedVisibility(
|
||||
expanded,
|
||||
enter = fadeIn(motionScheme.defaultEffectsSpec()) +
|
||||
expandVertically(motionScheme.defaultSpatialSpec()),
|
||||
exit = fadeOut(motionScheme.defaultEffectsSpec()) +
|
||||
shrinkVertically(motionScheme.defaultSpatialSpec())
|
||||
) {
|
||||
Text(
|
||||
stringResource(
|
||||
R.string.timer_session_count,
|
||||
timerState.currentFocusCount,
|
||||
timerState.totalFocusCount
|
||||
),
|
||||
fontFamily = googleFlex600,
|
||||
style = typography.titleLarge,
|
||||
color = colorScheme.outline
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
val interactionSources = remember { List(3) { MutableInteractionSource() } }
|
||||
ButtonGroup(
|
||||
overflowIndicator = { state ->
|
||||
ButtonGroupDefaults.OverflowIndicator(
|
||||
state,
|
||||
colors = IconButtonDefaults.filledTonalIconButtonColors(),
|
||||
modifier = Modifier.size(64.dp, 96.dp)
|
||||
)
|
||||
},
|
||||
{ state ->
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
modifier = Modifier.padding(16.dp)
|
||||
) {
|
||||
customItem(
|
||||
{
|
||||
FilledIconToggleButton(
|
||||
onCheckedChange = { checked ->
|
||||
onAction(TimerAction.ToggleTimer)
|
||||
|
||||
if (checked) haptic.performHapticFeedback(HapticFeedbackType.ToggleOn)
|
||||
else haptic.performHapticFeedback(HapticFeedbackType.ToggleOff)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && checked) {
|
||||
permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||
}
|
||||
},
|
||||
checked = timerState.timerRunning,
|
||||
colors = IconButtonDefaults.filledIconToggleButtonColors(
|
||||
checkedContainerColor = color,
|
||||
checkedContentColor = onColor
|
||||
),
|
||||
shapes = IconButtonDefaults.toggleableShapes(),
|
||||
interactionSource = interactionSources[0],
|
||||
modifier = Modifier
|
||||
.size(width = 128.dp, height = 96.dp)
|
||||
.animateWidth(interactionSources[0])
|
||||
) {
|
||||
if (timerState.timerRunning) {
|
||||
Icon(
|
||||
painterResource(R.drawable.pause),
|
||||
contentDescription = stringResource(R.string.pause)
|
||||
painterResource(R.drawable.pause_large),
|
||||
contentDescription = stringResource(R.string.pause),
|
||||
modifier = Modifier.size(32.dp)
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
painterResource(R.drawable.play),
|
||||
contentDescription = stringResource(R.string.play)
|
||||
painterResource(R.drawable.play_large),
|
||||
contentDescription = stringResource(R.string.play),
|
||||
modifier = Modifier.size(32.dp)
|
||||
)
|
||||
}
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
if (timerState.timerRunning) stringResource(R.string.pause) else stringResource(
|
||||
R.string.play
|
||||
}
|
||||
},
|
||||
{ state ->
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
if (timerState.timerRunning) {
|
||||
Icon(
|
||||
painterResource(R.drawable.pause),
|
||||
contentDescription = stringResource(R.string.pause)
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
painterResource(R.drawable.play),
|
||||
contentDescription = stringResource(R.string.play)
|
||||
)
|
||||
}
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
if (timerState.timerRunning) stringResource(R.string.pause) else stringResource(
|
||||
R.string.play
|
||||
)
|
||||
)
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
onAction(TimerAction.ToggleTimer)
|
||||
state.dismiss()
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
customItem(
|
||||
{
|
||||
FilledTonalIconButton(
|
||||
onClick = {
|
||||
onAction(TimerAction.ResetTimer)
|
||||
haptic.performHapticFeedback(HapticFeedbackType.VirtualKey)
|
||||
},
|
||||
colors = IconButtonDefaults.filledTonalIconButtonColors(
|
||||
containerColor = colorContainer
|
||||
),
|
||||
shapes = IconButtonDefaults.shapes(),
|
||||
interactionSource = interactionSources[1],
|
||||
modifier = Modifier
|
||||
.size(96.dp)
|
||||
.animateWidth(interactionSources[1])
|
||||
) {
|
||||
Icon(
|
||||
painterResource(R.drawable.restart_large),
|
||||
contentDescription = stringResource(R.string.restart),
|
||||
modifier = Modifier.size(32.dp)
|
||||
},
|
||||
onClick = {
|
||||
onAction(TimerAction.ToggleTimer)
|
||||
state.dismiss()
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
{ state ->
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
painterResource(R.drawable.restart),
|
||||
stringResource(R.string.restart)
|
||||
)
|
||||
},
|
||||
text = { Text(stringResource(R.string.restart)) },
|
||||
onClick = {
|
||||
onAction(TimerAction.ResetTimer)
|
||||
state.dismiss()
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
customItem(
|
||||
{
|
||||
FilledTonalIconButton(
|
||||
onClick = {
|
||||
onAction(TimerAction.SkipTimer(fromButton = true))
|
||||
haptic.performHapticFeedback(HapticFeedbackType.VirtualKey)
|
||||
},
|
||||
colors = IconButtonDefaults.filledTonalIconButtonColors(
|
||||
containerColor = colorContainer
|
||||
),
|
||||
shapes = IconButtonDefaults.shapes(),
|
||||
interactionSource = interactionSources[2],
|
||||
modifier = Modifier
|
||||
.size(64.dp, 96.dp)
|
||||
.animateWidth(interactionSources[2])
|
||||
) {
|
||||
Icon(
|
||||
painterResource(R.drawable.skip_next_large),
|
||||
contentDescription = stringResource(R.string.skip_to_next),
|
||||
modifier = Modifier.size(32.dp)
|
||||
customItem(
|
||||
{
|
||||
FilledTonalIconButton(
|
||||
onClick = {
|
||||
onAction(TimerAction.ResetTimer)
|
||||
haptic.performHapticFeedback(HapticFeedbackType.VirtualKey)
|
||||
},
|
||||
colors = IconButtonDefaults.filledTonalIconButtonColors(
|
||||
containerColor = colorContainer
|
||||
),
|
||||
shapes = IconButtonDefaults.shapes(),
|
||||
interactionSource = interactionSources[1],
|
||||
modifier = Modifier
|
||||
.size(96.dp)
|
||||
.animateWidth(interactionSources[1])
|
||||
) {
|
||||
Icon(
|
||||
painterResource(R.drawable.restart_large),
|
||||
contentDescription = stringResource(R.string.restart),
|
||||
modifier = Modifier.size(32.dp)
|
||||
)
|
||||
}
|
||||
},
|
||||
{ state ->
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
painterResource(R.drawable.restart),
|
||||
stringResource(R.string.restart)
|
||||
)
|
||||
},
|
||||
text = { Text(stringResource(R.string.restart)) },
|
||||
onClick = {
|
||||
onAction(TimerAction.ResetTimer)
|
||||
state.dismiss()
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
{ state ->
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
)
|
||||
|
||||
customItem(
|
||||
{
|
||||
FilledTonalIconButton(
|
||||
onClick = {
|
||||
onAction(TimerAction.SkipTimer(fromButton = true))
|
||||
haptic.performHapticFeedback(HapticFeedbackType.VirtualKey)
|
||||
},
|
||||
colors = IconButtonDefaults.filledTonalIconButtonColors(
|
||||
containerColor = colorContainer
|
||||
),
|
||||
shapes = IconButtonDefaults.shapes(),
|
||||
interactionSource = interactionSources[2],
|
||||
modifier = Modifier
|
||||
.size(64.dp, 96.dp)
|
||||
.animateWidth(interactionSources[2])
|
||||
) {
|
||||
Icon(
|
||||
painterResource(R.drawable.skip_next),
|
||||
stringResource(R.string.skip_to_next)
|
||||
painterResource(R.drawable.skip_next_large),
|
||||
contentDescription = stringResource(R.string.skip_to_next),
|
||||
modifier = Modifier.size(32.dp)
|
||||
)
|
||||
},
|
||||
text = { Text(stringResource(R.string.skip_to_next)) },
|
||||
onClick = {
|
||||
onAction(TimerAction.SkipTimer(fromButton = true))
|
||||
state.dismiss()
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
},
|
||||
{ state ->
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
painterResource(R.drawable.skip_next),
|
||||
stringResource(R.string.skip_to_next)
|
||||
)
|
||||
},
|
||||
text = { Text(stringResource(R.string.skip_to_next)) },
|
||||
onClick = {
|
||||
onAction(TimerAction.SkipTimer(fromButton = true))
|
||||
state.dismiss()
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(32.dp))
|
||||
item { Spacer(Modifier.height(32.dp)) }
|
||||
|
||||
Column(horizontalAlignment = CenterHorizontally) {
|
||||
Text(stringResource(R.string.up_next), style = typography.titleSmall)
|
||||
AnimatedContent(
|
||||
timerState.nextTimeStr,
|
||||
transitionSpec = {
|
||||
slideInVertically(
|
||||
animationSpec = motionScheme.defaultSpatialSpec(),
|
||||
initialOffsetY = { (-it * 1.25).toInt() }
|
||||
).togetherWith(
|
||||
slideOutVertically(
|
||||
item {
|
||||
Column(horizontalAlignment = CenterHorizontally) {
|
||||
Text(stringResource(R.string.up_next), style = typography.titleSmall)
|
||||
AnimatedContent(
|
||||
timerState.nextTimeStr,
|
||||
transitionSpec = {
|
||||
slideInVertically(
|
||||
animationSpec = motionScheme.defaultSpatialSpec(),
|
||||
targetOffsetY = { (it * 1.25).toInt() }
|
||||
initialOffsetY = { (-it * 1.25).toInt() }
|
||||
).togetherWith(
|
||||
slideOutVertically(
|
||||
animationSpec = motionScheme.defaultSpatialSpec(),
|
||||
targetOffsetY = { (it * 1.25).toInt() }
|
||||
)
|
||||
)
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
it,
|
||||
style = TextStyle(
|
||||
fontFamily = googleFlex600,
|
||||
fontSize = 22.sp,
|
||||
lineHeight = 28.sp,
|
||||
color = if (timerState.nextTimerMode == TimerMode.FOCUS) colorScheme.primary else colorScheme.tertiary,
|
||||
textAlign = TextAlign.Center
|
||||
),
|
||||
modifier = Modifier.width(200.dp)
|
||||
)
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
it,
|
||||
style = TextStyle(
|
||||
fontFamily = googleFlex600,
|
||||
fontSize = 22.sp,
|
||||
lineHeight = 28.sp,
|
||||
color = if (timerState.nextTimerMode == TimerMode.FOCUS) colorScheme.primary else colorScheme.tertiary,
|
||||
textAlign = TextAlign.Center
|
||||
),
|
||||
modifier = Modifier.width(200.dp)
|
||||
)
|
||||
}
|
||||
AnimatedContent(
|
||||
timerState.nextTimerMode,
|
||||
transitionSpec = {
|
||||
slideInVertically(
|
||||
animationSpec = motionScheme.defaultSpatialSpec(),
|
||||
initialOffsetY = { (-it * 1.25).toInt() }
|
||||
).togetherWith(
|
||||
slideOutVertically(
|
||||
AnimatedContent(
|
||||
timerState.nextTimerMode,
|
||||
transitionSpec = {
|
||||
slideInVertically(
|
||||
animationSpec = motionScheme.defaultSpatialSpec(),
|
||||
targetOffsetY = { (it * 1.25).toInt() }
|
||||
initialOffsetY = { (-it * 1.25).toInt() }
|
||||
).togetherWith(
|
||||
slideOutVertically(
|
||||
animationSpec = motionScheme.defaultSpatialSpec(),
|
||||
targetOffsetY = { (it * 1.25).toInt() }
|
||||
)
|
||||
)
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
when (it) {
|
||||
TimerMode.FOCUS -> stringResource(R.string.focus)
|
||||
TimerMode.SHORT_BREAK -> stringResource(R.string.short_break)
|
||||
else -> stringResource(R.string.long_break)
|
||||
},
|
||||
style = typography.titleMediumEmphasized,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.width(200.dp)
|
||||
)
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
when (it) {
|
||||
TimerMode.FOCUS -> stringResource(R.string.focus)
|
||||
TimerMode.SHORT_BREAK -> stringResource(R.string.short_break)
|
||||
else -> stringResource(R.string.long_break)
|
||||
},
|
||||
style = typography.titleMediumEmphasized,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.width(200.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(16.dp))
|
||||
item { Spacer(Modifier.height(16.dp)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Preview(
|
||||
showSystemUi = true,
|
||||
device = Devices.PIXEL_9_PRO
|
||||
@@ -553,6 +576,7 @@ fun TimerScreenPreview() {
|
||||
TimerScreen(
|
||||
timerState,
|
||||
isPlus = true,
|
||||
contentPadding = PaddingValues(),
|
||||
{ 0.3f },
|
||||
{}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user