Play sounds on each timer phase

This commit is contained in:
William Brawner 2020-07-04 19:41:45 -07:00
parent 46cb0e3c0a
commit 6ecc7e2a38
9 changed files with 67 additions and 25 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,26 +1,29 @@
package com.wbrawner.trainterval.activetimer
import android.media.AudioManager
import android.media.SoundPool
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat.getSystemService
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
import androidx.lifecycle.viewModelScope
import androidx.navigation.fragment.findNavController
import com.wbrawner.trainterval.Logger
import com.wbrawner.trainterval.R
import com.wbrawner.trainterval.model.IntervalTimerDao
import com.wbrawner.trainterval.model.Phase
import kotlinx.android.synthetic.main.fragment_active_timer.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.coroutines.*
import org.koin.android.ext.android.inject
import org.koin.core.parameter.parametersOf
class ActiveTimerFragment : Fragment() {
private var coroutineScope: CoroutineScope? = null
@ -28,11 +31,29 @@ class ActiveTimerFragment : Fragment() {
private val logger: Logger by inject(parameters = { parametersOf("ActiveTimerStore") })
private val timerDao: IntervalTimerDao by inject()
private var timerId: Long = 0
private lateinit var soundPool: SoundPool
private val soundIds = mutableListOf<Int>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
timerId = requireArguments().getLong(EXTRA_TIMER_ID)
setHasOptionsMenu(true)
soundPool = SoundPool.Builder()
.setMaxStreams(Phase.values().size)
.build()
activeTimerViewModel.viewModelScope.launch {
if (soundIds.isEmpty()) {
val assetManager = context?.assets ?: return@launch
withContext(Dispatchers.IO) {
Phase.values().forEachIndexed { index, phase ->
soundIds.add(
index,
soundPool.load(assetManager.openFd("audio/${phase.soundFile}"), 1)
)
}
}
}
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean =
@ -94,6 +115,17 @@ class ActiveTimerFragment : Fragment() {
timeRemaining.text = state.timeRemaining
timerSets.text = state.currentSet.toString()
timerRounds.text = state.currentRound.toString()
state.soundId?.let {
playSound(soundIds[it])
}
}
private fun playSound(soundId: Int) {
val context = context ?: return
val audioManager = getSystemService(context, AudioManager::class.java) ?: return
val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC).toFloat()
val volume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC).toFloat() / maxVolume
soundPool.play(soundId, volume, volume, 1, 0, 1f)
}
override fun onDestroyView() {

View file

@ -42,16 +42,7 @@ class ActiveTimerViewModel : ViewModel() {
currentRound = timer.cycles
timeRemaining = timer.warmUpDuration
currentPhase = Phase.WARM_UP
timerState.postValue(
TimerRunningState(
timer,
timeRemaining,
currentSet,
currentRound,
currentPhase,
timerJob != null
)
)
updateTimer()
}
}
@ -220,6 +211,7 @@ sealed class IntervalTimerActiveState {
val timeRemaining: String,
val currentSet: Int,
val currentRound: Int,
val soundId: Int?,
@ColorRes val timerBackground: Int,
@DrawableRes val playPauseIcon: Int
) : IntervalTimerActiveState() {
@ -236,6 +228,9 @@ sealed class IntervalTimerActiveState {
currentSet = currentSet,
currentRound = currentRound,
timerBackground = phase.colorRes,
soundId = if (timerRunning && timeRemaining == timer.durationForPhase(phase))
phase.ordinal
else null,
playPauseIcon = if (timerRunning) R.drawable.ic_pause else R.drawable.ic_play_arrow
)
}

View file

@ -18,14 +18,25 @@ data class IntervalTimer(
val coolDownDuration: Long = TimeUnit.MINUTES.toMillis(5),
val sets: Int = 4,
val cycles: Int = 1
)
) {
fun durationForPhase(phase: Phase) = when (phase) {
Phase.WARM_UP -> warmUpDuration
Phase.LOW_INTENSITY -> lowIntensityDuration
Phase.HIGH_INTENSITY -> highIntensityDuration
Phase.REST -> restDuration
Phase.COOL_DOWN -> coolDownDuration
}
}
enum class Phase(@ColorRes val colorRes: Int) {
WARM_UP(R.color.colorSurface),
LOW_INTENSITY(R.color.colorSurfaceLowIntensity),
HIGH_INTENSITY(R.color.colorSurfaceHighIntensity),
REST(R.color.colorSurfaceRest),
COOL_DOWN(R.color.colorSurfaceCoolDown),
enum class Phase(
@ColorRes val colorRes: Int,
val soundFile: String
) {
WARM_UP(R.color.colorSurface, "warm.mp3"),
LOW_INTENSITY(R.color.colorSurfaceLowIntensity, "low.mp3"),
HIGH_INTENSITY(R.color.colorSurfaceHighIntensity, "high.mp3"),
REST(R.color.colorSurfaceRest, "rest.mp3"),
COOL_DOWN(R.color.colorSurfaceCoolDown, "cool.mp3"),
}
@Dao

View file

@ -52,13 +52,15 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_set"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6" />
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
android:textColor="@color/colorOnSurface" />
<TextView
android:id="@+id/timerSets"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline3"
android:textColor="@color/colorOnSurface"
tools:text="5" />
</LinearLayout>
@ -77,13 +79,15 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_round"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6" />
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
android:textColor="@color/colorOnSurface" />
<TextView
android:id="@+id/timerRounds"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline3"
android:textColor="@color/colorOnSurface"
tools:text="4" />
</LinearLayout>