diff --git a/app/src/apkFlavor/AndroidManifest.xml b/app/src/apkFlavor/AndroidManifest.xml
index cf6a611..84b71d8 100644
--- a/app/src/apkFlavor/AndroidManifest.xml
+++ b/app/src/apkFlavor/AndroidManifest.xml
@@ -75,6 +75,9 @@
+
+
+
+
+
+
= 31 && Miscellaneous.getTargetSDK(Miscellaneous.getAnyContext()) >= 31)
+ addToArrayListUnique(Manifest.permission.SCHEDULE_EXACT_ALARM, requiredPermissions);
break;
case usb_host_connection:
addToArrayListUnique(Manifest.permission.READ_PHONE_STATE, requiredPermissions);
@@ -582,6 +585,8 @@ public class ActivityPermissions extends Activity
break;
case calendarEvent:
addToArrayListUnique(Manifest.permission.READ_CALENDAR, requiredPermissions);
+ if(Build.VERSION.SDK_INT >= 31 && Miscellaneous.getTargetSDK(Miscellaneous.getAnyContext()) >= 31)
+ addToArrayListUnique(Manifest.permission.SCHEDULE_EXACT_ALARM, requiredPermissions);
break;
default:
break;
@@ -831,6 +836,12 @@ public class ActivityPermissions extends Activity
case Manifest.permission.WRITE_EXTERNAL_STORAGE:
usingElements.add(getResources().getString(R.string.storeSettings));
break;
+ case Manifest.permission.SCHEDULE_EXACT_ALARM:
+ for(String ruleName : getRulesUsing(Trigger.Trigger_Enum.timeFrame))
+ usingElements.add(String.format(getResources().getString(R.string.ruleXrequiresThis), ruleName));
+ for(String ruleName : getRulesUsing(Trigger.Trigger_Enum.calendarEvent))
+ usingElements.add(String.format(getResources().getString(R.string.ruleXrequiresThis), ruleName));
+ break;
case Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE:
for(String ruleName : getRulesUsing(Trigger.Trigger_Enum.notification))
usingElements.add(String.format(getResources().getString(R.string.ruleXrequiresThis), ruleName));
@@ -1098,6 +1109,10 @@ public class ActivityPermissions extends Activity
if (requestCode == requestCodeForPermissionsAccessibility)
if(havePermission(Manifest.permission.BIND_ACCESSIBILITY_SERVICE, ActivityPermissions.this))
requestPermissions(cachedPermissionsToRequest, true);
+
+ if (requestCode == requestCodeForPermissionsScheduleExactAlarms)
+ if(havePermission(Manifest.permission.SCHEDULE_EXACT_ALARM, ActivityPermissions.this))
+ requestPermissions(cachedPermissionsToRequest, true);
}
}
@@ -1225,6 +1240,22 @@ public class ActivityPermissions extends Activity
return;
}
+ else if (s.equalsIgnoreCase(Manifest.permission.SCHEDULE_EXACT_ALARM))
+ {
+ AlertDialog diag = Miscellaneous.messageBox(getResources().getString(R.string.info), getResources().getString(R.string.alarmsPermissionHint), ActivityPermissions.this);
+ diag.setOnDismissListener(new DialogInterface.OnDismissListener()
+ {
+ @Override
+ public void onDismiss(DialogInterface dialogInterface)
+ {
+ requiredPermissions.remove(s);
+ cachedPermissionsToRequest = requiredPermissions;
+ requestScheduleExactAlarms();
+ }
+ });
+ diag.show();
+ return;
+ }
else if(s.equals(Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS))
{
requiredPermissions.remove(s);
@@ -1310,6 +1341,14 @@ public class ActivityPermissions extends Activity
startActivityForResult(intent, requestCodeForPermissionsNotifications);
}
+
+ void requestScheduleExactAlarms()
+ {
+ Intent intent = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM);
+ startActivityForResult(intent, requestCodeForPermissionsScheduleExactAlarms);
+ }
+
+
protected void applyChanges()
{
AutomationService service = AutomationService.getInstance();
diff --git a/app/src/main/java/com/jens/automation2/receivers/CalendarReceiver.java b/app/src/main/java/com/jens/automation2/receivers/CalendarReceiver.java
index 28244ca..5e25823 100644
--- a/app/src/main/java/com/jens/automation2/receivers/CalendarReceiver.java
+++ b/app/src/main/java/com/jens/automation2/receivers/CalendarReceiver.java
@@ -1,11 +1,15 @@
package com.jens.automation2.receivers;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Build;
+import android.os.SystemClock;
import androidx.annotation.NonNull;
@@ -37,6 +41,8 @@ public class CalendarReceiver extends BroadcastReceiver implements AutomationLis
static Timer timer = null;
static TimerTask timerTask = null;
static Calendar nextWakeup = null;
+ static AlarmManager alarmManager = null;
+ static boolean alarmHasChanged = false;
public static CalendarReceiver getInstance()
{
@@ -262,28 +268,56 @@ public class CalendarReceiver extends BroadcastReceiver implements AutomationLis
return calendarEventsCache;
}
+ public static class AlarmReceiver extends BroadcastReceiver
+ {
+ @Override
+ public void onReceive(Context context, Intent intent)
+ {
+ Miscellaneous.logEvent("i", "AlarmReceiver", "Received alarm for calendar receiver.", 5);
+ routineAtAlarm();
+ }
+ }
+
+ protected static void routineAtAlarm()
+ {
+ checkForRules(Miscellaneous.getAnyContext());
+
+ // Set next timer
+ calculateNextWakeup();
+ armOrRearmTimer();
+ }
+
private static void armOrRearmTimer()
{
- timerTask = new TimerTask()
+ PendingIntent pi = null;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
- @Override
- public void run()
+ if (alarmManager == null)
{
- checkForRules(Miscellaneous.getAnyContext());
+ alarmManager = (AlarmManager) Miscellaneous.getAnyContext().getSystemService(Context.ALARM_SERVICE);
- // Set next timer
-
- calculateNextWakeup();
- armOrRearmTimer();
+ Intent i = new Intent(Miscellaneous.getAnyContext(), AlarmReceiver.class);
+ pi = PendingIntent.getBroadcast(Miscellaneous.getAnyContext(), 0, i, 0);
}
- };
-
- if(timer != null)
- {
- timer.cancel();
- timer.purge();
}
- timer = new Timer();
+ else
+ {
+ timerTask = new TimerTask()
+ {
+ @Override
+ public void run()
+ {
+ routineAtAlarm();
+ }
+ };
+
+ if(timer != null)
+ {
+ timer.cancel();
+ timer.purge();
+ }
+ timer = new Timer();
+ }
if(nextWakeup == null)
{
@@ -293,7 +327,26 @@ public class CalendarReceiver extends BroadcastReceiver implements AutomationLis
// If it's now filled, go on
if(nextWakeup != null)
- timer.schedule(timerTask, nextWakeup.getTimeInMillis());
+ {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
+ {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && alarmManager.canScheduleExactAlarms()))
+ {
+ try
+ {
+ alarmManager.cancel(pi);
+ }
+ catch (Exception e)
+ {
+
+ }
+ Miscellaneous.logEvent("i", "armOrRearmTimer()", "Setting calendar alarm for " + nextWakeup.toString(), 5);
+ alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextWakeup.getTimeInMillis(), pi);
+ }
+ }
+ else
+ timer.schedule(timerTask, nextWakeup.getTimeInMillis());
+ }
}
private static void calculateNextWakeup()
@@ -313,6 +366,8 @@ public class CalendarReceiver extends BroadcastReceiver implements AutomationLis
{
nextWakeup = event.end;
Miscellaneous.logEvent("i", "calculateNextWakeupForCalendar()", "Choosing end of event " + event.title + " as next wakeup.", 5);
+ if(!alarmHasChanged)
+ alarmHasChanged = true;
}
}
else
@@ -321,6 +376,8 @@ public class CalendarReceiver extends BroadcastReceiver implements AutomationLis
{
nextWakeup = event.start;
Miscellaneous.logEvent("i", "calculateNextWakeupForCalendar()", "Choosing start of event " + event.title + " as next wakeup.", 5);
+ if(!alarmHasChanged)
+ alarmHasChanged = true;
}
}
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b67e2d7..45a91c9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -931,4 +931,6 @@
The permission to read your calendar will be required for a calendar trigger. It will already be required to populate the calendar fields in this window.
It appears like no calendars have been set up on your device. You can save this trigger, but it will never return true.
There was an error reading the calendars on your device.
+ Schedule exact alarms
+ After clicking OK a window will open. Please select Automation there and allow the app to schedule exact alarms.
\ No newline at end of file