fade out enabled for set minutes and added volumen bar in ui

This commit is contained in:
pi
2025-11-03 18:07:44 +01:00
parent fa671d6fc4
commit 0d5561feaf
2 changed files with 62 additions and 14 deletions

View File

@@ -7,7 +7,9 @@ import android.media.SoundPool
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.widget.EditText import android.widget.EditText
import android.widget.ProgressBar
import android.widget.TextView import android.widget.TextView
import kotlin.math.pow
class MainActivity : Activity() { class MainActivity : Activity() {
@@ -18,6 +20,9 @@ class MainActivity : Activity() {
private var isPlaying = false private var isPlaying = false
private var remainingMinutes = 0 private var remainingMinutes = 0
private var countdownRunnable: Runnable? = null private var countdownRunnable: Runnable? = null
private var fadeRunnable: Runnable? = null
private var totalMillis: Long = 0
private var startTime: Long = 0
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -25,6 +30,7 @@ class MainActivity : Activity() {
val status = findViewById<TextView>(R.id.statusText) val status = findViewById<TextView>(R.id.statusText)
val input = findViewById<EditText>(R.id.timerInput) val input = findViewById<EditText>(R.id.timerInput)
val volumeBar = findViewById<ProgressBar>(R.id.volumeBar)
val attrs = AudioAttributes.Builder() val attrs = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA) .setUsage(AudioAttributes.USAGE_MEDIA)
@@ -38,59 +44,92 @@ class MainActivity : Activity() {
soundId = soundPool.load(this, R.raw.sound, 1) soundId = soundPool.load(this, R.raw.sound, 1)
// Anfangszustand updateUI(status, volumeBar, "Ready", Color.LTGRAY, 1f)
updateUI(status, "Ready", Color.LTGRAY)
// Das Statusfeld ist jetzt klickbar
status.setOnClickListener { status.setOnClickListener {
if (isPlaying) { if (isPlaying) {
stopSound(status) stopSound(status, volumeBar)
} else { } else {
val min = input.text.toString().toIntOrNull() ?: 0 val min = input.text.toString().toIntOrNull() ?: 0
startSound(min, status) startSound(min, status, volumeBar)
} }
} }
} }
private fun startSound(minutes: Int, status: TextView) { private fun startSound(minutes: Int, status: TextView, volumeBar: ProgressBar) {
stopSound(status) stopSound(status, volumeBar)
streamId = soundPool.play(soundId, 1f, 1f, 1, -1, 1f) streamId = soundPool.play(soundId, 1f, 1f, 1, -1, 1f)
isPlaying = true isPlaying = true
startTime = System.currentTimeMillis()
if (minutes > 0) { if (minutes > 0) {
remainingMinutes = minutes remainingMinutes = minutes
updateUI(status, "Runs for: $remainingMinutes min", Color.GREEN) totalMillis = minutes * 60_000L
updateUI(status, volumeBar, "Runs for: $remainingMinutes min", Color.GREEN, 1f)
// Countdown jede Minute
countdownRunnable = object : Runnable { countdownRunnable = object : Runnable {
override fun run() { override fun run() {
remainingMinutes-- remainingMinutes--
if (remainingMinutes > 0) { if (remainingMinutes > 0) {
updateUI(status, "Runs for: $remainingMinutes min", Color.GREEN) updateUI(status, volumeBar, "Runs for: $remainingMinutes min", Color.GREEN, currentVolume())
handler.postDelayed(this, 60_000L) handler.postDelayed(this, 60_000L)
} else { } else {
stopSound(status) stopSound(status, volumeBar)
} }
} }
} }
handler.postDelayed(countdownRunnable!!, 60_000L) handler.postDelayed(countdownRunnable!!, 60_000L)
// Exponentieller Fade-Out alle 200 ms
fadeRunnable = object : Runnable {
override fun run() {
if (!isPlaying) return
val elapsed = System.currentTimeMillis() - startTime
val progress = (elapsed.toFloat() / totalMillis).coerceIn(0f, 1f)
// exponentielles Absenken: volume = (1 - progress)^2
val volume = (1 - progress).pow(2)
soundPool.setVolume(streamId, volume, volume)
volumeBar.progress = (volume * 100).toInt()
if (progress < 1f) {
handler.postDelayed(this, 200L)
} else { } else {
updateUI(status, "Runs: ∞", Color.GREEN) stopSound(status, volumeBar)
}
}
}
handler.postDelayed(fadeRunnable!!, 200L)
} else {
// unendlich
updateUI(status, volumeBar, "Runs: ∞", Color.GREEN, 1f)
volumeBar.progress = 100
} }
} }
private fun stopSound(status: TextView) { private fun stopSound(status: TextView, volumeBar: ProgressBar) {
if (isPlaying) { if (isPlaying) {
soundPool.stop(streamId) soundPool.stop(streamId)
isPlaying = false isPlaying = false
} }
remainingMinutes = 0 remainingMinutes = 0
handler.removeCallbacks(countdownRunnable ?: Runnable { }) handler.removeCallbacks(countdownRunnable ?: Runnable { })
updateUI(status, "Stopped", Color.RED) handler.removeCallbacks(fadeRunnable ?: Runnable { })
updateUI(status, volumeBar, "Stopped", Color.RED, 0f)
volumeBar.progress = 0
} }
private fun updateUI(view: TextView, text: String, color: Int) { private fun updateUI(view: TextView, bar: ProgressBar, text: String, color: Int, volume: Float) {
view.text = text view.text = text
view.setBackgroundColor(color) view.setBackgroundColor(color)
view.setTextColor(Color.BLACK) view.setTextColor(Color.BLACK)
bar.progress = (volume * 100).toInt()
}
private fun currentVolume(): Float {
val elapsed = System.currentTimeMillis() - startTime
val progress = (elapsed.toFloat() / totalMillis).coerceIn(0f, 1f)
return (1 - progress).pow(2)
} }
override fun onDestroy() { override fun onDestroy() {

View File

@@ -37,4 +37,13 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="20dp"/> android:layout_marginTop="20dp"/>
<ProgressBar
android:id="@+id/volumeBar"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="24dp"
android:layout_marginTop="20dp"
android:max="100"
android:progress="100"/>
</LinearLayout> </LinearLayout>