Fix style issues after leaving timer and rotation breaking timers
This commit is contained in:
parent
cb284b800d
commit
f21754a0be
12 changed files with 267 additions and 173 deletions
|
@ -9,7 +9,8 @@ data class IntervalDuration(
|
||||||
val seconds: Long = 0
|
val seconds: Long = 0
|
||||||
) {
|
) {
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "%02d:%02d:%02d".format(hours, minutes, seconds)
|
return if (hours > 0) "%02d:%02d:%02d".format(hours, minutes, seconds)
|
||||||
|
else "%02d:%02d".format(minutes, seconds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,9 +20,10 @@ fun IntervalDuration.toMillis(): Long {
|
||||||
TimeUnit.SECONDS.toMillis(seconds)
|
TimeUnit.SECONDS.toMillis(seconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const val SECONDS_IN_HOUR = 3600
|
||||||
|
private const val SECONDS_IN_MINUTE = 60
|
||||||
|
|
||||||
fun Long.toIntervalDuration(): IntervalDuration {
|
fun Long.toIntervalDuration(): IntervalDuration {
|
||||||
val SECONDS_IN_HOUR = 3600
|
|
||||||
val SECONDS_IN_MINUTE = 60
|
|
||||||
|
|
||||||
if (this < 1000) {
|
if (this < 1000) {
|
||||||
return IntervalDuration(0, 0, 0)
|
return IntervalDuration(0, 0, 0)
|
||||||
|
|
|
@ -2,7 +2,6 @@ package com.wbrawner.trainterval
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import com.wbrawner.trainterval.activetimer.ActiveTimerViewModel
|
|
||||||
import com.wbrawner.trainterval.timerform.TimerFormViewModel
|
import com.wbrawner.trainterval.timerform.TimerFormViewModel
|
||||||
import com.wbrawner.trainterval.timerlist.TimerListViewModel
|
import com.wbrawner.trainterval.timerlist.TimerListViewModel
|
||||||
import org.koin.android.ext.koin.androidContext
|
import org.koin.android.ext.koin.androidContext
|
||||||
|
@ -46,10 +45,6 @@ val traintervalModule = module {
|
||||||
TimerFormViewModel(get(parameters = { parametersOf("TimerFormStore") }), get())
|
TimerFormViewModel(get(parameters = { parametersOf("TimerFormStore") }), get())
|
||||||
}
|
}
|
||||||
|
|
||||||
factory {
|
|
||||||
ActiveTimerViewModel(get(parameters = { parametersOf("ActiveTimerStore") }), get())
|
|
||||||
}
|
|
||||||
|
|
||||||
factory<Logger> { params ->
|
factory<Logger> { params ->
|
||||||
AndroidLogger(params.component1())
|
AndroidLogger(params.component1())
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,25 +7,31 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
|
import com.wbrawner.trainterval.Logger
|
||||||
import com.wbrawner.trainterval.R
|
import com.wbrawner.trainterval.R
|
||||||
|
import com.wbrawner.trainterval.model.IntervalTimerDao
|
||||||
import kotlinx.android.synthetic.main.fragment_active_timer.*
|
import kotlinx.android.synthetic.main.fragment_active_timer.*
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.cancel
|
import kotlinx.coroutines.cancel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
|
import org.koin.core.parameter.parametersOf
|
||||||
|
|
||||||
class ActiveTimerFragment : Fragment() {
|
class ActiveTimerFragment : Fragment() {
|
||||||
|
|
||||||
private var coroutineScope: CoroutineScope? = null
|
private var coroutineScope: CoroutineScope? = null
|
||||||
private val activeTimerViewModel: ActiveTimerViewModel by inject()
|
private val activeTimerViewModel: ActiveTimerViewModel by activityViewModels()
|
||||||
|
private val logger: Logger by inject(parameters = { parametersOf("ActiveTimerStore") })
|
||||||
|
private val timerDao: IntervalTimerDao by inject()
|
||||||
private var timerId: Long = 0
|
private var timerId: Long = 0
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
timerId = requireArguments().getLong("timerId")
|
timerId = requireArguments().getLong(EXTRA_TIMER_ID)
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,35 +62,46 @@ class ActiveTimerFragment : Fragment() {
|
||||||
is IntervalTimerActiveState.ExitState -> findNavController().navigateUp()
|
is IntervalTimerActiveState.ExitState -> findNavController().navigateUp()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
activeTimerViewModel.init(timerId)
|
activeTimerViewModel.init(logger, timerDao, timerId)
|
||||||
}
|
}
|
||||||
skipPreviousButton.setOnClickListener {
|
skipPreviousButton.setOnClickListener {
|
||||||
coroutineScope!!.launch {
|
activeTimerViewModel.goBack()
|
||||||
activeTimerViewModel.goBack()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
playPauseButton.setOnClickListener {
|
playPauseButton.setOnClickListener {
|
||||||
coroutineScope!!.launch {
|
activeTimerViewModel.toggleTimer()
|
||||||
activeTimerViewModel.toggleTimer()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
skipNextButton.setOnClickListener {
|
skipNextButton.setOnClickListener {
|
||||||
coroutineScope!!.launch {
|
activeTimerViewModel.skipAhead()
|
||||||
activeTimerViewModel.skipAhead()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderLoading() {
|
private fun renderLoading() {
|
||||||
progressBar.visibility = View.VISIBLE
|
progressBar.visibility = View.VISIBLE
|
||||||
timerLayout.visibility = View.GONE
|
timerLayout.referencedIds.forEach {
|
||||||
|
view?.findViewById<View>(it)?.visibility = View.GONE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderTimer(state: IntervalTimerActiveState.TimerRunningState) {
|
private fun renderTimer(state: IntervalTimerActiveState.TimerRunningState) {
|
||||||
progressBar.visibility = View.GONE
|
progressBar.visibility = View.GONE
|
||||||
timerLayout.visibility = View.VISIBLE
|
timerLayout.referencedIds.forEach {
|
||||||
|
view?.findViewById<View>(it)?.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
(activity as? AppCompatActivity)?.supportActionBar?.title = state.timerName
|
||||||
|
val backgroundColor = resources.getColor(state.timerBackground, context?.theme)
|
||||||
|
timerBackground.setBackgroundColor(backgroundColor)
|
||||||
playPauseButton.setImageDrawable(requireContext().getDrawable(state.playPauseIcon))
|
playPauseButton.setImageDrawable(requireContext().getDrawable(state.playPauseIcon))
|
||||||
timeRemaining.text = state.timeRemaining
|
timeRemaining.text = state.timeRemaining
|
||||||
|
timerSets.text = getString(
|
||||||
|
R.string.timer_sets_formatted,
|
||||||
|
state.currentSet,
|
||||||
|
state.totalSets
|
||||||
|
)
|
||||||
|
timerRounds.text = getString(
|
||||||
|
R.string.timer_rounds_formatted,
|
||||||
|
state.currentRound,
|
||||||
|
state.totalRounds
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package com.wbrawner.trainterval.activetimer
|
package com.wbrawner.trainterval.activetimer
|
||||||
|
|
||||||
|
import androidx.annotation.ColorRes
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.wbrawner.trainterval.Logger
|
import com.wbrawner.trainterval.Logger
|
||||||
import com.wbrawner.trainterval.R
|
import com.wbrawner.trainterval.R
|
||||||
import com.wbrawner.trainterval.activetimer.IntervalTimerActiveState.LoadingState
|
import com.wbrawner.trainterval.activetimer.IntervalTimerActiveState.LoadingState
|
||||||
|
@ -11,93 +13,83 @@ import com.wbrawner.trainterval.model.IntervalTimer
|
||||||
import com.wbrawner.trainterval.model.IntervalTimerDao
|
import com.wbrawner.trainterval.model.IntervalTimerDao
|
||||||
import com.wbrawner.trainterval.model.Phase
|
import com.wbrawner.trainterval.model.Phase
|
||||||
import com.wbrawner.trainterval.toIntervalDuration
|
import com.wbrawner.trainterval.toIntervalDuration
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class ActiveTimerViewModel(
|
class ActiveTimerViewModel : ViewModel() {
|
||||||
private val logger: Logger,
|
|
||||||
private val timerDao: IntervalTimerDao
|
|
||||||
) : ViewModel() {
|
|
||||||
val timerState: MutableLiveData<IntervalTimerActiveState> = MutableLiveData(LoadingState)
|
val timerState: MutableLiveData<IntervalTimerActiveState> = MutableLiveData(LoadingState)
|
||||||
private var timerJob: Job? = null
|
private var timerJob: Job? = null
|
||||||
private lateinit var timer: IntervalTimer
|
private lateinit var timer: IntervalTimer
|
||||||
|
private lateinit var logger: Logger
|
||||||
private var timerComplete = false
|
private var timerComplete = false
|
||||||
private var timerRunning = false
|
|
||||||
private var currentPhase = Phase.WARM_UP
|
private var currentPhase = Phase.WARM_UP
|
||||||
private var currentSet = 1
|
private var currentSet = 1
|
||||||
private var currentRound = 1
|
private var currentRound = 1
|
||||||
private var timeRemaining: Long = 0
|
private var timeRemaining: Long = 0
|
||||||
|
|
||||||
suspend fun init(timerId: Long) {
|
suspend fun init(
|
||||||
logger.d(message = "Initializing with Timer id $timerId")
|
logger: Logger,
|
||||||
timer = timerDao.getById(timerId)
|
timerDao: IntervalTimerDao,
|
||||||
timeRemaining = timer.warmUpDuration
|
timerId: Long
|
||||||
timerState.postValue(
|
) {
|
||||||
TimerRunningState(
|
this.logger = logger
|
||||||
timerRunning,
|
if (timerJob == null || timer.id != timerId) {
|
||||||
timeRemaining.toIntervalDuration().toString(),
|
logger.d(message = "Initializing with Timer id $timerId")
|
||||||
currentSet,
|
timer = timerDao.getById(timerId)
|
||||||
timer.sets,
|
timeRemaining = timer.warmUpDuration
|
||||||
currentRound,
|
|
||||||
timer.cycles
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun toggleTimer() {
|
|
||||||
if (timerRunning) {
|
|
||||||
timerJob?.cancel()
|
|
||||||
timerRunning = false
|
|
||||||
timerState.postValue(
|
timerState.postValue(
|
||||||
TimerRunningState(
|
TimerRunningState(
|
||||||
timerRunning,
|
timer,
|
||||||
timeRemaining.toIntervalDuration().toString(),
|
timeRemaining,
|
||||||
currentSet,
|
currentSet,
|
||||||
timer.sets,
|
|
||||||
currentRound,
|
currentRound,
|
||||||
timer.cycles
|
currentPhase,
|
||||||
|
timerJob != null
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
startTimer()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun startTimer() {
|
fun toggleTimer() {
|
||||||
coroutineScope {
|
if (timerJob != null) {
|
||||||
timerJob = launch {
|
timerJob?.cancel()
|
||||||
timerRunning = true
|
timerJob = null
|
||||||
timerState.postValue(
|
timerState.postValue(
|
||||||
TimerRunningState(
|
TimerRunningState(
|
||||||
timerRunning,
|
timer,
|
||||||
timeRemaining.toIntervalDuration().toString(),
|
timeRemaining,
|
||||||
currentSet,
|
currentSet,
|
||||||
timer.sets,
|
currentRound,
|
||||||
currentRound,
|
currentPhase,
|
||||||
timer.cycles
|
timerJob != null
|
||||||
)
|
|
||||||
)
|
)
|
||||||
while (coroutineContext.isActive && timerRunning) {
|
)
|
||||||
|
} else {
|
||||||
|
viewModelScope.launch {
|
||||||
|
startTimer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startTimer() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
timerJob = launch {
|
||||||
|
updateTimer()
|
||||||
|
while (coroutineContext.isActive && timerJob != null) {
|
||||||
delay(1_000)
|
delay(1_000)
|
||||||
timeRemaining -= 1_000
|
timeRemaining -= 1_000
|
||||||
if (timeRemaining <= 0) {
|
if (timeRemaining <= 0) {
|
||||||
goForward()
|
goForward()
|
||||||
}
|
}
|
||||||
timerState.postValue(
|
updateTimer()
|
||||||
TimerRunningState(
|
|
||||||
timerRunning,
|
|
||||||
timeRemaining.toIntervalDuration().toString(),
|
|
||||||
currentSet,
|
|
||||||
timer.sets,
|
|
||||||
currentRound,
|
|
||||||
timer.cycles
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun skipAhead() {
|
fun skipAhead() {
|
||||||
timerJob?.cancel()
|
timerJob?.cancel()
|
||||||
when (currentPhase) {
|
when (currentPhase) {
|
||||||
Phase.COOL_DOWN -> {
|
Phase.COOL_DOWN -> {
|
||||||
|
@ -107,11 +99,26 @@ class ActiveTimerViewModel(
|
||||||
goForward()
|
goForward()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (timerRunning) {
|
if (timerJob != null) {
|
||||||
startTimer()
|
startTimer()
|
||||||
|
} else {
|
||||||
|
updateTimer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateTimer() {
|
||||||
|
timerState.postValue(
|
||||||
|
TimerRunningState(
|
||||||
|
timer,
|
||||||
|
timeRemaining,
|
||||||
|
currentSet,
|
||||||
|
currentRound,
|
||||||
|
currentPhase,
|
||||||
|
timerJob != null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun goForward() {
|
private fun goForward() {
|
||||||
timerComplete = currentPhase == Phase.COOL_DOWN
|
timerComplete = currentPhase == Phase.COOL_DOWN
|
||||||
when (currentPhase) {
|
when (currentPhase) {
|
||||||
|
@ -147,12 +154,14 @@ class ActiveTimerViewModel(
|
||||||
timeRemaining = timer.lowIntensityDuration
|
timeRemaining = timer.lowIntensityDuration
|
||||||
}
|
}
|
||||||
Phase.COOL_DOWN -> {
|
Phase.COOL_DOWN -> {
|
||||||
timerRunning = false
|
timeRemaining = 0
|
||||||
|
timerJob?.cancel()
|
||||||
|
timerJob = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun goBack() {
|
fun goBack() {
|
||||||
timerJob?.cancel()
|
timerJob?.cancel()
|
||||||
when (currentPhase) {
|
when (currentPhase) {
|
||||||
Phase.WARM_UP -> {
|
Phase.WARM_UP -> {
|
||||||
|
@ -169,11 +178,11 @@ class ActiveTimerViewModel(
|
||||||
timeRemaining = timer.restDuration
|
timeRemaining = timer.restDuration
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
currentSet--
|
||||||
currentPhase = Phase.HIGH_INTENSITY
|
currentPhase = Phase.HIGH_INTENSITY
|
||||||
timeRemaining = timer.highIntensityDuration
|
timeRemaining = timer.highIntensityDuration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
timeRemaining = timer.highIntensityDuration
|
|
||||||
}
|
}
|
||||||
Phase.HIGH_INTENSITY -> {
|
Phase.HIGH_INTENSITY -> {
|
||||||
currentPhase = Phase.LOW_INTENSITY
|
currentPhase = Phase.LOW_INTENSITY
|
||||||
|
@ -190,8 +199,10 @@ class ActiveTimerViewModel(
|
||||||
timeRemaining = timer.highIntensityDuration
|
timeRemaining = timer.highIntensityDuration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (timerRunning) {
|
if (timerJob != null) {
|
||||||
startTimer()
|
startTimer()
|
||||||
|
} else {
|
||||||
|
updateTimer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,14 +213,33 @@ class ActiveTimerViewModel(
|
||||||
sealed class IntervalTimerActiveState {
|
sealed class IntervalTimerActiveState {
|
||||||
object LoadingState : IntervalTimerActiveState()
|
object LoadingState : IntervalTimerActiveState()
|
||||||
class TimerRunningState(
|
class TimerRunningState(
|
||||||
timerRunning: Boolean,
|
val timerName: String,
|
||||||
val timeRemaining: String,
|
val timeRemaining: String,
|
||||||
val currentSet: Int,
|
val currentSet: Int,
|
||||||
val totalSets: Int,
|
val totalSets: Int,
|
||||||
val currentRound: Int,
|
val currentRound: Int,
|
||||||
val totalRounds: Int,
|
val totalRounds: Int,
|
||||||
val timerComplete: Boolean = false,
|
@ColorRes val timerBackground: Int,
|
||||||
@DrawableRes val playPauseIcon: Int = if (timerRunning) R.drawable.ic_pause else R.drawable.ic_play_arrow
|
@DrawableRes val playPauseIcon: Int
|
||||||
) : IntervalTimerActiveState()
|
) : IntervalTimerActiveState() {
|
||||||
|
constructor(
|
||||||
|
timer: IntervalTimer,
|
||||||
|
timeRemaining: Long,
|
||||||
|
currentSet: Int,
|
||||||
|
currentRound: Int,
|
||||||
|
phase: Phase,
|
||||||
|
timerRunning: Boolean
|
||||||
|
) : this(
|
||||||
|
timerName = timer.name,
|
||||||
|
timeRemaining = timeRemaining.toIntervalDuration().toString(),
|
||||||
|
currentSet = currentSet,
|
||||||
|
currentRound = currentRound,
|
||||||
|
totalSets = timer.sets,
|
||||||
|
totalRounds = timer.cycles,
|
||||||
|
timerBackground = phase.colorRes,
|
||||||
|
playPauseIcon = if (timerRunning) R.drawable.ic_pause else R.drawable.ic_play_arrow
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
object ExitState : IntervalTimerActiveState()
|
object ExitState : IntervalTimerActiveState()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package com.wbrawner.trainterval.model
|
package com.wbrawner.trainterval.model
|
||||||
|
|
||||||
|
import androidx.annotation.ColorRes
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
|
import com.wbrawner.trainterval.R
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
@ -18,12 +20,12 @@ data class IntervalTimer(
|
||||||
val cycles: Int = 1
|
val cycles: Int = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
enum class Phase {
|
enum class Phase(@ColorRes val colorRes: Int) {
|
||||||
WARM_UP,
|
WARM_UP(R.color.colorSurface),
|
||||||
LOW_INTENSITY,
|
LOW_INTENSITY(R.color.colorSurfaceLowIntensity),
|
||||||
HIGH_INTENSITY,
|
HIGH_INTENSITY(R.color.colorSurfaceHighIntensity),
|
||||||
REST,
|
REST(R.color.colorSurfaceRest),
|
||||||
COOL_DOWN,
|
COOL_DOWN(R.color.colorSurfaceCoolDown),
|
||||||
}
|
}
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
|
|
|
@ -1,21 +1,15 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/nav_host_fragment"
|
||||||
|
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".MainActivity">
|
app:defaultNavHost="true"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
<androidx.fragment.app.FragmentContainerView
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
android:id="@+id/nav_host_fragment"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
android:layout_width="0dp"
|
app:navGraph="@navigation/nav_graph"
|
||||||
android:layout_height="0dp"
|
tools:context=".MainActivity" />
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:defaultNavHost="true"
|
|
||||||
app:navGraph="@navigation/nav_graph" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/timerBackground"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:animateLayoutChanges="true"
|
android:animateLayoutChanges="true"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:keepScreenOn="true"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
tools:context="com.wbrawner.trainterval.activetimer.ActiveTimerFragment">
|
tools:context="com.wbrawner.trainterval.activetimer.ActiveTimerFragment">
|
||||||
|
|
||||||
|
@ -12,82 +15,125 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="#00000000"
|
android:background="#00000000"
|
||||||
android:clipChildren="false"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:elevation="0dp"
|
android:elevation="0dp"
|
||||||
app:elevation="0dp">
|
android:padding="16dp"
|
||||||
|
app:elevation="0dp"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="16dp"
|
|
||||||
android:background="@drawable/background_rounded_corners"
|
android:background="@drawable/background_rounded_corners"
|
||||||
app:layout_scrollFlags="scroll|enterAlways"
|
app:layout_scrollFlags="scroll|enterAlways"
|
||||||
app:title="@string/app_name" />
|
app:title="@string/app_name" />
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.Group
|
||||||
android:id="@+id/timerLayout"
|
android:id="@+id/timerLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="0dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
app:constraint_referenced_ids="playPauseButton,timerSets,timerRounds,timeRemaining,skipNextButton,skipPreviousButton" />
|
||||||
android:visibility="gone"
|
|
||||||
tools:visibility="visible">
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/timeRemaining"
|
android:id="@+id/timerSets"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textAlignment="center"
|
android:padding="16dp"
|
||||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline2"
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toStartOf="@+id/timerRounds"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
tools:text="00:00" />
|
tools:text="Set: 4/5" />
|
||||||
|
|
||||||
<ImageButton
|
<TextView
|
||||||
android:id="@+id/skipPreviousButton"
|
android:id="@+id/timerRounds"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:backgroundTint="@android:color/transparent"
|
android:padding="16dp"
|
||||||
android:contentDescription="@string/skip_previous"
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
|
||||||
android:src="@drawable/ic_skip_previous"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/playPauseButton"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintHorizontal_chainStyle="packed"
|
app:layout_constraintStart_toEndOf="@+id/timerSets"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
tools:text="Round: 4/5" />
|
||||||
app:layout_constraintTop_toBottomOf="@+id/timeRemaining" />
|
|
||||||
|
|
||||||
<ImageButton
|
<androidx.constraintlayout.widget.Barrier
|
||||||
android:id="@+id/playPauseButton"
|
android:id="@+id/timerInfo"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:backgroundTint="@android:color/transparent"
|
app:barrierDirection="top"
|
||||||
android:contentDescription="@string/start_timer"
|
app:constraint_referenced_ids="timerSets,timerRounds" />
|
||||||
app:layout_constraintEnd_toStartOf="@+id/skipNextButton"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/skipPreviousButton"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/timeRemaining"
|
|
||||||
tools:src="@drawable/ic_play_arrow" />
|
|
||||||
|
|
||||||
<ImageButton
|
<TextView
|
||||||
android:id="@+id/skipNextButton"
|
android:id="@+id/timeRemaining"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:backgroundTint="@android:color/transparent"
|
android:fontFamily="monospace"
|
||||||
android:contentDescription="@string/skip_next"
|
android:textAlignment="center"
|
||||||
android:src="@drawable/ic_skip_next"
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline2"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:textColor="@color/colorOnSurface"
|
||||||
app:layout_constraintStart_toEndOf="@+id/playPauseButton"
|
app:layout_constraintBottom_toTopOf="@+id/playPauseButton"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/timeRemaining" />
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/appBarLayout"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
|
tools:text="00:00" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/skipPreviousButton"
|
||||||
|
android:layout_width="64dp"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:backgroundTint="@android:color/transparent"
|
||||||
|
android:contentDescription="@string/skip_previous"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/ic_skip_previous"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/timerInfo"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/playPauseButton"
|
||||||
|
app:layout_constraintHorizontal_chainStyle="packed"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/timeRemaining"
|
||||||
|
app:layout_constraintVertical_bias="0.0" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/playPauseButton"
|
||||||
|
android:layout_width="64dp"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:backgroundTint="@android:color/transparent"
|
||||||
|
android:contentDescription="@string/start_timer"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/timerInfo"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/skipNextButton"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/skipPreviousButton"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/timeRemaining"
|
||||||
|
tools:src="@drawable/ic_play_arrow" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/skipNextButton"
|
||||||
|
android:layout_width="64dp"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:backgroundTint="@android:color/transparent"
|
||||||
|
android:contentDescription="@string/skip_next"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/ic_skip_next"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/timerInfo"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/playPauseButton"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/timeRemaining"
|
||||||
|
app:layout_constraintVertical_bias="0.0" />
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/progressBar"
|
android:id="@+id/progressBar"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="0dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:visibility="gone" />
|
android:visibility="gone"
|
||||||
</LinearLayout>
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:animateLayoutChanges="true"
|
android:animateLayoutChanges="true"
|
||||||
android:fillViewport="true"
|
android:fillViewport="true"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
tools:context="com.wbrawner.trainterval.timerform.TimerFormFragment">
|
tools:context="com.wbrawner.trainterval.timerform.TimerFormFragment">
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
<androidx.core.widget.NestedScrollView
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
android:animateLayoutChanges="true"
|
android:animateLayoutChanges="true"
|
||||||
android:clipChildren="false"
|
android:clipChildren="false"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
tools:context="com.wbrawner.trainterval.timerlist.TimerListFragment">
|
tools:context="com.wbrawner.trainterval.timerlist.TimerListFragment">
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="colorPrimary">#6200EE</color>
|
<color name="colorPrimary">#FFEB3B</color>
|
||||||
<color name="colorPrimaryDark">#3700B3</color>
|
<color name="colorPrimaryDark">#F9A825</color>
|
||||||
<color name="colorAccent">#03DAC5</color>
|
<color name="colorAccent">#F57C00</color>
|
||||||
<color name="colorOnSurface">#111111</color>
|
<color name="colorOnSurface">#111111</color>
|
||||||
<color name="colorSurface">#FFFFFF</color>
|
<color name="colorSurface">#FFFFFF</color>
|
||||||
<color name="colorSurfaceStroke">#F1F1F1</color>
|
<color name="colorSurfaceStroke">#F1F1F1</color>
|
||||||
|
<color name="colorSurfaceWarmUp">@color/colorSurface</color>
|
||||||
|
<color name="colorSurfaceLowIntensity">#F06292</color>
|
||||||
|
<color name="colorSurfaceHighIntensity">#81C784</color>
|
||||||
|
<color name="colorSurfaceRest">#FFF176</color>
|
||||||
|
<color name="colorSurfaceCoolDown">#64B5F6</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -18,4 +18,6 @@
|
||||||
<string name="rest_duration">Rest Duration</string>
|
<string name="rest_duration">Rest Duration</string>
|
||||||
<string name="title_item_list">Items</string>
|
<string name="title_item_list">Items</string>
|
||||||
<string name="title_item_detail">Item Detail</string>
|
<string name="title_item_detail">Item Detail</string>
|
||||||
|
<string name="timer_sets_formatted">Set: %1$d/%2$d</string>
|
||||||
|
<string name="timer_rounds_formatted">Round: %1$d/%2$d</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -6,9 +6,8 @@
|
||||||
<item name="colorPrimary">@color/colorPrimary</item>
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||||
<item name="colorAccent">@color/colorAccent</item>
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
<item name="android:windowLightStatusBar">true</item>
|
<item name="android:windowTranslucentStatus">true</item>
|
||||||
<item name="android:statusBarColor">?android:windowBackground</item>
|
<item name="android:windowTranslucentNavigation">true</item>
|
||||||
<item name="statusBarBackground">?android:windowBackground</item>
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="AppTheme.NoActionBar">
|
<style name="AppTheme.NoActionBar">
|
||||||
|
|
Loading…
Reference in a new issue