Final
This commit is contained in:
parent
8450a791d5
commit
d9093ba007
@ -11,6 +11,7 @@ import com.example.workouttimer.ui.screens.RunningWorkoutScreen
|
|||||||
import com.example.workouttimer.ui.screens.SavedWorkoutsScreen
|
import com.example.workouttimer.ui.screens.SavedWorkoutsScreen
|
||||||
import com.example.workouttimer.viewmodel.WorkoutViewModel
|
import com.example.workouttimer.viewmodel.WorkoutViewModel
|
||||||
import com.example.workouttimer.ui.screens.WorkoutEditorScreen
|
import com.example.workouttimer.ui.screens.WorkoutEditorScreen
|
||||||
|
import com.example.workouttimer.ui.screens.PreparationCountdownScreen
|
||||||
|
|
||||||
class AppNavigation(private val context: Context) {
|
class AppNavigation(private val context: Context) {
|
||||||
|
|
||||||
@ -31,7 +32,12 @@ class AppNavigation(private val context: Context) {
|
|||||||
setScreen(
|
setScreen(
|
||||||
HomeScreen(
|
HomeScreen(
|
||||||
context = context,
|
context = context,
|
||||||
onShowWorkouts = { showSavedWorkouts() }
|
onShowWorkouts = {
|
||||||
|
showSavedWorkouts()
|
||||||
|
},
|
||||||
|
onCreateWorkout = {
|
||||||
|
showCreateWorkout()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -101,20 +107,32 @@ class AppNavigation(private val context: Context) {
|
|||||||
context = context,
|
context = context,
|
||||||
workout = viewModel.currentWorkout,
|
workout = viewModel.currentWorkout,
|
||||||
onStart = {
|
onStart = {
|
||||||
viewModel.startWorkout()
|
showPreparationCountdown()
|
||||||
showRunningWorkout()
|
|
||||||
},
|
},
|
||||||
onBack = { showSavedWorkouts() }
|
onBack = { showSavedWorkouts() }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun showPreparationCountdown() {
|
||||||
|
setScreen(
|
||||||
|
PreparationCountdownScreen(
|
||||||
|
context = context,
|
||||||
|
onSkip = {
|
||||||
|
viewModel.startWorkout()
|
||||||
|
showRunningWorkout()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun showRunningWorkout() {
|
fun showRunningWorkout() {
|
||||||
setScreen(
|
setScreen(
|
||||||
RunningWorkoutScreen(
|
RunningWorkoutScreen(
|
||||||
context = context,
|
context = context,
|
||||||
workout = viewModel.currentWorkout,
|
workout = viewModel.currentWorkout,
|
||||||
exercise = viewModel.getCurrentExercise(),
|
exercise = viewModel.getCurrentExercise(),
|
||||||
|
nextExercise = viewModel.getNextExercise(),
|
||||||
exerciseNumber = viewModel.currentExerciseIndex + 1,
|
exerciseNumber = viewModel.currentExerciseIndex + 1,
|
||||||
onNextExercise = {
|
onNextExercise = {
|
||||||
viewModel.nextExercise()
|
viewModel.nextExercise()
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import android.widget.Button
|
|||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import com.example.workouttimer.domain.Workout
|
import com.example.workouttimer.domain.Workout
|
||||||
|
import com.example.workouttimer.ui.theme.AppStyle
|
||||||
|
|
||||||
class FinishedScreen(
|
class FinishedScreen(
|
||||||
context: Context,
|
context: Context,
|
||||||
@ -14,34 +15,33 @@ class FinishedScreen(
|
|||||||
) : LinearLayout(context) {
|
) : LinearLayout(context) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
orientation = VERTICAL
|
AppStyle.applyGradientBackground(this)
|
||||||
gravity = Gravity.CENTER
|
gravity = Gravity.CENTER_VERTICAL
|
||||||
setPadding(48, 48, 48, 48)
|
|
||||||
|
|
||||||
val title = TextView(context)
|
val title = TextView(context)
|
||||||
title.text = "Workout beendet!"
|
title.text = "Workout\nDone"
|
||||||
title.textSize = 32f
|
AppStyle.headline(title)
|
||||||
title.gravity = Gravity.CENTER
|
addView(title)
|
||||||
|
|
||||||
val workoutText = TextView(context)
|
val workoutText = TextView(context)
|
||||||
workoutText.text = workout?.name ?: ""
|
workoutText.text = workout?.name ?: "Training abgeschlossen"
|
||||||
workoutText.textSize = 22f
|
AppStyle.title(workoutText)
|
||||||
workoutText.gravity = Gravity.CENTER
|
AppStyle.setTextMargin(workoutText)
|
||||||
|
addView(workoutText)
|
||||||
|
|
||||||
val summary = TextView(context)
|
val summary = TextView(context)
|
||||||
summary.text = "Gute Arbeit. Dein Workout wurde erfolgreich abgeschlossen."
|
summary.text = "Gute Arbeit. Dein Workout wurde erfolgreich abgeschlossen."
|
||||||
summary.textSize = 18f
|
AppStyle.muted(summary)
|
||||||
summary.gravity = Gravity.CENTER
|
AppStyle.setTextMargin(summary)
|
||||||
|
addView(summary)
|
||||||
|
|
||||||
val homeButton = Button(context)
|
val homeButton = Button(context)
|
||||||
homeButton.text = "Zur Startseite"
|
homeButton.text = "Zur Startseite -->"
|
||||||
|
AppStyle.primaryButton(homeButton)
|
||||||
homeButton.setOnClickListener {
|
homeButton.setOnClickListener {
|
||||||
onBackHome()
|
onBackHome()
|
||||||
}
|
}
|
||||||
|
|
||||||
addView(title)
|
|
||||||
addView(workoutText)
|
|
||||||
addView(summary)
|
|
||||||
addView(homeButton)
|
addView(homeButton)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2,38 +2,70 @@ package com.example.workouttimer.ui.screens
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.widget.Button
|
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.Space
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import com.example.workouttimer.ui.theme.AppStyle
|
||||||
|
|
||||||
class HomeScreen(
|
class HomeScreen(
|
||||||
context: Context,
|
context: Context,
|
||||||
onShowWorkouts: () -> Unit
|
onShowWorkouts: () -> Unit,
|
||||||
|
onCreateWorkout: () -> Unit
|
||||||
) : LinearLayout(context) {
|
) : LinearLayout(context) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
orientation = VERTICAL
|
AppStyle.applyGradientBackground(this)
|
||||||
gravity = Gravity.CENTER
|
gravity = Gravity.CENTER_VERTICAL
|
||||||
setPadding(48, 48, 48, 48)
|
|
||||||
|
val topSpace = Space(context)
|
||||||
|
topSpace.layoutParams = LayoutParams(
|
||||||
|
LayoutParams.MATCH_PARENT,
|
||||||
|
AppStyle.dp(this, 90)
|
||||||
|
)
|
||||||
|
|
||||||
val title = TextView(context)
|
val title = TextView(context)
|
||||||
title.text = "Workout Timer"
|
title.text = "Workout Timer"
|
||||||
title.textSize = 32f
|
title.gravity = Gravity.START
|
||||||
title.gravity = Gravity.CENTER
|
AppStyle.headline(title)
|
||||||
|
|
||||||
val subtitle = TextView(context)
|
val subtitle = TextView(context)
|
||||||
subtitle.text = "Plane und starte dein Workout"
|
subtitle.text = "Build your own rhythm."
|
||||||
subtitle.textSize = 18f
|
subtitle.gravity = Gravity.START
|
||||||
subtitle.gravity = Gravity.CENTER
|
AppStyle.muted(subtitle)
|
||||||
|
AppStyle.setTextMargin(subtitle)
|
||||||
|
|
||||||
val button = Button(context)
|
val buttonSpace = Space(context)
|
||||||
button.text = "Gespeicherte Workouts"
|
buttonSpace.layoutParams = LayoutParams(
|
||||||
button.setOnClickListener {
|
LayoutParams.MATCH_PARENT,
|
||||||
onShowWorkouts()
|
AppStyle.dp(this, 32)
|
||||||
}
|
)
|
||||||
|
|
||||||
|
val showWorkoutsCard = AppStyle.actionCard(
|
||||||
|
context = context,
|
||||||
|
text = "Gespeicherte Workouts",
|
||||||
|
dark = true,
|
||||||
|
onClick = onShowWorkouts
|
||||||
|
)
|
||||||
|
|
||||||
|
val createWorkoutCard = AppStyle.actionCard(
|
||||||
|
context = context,
|
||||||
|
text = "Workout erstellen",
|
||||||
|
dark = false,
|
||||||
|
onClick = onCreateWorkout
|
||||||
|
)
|
||||||
|
|
||||||
|
val bottomNote = TextView(context)
|
||||||
|
bottomNote.text = "time based? rep based? your choice!"
|
||||||
|
bottomNote.gravity = Gravity.START
|
||||||
|
AppStyle.muted(bottomNote)
|
||||||
|
AppStyle.setTextMargin(bottomNote)
|
||||||
|
|
||||||
|
addView(topSpace)
|
||||||
addView(title)
|
addView(title)
|
||||||
addView(subtitle)
|
addView(subtitle)
|
||||||
addView(button)
|
addView(buttonSpace)
|
||||||
|
addView(showWorkoutsCard)
|
||||||
|
addView(createWorkoutCard)
|
||||||
|
addView(bottomNote)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5,6 +5,7 @@ import android.view.Gravity
|
|||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import com.example.workouttimer.ui.theme.AppStyle
|
||||||
|
|
||||||
class PauseScreen(
|
class PauseScreen(
|
||||||
context: Context,
|
context: Context,
|
||||||
@ -13,28 +14,34 @@ class PauseScreen(
|
|||||||
) : LinearLayout(context) {
|
) : LinearLayout(context) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
orientation = VERTICAL
|
AppStyle.applyGradientBackground(this)
|
||||||
gravity = Gravity.CENTER
|
gravity = Gravity.CENTER_VERTICAL
|
||||||
setPadding(48, 48, 48, 48)
|
|
||||||
|
|
||||||
val title = TextView(context)
|
val title = TextView(context)
|
||||||
title.text = "Pause"
|
title.text = "Pause"
|
||||||
title.textSize = 32f
|
AppStyle.headline(title)
|
||||||
title.gravity = Gravity.CENTER
|
addView(title)
|
||||||
|
|
||||||
|
val info = TextView(context)
|
||||||
|
info.text = "Atme kurz durch. Du kannst jederzeit weitermachen."
|
||||||
|
AppStyle.muted(info)
|
||||||
|
AppStyle.setTextMargin(info)
|
||||||
|
addView(info)
|
||||||
|
|
||||||
val resumeButton = Button(context)
|
val resumeButton = Button(context)
|
||||||
resumeButton.text = "Weiter trainieren"
|
resumeButton.text = "Weiter trainieren -->"
|
||||||
|
AppStyle.primaryButton(resumeButton)
|
||||||
resumeButton.setOnClickListener {
|
resumeButton.setOnClickListener {
|
||||||
onResume()
|
onResume()
|
||||||
}
|
}
|
||||||
|
|
||||||
val finishButton = Button(context)
|
val finishButton = Button(context)
|
||||||
finishButton.text = "Workout beenden"
|
finishButton.text = "Workout beenden"
|
||||||
|
AppStyle.secondaryButton(finishButton)
|
||||||
finishButton.setOnClickListener {
|
finishButton.setOnClickListener {
|
||||||
onFinish()
|
onFinish()
|
||||||
}
|
}
|
||||||
|
|
||||||
addView(title)
|
|
||||||
addView(resumeButton)
|
addView(resumeButton)
|
||||||
addView(finishButton)
|
addView(finishButton)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,84 @@
|
|||||||
|
package com.example.workouttimer.ui.screens
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.CountDownTimer
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.widget.Button
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import com.example.workouttimer.ui.theme.AppStyle
|
||||||
|
|
||||||
|
class PreparationCountdownScreen(
|
||||||
|
context: Context,
|
||||||
|
onSkip: () -> Unit
|
||||||
|
) : LinearLayout(context) {
|
||||||
|
|
||||||
|
private var remainingSeconds = 15
|
||||||
|
private var timer: CountDownTimer? = null
|
||||||
|
private val timerText = TextView(context)
|
||||||
|
|
||||||
|
init {
|
||||||
|
AppStyle.applyGradientBackground(this)
|
||||||
|
gravity = Gravity.CENTER_VERTICAL
|
||||||
|
|
||||||
|
val title = TextView(context)
|
||||||
|
title.text = "Get\nReady"
|
||||||
|
AppStyle.headline(title)
|
||||||
|
addView(title)
|
||||||
|
|
||||||
|
val info = TextView(context)
|
||||||
|
info.text = "Dein Workout startet gleich."
|
||||||
|
AppStyle.muted(info)
|
||||||
|
AppStyle.setTextMargin(info)
|
||||||
|
addView(info)
|
||||||
|
|
||||||
|
timerText.gravity = Gravity.START
|
||||||
|
AppStyle.timer(timerText)
|
||||||
|
AppStyle.setTextMargin(timerText)
|
||||||
|
addView(timerText)
|
||||||
|
|
||||||
|
val skipButton = Button(context)
|
||||||
|
skipButton.text = "Jetzt starten -->"
|
||||||
|
AppStyle.primaryButton(skipButton)
|
||||||
|
skipButton.setOnClickListener {
|
||||||
|
timer?.cancel()
|
||||||
|
onSkip()
|
||||||
|
}
|
||||||
|
addView(skipButton)
|
||||||
|
|
||||||
|
val extendButton = Button(context)
|
||||||
|
extendButton.text = "+10 Sekunden"
|
||||||
|
AppStyle.secondaryButton(extendButton)
|
||||||
|
extendButton.setOnClickListener {
|
||||||
|
remainingSeconds += 10
|
||||||
|
restartTimer(onSkip)
|
||||||
|
}
|
||||||
|
addView(extendButton)
|
||||||
|
|
||||||
|
restartTimer(onSkip)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun restartTimer(onFinished: () -> Unit) {
|
||||||
|
timer?.cancel()
|
||||||
|
timerText.text = remainingSeconds.toString()
|
||||||
|
|
||||||
|
timer = object : CountDownTimer(remainingSeconds * 1000L, 1000L) {
|
||||||
|
override fun onTick(millisUntilFinished: Long) {
|
||||||
|
remainingSeconds = (millisUntilFinished / 1000L).toInt()
|
||||||
|
timerText.text = remainingSeconds.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFinish() {
|
||||||
|
timerText.text = "0"
|
||||||
|
onFinished()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
timer?.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDetachedFromWindow() {
|
||||||
|
super.onDetachedFromWindow()
|
||||||
|
timer?.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,9 +4,11 @@ import android.content.Context
|
|||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.Space
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import com.example.workouttimer.domain.ExerciseTarget
|
import com.example.workouttimer.domain.ExerciseTarget
|
||||||
import com.example.workouttimer.domain.Workout
|
import com.example.workouttimer.domain.Workout
|
||||||
|
import com.example.workouttimer.ui.theme.AppStyle
|
||||||
|
|
||||||
class ReadyToStartScreen(
|
class ReadyToStartScreen(
|
||||||
context: Context,
|
context: Context,
|
||||||
@ -16,36 +18,70 @@ class ReadyToStartScreen(
|
|||||||
) : LinearLayout(context) {
|
) : LinearLayout(context) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
orientation = VERTICAL
|
AppStyle.applyGradientBackground(this)
|
||||||
gravity = Gravity.CENTER_HORIZONTAL
|
|
||||||
setPadding(48, 48, 48, 48)
|
layoutParams = LayoutParams(
|
||||||
|
LayoutParams.MATCH_PARENT,
|
||||||
|
LayoutParams.MATCH_PARENT
|
||||||
|
)
|
||||||
|
|
||||||
|
gravity = Gravity.TOP
|
||||||
|
|
||||||
val title = TextView(context)
|
val title = TextView(context)
|
||||||
title.text = workout?.name ?: "Kein Workout ausgewählt"
|
title.text = "Ready\nto Start"
|
||||||
title.textSize = 28f
|
AppStyle.headline(title)
|
||||||
title.gravity = Gravity.CENTER
|
|
||||||
|
|
||||||
addView(title)
|
val workoutName = TextView(context)
|
||||||
|
workoutName.text = workout?.name ?: "Kein Workout ausgewählt"
|
||||||
|
AppStyle.title(workoutName)
|
||||||
|
AppStyle.setTextMargin(workoutName)
|
||||||
|
|
||||||
workout?.exercises?.forEach { exercise ->
|
val exerciseList = LinearLayout(context)
|
||||||
val exerciseText = TextView(context)
|
exerciseList.orientation = VERTICAL
|
||||||
exerciseText.text = "${exercise.name} - ${formatTarget(exercise.target)}"
|
|
||||||
exerciseText.textSize = 18f
|
workout?.exercises?.forEachIndexed { index, exercise ->
|
||||||
addView(exerciseText)
|
|
||||||
|
val card = LinearLayout(context)
|
||||||
|
AppStyle.card(card)
|
||||||
|
|
||||||
|
val exerciseTitle = TextView(context)
|
||||||
|
exerciseTitle.text = "${index + 1}. ${exercise.name}"
|
||||||
|
AppStyle.title(exerciseTitle)
|
||||||
|
|
||||||
|
val target = TextView(context)
|
||||||
|
target.text = formatTarget(exercise.target)
|
||||||
|
AppStyle.body(target)
|
||||||
|
|
||||||
|
card.addView(exerciseTitle)
|
||||||
|
card.addView(target)
|
||||||
|
|
||||||
|
exerciseList.addView(card)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val spacer = Space(context)
|
||||||
|
spacer.layoutParams = LayoutParams(
|
||||||
|
LayoutParams.MATCH_PARENT,
|
||||||
|
AppStyle.dp(this, 32)
|
||||||
|
)
|
||||||
|
|
||||||
val startButton = Button(context)
|
val startButton = Button(context)
|
||||||
startButton.text = "Workout starten"
|
startButton.text = "Workout starten -->"
|
||||||
|
AppStyle.primaryButton(startButton)
|
||||||
startButton.setOnClickListener {
|
startButton.setOnClickListener {
|
||||||
onStart()
|
onStart()
|
||||||
}
|
}
|
||||||
|
|
||||||
val backButton = Button(context)
|
val backButton = Button(context)
|
||||||
backButton.text = "Zurück"
|
backButton.text = "Zurück"
|
||||||
|
AppStyle.secondaryButton(backButton)
|
||||||
backButton.setOnClickListener {
|
backButton.setOnClickListener {
|
||||||
onBack()
|
onBack()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addView(title)
|
||||||
|
addView(workoutName)
|
||||||
|
addView(exerciseList)
|
||||||
|
addView(spacer)
|
||||||
addView(startButton)
|
addView(startButton)
|
||||||
addView(backButton)
|
addView(backButton)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,110 +5,146 @@ import android.os.CountDownTimer
|
|||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.Space
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import com.example.workouttimer.domain.Exercise
|
import com.example.workouttimer.domain.Exercise
|
||||||
import com.example.workouttimer.domain.ExerciseTarget
|
import com.example.workouttimer.domain.ExerciseTarget
|
||||||
import com.example.workouttimer.domain.Workout
|
import com.example.workouttimer.domain.Workout
|
||||||
|
import com.example.workouttimer.ui.theme.AppStyle
|
||||||
|
|
||||||
class RunningWorkoutScreen(
|
class RunningWorkoutScreen(
|
||||||
context: Context,
|
context: Context,
|
||||||
workout: Workout?,
|
workout: Workout?,
|
||||||
exercise: Exercise?,
|
exercise: Exercise?,
|
||||||
|
nextExercise: Exercise?,
|
||||||
exerciseNumber: Int,
|
exerciseNumber: Int,
|
||||||
onNextExercise: () -> Unit,
|
onNextExercise: () -> Unit,
|
||||||
onPause: () -> Unit
|
onPause: () -> Unit
|
||||||
) : LinearLayout(context) {
|
) : LinearLayout(context) {
|
||||||
|
|
||||||
private var countDownTimer: CountDownTimer? = null
|
private var timer: CountDownTimer? = null
|
||||||
|
private val mainValueText = TextView(context)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
orientation = VERTICAL
|
AppStyle.applyGradientBackground(this)
|
||||||
gravity = Gravity.CENTER
|
gravity = Gravity.CENTER_VERTICAL
|
||||||
setPadding(48, 48, 48, 48)
|
|
||||||
|
|
||||||
val workoutTitle = TextView(context)
|
val workoutLabel = TextView(context)
|
||||||
workoutTitle.text = workout?.name ?: "Workout läuft"
|
workoutLabel.text = workout?.name ?: "Workout läuft"
|
||||||
workoutTitle.textSize = 22f
|
workoutLabel.gravity = Gravity.CENTER
|
||||||
workoutTitle.gravity = Gravity.CENTER
|
AppStyle.muted(workoutLabel)
|
||||||
|
|
||||||
|
val topSpace = Space(context)
|
||||||
|
topSpace.layoutParams = LayoutParams(
|
||||||
|
LayoutParams.MATCH_PARENT,
|
||||||
|
AppStyle.dp(this, 28)
|
||||||
|
)
|
||||||
|
|
||||||
val exerciseTitle = TextView(context)
|
val exerciseTitle = TextView(context)
|
||||||
exerciseTitle.text = "Übung $exerciseNumber: ${exercise?.name ?: "-"}"
|
exerciseTitle.text = "${exerciseNumber}. ${exercise?.name ?: "-"}"
|
||||||
exerciseTitle.textSize = 30f
|
|
||||||
exerciseTitle.gravity = Gravity.CENTER
|
exerciseTitle.gravity = Gravity.CENTER
|
||||||
|
AppStyle.headline(exerciseTitle)
|
||||||
|
|
||||||
val targetText = TextView(context)
|
val middleSpace = Space(context)
|
||||||
targetText.text = exercise?.target?.let { formatTarget(it) } ?: ""
|
middleSpace.layoutParams = LayoutParams(
|
||||||
targetText.textSize = 24f
|
LayoutParams.MATCH_PARENT,
|
||||||
targetText.gravity = Gravity.CENTER
|
AppStyle.dp(this, 26)
|
||||||
|
)
|
||||||
|
|
||||||
val timerText = TextView(context)
|
mainValueText.gravity = Gravity.CENTER
|
||||||
timerText.textSize = 36f
|
AppStyle.timer(mainValueText)
|
||||||
timerText.gravity = Gravity.CENTER
|
|
||||||
|
|
||||||
val infoText = TextView(context)
|
val unitText = TextView(context)
|
||||||
infoText.text = exercise?.info ?: ""
|
unitText.gravity = Gravity.CENTER
|
||||||
infoText.textSize = 16f
|
AppStyle.title(unitText)
|
||||||
infoText.gravity = Gravity.CENTER
|
|
||||||
|
when (val target = exercise?.target) {
|
||||||
|
is ExerciseTarget.Repetitions -> {
|
||||||
|
mainValueText.text = target.reps.toString()
|
||||||
|
unitText.text = "Wiederholungen"
|
||||||
|
}
|
||||||
|
|
||||||
|
is ExerciseTarget.Duration -> {
|
||||||
|
mainValueText.text = target.seconds.toString()
|
||||||
|
unitText.text = "Sekunden"
|
||||||
|
startTimer(target.seconds, onNextExercise)
|
||||||
|
}
|
||||||
|
|
||||||
|
null -> {
|
||||||
|
mainValueText.text = "-"
|
||||||
|
unitText.text = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val nextText = TextView(context)
|
||||||
|
nextText.gravity = Gravity.CENTER
|
||||||
|
nextText.text = if (nextExercise != null) {
|
||||||
|
"Next: ${nextExercise.name} / ${formatTarget(nextExercise.target)}"
|
||||||
|
} else {
|
||||||
|
"Next: Workout done"
|
||||||
|
}
|
||||||
|
AppStyle.body(nextText)
|
||||||
|
AppStyle.setTextMargin(nextText)
|
||||||
|
|
||||||
|
val buttonSpace = Space(context)
|
||||||
|
buttonSpace.layoutParams = LayoutParams(
|
||||||
|
LayoutParams.MATCH_PARENT,
|
||||||
|
AppStyle.dp(this, 34)
|
||||||
|
)
|
||||||
|
|
||||||
val nextButton = Button(context)
|
val nextButton = Button(context)
|
||||||
nextButton.text = "Übung geschafft / Weiter"
|
nextButton.text = if (exercise?.target is ExerciseTarget.Duration) {
|
||||||
|
"Übung überspringen -->"
|
||||||
|
} else {
|
||||||
|
"Übung geschafft -->"
|
||||||
|
}
|
||||||
|
AppStyle.primaryButton(nextButton)
|
||||||
nextButton.setOnClickListener {
|
nextButton.setOnClickListener {
|
||||||
countDownTimer?.cancel()
|
timer?.cancel()
|
||||||
onNextExercise()
|
onNextExercise()
|
||||||
}
|
}
|
||||||
|
|
||||||
val pauseButton = Button(context)
|
val pauseButton = Button(context)
|
||||||
pauseButton.text = "Pause"
|
pauseButton.text = "Pause"
|
||||||
|
AppStyle.secondaryButton(pauseButton)
|
||||||
pauseButton.setOnClickListener {
|
pauseButton.setOnClickListener {
|
||||||
countDownTimer?.cancel()
|
timer?.cancel()
|
||||||
onPause()
|
onPause()
|
||||||
}
|
}
|
||||||
|
|
||||||
addView(workoutTitle)
|
addView(workoutLabel)
|
||||||
|
addView(topSpace)
|
||||||
addView(exerciseTitle)
|
addView(exerciseTitle)
|
||||||
addView(targetText)
|
addView(middleSpace)
|
||||||
addView(timerText)
|
addView(mainValueText)
|
||||||
addView(infoText)
|
addView(unitText)
|
||||||
|
addView(nextText)
|
||||||
|
addView(buttonSpace)
|
||||||
addView(nextButton)
|
addView(nextButton)
|
||||||
addView(pauseButton)
|
addView(pauseButton)
|
||||||
|
|
||||||
if (exercise?.target is ExerciseTarget.Duration) {
|
|
||||||
nextButton.text = "Überspringen"
|
|
||||||
startCountdown(
|
|
||||||
seconds = exercise.target.seconds,
|
|
||||||
timerText = timerText,
|
|
||||||
onFinished = onNextExercise
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
timerText.text = ""
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startCountdown(
|
private fun startTimer(seconds: Int, onFinished: () -> Unit) {
|
||||||
seconds: Int,
|
mainValueText.text = seconds.toString()
|
||||||
timerText: TextView,
|
|
||||||
onFinished: () -> Unit
|
|
||||||
) {
|
|
||||||
timerText.text = "$seconds"
|
|
||||||
|
|
||||||
countDownTimer = object : CountDownTimer(seconds * 1000L, 1000L) {
|
timer = object : CountDownTimer(seconds * 1000L, 1000L) {
|
||||||
override fun onTick(millisUntilFinished: Long) {
|
override fun onTick(millisUntilFinished: Long) {
|
||||||
val remainingSeconds = (millisUntilFinished / 1000L).toInt()
|
val remaining = (millisUntilFinished / 1000L).toInt()
|
||||||
timerText.text = remainingSeconds.toString()
|
mainValueText.text = remaining.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFinish() {
|
override fun onFinish() {
|
||||||
timerText.text = "0"
|
mainValueText.text = "0"
|
||||||
onFinished()
|
onFinished()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
countDownTimer?.start()
|
timer?.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDetachedFromWindow() {
|
override fun onDetachedFromWindow() {
|
||||||
super.onDetachedFromWindow()
|
super.onDetachedFromWindow()
|
||||||
countDownTimer?.cancel()
|
timer?.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun formatTarget(target: ExerciseTarget): String {
|
private fun formatTarget(target: ExerciseTarget): String {
|
||||||
|
|||||||
@ -4,8 +4,10 @@ import android.content.Context
|
|||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.ScrollView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import com.example.workouttimer.domain.Workout
|
import com.example.workouttimer.domain.Workout
|
||||||
|
import com.example.workouttimer.ui.theme.AppStyle
|
||||||
|
|
||||||
class SavedWorkoutsScreen(
|
class SavedWorkoutsScreen(
|
||||||
context: Context,
|
context: Context,
|
||||||
@ -15,64 +17,72 @@ class SavedWorkoutsScreen(
|
|||||||
onEditWorkout: (Workout) -> Unit,
|
onEditWorkout: (Workout) -> Unit,
|
||||||
onDeleteWorkout: (Workout) -> Unit,
|
onDeleteWorkout: (Workout) -> Unit,
|
||||||
onBack: () -> Unit
|
onBack: () -> Unit
|
||||||
) : LinearLayout(context) {
|
) : ScrollView(context) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
orientation = VERTICAL
|
background = AppStyle.gradientDrawable()
|
||||||
gravity = Gravity.CENTER_HORIZONTAL
|
|
||||||
setPadding(48, 48, 48, 48)
|
val content = LinearLayout(context)
|
||||||
|
AppStyle.applyGradientBackground(content)
|
||||||
|
|
||||||
|
content.layoutParams = LayoutParams(
|
||||||
|
LayoutParams.MATCH_PARENT,
|
||||||
|
LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
|
||||||
|
addView(content)
|
||||||
|
|
||||||
val title = TextView(context)
|
val title = TextView(context)
|
||||||
title.text = "Gespeicherte Workouts"
|
title.text = "Saved\nWorkouts"
|
||||||
title.textSize = 28f
|
AppStyle.headline(title)
|
||||||
title.gravity = Gravity.CENTER
|
content.addView(title)
|
||||||
|
|
||||||
addView(title)
|
|
||||||
|
|
||||||
val createButton = Button(context)
|
val createButton = Button(context)
|
||||||
createButton.text = "Neues Workout erstellen"
|
createButton.text = "Neues Workout erstellen -->"
|
||||||
createButton.setOnClickListener {
|
AppStyle.primaryButton(createButton)
|
||||||
onCreateWorkout()
|
createButton.setOnClickListener { onCreateWorkout() }
|
||||||
}
|
content.addView(createButton)
|
||||||
addView(createButton)
|
|
||||||
|
|
||||||
workouts.forEach { workout ->
|
workouts.forEach { workout ->
|
||||||
|
val card = LinearLayout(context)
|
||||||
|
AppStyle.card(card)
|
||||||
|
|
||||||
val workoutRow = LinearLayout(context)
|
val name = TextView(context)
|
||||||
workoutRow.orientation = HORIZONTAL
|
name.text = workout.name
|
||||||
workoutRow.gravity = Gravity.CENTER
|
AppStyle.title(name)
|
||||||
|
|
||||||
val workoutButton = Button(context)
|
val details = TextView(context)
|
||||||
workoutButton.text = workout.name
|
details.text = "${workout.exercises.size} Übungen"
|
||||||
workoutButton.setOnClickListener {
|
AppStyle.muted(details)
|
||||||
onWorkoutSelected(workout)
|
|
||||||
}
|
val startButton = Button(context)
|
||||||
|
startButton.text = "Starten -->"
|
||||||
|
AppStyle.primaryButton(startButton)
|
||||||
|
startButton.setOnClickListener { onWorkoutSelected(workout) }
|
||||||
|
|
||||||
val editButton = Button(context)
|
val editButton = Button(context)
|
||||||
editButton.text = "Bearbeiten"
|
editButton.text = "Bearbeiten"
|
||||||
editButton.setOnClickListener {
|
AppStyle.secondaryButton(editButton)
|
||||||
onEditWorkout(workout)
|
editButton.setOnClickListener { onEditWorkout(workout) }
|
||||||
}
|
|
||||||
|
|
||||||
val deleteButton = Button(context)
|
val deleteButton = Button(context)
|
||||||
deleteButton.text = "Löschen"
|
deleteButton.text = "Workout löschen"
|
||||||
deleteButton.setOnClickListener {
|
AppStyle.secondaryButton(deleteButton)
|
||||||
onDeleteWorkout(workout)
|
deleteButton.setOnClickListener { onDeleteWorkout(workout) }
|
||||||
}
|
|
||||||
|
|
||||||
workoutRow.addView(workoutButton)
|
card.addView(name)
|
||||||
workoutRow.addView(editButton)
|
card.addView(details)
|
||||||
workoutRow.addView(deleteButton)
|
card.addView(startButton)
|
||||||
|
card.addView(editButton)
|
||||||
|
card.addView(deleteButton)
|
||||||
|
|
||||||
addView(workoutRow)
|
content.addView(card)
|
||||||
}
|
}
|
||||||
|
|
||||||
val backButton = Button(context)
|
val backButton = Button(context)
|
||||||
backButton.text = "Zurück"
|
backButton.text = "Zurück"
|
||||||
backButton.setOnClickListener {
|
AppStyle.secondaryButton(backButton)
|
||||||
onBack()
|
backButton.setOnClickListener { onBack() }
|
||||||
}
|
content.addView(backButton)
|
||||||
|
|
||||||
addView(backButton)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -12,56 +12,64 @@ import android.widget.TextView
|
|||||||
import com.example.workouttimer.domain.Exercise
|
import com.example.workouttimer.domain.Exercise
|
||||||
import com.example.workouttimer.domain.ExerciseTarget
|
import com.example.workouttimer.domain.ExerciseTarget
|
||||||
import com.example.workouttimer.domain.Workout
|
import com.example.workouttimer.domain.Workout
|
||||||
|
import com.example.workouttimer.ui.theme.AppStyle
|
||||||
|
|
||||||
class WorkoutEditorScreen(
|
class WorkoutEditorScreen(
|
||||||
context: Context,
|
context: Context,
|
||||||
workoutToEdit: Workout?,
|
workoutToEdit: Workout?,
|
||||||
onSave: (
|
onSave: (name: String, exercises: List<Exercise>) -> Unit,
|
||||||
name: String,
|
|
||||||
exercises: List<Exercise>
|
|
||||||
) -> Unit,
|
|
||||||
onCancel: () -> Unit
|
onCancel: () -> Unit
|
||||||
) : ScrollView(context) {
|
) : ScrollView(context) {
|
||||||
|
|
||||||
private val exercises = mutableListOf<Exercise>()
|
private val exercises = mutableListOf<Exercise>()
|
||||||
private val exerciseListContainer = LinearLayout(context)
|
private val exerciseListContainer = LinearLayout(context)
|
||||||
|
private var editingIndex: Int? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
exerciseListContainer.orientation = LinearLayout.VERTICAL
|
background = AppStyle.gradientDrawable()
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
val content = LinearLayout(context)
|
val content = LinearLayout(context)
|
||||||
content.orientation = LinearLayout.VERTICAL
|
AppStyle.applyGradientBackground(content)
|
||||||
content.gravity = Gravity.CENTER_HORIZONTAL
|
content.layoutParams = LayoutParams(
|
||||||
content.setPadding(48, 48, 48, 48)
|
LayoutParams.MATCH_PARENT,
|
||||||
|
LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
addView(content)
|
addView(content)
|
||||||
|
|
||||||
workoutToEdit?.exercises?.let {
|
workoutToEdit?.exercises?.let { exercises.addAll(it) }
|
||||||
exercises.addAll(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
val title = TextView(context)
|
val title = TextView(context)
|
||||||
title.text = if (workoutToEdit == null) "Workout erstellen" else "Workout bearbeiten"
|
title.text = if (workoutToEdit == null) "Create\nWorkout" else "Edit\nWorkout"
|
||||||
title.textSize = 28f
|
AppStyle.headline(title)
|
||||||
title.gravity = Gravity.CENTER
|
content.addView(title)
|
||||||
|
|
||||||
val workoutNameInput = EditText(context)
|
val workoutNameInput = EditText(context)
|
||||||
workoutNameInput.hint = "Workout Name"
|
workoutNameInput.hint = "Workout Name"
|
||||||
workoutNameInput.setText(workoutToEdit?.name ?: "")
|
workoutNameInput.setText(workoutToEdit?.name ?: "")
|
||||||
|
AppStyle.input(workoutNameInput)
|
||||||
|
content.addView(workoutNameInput)
|
||||||
|
|
||||||
|
val sectionTitle = TextView(context)
|
||||||
|
sectionTitle.text = "Neue Übung"
|
||||||
|
AppStyle.sectionTitle(sectionTitle)
|
||||||
|
content.addView(sectionTitle)
|
||||||
|
|
||||||
val exerciseNameInput = EditText(context)
|
val exerciseNameInput = EditText(context)
|
||||||
exerciseNameInput.hint = "Übungsname"
|
exerciseNameInput.hint = "Übungsname"
|
||||||
|
AppStyle.input(exerciseNameInput)
|
||||||
|
content.addView(exerciseNameInput)
|
||||||
|
|
||||||
val targetGroup = RadioGroup(context)
|
val targetGroup = RadioGroup(context)
|
||||||
targetGroup.orientation = RadioGroup.HORIZONTAL
|
targetGroup.orientation = RadioGroup.HORIZONTAL
|
||||||
|
targetGroup.gravity = Gravity.CENTER
|
||||||
|
|
||||||
val repsButton = RadioButton(context)
|
val repsButton = RadioButton(context)
|
||||||
repsButton.text = "Wiederholungen"
|
repsButton.text = "Wiederholungen"
|
||||||
|
AppStyle.body(repsButton)
|
||||||
|
|
||||||
val timeButton = RadioButton(context)
|
val timeButton = RadioButton(context)
|
||||||
timeButton.text = "Zeit"
|
timeButton.text = "Zeit"
|
||||||
|
AppStyle.body(timeButton)
|
||||||
|
|
||||||
val repsButtonId = generateViewId()
|
val repsButtonId = generateViewId()
|
||||||
val timeButtonId = generateViewId()
|
val timeButtonId = generateViewId()
|
||||||
@ -73,15 +81,63 @@ class WorkoutEditorScreen(
|
|||||||
targetGroup.addView(timeButton)
|
targetGroup.addView(timeButton)
|
||||||
targetGroup.check(repsButtonId)
|
targetGroup.check(repsButtonId)
|
||||||
|
|
||||||
|
AppStyle.setTextMargin(targetGroup)
|
||||||
|
content.addView(targetGroup)
|
||||||
|
|
||||||
val targetValueInput = EditText(context)
|
val targetValueInput = EditText(context)
|
||||||
targetValueInput.hint = "Wert, z.B. 12 oder 30"
|
targetValueInput.hint = "Wert, z.B. 12 oder 30"
|
||||||
targetValueInput.inputType = android.text.InputType.TYPE_CLASS_NUMBER
|
targetValueInput.inputType = android.text.InputType.TYPE_CLASS_NUMBER
|
||||||
|
AppStyle.input(targetValueInput)
|
||||||
|
content.addView(targetValueInput)
|
||||||
|
|
||||||
val infoInput = EditText(context)
|
val infoInput = EditText(context)
|
||||||
infoInput.hint = "Info / Anmerkung"
|
infoInput.hint = "Info / Anmerkung"
|
||||||
|
AppStyle.input(infoInput)
|
||||||
|
content.addView(infoInput)
|
||||||
|
|
||||||
val addExerciseButton = Button(context)
|
val addExerciseButton = Button(context)
|
||||||
addExerciseButton.text = "Übung hinzufügen"
|
addExerciseButton.text = "Übung hinzufügen -->"
|
||||||
|
AppStyle.primaryButton(addExerciseButton)
|
||||||
|
content.addView(addExerciseButton)
|
||||||
|
|
||||||
|
val listTitle = TextView(context)
|
||||||
|
listTitle.text = "Workout Plan"
|
||||||
|
AppStyle.sectionTitle(listTitle)
|
||||||
|
content.addView(listTitle)
|
||||||
|
|
||||||
|
exerciseListContainer.orientation = LinearLayout.VERTICAL
|
||||||
|
content.addView(exerciseListContainer)
|
||||||
|
|
||||||
|
fun clearExerciseInputs() {
|
||||||
|
exerciseNameInput.text.clear()
|
||||||
|
targetValueInput.text.clear()
|
||||||
|
infoInput.text.clear()
|
||||||
|
targetGroup.check(repsButtonId)
|
||||||
|
editingIndex = null
|
||||||
|
addExerciseButton.text = "Übung hinzufügen -->"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fillExerciseInputs(index: Int) {
|
||||||
|
val exercise = exercises[index]
|
||||||
|
|
||||||
|
exerciseNameInput.setText(exercise.name)
|
||||||
|
infoInput.setText(exercise.info)
|
||||||
|
|
||||||
|
when (val target = exercise.target) {
|
||||||
|
is ExerciseTarget.Repetitions -> {
|
||||||
|
targetGroup.check(repsButtonId)
|
||||||
|
targetValueInput.setText(target.reps.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
is ExerciseTarget.Duration -> {
|
||||||
|
targetGroup.check(timeButtonId)
|
||||||
|
targetValueInput.setText(target.seconds.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
editingIndex = index
|
||||||
|
addExerciseButton.text = "Änderung übernehmen -->"
|
||||||
|
}
|
||||||
|
|
||||||
addExerciseButton.setOnClickListener {
|
addExerciseButton.setOnClickListener {
|
||||||
val exerciseName = exerciseNameInput.text.toString()
|
val exerciseName = exerciseNameInput.text.toString()
|
||||||
@ -98,26 +154,34 @@ class WorkoutEditorScreen(
|
|||||||
ExerciseTarget.Duration(targetValueText.toInt())
|
ExerciseTarget.Duration(targetValueText.toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
val exercise = Exercise(
|
val indexToEdit = editingIndex
|
||||||
id = exercises.size + 1,
|
|
||||||
name = exerciseName,
|
|
||||||
target = target,
|
|
||||||
info = info
|
|
||||||
)
|
|
||||||
|
|
||||||
exercises.add(exercise)
|
if (indexToEdit == null) {
|
||||||
|
exercises.add(
|
||||||
|
Exercise(
|
||||||
|
id = exercises.size + 1,
|
||||||
|
name = exerciseName,
|
||||||
|
target = target,
|
||||||
|
info = info
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
exercises[indexToEdit] = Exercise(
|
||||||
|
id = indexToEdit + 1,
|
||||||
|
name = exerciseName,
|
||||||
|
target = target,
|
||||||
|
info = info
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
exerciseNameInput.text.clear()
|
updateExerciseIds()
|
||||||
targetValueInput.text.clear()
|
clearExerciseInputs()
|
||||||
infoInput.text.clear()
|
updateExerciseList(::fillExerciseInputs)
|
||||||
targetGroup.check(repsButtonId)
|
|
||||||
|
|
||||||
updateExerciseList()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val saveButton = Button(context)
|
val saveButton = Button(context)
|
||||||
saveButton.text = "Workout speichern"
|
saveButton.text = "Workout speichern -->"
|
||||||
|
AppStyle.primaryButton(saveButton)
|
||||||
saveButton.setOnClickListener {
|
saveButton.setOnClickListener {
|
||||||
val workoutName = workoutNameInput.text.toString()
|
val workoutName = workoutNameInput.text.toString()
|
||||||
|
|
||||||
@ -130,70 +194,99 @@ class WorkoutEditorScreen(
|
|||||||
|
|
||||||
val cancelButton = Button(context)
|
val cancelButton = Button(context)
|
||||||
cancelButton.text = "Abbrechen"
|
cancelButton.text = "Abbrechen"
|
||||||
|
AppStyle.secondaryButton(cancelButton)
|
||||||
cancelButton.setOnClickListener {
|
cancelButton.setOnClickListener {
|
||||||
onCancel()
|
onCancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
content.addView(title)
|
|
||||||
content.addView(workoutNameInput)
|
|
||||||
|
|
||||||
val exerciseSectionTitle = TextView(context)
|
|
||||||
exerciseSectionTitle.text = "Übung hinzufügen"
|
|
||||||
exerciseSectionTitle.textSize = 22f
|
|
||||||
|
|
||||||
content.addView(exerciseSectionTitle)
|
|
||||||
content.addView(exerciseNameInput)
|
|
||||||
content.addView(targetGroup)
|
|
||||||
content.addView(targetValueInput)
|
|
||||||
content.addView(infoInput)
|
|
||||||
content.addView(addExerciseButton)
|
|
||||||
|
|
||||||
val listTitle = TextView(context)
|
|
||||||
listTitle.text = "Übungen im Workout"
|
|
||||||
listTitle.textSize = 22f
|
|
||||||
|
|
||||||
content.addView(listTitle)
|
|
||||||
exerciseListContainer.orientation = LinearLayout.VERTICAL
|
|
||||||
content.addView(exerciseListContainer)
|
|
||||||
|
|
||||||
content.addView(saveButton)
|
content.addView(saveButton)
|
||||||
content.addView(cancelButton)
|
content.addView(cancelButton)
|
||||||
|
|
||||||
updateExerciseList()
|
updateExerciseList(::fillExerciseInputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateExerciseList() {
|
private fun updateExerciseList(onEditExercise: (Int) -> Unit) {
|
||||||
exerciseListContainer.removeAllViews()
|
exerciseListContainer.removeAllViews()
|
||||||
|
|
||||||
if (exercises.isEmpty()) {
|
if (exercises.isEmpty()) {
|
||||||
val emptyText = TextView(context)
|
val emptyText = TextView(context)
|
||||||
emptyText.text = "Noch keine Übungen hinzugefügt."
|
emptyText.text = "Noch keine Übungen hinzugefügt."
|
||||||
|
AppStyle.muted(emptyText)
|
||||||
|
AppStyle.setTextMargin(emptyText)
|
||||||
exerciseListContainer.addView(emptyText)
|
exerciseListContainer.addView(emptyText)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
exercises.forEachIndexed { index, exercise ->
|
exercises.forEachIndexed { index, exercise ->
|
||||||
|
val card = LinearLayout(context)
|
||||||
|
AppStyle.card(card)
|
||||||
|
|
||||||
val row = LinearLayout(context)
|
val exerciseTitle = TextView(context)
|
||||||
row.orientation = LinearLayout.HORIZONTAL
|
exerciseTitle.text = "${index + 1}. ${exercise.name}"
|
||||||
row.gravity = Gravity.CENTER_VERTICAL
|
AppStyle.title(exerciseTitle)
|
||||||
|
|
||||||
val exerciseText = TextView(context)
|
val targetText = TextView(context)
|
||||||
exerciseText.text = "${index + 1}. ${exercise.name} - ${formatTarget(exercise.target)}"
|
targetText.text = formatTarget(exercise.target)
|
||||||
exerciseText.textSize = 16f
|
AppStyle.body(targetText)
|
||||||
|
|
||||||
|
val infoText = TextView(context)
|
||||||
|
infoText.text = exercise.info
|
||||||
|
AppStyle.muted(infoText)
|
||||||
|
|
||||||
|
val editButton = Button(context)
|
||||||
|
editButton.text = "Bearbeiten"
|
||||||
|
AppStyle.primaryButton(editButton)
|
||||||
|
editButton.setOnClickListener {
|
||||||
|
onEditExercise(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
val upButton = Button(context)
|
||||||
|
upButton.text = "Nach oben"
|
||||||
|
AppStyle.secondaryButton(upButton)
|
||||||
|
upButton.isEnabled = index > 0
|
||||||
|
upButton.setOnClickListener {
|
||||||
|
val temp = exercises[index - 1]
|
||||||
|
exercises[index - 1] = exercises[index]
|
||||||
|
exercises[index] = temp
|
||||||
|
updateExerciseIds()
|
||||||
|
updateExerciseList(onEditExercise)
|
||||||
|
}
|
||||||
|
|
||||||
|
val downButton = Button(context)
|
||||||
|
downButton.text = "Nach unten"
|
||||||
|
AppStyle.secondaryButton(downButton)
|
||||||
|
downButton.isEnabled = index < exercises.lastIndex
|
||||||
|
downButton.setOnClickListener {
|
||||||
|
val temp = exercises[index + 1]
|
||||||
|
exercises[index + 1] = exercises[index]
|
||||||
|
exercises[index] = temp
|
||||||
|
updateExerciseIds()
|
||||||
|
updateExerciseList(onEditExercise)
|
||||||
|
}
|
||||||
|
|
||||||
val deleteButton = Button(context)
|
val deleteButton = Button(context)
|
||||||
deleteButton.text = "Löschen"
|
deleteButton.text = "Löschen"
|
||||||
|
AppStyle.secondaryButton(deleteButton)
|
||||||
deleteButton.setOnClickListener {
|
deleteButton.setOnClickListener {
|
||||||
exercises.removeAt(index)
|
exercises.removeAt(index)
|
||||||
|
editingIndex = null
|
||||||
updateExerciseIds()
|
updateExerciseIds()
|
||||||
updateExerciseList()
|
updateExerciseList(onEditExercise)
|
||||||
}
|
}
|
||||||
|
|
||||||
row.addView(exerciseText)
|
card.addView(exerciseTitle)
|
||||||
row.addView(deleteButton)
|
card.addView(targetText)
|
||||||
|
|
||||||
exerciseListContainer.addView(row)
|
if (exercise.info.isNotBlank()) {
|
||||||
|
card.addView(infoText)
|
||||||
|
}
|
||||||
|
|
||||||
|
card.addView(editButton)
|
||||||
|
card.addView(upButton)
|
||||||
|
card.addView(downButton)
|
||||||
|
card.addView(deleteButton)
|
||||||
|
|
||||||
|
exerciseListContainer.addView(card)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
266
app/src/main/java/com/example/workouttimer/ui/theme/AppStyle.kt
Normal file
266
app/src/main/java/com/example/workouttimer/ui/theme/AppStyle.kt
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
package com.example.workouttimer.ui.theme
|
||||||
|
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.drawable.GradientDrawable
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.Button
|
||||||
|
import android.widget.EditText
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.core.content.res.ResourcesCompat
|
||||||
|
import com.example.workouttimer.R
|
||||||
|
|
||||||
|
object AppStyle {
|
||||||
|
|
||||||
|
val yellow = Color.rgb(245, 218, 49)
|
||||||
|
val green = Color.rgb(172, 232, 122)
|
||||||
|
val cyan = Color.rgb(111, 219, 232)
|
||||||
|
val black = Color.rgb(0, 0, 0)
|
||||||
|
val white = Color.rgb(255, 255, 255)
|
||||||
|
val softWhite = Color.rgb(250, 250, 250)
|
||||||
|
val lightBlack = Color.rgb(24, 24, 24)
|
||||||
|
val mutedText = Color.rgb(70, 70, 70)
|
||||||
|
|
||||||
|
const val screenPadding = 48
|
||||||
|
const val smallSpacing = 16
|
||||||
|
const val mediumSpacing = 24
|
||||||
|
const val largeSpacing = 40
|
||||||
|
const val cornerRadius = 4f
|
||||||
|
|
||||||
|
private val regularFontId = R.font.funnel_display_regular
|
||||||
|
private val boldFontId = R.font.funnel_display_bold
|
||||||
|
private val mediumFontId = R.font.funnel_display_medium
|
||||||
|
private val extraBoldFontId = R.font.funnel_display_extra_bold
|
||||||
|
|
||||||
|
fun regular(view: TextView) {
|
||||||
|
view.typeface = ResourcesCompat.getFont(view.context, regularFontId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun medium(view: TextView) {
|
||||||
|
view.typeface = ResourcesCompat.getFont(view.context, mediumFontId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bold(view: TextView) {
|
||||||
|
view.typeface = ResourcesCompat.getFont(view.context, boldFontId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun extraBold(view: TextView) {
|
||||||
|
view.typeface = ResourcesCompat.getFont(view.context, extraBoldFontId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dp(view: View, value: Int): Int {
|
||||||
|
return (value * view.context.resources.displayMetrics.density).toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun applyGradientBackground(layout: LinearLayout) {
|
||||||
|
layout.orientation = LinearLayout.VERTICAL
|
||||||
|
layout.setPadding(
|
||||||
|
dp(layout, 28),
|
||||||
|
dp(layout, 56),
|
||||||
|
dp(layout, 28),
|
||||||
|
dp(layout, 28)
|
||||||
|
)
|
||||||
|
|
||||||
|
val gradient = GradientDrawable(
|
||||||
|
GradientDrawable.Orientation.TOP_BOTTOM,
|
||||||
|
intArrayOf(yellow, green, cyan)
|
||||||
|
)
|
||||||
|
|
||||||
|
layout.background = gradientDrawable()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun headline(textView: TextView) {
|
||||||
|
extraBold(textView)
|
||||||
|
textView.textSize = 48f
|
||||||
|
textView.setTextColor(black)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun title(textView: TextView) {
|
||||||
|
bold(textView)
|
||||||
|
textView.textSize = 30f
|
||||||
|
textView.setTextColor(black)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sectionTitle(textView: TextView) {
|
||||||
|
bold(textView)
|
||||||
|
textView.textSize = 22f
|
||||||
|
textView.setTextColor(black)
|
||||||
|
setTextMargin(textView)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun body(textView: TextView) {
|
||||||
|
regular(textView)
|
||||||
|
textView.textSize = 17f
|
||||||
|
textView.setTextColor(black)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun muted(textView: TextView) {
|
||||||
|
regular(textView)
|
||||||
|
textView.textSize = 15f
|
||||||
|
textView.setTextColor(mutedText)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun timer(textView: TextView) {
|
||||||
|
extraBold(textView)
|
||||||
|
textView.textSize = 76f
|
||||||
|
textView.setTextColor(black)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun primaryButton(button: Button) {
|
||||||
|
bold(button)
|
||||||
|
button.textSize = 16f
|
||||||
|
button.setTextColor(white)
|
||||||
|
button.background = roundedBackground(black)
|
||||||
|
button.isAllCaps = false
|
||||||
|
setFullWidthMargin(button)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun secondaryButton(button: Button) {
|
||||||
|
bold(button)
|
||||||
|
button.textSize = 16f
|
||||||
|
button.setTextColor(black)
|
||||||
|
button.background = roundedBackground(white)
|
||||||
|
button.isAllCaps = false
|
||||||
|
setFullWidthMargin(button)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dangerButton(button: Button) {
|
||||||
|
bold(button)
|
||||||
|
button.textSize = 16f
|
||||||
|
button.setTextColor(white)
|
||||||
|
button.background = roundedBackground(Color.rgb(210, 55, 55))
|
||||||
|
button.isAllCaps = false
|
||||||
|
setFullWidthMargin(button)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun compactButton(button: Button) {
|
||||||
|
medium(button)
|
||||||
|
button.textSize = 14f
|
||||||
|
button.setTextColor(black)
|
||||||
|
button.background = roundedBackground(white)
|
||||||
|
button.isAllCaps = false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun input(editText: EditText) {
|
||||||
|
regular(editText)
|
||||||
|
editText.textSize = 16f
|
||||||
|
editText.setTextColor(black)
|
||||||
|
editText.setHintTextColor(mutedText)
|
||||||
|
editText.background = roundedStrokeBackground(white)
|
||||||
|
editText.setPadding(28, 12, 28, 12)
|
||||||
|
setFullWidthMargin(editText)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun card(layout: LinearLayout) {
|
||||||
|
layout.orientation = LinearLayout.VERTICAL
|
||||||
|
layout.background = roundedBackground(softWhite)
|
||||||
|
layout.setPadding(28, 24, 28, 24)
|
||||||
|
|
||||||
|
val params = LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
params.setMargins(0, mediumSpacing, 0, 0)
|
||||||
|
layout.layoutParams = params
|
||||||
|
}
|
||||||
|
|
||||||
|
fun row(layout: LinearLayout) {
|
||||||
|
layout.orientation = LinearLayout.HORIZONTAL
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setFullWidthMargin(view: View) {
|
||||||
|
val params = LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
dp(view, 58)
|
||||||
|
)
|
||||||
|
params.setMargins(0, dp(view, 14), 0, 0)
|
||||||
|
view.layoutParams = params
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTextMargin(view: View) {
|
||||||
|
val params = LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
params.setMargins(0, mediumSpacing, 0, 0)
|
||||||
|
view.layoutParams = params
|
||||||
|
}
|
||||||
|
|
||||||
|
fun roundedBackground(color: Int): GradientDrawable {
|
||||||
|
return GradientDrawable().apply {
|
||||||
|
setColor(color)
|
||||||
|
cornerRadius = AppStyle.cornerRadius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun roundedStrokeBackground(color: Int): GradientDrawable {
|
||||||
|
return GradientDrawable().apply {
|
||||||
|
setColor(color)
|
||||||
|
cornerRadius = AppStyle.cornerRadius
|
||||||
|
setStroke(2, black)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun squareBackground(color: Int): GradientDrawable {
|
||||||
|
return GradientDrawable().apply {
|
||||||
|
setColor(color)
|
||||||
|
cornerRadius = 0f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun actionCard(
|
||||||
|
context: android.content.Context,
|
||||||
|
text: String,
|
||||||
|
dark: Boolean,
|
||||||
|
onClick: () -> Unit
|
||||||
|
): LinearLayout {
|
||||||
|
val card = LinearLayout(context)
|
||||||
|
card.orientation = LinearLayout.HORIZONTAL
|
||||||
|
card.gravity = android.view.Gravity.CENTER_VERTICAL
|
||||||
|
card.setPadding(dp(card, 20), 0, dp(card, 20), 0)
|
||||||
|
card.background = squareBackground(if (dark) black else white)
|
||||||
|
card.setOnClickListener { onClick() }
|
||||||
|
|
||||||
|
val label = TextView(context)
|
||||||
|
label.text = text
|
||||||
|
bold(label)
|
||||||
|
label.textSize = 20f
|
||||||
|
label.setTextColor(if (dark) white else black)
|
||||||
|
|
||||||
|
val arrow = TextView(context)
|
||||||
|
arrow.text = " -->"
|
||||||
|
bold(arrow)
|
||||||
|
arrow.textSize = 30f
|
||||||
|
arrow.setTextColor(if (dark) white else black)
|
||||||
|
arrow.gravity = android.view.Gravity.END
|
||||||
|
|
||||||
|
val labelParams = LinearLayout.LayoutParams(
|
||||||
|
0,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
1f
|
||||||
|
)
|
||||||
|
labelParams.setMargins(dp(card, 16), 0, dp(card, 16), 0)
|
||||||
|
|
||||||
|
label.layoutParams = labelParams
|
||||||
|
|
||||||
|
val cardParams = LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
dp(card, 62)
|
||||||
|
)
|
||||||
|
cardParams.setMargins(0, dp(card, 14), 0, 0)
|
||||||
|
card.layoutParams = cardParams
|
||||||
|
|
||||||
|
card.addView(label)
|
||||||
|
card.addView(arrow)
|
||||||
|
|
||||||
|
return card
|
||||||
|
}
|
||||||
|
|
||||||
|
fun gradientDrawable(): GradientDrawable {
|
||||||
|
return GradientDrawable(
|
||||||
|
GradientDrawable.Orientation.TOP_BOTTOM,
|
||||||
|
intArrayOf(yellow, green, cyan)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -114,4 +114,15 @@ class WorkoutViewModel(context: Context) {
|
|||||||
val workout = currentWorkout ?: return null
|
val workout = currentWorkout ?: return null
|
||||||
return workout.exercises[currentExerciseIndex]
|
return workout.exercises[currentExerciseIndex]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getNextExercise(): Exercise? {
|
||||||
|
val workout = currentWorkout ?: return null
|
||||||
|
val nextIndex = currentExerciseIndex + 1
|
||||||
|
|
||||||
|
return if (nextIndex <= workout.exercises.lastIndex) {
|
||||||
|
workout.exercises[nextIndex]
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
BIN
app/src/main/res/font/funnel_display_bold.ttf
Normal file
BIN
app/src/main/res/font/funnel_display_bold.ttf
Normal file
Binary file not shown.
BIN
app/src/main/res/font/funnel_display_extra_bold.ttf
Normal file
BIN
app/src/main/res/font/funnel_display_extra_bold.ttf
Normal file
Binary file not shown.
BIN
app/src/main/res/font/funnel_display_light.ttf
Normal file
BIN
app/src/main/res/font/funnel_display_light.ttf
Normal file
Binary file not shown.
BIN
app/src/main/res/font/funnel_display_medium.ttf
Normal file
BIN
app/src/main/res/font/funnel_display_medium.ttf
Normal file
Binary file not shown.
BIN
app/src/main/res/font/funnel_display_regular.ttf
Normal file
BIN
app/src/main/res/font/funnel_display_regular.ttf
Normal file
Binary file not shown.
BIN
app/src/main/res/font/funnel_display_semi_bold.ttf
Normal file
BIN
app/src/main/res/font/funnel_display_semi_bold.ttf
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user