feat: Implement a simple bottom navigation bar based UI
for facilitating multiple screens in further releases
This commit is contained in:
@@ -1,7 +1,16 @@
|
|||||||
package org.nsh07.pomodoro.ui
|
package org.nsh07.pomodoro.ui
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.calculateEndPadding
|
||||||
|
import androidx.compose.foundation.layout.calculateStartPadding
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.ShortNavigationBar
|
||||||
|
import androidx.compose.material3.ShortNavigationBarItem
|
||||||
|
import androidx.compose.material3.ShortNavigationBarItemDefaults
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -10,10 +19,13 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.rememberUpdatedState
|
import androidx.compose.runtime.rememberUpdatedState
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.nsh07.pomodoro.R
|
||||||
import org.nsh07.pomodoro.ui.timerScreen.TimerScreen
|
import org.nsh07.pomodoro.ui.timerScreen.TimerScreen
|
||||||
import org.nsh07.pomodoro.ui.viewModel.UiViewModel
|
import org.nsh07.pomodoro.ui.viewModel.UiViewModel
|
||||||
|
|
||||||
@@ -36,13 +48,33 @@ fun AppScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TimerScreen(
|
val layoutDirection = LocalLayoutDirection.current
|
||||||
uiState = uiState,
|
|
||||||
showBrandTitle = showBrandTitle,
|
Scaffold(
|
||||||
progress = { progress },
|
bottomBar = {
|
||||||
resetTimer = viewModel::resetTimer,
|
ShortNavigationBar {
|
||||||
skipTimer = viewModel::skipTimer,
|
ShortNavigationBarItem(
|
||||||
toggleTimer = viewModel::toggleTimer,
|
selected = true,
|
||||||
modifier = modifier
|
onClick = { },
|
||||||
)
|
icon = { Icon(painterResource(R.drawable.hourglass_filled), null) },
|
||||||
|
label = { Text("Timer") },
|
||||||
|
colors = ShortNavigationBarItemDefaults.colors()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) { contentPadding ->
|
||||||
|
TimerScreen(
|
||||||
|
uiState = uiState,
|
||||||
|
showBrandTitle = showBrandTitle,
|
||||||
|
progress = { progress },
|
||||||
|
resetTimer = viewModel::resetTimer,
|
||||||
|
skipTimer = viewModel::skipTimer,
|
||||||
|
toggleTimer = viewModel::toggleTimer,
|
||||||
|
modifier = modifier.padding(
|
||||||
|
start = contentPadding.calculateStartPadding(layoutDirection),
|
||||||
|
end = contentPadding.calculateEndPadding(layoutDirection),
|
||||||
|
bottom = contentPadding.calculateBottomPadding()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,6 @@ import androidx.compose.material3.IconButtonDefaults
|
|||||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||||
import androidx.compose.material3.MaterialTheme.motionScheme
|
import androidx.compose.material3.MaterialTheme.motionScheme
|
||||||
import androidx.compose.material3.MaterialTheme.typography
|
import androidx.compose.material3.MaterialTheme.typography
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -94,89 +93,84 @@ fun TimerScreen(
|
|||||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
Column(modifier = modifier) {
|
||||||
topBar = {
|
TopAppBar(
|
||||||
TopAppBar(
|
title = {
|
||||||
title = {
|
AnimatedContent(
|
||||||
AnimatedContent(
|
if (!showBrandTitle) uiState.timerMode else TimerMode.BRAND,
|
||||||
if (!showBrandTitle) uiState.timerMode else TimerMode.BRAND,
|
transitionSpec = {
|
||||||
transitionSpec = {
|
slideInVertically(
|
||||||
slideInVertically(
|
animationSpec = motionScheme.slowSpatialSpec(),
|
||||||
|
initialOffsetY = { (-it * 1.25).toInt() }
|
||||||
|
).togetherWith(
|
||||||
|
slideOutVertically(
|
||||||
animationSpec = motionScheme.slowSpatialSpec(),
|
animationSpec = motionScheme.slowSpatialSpec(),
|
||||||
initialOffsetY = { (-it * 1.25).toInt() }
|
targetOffsetY = { (it * 1.25).toInt() }
|
||||||
).togetherWith(
|
|
||||||
slideOutVertically(
|
|
||||||
animationSpec = motionScheme.slowSpatialSpec(),
|
|
||||||
targetOffsetY = { (it * 1.25).toInt() }
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
) {
|
|
||||||
when (it) {
|
|
||||||
TimerMode.BRAND ->
|
|
||||||
Text(
|
|
||||||
"Tomato",
|
|
||||||
style = TextStyle(
|
|
||||||
fontFamily = robotoFlexTitle,
|
|
||||||
fontSize = 32.sp,
|
|
||||||
lineHeight = 32.sp,
|
|
||||||
color = colorScheme.onErrorContainer
|
|
||||||
),
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
modifier = Modifier.width(210.dp)
|
|
||||||
)
|
|
||||||
|
|
||||||
TimerMode.FOCUS ->
|
|
||||||
Text(
|
|
||||||
"Focus",
|
|
||||||
style = TextStyle(
|
|
||||||
fontFamily = robotoFlexTitle,
|
|
||||||
fontSize = 32.sp,
|
|
||||||
lineHeight = 32.sp,
|
|
||||||
color = colorScheme.onPrimaryContainer
|
|
||||||
),
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
modifier = Modifier.width(210.dp)
|
|
||||||
)
|
|
||||||
|
|
||||||
TimerMode.SHORT_BREAK -> Text(
|
|
||||||
"Short Break",
|
|
||||||
style = TextStyle(
|
|
||||||
fontFamily = robotoFlexTitle,
|
|
||||||
fontSize = 32.sp,
|
|
||||||
lineHeight = 32.sp,
|
|
||||||
color = colorScheme.onTertiaryContainer
|
|
||||||
),
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
modifier = Modifier.width(210.dp)
|
|
||||||
)
|
|
||||||
|
|
||||||
TimerMode.LONG_BREAK -> Text(
|
|
||||||
"Long Break",
|
|
||||||
style = TextStyle(
|
|
||||||
fontFamily = robotoFlexTitle,
|
|
||||||
fontSize = 32.sp,
|
|
||||||
lineHeight = 32.sp,
|
|
||||||
color = colorScheme.onTertiaryContainer
|
|
||||||
),
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
modifier = Modifier.width(210.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
) {
|
||||||
subtitle = {},
|
when (it) {
|
||||||
titleHorizontalAlignment = Alignment.CenterHorizontally
|
TimerMode.BRAND ->
|
||||||
)
|
Text(
|
||||||
},
|
"Tomato",
|
||||||
modifier = modifier.fillMaxSize()
|
style = TextStyle(
|
||||||
) { insets ->
|
fontFamily = robotoFlexTitle,
|
||||||
|
fontSize = 32.sp,
|
||||||
|
lineHeight = 32.sp,
|
||||||
|
color = colorScheme.onErrorContainer
|
||||||
|
),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.width(210.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
TimerMode.FOCUS ->
|
||||||
|
Text(
|
||||||
|
"Focus",
|
||||||
|
style = TextStyle(
|
||||||
|
fontFamily = robotoFlexTitle,
|
||||||
|
fontSize = 32.sp,
|
||||||
|
lineHeight = 32.sp,
|
||||||
|
color = colorScheme.onPrimaryContainer
|
||||||
|
),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.width(210.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
TimerMode.SHORT_BREAK -> Text(
|
||||||
|
"Short Break",
|
||||||
|
style = TextStyle(
|
||||||
|
fontFamily = robotoFlexTitle,
|
||||||
|
fontSize = 32.sp,
|
||||||
|
lineHeight = 32.sp,
|
||||||
|
color = colorScheme.onTertiaryContainer
|
||||||
|
),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.width(210.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
TimerMode.LONG_BREAK -> Text(
|
||||||
|
"Long Break",
|
||||||
|
style = TextStyle(
|
||||||
|
fontFamily = robotoFlexTitle,
|
||||||
|
fontSize = 32.sp,
|
||||||
|
lineHeight = 32.sp,
|
||||||
|
color = colorScheme.onTertiaryContainer
|
||||||
|
),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.width(210.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
subtitle = {},
|
||||||
|
titleHorizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
)
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
verticalArrangement = Arrangement.Center,
|
verticalArrangement = Arrangement.Center,
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxSize()
|
||||||
.fillMaxSize()
|
|
||||||
.padding(insets)
|
|
||||||
) {
|
) {
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
Box(contentAlignment = Alignment.Center) {
|
Box(contentAlignment = Alignment.Center) {
|
||||||
@@ -423,6 +417,13 @@ fun TimerScreenPreview() {
|
|||||||
timeStr = "03:34", nextTimeStr = "5:00", timerMode = TimerMode.FOCUS, timerRunning = true
|
timeStr = "03:34", nextTimeStr = "5:00", timerMode = TimerMode.FOCUS, timerRunning = true
|
||||||
)
|
)
|
||||||
TomatoTheme {
|
TomatoTheme {
|
||||||
TimerScreen(uiState, false, { 0.3f }, {}, {}, {})
|
TimerScreen(
|
||||||
|
uiState,
|
||||||
|
false,
|
||||||
|
{ 0.3f },
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
app/src/main/res/drawable/hourglass.xml
Normal file
10
app/src/main/res/drawable/hourglass.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="#000000"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M320,800h320v-120q0,-66 -47,-113t-113,-47q-66,0 -113,47t-47,113v120ZM480,440q66,0 113,-47t47,-113v-120L320,160v120q0,66 47,113t113,47ZM200,880q-17,0 -28.5,-11.5T160,840q0,-17 11.5,-28.5T200,800h40v-120q0,-61 28.5,-114.5T348,480q-51,-32 -79.5,-85.5T240,280v-120h-40q-17,0 -28.5,-11.5T160,120q0,-17 11.5,-28.5T200,80h560q17,0 28.5,11.5T800,120q0,17 -11.5,28.5T760,160h-40v120q0,61 -28.5,114.5T612,480q51,32 79.5,85.5T720,680v120h40q17,0 28.5,11.5T800,840q0,17 -11.5,28.5T760,880L200,880ZM480,800ZM480,160Z" />
|
||||||
|
</vector>
|
||||||
10
app/src/main/res/drawable/hourglass_filled.xml
Normal file
10
app/src/main/res/drawable/hourglass_filled.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="#000000"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M200,880q-17,0 -28.5,-11.5T160,840q0,-17 11.5,-28.5T200,800h40v-120q0,-61 28.5,-114.5T348,480q-51,-32 -79.5,-85.5T240,280v-120h-40q-17,0 -28.5,-11.5T160,120q0,-17 11.5,-28.5T200,80h560q17,0 28.5,11.5T800,120q0,17 -11.5,28.5T760,160h-40v120q0,61 -28.5,114.5T612,480q51,32 79.5,85.5T720,680v120h40q17,0 28.5,11.5T800,840q0,17 -11.5,28.5T760,880L200,880Z" />
|
||||||
|
</vector>
|
||||||
10
app/src/main/res/drawable/more_vert.xml
Normal file
10
app/src/main/res/drawable/more_vert.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="#000000"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M480,800q-33,0 -56.5,-23.5T400,720q0,-33 23.5,-56.5T480,640q33,0 56.5,23.5T560,720q0,33 -23.5,56.5T480,800ZM480,560q-33,0 -56.5,-23.5T400,480q0,-33 23.5,-56.5T480,400q33,0 56.5,23.5T560,480q0,33 -23.5,56.5T480,560ZM480,320q-33,0 -56.5,-23.5T400,240q0,-33 23.5,-56.5T480,160q33,0 56.5,23.5T560,240q0,33 -23.5,56.5T480,320Z" />
|
||||||
|
</vector>
|
||||||
13
app/src/main/res/drawable/settings.xml
Normal file
13
app/src/main/res/drawable/settings.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:autoMirrored="true"
|
||||||
|
android:tint="#000000"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M433,880q-27,0 -46.5,-18T363,818l-9,-66q-13,-5 -24.5,-12T307,725l-62,26q-25,11 -50,2t-39,-32l-47,-82q-14,-23 -8,-49t27,-43l53,-40q-1,-7 -1,-13.5v-27q0,-6.5 1,-13.5l-53,-40q-21,-17 -27,-43t8,-49l47,-82q14,-23 39,-32t50,2l62,26q11,-8 23,-15t24,-12l9,-66q4,-26 23.5,-44t46.5,-18h94q27,0 46.5,18t23.5,44l9,66q13,5 24.5,12t22.5,15l62,-26q25,-11 50,-2t39,32l47,82q14,23 8,49t-27,43l-53,40q1,7 1,13.5v27q0,6.5 -2,13.5l53,40q21,17 27,43t-8,49l-48,82q-14,23 -39,32t-50,-2l-60,-26q-11,8 -23,15t-24,12l-9,66q-4,26 -23.5,44T527,880h-94ZM482,620q58,0 99,-41t41,-99q0,-58 -41,-99t-99,-41q-59,0 -99.5,41T342,480q0,58 40.5,99t99.5,41Z" />
|
||||||
|
|
||||||
|
</vector>
|
||||||
Reference in New Issue
Block a user