diff --git a/app/src/apkFlavor/java/com/jens/automation2/Rule.java b/app/src/apkFlavor/java/com/jens/automation2/Rule.java index 03f2da0..3569063 100644 --- a/app/src/apkFlavor/java/com/jens/automation2/Rule.java +++ b/app/src/apkFlavor/java/com/jens/automation2/Rule.java @@ -768,7 +768,7 @@ public class Rule implements Comparable for (StatusBarNotification sbn : NotificationListener.getInstance().getActiveNotifications()) { - if(lastExecution == null || sbn.getPostTime() > this.lastExecution.getTimeInMillis()) + if(getLastExecution() == null || sbn.getPostTime() > this.lastExecution.getTimeInMillis()) { String app = sbn.getPackageName(); String title = sbn.getNotification().extras.getString(EXTRA_TITLE); diff --git a/app/src/fdroidFlavor/java/com/jens/automation2/Rule.java b/app/src/fdroidFlavor/java/com/jens/automation2/Rule.java index 4d94fd7..f5034c6 100644 --- a/app/src/fdroidFlavor/java/com/jens/automation2/Rule.java +++ b/app/src/fdroidFlavor/java/com/jens/automation2/Rule.java @@ -1,5 +1,6 @@ package com.jens.automation2; +import android.annotation.SuppressLint; import android.bluetooth.BluetoothDevice; import android.content.Context; import android.os.AsyncTask; @@ -9,6 +10,7 @@ import android.service.notification.StatusBarNotification; import android.util.Log; import android.widget.Toast; +import com.jens.automation2.location.LocationProvider; import com.jens.automation2.location.WifiBroadcastReceiver; import com.jens.automation2.receivers.BatteryReceiver; import com.jens.automation2.receivers.BluetoothReceiver; @@ -129,6 +131,7 @@ public class Rule implements Comparable { return this.getName(); } + @SuppressLint("NewApi") public String toStringLong() { String returnString = ""; @@ -503,7 +506,7 @@ public class Rule implements Comparable { if(oneTrigger.getTriggerParameter()) { - if(com.jens.automation2.location.LocationProvider.getSpeed() < oneTrigger.getSpeed()) + if(LocationProvider.getSpeed() < oneTrigger.getSpeed()) { Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreSlowerThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3); return false; @@ -511,7 +514,7 @@ public class Rule implements Comparable } else { - if(com.jens.automation2.location.LocationProvider.getSpeed() > oneTrigger.getSpeed()) + if(LocationProvider.getSpeed() > oneTrigger.getSpeed()) { Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreFasterThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3); return false; @@ -634,109 +637,69 @@ public class Rule implements Comparable else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.bluetoothConnection)) { Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Checking for bluetooth...", 4); - -// if( // connected / disconnected -// (oneTrigger.getTriggerParameter() && (BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_CONNECTED) | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACL_CONNECTED"))) -// | -// (!oneTrigger.getTriggerParameter() && (BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED) | BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECTED) | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACTION_ACL_DISCONNECT_REQUESTED") | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACTION_ACL_DISCONNECTED"))) -// ) -// { -// if(oneTrigger.getBluetoothDeviceAddress() != null) -// { -// if(oneTrigger.getBluetoothDeviceAddress().equals("")) -// { -// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "No bluetooth address specified, any will do.", 4); -// } -// else if(oneTrigger.getBluetoothDeviceAddress().equals("")) -// { -// // ??? -// } -// else -// { -// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Bluetooth address specified, checking that.", 4); -// if(!BluetoothReceiver.getLastAffectedDevice().getAddress().equals(oneTrigger.getBluetoothDeviceAddress())) -// { -// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyNotTheCorrectDeviceAddress), 3); -// return false; -// } -// else -// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Bluetooth address matches. Rule will apply.", 4); -// } -// } -// } -// else if(BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_FOUND) | BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_FOUND)) -// { -// if(!oneTrigger.getTriggerParameter()) -// { -// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyDeviceInRangeButShouldNotBe), 3); -// return false; -// } -// } -// else // above only checks for last action, this checks for things in the past + + if(oneTrigger.getBluetoothDeviceAddress().equals("")) { - if(oneTrigger.getBluetoothDeviceAddress().equals("")) - { - if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) - { - if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter()) - return false; - } - else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) - { - if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter()) - return false; - } - else - { - // range - if(BluetoothReceiver.isAnyDeviceInRange() != oneTrigger.getTriggerParameter()) - return false; - } - } - else if(oneTrigger.getBluetoothDeviceAddress().equals("")) + if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) { - if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) - { - if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter()) - return false; - } - else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) - { - if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter()) - return false; - } - else - { - // range - if(BluetoothReceiver.isAnyDeviceInRange() == oneTrigger.getTriggerParameter()) - return false; - } + if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter()) + return false; } - else if(oneTrigger.getBluetoothDeviceAddress().length() > 0) + else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) { - if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) - { - if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter()) - return false; - } - else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) - { - if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter()) - return false; - } - else - { - // range - if(BluetoothReceiver.isDeviceInRange(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter()) - return false; - } + if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter()) + return false; } else { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyStateNotCorrect), 3); - return false; + // range + if(BluetoothReceiver.isAnyDeviceInRange() != oneTrigger.getTriggerParameter()) + return false; } } + else if(oneTrigger.getBluetoothDeviceAddress().equals("")) + { + if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) + { + if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter()) + return false; + } + else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) + { + if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter()) + return false; + } + else + { + // range + if(BluetoothReceiver.isAnyDeviceInRange() == oneTrigger.getTriggerParameter()) + return false; + } + } + else if(oneTrigger.getBluetoothDeviceAddress().length() > 0) + { + if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) + { + if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter()) + return false; + } + else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) + { + if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter()) + return false; + } + else + { + // range + if(BluetoothReceiver.isDeviceInRange(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter()) + return false; + } + } + else + { + Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyStateNotCorrect), 3); + return false; + } } else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.headsetPlugged)) { @@ -934,8 +897,17 @@ public class Rule implements Comparable publishProgress(message); for(int i = 0; i< Rule.this.getActionSet().size(); i++) - Rule.this.getActionSet().get(i).run(automationService, doToggle); - + { + try + { + Rule.this.getActionSet().get(i).run(automationService, doToggle); + } + catch(Exception e) + { + Miscellaneous.logEvent("e", "RuleExecution", "Error running action of rule " + Rule.this.getName() + ": " + Log.getStackTraceString(e), 1); + } + } + // Keep log of last x rule activations (Settings) try { diff --git a/app/src/googlePlayFlavor/java/com/jens/automation2/Rule.java b/app/src/googlePlayFlavor/java/com/jens/automation2/Rule.java index ae9a33f..3d84d95 100644 --- a/app/src/googlePlayFlavor/java/com/jens/automation2/Rule.java +++ b/app/src/googlePlayFlavor/java/com/jens/automation2/Rule.java @@ -1,5 +1,6 @@ package com.jens.automation2; +import android.annotation.SuppressLint; import android.bluetooth.BluetoothDevice; import android.content.Context; import android.os.AsyncTask; @@ -10,6 +11,7 @@ import android.util.Log; import android.widget.Toast; import com.google.android.gms.location.DetectedActivity; +import com.jens.automation2.location.LocationProvider; import com.jens.automation2.location.WifiBroadcastReceiver; import com.jens.automation2.receivers.ActivityDetectionReceiver; import com.jens.automation2.receivers.BatteryReceiver; @@ -131,6 +133,7 @@ public class Rule implements Comparable { return this.getName(); } + @SuppressLint("NewApi") public String toStringLong() { String returnString = ""; @@ -505,7 +508,7 @@ public class Rule implements Comparable { if(oneTrigger.getTriggerParameter()) { - if(com.jens.automation2.location.LocationProvider.getSpeed() < oneTrigger.getSpeed()) + if(LocationProvider.getSpeed() < oneTrigger.getSpeed()) { Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreSlowerThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3); return false; @@ -513,7 +516,7 @@ public class Rule implements Comparable } else { - if(com.jens.automation2.location.LocationProvider.getSpeed() > oneTrigger.getSpeed()) + if(LocationProvider.getSpeed() > oneTrigger.getSpeed()) { Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreFasterThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3); return false; @@ -665,109 +668,69 @@ public class Rule implements Comparable else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.bluetoothConnection)) { Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Checking for bluetooth...", 4); - -// if( // connected / disconnected -// (oneTrigger.getTriggerParameter() && (BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_CONNECTED) | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACL_CONNECTED"))) -// | -// (!oneTrigger.getTriggerParameter() && (BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED) | BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECTED) | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACTION_ACL_DISCONNECT_REQUESTED") | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACTION_ACL_DISCONNECTED"))) -// ) -// { -// if(oneTrigger.getBluetoothDeviceAddress() != null) -// { -// if(oneTrigger.getBluetoothDeviceAddress().equals("")) -// { -// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "No bluetooth address specified, any will do.", 4); -// } -// else if(oneTrigger.getBluetoothDeviceAddress().equals("")) -// { -// // ??? -// } -// else -// { -// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Bluetooth address specified, checking that.", 4); -// if(!BluetoothReceiver.getLastAffectedDevice().getAddress().equals(oneTrigger.getBluetoothDeviceAddress())) -// { -// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyNotTheCorrectDeviceAddress), 3); -// return false; -// } -// else -// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Bluetooth address matches. Rule will apply.", 4); -// } -// } -// } -// else if(BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_FOUND) | BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_FOUND)) -// { -// if(!oneTrigger.getTriggerParameter()) -// { -// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyDeviceInRangeButShouldNotBe), 3); -// return false; -// } -// } -// else // above only checks for last action, this checks for things in the past + + if(oneTrigger.getBluetoothDeviceAddress().equals("")) { - if(oneTrigger.getBluetoothDeviceAddress().equals("")) - { - if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) - { - if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter()) - return false; - } - else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) - { - if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter()) - return false; - } - else - { - // range - if(BluetoothReceiver.isAnyDeviceInRange() != oneTrigger.getTriggerParameter()) - return false; - } - } - else if(oneTrigger.getBluetoothDeviceAddress().equals("")) + if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) { - if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) - { - if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter()) - return false; - } - else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) - { - if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter()) - return false; - } - else - { - // range - if(BluetoothReceiver.isAnyDeviceInRange() == oneTrigger.getTriggerParameter()) - return false; - } + if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter()) + return false; } - else if(oneTrigger.getBluetoothDeviceAddress().length() > 0) + else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) { - if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) - { - if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter()) - return false; - } - else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) - { - if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter()) - return false; - } - else - { - // range - if(BluetoothReceiver.isDeviceInRange(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter()) - return false; - } + if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter()) + return false; } else { - Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyStateNotCorrect), 3); - return false; + // range + if(BluetoothReceiver.isAnyDeviceInRange() != oneTrigger.getTriggerParameter()) + return false; } } + else if(oneTrigger.getBluetoothDeviceAddress().equals("")) + { + if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) + { + if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter()) + return false; + } + else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) + { + if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter()) + return false; + } + else + { + // range + if(BluetoothReceiver.isAnyDeviceInRange() == oneTrigger.getTriggerParameter()) + return false; + } + } + else if(oneTrigger.getBluetoothDeviceAddress().length() > 0) + { + if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED)) + { + if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter()) + return false; + } + else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED))) + { + if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter()) + return false; + } + else + { + // range + if(BluetoothReceiver.isDeviceInRange(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter()) + return false; + } + } + else + { + Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyStateNotCorrect), 3); + return false; + } } else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.headsetPlugged)) { @@ -840,6 +803,7 @@ public class Rule implements Comparable } foundMatch = true; + break; } } @@ -965,7 +929,16 @@ public class Rule implements Comparable publishProgress(message); for(int i = 0; i< Rule.this.getActionSet().size(); i++) - Rule.this.getActionSet().get(i).run(automationService, doToggle); + { + try + { + Rule.this.getActionSet().get(i).run(automationService, doToggle); + } + catch(Exception e) + { + Miscellaneous.logEvent("e", "RuleExecution", "Error running action of rule " + Rule.this.getName() + ": " + Log.getStackTraceString(e), 1); + } + } // Keep log of last x rule activations (Settings) try diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6276ea6..d69ac25 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -609,7 +609,7 @@ Would you like to receive (only important) news about this app on the main screen? Those are downloaded from the developer\'s website. There will be no intrusive notification, just a text on the main screen when you open the app. Location disabled Location cannot be determined anymore. Click here to find out why. - Unfortunately your location cannot be determined anymore. A debt of gratitude is owed to Google for its infinite wisdom and amiableness.\\n\\nLet me explain this further. Starting with Android 10 a new permission was introduced that is needed to determine your location in the background (which of course is required for an app like this). Whilst I consider that a good idea in general the chicanery it involves for developers are not.\\n\\nWhen developing an app you can try to qualify for this permission by abiding to a catalog of requirements. Unfortunately new versions of my app have been rejected over a period of three months. I fulfilled all these requirements, Google\'s shitty development support claimed I would not. After giving them proof that I did after all - I got a response like \"I cannot help you anymore\". Eventually I gave up. \\n\\nAs a consequence the Google Play version can NOT use your location as a trigger anymore. My only alternative option would have been to have this application removed from the store entirely.\\n\\nI\'m very sorry about that, but I\'ve tried my best arguing with a \"support\" that repeatedly failed to pass the Turing test.\\n\\nThe good news: You can still have it all!\\n\\nAutomation is now open source and can be found in F-Droid. That is an app store that really cares about your privacy - rather than just acting like that. Simply backup your config file, uninstall this app, install it again from F-Droid, restore your config file - done.\\n\\nClick here to find out more: + Unfortunately your location cannot be determined anymore. A debt of gratitude is owed to Google for its infinite wisdom and amiableness.\\n\\nLet me explain this further. Starting with Android 10 a new permission was introduced that is needed to determine your location in the background (which of course is required for an app like this). Whilst I consider that a good idea in general the chicanery it involves for developers is not.\\n\\nWhen developing an app you can try to qualify for this permission by abiding to a catalog of requirements. Unfortunately new versions of my app have been rejected over a period of three months. I fulfilled all those requirements, Google\'s shitty development support claimed I would not. After giving them proof that I did after all - I got a response like \"I cannot help you anymore\". Eventually I gave up. \\n\\nAs a consequence the Google Play version can NOT use your location as a trigger anymore. My only alternative option would have been to have this application removed from the store entirely.\\n\\nI\'m very sorry about that, but I\'ve tried my best arguing with a \"support\" that repeatedly failed to pass the Turing test.\\n\\nThe good news: You can still have it all!\\n\\nAutomation is now open source and can be found in F-Droid. That is an app store that really cares about your privacy - rather than just acting like that. Simply backup your config file, uninstall this app, install it again from F-Droid, restore your config file - done.\\n\\nClick here to find out more: Config and log files are stored in folder %1$s. Click on this text to open a file explorer. Unfortunately this will only work with a rooted device or a debug version. If you want to send me or yourself the config- and logfiles you can use the below button. Notification Title