Compare commits

...

62 Commits

Author SHA1 Message Date
e57b888393 Spanish translation 2021-05-13 14:40:23 +02:00
c922f8c7fc Spanish translation 2021-05-13 14:33:44 +02:00
357c7f894f Crash resolved when trying display an unknown translation for a permission 2021-05-13 12:06:15 +02:00
34091a7b73 Spanish translation. 2021-05-13 02:44:10 +02:00
8d26abdede Spanish translation 2021-05-12 22:55:44 +02:00
8d42bb48d2 WG now works. Many small changes and fixes. 2021-05-12 19:41:04 +02:00
f79efa7739 WG now works. Many small changes and fixes. 2021-05-12 17:16:56 +02:00
d257c4ccb0 Miscellaneous changes. 2021-05-11 22:49:41 +02:00
e39f0c2497 startApp changes 2021-05-11 20:00:50 +02:00
327a992cac Synced manifests. 2021-05-11 17:02:06 +02:00
9021b03732 Export/import finished. 2021-05-11 16:53:19 +02:00
514a9ae0e4 Import/export configuration and Spanish translation. 2021-05-10 22:42:16 +02:00
09298bce55 UI changes. 2021-05-10 19:56:54 +02:00
74f50d3e13 Wireguard integration 2021-05-09 23:10:58 +02:00
1946fb6b9f Start app changed 2021-05-09 14:42:24 +02:00
acc0f592d1 Wireguard integration 2021-05-08 22:48:05 +02:00
2e09ea1c90 Start app changed 2021-05-08 19:58:44 +02:00
769f227689 Start app changed 2021-05-08 15:14:31 +02:00
22533fcfee Merge remote-tracking branch 'origin/development' into development 2021-05-08 02:03:18 +02:00
004ca6993a Small fixes and layout corrections. 2021-05-08 02:03:11 +02:00
0bea81a630 Small fixes and layout corrections. 2021-05-08 02:00:57 +02:00
88decce426 Wireguard integration 2021-05-07 23:11:43 +02:00
2341d714c0 Small fixes and layout corrections. 2021-04-30 13:56:59 +02:00
34c0067736 Small fixes and layout corrections. 2021-04-30 13:52:06 +02:00
e0c3b9d450 Changelog updated and translated to German. 2021-04-27 15:51:02 +02:00
c134b15ab1 Changelog updated and translated to German. 2021-04-27 15:43:52 +02:00
6ef6277976 Changelog updated and translated to German. 2021-04-27 15:30:46 +02:00
10e6c74ba8 Cleanups. 2021-04-27 14:49:34 +02:00
b6f3d928ae Cleanups. 2021-04-27 14:42:29 +02:00
e898264178 StartAppChanges 2021-04-20 19:49:19 +02:00
0891dca98d StartAppChanges 2021-04-17 14:52:58 +02:00
8843a97b79 StartAppChanges 2021-04-13 20:00:36 +02:00
1c24af7bcb StartAppChanges 2021-04-11 19:37:50 +02:00
ee43e109da StartAppChanges 2021-04-09 17:39:59 +02:00
13bcb02ffc Notification trigger. 2021-03-30 23:06:07 +02:00
864ed2111e Notification listener finished. 2021-03-30 19:52:31 +02:00
67e6a38ddc Notification listener finished. 2021-03-29 16:36:21 +02:00
fb44196a0d Notification trigger. 2021-03-28 23:23:29 +02:00
7894504791 Notification listener started. 2021-03-28 20:33:44 +02:00
0df5342036 Notification trigger. 2021-03-27 22:52:42 +01:00
712a374adb Notification listener started. 2021-03-27 19:57:39 +01:00
80f8f9cfe2 Notification listener started. 2021-03-27 14:11:39 +01:00
1d9ed8b3ff Notification trigger. 2021-03-26 23:11:36 +01:00
5d6221888a Notification listener started. 2021-03-26 19:58:27 +01:00
56806f0349 Merge remote-tracking branch 'origin/master' into development
# Conflicts:
#	app/googlePlayFlavor/release/output-metadata.json
2021-03-21 22:45:34 +01:00
7127ac45bc Fixed app crashing when saving rules while service is running. 2021-03-21 22:44:40 +01:00
8a43741b21 Merge remote-tracking branch 'origin/master' into development 2021-03-21 22:30:20 +01:00
b7ebf2cdcd Fixed app crashing when saving rules while service is running. 2021-03-21 22:27:34 +01:00
054ab6d84b Background location notifications in googlePlayFlavor 2021-03-20 22:49:05 +01:00
47b56d4978 Google shit again. 2021-03-20 13:37:18 +01:00
d4288dfc40 Google shit again. 2021-03-20 12:18:37 +01:00
01f4bba995 Google shit again. 2021-03-20 12:16:39 +01:00
4a2a1a0550 Google shit again. 2021-03-20 02:44:27 +01:00
a693ced32e Background location notifications in googlePlayFlavor 2021-03-18 23:37:21 +01:00
64d1aec910 Google shit again. 2021-03-18 20:00:19 +01:00
221cfa4339 Trying to fix notifications. 2021-03-17 22:58:58 +01:00
810b6f03aa Google shit again. 2021-03-17 21:42:01 +01:00
4e51cbe36e GooglePlayFlavor location engine disabled notice. 2021-03-16 23:23:42 +01:00
722f9f0e6b News opt-in done. 2021-03-15 23:06:37 +01:00
c1f03d3395 Update 'fastlane/metadata/android/en-US/full_description.txt' 2021-03-14 14:25:35 +01:00
fc71a3548c Migration of old files finished. 2021-03-13 00:54:17 +01:00
e33915b578 Migration of old files finished. 2021-03-13 00:40:59 +01:00
86 changed files with 5238 additions and 2040 deletions

117
.idea/codeStyles/Project.xml generated Normal file
View File

@ -0,0 +1,117 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

10
.idea/runConfigurations.xml generated Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
</set>
</option>
</component>
</project>

View File

@ -11,8 +11,8 @@ android {
compileSdkVersion 29
buildToolsVersion '29.0.2'
useLibrary 'org.apache.http.legacy'
versionCode 98
versionName "1.6.22"
versionCode 104
versionName "1.6.33"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@ -42,7 +42,7 @@ android {
dimension "version"
// applicationIdSuffix ".googlePlay"
versionNameSuffix "-googlePlay"
targetSdkVersion 30
targetSdkVersion 29
}
fdroidFlavor

View File

@ -10,8 +10,8 @@
{
"type": "SINGLE",
"filters": [],
"versionCode": 98,
"versionName": "1.6.22-googlePlay",
"versionCode": 102,
"versionName": "1.6.30-googlePlay",
"outputFile": "app-googlePlayFlavor-release.apk"
}
]

View File

@ -62,8 +62,8 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<!-- Commented out because of Google Play policy -->
<uses-permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"/>
<uses-permission android:name="com.wireguard.android.permission.CONTROL_TUNNELS"/>
<uses-feature
android:name="android.hardware.telephony"
@ -76,7 +76,7 @@
android:allowBackup="true"
android:allowClearUserData="true"
android:icon="@drawable/gears"
android:label="@string/title_activity_main"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:networkSecurityConfig="@xml/network_security_config">
@ -95,15 +95,15 @@
android:label="@string/app_name"></activity>
<activity
android:name=".ActivityManagePoi"
android:label="@string/title_activity_main"></activity>
android:label="@string/app_name"></activity>
<activity
android:name=".ActivitySettings"
android:label="@string/title_activity_main"></activity>
android:label="@string/app_name"></activity>
<service
android:name=".AutomationService"
android:exported="false"
android:label="@string/title_activity_main" />
android:label="@string/app_name" />
<receiver android:name=".receivers.StartupIntentReceiver" android:enabled="true" android:exported="true">
<intent-filter>
@ -136,10 +136,14 @@
<receiver android:name=".receivers.TimeZoneListener" />
<activity android:name=".ActivityManageRule" />
<activity android:name=".ActivityEditTriggerUrl" />
<activity android:name=".ActivityEditSendTextMessage" />
<activity android:name=".ActivityManageTimeFrame" />
<activity android:name=".ActivityManageBrightnessSetting" />
<activity android:name=".ActivityManageActionTriggerUrl" />
<activity android:name=".ActivityDisplayLongMessage" />
<activity android:name=".ActivityManageActionSendTextMessage" />
<activity android:name=".ActivityManageActionPlaySound" />
<activity android:name=".ActivityManageTriggerTimeFrame" />
<activity android:name=".ActivityMaintenance" />
<activity android:name=".ActivityTriggerPhoneCall" />
<activity android:name=".ActivityManageActionBrightnessSetting" />
<activity android:name=".ActivityHelp" />
<activity
android:name=".ActivityMainTabLayout"
@ -177,13 +181,26 @@
<activity android:name=".ActivityMainPoi" />
<activity android:name=".ActivityMainRules" />
<activity android:name=".ActivityGeneric" />
<activity android:name=".ActivityManageStartActivity" />
<activity android:name=".ActivityManageNfc" />
<activity android:name=".ActivityEditSpeakText" />
<activity android:name=".ActivityManageBluetoothTrigger" />
<activity android:name=".ActivityManageActionStartActivity" />
<activity android:name=".ActivityManageTriggerNfc" />
<activity android:name=".ActivityManageActionSpeakText" />
<activity android:name=".ActivityManageActionPlaySound" />
<activity android:name=".ActivityManageTriggerBluetooth" />
<activity android:name=".ActivityMainProfiles" />
<activity android:name=".ActivityManageProfile" />
<activity android:name=".ActivityVolumeTest" />
<activity android:name=".ActivityPermissions"></activity>
<activity android:name=".ActivityManageTriggerNotification"></activity>
<service
android:name=".receivers.NotificationListener"
android:label="@string/app_name"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" >
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
<service
android:name=".receivers.ActivityDetectionReceiver"
@ -202,6 +219,13 @@
<service android:name=".location.GeofenceIntentService"/>
<provider
android:name=".FileShareProvider"
android:authorities="com.jens.automation2"
android:exported="true"
/>
</application>
</manifest>

View File

@ -1,13 +1,17 @@
package com.jens.automation2;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Looper;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.widget.Toast;
import com.google.android.gms.location.DetectedActivity;
import com.jens.automation2.location.LocationProvider;
import com.jens.automation2.location.WifiBroadcastReceiver;
import com.jens.automation2.receivers.ActivityDetectionReceiver;
import com.jens.automation2.receivers.BatteryReceiver;
@ -16,6 +20,7 @@ import com.jens.automation2.receivers.ConnectivityReceiver;
import com.jens.automation2.receivers.HeadphoneJackListener;
import com.jens.automation2.receivers.NfcReceiver;
import com.jens.automation2.receivers.NoiseListener;
import com.jens.automation2.receivers.NotificationListener;
import com.jens.automation2.receivers.PhoneStatusListener;
import com.jens.automation2.receivers.ProcessListener;
@ -24,6 +29,10 @@ import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
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;
public class Rule implements Comparable<Rule>
{
@ -42,9 +51,20 @@ public class Rule implements Comparable<Rule>
private String name;
private boolean ruleActive = true; // rules can be deactivated, so they won't fire if you don't want them temporarily
private boolean ruleToggle = false; // rule will run again and do the opposite of its actions if applicable
private Calendar lastExecution;
private static Date lastActivatedRuleActivationTime;
public Calendar getLastExecution()
{
return lastExecution;
}
public void setLastExecution(Calendar lastExecution)
{
this.lastExecution = lastExecution;
}
public boolean isRuleToggle()
{
return ruleToggle;
@ -114,6 +134,7 @@ public class Rule implements Comparable<Rule>
{
return this.getName();
}
@SuppressLint("NewApi")
public String toStringLong()
{
String returnString = "";
@ -469,7 +490,7 @@ public class Rule implements Comparable<Rule>
{
if(oneTrigger.getTriggerParameter())
{
if(BatteryReceiver.getBatteryLevel() < oneTrigger.getBatteryLevel())
if(BatteryReceiver.getBatteryLevel() <= oneTrigger.getBatteryLevel())
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryLowerThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3);
return false;
@ -477,7 +498,7 @@ public class Rule implements Comparable<Rule>
}
else
{
if(BatteryReceiver.getBatteryLevel() > oneTrigger.getBatteryLevel())
if(oneTrigger.getBatteryLevel() >= oneTrigger.getBatteryLevel())
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryHigherThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3);
return false;
@ -488,7 +509,7 @@ public class Rule implements Comparable<Rule>
{
if(oneTrigger.getTriggerParameter())
{
if(com.jens.automation2.location.LocationProvider.getSpeed() < oneTrigger.getSpeed())
if(LocationProvider.getSpeed() < oneTrigger.getSpeed())
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreSlowerThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3);
return false;
@ -496,7 +517,7 @@ public class Rule implements Comparable<Rule>
}
else
{
if(com.jens.automation2.location.LocationProvider.getSpeed() > oneTrigger.getSpeed())
if(LocationProvider.getSpeed() > oneTrigger.getSpeed())
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreFasterThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3);
return false;
@ -648,109 +669,69 @@ public class Rule implements Comparable<Rule>
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.bluetoothConnection))
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Checking for bluetooth...", 4);
// if( // connected / disconnected
// (oneTrigger.getTriggerParameter() && (BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_CONNECTED) | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACL_CONNECTED")))
// |
// (!oneTrigger.getTriggerParameter() && (BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED) | BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECTED) | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACTION_ACL_DISCONNECT_REQUESTED") | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACTION_ACL_DISCONNECTED")))
// )
// {
// if(oneTrigger.getBluetoothDeviceAddress() != null)
// {
// if(oneTrigger.getBluetoothDeviceAddress().equals("<any>"))
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "No bluetooth address specified, any will do.", 4);
// }
// else if(oneTrigger.getBluetoothDeviceAddress().equals("<none>"))
// {
// // ???
// }
// else
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Bluetooth address specified, checking that.", 4);
// if(!BluetoothReceiver.getLastAffectedDevice().getAddress().equals(oneTrigger.getBluetoothDeviceAddress()))
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyNotTheCorrectDeviceAddress), 3);
// return false;
// }
// else
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Bluetooth address matches. Rule will apply.", 4);
// }
// }
// }
// else if(BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_FOUND) | BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_FOUND))
// {
// if(!oneTrigger.getTriggerParameter())
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyDeviceInRangeButShouldNotBe), 3);
// return false;
// }
// }
// else // above only checks for last action, this checks for things in the past
if(oneTrigger.getBluetoothDeviceAddress().equals("<any>"))
{
if(oneTrigger.getBluetoothDeviceAddress().equals("<any>"))
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isAnyDeviceInRange() != oneTrigger.getTriggerParameter())
return false;
}
}
else if(oneTrigger.getBluetoothDeviceAddress().equals("<none>"))
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isAnyDeviceInRange() == oneTrigger.getTriggerParameter())
return false;
}
if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
return false;
}
else if(oneTrigger.getBluetoothDeviceAddress().length() > 0)
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isDeviceInRange(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
return false;
}
else
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyStateNotCorrect), 3);
return false;
// range
if(BluetoothReceiver.isAnyDeviceInRange() != oneTrigger.getTriggerParameter())
return false;
}
}
else if(oneTrigger.getBluetoothDeviceAddress().equals("<none>"))
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isAnyDeviceInRange() == oneTrigger.getTriggerParameter())
return false;
}
}
else if(oneTrigger.getBluetoothDeviceAddress().length() > 0)
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isDeviceInRange(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
}
else
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyStateNotCorrect), 3);
return false;
}
}
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.headsetPlugged))
{
@ -763,6 +744,109 @@ public class Rule implements Comparable<Rule>
return false;
}
}
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.notification))
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
{
String[] params = oneTrigger.getTriggerParameter2().split(triggerParameter2Split);
String myApp = params[0];
String myTitleDir = params[1];
String myTitle = params[2];
String myTextDir = params[3];
String myText;
if (params.length >= 5)
myText = params[4];
else
myText = "";
if(oneTrigger.getTriggerParameter())
{
// Check an active notification that is still there
boolean foundMatch = false;
for (StatusBarNotification sbn : NotificationListener.getInstance().getActiveNotifications())
{
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);
Miscellaneous.logEvent("i", "NotificationCheck", "Checking if this notification matches our rule " + this.getName() + ". App: " + app + ", title: " + title + ", text: " + text, 5);
if (!myApp.equals("-1"))
{
if (!app.equalsIgnoreCase(myApp))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification app name does not match rule.", 5);
continue;
}
}
if (myTitle.length() > 0)
{
if (!Miscellaneous.compare(myTitleDir, myTitle, title))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification title does not match rule.", 5);
continue;
}
}
if (myText.length() > 0)
{
if (!Miscellaneous.compare(myTextDir, myText, text))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification text does not match rule.", 5);
continue;
}
}
foundMatch = true;
break;
}
}
if(!foundMatch)
return false;
}
else
{
// check a notification that is gone
if(NotificationListener.getLastNotification() != null)
{
if(!NotificationListener.getLastNotification().isCreated())
{
String app = NotificationListener.getLastNotification().getApp();
String title = NotificationListener.getLastNotification().getTitle();
String text = NotificationListener.getLastNotification().getText();
if (!myApp.equals("-1"))
{
if (!app.equalsIgnoreCase(myApp))
return false;
}
if (myTitle.length() > 0)
{
if (!Miscellaneous.compare(myTitleDir, title, myTitle))
return false;
}
if (myText.length() > 0)
{
if (!Miscellaneous.compare(myTextDir, text, myText))
return false;
}
}
else
return false;
}
}
}
}
}
return true;
@ -774,6 +858,8 @@ public class Rule implements Comparable<Rule>
private class ActivateRuleTask extends AsyncTask<Object, String, Void>
{
boolean wasActivated = false;
@Override
protected Void doInBackground(Object... params)
{
@ -788,7 +874,7 @@ public class Rule implements Comparable<Rule>
if (Looper.myLooper() == null)
Looper.prepare();
activateInternally((AutomationService)params[0], (Boolean)params[1]);
wasActivated = activateInternally((AutomationService)params[0], (Boolean)params[1]);
return null;
}
@ -806,27 +892,35 @@ public class Rule implements Comparable<Rule>
@Override
protected void onPostExecute(Void result)
{
AutomationService.updateNotification();
ActivityMainScreen.updateMainScreen();
super.onPostExecute(result);
}
/*
Only update if the rules was actually executed. Became necessary for the notification trigger. If a user created a rule
with a notification trigger and this app creates a notification itself this will otherwise end in an infinite loop.
*/
if(wasActivated)
{
setLastExecution(Calendar.getInstance());
AutomationService.updateNotification();
ActivityMainScreen.updateMainScreen();
super.onPostExecute(result);
}
}
/**
* Will activate the rule. Should be called by a separate execution thread
* @param automationService
*/
protected void activateInternally(AutomationService automationService, boolean force)
protected boolean activateInternally(AutomationService automationService, boolean force)
{
boolean isActuallyToggable = isActuallyToggable();
boolean notLastActive = getLastActivatedRule() == null || !getLastActivatedRule().equals(Rule.this);
boolean doToggle = ruleToggle && isActuallyToggable;
if(notLastActive | force | doToggle)
{
String message;
if(!doToggle)
message = String.format(automationService.getResources().getString(R.string.ruleActivate), Rule.this.getName());
message = String.format(automationService.getResources().getString(R.string.ruleActivate), Rule.this.getName());
else
message = String.format(automationService.getResources().getString(R.string.ruleActivateToggle), Rule.this.getName());
Miscellaneous.logEvent("i", "Rule", message, 2);
@ -834,10 +928,19 @@ public class Rule implements Comparable<Rule>
// Toast.makeText(automationService, message, Toast.LENGTH_LONG).show();
if(Settings.startNewThreadForRuleActivation)
publishProgress(message);
for(int i = 0; i< Rule.this.getActionSet().size(); i++)
Rule.this.getActionSet().get(i).run(automationService, doToggle);
{
try
{
Rule.this.getActionSet().get(i).run(automationService, doToggle);
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "RuleExecution", "Error running action of rule " + Rule.this.getName() + ": " + Log.getStackTraceString(e), 1);
}
}
// Keep log of last x rule activations (Settings)
try
{
@ -862,9 +965,12 @@ public class Rule implements Comparable<Rule>
else
{
Miscellaneous.logEvent("i", "Rule", "Request to activate rule " + Rule.this.getName() + ", but it is the last one that was activated. Won't do it again.", 3);
return false;
}
}
}
return true;
}
}
public void activate(AutomationService automationService, boolean force)
{
@ -1279,6 +1385,26 @@ public class Rule implements Comparable<Rule>
return ruleCandidates;
}
public static ArrayList<Rule> findRuleCandidates(Trigger.Trigger_Enum triggerType)
{
ArrayList<Rule> ruleCandidates = new ArrayList<Rule>();
for(Rule oneRule : ruleCollection)
{
innerloop:
for(Trigger oneTrigger : oneRule.getTriggerSet())
{
if(oneTrigger.getTriggerType() == triggerType)
{
ruleCandidates.add(oneRule);
break innerloop; //we don't need to search the other triggers in the same rule
}
}
}
return ruleCandidates;
}
public static ArrayList<Rule> findRuleCandidatesByActivityDetection()
{

View File

@ -60,6 +60,8 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"/>
<uses-permission android:name="com.wireguard.android.permission.CONTROL_TUNNELS"/>
<uses-feature
android:name="android.hardware.telephony"
@ -72,7 +74,7 @@
android:allowBackup="true"
android:allowClearUserData="true"
android:icon="@drawable/gears"
android:label="@string/title_activity_main"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:networkSecurityConfig="@xml/network_security_config">
@ -91,15 +93,15 @@
android:label="@string/app_name"></activity>
<activity
android:name=".ActivityManagePoi"
android:label="@string/title_activity_main"></activity>
android:label="@string/app_name"></activity>
<activity
android:name=".ActivitySettings"
android:label="@string/title_activity_main"></activity>
android:label="@string/app_name"></activity>
<service
android:name=".AutomationService"
android:exported="false"
android:label="@string/title_activity_main" />
android:label="@string/app_name" />
<receiver android:name=".receivers.StartupIntentReceiver" android:enabled="true" android:exported="true">
<intent-filter>
@ -132,10 +134,14 @@
<receiver android:name=".receivers.TimeZoneListener" />
<activity android:name=".ActivityManageRule" />
<activity android:name=".ActivityEditTriggerUrl" />
<activity android:name=".ActivityEditSendTextMessage" />
<activity android:name=".ActivityManageTimeFrame" />
<activity android:name=".ActivityManageBrightnessSetting" />
<activity android:name=".ActivityManageActionTriggerUrl" />
<activity android:name=".ActivityDisplayLongMessage" />
<activity android:name=".ActivityManageActionSendTextMessage" />
<activity android:name=".ActivityManageActionPlaySound" />
<activity android:name=".ActivityManageTriggerTimeFrame" />
<activity android:name=".ActivityMaintenance" />
<activity android:name=".ActivityTriggerPhoneCall" />
<activity android:name=".ActivityManageActionBrightnessSetting" />
<activity android:name=".ActivityHelp" />
<activity
android:name=".ActivityMainTabLayout"
@ -172,18 +178,41 @@
<activity android:name=".ActivityMainPoi" />
<activity android:name=".ActivityMainRules" />
<activity android:name=".ActivityGeneric" />
<activity android:name=".ActivityManageStartActivity" />
<activity android:name=".ActivityManageNfc" />
<activity android:name=".ActivityEditSpeakText" />
<activity android:name=".ActivityManageBluetoothTrigger" />
<activity android:name=".ActivityManageActionStartActivity" />
<activity android:name=".ActivityManageTriggerNfc" />
<activity android:name=".ActivityManageActionSpeakText" />
<activity android:name=".ActivityManageActionPlaySound" />
<activity android:name=".ActivityManageTriggerBluetooth" />
<activity android:name=".ActivityMainProfiles" />
<activity android:name=".ActivityManageProfile" />
<activity android:name=".ActivityVolumeTest" />
<activity android:name=".ActivityPermissions"></activity>
<!-- https://developer.android.com/about/versions/pie/android-9.0-changes-28#apache-p-->
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
<service android:name=".location.GeofenceIntentService"/>
<activity android:name=".ActivityManageTriggerNotification" />
<service
android:name=".receivers.NotificationListener"
android:label="@string/app_name"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" >
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
<provider
android:name=".FileShareProvider"
android:authorities="com.jens.automation2"
android:exported="true"
/>
</application>
</manifest>

View File

@ -1,12 +1,16 @@
package com.jens.automation2;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Looper;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.widget.Toast;
import com.jens.automation2.location.LocationProvider;
import com.jens.automation2.location.WifiBroadcastReceiver;
import com.jens.automation2.receivers.BatteryReceiver;
import com.jens.automation2.receivers.BluetoothReceiver;
@ -14,6 +18,7 @@ import com.jens.automation2.receivers.ConnectivityReceiver;
import com.jens.automation2.receivers.HeadphoneJackListener;
import com.jens.automation2.receivers.NfcReceiver;
import com.jens.automation2.receivers.NoiseListener;
import com.jens.automation2.receivers.NotificationListener;
import com.jens.automation2.receivers.PhoneStatusListener;
import com.jens.automation2.receivers.ProcessListener;
@ -22,6 +27,9 @@ import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
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;
public class Rule implements Comparable<Rule>
{
@ -40,8 +48,19 @@ public class Rule implements Comparable<Rule>
private String name;
private boolean ruleActive = true; // rules can be deactivated, so they won't fire if you don't want them temporarily
private boolean ruleToggle = false; // rule will run again and do the opposite of its actions if applicable
private Calendar lastExecution;
private static Date lastActivatedRuleActivationTime;
public Calendar getLastExecution()
{
return lastExecution;
}
public void setLastExecution(Calendar lastExecution)
{
this.lastExecution = lastExecution;
}
public boolean isRuleToggle()
{
@ -112,6 +131,7 @@ public class Rule implements Comparable<Rule>
{
return this.getName();
}
@SuppressLint("NewApi")
public String toStringLong()
{
String returnString = "";
@ -467,7 +487,7 @@ public class Rule implements Comparable<Rule>
{
if(oneTrigger.getTriggerParameter())
{
if(BatteryReceiver.getBatteryLevel() < oneTrigger.getBatteryLevel())
if(BatteryReceiver.getBatteryLevel() <= oneTrigger.getBatteryLevel())
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryLowerThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3);
return false;
@ -475,7 +495,7 @@ public class Rule implements Comparable<Rule>
}
else
{
if(BatteryReceiver.getBatteryLevel() > oneTrigger.getBatteryLevel())
if(oneTrigger.getBatteryLevel() >= oneTrigger.getBatteryLevel())
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryHigherThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3);
return false;
@ -486,7 +506,7 @@ public class Rule implements Comparable<Rule>
{
if(oneTrigger.getTriggerParameter())
{
if(com.jens.automation2.location.LocationProvider.getSpeed() < oneTrigger.getSpeed())
if(LocationProvider.getSpeed() < oneTrigger.getSpeed())
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreSlowerThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3);
return false;
@ -494,7 +514,7 @@ public class Rule implements Comparable<Rule>
}
else
{
if(com.jens.automation2.location.LocationProvider.getSpeed() > oneTrigger.getSpeed())
if(LocationProvider.getSpeed() > oneTrigger.getSpeed())
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreFasterThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3);
return false;
@ -617,109 +637,69 @@ public class Rule implements Comparable<Rule>
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.bluetoothConnection))
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Checking for bluetooth...", 4);
// if( // connected / disconnected
// (oneTrigger.getTriggerParameter() && (BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_CONNECTED) | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACL_CONNECTED")))
// |
// (!oneTrigger.getTriggerParameter() && (BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED) | BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECTED) | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACTION_ACL_DISCONNECT_REQUESTED") | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACTION_ACL_DISCONNECTED")))
// )
// {
// if(oneTrigger.getBluetoothDeviceAddress() != null)
// {
// if(oneTrigger.getBluetoothDeviceAddress().equals("<any>"))
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "No bluetooth address specified, any will do.", 4);
// }
// else if(oneTrigger.getBluetoothDeviceAddress().equals("<none>"))
// {
// // ???
// }
// else
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Bluetooth address specified, checking that.", 4);
// if(!BluetoothReceiver.getLastAffectedDevice().getAddress().equals(oneTrigger.getBluetoothDeviceAddress()))
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyNotTheCorrectDeviceAddress), 3);
// return false;
// }
// else
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Bluetooth address matches. Rule will apply.", 4);
// }
// }
// }
// else if(BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_FOUND) | BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_FOUND))
// {
// if(!oneTrigger.getTriggerParameter())
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyDeviceInRangeButShouldNotBe), 3);
// return false;
// }
// }
// else // above only checks for last action, this checks for things in the past
if(oneTrigger.getBluetoothDeviceAddress().equals("<any>"))
{
if(oneTrigger.getBluetoothDeviceAddress().equals("<any>"))
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isAnyDeviceInRange() != oneTrigger.getTriggerParameter())
return false;
}
}
else if(oneTrigger.getBluetoothDeviceAddress().equals("<none>"))
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isAnyDeviceInRange() == oneTrigger.getTriggerParameter())
return false;
}
if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
return false;
}
else if(oneTrigger.getBluetoothDeviceAddress().length() > 0)
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isDeviceInRange(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
return false;
}
else
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyStateNotCorrect), 3);
return false;
// range
if(BluetoothReceiver.isAnyDeviceInRange() != oneTrigger.getTriggerParameter())
return false;
}
}
else if(oneTrigger.getBluetoothDeviceAddress().equals("<none>"))
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isAnyDeviceInRange() == oneTrigger.getTriggerParameter())
return false;
}
}
else if(oneTrigger.getBluetoothDeviceAddress().length() > 0)
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isDeviceInRange(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
}
else
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyStateNotCorrect), 3);
return false;
}
}
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.headsetPlugged))
{
@ -732,6 +712,108 @@ public class Rule implements Comparable<Rule>
return false;
}
}
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.notification))
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
{
String[] params = oneTrigger.getTriggerParameter2().split(triggerParameter2Split);
String myApp = params[0];
String myTitleDir = params[1];
String myTitle = params[2];
String myTextDir = params[3];
String myText;
if (params.length >= 5)
myText = params[4];
else
myText = "";
if(oneTrigger.getTriggerParameter())
{
// Check an active notification that is still there
boolean foundMatch = false;
for (StatusBarNotification sbn : NotificationListener.getInstance().getActiveNotifications())
{
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);
Miscellaneous.logEvent("i", "NotificationCheck", "Checking if this notification matches our rule " + this.getName() + ". App: " + app + ", title: " + title + ", text: " + text, 5);
if (!myApp.equals("-1"))
{
if (!app.equalsIgnoreCase(myApp))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification app name does not match rule.", 5);
continue;
}
}
if (myTitle.length() > 0)
{
if (!Miscellaneous.compare(myTitleDir, myTitle, title))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification title does not match rule.", 5);
continue;
}
}
if (myText.length() > 0)
{
if (!Miscellaneous.compare(myTextDir, myText, text))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification text does not match rule.", 5);
continue;
}
}
foundMatch = true;
}
}
if(!foundMatch)
return false;
}
else
{
// check a notification that is gone
if(NotificationListener.getLastNotification() != null)
{
if(!NotificationListener.getLastNotification().isCreated())
{
String app = NotificationListener.getLastNotification().getApp();
String title = NotificationListener.getLastNotification().getTitle();
String text = NotificationListener.getLastNotification().getText();
if (!myApp.equals("-1"))
{
if (!app.equalsIgnoreCase(myApp))
return false;
}
if (myTitle.length() > 0)
{
if (!Miscellaneous.compare(myTitleDir, title, myTitle))
return false;
}
if (myText.length() > 0)
{
if (!Miscellaneous.compare(myTextDir, text, myText))
return false;
}
}
else
return false;
}
}
}
}
}
return true;
@ -743,6 +825,8 @@ public class Rule implements Comparable<Rule>
private class ActivateRuleTask extends AsyncTask<Object, String, Void>
{
boolean wasActivated = false;
@Override
protected Void doInBackground(Object... params)
{
@ -756,8 +840,8 @@ public class Rule implements Comparable<Rule>
if (Looper.myLooper() == null)
Looper.prepare();
activateInternally((AutomationService)params[0], (Boolean)params[1]);
wasActivated = activateInternally((AutomationService)params[0], (Boolean)params[1]);
return null;
}
@ -775,16 +859,24 @@ public class Rule implements Comparable<Rule>
@Override
protected void onPostExecute(Void result)
{
AutomationService.updateNotification();
ActivityMainScreen.updateMainScreen();
super.onPostExecute(result);
/*
Only update if the rules was actually executed. Became necessary for the notification trigger. If a user created a rule
with a notification trigger and this app creates a notification itself this will otherwise end in an infinite loop.
*/
if(wasActivated)
{
setLastExecution(Calendar.getInstance());
AutomationService.updateNotification();
ActivityMainScreen.updateMainScreen();
super.onPostExecute(result);
}
}
/**
* Will activate the rule. Should be called by a separate execution thread
* @param automationService
*/
protected void activateInternally(AutomationService automationService, boolean force)
protected boolean activateInternally(AutomationService automationService, boolean force)
{
boolean isActuallyToggable = isActuallyToggable();
@ -805,8 +897,17 @@ public class Rule implements Comparable<Rule>
publishProgress(message);
for(int i = 0; i< Rule.this.getActionSet().size(); i++)
Rule.this.getActionSet().get(i).run(automationService, doToggle);
{
try
{
Rule.this.getActionSet().get(i).run(automationService, doToggle);
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "RuleExecution", "Error running action of rule " + Rule.this.getName() + ": " + Log.getStackTraceString(e), 1);
}
}
// Keep log of last x rule activations (Settings)
try
{
@ -831,9 +932,12 @@ public class Rule implements Comparable<Rule>
else
{
Miscellaneous.logEvent("i", "Rule", "Request to activate rule " + Rule.this.getName() + ", but it is the last one that was activated. Won't do it again.", 3);
return false;
}
}
}
return true;
}
}
public void activate(AutomationService automationService, boolean force)
{
@ -1248,6 +1352,26 @@ public class Rule implements Comparable<Rule>
return ruleCandidates;
}
public static ArrayList<Rule> findRuleCandidates(Trigger.Trigger_Enum triggerType)
{
ArrayList<Rule> ruleCandidates = new ArrayList<Rule>();
for(Rule oneRule : ruleCollection)
{
innerloop:
for(Trigger oneTrigger : oneRule.getTriggerSet())
{
if(oneTrigger.getTriggerType() == triggerType)
{
ruleCandidates.add(oneRule);
break innerloop; //we don't need to search the other triggers in the same rule
}
}
}
return ruleCandidates;
}
public static ArrayList<Rule> findRuleCandidatesByActivityDetection()
{

View File

@ -36,7 +36,6 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
@ -62,21 +61,14 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<!-- Commented out because of Google Play policy -->
<!--
<uses-feature
android:name="android.hardware.telephony"
android:required="false" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.SEND_SMS"/>
-->
<uses-permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"/>
<uses-permission android:name="com.wireguard.android.permission.CONTROL_TUNNELS"/>
<application
android:allowBackup="true"
android:allowClearUserData="true"
android:icon="@drawable/gears"
android:label="@string/title_activity_main"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:networkSecurityConfig="@xml/network_security_config">
@ -95,15 +87,15 @@
android:label="@string/app_name"></activity>
<activity
android:name=".ActivityManagePoi"
android:label="@string/title_activity_main"></activity>
android:label="@string/app_name"></activity>
<activity
android:name=".ActivitySettings"
android:label="@string/title_activity_main"></activity>
android:label="@string/app_name"></activity>
<service
android:name=".AutomationService"
android:exported="false"
android:label="@string/title_activity_main" />
android:label="@string/app_name" />
<receiver android:name=".receivers.StartupIntentReceiver" android:enabled="true" android:exported="true">
<intent-filter>
@ -136,10 +128,14 @@
<receiver android:name=".receivers.TimeZoneListener" />
<activity android:name=".ActivityManageRule" />
<activity android:name=".ActivityEditTriggerUrl" />
<activity android:name=".ActivityEditSendTextMessage" />
<activity android:name=".ActivityManageTimeFrame" />
<activity android:name=".ActivityManageBrightnessSetting" />
<activity android:name=".ActivityManageActionTriggerUrl" />
<activity android:name=".ActivityDisplayLongMessage" />
<activity android:name=".ActivityManageActionSendTextMessage" />
<activity android:name=".ActivityManageActionPlaySound" />
<activity android:name=".ActivityManageTriggerTimeFrame" />
<activity android:name=".ActivityMaintenance" />
<activity android:name=".ActivityTriggerPhoneCall" />
<activity android:name=".ActivityManageActionBrightnessSetting" />
<activity android:name=".ActivityHelp" />
<activity
android:name=".ActivityMainTabLayout"
@ -177,13 +173,26 @@
<activity android:name=".ActivityMainPoi" />
<activity android:name=".ActivityMainRules" />
<activity android:name=".ActivityGeneric" />
<activity android:name=".ActivityManageStartActivity" />
<activity android:name=".ActivityManageNfc" />
<activity android:name=".ActivityEditSpeakText" />
<activity android:name=".ActivityManageBluetoothTrigger" />
<activity android:name=".ActivityManageActionStartActivity" />
<activity android:name=".ActivityManageTriggerNfc" />
<activity android:name=".ActivityManageActionSpeakText" />
<activity android:name=".ActivityManageActionPlaySound" />
<activity android:name=".ActivityManageTriggerBluetooth" />
<activity android:name=".ActivityMainProfiles" />
<activity android:name=".ActivityManageProfile" />
<activity android:name=".ActivityVolumeTest" />
<activity android:name=".ActivityPermissions"></activity>
<activity android:name=".ActivityManageTriggerNotification"></activity>
<service
android:name=".receivers.NotificationListener"
android:label="@string/app_name"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" >
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
<service
android:name=".receivers.ActivityDetectionReceiver"
@ -196,9 +205,18 @@
<activity android:name=".ActivityPermissions"></activity>
<!-- https://developer.android.com/about/versions/pie/android-9.0-changes-28#apache-p-->
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
<service android:name=".location.GeofenceIntentService"/>
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
<provider
android:name=".FileShareProvider"
android:authorities="com.jens.automation2"
android:exported="true"
/>
</application>

View File

@ -1,13 +1,17 @@
package com.jens.automation2;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Looper;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.widget.Toast;
import com.google.android.gms.location.DetectedActivity;
import com.jens.automation2.location.LocationProvider;
import com.jens.automation2.location.WifiBroadcastReceiver;
import com.jens.automation2.receivers.ActivityDetectionReceiver;
import com.jens.automation2.receivers.BatteryReceiver;
@ -16,6 +20,7 @@ import com.jens.automation2.receivers.ConnectivityReceiver;
import com.jens.automation2.receivers.HeadphoneJackListener;
import com.jens.automation2.receivers.NfcReceiver;
import com.jens.automation2.receivers.NoiseListener;
import com.jens.automation2.receivers.NotificationListener;
import com.jens.automation2.receivers.PhoneStatusListener;
import com.jens.automation2.receivers.ProcessListener;
@ -24,6 +29,9 @@ import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
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;
public class Rule implements Comparable<Rule>
{
@ -42,8 +50,19 @@ public class Rule implements Comparable<Rule>
private String name;
private boolean ruleActive = true; // rules can be deactivated, so they won't fire if you don't want them temporarily
private boolean ruleToggle = false; // rule will run again and do the opposite of its actions if applicable
private Calendar lastExecution;
private static Date lastActivatedRuleActivationTime;
public Calendar getLastExecution()
{
return lastExecution;
}
public void setLastExecution(Calendar lastExecution)
{
this.lastExecution = lastExecution;
}
public boolean isRuleToggle()
{
@ -114,6 +133,7 @@ public class Rule implements Comparable<Rule>
{
return this.getName();
}
@SuppressLint("NewApi")
public String toStringLong()
{
String returnString = "";
@ -469,7 +489,7 @@ public class Rule implements Comparable<Rule>
{
if(oneTrigger.getTriggerParameter())
{
if(BatteryReceiver.getBatteryLevel() < oneTrigger.getBatteryLevel())
if(BatteryReceiver.getBatteryLevel() <= oneTrigger.getBatteryLevel())
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryLowerThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3);
return false;
@ -477,7 +497,7 @@ public class Rule implements Comparable<Rule>
}
else
{
if(BatteryReceiver.getBatteryLevel() > oneTrigger.getBatteryLevel())
if(oneTrigger.getBatteryLevel() >= oneTrigger.getBatteryLevel())
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyBatteryHigherThan) + " " + String.valueOf(oneTrigger.getBatteryLevel()), 3);
return false;
@ -488,7 +508,7 @@ public class Rule implements Comparable<Rule>
{
if(oneTrigger.getTriggerParameter())
{
if(com.jens.automation2.location.LocationProvider.getSpeed() < oneTrigger.getSpeed())
if(LocationProvider.getSpeed() < oneTrigger.getSpeed())
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreSlowerThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3);
return false;
@ -496,7 +516,7 @@ public class Rule implements Comparable<Rule>
}
else
{
if(com.jens.automation2.location.LocationProvider.getSpeed() > oneTrigger.getSpeed())
if(LocationProvider.getSpeed() > oneTrigger.getSpeed())
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyWeAreFasterThan) + " " + String.valueOf(oneTrigger.getSpeed()), 3);
return false;
@ -648,109 +668,69 @@ public class Rule implements Comparable<Rule>
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.bluetoothConnection))
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Checking for bluetooth...", 4);
// if( // connected / disconnected
// (oneTrigger.getTriggerParameter() && (BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_CONNECTED) | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACL_CONNECTED")))
// |
// (!oneTrigger.getTriggerParameter() && (BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED) | BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_ACL_DISCONNECTED) | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACTION_ACL_DISCONNECT_REQUESTED") | BluetoothReceiver.getLastAction().equals("android.bluetooth.device.action.ACTION_ACL_DISCONNECTED")))
// )
// {
// if(oneTrigger.getBluetoothDeviceAddress() != null)
// {
// if(oneTrigger.getBluetoothDeviceAddress().equals("<any>"))
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "No bluetooth address specified, any will do.", 4);
// }
// else if(oneTrigger.getBluetoothDeviceAddress().equals("<none>"))
// {
// // ???
// }
// else
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Bluetooth address specified, checking that.", 4);
// if(!BluetoothReceiver.getLastAffectedDevice().getAddress().equals(oneTrigger.getBluetoothDeviceAddress()))
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyNotTheCorrectDeviceAddress), 3);
// return false;
// }
// else
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), "Bluetooth address matches. Rule will apply.", 4);
// }
// }
// }
// else if(BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_FOUND) | BluetoothReceiver.getLastAction().equals(android.bluetooth.BluetoothDevice.ACTION_FOUND))
// {
// if(!oneTrigger.getTriggerParameter())
// {
// Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyDeviceInRangeButShouldNotBe), 3);
// return false;
// }
// }
// else // above only checks for last action, this checks for things in the past
if(oneTrigger.getBluetoothDeviceAddress().equals("<any>"))
{
if(oneTrigger.getBluetoothDeviceAddress().equals("<any>"))
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isAnyDeviceInRange() != oneTrigger.getTriggerParameter())
return false;
}
}
else if(oneTrigger.getBluetoothDeviceAddress().equals("<none>"))
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isAnyDeviceInRange() == oneTrigger.getTriggerParameter())
return false;
}
if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
return false;
}
else if(oneTrigger.getBluetoothDeviceAddress().length() > 0)
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isDeviceInRange(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
if(BluetoothReceiver.isAnyDeviceConnected() != oneTrigger.getTriggerParameter())
return false;
}
else
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyStateNotCorrect), 3);
return false;
// range
if(BluetoothReceiver.isAnyDeviceInRange() != oneTrigger.getTriggerParameter())
return false;
}
}
else if(oneTrigger.getBluetoothDeviceAddress().equals("<none>"))
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isAnyDeviceConnected() == oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isAnyDeviceInRange() == oneTrigger.getTriggerParameter())
return false;
}
}
else if(oneTrigger.getBluetoothDeviceAddress().length() > 0)
{
if(oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_CONNECTED))
{
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else if((oneTrigger.getBluetoothEvent().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)))
{
if(BluetoothReceiver.isDeviceCurrentlyConnected(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
else
{
// range
if(BluetoothReceiver.isDeviceInRange(BluetoothReceiver.getDeviceByAddress(oneTrigger.getBluetoothDeviceAddress())) != oneTrigger.getTriggerParameter())
return false;
}
}
else
{
Miscellaneous.logEvent("i", String.format(context.getResources().getString(R.string.ruleCheckOf), this.getName()), context.getResources().getString(R.string.ruleDoesntApplyStateNotCorrect), 3);
return false;
}
}
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.headsetPlugged))
{
@ -763,6 +743,109 @@ public class Rule implements Comparable<Rule>
return false;
}
}
else if(oneTrigger.getTriggerType().equals(Trigger.Trigger_Enum.notification))
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
{
String[] params = oneTrigger.getTriggerParameter2().split(triggerParameter2Split);
String myApp = params[0];
String myTitleDir = params[1];
String myTitle = params[2];
String myTextDir = params[3];
String myText;
if (params.length >= 5)
myText = params[4];
else
myText = "";
if(oneTrigger.getTriggerParameter())
{
// Check an active notification that is still there
boolean foundMatch = false;
for (StatusBarNotification sbn : NotificationListener.getInstance().getActiveNotifications())
{
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);
Miscellaneous.logEvent("i", "NotificationCheck", "Checking if this notification matches our rule " + this.getName() + ". App: " + app + ", title: " + title + ", text: " + text, 5);
if (!myApp.equals("-1"))
{
if (!app.equalsIgnoreCase(myApp))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification app name does not match rule.", 5);
continue;
}
}
if (myTitle.length() > 0)
{
if (!Miscellaneous.compare(myTitleDir, myTitle, title))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification title does not match rule.", 5);
continue;
}
}
if (myText.length() > 0)
{
if (!Miscellaneous.compare(myTextDir, myText, text))
{
Miscellaneous.logEvent("i", "NotificationCheck", "Notification text does not match rule.", 5);
continue;
}
}
foundMatch = true;
break;
}
}
if(!foundMatch)
return false;
}
else
{
// check a notification that is gone
if(NotificationListener.getLastNotification() != null)
{
if(!NotificationListener.getLastNotification().isCreated())
{
String app = NotificationListener.getLastNotification().getApp();
String title = NotificationListener.getLastNotification().getTitle();
String text = NotificationListener.getLastNotification().getText();
if (!myApp.equals("-1"))
{
if (!app.equalsIgnoreCase(myApp))
return false;
}
if (myTitle.length() > 0)
{
if (!Miscellaneous.compare(myTitleDir, title, myTitle))
return false;
}
if (myText.length() > 0)
{
if (!Miscellaneous.compare(myTextDir, text, myText))
return false;
}
}
else
return false;
}
}
}
}
}
return true;
@ -774,6 +857,8 @@ public class Rule implements Comparable<Rule>
private class ActivateRuleTask extends AsyncTask<Object, String, Void>
{
boolean wasActivated = false;
@Override
protected Void doInBackground(Object... params)
{
@ -787,8 +872,8 @@ public class Rule implements Comparable<Rule>
if (Looper.myLooper() == null)
Looper.prepare();
activateInternally((AutomationService)params[0], (Boolean)params[1]);
wasActivated = activateInternally((AutomationService)params[0], (Boolean)params[1]);
return null;
}
@ -806,27 +891,35 @@ public class Rule implements Comparable<Rule>
@Override
protected void onPostExecute(Void result)
{
AutomationService.updateNotification();
ActivityMainScreen.updateMainScreen();
super.onPostExecute(result);
}
/*
Only update if the rules was actually executed. Became necessary for the notification trigger. If a user created a rule
with a notification trigger and this app creates a notification itself this will otherwise end in an infinite loop.
*/
if(wasActivated)
{
setLastExecution(Calendar.getInstance());
AutomationService.updateNotification();
ActivityMainScreen.updateMainScreen();
super.onPostExecute(result);
}
}
/**
* Will activate the rule. Should be called by a separate execution thread
* @param automationService
*/
protected void activateInternally(AutomationService automationService, boolean force)
protected boolean activateInternally(AutomationService automationService, boolean force)
{
boolean isActuallyToggable = isActuallyToggable();
boolean notLastActive = getLastActivatedRule() == null || !getLastActivatedRule().equals(Rule.this);
boolean doToggle = ruleToggle && isActuallyToggable;
if(notLastActive | force | doToggle)
{
String message;
if(!doToggle)
message = String.format(automationService.getResources().getString(R.string.ruleActivate), Rule.this.getName());
message = String.format(automationService.getResources().getString(R.string.ruleActivate), Rule.this.getName());
else
message = String.format(automationService.getResources().getString(R.string.ruleActivateToggle), Rule.this.getName());
Miscellaneous.logEvent("i", "Rule", message, 2);
@ -834,10 +927,19 @@ public class Rule implements Comparable<Rule>
// Toast.makeText(automationService, message, Toast.LENGTH_LONG).show();
if(Settings.startNewThreadForRuleActivation)
publishProgress(message);
for(int i = 0; i< Rule.this.getActionSet().size(); i++)
Rule.this.getActionSet().get(i).run(automationService, doToggle);
{
try
{
Rule.this.getActionSet().get(i).run(automationService, doToggle);
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "RuleExecution", "Error running action of rule " + Rule.this.getName() + ": " + Log.getStackTraceString(e), 1);
}
}
// Keep log of last x rule activations (Settings)
try
{
@ -862,9 +964,12 @@ public class Rule implements Comparable<Rule>
else
{
Miscellaneous.logEvent("i", "Rule", "Request to activate rule " + Rule.this.getName() + ", but it is the last one that was activated. Won't do it again.", 3);
return false;
}
}
}
return true;
}
}
public void activate(AutomationService automationService, boolean force)
{
@ -1279,6 +1384,26 @@ public class Rule implements Comparable<Rule>
return ruleCandidates;
}
public static ArrayList<Rule> findRuleCandidates(Trigger.Trigger_Enum triggerType)
{
ArrayList<Rule> ruleCandidates = new ArrayList<Rule>();
for(Rule oneRule : ruleCollection)
{
innerloop:
for(Trigger oneTrigger : oneRule.getTriggerSet())
{
if(oneTrigger.getTriggerType() == triggerType)
{
ruleCandidates.add(oneRule);
break innerloop; //we don't need to search the other triggers in the same rule
}
}
}
return ruleCandidates;
}
public static ArrayList<Rule> findRuleCandidatesByActivityDetection()
{

View File

@ -3,6 +3,7 @@ package com.jens.automation2.receivers;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
@ -13,6 +14,7 @@ import com.google.android.gms.location.ActivityRecognition;
import com.google.android.gms.location.ActivityRecognitionApi;
import com.google.android.gms.location.ActivityRecognitionResult;
import com.google.android.gms.location.DetectedActivity;
import com.jens.automation2.ActivityDisplayLongMessage;
import com.jens.automation2.ActivityPermissions;
import com.jens.automation2.AutomationService;
import com.jens.automation2.Miscellaneous;
@ -110,6 +112,14 @@ public class ActivityDetectionReceiver extends IntentService implements Automati
}
public static void startActivityDetectionReceiver()
{
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
if(!ActivityPermissions.havePermission("android.permission.ACTIVITY_RECOGNITION", Miscellaneous.getAnyContext()))
{
Miscellaneous.logEvent("w", "Activity Detection", "Don't have android.permission.ACTIVITY_RECOGNITION. Aborting receiver start..", 2);
return;
}
}
try
{
Miscellaneous.logEvent("i", "ActivityDetectionReceiver", "Starting ActivityDetectionReceiver", 3);

View File

@ -1,195 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jens.automation2">
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true" />
<!-- android:xlargeScreens="true" -->
<uses-feature
android:name="android.hardware.location"
android:required="false" />
<uses-feature
android:name="android.hardware.location.gps"
android:required="false" />
<uses-feature
android:name="android.hardware.location.network"
android:required="false" />
<uses-feature
android:name="android.hardware.bluetooth"
android:required="false" />
<uses-feature
android:name="android.hardware.microphone"
android:required="false" />
<uses-feature
android:name="android.hardware.wifi"
android:required="false" />
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
<uses-feature
android:name="android.hardware.nfc"
android:required="false" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.BATTERY_STATS" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_SUPERUSER" />
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<!-- Commented out because of Google Play policy -->
<!--
<uses-feature
android:name="android.hardware.telephony"
android:required="false" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.SEND_SMS"/>
-->
<application
android:allowBackup="true"
android:allowClearUserData="true"
android:icon="@drawable/gears"
android:label="@string/title_activity_main"
android:theme="@style/AppTheme"
android:networkSecurityConfig="@xml/network_security_config">
<meta-data
android:name="firebase_analytics_collection_deactivated"
android:value="true" />
<meta-data
android:name="google_analytics_adid_collection_enabled"
android:value="false" />
<meta-data
android:name="google_analytics_ssaid_collection_enabled"
android:value="false" />
<activity
android:name=".ActivityMainScreen"
android:label="@string/app_name"></activity>
<activity
android:name=".ActivityManagePoi"
android:label="@string/title_activity_main"></activity>
<activity
android:name=".ActivitySettings"
android:label="@string/title_activity_main"></activity>
<service
android:name=".AutomationService"
android:exported="false"
android:label="@string/title_activity_main" />
<receiver android:name=".receivers.StartupIntentReceiver" android:enabled="true" android:exported="true">
<intent-filter>
<!--<action android:name="android.intent.action.SCREEN_ON" />-->
<!--<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />-->
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.REBOOT"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<receiver android:name=".receivers.PackageReplacedReceiver"
android:enabled="true">
<intent-filter>
<!--<action android:name="android.intent.action.PACKAGE_ADDED"/>
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<action android:name="android.intent.action.PACKAGE_REMOVED"/>
<action android:name="android.intent.action.ACTION_PACKAGE_REPLACED" />-->
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
<!--<data
android:path="com.jens.automation2"
android:scheme="package" />-->
</intent-filter>
</receiver>
<receiver android:name=".receivers.AlarmListener" />
<receiver android:name=".receivers.ConnectivityReceiver" />
<receiver android:name=".receivers.TimeZoneListener" />
<activity android:name=".ActivityManageRule" />
<activity android:name=".ActivityEditTriggerUrl" />
<activity android:name=".ActivityEditSendTextMessage" />
<activity android:name=".ActivityManageTimeFrame" />
<activity android:name=".ActivityManageBrightnessSetting" />
<activity android:name=".ActivityHelp" />
<activity
android:name=".ActivityMainTabLayout"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<!-- <action android:name="android.nfc.action.TECH_DISCOVERED"/> -->
<!-- <action android:name="android.nfc.action.TAG_DISCOVERED"/> -->
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
<!-- <data android:mimeType="application/com.jens.automation2" /> -->
</intent-filter>
<!--
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/com.jens.automation2" />
</intent-filter>
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.jens.automation2">
<!--
<meta-data
android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="@xml/nfc_tech_filter" />
-->
</activity>
<activity android:name=".ActivityMainPoi" />
<activity android:name=".ActivityMainRules" />
<activity android:name=".ActivityGeneric" />
<activity android:name=".ActivityManageStartActivity" />
<activity android:name=".ActivityManageNfc" />
<activity android:name=".ActivityEditSpeakText" />
<activity android:name=".ActivityManageBluetoothTrigger" />
<activity android:name=".ActivityMainProfiles" />
<activity android:name=".ActivityManageProfile" />
<activity android:name=".ActivityVolumeTest" />
<activity android:name=".ActivityPermissions"></activity>
<!-- https://developer.android.com/about/versions/pie/android-9.0-changes-28#apache-p-->
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
</application>
</manifest>

View File

@ -2,7 +2,9 @@ package com.jens.automation2;
import android.content.Context;
import android.os.AsyncTask;
import android.text.style.TabStopSpan;
import android.util.Log;
import android.widget.Toast;
import org.apache.http.client.methods.HttpGet;
@ -12,6 +14,9 @@ import java.util.Locale;
public class Action
{
public static final String actionParameter2Split = "ap2split";
public static final String intentPairSeperator = "intPairSplit";
public enum Action_Enum {
setWifi,
setBluetooth,
@ -33,6 +38,7 @@ public class Action
speakText,
playMusic,
setScreenBrightness,
playSound,
sendTextMessage;
public String getFullName(Context context)
@ -87,6 +93,8 @@ public class Action
return context.getResources().getString(R.string.actionSpeakText);
case playMusic:
return context.getResources().getString(R.string.actionPlayMusic);
case playSound:
return context.getResources().getString(R.string.playSound);
case sendTextMessage:
return context.getResources().getString(R.string.sendTextMessage);
case setScreenBrightness:
@ -210,9 +218,13 @@ public class Action
{
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.wakeupDevice));
}
else if(this.getAction().equals(Action_Enum.playSound))
{
returnString.append(Miscellaneous.getAnyContext().getResources().getString(R.string.playSound));
}
else
returnString.append(action.toString());
if(this.getAction().equals(Action_Enum.triggerUrl))
{
String[] components = parameter2.split(";");
@ -226,6 +238,10 @@ public class Action
else
returnString.append(": " + components[0]);
}
else if(this.getAction().equals(Action_Enum.startOtherActivity))
{
returnString.append(": " + parameter2.replace(Action.intentPairSeperator, "/"));
}
else if(this.getAction().equals(Action_Enum.sendTextMessage))
{
String[] components = parameter2.split(Actions.smsSeparator);
@ -320,14 +336,16 @@ public class Action
}
public void run(Context context, boolean toggleActionIfPossible)
{
switch(this.getAction())
{
try
{
case changeSoundProfile:
/*
* Old version. Those checks should not be necessary anymore. Also they didn't work
* because profiles were created with names like silent, vibrate and normal.
*/
switch(this.getAction())
{
case changeSoundProfile:
/*
* Old version. Those checks should not be necessary anymore. Also they didn't work
* because profiles were created with names like silent, vibrate and normal.
*/
// if(this.getParameter2().equals("silent"))
// Actions.setSound(context, AudioManager.RINGER_MODE_SILENT);
// else if(this.getParameter2().equals("vibrate"))
@ -337,67 +355,76 @@ public class Action
// else
// {
Profile p = Profile.getByName(this.getParameter2());
if(p != null)
if (p != null)
p.activate(context);
// }
break;
case triggerUrl:
triggerUrl(context);
break;
case setBluetooth:
Actions.setBluetooth(context, getParameter1(), toggleActionIfPossible);
break;
case setUsbTethering:
Actions.setUsbTethering(context, getParameter1(), toggleActionIfPossible);
break;
case setWifi:
Actions.setWifi(context, getParameter1(), toggleActionIfPossible);
break;
case setWifiTethering:
Actions.setWifiTethering(context, getParameter1(), toggleActionIfPossible);
break;
case setDisplayRotation:
Actions.setDisplayRotation(context, getParameter1(), toggleActionIfPossible);
break;
case startOtherActivity:
Actions.startOtherActivity(getParameter2());
break;
case waitBeforeNextAction:
Actions.waitBeforeNextAction(Long.parseLong(this.getParameter2()));
break;
case wakeupDevice:
Actions.wakeupDevice(Long.parseLong(this.getParameter2()));
// wakeupDevice() will create a seperate thread. That'll take some time, we wait 100ms.
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
break;
case setAirplaneMode:
Actions.setAirplaneMode(this.getParameter1(), toggleActionIfPossible);
break;
case setDataConnection:
Actions.MobileDataStuff.setDataConnection(this.getParameter1(), toggleActionIfPossible);
break;
case speakText:
Actions.speakText(this.getParameter2());
break;
case playMusic:
Actions.playMusic(this.getParameter1(), toggleActionIfPossible);
break;
case sendTextMessage:
Actions.sendTextMessage(context, this.getParameter2().split(Actions.smsSeparator));
break;
case setScreenBrightness:
Actions.setScreenBrightness(getParameter1(), Integer.parseInt(getParameter2()));
break;
default:
Miscellaneous.logEvent("w", "Action", context.getResources().getString(R.string.unknownActionSpecified), 3);
break;
break;
case triggerUrl:
triggerUrl(context);
break;
case setBluetooth:
Actions.setBluetooth(context, getParameter1(), toggleActionIfPossible);
break;
case setUsbTethering:
Actions.setUsbTethering(context, getParameter1(), toggleActionIfPossible);
break;
case setWifi:
Actions.setWifi(context, getParameter1(), toggleActionIfPossible);
break;
case setWifiTethering:
Actions.setWifiTethering(context, getParameter1(), toggleActionIfPossible);
break;
case setDisplayRotation:
Actions.setDisplayRotation(context, getParameter1(), toggleActionIfPossible);
break;
case startOtherActivity:
Actions.startOtherActivity(getParameter1(), getParameter2());
break;
case waitBeforeNextAction:
Actions.waitBeforeNextAction(Long.parseLong(this.getParameter2()));
break;
case wakeupDevice:
Actions.wakeupDevice(Long.parseLong(this.getParameter2()));
// wakeupDevice() will create a seperate thread. That'll take some time, we wait 100ms.
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
break;
case setAirplaneMode:
Actions.setAirplaneMode(this.getParameter1(), toggleActionIfPossible);
break;
case setDataConnection:
Actions.MobileDataStuff.setDataConnection(this.getParameter1(), toggleActionIfPossible);
break;
case speakText:
Actions.speakText(this.getParameter2());
break;
case playMusic:
Actions.playMusic(this.getParameter1(), toggleActionIfPossible);
break;
case sendTextMessage:
Actions.sendTextMessage(context, this.getParameter2().split(Actions.smsSeparator));
break;
case setScreenBrightness:
Actions.setScreenBrightness(getParameter1(), Integer.parseInt(getParameter2()));
break;
case playSound:
Actions.playSound(getParameter1(), getParameter2());
break;
default:
Miscellaneous.logEvent("w", "Action", context.getResources().getString(R.string.unknownActionSpecified), 3);
break;
}
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "ActionExecution", Log.getStackTraceString(e), 1);
Toast.makeText(context, context.getResources().getString(R.string.errorRunningRule), Toast.LENGTH_LONG).show();
}
}

View File

@ -8,7 +8,9 @@ import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.PowerManager;
@ -32,6 +34,7 @@ import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.util.InetAddressUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.InetAddress;
@ -53,7 +56,12 @@ public class Actions
private static String suVersion = null;
private static String suVersionInternal = null;
private static List<String> suResult = null;
final static String smsSeparator = "&sms&";
public final static String smsSeparator = "&sms&";
public final static String dummyPackageString = "dummyPkg239asd";
public static final String wireguard_tunnel_up = "com.wireguard.android.action.SET_TUNNEL_UP";
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)
{
@ -465,6 +473,45 @@ public class Actions
return "";
}
public static void playSound(boolean alwaysPlay, String soundFileLocation)
{
if(alwaysPlay || ((AudioManager) Miscellaneous.getAnyContext().getSystemService(Context.AUDIO_SERVICE)).getRingerMode() == AudioManager.RINGER_MODE_NORMAL)
{
MediaPlayer mp = new MediaPlayer();
try
{
File file = new File(soundFileLocation);
if(file.exists())
{
Uri fileUri = Uri.parse(soundFileLocation);
mp.setLooping(false);
mp.setDataSource(Miscellaneous.getAnyContext(), fileUri);
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener()
{
@Override
public void onCompletion(MediaPlayer mp)
{
mp.release();
}
});
mp.prepare();
mp.start();
}
else
{
Miscellaneous.logEvent("w", "Play sound file", "Sound file " + soundFileLocation + " does not exist. Can't play it.", 2);
Toast.makeText(context, String.format(context.getResources().getString(R.string.cantFindSoundFile), soundFileLocation), Toast.LENGTH_SHORT).show();
}
}
catch (Exception e)
{
Miscellaneous.logEvent("e", "Play sound file", "Error playing sound: " + Log.getStackTraceString(e), 2);
}
}
else
Miscellaneous.logEvent("i", "Play sound file", "Not playing sound file because phone is on some kind of mute state.", 2);
}
public void useDownloadedWebpage(String result)
{
// Toast.makeText(context, "Result: " + result, Toast.LENGTH_LONG).show();
@ -522,28 +569,62 @@ public class Actions
}
}
public static void startOtherActivity(String param)
public static void startOtherActivity(boolean startByAction, String param)
{
Miscellaneous.logEvent("i", "StartOtherActivity", "Starting other Activity...", 4);
String packageName, className;
String params[] = param.split(";");
packageName = params[0];
className = params[1];
try
{
Miscellaneous.logEvent("i", "StartOtherApp", "Starting " + packageName + " " + className, 3);
Intent externalActivityIntent = new Intent(Intent.ACTION_MAIN);
externalActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
externalActivityIntent.addCategory(Intent.CATEGORY_LAUNCHER);
externalActivityIntent.setClassName(packageName, className);
Intent externalActivityIntent;
int paramsStartIndex;
if(!startByAction)
{
// selected by activity
String packageName, className;
packageName = params[0];
className = params[1];
Miscellaneous.logEvent("i", "StartOtherApp", "Starting app by activity: " + packageName + " " + className, 3);
paramsStartIndex = 2;
externalActivityIntent = new Intent(Intent.ACTION_MAIN);
externalActivityIntent.addCategory(Intent.CATEGORY_LAUNCHER);
// if(packageName.equals("dummyPkg"))
// externalActivityIntent.setAction(className);
// else
externalActivityIntent.setClassName(packageName, className);
if(!Miscellaneous.doesActivityExist(externalActivityIntent, Miscellaneous.getAnyContext()))
Miscellaneous.logEvent("w", "StartOtherApp", "Activity not found: " + className, 2);
}
else
{
// selected by action
Miscellaneous.logEvent("i", "StartOtherApp", "Starting app by action: " + param, 3);
externalActivityIntent = new Intent();
if(!params[0].equals(dummyPackageString))
externalActivityIntent.setPackage(params[0]);
externalActivityIntent.setAction(params[1]);
}
externalActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// Pack intents
for (int i = 3; i < params.length; i++)
{
String[] singleParam = params[i].split(Action.intentPairSeperator);
// has intent values to deliver
for(int i=2; i<params.length; i++)
{
String[] singleParam = params[i].split("/");
/*Class c = Class.forName(singleParam[0]);
for(Method m : c.getMethods())
{
@ -554,63 +635,79 @@ public class Actions
}
}*/
if(singleParam[0].equals("boolean"))
{
Miscellaneous.logEvent("i", "StartOtherApp" , "Adding parameter of type " + singleParam[0] + " with name " + singleParam[1] + " and value " + singleParam[2], 3);
externalActivityIntent.putExtra(singleParam[1], Boolean.parseBoolean(singleParam[2]));
}
else if(singleParam[0].equals("byte"))
{
Miscellaneous.logEvent("i", "StartOtherApp" , "Adding parameter of type " + singleParam[0] + " with name " + singleParam[1] + " and value " + singleParam[2], 3);
externalActivityIntent.putExtra(singleParam[1], Byte.parseByte(singleParam[2]));
}
else if(singleParam[0].equals("char"))
{
Miscellaneous.logEvent("i", "StartOtherApp" , "Adding parameter of type " + singleParam[0] + " with name " + singleParam[1] + " and value " + singleParam[2], 3);
externalActivityIntent.putExtra(singleParam[1], singleParam[2].charAt(0));
}
else if(singleParam[0].equals("CharSequence"))
{
Miscellaneous.logEvent("i", "StartOtherApp" , "Adding parameter of type " + singleParam[0] + " with name " + singleParam[1] + " and value " + singleParam[2], 3);
externalActivityIntent.putExtra(singleParam[1], (CharSequence)singleParam[2]);
}
else if(singleParam[0].equals("double"))
{
Miscellaneous.logEvent("i", "StartOtherApp" , "Adding parameter of type " + singleParam[0] + " with name " + singleParam[1] + " and value " + singleParam[2], 3);
externalActivityIntent.putExtra(singleParam[1], Double.parseDouble(singleParam[2]));
}
else if(singleParam[0].equals("float"))
{
Miscellaneous.logEvent("i", "StartOtherApp" , "Adding parameter of type " + singleParam[0] + " with name " + singleParam[1] + " and value " + singleParam[2], 3);
externalActivityIntent.putExtra(singleParam[1], Float.parseFloat(singleParam[2]));
}
else if(singleParam[0].equals("int"))
if (singleParam[0].equals("boolean"))
{
Miscellaneous.logEvent("i", "StartOtherApp" , "Adding parameter of type " + singleParam[0] + " with name " + singleParam[1] + " and value " + singleParam[2], 3);
externalActivityIntent.putExtra(singleParam[1], Integer.parseInt(singleParam[2]));
Miscellaneous.logEvent("i", "StartOtherApp", "Adding parameter of type " + singleParam[0] + " with name " + singleParam[1] + " and value " + singleParam[2], 3);
externalActivityIntent.putExtra(singleParam[1], Boolean.parseBoolean(singleParam[2]));
}
else if(singleParam[0].equals("long"))
else if (singleParam[0].equals("byte"))
{
Miscellaneous.logEvent("i", "StartOtherApp" , "Adding parameter of type " + singleParam[0] + " with name " + singleParam[1] + " and value " + singleParam[2], 3);
externalActivityIntent.putExtra(singleParam[1], Long.parseLong(singleParam[2]));
Miscellaneous.logEvent("i", "StartOtherApp", "Adding parameter of type " + singleParam[0] + " with name " + singleParam[1] + " and value " + singleParam[2], 3);
externalActivityIntent.putExtra(singleParam[1], Byte.parseByte(singleParam[2]));
}
else if(singleParam[0].equals("short"))
else if (singleParam[0].equals("char"))
{
Miscellaneous.logEvent("i", "StartOtherApp" , "Adding parameter of type " + singleParam[0] + " with name " + singleParam[1] + " and value " + singleParam[2], 3);
externalActivityIntent.putExtra(singleParam[1], Short.parseShort(singleParam[2]));
Miscellaneous.logEvent("i", "StartOtherApp", "Adding parameter of type " + singleParam[0] + " with name " + singleParam[1] + " and value " + singleParam[2], 3);
externalActivityIntent.putExtra(singleParam[1], singleParam[2].charAt(0));
}
else if(singleParam[0].equals("String"))
else if (singleParam[0].equals("CharSequence"))
{
Miscellaneous.logEvent("i", "StartOtherApp" , "Adding parameter of type " + singleParam[0] + " with name " + singleParam[1] + " and value " + singleParam[2], 3);
externalActivityIntent.putExtra(singleParam[1], singleParam[2]);
Miscellaneous.logEvent("i", "StartOtherApp", "Adding parameter of type " + singleParam[0] + " with name " + singleParam[1] + " and value " + singleParam[2], 3);
externalActivityIntent.putExtra(singleParam[1], (CharSequence) singleParam[2]);
}
else
Miscellaneous.logEvent("w", "StartOtherApp" , "Unknown type of parameter " + singleParam[0] + " found. Name " + singleParam[1] + " and value " + singleParam[2], 3);
}
else if (singleParam[0].equals("double"))
{
Miscellaneous.logEvent("i", "StartOtherApp", "Adding parameter of type " + singleParam[0] + " with name " + singleParam[1] + " and value " + singleParam[2], 3);
externalActivityIntent.putExtra(singleParam[1], Double.parseDouble(singleParam[2]));
}
else if (singleParam[0].equals("float"))
{
Miscellaneous.logEvent("i", "StartOtherApp", "Adding parameter of type " + singleParam[0] + " with name " + singleParam[1] + " and value " + singleParam[2], 3);
externalActivityIntent.putExtra(singleParam[1], Float.parseFloat(singleParam[2]));
}
else if (singleParam[0].equals("int"))
{
Miscellaneous.logEvent("i", "StartOtherApp", "Adding parameter of type " + singleParam[0] + " with name " + singleParam[1] + " and value " + singleParam[2], 3);
externalActivityIntent.putExtra(singleParam[1], Integer.parseInt(singleParam[2]));
}
else if (singleParam[0].equals("long"))
{
Miscellaneous.logEvent("i", "StartOtherApp", "Adding parameter of type " + singleParam[0] + " with name " + singleParam[1] + " and value " + singleParam[2], 3);
externalActivityIntent.putExtra(singleParam[1], Long.parseLong(singleParam[2]));
}
else if (singleParam[0].equals("short"))
{
Miscellaneous.logEvent("i", "StartOtherApp", "Adding parameter of type " + singleParam[0] + " with name " + singleParam[1] + " and value " + singleParam[2], 3);
externalActivityIntent.putExtra(singleParam[1], Short.parseShort(singleParam[2]));
}
else if (singleParam[0].equals("Uri"))
{
if(singleParam[1].equalsIgnoreCase("IntentData"))
{
Miscellaneous.logEvent("i", "StartOtherApp", "Adding parameter of type " + singleParam[0] + " with value " + singleParam[2] + " as standard data parameter.", 3);
externalActivityIntent.setData(Uri.parse(singleParam[2]));
}
else
{
Miscellaneous.logEvent("i", "StartOtherApp", "Adding parameter of type " + singleParam[0] + " with name " + singleParam[1] + " and value " + singleParam[2], 3);
externalActivityIntent.putExtra(singleParam[1], Uri.parse(singleParam[2]));
}
}
else if (singleParam[0].equals("String"))
{
Miscellaneous.logEvent("i", "StartOtherApp", "Adding parameter of type " + singleParam[0] + " with name " + singleParam[1] + " and value " + singleParam[2], 3);
externalActivityIntent.putExtra(singleParam[1], singleParam[2]);
}
else
Miscellaneous.logEvent("w", "StartOtherApp", "Unknown type of parameter " + singleParam[0] + " found. Name " + singleParam[1] + " and value " + singleParam[2], 3);
}
autoMationServerRef.startActivity(externalActivityIntent);
if(params[2].equals(ActivityManageActionStartActivity.startByActivityString))
autoMationServerRef.startActivity(externalActivityIntent);
else
autoMationServerRef.sendBroadcast(externalActivityIntent);
}
catch(ActivityNotFoundException | SecurityException e)
catch(Exception e)
{
Miscellaneous.logEvent("e", "StartOtherApp", autoMationServerRef.getResources().getString(R.string.errorStartingOtherActivity) + ": " + Log.getStackTraceString(e), 2);
Toast.makeText(autoMationServerRef, autoMationServerRef.getResources().getString(R.string.errorStartingOtherActivity) + ": " + e.getMessage(), Toast.LENGTH_LONG).show();
@ -1009,7 +1106,7 @@ public class Actions
if(enable)
desiredState = 1;
if(MobileDataStuff.setMobileNetworkfromLollipop(desiredState, autoMationServerRef))
if(MobileDataStuff.setMobileNetworkFromLollipop(desiredState, autoMationServerRef))
{
Miscellaneous.logEvent("i", "setDataConnectionWithRoot()", Miscellaneous.getAnyContext().getResources().getString(R.string.dataConWithRootSuccess), 2);
return true;
@ -1034,7 +1131,7 @@ public class Actions
}
@SuppressLint("NewApi")
public static boolean setMobileNetworkfromLollipop(int desiredState, Context context) throws Exception
public static boolean setMobileNetworkFromLollipop(int desiredState, Context context) throws Exception
{
String command = null;
int state = 0;

View File

@ -0,0 +1,56 @@
package com.jens.automation2;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.Html;
import android.view.View;
import android.widget.QuickContactBadge;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.core.text.HtmlCompat;
public class ActivityDisplayLongMessage extends Activity
{
TextView tvMessageTitle, tvLongMessage, tvMessageLink;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_display_long_message);
tvMessageTitle = (TextView)findViewById(R.id.tvMessageTitle);
tvLongMessage = (TextView)findViewById(R.id.tvLongMessage);
tvMessageLink = (TextView)findViewById(R.id.tvMessageLink);
String title = getIntent().getStringExtra("messageTitle");
String message = getIntent().getStringExtra("longMessage").replace("\\n", Miscellaneous.lineSeparator);
String link = null;
if(getIntent().hasExtra("messageLink"))
link = getIntent().getStringExtra("messageLink");
tvMessageTitle.setText(HtmlCompat.fromHtml(title, HtmlCompat.FROM_HTML_MODE_LEGACY));
tvLongMessage.setText(message);
if(link != null && link.length() > 0)
{
tvMessageLink.setText(HtmlCompat.fromHtml("<u>" + link + "</u>", HtmlCompat.FROM_HTML_MODE_LEGACY));
String finalLink = link;
tvMessageLink.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View view)
{
Uri uriUrl = Uri.parse(finalLink);
Intent launchBrowser = new Intent(Intent.ACTION_VIEW, uriUrl);
startActivity(launchBrowser);
}
});
}
}
}

View File

@ -53,23 +53,25 @@ public class ActivityMainPoi extends ActivityGeneric
@Override
public void onClick(View v)
{
// if(!ActivityPermissions.havePermission(ActivityPermissions.writeExternalStoragePermissionName, ActivityMainPoi.this))
// {
// Toast.makeText(ActivityMainPoi.this, getResources().getString(R.string.appRequiresPermissiontoAccessExternalStorage), Toast.LENGTH_LONG).show();
// return;
// }
if(!ActivityPermissions.havePermission(ActivityPermissions.permissionNameLocationCoarse, ActivityMainPoi.this) || !ActivityPermissions.havePermission(ActivityPermissions.permissionNameLocationFine, ActivityMainPoi.this))
if(Miscellaneous.googleToBlameForLocation(false))
{
Intent permissionIntent = new Intent(ActivityMainPoi.this, ActivityPermissions.class);
permissionIntent.putExtra(ActivityPermissions.intentExtraName, new String[] { ActivityPermissions.permissionNameLocationCoarse, ActivityPermissions.permissionNameLocationFine });
startActivityForResult(permissionIntent, requestCodeForPermission);
ActivityMainScreen.openGoogleBlamingWindow();
return;
}
else
{
buttonAddPoi();
if (!ActivityPermissions.havePermission(ActivityPermissions.permissionNameLocationCoarse, ActivityMainPoi.this) || !ActivityPermissions.havePermission(ActivityPermissions.permissionNameLocationFine, ActivityMainPoi.this))
{
Intent permissionIntent = new Intent(ActivityMainPoi.this, ActivityPermissions.class);
permissionIntent.putExtra(ActivityPermissions.intentExtraName, new String[]{ActivityPermissions.permissionNameLocationCoarse, ActivityPermissions.permissionNameLocationFine});
startActivityForResult(permissionIntent, requestCodeForPermission);
}
else
{
buttonAddPoi();
}
}
}
});

View File

@ -2,14 +2,15 @@ package com.jens.automation2;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.StrictMode;
import android.util.Log;
import android.util.Xml;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
@ -29,6 +30,7 @@ import com.jens.automation2.AutomationService.serviceCommands;
import com.jens.automation2.Trigger.Trigger_Enum;
import com.jens.automation2.location.LocationProvider;
import java.io.File;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Calendar;
@ -40,8 +42,8 @@ public class ActivityMainScreen extends ActivityGeneric
private static ActivityMainScreen activityMainScreenInstance = null;
private ToggleButton toggleService, tbLockSound;
private Button bShowHelp, bPrivacy, bSettingsErase, bSettingsSetToDefault, bVolumeTest, bAddSoundLockTIme;
private TextView tvActivePoi, tvClosestPoi, tvLastRule, tvMainScreenNote1, tvMainScreenNote2, tvMainScreenNote3, tvlockSoundDuration;
private Button bShowHelp, bPrivacy, bSettingsErase, bAddSoundLockTIme;
private TextView tvActivePoi, tvClosestPoi, tvLastRule, tvMainScreenNotePermissions, tvMainScreenNoteFeaturesFromOtherFlavor, tvMainScreenNoteLocationImpossibleBlameGoogle, tvMainScreenNoteNews, tvlockSoundDuration;
private ListView lvRuleHistory;
private ArrayAdapter<Rule> ruleHistoryListViewAdapter;
@ -70,9 +72,10 @@ public class ActivityMainScreen extends ActivityGeneric
tvClosestPoi = (TextView) findViewById(R.id.tvClosestPoi);
lvRuleHistory = (ListView) findViewById(R.id.lvRuleHistory);
tvLastRule = (TextView) findViewById(R.id.tvTimeFrameHelpText);
tvMainScreenNote1 = (TextView) findViewById(R.id.tvMainScreenNote1);
tvMainScreenNote2 = (TextView) findViewById(R.id.tvMainScreenNote2);
tvMainScreenNote3 = (TextView) findViewById(R.id.tvMainScreenNote3);
tvMainScreenNotePermissions = (TextView) findViewById(R.id.tvMainScreenNotePermissions);
tvMainScreenNoteFeaturesFromOtherFlavor = (TextView) findViewById(R.id.tvMainScreenNoteFeaturesFromOtherFlavor);
tvMainScreenNoteLocationImpossibleBlameGoogle = (TextView) findViewById(R.id.tvMainScreenNoteLocationImpossibleBlameGoogle);
tvMainScreenNoteNews = (TextView) findViewById(R.id.tvMainScreenNoteNews);
tvlockSoundDuration = (TextView)findViewById(R.id.tvlockSoundDuration);
tbLockSound = (ToggleButton) findViewById(R.id.tbLockSound);
toggleService = (ToggleButton) findViewById(R.id.tbArmMastListener);
@ -95,7 +98,7 @@ public class ActivityMainScreen extends ActivityGeneric
}
});
tvMainScreenNote1.setOnClickListener(new OnClickListener()
tvMainScreenNotePermissions.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
@ -129,19 +132,8 @@ public class ActivityMainScreen extends ActivityGeneric
@Override
public void onClick(View v)
{
Intent myIntent = new Intent(ActivityMainScreen.this, ActivitySettings.class);
startActivityForResult(myIntent, 6000);
}
});
Button bVolumeTest = (Button) findViewById(R.id.bVolumeTest);
bVolumeTest.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
Intent intent = new Intent(ActivityMainScreen.this, ActivityVolumeTest.class);
startActivity(intent);
Intent myIntent = new Intent(ActivityMainScreen.this, ActivityMaintenance.class);
startActivity(myIntent);
}
});
@ -177,25 +169,6 @@ public class ActivityMainScreen extends ActivityGeneric
builder.create().show();
}
});
/*bSettingsErase = (Button)findViewById(R.id.bSettingsErase);
bSettingsErase.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
getEraseSettingsDialog(ActivityMainScreen.this).show();
}
});*/
bSettingsSetToDefault = (Button) findViewById(R.id.bSettingsSetToDefault);
bSettingsSetToDefault.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
getDefaultSettingsDialog(ActivityMainScreen.this).show();
}
});
lvRuleHistory.setOnTouchListener(new OnTouchListener()
{
@ -259,25 +232,6 @@ public class ActivityMainScreen extends ActivityGeneric
return alertDialog;
}
private static AlertDialog getDefaultSettingsDialog(final Context context)
{
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context);
alertDialogBuilder.setTitle(context.getResources().getString(R.string.areYouSure));
alertDialogBuilder.setPositiveButton(context.getResources().getString(R.string.yes), new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
if (Settings.initializeSettings(context, true))
Toast.makeText(context, context.getResources().getString(R.string.settingsSetToDefault), Toast.LENGTH_LONG).show();
}
});
alertDialogBuilder.setNegativeButton(context.getResources().getString(R.string.no), null);
AlertDialog alertDialog = alertDialogBuilder.create();
return alertDialog;
}
public static ActivityMainScreen getActivityMainScreenInstance()
{
return activityMainScreenInstance;
@ -291,24 +245,52 @@ public class ActivityMainScreen extends ActivityGeneric
{
if(ActivityPermissions.needMorePermissions(activityMainScreenInstance))
{
activityMainScreenInstance.tvMainScreenNote1.setText(R.string.mainScreenPermissionNote);
activityMainScreenInstance.tvMainScreenNote1.setVisibility(View.VISIBLE);
activityMainScreenInstance.tvMainScreenNotePermissions.setText(R.string.mainScreenPermissionNote);
activityMainScreenInstance.tvMainScreenNotePermissions.setVisibility(View.VISIBLE);
}
else
{
activityMainScreenInstance.tvMainScreenNote1.setText("");
activityMainScreenInstance.tvMainScreenNote1.setVisibility(View.GONE);
activityMainScreenInstance.tvMainScreenNotePermissions.setText("");
activityMainScreenInstance.tvMainScreenNotePermissions.setVisibility(View.GONE);
}
if(Miscellaneous.restrictedFeaturesConfigured())
{
activityMainScreenInstance.tvMainScreenNote2.setText(R.string.settingsReferringToRestrictedFeatures);
activityMainScreenInstance.tvMainScreenNote2.setVisibility(View.VISIBLE);
activityMainScreenInstance.tvMainScreenNoteFeaturesFromOtherFlavor.setText(R.string.settingsReferringToRestrictedFeatures);
activityMainScreenInstance.tvMainScreenNoteFeaturesFromOtherFlavor.setVisibility(View.VISIBLE);
}
else
{
activityMainScreenInstance.tvMainScreenNote2.setText("");
activityMainScreenInstance.tvMainScreenNote2.setVisibility(View.GONE);
activityMainScreenInstance.tvMainScreenNoteFeaturesFromOtherFlavor.setText("");
activityMainScreenInstance.tvMainScreenNoteFeaturesFromOtherFlavor.setVisibility(View.GONE);
}
if(Miscellaneous.googleToBlameForLocation(true))
{
// Intent intent = new Intent(AutomationService.this, ActivityDisplayLongMessage.class);
// intent.putExtra("longMessage", getResources().getString(R.string.locationEngineDisabledLong));
// 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);
activityMainScreenInstance.tvMainScreenNoteLocationImpossibleBlameGoogle.setText(R.string.locationEngineDisabledShort);
activityMainScreenInstance.tvMainScreenNoteLocationImpossibleBlameGoogle.setVisibility(View.VISIBLE);
activityMainScreenInstance.tvMainScreenNoteLocationImpossibleBlameGoogle.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
openGoogleBlamingWindow();
}
});
}
else
{
activityMainScreenInstance.tvMainScreenNoteLocationImpossibleBlameGoogle.setText("");
activityMainScreenInstance.tvMainScreenNoteLocationImpossibleBlameGoogle.setVisibility(View.GONE);
activityMainScreenInstance.tvMainScreenNoteLocationImpossibleBlameGoogle.setOnClickListener(null);
}
if (AutomationService.isMyServiceRunning(activityMainScreenInstance))
@ -341,9 +323,9 @@ public class ActivityMainScreen extends ActivityGeneric
if(
Rule.isAnyRuleUsing(Trigger_Enum.pointOfInterest)
&&
ActivityPermissions.havePermission(ActivityPermissions.permissionNameLocationCoarse, AutomationService.getInstance())
ActivityPermissions.havePermission(ActivityPermissions.permissionNameLocationCoarse, Miscellaneous.getAnyContext())
&&
ActivityPermissions.havePermission(ActivityPermissions.permissionNameLocationFine, AutomationService.getInstance())
ActivityPermissions.havePermission(ActivityPermissions.permissionNameLocationFine, Miscellaneous.getAnyContext())
)
activityMainScreenInstance.tvActivePoi.setText(activityMainScreenInstance.getResources().getString(R.string.stillGettingPosition));
else
@ -418,7 +400,52 @@ public class ActivityMainScreen extends ActivityGeneric
Miscellaneous.logEvent("i", "ActivityMainScreen", "Activity not running. No need to update.", 5);
if(activityMainScreenInstance != null)
activityMainScreenInstance.checkForNews();
{
if(!Settings.hasBeenDone(Settings.constNewsOptInDone))
newsOptIn();
else
activityMainScreenInstance.checkForNews();
Settings.considerDone(Settings.constNewsOptInDone);
Settings.writeSettings(Miscellaneous.getAnyContext());
}
}
public static void openGoogleBlamingWindow()
{
Intent intent = new Intent(Miscellaneous.getAnyContext(), ActivityDisplayLongMessage.class);
String message = Miscellaneous.getAnyContext().getResources().getText(R.string.locationEngineDisabledLong).toString();
intent.putExtra("messageTitle", Miscellaneous.getAnyContext().getResources().getString(R.string.locationDisabled));
intent.putExtra("longMessage", message);
intent.putExtra("messageLink", "https://server47.de/automation/fdroidMigration.html");
ActivityMainScreen.getActivityMainScreenInstance().startActivity(intent);
}
static void newsOptIn()
{
AlertDialog.Builder builder = new AlertDialog.Builder(Miscellaneous.getAnyContext());
builder.setMessage(Miscellaneous.getAnyContext().getResources().getString(R.string.newsOptIn));
builder.setPositiveButton(Miscellaneous.getAnyContext().getResources().getString(R.string.yes), new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
Settings.displayNewsOnMainScreen = true;
Settings.writeSettings(Miscellaneous.getAnyContext());
try
{
activityMainScreenInstance.checkForNews();
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "NewsOptIn", "There was a problem showing the news opt-in: " + Log.getStackTraceString(e), 2);
}
}
});
builder.setNegativeButton(Miscellaneous.getAnyContext().getResources().getString(R.string.no), null);
builder.create().show();
}
@Override
@ -436,16 +463,6 @@ public class ActivityMainScreen extends ActivityGeneric
case ActivityPermissions.requestCodeForPermissions:
updateMainScreen();
break;
case 6000: //settings
Settings.readFromPersistentStorage(this);
if (boundToService && AutomationService.isMyServiceRunning(this))
myAutomationService.serviceInterface(serviceCommands.reloadSettings);
if(AutomationService.isMyServiceRunning(ActivityMainScreen.this))
Toast.makeText(this, getResources().getString(R.string.settingsWillTakeTime), Toast.LENGTH_LONG).show();
break;
}
if (AutomationService.isMyServiceRunning(this))
@ -572,57 +589,31 @@ public class ActivityMainScreen extends ActivityGeneric
synchronized void checkForNews()
{
News.AsyncTaskDownloadNews dnTask = new News.AsyncTaskDownloadNews();
dnTask.execute(ActivityMainScreen.this);
// StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
// StrictMode.setThreadPolicy(policy);
// Calendar now = Calendar.getInstance();
// if (true || Settings.lastNewsPolltime == -1 || now.getTimeInMillis() >= Settings.lastNewsPolltime + (long)(Settings.pollNewsEveryXDays * 24 * 60 * 60 * 1000))
// {
// ArrayList<News> newsToDisplay;
//
//// if(Settings.lastNewsPolltime == -1)
// newsToDisplay = News.downloadNews(ActivityMainScreen.this);
//// else
//// newsToDisplay = News.downloadNews(ActivityMainScreen.this, Miscellaneous.calendarFromLong(Settings.lastNewsPolltime));
//
// if (newsToDisplay.size() > 0)
// {
// activityMainScreenInstance.tvMainScreenNote3.setText(newsToDisplay.get(0).toString());
// activityMainScreenInstance.tvMainScreenNote3.setVisibility(View.VISIBLE);
// }
// else
// {
// activityMainScreenInstance.tvMainScreenNote3.setText("");
// activityMainScreenInstance.tvMainScreenNote3.setVisibility(View.GONE);
// }
// }
if(Settings.displayNewsOnMainScreen)
{
News.AsyncTaskDownloadNews dnTask = new News.AsyncTaskDownloadNews();
dnTask.execute(ActivityMainScreen.this);
}
}
public void processNewsResult(ArrayList<News> newsToDisplay)
{
if(Settings.displayNewsOnMainScreen)
try
{
try
if (newsToDisplay.size() > 0)
{
if (newsToDisplay.size() > 0)
{
activityMainScreenInstance.tvMainScreenNote3.setText(HtmlCompat.fromHtml(newsToDisplay.get(0).toStringHtml(), 0));
activityMainScreenInstance.tvMainScreenNote3.setVisibility(View.VISIBLE);
}
else
{
activityMainScreenInstance.tvMainScreenNote3.setText("");
activityMainScreenInstance.tvMainScreenNote3.setVisibility(View.GONE);
}
activityMainScreenInstance.tvMainScreenNoteNews.setText(HtmlCompat.fromHtml(newsToDisplay.get(0).toStringHtml(), 0));
activityMainScreenInstance.tvMainScreenNoteNews.setVisibility(View.VISIBLE);
}
catch(Exception e)
else
{
Miscellaneous.logEvent("e", "Error displaying news", Log.getStackTraceString(e), 3);
activityMainScreenInstance.tvMainScreenNoteNews.setText("");
activityMainScreenInstance.tvMainScreenNoteNews.setVisibility(View.GONE);
}
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "Error displaying news", Log.getStackTraceString(e), 3);
}
}
}

View File

@ -0,0 +1,353 @@
package com.jens.automation2;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.documentfile.provider.DocumentFile;
import java.io.File;
import java.util.ArrayList;
public class ActivityMaintenance extends Activity
{
final static int requestCodeImport = 1001;
final static int requestCodeExport = 1002;
final static int requestCodeMoreSettings = 6000;
final static String prefsFileName = "com.jens.automation2_preferences.xml";
TextView tvFileStoreLocation;
Button bVolumeTest, bMoreSettings, bSettingsSetToDefault, bShareConfigAndLog, bImportConfiguration, bExportConfiguration;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maintenance);
bVolumeTest = (Button) findViewById(R.id.bVolumeTest);
bVolumeTest.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
Intent intent = new Intent(ActivityMaintenance.this, ActivityVolumeTest.class);
startActivity(intent);
}
});
bShareConfigAndLog = (Button) findViewById(R.id.bShareConfigAndLog);
bShareConfigAndLog.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
getShareConfigAndLogDialogue(ActivityMaintenance.this).show();
}
});
bSettingsSetToDefault = (Button) findViewById(R.id.bSettingsSetToDefault);
bSettingsSetToDefault.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
getDefaultSettingsDialog(ActivityMaintenance.this).show();
}
});
Button bMoreSettings = (Button) findViewById(R.id.bMoreSettings);
bMoreSettings.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
Intent myIntent = new Intent(ActivityMaintenance.this, ActivitySettings.class);
startActivityForResult(myIntent, requestCodeMoreSettings);
}
});
bImportConfiguration = (Button) findViewById(R.id.bImportConfiguration);
bImportConfiguration.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, requestCodeImport);
}
});
bExportConfiguration = (Button) findViewById(R.id.bExportConfiguration);
bExportConfiguration.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, requestCodeExport);
}
});
tvFileStoreLocation = (TextView)findViewById(R.id.tvFileStoreLocation);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
switch(requestCode)
{
case requestCodeMoreSettings: //settings
Settings.readFromPersistentStorage(this);
if (AutomationService.isMyServiceRunning(this))
AutomationService.getInstance().serviceInterface(AutomationService.serviceCommands.reloadSettings);
if (AutomationService.isMyServiceRunning(ActivityMaintenance.this))
Toast.makeText(this, getResources().getString(R.string.settingsWillTakeTime), Toast.LENGTH_LONG).show();
break;
case requestCodeImport:
if(resultCode == RESULT_OK)
{
Uri uriTree = data.getData();
importFiles(uriTree);
}
break;
case requestCodeExport:
if(resultCode == RESULT_OK)
{
Uri uriTree = data.getData();
exportFiles(uriTree);
}
break;
}
}
void importFiles(Uri uriTree)
{
// https://stackoverflow.com/questions/46237558/android-strange-behavior-of-documentfile-inputstream
File dstRules = new File(Miscellaneous.getWriteableFolder() + "/" + XmlFileInterface.settingsFileName);
File dstPrefs = new File(Miscellaneous.getWriteableFolder() + "/../shared_prefs/" + prefsFileName);
DocumentFile directory = DocumentFile.fromTreeUri(this, uriTree);
int applicableFilesFound = 0;
int filesImported = 0;
if(directory.listFiles().length > 0)
{
for (DocumentFile file : directory.listFiles())
{
if (file.getName().equals(XmlFileInterface.settingsFileName))
{
applicableFilesFound++;
if(file.canRead())
{
// import rules, locations, etc.
if (Miscellaneous.copyDocumentFileToFile(file, dstRules))
filesImported++;
else
Toast.makeText(ActivityMaintenance.this, getResources().getString(R.string.rulesImportError), Toast.LENGTH_LONG).show();
}
}
else if (file.getName().equals(prefsFileName))
{
applicableFilesFound++;
if(file.canRead())
{
// import rules, locations, etc.
if (Miscellaneous.copyDocumentFileToFile(file, dstPrefs))
filesImported++;
else
Toast.makeText(ActivityMaintenance.this, getResources().getString(R.string.prefsImportError), Toast.LENGTH_LONG).show();
}
}
}
if(applicableFilesFound > 0)
{
if(filesImported == 0)
Toast.makeText(ActivityMaintenance.this, getResources().getString(R.string.noFilesImported), Toast.LENGTH_LONG).show();
else if(filesImported < applicableFilesFound)
Toast.makeText(ActivityMaintenance.this, getResources().getString(R.string.notAllFilesImported), Toast.LENGTH_LONG).show();
else if (filesImported == applicableFilesFound)
{
Toast.makeText(ActivityMaintenance.this, getResources().getString(R.string.configurationImportedSuccessfully), Toast.LENGTH_LONG).show();
try
{
XmlFileInterface.readFile();
}
catch (Exception e)
{
Miscellaneous.logEvent("e", "Reading import", "Rules re-read failed: " + Log.getStackTraceString(e), 1);
Toast.makeText(ActivityMaintenance.this, getResources().getString(R.string.errorReadingPoisAndRulesFromFile), Toast.LENGTH_LONG).show();
}
Settings.readFromPersistentStorage(ActivityMaintenance.this);
}
else
Toast.makeText(ActivityMaintenance.this, getResources().getString(R.string.noFilesImported), Toast.LENGTH_LONG).show();
}
else
Toast.makeText(ActivityMaintenance.this, getResources().getString(R.string.noApplicableFilesFoundInDirectory), Toast.LENGTH_LONG).show();
}
else
Toast.makeText(ActivityMaintenance.this, getResources().getString(R.string.noApplicableFilesFoundInDirectory), Toast.LENGTH_LONG).show();
}
void exportFiles(Uri uriTree)
{
DocumentFile directory = DocumentFile.fromTreeUri(this, uriTree);
File srcRules = new File(Miscellaneous.getWriteableFolder() + "/" + XmlFileInterface.settingsFileName);
File srcPrefs = new File(Miscellaneous.getWriteableFolder() + "/../shared_prefs/" + prefsFileName);
// Clean up
for(DocumentFile file : directory.listFiles())
{
if(file.getName().equals(XmlFileInterface.settingsFileName) && file.canWrite())
file.delete();
else if(file.getName().equals(prefsFileName) && file.canWrite())
file.delete();
}
DocumentFile dstRules = directory.createFile("text/xml", XmlFileInterface.settingsFileName);
DocumentFile dstPrefs = directory.createFile("text/xml", prefsFileName);
if(dstRules.canWrite() && dstPrefs.canWrite())
{
if(Miscellaneous.copyFileToDocumentFile(srcRules, dstRules) && Miscellaneous.copyFileToDocumentFile(srcPrefs, dstPrefs))
Toast.makeText(ActivityMaintenance.this, getResources().getString(R.string.configurationExportedSuccessfully), Toast.LENGTH_LONG).show();
else
Toast.makeText(ActivityMaintenance.this, getResources().getString(R.string.ConfigurationExportError), Toast.LENGTH_LONG).show();
}
else
Toast.makeText(ActivityMaintenance.this, getResources().getString(R.string.ConfigurationExportError), Toast.LENGTH_LONG).show();
}
private static AlertDialog getDefaultSettingsDialog(final Context context)
{
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context);
alertDialogBuilder.setTitle(context.getResources().getString(R.string.areYouSure));
alertDialogBuilder.setPositiveButton(context.getResources().getString(R.string.yes), new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
if (Settings.initializeSettings(context, true))
Toast.makeText(context, context.getResources().getString(R.string.settingsSetToDefault), Toast.LENGTH_LONG).show();
}
});
alertDialogBuilder.setNegativeButton(context.getResources().getString(R.string.no), null);
AlertDialog alertDialog = alertDialogBuilder.create();
return alertDialog;
}
AlertDialog getShareConfigAndLogDialogue(final Context context)
{
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context);
alertDialogBuilder.setTitle(context.getResources().getString(R.string.shareConfigAndLogFilesWithDev));
alertDialogBuilder.setMessage(context.getResources().getString(R.string.shareConfigAndLogExplanation));
alertDialogBuilder.setPositiveButton(context.getResources().getString(R.string.yes), new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
File dstZipFile = new File(Miscellaneous.getAnyContext().getCacheDir() + "/" + Settings.zipFileName);
ArrayList<String> srcFilesList = new ArrayList<>();
srcFilesList.add(Miscellaneous.getWriteableFolder() + "/" + XmlFileInterface.settingsFileName);
srcFilesList.add(Miscellaneous.getWriteableFolder() + "/../shared_prefs/" + prefsFileName);
String logFilePath = Miscellaneous.getWriteableFolder() + "/" + Miscellaneous.logFileName;
if((new File(logFilePath)).exists())
srcFilesList.add(logFilePath);
String logFilePathArchive = Miscellaneous.getWriteableFolder() + "/" + Miscellaneous.logFileName + "-old";
if((new File(logFilePathArchive)).exists())
srcFilesList.add(logFilePathArchive);
String[] srcFiles = srcFilesList.toArray(new String[srcFilesList.size()]);
if(dstZipFile.exists())
dstZipFile.delete();
Miscellaneous.zip(srcFiles, dstZipFile.getAbsolutePath());
/*
Without root the zip file in the cache directory is not directly accessible.
But have to route it through this content provider crap.
*/
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);
Uri uri = Uri.parse("content://com.jens.automation2/" + Settings.zipFileName);
Miscellaneous.sendEmail(ActivityMaintenance.this, "android-development@gmx.de", "Automation logs", emailBody.toString(), uri);
}
});
alertDialogBuilder.setNegativeButton(context.getResources().getString(R.string.no), null);
AlertDialog alertDialog = alertDialogBuilder.create();
return alertDialog;
}
@Override
protected void onResume()
{
super.onResume();
String folder = Miscellaneous.getWriteableFolder();
if (folder != null && folder.length() > 0)
{
tvFileStoreLocation.setText(String.format(getResources().getString(R.string.filesStoredAt), folder));
tvFileStoreLocation.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
Uri selectedUri = Uri.parse(folder);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(selectedUri, "resource/folder");
if (intent.resolveActivityInfo(getPackageManager(), 0) != null)
{
startActivity(intent);
}
else
{
// if you reach this place, it means there is no any file
// explorer app installed on your device
Toast.makeText(ActivityMaintenance.this, getResources().getString(R.string.noFileManageInstalled), Toast.LENGTH_LONG).show();
}
}
});
}
}
}

View File

@ -12,7 +12,7 @@ import android.widget.TextView;
import androidx.annotation.Nullable;
public class ActivityManageBrightnessSetting extends Activity
public class ActivityManageActionBrightnessSetting extends Activity
{
CheckBox chkAutoBrightness;
SeekBar sbBrightness;

View File

@ -0,0 +1,110 @@
package com.jens.automation2;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import java.io.File;
public class ActivityManageActionPlaySound extends Activity
{
final static int PICKFILE_RESULT_CODE = 4711;
CheckBox chkPlaySoundAlwaysPlay;
EditText etSelectedSoundFile;
Button bSelectSoundFile, bSavePlaySound;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_manage_play_sound);
chkPlaySoundAlwaysPlay = (CheckBox)findViewById(R.id.chkPlaySoundAlwaysPlay);
etSelectedSoundFile = (EditText)findViewById(R.id.etSelectedSoundFile);
bSelectSoundFile = (Button)findViewById(R.id.bSelectSoundFile);
bSavePlaySound = (Button)findViewById(R.id.bSavePlaySound);
boolean edit = getIntent().getBooleanExtra("edit", false);
if(edit)
{
boolean param1 = getIntent().getBooleanExtra("actionParameter1", false);
String param2 = getIntent().getStringExtra("actionParameter2");
chkPlaySoundAlwaysPlay.setChecked(param1);
etSelectedSoundFile.setText(param2);
}
bSelectSoundFile.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
//Need to check for storage permissions
Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
chooseFile.setType("*/*");
chooseFile = Intent.createChooser(chooseFile, getResources().getString(R.string.selectSoundFile));
startActivityForResult(chooseFile, PICKFILE_RESULT_CODE);
}
});
bSavePlaySound.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
savePlaySoundSettings();
}
});
}
void savePlaySoundSettings()
{
if(etSelectedSoundFile.getText().toString() == null || etSelectedSoundFile.getText().toString().length() == 0)
{
Toast.makeText(ActivityManageActionPlaySound.this, getResources().getString(R.string.selectSoundFile), Toast.LENGTH_LONG).show();
return;
}
else
{
File soundFile = new File(etSelectedSoundFile.getText().toString());
if(!soundFile.exists())
{
Toast.makeText(ActivityManageActionPlaySound.this, getResources().getString(R.string.fileDoesNotExist), Toast.LENGTH_LONG).show();
return;
}
}
Intent returnData = new Intent();
returnData.putExtra("actionParameter1", chkPlaySoundAlwaysPlay.isChecked());
returnData.putExtra("actionParameter2", etSelectedSoundFile.getText().toString());
setResult(RESULT_OK, returnData);
finish();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == RESULT_OK)
{
if(requestCode == PICKFILE_RESULT_CODE)
{
Uri fileUri = data.getData();
String filePath = CompensateCrappyAndroidPaths.getPath(ActivityManageActionPlaySound.this, fileUri);
etSelectedSoundFile.setText(filePath);
}
}
}
}

View File

@ -19,7 +19,7 @@ import androidx.annotation.NonNull;
import com.jens.automation2.Action.Action_Enum;
public class ActivityEditSendTextMessage extends Activity
public class ActivityManageActionSendTextMessage extends Activity
{
Button bSaveSendTextMessage, bImportNumberFromContacts;
EditText etPhoneNumber, etSendTextMessage;
@ -67,7 +67,7 @@ public class ActivityEditSendTextMessage extends Activity
@Override
public void onClick(View view)
{
if(!ActivityPermissions.havePermission("android.permission.READ_CONTACTS", ActivityEditSendTextMessage.this))
if(!ActivityPermissions.havePermission("android.permission.READ_CONTACTS", ActivityManageActionSendTextMessage.this))
{
requestPermissions("android.permission.READ_CONTACTS");
}
@ -76,10 +76,10 @@ public class ActivityEditSendTextMessage extends Activity
}
});
ActivityEditSendTextMessage.edit = getIntent().getBooleanExtra("edit", false);
ActivityManageActionSendTextMessage.edit = getIntent().getBooleanExtra("edit", false);
if(edit)
{
String[] parameters = ActivityEditSendTextMessage.resultingAction.getParameter2().split(Actions.smsSeparator);
String[] parameters = ActivityManageActionSendTextMessage.resultingAction.getParameter2().split(Actions.smsSeparator);
etPhoneNumber.setText(parameters[0]);
etSendTextMessage.setText(parameters[1]);
}
@ -99,7 +99,7 @@ public class ActivityEditSendTextMessage extends Activity
if(edit && resultingAction != null)
{
ActivityEditSendTextMessage.resultingAction.setParameter2(etPhoneNumber.getText().toString() + Actions.smsSeparator + etSendTextMessage.getText().toString());
ActivityManageActionSendTextMessage.resultingAction.setParameter2(etPhoneNumber.getText().toString() + Actions.smsSeparator + etSendTextMessage.getText().toString());
}
setResult(RESULT_OK);
@ -162,7 +162,7 @@ public class ActivityEditSendTextMessage extends Activity
String name = null;
Uri uri = data.getData();
Cursor cursor = ActivityEditSendTextMessage.this.getContentResolver().query(uri, null, null, null, null);
Cursor cursor = ActivityManageActionSendTextMessage.this.getContentResolver().query(uri, null, null, null, null);
if (cursor.moveToFirst())
{

View File

@ -10,7 +10,7 @@ import android.widget.Toast;
import com.jens.automation2.Action.Action_Enum;
public class ActivityEditSpeakText extends Activity
public class ActivityManageActionSpeakText extends Activity
{
private Button bSaveSpeakText;
private EditText etSpeakText;
@ -48,9 +48,9 @@ public class ActivityEditSpeakText extends Activity
}
});
ActivityEditSpeakText.edit = getIntent().getBooleanExtra("edit", false);
ActivityManageActionSpeakText.edit = getIntent().getBooleanExtra("edit", false);
if(edit)
etSpeakText.setText(ActivityEditSpeakText.resultingAction.getParameter2());
etSpeakText.setText(ActivityManageActionSpeakText.resultingAction.getParameter2());
// String url = getIntent().getStringExtra("urlToTrigger");
@ -66,7 +66,7 @@ public class ActivityEditSpeakText extends Activity
// setResult(RESULT_OK, returnIntent);
if(edit && resultingAction != null)
ActivityEditSpeakText.resultingAction.setParameter2(etSpeakText.getText().toString());
ActivityManageActionSpeakText.resultingAction.setParameter2(etSpeakText.getText().toString());
setResult(RESULT_OK);

View File

@ -10,6 +10,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.InputType;
@ -22,10 +23,11 @@ import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.RadioButton;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.jens.automation2.Action.Action_Enum;
@ -35,15 +37,23 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ActivityManageStartActivity extends Activity
public class ActivityManageActionStartActivity extends Activity
{
/*
This page might qualify as a help page: https://stackoverflow.com/questions/55323947/open-url-in-firefox-for-android-using-intent
*/
ListView lvIntentPairs;
EditText etParameterName, etParameterValue;
Button bSelectApp, bAddIntentPair, bSaveActionStartOtherActivity;
EditText etParameterName, etParameterValue, etPackageName, etActivityOrActionPath;
Button bSelectApp, bAddIntentPair, bSaveActionStartOtherActivity, showStartProgramExamples;
Spinner spinnerParameterType;
TextView tvSelectedActivity;
boolean edit = false;
ProgressDialog progressDialog = null;
RadioButton rbStartAppSelectByActivity, rbStartAppSelectByAction, rbStartAppByActivity, rbStartAppByBroadcast;
final String urlShowExamples = "https://server47.de/automation/examples_startProgram.html";
final static String startByActivityString = "0";
final static String startByBroadcastString = "1";
private class CustomPackageInfo extends PackageInfo implements Comparable<CustomPackageInfo>
{
@ -56,12 +66,12 @@ public class ActivityManageStartActivity extends Activity
ApplicationInfo aInfo1 = this.applicationInfo;
if (aInfo1 != null)
{
name1 = (String) ActivityManageStartActivity.this.getPackageManager().getApplicationLabel(aInfo1);
name1 = (String) ActivityManageActionStartActivity.this.getPackageManager().getApplicationLabel(aInfo1);
}
ApplicationInfo aInfo2 = another.applicationInfo;
if (aInfo2 != null)
{
name2 = (String) ActivityManageStartActivity.this.getPackageManager().getApplicationLabel(aInfo2);
name2 = (String) ActivityManageActionStartActivity.this.getPackageManager().getApplicationLabel(aInfo2);
}
return name1.compareTo(name2);
@ -72,7 +82,7 @@ public class ActivityManageStartActivity extends Activity
private static List<PackageInfo> pInfos = null;
public static Action resultingAction;
private static final String[] supportedIntentTypes = { "boolean", "byte", "char", "double", "float", "int", "long", "short", "String" };
private static final String[] supportedIntentTypes = { "boolean", "byte", "char", "double", "float", "int", "long", "short", "String", "Uri" };
private ArrayList<String> intentPairList = new ArrayList<String>();
ArrayAdapter<String> intentTypeSpinnerAdapter, intentPairAdapter;
@ -224,7 +234,7 @@ public class ActivityManageStartActivity extends Activity
{
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
alertDialogBuilder.setTitle(getResources().getString(R.string.selectApplication));
final String[] applicationArray = ActivityManageStartActivity.getApplicationNameListString(this);
final String[] applicationArray = ActivityManageActionStartActivity.getApplicationNameListString(this);
alertDialogBuilder.setItems(applicationArray, new DialogInterface.OnClickListener()
{
@Override
@ -242,14 +252,14 @@ public class ActivityManageStartActivity extends Activity
{
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
alertDialogBuilder.setTitle(getResources().getString(R.string.selectPackageOfApplication));
final String[] packageArray = ActivityManageStartActivity.getPackageListString(this, applicationName);
final String[] packageArray = ActivityManageActionStartActivity.getPackageListString(this, applicationName);
alertDialogBuilder.setItems(packageArray, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
getActionStartActivityDialog3(packageArray[which]).show();
Miscellaneous.messageBox(getResources().getString(R.string.hint), getResources().getString(R.string.chooseActivityHint), ActivityManageStartActivity.this).show();
Miscellaneous.messageBox(getResources().getString(R.string.hint), getResources().getString(R.string.chooseActivityHint), ActivityManageActionStartActivity.this).show();
}
});
@ -261,14 +271,15 @@ public class ActivityManageStartActivity extends Activity
{
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
alertDialogBuilder.setTitle(getResources().getString(R.string.selectActivityToBeStarted));
final String activityArray[] = ActivityManageStartActivity.getActivityListForPackageName(packageName);
final String activityArray[] = ActivityManageActionStartActivity.getActivityListForPackageName(packageName);
alertDialogBuilder.setItems(activityArray, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
ActivityInfo ai = ActivityManageStartActivity.getActivityInfoForPackageNameAndActivityName(packageName, activityArray[which]);
tvSelectedActivity.setText(ai.packageName + ";" + ai.name);
ActivityInfo ai = ActivityManageActionStartActivity.getActivityInfoForPackageNameAndActivityName(packageName, activityArray[which]);
etPackageName.setText(ai.packageName);
etActivityOrActionPath.setText(ai.name);
}
});
AlertDialog alertDialog = alertDialogBuilder.create();
@ -280,7 +291,7 @@ public class ActivityManageStartActivity extends Activity
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.action_start_activity);
setContentView(R.layout.activity_manage_action_start_activity);
lvIntentPairs = (ListView)findViewById(R.id.lvIntentPairs);
etParameterName = (EditText)findViewById(R.id.etParameterName);
@ -289,13 +300,20 @@ public class ActivityManageStartActivity extends Activity
bAddIntentPair = (Button)findViewById(R.id.bAddIntentPair);
bSaveActionStartOtherActivity = (Button)findViewById(R.id.bSaveActionStartOtherActivity);
spinnerParameterType = (Spinner)findViewById(R.id.spinnerParameterType);
tvSelectedActivity = (TextView)findViewById(R.id.tvSelectedActivity);
etPackageName = (EditText) findViewById(R.id.etPackageName);
etActivityOrActionPath = (EditText) findViewById(R.id.etActivityOrActionPath);
rbStartAppSelectByActivity = (RadioButton)findViewById(R.id.rbStartAppSelectByActivity);
rbStartAppSelectByAction = (RadioButton)findViewById(R.id.rbStartAppSelectByAction);
showStartProgramExamples = (Button)findViewById(R.id.showStartProgramExamples);
rbStartAppByActivity = (RadioButton)findViewById(R.id.rbStartAppByActivity);
rbStartAppByBroadcast = (RadioButton)findViewById(R.id.rbStartAppByBroadcast);
intentTypeSpinnerAdapter = new ArrayAdapter<String>(this, R.layout.text_view_for_poi_listview_mediumtextsize, ActivityManageStartActivity.supportedIntentTypes);
intentTypeSpinnerAdapter = new ArrayAdapter<String>(this, R.layout.text_view_for_poi_listview_mediumtextsize, ActivityManageActionStartActivity.supportedIntentTypes);
spinnerParameterType.setAdapter(intentTypeSpinnerAdapter);
intentTypeSpinnerAdapter.notifyDataSetChanged();
intentPairAdapter = new ArrayAdapter<String>(this, R.layout.text_view_for_poi_listview_smalltextsize, intentPairList);
intentPairAdapter = new ArrayAdapter<String>(this, R.layout.text_view_for_poi_listview_mediumtextsize, intentPairList);
bSelectApp.setOnClickListener(new OnClickListener()
{
@ -304,7 +322,7 @@ public class ActivityManageStartActivity extends Activity
{
GetActivityListTask getActivityListTask = new GetActivityListTask();
getActivityListTask.execute();
progressDialog = ProgressDialog.show(ActivityManageStartActivity.this, "", ActivityManageStartActivity.this.getResources().getString(R.string.gettingListOfInstalledApplications));
progressDialog = ProgressDialog.show(ActivityManageActionStartActivity.this, "", ActivityManageActionStartActivity.this.getResources().getString(R.string.gettingListOfInstalledApplications));
}
});
@ -316,23 +334,43 @@ public class ActivityManageStartActivity extends Activity
// type;name;value
if(spinnerParameterType.getSelectedItem().toString().length() == 0)
{
Toast.makeText(ActivityManageStartActivity.this, getResources().getString(R.string.selectTypeOfIntentPair), Toast.LENGTH_LONG).show();
Toast.makeText(ActivityManageActionStartActivity.this, getResources().getString(R.string.selectTypeOfIntentPair), Toast.LENGTH_LONG).show();
return;
}
if(etParameterName.getText().toString().length() == 0)
{
Toast.makeText(ActivityManageStartActivity.this, getResources().getString(R.string.enterNameForIntentPair), Toast.LENGTH_LONG).show();
Toast.makeText(ActivityManageActionStartActivity.this, getResources().getString(R.string.enterNameForIntentPair), Toast.LENGTH_LONG).show();
return;
}
else if(etParameterName.getText().toString().contains(Action.intentPairSeperator))
{
Toast.makeText(ActivityManageActionStartActivity.this, String.format(getResources().getString(R.string.stringNotAllowed), Action.intentPairSeperator), Toast.LENGTH_LONG).show();
return;
}
else if(etParameterName.getText().toString().contains(";"))
{
Toast.makeText(ActivityManageActionStartActivity.this, String.format(getResources().getString(R.string.stringNotAllowed), ";"), Toast.LENGTH_LONG).show();
return;
}
if(etParameterValue.getText().toString().length() == 0)
{
Toast.makeText(ActivityManageStartActivity.this, getResources().getString(R.string.enterValueForIntentPair), Toast.LENGTH_LONG).show();
Toast.makeText(ActivityManageActionStartActivity.this, getResources().getString(R.string.enterValueForIntentPair), Toast.LENGTH_LONG).show();
return;
}
else if(etParameterValue.getText().toString().contains(Action.intentPairSeperator))
{
Toast.makeText(ActivityManageActionStartActivity.this, String.format(getResources().getString(R.string.stringNotAllowed), Action.intentPairSeperator), Toast.LENGTH_LONG).show();
return;
}
else if(etParameterValue.getText().toString().contains(";"))
{
Toast.makeText(ActivityManageActionStartActivity.this, String.format(getResources().getString(R.string.stringNotAllowed), ";"), Toast.LENGTH_LONG).show();
return;
}
String param = supportedIntentTypes[spinnerParameterType.getSelectedItemPosition()] + "/" + etParameterName.getText().toString() + "/" + etParameterValue.getText().toString();
String param = supportedIntentTypes[spinnerParameterType.getSelectedItemPosition()] + Action.intentPairSeperator + etParameterName.getText().toString() + Action.intentPairSeperator + etParameterValue.getText().toString();
intentPairList.add(param);
spinnerParameterType.setSelection(0);
@ -340,6 +378,19 @@ public class ActivityManageStartActivity extends Activity
etParameterValue.setText("");
updateIntentPairList();
if(lvIntentPairs.getVisibility() != View.VISIBLE)
lvIntentPairs.setVisibility(View.VISIBLE);
}
});
showStartProgramExamples.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(urlShowExamples));
startActivity(browserIntent);
}
});
@ -360,7 +411,7 @@ public class ActivityManageStartActivity extends Activity
{
if(saveAction())
{
ActivityManageStartActivity.this.setResult(RESULT_OK);
ActivityManageActionStartActivity.this.setResult(RESULT_OK);
finish();
}
}
@ -382,9 +433,9 @@ public class ActivityManageStartActivity extends Activity
public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3)
{
if(supportedIntentTypes[arg2].equals("double") | supportedIntentTypes[arg2].equals("float") | supportedIntentTypes[arg2].equals("int") | supportedIntentTypes[arg2].equals("long") | supportedIntentTypes[arg2].equals("short"))
ActivityManageStartActivity.this.etParameterValue.setInputType(InputType.TYPE_CLASS_NUMBER);
ActivityManageActionStartActivity.this.etParameterValue.setInputType(InputType.TYPE_CLASS_NUMBER);
else
ActivityManageStartActivity.this.etParameterValue.setInputType(InputType.TYPE_CLASS_TEXT);
ActivityManageActionStartActivity.this.etParameterValue.setInputType(InputType.TYPE_CLASS_TEXT);
}
@Override
@ -394,6 +445,26 @@ public class ActivityManageStartActivity extends Activity
}
});
rbStartAppSelectByActivity.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
{
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
{
if(isChecked)
bSelectApp.setEnabled(isChecked);
}
});
rbStartAppSelectByAction.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
{
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
{
if(isChecked)
bSelectApp.setEnabled(!isChecked);
}
});
Intent i = getIntent();
if(i.getBooleanExtra("edit", false) == true)
@ -405,22 +476,46 @@ public class ActivityManageStartActivity extends Activity
private void loadValuesIntoGui()
{
boolean selectionByAction = resultingAction.getParameter1();
rbStartAppSelectByActivity.setChecked(!selectionByAction);
rbStartAppSelectByAction.setChecked(selectionByAction);
String[] params = resultingAction.getParameter2().split(";");
if(params.length >= 2)
rbStartAppByActivity.setChecked(params[2].equals(startByActivityString));
rbStartAppByBroadcast.setChecked(params[2].equals(startByBroadcastString));
int startIndex = -1;
if(!selectionByAction)
{
tvSelectedActivity.setText(params[0] + ";" + params[1]);
if(params.length > 2)
etPackageName.setText(params[0]);
etActivityOrActionPath.setText(params[1]);
}
else
{
if(!params[0].contains(Actions.dummyPackageString))
etPackageName.setText(params[0]);
etActivityOrActionPath.setText(params[1]);
}
if (params.length >= 3)
startIndex = 3;
if(startIndex > -1 && params.length > startIndex)
{
intentPairList.clear();
for(int i=startIndex; i<params.length; i++)
{
intentPairList.clear();
for(int i=2; i<params.length; i++)
{
intentPairList.add(params[i]);
}
updateIntentPairList();
if(lvIntentPairs.getVisibility() != View.VISIBLE)
lvIntentPairs.setVisibility(View.VISIBLE);
intentPairList.add(params[i]);
}
updateIntentPairList();
}
}
@ -434,24 +529,52 @@ public class ActivityManageStartActivity extends Activity
private boolean saveAction()
{
if(tvSelectedActivity.getText().toString().length() == 0)
if(rbStartAppSelectByActivity.isChecked())
{
Toast.makeText(ActivityManageStartActivity.this, getResources().getString(R.string.selectApplication), Toast.LENGTH_LONG).show();
return false;
if (etPackageName.getText().toString().length() == 0)
{
Toast.makeText(ActivityManageActionStartActivity.this, getResources().getString(R.string.enterPackageName), Toast.LENGTH_LONG).show();
return false;
}
else if (etActivityOrActionPath.getText().toString().length() == 0)
{
Toast.makeText(ActivityManageActionStartActivity.this, getResources().getString(R.string.selectApplication), Toast.LENGTH_LONG).show();
return false;
}
}
if(tvSelectedActivity.getText().toString().equals(getResources().getString(R.string.selectApplication)))
else
{
Toast.makeText(this, getResources().getString(R.string.selectApplication), Toast.LENGTH_LONG).show();
return false;
if(etActivityOrActionPath.getText().toString().contains(";"))
{
Toast.makeText(this, getResources().getString(R.string.enterValidAction), Toast.LENGTH_LONG).show();
return false;
}
}
if(resultingAction == null)
resultingAction = new Action();
resultingAction.setParameter1(rbStartAppSelectByAction.isChecked());
resultingAction.setAction(Action_Enum.startOtherActivity);
String parameter2 = tvSelectedActivity.getText().toString();
String parameter2 = "";
if(rbStartAppSelectByActivity.isChecked())
parameter2 += etPackageName.getText().toString() + ";" + etActivityOrActionPath.getText().toString();
else
{
if(etPackageName.getText().toString() != null && etPackageName.getText().toString().length() > 0)
parameter2 += etPackageName.getText().toString() + ";" + etActivityOrActionPath.getText().toString();
else
parameter2 += Actions.dummyPackageString + ";" + etActivityOrActionPath.getText().toString();
}
if(rbStartAppByActivity.isChecked())
parameter2 += ";" + startByActivityString;
else
parameter2 += ";" + startByBroadcastString;
for(String s : intentPairList)
parameter2 += ";" + s;
@ -462,7 +585,7 @@ public class ActivityManageStartActivity extends Activity
private AlertDialog getIntentPairDialog(final int itemPosition)
{
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(ActivityManageStartActivity.this);
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(ActivityManageActionStartActivity.this);
alertDialogBuilder.setTitle(getResources().getString(R.string.whatToDoWithIntentPair));
alertDialogBuilder.setItems(new String[]{getResources().getString(R.string.delete)}, new DialogInterface.OnClickListener()
{
@ -470,7 +593,7 @@ public class ActivityManageStartActivity extends Activity
public void onClick(DialogInterface dialog, int which)
{
// Only 1 choice at the moment, no need to check
ActivityManageStartActivity.this.intentPairList.remove(itemPosition);
ActivityManageActionStartActivity.this.intentPairList.remove(itemPosition);
updateIntentPairList();
}
});
@ -484,7 +607,7 @@ public class ActivityManageStartActivity extends Activity
@Override
protected Void doInBackground(Void... params)
{
getActivityList(ActivityManageStartActivity.this);
getActivityList(ActivityManageActionStartActivity.this);
return null;
}

View File

@ -20,7 +20,7 @@ import com.jens.automation2.Action.Action_Enum;
import java.util.Map;
public class ActivityEditTriggerUrl extends Activity
public class ActivityManageActionTriggerUrl extends Activity
{
Button bSaveTriggerUrl;
EditText etTriggerUrl, etTriggerUrlUsername, etTriggerUrlPassword;
@ -70,7 +70,7 @@ public class ActivityEditTriggerUrl extends Activity
if(password == null)
password = "";
ActivityEditTriggerUrl.resultingAction.setParameter2(
ActivityManageActionTriggerUrl.resultingAction.setParameter2(
username + ";" +
password + ";" +
etTriggerUrl.getText().toString().trim()
@ -110,16 +110,16 @@ public class ActivityEditTriggerUrl extends Activity
updateListView();
ActivityEditTriggerUrl.edit = getIntent().getBooleanExtra("edit", false);
ActivityManageActionTriggerUrl.edit = getIntent().getBooleanExtra("edit", false);
if(edit)
{
// username,password,URL
String[] components = ActivityEditTriggerUrl.resultingAction.getParameter2().split(";");
String[] components = ActivityManageActionTriggerUrl.resultingAction.getParameter2().split(";");
if(components.length >= 3)
{
etTriggerUrl.setText(components[2]);
chkTriggerUrlUseAuthentication.setChecked(ActivityEditTriggerUrl.resultingAction.getParameter1());
chkTriggerUrlUseAuthentication.setChecked(ActivityManageActionTriggerUrl.resultingAction.getParameter1());
etTriggerUrlUsername.setText(components[0]);
etTriggerUrlPassword.setText(components[1]);
}
@ -141,9 +141,9 @@ public class ActivityEditTriggerUrl extends Activity
if(password == null)
password = "";
ActivityEditTriggerUrl.resultingAction.setParameter1(chkTriggerUrlUseAuthentication.isChecked());
ActivityManageActionTriggerUrl.resultingAction.setParameter1(chkTriggerUrlUseAuthentication.isChecked());
ActivityEditTriggerUrl.resultingAction.setParameter2(
ActivityManageActionTriggerUrl.resultingAction.setParameter2(
username + ";" +
password + ";" +
etTriggerUrl.getText().toString()

View File

@ -95,6 +95,10 @@ public class ActivityManageRule extends Activity
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;
final static int requestCodeActionPlaySoundEdit = 502;
public static ActivityManageRule getInstance()
{
@ -165,7 +169,7 @@ public class ActivityManageRule extends Activity
hideKeyboard();
getActionTypeDialog().show();
}
});
});
cmdSaveRule.setOnClickListener(new OnClickListener()
{
@ -233,8 +237,8 @@ public class ActivityManageRule extends Activity
// case speed:
// break;
case timeFrame:
ActivityManageTimeFrame.editedTimeFrameTrigger = selectedTrigger;
Intent timeFrameEditor = new Intent(ActivityManageRule.this, ActivityManageTimeFrame.class);
ActivityManageTriggerTimeFrame.editedTimeFrameTrigger = selectedTrigger;
Intent timeFrameEditor = new Intent(ActivityManageRule.this, ActivityManageTriggerTimeFrame.class);
startActivityForResult(timeFrameEditor, requestCodeTriggerTimeframeEdit);
break;
// case usb_host_connection:
@ -242,10 +246,16 @@ public class ActivityManageRule extends Activity
// case wifiConnection:
// break;
case bluetoothConnection:
ActivityManageBluetoothTrigger.editedBluetoothTrigger = selectedTrigger;
Intent bluetoothEditor = new Intent(ActivityManageRule.this, ActivityManageBluetoothTrigger.class);
ActivityManageTriggerBluetooth.editedBluetoothTrigger = selectedTrigger;
Intent bluetoothEditor = new Intent(ActivityManageRule.this, ActivityManageTriggerBluetooth.class);
startActivityForResult(bluetoothEditor, requestCodeTriggerBluetoothEdit);
break;
case notification:
ActivityManageTriggerNotification.editedNotificationTrigger = selectedTrigger;
Intent notificationEditor = new Intent(ActivityManageRule.this, ActivityManageTriggerNotification.class);
notificationEditor.putExtra("edit", true);
startActivityForResult(notificationEditor, requestCodeTriggerNfcNotificationEdit);
break;
default:
break;
}
@ -288,15 +298,15 @@ public class ActivityManageRule extends Activity
// case setAirplaneMode:
// break;
case startOtherActivity:
Intent intent = new Intent(ActivityManageRule.this, ActivityManageStartActivity.class);
ActivityManageStartActivity.resultingAction = a;
Intent intent = new Intent(ActivityManageRule.this, ActivityManageActionStartActivity.class);
ActivityManageActionStartActivity.resultingAction = a;
intent.putExtra("edit", true);
startActivityForResult(intent, requestCodeActionStartActivityEdit);
break;
case triggerUrl:
Intent activityEditTriggerUrlIntent = new Intent(ActivityManageRule.this, ActivityEditTriggerUrl.class);
Intent activityEditTriggerUrlIntent = new Intent(ActivityManageRule.this, ActivityManageActionTriggerUrl.class);
// activityEditTriggerUrlIntent.putExtra("urlToTrigger", a.getParameter2());
ActivityEditTriggerUrl.resultingAction = a;
ActivityManageActionTriggerUrl.resultingAction = a;
activityEditTriggerUrlIntent.putExtra("edit", true);
startActivityForResult(activityEditTriggerUrlIntent, requestCodeActionTriggerUrlEdit);
break;
@ -321,24 +331,31 @@ public class ActivityManageRule extends Activity
// case wakeupDevice:
// break;
case speakText:
Intent activitySpeakTextIntent = new Intent(ActivityManageRule.this, ActivityEditSpeakText.class);
ActivityEditSpeakText.resultingAction = a;
Intent activitySpeakTextIntent = new Intent(ActivityManageRule.this, ActivityManageActionSpeakText.class);
ActivityManageActionSpeakText.resultingAction = a;
activitySpeakTextIntent.putExtra("edit", true);
startActivityForResult(activitySpeakTextIntent, requestCodeActionSpeakTextEdit);
break;
case sendTextMessage:
Intent activitySendTextMessageIntent = new Intent(ActivityManageRule.this, ActivityEditSendTextMessage.class);
ActivityEditSendTextMessage.resultingAction = a;
Intent activitySendTextMessageIntent = new Intent(ActivityManageRule.this, ActivityManageActionSendTextMessage.class);
ActivityManageActionSendTextMessage.resultingAction = a;
activitySendTextMessageIntent.putExtra("edit", true);
startActivityForResult(activitySendTextMessageIntent, requestCodeActionSendTextMessage);
break;
case setScreenBrightness:
Intent activityEditScreenBrightnessIntent = new Intent(ActivityManageRule.this, ActivityManageBrightnessSetting.class);
Intent activityEditScreenBrightnessIntent = new Intent(ActivityManageRule.this, ActivityManageActionBrightnessSetting.class);
// ActivityEditTriggerUrl.resultingAction = a;
activityEditScreenBrightnessIntent.putExtra("autoBrightness", a.getParameter1());
activityEditScreenBrightnessIntent.putExtra("brightnessValue", Integer.parseInt(a.getParameter2()));
startActivityForResult(activityEditScreenBrightnessIntent, requestCodeActionScreenBrightnessEdit);
break;
case playSound:
Intent actionPlaySoundIntent = new Intent(context, ActivityManageActionPlaySound.class);
actionPlaySoundIntent.putExtra("edit", true);
actionPlaySoundIntent.putExtra("actionParameter1", a.getParameter1());
actionPlaySoundIntent.putExtra("actionParameter2", a.getParameter2());
startActivityForResult(actionPlaySoundIntent, requestCodeActionPlaySoundEdit);
break;
default:
Miscellaneous.logEvent("w", "Edit action", "Editing of action type " + a.getAction().toString() + " not implemented, yet.", 4);
break;
@ -466,6 +483,8 @@ public class ActivityManageRule extends Activity
items.add(new Item(typesLong[i].toString(), R.drawable.bluetooth));
else if(types[i].toString().equals(Trigger_Enum.headsetPlugged.toString()))
items.add(new Item(typesLong[i].toString(), R.drawable.headphone));
else if(types[i].toString().equals(Trigger_Enum.notification.toString()))
items.add(new Item(typesLong[i].toString(), R.drawable.notification));
else
items.add(new Item(typesLong[i].toString(), R.drawable.placeholder));
}
@ -502,19 +521,27 @@ public class ActivityManageRule extends Activity
String[] booleanChoices = null;
if(triggerType == Trigger_Enum.pointOfInterest)
{
if(PointOfInterest.getPointOfInterestCollection() != null && PointOfInterest.getPointOfInterestCollection().size() > 0)
booleanChoices = new String[]{getResources().getString(R.string.entering), getResources().getString(R.string.leaving)};
if(Miscellaneous.googleToBlameForLocation(false))
{
ActivityMainScreen.openGoogleBlamingWindow();
return;
}
else
{
Toast.makeText(myContext, getResources().getString(R.string.noPoisSpecified), Toast.LENGTH_LONG).show();
return;
if (PointOfInterest.getPointOfInterestCollection() != null && PointOfInterest.getPointOfInterestCollection().size() > 0)
booleanChoices = new String[]{getResources().getString(R.string.entering), getResources().getString(R.string.leaving)};
else
{
Toast.makeText(myContext, getResources().getString(R.string.noPoisSpecified), Toast.LENGTH_LONG).show();
return;
}
}
}
else if(triggerType == Trigger_Enum.timeFrame)
{
newTrigger.setTriggerType(Trigger_Enum.timeFrame);
ActivityManageTimeFrame.editedTimeFrameTrigger = newTrigger;
Intent timeFrameEditor = new Intent(myContext, ActivityManageTimeFrame.class);
ActivityManageTriggerTimeFrame.editedTimeFrameTrigger = newTrigger;
Intent timeFrameEditor = new Intent(myContext, ActivityManageTriggerTimeFrame.class);
startActivityForResult(timeFrameEditor, requestCodeTriggerTimeframeAdd);
return;
}
@ -528,6 +555,13 @@ public class ActivityManageRule extends Activity
booleanChoices = new String[]{getResources().getString(R.string.connected), getResources().getString(R.string.disconnected)};
else if(triggerType == Trigger_Enum.process_started_stopped)
booleanChoices = new String[]{getResources().getString(R.string.started), getResources().getString(R.string.stopped)};
else if(triggerType == Trigger_Enum.notification)
{
newTrigger.setTriggerType(Trigger_Enum.notification);
Intent nfcEditor = new Intent(myContext, ActivityManageTriggerNotification.class);
startActivityForResult(nfcEditor, requestCodeTriggerNotificationAdd);
return;
}
else if(triggerType == Trigger_Enum.airplaneMode)
booleanChoices = new String[]{getResources().getString(R.string.activated), getResources().getString(R.string.deactivated)};
else if(triggerType == Trigger_Enum.roaming)
@ -564,7 +598,7 @@ public class ActivityManageRule extends Activity
if(NfcReceiver.checkNfcRequirements(ActivityManageRule.this, true))
{
newTrigger.setTriggerType(Trigger_Enum.nfcTag);
Intent nfcEditor = new Intent(myContext, ActivityManageNfc.class);
Intent nfcEditor = new Intent(myContext, ActivityManageTriggerNfc.class);
startActivityForResult(nfcEditor, requestCodeTriggerNfcTagAdd);
return;
}
@ -575,8 +609,8 @@ public class ActivityManageRule extends Activity
Miscellaneous.messageBox("Bluetooth", getResources().getString(R.string.deviceDoesNotHaveBluetooth), ActivityManageRule.this).show();;
newTrigger.setTriggerType(Trigger_Enum.bluetoothConnection);
ActivityManageBluetoothTrigger.editedBluetoothTrigger = newTrigger;
Intent bluetoothEditor = new Intent(myContext, ActivityManageBluetoothTrigger.class);
ActivityManageTriggerBluetooth.editedBluetoothTrigger = newTrigger;
Intent bluetoothEditor = new Intent(myContext, ActivityManageTriggerBluetooth.class);
startActivityForResult(bluetoothEditor, requestCodeTriggerBluetoothAdd);
return;
}
@ -971,7 +1005,7 @@ public class ActivityManageRule extends Activity
protected String[] doInBackground(ActivityManageRule... params)
{
// Looper.prepare();
final String[] applicationArray = ActivityManageStartActivity.getApplicationNameListString(params[0]);
final String[] applicationArray = ActivityManageActionStartActivity.getApplicationNameListString(params[0]);
return applicationArray;
}
@ -1018,7 +1052,7 @@ public class ActivityManageRule extends Activity
{
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(myContext);
alertDialogBuilder.setTitle(myContext.getResources().getString(R.string.selectPackageOfApplication));
final String[] packageArray = ActivityManageStartActivity.getPackageListString(myContext, applicationName);
final String[] packageArray = ActivityManageActionStartActivity.getPackageListString(myContext, applicationName);
alertDialogBuilder.setItems(packageArray, new DialogInterface.OnClickListener()
{
@Override
@ -1038,7 +1072,7 @@ public class ActivityManageRule extends Activity
{
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(myContext);
alertDialogBuilder.setTitle(myContext.getResources().getString(R.string.selectActivityToBeStarted));
final String activityArray[] = ActivityManageStartActivity.getActivityListForPackageName(packageName);
final String activityArray[] = ActivityManageActionStartActivity.getActivityListForPackageName(packageName);
alertDialogBuilder.setItems(activityArray, new DialogInterface.OnClickListener()
{
@Override
@ -1066,7 +1100,7 @@ public class ActivityManageRule extends Activity
if(resultCode == RESULT_OK)
{
//add TriggerUrl
ruleToEdit.getActionSet().add(ActivityEditTriggerUrl.resultingAction);
ruleToEdit.getActionSet().add(ActivityManageActionTriggerUrl.resultingAction);
this.refreshActionList();
}
}
@ -1081,7 +1115,7 @@ public class ActivityManageRule extends Activity
else if(requestCode == requestCodeTriggerTimeframeAdd)
{
//add TimeFrame
if(resultCode == RESULT_OK && ActivityManageTimeFrame.editedTimeFrameTrigger != null)
if(resultCode == RESULT_OK && ActivityManageTriggerTimeFrame.editedTimeFrameTrigger != null)
{
ruleToEdit.getTriggerSet().add(newTrigger);
this.refreshTriggerList();
@ -1092,7 +1126,7 @@ public class ActivityManageRule extends Activity
else if(requestCode == requestCodeTriggerTimeframeEdit)
{
//edit TimeFrame
if(resultCode == RESULT_OK && ActivityManageTimeFrame.editedTimeFrameTrigger != null)
if(resultCode == RESULT_OK && ActivityManageTriggerTimeFrame.editedTimeFrameTrigger != null)
{
this.refreshTriggerList();
}
@ -1104,7 +1138,7 @@ public class ActivityManageRule extends Activity
// manage start of other activity
if(resultCode == RESULT_OK)
{
newAction = ActivityManageStartActivity.resultingAction;
newAction = ActivityManageActionStartActivity.resultingAction;
ruleToEdit.getActionSet().add(newAction);
this.refreshActionList();
}
@ -1114,7 +1148,7 @@ public class ActivityManageRule extends Activity
// manage start of other activity
if(resultCode == RESULT_OK)
{
newAction = ActivityManageStartActivity.resultingAction;
newAction = ActivityManageActionStartActivity.resultingAction;
// ruleToEdit.getActionSet().add(newAction);
this.refreshActionList();
}
@ -1122,21 +1156,49 @@ public class ActivityManageRule extends Activity
else if(requestCode == requestCodeTriggerNfcTagAdd)
{
//add TimeFrame
if(resultCode == RESULT_OK && ActivityManageNfc.generatedId != null)
if(resultCode == RESULT_OK && ActivityManageTriggerNfc.generatedId != null)
{
newTrigger.setNfcTagId(ActivityManageNfc.generatedId);
newTrigger.setNfcTagId(ActivityManageTriggerNfc.generatedId);
ruleToEdit.getTriggerSet().add(newTrigger);
this.refreshTriggerList();
}
else
Miscellaneous.logEvent("w", "ActivityManageNfc", "No nfc id returned. Assuming abort.", 5);
}
else if(requestCode == requestCodeTriggerNotificationAdd)
{
//add notification
if(resultCode == RESULT_OK)
{
//newTrigger.setNfcTagId(ActivityManageNfc.generatedId);
ruleToEdit.getTriggerSet().add(newTrigger);
newTrigger.setTriggerParameter2(
data.getStringExtra("app") + Trigger.triggerParameter2Split +
data.getStringExtra("titleDir") + Trigger.triggerParameter2Split +
data.getStringExtra("title") + Trigger.triggerParameter2Split +
data.getStringExtra("textDir") + Trigger.triggerParameter2Split +
data.getStringExtra("text")
);
this.refreshTriggerList();
}
else
Miscellaneous.logEvent("w", "ActivityManageNfc", "No nfc id returned. Assuming abort.", 5);
}
else if(requestCode == requestCodeTriggerNfcNotificationEdit)
{
if(resultCode == RESULT_OK)
{
newTrigger = ActivityManageTriggerNotification.resultingTrigger;
this.refreshTriggerList();
}
}
else if(requestCode == requestCodeActionSpeakTextAdd)
{
if(resultCode == RESULT_OK)
{
//add SpeakText
ruleToEdit.getActionSet().add(ActivityEditSpeakText.resultingAction);
ruleToEdit.getActionSet().add(ActivityManageActionSpeakText.resultingAction);
this.refreshActionList();
}
}
@ -1145,14 +1207,14 @@ public class ActivityManageRule extends Activity
if(resultCode == RESULT_OK)
{
//add SpeakText
ruleToEdit.getActionSet().add(ActivityEditSendTextMessage.resultingAction);
ruleToEdit.getActionSet().add(ActivityManageActionSendTextMessage.resultingAction);
this.refreshActionList();
}
}
else if(requestCode == requestCodeTriggerBluetoothAdd)
{
//add bluetooth trigger
if(resultCode == RESULT_OK && ActivityManageBluetoothTrigger.editedBluetoothTrigger != null)
if(resultCode == RESULT_OK && ActivityManageTriggerBluetooth.editedBluetoothTrigger != null)
{
ruleToEdit.getTriggerSet().add(newTrigger);
this.refreshTriggerList();
@ -1163,7 +1225,7 @@ public class ActivityManageRule extends Activity
else if(requestCode == requestCodeTriggerBluetoothEdit)
{
//edit bluetooth trigger
if(resultCode == RESULT_OK && ActivityManageBluetoothTrigger.editedBluetoothTrigger != null)
if(resultCode == RESULT_OK && ActivityManageTriggerBluetooth.editedBluetoothTrigger != null)
{
this.refreshTriggerList();
}
@ -1193,6 +1255,29 @@ public class ActivityManageRule extends Activity
this.refreshActionList();
}
}
else if(requestCode == requestCodeActionPlaySoundAdd)
{
if(resultCode == RESULT_OK)
{
newAction.setParameter1(data.getBooleanExtra("actionParameter1", false));
newAction.setParameter2(data.getStringExtra("actionParameter2"));
ruleToEdit.getActionSet().add(newAction);
this.refreshActionList();
}
}
else if(requestCode == requestCodeActionPlaySoundEdit)
{
if(resultCode == RESULT_OK)
{
if(data.hasExtra("actionParameter1"))
ruleToEdit.getActionSet().get(editIndex).setParameter1(data.getBooleanExtra("actionParameter1", false));
if(data.hasExtra("actionParameter2"))
ruleToEdit.getActionSet().get(editIndex).setParameter2(data.getStringExtra("actionParameter2"));
this.refreshActionList();
}
}
//TODO: Check with has data been changed or something like that.
/*try
@ -1245,6 +1330,8 @@ public class ActivityManageRule extends Activity
items.add(new Item(typesLong[i].toString(), R.drawable.tune));
else if(types[i].toString().equals(Action_Enum.setScreenBrightness.toString()))
items.add(new Item(typesLong[i].toString(), R.drawable.brightness));
else if(types[i].toString().equals(Action_Enum.playSound.toString()))
items.add(new Item(typesLong[i].toString(), R.drawable.sound));
else if(types[i].toString().equals(Action_Enum.sendTextMessage.toString()))
{
// if(ActivityPermissions.isPermissionDeclaratedInManifest(ActivityManageSpecificRule.this, "android.permission.SEND_SMS") && !Miscellaneous.isGooglePlayInstalled(ActivityManageSpecificRule.this))
@ -1254,159 +1341,158 @@ public class ActivityManageRule extends Activity
else
items.add(new Item(typesLong[i].toString(), R.drawable.placeholder));
}
// = {
// new Item("Bluetooth", R.drawable.bluetooth),
// new Item("Wifi", R.drawable.wifi),
// new Item("...", 0), //no icon for this one
// };
// ListAdapter adapter = new ArrayAdapter<Item>(this, android.R.layout.select_dialog_item, android.R.id.text1, items)
ListAdapter adapter = new ArrayAdapter<Item>(this, android.R.layout.select_dialog_item, android.R.id.text1, items)
{
public View getView(int position, View convertView, ViewGroup parent)
{
//User super class to create the View
View v = super.getView(position, convertView, parent);
TextView tv = (TextView)v.findViewById(android.R.id.text1);
ListAdapter adapter = new ArrayAdapter<Item>(this, android.R.layout.select_dialog_item, android.R.id.text1, items)
{
public View getView(int position, View convertView, ViewGroup parent)
{
//User super class to create the View
View v = super.getView(position, convertView, parent);
//Put the image on the TextView
tv.setCompoundDrawablesWithIntrinsicBounds(items.get(position).icon, 0, 0, 0);
TextView tv = (TextView)v.findViewById(android.R.id.text1);
//Add margin between image and text (support various screen densities)
int dp5 = (int) (5 * getResources().getDisplayMetrics().density + 0.5f);
tv.setCompoundDrawablePadding(dp5);
//Put the image on the TextView
tv.setCompoundDrawablesWithIntrinsicBounds(items.get(position).icon, 0, 0, 0);
return v;
}
};
//Add margin between image and text (support various screen densities)
int dp5 = (int) (5 * getResources().getDisplayMetrics().density + 0.5f);
tv.setCompoundDrawablePadding(dp5);
AlertDialog.Builder builder = new AlertDialog.Builder(this)
.setTitle(getResources().getString(R.string.selectTypeOfAction))
.setAdapter(adapter, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which)
{
newAction = new Action();
if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.triggerUrl.toString()))
return v;
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(this)
.setTitle(getResources().getString(R.string.selectTypeOfAction))
.setAdapter(adapter, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which)
{
newAction = new Action();
if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.triggerUrl.toString()))
{
//launch other activity to enter a url and parameters;
newAction.setAction(Action_Enum.triggerUrl);
ActivityManageActionTriggerUrl.resultingAction = null;
Intent editTriggerIntent = new Intent(context, ActivityManageActionTriggerUrl.class);
startActivityForResult(editTriggerIntent, 1000);
}
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();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setBluetooth.toString()))
{
if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH))
Miscellaneous.messageBox("Bluetooth", getResources().getString(R.string.deviceDoesNotHaveBluetooth), ActivityManageRule.this).show();;
newAction.setAction(Action_Enum.setBluetooth);
getActionParameter1Dialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setUsbTethering.toString()))
{
newAction.setAction(Action_Enum.setUsbTethering);
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1)
Toast.makeText(context, context.getResources().getString(R.string.usbTetheringFailForAboveGingerbread), Toast.LENGTH_LONG).show();
getActionParameter1Dialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setWifiTethering.toString()))
{
newAction.setAction(Action_Enum.setWifiTethering);
getActionParameter1Dialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setDisplayRotation.toString()))
{
newAction.setAction(Action_Enum.setDisplayRotation);
getActionParameter1Dialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.changeSoundProfile.toString()))
{
if(Profile.getProfileCollection().size() > 0)
{
//launch other activity to enter a url and parameters;
newAction.setAction(Action_Enum.triggerUrl);
ActivityEditTriggerUrl.resultingAction = null;
Intent editTriggerIntent = new Intent(context, ActivityEditTriggerUrl.class);
startActivityForResult(editTriggerIntent, 1000);
newAction.setAction(Action_Enum.changeSoundProfile);
getActionSoundProfileDialog(context).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setWifi.toString()))
else
Toast.makeText(context, getResources().getString(R.string.noProfilesCreateOneFirst), Toast.LENGTH_LONG).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.startOtherActivity.toString()))
{
newAction.setAction(Action_Enum.startOtherActivity);
Intent intent = new Intent(ActivityManageRule.this, ActivityManageActionStartActivity.class);
startActivityForResult(intent, 3000);
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.waitBeforeNextAction.toString()))
{
newAction.setAction(Action_Enum.waitBeforeNextAction);
getActionWaitBeforeNextActionDialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.wakeupDevice.toString()))
{
newAction.setAction(Action_Enum.wakeupDevice);
getActionWakeupDeviceDialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setAirplaneMode.toString()))
{
if(Build.VERSION.SDK_INT >= 17)
{
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();
Toast.makeText(context, getResources().getString(R.string.airplaneModeSdk17Warning), Toast.LENGTH_LONG).show();
Miscellaneous.messageBox(getResources().getString(R.string.airplaneMode), getResources().getString(R.string.rootExplanation), ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setBluetooth.toString()))
{
if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH))
Miscellaneous.messageBox("Bluetooth", getResources().getString(R.string.deviceDoesNotHaveBluetooth), ActivityManageRule.this).show();;
newAction.setAction(Action_Enum.setBluetooth);
getActionParameter1Dialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setUsbTethering.toString()))
{
newAction.setAction(Action_Enum.setUsbTethering);
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1)
Toast.makeText(context, context.getResources().getString(R.string.usbTetheringFailForAboveGingerbread), Toast.LENGTH_LONG).show();
getActionParameter1Dialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setWifiTethering.toString()))
{
newAction.setAction(Action_Enum.setWifiTethering);
getActionParameter1Dialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setDisplayRotation.toString()))
{
newAction.setAction(Action_Enum.setDisplayRotation);
getActionParameter1Dialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.changeSoundProfile.toString()))
{
if(Profile.getProfileCollection().size() > 0)
{
newAction.setAction(Action_Enum.changeSoundProfile);
getActionSoundProfileDialog(context).show();
}
else
Toast.makeText(context, getResources().getString(R.string.noProfilesCreateOneFirst), Toast.LENGTH_LONG).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.startOtherActivity.toString()))
{
newAction.setAction(Action_Enum.startOtherActivity);
Intent intent = new Intent(ActivityManageRule.this, ActivityManageStartActivity.class);
startActivityForResult(intent, 3000);
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.waitBeforeNextAction.toString()))
{
newAction.setAction(Action_Enum.waitBeforeNextAction);
getActionWaitBeforeNextActionDialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.wakeupDevice.toString()))
{
newAction.setAction(Action_Enum.wakeupDevice);
getActionWakeupDeviceDialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setAirplaneMode.toString()))
{
if(Build.VERSION.SDK_INT >= 17)
{
Toast.makeText(context, getResources().getString(R.string.airplaneModeSdk17Warning), Toast.LENGTH_LONG).show();
Miscellaneous.messageBox(getResources().getString(R.string.airplaneMode), getResources().getString(R.string.rootExplanation), ActivityManageRule.this).show();
}
newAction.setAction(Action_Enum.setAirplaneMode);
getActionParameter1Dialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setDataConnection.toString()))
{
newAction.setAction(Action_Enum.setDataConnection);
getActionParameter1Dialog(ActivityManageRule.this).show();
Miscellaneous.messageBox(getResources().getString(R.string.actionDataConnection), getResources().getString(R.string.rootExplanation), ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.speakText.toString()))
{
//launch other activity to enter a url and parameters;
newAction.setAction(Action_Enum.speakText);
ActivityEditSpeakText.resultingAction = null;
Intent editTriggerIntent = new Intent(context, ActivityEditSpeakText.class);
startActivityForResult(editTriggerIntent, 5000);
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.sendTextMessage.toString()))
{
newAction.setAction(Action_Enum.setAirplaneMode);
getActionParameter1Dialog(ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setDataConnection.toString()))
{
newAction.setAction(Action_Enum.setDataConnection);
getActionParameter1Dialog(ActivityManageRule.this).show();
Miscellaneous.messageBox(getResources().getString(R.string.actionDataConnection), getResources().getString(R.string.rootExplanation), ActivityManageRule.this).show();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.speakText.toString()))
{
//launch other activity to enter a url and parameters;
newAction.setAction(Action_Enum.speakText);
ActivityManageActionSpeakText.resultingAction = null;
Intent editTriggerIntent = new Intent(context, ActivityManageActionSpeakText.class);
startActivityForResult(editTriggerIntent, 5000);
}
else if(Action.getActionTypesAsArray()[which].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"))
{
//launch other activity to enter parameters;
newAction.setAction(Action_Enum.sendTextMessage);
ActivityEditSendTextMessage.resultingAction = null;
Intent editTriggerIntent = new Intent(context, ActivityEditSendTextMessage.class);
startActivityForResult(editTriggerIntent, 5001);
}
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.playMusic.toString()))
if(ActivityPermissions.isPermissionDeclaratedInManifest(ActivityManageRule.this, "android.permission.SEND_SMS"))
{
newAction.setAction(Action_Enum.playMusic);
ruleToEdit.getActionSet().add(newAction);
refreshActionList();
//launch other activity to enter parameters;
newAction.setAction(Action_Enum.sendTextMessage);
ActivityManageActionSendTextMessage.resultingAction = null;
Intent editTriggerIntent = new Intent(context, ActivityManageActionSendTextMessage.class);
startActivityForResult(editTriggerIntent, 5001);
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setScreenBrightness.toString()))
{
newAction.setAction(Action_Enum.setScreenBrightness);
Intent actionScreenBrightnessIntent = new Intent(context, ActivityManageBrightnessSetting.class);
startActivityForResult(actionScreenBrightnessIntent, requestCodeActionScreenBrightnessAdd);
}
}
});
return builder.create();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.playMusic.toString()))
{
newAction.setAction(Action_Enum.playMusic);
ruleToEdit.getActionSet().add(newAction);
refreshActionList();
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setScreenBrightness.toString()))
{
newAction.setAction(Action_Enum.setScreenBrightness);
Intent actionScreenBrightnessIntent = new Intent(context, ActivityManageActionBrightnessSetting.class);
startActivityForResult(actionScreenBrightnessIntent, requestCodeActionScreenBrightnessAdd);
}
else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.playSound.toString()))
{
newAction.setAction(Action_Enum.playSound);
Intent actionPlaySoundIntent = new Intent(context, ActivityManageActionPlaySound.class);
startActivityForResult(actionPlaySoundIntent, requestCodeActionPlaySoundAdd);
}
}
});
return builder.create();
}
private AlertDialog getActionSoundProfileDialog(final Context myContext)
{

View File

@ -16,7 +16,7 @@ import android.widget.Toast;
import com.jens.automation2.receivers.BluetoothReceiver;
public class ActivityManageBluetoothTrigger extends Activity
public class ActivityManageTriggerBluetooth extends Activity
{
protected static Trigger editedBluetoothTrigger;
RadioButton radioAnyBluetoothDevice, radioNoDevice, radioDeviceFromList, radioBluetoothConnected, radioBluetoothDisconnected, radioBluetoothInRange, radioBluetoothOutRange;
@ -109,7 +109,7 @@ public class ActivityManageBluetoothTrigger extends Activity
}
else
{
Toast.makeText(ActivityManageBluetoothTrigger.this, getResources().getString(R.string.selectDeviceOption), Toast.LENGTH_LONG).show();
Toast.makeText(ActivityManageTriggerBluetooth.this, getResources().getString(R.string.selectDeviceOption), Toast.LENGTH_LONG).show();
return false;
}
@ -138,7 +138,7 @@ public class ActivityManageBluetoothTrigger extends Activity
}
else
{
Toast.makeText(ActivityManageBluetoothTrigger.this, getResources().getString(R.string.selectConnectionOption), Toast.LENGTH_LONG).show();
Toast.makeText(ActivityManageTriggerBluetooth.this, getResources().getString(R.string.selectConnectionOption), Toast.LENGTH_LONG).show();
return false;
}

View File

@ -21,7 +21,7 @@ import android.widget.Toast;
import com.jens.automation2.receivers.NfcReceiver;
@SuppressLint("NewApi")
public class ActivityManageNfc extends Activity
public class ActivityManageTriggerNfc extends Activity
{
public static String generatedId = null;
private static Tag discoveredTag = null;
@ -39,7 +39,7 @@ public class ActivityManageNfc extends Activity
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.manage_nfc);
setContentView(R.layout.activity_manage_trigger_nfc);
etNewNfcIdValue = (EditText)findViewById(R.id.etNewNfcIdValue);
bReadNfcTag = (Button)findViewById(R.id.bReadNfcTag);
@ -59,7 +59,7 @@ public class ActivityManageNfc extends Activity
}
else
{
progressDialog = ProgressDialog.show(ActivityManageNfc.this, null, getResources().getString(R.string.nfcBringTagIntoRange), false, true, new OnCancelListener()
progressDialog = ProgressDialog.show(ActivityManageTriggerNfc.this, null, getResources().getString(R.string.nfcBringTagIntoRange), false, true, new OnCancelListener()
{
@Override
public void onCancel(DialogInterface dialog)
@ -88,7 +88,7 @@ public class ActivityManageNfc extends Activity
}
else
{
progressDialog = ProgressDialog.show(ActivityManageNfc.this, null, getResources().getString(R.string.nfcBringTagIntoRange), false, true, new OnCancelListener()
progressDialog = ProgressDialog.show(ActivityManageTriggerNfc.this, null, getResources().getString(R.string.nfcBringTagIntoRange), false, true, new OnCancelListener()
{
@Override
public void onCancel(DialogInterface dialog)
@ -118,7 +118,7 @@ public class ActivityManageNfc extends Activity
}
else
{
progressDialog = ProgressDialog.show(ActivityManageNfc.this, null, getResources().getString(R.string.nfcBringTagIntoRange), false, true, new OnCancelListener()
progressDialog = ProgressDialog.show(ActivityManageTriggerNfc.this, null, getResources().getString(R.string.nfcBringTagIntoRange), false, true, new OnCancelListener()
{
@Override
public void onCancel(DialogInterface dialog)
@ -255,7 +255,7 @@ public class ActivityManageNfc extends Activity
if(generatedId.length() == 0)
{
generatedId = null;
Toast.makeText(ActivityManageNfc.this, getResources().getString(R.string.nfcEnterValidIdentifier), Toast.LENGTH_LONG).show();
Toast.makeText(ActivityManageTriggerNfc.this, getResources().getString(R.string.nfcEnterValidIdentifier), Toast.LENGTH_LONG).show();
return false;
}
else
@ -267,14 +267,14 @@ public class ActivityManageNfc extends Activity
if(NfcReceiver.writeTag(generatedId, discoveredTag))
{
currentStatus = 0;
Toast.makeText(ActivityManageNfc.this, getResources().getString(R.string.nfcTagWrittenSuccessfully), Toast.LENGTH_LONG).show();
Toast.makeText(ActivityManageTriggerNfc.this, getResources().getString(R.string.nfcTagWrittenSuccessfully), Toast.LENGTH_LONG).show();
setResult(RESULT_OK);
finish();
}
else
{
currentStatus = 0;
Toast.makeText(ActivityManageNfc.this, getResources().getString(R.string.nfcTagWriteError), Toast.LENGTH_LONG).show();
Toast.makeText(ActivityManageTriggerNfc.this, getResources().getString(R.string.nfcTagWriteError), Toast.LENGTH_LONG).show();
Miscellaneous.logEvent("e", "NFC", getResources().getString(R.string.nfcTagWriteError), 2);
}
}
@ -285,14 +285,14 @@ public class ActivityManageNfc extends Activity
if(checkEnteredText(false))
{
currentStatus = 0;
Toast.makeText(ActivityManageNfc.this, getResources().getString(R.string.nfcTagReadSuccessfully), Toast.LENGTH_LONG).show();
Toast.makeText(ActivityManageTriggerNfc.this, getResources().getString(R.string.nfcTagReadSuccessfully), Toast.LENGTH_LONG).show();
setResult(RESULT_OK);
finish();
}
else
{
currentStatus = 0;
Toast.makeText(ActivityManageNfc.this, getResources().getString(R.string.nfcValueNotSuitable), Toast.LENGTH_LONG).show();
Toast.makeText(ActivityManageTriggerNfc.this, getResources().getString(R.string.nfcValueNotSuitable), Toast.LENGTH_LONG).show();
generatedId = null;
}
}

View File

@ -0,0 +1,399 @@
package com.jens.automation2;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import static com.jens.automation2.Trigger.triggerParameter2Split;
public class ActivityManageTriggerNotification extends Activity
{
public static Trigger editedNotificationTrigger;
EditText etNotificationTitle, etNotificationText;
Button bSelectApp, bSaveTriggerNotification;
Spinner spinnerTitleDirection, spinnerTextDirection;
TextView tvSelectedApplication;
CheckBox chkNotificationDirection;
boolean edit = false;
ProgressDialog progressDialog = null;
private static List<PackageInfo> pInfos = null;
public static Trigger resultingTrigger;
private static String[] directions;
ArrayAdapter<String> directionSpinnerAdapter;
public static void getActivityList(final Context context)
{
if(pInfos == null)
{
pInfos = context.getPackageManager().getInstalledPackages(PackageManager.GET_ACTIVITIES);
Collections.sort(pInfos, new Comparator<PackageInfo>()
{
public int compare(PackageInfo obj1, PackageInfo obj2)
{
String name1 = "";
String name2 = "";
ApplicationInfo aInfo1 = obj1.applicationInfo;
if (aInfo1 != null)
{
name1 = (String) context.getPackageManager().getApplicationLabel(aInfo1);
}
ApplicationInfo aInfo2 = obj2.applicationInfo;
if (aInfo2 != null)
{
name2 = (String) context.getPackageManager().getApplicationLabel(aInfo2);
}
return name1.compareTo(name2);
}
});
}
}
public static String[] getApplicationNameListString(Context myContext)
{
// Generate the actual list
getActivityList(myContext);
ArrayList<String> returnList = new ArrayList<String>();
for (PackageInfo pInfo : pInfos)
{
ApplicationInfo aInfo = pInfo.applicationInfo;
if (aInfo != null)
{
String aLabel;
aLabel = (String) myContext.getPackageManager().getApplicationLabel(aInfo);
ActivityInfo[] aInfos = pInfo.activities;
if (aInfos != null && aInfos.length > 0) // Only put Applications into the list that have packages.
{
if(!returnList.contains(aLabel))
returnList.add(aLabel);
}
}
}
return returnList.toArray(new String[returnList.size()]);
}
public static String[] getPackageListString(Context myContext, String applicationLabel)
{
// Generate the actual list
getActivityList(myContext);
ArrayList<String> returnList = new ArrayList<String>();
for (PackageInfo pInfo : pInfos)
{
if(myContext.getPackageManager().getApplicationLabel(pInfo.applicationInfo).equals(applicationLabel))
{
ActivityInfo[] aInfos = pInfo.activities;
if (aInfos != null && aInfos.length > 0)
{
returnList.add(pInfo.packageName);
}
}
}
return returnList.toArray(new String[returnList.size()]);
}
public static String[] getPackageListString(Context myContext)
{
// Generate the actual list
getActivityList(myContext);
ArrayList<String> returnList = new ArrayList<String>();
for (PackageInfo pInfo : pInfos)
{
ActivityInfo[] aInfos = pInfo.activities;
if (aInfos != null && aInfos.length > 0)
{
returnList.add(pInfo.packageName);
}
else
Miscellaneous.logEvent("w", "Empty Application", "Application " + myContext.getPackageManager().getApplicationLabel(pInfo.applicationInfo) + " doesn\'t have packages.", 5);
}
return returnList.toArray(new String[returnList.size()]);
}
public static String[] getActivityListForPackageName(String packageName)
{
ArrayList<String> returnList = new ArrayList<String>();
for (PackageInfo pInfo : pInfos)
{
if(pInfo.packageName.equals(packageName))
{
ActivityInfo[] aInfos = pInfo.activities;
if (aInfos != null)
{
for (ActivityInfo activityInfo : aInfos)
{
returnList.add(activityInfo.name);
}
}
}
}
return returnList.toArray(new String[returnList.size()]);
}
public static ActivityInfo getActivityInfoForPackageNameAndActivityName(String packageName, String activityName)
{
for (PackageInfo pInfo : pInfos)
{
if(pInfo.packageName.equals(packageName))
{
ActivityInfo[] aInfos = pInfo.activities;
if (aInfos != null)
{
for (ActivityInfo activityInfo : aInfos)
{
if(activityInfo.name.equals(activityName))
return activityInfo;
}
}
}
}
return null;
}
private AlertDialog getActionStartActivityDialog1()
{
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
alertDialogBuilder.setTitle(getResources().getString(R.string.selectApplication));
final String[] applicationArray = ActivityManageTriggerNotification.getApplicationNameListString(this);
alertDialogBuilder.setItems(applicationArray, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
getActionStartActivityDialog2(applicationArray[which]).show();
}
});
AlertDialog alertDialog = alertDialogBuilder.create();
return alertDialog;
}
private AlertDialog getActionStartActivityDialog2(String applicationName)
{
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
alertDialogBuilder.setTitle(getResources().getString(R.string.selectPackageOfApplication));
final String[] packageArray = ActivityManageTriggerNotification.getPackageListString(this, applicationName);
alertDialogBuilder.setItems(packageArray, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
//getActionStartActivityDialog3(packageArray[which]).show();
//Miscellaneous.messageBox(getResources().getString(R.string.hint), getResources().getString(R.string.chooseActivityHint), ActivityManageNotificationTrigger.this).show();
tvSelectedApplication.setText(packageArray[which]);
}
});
AlertDialog alertDialog = alertDialogBuilder.create();
return alertDialog;
}
private AlertDialog getActionStartActivityDialog3(final String packageName)
{
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
alertDialogBuilder.setTitle(getResources().getString(R.string.selectActivityToBeStarted));
final String activityArray[] = ActivityManageTriggerNotification.getActivityListForPackageName(packageName);
alertDialogBuilder.setItems(activityArray, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
ActivityInfo ai = ActivityManageTriggerNotification.getActivityInfoForPackageNameAndActivityName(packageName, activityArray[which]);
tvSelectedApplication.setText(ai.packageName + ";" + ai.name);
}
});
AlertDialog alertDialog = alertDialogBuilder.create();
return alertDialog;
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.manage_trigger_notification);
etNotificationTitle = (EditText)findViewById(R.id.etNotificationTitle);
etNotificationText = (EditText)findViewById(R.id.etNotificationText);
bSelectApp = (Button)findViewById(R.id.bSelectApp);
bSaveTriggerNotification = (Button)findViewById(R.id.bSaveTriggerNotification);
spinnerTitleDirection = (Spinner)findViewById(R.id.spinnerTitleDirection);
spinnerTextDirection = (Spinner)findViewById(R.id.spinnerTextDirection);
tvSelectedApplication = (TextView)findViewById(R.id.etActivityOrActionPath);
chkNotificationDirection = (CheckBox)findViewById(R.id.chkNotificationDirection);
directions = new String[] {
getResources().getString(R.string.directionStringEquals),
getResources().getString(R.string.directionStringContains),
getResources().getString(R.string.directionStringStartsWith),
getResources().getString(R.string.directionStringEndsWith),
getResources().getString(R.string.directionStringNotEquals)
};
directionSpinnerAdapter = new ArrayAdapter<String>(this, R.layout.text_view_for_poi_listview_mediumtextsize, ActivityManageTriggerNotification.directions);
spinnerTitleDirection.setAdapter(directionSpinnerAdapter);
spinnerTextDirection.setAdapter(directionSpinnerAdapter);
directionSpinnerAdapter.notifyDataSetChanged();
bSelectApp.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
GetActivityListTask getActivityListTask = new GetActivityListTask();
getActivityListTask.execute();
progressDialog = ProgressDialog.show(ActivityManageTriggerNotification.this, "", ActivityManageTriggerNotification.this.getResources().getString(R.string.gettingListOfInstalledApplications));
}
});
chkNotificationDirection.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
{
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
{
if(isChecked)
chkNotificationDirection.setText(getResources().getString(R.string.notificationAppears));
else
chkNotificationDirection.setText(getResources().getString(R.string.notificationDisappears));
}
});
bSaveTriggerNotification.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
String app;
if(tvSelectedApplication.getText().toString().equalsIgnoreCase(getResources().getString(R.string.anyApp)))
app = "-1";
else
app = tvSelectedApplication.getText().toString();
String titleDir = Trigger.getMatchCode(spinnerTitleDirection.getSelectedItem().toString());
String title = etNotificationTitle.getText().toString();
String textDir = Trigger.getMatchCode(spinnerTextDirection.getSelectedItem().toString());
String text = etNotificationText.getText().toString();
if(edit)
{
editedNotificationTrigger.setTriggerParameter(chkNotificationDirection.isChecked());
editedNotificationTrigger.setTriggerParameter2(app + triggerParameter2Split + titleDir + triggerParameter2Split + title + triggerParameter2Split + textDir + triggerParameter2Split + text);
ActivityManageTriggerNotification.this.setResult(RESULT_OK);
}
else
{
Intent data = new Intent();
data.putExtra("direction", chkNotificationDirection.isChecked());
data.putExtra("app", app);
data.putExtra("titleDir", titleDir);
data.putExtra("title", title);
data.putExtra("textDir", textDir);
data.putExtra("text", text);
ActivityManageTriggerNotification.this.setResult(RESULT_OK, data);
}
finish();
}
});
Intent i = getIntent();
if(i.getBooleanExtra("edit", false) == true)
{
edit = true;
loadValuesIntoGui();
}
}
private void loadValuesIntoGui()
{
chkNotificationDirection.setChecked(editedNotificationTrigger.getTriggerParameter());
String[] params = editedNotificationTrigger.getTriggerParameter2().split(triggerParameter2Split);
String app = params[0];
String titleDir = params[1];
String title = params[2];
String textDir = params[3];
String text;
if (params.length >= 5)
text = params[4];
else
text = "";
if(!app.equals("-1"))
tvSelectedApplication.setText(app);
for(int i = 0; i < directions.length; i++)
{
if(Trigger.getMatchCode(directions[i]).equalsIgnoreCase(titleDir))
spinnerTitleDirection.setSelection(i);
if(Trigger.getMatchCode(directions[i]).equalsIgnoreCase(textDir))
spinnerTextDirection.setSelection(i);
}
if(title.length() > 0)
etNotificationTitle.setText(title);
if(text.length() > 0)
etNotificationText.setText(text);
}
private class GetActivityListTask extends AsyncTask<Void, Void, Void>
{
@Override
protected Void doInBackground(Void... params)
{
getActivityList(ActivityManageTriggerNotification.this);
return null;
}
@Override
protected void onPostExecute(Void result)
{
progressDialog.dismiss();
getActionStartActivityDialog1().show();
}
}
}

View File

@ -14,7 +14,7 @@ import java.sql.Time;
import java.util.ArrayList;
import java.util.Calendar;
public class ActivityManageTimeFrame extends Activity
public class ActivityManageTriggerTimeFrame extends Activity
{
Button bSaveTimeFrame;
TimePicker startPicker, stopPicker;
@ -27,7 +27,7 @@ public class ActivityManageTimeFrame extends Activity
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.trigger_timeframe_editor);
setContentView(R.layout.manage_trigger_timeframe);
startPicker = (TimePicker)findViewById(R.id.tpTimeFrameStart);
stopPicker = (TimePicker)findViewById(R.id.tpTimeFrameStop);

View File

@ -2,10 +2,12 @@ package com.jens.automation2;
import android.app.Activity;
import android.app.NotificationManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@ -15,6 +17,8 @@ import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.jens.automation2.receivers.NotificationListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@ -34,12 +38,13 @@ public class ActivityPermissions extends Activity
private static final int requestCodeForPermissionsWriteSettings = 12043;
private static final int requestCodeForPermissionsNotificationPolicy = 12044;
private static final int requestCodeForPermissionsBackgroundLocation = 12045;
private static final int requestCodeForPermissionsNotifications = 12046;
protected String[] specificPermissionsToRequest = null;
public static String intentExtraName = "permissionsToBeRequested";
Button bCancelPermissions, bRequestPermissions;
TextView tvPermissionsExplanation, tvPermissionosExplanationSystemSettings, tvPermissionsExplanationLong;
TextView tvPermissionsExplanation, tvPermissionsExplanationSystemSettings, tvPermissionsExplanationLong;
static ActivityPermissions instance = null;
public static final String writeSystemSettingsPermissionName = "android.permission.WRITE_SETTINGS";
@ -50,6 +55,8 @@ public class ActivityPermissions extends Activity
public static final String permissionNameLocationBackground = "android.permission.ACCESS_BACKGROUND_LOCATION";
public static final String permissionNameCall = "android.permission.PROCESS_OUTGOING_CALLS";
public static final String permissionNameStartService = "android.permission.FOREGROUND_SERVICE";
public static final String permissionNameReadNotifications = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
public static final String permissionNameWireguard = "com.wireguard.android.permission.CONTROL_TUNNELS";
public static ActivityPermissions getInstance()
{
@ -72,7 +79,7 @@ public class ActivityPermissions extends Activity
bCancelPermissions = (Button)findViewById(R.id.bCancelPermissions);
bRequestPermissions = (Button)findViewById(R.id.bRequestPermissions);
tvPermissionsExplanation = (TextView)findViewById(R.id.tvPermissionsExplanation);
tvPermissionosExplanationSystemSettings = (TextView)findViewById(R.id.tvPermissionsExplanationSystemSettings);
tvPermissionsExplanationSystemSettings = (TextView)findViewById(R.id.tvPermissionsExplanationSystemSettings);
tvPermissionsExplanationLong = (TextView)findViewById(R.id.tvPermissionsExplanationLong);
bCancelPermissions.setOnClickListener(new View.OnClickListener()
@ -168,8 +175,6 @@ public class ActivityPermissions extends Activity
explanation.append(
"<br />" +
"<u>" +
getResources().getString(R.string.readLocation)
+ "</u>"
@ -185,16 +190,19 @@ public class ActivityPermissions extends Activity
}
else
{
explanation.append(
explanation.append("<br /><u>");
"<br />" +
try
{
explanation.append(getResources().getString(getResources().getIdentifier(s, "string", getPackageName())));
}
catch(Resources.NotFoundException e)
{
Miscellaneous.logEvent("w", "ActivityPermissions", "Could not find translation for " + s, 4);
explanation.append(s);
}
"<u>" +
getResources().getString(getResources().getIdentifier(s, "string", getPackageName()))
+ "</u>"
+ "<br />");
explanation.append("</u><br />");
for (String reason : getReasonForPermission(s))
explanation.append(reason + "<br />");
@ -208,15 +216,16 @@ public class ActivityPermissions extends Activity
if (s.equalsIgnoreCase(writeSystemSettingsPermissionName))
{
if (requiredPerms.length == 1)
tvPermissionosExplanationSystemSettings.setText(getResources().getString(R.string.systemSettingsNote1));
tvPermissionsExplanationSystemSettings.setText(getResources().getString(R.string.systemSettingsNote1));
else if (requiredPerms.length > 1)
tvPermissionosExplanationSystemSettings.setText(getResources().getString(R.string.systemSettingsNote1) + getResources().getString(R.string.systemSettingsNote2));
tvPermissionsExplanationSystemSettings.setText(getResources().getString(R.string.systemSettingsNote1) + getResources().getString(R.string.systemSettingsNote2));
break;
}
}
ActivityMainScreen.updateMainScreen();
ActivityMainRules.getInstance().updateListView();
}
protected static void addToArrayListUnique(String value, ArrayList<String> list)
@ -231,8 +240,27 @@ public class ActivityPermissions extends Activity
{
for (String s : getRequiredPermissions(false))
{
if (!havePermission(s, context))
return true;
if(
s.equalsIgnoreCase(permissionNameLocationBackground)
||
s.equalsIgnoreCase(permissionNameLocationFine)
||
s.equalsIgnoreCase(permissionNameLocationCoarse)
)
{
if (!Miscellaneous.googleToBlameForLocation(true))
if (!havePermission(s, context))
return true;
}
else if(s.equalsIgnoreCase("android.permission.ACTIVITY_RECOGNITION") || s.equalsIgnoreCase("com.google.android.gms.permission.ACTIVITY_RECOGNITION"))
{
if(!BuildConfig.FLAVOR.equalsIgnoreCase("fdroidFlavor"))
if (!havePermission(s, context))
return true;
}
else
if (!havePermission(s, context))
return true;
}
}
@ -255,6 +283,10 @@ public class ActivityPermissions extends Activity
else
return true;
}
else if (s.equals(permissionNameReadNotifications))
{
return verifyNotificationPermission();
}
else
{
int res = context.checkCallingOrSelfPermission(s);
@ -284,12 +316,15 @@ public class ActivityPermissions extends Activity
// if (!havePermission(ActivityPermissions.writeExternalStoragePermissionName, workingContext))
// addToArrayListUnique(ActivityPermissions.writeExternalStoragePermissionName, requiredPermissions);
for(Profile profile : Profile.getProfileCollection())
if(!havePermission(writeSystemSettingsPermissionName, workingContext))
{
if(profile.changeIncomingCallsRingtone)
{
addToArrayListUnique("android.permission.WRITE_SETTINGS", requiredPermissions);
}
for (Profile profile : Profile.getProfileCollection())
{
if (profile.changeIncomingCallsRingtone)
{
addToArrayListUnique(writeSystemSettingsPermissionName, requiredPermissions);
}
}
}
if (!onlyGeneral)
@ -298,7 +333,27 @@ public class ActivityPermissions extends Activity
{
for (String singlePermission : getPermissionsForRule(rule))
if (!havePermission(singlePermission, workingContext))
addToArrayListUnique(singlePermission, requiredPermissions);
{
if(
singlePermission.equalsIgnoreCase(permissionNameLocationBackground)
||
singlePermission.equalsIgnoreCase(permissionNameLocationFine)
||
singlePermission.equalsIgnoreCase(permissionNameLocationCoarse)
)
{
if (!Miscellaneous.googleToBlameForLocation(true))
addToArrayListUnique(singlePermission, requiredPermissions);
}
else if(singlePermission.equalsIgnoreCase("android.permission.ACTIVITY_RECOGNITION") || singlePermission.equalsIgnoreCase("com.google.android.gms.permission.ACTIVITY_RECOGNITION"))
{
if(!BuildConfig.FLAVOR.equalsIgnoreCase("fdroidFlavor"))
addToArrayListUnique(singlePermission, requiredPermissions);
}
else
addToArrayListUnique(singlePermission, requiredPermissions);
}
}
}
@ -356,7 +411,7 @@ public class ActivityPermissions extends Activity
addToArrayListUnique("android.permission.ACCESS_NETWORK_STATE", requiredPermissions);
break;
case batteryLevel:
addToArrayListUnique("android.permission.READ_PHONE_STATE", requiredPermissions);
// addToArrayListUnique("android.permission.READ_PHONE_STATE", requiredPermissions);
// addToArrayListUnique("android.permission.BATTERY_STATS", requiredPermissions);
break;
case bluetoothConnection:
@ -421,6 +476,9 @@ public class ActivityPermissions extends Activity
addToArrayListUnique("android.permission.ACCESS_NETWORK_STATE", requiredPermissions);
addToArrayListUnique("android.permission.ACCESS_WIFI_STATE", requiredPermissions);
break;
case notification:
addToArrayListUnique(permissionNameReadNotifications, requiredPermissions);
break;
default:
break;
}
@ -484,6 +542,20 @@ public class ActivityPermissions extends Activity
case speakText:
break;
case startOtherActivity:
if(
action.getParameter2().contains(Actions.wireguard_tunnel_up)
||
action.getParameter2().contains(Actions.wireguard_tunnel_down)
||
action.getParameter2().contains(Actions.wireguard_tunnel_refresh)
)
addToArrayListUnique(ActivityPermissions.permissionNameWireguard, requiredPermissions);
// if(
// action.getParameter2().contains("eu.faircode.netguard.START_PORT_FORWARD")
// ||
// action.getParameter2().contains("eu.faircode.netguard.STOP_PORT_FORWARD")
// )
// addToArrayListUnique("net.kollnig.missioncontrol.permission.ADMIN", requiredPermissions);
break;
case triggerUrl:
addToArrayListUnique("android.permission.INTERNET", requiredPermissions);
@ -596,6 +668,11 @@ public class ActivityPermissions extends Activity
break;
case "android.permission.WRITE_EXTERNAL_STORAGE":
usingElements.add(getResources().getString(R.string.storeSettings));
break;
case permissionNameReadNotifications:
for(String ruleName : getRulesUsing(Trigger.Trigger_Enum.notification))
usingElements.add(String.format(getResources().getString(R.string.ruleXrequiresThis), ruleName));
break;
case "com.google.android.gms.permission.ACTIVITY_RECOGNITION":
for(String ruleName : getRulesUsing(Trigger.Trigger_Enum.activityDetection))
@ -779,6 +856,10 @@ public class ActivityPermissions extends Activity
requestPermissions(cachedPermissionsToRequest, true);
}
}
if (requestCode == requestCodeForPermissionsNotifications)
if(havePermission(permissionNameReadNotifications, ActivityPermissions.this))
requestPermissions(cachedPermissionsToRequest, true);
}
}
@ -838,6 +919,14 @@ public class ActivityPermissions extends Activity
startActivityForResult(intent, requestCodeForPermissionsNotificationPolicy);
return;
}
else if (s.equalsIgnoreCase(permissionNameReadNotifications))
{
requiredPermissions.remove(s);
cachedPermissionsToRequest = requiredPermissions;
Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
startActivityForResult(intent, requestCodeForPermissionsNotifications);
return;
}
// else if (s.equalsIgnoreCase(permissionNameLocationBackground) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
// {
// requiredPermissions.remove(s);
@ -977,7 +1066,7 @@ public class ActivityPermissions extends Activity
and simply disable features while keeping the notification alive. The user may dismiss it anyway.
*/
Miscellaneous.logEvent("w", "Denied permissions", getResources().getString(R.string.theFollowingPermissionsHaveBeenDenied) + Miscellaneous.explode(deniedPermissions), 3);
Miscellaneous.logEvent("w", "Denied permissions", getResources().getString(R.string.theFollowingPermissionsHaveBeenDenied) + Miscellaneous.explode(", ", deniedPermissions), 3);
// this.finish();
}
else
@ -999,6 +1088,7 @@ public class ActivityPermissions extends Activity
NotificationManager mNotificationManager = (NotificationManager) Miscellaneous.getAnyContext().getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.cancel(notificationIdPermissions);
ActivityMainScreen.updateMainScreen();
ActivityMainRules.getInstance().updateListView();
this.finish();
}
@ -1388,4 +1478,24 @@ public class ActivityPermissions extends Activity
return false;
}
}
public static Boolean verifyNotificationPermission()
{
try
{
String theList = android.provider.Settings.Secure.getString(Miscellaneous.getAnyContext().getContentResolver(), "enabled_notification_listeners");
String[] theListList = theList.split(":");
String me = (new ComponentName(Miscellaneous.getAnyContext(), NotificationListener.class)).flattenToString();
for (String next : theListList)
{
if (me.equals(next))
return true;
}
return false;
}
catch(Exception e)
{
return false;
}
}
}

View File

@ -0,0 +1,16 @@
package com.jens.automation2;
import android.app.Activity;
import android.os.Bundle;
import androidx.annotation.Nullable;
public class ActivityTriggerPhoneCall extends Activity
{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.trigger_phone_call);
}
}

View File

@ -37,9 +37,10 @@ public class AutomationService extends Service implements OnInitListener
protected TextToSpeech ttsEngine = null;
protected final static int notificationId = 1000;
protected final static int notificationIdRestrictions = 1005;
protected final static int notificationIdLocationRestriction = 1006;
final static String NOTIFICATION_CHANNEL_ID = "com.jens.automation2";
final static String channelName = "Automation notifications";
final static String channelName = "Service notification";
protected static Notification myNotification;
protected static NotificationCompat.Builder notificationBuilder = null;
@ -154,7 +155,8 @@ public class AutomationService extends Service implements OnInitListener
{
Miscellaneous.logEvent("w", "AutoStart", "Service is started via boot. Settingsfile not available because storage is not mounted, yet. Waiting for 3 seconds.", 4);
Thread.sleep(3000);
} catch (InterruptedException e)
}
catch (InterruptedException e)
{
e.printStackTrace();
}
@ -169,15 +171,13 @@ public class AutomationService extends Service implements OnInitListener
}
//if still no POIs...
if (//PointOfInterest.getPointOfInterestCollection() == null | PointOfInterest.getPointOfInterestCollection().size() == 0
// &&
Rule.getRuleCollection() == null | Rule.getRuleCollection().size() == 0
)
if (Rule.getRuleCollection() == null | Rule.getRuleCollection().size() == 0)
{
Miscellaneous.logEvent("w", "AutomationService", context.getResources().getString(R.string.serviceWontStart), 1);
Toast.makeText(context, context.getResources().getString(R.string.serviceWontStart), Toast.LENGTH_LONG).show();
return false;
} else
}
else
{
return true;
}
@ -318,6 +318,7 @@ public class AutomationService extends Service implements OnInitListener
checkForTtsEngine();
checkForPermissions();
checkForRestrictedFeatures();
checkForMissingBackgroundLocationPermission();
Actions.context = this;
Actions.autoMationServerRef = this;
@ -365,7 +366,10 @@ public class AutomationService extends Service implements OnInitListener
Intent intent = new Intent(AutomationService.this, ActivityPermissions.class);
PendingIntent pi = PendingIntent.getActivity(AutomationService.this, 0, intent, 0);
Miscellaneous.createDismissableNotification(getResources().getString(R.string.appRunningInLimitedMode), ActivityPermissions.notificationIdPermissions, pi);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1)
Miscellaneous.createDismissableNotificationWithDelay(1010, getResources().getString(R.string.featuresDisabled), ActivityPermissions.notificationIdPermissions, pi);
else
Miscellaneous.createDismissableNotification(getResources().getString(R.string.featuresDisabled), ActivityPermissions.notificationIdPermissions, pi);
}
// else
// Toast.makeText(Miscellaneous.getAnyContext(), "Have all required permissions.", Toast.LENGTH_LONG).show();
@ -385,11 +389,45 @@ public class AutomationService extends Service implements OnInitListener
Intent intent = new Intent(AutomationService.this, ActivityMainTabLayout.class);
PendingIntent pi = PendingIntent.getActivity(AutomationService.this, 0, intent, 0);
// Miscellaneous.createDismissableNotification(getResources().getString(R.string.settingsReferringToRestrictedFeatures), ActivityPermissions.notificationIdPermissions, pi);
Miscellaneous.createDismissableNotification(getResources().getString(R.string.settingsReferringToRestrictedFeatures), notificationIdRestrictions, pi);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1)
Miscellaneous.createDismissableNotificationWithDelay(3300, getResources().getString(R.string.featuresDisabled), notificationIdRestrictions, pi);
else
Miscellaneous.createDismissableNotification(getResources().getString(R.string.featuresDisabled), notificationIdRestrictions, pi);
}
}
}
protected void checkForMissingBackgroundLocationPermission()
{
if(Miscellaneous.googleToBlameForLocation(true))
{
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);
}
/*
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)
{
if(!(isMyServiceRunning(context)))
@ -543,11 +581,11 @@ public class AutomationService extends Service implements OnInitListener
if(activePoi == null)
{
PointOfInterest closestPoi = PointOfInterest.getClosestPOI(instance.getLocationProvider().getCurrentLocation());
bodyText = "Active POI: none" + "\n" + "Closest POI: " + closestPoi.getName() + lastRuleString;
bodyText = AutomationService.getInstance().getResources().getString(R.string.activePoi) + ": " + AutomationService.getInstance().getResources().getString(R.string.none) + "\n" + AutomationService.getInstance().getResources().getString(R.string.closestPoi) + ": " + closestPoi.getName() + lastRuleString;
}
else
{
bodyText = "Active POI: " + activePoi.getName() + lastRuleString;
bodyText = AutomationService.getInstance().getResources().getString(R.string.activePoi) + ": " + activePoi.getName() + lastRuleString;
}
}
catch(NullPointerException e)

View File

@ -33,7 +33,8 @@ public class CompensateCrappyAndroidPaths
* @param uri The Uri to query.
*/
@SuppressLint("NewApi")
public static String getPath(final Context context, final Uri uri) {
public static String getPath(final Context context, final Uri uri)
{
// check here to KITKAT or new version
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
String selection = null;

View File

@ -0,0 +1,65 @@
package com.jens.automation2;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.File;
import java.io.FileNotFoundException;
import java.security.Provider;
public class FileShareProvider extends ContentProvider
{
@Override
public boolean onCreate()
{
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder)
{
return null;
}
@Nullable
@Override
public String getType(@NonNull Uri uri)
{
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values)
{
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs)
{
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs)
{
return 0;
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException
{
File cacheDir = getContext().getCacheDir();
File privateFile = new File(cacheDir, Settings.zipFileName);
return ParcelFileDescriptor.open(privateFile, ParcelFileDescriptor.MODE_READ_ONLY);
}
}

View File

@ -7,6 +7,7 @@ import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ContentProvider;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@ -14,6 +15,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Environment;
import android.os.IBinder;
@ -23,9 +25,8 @@ import android.util.Base64;
import android.util.Log;
import android.widget.Toast;
import androidx.core.app.NotificationCompat;
import com.jens.automation2.location.LocationProvider;
import com.jens.automation2.receivers.NotificationListener;
import com.jens.automation2.receivers.PhoneStatusListener;
import org.apache.http.HttpEntity;
@ -44,14 +45,19 @@ import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringReader;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.reflect.InvocationTargetException;
@ -70,6 +76,9 @@ import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Scanner;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
@ -81,11 +90,15 @@ import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import androidx.core.app.NotificationCompat;
import androidx.documentfile.provider.DocumentFile;
import static android.provider.CalendarContract.CalendarCache.URI;
import static com.jens.automation2.AutomationService.NOTIFICATION_CHANNEL_ID;
import static com.jens.automation2.AutomationService.channelName;
public class Miscellaneous extends Service
{
{
protected static String writeableFolderStringCache = null;
public static final String lineSeparator = System.getProperty("line.separator");
@ -340,8 +353,8 @@ public class Miscellaneous extends Service
for (String f : foldersToTestArray)
{
if (testFolder(f))
{
// if (testFolder(f))
// {
String pathToUse = f + "/" + Settings.folderName;
// Toast.makeText(getAnyContext(), "Using " + pathToUse + " to store settings and log.", Toast.LENGTH_LONG).show();
@ -353,14 +366,24 @@ public class Miscellaneous extends Service
{
Miscellaneous.logEvent("i", "Path", "Found old path " + pathToUse + " for settings and logs. Migrating old files to new directory.", 2);
for (File moveFile : oldDirectory.listFiles())
moveFile.renameTo(newDirectory);
for (File fileToBeMoved : oldDirectory.listFiles())
{
File dstFile = new File(writeableFolderStringCache + "/" + fileToBeMoved.getName());
/*
For some stupid reason Android's file.moveTo can't move files between
mount points. That's why we have to copy it and delete the src if successful.
*/
if(copyFileUsingStream(fileToBeMoved, dstFile))
fileToBeMoved.delete();
}
String message = String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.filesHaveBeenMovedTo), newDirectory.getAbsolutePath());
Miscellaneous.writeStringToFile(oldDirectory.getAbsolutePath() + "readme.txt", message);
Miscellaneous.writeStringToFile(oldDirectory.getAbsolutePath() + "/readme.txt", message);
break migration;
}
}
// }
}
} catch (Exception e)
{
@ -427,6 +450,25 @@ public class Miscellaneous extends Service
// Miscellaneous.logEvent("i", TAG, "isEmulator=" + isEmulator);
return isEmulator;
}
public static boolean compare(String direction, String needle, String haystack)
{
switch(direction)
{
case Trigger.directionEquals:
return haystack.equalsIgnoreCase(needle);
case Trigger.directionNotEquals:
return !haystack.equalsIgnoreCase(needle);
case Trigger.directionContains:
return haystack.toLowerCase().contains(needle.toLowerCase());
case Trigger.directionStartsWith:
return haystack.toLowerCase().startsWith(needle.toLowerCase());
case Trigger.directionEndsWith:
return haystack.toLowerCase().endsWith(needle.toLowerCase());
default:
return false;
}
}
public static int compareTimes(Time time1, Time time2)
{
@ -555,6 +597,26 @@ public class Miscellaneous extends Service
source = source.replace("[s]", String.valueOf(cal.get(Calendar.SECOND)));
source = source.replace("[ms]", String.valueOf(cal.get(Calendar.MILLISECOND)));
}
if(source.contains("[notificationTitle]"))
{
String notificationTitle = NotificationListener.getLastNotification().getTitle();
if(notificationTitle != null && notificationTitle.length() > 0)
source = source.replace("[notificationTitle]", notificationTitle);
else
Miscellaneous.logEvent("w", "Variable replacement", "notificationTitle was empty.", 3);
}
if(source.contains("[notificationText]"))
{
String notificationText = NotificationListener.getLastNotification().getText();
if(notificationText != null && notificationText.length() > 0)
source = source.replace("[notificationText]", notificationText);
else
Miscellaneous.logEvent("w", "Variable replacement", "notificationText was empty.", 3);
}
// Miscellaneous.logEvent("i", "URL after replace", source);
@ -754,10 +816,63 @@ public class Miscellaneous extends Service
return allHostsValid;
}
@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
public static void createDismissableNotificationWithDelay(long delay, String textToDisplay, int notificationId, PendingIntent pendingIntent)
{
/*
Now what's this about?
From SDK 27 onwards you can only fire 1 notification per second:
https://developer.android.com/about/versions/oreo/android-8.1?hl=bn#notify
There are some situations where the service is just being started - resulting in a notification. But we have
additional need to inform the user about something and want to create another notification. That's why we have
to delay it for a moment, but don't want to hold off the main threat.
*/
class AsyncTaskCreateNotification extends AsyncTask<Void, Void, Void>
{
@Override
protected Void doInBackground(Void... voids)
{
setDefaultBehaviour(this);
try
{
Thread.sleep(delay);
}
catch(Exception e)
{}
createDismissableNotification(textToDisplay, notificationId, pendingIntent);
return null;
}
}
AsyncTaskCreateNotification astCn = new AsyncTaskCreateNotification();
astCn.execute(null, null);
}
private static void setDefaultBehaviour(AsyncTask asyncTask)
{
// without this line debugger will - for some reason - skip all breakpoints in this class
if(android.os.Debug.isDebuggerConnected())
android.os.Debug.waitForDebugger();
// Thread.setDefaultUncaughtExceptionHandler(Miscellaneous.getUncaughtExceptionHandler(activityMainRef, true));
}
@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
public static void createDismissableNotification(String textToDisplay, int notificationId, PendingIntent pendingIntent)
{
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
createDismissableNotificationSdk26(textToDisplay, notificationId, pendingIntent);
return;
}
NotificationManager mNotificationManager = (NotificationManager) Miscellaneous.getAnyContext().getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder dismissableNotificationBuilder = createDismissableNotificationBuilder(pendingIntent);
@ -769,50 +884,78 @@ public class Miscellaneous extends Service
mNotificationManager.notify(notificationId, dismissableNotification);
/*NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher) // notification icon
.setContentTitle("Notification!") // title for notification
.setContentText("Hello word") // message for notification
.setAutoCancel(true); // clear notification after click
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(this,0,intent,Intent.FLAG_ACTIVITY_NEW_TASK);
mBuilder.setContentIntent(pi);
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(0, dismissableNotification);*/
/*NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher) // notification icon
.setContentTitle("Notification!") // title for notification
.setContentText("Hello word") // message for notification
.setAutoCancel(true); // clear notification after click
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(this,0,intent,Intent.FLAG_ACTIVITY_NEW_TASK);
mBuilder.setContentIntent(pi);
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(0, dismissableNotification);*/
}
/*protected static Notification.Builder createDismissableNotificationBuilder()
static void createDismissableNotificationSdk26(String textToDisplay, int notificationId, PendingIntent pendingIntent)
{
Notification.Builder builder = new Notification.Builder(AutomationService.getInstance());
builder.setContentTitle("Automation");
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setCategory(Notification.CATEGORY_EVENT);
NotificationManager mNotificationManager = (NotificationManager) AutomationService.getInstance().getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder builder;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "Functionality warnings", NotificationManager.IMPORTANCE_HIGH);
// chan.setLightColor(Color.BLUE);
chan.enableVibration(false);
// chan.setSound(null, null);
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
mNotificationManager.createNotificationChannel(chan);
builder = new NotificationCompat.Builder(AutomationService.getInstance(), NOTIFICATION_CHANNEL_ID);
}
else
builder = new NotificationCompat.Builder(AutomationService.getInstance());
// if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
// builder.setCategory(Notification.CATEGORY_SERVICE);
builder.setWhen(System.currentTimeMillis());
builder.setContentIntent(pendingIntent);
//static PendingIntent myPendingIntent = PendingIntent.getActivity(this, 0, myIntent, 0);
builder.setContentTitle(AutomationService.getInstance().getResources().getString(R.string.app_name));
builder.setOnlyAlertOnce(true);
//builder.setContentIntent(myPendingIntent);
if(Settings.showIconWhenServiceIsRunning)
builder.setSmallIcon(R.drawable.ic_launcher);
// Notification defaultNotification = new Notification();
*//* Notification defaultNotification = builder.build();
builder.setContentText(textToDisplay);
builder.setStyle(new NotificationCompat.BigTextStyle().bigText(textToDisplay));
defaultNotification.icon = R.drawable.ic_launcher;
defaultNotification.when = System.currentTimeMillis();
NotificationManager notificationManager = (NotificationManager) Miscellaneous.getAnyContext().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(1, builder.build());
// defaultNotification.defaults |= Notification.DEFAULT_VIBRATE;
// defaultNotification.defaults |= Notification.DEFAULT_LIGHTS;
defaultNotification.flags |= Notification.FLAG_AUTO_CANCEL;
// defaultNotification.flags |= Notification.FLAG_SHOW_LIGHTS;
defaultNotification.flags |= Notification.FLAG_ONLY_ALERT_ONCE;
// defaultNotification.ledARGB = Color.YELLOW;
// defaultNotification.ledOnMS = 1500;
// defaultNotification.ledOffMS = 1500;
*//*
return builder;
}*/
// Intent notifyIntent = new Intent(context, notification.class);
// notifyIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
//
// pendingIntent.getIntentSender().g
//
// PendingIntent pendingIntent = PendingIntent.getActivities(context, 0,
// new Intent[]{notifyIntent}, PendingIntent.FLAG_UPDATE_CURRENT);
//
// Notification notification = new Notification.Builder(Miscellaneous.getAnyContext())
// .setSmallIcon(android.R.drawable.ic_dialog_info)
// .setContentTitle("Automation")
// .setContentText(textToDisplay)
// .setAutoCancel(true)
// .setContentIntent(pendingIntent)
// .build();
// notification.defaults |= Notification.DEFAULT_SOUND;
// NotificationManager notificationManager =
// (NotificationManager) Miscellaneous.getAnyContext().getSystemService(Context.NOTIFICATION_SERVICE);
// notificationManager.notify(1, notification);
}
protected static NotificationCompat.Builder createDismissableNotificationBuilder(PendingIntent myPendingIntent)
{
@ -822,7 +965,7 @@ public class Miscellaneous extends Service
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_LOW);
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_HIGH);
// chan.setLightColor(Color.BLUE);
// chan.enableVibration(false);
// chan.setSound(null, null);
@ -852,13 +995,21 @@ public class Miscellaneous extends Service
return builder;
}
public static String explode(ArrayList<String> arrayList)
public static String explode(String glue, ArrayList<String> arrayList)
{
StringBuilder builder = new StringBuilder();
for(String s : arrayList)
builder.append(s);
if(arrayList != null)
{
StringBuilder builder = new StringBuilder();
for (String s : arrayList)
builder.append(s + glue);
return builder.toString();
if (builder.length() > 0)
builder.delete(builder.length() - glue.length(), builder.length());
return builder.toString();
}
else
return "";
}
public static boolean isGooglePlayInstalled(Context context)
@ -1056,4 +1207,268 @@ public class Miscellaneous extends Service
return null;
}
}
public static boolean copyFileUsingStream(File source, File dest) throws IOException
{
boolean returnValue = false;
InputStream is = null;
OutputStream os = null;
try
{
is = new FileInputStream(source);
os = new FileOutputStream(dest);
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0)
{
os.write(buffer, 0, length);
}
returnValue = true;
}
finally
{
is.close();
os.close();
}
return returnValue;
}
public static boolean copyDocumentFileToFile(DocumentFile src, File dst)
{
InputStream in = null;
OutputStream out = null;
String error = null;
try
{
in = Miscellaneous.getAnyContext().getContentResolver().openInputStream(src.getUri());
out = new FileOutputStream(dst);
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1)
{
out.write(buffer, 0, read);
}
in.close();
// write the output file (You have now copied the file)
out.flush();
out.close();
return true;
}
catch (FileNotFoundException fnfe1)
{
error = fnfe1.getMessage();
}
catch (Exception e)
{
error = e.getMessage();
}
return false;
// return error;
}
public static boolean copyFileToDocumentFile(File src, DocumentFile dst)
{
InputStream in = null;
OutputStream out = null;
String error = null;
try
{
in = new FileInputStream(src);
out = Miscellaneous.getAnyContext().getContentResolver().openOutputStream(dst.getUri());
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1)
{
out.write(buffer, 0, read);
}
in.close();
// write the output file (You have now copied the file)
out.flush();
out.close();
return true;
}
catch (FileNotFoundException fnfe1)
{
error = fnfe1.getMessage();
}
catch (Exception e)
{
error = e.getMessage();
}
return false;
// return error;
}
/*public static String copyDocumentFile(String inputPath, String inputFile, Uri treeUri)
{
InputStream in = null;
OutputStream out = null;
String error = null;
DocumentFile pickedDir = DocumentFile.fromTreeUri(getActivity(), treeUri);
String extension = inputFile.substring(inputFile.lastIndexOf(".")+1,inputFile.length());
try
{
DocumentFile newFile = pickedDir.createFile("audio/"+extension, inputFile);
out = getActivity().getContentResolver().openOutputStream(newFile.getUri());
in = new FileInputStream(inputPath + inputFile);
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1)
{
out.write(buffer, 0, read);
}
in.close();
// write the output file (You have now copied the file)
out.flush();
out.close();
}
catch (FileNotFoundException fnfe1)
{
error = fnfe1.getMessage();
}
catch (Exception e)
{
error = e.getMessage();
}
return error;
}*/
public static boolean googleToBlameForLocation(boolean checkExistingRules)
{
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
{
if (BuildConfig.FLAVOR.equalsIgnoreCase("googlePlayFlavor"))
{
if(checkExistingRules)
{
if (Rule.isAnyRuleUsing(Trigger.Trigger_Enum.pointOfInterest))
{
return true;
}
}
else
return true;
}
}
return false;
}
public static void zip(String[] _files, String zipFileName)
{
int BUFFER = 2048;
try
{
BufferedInputStream origin = null;
FileOutputStream dest = new FileOutputStream(zipFileName);
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(
dest));
byte data[] = new byte[BUFFER];
for (int i = 0; i < _files.length; i++)
{
Log.v("Compress", "Adding: " + _files[i]);
FileInputStream fi = new FileInputStream(_files[i]);
origin = new BufferedInputStream(fi, BUFFER);
ZipEntry entry = new ZipEntry(_files[i].substring(_files[i].lastIndexOf("/") + 1));
out.putNextEntry(entry);
int count;
while ((count = origin.read(data, 0, BUFFER)) != -1)
{
out.write(data, 0, count);
}
origin.close();
}
out.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static void unzip(String _zipFile, String _targetLocation)
{
int BUFFER = 2048;
try
{
FileInputStream fin = new FileInputStream(_zipFile);
ZipInputStream zin = new ZipInputStream(fin);
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null)
{
//create dir if required while unzipping
if (ze.isDirectory())
{
// dirChecker(ze.getName());
}
else
{
FileOutputStream fout = new FileOutputStream(_targetLocation + ze.getName());
for (int c = zin.read(); c != -1; c = zin.read())
{
fout.write(c);
}
zin.closeEntry();
fout.close();
}
}
zin.close();
}
catch (Exception e)
{
System.out.println(e);
}
}
public static void sendEmail(Context context, String targetAddress, String subject, String message, Uri fileAttachment)
{
try
{
final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.setType("plain/text");
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[]{targetAddress});
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject);
if (fileAttachment != null)
{
emailIntent.putExtra(Intent.EXTRA_STREAM, fileAttachment);
}
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message);
context.startActivity(Intent.createChooser(emailIntent, "Sending email..."));
}
catch (Throwable t)
{
Toast.makeText(context, "Request failed try again: "+ t.toString(), Toast.LENGTH_LONG).show();
}
}
public static boolean doesActivityExist(Intent intent, Context context)
{
return intent.resolveActivityInfo(context.getPackageManager(), 0) != null;
}
}

View File

@ -68,7 +68,13 @@ public class News
Calendar now = Calendar.getInstance();
String newsContent;
String filePath = context.getFilesDir() + "/appNews.xml";
String newsFileName = "appNews.xml";
String filePath = context.getCacheDir() + "/" + newsFileName;
File oldFilePath = new File(context.getFilesDir() + "/" + newsFileName);
if(oldFilePath.exists())
oldFilePath.delete();
if (!(new File(filePath)).exists() || Settings.lastNewsPolltime == -1 || now.getTimeInMillis() >= Settings.lastNewsPolltime + (long)(Settings.newsDisplayForXDays * 24 * 60 * 60 * 1000))
{
@ -80,14 +86,14 @@ public class News
{
Settings.lastNewsPolltime = now.getTimeInMillis();
Settings.writeSettings(context);
Miscellaneous.logEvent("i", "appNews.xml", "File stored to " + filePath, 5);
Miscellaneous.logEvent("i", newsFileName, "File stored to " + filePath, 5);
}
}
else
{
// Just read local cache file
newsContent = Miscellaneous.readFileToString(filePath);
Miscellaneous.logEvent("i", "appNews.xml", "Using cache to retrieve news: " + filePath, 5);
Miscellaneous.logEvent("i", newsFileName, "Using cache to retrieve news: " + filePath, 5);
}
ArrayList<News> returnList = new ArrayList<>();
@ -256,7 +262,18 @@ public class News
@Override
protected void onPostExecute(ArrayList arrayList)
{
ActivityMainScreen.getActivityMainScreenInstance().processNewsResult(arrayList);
try
{
ActivityMainScreen.getActivityMainScreenInstance().processNewsResult(arrayList);
}
catch(NullPointerException e)
{
Miscellaneous.logEvent("e", "NewsDownload", "There was a problem displaying the already downloded news, probably ActivityMainScreen isn't currently shown: " + Log.getStackTraceString(e), 2);
}
catch(Exception e)
{
Miscellaneous.logEvent("e", "NewsDownload", "There was a problem displaying the already downloded news: " + Log.getStackTraceString(e), 2);
}
}
}
}

View File

@ -145,7 +145,10 @@ public class ReceiverCoordinator
// startCellLocationChangedReceiver
if(!ConnectivityReceiver.isAirplaneMode(AutomationService.getInstance()) && WifiBroadcastReceiver.mayCellLocationReceiverBeActivated() && (Rule.isAnyRuleUsing(Trigger.Trigger_Enum.pointOfInterest) | Rule.isAnyRuleUsing(Trigger.Trigger_Enum.speed)))
CellLocationChangedReceiver.startCellLocationChangedReceiver();
{
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))
@ -270,32 +273,40 @@ public class ReceiverCoordinator
if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.activityDetection))
{
boolean isRunning = (Boolean)Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "isActivityDetectionReceiverRunning", null);
if(isRunning)
Object runResult = Miscellaneous.runMethodReflective(activityDetectionClassPath, "isActivityDetectionReceiverRunning", null);;
if(runResult instanceof Boolean)
{
Miscellaneous.logEvent("i", "LocationProvider", "Restarting ActivityDetectionReceiver because used in a new/changed rule.", 4);
boolean haveAllPerms = (Boolean)Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "haveAllPermission", null);
if(haveAllPerms)
Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "restartActivityDetectionReceiver", null);
boolean isRunning = (Boolean) runResult;
if (isRunning)
{
Miscellaneous.logEvent("i", "LocationProvider", "Restarting ActivityDetectionReceiver because used in a new/changed rule.", 4);
boolean haveAllPerms = (Boolean) Miscellaneous.runMethodReflective(activityDetectionClassPath, "haveAllPermission", null);
if (haveAllPerms)
Miscellaneous.runMethodReflective(activityDetectionClassPath, "restartActivityDetectionReceiver", null);
// ActivityDetectionReceiver.restartActivityDetectionReceiver();
}
else
{
Miscellaneous.logEvent("i", "LocationProvider", "Starting ActivityDetectionReceiver because used in a new/changed rule.", 4);
boolean haveAllPerms = (Boolean)Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "haveAllPermission", null);
if(haveAllPerms)
Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "startActivityDetectionReceiver", null);
}
else
{
Miscellaneous.logEvent("i", "LocationProvider", "Starting ActivityDetectionReceiver because used in a new/changed rule.", 4);
boolean haveAllPerms = (Boolean) Miscellaneous.runMethodReflective(activityDetectionClassPath, "haveAllPermission", null);
if (haveAllPerms)
Miscellaneous.runMethodReflective(activityDetectionClassPath, "startActivityDetectionReceiver", null);
// ActivityDetectionReceiver.startActivityDetectionReceiver();
}
}
}
else
{
boolean isRunning = (Boolean)Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "isActivityDetectionReceiverRunning", null);
if(isRunning)
Object runResult = Miscellaneous.runMethodReflective(activityDetectionClassPath, "isActivityDetectionReceiverRunning", null);
if(runResult instanceof Boolean)
{
Miscellaneous.logEvent("i", "LocationProvider", "Shutting down ActivityDetectionReceiver because not used in any rule.", 4);
Miscellaneous.runMethodReflective("ActivityDetectionReceiver", "stopActivityDetectionReceiver", null);
boolean isRunning = (Boolean) runResult;
if (isRunning)
{
Miscellaneous.logEvent("i", "LocationProvider", "Shutting down ActivityDetectionReceiver because not used in any rule.", 4);
Miscellaneous.runMethodReflective(activityDetectionClassPath, "stopActivityDetectionReceiver", null);
// ActivityDetectionReceiver.stopActivityDetectionReceiver();
}
}
}

View File

@ -5,6 +5,7 @@ import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
@ -15,6 +16,9 @@ public class Settings implements SharedPreferences
public static final int newsPollEveryXDays = 3;
public static final int newsDisplayForXDays = 3;
public static final String folderName = "Automation";
public static final String zipFileName = "automation.zip";
public static final String constNewsOptInDone ="newsOptInDone";
public static long minimumDistanceChangeForGpsUpdate;
public static long minimumDistanceChangeForNetworkUpdate;
@ -62,6 +66,8 @@ public class Settings implements SharedPreferences
public static boolean noticeAndroid10WifiShown;
public static long lastNewsPolltime;
public static ArrayList<String> whatHasBeenDone;
/*
Generic settings valid for all installations and not changable
*/
@ -250,6 +256,16 @@ public class Settings implements SharedPreferences
noticeAndroid10WifiShown = prefs.getBoolean("noticeAndroid10WifiShown", false);
lastNewsPolltime = prefs.getLong("lastNewsPolltime", default_lastNewsPolltime);
String whbdString = prefs.getString("whatHasBeenDone", "");
if(whbdString != null && whbdString.length() > 0)
{
whatHasBeenDone = new ArrayList<>();
for(String s : whbdString.split(";"))
{
whatHasBeenDone.add(s);
}
}
}
catch(Exception e)
{
@ -261,6 +277,26 @@ public class Settings implements SharedPreferences
initializeSettings(context, false);
}
}
public static void considerDone(String key)
{
if(whatHasBeenDone == null)
whatHasBeenDone = new ArrayList<>();
if(!whatHasBeenDone.contains(key))
whatHasBeenDone.add(key);
}
public static boolean hasBeenDone(String key)
{
if(whatHasBeenDone != null)
{
if(whatHasBeenDone.contains(key))
return true;
}
return false;
}
/**Makes sure a settings has a valid setting. If not it will assign a reasonable default setting to it.
* If force settings will be initialized even if the user has set something.**/
@ -410,6 +446,9 @@ public class Settings implements SharedPreferences
if(!prefs.contains("lastNewsPolltime") | force)
editor.putLong("lastNewsPolltime", default_lastNewsPolltime);
if(!prefs.contains("whatHasBeenDone") | force)
editor.putString("whatHasBeenDone", "");
editor.commit();
@ -480,6 +519,8 @@ public class Settings implements SharedPreferences
editor.putLong("lastNewsPolltime", lastNewsPolltime);
editor.putString("whatHasBeenDone", Miscellaneous.explode(";", whatHasBeenDone));
if(lastActivePoi == null)
editor.putString("lastActivePoi", "null");
else

View File

@ -21,7 +21,7 @@ public class Trigger
*/
public enum Trigger_Enum {
pointOfInterest, timeFrame, charging, batteryLevel, usb_host_connection, speed, noiseLevel, wifiConnection, process_started_stopped, airplaneMode, roaming, nfcTag, activityDetection, bluetoothConnection, headsetPlugged, phoneCall; //phoneCall always needs to be at the very end because of Google's shitty so called privacy
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)
{
@ -59,6 +59,8 @@ public class Trigger
return context.getResources().getString(R.string.bluetoothConnection);
case headsetPlugged:
return context.getResources().getString(R.string.triggerHeadsetPlugged);
case notification:
return context.getResources().getString(R.string.notification);
default:
return "Unknown";
}
@ -67,6 +69,9 @@ public class Trigger
};
private boolean triggerParameter; //if true->started event, if false->stopped
private String triggerParameter2;
public static final String triggerParameter2Split = "tp2split";
private Trigger_Enum triggerType = null;
private PointOfInterest pointOfInterest = null;
@ -206,6 +211,16 @@ public class Trigger
this.triggerParameter = triggerParameter;
}
public String getTriggerParameter2()
{
return triggerParameter2;
}
public void setTriggerParameter2(String triggerParameter2)
{
this.triggerParameter2 = triggerParameter2;
}
public TimeFrame getTimeFrame()
{
return timeFrame;
@ -216,7 +231,6 @@ public class Trigger
this.timeFrame = timeFrame;
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@SuppressWarnings("unused")
@Override
@ -440,6 +454,46 @@ public class Trigger
else
returnString.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.headsetDisconnected), type));
break;
case notification:
if(this.getTriggerParameter2().contains(triggerParameter2Split))
{
String[] params = getTriggerParameter2().split(triggerParameter2Split);
String app = params[0];
String titleDir = params[1];
String title = params[2];
String textDir = params[3];
String text;
if (params.length >= 5)
text = params[4];
else
text = "";
StringBuilder triggerBuilder = new StringBuilder();
String appString;
if (app.equalsIgnoreCase("-1"))
appString = Miscellaneous.getAnyContext().getResources().getString(R.string.anyApp);
else
appString = "app " + app;
if(triggerParameter)
triggerBuilder.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.postsNotification), appString));
else
triggerBuilder.append(String.format(Miscellaneous.getAnyContext().getResources().getString(R.string.removedNotification), appString));
if (title.length() > 0)
triggerBuilder.append(", " + Miscellaneous.getAnyContext().getString(R.string.title) + " " + Trigger.getMatchString(titleDir) + " " + title);
if (text.length() > 0)
triggerBuilder.append(", " + Miscellaneous.getAnyContext().getString(R.string.text) + " " + Trigger.getMatchString(textDir) + " " + text);
returnString.append(triggerBuilder.toString());
}
else
{
setTriggerParameter2("-1" + triggerParameter2Split + directionEquals + triggerParameter2Split + triggerParameter2Split + directionEquals + triggerParameter2Split + triggerParameter2Split);
}
break;
default:
returnString.append("error");
break;
@ -447,7 +501,47 @@ public class Trigger
return returnString.toString();
}
public static final String directionEquals = "eq";
public static final String directionContains = "ct";
public static final String directionStartsWith = "sw";
public static final String directionEndsWith = "ew";
public static final String directionNotEquals = "ne";
public static String getMatchString(String direction)
{
switch(direction)
{
case directionEquals:
return Miscellaneous.getAnyContext().getString(R.string.directionStringEquals);
case directionContains:
return Miscellaneous.getAnyContext().getString(R.string.directionStringContains);
case directionStartsWith:
return Miscellaneous.getAnyContext().getString(R.string.directionStringStartsWith);
case directionEndsWith:
return Miscellaneous.getAnyContext().getString(R.string.directionStringEndsWith);
case directionNotEquals:
return Miscellaneous.getAnyContext().getString(R.string.directionStringNotEquals);
default:
return Miscellaneous.getAnyContext().getString(R.string.error);
}
}
public static String getMatchCode(String direction)
{
if(direction.equalsIgnoreCase(Miscellaneous.getAnyContext().getString(R.string.directionStringEquals)))
return directionEquals;
else if(direction.equalsIgnoreCase(Miscellaneous.getAnyContext().getString(R.string.directionStringContains)))
return directionContains;
else if(direction.equalsIgnoreCase(Miscellaneous.getAnyContext().getString(R.string.directionStringStartsWith)))
return directionStartsWith;
else if(direction.equalsIgnoreCase(Miscellaneous.getAnyContext().getString(R.string.directionStringEndsWith)))
return directionEndsWith;
else if(direction.equalsIgnoreCase(Miscellaneous.getAnyContext().getString(R.string.directionStringNotEquals)))
return directionNotEquals;
else
return Miscellaneous.getAnyContext().getString(R.string.error);
}
public static String[] getTriggerTypesAsArray()
{

View File

@ -271,6 +271,8 @@ public class XmlFileInterface
}
else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.headsetPlugged)
serializer.text(String.valueOf(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getHeadphoneType()));
else if(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerType() == Trigger_Enum.notification)
serializer.text(String.valueOf(Rule.getRuleCollection().get(i).getTriggerSet().get(j).getTriggerParameter2()));
serializer.endTag(null, "TriggerParameter2");
serializer.endTag(null, "Trigger");
}
@ -377,7 +379,7 @@ public class XmlFileInterface
}
catch (XmlPullParserException e)
{
e.printStackTrace();
Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
}
catch(FileNotFoundException e)
{
@ -392,12 +394,12 @@ public class XmlFileInterface
}
catch(Exception ex)
{
Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
}
}
catch (IOException e)
{
e.printStackTrace();
Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
}
catch(Exception e)
{
@ -532,11 +534,11 @@ public class XmlFileInterface
}
catch (NumberFormatException e)
{
e.printStackTrace();
Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
}
catch (Exception e)
{
e.printStackTrace();
Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
}
}
else
@ -749,13 +751,11 @@ public class XmlFileInterface
}
catch (XmlPullParserException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
}
}
else if (name.equals("ActionCollection"))
@ -766,13 +766,11 @@ public class XmlFileInterface
}
catch (XmlPullParserException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
Miscellaneous.logEvent("e", "XmlFileInterface", Log.getStackTraceString(e), 1);
}
}
else
@ -863,38 +861,11 @@ public class XmlFileInterface
if (name.equals("TriggerEvent"))
{
String triggerEventString = readTag(parser, "TriggerEvent");
if(triggerEventString.equals("pointOfInterest"))
newTrigger.setTriggerType(Trigger_Enum.pointOfInterest);
else if(triggerEventString.equals("timeFrame"))
newTrigger.setTriggerType(Trigger_Enum.timeFrame);
else if(triggerEventString.equals("charging"))
newTrigger.setTriggerType(Trigger_Enum.charging);
else if(triggerEventString.equals("usb_host_connection"))
newTrigger.setTriggerType(Trigger_Enum.usb_host_connection);
else if(triggerEventString.equals("batteryLevel"))
newTrigger.setTriggerType(Trigger_Enum.batteryLevel);
else if(triggerEventString.equals("speed"))
newTrigger.setTriggerType(Trigger_Enum.speed);
else if(triggerEventString.equals("noiseLevel"))
newTrigger.setTriggerType(Trigger_Enum.noiseLevel);
else if(triggerEventString.equals("wifiConnection"))
newTrigger.setTriggerType(Trigger_Enum.wifiConnection);
else if(triggerEventString.equals("process_started_stopped") | triggerEventString.equals("process_running"))
if(triggerEventString.equals("process_started_stopped") | triggerEventString.equals("process_running"))
newTrigger.setTriggerType(Trigger_Enum.process_started_stopped);
else if(triggerEventString.equals("airplaneMode"))
newTrigger.setTriggerType(Trigger_Enum.airplaneMode);
else if(triggerEventString.equals("roaming"))
newTrigger.setTriggerType(Trigger_Enum.roaming);
else if(triggerEventString.equals("phoneCall"))
newTrigger.setTriggerType(Trigger_Enum.phoneCall);
else if(triggerEventString.equals("nfcTag"))
newTrigger.setTriggerType(Trigger_Enum.nfcTag);
else if(triggerEventString.equals("activityDetection"))
newTrigger.setTriggerType(Trigger_Enum.activityDetection);
else if(triggerEventString.equals("bluetoothConnection"))
newTrigger.setTriggerType(Trigger_Enum.bluetoothConnection);
else if(triggerEventString.equals("headsetPlugged"))
newTrigger.setTriggerType(Trigger_Enum.headsetPlugged);
else
newTrigger.setTriggerType(Trigger_Enum.valueOf(triggerEventString));
}
else if (name.equals("TriggerParameter1"))
{
@ -985,6 +956,8 @@ public class XmlFileInterface
newTrigger.setHeadphoneType(-1);
}
}
newTrigger.setTriggerParameter2(triggerParameter2);
}
else
{
@ -1072,19 +1045,9 @@ public class XmlFileInterface
{
String actionNameString = readTag(parser, "ActionName");
if(actionNameString.equals("setWifi"))
newAction.setAction(Action_Enum.setWifi);
else if(actionNameString.equals("setBluetooth"))
newAction.setAction(Action_Enum.setBluetooth);
else if(actionNameString.equals("setUsbTethering"))
newAction.setAction(Action_Enum.setUsbTethering);
else if(actionNameString.equals("setWifiTethering"))
newAction.setAction(Action_Enum.setWifiTethering);
else if(actionNameString.equals("setDisplayRotation"))
newAction.setAction(Action_Enum.setDisplayRotation);
// *** deprecated
else if(actionNameString.equals("turnWifiOn"))
//else
if(actionNameString.equals("turnWifiOn"))
newAction.setAction(Action_Enum.turnWifiOn);
else if(actionNameString.equals("turnWifiOff"))
newAction.setAction(Action_Enum.turnWifiOff);
@ -1105,29 +1068,9 @@ public class XmlFileInterface
else if(actionNameString.equals("disableScreenRotation"))
newAction.setAction(Action_Enum.disableScreenRotation);
// *** deprecated
else if(actionNameString.equals("triggerUrl"))
newAction.setAction(Action_Enum.triggerUrl);
else if(actionNameString.equals("changeSoundProfile"))
newAction.setAction(Action_Enum.changeSoundProfile);
else if(actionNameString.equals("startOtherActivity"))
newAction.setAction(Action_Enum.startOtherActivity);
else if(actionNameString.equals("waitBeforeNextAction"))
newAction.setAction(Action_Enum.waitBeforeNextAction);
else if(actionNameString.equals("wakeupDevice"))
newAction.setAction(Action_Enum.wakeupDevice);
else if(actionNameString.equals("setAirplaneMode"))
newAction.setAction(Action_Enum.setAirplaneMode);
else if(actionNameString.equals("setDataConnection"))
newAction.setAction(Action_Enum.setDataConnection);
else if(actionNameString.equals("speakText"))
newAction.setAction(Action_Enum.speakText);
else if(actionNameString.equals("sendTextMessage"))
newAction.setAction(Action_Enum.sendTextMessage);
else if(actionNameString.equals("playMusic"))
newAction.setAction(Action_Enum.playMusic);
else if(actionNameString.equals("setScreenBrightness"))
newAction.setAction(Action_Enum.setScreenBrightness);
else
newAction.setAction(Action_Enum.valueOf(actionNameString));
}
else if (name.equals("ActionParameter1"))
{
@ -1215,6 +1158,33 @@ public class XmlFileInterface
}
}
}
else if(newAction.getAction().equals(Action_Enum.startOtherActivity)) // separator has been changed, convert in old files
{
String newTag;
if(tag.contains(Action.intentPairSeperator)) // already has new format
newTag = tag;
else
newTag = tag.replace("/", Action.intentPairSeperator);
String[] newTagPieces = newTag.split(";");
if(newTagPieces.length < 2 || (!newTagPieces[0].contains(Actions.dummyPackageString) && newTagPieces[1].contains(Action.intentPairSeperator)))
{
newTag = Actions.dummyPackageString + ";" + newTag;
newTagPieces = newTag.split(";");
}
if(newTagPieces.length < 3)
newTag += ";" + ActivityManageActionStartActivity.startByActivityString;
else if(newTagPieces.length >= 3)
{
if(newTagPieces[2].contains(Action.intentPairSeperator))
newTag = newTagPieces[0] + ";" + newTagPieces[1] + ";" + ActivityManageActionStartActivity.startByActivityString + ";" + newTagPieces[2];
}
newAction.setParameter2(newTag);
}
else
newAction.setParameter2(tag);
}

View File

@ -114,7 +114,14 @@ public class CellLocationChangedReceiver extends PhoneStateListener
myLocationManager = (LocationManager) AutomationService.getInstance().getSystemService(Context.LOCATION_SERVICE);
currentLocation = getLocation("coarse");
AutomationService.getInstance().getLocationProvider().setCurrentLocation(currentLocation, false);
try
{
AutomationService.getInstance().getLocationProvider().setCurrentLocation(currentLocation, false);
}
catch(NullPointerException e)
{
Miscellaneous.logEvent("e", "LocationProvider", "Location provider is null: " + Log.getStackTraceString(e), 1);
}
}
else
{
@ -123,7 +130,6 @@ public class CellLocationChangedReceiver extends PhoneStateListener
}
}
public Location getLocation(String accuracy)
{
Criteria crit = new Criteria();

View File

@ -0,0 +1,178 @@
package com.jens.automation2.receivers;
import android.annotation.SuppressLint;
import android.os.Build;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import androidx.annotation.RequiresApi;
import com.jens.automation2.AutomationService;
import com.jens.automation2.Miscellaneous;
import com.jens.automation2.Rule;
import com.jens.automation2.Trigger;
import java.util.ArrayList;
import java.util.Calendar;
// See here for reference: http://gmariotti.blogspot.com/2013/11/notificationlistenerservice-and-kitkat.html
@SuppressLint("OverrideAbstract")
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public class NotificationListener extends NotificationListenerService
{
static Calendar lastResponseToNotification = null;
static NotificationListener instance;
static SimpleNotification lastNotification = null;
// the title of the notification,
public static final String EXTRA_TITLE = "android.title";
// the main text payload
public static final String EXTRA_TEXT = "android.text";
// a third line of text, as supplied to
public static final String EXTRA_SUB_TEXT = "android.subText";
// a bitmap to be used instead of the small icon when showing the notification payload
public static final String EXTRA_LARGE_ICON = "android.largeIcon";
public static SimpleNotification getLastNotification()
{
return lastNotification;
}
@Override
public void onCreate()
{
super.onCreate();
instance = this;
}
public static NotificationListener getInstance()
{
return instance;
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void onNotificationPosted(StatusBarNotification sbn)
{
super.onNotificationPosted(sbn);
if(AutomationService.isMyServiceRunning(NotificationListener.this))
checkNotification(true, sbn);
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void onNotificationRemoved(StatusBarNotification sbn)
{
super.onNotificationRemoved(sbn);
if(AutomationService.isMyServiceRunning(NotificationListener.this))
checkNotification(false, sbn);
}
synchronized boolean checkNotification(boolean created, StatusBarNotification sbn)
{
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT)
{
String app = sbn.getPackageName();
String title = sbn.getNotification().extras.getString(EXTRA_TITLE);
String text = sbn.getNotification().extras.getString(EXTRA_TEXT);
lastNotification = new SimpleNotification();
lastNotification.publishTime = Miscellaneous.calendarFromLong(sbn.getPostTime());
lastNotification.created = created;
lastNotification.app = app;
lastNotification.title = title;
lastNotification.text = text;
// if(lastResponseToNotification == null || lastResponseToNotification.getTimeInMillis() < lastNotification.publishTime.getTimeInMillis())
// {
// lastResponseToNotification = Calendar.getInstance();
ArrayList<Rule> ruleCandidates = Rule.findRuleCandidates(Trigger.Trigger_Enum.notification);
for (int i = 0; i < ruleCandidates.size(); i++)
{
if (ruleCandidates.get(i).applies(NotificationListener.this))
ruleCandidates.get(i).activate(AutomationService.getInstance(), false);
}
// }
// else
// Miscellaneous.logEvent("e", "NotificationCheck", "Ignoring notification as it is old.", 5);
}
return false;
}
public static class SimpleNotification
{
boolean created;
Calendar publishTime;
String app, title, text;
public Calendar getPublishTime()
{
return publishTime;
}
public void setPublishTime(Calendar publishTime)
{
this.publishTime = publishTime;
}
public boolean isCreated()
{
return created;
}
public void setCreated(boolean created)
{
this.created = created;
}
public String getApp()
{
return app;
}
public void setApp(String app)
{
this.app = app;
}
public String getTitle()
{
return title;
}
public void setTitle(String title)
{
this.title = title;
}
public String getText()
{
return text;
}
public void setText(String text)
{
this.text = text;
}
}
@Override
public void onListenerConnected()
{
super.onListenerConnected();
}
@Override
public void onListenerDisconnected()
{
super.onListenerDisconnected();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,117 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/bSelectApp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/selectApplication" />
<TextView
android:id="@+id/tvSelectedActivity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
<ImageView
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_margin="10dp"
android:background="#aa000000" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/parameterType"
android:layout_gravity="center_vertical"/>
<Spinner
android:id="@+id/spinnerParameterType"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tvCurrentNfcIdValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/parameterName" />
<EditText
android:id="@+id/etParameterName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10" >
<requestFocus />
</EditText>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/parameterValue" />
<EditText
android:id="@+id/etParameterValue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10" />
</LinearLayout>
<Button
android:id="@+id/bAddIntentPair"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/addIntentValue" />
<ImageView
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_margin="10dp"
android:background="#aa000000" />
<ListView
android:id="@+id/lvIntentPairs"
android:layout_width="match_parent"
android:layout_height="115dp" >
</ListView>
<Button
android:id="@+id/bSaveActionStartOtherActivity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/save" />
</LinearLayout>
</ScrollView>

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/default_margin">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tvMessageTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="30dp" />
<TextView
android:id="@+id/tvLongMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20dp"
android:layout_marginTop="@dimen/default_margin" />
<TextView
android:id="@+id/tvMessageLink"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20dp"
android:layout_marginTop="@dimen/default_margin" />
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_margin" >
<androidx.appcompat.widget.LinearLayoutCompat
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_height="match_parent"
android:layout_width="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
android:text="@string/settings"
android:layout_marginBottom="@dimen/default_margin"/>
<Button
android:id="@+id/bMoreSettings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/moreSettings" />
<ImageView
android:layout_width="match_parent"
android:layout_span="2"
android:layout_height="1dp"
android:layout_margin="@dimen/default_margin"
android:background="#aa000000" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/default_margin"
android:text="@string/importExportExplanation" />
<Button
android:id="@+id/bImportConfiguration"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/importConfiguration" />
<Button
android:id="@+id/bExportConfiguration"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/exportConfiguration" />
<ImageView
android:layout_width="match_parent"
android:layout_span="2"
android:layout_height="1dp"
android:layout_margin="@dimen/default_margin"
android:background="#aa000000" />
<Button
android:id="@+id/bVolumeTest"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/volumeTest" />
<ImageView
android:layout_width="match_parent"
android:layout_span="2"
android:layout_height="1dp"
android:layout_margin="@dimen/default_margin"
android:background="#aa000000" />
<Button
android:id="@+id/bSettingsSetToDefault"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/defaultSettings" />
<ImageView
android:layout_width="match_parent"
android:layout_span="2"
android:layout_height="1dp"
android:layout_margin="@dimen/default_margin"
android:background="#aa000000" />
<Button
android:id="@+id/bShareConfigAndLog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/shareConfigAndLogFilesWithDev" />
<TextView
android:id="@+id/tvFileStoreLocation"
android:layout_marginVertical="@dimen/default_margin"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</androidx.appcompat.widget.LinearLayoutCompat>
</ScrollView>

View File

@ -0,0 +1,322 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_margin" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:stretchColumns="1"
android:shrinkColumns="1" >
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_span="2"
android:textSize="25dp"
android:textStyle="bold"
android:layout_marginBottom="@dimen/default_margin"
android:text="@string/selectApplication" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_span="2"
android:inputType="textMultiLine"
android:text="@string/startAppChoiceNote" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/showStartProgramExamples"
android:layout_marginTop="@dimen/default_margin"
android:layout_span="2"
android:text="@string/openExamplesPage" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:text="@string/startAppSelectionType" />
<RadioGroup
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RadioButton
android:id="@+id/rbStartAppSelectByActivity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/startAppByActivity" />
<RadioButton
android:id="@+id/rbStartAppSelectByAction"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/startAppByAction" />
</RadioGroup>
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="match_parent"
android:layout_span="2"
android:layout_height="1dp"
android:layout_margin="10dp"
android:background="#aa000000" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:text="@string/startAppStartType" />
<RadioGroup
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RadioButton
android:id="@+id/rbStartAppByActivity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/startAppByStartActivity" />
<RadioButton
android:id="@+id/rbStartAppByBroadcast"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/startAppBySendBroadcast" />
</RadioGroup>
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="match_parent"
android:layout_span="2"
android:layout_height="1dp"
android:layout_margin="10dp"
android:background="#aa000000" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:id="@+id/bSelectApp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/selectApplication" />
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/packageName" />
<EditText
android:id="@+id/etPackageName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textMultiLine"
android:text=""
android:textAppearance="?android:attr/textAppearanceMedium" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/activityOrActionName" />
<EditText
android:id="@+id/etActivityOrActionPath"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textMultiLine"
android:text=""
android:textAppearance="?android:attr/textAppearanceMedium" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="match_parent"
android:layout_span="2"
android:layout_height="1dp"
android:layout_margin="10dp"
android:background="#aa000000" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_span="2"
android:textSize="25dp"
android:textStyle="bold"
android:layout_marginBottom="@dimen/default_margin"
android:text="@string/addParameters" />
</TableRow>
<TableRow>
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_span="2"
android:layout_marginBottom="@dimen/default_margin"
android:text="@string/intentDataComment" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/parameterType" />
<Spinner
android:id="@+id/spinnerParameterType"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tvCurrentNfcIdValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/parameterName" />
<EditText
android:id="@+id/etParameterName"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/parameterValue" />
<EditText
android:id="@+id/etParameterValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</TableRow>
</TableLayout>
<Button
android:id="@+id/bAddIntentPair"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/addIntentValue" />
<ImageView
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_margin="10dp"
android:background="#aa000000" />
<ListView
android:id="@+id/lvIntentPairs"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="115dp"
android:layout_marginBottom="@dimen/default_margin" />
<Button
android:id="@+id/bSaveActionStartOtherActivity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/save" />
</LinearLayout>
</ScrollView>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_margin="@dimen/default_margin">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="40dp"
android:layout_marginBottom="@dimen/default_margin"
android:text="@string/playSound" />
<CheckBox
android:id="@+id/chkPlaySoundAlwaysPlay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/alwaysPlay" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/alwaysPlayExplanation" />
<EditText
android:id="@+id/etSelectedSoundFile"
android:layout_marginVertical="@dimen/default_margin"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/bSelectSoundFile"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/selectSoundFile" />
<Button
android:id="@+id/bSavePlaySound"
android:layout_marginTop="@dimen/default_margin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/save" />
</LinearLayout>

View File

@ -17,7 +17,7 @@
android:background="@color/barBackgroundColor" >
<TextView
android:id="@+id/tvSelectedActivity"
android:id="@+id/etActivityOrActionPath"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/general"

View File

@ -27,7 +27,7 @@
</LinearLayout>
<TextView
android:id="@+id/tvMainScreenNote1"
android:id="@+id/tvMainScreenNotePermissions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
@ -40,7 +40,7 @@
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="@+id/tvMainScreenNote2"
android:id="@+id/tvMainScreenNoteFeaturesFromOtherFlavor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
@ -53,7 +53,20 @@
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="@+id/tvMainScreenNote3"
android:id="@+id/tvMainScreenNoteLocationImpossibleBlameGoogle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:scrollHorizontally="false"
android:textColor="@color/importantMessage"
android:singleLine="false"
android:visibility="gone"
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="@+id/tvMainScreenNoteNews"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
@ -289,70 +302,36 @@
</TableRow>
</TableLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_margin="10dp"
android:layout_marginTop="30dp"
android:gravity="top" >
<Button
android:id="@+id/bShowHelp"
android:layout_gravity="center_vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/showHelp"
android:layout_weight="1" />
<Button
android:id="@+id/bVolumeTest"
android:layout_gravity="center_vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/volumeTest"
android:layout_weight="1" />
<Button
android:id="@+id/bPrivacy"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:enabled="true"
android:text="@string/privacy" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:orientation="vertical"
android:layout_margin="10dp"
android:layout_marginTop="30dp"
android:gravity="center_horizontal" >
<Button
android:id="@+id/bShowHelp"
android:layout_gravity="center_vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/showHelp" />
<Button
android:id="@+id/bPrivacy"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:enabled="true"
android:text="@string/privacy" />
<Button
android:id="@+id/bSettings"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:layout_weight="1"
android:text="@string/menu_settings" />
<!-- <Button
android:id="@+id/bSettingsErase"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/eraseSettings" /> -->
<Button
android:id="@+id/bSettingsSetToDefault"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/defaultSettings" />
android:text="@string/settings" />
</LinearLayout>

View File

@ -40,7 +40,7 @@
android:id="@+id/tvRuleTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/profileList"
android:text="@string/profiles"
android:layout_marginLeft="10dp"
android:textAppearance="?android:attr/textAppearanceLarge" />

View File

@ -84,18 +84,21 @@
<Button
android:id="@+id/cmdTriggerAdd"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/addTrigger" />
<Button
android:id="@+id/cmdActionAdd"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/addAction" />
<Button
android:id="@+id/cmdSaveRule"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/saveRule" />

View File

@ -0,0 +1,148 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp" >
<LinearLayout
android:layout_margin="@dimen/default_margin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/notification"
android:textSize="25dp"
android:layout_marginBottom="@dimen/default_margin" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/default_margin"
android:text="@string/notificationTriggerExplanation" />
<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TableRow
android:layout_marginBottom="@dimen/activity_vertical_margin">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/direction"/>
<CheckBox
android:id="@+id/chkNotificationDirection"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/notificationAppears"
android:checked="true"/>
</TableRow>
<TableRow
android:layout_marginBottom="@dimen/activity_vertical_margin">
<TextView
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/application" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/etActivityOrActionPath"
android:layout_marginHorizontal="@dimen/default_margin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/anyApp"
android:textAppearance="?android:attr/textAppearanceMedium" />
<Button
android:id="@+id/bSelectApp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/selectApplication" />
</LinearLayout>
</TableRow>
<TableRow
android:layout_marginBottom="@dimen/activity_vertical_margin">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/title" />
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Spinner
android:id="@+id/spinnerTitleDirection"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/etNotificationTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10" />
</LinearLayout>
</TableRow>
<TableRow
android:layout_marginBottom="@dimen/activity_vertical_margin">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/text" />
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Spinner
android:id="@+id/spinnerTextDirection"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/etNotificationText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10" />
</LinearLayout>
</TableRow>>
</TableLayout>
<Button
android:id="@+id/bSaveTriggerNotification"
android:layout_marginTop="@dimen/default_margin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/save" />
</LinearLayout>
</ScrollView>

View File

@ -31,7 +31,7 @@
<CheckBoxPreference
android:key="writeLogFile"
android:summary="@string/onOff"
android:title="@string/writeLogFileToSd" />
android:title="@string/writeLogFile" />
<EditTextPreference
android:key="logLevel"

View File

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/default_margin"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
android:text="@string/phoneCall" />
<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:shrinkColumns="1"
android:stretchColumns="1" >
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/state" />
<RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/started" />
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/stopped" />
</RadioGroup>
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/phoneDirection" />
<RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/incoming" />
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/outgoing" />
</RadioGroup>
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/phoneNumber" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/phoneNumberExplanation" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/importNumberFromContacts" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/phoneNumberExplanation" />
</TableRow>
</TableLayout>
</androidx.appcompat.widget.LinearLayoutCompat>

View File

@ -1,5 +1,5 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_settings"
android:title="@string/menu_settings"
android:title="@string/settings"
android:orderInCategory="100" />
</menu>

View File

@ -1,8 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="menu_settings">Einstellungen</string>
<string name="app_name">Automation</string>
<string name="title_activity_main">Automation</string>
<string name="ruleActivate">Aktiviere Regel %1$s</string>
<string name="profileActivate">Aktiviere Profil %1$s</string>
<string name="ruleActivateToggle">Aktiviere Regel %1$s im Umschaltmodus</string>
@ -73,7 +71,7 @@
<string name="end">Ende</string>
<string name="save">Speichern</string>
<string name="urlToTrigger">URL, die ausgelöst werden soll:</string>
<string name="urlLegend">Variablen:\nSie können die folgenden Variablen verwenden. Vor dem Auslösen werden sie mit dem entsprechenden Wert Ihres Geräts ersetzt. Die Klammern müssen in den Text mit aufgenommen werden.\n\n[uniqueid] - Die Unique ID Ihres Geräts\n[serialnr] - Die Seriennummer Ihres Geräts\n[latitude] - Ihr gegenwärtiger Breitengrad\n[longitude] - Ihr gegenwärtiger Längengrad\n[phonenr] - Nummer des letzten ein- oder ausgehenden Anrufs\n[d] - Tag des Monats, 2-stellig mit führender Null\n[m] - Monat als Zahl, mit führenden Nullen\n[Y] - Vierstellige Jahreszahl\n[h] - Stunde im 12-Stunden-Format, mit führenden Nullen\n[H] - Stunde im 24-Stunden-Format, mit führenden Nullen\n[i] - Minuten, mit führenden Nullen\n[s] - Sekunden, mit führenden Nullen\n[ms] - milliseconds</string>
<string name="urlLegend">Variablen:\nSie können die folgenden Variablen verwenden. Vor dem Auslösen werden sie mit dem entsprechenden Wert Ihres Geräts ersetzt. Die Klammern müssen in den Text mit aufgenommen werden.\n\n[uniqueid] - Die Unique ID Ihres Geräts\n[serialnr] - Die Seriennummer Ihres Geräts\n[latitude] - Ihr gegenwärtiger Breitengrad\n[longitude] - Ihr gegenwärtiger Längengrad\n[phonenr] - Nummer des letzten ein- oder ausgehenden Anrufs\n[d] - Tag des Monats, 2-stellig mit führender Null\n[m] - Monat als Zahl, mit führenden Nullen\n[Y] - Vierstellige Jahreszahl\n[h] - Stunde im 12-Stunden-Format, mit führenden Nullen\n[H] - Stunde im 24-Stunden-Format, mit führenden Nullen\n[i] - Minuten, mit führenden Nullen\n[s] - Sekunden, mit führenden Nullen\n[ms] - milliseconds\n[notificationTitle] - Titel der letzten Benachrichtigung\n[notificationText] - Text der letzten Benachrichtigung</string>
<string name="wifi">WLAN</string>
<string name="activating">Aktiviere</string>
<string name="deactivating">Deaktiviere</string>
@ -98,7 +96,7 @@
<string name="generalSettings">Allgemeine Einstellungen</string>
<string name="startAtSystemBoot">Beim Gerätestart mitstarten</string>
<string name="onOff">Ein/aus</string>
<string name="writeLogFileToSd">Protokoll auf SD Karte schreiben</string>
<string name="writeLogFile">Protokoll schreiben</string>
<string name="useTextToSpeechOnNormalSummary">TTS auf normal</string>
<string name="useTextToSpeechOnVibrateSummary">TTS auf vibration</string>
<string name="useTextToSpeechOnSilentSummary">TTS auf stumm</string>
@ -257,7 +255,7 @@
<string name="anotherPoiByThatName">Es gibt bereits einen Ort mit diesem Namen.</string>
<string name="anotherRuleByThatName">Es gibt bereits eine Regel mit diesem Namen.</string>
<string name="startOtherActivity">Programm starten</string>
<string name="selectApplication">Wählen Sie eine Anwendung</string>
<string name="selectApplication">Wählen Sie\neine Anwendung</string>
<string name="selectPackageOfApplication">Wählen Sie ein Paket der Anwendung</string>
<string name="selectActivityToBeStarted">Wählen Sie die Activity des Pakets</string>
<string name="errorStartingOtherActivity">Fehler beim Starten einer anderen Anwendung</string>
@ -464,8 +462,7 @@
<string name="noMapsApplicationFound">Auf Ihrem Gerät konnte keine Kartenanwendung gefunden werden.</string>
<string name="locationEngineNotActive">Positionsbestimmung nicht aktiv.</string>
<string name="addProfile">Profil erstellen</string>
<string name="profileList">Profile</string>
<string name="profile">Profil</string>
<string name="profile">Profil</string>
<string name="soundMode">Tonmodus</string>
<string name="volumes">Lautstärken</string>
<string name="incomingCallsRingtone">Ton für eingehende Anrufe</string>
@ -526,7 +523,7 @@
<string name="continueText">Fortfahren</string>
<string name="rule">Regel</string>
<string name="storeSettings">Einstellungen lesen und speichern</string>
<string name="appRunningInLimitedMode">Die Anwendung läuft in einem eingeschränkten Modus, weil nicht alle benötigen Rechte genehmigt wurden.</string>
<string name="featuresDisabled">WARNING: Funktionen wurden deaktiviert, Automation läuft in einem eingeschränkten Modus. Klicken Sie hier für mehr Informationen.</string>
<string name="ruleLegend">Grün = aktiviert, rot = deaktiviert, gelb = nicht genügend Rechte</string>
<string name="systemSettingsNote1">Die Berechtigung Betriebssystemeinstellungen ändern zu können, wird benötigt (auch für einfache Dinge wie Bluetooth oder WLAN einschalten). Nach dem Klick auf "Fortfahren" öffnet sich ein Fenster, in dem Sie das für Automation aktivieren müssen. Drücken Sie dann auf Ihren "Zurück" Knopf.</string>
<string name="systemSettingsNote2">Anschließend werden in einem zweiten Dialog weitere Berechtigungen angefragt.</string>
@ -594,4 +591,78 @@
<string name="deviceDoesNotHaveBluetooth">Dieses Gerät scheint kein Bluetooth zu haben. Sie können mit der Konfiguration fortfahren, aber es wird vermutlich keinen Effekt haben.</string>
<string name="manageLocations">Orte anlegen oder ändern</string>
<string name="publishedOn">veröffentlicht am</string>
<string name="filesHaveBeenMovedTo">Automation benutzt jetzt ein anderes Verzeichnis, um Ihre Daten zu speichern. Alle Ihre Automation-Dateien wurden hierhin verschoben: \"%s\". Die Berechtigung für den externen Speicher wird nun nicht mehr benötigt; Sie können Sie entfernen. In einer künftigen Version wird sie entfernt werden.</string>
<string name="locationEngineDisabledShort">Die Position kann nicht mehr bestimmt werden.</string>
<string name="locationEngineDisabledLong">Leider kann die Position nicht mehr bestimmt werden. Großer Dank dafür geht an Google für seine unendliche Weisheit und Großzügigkeit.\\n\\nBeginnend mit Android 10 wurde eine neue Berechtigung eingeführt, die benötigt wird, um als App die Position auch im Hintergrund bestimmen zu können, was, für eine App wie diese, natürlich notwendig ist.\\n\\nWährend ich das grundsätzlich für eine gute Idee halte, gilt das nicht für die Schikanen, die man Entwicklern damit zumutet.\\n\\nWenn man eine App entwickelt, kann man versuchen sich für diese Berechtigung zu qualifizieren, indem man einen Katalog von Bedingungen erfüllt. Leider wurden neue Versionen meiner Anwendung über einen Zeitraum von drei Monaten immer wieder abgelehnt.\\n\\nDas lief auf die immer gleiche Art ab:\\n\\nIch habe eine neue Version eingereicht, die all diese Anforderungen erfüllt hat.\\n\\nGoogles miserabler Entwickler-Support behauptete ich würde sie nicht einhalten.\\n\\nIch habe Beweise geliefert, daß ich alles einhalte.<br />Ich bekam eine Antwort wie "Ich kann Ihnen nicht weiterhelfen.\\n\\nIrgendwann habe ich aufgegeben.\\n\\nDie Folge davon ist nun, daß die Google Play Version keine Positionsbestimmung mehr im Hintergrund durchführen kann. Meine einzige Alternative wäre es gewesen, daß die ganze Anwendung aus dem Store fliegt.\\n\\nDas tut mir sehr leid, aber ich habe mein Bestes gegeben mit einem Kunden\"dienst\" zu diskutieren, der mehrfach beim Turing-Test durchgefallen ist.\\n\\nDie gute Nachricht: Die Anwendung kann es immer noch!\\n\\nAutomation ist nun Open Source Software und kann ab sofort bei F-Droid heruntergeladen werden. F-Droid ist ein freier Appstore, der Ihre Privatsphäre respektiert - statt nur so zu tun wie Google das macht.\\n\\nSichern Sie Ihre Konfiguratinsdatei, deinstallieren Sie dazu diese Anwendung, installieren sie von F-Droid neu, Konfigurationsdatei zurückspielen und fertig.\\n\\nKlicken Sie hier, um mehr herauszufinden:</string>
<string name="filesStoredAt">Konfigurations- und Logdateien werden hier gespeichert: %1$s. Klicken Sie hier auf diesen Text, um dieses Verzeichnis in einem Datei-Explorer zu öffnen. Leider funktioniert das nur auf gerooteten Geräten.\n\nFür alle anderen Geräte: Benutzen Sie einfach den Export Button, wenn Sie eine Sicherung anlegen wollen.</string>
<string name="directionStringEquals">ist gleich</string>
<string name="directionStringContains">enthält</string>
<string name="directionStringStartsWith">beginnt mit</string>
<string name="directionStringEndsWith">endet mit</string>
<string name="directionStringNotEquals">ist nicht gleich</string>
<string name="positioningEngine">Ortungsroutine</string>
<string name="googleSarcasm">Dank Google\'s unendlicher Weisheit und konstantem Unterfangen jederman\'s Privatsphäre zu schützen (Sarkasmus) müssen Auslöser und Aktionen, die SMS oder den Telefoniestatus betreffen, entfernt werden.</string>
<string name="screenLockSoundNotice">Die Töne bei der Bildschirmsperre können ab Android 6 nicht mehr verändert werden. Was immer Sie hier einstellen, wird voraussichtlich nicht mehr funktionieren.</string>
<string name="googleLocationChicaneryOld">Diese Anwendung sammelt Positionsdaten, um festzustellen, ob Sie sich gerade an einem der Orte aufhalten, die Sie definiert haben. Außerdem wird es benutzt, um Ihre Geschwindigkeit zu ermitteln, falls Sie diese in Regeln verwenden. Das wird auch dann gemacht, wenn das Programm nicht im Vordergrund ist (aber nur, wenn der Dienst aktiv ist).</string>
<string name="error">Fehler</string>
<string name="featureNotInFdroidVersion">Diese Funktion basiert auf nicht-freier Software (Google Bibliotheken). Daher ist sie in der F-Droid Version nicht verfügbar.</string>
<string name="settingsReferringToRestrictedFeatures">Ihre Einstellungen und Regeln verwenden derzeit nicht-freie Funktionen (Google Bibliotheken). Diese sind in der F-Droid Version nicht funktionsfähig. Das schließt die Erkennung Ihrer gegenwärtigen körperlichen Aktivität ein.</string>
<string name="displayNewsOnMainScreen">Nachrichten auf dem Hauptbildschirm anzeigen</string>
<string name="displayNewsOnMainScreenDescription">Nachrichten ausschließlich über diese Anwendung, wir sprechen von 1-2 pro Jahr, nicht mehr.</string>
<string name="newsOptIn">Möchten Sie wichtige Nachrichten über diese Anwendung auf dem Hauptbildschirm angezeigt bekommen? Diese werden von der Webseite des Entwicklers heruntergeladen. Es gibt keine aufdringliche Benachrichtigung, etc., lediglich eine stille Anzeige auf dem Hauptbildschirm.</string>
<string name="locationDisabled">Ortung deaktiviert</string>
<string name="notification">Benachrichtigung</string>
<string name="title">Titel</string>
<string name="text">Text</string>
<string name="anyApp">Irgendeine Anwendung</string>
<string name="postsNotification">%1$s zeigt eine Benachrichtung an</string>
<string name="removedNotification">Benachrichtigung von %1$s wurde entfernt</string>
<string name="notificationAppears">Benachrichtigung wird angezeigt</string>
<string name="notificationDisappears">Benachrichtigung wird entfernt</string>
<string name="direction">Richtung</string>
<string name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">Systembenachrichtigungen lesen</string>
<string name="playSound">Tondatei abspielen</string>
<string name="alwaysPlay">immer abspielen</string>
<string name="selectSoundFile">Tondatei auswählen</string>
<string name="fileDoesNotExist">Datei existiert nicht.</string>
<string name="noFileManageInstalled">Kein Dateimanager installiert.</string>
<string name="alwaysPlayExplanation">Wenn diese Einstellung aktiv ist, wird der Ton immer abgespielt. Wenn die Einstellung inaktiv ist, wird der Ton nur dann abgespielt, wenn das Telefon weder auf stumm noch auf Vibration steht, d.h. Klingeltöne aktiv sind. Allerdings hat es keinen Einfluß auf die Medien-Lautstärke. D.h., wenn diese stumm ist, werden Sie so oder so nichts zu hören bekommen.</string>
<string name="shareConfigAndLogFilesWithDev">Konfigurations- und Logdatei mit Entwickler teilen (via email).</string>
<string name="shareConfigAndLogExplanation">Dies wird eine neue Email öffnen mit Konfigurations- und Logdateien als Zip-Anhang. Sie wird nicht automatisch versendet. D.h. Sie können so z.B. auch den Adressaten zu sich selbst ändern.</string>
<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="ruleActivationComplete">Regel \"%1$s\" wurde fertig ausgeführt.</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="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>
<string name="startAppSelectionType">Auswahlmethode</string>
<string name="com.wireguard.android.permission.CONTROL_TUNNELS">Tunnelverbindungen der Wireguard Anwendung steuern</string>
<string name="enterPackageName">Geben Sie einen gültigen Paketnamen ein.</string>
<string name="configurationExportedSuccessfully">Konfiguration erfolgreich exportiert.</string>
<string name="ConfigurationExportError">Beim Exportieren der Konfiguration ist ein Fehler aufgetreten.</string>
<string name="configurationImportedSuccessfully">Konfiguration erfolgreich importiert.</string>
<string name="enterValidAction">Geben Sie eine gültige Action ein.</string>
<string name="exportConfiguration">Konfiguration exportieren</string>
<string name="importConfiguration">Konfiguration importieren</string>
<string name="moreSettings">Mehr Einstellungen</string>
<string name="importExportExplanation">Wenn Sie auf im- oder exportieren klicken, müssen Sie im nächsten Schritt das Verzeichnis auswählen, in das Dateien exportiert oder von dem Dateien importiert werden. Im Falle des Exports können vorhandene Dateien in diesem Ordner überschrieben werden.</string>
<string name="intentDataComment">Wenn Ihr Parameter vom Typ \"Uri\" ist und Sie \"IntentName\" als Name angeben (Groß-/Kleinschreibung ist irrelevant), wird der Parameter nicht als normaler Parameter mit putExtra() angehängt, sondern wird stattdessen mit setData() angehängt.</string>
<string name="noApplicableFilesFoundInDirectory">Keine passenden Dateien im Ordner gefunden.</string>
<string name="noFilesImported">Keine Dateien konnten importiert werden.</string>
<string name="notAllFilesImported">Nicht alle passenden Dateien konnten importiert werden.</string>
<string name="openExamplesPage">Webseite mit Beispielen öffnen</string>
<string name="phoneNumberExplanation">Sie können eine bestimmte Nummer eingeben, aber müssen nicht. Wenn Sie eine angeben wollen, können Sie auch eine aus dem Adressbuch auswählen.</string>
<string name="prefsImportError">Fehler beim Importieren der Einstellungen.</string>
<string name="rulesImportedSuccessfully">Regeln und Orte wurden erfolgreich importiert.</string>
<string name="rulesImportError">Fehler beim Importieren der Regeln.</string>
<string name="startAppBySendBroadcast">per sendBroadcast()</string>
<string name="startAppByStartActivity">per startActivity()</string>
<string name="startAppStartType">Start-Typ wählen</string>
<string name="state">Status</string>
<string name="stringNotAllowed">Zeichenkette %1$s is nicht erlaubt.</string>
<string name="android.permission.ACTIVITY_RECOGNITION">Aktivitätserkennung</string>
<string name="packageName">Paketname</string>
<string name="activityOrActionName">Acitivity/Action name</string>
</resources>

View File

@ -1,15 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="menu_settings">Configuratión</string>
<string name="app_name">Automation</string>
<string name="title_activity_main">Automation</string>
<string name="ruleActivate">Estoy activando regla %1$s</string>
<string name="profileActivate">Estoy activando perfil %1$s</string>
<string name="ruleActivateToggle">Estoy activando regla %1$s in el modo del inventir</string>
<string name="addPoi">Crear lugar</string>
<string name="addRule">Crear regla</string>
<string name="poiList">Listo de lugares:</string>
<string name="ruleList">Listo del reglas:</string>
<string name="ruleList">Lista de reglas:</string>
<string name="pleaseEnterValidName">Inserta un nombre válido, por favor.</string>
<string name="pleaseSpecifiyTrigger">Inserta al menos un disparador, por favor.</string>
<string name="pleaseSpecifiyAction">Inserta al menos un acción, por favor.</string>
@ -29,549 +27,289 @@
<string name="no">No</string>
<string name="logGotGpsUpdate">He recibido una posición de GPS. Precisión:</string>
<string name="logGotNetworkUpdate">He recibido una posición de network. Precisión:</string>
<string name="pleaseEnterValidLatitude">Please enter a valid latitude.</string>
<string name="pleaseEnterValidLongitude">Please enter a valid longitude.</string>
<string name="pleaseEnterValidRadius">Please enter a valid positive radius.</string>
<string name="selectOneDay">Select at least one day.</string>
<string name="logAttemptingToBindToService">Attempting to bind to service... </string>
<string name="logAttemptingToUnbindFromService">Attempting to unbind from service... </string>
<string name="logBoundToService">Bound to service.</string>
<string name="logUnboundFromService">Unbound from service.</string>
<string name="logServiceAlreadyRunning">Request to start service, but it is already running.</string>
<string name="whatToDoWithRule">Do what with rule?</string>
<string name="whatToDoWithPoi">Do what with location?</string>
<string name="whatToDoWithProfile">Do what with profile?</string>
<string name="delete">delete</string>
<string name="deleteCapital">Delete</string>
<string name="serviceStopped">Automation service stopped.</string>
<string name="logServiceStopping">Stopping service.</string>
<string name="stillGettingPosition">Still getting position</string>
<string name="lastRule">Last Rule:</string>
<string name="at">at</string>
<string name="service">Service:</string>
<string name="getCurrentPosition">Get current location</string>
<string name="savePoi">Save location</string>
<string name="deletePoi">Delete location</string>
<string name="latitude">Latitude</string>
<string name="longitude">Longitude</string>
<string name="ruleName">Rule name</string>
<string name="triggers">Trigger(s)</string>
<string name="triggersComment">and-connected (all have to apply at the same time)</string>
<string name="addTrigger">Add trigger</string>
<string name="actions">Action(s)</string>
<string name="actionsComment">(will be executed in that order)</string>
<string name="addAction">Add action</string>
<string name="saveRule">Save Rule</string>
<string name="monday">Monday</string>
<string name="tuesday">Tuesday</string>
<string name="wednesday">Wednesday</string>
<string name="thursday">Thursday</string>
<string name="friday">Friday</string>
<string name="saturday">Saturday</string>
<string name="sunday">Sunday</string>
<string name="start">Start</string>
<string name="end">End</string>
<string name="save">Save</string>
<string name="urlToTrigger">URL to trigger:</string>
<string name="urlLegend">Variables:\nYou can use the following variables. Upon triggering they will be replaced with the corresponding value on your device. Include the brackets in your text.\n\n[uniqueid] - Your device\'s unique id\n[serialnr] - Your device\'s serial number\n[latitude] - Your device\'s latitude\n[longitude] - Your device\'s longitude\n[phonenr] - Number of last incoming or outgoing call\n[d] - Day of the month, 2 digits with leading zeros\n[m] - Numeric representation of a month, with leading zeros\n[Y] - A full numeric representation of a year, 4 digits\n[h] - 12-hour format of an hour with leading zeros\n[H] - 24-hour format of an hour with leading zeros\n[i] - Minutes with leading zeros\n[s] - Seconds, with leading zeros\n[ms] - milliseconds</string>
<string name="wifi">wifi</string>
<string name="activating">Activating</string>
<string name="deactivating">Deactivating</string>
<string name="bluetoothFailed">Failed to trigger Bluetooth. Does this device have Bluetooth?</string>
<string name="urlTooShort">The url has to have least 10 characters.</string>
<string name="textTooShort">The text has to have least 10 characters.</string>
<string name="selectTypeOfTrigger">Select type of trigger</string>
<string name="entering">entering</string>
<string name="leaving">leaving</string>
<string name="noPoisSpecified">You haven\'t specified any locations. Do that first.</string>
<string name="started">started</string>
<string name="stopped">stopped</string>
<string name="connected">connected</string>
<string name="disconnected">disconnected</string>
<string name="selectPoi">Select location</string>
<string name="selectTypeOfAction">Select type of action</string>
<string name="selectSoundProfile">Select sound profile</string>
<string name="whatToDoWithTrigger">What to do with it trigger?</string>
<string name="whatToDoWithAction">What to do with it action?</string>
<string name="radiusHasToBePositive">Radius has to be a positive number.</string>
<string name="poiStillReferenced">There are still rules that reference this location (%1$s). I can\'t delete it, yet.</string>
<string name="generalSettings">General settings</string>
<string name="startAtSystemBoot">Start at system boot</string>
<string name="onOff">On/Off</string>
<string name="writeLogFileToSd">Write log file to SD card</string>
<string name="useTextToSpeechOnNormalSummary">Use TextToSpeech on normal</string>
<string name="useTextToSpeechOnVibrateSummary">Use TextToSpeech on vibrate</string>
<string name="useTextToSpeechOnSilentSummary">Use TextToSpeech on silent</string>
<string name="useTextToSpeechOnNormalTitle">TTS on normal</string>
<string name="useTextToSpeechOnVibrateTitle">TTS on vibrate</string>
<string name="useTextToSpeechOnSilentTitle">TTS on silent</string>
<string name="positioningSettings">Positioning settings</string>
<string name="listenToWifiState">Listen to wifi state changes where possible</string>
<string name="wifiState">Wifi state</string>
<string name="listenToAccelerometerState">Observe device movement where wifi is not available</string>
<string name="accelerometer">Accelerometer</string>
<string name="accelerometerTimer">Use Accelerometer after x minutes without cell mast change</string>
<string name="cellMastIdleTime">Cell mast idle time</string>
<string name="accelerometerThresholdDescription">Threshold for accelerometer movements</string>
<string name="accelerometerThreshold">Accelerometer threshold</string>
<string name="positioningThresholds">Positioning thresholds</string>
<string name="minimumDistanceChangeForGpsLocationUpdates">Minimum distance change for gps location updates</string>
<string name="distanceForGpsUpdate">Distance for gps update [m]</string>
<string name="minimumDistanceChangeForNetworkLocationUpdates">Minimum distance change for network location updates</string>
<string name="distanceForNetworkUpdate">Distance for network update [m]</string>
<string name="satisfactoryAccuracyGps">Satisfactory accuracy when getting location via gps in meters</string>
<string name="gpsAccuracy">GPS accuracy [m]</string>
<string name="satisfactoryAccuracyNetwork">Satisfactory accuracy when getting location via cell towers in meters</string>
<string name="networkAccuracy">Network accuracy [m]</string>
<string name="minimumTimeForLocationUpdates">Minimum time change in seconds for location updates</string>
<string name="timeForUpdate">Time for update [milliseconds]</string>
<string name="soundSettings">Sound settings</string>
<string name="showHelp">Show help</string>
<string name="rules">Rules</string>
<string name="helpTextRules">All triggers in a rule are AND-connected. The rule will only apply if all triggers are met. If you want OR create another rule.</string>
<string name="timeframes">TimeFrames</string>
<string name="helpTextTimeFrame">If you specify a rule with a timeframe you have two choices. You can choose between entering and leaving a timeframe. Either way an action is triggered only once.\nSo if you create a rule that has \"entering timeframe xyz\" as trigger and let it change your sound profile to vibrate that does not mean that the phone will automatically go to ring if the timeframe is over. If you want that you need to specify another rule with another timeframe.</string>
<string name="toggableRules">Toggable rules</string>
<string name="helpTextToggable">Rules have a flag called \"Toggable\". This means that if a rule is executed and afterwards the same triggers apply again the rule will be executed in an opposite mode where applicable. Currently this will only happen in conjunction with NFC tags. If you tap them twice and there\'s a toggable rule associated with it it will do the opposite of the current situation, e.g. deactivate wifi if it\'s currently activated.</string>
<string name="helpTextProcessMonitoring">If you specify a rule with a process monitor the application will check for that process every x seconds (you can change that in settings). I know that can be kind of slow, but continuous monitoring would drain the battery to fast. And there is no broadcast from the OS for that event.</string>
<string name="speedMaximumTimeBetweenLocations">Maximum time between 2 locations for speed determination.</string>
<string name="speedMaximumTime">Time in minutes</string>
<string name="exceeds">exceeds</string>
<string name="dropsBelow">drops below</string>
<string name="settingsCategoryNoiseLevelMeasurements">Noise level measurement</string>
<string name="timeBetweenNoiseLevelMeasurementsSummary">Seconds between noise level measurements</string>
<string name="timeBetweenNoiseLevelMeasurementsTitle">Seconds between noise level measurements</string>
<string name="lengthOfNoiseLevelMeasurementsSummary">Length in seconds for each noise level measurement</string>
<string name="lengthOfNoiseLevelMeasurementsTitle">Length of each noise level measurement</string>
<string name="referenceValueForNoiseLevelMeasurementsSummary">Physical reference value for noise level measurement</string>
<string name="referenceValueForNoiseLevelMeasurementsTitle">Reference for noise measurement</string>
<string name="logLevelSummary">Log level (1=minimum, 5=maximum)</string>
<string name="logLevelTitle">Log level</string>
<string name="ruleActive">Rule active</string>
<string name="triggerPointOfInterest">Location</string>
<string name="triggerTimeFrame">Timeframe</string>
<string name="triggerCharging">Battery charging</string>
<string name="triggerUsb_host_connection">USB connection to a computer</string>
<string name="triggerSpeed">Speed</string>
<string name="triggerNoiseLevel">Background noise level</string>
<string name="actionSetWifi">Wifi</string>
<string name="actionSetBluetooth">Bluetooth</string>
<string name="actionSetUsbTethering">USB Tethering</string>
<string name="actionSetWifiTethering">Wifi Tethering</string>
<string name="actionSetDisplayRotation">Display rotation</string>
<string name="actionTurnWifiOn">turn Wifi on</string>
<string name="actionTurnWifiOff">turn Wifi off</string>
<string name="actionTurnBluetoothOn">turn Bluetooth on</string>
<string name="actionTurnBluetoothOff">turn Bluetooth off</string>
<string name="actionTriggerUrl">Trigger a URL</string>
<string name="actionChangeSoundProfile">Change sound profile</string>
<string name="actionTurnUsbTetheringOn">turn USB Tethering on</string>
<string name="actionTurnUsbTetheringOff">turn USB Tethering off</string>
<string name="actionTurnWifiTetheringOn">turn Wifi Tethering on</string>
<string name="actionTurnWifiTetheringOff">turn Wifi Tethering off</string>
<string name="actionTurnAirplaneModeOn">turn airplane mode on</string>
<string name="actionTurnAirplaneModeOff">turn airplane mode off</string>
<string name="actionEnableScreenRotation">enable screen rotation</string>
<string name="actionDisableScreenRotation">disable screen rotation</string>
<string name="screenRotationEnabled">ScreenRotation enabled.</string>
<string name="screenRotationDisabled">ScreenRotation disabled.</string>
<string name="screenRotationAlreadyEnabled">ScreenRotation was already enabled.</string>
<string name="screenRotationAlreadyDisabled">ScreenRotation was already disabled.</string>
<string name="noPoisDefinedShort">No locations defined.</string>
<string name="activePoi">Active location:</string>
<string name="closestPoi">Closest location:</string>
<string name="overview">Overview</string>
<string name="poi">Location</string>
<string name="pois">Locations</string>
<string name="helpTextPoi">A location is made up of GPS coordinates and a radius. Since positioning via cell towers is rather unprecise (but fast and cheap) do not specify the radius too small. The application will suggest you a minimum radius when you create a new location.</string>
<string name="serviceNotRunning">Service is not running.</string>
<string name="general">General</string>
<string name="generalText">To use this program you must setup rules. Those contain triggers, e.g. if you reach a specified area or you enter a certain time. After that\'s been done click the on/off button on the main screen.</string>
<string name="unknownActionSpecified">Unknown action specified</string>
<string name="errorTriggeringUrl">Error triggering URL</string>
<string name="errorChangingScreenRotation">Error changing screen rotation</string>
<string name="errorDeterminingWifiApState">Error determining wifiAp state</string>
<string name="errorActivatingWifiAp">Error activating wifiAp</string>
<string name="failedToTriggerBluetooth">Failed to trigger Bluetooth. Does this device have Bluetooth?</string>
<string name="logAttemptingDownloadOf">attempting download of</string>
<string name="logErrorGettingConnectionManagerService">Error getting connectionManager service. Not doing anything to UsbTethering.</string>
<string name="logErrorDeterminingCurrentUsbTetheringState">Error determining current UsbTethering state.</string>
<string name="logDetectingTetherableUsbInterface">Detecting tetherable usb interface.</string>
<string name="logClearingBothLocationListeners">Clearing both location listeners.</string>
<string name="logStartingServiceAfterAppUpdate">Starting service after app update.</string>
<string name="logNotStartingServiceAfterAppUpdate">Not starting service after app update.</string>
<string name="logStartingServiceAtPhoneBoot">Starting service at phone boot.</string>
<string name="logNotStartingServiceAtPhoneBoot">Not starting service at phone boot.</string>
<string name="applicationHasBeenUpdated">Application has been updated.</string>
<string name="startServiceAfterAppUpdate">Start service automatically after app update if it has been running before.</string>
<string name="startServiceAfterAppUpdateShort">Start service after update</string>
<string name="wifiConnection">Wifi connection</string>
<string name="wifiName">Wifi name</string>
<string name="enterWifiName">Enter a wifi name. Leave empty for any wifi.</string>
<string name="cancel">Cancel</string>
<string name="ruleDoesntApplyWeAreSlowerThan">Rule doesn\'t apply. We are slower than</string>
<string name="ruleDoesntApplyWeAreFasterThan">Rule doesn\'t apply. We are faster than</string>
<string name="ruleDoesntApplyItsQuieterThan">Rule doesn\'t apply. It\'s quieter than</string>
<string name="ruleDoesntApplyItsLouderThan">Rule doesn\'t apply. It\'s louder than</string>
<string name="ruleDoesntApplyBatteryLowerThan">Rule doesn\'t apply. Battery level is lower than</string>
<string name="ruleDoesntApplyBatteryHigherThan">Rule doesn\'t apply. Battery level is higher than</string>
<string name="ruleDoesntApplyNotTheCorrectSsid">Rule doesn\'t apply. Not the correct SSID (demanded: \"%1$s\", given: \"%2$s\").</string>
<string name="ruleDoesntApplyNoTagLabel">Rule doesn\'t apply. There is no tag label or not tag at all.</string>
<string name="ruleDoesntApplyWrongTagLabel">Rule doesn\'t apply. Wrong tag label.</string>
<string name="ruleIsDeactivatedCantApply">Rule %1$s is deactivated, can\'t apply.</string>
<string name="starting">starting</string>
<string name="stopping">stopping</string>
<string name="connecting">connecting</string>
<string name="disconnecting">disconnecting</string>
<string name="exceeding">exceeding</string>
<string name="droppingBelow">dropping below</string>
<string name="connectedToWifi">connected to wifi \"%1$s\"</string>
<string name="disconnectedFromWifi">disconnected from wifi \"%1$s\"</string>
<string name="anyWifi">any wifi</string>
<string name="cantStopIt">Can\'t stop it.</string>
<string name="settingsCategoryHttp">HTTP(s) Requests</string>
<string name="httpAcceptAllCertificatesTitle">Accept all certificates</string>
<string name="httpAcceptAllCertificatesSummary">Skip validity check of SSL certificates (recommended against activating this)</string>
<string name="httpAttemptsSummary">Number of attempts in case HTTP requests fail for connectivity reasons</string>
<string name="httpAttemptsTitle">Number of HTTP attempts</string>
<string name="httpAttemptsTimeoutSummary">Timeout for HTTP requests [seconds]</string>
<string name="httpAttemptsTimeoutTitle">Timeout [sec]</string>
<string name="httpAttemptGapSummary">Pause before another attempt [seconds]</string>
<string name="httpAttemptGapTitle">Pause [sec]</string>
<string name="runManually">Run manually</string>
<string name="serviceHasToRunForThat">The service has to be running for that.</string>
<string name="gpsComparison">GPS comparison</string>
<string name="gpsComparisonTimeoutStop">Stopping comparison GPS measurement due to timeout.</string>
<string name="timeoutForGpsComparisonsTitle">GPS timeout [sec]</string>
<string name="timeoutForGpsComparisonsSummary">Maximum time in seconds to trying getting a GPS location for comparison. If over last known location will be applied.</string>
<string name="startingGpsTimeout">Starting GPS timeout.</string>
<string name="forcedLocationUpdate">Forced location update</string>
<string name="forcedLocationUpdateLong">Due to timeout in comparison measurement the last best location will be applied.</string>
<string name="rememberLastActivePoiSummary">If you are at a location, restart your device or the application and leave the location the application will run rules accociated to leaving the location upon its next start.</string>
<string name="rememberLastActivePoiTitle">Remember last active location</string>
<string name="muteTextToSpeechDuringCallsTitle">Mute during calls</string>
<string name="muteTextToSpeechDuringCallsSummary">Mute TextToSpeech during calls</string>
<string name="anotherPoiByThatName">There is already another location by that name.</string>
<string name="anotherRuleByThatName">There is already another rule by that name.</string>
<string name="startOtherActivity">Start another program</string>
<string name="selectApplication">Select app</string>
<string name="selectPackageOfApplication">Select package of application</string>
<string name="selectActivityToBeStarted">Select activity of chosen package</string>
<string name="errorStartingOtherActivity">Error starting other activity</string>
<string name="anotherAppIsRunning">Another app is started/stopped</string>
<string name="settingsCategoryProcessMonitoring">Process monitoring</string>
<string name="timeBetweenProcessMonitoringsTitle">Seconds between process monitorings</string>
<string name="timeBetweenProcessMonitoringsSummary">The lower the higher the battery usage</string>
<string name="refreshingProcessList">Refreshing process list.</string>
<string name="processes">Processes</string>
<string name="startingPeriodicProcessMonitoringEngine">Starting periodic process monitoring engine.</string>
<string name="processMonitoring">Process monitoring</string>
<string name="periodicProcessMonitoringIsAlreadyRunning">Periodic process monitoring is already running. Won\'t start it again.</string>
<string name="stoppingPeriodicProcessMonitoringEngine">Stopping periodic process monitoring engine.</string>
<string name="periodicProcessMonitoringIsNotActive">Periodic process monitoring is not active. Can\'t stop it.</string>
<string name="periodicProcessMonitoringStarted">Periodic process monitoring started.</string>
<string name="periodicProcessMonitoringStopped">Periodic process monitoring stopped.</string>
<string name="rearmingProcessMonitoringMessage">Rearming process monitoring message.</string>
<string name="notRearmingProcessMonitoringMessageStopRequested">Not rearming process monitoring message, stop requested.</string>
<string name="messageReceivedStatingProcessMonitoringIsComplete">Message received stating process monitoring is complete.</string>
<string name="appStarted">App started.</string>
<string name="appStopped">App stopped.</string>
<string name="runningApp">Running app</string>
<string name="errorWritingSettingsToPersistentMemory">Error writing settings to persistent memory.</string>
<string name="settings">Settings</string>
<string name="writingSettingsToPersistentMemory">Writing settings to persistent memory.</string>
<string name="refreshingSettingsFromFileToMemory">Refreshing settings from file to memory.</string>
<string name="errorReadingSettings">Error reading settings.</string>
<string name="invalidStuffStoredInSettingsErasing">Invalid stuff stored in settings. Erasing settings...</string>
<string name="initializingSettingsToPersistentMemory">Initializing settings to persistent memory.</string>
<string name="errorInitializingSettingsToPersistentMemory">Error initializing settings to persistent memory.</string>
<string name="settingsErased">Settings erased.</string>
<string name="settingsSetToDefault">Settings set to default.</string>
<string name="batteryLevel">Battery level</string>
<string name="selectSpeed">Select speed</string>
<string name="selectBattery">Select battery level</string>
<string name="applyingSettingsAndRules">Applying settings, rules and locations.</string>
<string name="privacy">Privacy Policy</string>
<string name="privacyConfirmationText">A browser will now open on your device and load the privacy policy from the developer\'s website.</string>
<string name="waitBeforeNextAction">Wait before next action</string>
<string name="wakeupDevice">Wakeup device</string>
<string name="waitBeforeNextActionEnterValue">Enter a value in milliseconds how long it should be waited before next action.</string>
<string name="wakeupDeviceValue">Enter a value in milliseconds how long device should at least stay awake. 0 for default values.</string>
<string name="enterAPositiveValidNonDecimalNumber">Enter a positive valid non-decimal number.</string>
<string name="moveUp">Move up</string>
<string name="moveDown">Move down</string>
<string name="cantMoveUp">Can\'t move item up. It is already at the top.</string>
<string name="cantMoveDown">Can\'t move item down. It is already at the bottom.</string>
<string name="wifiNameSpecifiedCheckingThat">Wifi name specified, checking that.</string>
<string name="wifiNameMatchesRuleWillApply">Wifi name matches. Rule will apply.</string>
<string name="noWifiNameSpecifiedAnyWillDo">No wifi name specified, any will do.</string>
<string name="ruleCheckOf">RuleCheck of %1$s</string>
<string name="airplaneMode">Airplane mode</string>
<string name="activate">Activate</string>
<string name="deactivate">Deactivate</string>
<string name="airplaneModeSdk17Warning">Beginning from Android version 4.2 this feature only works if your device is rooted.</string>
<string name="triggerUrlReplacementPositionError">You asked for a position to be added to your URL. Unfortunately at this point I do not have any location, yet.</string>
<string name="addIntentValue">Add Intent pair</string>
<string name="parameterName">Parameter name</string>
<string name="parameterValue">Parameter value</string>
<string name="parameterType">Parameter type</string>
<string name="selectTypeOfIntentPair">Select a type for the intent pair.</string>
<string name="enterNameForIntentPair">Enter a name for the intent pair.</string>
<string name="enterValueForIntentPair">Enter a value for the intent pair.</string>
<string name="whatToDoWithIntentPair">What to do with pair?</string>
<string name="gettingListOfInstalledApplications">Getting list of installed applications...</string>
<string name="timeFrameWhichDays">On which days?</string>
<string name="insideOrOutsideTimeFrames">Inside or outside those timeframes?</string>
<string name="selectToggleDirection">Switch on or off?</string>
<string name="name">Name</string>
<string name="radiusWithUnit">Radius [m]</string>
<string name="status">Status</string>
<string name="actionDataConnection">Data connection</string>
<string name="actionSetDataConnectionOn">turn mobile data on</string>
<string name="actionSetDataConnectionOff">turn mobile data off</string>
<string name="roaming">Roaming</string>
<string name="activated">activated</string>
<string name="deactivated">deactivated</string>
<string name="until">until</string>
<string name="application">Application</string>
<string name="is">is</string>
<string name="phoneCall">Phone call</string>
<string name="with">with</string>
<string name="phoneNumber">Phone number</string>
<string name="enterPhoneNumber">Enter phone number. Leave empty for any number.</string>
<string name="phoneDirection">Select call direction</string>
<string name="any">any</string>
<string name="incoming">incoming</string>
<string name="outgoing">outgoing</string>
<string name="incomingAdjective">incoming</string>
<string name="outgoingAdjective">outgoing</string>
<string name="anyNumber">any number</string>
<string name="number">number</string>
<string name="nfcTag">NFC tag</string>
<string name="closeTo">close to</string>
<string name="withLabel">with label</string>
<string name="deviceDoesNotHaveNfc">It appears this device does not have NFC.</string>
<string name="nfcReadTag">Read ID from tag</string>
<string name="nfcWriteTag">Write tag</string>
<string name="nfcEnterValidIdentifier">Enter a valid identifier for the tag (like \"Home front door\").</string>
<string name="nfcTagWrittenSuccessfully">Tag written successfully.</string>
<string name="nfcTagWriteError">Error writing tag. Is it in range?</string>
<string name="nfcTagDiscovered">Tag discovered.</string>
<string name="nfcBringTagIntoRange">Bring an NFC tag into range.</string>
<string name="nfcTagFoundWithText">Tag found with text:</string>
<string name="nfcUnsupportedEncoding">Unsupported Encoding:</string>
<string name="nfcNoNdefIntentBut">No NFC NDEF intent, but</string>
<string name="nfcNotSupportedInThisAndroidVersionYet">NFC not supported in this Android version, yet.</string>
<string name="cantRunRule">Cannot run rules.</string>
<string name="cantDownloadTooFewRequestsInSettings">Can\'t download anything. Amount of http requests in settings is lower than 1.</string>
<string name="nfcApplyTagToRule">Apply tag to rule</string>
<string name="nfcTagReadSuccessfully">Tag read successfully.</string>
<string name="nfcValueNotSuitable">Value stored not suitable.</string>
<string name="nfcNoTag">No tag present.</string>
<string name="newNfcId">Write new NFC ID</string>
<string name="useExistingTag">Use existing NFC tag</string>
<string name="newId">New ID:</string>
<string name="currentId">Current ID:</string>
<string name="nfcTagDataNotUsable">Tag data no usable, write anew.</string>
<string name="nfcBringTagIntoRangeToRead">Bring a tag into range to read.</string>
<string name="toggleRule">Toggle rule</string>
<string name="toggling">Toggling</string>
<string name="toggle">toggle</string>
<string name="overlapBetweenPois">Overlap detected to location %1$s of %2$s meters. Reduce radius by at least that.</string>
<string name="noOverLap">No overlap to other locations detected.</string>
<string name="ruleToggable">Rule %1$s is toggable.</string>
<string name="ruleNotToggable">Rule %1$s is not suitable for toggling.</string>
<string name="none">none</string>
<string name="anyLocation">any location</string>
<string name="invalidPoiName">Invalid name for location.</string>
<string name="eraseSettings">Erase settings</string>
<string name="defaultSettings">Default settings</string>
<string name="areYouSure">Are you sure?</string>
<string name="poiCouldBeInRange">At least location %1$s could be in range, if not others in addition.</string>
<string name="noPoiInRelevantRange">No location in relevant range.</string>
<string name="activityDetection">Activity detection</string>
<string name="detectedActivity">Detected activity:</string>
<string name="detectedActivityInVehicle">In vehicle (car/bus)</string>
<string name="detectedActivityOnBicycle">On bicycle</string>
<string name="detectedActivityOnFoot">On foot</string>
<string name="detectedActivityStill">Still</string>
<string name="detectedActivityUnknown">Unknown</string>
<string name="detectedActivityTilting">Tilting</string>
<string name="detectedActivityWalking">Walking</string>
<string name="detectedActivityRunning">Running</string>
<string name="detectedActivityInvalidStatus">Invalid activity</string>
<string name="ruleDoesntApplyActivityGivenButTooLowProbability">Rule doesn\'t apply. Detected activity %1$s given, but too low probability (%2$s %%), required %3$s %%.</string>
<string name="ruleDoesntApplyActivityNotPresent">Rule doesn\'t apply. Required activity %1$s not present.</string>
<string name="selectTypeOfActivity">Select type of activity</string>
<string name="triggerOnlyAvailableIfPlayServicesInstalled">This trigger is only available if Google Play Services is installed.</string>
<string name="activityDetectionFrequencyTitle">Activity detection frequency [sec]</string>
<string name="activityDetectionFrequencySummary">Seconds between attempts to detect activity.</string>
<string name="activityDetectionRequiredProbabilityTitle">Activity detection probability</string>
<string name="activityDetectionRequiredProbabilitySummary">Certainty from which activities are accepted as fact.</string>
<string name="incomingCallFrom">Incoming telephone call from %1$s.</string>
<string name="outgoingCallFrom">Outgoing telephone call to %1$s.</string>
<string name="actionSpeakText">Speak text</string>
<string name="textToSpeak">Text to speak</string>
<string name="toggleNotAllowed">Toggling is currently only allowed for rules that have NFC tags as trigger. See help for further information.</string>
<string name="errorReadingPoisAndRulesFromFile">Error reading locations and rules from file.</string>
<string name="noDataChangedReadingAnyway">It appears no data change has been saved. However there may have been changes in memory that need to be rolled back. Rereading file.</string>
<string name="bluetoothConnection">Bluetooth connection</string>
<string name="bluetoothConnectionTo">Bluetooth connection to %1$s</string>
<string name="bluetoothDisconnectFrom">Bluetooth connection to %1$s torn</string>
<string name="bluetoothDeviceInRange">Bluetooth device %1$s in range.</string>
<string name="bluetoothDeviceOutOfRange">Bluetooth device %1$s out of range.</string>
<string name="anyDevice">any device</string>
<string name="ruleDoesntApplyNotTheCorrectDeviceName">Rule doesn\'t apply. Not the correct bluetooth device name.</string>
<string name="ruleDoesntApplyNotTheCorrectDeviceAddress">Rule doesn\'t apply. Not the correct bluetooth device address.</string>
<string name="noDevice">no device</string>
<string name="selectDeviceFromList">one from list</string>
<string name="connectionToDevice">connection to device</string>
<string name="disconnectionFromDevice">disconnection from device</string>
<string name="deviceInRange">device in range</string>
<string name="deviceOutOfRange">device out of range</string>
<string name="selectDeviceOption">Select a device option.</string>
<string name="selectConnectionOption">Select a connection option.</string>
<string name="ruleDoesntApplyDeviceInRangeButShouldNotBe">Rule doesn\'t apply. Device is in range, but should not be.</string>
<string name="ruleDoesntApplyStateNotCorrect">Rule doesn\'t apply. Wrong state.</string>
<string name="triggerHeadsetPlugged">Headset connection</string>
<string name="actionPlayMusic">Open music player</string>
<string name="headsetConnected">Headset (type: %1$s) connected</string>
<string name="headsetDisconnected">Headset (type: %1$s) disconnected</string>
<string name="headphoneSimple">Headphone</string>
<string name="headphoneMicrophone">Microphone</string>
<string name="headphoneAny">Either</string>
<string name="headphoneSelectType">Select type of headphone</string>
<string name="ruleDoesntApplyWrongHeadphoneType">Rule doesn\'t apply. Wrong headphone type.</string>
<string name="ignoringActivityDetectionUpdateTooSoon">Ignoring activity detection update. Came in sooner that %1$s seconds.</string>
<string name="whatsThis">What\'s this?</string>
<string name="atLeastRuleXisUsingY">At least rule \"%1$s\" is using a trigger of type \"%2$s\".</string>
<string name="privacyLocationingTitle">Only private locationing</string>
<string name="monday">Lunes</string>
<string name="tuesday">Martes</string>
<string name="wednesday">Miercoles</string>
<string name="thursday">Jueves</string>
<string name="friday">Viernes</string>
<string name="saturday">Sabado</string>
<string name="headphoneMicrophone">Microfóno</string>
<string name="whatsThis">Que es eso?</string>
<string name="privacyLocationingTitle">Solo usar localización privada</string>
<string name="privacyLocationingSummary">Avoid locationing methods that may send your location to a provider, e.g. Google. This will use GPS only and may therefore be slow or not work reliably.</string>
<string name="enforcingGps">Private Locationing enabled, enforcing GPS use.</string>
<string name="notEnforcingGps">Private Locationing not enabled, using regular provider search.</string>
<string name="gpsMeasurement">GPS measurement</string>
<string name="gpsMeasurementTimeout">GPS measurement stopped due to timeout.</string>
<string name="cellMastChanged">Cell mast changed: %1$s</string>
<string name="noiseDetectionHint">If you think the noise detection isn\'t working correctly (depending on the value you specify) please keep in mind that every phone is different. You can therefore change \"Reference for noise measurement\" in settings. See http://en.wikipedia.org/wiki/Decibel for more information. You can use the volume tester from the main screen to calibrate your device.</string>
<string name="hint">Hint</string>
<string name="selectNoiseLevel">Select noise level</string>
<string name="poiHasWifiStoppingCellLocationListener">Location has wifi. Stopping CellLocationListener.</string>
<string name="poiHasNoWifiNotStoppingCellLocationListener">Location doesn\'t have wifi. Not stopping CellLocationListener.</string>
<string name="showOnMap">Show on map</string>
<string name="noMapsApplicationFound">No maps application found on your device.</string>
<string name="locationEngineNotActive">Location engine not active.</string>
<string name="addProfile">Add profile</string>
<string name="profileList">Profiles</string>
<string name="profile">Profile</string>
<string name="soundMode">Sound mode</string>
<string name="volumes">Volumes</string>
<string name="incomingCallsRingtone">Tone for incoming calls</string>
<string name="notificationRingtone">Tone for notifications</string>
<string name="hapticFeedback">Haptic feedback (vibrate when touching screen)</string>
<string name="volumeMusicVideoGameMedia">Music, video, game and other media</string>
<string name="volumeRingtoneNotifications">Ringtone and notifications</string>
<string name="volumeAlarms">Alarms</string>
<string name="change">Change</string>
<string name="audibleSelection">Audible selection (sound when making screen selection)</string>
<string name="screenLockUnlockSound">Screen lock/unlock sound</string>
<string name="vibrateWhenRinging">Vibrate when ringing</string>
<string name="profiles">Profiles</string>
<string name="volumeAlarms">Alarmas</string>
<string name="change">modificar</string>
<string name="soundModeNormal">Normal</string>
<string name="soundModeVibrate">Vibrate</string>
<string name="soundModeSilent">Silent</string>
<string name="soundModeVibrate">Vibración</string>
<string name="soundModeSilent">Silencio</string>
<string name="enterAname">Enter a name!</string>
<string name="noChangeSelectedProfileDoesntMakeSense">No change selected. Profile doesn\'t make sense.</string>
<string name="noProfilesCreateOneFirst">There are no profiles in your configuration. Create one first.</string>
<string name="errorActivatingProfile">Error activating profile:</string>
<string name="anotherProfileByThatName">There is already another profile by that name.</string>
<string name="invalidProfileName">Invalid name for profile.</string>
<string name="errorWritingFile">Error writing settings file.</string>
<string name="unknownError">Unknown error.</string>
<string name="noWritableFolderFound">No writable folder found to store config file.</string>
<string name="usbTetheringFailForAboveGingerbread">This will most likely not work as you\'re above Android 2.3. However you could use wifi tethering instead.</string>
<string name="usingNewThreadForRuleExecution">Using new thread for rule activation.</string>
<string name="startNewThreadForRuleExecution">Start new thread for rule activation.</string>
<string name="newThreadRules">New thread</string>
<string name="showIcon">Show icon</string>
<string name="showIconWhenServiceIsRunning">Show icon when service is running (works only below Android 7)</string>
<string name="ruleHistory">Rule history (most recent first):</string>
<string name="someOptionsNotAvailableYet">Some options are disabled as they cannot be used, yet. They will be introduced in a later program version.</string>
<string name="lockSoundChanges">Lock sound changes</string>
<string name="noProfileChangeSoundLocked">Profile will not be activated. Last activated profile was locked.</string>
<string name="currentVolume">Current volume</string>
<string name="enterValidReferenceValue">Enter a valid reference value.</string>
<string name="volumeTest">Volume test</string>
<string name="volumeTesterExplanation">To calculate a dB value for noise monitoring you need to specify a so called physical reference value. Please read Wikipedia for further information. This value is most likely different for every phone. Drag the seekbar to change the defined physical reference value. The higher the reference value the lower the dB value will be. Constant measurings will be performed every %1$s seconds and the results displayed below. Press back when you have found a suitable value.</string>
<string name="settingsWillTakeTime">Some settings will not be applied before certain environment settings change or service is restarted.</string>
<string name="phoneIsRooted">Phone is rooted.</string>
<string name="phoneIsNotRooted">Phone is not rooted.</string>
<string name="dataConWithRootSuccess">Data connection was successfully changed using superuser permissions.</string>
<string name="dataConWithRootFail">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="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>
<string name="password">Password</string>
<string name="useAuthentication">Use authentication</string>
<string name="permissionsTitle">Required permissions</string>
<string name="permissionsExplanation">Explanation of required permissions</string>
<string name="username">Nombre de usuario</string>
<string name="ok">Ok</string>
<string name="disabledFeatures">Disabled features</string>
<string name="theFollowingPermissionsHaveBeenDenied">The following permissions have been denied:</string>
<string name="permissionsExplanationGeneric">The app is current running in limited mode and has deactivated some features. To fully function it requires permissions. If you want to use all functionality you have to grant the permissions in the following rights dialogues. If you do not certain rules can not be executed. In the following you are given an explanation for the requested permissions. Click "continue", when you are ready to proceed.</string>
<string name="permissionsExplanationSmall">To enable the feature you just tried to use more permissions are required. Click continue to request them.</string>
<string name="continueText">continue</string>
<string name="rule">Rule</string>
<string name="storeSettings">Read and store settings</string>
<string name="appRunningInLimitedMode">The app is running in limited mode because of lacking permissions.</string>
<string name="ruleLegend">Green = enabled, red = disabled, yellow = not enough permissions</string>
<string name="systemSettingsNote1">The permission to change some OS settings is required (even simple stuff like turn on bluetooth or wifi). After clicking "continue" a window will popup where you need to enable this for Automation. Then hit your "back" key.</string>
<string name="systemSettingsNote2">Further permissions will be requested in a second dialog afterwards.</string>
<string name="appRequiresPermissiontoAccessExternalStorage">Automation requires access to external storage to read its settings and rules.</string>
<string name="mainScreenPermissionNote">Automation requires more permissions to fully function. Click on this text to find out more and request them.</string>
<string name="invalidDevice">Invalid device</string>
<string name="google_app_id">your app id</string>
<string name="logFileMaxSizeSummary">Maximum log file size in Megabyte. Will be rotated if bigger.</string>
<string name="logFileMaxSizeTitle">Maximum log file size [Mb]</string>
<string name="android.permission.READ_CALL_LOG">Read phone log</string>
<string name="android.permission.READ_CALENDAR">Read calendar entries</string>
<string name="android.permission.ACCESS_FINE_LOCATION">Read exact location</string>
<string name="android.permission.ACCESS_COARSE_LOCATION">Read coarse location</string>
<string name="readLocation">Read location</string>
<string name="android.permission.INTERNET">Send data over a network connection</string>
<string name="android.permission.ACCESS_NETWORK_STATE">Read device\'s network state</string>
<string name="android.permission.ACCESS_WIFI_STATE">Read device\'s wifi state</string>
<string name="android.permission.BLUETOOTH">Change Bluetooth settings</string>
<string name="android.permission.BLUETOOTH_ADMIN">Change Bluetooth settings</string>
<string name="android.permission.NFC">Use NFC module</string>
<string name="android.permission.VIBRATE">Let phone vibrate</string>
<string name="android.permission.WAKE_LOCK">Keep phone on</string>
<string name="android.permission.MODIFY_AUDIO_SETTINGS">Change audio settings</string>
<string name="android.permission.RECORD_AUDIO">Record audio</string>
<string name="android.permission.PROCESS_OUTGOING_CALLS">Detect outgoing calls</string>
<string name="android.permission.MODIFY_PHONE_STATE">Change device settings</string>
<string name="android.permission.READ_PHONE_STATE">Detect phone state</string>
<string name="android.permission.READ_EXTERNAL_STORAGE">Read storage</string>
<string name="android.permission.WRITE_EXTERNAL_STORAGE">Write storage</string>
<string name="android.permission.GET_TASKS">Detect running processes</string>
<string name="android.permission.WRITE_SETTINGS">Change device settings</string>
<string name="android.permission.RECEIVE_BOOT_COMPLETED">Detect device reboot</string>
<string name="android.permission.WRITE_SECURE_SETTINGS">Change device settings</string>
<string name="android.permission.BATTERY_STATS">Read battery state</string>
<string name="android.permission.CHANGE_BACKGROUND_DATA_SETTING">Change data connection</string>
<string name="android.permission.SEND_SMS">Send text messages</string>
<string name="continueText">continuar</string>
<string name="rule">Regla</string>
<string name="android.permission.SEND_SMS">Enviar mensajes SMS</string>
<string name="android.permission.READ_CONTACTS">Read contact data</string>
<string name="android.permission.ACCESS_NOTIFICATION_POLICY">Override do not disturb policy</string>
<string name="theseAreThePermissionsRequired">These are the permissions required:</string>
<string name="ruleXrequiresThis">Rule \"%1$s\" requires this.</string>
<string name="helpTextActivityDetection">This feature can detect if you\'re currently on the go and if it is on foot or in which type of vehicle (to a certain extent). The feature is not fully built into Automation, but is provided by Google Play Services. Technically it does not give a yes/no result, but return a percentage to which level it is sure it detected you\'re status. You can setup the percentage value from which Automation will accept a result. Two remarks: 1) More than 1 status could occur at the same time. For example you might be WALKING inside a driving bus. 2) This sensor is relative cost intensive. If it is possible you might consider using alternatives, e.g. require your car\'s handsfree device to be connected to detect you\'re driving.</string>
<string name="sendTextMessage">Send text message</string>
<string name="textToSend">Text to send</string>
<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="ruleXrequiresThis">Regla \"%1$s\" requires this.</string>
<string name="sendTextMessage">Enviar mensaje SMS</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="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>
<string name="clickAndHoldForOptions">Klicken und halten Sie ein Objekt für Optionen.</string>
<string name="textToSend">Texto de enviar</string>
<string name="password">Contraseña</string>
<string name="showOnMap">Monstrar en una mapa</string>
<string name="headphoneAny">Igual</string>
<string name="sunday">Domingo</string>
<string name="pleaseEnterValidLatitude">Por favor inserte un grado de latitud válido.</string>
<string name="pleaseEnterValidLongitude">Por favor inserte un grade de longitud válido.</string>
<string name="pleaseEnterValidRadius">Por favor inserte un radio válido.</string>
<string name="selectOneDay">Por favor selectar al menos un dia.</string>
<string name="logAttemptingToBindToService">Intentando de connectar al servicio...</string>
<string name="logAttemptingToUnbindFromService">Intentando de disconnectar del servicio...</string>
<string name="logBoundToService">Connectado al servicio.</string>
<string name="logUnboundFromService">Separado del servicio.</string>
<string name="whatToDoWithRule">Hacer que con la regla?</string>
<string name="whatToDoWithPoi">Hacer que con el lugar?</string>
<string name="whatToDoWithProfile">Hacer que con el perfil?</string>
<string name="delete">borrar</string>
<string name="deleteCapital">Borrar</string>
<string name="serviceStopped">Servicio automation terminado.</string>
<string name="logServiceStopping">Terminando servicio.</string>
<string name="stillGettingPosition">Todavia buscando posición</string>
<string name="lastRule">Ultima regla:</string>
<string name="at">al</string>
<string name="service">Servicio:</string>
<string name="getCurrentPosition">Buscar positión actual</string>
<string name="savePoi">Guardar lugar</string>
<string name="deletePoi">Borrar posición</string>
<string name="latitude">Latitud</string>
<string name="longitude">Longitud</string>
<string name="ruleName">Nombre de regla</string>
<string name="triggers">Disparador(es)</string>
<string name="triggersComment">y-connectado (todo tienen que applicar al mismo tiempo)</string>
<string name="addTrigger">Añadir disparador</string>
<string name="actions">Acción(es)</string>
<string name="actionsComment">(ejecutado in esta orden)</string>
<string name="addAction">Añadir acción</string>
<string name="saveRule">Guardar regla</string>
<string name="start">Inicio</string>
<string name="end">Final</string>
<string name="save">Guardar</string>
<string name="urlToTrigger">URL para ejecutar</string>
<string name="wifi">wifi</string>
<string name="activating">Estoy activando</string>
<string name="deactivating">Estoy desctivando</string>
<string name="entering">entrando</string>
<string name="leaving">saliendo</string>
<string name="noPoisSpecified">Al primer tienes que crear lugares.</string>
<string name="selectPoi">Seleccionar lugar</string>
<string name="selectTypeOfAction">Selecte tipo the acción</string>
<string name="connected">connectado</string>
<string name="stopped">terminado</string>
<string name="started">Commencado</string>
<string name="disconnected">separado</string>
<string name="selectSoundProfile">Selecte perfil de sonido</string>
<string name="whatToDoWithTrigger">Hacer que con el disparador?</string>
<string name="whatToDoWithAction">Hacer que con la acción?</string>
<string name="radiusHasToBePositive">Radio tiene que ser un numero positivo.</string>
<string name="poiStillReferenced">Todavia hay reglas cuales usan este lugar (%1$s). No puedo borrar el.</string>
<string name="generalSettings">Reglajes generales.</string>
<string name="startAtSystemBoot">Inicializar al boot.</string>
<string name="writeLogFile">Guardar un archivo protocolo</string>
<string name="wifiState">Estado wifi</string>
<string name="showHelp">Descripción</string>
<string name="rules">Reglas</string>
<string name="timeframes">Intervalo</string>
<string name="helpTitleEnergySaving">Configuración de ahorro de energia</string>
<string name="speedMaximumTime">Tiempo en minutos</string>
<string name="exceeds">exede</string>
<string name="dropsBelow">es menos que</string>
<string name="triggerPointOfInterest">Lugar</string>
<string name="triggerTimeFrame">Intervalo</string>
<string name="triggerSpeed">Velocidad</string>
<string name="actionSetWifi">Wifi</string>
<string name="actionSetBluetooth">Bluetooth</string>
<string name="actionSetWifiTethering">Enrutador wifi</string>
<string name="actionSetUsbTethering">Enrutador USB</string>
<string name="actionTurnBluetoothOn">encender Bluetooth</string>
<string name="actionTurnWifiOn">encender wifi</string>
<string name="actionTurnWifiOff">desactivar Bluetooth</string>
<string name="actionTurnBluetoothOff">desactivar Bluetooth</string>
<string name="actionTriggerUrl">Abrir URL en antecedentes</string>
<string name="actionChangeSoundProfile">Cambiar perfil sonido</string>
<string name="actionTurnUsbTetheringOn">encender enrutador USB</string>
<string name="actionTurnUsbTetheringOff">desactivar enrutador USB</string>
<string name="actionTurnWifiTetheringOn">encender enrutatdor wifi</string>
<string name="actionTurnWifiTetheringOff">desactivar enrutador wifi</string>
<string name="actionTurnAirplaneModeOn">encender modo de vuelo</string>
<string name="actionTurnAirplaneModeOff">desactivar modo de vuelo</string>
<string name="activePoi">Lugar activo</string>
<string name="closestPoi">Lugar mas cerca</string>
<string name="poi">Posición</string>
<string name="pois">posiciónes</string>
<string name="serviceNotRunning">Servicio not esta activo</string>
<string name="general">General</string>
<string name="startServiceAfterAppUpdate">Encender servicio después un update si estuve activado</string>
<string name="startServiceAfterAppUpdateShort">Encender servicio después un update</string>
<string name="cancel">Cancelar</string>
<string name="wifiName">Nombre de wifi</string>
<string name="wifiConnection">Coneción a un wifi</string>
<string name="exceeding">exedendo</string>
<string name="droppingBelow">estendo menos que</string>
<string name="anyWifi">algun wifi</string>
<string name="selectApplication">Elega la app</string>
<string name="selectPackageOfApplication">Elega el paquete de la app</string>
<string name="selectActivityToBeStarted">Elega la actividad de la app</string>
<string name="errorStartingOtherActivity">Error encendiendo otra app</string>
<string name="startAppBySendBroadcast">con sendBroadcast()</string>
<string name="startAppByStartActivity">con startActivity()</string>
<string name="stringNotAllowed">String %1$s not esta permitido.</string>
<string name="noFilesImported">No pudo importar archivos.</string>
<string name="noApplicableFilesFoundInDirectory">No pudo encontrar archivos.</string>
<string name="prefsImportError">Hubo un error en importar la configuracion.</string>
<string name="configurationImportedSuccessfully">Importe la configuracion con éxito.</string>
<string name="importConfiguration">Importar configuracion</string>
<string name="exportConfiguration">Exportar configuracion</string>
<string name="startAppSelectionType">Método de elegir applicación</string>
<string name="addParameters">Añade parametrós</string>
<string name="fileDoesNotExist">Archivo no exista.</string>
<string name="selectSoundFile">Elija archivo sonido</string>
<string name="alwaysPlay">siempre tocar</string>
<string name="playSound">Tocar sonido</string>
<string name="direction">dirección</string>
<string name="anyApp">alguna app</string>
<string name="directionStringNotEquals">no es igual a</string>
<string name="directionStringStartsWith">comenza con</string>
<string name="directionStringEndsWith">termina con</string>
<string name="directionStringContains">incluye</string>
<string name="directionStringEquals">es igual a</string>
<string name="text">Texto</string>
<string name="title">Titulo</string>
<string name="notification">Notificaión</string>
<string name="locationDisabled">Localización desactivado</string>
<string name="error">Error</string>
<string name="android.permission.ACCESS_BACKGROUND_LOCATION">Determinar su posición en el contexto</string>
<string name="manageLocations">Crear p editar lugares</string>
<string name="startScreen">Ventana incial</string>
<string name="positioningEngine">Metodo de localización</string>
<string name="deviceDoesNotHaveBluetooth">Este móvil no tiene Bluetooth. Puede continuar pero probablemente no va a funciónar.</string>
<string name="android.permission.READ_CALL_LOG">Leer protocolo de teléfono</string>
<string name="android.permission.READ_CALENDAR">Leer calendario</string>
<string name="android.permission.ACCESS_FINE_LOCATION">Determinar la posición exacta</string>
<string name="android.permission.ACCESS_COARSE_LOCATION">Determinar la posición aproximada</string>
<string name="readLocation">Determinar la posición</string>
<string name="android.permission.INTERNET">Usar la conexión a internet</string>
<string name="android.permission.NFC">Usar NFC</string>
<string name="android.permission.VIBRATE">Vibrar</string>
<string name="android.permission.MODIFY_AUDIO_SETTINGS">Modificar la configuración sonida</string>
<string name="android.permission.RECORD_AUDIO">Grabar audio</string>
<string name="android.permission.PROCESS_OUTGOING_CALLS">Detecar llamados saliendos</string>
<string name="android.permission.READ_PHONE_STATE">Detecar el estado del móvil</string>
<string name="android.permission.READ_EXTERNAL_STORAGE">Leer la memoria</string>
<string name="android.permission.WRITE_EXTERNAL_STORAGE">Escribir a la memoria</string>
<string name="android.permission.WRITE_SETTINGS">Modificar la configuración del móvil</string>
<string name="android.permission.BATTERY_STATS">Determinar el estado de la batteria</string>
<string name="android.permission.CHANGE_BACKGROUND_DATA_SETTING">Modificar la conexión internet</string>
<string name="android.permission.ACCESS_NOTIFICATION_POLICY">Exeder configuración no molestar</string>
<string name="android.permission.WRITE_SECURE_SETTINGS">Escribir a la memoria</string>
<string name="apply">aceptar</string>
<string name="publishedOn">publicitado el</string>
<string name="postsNotification">%1$s crea notificación</string>
<string name="removedNotification">notificación de %1$s removido</string>
<string name="notificationAppears">Notificación aparece</string>
<string name="notificationDisappears">Notificación deaparece</string>
<string name="startAppByActivity">a través de activity</string>
<string name="startAppByAction">a través de action</string>
<string name="enterValidAction">Inserte una action válida</string>
<string name="enterPackageName">Inserte un package válido</string>
<string name="configurationExportedSuccessfully">Exportación completada con éxito</string>
<string name="noFileManageInstalled">No mánager archivo esta instalada</string>
<string name="cantFindSoundFile">No puedo buscar el archivo sonido %1$s, por eso no puedo tocar lo.</string>
<string name="ruleActive">Regla activa</string>
<string name="triggerCharging">Batteria esta cargando</string>
<string name="triggerUsb_host_connection">USB conexión a un computador</string>
<string name="actionSetDisplayRotation">Girar monitor</string>
<string name="actionEnableScreenRotation">activar girar monitor</string>
<string name="actionDisableScreenRotation">desactivar girar monitor</string>
<string name="overview">Sinopsis</string>
<string name="enterWifiName">Inserta el nombre del wifi. Deje vacio para applicar a todos wifis.</string>
<string name="startOtherActivity">Iniciar otra app</string>
<string name="settings">Ajustes</string>
<string name="errorReadingSettings">Error leer ajustes.</string>
<string name="bluetoothConnection">Bluetooth conexión</string>
<string name="bluetoothConnectionTo">Bluetooth conexión to %1$s</string>
<string name="anyDevice">algun aparato</string>
<string name="noDevice">no aparato</string>
<string name="actionPlayMusic">Abrir jugador musica</string>
<string name="profiles">Perfiles</string>
<string name="ruleHistory">Historia de reglas (más ultimas al primero)</string>
<string name="lockSoundChanges">Bloquerar modificaciónes sonidas</string>
<string name="status">Estado</string>
<string name="android.permission.ACCESS_NETWORK_STATE">Determinar el estado de la red</string>
<string name="clickAndHoldForOptions">Clice ý ase un elemento para opciónes</string>
<string name="ruleLegend">Verde = activado, roja = desactivado, amarillo = no sufienctes permisos</string>
<string name="addProfile">Añadir perfil</string>
<string name="profile">Perfil</string>
<string name="invalidProfileName">Nombre invalido</string>
<string name="anotherProfileByThatName">Hay otro perfil con lo mismo nombre.</string>
<string name="errorActivatingProfile">Error activando perfil:</string>
<string name="executeRulesAndProfilesWithSingleClickTitle">Activar reglas/perfiles con 1 clic</string>
<string name="name">Nombre</string>
<string name="useAuthentication">Usar verificación de la autenticidad</string>
<string name="radiusWithUnit">Radio [m]</string>
<string name="volumes">Niveles sonidos</string>
<string name="volumeRingtoneNotifications">Sonido polifónico ý notificaciónes</string>
<string name="notificationRingtone">Sonido polifónico para notificaciónes</string>
<string name="incomingCallsRingtone">Sonido polifónico para llamadas</string>
<string name="batteryLevel">NIvel de la bateria</string>
<string name="selectBattery">Elegir nivel de la bateria</string>
<string name="triggerNoiseLevel">Nivel del rudio fondo</string>
<string name="anotherAppIsRunning">Otra app esta enciendo/terminado</string>
<string name="airplaneMode">Modo vuelo</string>
<string name="triggerHeadsetPlugged">Auriculares conectado</string>
<string name="headsetConnected">Auriculares (tipo: %1$s) conectado</string>
<string name="headsetDisconnected">Auriculares (tipo: %1$s) desconectado</string>
<string name="phoneCall">Llamado</string>
<string name="phoneNumber">Número de teléfono</string>
<string name="enterPhoneNumber">Inserte numbero de teléfono. Vacio para algun número</string>
<string name="phoneDirection">Direción de llamada</string>
<string name="headphoneSimple">Auriculares</string>
<string name="headphoneSelectType">Elegir tipo de los auriculares</string>
<string name="accelerometer">" Acelerómetro "</string>
<string name="gpsAccuracy">GPS exactitud [m]</string>
<string name="soundSettings">Ajustes sonidos</string>
<string name="settingsCategoryNoiseLevelMeasurements">Medición de ruido fondo</string>
<string name="waitBeforeNextAction">Esperar antes de la ación próxima</string>
<string name="wakeupDevice">Desperatar móvil</string>
<string name="textToSpeak">Text para hablar</string>
<string name="state">Estado</string>
<string name="setScreenBrightness">Poner luminosidad del monitor</string>
<string name="brightnessManual">luminosidad manual del monitor</string>
<string name="brightnessAuto">luminosidad automatico</string>
<string name="autoBrightness">Activar luminosidad automatico</string>
<string name="setScreenBrightnessEnterValue">Inserte luminosidad deseada (de 0 a 100).</string>
<string name="autoBrightnessNotice">Si usa luminosidad automatica el valor probablemente no va a durar mucho tiempo.</string>
<string name="actionDataConnection">Datos móviles</string>
<string name="actionSpeakText">Hablar texto</string>
<string name="selectToggleDirection">Activar o desactivar</string>
<string name="activated">activado</string>
<string name="activate">Activar</string>
<string name="deactivate">Desactivar</string>
<string name="deactivated">desactivado</string>
<string name="selectNoiseLevel">Elija nivel del ruido fondo</string>
<string name="selectSpeed">Elegir velocidad</string>
<string name="selectTypeOfActivity">Elija tipo de actividad</string>
<string name="selectTypeOfTrigger">Elija tipo de disparador</string>
<string name="startAppStartType">Elija tipo de comienzo</string>
<string name="android.permission.BLUETOOTH">Cambiar ajusted Bluetooth</string>
<string name="android.permission.BLUETOOTH_ADMIN">Cambiar ajusted Bluetooth</string>
<string name="moreSettings">Mas ajustes</string>
<string name="openExamplesPage">Abrir pagina con ejemplos</string>
<string name="activityOrActionName">Nombre del la\nactivitdad or la action</string>
<string name="packageName">Nombre del paquete</string>
<string name="parameterName">Nombre del parámetro</string>
<string name="parameterValue">Valor del parámetro</string>
<string name="addIntentValue">Añadir pareja intento</string>
<string name="parameterType">Tipo del parámetro</string>
</resources>

View File

@ -82,7 +82,7 @@
<string name="anyNumber">qualsiasi numero</string>
<string name="anyWifi">qualsiasi wifi</string>
<string name="appRequiresPermissiontoAccessExternalStorage">Automation richiede lautorizzazione per archiviare e leggere le impostazioni e le regole.</string>
<string name="appRunningInLimitedMode">L\'applicazione è in esecuzione in modalità limitata a causa di autorizzazioni mancanti.</string>
<string name="featuresDisabled">L\'applicazione è in esecuzione in modalità limitata a causa di autorizzazioni mancanti.</string>
<string name="appStarted">App avviata.</string>
<string name="appStopped">App terminata.</string>
<string name="app_name">Automation</string>
@ -253,7 +253,6 @@ Quindi, se si crea una regola che imposta il profilo su vibrazione nell\'interva
<string name="logLevelTitle">Dettaglio del file di log</string>
<string name="longitude">Longitudine</string>
<string name="mainScreenPermissionNote">Automation richiede ulteriori autorizzazioni. Clicca su questo testo per saperne di più e concederle.</string>
<string name="menu_settings">Impostazioni</string>
<string name="messageReceivedStatingProcessMonitoringIsComplete">Il messaggio ricevuto attesta che il monitoraggio del processo è completato.</string>
<string name="minimumDistanceChangeForGpsLocationUpdates">Minimo intervallo (im metri) per l\'aggiornamento GPS </string>
<string name="minimumDistanceChangeForNetworkLocationUpdates">Minima distanza percorsa per aggiornare la posizione della rete.</string>
@ -359,7 +358,6 @@ Selezionare su “Continua” quando si è pronti a procedere.</string>
<string name="processes">Processi</string>
<string name="profile">Profilo</string>
<string name="profileActivate">Attivazione del profilo %1$s</string>
<string name="profileList">Lista alfabetica dei profili</string>
<string name="profiles">Profili</string>
<string name="radiusHasToBePositive">Il raggio deve avere valore positivo.</string>
<string name="radiusSuggestion">metri. Il raggio minimo è +1 ma puoi aumentare.</string>
@ -498,7 +496,6 @@ Selezionare su “Continua” quando si è pronti a procedere.</string>
<string name="timeframes">Intervalli</string>
<string name="timeoutForGpsComparisonsSummary">Massimo tempo in secondi per cercare di individuare la posizione GPS per confront. Allo scadere sarà assunta valida l\'ultima localizzazione rilevata.</string>
<string name="timeoutForGpsComparisonsTitle">GPS timeout [sec]</string>
<string name="title_activity_main">Automation</string>
<string name="toggableRules">Regole “Reversibili”</string>
<string name="toggle">toggle</string>
<string name="toggleNotAllowed">La reversibilità al momento è disponibile solo per le regole che hanno come evento un tag NFC. Consulta l\'help per i dettagli.</string>
@ -562,7 +559,7 @@ Selezionare su “Continua” quando si è pronti a procedere.</string>
<string name="wifiState">Stato Wifi</string>
<string name="with">con</string>
<string name="withLabel">con etichetta</string>
<string name="writeLogFileToSd">Memorizza un file di log su SD card</string>
<string name="writeLogFile">Memorizza un file di log</string>
<string name="writingSettingsToPersistentMemory">Scrivo le impostazioni nella memoria di massa.</string>
<string name="yes">Si</string>
<string name="edit">Elaborare</string>

View File

@ -10,5 +10,9 @@
<color name="teal_200">#008080</color>
<color name="teal_700">#008080</color>
<color name="black">#000000</color>
<color name="brightScreenBackgroundColor">#F3F3F3</color>
<color name="brightScreenTextColor">#FFFFFF</color>
<color name="darkScreenBackgroundColor">#FFFFFF</color>
<color name="darkScreenTextColor">#F3F3F3</color>
</resources>

View File

@ -1,8 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="menu_settings">Settings</string>
<string name="app_name">Automation</string>
<string name="title_activity_main">Automation</string>
<string name="ruleActivate">Activating rule %1$s</string>
<string name="profileActivate">Activating profile %1$s</string>
<string name="ruleActivateToggle">Activating rule %1$s in Togglemode</string>
@ -73,7 +71,7 @@
<string name="end">End</string>
<string name="save">Save</string>
<string name="urlToTrigger">URL to trigger:</string>
<string name="urlLegend">Variables:\nYou can use the following variables. Upon triggering they will be replaced with the corresponding value on your device. Include the brackets in your text.\n\n[uniqueid] - Your device\'s unique id\n[serialnr] - Your device\'s serial number\n[latitude] - Your device\'s latitude\n[longitude] - Your device\'s longitude\n[phonenr] - Number of last incoming or outgoing call\n[d] - Day of the month, 2 digits with leading zeros\n[m] - Numeric representation of a month, with leading zeros\n[Y] - A full numeric representation of a year, 4 digits\n[h] - 12-hour format of an hour with leading zeros\n[H] - 24-hour format of an hour with leading zeros\n[i] - Minutes with leading zeros\n[s] - Seconds, with leading zeros\n[ms] - milliseconds</string>
<string name="urlLegend">Variables:\nYou can use the following variables. Upon triggering they will be replaced with the corresponding value on your device. Include the brackets in your text.\n\n[uniqueid] - Your device\'s unique id\n[serialnr] - Your device\'s serial number\n[latitude] - Your device\'s latitude\n[longitude] - Your device\'s longitude\n[phonenr] - Number of last incoming or outgoing call\n[d] - Day of the month, 2 digits with leading zeros\n[m] - Numeric representation of a month, with leading zeros\n[Y] - A full numeric representation of a year, 4 digits\n[h] - 12-hour format of an hour with leading zeros\n[H] - 24-hour format of an hour with leading zeros\n[i] - Minutes with leading zeros\n[s] - Seconds, with leading zeros\n[ms] - milliseconds\n[notificationTitle] - title of last notification\n[notificationText] - text of last notification</string>
<string name="wifi">wifi</string>
<string name="activating">Activating</string>
<string name="deactivating">Deactivating</string>
@ -91,14 +89,14 @@
<string name="selectPoi">Select location</string>
<string name="selectTypeOfAction">Select type of action</string>
<string name="selectSoundProfile">Select sound profile</string>
<string name="whatToDoWithTrigger">What to do with it trigger?</string>
<string name="whatToDoWithAction">What to do with it action?</string>
<string name="whatToDoWithTrigger">What to do with trigger?</string>
<string name="whatToDoWithAction">What to do with action?</string>
<string name="radiusHasToBePositive">Radius has to be a positive number.</string>
<string name="poiStillReferenced">There are still rules that reference this location (%1$s). I can\'t delete it, yet.</string>
<string name="generalSettings">General settings</string>
<string name="startAtSystemBoot">Start at system boot</string>
<string name="onOff">On/Off</string>
<string name="writeLogFileToSd">Write log file to SD card</string>
<string name="writeLogFile">Write log file</string>
<string name="useTextToSpeechOnNormalSummary">Use TextToSpeech on normal</string>
<string name="useTextToSpeechOnVibrateSummary">Use TextToSpeech on vibrate</string>
<string name="useTextToSpeechOnSilentSummary">Use TextToSpeech on silent</string>
@ -465,7 +463,6 @@
<string name="noMapsApplicationFound">No maps application found on your device.</string>
<string name="locationEngineNotActive">Location engine not active.</string>
<string name="addProfile">Add profile</string>
<string name="profileList">Profiles</string>
<string name="profile">Profile</string>
<string name="soundMode">Sound mode</string>
<string name="volumes">Volumes</string>
@ -527,7 +524,7 @@
<string name="continueText">continue</string>
<string name="rule">Rule</string>
<string name="storeSettings">Read and store settings</string>
<string name="appRunningInLimitedMode">The app is running in limited mode because of lacking permissions.</string>
<string name="featuresDisabled">WARNING: Features are disabled, Automation is running in limited mode. Click here for more information.</string>
<string name="ruleLegend">Green = enabled, red = disabled, yellow = not enough permissions</string>
<string name="systemSettingsNote1">The permission to change some OS settings is required (even simple stuff like turn on bluetooth or wifi). After clicking "continue" a window will popup where you need to enable this for Automation. Then hit your "back" key.</string>
<string name="systemSettingsNote2">Further permissions will be requested in a second dialog afterwards.</string>
@ -601,9 +598,72 @@
<string name="manageLocations">Create or edit locations</string>
<string name="error">Error</string>
<string name="featureNotInFdroidVersion">This feature is based on non-free software. Therefore is is not available in the F-Droid version.</string>
<string name="settingsReferringToRestrictedFeatures">Your settings and or rules are currently referencing non-free features that cannot be provided in the F-Droid version.</string>
<string name="settingsReferringToRestrictedFeatures">Your settings and or rules are currently referencing non-free features that cannot be provided in the F-Droid version. That includes detecting your current physical activity.</string>
<string name="publishedOn">published on</string>
<string name="displayNewsOnMainScreen">Display application news on main screen</string>
<string name="displayNewsOnMainScreenDescription">Announcements about this app only, we\'re probably talking about 1-2 per year, not more.</string>
<string name="filesHaveBeenMovedTo">Automation now uses another path to store your files. The existing ones have been moved. You can find them here: %s.</string>
<string name="filesHaveBeenMovedTo">Automation now uses another path to store your files. All your Automation-files have been moved here: \"%s\". The external storage permission is not required anymore; you can revoke it. It will be removed in a future version.</string>
<string name="newsOptIn">Would you like to receive (only important) news about this app on the main screen? Those are downloaded from the developer\'s website. There will be no intrusive notification, just a text on the main screen when you open the app.</string>
<string name="locationDisabled">Location disabled</string>
<string name="locationEngineDisabledShort">Location cannot be determined anymore. Click here to find out why.</string>
<string name="locationEngineDisabledLong">Unfortunately your location cannot be determined anymore. A debt of gratitude is owed to Google for its infinite wisdom and amiableness.\\n\\nLet me explain this further. Starting with Android 10 a new permission was introduced that is needed to determine your location in the background (which of course is required for an app like this). Whilst I consider that a good idea in general the chicanery it involves for developers is not.\\n\\nWhen developing an app you can try to qualify for this permission by abiding to a catalog of requirements. Unfortunately new versions of my app have been rejected over a period of three months. I fulfilled all those requirements, Google\'s shitty development support claimed I would not. After giving them proof that I did after all - I got a response like \"I cannot help you anymore\". Eventually I gave up. \\n\\nAs a consequence the Google Play version can NOT use your location as a trigger anymore. My only alternative option would have been to have this application removed from the store entirely.\\n\\nI\'m very sorry about that, but I\'ve tried my best arguing with a \"support\" that repeatedly failed to pass the Turing test.\\n\\nThe good news: You can still have it all!\\n\\nAutomation is now open source and can be found in F-Droid. That is an app store that really cares about your privacy - rather than just acting like that. Simply backup your config file, uninstall this app, install it again from F-Droid, restore your config file - done.\\n\\nClick here to find out more:</string>
<string name="filesStoredAt">Config and log files are stored in folder %1$s. Click on this text to open a file explorer. Unfortunately this will only work on a rooted device.\n\nFOR ALL OTHER DEVICES: Simply use the export button to make a backup.</string>
<string name="notification">Notification</string>
<string name="title">Title</string>
<string name="text">Text</string>
<string name="directionStringEquals">equals</string>
<string name="directionStringContains">contains</string>
<string name="directionStringStartsWith">starts with</string>
<string name="directionStringEndsWith">ends with</string>
<string name="directionStringNotEquals">not equals</string>
<string name="anyApp">Any app</string>
<string name="notificationTriggerExplanation">This trigger will respond to other applications opening notifications in the notification area (or such being closed). You can specify another application from which the notification has to come from. If you don\'t the notifications from any other application will count. You can also specify strings that must be or must not be in their title or notification body. The comparison is done case-INsensitive.</string>
<string name="postsNotification">%1$s posts notification</string>
<string name="removedNotification">notification from %1$s removed</string>
<string name="notificationAppears">Notification appears</string>
<string name="notificationDisappears">Notification disappears</string>
<string name="direction">Direction</string>
<string name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">Read system notifications</string>
<string name="playSound">Play sound</string>
<string name="alwaysPlay">always play</string>
<string name="alwaysPlayExplanation">If this settings is active the sound will always be played. If it is deactivated it will only play if your phone is neither on mute nor vibrate. However if active it will not have an effect on the volume. So if your phone is on ring mode it will not increase the media volume for example. So if media volume is on mute you won\'t hear anything either.</string>
<string name="selectSoundFile">Select sound file</string>
<string name="fileDoesNotExist">File does not exist.</string>
<string name="noFileManageInstalled">No file manager installed.</string>
<string name="shareConfigAndLogFilesWithDev">Share config and log files with developer (via email).</string>
<string name="shareConfigAndLogExplanation">This will start a new email with your config and log files attached as zip file. It will not be sent automatically, you still need to hit \"send\". You can also change the recipient to yourself for example.</string>
<string name="startAppChoiceNote">Here you have 2 general options: 1. You can start a program by selecting an activity. Imagine this like preselecting a specific screen/window of an application. Keep in mind this may not always work. This is because the windows of an app might interact with each other, e.g. pass on parameters. When bluntly starting a specific screen that interaction has not happened and the window might close instantly (therefore it\'s never really shown). Try it nevertheless! You can enter an activity path manually, but it\'s recommended to use the \"Select\" button. If you decide to enter it manually enter the app\'s package name in the upper field and the full path of the activity in the lower one. 2. Selection by action In contrast to selecting a specific window you can also start a program by an action. This is like shouting out \"I\'d would like xyz\" and if there\'s an app installed that can help you with that it will be started. A good example would be start browser - you might even have multiple installed (one is usually the default one). You need to enter this manually, PackageName is optional here. Keep in mind no variables will be resolved. If you want to start the camera for example using \"MediaStore.ACTION_IMAGE_CAPTURE\" will not work. You have to take a look at the Android documentation and use this variable\'s actual value instead which in this example would be \"android.media.action.IMAGE_CAPTURE\".</string>
<string name="errorRunningRule">There was an error running a rule.</string>
<string name="cantFindSoundFile">Cannot find sound file %1$s and therefore not play it.</string>
<string name="addParameters">Add parameters</string>
<string name="com.wireguard.android.permission.CONTROL_TUNNELS">Control tunnels of the wireguard app</string>
<string name="startAppSelectionType">Method to\nselect application</string>
<string name="startAppByActivity">by activity</string>
<string name="startAppByAction">by action</string>
<string name="enterValidAction">Enter a valid action</string>
<string name="enterPackageName">Enter a valid package name.</string>
<string name="state">State</string>
<string name="phoneNumberExplanation">You can enter a specific phone number, but you don\'t have to. If you want to specify one you can either pick one from your address book or enter it manually.</string>
<string name="importConfiguration">Import configuration</string>
<string name="exportConfiguration">Export configuration</string>
<string name="moreSettings">More settings</string>
<string name="configurationExportedSuccessfully">Configuration exported successfully.</string>
<string name="ConfigurationExportError">There was an error while exporting the configuration.</string>
<string name="rulesImportedSuccessfully">Rules and locations have been imported successfully.</string>
<string name="rulesImportError">There was an error importing rules and locations.</string>
<string name="configurationImportedSuccessfully">Configuration imported successfully.</string>
<string name="prefsImportError">There was an error importing the preferences.</string>
<string name="noApplicableFilesFoundInDirectory">No applicable files could be found in that directory.</string>
<string name="noFilesImported">No file could be imported.</string>
<string name="notAllFilesImported">Not all applicable files could be imported.</string>
<string name="importExportExplanation">When clicking import or export you select the directory from which files are imported or exported to. When exporting existing files might get overwritten.</string>
<string name="intentDataComment">If your parameter is of type Uri AND you specify \"IntentData\" as name (lower/upper case is not important), the parameter will not be added as a normal parameter with putExtra(), but will instead be added to the intent with setData().</string>
<string name="stringNotAllowed">String %1$s is not allowed.</string>
<string name="startAppStartType">Select start type</string>
<string name="startAppByStartActivity">by startActivity()</string>
<string name="startAppBySendBroadcast">by sendBroadcast()</string>
<string name="openExamplesPage">Open webpage with examples</string>
<string name="packageName">Package name</string>
<string name="activityOrActionName">Activity/action name</string>
<!-- <string name="net.kollnig.missioncontrol.permission.ADMIN">Control the app Tracker Control</string>-->
</resources>

View File

@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.1.2"
classpath 'com.android.tools.build:gradle:4.2.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View File

@ -0,0 +1 @@
Fehler behoben, bei dem die App abgest<73>rzt ist, wenn eine Regel gespeichert wurde w<>hrend der Dienst lief.

View File

@ -0,0 +1,3 @@
Neuer Ausl<73>ser: Benachrichtigungen anderer Anwendungen auswerten.
Neue Aktion: Sounddatei abspielen.
Die <20>blichen Aufr<66>umarbeiten und Fehlerbehebungen.

View File

@ -0,0 +1 @@
Kleine Fehler behoben sowie Layout-Korrekturen

View File

@ -0,0 +1,5 @@
* Konfiguration kann jetzt ex- und importiert werden
* Komponenten vom Hauptbildschirm auf seperaten Bildschirm ausgelagert
* Titel und Text von Benachrichtigungen können jetzt als Variable benutzt werden
* Änderungen in der Programm starten Aktion
* Fortschritte in der Spanisch Übersetzung

View File

@ -0,0 +1 @@
Initiale F-Droid Version

View File

@ -0,0 +1,2 @@
Ort verschoben, an dem die Konfigurationsdatei gespeichert wird (nun ein Programm-spezifisches Verzeichnis unterhalb von Android/data).
Wenn Sie das Programm bisher schon verwendet haben, d.h. Ihre Dateien am alten Ort angelegt wurden, werden sie beim ersten Start verschoben. Wenn das erfolgreich war, ist die Berechtigung android.permission.WRITE_EXTERNAL_STORAGE nicht l<>nger notwendig.

View File

@ -0,0 +1,49 @@
Erstellen Sie Regeln, die aus Auslösern und Aktionen bestehen. Ein typisches Beispiel wäre etwas wie "Telefon auf stumm stellen, wenn ich auf der Arbeit bin."
Hier ist eine Liste der möglichen Auslöser und Aktionen:
Mögliche Auslöser:
* Ort
* Tag/Uhrzeit
* Ladezustand (lädt/lädt nicht)
* Akkustand
* USB Verbindung zu einem Computer besteht
* Die gegenwärtige Geschwindigkeit (km/h)
* Hintergrundlautstärke (nur bis Android 7)
* WLAN Verbindung
* Anwendere Anwendung wurde gestartet
* Flugzeugmodus
* Roaming aktiv oder nicht
* NFC tags
* Bluetooth Gerät verbunden
* Headset verbunden
* Telefongespräch im Gange
* Benachrichtigungen anderer Anwendungen
Mögliche Aktionen:
* WLAN ein-/ausschalten
* Bluetooth ein-/ausschalten
* USB Router ein-/ausschalten
* WLAN Router ein-/ausschalten
* Bildschirmdrehung ein-/ausschalten
* HTTP Request im Hintergrund auslösen
* Klingelton und Toneinstellungen ändern
* Eine andere Anwendung starten
* Warten (zwischen anderen Aktionen)
* Den Bildschirm des Geräts einschalten
* Flugzeugmodus ein-/ausschalten
* Datenverbindung ein-/ausschalten
* Text sprechen (TTS)
* Musikplayer öffnen
* Bildschirmhelligkeit ändern
* 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.
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.
Spenden sind nicht die einzige Möglichkeit mich zu motivieren :-)
* Wer mir etwas Gutes tun will, kann die Anwendung auch im Play Store bewerten.
* Außerdem ist Hilfe bei der Übersetzung willkommen. Englisch, Spanisch und Deutsch kann ich selbst. Aber sonst ist alles gern gesehen.
Erklärungen zu den vielen Berechtigungen können hier abgerufen werden: https://server47.de/automation/permissions_de.html

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

View File

@ -0,0 +1 @@
Automatisieren Sie Dinge auf Ihrem Ger<65>t, indem Sie Regeln anlegen.

View File

@ -0,0 +1 @@
Automation

View File

@ -0,0 +1 @@
Fixed bug that crashed the app when saving rules while the service was running.

View File

@ -0,0 +1,3 @@
New trigger: You can now use notifications of other apps.
New action: Play a sound file.
The usual cleanups and bugfixes.

View File

@ -0,0 +1 @@
Small fixes and layout corrections.

View File

@ -0,0 +1,5 @@
* Configuration can now be exported and imported
* Moved some things from main screen to separate screen
* Notification title and text can be used as variable
* Changes in start other app action
* Progressed in Spanish translation

View File

@ -0,0 +1,2 @@
Place to storage config files has been moved to regular location (program specific directory in Android/data).
If you had previously used to app your existing files are moved to this new location. If that was successful the permission android.permission.WRITE_EXTERNAL_STORAGE is no longer required.

View File

@ -18,6 +18,7 @@ Supported triggers:
* Bluetooth connection
* Headset connected
* Phone call running
* Notifications of other apps
Supported actions:
* Change wifi state
@ -35,4 +36,14 @@ Supported actions:
* Speak text
* Open music player
* Change screen brightness
* Send text message
* Send text message
* Play sound file
It's quite hard to keep this app working across the many different hardwares as well as the many changes Android undergoes over the versions. I can test it in the emulator, but that cannot show all bugs.
So if a certain feature is not working on your device - let me know. Over the years I have fixed almost all bugs that have been reasonably reported to me. But for that I'm dependend on your input.
Donations are not the only way to motivate me :-)
* If you want to suport me, can also review the app on Google Play.
* Furthermore I can always use help in translating the app. English, German and some Spanish are among my own skills. But everything else is more than welcome.
Explanation of the many permissions can be found here: https://server47.de/automation/permissions_en.html

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip