From 26c6977fd65fad51937c120c4ad477246f04a703 Mon Sep 17 00:00:00 2001 From: William Brawner Date: Tue, 22 Sep 2020 15:45:54 -0700 Subject: [PATCH] Add animation to the timer screen --- .idea/codeStyles/Project.xml | 139 ------------------ .idea/codestyles/Project.xml | 123 ---------------- .idea/misc.xml | 2 +- app/build.gradle | 2 + .../activetimer/ActiveTimerFragment.kt | 20 ++- .../activetimer/ActiveTimerViewModel.kt | 37 +++-- .../main/res/layout/fragment_active_timer.xml | 6 +- .../trainterval/shared/IntervalTimerState.kt | 6 + 8 files changed, 48 insertions(+), 287 deletions(-) delete mode 100644 .idea/codeStyles/Project.xml delete mode 100644 .idea/codestyles/Project.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index baf56fc..0000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - - - - - - -
- - - - xmlns:android - - ^$ - - - -
-
- - - - xmlns:.* - - ^$ - - - BY_NAME - -
-
- - - - .*:id - - http://schemas.android.com/apk/res/android - - - -
-
- - - - .*:name - - http://schemas.android.com/apk/res/android - - - -
-
- - - - name - - ^$ - - - -
-
- - - - style - - ^$ - - - -
-
- - - - .* - - ^$ - - - BY_NAME - -
-
- - - - .* - - http://schemas.android.com/apk/res/android - - - ANDROID_ATTRIBUTE_ORDER - -
-
- - - - .* - - .* - - - BY_NAME - -
-
-
-
- - -
-
\ No newline at end of file diff --git a/.idea/codestyles/Project.xml b/.idea/codestyles/Project.xml deleted file mode 100644 index adef587..0000000 --- a/.idea/codestyles/Project.xml +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - -
- - - - xmlns:android - - ^$ - - - -
-
- - - - xmlns:.* - - ^$ - - - BY_NAME - -
-
- - - - .*:id - - http://schemas.android.com/apk/res/android - - - -
-
- - - - .*:name - - http://schemas.android.com/apk/res/android - - - -
-
- - - - name - - ^$ - - - -
-
- - - - style - - ^$ - - - -
-
- - - - .* - - ^$ - - - BY_NAME - -
-
- - - - .* - - http://schemas.android.com/apk/res/android - - - ANDROID_ATTRIBUTE_ORDER - -
-
- - - - .* - - .* - - - BY_NAME - -
-
-
-
- - -
-
\ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index bee4a02..7bfef59 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,6 @@ - + diff --git a/app/build.gradle b/app/build.gradle index 90c87b2..059de90 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -102,6 +102,8 @@ dependencies { implementation "org.koin:koin-android:$koin_version" testImplementation "org.koin:koin-test:$koin_version" + implementation 'com.robinhood.ticker:ticker:2.0.2' + testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' diff --git a/app/src/main/java/com/wbrawner/trainterval/activetimer/ActiveTimerFragment.kt b/app/src/main/java/com/wbrawner/trainterval/activetimer/ActiveTimerFragment.kt index f7c9601..ac9c8f8 100644 --- a/app/src/main/java/com/wbrawner/trainterval/activetimer/ActiveTimerFragment.kt +++ b/app/src/main/java/com/wbrawner/trainterval/activetimer/ActiveTimerFragment.kt @@ -1,5 +1,7 @@ package com.wbrawner.trainterval.activetimer +import android.animation.ArgbEvaluator +import android.animation.ValueAnimator import android.content.Context import android.media.AudioManager import android.media.SoundPool @@ -16,6 +18,7 @@ import androidx.fragment.app.activityViewModels import androidx.lifecycle.Observer import androidx.lifecycle.viewModelScope import androidx.navigation.fragment.findNavController +import com.robinhood.ticker.TickerUtils import com.google.android.gms.wearable.* import com.wbrawner.trainterval.Logger import com.wbrawner.trainterval.R @@ -89,6 +92,9 @@ class ActiveTimerFragment : Fragment(), MessageClient.OnMessageReceivedListener it.setSupportActionBar(toolbar) it.supportActionBar?.setDisplayHomeAsUpEnabled(true) } + timeRemaining.setCharacterLists(TickerUtils.provideNumberList() + ":") + timerSets.setCharacterLists(TickerUtils.provideNumberList()) + timerRounds.setCharacterLists(TickerUtils.provideNumberList()) coroutineScope = CoroutineScope(Dispatchers.Main) coroutineScope!!.launch { activeTimerViewModel.timerState.observe(viewLifecycleOwner, Observer { state -> @@ -131,7 +137,19 @@ class ActiveTimerFragment : Fragment(), MessageClient.OnMessageReceivedListener } (activity as? AppCompatActivity)?.supportActionBar?.title = state.timerName val backgroundColor = resources.getColor(state.phase.colorRes, context?.theme) - timerBackground.setBackgroundColor(backgroundColor) + Log.d("ActiveTimerFragment", "State: $state") + state.previousPhase?.let { + val previousBackgroundColor = resources.getColor(it.colorRes, context?.theme) + val colorAnimation = + ValueAnimator.ofObject(ArgbEvaluator(), previousBackgroundColor, backgroundColor) + colorAnimation.duration = 250 + colorAnimation.addUpdateListener { animator -> + timerBackground.setBackgroundColor( + animator.animatedValue as Int + ) + } + colorAnimation.start() + } ?: timerBackground.setBackgroundColor(backgroundColor) playPauseButton.setImageDrawable( requireContext().getDrawable( if (state.isRunning) R.drawable.ic_pause diff --git a/app/src/main/java/com/wbrawner/trainterval/activetimer/ActiveTimerViewModel.kt b/app/src/main/java/com/wbrawner/trainterval/activetimer/ActiveTimerViewModel.kt index cb7d306..1245a4d 100644 --- a/app/src/main/java/com/wbrawner/trainterval/activetimer/ActiveTimerViewModel.kt +++ b/app/src/main/java/com/wbrawner/trainterval/activetimer/ActiveTimerViewModel.kt @@ -39,7 +39,7 @@ class ActiveTimerViewModel : ViewModel() { currentRound = timer.cycles timeRemaining = timer.warmUpDuration currentPhase = Phase.WARM_UP - updateTimer() + updateTimer(null) } } @@ -47,34 +47,27 @@ class ActiveTimerViewModel : ViewModel() { if (timerJob != null) { timerJob?.cancel() timerJob = null - timerState.postValue( - TimerRunningState( - timer, - timeRemaining, - currentSet, - currentRound, - currentPhase, - timerJob != null - ) - ) + updateTimer(null) } else { viewModelScope.launch { - startTimer() + startTimer(null) } } } - private fun startTimer() { + private fun startTimer(previousPhase: Phase?) { viewModelScope.launch { timerJob = launch { - updateTimer() + updateTimer(previousPhase) while (coroutineContext.isActive && timerJob != null) { delay(1_000) timeRemaining -= 1_000 + // We need to recalculate the previous phase on each iteration + val previousPhaseOngoing = currentPhase if (timeRemaining <= 0) { goForward() } - updateTimer() + updateTimer(if (previousPhaseOngoing != currentPhase) previousPhaseOngoing else null) } } } @@ -82,22 +75,24 @@ class ActiveTimerViewModel : ViewModel() { fun skipAhead() { timerJob?.cancel() + var previousPhase: Phase? = null when (currentPhase) { Phase.COOL_DOWN -> { timeRemaining = 0 } else -> { + previousPhase = currentPhase goForward() } } if (timerJob != null) { - startTimer() + startTimer(previousPhase) } else { - updateTimer() + updateTimer(previousPhase) } } - private fun updateTimer() { + private fun updateTimer(previousPhase: Phase?) { timerState.postValue( TimerRunningState( timer, @@ -105,6 +100,7 @@ class ActiveTimerViewModel : ViewModel() { currentSet, currentRound, currentPhase, + previousPhase, timerJob != null ) ) @@ -154,6 +150,7 @@ class ActiveTimerViewModel : ViewModel() { fun goBack() { timerJob?.cancel() + var previousPhase: Phase = currentPhase when (currentPhase) { Phase.WARM_UP -> { timeRemaining = timer.warmUpDuration @@ -191,9 +188,9 @@ class ActiveTimerViewModel : ViewModel() { } } if (timerJob != null) { - startTimer() + startTimer(previousPhase) } else { - updateTimer() + updateTimer(previousPhase) } } } diff --git a/app/src/main/res/layout/fragment_active_timer.xml b/app/src/main/res/layout/fragment_active_timer.xml index f1312fd..241b879 100644 --- a/app/src/main/res/layout/fragment_active_timer.xml +++ b/app/src/main/res/layout/fragment_active_timer.xml @@ -55,7 +55,7 @@ android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6" android:textColor="@color/colorOnSurface" /> - - -