fade out now exponential and optional when minutes set

This commit is contained in:
pi
2025-11-03 18:17:10 +01:00
parent 0d5561feaf
commit d14ec42a92
2 changed files with 59 additions and 42 deletions

View File

@@ -10,6 +10,7 @@ import android.widget.EditText
import android.widget.ProgressBar import android.widget.ProgressBar
import android.widget.TextView import android.widget.TextView
import kotlin.math.pow import kotlin.math.pow
import kotlin.math.max
class MainActivity : Activity() { class MainActivity : Activity() {
@@ -18,11 +19,11 @@ class MainActivity : Activity() {
private var streamId = 0 private var streamId = 0
private val handler = Handler() private val handler = Handler()
private var isPlaying = false private var isPlaying = false
private var remainingMinutes = 0 private var fadeEnabled = false
private var countdownRunnable: Runnable? = null
private var fadeRunnable: Runnable? = null
private var totalMillis: Long = 0 private var totalMillis: Long = 0
private var startTime: Long = 0 private var startTime: Long = 0
private var fadeRunnable: Runnable? = null
private var countdownRunnable: Runnable? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -30,6 +31,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 fadeToggle = findViewById<TextView>(R.id.fadeToggle)
val volumeBar = findViewById<ProgressBar>(R.id.volumeBar) val volumeBar = findViewById<ProgressBar>(R.id.volumeBar)
val attrs = AudioAttributes.Builder() val attrs = AudioAttributes.Builder()
@@ -44,14 +46,18 @@ class MainActivity : Activity() {
soundId = soundPool.load(this, R.raw.sound, 1) soundId = soundPool.load(this, R.raw.sound, 1)
updateUI(status, volumeBar, "Ready", Color.LTGRAY, 1f) updateUI(status, volumeBar, "Bereit", Color.LTGRAY, 1f)
fadeToggle.setOnClickListener {
fadeEnabled = !fadeEnabled
fadeToggle.text = "Fade-Out: " + if (fadeEnabled) "ON" else "OFF"
}
status.setOnClickListener { status.setOnClickListener {
if (isPlaying) { if (isPlaying) {
stopSound(status, volumeBar) stopSound(status, volumeBar)
} else { } else {
val min = input.text.toString().toIntOrNull() ?: 0 val minutes = input.text.toString().toIntOrNull() ?: 0
startSound(min, status, volumeBar) startSound(minutes, status, volumeBar)
} }
} }
} }
@@ -63,59 +69,65 @@ class MainActivity : Activity() {
startTime = System.currentTimeMillis() startTime = System.currentTimeMillis()
if (minutes > 0) { if (minutes > 0) {
remainingMinutes = minutes
totalMillis = minutes * 60_000L totalMillis = minutes * 60_000L
updateUI(status, volumeBar, "Runs for: $remainingMinutes min", Color.GREEN, 1f) updateCountdown(minutes * 60, status)
// Countdown jede Sekunde
// Countdown jede Minute
countdownRunnable = object : Runnable { countdownRunnable = object : Runnable {
override fun run() { override fun run() {
remainingMinutes-- val elapsedSec = ((System.currentTimeMillis() - startTime) / 1000).toInt()
if (remainingMinutes > 0) { val remainingSec = max(minutes * 60 - elapsedSec, 0)
updateUI(status, volumeBar, "Runs for: $remainingMinutes min", Color.GREEN, currentVolume()) updateCountdown(remainingSec, status)
handler.postDelayed(this, 60_000L) if (remainingSec > 0) {
handler.postDelayed(this, 1000L)
} else { } else {
stopSound(status, volumeBar) stopSound(status, volumeBar)
} }
} }
} }
handler.postDelayed(countdownRunnable!!, 60_000L) handler.postDelayed(countdownRunnable!!, 1000L)
// Exponentieller Fade-Out alle 200 ms // Fade-Out (optional)
fadeRunnable = object : Runnable { if (fadeEnabled) {
override fun run() { fadeRunnable = object : Runnable {
if (!isPlaying) return override fun run() {
val elapsed = System.currentTimeMillis() - startTime if (!isPlaying) return
val progress = (elapsed.toFloat() / totalMillis).coerceIn(0f, 1f) val elapsed = System.currentTimeMillis() - startTime
// exponentielles Absenken: volume = (1 - progress)^2 val progress = (elapsed.toFloat() / totalMillis).coerceIn(0f, 1f)
val volume = (1 - progress).pow(2) val volume = (1 - progress).pow(2) // exponentielles Fade
soundPool.setVolume(streamId, volume, volume) soundPool.setVolume(streamId, volume, volume)
volumeBar.progress = (volume * 100).toInt() volumeBar.progress = (volume * 100).toInt()
if (progress < 1f) { if (progress < 1f) {
handler.postDelayed(this, 200L) handler.postDelayed(this, 200L)
} else { }
stopSound(status, volumeBar)
} }
} }
handler.postDelayed(fadeRunnable!!, 200L)
} else {
volumeBar.progress = 100
} }
handler.postDelayed(fadeRunnable!!, 200L)
} else { } else {
// unendlich // unendlich
updateUI(status, volumeBar, "Runs: ∞", Color.GREEN, 1f) updateUI(status, volumeBar, "Läuft: ∞", Color.GREEN, 1f)
volumeBar.progress = 100 volumeBar.progress = 100
} }
} }
private fun updateCountdown(remainingSec: Int, status: TextView) {
val minutes = remainingSec / 60
val seconds = remainingSec % 60
status.text = String.format("Läuft: %02d:%02d", minutes, seconds)
status.setBackgroundColor(Color.GREEN)
status.setTextColor(Color.BLACK)
}
private fun stopSound(status: TextView, volumeBar: ProgressBar) { private fun stopSound(status: TextView, volumeBar: ProgressBar) {
if (isPlaying) { if (isPlaying) {
soundPool.stop(streamId) soundPool.stop(streamId)
isPlaying = false isPlaying = false
} }
remainingMinutes = 0 handler.removeCallbacks(countdownRunnable ?: Runnable {})
handler.removeCallbacks(countdownRunnable ?: Runnable { }) handler.removeCallbacks(fadeRunnable ?: Runnable {})
handler.removeCallbacks(fadeRunnable ?: Runnable { }) updateUI(status, volumeBar, "Gestoppt", Color.RED, 0f)
updateUI(status, volumeBar, "Stopped", Color.RED, 0f)
volumeBar.progress = 0 volumeBar.progress = 0
} }
@@ -126,12 +138,6 @@ class MainActivity : Activity() {
bar.progress = (volume * 100).toInt() 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() {
super.onDestroy() super.onDestroy()
soundPool.release() soundPool.release()

View File

@@ -38,6 +38,17 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="20dp"/> android:layout_marginTop="20dp"/>
<TextView
android:id="@+id/fadeToggle"
android:text="Fade-Out: OFF"
android:gravity="center"
android:padding="12dp"
android:layout_marginTop="20dp"
android:background="#AAAAAA"
android:textColor="#000000"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<ProgressBar <ProgressBar
android:id="@+id/volumeBar" android:id="@+id/volumeBar"
style="@android:style/Widget.ProgressBar.Horizontal" style="@android:style/Widget.ProgressBar.Horizontal"