Agent says it now has the app working properly with a foreground service.
This commit is contained in:
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");
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user