diff --git a/README.md b/README.md
index c19bbdc..8a92f25 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/app/src/main/java/com/example/lostmyphone/RingConfig.kt b/app/src/main/java/com/example/lostmyphone/RingConfig.kt
index 7e9aad3..379a5fe 100644
--- a/app/src/main/java/com/example/lostmyphone/RingConfig.kt
+++ b/app/src/main/java/com/example/lostmyphone/RingConfig.kt
@@ -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,
diff --git a/app/src/main/java/com/example/lostmyphone/RingService.kt b/app/src/main/java/com/example/lostmyphone/RingService.kt
index d2fc68b..1bec219 100644
--- a/app/src/main/java/com/example/lostmyphone/RingService.kt
+++ b/app/src/main/java/com/example/lostmyphone/RingService.kt
@@ -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.
)
diff --git a/app/src/main/java/com/example/lostmyphone/SettingsActivity.kt b/app/src/main/java/com/example/lostmyphone/SettingsActivity.kt
index 9412087..588679a 100644
--- a/app/src/main/java/com/example/lostmyphone/SettingsActivity.kt
+++ b/app/src/main/java/com/example/lostmyphone/SettingsActivity.kt
@@ -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()
diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml
index 30bd3b1..4eb1b52 100644
--- a/app/src/main/res/layout/activity_settings.xml
+++ b/app/src/main/res/layout/activity_settings.xml
@@ -138,6 +138,33 @@
android:text="@string/label_duration_range"
android:textSize="12sp" />
+
+
+
+
+
+
+
+
+