From 58ec35aae58e58b63d8209b80dd7c9cec3f660b0 Mon Sep 17 00:00:00 2001 From: jens Date: Mon, 1 Jan 2024 13:35:21 +0100 Subject: [PATCH] calendar trigger --- .../ActivityManageTriggerCalendar.java | 3 + .../java/com/jens/automation2/Trigger.java | 158 ++++++++++-------- .../receivers/CalendarReceiver.java | 77 +++++---- .../activity_manage_trigger_calendar.xml | 7 + app/src/main/res/values/strings.xml | 1 + 5 files changed, 151 insertions(+), 95 deletions(-) diff --git a/app/src/main/java/com/jens/automation2/ActivityManageTriggerCalendar.java b/app/src/main/java/com/jens/automation2/ActivityManageTriggerCalendar.java index 2539a9a..21da88f 100644 --- a/app/src/main/java/com/jens/automation2/ActivityManageTriggerCalendar.java +++ b/app/src/main/java/com/jens/automation2/ActivityManageTriggerCalendar.java @@ -51,6 +51,9 @@ public class ActivityManageTriggerCalendar extends Activity Miscellaneous.setDisplayLanguage(this); setContentView(R.layout.activity_manage_trigger_calendar); +// TODO: calculateNextWakeup() muß neu ausgeführt werden, wenn die Regeln geändert werden. + + chkCalendarEventActive = (CheckBox) findViewById(R.id.chkCalendarEventActive); spinnerCalendarTitleDirection = (Spinner)findViewById(R.id.spinnerCalendarTitleDirection); spinnerCalendarLocationDirection = (Spinner)findViewById(R.id.spinnerCalendarLocationDirection); diff --git a/app/src/main/java/com/jens/automation2/Trigger.java b/app/src/main/java/com/jens/automation2/Trigger.java index 1db9c20..4ae136e 100644 --- a/app/src/main/java/com/jens/automation2/Trigger.java +++ b/app/src/main/java/com/jens/automation2/Trigger.java @@ -257,7 +257,7 @@ public class Trigger result = false; break; case calendarEvent: - if(!checkCalendarEvent()) + if(!checkCalendarEvent(false)) result = false; break; default: @@ -617,7 +617,7 @@ public class Trigger return false; } - boolean checkCalendarEvent() + public boolean checkCalendarEvent(boolean ignoreActive) { try { @@ -638,71 +638,8 @@ public class Trigger for(CalendarReceiver.CalendarEvent event : calendarEvents) { - boolean isActive = getTriggerParameter(); - if(isActive != event.isCurrentlyActive()) - { - Miscellaneous.logEvent("i", "CalendarCheck", "Event has to be currently active: " + String.valueOf(triggerParameter) + ", but is required otherwise.", 5); + if(!checkCalendarEvent(event, ignoreActive)) continue; - } - - if(!StringUtils.isEmpty(conditions[1])) - { - if (!Miscellaneous.compare(conditions[0], conditions[1], event.title)) - { - Miscellaneous.logEvent("i", "CalendarCheck", "Title does not match.", 5); - continue; - } - } - - if(!StringUtils.isEmpty(conditions[3])) - { - if (!Miscellaneous.compare(conditions[2], conditions[3], event.description)) - { - Miscellaneous.logEvent("i", "CalendarCheck", "Description does not match.", 5); - continue; - } - } - - if(!StringUtils.isEmpty(conditions[5])) - { - if (!Miscellaneous.compare(conditions[4], conditions[5], event.location)) - { - Miscellaneous.logEvent("i", "CalendarCheck", "Location does not match.", 5); - continue; - } - } - - if (Boolean.parseBoolean(conditions[6]) != event.allDay) - { - Miscellaneous.logEvent("i", "CalendarCheck", "All day setting does not match.", 5); - continue; - } - - if(!StringUtils.isEmpty(conditions[7])) - { - String[] availabilities = conditions[7].split(ActivityManageTriggerCalendar.separator); - if (availabilities.length > 0) - { - if (!Miscellaneous.arraySearch(availabilities, event.availability, false, true)) - { - Miscellaneous.logEvent("i", "CalendarCheck", "Availability does not match.", 5); - continue; - } - } - } - - if(!StringUtils.isEmpty(conditions[8])) - { - String[] calendars = conditions[8].split(ActivityManageTriggerCalendar.separator); - if (calendars.length > 0) - { - if (!Miscellaneous.arraySearch(calendars, String.valueOf(event.calendarId), false, true)) - { - Miscellaneous.logEvent("i", "CalendarCheck", "Calendar does not match.", 5); - continue; - } - } - } // No contradictions found Miscellaneous.logEvent("i", "CalendarCheck", "Event " + event + " matches.", 4); @@ -728,6 +665,95 @@ public class Trigger return false; } + public boolean checkCalendarEvent(CalendarReceiver.CalendarEvent event, boolean ignoreActive) + { + String[] conditions = this.getTriggerParameter2().split(Trigger.triggerParameter2Split); + List calendarEvents = CalendarReceiver.readCalendarEvents(AutomationService.getInstance(), false); + + /* + 0 = titleDirection + 1 = title; + 2 = descriptionDirection + 3 = description + 4 = eventLocationDirection + 5 = eventLocation + 6 = all day event + 7 = availabilityList + 8 = calendarList + */ + + + boolean isActive = getTriggerParameter(); + if (!ignoreActive && isActive != event.isCurrentlyActive()) + { + Miscellaneous.logEvent("i", "CalendarCheck", "Event has to be currently active: " + String.valueOf(triggerParameter) + ", but is required otherwise.", 5); + return false; + } + + if (!StringUtils.isEmpty(conditions[1])) + { + if (!Miscellaneous.compare(conditions[0], conditions[1], event.title)) + { + Miscellaneous.logEvent("i", "CalendarCheck", "Title does not match.", 5); + return false; + } + } + + if (!StringUtils.isEmpty(conditions[3])) + { + if (!Miscellaneous.compare(conditions[2], conditions[3], event.description)) + { + Miscellaneous.logEvent("i", "CalendarCheck", "Description does not match.", 5); + return false; + } + } + + if (!StringUtils.isEmpty(conditions[5])) + { + if (!Miscellaneous.compare(conditions[4], conditions[5], event.location)) + { + Miscellaneous.logEvent("i", "CalendarCheck", "Location does not match.", 5); + return false; + } + } + + if (Boolean.parseBoolean(conditions[6]) != event.allDay) + { + Miscellaneous.logEvent("i", "CalendarCheck", "All day setting does not match.", 5); + return false; + } + + if (!StringUtils.isEmpty(conditions[7])) + { + String[] availabilities = conditions[7].split(ActivityManageTriggerCalendar.separator); + if (availabilities.length > 0) + { + if (!Miscellaneous.arraySearch(availabilities, event.availability, false, true)) + { + Miscellaneous.logEvent("i", "CalendarCheck", "Availability does not match.", 5); + return false; + } + } + } + + if (!StringUtils.isEmpty(conditions[8])) + { + String[] calendars = conditions[8].split(ActivityManageTriggerCalendar.separator); + if (calendars.length > 0) + { + if (!Miscellaneous.arraySearch(calendars, String.valueOf(event.calendarId), false, true)) + { + Miscellaneous.logEvent("i", "CalendarCheck", "Calendar does not match.", 5); + return false; + } + } + } +//TODO: all-day abwählbar machen + // No contradictions found + Miscellaneous.logEvent("i", "CalendarCheck", "Event " + event + " matches.", 4); + return true; + } + boolean checkBluetooth() { Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format("Checking for bluetooth...", this.getParentRule().getName()), 4); 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 4af9b4e..5f4e054 100644 --- a/app/src/main/java/com/jens/automation2/receivers/CalendarReceiver.java +++ b/app/src/main/java/com/jens/automation2/receivers/CalendarReceiver.java @@ -9,7 +9,6 @@ import android.content.IntentFilter; import android.database.Cursor; import android.net.Uri; import android.os.Build; -import android.os.SystemClock; import androidx.annotation.NonNull; @@ -20,6 +19,7 @@ import com.jens.automation2.Trigger; import java.util.ArrayList; import java.util.Calendar; +import java.util.Collections; import java.util.List; import java.util.Timer; import java.util.TimerTask; @@ -43,7 +43,7 @@ public class CalendarReceiver extends BroadcastReceiver implements AutomationLis static TimerTask timerTask = null; static Calendar nextWakeup = null; static AlarmManager alarmManager = null; - static boolean alarmHasChanged = false; + static boolean wakeupNeedsToBeScheduled = false; public static CalendarReceiver getInstance() { @@ -68,7 +68,7 @@ public class CalendarReceiver extends BroadcastReceiver implements AutomationLis } else if(intent.getAction().equalsIgnoreCase(calendarAlarmAction)) { - Miscellaneous.logEvent("i", "AlarmReceiver", "Received alarm for calendar receiver.", 5); + Miscellaneous.logEvent("i", "CalendarReceiver", "Received alarm for calendar receiver.", 5); routineAtAlarm(); } } @@ -326,7 +326,7 @@ public class CalendarReceiver extends BroadcastReceiver implements AutomationLis } // If it's now filled, go on - if(nextWakeup != null) + if(nextWakeup != null && wakeupNeedsToBeScheduled) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { @@ -340,10 +340,9 @@ public class CalendarReceiver extends BroadcastReceiver implements AutomationLis { } - Miscellaneous.logEvent("i", "armOrRearmTimer()", "Setting calendar alarm for " + nextWakeup.toString(), 5); + Miscellaneous.logEvent("i", "armOrRearmTimer()", "Scheduling wakeup for calendar at " + Miscellaneous.formatDate(nextWakeup.getTime()), 5); alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nextWakeup.getTimeInMillis(), pi); - - //alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextWakeup.getTimeInMillis(), pi); + wakeupNeedsToBeScheduled = false; } } else @@ -354,38 +353,58 @@ public class CalendarReceiver extends BroadcastReceiver implements AutomationLis private static void calculateNextWakeup() { Calendar now = Calendar.getInstance(); - if (nextWakeup != null && nextWakeup.getTimeInMillis() < now.getTimeInMillis()) - nextWakeup = null; - List events = readCalendarEvents(Miscellaneous.getAnyContext(), false); - if (events.size() > 0) + + if (events.size() == 0) { + Miscellaneous.logEvent("i", "calculateNextWakeup()", "No future events, nothing to schedule.", 5); + } + else + { + List ruleCandidates = Rule.findRuleCandidates(Trigger.Trigger_Enum.calendarEvent); + List wakeUpCandidatesList = new ArrayList<>(); + for (CalendarEvent event : events) { - if (event.isCurrentlyActive()) + for (Rule r : ruleCandidates) { - if (nextWakeup == null || event.end.getTimeInMillis() < nextWakeup.getTimeInMillis()) + for (Trigger t : r.getTriggerSet()) { - nextWakeup = event.end; - Miscellaneous.logEvent("i", "calculateNextWakeupForCalendar()", "Chose end of event " + event.title + " as next wakeup.", 5); - if(!alarmHasChanged) - alarmHasChanged = true; - } - } - else - { - if (nextWakeup == null || event.start.getTimeInMillis() < nextWakeup.getTimeInMillis()) - { - nextWakeup = event.start; - Miscellaneous.logEvent("i", "calculateNextWakeupForCalendar()", "Chose start of event " + event.title + " as next wakeup.", 5); - if(!alarmHasChanged) - alarmHasChanged = true; + if (t.getTriggerType().equals(Trigger.Trigger_Enum.calendarEvent) && t.checkCalendarEvent(event, true)) + { + /* + Device needs to wakeup at start AND end of events, no matter what is specified in triggers. + This is because we also need to know when a trigger doesn't apply anymore to make it + count for hasStateNotAppliedSinceLastRuleExecution(). + Otherwise the same rule would not get executed again even after calendar events have come and gone. + */ + + if(event.start.getTimeInMillis() > now.getTimeInMillis()) + wakeUpCandidatesList.add(event.start.getTimeInMillis()); + + if(event.end.getTimeInMillis() > now.getTimeInMillis()) + wakeUpCandidatesList.add(event.end.getTimeInMillis()); + } } } } + + Collections.sort(wakeUpCandidatesList); + + if(nextWakeup == null || nextWakeup.getTimeInMillis() != wakeUpCandidatesList.get(0)) + { + Calendar newAlarm = Miscellaneous.calendarFromLong(wakeUpCandidatesList.get(0)); + if(nextWakeup == null) + Miscellaneous.logEvent("i", "calculateNextWakeupForCalendar()", "Chose " + Miscellaneous.formatDate(newAlarm.getTime()) + " as next wakeup for calendar triggers. Old was null.", 5); + else + Miscellaneous.logEvent("i", "calculateNextWakeupForCalendar()", "Chose " + Miscellaneous.formatDate(newAlarm.getTime()) + " as next wakeup for calendar triggers. Old was " + Miscellaneous.formatDate(nextWakeup.getTime()), 5); + nextWakeup = newAlarm; + if (!wakeupNeedsToBeScheduled) + wakeupNeedsToBeScheduled = true; + } + else + Miscellaneous.logEvent("i", "calculateNextWakeupForCalendar()", "Alarm " + Miscellaneous.formatDate(nextWakeup.getTime()) + " has been selected as next wakeup, but not rescheduling since this was not a change.", 5); } - //else - // we expect to be called byOnReceive() when new items exist } public static void startCalendarReceiver(final AutomationService automationServiceRef) diff --git a/app/src/main/res/layout/activity_manage_trigger_calendar.xml b/app/src/main/res/layout/activity_manage_trigger_calendar.xml index dac9a54..a29be92 100644 --- a/app/src/main/res/layout/activity_manage_trigger_calendar.xml +++ b/app/src/main/res/layout/activity_manage_trigger_calendar.xml @@ -175,6 +175,13 @@ android:layout_marginVertical="@dimen/default_margin" android:background="#aa000000" /> + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 45a91c9..94742bd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -933,4 +933,5 @@ 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. + Evaluate \ No newline at end of file