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
|
||||
|
||||
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.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.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -10,10 +19,13 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.nsh07.pomodoro.R
|
||||
import org.nsh07.pomodoro.ui.timerScreen.TimerScreen
|
||||
import org.nsh07.pomodoro.ui.viewModel.UiViewModel
|
||||
|
||||
@@ -36,13 +48,33 @@ fun AppScreen(
|
||||
}
|
||||
}
|
||||
|
||||
TimerScreen(
|
||||
uiState = uiState,
|
||||
showBrandTitle = showBrandTitle,
|
||||
progress = { progress },
|
||||
resetTimer = viewModel::resetTimer,
|
||||
skipTimer = viewModel::skipTimer,
|
||||
toggleTimer = viewModel::toggleTimer,
|
||||
modifier = modifier
|
||||
)
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
|
||||
Scaffold(
|
||||
bottomBar = {
|
||||
ShortNavigationBar {
|
||||
ShortNavigationBarItem(
|
||||
selected = true,
|
||||
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.motionScheme
|
||||
import androidx.compose.material3.MaterialTheme.typography
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -94,89 +93,84 @@ fun TimerScreen(
|
||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
AnimatedContent(
|
||||
if (!showBrandTitle) uiState.timerMode else TimerMode.BRAND,
|
||||
transitionSpec = {
|
||||
slideInVertically(
|
||||
Column(modifier = modifier) {
|
||||
TopAppBar(
|
||||
title = {
|
||||
AnimatedContent(
|
||||
if (!showBrandTitle) uiState.timerMode else TimerMode.BRAND,
|
||||
transitionSpec = {
|
||||
slideInVertically(
|
||||
animationSpec = motionScheme.slowSpatialSpec(),
|
||||
initialOffsetY = { (-it * 1.25).toInt() }
|
||||
).togetherWith(
|
||||
slideOutVertically(
|
||||
animationSpec = motionScheme.slowSpatialSpec(),
|
||||
initialOffsetY = { (-it * 1.25).toInt() }
|
||||
).togetherWith(
|
||||
slideOutVertically(
|
||||
animationSpec = motionScheme.slowSpatialSpec(),
|
||||
targetOffsetY = { (it * 1.25).toInt() }
|
||||
)
|
||||
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 = {},
|
||||
titleHorizontalAlignment = Alignment.CenterHorizontally
|
||||
)
|
||||
},
|
||||
modifier = modifier.fillMaxSize()
|
||||
) { insets ->
|
||||
) {
|
||||
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 = {},
|
||||
titleHorizontalAlignment = Alignment.CenterHorizontally
|
||||
)
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(insets)
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
@@ -423,6 +417,13 @@ fun TimerScreenPreview() {
|
||||
timeStr = "03:34", nextTimeStr = "5:00", timerMode = TimerMode.FOCUS, timerRunning = true
|
||||
)
|
||||
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