Merge remote-tracking branch 'origin/reoccuring_time_trigger' into development

This commit is contained in:
jens 2021-11-13 12:31:27 +01:00
commit 5fdc68e396
20 changed files with 1175 additions and 425 deletions

View File

@ -132,7 +132,7 @@
android:scheme="package" />-->
</intent-filter>
</receiver>
<receiver android:name=".receivers.AlarmListener" />
<receiver android:name=".receivers.DateTimeListener" />
<receiver android:name=".receivers.ConnectivityReceiver" />
<receiver android:name=".receivers.TimeZoneListener" />

View File

@ -988,6 +988,32 @@ public class Rule implements Comparable<Rule>
}
}
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<Rule>
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)

View File

@ -129,7 +129,7 @@
android:scheme="package" />-->
</intent-filter>
</receiver>
<receiver android:name=".receivers.AlarmListener" />
<receiver android:name=".receivers.DateTimeListener" />
<receiver android:name=".receivers.ConnectivityReceiver" />
<receiver android:name=".receivers.TimeZoneListener" />

View File

@ -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<Rule>
{
private static ArrayList<Rule> ruleCollection = new ArrayList<Rule>();
@ -762,13 +770,13 @@ public class Rule implements Comparable<Rule>
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<Rule>
{
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<Rule>
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<Rule>
}
}
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<Rule>
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)

View File

@ -123,7 +123,7 @@
android:scheme="package" />-->
</intent-filter>
</receiver>
<receiver android:name=".receivers.AlarmListener" />
<receiver android:name=".receivers.DateTimeListener" />
<receiver android:name=".receivers.ConnectivityReceiver" />
<receiver android:name=".receivers.TimeZoneListener" />

View File

@ -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<Rule>
{
private static ArrayList<Rule> ruleCollection = new ArrayList<Rule>();
@ -793,13 +801,13 @@ public class Rule implements Comparable<Rule>
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<Rule>
{
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<Rule>
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<Rule>
}
}
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<Rule>
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)

View File

@ -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<String> actionTypesList = new ArrayList<String>();

View File

@ -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)
{

View File

@ -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()

View File

@ -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()));
}
}
}

View File

@ -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;
}
}

View File

@ -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());

View File

@ -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<Integer> dayList = new ArrayList<Integer>();
public ArrayList<Integer> getDayList()
protected final static String separator = "/";
private ArrayList<Integer> dayList = new ArrayList<Integer>();
public ArrayList<Integer> getDayList()
{
return dayList;
}
public void setDayList(ArrayList<Integer> dayList)
{
this.dayList = dayList;
}
public void setDayListFromString(String dayListString)
{
// Log.i("Parsing", "Full string: " + dayListString);
char[] dayListCharArray = dayListString.toCharArray();
dayList = new ArrayList<Integer>();
for(char item : dayListCharArray)
{
return dayList;
}
public void setDayList(ArrayList<Integer> dayList)
{
this.dayList = dayList;
}
public void setDayListFromString(String dayListString)
{
// Log.i("Parsing", "Full string: " + dayListString);
char[] dayListCharArray = dayListString.toCharArray();
dayList = new ArrayList<Integer>();
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<Integer> 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<Integer> 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;
}
}

View File

@ -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;
}
}