feat: configurable ring volume
Adds a volume slider (1-100% of the alarm stream) to Settings. RingService plays at the configured percentage instead of always maxing STREAM_ALARM.
This commit is contained in:
parent
e83c7c3344
commit
4a04052845
6 changed files with 71 additions and 3 deletions
|
|
@ -30,6 +30,9 @@ Choose between:
|
|||
Playback runs for the configured number of seconds (`5–300`, default **30**), then
|
||||
the previous volume is restored.
|
||||
|
||||
### Volume
|
||||
The ring plays at a configurable percentage (`1–100%`) of the alarm-stream volume.
|
||||
|
||||
---
|
||||
|
||||
## How Do Not Disturb Override Works (read this)
|
||||
|
|
@ -50,7 +53,7 @@ Only after the user flips that toggle will a trigger actually ring.
|
|||
|
||||
When access **is** granted, `RingService`:
|
||||
- saves the current **alarm-stream** volume,
|
||||
- forces `AudioManager.STREAM_ALARM` to max,
|
||||
- sets `AudioManager.STREAM_ALARM` to the configured volume percentage,
|
||||
- requests `AUDIOFOCUS_GAIN_TRANSIENT` using `AudioAttributes.USAGE_ALARM`,
|
||||
- plays on the **alarm stream** — the one stream Do Not Disturb does **not**
|
||||
silence by default (media/music *is* silenced), which is what lets the ring
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ object RingConfig {
|
|||
private const val KEY_RINGTONE_URI = "ringtone_uri"
|
||||
private const val KEY_SONG_URI = "song_uri"
|
||||
private const val KEY_DURATION_SEC = "duration_sec"
|
||||
private const val KEY_VOLUME_PERCENT = "volume_percent"
|
||||
|
||||
const val SOUND_RINGTONE = "ringtone"
|
||||
const val SOUND_SONG = "song"
|
||||
|
|
@ -82,6 +83,12 @@ object RingConfig {
|
|||
fun setDurationSeconds(context: Context, seconds: Int): Boolean =
|
||||
prefs(context).edit().putInt(KEY_DURATION_SEC, seconds.coerceIn(MIN_DURATION_SEC, MAX_DURATION_SEC)).commit()
|
||||
|
||||
fun volumePercent(context: Context): Int =
|
||||
prefs(context).getInt(KEY_VOLUME_PERCENT, 100).coerceIn(1, 100)
|
||||
|
||||
fun setVolumePercent(context: Context, percent: Int): Boolean =
|
||||
prefs(context).edit().putInt(KEY_VOLUME_PERCENT, percent.coerceIn(1, 100)).commit()
|
||||
|
||||
/**
|
||||
* The Uri the RingService should actually play.
|
||||
* - ringtone mode -> the system default ringtone,
|
||||
|
|
|
|||
|
|
@ -158,11 +158,12 @@ class RingService : Service() {
|
|||
// Step 1: save current alarm-stream volume so we can restore it later.
|
||||
originalVolume = audioManager.getStreamVolume(AudioManager.STREAM_ALARM)
|
||||
|
||||
// Step 2: force the alarm stream to max volume.
|
||||
// Step 2: force the alarm stream to the configured volume percentage.
|
||||
val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM)
|
||||
val targetVolume = (maxVolume * RingConfig.volumePercent(this) / 100).coerceAtLeast(1)
|
||||
audioManager.setStreamVolume(
|
||||
AudioManager.STREAM_ALARM,
|
||||
maxVolume,
|
||||
targetVolume,
|
||||
0 // flags = 0 so we do NOT show the system volume UI.
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ class SettingsActivity : AppCompatActivity() {
|
|||
private lateinit var tvSelectedSound: TextView
|
||||
private lateinit var sbDuration: SeekBar
|
||||
private lateinit var tvDurationValue: TextView
|
||||
private lateinit var sbVolume: SeekBar
|
||||
private lateinit var tvVolumeValue: TextView
|
||||
private lateinit var btnSave: Button
|
||||
|
||||
private var pickedRingtoneUri: Uri? = null
|
||||
|
|
@ -88,10 +90,13 @@ class SettingsActivity : AppCompatActivity() {
|
|||
tvSelectedSound = findViewById(R.id.tvSelectedSound)
|
||||
sbDuration = findViewById(R.id.sbDuration)
|
||||
tvDurationValue = findViewById(R.id.tvDurationValue)
|
||||
sbVolume = findViewById(R.id.sbVolume)
|
||||
tvVolumeValue = findViewById(R.id.tvVolumeValue)
|
||||
btnSave = findViewById(R.id.btnSave)
|
||||
|
||||
loadCurrentValues()
|
||||
setupDurationSeekBar()
|
||||
setupVolumeSeekBar()
|
||||
setupPickers()
|
||||
|
||||
btnSave.setOnClickListener { saveSettings() }
|
||||
|
|
@ -108,6 +113,9 @@ class SettingsActivity : AppCompatActivity() {
|
|||
|
||||
sbDuration.progress = RingConfig.durationSeconds(this) - RingConfig.MIN_DURATION_SEC
|
||||
updateDurationLabel()
|
||||
|
||||
sbVolume.progress = RingConfig.volumePercent(this) - 1
|
||||
updateVolumeLabel()
|
||||
updateSoundSummary()
|
||||
}
|
||||
|
||||
|
|
@ -129,6 +137,22 @@ class SettingsActivity : AppCompatActivity() {
|
|||
tvDurationValue.text = getString(R.string.duration_value, seconds)
|
||||
}
|
||||
|
||||
private fun setupVolumeSeekBar() {
|
||||
sbVolume.max = 99 // 1..100
|
||||
sbVolume.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
updateVolumeLabel()
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {}
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {}
|
||||
})
|
||||
}
|
||||
|
||||
private fun updateVolumeLabel() {
|
||||
tvVolumeValue.text = getString(R.string.volume_value, sbVolume.progress + 1)
|
||||
}
|
||||
|
||||
private fun setupPickers() {
|
||||
btnPickRingtone.setOnClickListener {
|
||||
val intent = Intent(RingtoneManager.ACTION_RINGTONE_PICKER).apply {
|
||||
|
|
@ -186,12 +210,14 @@ class SettingsActivity : AppCompatActivity() {
|
|||
|
||||
val mode = if (rdbSong.isChecked) RingConfig.SOUND_SONG else RingConfig.SOUND_RINGTONE
|
||||
val seconds = sbDuration.progress + RingConfig.MIN_DURATION_SEC
|
||||
val volume = sbVolume.progress + 1
|
||||
|
||||
RingConfig.setTriggerText(this, trigger)
|
||||
RingConfig.setSoundMode(this, mode)
|
||||
RingConfig.setRingtoneUri(this, pickedRingtoneUri)
|
||||
RingConfig.setSongUri(this, pickedSongUri)
|
||||
RingConfig.setDurationSeconds(this, seconds)
|
||||
RingConfig.setVolumePercent(this, volume)
|
||||
|
||||
Toast.makeText(this, R.string.toast_saved, Toast.LENGTH_SHORT).show()
|
||||
finish()
|
||||
|
|
|
|||
|
|
@ -138,6 +138,33 @@
|
|||
android:text="@string/label_duration_range"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<!-- Volume -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:text="@string/label_volume"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvVolumeValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sbVolume"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/label_volume_range"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnSave"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
|||
|
|
@ -57,6 +57,10 @@
|
|||
<string name="duration_value">%1$d seconds</string>
|
||||
<string name="label_duration_range">Between 5 and 300 seconds.</string>
|
||||
|
||||
<string name="label_volume">Ring volume</string>
|
||||
<string name="volume_value">%1$d%% of the alarm volume</string>
|
||||
<string name="label_volume_range">Between 1 and 100%.</string>
|
||||
|
||||
<string name="btn_save_settings">Save Settings</string>
|
||||
|
||||
<string name="toast_ringtone_default">Using default system ringtone.</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue