feat(ui): implement a simple click mechanism to switch to AOD
This commit is contained in:
@@ -51,7 +51,7 @@ class MainActivity : ComponentActivity() {
|
||||
appContainer.appTimerRepository.colorScheme = colorScheme
|
||||
}
|
||||
|
||||
AppScreen(timerViewModel = timerViewModel)
|
||||
AppScreen(timerViewModel = timerViewModel, isAODEnabled = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,13 +8,17 @@
|
||||
package org.nsh07.pomodoro.ui
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.ContentTransform
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.scaleOut
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.calculateEndPadding
|
||||
import androidx.compose.foundation.layout.calculateStartPadding
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
@@ -25,13 +29,17 @@ import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.ShortNavigationBar
|
||||
import androidx.compose.material3.ShortNavigationBarArrangement
|
||||
import androidx.compose.material3.ShortNavigationBarItem
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
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.rememberUpdatedState
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.painterResource
|
||||
@@ -55,7 +63,8 @@ import org.nsh07.pomodoro.ui.timerScreen.viewModel.TimerViewModel
|
||||
@Composable
|
||||
fun AppScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
timerViewModel: TimerViewModel = viewModel(factory = TimerViewModel.Factory)
|
||||
timerViewModel: TimerViewModel = viewModel(factory = TimerViewModel.Factory),
|
||||
isAODEnabled: Boolean
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
@@ -69,6 +78,7 @@ fun AppScreen(
|
||||
val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass
|
||||
|
||||
val backStack = rememberNavBackStack(Screen.Timer)
|
||||
var showAOD by remember { mutableStateOf(false) }
|
||||
|
||||
if (uiState.alarmRinging)
|
||||
AlarmDialog {
|
||||
@@ -78,128 +88,147 @@ fun AppScreen(
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
bottomBar = {
|
||||
val wide = remember {
|
||||
windowSizeClass.isWidthAtLeastBreakpoint(
|
||||
WindowSizeClass.WIDTH_DP_MEDIUM_LOWER_BOUND
|
||||
AnimatedContent(
|
||||
showAOD,
|
||||
transitionSpec = { fadeIn().togetherWith(fadeOut()) }
|
||||
) { aod ->
|
||||
if (!aod) {
|
||||
Scaffold(
|
||||
bottomBar = {
|
||||
val wide = remember {
|
||||
windowSizeClass.isWidthAtLeastBreakpoint(
|
||||
WindowSizeClass.WIDTH_DP_MEDIUM_LOWER_BOUND
|
||||
)
|
||||
}
|
||||
ShortNavigationBar(
|
||||
arrangement =
|
||||
if (wide) ShortNavigationBarArrangement.Centered
|
||||
else ShortNavigationBarArrangement.EqualWeight
|
||||
) {
|
||||
screens.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
|
||||
}
|
||||
} 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
|
||||
.then(
|
||||
if (isAODEnabled) Modifier.clickable { showAOD = !showAOD }
|
||||
else Modifier
|
||||
)
|
||||
) { contentPadding ->
|
||||
NavDisplay(
|
||||
backStack = backStack,
|
||||
onBack = { backStack.removeLastOrNull() },
|
||||
transitionSpec = {
|
||||
ContentTransform(
|
||||
fadeIn(motionScheme.defaultEffectsSpec()),
|
||||
fadeOut(motionScheme.defaultEffectsSpec())
|
||||
)
|
||||
},
|
||||
popTransitionSpec = {
|
||||
ContentTransform(
|
||||
fadeIn(motionScheme.defaultEffectsSpec()),
|
||||
fadeOut(motionScheme.defaultEffectsSpec())
|
||||
)
|
||||
},
|
||||
predictivePopTransitionSpec = {
|
||||
ContentTransform(
|
||||
fadeIn(motionScheme.defaultEffectsSpec()),
|
||||
fadeOut(motionScheme.defaultEffectsSpec()) +
|
||||
scaleOut(targetScale = 0.7f),
|
||||
)
|
||||
},
|
||||
entryProvider = entryProvider {
|
||||
entry<Screen.Timer> {
|
||||
TimerScreen(
|
||||
timerState = uiState,
|
||||
progress = { progress },
|
||||
onAction = { action ->
|
||||
when (action) {
|
||||
TimerAction.ResetTimer ->
|
||||
Intent(context, TimerService::class.java).also {
|
||||
it.action = TimerService.Actions.RESET.toString()
|
||||
context.startService(it)
|
||||
}
|
||||
|
||||
is TimerAction.SkipTimer ->
|
||||
Intent(context, TimerService::class.java).also {
|
||||
it.action = TimerService.Actions.SKIP.toString()
|
||||
context.startService(it)
|
||||
}
|
||||
|
||||
TimerAction.StopAlarm ->
|
||||
Intent(context, TimerService::class.java).also {
|
||||
it.action =
|
||||
TimerService.Actions.STOP_ALARM.toString()
|
||||
context.startService(it)
|
||||
}
|
||||
|
||||
TimerAction.ToggleTimer ->
|
||||
Intent(context, TimerService::class.java).also {
|
||||
it.action = TimerService.Actions.TOGGLE.toString()
|
||||
context.startService(it)
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = modifier.padding(
|
||||
start = contentPadding.calculateStartPadding(layoutDirection),
|
||||
end = contentPadding.calculateEndPadding(layoutDirection),
|
||||
bottom = contentPadding.calculateBottomPadding()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
entry<Screen.Settings> {
|
||||
SettingsScreenRoot(
|
||||
modifier = modifier.padding(
|
||||
start = contentPadding.calculateStartPadding(layoutDirection),
|
||||
end = contentPadding.calculateEndPadding(layoutDirection),
|
||||
bottom = contentPadding.calculateBottomPadding()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
entry<Screen.Stats> {
|
||||
StatsScreenRoot(
|
||||
contentPadding = contentPadding,
|
||||
modifier = modifier.padding(
|
||||
start = contentPadding.calculateStartPadding(layoutDirection),
|
||||
end = contentPadding.calculateEndPadding(layoutDirection),
|
||||
bottom = contentPadding.calculateBottomPadding()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
ShortNavigationBar(
|
||||
arrangement =
|
||||
if (wide) ShortNavigationBarArrangement.Centered
|
||||
else ShortNavigationBarArrangement.EqualWeight
|
||||
) {
|
||||
screens.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
|
||||
}
|
||||
} 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)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Surface(
|
||||
color = Color.Black,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.clickable { showAOD = !showAOD }) {}
|
||||
}
|
||||
) { contentPadding ->
|
||||
NavDisplay(
|
||||
backStack = backStack,
|
||||
onBack = { backStack.removeLastOrNull() },
|
||||
transitionSpec = {
|
||||
ContentTransform(
|
||||
fadeIn(motionScheme.defaultEffectsSpec()),
|
||||
fadeOut(motionScheme.defaultEffectsSpec())
|
||||
)
|
||||
},
|
||||
popTransitionSpec = {
|
||||
ContentTransform(
|
||||
fadeIn(motionScheme.defaultEffectsSpec()),
|
||||
fadeOut(motionScheme.defaultEffectsSpec())
|
||||
)
|
||||
},
|
||||
predictivePopTransitionSpec = {
|
||||
ContentTransform(
|
||||
fadeIn(motionScheme.defaultEffectsSpec()),
|
||||
fadeOut(motionScheme.defaultEffectsSpec()) +
|
||||
scaleOut(targetScale = 0.7f),
|
||||
)
|
||||
},
|
||||
entryProvider = entryProvider {
|
||||
entry<Screen.Timer> {
|
||||
TimerScreen(
|
||||
timerState = uiState,
|
||||
progress = { progress },
|
||||
onAction = { action ->
|
||||
when (action) {
|
||||
TimerAction.ResetTimer ->
|
||||
Intent(context, TimerService::class.java).also {
|
||||
it.action = TimerService.Actions.RESET.toString()
|
||||
context.startService(it)
|
||||
}
|
||||
|
||||
is TimerAction.SkipTimer ->
|
||||
Intent(context, TimerService::class.java).also {
|
||||
it.action = TimerService.Actions.SKIP.toString()
|
||||
context.startService(it)
|
||||
}
|
||||
|
||||
TimerAction.StopAlarm ->
|
||||
Intent(context, TimerService::class.java).also {
|
||||
it.action = TimerService.Actions.STOP_ALARM.toString()
|
||||
context.startService(it)
|
||||
}
|
||||
|
||||
TimerAction.ToggleTimer ->
|
||||
Intent(context, TimerService::class.java).also {
|
||||
it.action = TimerService.Actions.TOGGLE.toString()
|
||||
context.startService(it)
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = modifier.padding(
|
||||
start = contentPadding.calculateStartPadding(layoutDirection),
|
||||
end = contentPadding.calculateEndPadding(layoutDirection),
|
||||
bottom = contentPadding.calculateBottomPadding()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
entry<Screen.Settings> {
|
||||
SettingsScreenRoot(
|
||||
modifier = modifier.padding(
|
||||
start = contentPadding.calculateStartPadding(layoutDirection),
|
||||
end = contentPadding.calculateEndPadding(layoutDirection),
|
||||
bottom = contentPadding.calculateBottomPadding()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
entry<Screen.Stats> {
|
||||
StatsScreenRoot(
|
||||
contentPadding = contentPadding,
|
||||
modifier = modifier.padding(
|
||||
start = contentPadding.calculateStartPadding(layoutDirection),
|
||||
end = contentPadding.calculateEndPadding(layoutDirection),
|
||||
bottom = contentPadding.calculateBottomPadding()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user