86 lines
3.4 KiB
Java
86 lines
3.4 KiB
Java
package com.hyperling.apps.infinitetimer;
|
|
|
|
import android.app.AlarmManager;
|
|
import android.app.PendingIntent;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.os.Build;
|
|
import android.provider.Settings;
|
|
import android.util.Log;
|
|
|
|
public class Alert {
|
|
private static final String TAG = "Alert";
|
|
public static final String ACTION_TIMER = "com.hyperling.apps.infinitetimer.ACTION_TIMER";
|
|
|
|
/**
|
|
* Sets a persistent alarm that triggers the TimerReceiver.
|
|
* Uses setExactAndAllowWhileIdle to bypass Android Doze mode.
|
|
*/
|
|
public static void start(Context context, long intervalMs) {
|
|
Intent intent = new Intent(context, TimerReceiver.class);
|
|
intent.setAction(ACTION_TIMER);
|
|
|
|
// FLAG_IMMUTABLE required for Android 12+ (API 31+)
|
|
int flags = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_CANCEL_CURRENT;
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
flags |= PendingIntent.FLAG_IMMUTABLE;
|
|
}
|
|
|
|
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, flags);
|
|
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
|
|
|
if (alarmManager != null) {
|
|
long triggerTime = System.currentTimeMillis() + intervalMs;
|
|
// Exact alarm ensures it triggers even in Doze mode
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
|
|
} else {
|
|
alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
|
|
}
|
|
Log.d(TAG, "Alarm set for " + intervalMs + "ms at " + triggerTime);
|
|
} else {
|
|
Log.e(TAG, "Could not get AlarmManager service");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cancels the timer. If the app is killed, alarms are dropped automatically.
|
|
*/
|
|
public static void stop(Context context) {
|
|
Intent intent = new Intent(context, TimerReceiver.class);
|
|
int flags = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_CANCEL_CURRENT;
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
flags |= PendingIntent.FLAG_IMMUTABLE;
|
|
}
|
|
|
|
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, flags);
|
|
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
|
|
|
if (alarmManager != null) {
|
|
alarmManager.cancel(pendingIntent);
|
|
Log.d(TAG, "Alarm cancelled");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks/Requests permission for exact alarms on Android 12+
|
|
*/
|
|
public static boolean isExactAlarmAllowed(Context context) {
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
|
return am != null && am.canScheduleExactAlarms();
|
|
}
|
|
return true; // Android 11 and below allow exact alarms by default
|
|
}
|
|
|
|
/**
|
|
* Helper to request exact alarm permission if missing
|
|
*/
|
|
public static void requestExactAlarmPermission(android.app.Activity activity) {
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !isExactAlarmAllowed(activity)) {
|
|
Intent intent = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM);
|
|
activity.startActivity(intent);
|
|
}
|
|
}
|
|
}
|