@@ -157,12 +157,15 @@ private static final class AlertSoundHandle {
157157 private static final long SOUND_FADE_IN_MS = 1_200L ;
158158 private static final int SOUND_FADE_STEPS = 8 ;
159159 private static final float SOUND_START_VOLUME = 0.12f ;
160+ // Hard floor: sound must never accidentally go silent even if profile logic has a bug
161+ private static final float MIN_PLAY_VOLUME = 0.3f ;
160162
161163 private final Ringtone ringtone ;
162164 private final MediaPlayer mediaPlayer ;
163165 private final String title ;
164166 private boolean released = false ;
165167 private int playGeneration = 0 ;
168+ private float maxVolume = 1.0f ;
166169
167170 AlertSoundHandle (Ringtone ringtone , MediaPlayer mediaPlayer , String title ) {
168171 this .ringtone = ringtone ;
@@ -178,6 +181,10 @@ String getTitle() {
178181 return (title != null && !title .isEmpty ()) ? title : "alert sound" ;
179182 }
180183
184+ synchronized void setMaxVolume (float vol ) {
185+ maxVolume = Math .max (MIN_PLAY_VOLUME , Math .min (1.0f , vol ));
186+ }
187+
181188 private synchronized void setVolume (float volume ) {
182189 if (released ) {
183190 return ;
@@ -206,7 +213,7 @@ private void scheduleFadeIn(int generation) {
206213 }
207214 }
208215 final float progress = (float ) finalStep / (float ) SOUND_FADE_STEPS ;
209- setVolume (SOUND_START_VOLUME + ((1.0f - SOUND_START_VOLUME ) * progress ));
216+ setVolume (SOUND_START_VOLUME + ((maxVolume - SOUND_START_VOLUME ) * progress ));
210217 }, delayMs , TimeUnit .MILLISECONDS );
211218 }
212219 }
@@ -520,8 +527,7 @@ Ringtone mkring(String uristr, int kind, boolean disturb) {
520527 var ring = setring (uristr , defaults [kind ]);
521528 if (android .os .Build .VERSION .SDK_INT >= 21 ) {
522529 try {
523- // Use ALARM stream only if disturb=true AND USEALARM is enabled
524- boolean useAlarmStream = (!AlertType .Companion .isLegacyOnlyId (kind ) && getUSEALARM () && disturb );
530+ boolean useAlarmStream = (!AlertType .Companion .isLegacyOnlyId (kind ) && disturb );
525531 ring .setAudioAttributes (useAlarmStream ? ScanNfcV .audioattributes : notification_audio );
526532 } catch (Throwable e ) {
527533 Log .stack (LOG_ID , "mkring" , e );
@@ -1725,10 +1731,7 @@ private synchronized void playringhier(AlertSoundHandle soundHandle, int duratio
17251731 final CurrentDisplaySource .Snapshot current = resolveNotificationCurrentSnapshot ();
17261732 if (current != null ) {
17271733 SuperGattCallback .talker .speak (current .getPrimaryStr (),
1728- getUSEALARM () ? ScanNfcV .audioattributes : notification_audio );
1729- // Applic.scheduler.schedule( () -> SuperGattCallback.talker.speak(glu.value,
1730- // getUSEALARM()?ScanNfcV.audioattributes:notification_audio), 50,
1731- // TimeUnit.MILLISECONDS);
1734+ disturb ? ScanNfcV .audioattributes : notification_audio );
17321735 }
17331736 }
17341737 }
@@ -1812,7 +1815,7 @@ private synchronized void playringhier(AlertSoundHandle soundHandle, int duratio
18121815 if (current != null ) {
18131816 Applic .scheduler .schedule (
18141817 () -> SuperGattCallback .talker .speak (current .getPrimaryStr (),
1815- getUSEALARM () ? ScanNfcV .audioattributes : notification_audio ),
1818+ disturb ? ScanNfcV .audioattributes : notification_audio ),
18161819 300 , TimeUnit .MILLISECONDS );
18171820 } else
18181821 doTurnFocusoff ();
@@ -1858,6 +1861,7 @@ private synchronized void playringhier(AlertSoundHandle soundHandle, int duratio
18581861 if (sound ) {
18591862 if (doplaysound [0 ] && hasSoundHandle ) {
18601863 if (soundHandle != null ) {
1864+ soundHandle .setMaxVolume (soundVolumeForProfile (resolvedHapticProfile ));
18611865 soundHandle .play ();
18621866 } else if (doLog ) {
18631867 Log .w (LOG_ID , "playringhier: sound handle is null" );
@@ -1938,6 +1942,17 @@ private String getHapticProfile(int kind) {
19381942 }
19391943 }
19401944
1945+ // SOFT=0.4, STEADY=0.7, STRONG/ESCALATING=1.0 — matches the old dead getVolumeFromProfile() intent.
1946+ // Never returns below AlertSoundHandle.MIN_PLAY_VOLUME (enforced again in setMaxVolume as a safety net).
1947+ private static float soundVolumeForProfile (String profile ) {
1948+ if (profile == null ) return 1.0f ;
1949+ switch (profile .toUpperCase ()) {
1950+ case "SOFT" : case "LOW" : return 0.4f ;
1951+ case "STEADY" : case "MEDIUM" : return 0.7f ;
1952+ default : return 1.0f ;
1953+ }
1954+ }
1955+
19411956 void mksound (int kind ) {
19421957 String ringUri = null ;
19431958 try {
@@ -1970,8 +1985,7 @@ void mksound(int kind) {
19701985 final boolean sound = p .getBoolean ("alert_" + kind + "_sound" , defSound );
19711986 final boolean vibration = p .getBoolean ("alert_" + kind + "_vibration" , defVibrate );
19721987
1973- final boolean dist = isWearable || getalarmdisturb (kind ); // DND might need Prefs too, but keeping Natives for
1974- // now
1988+ final boolean dist = isWearable || p .getBoolean ("alert_" + kind + "_dnd" , getalarmdisturb (kind ));
19751989 final boolean useAlarmStream = shouldUseAlarmAudioStream (kind , dist );
19761990 final AlertSoundHandle soundHandle = sound ? buildAlertSoundHandle (ringUri , kind , useAlarmStream ) : null ;
19771991
0 commit comments