Skip to content

Commit c1168e2

Browse files
authored
Merge pull request #39 from robster7674/pr/audio-tts
Audio & TTS improvements
2 parents 08f8e34 + 1b0de4d commit c1168e2

6 files changed

Lines changed: 371 additions & 40 deletions

File tree

Common/src/main/java/tk/glucodata/Notify.java

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package tk.glucodata
2+
3+
import android.content.Context
4+
import java.util.Calendar
5+
6+
object SpeakSchedule {
7+
private const val PREFS = "tk.glucodata_preferences"
8+
private const val KEY_ENABLED = "voice_schedule_enabled"
9+
private const val KEY_START = "voice_schedule_start_minutes"
10+
private const val KEY_END = "voice_schedule_end_minutes"
11+
12+
private const val DEFAULT_START = 0 // 00:00
13+
private const val DEFAULT_END = 22 * 60 // 22:00
14+
15+
private fun prefs(context: Context) =
16+
context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
17+
18+
fun isEnabled(context: Context): Boolean =
19+
prefs(context).getBoolean(KEY_ENABLED, false)
20+
21+
fun setEnabled(context: Context, enabled: Boolean) {
22+
prefs(context).edit().putBoolean(KEY_ENABLED, enabled).apply()
23+
}
24+
25+
fun getStartMinutes(context: Context): Int =
26+
prefs(context).getInt(KEY_START, DEFAULT_START)
27+
28+
fun getEndMinutes(context: Context): Int =
29+
prefs(context).getInt(KEY_END, DEFAULT_END)
30+
31+
fun setStartMinutes(context: Context, minutes: Int) {
32+
prefs(context).edit().putInt(KEY_START, minutes.coerceIn(0, 1439)).apply()
33+
}
34+
35+
fun setEndMinutes(context: Context, minutes: Int) {
36+
prefs(context).edit().putInt(KEY_END, minutes.coerceIn(0, 1440)).apply()
37+
}
38+
39+
fun isWithinSchedule(context: Context): Boolean {
40+
if (!isEnabled(context)) return true
41+
val cal = Calendar.getInstance()
42+
val nowMinutes = cal.get(Calendar.HOUR_OF_DAY) * 60 + cal.get(Calendar.MINUTE)
43+
val start = getStartMinutes(context)
44+
val end = getEndMinutes(context)
45+
if (start == end) return true // equal = all day, no restriction
46+
return if (start < end) nowMinutes in start until end
47+
else nowMinutes >= start || nowMinutes < end // spans midnight
48+
}
49+
50+
fun formatMinutes(minutes: Int): String {
51+
val h = (minutes / 60).coerceIn(0, 23)
52+
val m = (minutes % 60).coerceIn(0, 59)
53+
return String.format("%02d:%02d", h, m)
54+
}
55+
}

0 commit comments

Comments
 (0)