calendar trigger

This commit is contained in:
jens 2024-01-01 13:35:21 +01:00
parent c61c5ba14c
commit 58ec35aae5
5 changed files with 151 additions and 95 deletions

View File

@ -51,6 +51,9 @@ public class ActivityManageTriggerCalendar extends Activity
Miscellaneous.setDisplayLanguage(this); Miscellaneous.setDisplayLanguage(this);
setContentView(R.layout.activity_manage_trigger_calendar); 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); chkCalendarEventActive = (CheckBox) findViewById(R.id.chkCalendarEventActive);
spinnerCalendarTitleDirection = (Spinner)findViewById(R.id.spinnerCalendarTitleDirection); spinnerCalendarTitleDirection = (Spinner)findViewById(R.id.spinnerCalendarTitleDirection);
spinnerCalendarLocationDirection = (Spinner)findViewById(R.id.spinnerCalendarLocationDirection); spinnerCalendarLocationDirection = (Spinner)findViewById(R.id.spinnerCalendarLocationDirection);

View File

@ -257,7 +257,7 @@ public class Trigger
result = false; result = false;
break; break;
case calendarEvent: case calendarEvent:
if(!checkCalendarEvent()) if(!checkCalendarEvent(false))
result = false; result = false;
break; break;
default: default:
@ -617,7 +617,7 @@ public class Trigger
return false; return false;
} }
boolean checkCalendarEvent() public boolean checkCalendarEvent(boolean ignoreActive)
{ {
try try
{ {
@ -638,71 +638,8 @@ public class Trigger
for(CalendarReceiver.CalendarEvent event : calendarEvents) for(CalendarReceiver.CalendarEvent event : calendarEvents)
{ {
boolean isActive = getTriggerParameter(); if(!checkCalendarEvent(event, ignoreActive))
if(isActive != event.isCurrentlyActive())
{
Miscellaneous.logEvent("i", "CalendarCheck", "Event has to be currently active: " + String.valueOf(triggerParameter) + ", but is required otherwise.", 5);
continue; 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 // No contradictions found
Miscellaneous.logEvent("i", "CalendarCheck", "Event " + event + " matches.", 4); Miscellaneous.logEvent("i", "CalendarCheck", "Event " + event + " matches.", 4);
@ -728,6 +665,95 @@ public class Trigger
return false; return false;
} }
public boolean checkCalendarEvent(CalendarReceiver.CalendarEvent event, boolean ignoreActive)
{
String[] conditions = this.getTriggerParameter2().split(Trigger.triggerParameter2Split);
List<CalendarReceiver.CalendarEvent> 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() boolean checkBluetooth()
{ {
Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format("Checking for bluetooth...", this.getParentRule().getName()), 4); Miscellaneous.logEvent("i", Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), String.format("Checking for bluetooth...", this.getParentRule().getName()), 4);

View File

@ -9,7 +9,6 @@ import android.content.IntentFilter;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.SystemClock;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -20,6 +19,7 @@ import com.jens.automation2.Trigger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
@ -43,7 +43,7 @@ public class CalendarReceiver extends BroadcastReceiver implements AutomationLis
static TimerTask timerTask = null; static TimerTask timerTask = null;
static Calendar nextWakeup = null; static Calendar nextWakeup = null;
static AlarmManager alarmManager = null; static AlarmManager alarmManager = null;
static boolean alarmHasChanged = false; static boolean wakeupNeedsToBeScheduled = false;
public static CalendarReceiver getInstance() public static CalendarReceiver getInstance()
{ {
@ -68,7 +68,7 @@ public class CalendarReceiver extends BroadcastReceiver implements AutomationLis
} }
else if(intent.getAction().equalsIgnoreCase(calendarAlarmAction)) 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(); routineAtAlarm();
} }
} }
@ -326,7 +326,7 @@ public class CalendarReceiver extends BroadcastReceiver implements AutomationLis
} }
// If it's now filled, go on // If it's now filled, go on
if(nextWakeup != null) if(nextWakeup != null && wakeupNeedsToBeScheduled)
{ {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 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.RTC_WAKEUP, nextWakeup.getTimeInMillis(), pi);
wakeupNeedsToBeScheduled = false;
//alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextWakeup.getTimeInMillis(), pi);
} }
} }
else else
@ -354,38 +353,58 @@ public class CalendarReceiver extends BroadcastReceiver implements AutomationLis
private static void calculateNextWakeup() private static void calculateNextWakeup()
{ {
Calendar now = Calendar.getInstance(); Calendar now = Calendar.getInstance();
if (nextWakeup != null && nextWakeup.getTimeInMillis() < now.getTimeInMillis())
nextWakeup = null;
List<CalendarEvent> events = readCalendarEvents(Miscellaneous.getAnyContext(), false); List<CalendarEvent> 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<Rule> ruleCandidates = Rule.findRuleCandidates(Trigger.Trigger_Enum.calendarEvent);
List<Long> wakeUpCandidatesList = new ArrayList<>();
for (CalendarEvent event : events) 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; if (t.getTriggerType().equals(Trigger.Trigger_Enum.calendarEvent) && t.checkCalendarEvent(event, true))
Miscellaneous.logEvent("i", "calculateNextWakeupForCalendar()", "Chose end of event " + event.title + " as next wakeup.", 5); {
if(!alarmHasChanged) /*
alarmHasChanged = 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().
else Otherwise the same rule would not get executed again even after calendar events have come and gone.
{ */
if (nextWakeup == null || event.start.getTimeInMillis() < nextWakeup.getTimeInMillis())
{ if(event.start.getTimeInMillis() > now.getTimeInMillis())
nextWakeup = event.start; wakeUpCandidatesList.add(event.start.getTimeInMillis());
Miscellaneous.logEvent("i", "calculateNextWakeupForCalendar()", "Chose start of event " + event.title + " as next wakeup.", 5);
if(!alarmHasChanged) if(event.end.getTimeInMillis() > now.getTimeInMillis())
alarmHasChanged = true; 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) public static void startCalendarReceiver(final AutomationService automationServiceRef)

View File

@ -175,6 +175,13 @@
android:layout_marginVertical="@dimen/default_margin" android:layout_marginVertical="@dimen/default_margin"
android:background="#aa000000" /> android:background="#aa000000" />
<CheckBox
android:id="@+id/careAboutAllDayEvent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/evaluate"
android:checked="true"/>
<TableRow <TableRow
android:layout_marginBottom="@dimen/activity_vertical_margin"> android:layout_marginBottom="@dimen/activity_vertical_margin">

View File

@ -933,4 +933,5 @@
<string name="errorReadingCalendars">There was an error reading the calendars on your device.</string> <string name="errorReadingCalendars">There was an error reading the calendars on your device.</string>
<string name="android.permission.SCHEDULE_EXACT_ALARM">Schedule exact alarms</string> <string name="android.permission.SCHEDULE_EXACT_ALARM">Schedule exact alarms</string>
<string name="alarmsPermissionHint">After clicking OK a window will open. Please select Automation there and allow the app to schedule exact alarms.</string> <string name="alarmsPermissionHint">After clicking OK a window will open. Please select Automation there and allow the app to schedule exact alarms.</string>
<string name="evaluate">Evaluate</string>
</resources> </resources>