Agent says it now has the app working properly with a foreground service.

This commit is contained in:
me committed 2026-11-08 15:03:46 -07:00
1 parent f59bd78716
commit 3a057f0878
2 files changed
+168 -132

No files matched your search

@@ -1,7 +1,10 @@
package com.hyperling.apps.infinitetimer;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.media.AudioManager;
@@ -66,6 +69,9 @@ public class MainActivity extends AppCompatActivity {
AudioManager audioManager;
TextView tvSeekBar;
// Local broadcast receiver to update UI while app is foregrounded
private BroadcastReceiver timerUpdateReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
String tag = "onCreate";
@@ -90,6 +96,9 @@ public class MainActivity extends AppCompatActivity {
if (DEBUG) Log.d(tag, "Got strings.");
/***** Setup Local Broadcast Receiver *****/
setupTimerUpdateReceiver();
/***** Shared Preferences *****/
sharedPreferences = getSharedPreferences(keySharedPreferences, MODE_PRIVATE);
DEBUG = sharedPreferences.getBoolean(keyDebug, true);
@@ -219,6 +228,20 @@ public class MainActivity extends AppCompatActivity {
if (DEBUG) Log.d(tag, "Finished");
}
private void setupTimerUpdateReceiver() {
timerUpdateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) Log.d(TAG + "Receiver", "Received service update");
// Update UI immediately with latest countdown state from Service
String remainingTime = intent.getStringExtra("remainingTime");
if (remainingTime != null && chronometer != null) {
chronometer.setText(remainingTime);
}
}
};
}
private void doTimeChooser(){
String tag = TAG + "doTimeChooser";
if (DEBUG) Log.d(tag, "Starting");
@@ -351,29 +374,26 @@ public class MainActivity extends AppCompatActivity {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putBoolean(keyStartStop, start);
editor.putBoolean(keyAppOpen, true);
editor.putBoolean(keyServiceRunning, false);
editor.apply();
// hours:minutes:seconds should be disabled during count down.
flipInputTexts();
if (buttonText.equals(getString(R.string.btnStart))){
incrementTime.sendEmptyMessage(0);
setLoopInterval();
resetTimeLeft();
setStartTime();
// Start the background alert service using Alert manager
long interval = longLoopInterval > 1000 ? longLoopInterval : 60000; // Minimum 1 minute interval for alarms
if (Alert.isExactAlarmAllowed(this)) {
Alert.start(this, interval);
if (DEBUG) Log.d(tag, "Starting ForegroundService with interval: " + interval);
// Delegate countdown logic to ForegroundService to survive Doze mode on SDK 36+
Intent svcIntent = new Intent(this, TimerForegroundService.class);
svcIntent.setAction(Alert.ACTION_TIMER);
svcIntent.putExtra(TimerForegroundService.EXTRA_INTERVAL_MS, longLoopInterval);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(svcIntent);
} else {
if (DEBUG) Log.w(tag, "Cannot start alert: Exact alarm permission denied.");
new AlertDialog.Builder(MainActivity.this)
.setTitle("Permission Required")
.setMessage("Please grant 'All files access' or 'Alarm' permissions in system settings for this feature to work.")
.setPositiveButton("OK", (d, w) -> d.dismiss())
.show();
startService(svcIntent);
}
}
@@ -384,13 +404,11 @@ public class MainActivity extends AppCompatActivity {
etMinutes.setEnabled(false);
etSeconds.setEnabled(false);
}
else if (buttonText.equals(getString(R.string.btnStop))){
setChronometer(longLoopInterval);
}
else if (buttonText.equals(getString(R.string.btnResume))){
setStartTime();
incrementTime.sendEmptyMessage(0);
btnResetPause.setEnabled(true);
else if (buttonText.equals(getString(R.string.btnStop)) || buttonText.equals(getString(R.string.btnResume))){
// Stop the Foreground Service and cancel alarms
Intent svcIntent = new Intent(this, TimerForegroundService.class);
stopService(svcIntent);
Alert.stop(this);
}
flipStartStopButton();
@@ -487,84 +505,7 @@ public class MainActivity extends AppCompatActivity {
if (DEBUG) Log.d(tag, "Finished");
}
private final Handler incrementTime = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
String tag = TAG + "handleMessage";
if (DEBUG) Log.d(tag, "Starting");
super.handleMessage(msg);
appOpen = sharedPreferences.getBoolean(keyAppOpen, true);
if (appOpen) {
sharedPreferences.edit().putBoolean(keyServiceRunning, false).apply();
} else {
sharedPreferences.edit().putBoolean(keyServiceRunning, true).apply();
}
start = sharedPreferences.getBoolean(keyStartStop, false);
if (start){
//start = !start;
//sharedPreferences.edit().putBoolean(keyStartStop, false).apply();
// Calculate new time and text
setTimeLeft();
if (DEBUG) Log.d(tag, "longTimeLeft=" + longTimeLeft);
// Check if we need to beep
if (longTimeLeft < 1000){
playSound();
resetTimeLeft();
setStartTime();
if (DEBUG) {
Log.d(tag, "longLoopInterval=" + longLoopInterval);
Log.d(tag, "longStartTime=" + longStartTime);
Log.d(tag, "longEndTime=" + longEndTime);
Log.d(tag, "longTimeLeft=" + longTimeLeft);
Log.d(tag, "waitInterval=" + waitInterval);
Log.d(tag, "start=" + start);
//chronometer.setText("Test");
//setChronometer(200);
/*
start=false;
startStopChronometer("Start");
*/
}
}
// Wait
Thread t = wait(tag);
t.start();
}
if (DEBUG) Log.d(tag, "Finished");
}
@NonNull
private Thread wait(String tag) {
Runnable task = new Runnable() {
@Override
public void run() {
synchronized (this){
try{
wait(waitInterval);
if (DEBUG) Log.d(tag, "Done waiting.");
}
catch (Exception e){
e.printStackTrace();
if (DEBUG) Log.d(tag, "Failed to wait.");
}
}
//sharedPreferences.edit().putBoolean(keyStartStop, true).apply();
incrementTime.sendEmptyMessage(0);
//startStopChronometer("Start");
}
};
return new Thread(task);
}
};
// Removed local countdown Handler - timing and background survival is now delegated to TimerForegroundService
private void setStartTime(){
String tag = TAG + "setStartTime";
@@ -728,6 +669,8 @@ public class MainActivity extends AppCompatActivity {
protected void onPause() {
String tag = TAG + "onPause";
if (DEBUG) Log.d(tag, "Starting");
unregisterReceiver(timerUpdateReceiver);
stringLoopInterval =
etHours.getText().toString() + ":" +
@@ -758,6 +701,9 @@ public class MainActivity extends AppCompatActivity {
recoverScreen();
syncVolume();
// Register receiver to get live updates from ForegroundService while app is visible
registerReceiver(timerUpdateReceiver, new IntentFilter(TimerForegroundService.class.getName() + ".UPDATE"));
if (DEBUG) Log.d(tag, "Finished");
}