Compare commits
41 Commits
v1.6.40
...
5fdc68e396
Author | SHA1 | Date | |
---|---|---|---|
5fdc68e396 | |||
ab0f2d88b4 | |||
06a6651fae | |||
473c464bf7 | |||
a5b9ced9ba | |||
9cea3f4285 | |||
0438a58f3e | |||
97f32bd012 | |||
6588443459 | |||
604ab0eb43 | |||
31c4f6c1d1 | |||
0c646b55fc | |||
88cdc366c5 | |||
2bd94e8a3d | |||
074f75ed20 | |||
d988e1f43d | |||
23502f52bb | |||
9a6083247f | |||
ba2a340bdf | |||
9e2f7c16f6 | |||
d042b3f35a | |||
220d2d316e | |||
4aa095e801 | |||
b5bd332ff5 | |||
969937f8a0 | |||
e3598cc475 | |||
e63d97be0c | |||
e60fb1535a | |||
8563234db3 | |||
448942e4e8 | |||
dcdb770d9f | |||
b5040cedb3 | |||
423839fa43 | |||
a6edab75ce | |||
cb430b957f | |||
b6a0f6dd91 | |||
bc32cbc179 | |||
d9cc604bdd | |||
db21011b7f | |||
dc35c8b7fb | |||
5a7cbfcdc9 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -148,3 +148,4 @@ fabric.properties
|
||||
|
||||
/app/app-release.apk
|
||||
Automation_settings.xml
|
||||
/app/googlePlayFlavor/
|
||||
|
2
.idea/deploymentTargetDropDown.xml
generated
2
.idea/deploymentTargetDropDown.xml
generated
@@ -12,6 +12,6 @@
|
||||
</deviceKey>
|
||||
</Target>
|
||||
</targetSelectedWithDropDown>
|
||||
<timeTargetWasSelectedWithDropDown value="2021-08-14T11:41:28.444891400Z" />
|
||||
<timeTargetWasSelectedWithDropDown value="2021-11-07T11:58:40.808135100Z" />
|
||||
</component>
|
||||
</project>
|
@@ -11,8 +11,8 @@ android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion '29.0.2'
|
||||
useLibrary 'org.apache.http.legacy'
|
||||
versionCode 110
|
||||
versionName "1.6.40"
|
||||
versionCode 113
|
||||
versionName "1.6.43"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
@@ -36,27 +36,27 @@ android {
|
||||
flavorDimensions "version"
|
||||
|
||||
productFlavors
|
||||
{
|
||||
googlePlayFlavor
|
||||
{
|
||||
dimension "version"
|
||||
versionNameSuffix "-googlePlay"
|
||||
targetSdkVersion 29
|
||||
}
|
||||
{
|
||||
googlePlayFlavor
|
||||
{
|
||||
dimension "version"
|
||||
versionNameSuffix "-googlePlay"
|
||||
targetSdkVersion 30
|
||||
}
|
||||
|
||||
fdroidFlavor
|
||||
{
|
||||
dimension "version"
|
||||
targetSdkVersion 28
|
||||
}
|
||||
fdroidFlavor
|
||||
{
|
||||
dimension "version"
|
||||
targetSdkVersion 28
|
||||
}
|
||||
|
||||
apkFlavor
|
||||
{
|
||||
dimension "version"
|
||||
versionNameSuffix "-apk"
|
||||
targetSdkVersion 28
|
||||
}
|
||||
}
|
||||
apkFlavor
|
||||
{
|
||||
dimension "version"
|
||||
versionNameSuffix "-apk"
|
||||
targetSdkVersion 28
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": 2,
|
||||
"version": 3,
|
||||
"artifactType": {
|
||||
"type": "APK",
|
||||
"kind": "Directory"
|
||||
@@ -10,9 +10,11 @@
|
||||
{
|
||||
"type": "SINGLE",
|
||||
"filters": [],
|
||||
"versionCode": 107,
|
||||
"versionName": "1.6.36-googlePlay",
|
||||
"attributes": [],
|
||||
"versionCode": 113,
|
||||
"versionName": "1.6.43-googlePlay",
|
||||
"outputFile": "app-googlePlayFlavor-release.apk"
|
||||
}
|
||||
]
|
||||
],
|
||||
"elementType": "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" />
|
||||
|
||||
|
@@ -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,8 @@ 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;
|
||||
|
||||
|
||||
@@ -796,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())
|
||||
{
|
||||
@@ -815,8 +820,8 @@ public class Rule implements Comparable<Rule>
|
||||
if(getLastExecution() == null || sbn.getPostTime() > this.lastExecution.getTimeInMillis())
|
||||
{
|
||||
String notificationApp = sbn.getPackageName();
|
||||
String notificationTitle = sbn.getNotification().extras.getString(EXTRA_TITLE);
|
||||
String notificationText = sbn.getNotification().extras.getString(EXTRA_TEXT);
|
||||
String notificationTitle = null;
|
||||
String notificationText = null;
|
||||
|
||||
Miscellaneous.logEvent("i", "NotificationCheck", "Checking if this notification matches our rule " + this.getName() + ". App: " + notificationApp + ", title: " + notificationTitle + ", text: " + notificationText, 5);
|
||||
|
||||
@@ -828,10 +833,28 @@ public class Rule implements Comparable<Rule>
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!StringUtils.isEmpty(myTitle))
|
||||
else
|
||||
{
|
||||
if (!Miscellaneous.compare(myTitleDir, myTitle, notificationTitle))
|
||||
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;
|
||||
@@ -840,9 +863,14 @@ public class Rule implements Comparable<Rule>
|
||||
else
|
||||
Miscellaneous.logEvent("i", "NotificationCheck", "A required title for a notification trigger was not specified.", 5);
|
||||
|
||||
if (!StringUtils.isEmpty(myText))
|
||||
// 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, notificationText))
|
||||
if (!Miscellaneous.compare(myTextDir, requiredText, notificationText))
|
||||
{
|
||||
Miscellaneous.logEvent("i", "NotificationCheck", "Notification text does not match rule.", 5);
|
||||
continue;
|
||||
@@ -876,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;
|
||||
}
|
||||
}
|
||||
@@ -953,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
|
||||
@@ -963,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)
|
||||
|
@@ -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" />
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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" />
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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>();
|
||||
@@ -365,7 +377,7 @@ public class Action
|
||||
Actions.setUsbTethering(context, getParameter1(), toggleActionIfPossible);
|
||||
break;
|
||||
case setWifi:
|
||||
Actions.setWifi(context, getParameter1(), toggleActionIfPossible);
|
||||
Actions.WifiStuff.setWifi(context, getParameter1(), toggleActionIfPossible);
|
||||
break;
|
||||
case setWifiTethering:
|
||||
Actions.setWifiTethering(context, getParameter1(), toggleActionIfPossible);
|
||||
@@ -381,7 +393,7 @@ public class Action
|
||||
break;
|
||||
case wakeupDevice:
|
||||
Actions.wakeupDevice(Long.parseLong(this.getParameter2()));
|
||||
// wakeupDevice() will create a seperate thread. That'll take some time, we wait 100ms.
|
||||
// wakeupDevice() will create a separate thread. That'll take some time, we wait 100ms.
|
||||
try
|
||||
{
|
||||
Thread.sleep(100);
|
||||
|
@@ -3,8 +3,12 @@ package com.jens.automation2;
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -23,10 +27,11 @@ import android.telephony.SmsManager;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import com.jens.automation2.actions.wifi_router.MyOnStartTetheringCallback;
|
||||
import com.jens.automation2.actions.wifi_router.MyOreoWifiManager;
|
||||
import com.jens.automation2.location.WifiBroadcastReceiver;
|
||||
@@ -41,13 +46,16 @@ import org.apache.http.conn.util.InetAddressUtils;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.security.KeyStore;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
@@ -70,48 +78,85 @@ public class Actions
|
||||
public static final String wireguard_tunnel_down = "com.wireguard.android.action.SET_TUNNEL_DOWN";
|
||||
public static final String wireguard_tunnel_refresh = "com.wireguard.android.action.REFRESH_TUNNEL_STATES";
|
||||
|
||||
public static Boolean setWifi(Context context, Boolean desiredState, boolean toggleActionIfPossible)
|
||||
public static class WifiStuff
|
||||
{
|
||||
Miscellaneous.logEvent("i", "Wifi", "Changing Wifi to " + String.valueOf(desiredState), 4);
|
||||
|
||||
if (desiredState && Settings.useWifiForPositioning)
|
||||
WifiBroadcastReceiver.startWifiReceiver(autoMationServerRef.getLocationProvider());
|
||||
|
||||
WifiManager myWifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
|
||||
|
||||
// toggle
|
||||
if (toggleActionIfPossible)
|
||||
public static Boolean setWifi(Context context, Boolean desiredState, boolean toggleActionIfPossible)
|
||||
{
|
||||
Toast.makeText(context, context.getResources().getString(R.string.toggling) + " " + context.getResources().getString(R.string.wifi), Toast.LENGTH_LONG).show();
|
||||
desiredState = !myWifi.isWifiEnabled();
|
||||
if(context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.Q)
|
||||
return setWifiWithRoot(context, desiredState, toggleActionIfPossible);
|
||||
else
|
||||
return setWifiOldFashioned(context, desiredState, toggleActionIfPossible);
|
||||
}
|
||||
|
||||
// Only perform action if necessary
|
||||
if ((!myWifi.isWifiEnabled() && desiredState) | (myWifi.isWifiEnabled() && !desiredState))
|
||||
public static Boolean setWifiWithRoot(Context context, Boolean desiredState, boolean toggleActionIfPossible)
|
||||
{
|
||||
String wifiString = "";
|
||||
Miscellaneous.logEvent("i", "Wifi", "Changing wifi to " + String.valueOf(desiredState) + ", but with root permissions.", 4);
|
||||
|
||||
if (desiredState)
|
||||
{
|
||||
wifiString = context.getResources().getString(R.string.activating) + " " + context.getResources().getString(R.string.wifi);
|
||||
}
|
||||
String command = null;
|
||||
int state = 0;
|
||||
|
||||
String desiredStateString;
|
||||
if(desiredState)
|
||||
desiredStateString = "enable";
|
||||
else
|
||||
desiredStateString = "disable";
|
||||
|
||||
try
|
||||
{
|
||||
wifiString = context.getResources().getString(R.string.deactivating) + " " + context.getResources().getString(R.string.wifi);
|
||||
command = "svc wifi " + desiredStateString;
|
||||
Miscellaneous.logEvent("i", "setWifiWithRoot()", "Running command as root: " + command.toString(), 5);
|
||||
return executeCommandViaSu(new String[]{command});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Oops! Something went wrong, so we throw the exception here.
|
||||
throw e;
|
||||
}
|
||||
|
||||
Toast.makeText(context, wifiString, Toast.LENGTH_LONG).show();
|
||||
|
||||
boolean returnValue = myWifi.setWifiEnabled(desiredState);
|
||||
if (!returnValue)
|
||||
Miscellaneous.logEvent("i", "Wifi", "Error changing Wifi to " + String.valueOf(desiredState), 2);
|
||||
else
|
||||
Miscellaneous.logEvent("i", "Wifi", "Wifi changed to " + String.valueOf(desiredState), 2);
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
return true;
|
||||
public static Boolean setWifiOldFashioned(Context context, Boolean desiredState, boolean toggleActionIfPossible)
|
||||
{
|
||||
Miscellaneous.logEvent("i", "Wifi", "Changing wifi to " + String.valueOf(desiredState), 4);
|
||||
|
||||
if (desiredState && Settings.useWifiForPositioning)
|
||||
WifiBroadcastReceiver.startWifiReceiver(autoMationServerRef.getLocationProvider());
|
||||
|
||||
WifiManager myWifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
|
||||
|
||||
// toggle
|
||||
if (toggleActionIfPossible)
|
||||
{
|
||||
Toast.makeText(context, context.getResources().getString(R.string.toggling) + " " + context.getResources().getString(R.string.wifi), Toast.LENGTH_LONG).show();
|
||||
desiredState = !myWifi.isWifiEnabled();
|
||||
}
|
||||
|
||||
// Only perform action if necessary
|
||||
if ((!myWifi.isWifiEnabled() && desiredState) | (myWifi.isWifiEnabled() && !desiredState))
|
||||
{
|
||||
String wifiString = "";
|
||||
|
||||
if (desiredState)
|
||||
{
|
||||
wifiString = context.getResources().getString(R.string.activating) + " " + context.getResources().getString(R.string.wifi);
|
||||
}
|
||||
else
|
||||
{
|
||||
wifiString = context.getResources().getString(R.string.deactivating) + " " + context.getResources().getString(R.string.wifi);
|
||||
}
|
||||
|
||||
Toast.makeText(context, wifiString, Toast.LENGTH_LONG).show();
|
||||
|
||||
boolean returnValue = myWifi.setWifiEnabled(desiredState);
|
||||
if (!returnValue)
|
||||
Miscellaneous.logEvent("i", "Wifi", "Error changing Wifi to " + String.valueOf(desiredState), 2);
|
||||
else
|
||||
Miscellaneous.logEvent("i", "Wifi", "Wifi changed to " + String.valueOf(desiredState), 2);
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void setDisplayRotation(Context myContext, Boolean desiredState, boolean toggleActionIfPossible)
|
||||
@@ -265,6 +310,118 @@ public class Actions
|
||||
return true;
|
||||
}
|
||||
|
||||
public static class BluetoothTetheringClass
|
||||
{
|
||||
static Object instance = null;
|
||||
static Method setTetheringOn = null;
|
||||
static Method isTetheringOn = null;
|
||||
static Object mutex = new Object();
|
||||
|
||||
public static Boolean setBluetoothTethering(Context context, Boolean desiredState, boolean toggleActionIfPossible)
|
||||
{
|
||||
Miscellaneous.logEvent("i", "Bluetooth Tethering", "Changing Bluetooth Tethering to " + String.valueOf(desiredState), 4);
|
||||
|
||||
boolean state = Actions.isWifiApEnabled(context);
|
||||
|
||||
if (toggleActionIfPossible)
|
||||
{
|
||||
Miscellaneous.logEvent("i", "Bluetooth Tethering", context.getResources().getString(R.string.toggling), 2);
|
||||
desiredState = !state;
|
||||
}
|
||||
|
||||
if (((state && !desiredState) || (!state && desiredState)))
|
||||
{
|
||||
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
Class<?> classBluetoothPan = null;
|
||||
Constructor<?> BTPanCtor = null;
|
||||
Object BTSrvInstance = null;
|
||||
Method mBTPanConnect = null;
|
||||
|
||||
try
|
||||
{
|
||||
classBluetoothPan = Class.forName("android.bluetooth.BluetoothPan");
|
||||
mBTPanConnect = classBluetoothPan.getDeclaredMethod("connect", BluetoothDevice.class);
|
||||
BTPanCtor = classBluetoothPan.getDeclaredConstructor(Context.class, BluetoothProfile.ServiceListener.class);
|
||||
BTPanCtor.setAccessible(true);
|
||||
BTSrvInstance = BTPanCtor.newInstance(context, new BTPanServiceListener(context));
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
Miscellaneous.logEvent("e", "Bluetooth Tethering", Log.getStackTraceString(e), 1);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Miscellaneous.logEvent("e", "Bluetooth Tethering", Log.getStackTraceString(e), 1);
|
||||
}
|
||||
|
||||
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
|
||||
|
||||
// If there are paired devices
|
||||
if (pairedDevices.size() > 0)
|
||||
{
|
||||
// Loop through paired devices
|
||||
for (BluetoothDevice device : pairedDevices)
|
||||
{
|
||||
try
|
||||
{
|
||||
mBTPanConnect.invoke(BTSrvInstance, device);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Miscellaneous.logEvent("e", "Bluetooth Tethering", Log.getStackTraceString(e), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static class BTPanServiceListener implements BluetoothProfile.ServiceListener
|
||||
{
|
||||
private final Context context;
|
||||
|
||||
public BTPanServiceListener(final Context context)
|
||||
{
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(final int profile, final BluetoothProfile proxy)
|
||||
{
|
||||
//Some code must be here or the compiler will optimize away this callback.
|
||||
|
||||
try
|
||||
{
|
||||
synchronized (mutex)
|
||||
{
|
||||
setTetheringOn.invoke(instance, true);
|
||||
if ((Boolean) isTetheringOn.invoke(instance, null))
|
||||
{
|
||||
Miscellaneous.logEvent("e", "Bluetooth Tethering", "BT Tethering is on", 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Miscellaneous.logEvent("e", "Bluetooth Tethering", "BT Tethering is off", 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InvocationTargetException e)
|
||||
{
|
||||
Miscellaneous.logEvent("e", "Bluetooth Tethering", Log.getStackTraceString(e), 1);
|
||||
}
|
||||
catch (IllegalAccessException e)
|
||||
{
|
||||
Miscellaneous.logEvent("e", "Bluetooth Tethering", Log.getStackTraceString(e), 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(final int profile)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean setUsbTethering(Context context2, Boolean desiredState, boolean toggleActionIfPossible)
|
||||
{
|
||||
//TODO:toggle not really implemented, yet
|
||||
@@ -459,12 +616,53 @@ public class Actions
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void setSound(Context context, int soundSetting)
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public static void setDoNotDisturb(Context context, int desiredSetting)
|
||||
{
|
||||
Miscellaneous.logEvent("i", context.getResources().getString(R.string.soundSettings), "Changing sound to " + String.valueOf(soundSetting), 4);
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
// Check if the notification policy access has been granted for the app.
|
||||
/* if (!notificationManager.isNotificationPolicyAccessGranted())
|
||||
{
|
||||
Intent intent = new Intent(android.provider.Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
|
||||
startActivity(intent);
|
||||
return;
|
||||
}*/
|
||||
|
||||
notificationManager.setInterruptionFilter(desiredSetting);
|
||||
|
||||
/*if (notificationManager.getCurrentInterruptionFilter() == NotificationManager.INTERRUPTION_FILTER_ALL)
|
||||
{
|
||||
notificationManager.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_NONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
notificationManager.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
|
||||
}*/
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public static boolean isDoNotDisturbActive(Context context)
|
||||
{
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
int result = notificationManager.getCurrentInterruptionFilter();
|
||||
return (notificationManager.getCurrentInterruptionFilter() != NotificationManager.INTERRUPTION_FILTER_ALL);
|
||||
}
|
||||
|
||||
public static void setSound(Context context, int desiredSoundSetting)
|
||||
{
|
||||
Miscellaneous.logEvent("i", context.getResources().getString(R.string.soundSettings), "Changing sound to " + String.valueOf(desiredSoundSetting), 4);
|
||||
|
||||
AudioManager myAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
myAudioManager.setRingerMode(soundSetting);
|
||||
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && desiredSoundSetting == AudioManager.RINGER_MODE_SILENT)
|
||||
{
|
||||
AudioManager am = (AudioManager) Miscellaneous.getAnyContext().getSystemService(Context.AUDIO_SERVICE);
|
||||
am.setStreamVolume(AudioManager.STREAM_NOTIFICATION, 0, AudioManager.FLAG_PLAY_SOUND);
|
||||
am.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER, AudioManager.VIBRATE_SETTING_OFF);
|
||||
}
|
||||
else
|
||||
myAudioManager.setRingerMode(desiredSoundSetting);
|
||||
}
|
||||
|
||||
private static String getIPAddressUsb(final boolean useIPv4)
|
||||
@@ -577,7 +775,19 @@ public class Actions
|
||||
}
|
||||
}
|
||||
|
||||
public void useDownloadedWebpage(String result)
|
||||
public static void setDND(Context context, int desiredDndMode)
|
||||
{
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
||||
{
|
||||
Miscellaneous.logEvent("i", context.getResources().getString(R.string.soundSettings), "Changing DND to " + String.valueOf(desiredDndMode), 4);
|
||||
NotificationManager mNotificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
mNotificationManager.setInterruptionFilter(desiredDndMode);
|
||||
}
|
||||
else
|
||||
Miscellaneous.logEvent("w", context.getResources().getString(R.string.soundSettings), "Cannot change DND to " + String.valueOf(desiredDndMode) + ". This Android version is too and doesn\'t have that feature, yet.", 4);
|
||||
}
|
||||
|
||||
public void useDownloadedWebpage(String result)
|
||||
{
|
||||
// Toast.makeText(context, "Result: " + result, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
@@ -1143,7 +1353,7 @@ public class Actions
|
||||
desiredState = !isEnabled;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT <= 20)
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT_WATCH)
|
||||
{
|
||||
for (Method m : iConnectivityManagerClass.getDeclaredMethods())
|
||||
{
|
||||
@@ -1169,23 +1379,48 @@ public class Actions
|
||||
}
|
||||
}
|
||||
|
||||
protected static boolean setDataConnectionWithRoot(boolean enable)
|
||||
protected static boolean setDataConnectionWithRoot(boolean desiredState)
|
||||
{
|
||||
try
|
||||
{
|
||||
int desiredState = 0;
|
||||
if (enable)
|
||||
desiredState = 1;
|
||||
|
||||
if (MobileDataStuff.setMobileNetworkFromLollipop(desiredState, autoMationServerRef))
|
||||
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.O_MR1)
|
||||
{
|
||||
Miscellaneous.logEvent("i", "setDataConnectionWithRoot()", Miscellaneous.getAnyContext().getResources().getString(R.string.dataConWithRootSuccess), 2);
|
||||
return true;
|
||||
if(MobileDataStuff.setMobileNetworkFromAndroid9(desiredState, autoMationServerRef))
|
||||
{
|
||||
Miscellaneous.logEvent("i", "setDataConnectionWithRoot()", Miscellaneous.getAnyContext().getResources().getString(R.string.dataConWithRootSuccess), 2);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Miscellaneous.logEvent("e", "setDataConnectionWithRoot()", Miscellaneous.getAnyContext().getResources().getString(R.string.dataConWithRootFail), 2);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP)
|
||||
{
|
||||
if (MobileDataStuff.setMobileNetworkTillAndroid5(desiredState, autoMationServerRef))
|
||||
{
|
||||
Miscellaneous.logEvent("i", "setDataConnectionWithRoot()", Miscellaneous.getAnyContext().getResources().getString(R.string.dataConWithRootSuccess), 2);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Miscellaneous.logEvent("e", "setDataConnectionWithRoot()", Miscellaneous.getAnyContext().getResources().getString(R.string.dataConWithRootFail), 2);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Miscellaneous.logEvent("e", "setDataConnectionWithRoot()", Miscellaneous.getAnyContext().getResources().getString(R.string.dataConWithRootFail), 2);
|
||||
return false;
|
||||
if (MobileDataStuff.setMobileNetworkAndroid6Till8(desiredState, autoMationServerRef))
|
||||
{
|
||||
Miscellaneous.logEvent("i", "setDataConnectionWithRoot()", Miscellaneous.getAnyContext().getResources().getString(R.string.dataConWithRootSuccess), 2);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Miscellaneous.logEvent("e", "setDataConnectionWithRoot()", Miscellaneous.getAnyContext().getResources().getString(R.string.dataConWithRootFail), 2);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -1202,43 +1437,35 @@ public class Actions
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public static boolean setMobileNetworkFromLollipop(int desiredState, Context context) throws Exception
|
||||
public static boolean setMobileNetworkAndroid6Till8(boolean desiredState, Context context) throws Exception
|
||||
{
|
||||
String command = null;
|
||||
int state = 0;
|
||||
|
||||
try
|
||||
{
|
||||
int desiredStateString;
|
||||
if(desiredState)
|
||||
desiredStateString = 1;
|
||||
else
|
||||
desiredStateString = 0;
|
||||
|
||||
// Get the current state of the mobile network.
|
||||
state = isMobileDataEnabled() ? 0 : 1;
|
||||
// boolean state = isMobileDataEnabled() ? 0 : 1;
|
||||
// Get the value of the "TRANSACTION_setDataEnabled" field.
|
||||
String transactionCode = getTransactionCode(context);
|
||||
// Android 5.1+ (API 22) and later.
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP)
|
||||
SubscriptionManager mSubscriptionManager = (SubscriptionManager) context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
|
||||
// Loop through the subscription list i.e. SIM list.
|
||||
for (int i = 0; i < mSubscriptionManager.getActiveSubscriptionInfoCountMax(); i++)
|
||||
{
|
||||
SubscriptionManager mSubscriptionManager = (SubscriptionManager) context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
|
||||
// Loop through the subscription list i.e. SIM list.
|
||||
for (int i = 0; i < mSubscriptionManager.getActiveSubscriptionInfoCountMax(); i++)
|
||||
{
|
||||
if (transactionCode != null && transactionCode.length() > 0)
|
||||
{
|
||||
// Get the active subscription ID for a given SIM card.
|
||||
int subscriptionId = mSubscriptionManager.getActiveSubscriptionInfoList().get(i).getSubscriptionId();
|
||||
// Execute the command via `su` to turn off
|
||||
// mobile network for a subscription service.
|
||||
command = "service call phone " + transactionCode + " i32 " + subscriptionId + " i32 " + desiredState;
|
||||
Miscellaneous.logEvent("i", "setDataConnectionWithRoot()", "Running command: " + command.toString(), 5);
|
||||
return executeCommandViaSu(new String[]{command});
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP)
|
||||
{
|
||||
// Android 5.0 (API 21) only.
|
||||
if (transactionCode != null && transactionCode.length() > 0)
|
||||
{
|
||||
// Execute the command via `su` to turn off mobile network.
|
||||
command = "service call phone " + transactionCode + " i32 " + desiredState;
|
||||
// Get the active subscription ID for a given SIM card.
|
||||
int subscriptionId = mSubscriptionManager.getActiveSubscriptionInfoList().get(i).getSubscriptionId();
|
||||
// Execute the command via `su` to turn off
|
||||
// mobile network for a subscription service.
|
||||
command = "service call phone " + transactionCode + " i32 " + subscriptionId + " i32 " + desiredStateString;
|
||||
Miscellaneous.logEvent("i", "setMobileNetworkAndroid6Till8()", "Running command: " + command.toString(), 5);
|
||||
return executeCommandViaSu(new String[]{command});
|
||||
}
|
||||
}
|
||||
@@ -1252,6 +1479,75 @@ public class Actions
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public static boolean setMobileNetworkTillAndroid5(boolean desiredState, Context context) throws Exception
|
||||
{
|
||||
String command = null;
|
||||
|
||||
try
|
||||
{
|
||||
int desiredStateString;
|
||||
if(desiredState)
|
||||
desiredStateString = 1;
|
||||
else
|
||||
desiredStateString = 0;
|
||||
|
||||
// Get the current state of the mobile network.
|
||||
// int currentState = isMobileDataEnabled() ? 0 : 1;
|
||||
// Get the value of the "TRANSACTION_setDataEnabled" field.
|
||||
String transactionCode = getTransactionCode(context);
|
||||
// Android 5.0 (API 21) only.
|
||||
if (transactionCode != null && transactionCode.length() > 0)
|
||||
{
|
||||
// Execute the command via `su` to turn off mobile network.
|
||||
command = "service call phone " + transactionCode + " i32 " + desiredStateString;
|
||||
Miscellaneous.logEvent("i", "setMobileNetworkTillAndroid5()", "Running command: " + command.toString(), 5);
|
||||
return executeCommandViaSu(new String[]{command});
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Oops! Something went wrong, so we throw the exception here.
|
||||
throw e;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public static boolean setMobileNetworkFromAndroid9(boolean desiredState, Context context) throws Exception
|
||||
{
|
||||
String command = null;
|
||||
|
||||
String desiredStateString;
|
||||
if(desiredState)
|
||||
desiredStateString = "enable";
|
||||
else
|
||||
desiredStateString = "disable";
|
||||
|
||||
try
|
||||
{
|
||||
/*
|
||||
Android 8.1 is the last version on which the transaction code can be determined
|
||||
with the below method. From 9.0 on the field TRANSACTION_setDataEnabled does not
|
||||
exist anymore. Usually it was 83 and we'll just try this number hardcoded.
|
||||
Alternatively the bottom of this might be an approach:
|
||||
https://stackoverflow.com/questions/26539445/the-setmobiledataenabled-method-is-no-longer-callable-as-of-android-l-and-later
|
||||
*/
|
||||
|
||||
// Execute the command via `su` to turn off
|
||||
// mobile network for a subscription service.
|
||||
command = "svc data " + desiredStateString;
|
||||
Miscellaneous.logEvent("i", "setMobileNetworkFromAndroid9()", "Running command: " + command.toString(), 5);
|
||||
return executeCommandViaSu(new String[]{command});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Oops! Something went wrong, so we throw the exception here.
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public static boolean isMobileDataEnabled()
|
||||
{
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -29,6 +29,7 @@ import androidx.core.text.HtmlCompat;
|
||||
|
||||
import com.jens.automation2.AutomationService.serviceCommands;
|
||||
import com.jens.automation2.Trigger.Trigger_Enum;
|
||||
import com.jens.automation2.location.CellLocationChangedReceiver;
|
||||
import com.jens.automation2.location.LocationProvider;
|
||||
|
||||
import java.io.File;
|
||||
|
@@ -201,6 +201,9 @@ public class ActivityMaintenance extends Activity
|
||||
try
|
||||
{
|
||||
XmlFileInterface.readFile();
|
||||
ActivityMainPoi.getInstance().updateListView();
|
||||
ActivityMainRules.getInstance().updateListView();
|
||||
ActivityMainProfiles.getInstance().updateListView();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -314,18 +317,9 @@ public class ActivityMaintenance extends Activity
|
||||
|
||||
String subject = "Automation logs";
|
||||
|
||||
StringBuilder emailBody = new StringBuilder();
|
||||
emailBody.append("Device details" + Miscellaneous.lineSeparator);
|
||||
emailBody.append("OS version: " + System.getProperty("os.version") + Miscellaneous.lineSeparator);
|
||||
emailBody.append("API Level: " + android.os.Build.VERSION.SDK + Miscellaneous.lineSeparator);
|
||||
emailBody.append("Device: " + android.os.Build.DEVICE + Miscellaneous.lineSeparator);
|
||||
emailBody.append("Model: " + android.os.Build.MODEL + Miscellaneous.lineSeparator);
|
||||
emailBody.append("Product: " + android.os.Build.PRODUCT);
|
||||
emailBody.append("Flavor: " + BuildConfig.FLAVOR);
|
||||
|
||||
Uri uri = Uri.parse("content://com.jens.automation2/" + Settings.zipFileName);
|
||||
|
||||
Miscellaneous.sendEmail(ActivityMaintenance.this, "android-development@gmx.de", "Automation logs", emailBody.toString(), uri);
|
||||
Miscellaneous.sendEmail(ActivityMaintenance.this, "android-development@gmx.de", "Automation logs", getSystemInfo(), uri);
|
||||
}
|
||||
});
|
||||
alertDialogBuilder.setNegativeButton(context.getResources().getString(R.string.no), null);
|
||||
@@ -334,6 +328,19 @@ public class ActivityMaintenance extends Activity
|
||||
return alertDialog;
|
||||
}
|
||||
|
||||
public static String getSystemInfo()
|
||||
{
|
||||
StringBuilder systemInfoText = new StringBuilder();
|
||||
systemInfoText.append("Device details" + Miscellaneous.lineSeparator);
|
||||
systemInfoText.append("OS version: " + System.getProperty("os.version") + Miscellaneous.lineSeparator);
|
||||
systemInfoText.append("API Level: " + android.os.Build.VERSION.SDK + Miscellaneous.lineSeparator);
|
||||
systemInfoText.append("Device: " + android.os.Build.DEVICE + Miscellaneous.lineSeparator);
|
||||
systemInfoText.append("Model: " + android.os.Build.MODEL + Miscellaneous.lineSeparator);
|
||||
systemInfoText.append("Product: " + android.os.Build.PRODUCT + Miscellaneous.lineSeparator);
|
||||
systemInfoText.append("Flavor: " + BuildConfig.FLAVOR);
|
||||
return systemInfoText.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume()
|
||||
{
|
||||
|
@@ -22,6 +22,8 @@ import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.jens.automation2.receivers.ConnectivityReceiver;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
@@ -163,7 +165,7 @@ public class ActivityManagePoi extends Activity
|
||||
locationSearchStart = Calendar.getInstance();
|
||||
startTimeout();
|
||||
|
||||
if(!Settings.privacyLocationing)
|
||||
if(!Settings.privacyLocationing || !ConnectivityReceiver.isDataConnectionAvailable(AutomationService.getInstance()))
|
||||
{
|
||||
Miscellaneous.logEvent("i", "POI Manager", getResources().getString(R.string.logGettingPositionWithProvider) + " " + provider1, 3);
|
||||
myLocationManager.requestLocationUpdates(provider1, 500, Settings.satisfactoryAccuracyNetwork, myLocationListenerNetwork);
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package com.jens.automation2;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
@@ -37,8 +38,8 @@ public class ActivityManageProfile extends Activity
|
||||
final static int intentCodeRingtonePickerNotificationsFile = 9020;
|
||||
final static int intentCodeRingtonePickerNotificationsRingtone = 9021;
|
||||
|
||||
CheckBox checkBoxChangeSoundMode, checkBoxChangeVolumeMusicVideoGameMedia, checkBoxChangeVolumeNotifications, checkBoxChangeVolumeAlarms, checkBoxChangeIncomingCallsRingtone, checkBoxChangeNotificationRingtone, checkBoxChangeAudibleSelection, checkBoxChangeScreenLockUnlockSound, checkBoxChangeHapticFeedback, checkBoxChangeVibrateWhenRinging, checkBoxVibrateWhenRinging, checkBoxAudibleSelection, checkBoxScreenLockUnlockSound, checkBoxHapticFeedback;
|
||||
Spinner spinnerSoundMode;
|
||||
CheckBox checkBoxChangeSoundMode, checkBoxChangeVolumeMusicVideoGameMedia, checkBoxChangeVolumeNotifications, checkBoxChangeVolumeAlarms, checkBoxChangeIncomingCallsRingtone, checkBoxChangeNotificationRingtone, checkBoxChangeAudibleSelection, checkBoxChangeScreenLockUnlockSound, checkBoxChangeHapticFeedback, checkBoxChangeVibrateWhenRinging, checkBoxVibrateWhenRinging, checkBoxAudibleSelection, checkBoxScreenLockUnlockSound, checkBoxHapticFeedback, checkBoxChangeDnd;
|
||||
Spinner spinnerSoundMode, spinnerDndMode;
|
||||
SeekBar seekBarVolumeMusic, seekBarVolumeNotifications, seekBarVolumeAlarms;
|
||||
Button bChangeSoundIncomingCalls, bChangeSoundNotifications, bSaveProfile;
|
||||
TextView tvIncomingCallsRingtone, tvNotificationsRingtone;
|
||||
@@ -47,6 +48,7 @@ public class ActivityManageProfile extends Activity
|
||||
File incomingCallsRingtone = null, notificationsRingtone = null;
|
||||
|
||||
ArrayAdapter<String> soundModeAdapter;
|
||||
ArrayAdapter<String> dndModeAdapter;
|
||||
|
||||
public void setIncomingCallsRingtone(File incomingCallsRingtone)
|
||||
{
|
||||
@@ -85,6 +87,7 @@ public class ActivityManageProfile extends Activity
|
||||
this.setContentView(R.layout.activity_manage_specific_profile);
|
||||
|
||||
checkBoxChangeSoundMode = (CheckBox)findViewById(R.id.checkBoxChangeSoundMode);
|
||||
checkBoxChangeDnd = (CheckBox)findViewById(R.id.checkBoxChangeDnd);
|
||||
checkBoxChangeVolumeMusicVideoGameMedia = (CheckBox)findViewById(R.id.checkBoxChangeVolumeMusicVideoGameMedia);
|
||||
checkBoxChangeVolumeNotifications = (CheckBox)findViewById(R.id.checkBoxChangeVolumeNotifications);
|
||||
checkBoxChangeVolumeAlarms = (CheckBox)findViewById(R.id.checkBoxChangeVolumeAlarms);
|
||||
@@ -99,6 +102,7 @@ public class ActivityManageProfile extends Activity
|
||||
checkBoxHapticFeedback = (CheckBox)findViewById(R.id.checkBoxHapticFeedback);
|
||||
checkBoxVibrateWhenRinging = (CheckBox)findViewById(R.id.checkBoxVibrateWhenRinging);
|
||||
spinnerSoundMode = (Spinner)findViewById(R.id.spinnerSoundMode);
|
||||
spinnerDndMode = (Spinner)findViewById(R.id.spinnerDndMode);
|
||||
seekBarVolumeMusic = (SeekBar)findViewById(R.id.seekBarVolumeMusic);
|
||||
seekBarVolumeNotifications = (SeekBar)findViewById(R.id.seekBarVolumeNotifications);
|
||||
seekBarVolumeAlarms = (SeekBar)findViewById(R.id.seekBarVolumeAlarms);
|
||||
@@ -114,6 +118,7 @@ public class ActivityManageProfile extends Activity
|
||||
checkBoxScreenLockUnlockSound.setEnabled(false);
|
||||
checkBoxHapticFeedback.setEnabled(false);
|
||||
spinnerSoundMode.setEnabled(false);
|
||||
spinnerDndMode.setEnabled(false);
|
||||
seekBarVolumeMusic.setEnabled(false);
|
||||
seekBarVolumeNotifications.setEnabled(false);
|
||||
seekBarVolumeAlarms.setEnabled(false);
|
||||
@@ -121,6 +126,14 @@ public class ActivityManageProfile extends Activity
|
||||
bChangeSoundNotifications.setEnabled(false);
|
||||
|
||||
spinnerSoundMode.setSelection(0);
|
||||
spinnerDndMode.setSelection(0);
|
||||
|
||||
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
|
||||
{
|
||||
// Disable DND controls
|
||||
checkBoxChangeDnd.setEnabled(false);
|
||||
spinnerDndMode.setEnabled(false);
|
||||
}
|
||||
|
||||
// Scale SeekBars to the system's maximum volume values
|
||||
AudioManager am = (AudioManager) Miscellaneous.getAnyContext().getSystemService(Context.AUDIO_SERVICE);
|
||||
@@ -128,8 +141,30 @@ public class ActivityManageProfile extends Activity
|
||||
seekBarVolumeNotifications.setMax(am.getStreamMaxVolume(AudioManager.STREAM_NOTIFICATION));
|
||||
seekBarVolumeAlarms.setMax(am.getStreamMaxVolume(AudioManager.STREAM_ALARM));
|
||||
|
||||
soundModeAdapter = new ArrayAdapter<String>(this, R.layout.text_view_for_poi_listview_mediumtextsize, new String[] { getResources().getString(R.string.soundModeSilent), getResources().getString(R.string.soundModeVibrate), getResources().getString(R.string.soundModeNormal) });
|
||||
soundModeAdapter = new ArrayAdapter<String>(this, R.layout.text_view_for_poi_listview_mediumtextsize, new String[]
|
||||
{
|
||||
getResources().getString(R.string.soundModeSilent),
|
||||
getResources().getString(R.string.soundModeVibrate),
|
||||
getResources().getString(R.string.soundModeNormal)
|
||||
});
|
||||
spinnerSoundMode.setAdapter(soundModeAdapter);
|
||||
|
||||
dndModeAdapter = new ArrayAdapter<String>(this, R.layout.text_view_for_poi_listview_mediumtextsize, new String[]
|
||||
{
|
||||
getResources().getString(R.string.dndOff),
|
||||
getResources().getString(R.string.dndPriority),
|
||||
getResources().getString(R.string.dndNothing),
|
||||
getResources().getString(R.string.dndAlarms)
|
||||
});
|
||||
spinnerDndMode.setAdapter(dndModeAdapter);
|
||||
/*
|
||||
Order in spinner: 1, 2, 4, 3
|
||||
NotificationManager.INTERRUPTION_FILTER_UNKNOWN -> Returned when the value is unavailable for any reason.
|
||||
NotificationManager.INTERRUPTION_FILTER_ALL -> 1 -> Normal interruption filter - no notifications are suppressed. -> essentially turn off DND
|
||||
NotificationManager.INTERRUPTION_FILTER_PRIORITY -> 2 -> Priority interruption filter - all notifications are suppressed except those that match the priority criteria.
|
||||
NotificationManager.INTERRUPTION_FILTER_ALARMS -> 4 -> Alarms only interruption filter - all notifications except those of category
|
||||
NotificationManager.INTERRUPTION_FILTER_NONE -> 3 -> No interruptions filter - all notifications are suppressed and all audio streams (except those used for phone calls) and vibrations are muted.
|
||||
*/
|
||||
|
||||
checkBoxChangeSoundMode.setOnCheckedChangeListener(new OnCheckedChangeListener()
|
||||
{
|
||||
@@ -139,6 +174,14 @@ public class ActivityManageProfile extends Activity
|
||||
spinnerSoundMode.setEnabled(isChecked);
|
||||
}
|
||||
});
|
||||
checkBoxChangeDnd.setOnCheckedChangeListener(new OnCheckedChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
|
||||
{
|
||||
spinnerDndMode.setEnabled(isChecked);
|
||||
}
|
||||
});
|
||||
checkBoxChangeVolumeMusicVideoGameMedia.setOnCheckedChangeListener(new OnCheckedChangeListener()
|
||||
{
|
||||
@Override
|
||||
@@ -327,6 +370,7 @@ public class ActivityManageProfile extends Activity
|
||||
{
|
||||
etName.setText(ActivityMainProfiles.profileToEdit.getName());
|
||||
checkBoxChangeSoundMode.setChecked(ActivityMainProfiles.profileToEdit.getChangeSoundMode());
|
||||
checkBoxChangeDnd.setChecked(ActivityMainProfiles.profileToEdit.getChangeDndMode());
|
||||
checkBoxChangeVolumeMusicVideoGameMedia.setChecked(ActivityMainProfiles.profileToEdit.getChangeVolumeMusicVideoGameMedia());
|
||||
checkBoxChangeVolumeNotifications.setChecked(ActivityMainProfiles.profileToEdit.getChangeVolumeNotifications());
|
||||
checkBoxChangeVolumeAlarms.setChecked(ActivityMainProfiles.profileToEdit.getChangeVolumeAlarms());
|
||||
@@ -338,6 +382,7 @@ public class ActivityManageProfile extends Activity
|
||||
checkBoxChangeVibrateWhenRinging.setChecked(ActivityMainProfiles.profileToEdit.getChangeVibrateWhenRinging());
|
||||
|
||||
spinnerSoundMode.setSelection(ActivityMainProfiles.profileToEdit.getSoundMode());
|
||||
spinnerDndMode.setSelection(ActivityMainProfiles.profileToEdit.getDndMode()-1);
|
||||
seekBarVolumeMusic.setProgress(ActivityMainProfiles.profileToEdit.getVolumeMusic());
|
||||
seekBarVolumeNotifications.setProgress(ActivityMainProfiles.profileToEdit.getVolumeNotifications());
|
||||
seekBarVolumeAlarms.setProgress(ActivityMainProfiles.profileToEdit.getVolumeAlarms());
|
||||
@@ -359,6 +404,7 @@ public class ActivityManageProfile extends Activity
|
||||
|
||||
ActivityMainProfiles.profileToEdit.setName(etName.getText().toString());
|
||||
ActivityMainProfiles.profileToEdit.setChangeSoundMode(checkBoxChangeSoundMode.isChecked());
|
||||
ActivityMainProfiles.profileToEdit.setChangeDndMode(checkBoxChangeDnd.isChecked());
|
||||
ActivityMainProfiles.profileToEdit.setChangeVolumeMusicVideoGameMedia(checkBoxChangeVolumeMusicVideoGameMedia.isChecked());
|
||||
ActivityMainProfiles.profileToEdit.setChangeVolumeNotifications(checkBoxChangeVolumeNotifications.isChecked());
|
||||
ActivityMainProfiles.profileToEdit.setChangeVolumeAlarms(checkBoxChangeVolumeAlarms.isChecked());
|
||||
@@ -374,6 +420,7 @@ public class ActivityManageProfile extends Activity
|
||||
ActivityMainProfiles.profileToEdit.setHapticFeedback(checkBoxHapticFeedback.isChecked());
|
||||
ActivityMainProfiles.profileToEdit.setVibrateWhenRinging(checkBoxVibrateWhenRinging.isChecked());
|
||||
ActivityMainProfiles.profileToEdit.setSoundMode(spinnerSoundMode.getSelectedItemPosition());
|
||||
ActivityMainProfiles.profileToEdit.setDndMode(spinnerDndMode.getSelectedItemPosition()+1);
|
||||
ActivityMainProfiles.profileToEdit.setVolumeMusic(seekBarVolumeMusic.getProgress());
|
||||
ActivityMainProfiles.profileToEdit.setVolumeNotifications(seekBarVolumeNotifications.getProgress());
|
||||
ActivityMainProfiles.profileToEdit.setVolumeAlarms(seekBarVolumeAlarms.getProgress());
|
||||
@@ -401,21 +448,23 @@ public class ActivityManageProfile extends Activity
|
||||
}
|
||||
|
||||
if(!checkBoxChangeSoundMode.isChecked()
|
||||
&
|
||||
&&
|
||||
!checkBoxChangeDnd.isChecked()
|
||||
&&
|
||||
!checkBoxChangeVolumeMusicVideoGameMedia.isChecked()
|
||||
&
|
||||
&&
|
||||
!checkBoxChangeVolumeNotifications.isChecked()
|
||||
&
|
||||
&&
|
||||
!checkBoxChangeVolumeAlarms.isChecked()
|
||||
&
|
||||
&&
|
||||
!checkBoxChangeIncomingCallsRingtone.isChecked()
|
||||
&
|
||||
&&
|
||||
!checkBoxChangeNotificationRingtone.isChecked()
|
||||
&
|
||||
&&
|
||||
!checkBoxChangeAudibleSelection.isChecked()
|
||||
&
|
||||
&&
|
||||
!checkBoxChangeScreenLockUnlockSound.isChecked()
|
||||
&
|
||||
&&
|
||||
!checkBoxChangeHapticFeedback.isChecked()
|
||||
)
|
||||
{
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package com.jens.automation2;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
@@ -88,13 +89,12 @@ public class ActivityManageRule extends Activity
|
||||
final static int requestCodeActionStartActivityEdit = 3001;
|
||||
final static int requestCodeTriggerNfcTagAdd = 4000;
|
||||
final static int requestCodeTriggerNfcTagEdit = 4001;
|
||||
final static int requestCodeActionSpeakTextAdd = 5000;
|
||||
final static int requestCodeActionSpeakTextEdit = 1001;
|
||||
final static int requestCodeActionSpeakTextAdd = 5101;
|
||||
final static int requestCodeActionSpeakTextEdit = 5102;
|
||||
final static int requestCodeTriggerBluetoothAdd = 6000;
|
||||
final static int requestCodeTriggerBluetoothEdit = 6001;
|
||||
final static int requestCodeActionScreenBrightnessAdd = 401;
|
||||
final static int requestCodeActionScreenBrightnessEdit = 402;
|
||||
final static int requestCodeActionSendTextMessage = 7001;
|
||||
final static int requestCodeTriggerNotificationAdd = 8000;
|
||||
final static int requestCodeTriggerNfcNotificationEdit = 8001;
|
||||
final static int requestCodeActionPlaySoundAdd = 501;
|
||||
@@ -104,6 +104,7 @@ public class ActivityManageRule extends Activity
|
||||
final static int requestCodeTriggerWifiAdd = 723;
|
||||
final static int requestCodeTriggerWifiEdit = 724;
|
||||
final static int requestCodeActionSendTextMessageAdd = 5001;
|
||||
final static int requestCodeActionSendTextMessageEdit = 5002;
|
||||
final static int requestCodeActionVibrateAdd = 801;
|
||||
final static int requestCodeActionVibrateEdit = 802;
|
||||
|
||||
@@ -120,7 +121,7 @@ public class ActivityManageRule extends Activity
|
||||
{
|
||||
context = this;
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.manage_specific_rule);
|
||||
setContentView(R.layout.activity_manage_specific_rule);
|
||||
|
||||
instance = this;
|
||||
|
||||
@@ -315,7 +316,7 @@ public class ActivityManageRule extends Activity
|
||||
Intent activitySendTextMessageIntent = new Intent(ActivityManageRule.this, ActivityManageActionSendTextMessage.class);
|
||||
ActivityManageActionSendTextMessage.resultingAction = a;
|
||||
activitySendTextMessageIntent.putExtra("edit", true);
|
||||
startActivityForResult(activitySendTextMessageIntent, requestCodeActionSendTextMessage);
|
||||
startActivityForResult(activitySendTextMessageIntent, requestCodeActionSendTextMessageEdit);
|
||||
break;
|
||||
case setScreenBrightness:
|
||||
Intent activityEditScreenBrightnessIntent = new Intent(ActivityManageRule.this, ActivityManageActionBrightnessSetting.class);
|
||||
@@ -402,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()
|
||||
@@ -613,16 +619,16 @@ public class ActivityManageRule extends Activity
|
||||
if(triggerType == Trigger_Enum.nfcTag)
|
||||
{
|
||||
if (NfcReceiver.checkNfcRequirements(ActivityManageRule.this, true))
|
||||
getTriggerParamterDialog(context, booleanChoices).show();
|
||||
getTriggerParameterDialog(context, booleanChoices).show();
|
||||
}
|
||||
else
|
||||
getTriggerParamterDialog(context, booleanChoices).show();
|
||||
getTriggerParameterDialog(context, booleanChoices).show();
|
||||
}
|
||||
});
|
||||
|
||||
return builder.create();
|
||||
}
|
||||
private AlertDialog getTriggerParamterDialog(final Context myContext, final String[] choices)
|
||||
private AlertDialog getTriggerParameterDialog(final Context myContext, final String[] choices)
|
||||
{
|
||||
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context);
|
||||
alertDialogBuilder.setTitle(getResources().getString(R.string.selectTypeOfTrigger));
|
||||
@@ -1112,9 +1118,7 @@ public class ActivityManageRule extends Activity
|
||||
{
|
||||
//edit TimeFrame
|
||||
if(resultCode == RESULT_OK && ActivityManageTriggerTimeFrame.editedTimeFrameTrigger != null)
|
||||
{
|
||||
this.refreshTriggerList();
|
||||
}
|
||||
else
|
||||
Miscellaneous.logEvent("w", "TimeFrameEdit", "No timeframe returned. Assuming abort.", 5);
|
||||
}
|
||||
@@ -1132,8 +1136,11 @@ public class ActivityManageRule extends Activity
|
||||
{
|
||||
if(resultCode == RESULT_OK)
|
||||
{
|
||||
newTrigger.setTriggerParameter(data.getBooleanExtra("wifiState", false));
|
||||
newTrigger.setTriggerParameter2(data.getStringExtra("wifiName"));
|
||||
Trigger editedTrigger = new Trigger();
|
||||
editedTrigger.setTriggerType(Trigger_Enum.wifiConnection);
|
||||
editedTrigger.setTriggerParameter(data.getBooleanExtra("wifiState", false));
|
||||
editedTrigger.setTriggerParameter2(data.getStringExtra("wifiName"));
|
||||
ruleToEdit.getTriggerSet().set(editIndex, editedTrigger);
|
||||
this.refreshTriggerList();
|
||||
}
|
||||
}
|
||||
@@ -1176,6 +1183,7 @@ public class ActivityManageRule extends Activity
|
||||
{
|
||||
ruleToEdit.getTriggerSet().add(newTrigger);
|
||||
|
||||
newTrigger.setTriggerParameter(data.getBooleanExtra("direction", false));
|
||||
newTrigger.setTriggerParameter2(
|
||||
data.getStringExtra("app") + Trigger.triggerParameter2Split +
|
||||
data.getStringExtra("titleDir") + Trigger.triggerParameter2Split +
|
||||
@@ -1224,8 +1232,8 @@ public class ActivityManageRule extends Activity
|
||||
{
|
||||
if(resultCode == RESULT_OK)
|
||||
{
|
||||
//add SpeakText
|
||||
ruleToEdit.getActionSet().add(ActivityManageActionSendTextMessage.resultingAction);
|
||||
//edit SpeakText
|
||||
newAction = ActivityManageActionSpeakText.resultingAction;
|
||||
this.refreshActionList();
|
||||
}
|
||||
}
|
||||
@@ -1315,17 +1323,25 @@ public class ActivityManageRule extends Activity
|
||||
this.refreshActionList();
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Check with has data been changed or something like that.
|
||||
/*try
|
||||
else if(requestCode == requestCodeActionSendTextMessageAdd)
|
||||
{
|
||||
Miscellaneous.logEvent("i", "ActivityManageSpecificRule", getResources().getString(R.string.noDataChangedReadingAnyway), 4);
|
||||
XmlFileInterface.readFile();
|
||||
if(resultCode == RESULT_OK)
|
||||
{
|
||||
//add SendTextMessage
|
||||
ruleToEdit.getActionSet().add(ActivityManageActionSendTextMessage.resultingAction);
|
||||
this.refreshActionList();
|
||||
}
|
||||
}
|
||||
catch (FileNotFoundException e)
|
||||
else if(requestCode == requestCodeActionSendTextMessageEdit)
|
||||
{
|
||||
Miscellaneous.logEvent("e", "ActivityManageSpecificRule", getResources().getString(R.string.errorReadingPoisAndRulesFromFile) + ": " + Log.getStackTraceString(e), 5);
|
||||
}*/
|
||||
if(resultCode == RESULT_OK)
|
||||
{
|
||||
//edit SendTextMessage
|
||||
newAction = ActivityManageActionSendTextMessage.resultingAction;
|
||||
//ruleToEdit.getActionSet().add(ActivityManageActionSendTextMessage.resultingAction);
|
||||
this.refreshActionList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Dialog getActionTypeDialog()
|
||||
@@ -1374,7 +1390,7 @@ public class ActivityManageRule extends Activity
|
||||
else if(types[i].toString().equals(Action_Enum.sendTextMessage.toString()))
|
||||
{
|
||||
// if(ActivityPermissions.isPermissionDeclaratedInManifest(ActivityManageSpecificRule.this, "android.permission.SEND_SMS") && !Miscellaneous.isGooglePlayInstalled(ActivityManageSpecificRule.this))
|
||||
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.message));
|
||||
}
|
||||
else
|
||||
@@ -1415,14 +1431,15 @@ public class ActivityManageRule extends Activity
|
||||
newAction.setAction(Action_Enum.triggerUrl);
|
||||
ActivityManageActionTriggerUrl.resultingAction = null;
|
||||
Intent editTriggerIntent = new Intent(context, ActivityManageActionTriggerUrl.class);
|
||||
startActivityForResult(editTriggerIntent, 1000);
|
||||
startActivityForResult(editTriggerIntent, requestCodeActionTriggerUrlAdd);
|
||||
}
|
||||
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setWifi.toString()))
|
||||
{
|
||||
newAction.setAction(Action_Enum.setWifi);
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||
Toast.makeText(context, context.getResources().getString(R.string.android10WifiToggleNotice), Toast.LENGTH_LONG).show();
|
||||
getActionParameter1Dialog(ActivityManageRule.this).show();
|
||||
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||
Miscellaneous.messageBox(context.getResources().getString(R.string.app_name), context.getResources().getString(R.string.android10WifiToggleNotice), context).show();
|
||||
}
|
||||
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setBluetooth.toString()))
|
||||
{
|
||||
@@ -1434,9 +1451,10 @@ public class ActivityManageRule extends Activity
|
||||
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setUsbTethering.toString()))
|
||||
{
|
||||
newAction.setAction(Action_Enum.setUsbTethering);
|
||||
getActionParameter1Dialog(ActivityManageRule.this).show();
|
||||
|
||||
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1)
|
||||
Miscellaneous.messageBox(context.getResources().getString(R.string.warning), context.getResources().getString(R.string.usbTetheringFailForAboveGingerbread), context).show();
|
||||
getActionParameter1Dialog(ActivityManageRule.this).show();
|
||||
}
|
||||
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setWifiTethering.toString()))
|
||||
{
|
||||
@@ -1462,7 +1480,7 @@ public class ActivityManageRule extends Activity
|
||||
{
|
||||
newAction.setAction(Action_Enum.startOtherActivity);
|
||||
Intent intent = new Intent(ActivityManageRule.this, ActivityManageActionStartActivity.class);
|
||||
startActivityForResult(intent, 3000);
|
||||
startActivityForResult(intent, requestCodeActionStartActivityAdd);
|
||||
}
|
||||
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.waitBeforeNextAction.toString()))
|
||||
{
|
||||
@@ -1496,7 +1514,7 @@ public class ActivityManageRule extends Activity
|
||||
newAction.setAction(Action_Enum.speakText);
|
||||
ActivityManageActionSpeakText.resultingAction = null;
|
||||
Intent editTriggerIntent = new Intent(context, ActivityManageActionSpeakText.class);
|
||||
startActivityForResult(editTriggerIntent, 5000);
|
||||
startActivityForResult(editTriggerIntent, requestCodeActionSpeakTextAdd);
|
||||
}
|
||||
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.sendTextMessage.toString()))
|
||||
{
|
||||
|
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -145,7 +145,7 @@ public class ActivityManageTriggerWifi extends Activity
|
||||
wifiList.add(wifi.SSID.replaceAll("\"+$", "").replaceAll("^\"+", ""));
|
||||
}
|
||||
|
||||
if (wifiList.size() > 0)
|
||||
if (wifiList.size() > 0)
|
||||
{
|
||||
spinnerWifiList.setEnabled(true);
|
||||
Collections.sort(wifiList);
|
||||
|
@@ -3,6 +3,7 @@ package com.jens.automation2;
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
@@ -19,6 +20,9 @@ import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
|
||||
import com.jens.automation2.receivers.NotificationListener;
|
||||
|
||||
@@ -411,11 +415,11 @@ public class ActivityPermissions extends Activity
|
||||
addToArrayListUnique(Manifest.permission.ACCESS_NETWORK_STATE, requiredPermissions);
|
||||
break;
|
||||
case charging:
|
||||
addToArrayListUnique(Manifest.permission.READ_PHONE_STATE, requiredPermissions);
|
||||
// addToArrayListUnique(Manifest.permission.READ_PHONE_STATE, requiredPermissions);
|
||||
// addToArrayListUnique("android.permission.BATTERY_STATS", requiredPermissions);
|
||||
break;
|
||||
case headsetPlugged:
|
||||
addToArrayListUnique(Manifest.permission.READ_PHONE_STATE, requiredPermissions);
|
||||
// addToArrayListUnique(Manifest.permission.READ_PHONE_STATE, requiredPermissions);
|
||||
break;
|
||||
case nfcTag:
|
||||
addToArrayListUnique(Manifest.permission.NFC, requiredPermissions);
|
||||
@@ -494,12 +498,13 @@ public class ActivityPermissions extends Activity
|
||||
break;
|
||||
case sendTextMessage:
|
||||
addToArrayListUnique(Manifest.permission.SEND_SMS, requiredPermissions);
|
||||
checkPermissionsInVariableUse(action.getParameter2(), requiredPermissions);
|
||||
checkPermissionsInVariableUse(action.getParameter2(), requiredPermissions);
|
||||
break;
|
||||
case setAirplaneMode:
|
||||
addToArrayListUnique(Manifest.permission.WRITE_SETTINGS, requiredPermissions);
|
||||
addToArrayListUnique(Manifest.permission.ACCESS_NETWORK_STATE, requiredPermissions);
|
||||
addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions);
|
||||
// https://stackoverflow.com/questions/32185628/connectivitymanager-requestnetwork-in-android-6-0
|
||||
// addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions);
|
||||
/* Permission was not required anymore, even before Android 6: https://su.chainfire.eu/#updates-permission
|
||||
addToArrayListUnique(permissionNameSuperuser, requiredPermissions);*/
|
||||
break;
|
||||
@@ -512,7 +517,8 @@ public class ActivityPermissions extends Activity
|
||||
case setDataConnection:
|
||||
addToArrayListUnique(Manifest.permission.WRITE_SETTINGS, requiredPermissions);
|
||||
addToArrayListUnique(Manifest.permission.ACCESS_NETWORK_STATE, requiredPermissions);
|
||||
addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions);
|
||||
// https://stackoverflow.com/questions/32185628/connectivitymanager-requestnetwork-in-android-6-0
|
||||
// addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions);
|
||||
addToArrayListUnique(Manifest.permission.READ_PHONE_STATE, requiredPermissions);
|
||||
/* Permission was not required anymore, even before Android 6: https://su.chainfire.eu/#updates-permission
|
||||
addToArrayListUnique(permissionNameSuperuser, requiredPermissions);*/
|
||||
@@ -522,16 +528,19 @@ public class ActivityPermissions extends Activity
|
||||
break;
|
||||
case setUsbTethering:
|
||||
addToArrayListUnique(Manifest.permission.WRITE_SETTINGS, requiredPermissions);
|
||||
addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions);
|
||||
// https://stackoverflow.com/questions/32185628/connectivitymanager-requestnetwork-in-android-6-0
|
||||
// addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions);
|
||||
break;
|
||||
case setWifi:
|
||||
addToArrayListUnique(Manifest.permission.WRITE_SETTINGS, requiredPermissions);
|
||||
addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions);
|
||||
// https://stackoverflow.com/questions/32185628/connectivitymanager-requestnetwork-in-android-6-0
|
||||
// addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions);
|
||||
addToArrayListUnique(Manifest.permission.ACCESS_NETWORK_STATE, requiredPermissions);
|
||||
break;
|
||||
case setWifiTethering:
|
||||
addToArrayListUnique(Manifest.permission.WRITE_SETTINGS, requiredPermissions);
|
||||
addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions);
|
||||
// https://stackoverflow.com/questions/32185628/connectivitymanager-requestnetwork-in-android-6-0
|
||||
// addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions);
|
||||
addToArrayListUnique(Manifest.permission.ACCESS_NETWORK_STATE, requiredPermissions);
|
||||
|
||||
/*
|
||||
@@ -578,30 +587,30 @@ public class ActivityPermissions extends Activity
|
||||
break;
|
||||
case turnUsbTetheringOff:
|
||||
addToArrayListUnique(Manifest.permission.WRITE_SETTINGS, requiredPermissions);
|
||||
addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions);
|
||||
// addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions);
|
||||
break;
|
||||
case turnUsbTetheringOn:
|
||||
addToArrayListUnique(Manifest.permission.WRITE_SETTINGS, requiredPermissions);
|
||||
addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions);
|
||||
// addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions);
|
||||
break;
|
||||
case turnWifiOff:
|
||||
addToArrayListUnique(Manifest.permission.WRITE_SETTINGS, requiredPermissions);
|
||||
addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions);
|
||||
// addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions);
|
||||
addToArrayListUnique(Manifest.permission.ACCESS_NETWORK_STATE, requiredPermissions);
|
||||
break;
|
||||
case turnWifiOn:
|
||||
addToArrayListUnique(Manifest.permission.WRITE_SETTINGS, requiredPermissions);
|
||||
addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions);
|
||||
// addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions);
|
||||
addToArrayListUnique(Manifest.permission.ACCESS_NETWORK_STATE, requiredPermissions);
|
||||
break;
|
||||
case turnWifiTetheringOff:
|
||||
addToArrayListUnique(Manifest.permission.WRITE_SETTINGS, requiredPermissions);
|
||||
addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions);
|
||||
// addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions);
|
||||
addToArrayListUnique(Manifest.permission.ACCESS_NETWORK_STATE, requiredPermissions);
|
||||
break;
|
||||
case turnWifiTetheringOn:
|
||||
addToArrayListUnique(Manifest.permission.WRITE_SETTINGS, requiredPermissions);
|
||||
addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions);
|
||||
// addToArrayListUnique(Manifest.permission.CHANGE_NETWORK_STATE, requiredPermissions);
|
||||
addToArrayListUnique(Manifest.permission.ACCESS_NETWORK_STATE, requiredPermissions);
|
||||
break;
|
||||
case waitBeforeNextAction:
|
||||
@@ -609,6 +618,9 @@ public class ActivityPermissions extends Activity
|
||||
case wakeupDevice:
|
||||
addToArrayListUnique(Manifest.permission.WAKE_LOCK, requiredPermissions);
|
||||
break;
|
||||
case playSound:
|
||||
addToArrayListUnique(Manifest.permission.READ_EXTERNAL_STORAGE, requiredPermissions);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -647,7 +659,7 @@ public class ActivityPermissions extends Activity
|
||||
{
|
||||
for (Action action : rule.getActionSet())
|
||||
{
|
||||
if(action.equals(actionType))
|
||||
if(action.getAction().equals(actionType))
|
||||
addToArrayListUnique(rule.getName(), returnList);
|
||||
}
|
||||
}
|
||||
@@ -674,17 +686,14 @@ public class ActivityPermissions extends Activity
|
||||
case Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE:
|
||||
for(String ruleName : getRulesUsing(Trigger.Trigger_Enum.notification))
|
||||
usingElements.add(String.format(getResources().getString(R.string.ruleXrequiresThis), ruleName));
|
||||
|
||||
break;
|
||||
case permissionNameGoogleActivityDetection:
|
||||
for(String ruleName : getRulesUsing(Trigger.Trigger_Enum.activityDetection))
|
||||
usingElements.add(String.format(getResources().getString(R.string.ruleXrequiresThis), ruleName));
|
||||
|
||||
break;
|
||||
case Manifest.permission.ACTIVITY_RECOGNITION:
|
||||
for(String ruleName : getRulesUsing(Trigger.Trigger_Enum.activityDetection))
|
||||
usingElements.add(String.format(getResources().getString(R.string.ruleXrequiresThis), ruleName));
|
||||
|
||||
break;
|
||||
case Manifest.permission.ACCESS_COARSE_LOCATION:
|
||||
// usingElements.add(getResources().getString(R.string.android_permission_ACCESS_COARSE_LOCATION));
|
||||
@@ -821,6 +830,10 @@ public class ActivityPermissions extends Activity
|
||||
case Manifest.permission.FOREGROUND_SERVICE:
|
||||
usingElements.add(getResources().getString(R.string.startAutomationAsService));
|
||||
break;
|
||||
case Manifest.permission.READ_EXTERNAL_STORAGE:
|
||||
for(String ruleName : getRulesUsing(Action.Action_Enum.playSound))
|
||||
usingElements.add(String.format(getResources().getString(R.string.ruleXrequiresThis), ruleName));
|
||||
break;
|
||||
}
|
||||
|
||||
return usingElements;
|
||||
@@ -965,8 +978,8 @@ public class ActivityPermissions extends Activity
|
||||
if(requiredPermissions.contains(Manifest.permission.SEND_SMS))
|
||||
{
|
||||
if(!ActivityPermissions.isPermissionDeclaratedInManifest(Miscellaneous.getAnyContext(), Manifest.permission.SEND_SMS)
|
||||
&&
|
||||
Miscellaneous.isGooglePlayInstalled(Miscellaneous.getAnyContext())
|
||||
// &&
|
||||
// Miscellaneous.isGooglePlayInstalled(Miscellaneous.getAnyContext())
|
||||
)
|
||||
{
|
||||
requiredPermissions.remove(Manifest.permission.SEND_SMS);
|
||||
@@ -984,7 +997,10 @@ public class ActivityPermissions extends Activity
|
||||
Miscellaneous.logEvent("i", "Permissions", "Requesting permissions: " + permissions, 2);
|
||||
|
||||
// Toast.makeText(ActivityPermissions.this, "Requesting permissions. Amount: " + String.valueOf(requiredPermissions.size()), Toast.LENGTH_LONG).show();
|
||||
requestPermissions(requiredPermissions.toArray(new String[requiredPermissions.size()]), requestCodeForPermissions);
|
||||
if(requiredPermissions.size() > 0)
|
||||
requestPermissions(requiredPermissions.toArray(new String[requiredPermissions.size()]), requestCodeForPermissions);
|
||||
// else
|
||||
// Miscellaneous.messageBox(getResources().getString(R.string.warning), getResources().getString(R.string.permissionsRequiredNotAvailable), ActivityPermissions.this).show();
|
||||
}
|
||||
else
|
||||
setHaveAllPermissions();
|
||||
@@ -1152,10 +1168,6 @@ public class ActivityPermissions extends Activity
|
||||
private void setHaveAllPermissions()
|
||||
{
|
||||
setResult(RESULT_OK);
|
||||
// All permissions have been granted.
|
||||
NotificationManager mNotificationManager = (NotificationManager) Miscellaneous.getAnyContext().getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
mNotificationManager.cancel(notificationIdPermissions);
|
||||
ActivityMainScreen.updateMainScreen();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -1166,6 +1178,14 @@ public class ActivityPermissions extends Activity
|
||||
// Activity may not have been loaded, yet.
|
||||
}
|
||||
|
||||
// All permissions have been granted.
|
||||
NotificationManager mNotificationManager = (NotificationManager) Miscellaneous.getAnyContext().getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
mNotificationManager.cancel(notificationIdPermissions);
|
||||
if(AutomationService.getInstance() != null)
|
||||
AutomationService.getInstance().cancelNotification();
|
||||
|
||||
ActivityMainScreen.updateMainScreen();
|
||||
|
||||
this.finish();
|
||||
}
|
||||
|
||||
@@ -1322,6 +1342,7 @@ public class ActivityPermissions extends Activity
|
||||
mapActionPermissions.put("turnWifiTetheringOn", Manifest.permission.WRITE_SETTINGS);
|
||||
mapActionPermissions.put("turnWifiTetheringOn", Manifest.permission.CHANGE_NETWORK_STATE);
|
||||
mapActionPermissions.put("turnWifiTetheringOn", Manifest.permission.ACCESS_NETWORK_STATE);
|
||||
mapActionPermissions.put("playSound", Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
// mapActionPermissions.put("waitBeforeNextAction", "");
|
||||
mapActionPermissions.put("wakeupDevice", Manifest.permission.WAKE_LOCK);
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
|
||||
import com.jens.automation2.Trigger.Trigger_Enum;
|
||||
import com.jens.automation2.location.LocationProvider;
|
||||
@@ -198,6 +199,7 @@ public class AutomationService extends Service implements OnInitListener
|
||||
if (checkStartupRequirements(this, startAtBoot))
|
||||
{
|
||||
Miscellaneous.logEvent("i", "Service", this.getResources().getString(R.string.logServiceStarting) + " VERSION_CODE: " + BuildConfig.VERSION_CODE + ", VERSION_NAME: " + BuildConfig.VERSION_NAME + ", flavor: " + BuildConfig.FLAVOR, 1);
|
||||
Miscellaneous.logEvent("i", "Service", ActivityMaintenance.getSystemInfo(), 1);
|
||||
|
||||
startUpRoutine();
|
||||
|
||||
@@ -333,7 +335,7 @@ public class AutomationService extends Service implements OnInitListener
|
||||
|
||||
protected void startLocationProvider()
|
||||
{
|
||||
if(ActivityPermissions.havePermission("android.permission.ACCESS_COARSE_LOCATION", AutomationService.this))
|
||||
if(ActivityPermissions.havePermission(Manifest.permission.ACCESS_COARSE_LOCATION, AutomationService.this))
|
||||
myLocationProvider = new LocationProvider(this); //autostart with this (only) constructor
|
||||
}
|
||||
|
||||
@@ -407,6 +409,14 @@ public class AutomationService extends Service implements OnInitListener
|
||||
}
|
||||
}
|
||||
|
||||
public void cancelNotification()
|
||||
{
|
||||
// stopForeground(false);
|
||||
NotificationManagerCompat.from(AutomationService.this).cancelAll();
|
||||
// NotificationManagerCompat.from(AutomationService.this).cancel(ActivityPermissions.notificationIdPermissions);
|
||||
// NotificationManagerCompat.from(AutomationService.this).cancel(AutomationService.notificationIdRestrictions);
|
||||
}
|
||||
|
||||
protected void checkForMissingBackgroundLocationPermission()
|
||||
{
|
||||
if(Miscellaneous.googleToBlameForLocation(true))
|
||||
@@ -421,23 +431,6 @@ public class AutomationService extends Service implements OnInitListener
|
||||
else
|
||||
Miscellaneous.createDismissableNotification(getResources().getString(R.string.featuresDisabled), notificationIdLocationRestriction, pi);
|
||||
}
|
||||
|
||||
/*
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||
{
|
||||
if (BuildConfig.FLAVOR.equalsIgnoreCase("googlePlayFlavor"))
|
||||
{
|
||||
if (Rule.isAnyRuleUsing(Trigger_Enum.pointOfInterest))
|
||||
{
|
||||
Intent intent = new Intent(AutomationService.this, ActivityMainTabLayout.class);
|
||||
PendingIntent pi = PendingIntent.getActivity(AutomationService.this, 0, intent, 0);
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1)
|
||||
Miscellaneous.createDismissableNotificationWithDelay(2200, getResources().getString(R.string.featuresDisabled), notificationIdLocationRestriction, pi);
|
||||
else
|
||||
Miscellaneous.createDismissableNotification(getResources().getString(R.string.featuresDisabled), notificationIdLocationRestriction, pi);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
public static void startAutomationService(Context context, boolean startAtBoot)
|
||||
@@ -631,7 +624,10 @@ public class AutomationService extends Service implements OnInitListener
|
||||
// myNotification.setLatestEventInfo(instance, "Automation", textToDisplay, myPendingIntent);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// {
|
||||
if(notificationBuilder == null)
|
||||
notificationBuilder = createDefaultNotificationBuilder();
|
||||
|
||||
notificationBuilder.setContentText(textToDisplay);
|
||||
notificationBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(textToDisplay));
|
||||
|
||||
|
@@ -15,6 +15,8 @@ import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
@@ -75,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;
|
||||
@@ -447,6 +451,14 @@ public class Miscellaneous extends Service
|
||||
|
||||
public static boolean compare(String direction, String needle, String haystack)
|
||||
{
|
||||
// If only one of needle or haystack is null
|
||||
if(
|
||||
(needle == null && haystack != null)
|
||||
||
|
||||
(needle != null && haystack == null)
|
||||
)
|
||||
return false;
|
||||
|
||||
switch(direction)
|
||||
{
|
||||
case Trigger.directionEquals:
|
||||
@@ -599,7 +611,10 @@ public class Miscellaneous extends Service
|
||||
if(notificationTitle != null && notificationTitle.length() > 0)
|
||||
source = source.replace("[notificationTitle]", notificationTitle);
|
||||
else
|
||||
{
|
||||
source = source.replace("notificationTitle unknown", notificationTitle);
|
||||
Miscellaneous.logEvent("w", "Variable replacement", "notificationTitle was empty.", 3);
|
||||
}
|
||||
}
|
||||
|
||||
if(source.contains("[notificationText]"))
|
||||
@@ -609,7 +624,10 @@ public class Miscellaneous extends Service
|
||||
if(notificationText != null && notificationText.length() > 0)
|
||||
source = source.replace("[notificationText]", notificationText);
|
||||
else
|
||||
{
|
||||
source = source.replace("notificationText unknown", notificationText);
|
||||
Miscellaneous.logEvent("w", "Variable replacement", "notificationText was empty.", 3);
|
||||
}
|
||||
}
|
||||
|
||||
// Miscellaneous.logEvent("i", "URL after replace", source);
|
||||
@@ -656,6 +674,24 @@ public class Miscellaneous extends Service
|
||||
|
||||
return alertDialog.create();
|
||||
}
|
||||
|
||||
private boolean haveNetworkConnection()
|
||||
{
|
||||
boolean haveConnectedWifi = false;
|
||||
boolean haveConnectedMobile = false;
|
||||
|
||||
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
NetworkInfo[] netInfo = cm.getAllNetworkInfo();
|
||||
for (NetworkInfo ni : netInfo) {
|
||||
if (ni.getTypeName().equalsIgnoreCase("WIFI"))
|
||||
if (ni.isConnected())
|
||||
haveConnectedWifi = true;
|
||||
if (ni.getTypeName().equalsIgnoreCase("MOBILE"))
|
||||
if (ni.isConnected())
|
||||
haveConnectedMobile = true;
|
||||
}
|
||||
return haveConnectedWifi || haveConnectedMobile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the device is rooted.
|
||||
@@ -664,9 +700,13 @@ public class Miscellaneous extends Service
|
||||
*/
|
||||
public static boolean isPhoneRooted()
|
||||
{
|
||||
// if(true)
|
||||
// return true;
|
||||
|
||||
// get from build info
|
||||
String buildTags = Build.TAGS;
|
||||
if (buildTags != null && buildTags.contains("test-keys")) {
|
||||
if (buildTags != null && buildTags.contains("test-keys"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -873,6 +913,7 @@ public class Miscellaneous extends Service
|
||||
dismissableNotificationBuilder.setContentText(textToDisplay);
|
||||
dismissableNotificationBuilder.setContentIntent(pendingIntent);
|
||||
dismissableNotificationBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(textToDisplay));
|
||||
dismissableNotificationBuilder.setAutoCancel(true);
|
||||
|
||||
Notification dismissableNotification = dismissableNotificationBuilder.build();
|
||||
|
||||
@@ -974,6 +1015,7 @@ public class Miscellaneous extends Service
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
builder.setCategory(Notification.CATEGORY_SERVICE);
|
||||
|
||||
builder.setAutoCancel(true);
|
||||
builder.setWhen(System.currentTimeMillis());
|
||||
builder.setContentIntent(myPendingIntent);
|
||||
|
||||
@@ -1492,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;
|
||||
}
|
||||
}
|
@@ -5,6 +5,7 @@ import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
@@ -24,6 +25,9 @@ public class Profile implements Comparable<Profile>
|
||||
protected boolean changeSoundMode;
|
||||
protected int soundMode;
|
||||
|
||||
protected boolean changeDndMode;
|
||||
protected int dndMode;
|
||||
|
||||
boolean changeVolumeMusicVideoGameMedia;
|
||||
protected int volumeMusic;
|
||||
|
||||
@@ -81,6 +85,26 @@ public class Profile implements Comparable<Profile>
|
||||
return soundMode;
|
||||
}
|
||||
|
||||
public boolean getChangeDndMode()
|
||||
{
|
||||
return changeDndMode;
|
||||
}
|
||||
|
||||
public void setChangeDndMode(boolean changeDndMode)
|
||||
{
|
||||
this.changeDndMode = changeDndMode;
|
||||
}
|
||||
|
||||
public int getDndMode()
|
||||
{
|
||||
return dndMode;
|
||||
}
|
||||
|
||||
public void setDndMode(int dndMode)
|
||||
{
|
||||
this.dndMode = dndMode;
|
||||
}
|
||||
|
||||
public void setChangeVolumeMusicVideoGameMedia(boolean changeVolumeMusicVideoGameMedia)
|
||||
{
|
||||
this.changeVolumeMusicVideoGameMedia = changeVolumeMusicVideoGameMedia;
|
||||
@@ -449,6 +473,9 @@ public class Profile implements Comparable<Profile>
|
||||
|
||||
if(changeSoundMode)
|
||||
Actions.setSound(context, soundMode);
|
||||
|
||||
if(changeDndMode)
|
||||
Actions.setDND(context, dndMode);
|
||||
|
||||
if(changeVolumeMusicVideoGameMedia)
|
||||
am.setStreamVolume(AudioManager.STREAM_MUSIC, volumeMusic, AudioManager.FLAG_PLAY_SOUND);
|
||||
@@ -464,10 +491,19 @@ public class Profile implements Comparable<Profile>
|
||||
applyRingTone(incomingCallsRingtone, RingtoneManager.TYPE_RINGTONE, context);
|
||||
|
||||
if(changeVibrateWhenRinging)
|
||||
if(vibrateWhenRinging)
|
||||
am.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER, AudioManager.VIBRATE_SETTING_ON);
|
||||
else
|
||||
am.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER, AudioManager.VIBRATE_SETTING_OFF);
|
||||
{
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||
{
|
||||
android.provider.Settings.System.putInt(context.getContentResolver(), "vibrate_when_ringing", vibrateWhenRinging?1:0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (vibrateWhenRinging)
|
||||
am.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER, AudioManager.VIBRATE_SETTING_ON);
|
||||
else
|
||||
am.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER, AudioManager.VIBRATE_SETTING_OFF);
|
||||
}
|
||||
}
|
||||
|
||||
if(changeNotificationRingtone)
|
||||
if(notificationRingtone != null)
|
||||
|
@@ -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,
|
||||
@@ -144,18 +144,18 @@ public class ReceiverCoordinator
|
||||
ConnectivityReceiver.startConnectivityReceiver(AutomationService.getInstance());
|
||||
|
||||
// startCellLocationChangedReceiver
|
||||
if(!ConnectivityReceiver.isAirplaneMode(AutomationService.getInstance()) && WifiBroadcastReceiver.mayCellLocationReceiverBeActivated() && (Rule.isAnyRuleUsing(Trigger.Trigger_Enum.pointOfInterest) | Rule.isAnyRuleUsing(Trigger.Trigger_Enum.speed)))
|
||||
if(!ConnectivityReceiver.isAirplaneMode(AutomationService.getInstance()) && WifiBroadcastReceiver.mayCellLocationReceiverBeActivated() && (Rule.isAnyRuleUsing(Trigger.Trigger_Enum.pointOfInterest) || Rule.isAnyRuleUsing(Trigger.Trigger_Enum.speed)))
|
||||
{
|
||||
if(!Miscellaneous.googleToBlameForLocation(true))
|
||||
CellLocationChangedReceiver.startCellLocationChangedReceiver();
|
||||
}
|
||||
|
||||
// startBatteryReceiver
|
||||
if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.charging) | Rule.isAnyRuleUsing(Trigger.Trigger_Enum.usb_host_connection) | Rule.isAnyRuleUsing(Trigger.Trigger_Enum.batteryLevel))
|
||||
if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.charging) || Rule.isAnyRuleUsing(Trigger.Trigger_Enum.usb_host_connection) || Rule.isAnyRuleUsing(Trigger.Trigger_Enum.batteryLevel))
|
||||
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());
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -170,9 +170,9 @@ public class XmlFileInterface
|
||||
serializer.startTag(null, "changeVibrateWhenRinging");
|
||||
serializer.text(String.valueOf(Profile.getProfileCollection().get(i).getChangeVibrateWhenRinging()));
|
||||
serializer.endTag(null, "changeVibrateWhenRinging");//
|
||||
serializer.startTag(null, "changeVibrateWhenRinging");
|
||||
serializer.startTag(null, "vibrateWhenRinging");
|
||||
serializer.text(String.valueOf(Profile.getProfileCollection().get(i).getVibrateWhenRinging()));
|
||||
serializer.endTag(null, "changeVibrateWhenRinging");
|
||||
serializer.endTag(null, "vibrateWhenRinging");
|
||||
|
||||
serializer.startTag(null, "changeNotificationRingtone");
|
||||
serializer.text(String.valueOf(Profile.getProfileCollection().get(i).getChangeNotificationRingtone()));
|
||||
@@ -204,13 +204,19 @@ public class XmlFileInterface
|
||||
serializer.endTag(null, "changeHapticFeedback");//
|
||||
serializer.startTag(null, "hapticFeedback");
|
||||
serializer.text(String.valueOf(Profile.getProfileCollection().get(i).getHapticFeedback()));
|
||||
serializer.endTag(null, "hapticFeedback");
|
||||
serializer.endTag(null, "hapticFeedback");
|
||||
|
||||
serializer.startTag(null, "changeDndMode");
|
||||
serializer.text(String.valueOf(Profile.getProfileCollection().get(i).getChangeDndMode()));
|
||||
serializer.endTag(null, "changeDndMode");//
|
||||
serializer.startTag(null, "dndMode");
|
||||
serializer.text(String.valueOf(Profile.getProfileCollection().get(i).getDndMode()));
|
||||
serializer.endTag(null, "dndMode");
|
||||
|
||||
serializer.endTag(null, "Profile");
|
||||
}
|
||||
serializer.endTag(null, "ProfileCollection");
|
||||
|
||||
|
||||
|
||||
|
||||
serializer.startTag(null, "RuleCollection");
|
||||
for(int i=0; i<Rule.getRuleCollection().size(); i++)
|
||||
@@ -607,6 +613,10 @@ public class XmlFileInterface
|
||||
newProfile.setChangeSoundMode(Boolean.parseBoolean(readTag(parser, "changeSoundMode")));
|
||||
else if (name.equals("soundMode"))
|
||||
newProfile.setSoundMode(Integer.parseInt(readTag(parser, "soundMode")));
|
||||
else if (name.equals("changeDndMode"))
|
||||
newProfile.setChangeDndMode(Boolean.parseBoolean(readTag(parser, "changeDndMode")));
|
||||
else if (name.equals("dndMode"))
|
||||
newProfile.setDndMode(Integer.parseInt(readTag(parser, "dndMode")));
|
||||
else if (name.equals("changeVolumeMusicVideoGameMedia"))
|
||||
newProfile.setChangeVolumeMusicVideoGameMedia(Boolean.parseBoolean(readTag(parser, "changeVolumeMusicVideoGameMedia")));
|
||||
else if (name.equals("volumeMusic"))
|
||||
@@ -641,6 +651,8 @@ public class XmlFileInterface
|
||||
else
|
||||
newProfile.setNotificationRingtone(null);
|
||||
}
|
||||
else if (name.equals("vibrateWhenRinging"))
|
||||
newProfile.setVibrateWhenRinging(Boolean.parseBoolean(readTag(parser, "vibrateWhenRinging")));
|
||||
else if (name.equals("changeAudibleSelection"))
|
||||
newProfile.setChangeAudibleSelection(Boolean.parseBoolean(readTag(parser, "changeAudibleSelection")));
|
||||
else if (name.equals("audibleSelection"))
|
||||
@@ -752,6 +764,8 @@ public class XmlFileInterface
|
||||
try
|
||||
{
|
||||
newRule.setTriggerSet(readTriggerCollection(parser));
|
||||
for(Trigger t : newRule.getTriggerSet())
|
||||
t.setParentRule(newRule);
|
||||
}
|
||||
catch (XmlPullParserException e)
|
||||
{
|
||||
@@ -767,6 +781,8 @@ public class XmlFileInterface
|
||||
try
|
||||
{
|
||||
newRule.setActionSet(readActionCollection(parser));
|
||||
for(Action a : newRule.getActionSet())
|
||||
a.setParentRule(newRule);
|
||||
}
|
||||
catch (XmlPullParserException e)
|
||||
{
|
||||
|
@@ -130,6 +130,21 @@ public class CellLocationChangedReceiver extends PhoneStateListener
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isCellLocationChangedReceiverPossible()
|
||||
{
|
||||
if(telephonyManager == null)
|
||||
telephonyManager = (TelephonyManager) AutomationService.getInstance().getSystemService(Context.TELEPHONY_SERVICE);
|
||||
|
||||
if(
|
||||
ConnectivityReceiver.isAirplaneMode(AutomationService.getInstance())
|
||||
||
|
||||
telephonyManager.getSimState() != TelephonyManager.SIM_STATE_READY
|
||||
)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
public Location getLocation(String accuracy)
|
||||
{
|
||||
Criteria crit = new Criteria();
|
||||
@@ -137,7 +152,7 @@ public class CellLocationChangedReceiver extends PhoneStateListener
|
||||
String myProviderName;
|
||||
|
||||
// If privacy mode or no data connection available
|
||||
if(Settings.privacyLocationing | !ConnectivityReceiver.isDataConnectionAvailable(AutomationService.getInstance()))
|
||||
if(Settings.privacyLocationing || !ConnectivityReceiver.isDataConnectionAvailable(AutomationService.getInstance()))
|
||||
{
|
||||
Miscellaneous.logEvent("i", "CellLocation", Miscellaneous.getAnyContext().getResources().getString(R.string.enforcingGps), 4);
|
||||
myProviderName = LocationManager.GPS_PROVIDER;
|
||||
@@ -175,6 +190,9 @@ public class CellLocationChangedReceiver extends PhoneStateListener
|
||||
}
|
||||
else
|
||||
{
|
||||
if(myLocationManager == null)
|
||||
myLocationManager = (LocationManager) AutomationService.getInstance().getSystemService(Context.LOCATION_SERVICE);
|
||||
|
||||
if(!myLocationManager.isProviderEnabled(myProviderName))
|
||||
{
|
||||
if(myProviderName.equals(LocationManager.NETWORK_PROVIDER))
|
||||
@@ -226,13 +244,11 @@ public class CellLocationChangedReceiver extends PhoneStateListener
|
||||
return currentLocation;
|
||||
}
|
||||
|
||||
|
||||
public void setCurrentLocation(Location currentLocation)
|
||||
{
|
||||
this.currentLocation = currentLocation;
|
||||
}
|
||||
|
||||
|
||||
public class MyLocationListener implements LocationListener
|
||||
{
|
||||
@Override
|
||||
@@ -322,12 +338,12 @@ public class CellLocationChangedReceiver extends PhoneStateListener
|
||||
{
|
||||
if(telephonyManager == null)
|
||||
telephonyManager = (TelephonyManager) AutomationService.getInstance().getSystemService(Context.TELEPHONY_SERVICE);
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
if(!cellLocationListenerActive)
|
||||
{
|
||||
if(!ConnectivityReceiver.isAirplaneMode(AutomationService.getInstance()))
|
||||
if(!ConnectivityReceiver.isAirplaneMode(AutomationService.getInstance()) && telephonyManager.getSimState() == TelephonyManager.SIM_STATE_READY)
|
||||
{
|
||||
if(WifiBroadcastReceiver.mayCellLocationReceiverBeActivated())
|
||||
{
|
||||
@@ -356,7 +372,7 @@ public class CellLocationChangedReceiver extends PhoneStateListener
|
||||
Miscellaneous.logEvent("w", "cellReceiver", "Wanted to activate CellLocationChangedReceiver, but Wifi-Receiver says not to.", 4);
|
||||
}
|
||||
else
|
||||
Miscellaneous.logEvent("i", "cellReceiver", "Not starting cellLocationListener because Airplane mode is active.", 4);
|
||||
Miscellaneous.logEvent("i", "cellReceiver", "Not starting cellLocationListener because Airplane mode is active or SIM_STATE is not ready.", 4);
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
@@ -409,5 +425,4 @@ public class CellLocationChangedReceiver extends PhoneStateListener
|
||||
&&
|
||||
ActivityPermissions.havePermission("android.permission.ACCESS_WIFI_STATE", Miscellaneous.getAnyContext());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -7,6 +7,7 @@ import android.location.LocationManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.jens.automation2.ActivityMainScreen;
|
||||
@@ -26,13 +27,9 @@ import java.util.Calendar;
|
||||
|
||||
public class LocationProvider
|
||||
{
|
||||
|
||||
protected static boolean passiveLocationListenerActive = false;
|
||||
|
||||
protected static LocationListener passiveLocationListener;
|
||||
|
||||
protected static LocationProvider locationProviderInstance = null;
|
||||
|
||||
protected AutomationService parentService;
|
||||
public AutomationService getParentService()
|
||||
{
|
||||
@@ -109,123 +106,128 @@ public class LocationProvider
|
||||
|
||||
public void setCurrentLocation(Location newLocation, boolean skipVerification)
|
||||
{
|
||||
Miscellaneous.logEvent("i", "Location", "Setting location.", 4);
|
||||
|
||||
currentLocation = newLocation;
|
||||
currentLocationStaticCopy = newLocation;
|
||||
|
||||
Miscellaneous.logEvent("i", "LocationListener", "Giving update to POI class", 4);
|
||||
PointOfInterest.positionUpdate(newLocation, parentService, false, skipVerification);
|
||||
|
||||
try
|
||||
if(newLocation != null)
|
||||
{
|
||||
if(
|
||||
locationList.size() >= 1
|
||||
&&
|
||||
locationList.get(locationList.size()-1).getTime() == newLocation.getTime()
|
||||
&&
|
||||
locationList.get(locationList.size()-1).getProvider().equals(newLocation.getProvider())
|
||||
Miscellaneous.logEvent("i", "Location", "Setting location.", 4);
|
||||
|
||||
currentLocation = newLocation;
|
||||
currentLocationStaticCopy = newLocation;
|
||||
|
||||
Miscellaneous.logEvent("i", "LocationListener", "Giving update to POI class", 4);
|
||||
PointOfInterest.positionUpdate(newLocation, parentService, false, skipVerification);
|
||||
|
||||
try
|
||||
{
|
||||
if (
|
||||
locationList.size() >= 1
|
||||
&&
|
||||
locationList.get(locationList.size() - 1).getTime() == newLocation.getTime()
|
||||
&&
|
||||
locationList.get(locationList.size() - 1).getProvider().equals(newLocation.getProvider())
|
||||
)
|
||||
{
|
||||
// This is a duplicate update, do not store it
|
||||
Miscellaneous.logEvent("i", "LocationListener", "Duplicate location, ignoring.", 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
Miscellaneous.logEvent("i", "Speed", "Commencing speed calculation.", 4);
|
||||
// This part keeps the last two location entries to determine the current speed.
|
||||
|
||||
locationList.add(newLocation);
|
||||
|
||||
if(newLocation.hasSpeed())
|
||||
{
|
||||
Miscellaneous.logEvent("i", "Speed", "Location has speed, taking that: " + String.valueOf(newLocation.getSpeed()) + " km/h", 4);
|
||||
setSpeed(newLocation.getSpeed()); // Take the value that came with the location, that should be more precise
|
||||
// This is a duplicate update, do not store it
|
||||
Miscellaneous.logEvent("i", "LocationListener", "Duplicate location, ignoring.", 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
speedCalculation:
|
||||
if (locationList.size() >= 2)
|
||||
Miscellaneous.logEvent("i", "Speed", "Commencing speed calculation.", 4);
|
||||
// This part keeps the last two location entries to determine the current speed.
|
||||
|
||||
locationList.add(newLocation);
|
||||
|
||||
if (newLocation.hasSpeed())
|
||||
{
|
||||
while (locationList.size() > 2)
|
||||
Miscellaneous.logEvent("i", "Speed", "Location has speed, taking that: " + String.valueOf(newLocation.getSpeed()) + " km/h", 4);
|
||||
setSpeed(newLocation.getSpeed()); // Take the value that came with the location, that should be more precise
|
||||
}
|
||||
else
|
||||
{
|
||||
speedCalculation:
|
||||
if (locationList.size() >= 2)
|
||||
{
|
||||
// Remove all entries except for the last 2
|
||||
Miscellaneous.logEvent("i", "Speed", "About to delete oldest position record until only 2 left. Currently have " + String.valueOf(locationList.size()) + " records.", 4);
|
||||
locationList.remove(0);
|
||||
}
|
||||
while (locationList.size() > 2)
|
||||
{
|
||||
// Remove all entries except for the last 2
|
||||
Miscellaneous.logEvent("i", "Speed", "About to delete oldest position record until only 2 left. Currently have " + String.valueOf(locationList.size()) + " records.", 4);
|
||||
locationList.remove(0);
|
||||
}
|
||||
|
||||
/*
|
||||
The two most recent locations in the list must have a usable accuracy.
|
||||
*/
|
||||
for(int i = 0; i < 2; i++)
|
||||
{
|
||||
if
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if
|
||||
(
|
||||
(locationList.get(i).getProvider().equals(LocationManager.GPS_PROVIDER) && locationList.get(i).getAccuracy() > Settings.satisfactoryAccuracyGps)
|
||||
||
|
||||
(locationList.get(i).getProvider().equals(LocationManager.NETWORK_PROVIDER) && locationList.get(i).getAccuracy() > Settings.satisfactoryAccuracyNetwork)
|
||||
(locationList.get(i).getProvider().equals(LocationManager.GPS_PROVIDER) && locationList.get(i).getAccuracy() > Settings.satisfactoryAccuracyGps)
|
||||
||
|
||||
(locationList.get(i).getProvider().equals(LocationManager.NETWORK_PROVIDER) && locationList.get(i).getAccuracy() > Settings.satisfactoryAccuracyNetwork)
|
||||
)
|
||||
{
|
||||
Miscellaneous.logEvent("i", "Speed", "Not using 2 most recent locations for speed calculation because at least one does not have a satisfactory accuracy: " + locationList.get(i).toString(), 4);
|
||||
break speedCalculation;
|
||||
}
|
||||
}
|
||||
|
||||
Miscellaneous.logEvent("i", "Speed", "Trying to calculate speed based on the last locations.", 4);
|
||||
|
||||
double currentSpeed;
|
||||
long timeDifferenceInSeconds = (Math.abs(locationList.get(locationList.size() - 2).getTime() - locationList.get(locationList.size() - 1).getTime())) / 1000; //milliseconds
|
||||
if (timeDifferenceInSeconds <= Settings.speedMaximumTimeBetweenLocations * 60)
|
||||
{
|
||||
double distanceTraveled = locationList.get(locationList.size() - 2).distanceTo(locationList.get(locationList.size() - 1)); //results in meters
|
||||
|
||||
if (timeDifferenceInSeconds == 0)
|
||||
{
|
||||
Miscellaneous.logEvent("w", "Speed", "No time passed since last position. Can't calculate speed here.", 4);
|
||||
return;
|
||||
{
|
||||
Miscellaneous.logEvent("i", "Speed", "Not using 2 most recent locations for speed calculation because at least one does not have a satisfactory accuracy: " + locationList.get(i).toString(), 4);
|
||||
break speedCalculation;
|
||||
}
|
||||
}
|
||||
|
||||
currentSpeed = distanceTraveled / timeDifferenceInSeconds * 3.6; // convert m/s --> km/h
|
||||
Miscellaneous.logEvent("i", "Speed", "Trying to calculate speed based on the last locations.", 4);
|
||||
|
||||
double currentSpeed;
|
||||
long timeDifferenceInSeconds = (Math.abs(locationList.get(locationList.size() - 2).getTime() - locationList.get(locationList.size() - 1).getTime())) / 1000; //milliseconds
|
||||
if (timeDifferenceInSeconds <= Settings.speedMaximumTimeBetweenLocations * 60)
|
||||
{
|
||||
double distanceTraveled = locationList.get(locationList.size() - 2).distanceTo(locationList.get(locationList.size() - 1)); //results in meters
|
||||
|
||||
if (timeDifferenceInSeconds == 0)
|
||||
{
|
||||
Miscellaneous.logEvent("w", "Speed", "No time passed since last position. Can't calculate speed here.", 4);
|
||||
return;
|
||||
}
|
||||
|
||||
currentSpeed = distanceTraveled / timeDifferenceInSeconds * 3.6; // convert m/s --> km/h
|
||||
|
||||
/*
|
||||
Due to strange factors the time difference might be 0 resulting in mathematical error.
|
||||
*/
|
||||
if (Double.isInfinite(currentSpeed) | Double.isNaN(currentSpeed))
|
||||
Miscellaneous.logEvent("i", "Speed", "Error while calculating speed.", 4);
|
||||
else
|
||||
{
|
||||
Miscellaneous.logEvent("i", "Speed", "Current speed: " + String.valueOf(currentSpeed) + " km/h", 2);
|
||||
|
||||
setSpeed(currentSpeed);
|
||||
|
||||
// execute matching rules containing speed
|
||||
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesBySpeed();
|
||||
for (Rule oneRule : ruleCandidates)
|
||||
if (Double.isInfinite(currentSpeed) | Double.isNaN(currentSpeed))
|
||||
Miscellaneous.logEvent("i", "Speed", "Error while calculating speed.", 4);
|
||||
else
|
||||
{
|
||||
if (oneRule.applies(this.getParentService()))
|
||||
oneRule.activate(getParentService(), false);
|
||||
Miscellaneous.logEvent("i", "Speed", "Current speed: " + String.valueOf(currentSpeed) + " km/h", 2);
|
||||
|
||||
setSpeed(currentSpeed);
|
||||
|
||||
// execute matching rules containing speed
|
||||
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidatesBySpeed();
|
||||
for (Rule oneRule : ruleCandidates)
|
||||
{
|
||||
if (oneRule.applies(this.getParentService()))
|
||||
oneRule.activate(getParentService(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
Miscellaneous.logEvent("i", "Speed", "Last two locations are too far apart in terms of time. Cannot use them for speed calculation.", 4);
|
||||
}
|
||||
else
|
||||
Miscellaneous.logEvent("i", "Speed", "Last two locations are too far apart in terms of time. Cannot use them for speed calculation.", 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
Miscellaneous.logEvent("w", "Speed", "Don't have enough values for speed calculation, yet.", 3);
|
||||
{
|
||||
Miscellaneous.logEvent("w", "Speed", "Don't have enough values for speed calculation, yet.", 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Miscellaneous.logEvent("e", "Speed", "Error during speed calculation: " + Log.getStackTraceString(e), 3);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Miscellaneous.logEvent("e", "Speed", "Error during speed calculation: " + Log.getStackTraceString(e), 3);
|
||||
}
|
||||
|
||||
AutomationService.updateNotification();
|
||||
AutomationService.updateNotification();
|
||||
|
||||
if(AutomationService.isMainActivityRunning(parentService))
|
||||
ActivityMainScreen.updateMainScreen();
|
||||
if (AutomationService.isMainActivityRunning(parentService))
|
||||
ActivityMainScreen.updateMainScreen();
|
||||
}
|
||||
else
|
||||
Miscellaneous.logEvent("w", "Location", "New location given is null. Ignoring.", 5);
|
||||
}
|
||||
|
||||
public void startLocationService()
|
||||
@@ -244,13 +246,37 @@ public class LocationProvider
|
||||
|
||||
if(Settings.positioningEngine == 0)
|
||||
{
|
||||
// startCellLocationChangedReceiver
|
||||
if (!ConnectivityReceiver.isAirplaneMode(this.parentService) && WifiBroadcastReceiver.mayCellLocationReceiverBeActivated() && (Rule.isAnyRuleUsing(Trigger_Enum.pointOfInterest) | Rule.isAnyRuleUsing(Trigger_Enum.speed)))
|
||||
CellLocationChangedReceiver.startCellLocationChangedReceiver();
|
||||
|
||||
// startPassiveLocationListener
|
||||
if(Rule.isAnyRuleUsing(Trigger_Enum.pointOfInterest) | Rule.isAnyRuleUsing(Trigger_Enum.speed))
|
||||
{
|
||||
// TelephonyManager telephonyManager = (TelephonyManager) AutomationService.getInstance().getSystemService(Context.TELEPHONY_SERVICE);
|
||||
|
||||
// startCellLocationChangedReceiver
|
||||
if (CellLocationChangedReceiver.isCellLocationChangedReceiverPossible())
|
||||
{
|
||||
if (WifiBroadcastReceiver.mayCellLocationReceiverBeActivated())
|
||||
CellLocationChangedReceiver.startCellLocationChangedReceiver();
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
Reasons why we may end up here:
|
||||
- Airplane mode is active
|
||||
- No phone module present (pure wifi device)
|
||||
- No SIM card is inserted or it's not unlocked
|
||||
|
||||
We'd have to try GPS now to get an initial position.
|
||||
For permanent use there is no way we could know when it
|
||||
would make sense to check the position again.
|
||||
*/
|
||||
|
||||
// Trigger a one-time-position-search
|
||||
Location loc = CellLocationChangedReceiver.getInstance().getLocation("fine");
|
||||
LocationProvider.getInstance().setCurrentLocation(loc, true);
|
||||
}
|
||||
|
||||
// startPassiveLocationListener
|
||||
startPassiveLocationListener();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@@ -1,301 +0,0 @@
|
||||
package com.jens.automation2.receivers;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import com.jens.automation2.AutomationService;
|
||||
import com.jens.automation2.Miscellaneous;
|
||||
import com.jens.automation2.Rule;
|
||||
import com.jens.automation2.Trigger;
|
||||
import com.jens.automation2.Trigger.Trigger_Enum;
|
||||
|
||||
import java.sql.Time;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
public class AlarmListener extends BroadcastReceiver implements AutomationListenerInterface
|
||||
{
|
||||
private static AutomationService automationServiceRef;
|
||||
private static AlarmManager centralAlarmManagerInstance;
|
||||
// private static Intent alarmIntent;
|
||||
// private static PendingIntent alarmPendingIntent;
|
||||
private static boolean alarmListenerActive=false;
|
||||
private static ArrayList<Long> alarmCandidates = new ArrayList<Long>();
|
||||
|
||||
private static ArrayList<Integer> requestCodeList = new ArrayList<Integer>();
|
||||
|
||||
public static void startAlarmListener(final AutomationService automationServiceRef)
|
||||
{
|
||||
AlarmListener.startAlarmListenerInternal(automationServiceRef);
|
||||
}
|
||||
public static void stopAlarmListener(Context context)
|
||||
{
|
||||
AlarmListener.stopAlarmListenerInternal();
|
||||
}
|
||||
|
||||
public static boolean isAlarmListenerActive()
|
||||
{
|
||||
return alarmListenerActive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent)
|
||||
{
|
||||
Miscellaneous.logEvent("i", "AlarmListener", "Alarm received", 2);
|
||||
Date now = new Date();
|
||||
String timeString = String.valueOf(now.getHours()) + ":" + String.valueOf(now.getMinutes()) + ":" + String.valueOf(now.getSeconds());
|
||||
Time passTime = Time.valueOf(timeString);
|
||||
|
||||
ArrayList<Rule> allRulesWithNowInTimeFrame = Rule.findRuleCandidatesByTime(passTime);
|
||||
for(int i=0; i<allRulesWithNowInTimeFrame.size(); i++)
|
||||
{
|
||||
if(allRulesWithNowInTimeFrame.get(i).applies(context))
|
||||
allRulesWithNowInTimeFrame.get(i).activate(automationServiceRef, false);
|
||||
}
|
||||
|
||||
setAlarms();
|
||||
}
|
||||
|
||||
public static void setAlarms()
|
||||
{
|
||||
alarmCandidates.clear();
|
||||
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("E dd.MM.yyyy HH:mm");
|
||||
|
||||
clearAlarms();
|
||||
|
||||
int i=0;
|
||||
|
||||
// // get a Calendar object with current time
|
||||
// Calendar cal = Calendar.getInstance();
|
||||
// // add 5 minutes to the calendar object
|
||||
// cal.add(Calendar.SECOND, 10);
|
||||
// String calSetWorkingCopyString2 = null;
|
||||
// SimpleDateFormat sdf2 = new SimpleDateFormat("E dd.MM.yyyy HH:mm");
|
||||
// if (cal != null)
|
||||
// {
|
||||
// calSetWorkingCopyString2 = sdf2.format(cal.getTime());
|
||||
// }
|
||||
// Miscellaneous.logEvent("i", "AlarmManager", "Setting repeating alarm because of hardcoded test: beginning at " + calSetWorkingCopyString2);
|
||||
// Intent alarmIntent2 = new Intent(automationServiceRef, AlarmListener.class);
|
||||
// PendingIntent alarmPendingIntent2 = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent2, 0);
|
||||
// centralAlarmManagerInstance.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 5000, alarmPendingIntent2);
|
||||
// requestCodeList.add(0);
|
||||
|
||||
ArrayList<Rule> allRulesWithTimeFrames = new ArrayList<Rule>();
|
||||
allRulesWithTimeFrames = Rule.findRuleCandidatesByTimeFrame();
|
||||
for(Rule oneRule : allRulesWithTimeFrames)
|
||||
{
|
||||
for(Trigger oneTrigger : oneRule.getTriggerSet())
|
||||
{
|
||||
if(oneTrigger.getTriggerType() == Trigger_Enum.timeFrame)
|
||||
{
|
||||
Calendar calNow, calSet;
|
||||
Time setTime;
|
||||
|
||||
if(oneTrigger.getTriggerParameter())
|
||||
setTime = oneTrigger.getTimeFrame().getTriggerTimeStart();
|
||||
else
|
||||
setTime = oneTrigger.getTimeFrame().getTriggerTimeStop();
|
||||
|
||||
calNow = Calendar.getInstance();
|
||||
calSet = (Calendar) calNow.clone();
|
||||
calSet.set(Calendar.HOUR_OF_DAY, setTime.getHours());
|
||||
calSet.set(Calendar.MINUTE, setTime.getMinutes());
|
||||
calSet.set(Calendar.SECOND, 0);
|
||||
calSet.set(Calendar.MILLISECOND, 0);
|
||||
// At this point calSet would be a scheduling candidate. It's just the day the might not be right, yet.
|
||||
|
||||
long milliSecondsInAWeek = 1000 * 60 * 60 * 24 * 7;
|
||||
|
||||
for(int dayOfWeek : oneTrigger.getTimeFrame().getDayList())
|
||||
{
|
||||
Calendar calSetWorkingCopy = (Calendar) calSet.clone();
|
||||
|
||||
// calSetWorkingCopy.set(Calendar.HOUR_OF_DAY, setTime.getHours());
|
||||
// calSetWorkingCopy.set(Calendar.MINUTE, setTime.getMinutes());
|
||||
// calSetWorkingCopy.set(Calendar.SECOND, 0);
|
||||
// calSetWorkingCopy.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
int diff = dayOfWeek - calNow.get(Calendar.DAY_OF_WEEK);
|
||||
// Log.i("AlarmManager", "Today: " + String.valueOf(calNow.get(Calendar.DAY_OF_WEEK)) + " / Sched.Day: " + String.valueOf(dayOfWeek) + " Difference to target day is: " + String.valueOf(diff));
|
||||
if(diff == 0) //if we're talking about the current day, is the time still in the future?
|
||||
{
|
||||
if(calSetWorkingCopy.getTime().getHours() < calNow.getTime().getHours())
|
||||
{
|
||||
// Log.i("AlarmManager", "calSetWorkingCopy.getTime().getHours(" + String.valueOf(calSetWorkingCopy.getTime().getHours()) + ") < calNow.getTime().getHours(" + String.valueOf(calNow.getTime().getHours()) + ")");
|
||||
calSetWorkingCopy.add(Calendar.DAY_OF_MONTH, 7); //add a week
|
||||
}
|
||||
else if(calSetWorkingCopy.getTime().getHours() == calNow.getTime().getHours())
|
||||
{
|
||||
// Log.i("AlarmManager", "calSetWorkingCopy.getTime().getHours() == calNow.getTime().getHours()");
|
||||
if(calSetWorkingCopy.getTime().getMinutes() <= calNow.getTime().getMinutes())
|
||||
{
|
||||
// Log.i("AlarmManager", "calSetWorkingCopy.getTime().getMinutes() < calNow.getTime().getMinutes()");
|
||||
calSetWorkingCopy.add(Calendar.DAY_OF_MONTH, 7); //add a week
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(diff < 0)
|
||||
{
|
||||
// Miscellaneous.logEvent("i", "AlarmManager", "Adding " + String.valueOf(diff+7) + " on top of " + String.valueOf(calSetWorkingCopy.get(Calendar.DAY_OF_WEEK)));
|
||||
calSetWorkingCopy.add(Calendar.DAY_OF_WEEK, diff+7); // it's a past weekday, schedule for next week
|
||||
}
|
||||
else
|
||||
{
|
||||
// Miscellaneous.logEvent("i", "AlarmManager", "Adding " + String.valueOf(diff) + " on top of " + String.valueOf(calSetWorkingCopy.get(Calendar.DAY_OF_WEEK)));
|
||||
calSetWorkingCopy.add(Calendar.DAY_OF_WEEK, diff); // it's a future weekday, schedule for that day
|
||||
}
|
||||
|
||||
i++;
|
||||
i=(int)System.currentTimeMillis();
|
||||
String calSetWorkingCopyString = sdf.format(calSetWorkingCopy.getTime()) + " RequestCode: " + String.valueOf(i);
|
||||
// Miscellaneous.logEvent("i", "AlarmManager", "Setting repeating alarm because of rule: " + oneRule.getName() + " beginning at " + calSetWorkingCopyString);
|
||||
|
||||
alarmCandidates.add(calSetWorkingCopy.getTimeInMillis());
|
||||
// Intent alarmIntent = new Intent(automationServiceRef, AlarmListener.class);
|
||||
// alarmIntent.setData(Uri.parse("myalarms://" + i));
|
||||
// PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, i, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
// centralAlarmManagerInstance.setInexactRepeating(AlarmManager.RTC_WAKEUP, calSetWorkingCopy.getTimeInMillis(), milliSecondsInAWeek, alarmPendingIntent);
|
||||
// requestCodeList.add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// // get a Calendar object with current time
|
||||
// Calendar cal = Calendar.getInstance();
|
||||
// cal.add(Calendar.SECOND, 10);
|
||||
// String calSetWorkingCopyString2 = sdf.format(cal.getTime());
|
||||
// Miscellaneous.logEvent("i", "AlarmManager", "Setting repeating alarm because of hardcoded test: beginning at " + calSetWorkingCopyString2);
|
||||
// Intent alarmIntent2 = new Intent(automationServiceRef, AlarmListener.class);
|
||||
// PendingIntent alarmPendingIntent2 = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent2, 0);
|
||||
// centralAlarmManagerInstance.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 5000, alarmPendingIntent2);
|
||||
// requestCodeList.add(0);
|
||||
|
||||
scheduleNextAlarm();
|
||||
}
|
||||
|
||||
private static void scheduleNextAlarm()
|
||||
{
|
||||
Long currentTime = System.currentTimeMillis();
|
||||
Long scheduleCandidate = null;
|
||||
|
||||
if(alarmCandidates.size() == 0)
|
||||
{
|
||||
Miscellaneous.logEvent("i", "AlarmManager", "No alarms to be scheduled.", 3);
|
||||
return;
|
||||
}
|
||||
else if(alarmCandidates.size() == 1)
|
||||
{
|
||||
// only one alarm, schedule that
|
||||
scheduleCandidate = alarmCandidates.get(0);
|
||||
}
|
||||
else if(alarmCandidates.size() > 1)
|
||||
{
|
||||
scheduleCandidate = alarmCandidates.get(0);
|
||||
|
||||
for(long alarmCandidate : alarmCandidates)
|
||||
{
|
||||
if(Math.abs(currentTime - alarmCandidate) < Math.abs(currentTime - scheduleCandidate))
|
||||
scheduleCandidate = alarmCandidate;
|
||||
}
|
||||
}
|
||||
|
||||
Intent alarmIntent = new Intent(automationServiceRef, AlarmListener.class);
|
||||
PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
centralAlarmManagerInstance.set(AlarmManager.RTC_WAKEUP, scheduleCandidate, alarmPendingIntent);
|
||||
|
||||
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("E dd.MM.yyyy HH:mm");
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTimeInMillis(scheduleCandidate);
|
||||
Miscellaneous.logEvent("i", "AlarmManager", "Chose " + sdf.format(calendar.getTime()) + " as next scheduled alarm.", 4);
|
||||
|
||||
}
|
||||
|
||||
public static void clearAlarms()
|
||||
{
|
||||
Miscellaneous.logEvent("i", "AlarmManager", "Clearing possibly standing alarms.", 4);
|
||||
for(int requestCode : requestCodeList)
|
||||
{
|
||||
Intent alarmIntent = new Intent(automationServiceRef, AlarmListener.class);
|
||||
PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, requestCode, alarmIntent, 0);
|
||||
// Miscellaneous.logEvent("i", "AlarmManager", "Clearing alarm with request code: " + String.valueOf(requestCode));
|
||||
centralAlarmManagerInstance.cancel(alarmPendingIntent);
|
||||
}
|
||||
requestCodeList.clear();
|
||||
}
|
||||
|
||||
private static void startAlarmListenerInternal(AutomationService givenAutomationServiceRef)
|
||||
{
|
||||
if(!alarmListenerActive)
|
||||
{
|
||||
Miscellaneous.logEvent("i", "AlarmListener", "Starting alarm listener.", 4);
|
||||
AlarmListener.automationServiceRef = givenAutomationServiceRef;
|
||||
centralAlarmManagerInstance = (AlarmManager)automationServiceRef.getSystemService(automationServiceRef.ALARM_SERVICE);
|
||||
// alarmIntent = new Intent(automationServiceRef, AlarmListener.class);
|
||||
// alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent, 0);
|
||||
alarmListenerActive = true;
|
||||
Miscellaneous.logEvent("i", "AlarmListener", "Alarm listener started.", 4);
|
||||
AlarmListener.setAlarms();
|
||||
|
||||
// // get a Calendar object with current time
|
||||
// Calendar cal = Calendar.getInstance();
|
||||
// // add 5 minutes to the calendar object
|
||||
// cal.add(Calendar.SECOND, 10);
|
||||
// centralAlarmManagerInstance.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 5000, alarmPendingIntent);
|
||||
}
|
||||
else
|
||||
Miscellaneous.logEvent("i", "AlarmListener", "Request to start AlarmListener. But it's already active.", 5);
|
||||
}
|
||||
|
||||
private static void stopAlarmListenerInternal()
|
||||
{
|
||||
if(alarmListenerActive)
|
||||
{
|
||||
Miscellaneous.logEvent("i", "AlarmListener", "Stopping alarm listener.", 4);
|
||||
clearAlarms();
|
||||
// centralAlarmManagerInstance.cancel(alarmPendingIntent);
|
||||
alarmListenerActive = false;
|
||||
}
|
||||
else
|
||||
Miscellaneous.logEvent("i", "AlarmListener", "Request to stop AlarmListener. But it's not running.", 5);
|
||||
}
|
||||
public static void reloadAlarms()
|
||||
{
|
||||
AlarmListener.setAlarms();
|
||||
}
|
||||
@Override
|
||||
public void startListener(AutomationService automationService)
|
||||
{
|
||||
AlarmListener.startAlarmListener(automationService);
|
||||
}
|
||||
@Override
|
||||
public void stopListener(AutomationService automationService)
|
||||
{
|
||||
AlarmListener.stopAlarmListener(automationService);
|
||||
}
|
||||
|
||||
public static boolean haveAllPermission()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isListenerRunning()
|
||||
{
|
||||
return isAlarmListenerActive();
|
||||
}
|
||||
@Override
|
||||
public Trigger_Enum[] getMonitoredTrigger()
|
||||
{
|
||||
return new Trigger_Enum[] { Trigger_Enum.timeFrame };
|
||||
}
|
||||
|
||||
}
|
@@ -112,7 +112,7 @@ public class ConnectivityReceiver extends BroadcastReceiver implements Automatio
|
||||
@SuppressLint("NewApi")
|
||||
public static boolean isAirplaneMode(Context context)
|
||||
{
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||
{
|
||||
int value = android.provider.Settings.System.getInt(context.getContentResolver(), android.provider.Settings.System.AIRPLANE_MODE_ON, 0);
|
||||
return value != 0;
|
||||
|
@@ -0,0 +1,431 @@
|
||||
package com.jens.automation2.receivers;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import com.jens.automation2.AutomationService;
|
||||
import com.jens.automation2.Miscellaneous;
|
||||
import com.jens.automation2.Rule;
|
||||
import com.jens.automation2.TimeFrame;
|
||||
import com.jens.automation2.Trigger;
|
||||
import com.jens.automation2.Trigger.Trigger_Enum;
|
||||
|
||||
import java.sql.Time;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
public class DateTimeListener extends BroadcastReceiver implements AutomationListenerInterface
|
||||
{
|
||||
private static AutomationService automationServiceRef;
|
||||
private static AlarmManager centralAlarmManagerInstance;
|
||||
// private static Intent alarmIntent;
|
||||
// private static PendingIntent alarmPendingIntent;
|
||||
private static boolean alarmListenerActive=false;
|
||||
private static ArrayList<ScheduleElement> alarmCandidates = new ArrayList<>();
|
||||
|
||||
private static ArrayList<Integer> requestCodeList = new ArrayList<Integer>();
|
||||
|
||||
public static void startAlarmListener(final AutomationService automationServiceRef)
|
||||
{
|
||||
DateTimeListener.startAlarmListenerInternal(automationServiceRef);
|
||||
}
|
||||
public static void stopAlarmListener(Context context)
|
||||
{
|
||||
DateTimeListener.stopAlarmListenerInternal();
|
||||
}
|
||||
|
||||
public static boolean isAlarmListenerActive()
|
||||
{
|
||||
return alarmListenerActive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent)
|
||||
{
|
||||
Miscellaneous.logEvent("i", "AlarmListener", "Alarm received", 2);
|
||||
Date now = new Date();
|
||||
String timeString = String.valueOf(now.getHours()) + ":" + String.valueOf(now.getMinutes()) + ":" + String.valueOf(now.getSeconds());
|
||||
Time passTime = Time.valueOf(timeString);
|
||||
|
||||
ArrayList<Rule> allRulesWithNowInTimeFrame = Rule.findRuleCandidatesByTime(passTime);
|
||||
for(int i=0; i<allRulesWithNowInTimeFrame.size(); i++)
|
||||
{
|
||||
if(allRulesWithNowInTimeFrame.get(i).applies(context))
|
||||
allRulesWithNowInTimeFrame.get(i).activate(automationServiceRef, false);
|
||||
}
|
||||
|
||||
setAlarms();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
|
||||
public static void setAlarms()
|
||||
{
|
||||
alarmCandidates.clear();
|
||||
|
||||
Calendar calNow = Calendar.getInstance();
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("E dd.MM.yyyy HH:mm");
|
||||
|
||||
clearAlarms();
|
||||
|
||||
int i=0;
|
||||
|
||||
// // get a Calendar object with current time
|
||||
// Calendar cal = Calendar.getInstance();
|
||||
// // add 5 minutes to the calendar object
|
||||
// cal.add(Calendar.SECOND, 10);
|
||||
// String calSetWorkingCopyString2 = null;
|
||||
// SimpleDateFormat sdf2 = new SimpleDateFormat("E dd.MM.yyyy HH:mm");
|
||||
// if (cal != null)
|
||||
// {
|
||||
// calSetWorkingCopyString2 = sdf2.format(cal.getTime());
|
||||
// }
|
||||
// Miscellaneous.logEvent("i", "AlarmManager", "Setting repeating alarm because of hardcoded test: beginning at " + calSetWorkingCopyString2);
|
||||
// Intent alarmIntent2 = new Intent(automationServiceRef, AlarmListener.class);
|
||||
// PendingIntent alarmPendingIntent2 = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent2, 0);
|
||||
// centralAlarmManagerInstance.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 5000, alarmPendingIntent2);
|
||||
// requestCodeList.add(0);
|
||||
|
||||
ArrayList<Rule> allRulesWithTimeFrames = new ArrayList<Rule>();
|
||||
allRulesWithTimeFrames = Rule.findRuleCandidatesByTimeFrame();
|
||||
/*
|
||||
* Take care of regular executions, no repetitions in between.
|
||||
*/
|
||||
Miscellaneous.logEvent("i", "DateTimeListener", "Checking rules for single run alarm candidates.", 5);
|
||||
for(Rule oneRule : allRulesWithTimeFrames)
|
||||
{
|
||||
Miscellaneous.logEvent("i", "DateTimeListener","Checking rule " + oneRule.getName() + " for single run alarm candidates.", 5);
|
||||
if(oneRule.isRuleActive())
|
||||
{
|
||||
try
|
||||
{
|
||||
for(Trigger oneTrigger : oneRule.getTriggerSet())
|
||||
{
|
||||
Miscellaneous.logEvent("i", "DateTimeListener","Checking trigger " + oneTrigger.toString() + " for single run alarm candidates.", 5);
|
||||
|
||||
if(oneTrigger.getTriggerType().equals(Trigger_Enum.timeFrame))
|
||||
{
|
||||
TimeFrame tf = new TimeFrame(oneTrigger.getTriggerParameter2());
|
||||
|
||||
Calendar calSet;
|
||||
Time setTime;
|
||||
|
||||
if(oneTrigger.getTriggerParameter())
|
||||
setTime = tf.getTriggerTimeStart();
|
||||
else
|
||||
setTime = tf.getTriggerTimeStop();
|
||||
|
||||
calSet = (Calendar) calNow.clone();
|
||||
calSet.set(Calendar.HOUR_OF_DAY, setTime.getHours());
|
||||
calSet.set(Calendar.MINUTE, setTime.getMinutes());
|
||||
calSet.set(Calendar.SECOND, 0);
|
||||
calSet.set(Calendar.MILLISECOND, 0);
|
||||
// At this point calSet would be a scheduling candidate. It's just the day that might not be right, yet.
|
||||
|
||||
for(int dayOfWeek : tf.getDayList())
|
||||
{
|
||||
Calendar calSetWorkingCopy = (Calendar) calSet.clone();
|
||||
|
||||
int diff = dayOfWeek - calNow.get(Calendar.DAY_OF_WEEK);
|
||||
if(diff == 0) // We're talking about the current weekday, but is the time still in the future?
|
||||
{
|
||||
if(calSetWorkingCopy.getTime().getHours() < calNow.getTime().getHours())
|
||||
{
|
||||
calSetWorkingCopy.add(Calendar.DAY_OF_MONTH, 7); //add a week
|
||||
}
|
||||
else if(calSetWorkingCopy.getTime().getHours() == calNow.getTime().getHours())
|
||||
{
|
||||
if(calSetWorkingCopy.getTime().getMinutes() <= calNow.getTime().getMinutes())
|
||||
{
|
||||
calSetWorkingCopy.add(Calendar.DAY_OF_MONTH, 7); //add a week
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(diff < 0)
|
||||
{
|
||||
calSetWorkingCopy.add(Calendar.DAY_OF_WEEK, diff+7); // it's a past weekday, schedule for next week
|
||||
}
|
||||
else
|
||||
{
|
||||
calSetWorkingCopy.add(Calendar.DAY_OF_WEEK, diff); // it's a future weekday, schedule for that day
|
||||
}
|
||||
|
||||
i++;
|
||||
i=(int)System.currentTimeMillis();
|
||||
sdf.format(calSetWorkingCopy.getTime());
|
||||
String.valueOf(i);
|
||||
|
||||
alarmCandidates.add(new ScheduleElement(calSetWorkingCopy, "Rule " + oneRule.getName() + ", trigger " + oneTrigger.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Miscellaneous.logEvent("e", "DateTimeListener","Error checking anything for rule " + oneRule.toString() + " needs to be added to candicates list: " + Log.getStackTraceString(e), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Only take care of repeated executions.
|
||||
*/
|
||||
Miscellaneous.logEvent("i", "DateTimeListener","Checking rules for repeated run alarm candidates.", 5);
|
||||
for(Rule oneRule : allRulesWithTimeFrames)
|
||||
{
|
||||
Miscellaneous.logEvent("i", "DateTimeListener","Checking rule " + oneRule.getName() + " for repeated run alarm candidates.", 5);
|
||||
if(oneRule.isRuleActive())
|
||||
{
|
||||
try
|
||||
{
|
||||
Miscellaneous.logEvent("i", "DateTimeListener","Checking rule " + oneRule.toString() , 5);
|
||||
|
||||
for(Trigger oneTrigger : oneRule.getTriggerSet())
|
||||
{
|
||||
Miscellaneous.logEvent("i", "DateTimeListener","Checking trigger " + oneTrigger.toString() + " for repeated run alarm candidates.", 5);
|
||||
if(oneTrigger.getTriggerType().equals(Trigger_Enum.timeFrame))
|
||||
{
|
||||
Miscellaneous.logEvent("i", "DateTimeListener","Checking rule trigger " + oneTrigger.toString() , 5);
|
||||
|
||||
/*
|
||||
* Check for next repeated execution:
|
||||
*
|
||||
* Check if the rule currently applies....
|
||||
*
|
||||
* If no -> do nothing
|
||||
* If yes -> Take starting time and calculate the next repeated execution
|
||||
* 1. Take starting time
|
||||
* 2. Take current time
|
||||
* 3. Calculate difference, but include check to see if we're after that time,
|
||||
* be it start or end of the timeframe.
|
||||
* 4. Take div result +1 and add this on top of starting time
|
||||
* 5. Is this next possible execution still inside timeframe? Also consider timeframes spanning over midnight
|
||||
*/
|
||||
Calendar calSet;
|
||||
Time setTime;
|
||||
TimeFrame tf = new TimeFrame(oneTrigger.getTriggerParameter2());
|
||||
|
||||
if(tf.getRepetition() > 0)
|
||||
{
|
||||
if(oneTrigger.applies(calNow))
|
||||
{
|
||||
Calendar calSchedule = getNextRepeatedExecutionAfter(oneTrigger, calNow);
|
||||
|
||||
alarmCandidates.add(new ScheduleElement(calSchedule, "Rule " + oneRule.getName() + ", trigger " + oneTrigger.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Miscellaneous.logEvent("e", "DateTimeListener","Error checking anything for rule " + oneRule.toString() + " needs to be added to candicates list: " + Log.getStackTraceString(e), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scheduleNextAlarm();
|
||||
}
|
||||
|
||||
private static void scheduleNextAlarm()
|
||||
{
|
||||
Long currentTime = System.currentTimeMillis();
|
||||
ScheduleElement scheduleCandidate = null;
|
||||
|
||||
if(alarmCandidates.size() == 0)
|
||||
{
|
||||
Miscellaneous.logEvent("i", "AlarmManager", "No alarms to be scheduled.", 3);
|
||||
return;
|
||||
}
|
||||
else if(alarmCandidates.size() == 1)
|
||||
{
|
||||
// only one alarm, schedule that
|
||||
scheduleCandidate = alarmCandidates.get(0);
|
||||
}
|
||||
else if(alarmCandidates.size() > 1)
|
||||
{
|
||||
scheduleCandidate = alarmCandidates.get(0);
|
||||
|
||||
for(ScheduleElement alarmCandidate : alarmCandidates)
|
||||
{
|
||||
if(Math.abs(currentTime - alarmCandidate.time.getTimeInMillis()) < Math.abs(currentTime - scheduleCandidate.time.getTimeInMillis()))
|
||||
scheduleCandidate = alarmCandidate;
|
||||
}
|
||||
}
|
||||
|
||||
Intent alarmIntent = new Intent(automationServiceRef, DateTimeListener.class);
|
||||
PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
centralAlarmManagerInstance.set(AlarmManager.RTC_WAKEUP, scheduleCandidate.time.getTimeInMillis(), alarmPendingIntent);
|
||||
|
||||
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("E dd.MM.yyyy HH:mm");
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTimeInMillis(scheduleCandidate.time.getTimeInMillis());
|
||||
Miscellaneous.logEvent("i", "AlarmManager", "Chose " + sdf.format(calendar.getTime()) + " as next scheduled alarm.", 4);
|
||||
}
|
||||
|
||||
public static void clearAlarms()
|
||||
{
|
||||
Miscellaneous.logEvent("i", "AlarmManager", "Clearing possibly standing alarms.", 4);
|
||||
for(int requestCode : requestCodeList)
|
||||
{
|
||||
Intent alarmIntent = new Intent(automationServiceRef, DateTimeListener.class);
|
||||
PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, requestCode, alarmIntent, 0);
|
||||
// Miscellaneous.logEvent("i", "AlarmManager", "Clearing alarm with request code: " + String.valueOf(requestCode));
|
||||
centralAlarmManagerInstance.cancel(alarmPendingIntent);
|
||||
}
|
||||
requestCodeList.clear();
|
||||
}
|
||||
|
||||
private static void startAlarmListenerInternal(AutomationService givenAutomationServiceRef)
|
||||
{
|
||||
if(!alarmListenerActive)
|
||||
{
|
||||
Miscellaneous.logEvent("i", "AlarmListener", "Starting alarm listener.", 4);
|
||||
DateTimeListener.automationServiceRef = givenAutomationServiceRef;
|
||||
centralAlarmManagerInstance = (AlarmManager)automationServiceRef.getSystemService(automationServiceRef.ALARM_SERVICE);
|
||||
// alarmIntent = new Intent(automationServiceRef, AlarmListener.class);
|
||||
// alarmPendingIntent = PendingIntent.getBroadcast(automationServiceRef, 0, alarmIntent, 0);
|
||||
alarmListenerActive = true;
|
||||
Miscellaneous.logEvent("i", "AlarmListener", "Alarm listener started.", 4);
|
||||
DateTimeListener.setAlarms();
|
||||
|
||||
// // get a Calendar object with current time
|
||||
// Calendar cal = Calendar.getInstance();
|
||||
// // add 5 minutes to the calendar object
|
||||
// cal.add(Calendar.SECOND, 10);
|
||||
// centralAlarmManagerInstance.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 5000, alarmPendingIntent);
|
||||
}
|
||||
else
|
||||
Miscellaneous.logEvent("i", "AlarmListener", "Request to start AlarmListener. But it's already active.", 5);
|
||||
}
|
||||
|
||||
private static void stopAlarmListenerInternal()
|
||||
{
|
||||
if(alarmListenerActive)
|
||||
{
|
||||
Miscellaneous.logEvent("i", "AlarmListener", "Stopping alarm listener.", 4);
|
||||
clearAlarms();
|
||||
// centralAlarmManagerInstance.cancel(alarmPendingIntent);
|
||||
alarmListenerActive = false;
|
||||
}
|
||||
else
|
||||
Miscellaneous.logEvent("i", "AlarmListener", "Request to stop AlarmListener. But it's not running.", 5);
|
||||
}
|
||||
public static void reloadAlarms()
|
||||
{
|
||||
DateTimeListener.setAlarms();
|
||||
}
|
||||
@Override
|
||||
public void startListener(AutomationService automationService)
|
||||
{
|
||||
DateTimeListener.startAlarmListener(automationService);
|
||||
}
|
||||
@Override
|
||||
public void stopListener(AutomationService automationService)
|
||||
{
|
||||
DateTimeListener.stopAlarmListener(automationService);
|
||||
}
|
||||
|
||||
public static boolean haveAllPermission()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isListenerRunning()
|
||||
{
|
||||
return isAlarmListenerActive();
|
||||
}
|
||||
@Override
|
||||
public Trigger_Enum[] getMonitoredTrigger()
|
||||
{
|
||||
return new Trigger_Enum[] { Trigger_Enum.timeFrame };
|
||||
}
|
||||
|
||||
static class ScheduleElement implements Comparable<ScheduleElement>
|
||||
{
|
||||
Calendar time;
|
||||
String reason;
|
||||
|
||||
public ScheduleElement(Calendar timestamp, String reason)
|
||||
{
|
||||
super();
|
||||
this.time = timestamp;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ScheduleElement o)
|
||||
{
|
||||
if(time.getTimeInMillis() == o.time.getTimeInMillis())
|
||||
return 0;
|
||||
if(time.getTimeInMillis() < o.time.getTimeInMillis())
|
||||
return -1;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return Miscellaneous.formatDate(time.getTime()) + ", reason : " + reason;
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public static Calendar getNextRepeatedExecutionAfter(Trigger trigger, Calendar now)
|
||||
{
|
||||
Calendar calSet;
|
||||
Time setTime;
|
||||
TimeFrame tf = new TimeFrame(trigger.getTriggerParameter2());
|
||||
|
||||
if(tf.getRepetition() > 0)
|
||||
{
|
||||
if(trigger.getTriggerParameter())
|
||||
setTime = tf.getTriggerTimeStart();
|
||||
else
|
||||
setTime = tf.getTriggerTimeStop();
|
||||
|
||||
calSet = (Calendar) now.clone();
|
||||
calSet.set(Calendar.HOUR_OF_DAY, setTime.getHours());
|
||||
calSet.set(Calendar.MINUTE, setTime.getMinutes());
|
||||
calSet.set(Calendar.SECOND, 0);
|
||||
calSet.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
// if(this.applies(null))
|
||||
// {
|
||||
// If the starting time is a day ahead remove 1 day.
|
||||
if(calSet.getTimeInMillis() > now.getTimeInMillis())
|
||||
calSet.add(Calendar.DAY_OF_MONTH, -1);
|
||||
|
||||
long differenceInSeconds = Math.abs(now.getTimeInMillis() - calSet.getTimeInMillis()) / 1000;
|
||||
long nextExecutionMultiplier = Math.floorDiv(differenceInSeconds, tf.getRepetition()) + 1;
|
||||
long nextScheduleTimestamp = (calSet.getTimeInMillis() / 1000) + (nextExecutionMultiplier * tf.getRepetition());
|
||||
Calendar calSchedule = Calendar.getInstance();
|
||||
calSchedule.setTimeInMillis(nextScheduleTimestamp * 1000);
|
||||
|
||||
/*
|
||||
* Das war mal aktiviert. Allerdings: Die ganze Funktion liefert zurück, wenn die Regel NOCH nicht
|
||||
* zutrifft, aber wir z.B. gleich den zeitlichen Bereich betreten.
|
||||
*/
|
||||
// if(trigger.checkDateTime(calSchedule.getTime(), false))
|
||||
// {
|
||||
return calSchedule;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
else
|
||||
Miscellaneous.logEvent("i", "DateTimeListener", "Trigger " + trigger.toString() + " is not executed repeatedly.", 5);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -77,12 +77,12 @@ public class TimeZoneListener extends BroadcastReceiver implements AutomationLis
|
||||
if(action.equals(Intent.ACTION_TIMEZONE_CHANGED))
|
||||
{
|
||||
Miscellaneous.logEvent("i", "TimeZoneListener", "Device timezone changed. Reloading alarms.", 3);
|
||||
AlarmListener.reloadAlarms();
|
||||
DateTimeListener.reloadAlarms();
|
||||
}
|
||||
else if(action.equals(Intent.ACTION_TIME_CHANGED))
|
||||
{
|
||||
Miscellaneous.logEvent("i", "TimeZoneListener", "Device time changed. Reloading alarms.", 4);
|
||||
AlarmListener.reloadAlarms();
|
||||
DateTimeListener.reloadAlarms();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
|
@@ -9,6 +9,13 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/smsDialogNotice"
|
||||
android:textColor="#ea131b"
|
||||
android:layout_marginBottom="@dimen/default_margin" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/bImportNumberFromContacts"
|
||||
android:drawableLeft="@drawable/contacts"
|
||||
|
@@ -45,6 +45,19 @@
|
||||
<requestFocus />
|
||||
</EditText>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_margin="10dp"
|
||||
android:layout_marginVertical="@dimen/default_margin"
|
||||
android:background="#aa000000" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/general"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkBoxChangeSoundMode"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -63,13 +76,54 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="40dp" />
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="40dp"
|
||||
android:text="@string/silentTriggersDnd" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_margin="10dp"
|
||||
android:layout_marginVertical="@dimen/default_margin"
|
||||
android:background="#aa000000" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dnd"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkBoxChangeDnd"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/change" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinnerDndMode"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="40dp" />
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="40dp"
|
||||
android:text="@string/dndRemarks" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_margin="10dp"
|
||||
android:layout_marginVertical="@dimen/default_margin"
|
||||
android:background="#aa000000" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/volumes"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:layout_marginLeft="40dp" />
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkBoxChangeVolumeMusicVideoGameMedia"
|
||||
@@ -121,7 +175,20 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="40dp" />
|
||||
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_margin="10dp"
|
||||
android:layout_marginVertical="@dimen/default_margin"
|
||||
android:background="#aa000000" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/tones"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkBoxChangeIncomingCallsRingtone"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -199,7 +266,20 @@
|
||||
android:text="@string/notificationRingtone"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_margin="10dp"
|
||||
android:layout_marginVertical="@dimen/default_margin"
|
||||
android:background="#aa000000" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/miscellaneous"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkBoxChangeAudibleSelection"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -240,6 +320,7 @@
|
||||
|
||||
<Button
|
||||
android:id="@+id/bSaveProfile"
|
||||
android:layout_marginTop="@dimen/default_margin"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/save" />
|
||||
|
@@ -45,6 +45,18 @@
|
||||
|
||||
</TableRow>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_margin="10dp"
|
||||
android:layout_marginVertical="@dimen/default_margin"
|
||||
android:background="#aa000000" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/automationNotificationsIgnored" />
|
||||
|
||||
<TableRow
|
||||
android:layout_marginBottom="@dimen/activity_vertical_margin">
|
||||
|
||||
@@ -78,6 +90,13 @@
|
||||
|
||||
</TableRow>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_margin="10dp"
|
||||
android:layout_marginVertical="@dimen/default_margin"
|
||||
android:background="#aa000000" />
|
||||
|
||||
<TableRow
|
||||
android:layout_marginBottom="@dimen/activity_vertical_margin">
|
||||
|
||||
@@ -106,6 +125,13 @@
|
||||
</LinearLayout>
|
||||
|
||||
</TableRow>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_margin="10dp"
|
||||
android:layout_marginVertical="@dimen/default_margin"
|
||||
android:background="#aa000000" />
|
||||
|
||||
<TableRow
|
||||
android:layout_marginBottom="@dimen/activity_vertical_margin">
|
||||
|
@@ -119,6 +119,34 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/sunday" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_margin="10dp"
|
||||
android:background="#aa000000" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/chkRepeat"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/repeatEveryXseconds" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etRepeatEvery"
|
||||
android:layout_marginLeft="@dimen/default_margin"
|
||||
android:minWidth="75dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:enabled="false"
|
||||
android:inputType="numberSigned" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/bSaveTimeFrame"
|
||||
|
@@ -475,14 +475,14 @@
|
||||
<string name="textMessageAnnotations">Sie können direkt eine Telefonnummer eingeben. Alternativ können Sie eine aus Ihren Kontakten auswählen. Aber: Es wird immer die Nummer gespeichert, nicht der Kontakt referenziert. D.h., wenn Sie einmal die Nummer eines Kontaktes ändern, müssen Sie diese Regel aktualisieren.</string>
|
||||
<string name="importNumberFromContacts">Von Kontakten importieren</string>
|
||||
<string name="android9RecordAudioNotice">Falls Sie die Umgebungslautstärke als Auslöser benutzen: Leider hat sich Google entschlossen ab Android Version 9 (Pie) Hintergrundanwendungen den Zugriff auf das Mikrofon zu verweigern. Das bedeutet, dass dieser Auslöser keinen Effekt mehr hat und keine Regeln auslösen wird.</string>
|
||||
<string name="android10WifiToggleNotice">Leider hat Google in Android 10 die Möglichkeit entfernt, daß normale Anwendungen WLAN an- oder ausschalten können. Diese Aktion wird also keinen Effekt auf Ihrem Gerät haben.</string>
|
||||
<string name="android10WifiToggleNotice">Leider hat Google in Android 10 die Möglichkeit entfernt, daß normale Anwendungen WLAN an- oder ausschalten können. Nur, wenn Ihr Gerät gerootet ist, sollte es weiterhin funktionieren. Wenn nicht, wird diese Aktion leider keinen Effekt mehr auf Ihrem Gerät haben.</string>
|
||||
<string name="messageNotShownAgain">Diese Nachricht wird nicht wieder angezeigt.</string>
|
||||
<string name="chooseActivityHint">In diesem letzten Auswahlfeld müssen Sie eine bestimmte \"Activity\" auswählen. Vereinfacht ist das ein bestimmtes Fenster der ausgewählten Anwendung. Wenn Sie nicht wissen, welche Sie auswählen sollen, ist es normalerweise eine gute Idee zunächst welche auszuprobieren, die \"main\" oder \"launcher\" im Namen haben.</string>
|
||||
<string name="edit">Bearbeiten</string>
|
||||
<string name="clickAndHoldForOptions">Klicken und halten Sie ein Objekt für Optionen.</string>
|
||||
<string name="startAutomationAsService">Automation als Dienst starten</string>
|
||||
<string name="setScreenBrightness">Set screen brightness</string>
|
||||
<string name="setScreenBrightnessEnterValue">Enter the desired brightness (from 0 to 100).</string>
|
||||
<string name="setScreenBrightness">Bildschirmhelligkeit einstellen</string>
|
||||
<string name="setScreenBrightnessEnterValue">Geben Sie den gewünschten Helligkeitswert ein (von 0 bis 100).</string>
|
||||
<string name="autoBrightness">Automatische Helligkeitseinstellung verwenden</string>
|
||||
<string name="apply">übernehmen</string>
|
||||
<string name="brightnessAuto">automatische Helligkeit</string>
|
||||
@@ -537,7 +537,7 @@
|
||||
<string name="notificationTriggerExplanation">Dieser Auslöser reagiert auf Benachrichtigungen anderer Anwendung im Benachrichtigungsbereich von Android (oder wenn diese geschlossen werden). Sie können eine bestimmte Anwendung festlegen, von die Nachricht stammen muß. Wenn nicht, zählt jede Benachrichtigung. Sie können auch Zeichenketten für Titel oder Nachrichteninhalt festlegen, die enthalten sein müssen. Die Groß-/Kleinschreibung wird hierbei nicht berücksichtigt.</string>
|
||||
<string name="addParameters">Parameter hinzufügen</string>
|
||||
<string name="errorRunningRule">Fehler beim Ausführen einer Regel.</string>
|
||||
<string name="startAppChoiceNote">Hier haben Sie 2 grundsätzliche Optionen:\n\n1. Sie können ein Programm starten, indem Sie eine Activity auswählen.\nStellen Sie sich das so vor, daß Sie ein bestimmtes Fenster einer Anwendung vorauswählen, in das man direkt springt. Behalten Sie im Kopf, daß das nicht immer funktionieren wird. Das liegt daran, daß die Fenster einer Anwendung miteinander interagieren können, sich u.U. Parameter übergeben. Wenn man jetzt ganz kalt in ein bestimmtes Fenster springt, könnte dieses zum Start z.B. bestimmte Parameter erwarten - die fehlen. So könnte es passieren, daß das Fenster zwar versucht zu öffnen, das aber nicht klappt und es somit nie wirlich sichtbar wird. Versuchen Sie\'s trotzdem!\nSie können den Pfad manuell eingeben, sollten aber den Auswählen-Knopf benutzen. Wenn Sie es dennoch manuell eingeben, geben Sie den PackageName ins obere Feld ein und den vollen Pfad der Activity ins untere.\n\n2. Auswahl per Action\nIm Gegensatz zur Auswahl eines bestimmten Fensters, können Sie ein Programm auch über eine Action starten lassen. Stellen Sie sich das so vor als würden Sie in den Wald rufen \"Ich hätte gerne XYZ\" und falls eine Anwendung installiert ist, die das liefern kann, wird sie gestartet. Ein gutes Beispiel wäre zum Beispiel "Browser starten" - es könnten sogar mehrere installiert sein, die das können (aber normalerweise gibts eine, die als Standard eingestellt ist).\nDiese Action müssen Sie manuell eingeben. Der PackageName ist hier optional. Behalten Sie dabei im Auge, daß mögliche Variablen nicht aufgelöst werden. Beispielsweise werden Sie häufig im Internet finden, daß man die Kamera über die Action \"MediaStore.ACTION_IMAGE_CAPTURE\" starten kann. Das ist grundsätzlich nicht richtig, wird aber nicht direkt funktionieren, denn das ist nur eine Variable. Sie müssen dann einen Blick in die Android Dokumentation werfen, wo Sie sehen werden, daß sich hinter dieser Variable eigentlich der Wert \"android.media.action.IMAGE_CAPTURE\" verbirgt. Gibt man diesen in das Feld ein, wird\'s funktionieren.</string>
|
||||
<string name="startAppChoiceNote">Hier haben Sie 2 grundsätzliche Optionen:\n\n1. Sie können ein Programm starten, indem Sie eine Activity auswählen.\nStellen Sie sich das so vor, daß Sie ein bestimmtes Fenster einer Anwendung vorauswählen, in das man direkt springt. Behalten Sie im Kopf, daß das nicht immer funktionieren wird. Das liegt daran, daß die Fenster einer Anwendung miteinander interagieren können, sich u.U. Parameter übergeben. Wenn man jetzt ganz kalt in ein bestimmtes Fenster springt, könnte dieses zum Start z.B. bestimmte Parameter erwarten - die fehlen. So könnte es passieren, daß das Fenster zwar versucht zu öffnen, das aber nicht klappt und es somit nie wirlich sichtbar wird. Versuchen Sie\'s trotzdem!\nSie können den Pfad manuell eingeben, sollten aber den Auswählen-Knopf benutzen. Wenn Sie es dennoch manuell eingeben, geben Sie den PackageName ins obere Feld ein und den vollen Pfad der Activity ins untere.\n\n2. Auswahl per Action\nIm Gegensatz zur Auswahl eines bestimmten Fensters, können Sie ein Programm auch über eine Action starten lassen. Stellen Sie sich das so vor als würden Sie in den Wald rufen \"Ich hätte gerne XYZ\" und falls eine Anwendung installiert ist, die das liefern kann, wird sie gestartet. Ein gutes Beispiel wäre zum Beispiel "Browser starten" - es könnten sogar mehrere installiert sein, die das können (aber normalerweise gibts eine, die als Standard eingestellt ist).\nDiese Action müssen Sie manuell eingeben. Der PackageName ist hier optional. Behalten Sie dabei im Auge, daß mögliche Variablen nicht aufgelöst werden. Beispielsweise werden Sie häufig im Internet finden, daß man die Kamera über die Action \"MediaStore.ACTION_IMAGE_CAPTURE\" starten kann. Das ist grundsätzlich richtig, wird aber nicht direkt funktionieren, denn das ist nur eine Variable. Sie müssen dann einen Blick in die Android Dokumentation werfen, wo Sie sehen werden, daß sich hinter dieser Variable eigentlich der Wert \"android.media.action.IMAGE_CAPTURE\" verbirgt. Gibt man diesen in das Feld ein, wird\'s funktionieren.</string>
|
||||
<string name="cantFindSoundFile">Kann die Audiodatei %1$s nicht finden und daher auch nicht abspielen.</string>
|
||||
<string name="startAppByActivity">per Activity</string>
|
||||
<string name="startAppByAction">per Action</string>
|
||||
@@ -594,4 +594,5 @@
|
||||
<string name="top">Oben</string>
|
||||
<string name="bottom">Unten</string>
|
||||
<string name="tabsPlacementSummary">Wol soll die Taskleiste angezeigt werden?</string>
|
||||
<string name="tones">Klingeltöne</string>
|
||||
</resources>
|
@@ -556,7 +556,7 @@
|
||||
<string name="logFileMaxSizeTitle">Maximo tamaño del archivo log [Mb]</string>
|
||||
<string name="theseAreThePermissionsRequired">Esos son los permisos necesarios.</string>
|
||||
<string name="android9RecordAudioNotice">Si usa la condición nivel del ruido fondo: Desafortunadamente iniciando con Android 9 (Pie) Google decidió prohibir aplicaciones de fondo usando el micrófono. Por eso esta condición no tendrá un efecto y no iniciará algo.</string>
|
||||
<string name="android10WifiToggleNotice">Si usa la condición nivel del ruido fondo: Desafortunadamente iniciando con Android 9 (Pie) Google decidió prohibir aplicaciones de fondo usando el micrófono. Por eso esta condición no tendrá un efecto y no iniciará algo.</string>
|
||||
<string name="android10WifiToggleNotice">Desafortunadamente Google dedició remover esta función en Android 10. Applicaciones regulares ya no pueden activar o desactivar wifi. Solo si su dispositivo esta rooted debe funcionar adelante. Si no este ación no va a continuar de funcionar.</string>
|
||||
<string name="messageNotShownAgain">Esta nota no aparecerá otra vez.</string>
|
||||
<string name="noLocationCouldBeFound">No pude encontrar una posición despues de un limite de %1$s segundos..</string>
|
||||
<string name="pleaseGiveBgLocation">En la proxima pantalla por favor vaya a permisos, luego a posición. Ahi elija \"Siempre permitir\" para permitir Automation determinar la posición en el fondo.</string>
|
||||
@@ -593,4 +593,5 @@
|
||||
<string name="intentDataComment">Si su parametro es de tipo Uri y especifica \"IntentData\" como nombre (minúscula/mayáscula no es importante), el parametro no está añadido como un parametro normal con puExtra(), pero estará añadido al intent con setData().</string>
|
||||
<string name="locationEngineDisabledLong">Desafortunadamente su posición todavia no puede ser determinada. Gratitud va para Google por su sabiduria y amabilidad infinita.\n\nDejenme explicarselo mas. Comenzando con Android 10 un nuevo permiso se introdujo que es necesario para determinar la posición en el fondo (que es necesario para una app como esta). Aunque lo considero una buena idea, conlleva a una chicana para desarolladores.\n\nCuando se esta desarrollando una app se puede intentar calificar para este permiso mientras se sigue un catalogo de condiciones. Desafortunadamente nuevas versiones de mi app fueron rechazadas por un periodo de trés meses. Cumplé todas las condiciones, pero Google\'s mierda servicio para desarolladores afirmó que no. Despues de presentar pruebas, que cumplí con todo, recibí una respuesta de \"No puedo ayudarte mas.\". En algun momento me rendí.\n\nComo consecuencia la version Google Play todavia no sabe usar la locación como una condición. Mi única alternativa fue remover la applicación de Google Play.\n\nLo siento mucho, pero hicé todo lo posible para discutir con un support que no sabe aprobar la prueba de Turing repetidamente.\n\nLa noticia positiva: Usted todavia puede tener todo!\n\nAutomation ahora es open source y se puede encontrar en F-Droid. Es un app store que se preocupa por su privacidad - en vez de solo simular eso. Simplemente guarde su configuración, desinstale la app, instale la de F-Droid, restaure su configuración - terminado.\n\nCliquee aqui para averiguar más:</string>
|
||||
<string name="startAppChoiceNote">Aqui tiene 2 opciones generales:\\n\\n1. Puede encender un programa seleccionando un activity. Imagine eso como preseleccionar una pantalla/ventana especifica de una aplicación. Tenga en cuenta que no siempre funcionará. Eso es porque las ventanas de una app pueden interactuar entre ellas, por ejemplo dar parametros. Si se abre una ventana especifica directamente esta interacción todavia no ha ocurrido y la ventana se podría cerrar al instante (por lo tanto nunca será presentada). Pruebe esto sin embargo! Puede introducir una trayectoria de una activity manualmente, pero es recomendable usar el boton \"Elegir\". Si decide introducir la trayectoria de la app manualmente en la casilla de arriba y la trayectoria completa de una activity en la de abajo.\\n\\n2.Elección con action\\nContrariamente a elegir una ventana especifica, tambien puede encender una app con un action. Es similar a llamar \"Queria xyz\" y si hay una app que le puede ayudar a usted sera encendida. Un ejemplo bueno seria \"abrir browser\" - podria tener multiples (una normalemente es el valor predeterminado). Usted necesita introducirlo manualmente, PackageName es opcional aqui. Tenga en cuenta las variables no seran resueltas. Si por ejemplo quiere encender la camara usando \"MediaStore.ACTION_IMAGE_CAPTURE\" no va a funcionar. Tiene que mirar en la documentación de Android y usar el valor real de esta variable que - en este ejemplo - seria \"android.media.action.IMAGE_CAPTURE\".</string>
|
||||
<string name="tones">Sonidos</string>
|
||||
</resources>
|
@@ -586,4 +586,5 @@
|
||||
<string name="locationFoundInaccurate">È stato possibile solo trovare una ubicazione con una precisione limitata. Potrebbe non funzionare in maniera affidabile. Il raggio minimo per le ubicazioni è di %1$d m.</string>
|
||||
<string name="noLocationCouldBeFound">Nessuna posizione è stata trovata dopo un tempo di attesa di %1$s seconds.</string>
|
||||
<string name="pleaseGiveBgLocation">Nella schermata successiva vai su permessi, poi posizione. Lì seleziona \"Consenti sempre\" per permettere ad Automation di determinare la tua posizione in secondo piano.</string>
|
||||
<string name="tones">Suonerias</string>
|
||||
</resources>
|
||||
|
@@ -507,7 +507,7 @@
|
||||
<string name="phoneIsNotRooted" translatable="false">Phone is not rooted.</string>
|
||||
<string name="dataConWithRootSuccess" translatable="false">Data connection was successfully changed using superuser permissions.</string>
|
||||
<string name="dataConWithRootFail" translatable="false">Data could not be changed using superuser permissions.</string>
|
||||
<string name="rootExplanation">You need to root your phone for this function to work. Afterwards you needs to \"run the rule manually\" to show up the superuser permission question. When the superuser popups shows up you need to always allow the application to do that. Otherwise the rule cannot function when the phone is unattended.</string>
|
||||
<string name="rootExplanation">You need to root your phone for this function to work. Afterwards you need to \"run the rule manually\" to show up the superuser permission question. When the superuser popups shows up you need to always allow the application to do that. Otherwise the rule cannot function when the phone is unattended.</string>
|
||||
<string name="errorWritingConfig">Error writing config. Do you have a writable memory?</string>
|
||||
<string name="phoneNrReplacementError">I could not insert the last phone nr in the variable. I don\'t have it.</string>
|
||||
<string name="username">Username</string>
|
||||
@@ -570,7 +570,7 @@
|
||||
<string name="textMessageAnnotations">You can directly enter a phone number. Alternatively use the contacts option to pick one. But keep in mind: The number will be stored here, not the contact. If you change the phone number of a selected contact you\'ll need to update this rule. It doesn\'t do that by itself.</string>
|
||||
<string name="importNumberFromContacts">Import number from contacts</string>
|
||||
<string name="android9RecordAudioNotice">If you\'re using the noise level trigger: Unfortunately beginning with Android 9 (Pie) Google decided to disallow background applications to use the microphone. So this trigger has no effect anymore and won\'t trigger anything.</string>
|
||||
<string name="android10WifiToggleNotice">Unfortunately Google decided to remove this feature in Android 10. Regular apps are not allowed anymore to turn wifi on or off. This means this action will have no effect on your device.</string>
|
||||
<string name="android10WifiToggleNotice">Unfortunately Google decided to remove this feature in Android 10. Regular apps are not allowed anymore to turn wifi on or off. Only if your device is rooted it should continue to work. If not, then I\'m afraid this won\'t have an effect anymore.</string>
|
||||
<string name="messageNotShownAgain">This message won\'t be shown again.</string>
|
||||
<string name="chooseActivityHint">In this final selection popup you need to select a specific activity. Simplified this is like a window of the desired application. If you do not know which one it is generally a good idea to pick one that has \"main\" or \"launcher\" in its name.</string>
|
||||
<string name="edit">Edit</string>
|
||||
@@ -690,5 +690,20 @@
|
||||
<string name="bottom">Bottom</string>
|
||||
<string name="tabsPlacement">Position of tab bar</string>
|
||||
<string name="tabsPlacementSummary">Choose where the tabs bar should be placed.</string>
|
||||
<string name="wifiApi30">Because Google screwd up yet another part of Android, starting with API 30 only the currently visible wifis can be displayed. Not all the ones your device knows.</string>
|
||||
<string name="wifiApi30">Because Google screwed up yet another part of Android, starting with API 30 only the currently visible wifis can be displayed. Not all the ones your device knows.</string>
|
||||
<string name="smsDialogNotice">If you have not used a send-sms action in this program before, Android may show an additional confirmation dialog, asking you to allow sending messages. You need to select the \"always allow\" checkbox and confirm if you want this action to work in the background. It\'s advised to run this rule manually once.</string>
|
||||
<string name="silentTriggersDnd">REMARK: The silent mode often triggers Do-Not-Disturb on newer devices. If that happens on your device, I recommend using the normal mode instead and lowering all volumes to zero.</string>
|
||||
<string name="tones">Tones</string>
|
||||
<string name="miscellaneous">Miscellaneous</string>
|
||||
<string name="dnd">Do not disturb</string>
|
||||
<string name="dndOff">DND off</string>
|
||||
<string name="dndPriority">Let priority notifications through</string>
|
||||
<string name="dndAlarms">Let alarms through</string>
|
||||
<string name="dndNothing">Let nothing through</string>
|
||||
<string name="dndRemarks">Fine tuning (like allowing phone calls, picking specific numbers, etc.) can only be done from the system\'s settings.</string>
|
||||
<string name="permissionsRequiredNotAvailable">Your rules required permissions which cannot be requested from this installed flavor of Automation.</string>
|
||||
<string name="automationNotificationsIgnored">If you do not choose a specific app, but choose \"Any app\" notifications from Automation will be ignored to avoid loops.</string>
|
||||
<string name="repeatEveryXseconds">Repeat every x seconds</string>
|
||||
<string name="repeatEveryXsecondsWithVariable">repeat every %1$s seconds</string>
|
||||
<string name="enterRepetitionTime">You need to enter a positive non-decimal value for reptition time.</string>
|
||||
</resources>
|
@@ -1,5 +1,6 @@
|
||||
<network-security-config>
|
||||
<base-config>
|
||||
<base-config
|
||||
cleartextTrafficPermitted="true">
|
||||
<trust-anchors>
|
||||
<!-- Trust preinstalled CAs -->
|
||||
<certificates src="system" />
|
||||
|
@@ -5,7 +5,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.0.0'
|
||||
classpath 'com.android.tools.build:gradle:7.0.3'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
1
fastlane/metadata/android/de-DE/changelogs/111.txt
Normal file
1
fastlane/metadata/android/de-DE/changelogs/111.txt
Normal file
@@ -0,0 +1 @@
|
||||
* Klartext HTTP Datenverkehr für URL-Auslösen-Aktionen ermöglicht.
|
4
fastlane/metadata/android/de-DE/changelogs/112.txt
Normal file
4
fastlane/metadata/android/de-DE/changelogs/112.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
* Fehler im Benachrichtigungsauslöser behoben
|
||||
* WLAN Liste laden für API > 30 (soweit möglich)
|
||||
* Fehler behoben beim Hinzufügen/Ändern der SMS-senden Aktion
|
||||
* Fehler behoben beim Ändern der Text-sprechen Aktion
|
6
fastlane/metadata/android/de-DE/changelogs/113.txt
Normal file
6
fastlane/metadata/android/de-DE/changelogs/113.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
* Fehler beim Erstellen einer Text-sprechen-Aktion behoben
|
||||
* Fehler beim Hinzufügen eines Benachrichtigungs-Auslösers behoben
|
||||
* Behoben: "beim Anrufen vibrieren" in Profilen hatte nicht funktioniert
|
||||
* Hinzugefügt: "Nicht stören" kann nun in Profilen gesteuert werden.
|
||||
* USB Router für Android 9 und höher unterstützt (benötigt root)
|
||||
* Berechtigung READ_EXTERNAL_STORAGE für Regeln hinzugefügt, die Tondateien abspielen.
|
@@ -39,7 +39,7 @@ Mögliche Aktionen:
|
||||
* SMS verschicken
|
||||
* Sounddatei abspielen.
|
||||
|
||||
Es ist ziemlich schwierig diese Anwendung über die vielen verschiedenen Geräte sowie die vielen Änderungen an Android Versionen am Laufen zu halten. Ich kann vieles im Emulator testen, aber eben nicht alles.
|
||||
Es ist ziemlich schwierig diese Anwendung über die vielen verschiedenen Geräte und Android Versionen am Laufen zu halten. Ich kann vieles im Emulator testen, aber eben nicht alles.
|
||||
Wenn also eine bestimmte Funktion nicht so tut wie sie sollte - lassen Sie es mich wissen. Über die Jahre habe ich noch alle Fehler behoben, die mir vernünftig gemeldet wurden. Aber dafür bin ich auf Ihre Mithilfe angewiesen.
|
||||
|
||||
Wenn Sie ein Problem mit der Anwendung haben und mich dazu kontaktieren möchten, updaten Sie bitte vorher auf die neueste Version und schauen Sie, ob Ihr Problem darin auch besteht.
|
||||
|
@@ -1 +1 @@
|
||||
Automatisieren Sie Dinge auf Ihrem Ger<EFBFBD>t, indem Sie Regeln anlegen.
|
||||
Automatisieren Sie Dinge auf Ihrem Gerät, indem Sie Regeln anlegen.
|
1
fastlane/metadata/android/en-US/changelogs/111.txt
Normal file
1
fastlane/metadata/android/en-US/changelogs/111.txt
Normal file
@@ -0,0 +1 @@
|
||||
* Allowed cleartext HTTP traffic for rules using triggerUrl action
|
4
fastlane/metadata/android/en-US/changelogs/112.txt
Normal file
4
fastlane/metadata/android/en-US/changelogs/112.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
* Fixed bug in notification trigger.
|
||||
* Load wifi list for API > 30 (as far as possible)
|
||||
* Fixed a bug when adding/editing sendTextMessage action
|
||||
* Fixed a bug when editing speakText action
|
6
fastlane/metadata/android/en-US/changelogs/113.txt
Normal file
6
fastlane/metadata/android/en-US/changelogs/113.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
* Fixed bug in adding speakText action
|
||||
* Fixed bug in adding notification trigger
|
||||
* Fixed: vibrateWhenRinging setting in profiles didn't save nor activate
|
||||
* Added: Do not disturb can be controlled in a profile
|
||||
* Enabled USB Tethering for Android 9 and above (requires root)
|
||||
* Added permission READ_EXTERNAL_STORAGE for rules that play sound files.
|
4
fastlane/metadata/android/es-ES/112.txt
Normal file
4
fastlane/metadata/android/es-ES/112.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
* Error fijo en el gatillo de notificación.
|
||||
* Lista de wifi de carga para API > 30 (en la medida de lo posible)
|
||||
* Arregló un error al añadir/editar sendTextMessage acción
|
||||
* Arregló un error al editar la acción de TalkText
|
1
fastlane/metadata/android/es-ES/changelogs/111.txt
Normal file
1
fastlane/metadata/android/es-ES/changelogs/111.txt
Normal file
@@ -0,0 +1 @@
|
||||
* Permitido texto claro HTTP tráfico para reglas usando el gatillo Acción urgente
|
6
fastlane/metadata/android/es-ES/changelogs/113.txt
Normal file
6
fastlane/metadata/android/es-ES/changelogs/113.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
* Se ha corregido un error al agregar la acción speakText
|
||||
* Se ha corregido un error en la adición de un disparador de notificación
|
||||
* Corregido: vibrarCuando la configuración deRinging en los perfiles no se guardó ni se activó
|
||||
* Añadido: No molestar se puede controlar en un perfil
|
||||
* Conexión USB habilitada para Android 9 y superior (requiere root)
|
||||
* Se agregó permiso READ_EXTERNAL_STORAGE para las reglas que reproducen archivos de sonido.
|
1
fastlane/metadata/android/it-IT/changelogs/111.txt
Normal file
1
fastlane/metadata/android/it-IT/changelogs/111.txt
Normal file
@@ -0,0 +1 @@
|
||||
* Cleartext http traffico dati per le azioni di trigger URL abilitate.
|
4
fastlane/metadata/android/it-IT/changelogs/112.txt
Normal file
4
fastlane/metadata/android/it-IT/changelogs/112.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
* Corretto bug nel trigger di notifica.
|
||||
* Carica elenco wifi per API > 30 (per quanto possibile)
|
||||
* Corretto un bug quando si aggiunge / modifica l'azione di sendTextMessage
|
||||
* Corretto un bug quando si modifica l'azione del testo
|
6
fastlane/metadata/android/it-IT/changelogs/113.txt
Normal file
6
fastlane/metadata/android/it-IT/changelogs/113.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
* Corretto bug nell'aggiunta dell'azione speakText
|
||||
* Risolto bug nell'aggiunta del trigger di notifica
|
||||
* Risolto: vibrazioneQuando l'impostazioneringing nei profili non è stata salvata né attivata
|
||||
* Aggiunto: Non disturbare può essere controllato in un profilo
|
||||
* Tethering USB abilitato per Android 9 e versioni successive (richiede root)
|
||||
* Aggiunta la READ_EXTERNAL_STORAGE delle autorizzazioni per le regole che riproduceno file audio.
|
Reference in New Issue
Block a user