diff --git a/app/src/apkFlavor/AndroidManifest.xml b/app/src/apkFlavor/AndroidManifest.xml
index acac67e7..73643316 100644
--- a/app/src/apkFlavor/AndroidManifest.xml
+++ b/app/src/apkFlavor/AndroidManifest.xml
@@ -132,7 +132,7 @@
android:scheme="package" />-->
-
+
diff --git a/app/src/apkFlavor/java/com/jens/automation2/Rule.java b/app/src/apkFlavor/java/com/jens/automation2/Rule.java
index ef093a68..23bba173 100644
--- a/app/src/apkFlavor/java/com/jens/automation2/Rule.java
+++ b/app/src/apkFlavor/java/com/jens/automation2/Rule.java
@@ -988,6 +988,32 @@ public class Rule implements Comparable
}
}
+ public boolean haveTriggersReallyChanged(Object triggeringObject)
+ {
+ boolean returnValue = false;
+
+ try
+ {
+ for(int i=0; i < triggerSet.size(); i++)
+ {
+ Trigger t = (Trigger) triggerSet.get(i);
+
+ if(t.hasStateRecentlyNotApplied(triggeringObject))
+ {
+ Miscellaneous.logEvent("i", "Rule", "Rule \"" + getName() + "\" has trigger that flipped: " + t.toString(), 4);
+ returnValue = true; // only 1 trigger needs to have flipped recently
+ }
+ }
+
+ return returnValue;
+ }
+ catch(Exception e)
+ {
+ Miscellaneous.logEvent("e", "Rule", "Error while checking if rule \"" + getName() + "\" haveTriggersReallyChanged(): " + Log.getStackTraceString(e), 1);
+ return false;
+ }
+ }
+
/**
* Will activate the rule. Should be called by a separate execution thread
* @param automationService
@@ -998,8 +1024,9 @@ public class Rule implements Comparable
boolean notLastActive = getLastActivatedRule() == null || !getLastActivatedRule().equals(Rule.this);
boolean doToggle = ruleToggle && isActuallyToggable;
+ boolean triggersApplyAnew = haveTriggersReallyChanged(new Date());
- if(notLastActive || force || doToggle)
+ if(notLastActive || force || doToggle || triggersApplyAnew)
{
String message;
if(!doToggle)
diff --git a/app/src/fdroidFlavor/AndroidManifest.xml b/app/src/fdroidFlavor/AndroidManifest.xml
index b43e4ff8..3037baed 100644
--- a/app/src/fdroidFlavor/AndroidManifest.xml
+++ b/app/src/fdroidFlavor/AndroidManifest.xml
@@ -129,7 +129,7 @@
android:scheme="package" />-->
-
+
diff --git a/app/src/fdroidFlavor/java/com/jens/automation2/Rule.java b/app/src/fdroidFlavor/java/com/jens/automation2/Rule.java
index 210615f6..e2c1040b 100644
--- a/app/src/fdroidFlavor/java/com/jens/automation2/Rule.java
+++ b/app/src/fdroidFlavor/java/com/jens/automation2/Rule.java
@@ -1,11 +1,14 @@
package com.jens.automation2;
import android.annotation.SuppressLint;
+import android.app.Notification;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Build;
+import android.os.Bundle;
import android.os.Looper;
+import android.os.Parcelable;
import android.service.notification.StatusBarNotification;
import android.telephony.TelephonyManager;
import android.util.Log;
@@ -32,6 +35,11 @@ import static com.jens.automation2.Trigger.triggerParameter2Split;
import static com.jens.automation2.receivers.NotificationListener.EXTRA_TEXT;
import static com.jens.automation2.receivers.NotificationListener.EXTRA_TITLE;
+import androidx.core.app.NotificationCompat;
+
+import org.apache.commons.lang3.StringUtils;
+
+
public class Rule implements Comparable
{
private static ArrayList ruleCollection = new ArrayList();
@@ -762,13 +770,13 @@ public class Rule implements Comparable
String myApp = params[0];
String myTitleDir = params[1];
- String myTitle = params[2];
+ String requiredTitle = params[2];
String myTextDir = params[3];
- String myText;
+ String requiredText;
if (params.length >= 5)
- myText = params[4];
+ requiredText = params[4];
else
- myText = "";
+ requiredText = "";
if(oneTrigger.getTriggerParameter())
{
@@ -780,38 +788,65 @@ public class Rule implements Comparable
{
if(getLastExecution() == null || sbn.getPostTime() > this.lastExecution.getTimeInMillis())
{
- String app = sbn.getPackageName();
- String title = sbn.getNotification().extras.getString(EXTRA_TITLE);
- String text = sbn.getNotification().extras.getString(EXTRA_TEXT);
+ String notificationApp = sbn.getPackageName();
+ String notificationTitle = null;
+ String notificationText = null;
- Miscellaneous.logEvent("i", "NotificationCheck", "Checking if this notification matches our rule " + this.getName() + ". App: " + app + ", title: " + title + ", text: " + text, 5);
+ Miscellaneous.logEvent("i", "NotificationCheck", "Checking if this notification matches our rule " + this.getName() + ". App: " + notificationApp + ", title: " + notificationTitle + ", text: " + notificationText, 5);
if (!myApp.equals("-1"))
{
- if (!app.equalsIgnoreCase(myApp))
+ if (!notificationApp.equalsIgnoreCase(myApp))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification app name does not match rule.", 5);
continue;
}
}
-
- if (myTitle.length() > 0)
+ else
{
- if (!Miscellaneous.compare(myTitleDir, myTitle, title))
+ if(myApp.equals(BuildConfig.APPLICATION_ID))
+ {
+ return false;
+ }
+ }
+
+ /*
+ If there are multiple notifications ("stacked") title or text might be null:
+ https://stackoverflow.com/questions/28047767/notificationlistenerservice-not-reading-text-of-stacked-notifications
+ */
+
+ Bundle extras = sbn.getNotification().extras;
+
+ // T I T L E
+ if (extras.containsKey(EXTRA_TITLE))
+ notificationTitle = sbn.getNotification().extras.getString(EXTRA_TITLE);
+
+ if (!StringUtils.isEmpty(requiredTitle))
+ {
+ if (!Miscellaneous.compare(myTitleDir, requiredTitle, notificationTitle))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification title does not match rule.", 5);
continue;
}
}
+ else
+ Miscellaneous.logEvent("i", "NotificationCheck", "A required title for a notification trigger was not specified.", 5);
- if (myText.length() > 0)
+ // T E X T
+
+ if (extras.containsKey(EXTRA_TEXT))
+ notificationText = sbn.getNotification().extras.getString(EXTRA_TEXT);
+
+ if (!StringUtils.isEmpty(requiredText))
{
- if (!Miscellaneous.compare(myTextDir, myText, text))
+ if (!Miscellaneous.compare(myTextDir, requiredText, notificationText))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification text does not match rule.", 5);
continue;
}
}
+ else
+ Miscellaneous.logEvent("i", "NotificationCheck", "A required text for a notification trigger was not specified.", 5);
foundMatch = true;
break;
@@ -838,16 +873,23 @@ public class Rule implements Comparable
if (!app.equalsIgnoreCase(myApp))
return false;
}
-
- if (myTitle.length() > 0)
+ else
{
- if (!Miscellaneous.compare(myTitleDir, title, myTitle))
+ if(myApp.equals(BuildConfig.APPLICATION_ID))
+ {
+ return false;
+ }
+ }
+
+ if (requiredTitle.length() > 0)
+ {
+ if (!Miscellaneous.compare(myTitleDir, title, requiredTitle))
return false;
}
- if (myText.length() > 0)
+ if (requiredText.length() > 0)
{
- if (!Miscellaneous.compare(myTextDir, text, myText))
+ if (!Miscellaneous.compare(myTextDir, text, requiredText))
return false;
}
}
@@ -915,6 +957,32 @@ public class Rule implements Comparable
}
}
+ public boolean haveTriggersReallyChanged(Object triggeringObject)
+ {
+ boolean returnValue = false;
+
+ try
+ {
+ for(int i=0; i < triggerSet.size(); i++)
+ {
+ Trigger t = (Trigger) triggerSet.get(i);
+
+ if(t.hasStateRecentlyNotApplied(triggeringObject))
+ {
+ Miscellaneous.logEvent("i", "Rule", "Rule \"" + getName() + "\" has trigger that flipped: " + t.toString(), 4);
+ returnValue = true; // only 1 trigger needs to have flipped recently
+ }
+ }
+
+ return returnValue;
+ }
+ catch(Exception e)
+ {
+ Miscellaneous.logEvent("e", "Rule", "Error while checking if rule \"" + getName() + "\" haveTriggersReallyChanged(): " + Log.getStackTraceString(e), 1);
+ return false;
+ }
+ }
+
/**
* Will activate the rule. Should be called by a separate execution thread
* @param automationService
@@ -925,8 +993,9 @@ public class Rule implements Comparable
boolean notLastActive = getLastActivatedRule() == null || !getLastActivatedRule().equals(Rule.this);
boolean doToggle = ruleToggle && isActuallyToggable;
+ boolean triggersApplyAnew = haveTriggersReallyChanged(new Date());
- if(notLastActive || force || doToggle)
+ if(notLastActive || force || doToggle || triggersApplyAnew)
{
String message;
if(!doToggle)
diff --git a/app/src/googlePlayFlavor/AndroidManifest.xml b/app/src/googlePlayFlavor/AndroidManifest.xml
index 71eee3e7..1c457831 100644
--- a/app/src/googlePlayFlavor/AndroidManifest.xml
+++ b/app/src/googlePlayFlavor/AndroidManifest.xml
@@ -123,7 +123,7 @@
android:scheme="package" />-->
-
+
diff --git a/app/src/googlePlayFlavor/java/com/jens/automation2/Rule.java b/app/src/googlePlayFlavor/java/com/jens/automation2/Rule.java
index de358084..d1664063 100644
--- a/app/src/googlePlayFlavor/java/com/jens/automation2/Rule.java
+++ b/app/src/googlePlayFlavor/java/com/jens/automation2/Rule.java
@@ -1,11 +1,14 @@
package com.jens.automation2;
import android.annotation.SuppressLint;
+import android.app.Notification;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Build;
+import android.os.Bundle;
import android.os.Looper;
+import android.os.Parcelable;
import android.service.notification.StatusBarNotification;
import android.telephony.TelephonyManager;
import android.util.Log;
@@ -34,6 +37,11 @@ import static com.jens.automation2.Trigger.triggerParameter2Split;
import static com.jens.automation2.receivers.NotificationListener.EXTRA_TEXT;
import static com.jens.automation2.receivers.NotificationListener.EXTRA_TITLE;
+import androidx.core.app.NotificationCompat;
+
+import org.apache.commons.lang3.StringUtils;
+
+
public class Rule implements Comparable
{
private static ArrayList ruleCollection = new ArrayList();
@@ -793,13 +801,13 @@ public class Rule implements Comparable
String myApp = params[0];
String myTitleDir = params[1];
- String myTitle = params[2];
+ String requiredTitle = params[2];
String myTextDir = params[3];
- String myText;
+ String requiredText;
if (params.length >= 5)
- myText = params[4];
+ requiredText = params[4];
else
- myText = "";
+ requiredText = "";
if(oneTrigger.getTriggerParameter())
{
@@ -811,38 +819,65 @@ public class Rule implements Comparable
{
if(getLastExecution() == null || sbn.getPostTime() > this.lastExecution.getTimeInMillis())
{
- String app = sbn.getPackageName();
- String title = sbn.getNotification().extras.getString(EXTRA_TITLE);
- String text = sbn.getNotification().extras.getString(EXTRA_TEXT);
+ String notificationApp = sbn.getPackageName();
+ String notificationTitle = null;
+ String notificationText = null;
- Miscellaneous.logEvent("i", "NotificationCheck", "Checking if this notification matches our rule " + this.getName() + ". App: " + app + ", title: " + title + ", text: " + text, 5);
+ Miscellaneous.logEvent("i", "NotificationCheck", "Checking if this notification matches our rule " + this.getName() + ". App: " + notificationApp + ", title: " + notificationTitle + ", text: " + notificationText, 5);
if (!myApp.equals("-1"))
{
- if (!app.equalsIgnoreCase(myApp))
+ if (!notificationApp.equalsIgnoreCase(myApp))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification app name does not match rule.", 5);
continue;
}
}
-
- if (myTitle.length() > 0)
+ else
{
- if (!Miscellaneous.compare(myTitleDir, myTitle, title))
+ if(myApp.equals(BuildConfig.APPLICATION_ID))
+ {
+ return false;
+ }
+ }
+
+ /*
+ If there are multiple notifications ("stacked") title or text might be null:
+ https://stackoverflow.com/questions/28047767/notificationlistenerservice-not-reading-text-of-stacked-notifications
+ */
+
+ Bundle extras = sbn.getNotification().extras;
+
+ // T I T L E
+ if (extras.containsKey(EXTRA_TITLE))
+ notificationTitle = sbn.getNotification().extras.getString(EXTRA_TITLE);
+
+ if (!StringUtils.isEmpty(requiredTitle))
+ {
+ if (!Miscellaneous.compare(myTitleDir, requiredTitle, notificationTitle))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification title does not match rule.", 5);
continue;
}
}
+ else
+ Miscellaneous.logEvent("i", "NotificationCheck", "A required title for a notification trigger was not specified.", 5);
- if (myText.length() > 0)
+ // T E X T
+
+ if (extras.containsKey(EXTRA_TEXT))
+ notificationText = sbn.getNotification().extras.getString(EXTRA_TEXT);
+
+ if (!StringUtils.isEmpty(requiredText))
{
- if (!Miscellaneous.compare(myTextDir, myText, text))
+ if (!Miscellaneous.compare(myTextDir, requiredText, notificationText))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification text does not match rule.", 5);
continue;
}
}
+ else
+ Miscellaneous.logEvent("i", "NotificationCheck", "A required text for a notification trigger was not specified.", 5);
foundMatch = true;
break;
@@ -869,16 +904,23 @@ public class Rule implements Comparable
if (!app.equalsIgnoreCase(myApp))
return false;
}
-
- if (myTitle.length() > 0)
+ else
{
- if (!Miscellaneous.compare(myTitleDir, title, myTitle))
+ if(myApp.equals(BuildConfig.APPLICATION_ID))
+ {
+ return false;
+ }
+ }
+
+ if (requiredTitle.length() > 0)
+ {
+ if (!Miscellaneous.compare(myTitleDir, title, requiredTitle))
return false;
}
- if (myText.length() > 0)
+ if (requiredText.length() > 0)
{
- if (!Miscellaneous.compare(myTextDir, text, myText))
+ if (!Miscellaneous.compare(myTextDir, text, requiredText))
return false;
}
}
@@ -946,6 +988,32 @@ public class Rule implements Comparable
}
}
+ public boolean haveTriggersReallyChanged(Object triggeringObject)
+ {
+ boolean returnValue = false;
+
+ try
+ {
+ for(int i=0; i < triggerSet.size(); i++)
+ {
+ Trigger t = (Trigger) triggerSet.get(i);
+
+ if(t.hasStateRecentlyNotApplied(triggeringObject))
+ {
+ Miscellaneous.logEvent("i", "Rule", "Rule \"" + getName() + "\" has trigger that flipped: " + t.toString(), 4);
+ returnValue = true; // only 1 trigger needs to have flipped recently
+ }
+ }
+
+ return returnValue;
+ }
+ catch(Exception e)
+ {
+ Miscellaneous.logEvent("e", "Rule", "Error while checking if rule \"" + getName() + "\" haveTriggersReallyChanged(): " + Log.getStackTraceString(e), 1);
+ return false;
+ }
+ }
+
/**
* Will activate the rule. Should be called by a separate execution thread
* @param automationService
@@ -956,8 +1024,9 @@ public class Rule implements Comparable
boolean notLastActive = getLastActivatedRule() == null || !getLastActivatedRule().equals(Rule.this);
boolean doToggle = ruleToggle && isActuallyToggable;
+ boolean triggersApplyAnew = haveTriggersReallyChanged(new Date());
- if(notLastActive || force || doToggle)
+ if(notLastActive || force || doToggle || triggersApplyAnew)
{
String message;
if(!doToggle)
diff --git a/app/src/main/java/com/jens/automation2/Action.java b/app/src/main/java/com/jens/automation2/Action.java
index fa16fe13..215a085d 100644
--- a/app/src/main/java/com/jens/automation2/Action.java
+++ b/app/src/main/java/com/jens/automation2/Action.java
@@ -13,6 +13,8 @@ import java.util.Locale;
public class Action
{
+ Rule parentRule = null;
+
public static final String actionParameter2Split = "ap2split";
public static final String intentPairSeperator = "intPairSplit";
public static final String vibrateSeparator = ",";
@@ -273,6 +275,16 @@ public class Action
return returnString.toString();
}
+ public Rule getParentRule()
+ {
+ return parentRule;
+ }
+
+ public void setParentRule(Rule parentRule)
+ {
+ this.parentRule = parentRule;
+ }
+
public static CharSequence[] getActionTypesAsArray()
{
ArrayList actionTypesList = new ArrayList();
diff --git a/app/src/main/java/com/jens/automation2/ActivityMainRules.java b/app/src/main/java/com/jens/automation2/ActivityMainRules.java
index fa037e65..afcacbc8 100644
--- a/app/src/main/java/com/jens/automation2/ActivityMainRules.java
+++ b/app/src/main/java/com/jens/automation2/ActivityMainRules.java
@@ -19,7 +19,7 @@ import android.widget.TextView;
import android.widget.Toast;
import com.jens.automation2.AutomationService.serviceCommands;
-import com.jens.automation2.receivers.AlarmListener;
+import com.jens.automation2.receivers.DateTimeListener;
import java.util.ArrayList;
@@ -254,7 +254,7 @@ public class ActivityMainRules extends ActivityGeneric
try
{
if(AutomationService.isMyServiceRunning(this))
- AlarmListener.reloadAlarms();
+ DateTimeListener.reloadAlarms();
}
catch(NullPointerException e)
{
diff --git a/app/src/main/java/com/jens/automation2/ActivityManageRule.java b/app/src/main/java/com/jens/automation2/ActivityManageRule.java
index 73bfd7d0..c69d9e8c 100644
--- a/app/src/main/java/com/jens/automation2/ActivityManageRule.java
+++ b/app/src/main/java/com/jens/automation2/ActivityManageRule.java
@@ -403,6 +403,11 @@ public class ActivityManageRule extends Activity
ruleToEdit.setName(etRuleName.getText().toString());
ruleToEdit.setRuleActive(chkRuleActive.isChecked());
ruleToEdit.setRuleToggle(chkRuleToggle.isChecked());
+
+ for(Trigger t : ruleToEdit.getTriggerSet())
+ t.setParentRule(ruleToEdit);
+ for(Action a : ruleToEdit.getActionSet())
+ a.setParentRule(ruleToEdit);
}
private void loadVariablesIntoGui()
diff --git a/app/src/main/java/com/jens/automation2/ActivityManageTriggerTimeFrame.java b/app/src/main/java/com/jens/automation2/ActivityManageTriggerTimeFrame.java
index c67edf7f..9bbf6066 100644
--- a/app/src/main/java/com/jens/automation2/ActivityManageTriggerTimeFrame.java
+++ b/app/src/main/java/com/jens/automation2/ActivityManageTriggerTimeFrame.java
@@ -6,11 +6,16 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.TimePicker;
import android.widget.Toast;
+import org.apache.commons.lang3.StringUtils;
+
import java.sql.Time;
+import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
@@ -18,8 +23,9 @@ public class ActivityManageTriggerTimeFrame extends Activity
{
Button bSaveTimeFrame;
TimePicker startPicker, stopPicker;
- CheckBox checkMonday, checkTuesday, checkWednesday, checkThursday, checkFriday, checkSaturday, checkSunday;
+ CheckBox checkMonday, checkTuesday, checkWednesday, checkThursday, checkFriday, checkSaturday, checkSunday, chkRepeat;
RadioButton radioTimeFrameEntering, radioTimeFrameLeaving;
+ EditText etRepeatEvery;
public static Trigger editedTimeFrameTrigger = null;
@@ -44,7 +50,9 @@ public class ActivityManageTriggerTimeFrame extends Activity
checkSunday = (CheckBox)findViewById(R.id.checkSunday);
radioTimeFrameEntering = (RadioButton)findViewById(R.id.radioTimeFrameEntering);
radioTimeFrameLeaving = (RadioButton)findViewById(R.id.radioTimeFrameLeaving);
-
+ chkRepeat = (CheckBox)findViewById(R.id.chkRepeat);
+ etRepeatEvery = (EditText)findViewById(R.id.etRepeatEvery);
+
bSaveTimeFrame.setOnClickListener(new OnClickListener()
{
@Override
@@ -92,11 +100,43 @@ public class ActivityManageTriggerTimeFrame extends Activity
{
Toast.makeText(getBaseContext(), getResources().getString(R.string.selectOneDay), Toast.LENGTH_LONG).show();
return;
- }
+ }
+
+ boolean goOn = false;
+ if(chkRepeat.isChecked())
+ {
+ if(!StringUtils.isEmpty(etRepeatEvery.getText().toString()))
+ {
+ try
+ {
+ long value = Long.parseLong(etRepeatEvery.getText().toString());
+ if(value > 0)
+ {
+ goOn = true;
+ }
+ }
+ catch(Exception e)
+ {
+ }
+ }
+ }
+ else
+ goOn = true;
+
+ if(!goOn)
+ {
+ Toast.makeText(getBaseContext(), getResources().getString(R.string.enterRepetitionTime), Toast.LENGTH_LONG).show();
+ return;
+ }
if(editedTimeFrameTrigger.getTimeFrame() == null)
+ {
// add new one
- editedTimeFrameTrigger.setTimeFrame(new TimeFrame(startTime, stopTime, dayList));
+ if(chkRepeat.isChecked())
+ editedTimeFrameTrigger.setTimeFrame(new TimeFrame(startTime, stopTime, dayList, Long.parseLong(etRepeatEvery.getText().toString())));
+ else
+ editedTimeFrameTrigger.setTimeFrame(new TimeFrame(startTime, stopTime, dayList, 0));
+ }
else
{
// edit one
@@ -104,6 +144,11 @@ public class ActivityManageTriggerTimeFrame extends Activity
editedTimeFrameTrigger.getTimeFrame().setTriggerTimeStop(stopTime);
editedTimeFrameTrigger.getTimeFrame().getDayList().clear();
editedTimeFrameTrigger.getTimeFrame().setDayList(dayList);
+
+ if(chkRepeat.isChecked())
+ editedTimeFrameTrigger.getTimeFrame().setRepetition(Long.parseLong(etRepeatEvery.getText().toString()));
+ else
+ editedTimeFrameTrigger.getTimeFrame().setRepetition(0);
}
editedTimeFrameTrigger.setTriggerParameter(radioTimeFrameEntering.isChecked());
@@ -112,6 +157,15 @@ public class ActivityManageTriggerTimeFrame extends Activity
finish();
}
});
+
+ chkRepeat.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
+ {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
+ {
+ etRepeatEvery.setEnabled(isChecked);
+ }
+ });
if(editedTimeFrameTrigger.getTimeFrame() != null)
loadVariableIntoGui();
@@ -158,6 +212,12 @@ public class ActivityManageTriggerTimeFrame extends Activity
break;
}
}
+
+ if(editedTimeFrameTrigger.getTimeFrame().getRepetition() > 0)
+ {
+ chkRepeat.setChecked(true);
+ etRepeatEvery.setText(String.valueOf(editedTimeFrameTrigger.getTimeFrame().getRepetition()));
+ }
}
}
diff --git a/app/src/main/java/com/jens/automation2/Miscellaneous.java b/app/src/main/java/com/jens/automation2/Miscellaneous.java
index bae4a4c8..0658b465 100644
--- a/app/src/main/java/com/jens/automation2/Miscellaneous.java
+++ b/app/src/main/java/com/jens/automation2/Miscellaneous.java
@@ -77,6 +77,8 @@ import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.sql.Time;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
@@ -1532,4 +1534,21 @@ public class Miscellaneous extends Service
else*/
return PhoneNumberUtils.compare(number1, number2);
}
+
+ public static String formatDate(Date input)
+ {
+ DateFormat sdf = null;
+ SimpleDateFormat fallBackFormatter = new SimpleDateFormat(Settings.dateFormat);
+
+ if(sdf == null && Settings.dateFormat != null)
+ sdf = new SimpleDateFormat(Settings.dateFormat);
+
+ String formattedDate;
+ if(sdf != null)
+ formattedDate = sdf.format(input);
+ else
+ formattedDate = fallBackFormatter.format(input);
+
+ return formattedDate;
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/jens/automation2/ReceiverCoordinator.java b/app/src/main/java/com/jens/automation2/ReceiverCoordinator.java
index 9921ba13..c36aefe9 100644
--- a/app/src/main/java/com/jens/automation2/ReceiverCoordinator.java
+++ b/app/src/main/java/com/jens/automation2/ReceiverCoordinator.java
@@ -5,7 +5,7 @@ import android.util.Log;
import com.jens.automation2.location.CellLocationChangedReceiver;
import com.jens.automation2.location.WifiBroadcastReceiver;
-import com.jens.automation2.receivers.AlarmListener;
+import com.jens.automation2.receivers.DateTimeListener;
import com.jens.automation2.receivers.AutomationListenerInterface;
import com.jens.automation2.receivers.BatteryReceiver;
import com.jens.automation2.receivers.BluetoothReceiver;
@@ -42,7 +42,7 @@ public class ReceiverCoordinator
Class adClass = Class.forName("ActivityDetectionReceiver");
allImplementers = new Class[] {
adClass,
- AlarmListener.class,
+ DateTimeListener.class,
BatteryReceiver.class,
BluetoothReceiver.class,
ConnectivityReceiver.class,
@@ -59,7 +59,7 @@ public class ReceiverCoordinator
// e.printStackTrace();
allImplementers = new Class[] {
- AlarmListener.class,
+ DateTimeListener.class,
BatteryReceiver.class,
BluetoothReceiver.class,
ConnectivityReceiver.class,
@@ -155,7 +155,7 @@ public class ReceiverCoordinator
BatteryReceiver.startBatteryReceiver(AutomationService.getInstance());
// startAlarmListener
- AlarmListener.startAlarmListener(AutomationService.getInstance());
+ DateTimeListener.startAlarmListener(AutomationService.getInstance());
TimeZoneListener.startTimeZoneListener(AutomationService.getInstance());
// startNoiseListener
@@ -199,7 +199,7 @@ public class ReceiverCoordinator
WifiBroadcastReceiver.stopWifiReceiver();
BatteryReceiver.stopBatteryReceiver();
TimeZoneListener.stopTimeZoneListener();
- AlarmListener.stopAlarmListener(AutomationService.getInstance());
+ DateTimeListener.stopAlarmListener(AutomationService.getInstance());
NoiseListener.stopNoiseListener();
ProcessListener.stopProcessListener(AutomationService.getInstance());
diff --git a/app/src/main/java/com/jens/automation2/TimeFrame.java b/app/src/main/java/com/jens/automation2/TimeFrame.java
index 757d85b8..a15059e0 100644
--- a/app/src/main/java/com/jens/automation2/TimeFrame.java
+++ b/app/src/main/java/com/jens/automation2/TimeFrame.java
@@ -6,70 +6,90 @@ import java.util.ArrayList;
public class TimeFrame
{
// Defines a timeframe
- private Time triggerTimeStart;
- private Time triggerTimeStop;
+ protected Time triggerTimeStart;
+ protected Time triggerTimeStop;
+ protected long repetition;
- private ArrayList dayList = new ArrayList();
- public ArrayList getDayList()
+ protected final static String separator = "/";
+
+ private ArrayList dayList = new ArrayList();
+ public ArrayList getDayList()
+ {
+ return dayList;
+ }
+ public void setDayList(ArrayList dayList)
+ {
+ this.dayList = dayList;
+ }
+ public void setDayListFromString(String dayListString)
+ {
+// Log.i("Parsing", "Full string: " + dayListString);
+ char[] dayListCharArray = dayListString.toCharArray();
+
+ dayList = new ArrayList();
+ for(char item : dayListCharArray)
{
- return dayList;
- }
- public void setDayList(ArrayList dayList)
- {
- this.dayList = dayList;
- }
- public void setDayListFromString(String dayListString)
- {
-// Log.i("Parsing", "Full string: " + dayListString);
- char[] dayListCharArray = dayListString.toCharArray();
-
- dayList = new ArrayList();
- for(char item : dayListCharArray)
- {
// Log.i("Parsing", String.valueOf(item));
- dayList.add(Integer.parseInt(String.valueOf(item)));
- }
+ dayList.add(Integer.parseInt(String.valueOf(item)));
}
+ }
-
- public Time getTriggerTimeStart()
- {
- return triggerTimeStart;
- }
- public void setTriggerTimeStart(Time triggerTimeStart)
- {
- this.triggerTimeStart = triggerTimeStart;
- }
- public Time getTriggerTimeStop()
- {
- return triggerTimeStop;
- }
- public void setTriggerTimeStop(Time triggerTimeStop)
- {
- this.triggerTimeStop = triggerTimeStop;
- }
-
- public TimeFrame (Time timeStart, Time timeEnd, ArrayList dayList2)
- {
- this.setTriggerTimeStart(timeStart);
- this.setTriggerTimeStop(timeEnd);
- this.setDayList(dayList2);
- }
- TimeFrame (String fileContent)
- {
- String[] dateArray = fileContent.split("/"); // example: timestart/timestop/days[int]
- this.setTriggerTimeStart(Time.valueOf(dateArray[0]));
- this.setTriggerTimeStop(Time.valueOf(dateArray[1]));
- this.setDayListFromString(dateArray[2]);
- }
- @Override
- public String toString()
- {
- String returnString = this.getTriggerTimeStart().toString() + "/" + this.getTriggerTimeStop().toString() + "/";
-
- for(Integer oneDay : this.getDayList())
- returnString += String.valueOf(oneDay);
-
- return returnString;
- }
-}
+ public Time getTriggerTimeStart()
+ {
+ return triggerTimeStart;
+ }
+ public void setTriggerTimeStart(Time triggerTimeStart)
+ {
+ this.triggerTimeStart = triggerTimeStart;
+ }
+
+ public Time getTriggerTimeStop()
+ {
+ return triggerTimeStop;
+ }
+ public void setTriggerTimeStop(Time triggerTimeStop)
+ {
+ this.triggerTimeStop = triggerTimeStop;
+ }
+
+ public long getRepetition()
+ {
+ return repetition;
+ }
+
+ public void setRepetition(long repetition)
+ {
+ this.repetition = repetition;
+ }
+
+ public TimeFrame (Time timeStart, Time timeEnd, ArrayList dayList2, long repetition)
+ {
+ this.setTriggerTimeStart(timeStart);
+ this.setTriggerTimeStop(timeEnd);
+ this.setDayList(dayList2);
+ this.setRepetition(repetition);
+ }
+
+ public TimeFrame (String fileContent)
+ {
+ String[] dateArray = fileContent.split(separator); // example: timestart/timestop/days[int]/repetition
+ this.setTriggerTimeStart(Time.valueOf(dateArray[0]));
+ this.setTriggerTimeStop(Time.valueOf(dateArray[1]));
+ this.setDayListFromString(dateArray[2]);
+ if(dateArray.length > 3) // may not exist in old config files
+ this.setRepetition(Long.parseLong(dateArray[3]));
+ }
+
+ @Override
+ public String toString()
+ {
+ String returnString = this.getTriggerTimeStart().toString() + separator + this.getTriggerTimeStop().toString() + separator;
+
+ for(Integer oneDay : this.getDayList())
+ returnString += String.valueOf(oneDay);
+
+ returnString += separator + String.valueOf(repetition);
+
+ return returnString;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/jens/automation2/Trigger.java b/app/src/main/java/com/jens/automation2/Trigger.java
index 04e41983..72442a51 100644
--- a/app/src/main/java/com/jens/automation2/Trigger.java
+++ b/app/src/main/java/com/jens/automation2/Trigger.java
@@ -3,24 +3,315 @@ package com.jens.automation2;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.os.Build;
+import android.util.Log;
import androidx.annotation.RequiresApi;
import com.jens.automation2.receivers.BluetoothReceiver;
+import java.sql.Time;
import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
public class Trigger
{
- /*
+ Rule parentRule = null;
+
+ public boolean applies(Object triggeringObject)
+ {
+ try
+ {
+ switch(this.getTriggerType())
+ {
+ case timeFrame:
+ if(!checkDateTime(triggeringObject, false))
+ return false;
+ break;
+ default:
+ break;
+ }
+
+ return true;
+ }
+ catch(Exception e)
+ {
+ Miscellaneous.logEvent("e", "Trigger", "Error while checking if rule " + getParentRule().getName() + " applies. Error occured in trigger " + this.toString() + "." + Miscellaneous.lineSeparator + Log.getStackTraceString(e), 1);
+ return false;
+ }
+ }
+
+ public boolean hasStateRecentlyNotApplied(Object triggeringObject)
+ {
+ // nur mit einem Trigger?
+
+ // door -> was state different in previous step
+
+ try
+ {
+ switch(getTriggerType())
+ {
+ case timeFrame:
+ if(!checkDateTime(triggeringObject, true))
+ return false;
+ break;
+ default:
+ break;
+ }
+
+ return true;
+ }
+ catch(Exception e)
+ {
+ Miscellaneous.logEvent("e", "Trigger", "Error while checking if rule " + getParentRule().getName() + " applies. Error occured in trigger " + this.toString() + "." + Miscellaneous.lineSeparator + Log.getStackTraceString(e), 1);
+ return false;
+ }
+ }
+
+ public boolean checkDateTime(Object triggeringObject, boolean checkifStateChangedSinceLastRuleExecution)
+ {
+ /*
+ * Use format known from Automation
+ * 07:30:00/17:30:00/23456/300 <-- last parameter is optional: repetition in seconds
+ * Also required: inside or outside that interval
+ */
+
+ Date triggeringTime;
+ if(triggeringObject instanceof Date)
+ triggeringTime = (Date)triggeringObject;
+ else
+ triggeringTime = new Date();
+
+ String timeString = String.valueOf(triggeringTime.getHours()) + ":" + String.valueOf(triggeringTime.getMinutes()) + ":" + String.valueOf(triggeringTime.getSeconds());
+ Time nowTime = Time.valueOf(timeString);
+ Calendar calNow = Calendar.getInstance();
+
+ try
+ {
+ TimeFrame tf = new TimeFrame(getTriggerParameter2());
+
+ if(tf.getDayList().contains(calNow.get(Calendar.DAY_OF_WEEK)))
+ {
+ if(
+ // Regular case, start time is lower than end time
+ (
+ Miscellaneous.compareTimes(tf.getTriggerTimeStart(), nowTime) >= 0
+ &&
+ Miscellaneous.compareTimes(nowTime, tf.getTriggerTimeStop()) > 0
+ )
+ |
+ // Other case, start time higher than end time, timeframe goes over midnight
+ (
+ Miscellaneous.compareTimes(tf.getTriggerTimeStart(), tf.getTriggerTimeStop()) < 0
+ &&
+ (Miscellaneous.compareTimes(tf.getTriggerTimeStart(), nowTime) >= 0
+ |
+ Miscellaneous.compareTimes(nowTime, tf.getTriggerTimeStop()) > 0)
+ )
+
+ )
+ {
+ // We are in the timeframe
+ Miscellaneous.logEvent("i", "Trigger", "TimeFrame: We're currently (" + calNow.getTime().toString() + ") in the specified TimeFrame (" + tf.toString() + ").", 4);
+ if(getTriggerParameter())
+ {
+ if(checkifStateChangedSinceLastRuleExecution)
+ {
+ /*
+ * Was there a target repetition time between last execution and now?
+ * If not -> return false.
+ */
+ Calendar compareCal = Calendar.getInstance();
+ compareCal.setTimeInMillis(triggeringTime.getTime());
+ if(tf.getRepetition() > 0)
+ {
+ if(!isSupposedToRepeatSinceLastExecution(compareCal))
+ {
+ Miscellaneous.logEvent("i", "TimeFrame", "TimeFrame: Trigger of rule " + this.getParentRule().getName() + " applies, but repeated execution is not due, yet.", 4);
+ return false;
+ }
+ }
+ else
+ {
+ /*
+ * This is not a repeating rule. Have we left
+ * the relevant timeframe since the last run?
+ * Determine if it has ran today already. If yes
+ * return false because every rule that is not
+ * repeating can only be executed once per day.
+ */
+
+ if(
+ getParentRule().getLastExecution().get(Calendar.YEAR) == calNow.get(Calendar.YEAR)
+ &&
+ getParentRule().getLastExecution().get(Calendar.MONTH) == calNow.get(Calendar.MONTH)
+ &&
+ getParentRule().getLastExecution().get(Calendar.DAY_OF_MONTH) == calNow.get(Calendar.DAY_OF_MONTH)
+ )
+ {
+ Miscellaneous.logEvent("i", "TimeFrame", "TimeFrame: Trigger of rule " + this.getParentRule().getName() + " applies, but it was already executed today.", 4);
+ return false;
+ }
+ }
+ }
+
+ Miscellaneous.logEvent("i", "Trigger", "TimeFrame: That's what's specified. Trigger of rule " + this.getParentRule().getName() + " applies.", 4);
+ return true;
+ }
+ else
+ {
+ Miscellaneous.logEvent("i", "Trigger", "TimeFrame: That's not what's specified. Trigger of rule " + this.getParentRule().getName() + " doesn't apply.", 4);
+ return false;
+ }
+ }
+ else
+ {
+ Miscellaneous.logEvent("i", "Trigger", "TimeFrame: We're currently (" + calNow.getTime().toString() + ", Day: " + String.valueOf(calNow.get(Calendar.DAY_OF_WEEK)) + ") not in the specified TimeFrame (" + tf.toString() + ") because of the time.", 5);
+ if(!getTriggerParameter())
+ {
+ if(checkifStateChangedSinceLastRuleExecution)
+ {
+ /*
+ * Was there a target repetition time between last execution and now?
+ * If not -> return false.
+ */
+ Calendar compareCal = Calendar.getInstance();
+ compareCal.setTimeInMillis(triggeringTime.getTime());
+ if(tf.getRepetition() > 0)
+ {
+ if(!isSupposedToRepeatSinceLastExecution(compareCal))
+ {
+ Miscellaneous.logEvent("i", "Trigger", "TimeFrame: Trigger of rule " + this.getParentRule().getName() + " applies, but repeated execution is not due, yet.", 4);
+ return false;
+ }
+ }
+ else
+ {
+ /*
+ * This is not a repeating rule. Have we left
+ * the relevant timeframe since the last run?
+ * Determine if it has ran today already. If yes
+ * return false because every rule that is not
+ * repeating can only be executed once per day.
+ */
+
+ if(
+ getParentRule().getLastExecution().get(Calendar.YEAR) == calNow.get(Calendar.YEAR)
+ &&
+ getParentRule().getLastExecution().get(Calendar.MONTH) == calNow.get(Calendar.MONTH)
+ &&
+ getParentRule().getLastExecution().get(Calendar.DAY_OF_MONTH) == calNow.get(Calendar.DAY_OF_MONTH)
+ )
+ {
+ Miscellaneous.logEvent("i", "Trigger", "TimeFrame: Trigger of rule " + this.getParentRule().getName() + " applies, but it was already executed today.", 4);
+ return false;
+ }
+ }
+ }
+ Miscellaneous.logEvent("i", "Trigger", "TimeFrame: That's what's specified. Trigger of rule " + this.getParentRule().getName() + " applies.", 5);
+ return true;
+ }
+ else
+ {
+ Miscellaneous.logEvent("i", "Trigger", "TimeFrame: That's not what's specified. Trigger of rule " + this.getParentRule().getName() + " doesn't apply.", 5);
+ return false;
+ }
+ }
+ }
+ else
+ {
+ Miscellaneous.logEvent("i", "Trigger", "TimeFrame: We're currently (" + calNow.getTime().toString() + ", Day: " + String.valueOf(calNow.get(Calendar.DAY_OF_WEEK)) + ") not in the specified TimeFrame (" + tf.toString() + ") because of the day.", 5);
+ return false;
+ }
+ }
+ catch(Exception e)
+ {
+ Miscellaneous.logEvent("e", "Trigger", "There was an error while checking if the time based trigger applies: " + Log.getStackTraceString(e), 1);
+ return false;
+ }
+ }
+
+ public static Calendar getNextRepeatedExecutionAfter(Trigger trigger, Calendar now)
+ {
+ Calendar calSet;
+ Time setTime;
+ TimeFrame tf = new TimeFrame(trigger.getTriggerParameter2());
+
+ if(tf.getRepetition() > 0)
+ {
+ if(trigger.getTriggerParameter())
+ setTime = tf.getTriggerTimeStart();
+ else
+ setTime = tf.getTriggerTimeStop();
+
+ calSet = (Calendar) now.clone();
+ calSet.set(Calendar.HOUR_OF_DAY, setTime.getHours());
+ calSet.set(Calendar.MINUTE, setTime.getMinutes());
+ calSet.set(Calendar.SECOND, 0);
+ calSet.set(Calendar.MILLISECOND, 0);
+
+// if(this.applies(null))
+// {
+ // If the starting time is a day ahead remove 1 day.
+ if(calSet.getTimeInMillis() > now.getTimeInMillis())
+ calSet.add(Calendar.DAY_OF_MONTH, -1);
+
+ long differenceInSeconds = Math.abs(now.getTimeInMillis() - calSet.getTimeInMillis()) / 1000;
+ long nextExecutionMultiplier = Math.floorDiv(differenceInSeconds, tf.getRepetition()) + 1;
+ long nextScheduleTimestamp = (calSet.getTimeInMillis() / 1000) + (nextExecutionMultiplier * tf.getRepetition());
+ Calendar calSchedule = Calendar.getInstance();
+ calSchedule.setTimeInMillis(nextScheduleTimestamp * 1000);
+
+ /*
+ * Das war mal aktiviert. Allerdings: Die ganze Funktion liefert zurück, wenn die Regel NOCH nicht
+ * zutrifft, aber wir z.B. gleich den zeitlichen Bereich betreten.
+ */
+// if(trigger.checkDateTime(calSchedule.getTime(), false))
+// {
+ return calSchedule;
+// }
+// }
+ }
+ else
+ Miscellaneous.logEvent("i", "Trigger", "Trigger " + trigger.toString() + " is not executed repeatedly.", 5);
+
+ return null;
+ }
+
+ boolean isSupposedToRepeatSinceLastExecution(Calendar now)
+ {
+ TimeFrame tf = new TimeFrame(getTriggerParameter2());
+ Calendar lastExec = getParentRule().getLastExecution();
+
+ // the simple stuff:
+
+ if(lastExec == null) // rule never run, go any way
+ return true;
+ else if(tf.getRepetition() <= 0) // is not set to repeat at all
+ return false;
+
+ /*
+ * We don't need to check if the trigger currently applies, that has
+ * been done externally via the applies() function. We can safely assume
+ * we're inside the specified timeframe.
+ */
+
+ Calendar timeSupposedToRunNext = getNextRepeatedExecutionAfter(this, lastExec);
+ if(now.getTimeInMillis() > timeSupposedToRunNext.getTimeInMillis())
+ return true;
+
+ return false;
+ }
+
+ /*
* Can be several things:
* -PointOfInterest
* -TimeFrame
* -Event (like charging, cable plugged, etc.)
*/
- public enum Trigger_Enum {
+ public enum Trigger_Enum {
pointOfInterest, timeFrame, charging, batteryLevel, usb_host_connection, speed, noiseLevel, wifiConnection, process_started_stopped, airplaneMode, roaming, nfcTag, activityDetection, bluetoothConnection, headsetPlugged, notification, phoneCall; //phoneCall always needs to be at the very end because of Google's shitty so called privacy
public String getFullName(Context context)
@@ -294,7 +585,11 @@ public class Trigger
else
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.leaving) + " ");
- returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.triggerTimeFrame) + ": " + this.getTimeFrame().getTriggerTimeStart().toString() + " " + Miscellaneous.getAnyContext().getResources().getString(R.string.until) + " " + this.getTimeFrame().getTriggerTimeStop().toString() + " on days " + this.getTimeFrame().getDayList().toString());
+ String repeat = ", no repetition";
+ if(this.getTimeFrame().getRepetition() > 0)
+ repeat = ", " + String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.repeatEveryXsecondsWithVariable), String.valueOf(this.getTimeFrame().getRepetition()));
+
+ returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.triggerTimeFrame) + ": " + this.getTimeFrame().getTriggerTimeStart().toString() + " " + Miscellaneous.getAnyContext().getResources().getString(R.string.until) + " " + this.getTimeFrame().getTriggerTimeStop().toString() + " on days " + this.getTimeFrame().getDayList().toString() + repeat);
break;
case speed:
if(getTriggerParameter())
@@ -611,5 +906,14 @@ public class Trigger
{
return this.bluetoothEvent;
}
-
+
+ public Rule getParentRule()
+ {
+ return parentRule;
+ }
+
+ public void setParentRule(Rule parentRule)
+ {
+ this.parentRule = parentRule;
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/jens/automation2/XmlFileInterface.java b/app/src/main/java/com/jens/automation2/XmlFileInterface.java
index 2b170b74..5c469307 100644
--- a/app/src/main/java/com/jens/automation2/XmlFileInterface.java
+++ b/app/src/main/java/com/jens/automation2/XmlFileInterface.java
@@ -764,6 +764,8 @@ public class XmlFileInterface
try
{
newRule.setTriggerSet(readTriggerCollection(parser));
+ for(Trigger t : newRule.getTriggerSet())
+ t.setParentRule(newRule);
}
catch (XmlPullParserException e)
{
@@ -779,6 +781,8 @@ public class XmlFileInterface
try
{
newRule.setActionSet(readActionCollection(parser));
+ for(Action a : newRule.getActionSet())
+ a.setParentRule(newRule);
}
catch (XmlPullParserException e)
{
diff --git a/app/src/main/java/com/jens/automation2/receivers/AlarmListener.java b/app/src/main/java/com/jens/automation2/receivers/AlarmListener.java
deleted file mode 100644
index 4c7bf102..00000000
--- a/app/src/main/java/com/jens/automation2/receivers/AlarmListener.java
+++ /dev/null
@@ -1,301 +0,0 @@
-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 com.jens.automation2.AutomationService;
-import com.jens.automation2.Miscellaneous;
-import com.jens.automation2.Rule;
-import com.jens.automation2.Trigger;
-import com.jens.automation2.Trigger.Trigger_Enum;
-
-import java.sql.Time;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Date;
-
-public class AlarmListener extends BroadcastReceiver implements AutomationListenerInterface
-{
- private static AutomationService automationServiceRef;
- private static AlarmManager centralAlarmManagerInstance;
-// private static Intent alarmIntent;
-// private static PendingIntent alarmPendingIntent;
- private static boolean alarmListenerActive=false;
- private static ArrayList alarmCandidates = new ArrayList();
-
- private static ArrayList requestCodeList = new ArrayList();
-
- public static void startAlarmListener(final AutomationService automationServiceRef)
- {
- AlarmListener.startAlarmListenerInternal(automationServiceRef);
- }
- public static void stopAlarmListener(Context context)
- {
- AlarmListener.stopAlarmListenerInternal();
- }
-
- public static boolean isAlarmListenerActive()
- {
- return alarmListenerActive;
- }
-
- @Override
- public void onReceive(Context context, Intent intent)
- {
- Miscellaneous.logEvent("i", "AlarmListener", "Alarm received", 2);
- Date now = new Date();
- String timeString = String.valueOf(now.getHours()) + ":" + String.valueOf(now.getMinutes()) + ":" + String.valueOf(now.getSeconds());
- Time passTime = Time.valueOf(timeString);
-
- ArrayList allRulesWithNowInTimeFrame = Rule.findRuleCandidatesByTime(passTime);
- for(int i=0; i allRulesWithTimeFrames = new ArrayList();
- allRulesWithTimeFrames = Rule.findRuleCandidatesByTimeFrame();
- for(Rule oneRule : allRulesWithTimeFrames)
- {
- for(Trigger oneTrigger : oneRule.getTriggerSet())
- {
- if(oneTrigger.getTriggerType() == Trigger_Enum.timeFrame)
- {
- Calendar calNow, calSet;
- Time setTime;
-
- if(oneTrigger.getTriggerParameter())
- setTime = oneTrigger.getTimeFrame().getTriggerTimeStart();
- else
- setTime = oneTrigger.getTimeFrame().getTriggerTimeStop();
-
- calNow = Calendar.getInstance();
- calSet = (Calendar) calNow.clone();
- calSet.set(Calendar.HOUR_OF_DAY, setTime.getHours());
- calSet.set(Calendar.MINUTE, setTime.getMinutes());
- calSet.set(Calendar.SECOND, 0);
- calSet.set(Calendar.MILLISECOND, 0);
- // At this point calSet would be a scheduling candidate. It's just the day the might not be right, yet.
-
- long milliSecondsInAWeek = 1000 * 60 * 60 * 24 * 7;
-
- for(int dayOfWeek : oneTrigger.getTimeFrame().getDayList())
- {
- Calendar calSetWorkingCopy = (Calendar) calSet.clone();
-
-// calSetWorkingCopy.set(Calendar.HOUR_OF_DAY, setTime.getHours());
-// calSetWorkingCopy.set(Calendar.MINUTE, setTime.getMinutes());
-// calSetWorkingCopy.set(Calendar.SECOND, 0);
-// calSetWorkingCopy.set(Calendar.MILLISECOND, 0);
-
- int diff = dayOfWeek - calNow.get(Calendar.DAY_OF_WEEK);
-// Log.i("AlarmManager", "Today: " + String.valueOf(calNow.get(Calendar.DAY_OF_WEEK)) + " / Sched.Day: " + String.valueOf(dayOfWeek) + " Difference to target day is: " + String.valueOf(diff));
- if(diff == 0) //if we're talking about the current day, is the time still in the future?
- {
- if(calSetWorkingCopy.getTime().getHours() < calNow.getTime().getHours())
- {
-// Log.i("AlarmManager", "calSetWorkingCopy.getTime().getHours(" + String.valueOf(calSetWorkingCopy.getTime().getHours()) + ") < calNow.getTime().getHours(" + String.valueOf(calNow.getTime().getHours()) + ")");
- calSetWorkingCopy.add(Calendar.DAY_OF_MONTH, 7); //add a week
- }
- else if(calSetWorkingCopy.getTime().getHours() == calNow.getTime().getHours())
- {
-// Log.i("AlarmManager", "calSetWorkingCopy.getTime().getHours() == calNow.getTime().getHours()");
- if(calSetWorkingCopy.getTime().getMinutes() <= calNow.getTime().getMinutes())
- {
-// Log.i("AlarmManager", "calSetWorkingCopy.getTime().getMinutes() < calNow.getTime().getMinutes()");
- calSetWorkingCopy.add(Calendar.DAY_OF_MONTH, 7); //add a week
- }
- }
- }
- else if(diff < 0)
- {
-// Miscellaneous.logEvent("i", "AlarmManager", "Adding " + String.valueOf(diff+7) + " on top of " + String.valueOf(calSetWorkingCopy.get(Calendar.DAY_OF_WEEK)));
- calSetWorkingCopy.add(Calendar.DAY_OF_WEEK, diff+7); // it's a past weekday, schedule for next week
- }
- else
- {
-// Miscellaneous.logEvent("i", "AlarmManager", "Adding " + String.valueOf(diff) + " on top of " + String.valueOf(calSetWorkingCopy.get(Calendar.DAY_OF_WEEK)));
- calSetWorkingCopy.add(Calendar.DAY_OF_WEEK, diff); // it's a future weekday, schedule for that day
- }
-
- i++;
- i=(int)System.currentTimeMillis();
- String calSetWorkingCopyString = sdf.format(calSetWorkingCopy.getTime()) + " RequestCode: " + String.valueOf(i);
-// Miscellaneous.logEvent("i", "AlarmManager", "Setting repeating alarm because of rule: " + oneRule.getName() + " beginning at " + calSetWorkingCopyString);
-
- alarmCandidates.add(calSetWorkingCopy.getTimeInMillis());
-// Intent alarmIntent = new Intent(automationServiceRef, AlarmListener.class);
-// alarmIntent.setData(Uri.parse("myalarms://" + i));
-// PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, i, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-// centralAlarmManagerInstance.setInexactRepeating(AlarmManager.RTC_WAKEUP, calSetWorkingCopy.getTimeInMillis(), milliSecondsInAWeek, alarmPendingIntent);
-// requestCodeList.add(i);
- }
- }
- }
- }
-
-// // get a Calendar object with current time
-// Calendar cal = Calendar.getInstance();
-// cal.add(Calendar.SECOND, 10);
-// String calSetWorkingCopyString2 = sdf.format(cal.getTime());
-// Miscellaneous.logEvent("i", "AlarmManager", "Setting repeating alarm because of hardcoded test: beginning at " + calSetWorkingCopyString2);
-// Intent alarmIntent2 = new Intent(automationServiceRef, AlarmListener.class);
-// PendingIntent alarmPendingIntent2 = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent2, 0);
-// centralAlarmManagerInstance.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 5000, alarmPendingIntent2);
-// requestCodeList.add(0);
-
- scheduleNextAlarm();
- }
-
- private static void scheduleNextAlarm()
- {
- Long currentTime = System.currentTimeMillis();
- Long scheduleCandidate = null;
-
- if(alarmCandidates.size() == 0)
- {
- Miscellaneous.logEvent("i", "AlarmManager", "No alarms to be scheduled.", 3);
- return;
- }
- else if(alarmCandidates.size() == 1)
- {
- // only one alarm, schedule that
- scheduleCandidate = alarmCandidates.get(0);
- }
- else if(alarmCandidates.size() > 1)
- {
- scheduleCandidate = alarmCandidates.get(0);
-
- for(long alarmCandidate : alarmCandidates)
- {
- if(Math.abs(currentTime - alarmCandidate) < Math.abs(currentTime - scheduleCandidate))
- scheduleCandidate = alarmCandidate;
- }
- }
-
- Intent alarmIntent = new Intent(automationServiceRef, AlarmListener.class);
- PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- centralAlarmManagerInstance.set(AlarmManager.RTC_WAKEUP, scheduleCandidate, alarmPendingIntent);
-
-
- SimpleDateFormat sdf = new SimpleDateFormat("E dd.MM.yyyy HH:mm");
- Calendar calendar = Calendar.getInstance();
- calendar.setTimeInMillis(scheduleCandidate);
- Miscellaneous.logEvent("i", "AlarmManager", "Chose " + sdf.format(calendar.getTime()) + " as next scheduled alarm.", 4);
-
- }
-
- public static void clearAlarms()
- {
- Miscellaneous.logEvent("i", "AlarmManager", "Clearing possibly standing alarms.", 4);
- for(int requestCode : requestCodeList)
- {
- Intent alarmIntent = new Intent(automationServiceRef, AlarmListener.class);
- PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, requestCode, alarmIntent, 0);
-// Miscellaneous.logEvent("i", "AlarmManager", "Clearing alarm with request code: " + String.valueOf(requestCode));
- centralAlarmManagerInstance.cancel(alarmPendingIntent);
- }
- requestCodeList.clear();
- }
-
- private static void startAlarmListenerInternal(AutomationService givenAutomationServiceRef)
- {
- if(!alarmListenerActive)
- {
- Miscellaneous.logEvent("i", "AlarmListener", "Starting alarm listener.", 4);
- AlarmListener.automationServiceRef = givenAutomationServiceRef;
- centralAlarmManagerInstance = (AlarmManager)automationServiceRef.getSystemService(automationServiceRef.ALARM_SERVICE);
-// alarmIntent = new Intent(automationServiceRef, AlarmListener.class);
-// alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent, 0);
- alarmListenerActive = true;
- Miscellaneous.logEvent("i", "AlarmListener", "Alarm listener started.", 4);
- AlarmListener.setAlarms();
-
-// // get a Calendar object with current time
-// Calendar cal = Calendar.getInstance();
-// // add 5 minutes to the calendar object
-// cal.add(Calendar.SECOND, 10);
-// centralAlarmManagerInstance.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 5000, alarmPendingIntent);
- }
- else
- Miscellaneous.logEvent("i", "AlarmListener", "Request to start AlarmListener. But it's already active.", 5);
- }
-
- private static void stopAlarmListenerInternal()
- {
- if(alarmListenerActive)
- {
- Miscellaneous.logEvent("i", "AlarmListener", "Stopping alarm listener.", 4);
- clearAlarms();
-// centralAlarmManagerInstance.cancel(alarmPendingIntent);
- alarmListenerActive = false;
- }
- else
- Miscellaneous.logEvent("i", "AlarmListener", "Request to stop AlarmListener. But it's not running.", 5);
- }
- public static void reloadAlarms()
- {
- AlarmListener.setAlarms();
- }
- @Override
- public void startListener(AutomationService automationService)
- {
- AlarmListener.startAlarmListener(automationService);
- }
- @Override
- public void stopListener(AutomationService automationService)
- {
- AlarmListener.stopAlarmListener(automationService);
- }
-
- public static boolean haveAllPermission()
- {
- return true;
- }
-
- @Override
- public boolean isListenerRunning()
- {
- return isAlarmListenerActive();
- }
- @Override
- public Trigger_Enum[] getMonitoredTrigger()
- {
- return new Trigger_Enum[] { Trigger_Enum.timeFrame };
- }
-
-}
diff --git a/app/src/main/java/com/jens/automation2/receivers/DateTimeListener.java b/app/src/main/java/com/jens/automation2/receivers/DateTimeListener.java
new file mode 100644
index 00000000..3c4cfc1e
--- /dev/null
+++ b/app/src/main/java/com/jens/automation2/receivers/DateTimeListener.java
@@ -0,0 +1,431 @@
+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.os.Build;
+import android.util.Log;
+
+import androidx.annotation.RequiresApi;
+
+import com.jens.automation2.AutomationService;
+import com.jens.automation2.Miscellaneous;
+import com.jens.automation2.Rule;
+import com.jens.automation2.TimeFrame;
+import com.jens.automation2.Trigger;
+import com.jens.automation2.Trigger.Trigger_Enum;
+
+import java.sql.Time;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+
+public class DateTimeListener extends BroadcastReceiver implements AutomationListenerInterface
+{
+ private static AutomationService automationServiceRef;
+ private static AlarmManager centralAlarmManagerInstance;
+// private static Intent alarmIntent;
+// private static PendingIntent alarmPendingIntent;
+ private static boolean alarmListenerActive=false;
+ private static ArrayList alarmCandidates = new ArrayList<>();
+
+ private static ArrayList requestCodeList = new ArrayList();
+
+ public static void startAlarmListener(final AutomationService automationServiceRef)
+ {
+ DateTimeListener.startAlarmListenerInternal(automationServiceRef);
+ }
+ public static void stopAlarmListener(Context context)
+ {
+ DateTimeListener.stopAlarmListenerInternal();
+ }
+
+ public static boolean isAlarmListenerActive()
+ {
+ return alarmListenerActive;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent)
+ {
+ Miscellaneous.logEvent("i", "AlarmListener", "Alarm received", 2);
+ Date now = new Date();
+ String timeString = String.valueOf(now.getHours()) + ":" + String.valueOf(now.getMinutes()) + ":" + String.valueOf(now.getSeconds());
+ Time passTime = Time.valueOf(timeString);
+
+ ArrayList allRulesWithNowInTimeFrame = Rule.findRuleCandidatesByTime(passTime);
+ for(int i=0; i allRulesWithTimeFrames = new ArrayList();
+ allRulesWithTimeFrames = Rule.findRuleCandidatesByTimeFrame();
+ /*
+ * Take care of regular executions, no repetitions in between.
+ */
+ Miscellaneous.logEvent("i", "DateTimeListener", "Checking rules for single run alarm candidates.", 5);
+ for(Rule oneRule : allRulesWithTimeFrames)
+ {
+ Miscellaneous.logEvent("i", "DateTimeListener","Checking rule " + oneRule.getName() + " for single run alarm candidates.", 5);
+ if(oneRule.isRuleActive())
+ {
+ try
+ {
+ for(Trigger oneTrigger : oneRule.getTriggerSet())
+ {
+ Miscellaneous.logEvent("i", "DateTimeListener","Checking trigger " + oneTrigger.toString() + " for single run alarm candidates.", 5);
+
+ if(oneTrigger.getTriggerType().equals(Trigger_Enum.timeFrame))
+ {
+ TimeFrame tf = new TimeFrame(oneTrigger.getTriggerParameter2());
+
+ Calendar calSet;
+ Time setTime;
+
+ if(oneTrigger.getTriggerParameter())
+ setTime = tf.getTriggerTimeStart();
+ else
+ setTime = tf.getTriggerTimeStop();
+
+ calSet = (Calendar) calNow.clone();
+ calSet.set(Calendar.HOUR_OF_DAY, setTime.getHours());
+ calSet.set(Calendar.MINUTE, setTime.getMinutes());
+ calSet.set(Calendar.SECOND, 0);
+ calSet.set(Calendar.MILLISECOND, 0);
+ // At this point calSet would be a scheduling candidate. It's just the day that might not be right, yet.
+
+ for(int dayOfWeek : tf.getDayList())
+ {
+ Calendar calSetWorkingCopy = (Calendar) calSet.clone();
+
+ int diff = dayOfWeek - calNow.get(Calendar.DAY_OF_WEEK);
+ if(diff == 0) // We're talking about the current weekday, but is the time still in the future?
+ {
+ if(calSetWorkingCopy.getTime().getHours() < calNow.getTime().getHours())
+ {
+ calSetWorkingCopy.add(Calendar.DAY_OF_MONTH, 7); //add a week
+ }
+ else if(calSetWorkingCopy.getTime().getHours() == calNow.getTime().getHours())
+ {
+ if(calSetWorkingCopy.getTime().getMinutes() <= calNow.getTime().getMinutes())
+ {
+ calSetWorkingCopy.add(Calendar.DAY_OF_MONTH, 7); //add a week
+ }
+ }
+ }
+ else if(diff < 0)
+ {
+ calSetWorkingCopy.add(Calendar.DAY_OF_WEEK, diff+7); // it's a past weekday, schedule for next week
+ }
+ else
+ {
+ calSetWorkingCopy.add(Calendar.DAY_OF_WEEK, diff); // it's a future weekday, schedule for that day
+ }
+
+ i++;
+ i=(int)System.currentTimeMillis();
+ sdf.format(calSetWorkingCopy.getTime());
+ String.valueOf(i);
+
+ alarmCandidates.add(new ScheduleElement(calSetWorkingCopy, "Rule " + oneRule.getName() + ", trigger " + oneTrigger.toString()));
+ }
+ }
+ }
+ }
+ catch(Exception e)
+ {
+ Miscellaneous.logEvent("e", "DateTimeListener","Error checking anything for rule " + oneRule.toString() + " needs to be added to candicates list: " + Log.getStackTraceString(e), 1);
+ }
+ }
+ }
+
+ /*
+ * Only take care of repeated executions.
+ */
+ Miscellaneous.logEvent("i", "DateTimeListener","Checking rules for repeated run alarm candidates.", 5);
+ for(Rule oneRule : allRulesWithTimeFrames)
+ {
+ Miscellaneous.logEvent("i", "DateTimeListener","Checking rule " + oneRule.getName() + " for repeated run alarm candidates.", 5);
+ if(oneRule.isRuleActive())
+ {
+ try
+ {
+ Miscellaneous.logEvent("i", "DateTimeListener","Checking rule " + oneRule.toString() , 5);
+
+ for(Trigger oneTrigger : oneRule.getTriggerSet())
+ {
+ Miscellaneous.logEvent("i", "DateTimeListener","Checking trigger " + oneTrigger.toString() + " for repeated run alarm candidates.", 5);
+ if(oneTrigger.getTriggerType().equals(Trigger_Enum.timeFrame))
+ {
+ Miscellaneous.logEvent("i", "DateTimeListener","Checking rule trigger " + oneTrigger.toString() , 5);
+
+ /*
+ * Check for next repeated execution:
+ *
+ * Check if the rule currently applies....
+ *
+ * If no -> do nothing
+ * If yes -> Take starting time and calculate the next repeated execution
+ * 1. Take starting time
+ * 2. Take current time
+ * 3. Calculate difference, but include check to see if we're after that time,
+ * be it start or end of the timeframe.
+ * 4. Take div result +1 and add this on top of starting time
+ * 5. Is this next possible execution still inside timeframe? Also consider timeframes spanning over midnight
+ */
+ Calendar calSet;
+ Time setTime;
+ TimeFrame tf = new TimeFrame(oneTrigger.getTriggerParameter2());
+
+ if(tf.getRepetition() > 0)
+ {
+ if(oneTrigger.applies(calNow))
+ {
+ Calendar calSchedule = getNextRepeatedExecutionAfter(oneTrigger, calNow);
+
+ alarmCandidates.add(new ScheduleElement(calSchedule, "Rule " + oneRule.getName() + ", trigger " + oneTrigger.toString()));
+ }
+ }
+ }
+ }
+ }
+ catch(Exception e)
+ {
+ Miscellaneous.logEvent("e", "DateTimeListener","Error checking anything for rule " + oneRule.toString() + " needs to be added to candicates list: " + Log.getStackTraceString(e), 1);
+ }
+ }
+ }
+
+ scheduleNextAlarm();
+ }
+
+ private static void scheduleNextAlarm()
+ {
+ Long currentTime = System.currentTimeMillis();
+ ScheduleElement scheduleCandidate = null;
+
+ if(alarmCandidates.size() == 0)
+ {
+ Miscellaneous.logEvent("i", "AlarmManager", "No alarms to be scheduled.", 3);
+ return;
+ }
+ else if(alarmCandidates.size() == 1)
+ {
+ // only one alarm, schedule that
+ scheduleCandidate = alarmCandidates.get(0);
+ }
+ else if(alarmCandidates.size() > 1)
+ {
+ scheduleCandidate = alarmCandidates.get(0);
+
+ for(ScheduleElement alarmCandidate : alarmCandidates)
+ {
+ if(Math.abs(currentTime - alarmCandidate.time.getTimeInMillis()) < Math.abs(currentTime - scheduleCandidate.time.getTimeInMillis()))
+ scheduleCandidate = alarmCandidate;
+ }
+ }
+
+ Intent alarmIntent = new Intent(automationServiceRef, DateTimeListener.class);
+ PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ centralAlarmManagerInstance.set(AlarmManager.RTC_WAKEUP, scheduleCandidate.time.getTimeInMillis(), alarmPendingIntent);
+
+
+ SimpleDateFormat sdf = new SimpleDateFormat("E dd.MM.yyyy HH:mm");
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTimeInMillis(scheduleCandidate.time.getTimeInMillis());
+ Miscellaneous.logEvent("i", "AlarmManager", "Chose " + sdf.format(calendar.getTime()) + " as next scheduled alarm.", 4);
+ }
+
+ public static void clearAlarms()
+ {
+ Miscellaneous.logEvent("i", "AlarmManager", "Clearing possibly standing alarms.", 4);
+ for(int requestCode : requestCodeList)
+ {
+ Intent alarmIntent = new Intent(automationServiceRef, DateTimeListener.class);
+ PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, requestCode, alarmIntent, 0);
+// Miscellaneous.logEvent("i", "AlarmManager", "Clearing alarm with request code: " + String.valueOf(requestCode));
+ centralAlarmManagerInstance.cancel(alarmPendingIntent);
+ }
+ requestCodeList.clear();
+ }
+
+ private static void startAlarmListenerInternal(AutomationService givenAutomationServiceRef)
+ {
+ if(!alarmListenerActive)
+ {
+ Miscellaneous.logEvent("i", "AlarmListener", "Starting alarm listener.", 4);
+ DateTimeListener.automationServiceRef = givenAutomationServiceRef;
+ centralAlarmManagerInstance = (AlarmManager)automationServiceRef.getSystemService(automationServiceRef.ALARM_SERVICE);
+// alarmIntent = new Intent(automationServiceRef, AlarmListener.class);
+// alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent, 0);
+ alarmListenerActive = true;
+ Miscellaneous.logEvent("i", "AlarmListener", "Alarm listener started.", 4);
+ DateTimeListener.setAlarms();
+
+// // get a Calendar object with current time
+// Calendar cal = Calendar.getInstance();
+// // add 5 minutes to the calendar object
+// cal.add(Calendar.SECOND, 10);
+// centralAlarmManagerInstance.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 5000, alarmPendingIntent);
+ }
+ else
+ Miscellaneous.logEvent("i", "AlarmListener", "Request to start AlarmListener. But it's already active.", 5);
+ }
+
+ private static void stopAlarmListenerInternal()
+ {
+ if(alarmListenerActive)
+ {
+ Miscellaneous.logEvent("i", "AlarmListener", "Stopping alarm listener.", 4);
+ clearAlarms();
+// centralAlarmManagerInstance.cancel(alarmPendingIntent);
+ alarmListenerActive = false;
+ }
+ else
+ Miscellaneous.logEvent("i", "AlarmListener", "Request to stop AlarmListener. But it's not running.", 5);
+ }
+ public static void reloadAlarms()
+ {
+ DateTimeListener.setAlarms();
+ }
+ @Override
+ public void startListener(AutomationService automationService)
+ {
+ DateTimeListener.startAlarmListener(automationService);
+ }
+ @Override
+ public void stopListener(AutomationService automationService)
+ {
+ DateTimeListener.stopAlarmListener(automationService);
+ }
+
+ public static boolean haveAllPermission()
+ {
+ return true;
+ }
+
+ @Override
+ public boolean isListenerRunning()
+ {
+ return isAlarmListenerActive();
+ }
+ @Override
+ public Trigger_Enum[] getMonitoredTrigger()
+ {
+ return new Trigger_Enum[] { Trigger_Enum.timeFrame };
+ }
+
+ static class ScheduleElement implements Comparable
+ {
+ Calendar time;
+ String reason;
+
+ public ScheduleElement(Calendar timestamp, String reason)
+ {
+ super();
+ this.time = timestamp;
+ this.reason = reason;
+ }
+
+ @Override
+ public int compareTo(ScheduleElement o)
+ {
+ if(time.getTimeInMillis() == o.time.getTimeInMillis())
+ return 0;
+ if(time.getTimeInMillis() < o.time.getTimeInMillis())
+ return -1;
+ else
+ return 1;
+ }
+
+ @Override
+ public String toString()
+ {
+ return Miscellaneous.formatDate(time.getTime()) + ", reason : " + reason;
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ public static Calendar getNextRepeatedExecutionAfter(Trigger trigger, Calendar now)
+ {
+ Calendar calSet;
+ Time setTime;
+ TimeFrame tf = new TimeFrame(trigger.getTriggerParameter2());
+
+ if(tf.getRepetition() > 0)
+ {
+ if(trigger.getTriggerParameter())
+ setTime = tf.getTriggerTimeStart();
+ else
+ setTime = tf.getTriggerTimeStop();
+
+ calSet = (Calendar) now.clone();
+ calSet.set(Calendar.HOUR_OF_DAY, setTime.getHours());
+ calSet.set(Calendar.MINUTE, setTime.getMinutes());
+ calSet.set(Calendar.SECOND, 0);
+ calSet.set(Calendar.MILLISECOND, 0);
+
+// if(this.applies(null))
+// {
+ // If the starting time is a day ahead remove 1 day.
+ if(calSet.getTimeInMillis() > now.getTimeInMillis())
+ calSet.add(Calendar.DAY_OF_MONTH, -1);
+
+ long differenceInSeconds = Math.abs(now.getTimeInMillis() - calSet.getTimeInMillis()) / 1000;
+ long nextExecutionMultiplier = Math.floorDiv(differenceInSeconds, tf.getRepetition()) + 1;
+ long nextScheduleTimestamp = (calSet.getTimeInMillis() / 1000) + (nextExecutionMultiplier * tf.getRepetition());
+ Calendar calSchedule = Calendar.getInstance();
+ calSchedule.setTimeInMillis(nextScheduleTimestamp * 1000);
+
+ /*
+ * Das war mal aktiviert. Allerdings: Die ganze Funktion liefert zurück, wenn die Regel NOCH nicht
+ * zutrifft, aber wir z.B. gleich den zeitlichen Bereich betreten.
+ */
+// if(trigger.checkDateTime(calSchedule.getTime(), false))
+// {
+ return calSchedule;
+// }
+// }
+ }
+ else
+ Miscellaneous.logEvent("i", "DateTimeListener", "Trigger " + trigger.toString() + " is not executed repeatedly.", 5);
+
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/jens/automation2/receivers/TimeZoneListener.java b/app/src/main/java/com/jens/automation2/receivers/TimeZoneListener.java
index c2856f2f..5961a98c 100644
--- a/app/src/main/java/com/jens/automation2/receivers/TimeZoneListener.java
+++ b/app/src/main/java/com/jens/automation2/receivers/TimeZoneListener.java
@@ -77,12 +77,12 @@ public class TimeZoneListener extends BroadcastReceiver implements AutomationLis
if(action.equals(Intent.ACTION_TIMEZONE_CHANGED))
{
Miscellaneous.logEvent("i", "TimeZoneListener", "Device timezone changed. Reloading alarms.", 3);
- AlarmListener.reloadAlarms();
+ DateTimeListener.reloadAlarms();
}
else if(action.equals(Intent.ACTION_TIME_CHANGED))
{
Miscellaneous.logEvent("i", "TimeZoneListener", "Device time changed. Reloading alarms.", 4);
- AlarmListener.reloadAlarms();
+ DateTimeListener.reloadAlarms();
}
}
@Override
diff --git a/app/src/main/res/layout/activity_manage_trigger_timeframe.xml b/app/src/main/res/layout/activity_manage_trigger_timeframe.xml
index a85659ac..0553e16f 100644
--- a/app/src/main/res/layout/activity_manage_trigger_timeframe.xml
+++ b/app/src/main/res/layout/activity_manage_trigger_timeframe.xml
@@ -119,6 +119,34 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/sunday" />
+
+
+
+
+
+
+
+
+
+
Position of tab bar
Choose where the tabs bar should be placed.
- Because Google screwd up yet another part of Android, starting with API 30 only the currently visible wifis can be displayed. Not all the ones your device knows.
+ Because Google screwed up yet another part of Android, starting with API 30 only the currently visible wifis can be displayed. Not all the ones your device knows.
If you have not used a send-sms action in this program before, Android may show an additional confirmation dialog, asking you to allow sending messages. You need to select the \"always allow\" checkbox and confirm if you want this action to work in the background. It\'s advised to run this rule manually once.
REMARK: The silent mode often triggers Do-Not-Disturb on newer devices. If that happens on your device, I recommend using the normal mode instead and lowering all volumes to zero.
Tones
@@ -703,4 +703,7 @@
Fine tuning (like allowing phone calls, picking specific numbers, etc.) can only be done from the system\'s settings.
Your rules required permissions which cannot be requested from this installed flavor of Automation.
If you do not choose a specific app, but choose \"Any app\" notifications from Automation will be ignored to avoid loops.
+ Repeat every x seconds
+ repeat every %1$s seconds
+ You need to enter a positive non-decimal value for reptition time.
\ No newline at end of file