From 4ede74e7a2a86e1204fc4523ecc8b9af1128d9d9 Mon Sep 17 00:00:00 2001 From: Hyperling Date: Fri, 6 Nov 2026 13:35:08 -0700 Subject: [PATCH] Agent's attempt to ensure the sound gets played every interval (adding Looper to Handler and moving release() from playSound to onDestroy) as well as ensuring it's not delayed (using play() rather than start()). --- .../apps/infinitetimer/MainActivity.java | 49 +++++++++++++++---- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/hyperling/apps/infinitetimer/MainActivity.java b/app/src/main/java/com/hyperling/apps/infinitetimer/MainActivity.java index d2065f3..09dbbd2 100755 --- a/app/src/main/java/com/hyperling/apps/infinitetimer/MainActivity.java +++ b/app/src/main/java/com/hyperling/apps/infinitetimer/MainActivity.java @@ -13,6 +13,7 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.Looper; import android.os.Message; import android.provider.Settings; import android.support.v7.app.AlertDialog; @@ -55,6 +56,12 @@ public class MainActivity extends AppCompatActivity { TextView tvDot; Uri ringtoneUri; + + // RingtoneManager reference for consistent notification sound playback across loops + private android.media.Ringtone notificationRingtone; + + // For proper sound lifecycle management across timer loops + private Ringtone ringtonePlayer; MediaPlayer mediaPlayer; SeekBar seekBar; @@ -482,7 +489,7 @@ public class MainActivity extends AppCompatActivity { if (DEBUG) Log.d(tag, "Finished"); } - private final Handler incrementTime = new Handler(){ + private final android.os.Handler incrementTime = new android.os.Handler(android.os.Looper.getMainLooper()){ @Override public void handleMessage(Message msg) { String tag = TAG + "handleMessage"; @@ -605,17 +612,34 @@ public class MainActivity extends AppCompatActivity { if (DEBUG) Log.d(tag, "Starting"); try { - // Safely instantiate MediaPlayer locally to prevent NPE - Uri uri = android.media.RingtoneManager.getDefaultUri(android.media.RingtoneManager.TYPE_NOTIFICATION); - MediaPlayer localPlayer = MediaPlayer.create(this, uri); + // Stop any existing notification ringtone first to prevent overlap + if (notificationRingtone != null && notificationRingtone.isPlaying()) { + notificationRingtone.stop(); + } - if (localPlayer != null) { - localPlayer.start(); - localPlayer.release(); // Clean up resources immediately + // Use RingtoneManager which handles async playback lifecycle properly + Uri uri = android.media.RingtoneManager.getDefaultUri(android.media.RingtoneManager.TYPE_NOTIFICATION); + notificationRingtone = android.media.RingtoneManager.getRingtone(this, uri); + + if (notificationRingtone != null) { + // Ringtone.play() is asynchronous and handles its own lifecycle, + // avoiding the MediaPlayer.create() + immediate release bug that + // caused silence on subsequent loops + notificationRingtone.play(); } else { - // Fallback for edge cases where create() fails - android.media.Ringtone ringtone = android.media.RingtoneManager.getRingtone(this, uri); - if (ringtone != null) ringtone.play(); + // Fallback: if getRingtone fails, try direct MediaPlayer with proper lifecycle + MediaPlayer localPlayer = MediaPlayer.create(this, uri); + if (localPlayer != null) { + localPlayer.start(); + final MediaPlayer fp = localPlayer; + // Release after playback completes (use delay as a safety net) + new android.os.Handler(android.os.Looper.getMainLooper()).postDelayed( + new Runnable() { @Override public void run() { + if (fp.isPlaying()) fp.stop(); + fp.release(); + }}, 2000 + ); + } } } catch (Exception e) { sharedPreferences.edit().putBoolean(keyStartStop, false).apply(); @@ -744,6 +768,11 @@ public class MainActivity extends AppCompatActivity { Alert.stop(this); if (mediaPlayer != null) { mediaPlayer.release(); mediaPlayer = null; } + // Clean up ringtone player to prevent resource leak across config changes + if (notificationRingtone != null && notificationRingtone.isPlaying()) { + notificationRingtone.stop(); + } + if (DEBUG) Log.d(tag, "Done with local code, calling super.."); super.onDestroy();