feat(wearos): add pause/resume workout, water lock, and swipeable workout pages

This commit is contained in:
2026-08-24 10:24:24 +02:00
parent 31b2a115ec
commit a94ebf3653
18 changed files with 436 additions and 33 deletions
+1 -1
View File
@@ -50,7 +50,7 @@ android {
dependencies { dependencies {
// Wear OS Compose // Wear OS Compose
implementation("androidx.wear.compose:compose-material:1.3.1") implementation("androidx.wear.compose:compose-material:1.3.1")
implementation("androidx.wear.compose:compose-foundation:1.3.1") implementation("androidx.wear.compose:compose-foundation:1.5.0")
implementation("androidx.wear.compose:compose-navigation:1.3.1") implementation("androidx.wear.compose:compose-navigation:1.3.1")
// Compose // Compose
@@ -20,6 +20,7 @@ class SwimBuddyWearActivity : ComponentActivity() {
val completedWorkouts by viewModel.completedWorkouts.collectAsState() val completedWorkouts by viewModel.completedWorkouts.collectAsState()
val selectedWorkout by viewModel.selectedWorkout.collectAsState() val selectedWorkout by viewModel.selectedWorkout.collectAsState()
val isWorkoutActive by viewModel.isWorkoutActive.collectAsState() val isWorkoutActive by viewModel.isWorkoutActive.collectAsState()
val isPaused by viewModel.isPaused.collectAsState()
val currentSetIndex by viewModel.currentSetIndex.collectAsState() val currentSetIndex by viewModel.currentSetIndex.collectAsState()
val currentRep by viewModel.currentRep.collectAsState() val currentRep by viewModel.currentRep.collectAsState()
val elapsedSeconds by viewModel.elapsedSeconds.collectAsState() val elapsedSeconds by viewModel.elapsedSeconds.collectAsState()
@@ -29,12 +30,14 @@ class SwimBuddyWearActivity : ComponentActivity() {
val isResting by viewModel.isResting.collectAsState() val isResting by viewModel.isResting.collectAsState()
val restTimeRemaining by viewModel.restTimeRemaining.collectAsState() val restTimeRemaining by viewModel.restTimeRemaining.collectAsState()
val workoutSummary by viewModel.workoutSummary.collectAsState() val workoutSummary by viewModel.workoutSummary.collectAsState()
val isWaterLockActive by viewModel.isWaterLockActive.collectAsState()
SwimBuddyApp( SwimBuddyApp(
savedWorkouts = savedWorkouts, savedWorkouts = savedWorkouts,
completedWorkouts = completedWorkouts, completedWorkouts = completedWorkouts,
selectedWorkout = selectedWorkout, selectedWorkout = selectedWorkout,
isWorkoutActive = isWorkoutActive, isWorkoutActive = isWorkoutActive,
isPaused = isPaused,
currentSetIndex = currentSetIndex, currentSetIndex = currentSetIndex,
currentRep = currentRep, currentRep = currentRep,
elapsedSeconds = elapsedSeconds, elapsedSeconds = elapsedSeconds,
@@ -47,9 +50,13 @@ class SwimBuddyWearActivity : ComponentActivity() {
totalDistanceSwum = viewModel.totalDistanceSwum(), totalDistanceSwum = viewModel.totalDistanceSwum(),
currentSetDistanceSwum = viewModel.currentSetDistanceSwum(), currentSetDistanceSwum = viewModel.currentSetDistanceSwum(),
currentSetTotalDistance = viewModel.currentSetTotalDistance(), currentSetTotalDistance = viewModel.currentSetTotalDistance(),
isWaterLockActive = isWaterLockActive,
onSelectWorkout = { viewModel.selectWorkout(it) }, onSelectWorkout = { viewModel.selectWorkout(it) },
onStartWorkout = { viewModel.startWorkout() }, onStartWorkout = { viewModel.startWorkout() },
onCompleteLap = { viewModel.completeLap() }, onCompleteLap = { viewModel.completeLap() },
onPauseResume = {
if (isPaused) viewModel.resumeWorkout() else viewModel.pauseWorkout()
},
onEndWorkout = { viewModel.endWorkout() }, onEndWorkout = { viewModel.endWorkout() },
onBack = { viewModel.selectWorkout(null) }, onBack = { viewModel.selectWorkout(null) },
onDeleteSaved = { viewModel.deleteSavedWorkout(it) }, onDeleteSaved = { viewModel.deleteSavedWorkout(it) },
@@ -0,0 +1,19 @@
package nl.guidoit.swimbuddy
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
class WaterLockHelper {
private val _isActive = MutableStateFlow(false)
val isActive: StateFlow<Boolean> = _isActive.asStateFlow()
fun onWorkoutStarted() {
_isActive.value = true
}
fun onWorkoutEnded() {
_isActive.value = false
}
}
@@ -22,6 +22,7 @@ class WorkoutViewModel(application: Application) : AndroidViewModel(application)
private val healthManager = HealthServicesManager(application) private val healthManager = HealthServicesManager(application)
private val storage = WorkoutStorage(application) private val storage = WorkoutStorage(application)
private val waterLockHelper = WaterLockHelper()
private val gson = Gson() private val gson = Gson()
private val appContext = application private val appContext = application
@@ -67,8 +68,14 @@ class WorkoutViewModel(application: Application) : AndroidViewModel(application)
private val _workoutSummary = MutableStateFlow<WorkoutSummary?>(null) private val _workoutSummary = MutableStateFlow<WorkoutSummary?>(null)
val workoutSummary: StateFlow<WorkoutSummary?> = _workoutSummary.asStateFlow() val workoutSummary: StateFlow<WorkoutSummary?> = _workoutSummary.asStateFlow()
private val _isPaused = MutableStateFlow(false)
val isPaused: StateFlow<Boolean> = _isPaused.asStateFlow()
val isWaterLockActive: StateFlow<Boolean> = waterLockHelper.isActive
private var startTime = 0L private var startTime = 0L
private var lapStartTime = 0L private var lapStartTime = 0L
private var pausedElapsedSeconds = 0f
private var lapDetector: LapDetector? = null private var lapDetector: LapDetector? = null
private var currentStrokeCount = 0L private var currentStrokeCount = 0L
@@ -137,8 +144,11 @@ class WorkoutViewModel(application: Application) : AndroidViewModel(application)
viewModelScope.launch { viewModelScope.launch {
healthManager.startSwimmingWorkout(workout.poolLength) healthManager.startSwimmingWorkout(workout.poolLength)
_isWorkoutActive.value = true _isWorkoutActive.value = true
_isPaused.value = false
waterLockHelper.onWorkoutStarted()
startTime = System.currentTimeMillis() startTime = System.currentTimeMillis()
lapStartTime = startTime lapStartTime = startTime
pausedElapsedSeconds = 0f
_currentSetIndex.value = 0 _currentSetIndex.value = 0
_currentRep.value = 0 _currentRep.value = 0
_completedLaps.value = emptyList() _completedLaps.value = emptyList()
@@ -152,6 +162,27 @@ class WorkoutViewModel(application: Application) : AndroidViewModel(application)
} }
} }
fun pauseWorkout() {
if (!_isWorkoutActive.value || _isPaused.value) return
viewModelScope.launch {
healthManager.pauseWorkout()
_isPaused.value = true
pausedElapsedSeconds = _elapsedSeconds.value
HapticFeedback.play(appContext, HapticFeedback.Type.NOTIFICATION)
}
}
fun resumeWorkout() {
if (!_isWorkoutActive.value || !_isPaused.value) return
viewModelScope.launch {
healthManager.resumeWorkout()
_isPaused.value = false
startTime = System.currentTimeMillis() - (pausedElapsedSeconds * 1000).toLong()
lapStartTime = System.currentTimeMillis()
HapticFeedback.play(appContext, HapticFeedback.Type.START)
}
}
private fun startLapDetection() { private fun startLapDetection() {
lapDetector = LapDetector(appContext) { lapDetector = LapDetector(appContext) {
viewModelScope.launch { viewModelScope.launch {
@@ -167,7 +198,9 @@ class WorkoutViewModel(application: Application) : AndroidViewModel(application)
private fun startElapsedTimeTracking() { private fun startElapsedTimeTracking() {
viewModelScope.launch { viewModelScope.launch {
while (_isWorkoutActive.value) { while (_isWorkoutActive.value) {
_elapsedSeconds.value = (System.currentTimeMillis() - startTime) / 1000f if (!_isPaused.value) {
_elapsedSeconds.value = (System.currentTimeMillis() - startTime) / 1000f
}
kotlinx.coroutines.delay(1000) kotlinx.coroutines.delay(1000)
} }
} }
@@ -259,6 +292,8 @@ class WorkoutViewModel(application: Application) : AndroidViewModel(application)
viewModelScope.launch { viewModelScope.launch {
healthManager.endWorkout() healthManager.endWorkout()
_isWorkoutActive.value = false _isWorkoutActive.value = false
_isPaused.value = false
waterLockHelper.onWorkoutEnded()
_currentSetIndex.value = 0 _currentSetIndex.value = 0
_currentRep.value = 0 _currentRep.value = 0
_elapsedSeconds.value = 0f _elapsedSeconds.value = 0f
@@ -151,14 +151,17 @@ fun ActiveWorkoutScreen(
) )
} }
Text( Button(
text = stringResource(R.string.end), onClick = onEndWorkout,
modifier = Modifier modifier = Modifier.fillMaxWidth(),
.fillMaxWidth() colors = ButtonDefaults.buttonColors(
.padding(top = 4.dp), backgroundColor = Color.Red
color = Color.Red, )
style = MaterialTheme.typography.caption3, ) {
textAlign = androidx.compose.ui.text.style.TextAlign.Center Text(
) text = stringResource(R.string.end_workout),
color = Color.White
)
}
} }
} }
@@ -0,0 +1,79 @@
package nl.guidoit.swimbuddy.presentation
import androidx.compose.foundation.layout.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.wear.compose.material.Button
import androidx.wear.compose.material.ButtonDefaults
import androidx.wear.compose.material.MaterialTheme
import androidx.wear.compose.material.Text
import nl.guidoit.swimbuddy.R
@Composable
fun ControlsScreen(
isPaused: Boolean,
isWaterLockActive: Boolean,
onPauseResume: () -> Unit,
onEndWorkout: () -> Unit
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceEvenly
) {
if (isWaterLockActive) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Text(
text = "\uD83D\uDCA7",
fontSize = 12.sp
)
Spacer(modifier = Modifier.width(4.dp))
Text(
text = stringResource(R.string.water_lock),
style = MaterialTheme.typography.caption,
color = Color.Cyan
)
}
}
Spacer(modifier = Modifier.weight(1f))
Button(
onClick = onPauseResume,
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(
backgroundColor = Color(0xFFFF9800)
)
) {
Text(
text = if (isPaused) stringResource(R.string.resume) else stringResource(R.string.pause),
color = Color.Black
)
}
Button(
onClick = onEndWorkout,
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Red
)
) {
Text(
text = stringResource(R.string.end_workout),
color = Color.White
)
}
Spacer(modifier = Modifier.weight(1f))
}
}
@@ -1,6 +1,11 @@
package nl.guidoit.swimbuddy.presentation package nl.guidoit.swimbuddy.presentation
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
import androidx.wear.compose.foundation.pager.HorizontalPager
import androidx.wear.compose.foundation.pager.rememberPagerState
import nl.guidoit.swimbuddy.data.StoredWorkout import nl.guidoit.swimbuddy.data.StoredWorkout
import nl.guidoit.swimbuddy.data.WorkoutSummary import nl.guidoit.swimbuddy.data.WorkoutSummary
@@ -10,6 +15,7 @@ fun SwimBuddyApp(
completedWorkouts: List<StoredWorkout>, completedWorkouts: List<StoredWorkout>,
selectedWorkout: StoredWorkout?, selectedWorkout: StoredWorkout?,
isWorkoutActive: Boolean, isWorkoutActive: Boolean,
isPaused: Boolean,
currentSetIndex: Int, currentSetIndex: Int,
currentRep: Int, currentRep: Int,
elapsedSeconds: Float, elapsedSeconds: Float,
@@ -22,9 +28,11 @@ fun SwimBuddyApp(
totalDistanceSwum: Int, totalDistanceSwum: Int,
currentSetDistanceSwum: Int, currentSetDistanceSwum: Int,
currentSetTotalDistance: Int, currentSetTotalDistance: Int,
isWaterLockActive: Boolean,
onSelectWorkout: (StoredWorkout) -> Unit, onSelectWorkout: (StoredWorkout) -> Unit,
onStartWorkout: () -> Unit, onStartWorkout: () -> Unit,
onCompleteLap: () -> Unit, onCompleteLap: () -> Unit,
onPauseResume: () -> Unit,
onEndWorkout: () -> Unit, onEndWorkout: () -> Unit,
onBack: () -> Unit, onBack: () -> Unit,
onDeleteSaved: (StoredWorkout) -> Unit, onDeleteSaved: (StoredWorkout) -> Unit,
@@ -38,16 +46,8 @@ fun SwimBuddyApp(
onDismiss = onDismissSummary onDismiss = onDismissSummary
) )
} }
isWorkoutActive && isResting -> {
RestScreen(
restTimeRemaining = restTimeRemaining,
totalDistanceSwum = totalDistanceSwum,
calories = calories,
nextSet = selectedWorkout?.sets?.getOrNull(currentSetIndex)
)
}
isWorkoutActive -> { isWorkoutActive -> {
ActiveWorkoutScreen( ActiveWorkoutPager(
workout = selectedWorkout, workout = selectedWorkout,
currentSetIndex = currentSetIndex, currentSetIndex = currentSetIndex,
currentRep = currentRep, currentRep = currentRep,
@@ -55,10 +55,15 @@ fun SwimBuddyApp(
heartRate = heartRate, heartRate = heartRate,
calories = calories, calories = calories,
lapDetected = lapDetected, lapDetected = lapDetected,
isResting = isResting,
restTimeRemaining = restTimeRemaining,
totalDistanceSwum = totalDistanceSwum, totalDistanceSwum = totalDistanceSwum,
currentSetDistanceSwum = currentSetDistanceSwum, currentSetDistanceSwum = currentSetDistanceSwum,
currentSetTotalDistance = currentSetTotalDistance, currentSetTotalDistance = currentSetTotalDistance,
isPaused = isPaused,
isWaterLockActive = isWaterLockActive,
onCompleteLap = onCompleteLap, onCompleteLap = onCompleteLap,
onPauseResume = onPauseResume,
onEndWorkout = onEndWorkout onEndWorkout = onEndWorkout
) )
} }
@@ -80,3 +85,76 @@ fun SwimBuddyApp(
} }
} }
} }
@OptIn(ExperimentalWearFoundationApi::class)
@Composable
private fun ActiveWorkoutPager(
workout: StoredWorkout?,
currentSetIndex: Int,
currentRep: Int,
elapsedSeconds: Float,
heartRate: Int,
calories: Float,
lapDetected: Boolean,
isResting: Boolean,
restTimeRemaining: Float,
totalDistanceSwum: Int,
currentSetDistanceSwum: Int,
currentSetTotalDistance: Int,
isPaused: Boolean,
isWaterLockActive: Boolean,
onCompleteLap: () -> Unit,
onPauseResume: () -> Unit,
onEndWorkout: () -> Unit
) {
val pagerState = rememberPagerState(pageCount = { 3 })
HorizontalPager(
state = pagerState,
modifier = Modifier.fillMaxSize()
) { page ->
when (page) {
0 -> {
if (isResting) {
RestScreen(
restTimeRemaining = restTimeRemaining,
totalDistanceSwum = totalDistanceSwum,
calories = calories,
nextSet = workout?.sets?.getOrNull(currentSetIndex)
)
} else {
ActiveWorkoutScreen(
workout = workout,
currentSetIndex = currentSetIndex,
currentRep = currentRep,
elapsedSeconds = elapsedSeconds,
heartRate = heartRate,
calories = calories,
lapDetected = lapDetected,
totalDistanceSwum = totalDistanceSwum,
currentSetDistanceSwum = currentSetDistanceSwum,
currentSetTotalDistance = currentSetTotalDistance,
onCompleteLap = onCompleteLap,
onEndWorkout = onEndWorkout
)
}
}
1 -> {
WorkoutOverviewScreen(
workout = workout,
currentSetIndex = currentSetIndex,
isResting = isResting,
restTimeRemaining = restTimeRemaining
)
}
2 -> {
ControlsScreen(
isPaused = isPaused,
isWaterLockActive = isWaterLockActive,
onPauseResume = onPauseResume,
onEndWorkout = onEndWorkout
)
}
}
}
}
@@ -7,6 +7,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.wear.compose.material.Button import androidx.wear.compose.material.Button
import androidx.wear.compose.material.ButtonDefaults import androidx.wear.compose.material.ButtonDefaults
import androidx.wear.compose.material.MaterialTheme import androidx.wear.compose.material.MaterialTheme
@@ -55,19 +56,38 @@ fun WorkoutDetailScreen(
} }
items(workout.sets.size) { index -> items(workout.sets.size) { index ->
val set = workout.sets[index] val set = workout.sets[index]
Row( Column {
modifier = Modifier.fillMaxWidth(), Row(
horizontalArrangement = Arrangement.SpaceBetween modifier = Modifier.fillMaxWidth(),
) { horizontalArrangement = Arrangement.SpaceBetween
Text( ) {
text = "${index + 1}. ${set.stroke}", Text(
style = MaterialTheme.typography.body1 text = "${index + 1}. ${set.stroke}",
) style = MaterialTheme.typography.body1
Text( )
text = "${set.repCount}x${set.distancePerRep}m", Text(
style = MaterialTheme.typography.caption3, text = "${set.repCount}x${set.distancePerRep}m",
color = MaterialTheme.colors.onSecondary.copy(alpha = 0.7f) style = MaterialTheme.typography.caption3,
) color = MaterialTheme.colors.onSecondary.copy(alpha = 0.7f)
)
}
if (set.restType != "none" && set.restDuration > 0) {
Row(
modifier = Modifier.padding(start = 14.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "\u23F0",
fontSize = 10.sp
)
Spacer(modifier = Modifier.width(2.dp))
Text(
text = stringResource(R.string.rest_duration, set.restDuration.toInt()),
style = MaterialTheme.typography.caption2,
color = Color(0xFFFF9800)
)
}
}
} }
} }
item { item {
@@ -0,0 +1,108 @@
package nl.guidoit.swimbuddy.presentation
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.wear.compose.material.ListColumn
import androidx.wear.compose.material.MaterialTheme
import androidx.wear.compose.material.Text
import nl.guidoit.swimbuddy.R
import nl.guidoit.swimbuddy.data.StoredWorkout
@Composable
fun WorkoutOverviewScreen(
workout: StoredWorkout?,
currentSetIndex: Int,
isResting: Boolean,
restTimeRemaining: Float
) {
if (workout == null) return
val restMins = (restTimeRemaining / 60).toInt()
val restSecs = (restTimeRemaining % 60).toInt()
ListColumn(
modifier = Modifier.padding(horizontal = 16.dp)
) {
if (isResting) {
item {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "\u23F8",
fontSize = 12.sp
)
Spacer(modifier = Modifier.width(4.dp))
Text(
text = stringResource(R.string.rest) + " " + String.format("%d:%02d", restMins, restSecs),
style = MaterialTheme.typography.caption,
color = Color(0xFFFF9800)
)
}
}
}
items(workout.sets.size) { index ->
val set = workout.sets[index]
val statusColor = when {
index < currentSetIndex -> Color.Green
index == currentSetIndex -> Color.Cyan
else -> Color.Gray.copy(alpha = 0.4f)
}
val textColor = if (index <= currentSetIndex) {
MaterialTheme.colors.onPrimary
} else {
MaterialTheme.colors.onSecondary.copy(alpha = 0.7f)
}
Column {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Canvas(modifier = Modifier.size(8.dp)) {
drawCircle(color = statusColor)
}
Spacer(modifier = Modifier.width(8.dp))
Text(
text = "${index + 1}. ${set.stroke}",
style = MaterialTheme.typography.caption,
color = textColor,
modifier = Modifier.weight(1f)
)
Text(
text = "${set.repCount}x${set.distancePerRep}m",
style = MaterialTheme.typography.caption2,
color = MaterialTheme.colors.onSecondary.copy(alpha = 0.7f)
)
}
if (set.restType != "none" && set.restDuration > 0) {
Row(
modifier = Modifier.padding(start = 22.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "\u23F0",
fontSize = 10.sp
)
Spacer(modifier = Modifier.width(2.dp))
Text(
text = stringResource(R.string.rest_duration, set.restDuration.toInt()),
style = MaterialTheme.typography.caption2,
color = Color(0xFFFF9800)
)
}
}
}
}
}
}
@@ -27,4 +27,10 @@
<string name="laps_label">Omgange</string> <string name="laps_label">Omgange</string>
<string name="tap_to_dismiss">Tryk for at lukke</string> <string name="tap_to_dismiss">Tryk for at lukke</string>
<string name="ok">OK</string> <string name="ok">OK</string>
<string name="water_lock">Water Lock</string>
<string name="pause">Pause</string>
<string name="resume">Genoptag</string>
<string name="end_workout">Afslut træning</string>
<string name="workout_overview">Oversigt</string>
<string name="rest_duration">%ds pause</string>
</resources> </resources>
@@ -27,4 +27,10 @@
<string name="laps_label">Runden</string> <string name="laps_label">Runden</string>
<string name="tap_to_dismiss">Tippen zum Schließen</string> <string name="tap_to_dismiss">Tippen zum Schließen</string>
<string name="ok">OK</string> <string name="ok">OK</string>
<string name="water_lock">Water Lock</string>
<string name="pause">Pause</string>
<string name="resume">Fortsetzen</string>
<string name="end_workout">Training beenden</string>
<string name="workout_overview">Übersicht</string>
<string name="rest_duration">%ds Pause</string>
</resources> </resources>
@@ -27,4 +27,10 @@
<string name="laps_label">Vueltas</string> <string name="laps_label">Vueltas</string>
<string name="tap_to_dismiss">Toca para cerrar</string> <string name="tap_to_dismiss">Toca para cerrar</string>
<string name="ok">OK</string> <string name="ok">OK</string>
<string name="water_lock">Water Lock</string>
<string name="pause">Pausa</string>
<string name="resume">Reanudar</string>
<string name="end_workout">Finalizar entrenamiento</string>
<string name="workout_overview">Resumen</string>
<string name="rest_duration">%ds descanso</string>
</resources> </resources>
@@ -27,4 +27,10 @@
<string name="laps_label">Tours</string> <string name="laps_label">Tours</string>
<string name="tap_to_dismiss">Appuyez pour fermer</string> <string name="tap_to_dismiss">Appuyez pour fermer</string>
<string name="ok">OK</string> <string name="ok">OK</string>
<string name="water_lock">Water Lock</string>
<string name="pause">Pause</string>
<string name="resume">Reprendre</string>
<string name="end_workout">Terminer l\'entraînement</string>
<string name="workout_overview">Aperçu</string>
<string name="rest_duration">%ds repos</string>
</resources> </resources>
@@ -27,4 +27,10 @@
<string name="laps_label">Giri</string> <string name="laps_label">Giri</string>
<string name="tap_to_dismiss">Tocca per chiudere</string> <string name="tap_to_dismiss">Tocca per chiudere</string>
<string name="ok">OK</string> <string name="ok">OK</string>
<string name="water_lock">Water Lock</string>
<string name="pause">Pausa</string>
<string name="resume">Riprendi</string>
<string name="end_workout">Termina allenamento</string>
<string name="workout_overview">Panoramica</string>
<string name="rest_duration">%ds pausa</string>
</resources> </resources>
@@ -27,4 +27,10 @@
<string name="laps_label">Rondes</string> <string name="laps_label">Rondes</string>
<string name="tap_to_dismiss">Tik om te sluiten</string> <string name="tap_to_dismiss">Tik om te sluiten</string>
<string name="ok">OK</string> <string name="ok">OK</string>
<string name="water_lock">Water Lock</string>
<string name="pause">Pauze</string>
<string name="resume">Hervat</string>
<string name="end_workout">Training beëindigen</string>
<string name="workout_overview">Overzicht</string>
<string name="rest_duration">%ds rust</string>
</resources> </resources>
@@ -27,4 +27,10 @@
<string name="laps_label">Okrążenia</string> <string name="laps_label">Okrążenia</string>
<string name="tap_to_dismiss">Stuknij aby zamknąć</string> <string name="tap_to_dismiss">Stuknij aby zamknąć</string>
<string name="ok">OK</string> <string name="ok">OK</string>
<string name="water_lock">Water Lock</string>
<string name="pause">Pauza</string>
<string name="resume">Wznów</string>
<string name="end_workout">Zakończ trening</string>
<string name="workout_overview">Przegląd</string>
<string name="rest_duration">%ds przerwy</string>
</resources> </resources>
@@ -27,4 +27,10 @@
<string name="laps_label">Voltas</string> <string name="laps_label">Voltas</string>
<string name="tap_to_dismiss">Toque para fechar</string> <string name="tap_to_dismiss">Toque para fechar</string>
<string name="ok">OK</string> <string name="ok">OK</string>
<string name="water_lock">Water Lock</string>
<string name="pause">Pausar</string>
<string name="resume">Retomar</string>
<string name="end_workout">Terminar treino</string>
<string name="workout_overview">Resumo</string>
<string name="rest_duration">%ds descanso</string>
</resources> </resources>
@@ -27,4 +27,10 @@
<string name="laps_label">Laps</string> <string name="laps_label">Laps</string>
<string name="tap_to_dismiss">Tap to dismiss</string> <string name="tap_to_dismiss">Tap to dismiss</string>
<string name="ok">OK</string> <string name="ok">OK</string>
<string name="water_lock">Water Lock</string>
<string name="pause">Pause</string>
<string name="resume">Resume</string>
<string name="end_workout">End Workout</string>
<string name="workout_overview">Overview</string>
<string name="rest_duration">%ds rest</string>
</resources> </resources>