Add dummy start exercise button and dummy screen

This commit is contained in:
jonasgaudian
2026-02-16 13:52:02 +01:00
parent ef90df2150
commit 5ae96d1f5c
4 changed files with 189 additions and 94 deletions

View File

@@ -285,6 +285,16 @@ fun TranslatorApp(
restoreState = false restoreState = false
} }
} }
},
onPlayClicked = {
navController.navigate("start_exercise") {
popUpTo(0) {
inclusive = true
saveState = false
}
launchSingleTop = true
restoreState = false
}
} }
) )
}, },

View File

@@ -26,6 +26,7 @@ import eu.gaudian.translator.view.dictionary.EtymologyResultScreen
import eu.gaudian.translator.view.dictionary.MainDictionaryScreen import eu.gaudian.translator.view.dictionary.MainDictionaryScreen
import eu.gaudian.translator.view.exercises.ExerciseSessionScreen import eu.gaudian.translator.view.exercises.ExerciseSessionScreen
import eu.gaudian.translator.view.exercises.MainExerciseScreen import eu.gaudian.translator.view.exercises.MainExerciseScreen
import eu.gaudian.translator.view.exercises.StartExerciseScreen
import eu.gaudian.translator.view.exercises.YouTubeBrowserScreen import eu.gaudian.translator.view.exercises.YouTubeBrowserScreen
import eu.gaudian.translator.view.exercises.YouTubeExerciseScreen import eu.gaudian.translator.view.exercises.YouTubeExerciseScreen
import eu.gaudian.translator.view.home.HomeScreen import eu.gaudian.translator.view.home.HomeScreen
@@ -131,6 +132,10 @@ fun AppNavHost(
StatsScreen(navController = navController) StatsScreen(navController = navController)
} }
composable("start_exercise") {
StartExerciseScreen(navController = navController)
}
// Define all other navigation graphs at the same top level. // Define all other navigation graphs at the same top level.
homeGraph(navController) homeGraph(navController)
translationGraph(navController) translationGraph(navController)

View File

@@ -21,8 +21,13 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@@ -40,7 +45,10 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.scale import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
@@ -106,6 +114,7 @@ fun BottomNavigationBar(
showLabels: Boolean, showLabels: Boolean,
onItemSelected: (Screen) -> Unit, onItemSelected: (Screen) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onPlayClicked: () -> Unit = {}
) { ) {
val showExperimental = LocalShowExperimentalFeatures.current val showExperimental = LocalShowExperimentalFeatures.current
val screens = remember(showExperimental) { Screen.getAllScreens(showExperimental) } val screens = remember(showExperimental) { Screen.getAllScreens(showExperimental) }
@@ -115,14 +124,22 @@ fun BottomNavigationBar(
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
// Configuration for the play button
val playButtonSize = 56.dp
val glowPadding = 32.dp // Total extra space for the glow (16dp on each side)
// This dictates how far up the button shifts.
// Setting it to around half the button size centers it on the top border.
val upwardOffset = 28.dp
AnimatedVisibility( AnimatedVisibility(
visible = isVisible, visible = isVisible,
enter = slideInVertically( enter = slideInVertically(
animationSpec = androidx.compose.animation.core.tween(durationMillis = 220), animationSpec = spring(stiffness = Spring.StiffnessMediumLow),
initialOffsetY = { it } initialOffsetY = { it }
), ),
exit = slideOutVertically( exit = slideOutVertically(
animationSpec = androidx.compose.animation.core.tween(durationMillis = 220), animationSpec = spring(stiffness = Spring.StiffnessMediumLow),
targetOffsetY = { it } targetOffsetY = { it }
) )
) { ) {
@@ -131,13 +148,49 @@ fun BottomNavigationBar(
val navBarDp = with(density) { WindowInsets.navigationBars.getBottom(this).toDp() } val navBarDp = with(density) { WindowInsets.navigationBars.getBottom(this).toDp() }
val height = baseHeight + navBarDp val height = baseHeight + navBarDp
// Outer Box height is purely determined by the NavigationBar now
Box(
modifier = modifier.fillMaxWidth(),
contentAlignment = Alignment.TopCenter
) {
// The actual Navigation Bar
NavigationBar( NavigationBar(
modifier = modifier.height(height), modifier = Modifier.height(height),
containerColor = MaterialTheme.colorScheme.surface, containerColor = MaterialTheme.colorScheme.surface,
tonalElevation = 8.dp, tonalElevation = 8.dp,
) { ) {
screens.forEach { screen -> // Create a list of 5 items (2 left, 1 empty spacer, 2 right)
val isSelected = screen == selectedItem val allNavItems = buildList {
addAll(screens.take(2))
add(null) // Empty spacer for Play Button gap
if (screens.size > 2) {
addAll(screens.drop(2))
}
add(moreScreen)
}
allNavItems.forEach { screen ->
if (screen == null) {
// Dummy item to create the gap
NavigationBarItem(
selected = false,
onClick = {},
enabled = false, // Disables ripples and clicks
icon = { Spacer(modifier = Modifier.size(24.dp)) },
label = if (showLabels) { { Spacer(modifier = Modifier.size(10.dp)) } } else null,
colors = NavigationBarItemDefaults.colors(
disabledIconColor = Color.Transparent,
disabledTextColor = Color.Transparent
)
)
} else {
// Regular or More items
val isSelected = if (screen == Screen.More) {
selectedItem is Screen.More || Screen.getMoreMenuItems(showExperimental).contains(selectedItem)
} else {
screen == selectedItem
}
val title = stringResource(id = screen.title) val title = stringResource(id = screen.title)
val scale by animateFloatAsState( val scale by animateFloatAsState(
@@ -153,7 +206,7 @@ fun BottomNavigationBar(
selected = isSelected, selected = isSelected,
onClick = { onClick = {
haptic.performHapticFeedback(HapticFeedbackType.LongPress) haptic.performHapticFeedback(HapticFeedbackType.LongPress)
onItemSelected(screen) if (screen == Screen.More) showMoreMenu = true else onItemSelected(screen)
}, },
label = if (showLabels) { label = if (showLabels) {
{ {
@@ -184,56 +237,57 @@ fun BottomNavigationBar(
) )
) )
} }
}
}
// More menu item // The Glowing Play Button
val moreSelected = selectedItem is Screen.More || Box(
Screen.getMoreMenuItems(showExperimental).contains(selectedItem) modifier = Modifier
val moreTitle = stringResource(R.string.label_more) // This negative offset pulls the button UP out of the bounding box
val moreScale by animateFloatAsState( // without increasing the layout height of the parent Box.
targetValue = if (moreSelected) 1.2f else 1.0f, .offset(y = -upwardOffset)
animationSpec = spring( .size(playButtonSize + glowPadding),
dampingRatio = Spring.DampingRatioMediumBouncy, contentAlignment = Alignment.Center
stiffness = Spring.StiffnessLow ) {
// Background radial glow
Box(
modifier = Modifier
.matchParentSize()
.background(
brush = Brush.radialGradient(
colors = listOf(
Color(0xFF3B82F6).copy(alpha = 0.5f),
Color.Transparent
)
), ),
label = "moreIconScale" shape = CircleShape
)
) )
NavigationBarItem( // Actual clickable button
selected = moreSelected, Box(
onClick = { modifier = Modifier
.size(playButtonSize)
.clip(CircleShape)
.background(Color(0xFF3B82F6))
.clickable {
haptic.performHapticFeedback(HapticFeedbackType.LongPress) haptic.performHapticFeedback(HapticFeedbackType.LongPress)
showMoreMenu = true onPlayClicked()
}, },
label = if (showLabels) { contentAlignment = Alignment.Center
{ ) {
Text( Icon(
text = moreTitle, imageVector = Icons.Filled.PlayArrow,
maxLines = 1, contentDescription = "Play",
fontSize = 10.sp, tint = Color.White,
fontWeight = if (moreSelected) FontWeight.Bold else FontWeight.Normal, modifier = Modifier.size(32.dp)
color = if(moreSelected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant
) )
} }
} else null, }
icon = {
Icon(
imageVector = moreScreen.selectedIcon,
contentDescription = moreTitle,
modifier = Modifier.scale(moreScale)
)
},
colors = NavigationBarItemDefaults.colors(
indicatorColor = MaterialTheme.colorScheme.primaryContainer,
selectedIconColor = MaterialTheme.colorScheme.onPrimaryContainer,
selectedTextColor = MaterialTheme.colorScheme.primary,
unselectedIconColor = MaterialTheme.colorScheme.onSurfaceVariant,
unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant
)
)
} }
} }
// Modal Bottom Sheet for More menu // Modal Bottom Sheet for More menu (Remains exactly the same)
if (showMoreMenu) { if (showMoreMenu) {
ModalBottomSheet( ModalBottomSheet(
onDismissRequest = { showMoreMenu = false }, onDismissRequest = { showMoreMenu = false },

View File

@@ -0,0 +1,26 @@
package eu.gaudian.translator.view.exercises
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
@Composable
fun StartExerciseScreen(
navController: NavHostController,
modifier: Modifier = Modifier
) {
Box(
modifier = modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
text = "Start Exercise Screen",
style = MaterialTheme.typography.headlineMedium
)
}
}