Compare commits

...

15 Commits

40 changed files with 569 additions and 305 deletions

View File

@ -11,8 +11,8 @@ android {
compileSdkVersion 33 compileSdkVersion 33
buildToolsVersion '29.0.2' buildToolsVersion '29.0.2'
useLibrary 'org.apache.http.legacy' useLibrary 'org.apache.http.legacy'
versionCode 138 versionCode 142
versionName "1.8" versionName "1.8.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }

View File

@ -66,7 +66,6 @@
<uses-permission android:name="com.wireguard.android.permission.CONTROL_TUNNELS"/> <uses-permission android:name="com.wireguard.android.permission.CONTROL_TUNNELS"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission <uses-permission
android:name="android.permission.WRITE_SECURE_SETTINGS" android:name="android.permission.WRITE_SECURE_SETTINGS"
tools:ignore="ProtectedPermissions" /> tools:ignore="ProtectedPermissions" />
@ -258,18 +257,6 @@
android:exported="true" android:exported="true"
/> />
<service android:name=".MyAccessibilityService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/config_accessibility_service" />
</service>
</application> </application>
</manifest> </manifest>

View File

@ -1107,7 +1107,13 @@ public class Actions
externalApplicationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); externalApplicationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// Pack intents // Pack intents
externalApplicationIntent = packParametersIntoIntent(externalApplicationIntent, params, 3); if(params.length >= 4)
{
if(Miscellaneous.isNumeric(params[3]))
externalApplicationIntent = packParametersIntoIntent(externalApplicationIntent, params, 4);
else
externalApplicationIntent = packParametersIntoIntent(externalApplicationIntent, params, 3);
}
if (params[2].equals(ActivityManageActionStartActivity.startByActivityString)) if (params[2].equals(ActivityManageActionStartActivity.startByActivityString))
automationServerRef.startActivity(externalApplicationIntent); automationServerRef.startActivity(externalApplicationIntent);
@ -2353,7 +2359,8 @@ public class Actions
{ {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
{ {
MyAccessibilityService.getInstance().performGlobalAction(AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT); if(!BuildConfig.FLAVOR.equals(AutomationService.flavor_name_googleplay))
MyAccessibilityService.getInstance().performGlobalAction(AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT);
} }
} }

View File

@ -622,7 +622,7 @@ public class ActivityManageRule extends Activity
items.add(new Item(typesLong[i].toString(), R.drawable.megaphone)); items.add(new Item(typesLong[i].toString(), R.drawable.megaphone));
else if(types[i].toString().equals(Trigger_Enum.phoneCall.toString())) else if(types[i].toString().equals(Trigger_Enum.phoneCall.toString()))
{ {
if(ActivityPermissions.isPermissionDeclaratedInManifest(ActivityManageRule.this, "android.permission.SEND_SMS")) if(ActivityPermissions.isPermissionDeclaratedInManifest(ActivityManageRule.this, Manifest.permission.SEND_SMS))
items.add(new Item(typesLong[i].toString(), R.drawable.phone)); items.add(new Item(typesLong[i].toString(), R.drawable.phone));
} }
else if(types[i].toString().equals(Trigger_Enum.nfcTag.toString())) else if(types[i].toString().equals(Trigger_Enum.nfcTag.toString()))
@ -654,7 +654,10 @@ public class ActivityManageRule extends Activity
else if(types[i].toString().equals(Trigger_Enum.checkVariable.toString())) else if(types[i].toString().equals(Trigger_Enum.checkVariable.toString()))
items.add(new Item(typesLong[i].toString(), R.drawable.variable)); items.add(new Item(typesLong[i].toString(), R.drawable.variable));
else if(types[i].toString().equals(Trigger_Enum.calendarEvent.toString())) else if(types[i].toString().equals(Trigger_Enum.calendarEvent.toString()))
items.add(new Item(typesLong[i].toString(), R.drawable.calendar)); {
if(ActivityPermissions.isPermissionDeclaratedInManifest(ActivityManageRule.this, Manifest.permission.READ_CALENDAR))
items.add(new Item(typesLong[i].toString(), R.drawable.calendar));
}
else else
items.add(new Item(typesLong[i].toString(), R.drawable.placeholder)); items.add(new Item(typesLong[i].toString(), R.drawable.placeholder));
} }
@ -2250,12 +2253,15 @@ public class ActivityManageRule extends Activity
} }
else if(types[i].toString().equals(Action_Enum.copyToClipboard.toString())) else if(types[i].toString().equals(Action_Enum.copyToClipboard.toString()))
items.add(new Item(typesLong[i].toString(), R.drawable.clipboard)); items.add(new Item(typesLong[i].toString(), R.drawable.clipboard));
else if(types[i].toString().equals(Action_Enum.takeScreenshot.toString()))
items.add(new Item(typesLong[i].toString(), R.drawable.copier));
else if(types[i].toString().equals(Action_Enum.setVariable.toString())) else if(types[i].toString().equals(Action_Enum.setVariable.toString()))
items.add(new Item(typesLong[i].toString(), R.drawable.variable)); items.add(new Item(typesLong[i].toString(), R.drawable.variable));
else if(types[i].toString().equals(Action_Enum.setLocationService.toString())) else if(types[i].toString().equals(Action_Enum.setLocationService.toString()))
items.add(new Item(typesLong[i].toString(), R.drawable.compass_small)); items.add(new Item(typesLong[i].toString(), R.drawable.compass_small));
else if(types[i].toString().equals(Action_Enum.takeScreenshot.toString()))
{
if(ActivityPermissions.isPermissionDeclaratedInManifest(ActivityManageRule.this, Manifest.permission.BIND_ACCESSIBILITY_SERVICE))
items.add(new Item(typesLong[i].toString(), R.drawable.copier));
}
else else
items.add(new Item(typesLong[i].toString(), R.drawable.placeholder)); items.add(new Item(typesLong[i].toString(), R.drawable.placeholder));
} }

View File

@ -320,8 +320,10 @@ public class AutomationService extends Service implements OnInitListener
ReceiverCoordinator.applySettingsAndRules(); ReceiverCoordinator.applySettingsAndRules();
DateTimeListener.reloadAlarms(); DateTimeListener.setOrResetAlarms();
CalendarReceiver.armOrRearmTimer();
if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.calendarEvent) && ActivityPermissions.isPermissionDeclaratedInManifest(AutomationService.getInstance(), Manifest.permission.READ_CALENDAR) && ActivityPermissions.havePermission(Manifest.permission.READ_CALENDAR, AutomationService.getInstance()))
CalendarReceiver.armOrRearmTimer();
} }
@Override @Override

View File

@ -585,7 +585,45 @@ public class Miscellaneous extends Service
Miscellaneous.logEvent("i", "TimeCompare", "Default return code. Shouldn't be here.", 5); Miscellaneous.logEvent("i", "TimeCompare", "Default return code. Shouldn't be here.", 5);
return 0; return 0;
}
public static int compareTimes(Calendar calOne, Calendar calTwo)
{
if(calOne.get(Calendar.HOUR_OF_DAY) == calTwo.get(Calendar.HOUR_OF_DAY) && calOne.get(Calendar.MINUTE) == calTwo.get((Calendar.MINUTE)))
{
// Miscellaneous.logEvent("i", "TimeCompare", "Times are equal.");
return 0;
}
if(calOne.get(Calendar.HOUR_OF_DAY) > calTwo.get(Calendar.HOUR_OF_DAY))
{
// Miscellaneous.logEvent("i", "TimeCompare", "Time1 is bigger/later by hours.");
return -1;
}
if(calOne.get(Calendar.HOUR_OF_DAY) < calTwo.get(Calendar.HOUR_OF_DAY))
{
// Miscellaneous.logEvent("i", "TimeCompare", "Time2 is bigger/later by hours.");
return 1;
}
if(calOne.get(Calendar.HOUR_OF_DAY) == calTwo.get(Calendar.HOUR_OF_DAY))
{
if(calOne.get(Calendar.MINUTE) < calTwo.get(Calendar.MINUTE))
{
// Miscellaneous.logEvent("i", "TimeCompare", "Hours are equal. Time2 is bigger/later by minutes.");
return 1;
}
if(calOne.get(Calendar.MINUTE) > calTwo.get(Calendar.MINUTE))
{
// Miscellaneous.logEvent("i", "TimeCompare", "Hours are equal. Time1 is bigger/later by minutes.");
return -1;
}
}
Miscellaneous.logEvent("i", "TimeCompare", "Default return code. Shouldn't be here.", 5);
return 0;
} }
public static String convertStreamToString(InputStream is) public static String convertStreamToString(InputStream is)

View File

@ -1,5 +1,6 @@
package com.jens.automation2; package com.jens.automation2;
import android.Manifest;
import android.os.Build; import android.os.Build;
import android.util.Log; import android.util.Log;
@ -212,7 +213,7 @@ public class ReceiverCoordinator
if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.screenState)) if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.screenState))
ScreenStateReceiver.startScreenStateReceiver(AutomationService.getInstance()); ScreenStateReceiver.startScreenStateReceiver(AutomationService.getInstance());
if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.calendarEvent)) if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.calendarEvent) && ActivityPermissions.isPermissionDeclaratedInManifest(AutomationService.getInstance(), Manifest.permission.READ_CALENDAR) && ActivityPermissions.havePermission(Manifest.permission.READ_CALENDAR, AutomationService.getInstance()))
CalendarReceiver.startCalendarReceiver(AutomationService.getInstance()); CalendarReceiver.startCalendarReceiver(AutomationService.getInstance());
} }
@ -469,7 +470,7 @@ public class ReceiverCoordinator
} }
} }
if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.calendarEvent)) if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.calendarEvent) && ActivityPermissions.isPermissionDeclaratedInManifest(AutomationService.getInstance(), Manifest.permission.READ_CALENDAR) && ActivityPermissions.havePermission(Manifest.permission.READ_CALENDAR, AutomationService.getInstance()))
{ {
if(!CalendarReceiver.getInstance().isListenerRunning()) if(!CalendarReceiver.getInstance().isListenerRunning())
CalendarReceiver.getInstance().startListener(AutomationService.getInstance()); CalendarReceiver.getInstance().startListener(AutomationService.getInstance());

View File

@ -2,6 +2,7 @@ package com.jens.automation2;
import java.sql.Time; import java.sql.Time;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
public class TimeFrame public class TimeFrame
{ {
@ -20,18 +21,19 @@ public class TimeFrame
public void setDayList(ArrayList<Integer> dayList) public void setDayList(ArrayList<Integer> dayList)
{ {
this.dayList = dayList; this.dayList = dayList;
Collections.sort(dayList);
} }
public void setDayListFromString(String dayListString) public void setDayListFromString(String dayListString)
{ {
// Log.i("Parsing", "Full string: " + dayListString);
char[] dayListCharArray = dayListString.toCharArray(); char[] dayListCharArray = dayListString.toCharArray();
dayList = new ArrayList<Integer>(); dayList = new ArrayList<Integer>();
for(char item : dayListCharArray) for(char item : dayListCharArray)
{ {
// Log.i("Parsing", String.valueOf(item));
dayList.add(Integer.parseInt(String.valueOf(item))); dayList.add(Integer.parseInt(String.valueOf(item)));
} }
Collections.sort(dayList);
} }
public TimeObject getTriggerTimeStart() public TimeObject getTriggerTimeStart()

View File

@ -17,6 +17,7 @@ import com.jens.automation2.receivers.BluetoothReceiver;
import com.jens.automation2.receivers.BroadcastListener; import com.jens.automation2.receivers.BroadcastListener;
import com.jens.automation2.receivers.CalendarReceiver; import com.jens.automation2.receivers.CalendarReceiver;
import com.jens.automation2.receivers.ConnectivityReceiver; import com.jens.automation2.receivers.ConnectivityReceiver;
import com.jens.automation2.receivers.DateTimeListener;
import com.jens.automation2.receivers.DeviceOrientationListener; import com.jens.automation2.receivers.DeviceOrientationListener;
import com.jens.automation2.receivers.HeadphoneJackListener; import com.jens.automation2.receivers.HeadphoneJackListener;
import com.jens.automation2.receivers.MediaPlayerListener; import com.jens.automation2.receivers.MediaPlayerListener;
@ -135,6 +136,35 @@ public class Trigger
} }
}; };
boolean triggerParameter; //if true->started event, if false->stopped
String triggerParameter2;
public static final String triggerParameter2Split = "tp2split";
Trigger_Enum triggerType = null;
PointOfInterest pointOfInterest = null;
TimeFrame timeFrame;
public static String triggerPhoneCallStateRinging = "ringing";
public static String triggerPhoneCallStateStarted = "started";
public static String triggerPhoneCallStateStopped = "stopped";
public static String triggerPhoneCallDirectionIncoming = "incoming";
public static String triggerPhoneCallDirectionOutgoing = "outgoing";
public static String triggerPhoneCallDirectionAny = "any";
public static String triggerPhoneCallNumberAny = "any";
double speed; //km/h
long noiseLevelDb;
String processName = null;
int batteryLevel;
int phoneDirection = 0; // 0=any, 1=incoming, 2=outgoing
String phoneNumber = null;
String nfcTagId = null;
String bluetoothEvent = null;
String bluetoothDeviceAddress = null;
int activityDetectionType = -1;
int headphoneType = -1;
public static enum subSystemStates { wifi, bluetooth }; public static enum subSystemStates { wifi, bluetooth };
Rule parentRule = null; Rule parentRule = null;
@ -1124,8 +1154,6 @@ public class Trigger
// We are not at any POI. But if this trigger requires us NOT to be there, that may be fine. // We are not at any POI. But if this trigger requires us NOT to be there, that may be fine.
if(this.getPointOfInterest() != null) if(this.getPointOfInterest() != null)
{ {
// if(activePoi.equals(oneTrigger.getPointOfInterest()))
// {
if(!this.getTriggerParameter()) if(!this.getTriggerParameter())
{ {
Miscellaneous.logEvent("i", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), this.getParentRule().getName()), "We are not at POI \"" + this.getPointOfInterest().getName() + "\". But since that's required by this rule that's fine.", 4); Miscellaneous.logEvent("i", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), this.getParentRule().getName()), "We are not at POI \"" + this.getPointOfInterest().getName() + "\". But since that's required by this rule that's fine.", 4);
@ -1135,7 +1163,6 @@ public class Trigger
Miscellaneous.logEvent("i", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), this.getParentRule().getName()), String.format("Rule \"%1$s\" doesn't apply. We're not at POI \"" + this.getPointOfInterest().getName() + "\".", getParentRule().getName()), 3); Miscellaneous.logEvent("i", String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.ruleCheckOf), this.getParentRule().getName()), String.format("Rule \"%1$s\" doesn't apply. We're not at POI \"" + this.getPointOfInterest().getName() + "\".", getParentRule().getName()), 3);
return false; return false;
} }
// }
} }
else if(this.getPointOfInterest() == null) else if(this.getPointOfInterest() == null)
{ {
@ -1224,7 +1251,7 @@ public class Trigger
return false; return false;
} }
public boolean checkDateTime(Object triggeringObject, boolean checkifStateChangedSinceLastRuleExecution) public boolean checkDateTime(Object triggeringObject, boolean checkIfStateChangedSinceLastRuleExecution)
{ {
/* /*
* Use format known from Automation * Use format known from Automation
@ -1246,142 +1273,52 @@ public class Trigger
{ {
TimeFrame tf = new TimeFrame(getTriggerParameter2()); TimeFrame tf = new TimeFrame(getTriggerParameter2());
if(tf.getDayList().contains(calNow.get(Calendar.DAY_OF_WEEK))) if (DateTimeListener.areWeInTimeFrame(this, triggeringObject) != this.getTriggerParameter())
{ {
if( if (triggerParameter)
// Regular case, start time is lower than end time Miscellaneous.logEvent("i", "Trigger", "TimeFrame: We're currently (" + calNow.getTime().toString() + ", Day: " + String.valueOf(calNow.get(Calendar.DAY_OF_WEEK)) + ") outside of the specified TimeFrame (" + tf.toString() + "), but demanded is inside.", 5);
( else
Miscellaneous.compareTimes(tf.getTriggerTimeStart(), nowTime) >= 0 Miscellaneous.logEvent("i", "Trigger", "TimeFrame: We're currently (" + calNow.getTime().toString() + ") in the specified TimeFrame (" + tf.toString() + "), but demanded is outside.", 4);
&&
Miscellaneous.compareTimes(nowTime, tf.getTriggerTimeStop()) > 0 return false;
) }
||
// Other case, start time higher than end time, timeframe goes over midnight // We are inside or outside of the timeframe as demanded by the trigger
(
Miscellaneous.compareTimes(tf.getTriggerTimeStart(), tf.getTriggerTimeStop()) < 0 if (tf.getRepetition() > 0)
&& {
(Miscellaneous.compareTimes(tf.getTriggerTimeStart(), nowTime) >= 0 if (!isSupposedToRepeatSinceLastExecution(Calendar.getInstance()))
||
Miscellaneous.compareTimes(nowTime, tf.getTriggerTimeStop()) > 0)
)
||
// further case: start and end times are identical, meaning a 24h window
(
Miscellaneous.compareTimes(tf.getTriggerTimeStart(), tf.getTriggerTimeStop()) == 0
)
)
{ {
// We are in the timeframe Miscellaneous.logEvent("i", "TimeFrame", "TimeFrame: Trigger of rule " + this.getParentRule().getName() + " applies, but repetition is not due, yet.", 4);
Miscellaneous.logEvent("i", "Trigger", "TimeFrame: We're currently (" + calNow.getTime().toString() + ") in the specified TimeFrame (" + tf.toString() + ").", 4); return false;
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 else
{ Miscellaneous.logEvent("i", "TimeFrame", "TimeFrame: Trigger of rule " + this.getParentRule().getName() + " would apply because repetition is due.", 4);
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 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); if (checkIfStateChangedSinceLastRuleExecution)
return false; {
/*
* 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;
}
}
} }
} }
catch(Exception e) catch(Exception e)
@ -1389,43 +1326,8 @@ public class Trigger
Miscellaneous.logEvent("e", "Trigger", "There was an error while checking if the time based trigger applies: " + Log.getStackTraceString(e), 1); Miscellaneous.logEvent("e", "Trigger", "There was an error while checking if the time based trigger applies: " + Log.getStackTraceString(e), 1);
return false; return false;
} }
}
public static Calendar getNextRepeatedExecutionAfter(Trigger trigger, Calendar now) return true;
{
Calendar calSet;
TimeObject 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 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);
return calSchedule;
}
else
Miscellaneous.logEvent("i", "Trigger", "Trigger " + trigger.toString() + " is not executed repeatedly.", 5);
return null;
} }
boolean isSupposedToRepeatSinceLastExecution(Calendar now) boolean isSupposedToRepeatSinceLastExecution(Calendar now)
@ -1435,7 +1337,7 @@ public class Trigger
// the simple stuff: // the simple stuff:
if(lastExec == null) // rule never run, go any way if(lastExec == null) // rule never ran, go in any case
return true; return true;
else if(tf.getRepetition() <= 0) // is not set to repeat at all else if(tf.getRepetition() <= 0) // is not set to repeat at all
return false; return false;
@ -1446,41 +1348,17 @@ public class Trigger
* we're inside the specified timeframe. * we're inside the specified timeframe.
*/ */
Calendar timeSupposedToRunNext = getNextRepeatedExecutionAfter(this, lastExec); Calendar lastRepetitionNotExecutedYet = DateTimeListener.getNextRepeatedExecution(this);
if(now.getTimeInMillis() > timeSupposedToRunNext.getTimeInMillis()) lastRepetitionNotExecutedYet.add(Calendar.SECOND, (int) -(tf.getRepetition()));
Miscellaneous.logEvent("i", "isSupposedToRepeatSinceLastExecution()", "Last execution: " + Miscellaneous.formatDate(lastExec.getTime()), 5);
Miscellaneous.logEvent("i", "isSupposedToRepeatSinceLastExecution()", "lastRepetitionNotExecutedYet: " + Miscellaneous.formatDate(lastRepetitionNotExecutedYet.getTime()), 5);
if(now.getTimeInMillis() > lastRepetitionNotExecutedYet.getTimeInMillis())
return true; return true;
return false; return false;
} }
boolean triggerParameter; //if true->started event, if false->stopped
String triggerParameter2;
public static final String triggerParameter2Split = "tp2split";
Trigger_Enum triggerType = null;
PointOfInterest pointOfInterest = null;
TimeFrame timeFrame;
public static String triggerPhoneCallStateRinging = "ringing";
public static String triggerPhoneCallStateStarted = "started";
public static String triggerPhoneCallStateStopped = "stopped";
public static String triggerPhoneCallDirectionIncoming = "incoming";
public static String triggerPhoneCallDirectionOutgoing = "outgoing";
public static String triggerPhoneCallDirectionAny = "any";
public static String triggerPhoneCallNumberAny = "any";
double speed; //km/h
long noiseLevelDb;
String processName = null;
int batteryLevel;
int phoneDirection = 0; // 0=any, 1=incoming, 2=outgoing
String phoneNumber = null;
String nfcTagId = null;
String bluetoothEvent = null;
String bluetoothDeviceAddress = null;
int activityDetectionType = -1;
int headphoneType = -1;
public int getHeadphoneType() public int getHeadphoneType()
{ {

View File

@ -8,10 +8,10 @@ import android.content.Intent;
import android.os.Build; import android.os.Build;
import android.util.Log; import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import com.jens.automation2.AutomationService; import com.jens.automation2.AutomationService;
import com.jens.automation2.BuildConfig;
import com.jens.automation2.Miscellaneous; import com.jens.automation2.Miscellaneous;
import com.jens.automation2.Rule; import com.jens.automation2.Rule;
import com.jens.automation2.TimeFrame; import com.jens.automation2.TimeFrame;
@ -19,7 +19,6 @@ import com.jens.automation2.TimeObject;
import com.jens.automation2.Trigger; import com.jens.automation2.Trigger;
import com.jens.automation2.Trigger.Trigger_Enum; import com.jens.automation2.Trigger.Trigger_Enum;
import java.sql.Time;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
@ -30,13 +29,13 @@ public class DateTimeListener extends BroadcastReceiver implements AutomationLis
{ {
private static AutomationService automationServiceRef; private static AutomationService automationServiceRef;
private static AlarmManager centralAlarmManagerInstance; private static AlarmManager centralAlarmManagerInstance;
private static boolean alarmListenerActive = false; private static boolean alarmListenerActive=false;
private static ArrayList<ScheduleElement> alarmCandidates = new ArrayList<>(); private static ArrayList<ScheduleElement> alarmCandidates = new ArrayList<>();
private static ArrayList<Integer> requestCodeList = new ArrayList<Integer>(); private static ArrayList<Integer> requestCodeList = new ArrayList<Integer>();
static PendingIntent alarmPendingIntent = null; static PendingIntent alarmPendingIntent = null;
public static void startAlarmListener(final AutomationService automationServiceRef) public static void startAlarmListener(final AutomationService automationServiceRef)
{ {
DateTimeListener.startAlarmListenerInternal(automationServiceRef); DateTimeListener.startAlarmListenerInternal(automationServiceRef);
} }
public static void stopAlarmListener(Context context) public static void stopAlarmListener(Context context)
@ -48,37 +47,37 @@ public class DateTimeListener extends BroadcastReceiver implements AutomationLis
{ {
return alarmListenerActive; return alarmListenerActive;
} }
@Override @Override
public void onReceive(Context context, Intent intent) public void onReceive(Context context, Intent intent)
{ {
Miscellaneous.logEvent("i", "AlarmListener", "Alarm received", 2); Miscellaneous.logEvent("i", "AlarmListener", "Alarm received", 2);
ArrayList<Rule> allRulesWithNowInTimeFrame = Rule.findRuleCandidates(Trigger_Enum.timeFrame); ArrayList<Rule> allRulesWithTimeFrame = Rule.findRuleCandidates(Trigger_Enum.timeFrame);
for(int i=0; i < allRulesWithNowInTimeFrame.size(); i++) for(int i=0; i < allRulesWithTimeFrame.size(); i++)
{ {
if(allRulesWithNowInTimeFrame.get(i).getsGreenLight(context)) if(allRulesWithTimeFrame.get(i).getsGreenLight(context))
allRulesWithNowInTimeFrame.get(i).activate(automationServiceRef, false); allRulesWithTimeFrame.get(i).activate(automationServiceRef, false);
} }
setAlarms(); setOrResetAlarms();
} }
@RequiresApi(api = Build.VERSION_CODES.KITKAT) @RequiresApi(api = Build.VERSION_CODES.KITKAT)
public static void setAlarms() public static void setOrResetAlarms()
{ {
alarmCandidates.clear(); alarmCandidates.clear();
Calendar calNow = Calendar.getInstance(); Calendar calNow = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("E dd.MM.yyyy HH:mm"); SimpleDateFormat sdf = new SimpleDateFormat("E dd.MM.yyyy HH:mm");
clearAlarms(); clearAlarms();
int i=0; int i=0;
ArrayList<Rule> allRulesWithTimeFrames = new ArrayList<Rule>(); ArrayList<Rule> allRulesWithTimeFrames = new ArrayList<Rule>();
allRulesWithTimeFrames = Rule.findRuleCandidates(Trigger_Enum.timeFrame); allRulesWithTimeFrames = Rule.findRuleCandidates(Trigger_Enum.timeFrame);
// allRulesWithTimeFrames = Rule.findRuleCandidatesByTimeFrame();
/* /*
* Take care of regular executions, no repetitions in between. * Take care of regular executions, no repetitions in between.
*/ */
@ -192,18 +191,17 @@ public class DateTimeListener extends BroadcastReceiver implements AutomationLis
* 4. Take div result +1 and add this on top of starting time * 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 * 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()); TimeFrame tf = new TimeFrame(oneTrigger.getTriggerParameter2());
if(tf.getRepetition() > 0) if(tf.getRepetition() > 0)
{ {
if(oneTrigger.applies(calNow, Miscellaneous.getAnyContext())) // if(oneTrigger.applies(calNow, Miscellaneous.getAnyContext()))
{ // {
Calendar calSchedule = getNextRepeatedExecutionAfter(oneTrigger, calNow); Calendar calSchedule = getNextRepeatedExecution(oneTrigger);
alarmCandidates.add(new ScheduleElement(calSchedule, "Rule " + oneRule.getName() + ", repetition in trigger " + oneTrigger.toString())); alarmCandidates.add(new ScheduleElement(calSchedule, "Rule " + oneRule.getName() + ", trigger " + oneTrigger.toString()));
} // }
} }
} }
} }
@ -214,39 +212,58 @@ public class DateTimeListener extends BroadcastReceiver implements AutomationLis
} }
} }
} }
scheduleNextAlarm(); scheduleNextAlarm();
} }
private static void scheduleNextAlarm() private static void scheduleNextAlarm()
{ {
Long currentTime = System.currentTimeMillis();
ScheduleElement scheduleCandidate = null;
if(alarmCandidates.size() == 0) if(alarmCandidates.size() == 0)
{ {
Miscellaneous.logEvent("i", "AlarmManager", "No alarms to be scheduled.", 3); Miscellaneous.logEvent("i", "AlarmManager", "No alarms to be scheduled.", 3);
return;
} }
else else if(alarmCandidates.size() == 1)
{ {
Collections.sort(alarmCandidates); // only one alarm, schedule that
scheduleCandidate = alarmCandidates.get(0);
Miscellaneous.logEvent("i", "AlarmManager", "Chose this as next scheduled alarm: " + alarmCandidates.get(0), 4);
Intent alarmIntent = new Intent(automationServiceRef, DateTimeListener.class);
if(Miscellaneous.getAnyContext().getApplicationContext().getApplicationInfo().targetSdkVersion >= 31)
alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
else
alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
centralAlarmManagerInstance.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, alarmCandidates.get(0).time.getTimeInMillis(), alarmPendingIntent);
else
centralAlarmManagerInstance.set(AlarmManager.RTC_WAKEUP, alarmCandidates.get(0).time.getTimeInMillis(), alarmPendingIntent);
} }
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);
if(Miscellaneous.getAnyContext().getApplicationContext().getApplicationInfo().targetSdkVersion >= 31)
alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
else
alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
centralAlarmManagerInstance.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, scheduleCandidate.time.getTimeInMillis(), alarmPendingIntent);
else
centralAlarmManagerInstance.set(AlarmManager.RTC_WAKEUP, scheduleCandidate.time.getTimeInMillis(), alarmPendingIntent);
SimpleDateFormat sdf = new SimpleDateFormat("E dd.MM.yyyy HH:mm:ss");
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() public static void clearAlarms()
{ {
Miscellaneous.logEvent("i", "AlarmManager", "Clearing possibly standing alarms.", 4); Miscellaneous.logEvent("i", "AlarmManager", "Clearing possibly standing alarms.", 4);
for(int requestCode : requestCodeList) for(int requestCode : requestCodeList)
{ {
Intent alarmIntent = new Intent(automationServiceRef, DateTimeListener.class); Intent alarmIntent = new Intent(automationServiceRef, DateTimeListener.class);
@ -255,9 +272,10 @@ public class DateTimeListener extends BroadcastReceiver implements AutomationLis
centralAlarmManagerInstance.cancel(alarmPendingIntent); centralAlarmManagerInstance.cancel(alarmPendingIntent);
} }
requestCodeList.clear(); requestCodeList.clear();
} }
private static void startAlarmListenerInternal(AutomationService givenAutomationServiceRef) private static void startAlarmListenerInternal(AutomationService givenAutomationServiceRef)
{ {
if(!alarmListenerActive) if(!alarmListenerActive)
@ -265,29 +283,34 @@ public class DateTimeListener extends BroadcastReceiver implements AutomationLis
Miscellaneous.logEvent("i", "AlarmListener", "Starting alarm listener.", 4); Miscellaneous.logEvent("i", "AlarmListener", "Starting alarm listener.", 4);
DateTimeListener.automationServiceRef = givenAutomationServiceRef; DateTimeListener.automationServiceRef = givenAutomationServiceRef;
centralAlarmManagerInstance = (AlarmManager)automationServiceRef.getSystemService(automationServiceRef.ALARM_SERVICE); centralAlarmManagerInstance = (AlarmManager)automationServiceRef.getSystemService(automationServiceRef.ALARM_SERVICE);
alarmListenerActive = true; alarmListenerActive = true;
Miscellaneous.logEvent("i", "AlarmListener", "Alarm listener started.", 4); Miscellaneous.logEvent("i", "AlarmListener", "Alarm listener started.", 4);
DateTimeListener.setAlarms(); DateTimeListener.setOrResetAlarms();
// // 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 else
Miscellaneous.logEvent("i", "AlarmListener", "Request to start AlarmListener. But it's already active.", 5); Miscellaneous.logEvent("i", "AlarmListener", "Request to start AlarmListener. But it's already active.", 5);
} }
private static void stopAlarmListenerInternal() private static void stopAlarmListenerInternal()
{ {
if(alarmListenerActive) if(alarmListenerActive)
{ {
Miscellaneous.logEvent("i", "AlarmListener", "Stopping alarm listener.", 4); Miscellaneous.logEvent("i", "AlarmListener", "Stopping alarm listener.", 4);
clearAlarms(); clearAlarms();
centralAlarmManagerInstance.cancel(alarmPendingIntent);
alarmListenerActive = false; alarmListenerActive = false;
} }
else else
Miscellaneous.logEvent("i", "AlarmListener", "Request to stop AlarmListener. But it's not running.", 5); Miscellaneous.logEvent("i", "AlarmListener", "Request to stop AlarmListener. But it's not running.", 5);
} }
public static void reloadAlarms()
{
DateTimeListener.setAlarms();
}
@Override @Override
public void startListener(AutomationService automationService) public void startListener(AutomationService automationService)
{ {
@ -345,41 +368,217 @@ public class DateTimeListener extends BroadcastReceiver implements AutomationLis
} }
} }
@RequiresApi(api = Build.VERSION_CODES.N) static int getNextDayIntForExecution(Trigger trigger)
public static Calendar getNextRepeatedExecutionAfter(Trigger trigger, Calendar now)
{ {
Calendar calSet; TimeFrame tf = new TimeFrame(trigger.getTriggerParameter2());
TimeObject setTime; Calendar now = Calendar.getInstance();
if(tf.getDayList().contains(now.get(Calendar.DAY_OF_WEEK)))
return now.get(Calendar.DAY_OF_WEEK);
else
{
int dayNumberOfNextExecution = now.get(Calendar.DAY_OF_WEEK);
while(!tf.getDayList().contains(dayNumberOfNextExecution))
{
dayNumberOfNextExecution++;
if(dayNumberOfNextExecution > 6)
dayNumberOfNextExecution = 1;
}
return dayNumberOfNextExecution;
}
}
static int getDayDelta(Calendar now, int dayNumberOfNextExecution)
{
int result = dayNumberOfNextExecution - now.get(Calendar.DAY_OF_WEEK);
if(result >= 0)
return result;
else
return 6 + result;
}
@Nullable
@RequiresApi(api = Build.VERSION_CODES.N)
public static Calendar getNextRepeatedExecution(Trigger trigger)
{
Calendar now = Calendar.getInstance();
Miscellaneous.logEvent("i", "DateTimeListener", "Checking for next repetition execution after " + Miscellaneous.formatDate(now.getTime()), 5);
Calendar calculationStart, calSchedule = null;
TimeFrame tf = new TimeFrame(trigger.getTriggerParameter2()); TimeFrame tf = new TimeFrame(trigger.getTriggerParameter2());
if(tf.getRepetition() > 0) if(tf.getRepetition() > 0)
{ {
if(trigger.getTriggerParameter()) /*
setTime = tf.getTriggerTimeStart(); Are we inside of the timeframe or outside?
else
setTime = tf.getTriggerTimeStop();
calSet = (Calendar) now.clone(); Inside -> is this demanded?
calSet.set(Calendar.HOUR_OF_DAY, setTime.getHours()); Yes:
calSet.set(Calendar.MINUTE, setTime.getMinutes()); If last execution known, calculate from it
calSet.set(Calendar.SECOND, 0); If not known, calculate from start of timeframe
calSet.set(Calendar.MILLISECOND, 0); No:
Use end-time and add repetition
// If the starting time is a day ahead remove 1 day. Outside? -> is this demanded?
if(calSet.getTimeInMillis() > now.getTimeInMillis()) Yes:
calSet.add(Calendar.DAY_OF_MONTH, -1); If last execution known, calculate from it
If not known, calculate from end of timeframe
No:
Use start-time and add repetition
*/
long differenceInSeconds = Math.abs(now.getTimeInMillis() - calSet.getTimeInMillis()) / 1000; if(areWeInTimeFrame(trigger, new Date()))
long nextExecutionMultiplier = Math.floorDiv(differenceInSeconds, tf.getRepetition()) + 1; {
long nextScheduleTimestamp = (calSet.getTimeInMillis() / 1000) + (nextExecutionMultiplier * tf.getRepetition()); if(trigger.getTriggerParameter())
Calendar calSchedule = Calendar.getInstance(); {
calSchedule.setTimeInMillis(nextScheduleTimestamp * 1000); if(trigger.getParentRule().getLastExecution() != null)
{
calculationStart = (Calendar) trigger.getParentRule().getLastExecution().clone();
}
else
{
calculationStart = (Calendar) now.clone();
calculationStart.set(Calendar.HOUR_OF_DAY, tf.getTriggerTimeStart().getHours());
calculationStart.set(Calendar.MINUTE, tf.getTriggerTimeStart().getMinutes());
calculationStart.set(Calendar.SECOND, tf.getTriggerTimeStart().getSeconds());
calculationStart.set(Calendar.MILLISECOND, 0);
}
long differenceInSeconds = Math.abs(now.getTimeInMillis() - calculationStart.getTimeInMillis()) / 1000;
long nextExecutionMultiplier = Math.floorDiv(differenceInSeconds, tf.getRepetition()) + 1;
calSchedule = (Calendar) calculationStart.clone();
calSchedule.add(Calendar.SECOND, (int) (nextExecutionMultiplier * tf.getRepetition()));
}
else
{
calculationStart = (Calendar) now.clone();
calculationStart.set(Calendar.HOUR_OF_DAY, tf.getTriggerTimeStop().getHours());
calculationStart.set(Calendar.MINUTE, tf.getTriggerTimeStop().getMinutes());
calculationStart.set(Calendar.SECOND, tf.getTriggerTimeStop().getSeconds());
calculationStart.set(Calendar.MILLISECOND, 0);
calSchedule = (Calendar) calculationStart.clone();
calSchedule.add(Calendar.SECOND, (int) tf.getRepetition());
}
}
else // not in timeframe
{
if (!trigger.getTriggerParameter())
{
if (trigger.getParentRule().getLastExecution() != null)
{
calculationStart = (Calendar) trigger.getParentRule().getLastExecution().clone();
long differenceInSeconds = Math.abs(now.getTimeInMillis() - calculationStart.getTimeInMillis()) / 1000;
long nextExecutionMultiplier = Math.floorDiv(differenceInSeconds, tf.getRepetition()) + 1;
calSchedule = (Calendar) calculationStart.clone();
calSchedule.add(Calendar.SECOND, (int) (nextExecutionMultiplier * tf.getRepetition()));
Miscellaneous.logEvent("i", "getNextRepeatedExecutionAfter()", "Chose " + Miscellaneous.formatDate(calSchedule.getTime()) + " as next repeated execution time.", 5);
return calSchedule;
}
else
{
calculationStart = (Calendar) now.clone();
if(tf.getDayList().contains(now.get(Calendar.DAY_OF_WEEK)))
{
calculationStart.set(Calendar.HOUR_OF_DAY, tf.getTriggerTimeStop().getHours());
calculationStart.set(Calendar.MINUTE, tf.getTriggerTimeStop().getMinutes());
calculationStart.set(Calendar.SECOND, tf.getTriggerTimeStop().getSeconds());
calculationStart.set(Calendar.MILLISECOND, 0);
calculationStart.add(Calendar.SECOND, (int) tf.getRepetition());
return calSchedule; int dayDelta = getDayDelta(now, getNextDayIntForExecution(trigger));
calculationStart.add(Calendar.DAY_OF_WEEK, dayDelta);
}
calSchedule = (Calendar) calculationStart.clone();
Miscellaneous.logEvent("i", "getNextRepeatedExecutionAfter()", "Chose " + Miscellaneous.formatDate(calSchedule.getTime()) + " as next repeated execution time.", 5);
return calSchedule;
}
/*long differenceInSeconds = Math.abs(now.getTimeInMillis() - calculationStart.getTimeInMillis()) / 1000;
long nextExecutionMultiplier = Math.floorDiv(differenceInSeconds, tf.getRepetition()) + 1;
calSchedule = (Calendar) calculationStart.clone();
calSchedule.add(Calendar.SECOND, (int) (nextExecutionMultiplier * tf.getRepetition()));*/
}
else
{
calculationStart = (Calendar) now.clone();
calculationStart.set(Calendar.HOUR_OF_DAY, tf.getTriggerTimeStart().getHours());
calculationStart.set(Calendar.MINUTE, tf.getTriggerTimeStart().getMinutes());
calculationStart.set(Calendar.SECOND, tf.getTriggerTimeStart().getSeconds());
calculationStart.set(Calendar.MILLISECOND, 0);
calSchedule = (Calendar) calculationStart.clone();
calSchedule.add(Calendar.SECOND, (int) (tf.getRepetition()));
}
if (Miscellaneous.compareTimes(calSchedule, now) > 0)
calSchedule.add(Calendar.DAY_OF_MONTH, 1);
}
int dayDelta = getDayDelta(now, getNextDayIntForExecution(trigger));
calSchedule.add(Calendar.DAY_OF_WEEK, dayDelta);
Miscellaneous.logEvent("i", "getNextRepeatedExecutionAfter()", "Chose " + Miscellaneous.formatDate(calSchedule.getTime()) + " as next repeated execution time.", 5);
} }
else else
Miscellaneous.logEvent("i", "DateTimeListener", "Trigger " + trigger.toString() + " is not executed repeatedly.", 5); Miscellaneous.logEvent("i", "getNextRepeatedExecutionAfter()", "Trigger " + trigger.toString() + " is not configured to repeat.", 5);
return null; return calSchedule;
}
public static boolean areWeInTimeFrame(Trigger trigger, Object triggeringObject)
{
/*
* 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());
TimeObject nowTime = TimeObject.valueOf(timeString);
Calendar calNow = Calendar.getInstance();
try
{
TimeFrame tf = new TimeFrame(trigger.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)
)
||
// further case: start and end times are identical, meaning a 24h window
(
Miscellaneous.compareTimes(tf.getTriggerTimeStart(), tf.getTriggerTimeStop()) == 0
)
)
return true;
}
}
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;
}
return false;
} }
} }

View File

@ -76,12 +76,12 @@ public class TimeZoneListener extends BroadcastReceiver implements AutomationLis
if(action.equals(Intent.ACTION_TIMEZONE_CHANGED)) if(action.equals(Intent.ACTION_TIMEZONE_CHANGED))
{ {
Miscellaneous.logEvent("i", "TimeZoneListener", "Device timezone changed. Reloading alarms.", 3); Miscellaneous.logEvent("i", "TimeZoneListener", "Device timezone changed. Reloading alarms.", 3);
DateTimeListener.reloadAlarms(); DateTimeListener.setOrResetAlarms();
} }
else if(action.equals(Intent.ACTION_TIME_CHANGED)) else if(action.equals(Intent.ACTION_TIME_CHANGED))
{ {
Miscellaneous.logEvent("i", "TimeZoneListener", "Device time changed. Reloading alarms.", 3); Miscellaneous.logEvent("i", "TimeZoneListener", "Device time changed. Reloading alarms.", 3);
DateTimeListener.reloadAlarms(); DateTimeListener.setOrResetAlarms();
} }
} }
@Override @Override

View File

@ -0,0 +1,12 @@
* Behoben: Overlay-Berechtigung zum Starten einer anderen Programmaktion nur erforderlich, wenn startByActivity() ausgewählt ist
* Behoben: Broadcast-Receiver-Trigger löste nichts aus, stürzte aber ab * Behoben: Fehler in Android 14 (nicht in Automation!!) erforderte eine Änderung beim Wählen von MMI-Codes, die ein #-Zeichen enthalten.
* Behoben: Speicherberechtigung wurde möglicherweise als nicht erteilt angezeigt, auch wenn sie
* Behoben: Ein sehr alter Bugfix wurde auch auf F-Droid- und Google-Play-Editionen angewendet, die irrtümlicherweise nur in der APK-Edition implementiert waren (TimeFrame-Trigger mit Wiederholungen)
* Behoben: Seltener Absturz beim Starten des Dienstes
* Behoben: Kompensiert für Android-Änderungen, zeitbasierte Trigger sind nun wieder präzise
* Hinzugefügt: neue Aktion -> Screenshot machen * Hinzugefügt: Der Ortungsdienst (GPS) kann zwischen den Zuständen umgeschaltet werden, wenn WRITE_SECURE_SETTINGS von einem Computer aus erteilt wurde
* Hinzugefügt: triggerUrl-Aktion kann jetzt mit POST und Parametern verwendet werden
* Hinzugefügt: Das Ergebnis der triggerUrl-Aktion wird jetzt in einer Variablen gespeichert, wenn Sie es überprüfen möchten
* Hinzugefügt: Neuer Auslöser: Kalenderereignisse
* Hinzugefügt: Das Ergebnis der runExecutable-Aktion wird jetzt in einer Variablen gespeichert, wenn Sie es überprüfen möchten
* Hinzugefügt: Ladeauslöser kann nun zwischen Typen unterscheiden (AC, USB, kabellos)

View File

@ -0,0 +1 @@
* Kalendar Funktion aus Google Version entfernt

View File

@ -0,0 +1,3 @@
* Behoben: Kalender Auslöser wird jetzt in Play Version verborgen
* Behoben: Parameter wurde beim Starten anderer Apps nicht korrekt übergeben
* Behoben: Absturz beim Verlassen der Einstellungen

View File

@ -0,0 +1 @@
* Fixed: Corrected multiple code points that caused crashes when targetApk > 31 (affected only Google Play version)

View File

@ -0,0 +1 @@
* Calendar trigger removed from Google version

View File

@ -0,0 +1,3 @@
* Fixed: Calendar trigger hidden from Play version
* Fixed: Parameters not correctly supplied when starting other apps
* Fixed: Crash when exiting settings while service is running

View File

@ -0,0 +1,14 @@
* Corregido: El permiso de superposición para iniciar otra acción del programa solo se requiere si se selecciona startByActivity()
* Corregido: El disparador del receptor de transmisión no activaba nada, pero se bloqueaba
* Corregido: El error en Android 14 (no en Automatización!!) requería un cambio al marcar códigos MMI que contenían un carácter #.
* Corregido: El permiso de almacenamiento podía mostrarse como no concedido incluso si se
* Corregido: Se aplicó una corrección de errores muy antigua también a las ediciones F-Droid y Google-Play que por error se había implementado solo en la edición APK (disparador de timeFrame con repeticiones)
* Corregido: Raro bloqueo al iniciar el servicio
* Corregido: Compensado por los cambios de Android, los disparadores basados en el tiempo ahora son precisos nuevamente
* Añadido: nueva acción -> tomar captura de pantalla
* Agregado: El servicio de ubicación (GPS) se puede alternar entre estados si WRITE_SECURE_SETTINGS se ha otorgado desde una computadora
* Añadido: la acción triggerUrl ahora se puede usar con POST y parámetros
* Añadido: El resultado de la acción triggerUrl ahora se almacena en una variable si desea verificarlo
* Añadido: Nuevo activador: Eventos de calendario
* Añadido: El resultado de la acción runExecutable ahora se almacena en una variable si desea comprobarlo
* Añadido: El gatillo de carga ahora puede diferenciar entre tipos (CA, USB, de forma inalámbrica)

View File

@ -0,0 +1 @@
* Eliminado el activador de calendario de la versión de Google

View File

@ -0,0 +1,3 @@
* Corregido: Condición de calendario ahora es oculto en la versión Play
* Corregido: Los parámetros no se suministran correctamente al iniciar otras aplicaciones
* Corregido: Bloqueo al salir de la configuración mientras el servicio se está ejecutando

View File

@ -0,0 +1,12 @@
* Corrigé : Autorisation de superposition pour démarrer une autre action de programme requise uniquement si startByActivity() est sélectionnée
* Corrigé : Le déclenchement du récepteur de diffusion ne déclenchait rien, mais plantait
* Corrigé : Un bug dans Android 14 (pas dans Automation !!) nécessitait un changement lors de la composition des codes MMI contenant un caractère #.
* Résolu : L'autorisation de stockage pouvait être affichée comme n'ayant pas été accordée, même si elle l'était
* Corrigé : Correction d'un très ancien bug également sur les éditions F-Droid et Google-Play qui n'avait été implémenté par erreur que dans l'édition APK (déclencheur timeFrame avec répétitions)
* Corrigé : Crash rare lors du démarrage du service
* Corrigé : Corrigé des changements d'Android, les déclencheurs basés sur le temps sont à nouveau précis
* Ajouté : nouvelle action > prendre une capture d'écran * Ajouté : Le service de localisation (GPS) peut être basculé entre les états s'WRITE_SECURE_SETTINGS a été accordé à partir d'un ordinateur
* Ajout : l'action triggerUrl peut maintenant être utilisée avec l'auto-test de démarrage (POST) et les paramètres
* Ajouté : Le résultat de l'action triggerUrl est maintenant stocké dans une variable si vous souhaitez le vérifier
* Ajout : Nouveau déclencheur : Événements de calendrier * Ajouté : Le résultat de l'action runExecutable est maintenant stocké dans une variable si vous souhaitez le vérifier
* Ajouté : La gâchette de charge peut désormais différencier les types (AC, USB, sans fil)

View File

@ -0,0 +1 @@
* Déclencheur d'agenda supprimé de la version Google

View File

@ -0,0 +1,3 @@
* Correction : Déclencheur de calendrier caché dans la version Play
* Correction : Les paramètres n'étaient pas correctement fournis lors du démarrage d'autres applications
* Correction : Plantage lors de la fermeture des paramètres pendant l'exécution du service

View File

@ -0,0 +1,14 @@
* Risolto: l'autorizzazione di sovrapposizione per l'avvio di un'altra azione del programma è richiesta solo se è selezionato startByActivity()
* Risolto: il trigger del ricevitore di trasmissione non attivava nulla, ma si bloccava
* Risolto: un bug in Android 14 (non in Automazione!!) richiedeva una modifica durante la composizione dei codici MMI contenenti un carattere #.
* Risolto: l'autorizzazione di archiviazione poteva essere visualizzata come non concessa anche se lo era
* Risolto: Applicato un bug molto vecchio anche alle edizioni F-Droid e Google-Play che per errore era stato implementato solo nell'edizione APK (trigger timeFrame con ripetizioni)
* Risolto: raro crash durante l'avvio del servizio
* Risolto: compensato per le modifiche di Android, i trigger basati sul tempo ora sono di nuovo precisi
* Aggiunto: nuova azione -> fai screenshot
* Aggiunto: il servizio di localizzazione (GPS) può essere commutato tra gli stati se WRITE_SECURE_SETTINGS è stato concesso da un computer
* Aggiunto: l'azione triggerUrl può ora essere utilizzata con POST e parametri
* Aggiunto: il risultato dell'azione triggerUrl è ora memorizzato in una variabile se si desidera controllarlo
* Aggiunto: Nuovo trigger: Eventi del calendario
* Aggiunto: il risultato dell'azione runExecutable è ora memorizzato in una variabile se si desidera controllarlo
* Aggiunto: il trigger di ricarica ora può distinguere tra i tipi (AC, USB, wireless)

View File

@ -0,0 +1 @@
* Il trigger del calendario è stato rimosso dalla versione Google

View File

@ -0,0 +1,3 @@
* Risolto: trigger del calendario nascosto dalla versione Play
* Risolto: parametri non forniti correttamente all'avvio di altre app
* Risolto: arresto anomalo quando si esce dalle impostazioni mentre il servizio è in esecuzione

View File

@ -0,0 +1,14 @@
* Opgelost: overlay-toestemming voor het starten van een andere programmaactie is alleen vereist als startByActivity() is geselecteerd
* Opgelost: de trigger van de uitzendontvanger zou niets activeren, maar crashen
* Opgelost: bug in Android 14 (niet in Automation!!) vereiste een wijziging bij het kiezen van MMI-codes met een #-teken.
* Opgelost: opslagtoestemming kan worden weergegeven als niet verleend, zelfs als dit wel het geval was
* Opgelost: een zeer oude bugfix ook toegepast op F-Droid- en Google-Play-edities die per ongeluk alleen in de APK-editie waren geïmplementeerd (timeFrame-trigger met herhalingen)
* Opgelost: zeldzame crash tijdens het starten van de service
* Opgelost: gecompenseerd voor Android-wijzigingen, op tijd gebaseerde triggers zijn nu weer nauwkeurig
* Toegevoegd: nieuwe actie > screenshot maken
* Toegevoegd: Locatieservice (GPS) kan worden omgeschakeld tussen staten als WRITE_SECURE_SETTINGS is verleend vanaf een computer
* Toegevoegd: triggerUrl-actie kan nu worden gebruikt met POST en parameters
* Toegevoegd: Resultaat van triggerUrl-actie wordt nu opgeslagen in een variabele als u deze wilt controleren
* Toegevoegd: Nieuwe trigger: Agenda-afspraken
* Toegevoegd: Resultaat van runExecutable actie wordt nu opgeslagen in een variabele als u deze wilt controleren
* Toegevoegd: oplaadtrigger kan nu onderscheid maken tussen typen (AC, USB, draadloos)

View File

@ -0,0 +1 @@
* Agendatrigger verwijderd uit Google-versie

View File

@ -0,0 +1,3 @@
* Opgelost: kalendertrigger verborgen voor Play-versie
* Opgelost: parameters worden niet correct opgegeven bij het starten van andere apps
* Opgelost: crash bij het afsluiten van instellingen terwijl de service actief is

View File

@ -0,0 +1,14 @@
* Naprawiono: Uprawnienie nakładki do uruchamiania innej akcji programu jest wymagane tylko wtedy, gdy wybrano startByActivity()
* Naprawiono: Wyzwalacz odbiornika transmisji nie uruchamiał niczego, ale zawieszał się
* Naprawiono: Błąd w Androidzie 14 (nie w Automation!!) wymagał zmiany podczas wybierania kodów MMI zawierających znak #.
* Naprawiono: Uprawnienie do przechowywania może być wyświetlane jako nieprzyznane, nawet jeśli było
* Naprawiono: Zastosowano bardzo starą poprawkę błędu również do edycji F-Droid i Google-Play, która przez pomyłkę została zaimplementowana tylko w edycji APK (wyzwalacz timeFrame z powtórzeniami)
* Naprawiono: Rzadka awaria podczas uruchamiania usługi
* Naprawiono: Zrekompensowano zmiany w Androidzie, wyzwalacze oparte na czasie są teraz ponownie precyzyjne
* Dodano: nowa akcja -> zrób zrzut ekranu
* Dodano: Usługa lokalizacyjna (GPS) może być przełączana między stanami, jeśli WRITE_SECURE_SETTINGS została przyznana z komputera
* Dodano: akcja triggerUrl może być teraz używana z POST i parametrami
* Dodano: Wynik akcji triggerUrl jest teraz przechowywany w zmiennej, jeśli chcesz to sprawdzić
* Dodano: Nowy wyzwalacz: Wydarzenia w kalendarzu
* Dodano: Wynik akcji runExecutable jest teraz przechowywany w zmiennej, jeśli chcesz to sprawdzić
* Dodano: Wyzwalacz ładowania może teraz rozróżniać typy (AC, USB, bezprzewodowo)

View File

@ -0,0 +1 @@
* Wyzwalacz kalendarza usunięty z wersji Google

View File

@ -0,0 +1,3 @@
* Naprawiono: Wyzwalacz kalendarza ukryty w wersji Play
* Naprawiono: Parametry nie są poprawnie dostarczane podczas uruchamiania innych aplikacji
* Naprawiono: Awaria podczas wychodzenia z ustawień, gdy usługa jest uruchomiona

View File

@ -0,0 +1,14 @@
* Исправлено: Разрешение на оверлей для запуска других действий программы требуется только в том случае, если выбран startByActivity()
* Исправлено: Триггер широковещательного приемника не вызывал ничего, кроме сбоя.
* Исправлено: Ошибка в Android 14 (не в Automation!!) требовала изменения при наборе кодов MMI, содержащих символ #.
* Исправлено: Разрешение на хранение могло отображаться как не предоставленное, даже если оно было
* Исправлено: Применено очень старое исправление ошибки также к редакциям F-Droid и Google-Play, которые по ошибке были реализованы только в редакции APK (триггер timeFrame с повторами)
* Исправлено: редкий сбой при запуске сервиса.
* Исправлено: Компенсированы изменения Android, триггеры, основанные на времени, теперь снова точны
* Добавлено: новое действие -> сделать скриншот
* Добавлено: Служба определения местоположения (GPS) может переключаться между состояниями, если WRITE_SECURE_SETTINGS было предоставлено с компьютера.
* Добавлено: действие triggerUrl теперь можно использовать с POST и параметрами
* Добавлено: Результат действия triggerUrl теперь сохраняется в переменной, если вы хотите проверить это
* Добавлено: Новый триггер: События календаря
* Добавлено: Результат действия runExecutable теперь сохраняется в переменной, если вы хотите проверить это
* Добавлено: Триггер зарядки теперь может различать типы (переменный ток, USB, беспроводная связь)

View File

@ -0,0 +1 @@
* Триггер календаря удален из версии Google

View File

@ -0,0 +1,3 @@
* Исправлено: Триггер календаря скрыт в игровой версии
* Исправлено: Параметры неправильно указываются при запуске других приложений.
* Исправлено: Сбой при выходе из настроек во время работы сервиса

View File

@ -0,0 +1,13 @@
* 修复:只有在选择了 startByActivity 时才需要启动其他程序操作的叠加权限
* 修复:广播接收机触发不会触发任何内容,但会崩溃
* 已修复Android 14不在 Automation!! 中)中的错误在拨打包含 # 字符的 MMI 代码时需要更改。
* 修复:存储权限可能显示为未授予,即使已授予
* 修复:将一个非常古老的错误修复也应用于 F-Droid 和 Google-Play 版本,这些版本错误地仅在 APK 版本中实现(具有重复的 timeFrame 触发器)
* 修复:启动服务时罕见崩溃
* 已修复:补偿了 Android 更改,基于时间的触发器现在再次精确
* 新增:新动作 ->截图 * 新增:定位服务 GPS 可以在各州之间切换前提是已从计算机授予WRITE_SECURE_SETTINGS
* 新增triggerUrl 操作现在可以与 POST 和参数一起使用
* 新增triggerUrl 操作的结果现在存储在变量中,如果您想检查它
* 新增:新触发器:日历事件
* 新增runExecutable 操作的结果现在存储在变量中,如果您想检查它
* 新增充电触发器现在可以区分类型交流、USB、无线

View File

@ -0,0 +1 @@
* 日历触发器已从 Google 版本中删除

View File

@ -0,0 +1,3 @@
* 修复:播放版本中隐藏的日历触发器
* 修复:启动其他应用程序时未正确提供参数的问题
* 修复:服务运行时退出设置时崩溃