diff --git a/app/src/main/java/nl/eddydevink/buschauffeurswekker/AlarmActivity.kt b/app/src/main/java/nl/eddydevink/buschauffeurswekker/AlarmActivity.kt index d710cab..9e3438a 100644 --- a/app/src/main/java/nl/eddydevink/buschauffeurswekker/AlarmActivity.kt +++ b/app/src/main/java/nl/eddydevink/buschauffeurswekker/AlarmActivity.kt @@ -11,6 +11,7 @@ import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import nl.eddydevink.buschauffeurswekker.core.AlarmScheduler import nl.eddydevink.buschauffeurswekker.core.Notifier +import nl.eddydevink.buschauffeurswekker.core.Prefs import nl.eddydevink.buschauffeurswekker.core.Ringer import nl.eddydevink.buschauffeurswekker.receivers.AlarmRingReceiver import java.time.Instant @@ -22,6 +23,7 @@ import java.time.format.DateTimeFormatter class AlarmActivity : AppCompatActivity() { private var ringer: Ringer? = null + private var alarmName: String = "" private val clockTick = object : Runnable { override fun run() { @@ -61,6 +63,7 @@ class AlarmActivity : AppCompatActivity() { private fun bindUi() { val name = intent?.getStringExtra(AlarmScheduler.EXTRA_NAME) ?: "Wekker" + alarmName = name val ringTime = intent?.getLongExtra(AlarmScheduler.EXTRA_RING_TIME, System.currentTimeMillis()) ?: System.currentTimeMillis() val snoozed = intent?.getBooleanExtra(AlarmScheduler.EXTRA_SNOOZED, false) ?: false @@ -79,7 +82,8 @@ class AlarmActivity : AppCompatActivity() { } private fun startRinging() { - ringer = Ringer(this).also { it.start() } + val uri = Prefs(this).ringtoneFor(alarmName) + ringer = Ringer(this, uri).also { it.start() } handler.removeCallbacks(clockTick) handler.post(clockTick) } diff --git a/app/src/main/java/nl/eddydevink/buschauffeurswekker/SettingsActivity.kt b/app/src/main/java/nl/eddydevink/buschauffeurswekker/SettingsActivity.kt index b6110a0..9b9a670 100644 --- a/app/src/main/java/nl/eddydevink/buschauffeurswekker/SettingsActivity.kt +++ b/app/src/main/java/nl/eddydevink/buschauffeurswekker/SettingsActivity.kt @@ -1,20 +1,41 @@ package nl.eddydevink.buschauffeurswekker +import android.content.Intent +import android.media.RingtoneManager +import android.net.Uri import android.os.Bundle import android.widget.EditText import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import com.google.android.material.button.MaterialButton import nl.eddydevink.buschauffeurswekker.core.Prefs -/** Apart instellingenscherm: CalDAV-gegevens (URL, gebruiker, app-wachtwoord). */ +/** + * Instellingen: CalDAV-gegevens en per wekker een eigen geluid + * (via de systeem-ringtone-kiezer; "Standaard geluid" = systeem-alarmtoon). + */ class SettingsActivity : AppCompatActivity() { + private lateinit var prefs: Prefs + private var pendingSoundName: String? = null + + private val ringtonePicker = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + val name = pendingSoundName ?: return@registerForActivityResult + pendingSoundName = null + if (result.resultCode == RESULT_OK) { + val uri = result.data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI) as Uri? + prefs.setRingtone(name, uri) + updateSoundRow(name) + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_settings) - val prefs = Prefs(this) + prefs = Prefs(this) findViewById(R.id.et_url).setText(prefs.calendarUrl.orEmpty()) findViewById(R.id.et_user).setText(prefs.username.orEmpty()) findViewById(R.id.et_pass).setText(prefs.password.orEmpty()) @@ -26,5 +47,52 @@ class SettingsActivity : AppCompatActivity() { Toast.makeText(this, "Instellingen opgeslagen", Toast.LENGTH_SHORT).show() finish() } + + findViewById(R.id.btn_sound_opstaan).setOnClickListener { openSoundPicker("Opstaan") } + findViewById(R.id.btn_sound_wally).setOnClickListener { openSoundPicker("Wally") } + findViewById(R.id.btn_sound_vertrek).setOnClickListener { openSoundPicker("Vertrek") } + } + + override fun onResume() { + super.onResume() + SOUND_ALARMS.forEach { updateSoundRow(it) } + } + + private fun openSoundPicker(name: String) { + pendingSoundName = name + val intent = Intent(RingtoneManager.ACTION_RINGTONE_PICKER).apply { + putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_ALARM) + putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, "Geluid voor $name") + putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true) + putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, false) + putExtra( + RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, + prefs.ringtoneFor(name) + ?: RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM), + ) + } + ringtonePicker.launch(intent) + } + + private fun updateSoundRow(name: String) { + val uri = prefs.ringtoneFor(name) + val label = if (uri != null) { + RingtoneManager.getRingtone(this, uri)?.getTitle(this) ?: "Eigen geluid" + } else { + "Standaard" + } + findViewById(SOUND_BUTTON_ID[name]!!).text = "$name — $label" + } + + private companion object { + const val ALARM_OPSTAAN = "Opstaan" + const val ALARM_WALLY = "Wally" + const val ALARM_VERTREK = "Vertrek" + val SOUND_ALARMS = listOf(ALARM_OPSTAAN, ALARM_WALLY, ALARM_VERTREK) + val SOUND_BUTTON_ID = mapOf( + ALARM_OPSTAAN to R.id.btn_sound_opstaan, + ALARM_WALLY to R.id.btn_sound_wally, + ALARM_VERTREK to R.id.btn_sound_vertrek, + ) } } \ No newline at end of file diff --git a/app/src/main/java/nl/eddydevink/buschauffeurswekker/core/Prefs.kt b/app/src/main/java/nl/eddydevink/buschauffeurswekker/core/Prefs.kt index 48e4264..3a5aa9f 100644 --- a/app/src/main/java/nl/eddydevink/buschauffeurswekker/core/Prefs.kt +++ b/app/src/main/java/nl/eddydevink/buschauffeurswekker/core/Prefs.kt @@ -2,6 +2,7 @@ package nl.eddydevink.buschauffeurswekker.core import android.content.Context import android.content.SharedPreferences +import android.net.Uri import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.MasterKey import java.time.Instant @@ -83,6 +84,16 @@ class Prefs(context: Context) { } else null } + /** Gekozen geluid voor een wekker (naam), of null voor het systeemgeluid. */ + fun ringtoneFor(name: String): Uri? = + sp.getString("ringtone_$name", null)?.let { + runCatching { Uri.parse(it) }.getOrNull() + } + + fun setRingtone(name: String, uri: Uri?) { + sp.edit().putString("ringtone_$name", uri?.toString()).apply() + } + fun storePlan(day: LocalDate, alarms: List) { val zone = ZoneId.systemDefault() plannedDayIso = day.toString() diff --git a/app/src/main/java/nl/eddydevink/buschauffeurswekker/core/Ringer.kt b/app/src/main/java/nl/eddydevink/buschauffeurswekker/core/Ringer.kt index 92136ec..13068e3 100644 --- a/app/src/main/java/nl/eddydevink/buschauffeurswekker/core/Ringer.kt +++ b/app/src/main/java/nl/eddydevink/buschauffeurswekker/core/Ringer.kt @@ -10,22 +10,35 @@ import android.os.VibrationEffect import android.os.Vibrator import android.os.VibratorManager -/** Speelt het standaard wekkergeluid (herhalend) en laat de telefoon trillen. */ -class Ringer(private val context: Context) { +/** + * Speelt het wekkergeluid (herhalend) en laat de telefoon trillen. + * Een eigen gekozen geluid ([chosenUri], null = systeem-alarmtoon) wordt + * afgespeeld; valt die weg (verwijderd bestand), dan valt het terug op de + * standaard alarmtoon en daarna de beltoon. + */ +class Ringer( + private val context: Context, + private val chosenUri: Uri? = null, +) { private var player: MediaPlayer? = null private var vibrator: Vibrator? = null fun start() { - val uri = RingtoneManager.getActualDefaultRingtoneUri(context, RingtoneManager.TYPE_ALARM) - ?: RingtoneManager.getActualDefaultRingtoneUri(context, RingtoneManager.TYPE_RINGTONE) - playLooping(uri) + val candidates = listOfNotNull( + chosenUri, + RingtoneManager.getActualDefaultRingtoneUri(context, RingtoneManager.TYPE_ALARM), + RingtoneManager.getActualDefaultRingtoneUri(context, RingtoneManager.TYPE_RINGTONE), + ) + for (uri in candidates) { + if (playLooping(uri)) break + } vibrate() } - private fun playLooping(uri: Uri?) { - if (uri == null) return - try { + /** @return true als het geluid is gestart. */ + private fun playLooping(uri: Uri): Boolean { + return try { player = MediaPlayer().apply { setAudioAttributes( AudioAttributes.Builder() @@ -38,8 +51,10 @@ class Ringer(private val context: Context) { prepare() start() } + true } catch (t: Throwable) { player = null + false } } diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index c33ac38..2fdc3b5 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -46,5 +46,35 @@ android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="Opslaan" /> + + + + + + + + \ No newline at end of file